?? 12.htm
字號:
<p>有時(shí)需要確定一個(gè)描述符是否引用一個(gè)流。這與調(diào)用isatty函數(shù)來確定一個(gè)描述符
</p>
<p>是否引用一個(gè)終端設(shè)備相類似(11.9節(jié))。SVR4提供isastream函數(shù)。 </p>
<p>int isastream(int filedes); </p>
<p>返回:若是流返回1,否則返回0 </p>
<p>(由于某種原因,SVR4的設(shè)計(jì)者忘記將此函數(shù)的原型放在頭文件中,所以我們也不
</p>
<p>能為此函數(shù)寫一條#include語句。) </p>
<p>與isatty類似,它通常是用一個(gè)只對流設(shè)備才有效的ioctl函數(shù)來進(jìn)行測試的
</p>
<p>。程序12.8是該函數(shù)的一種可能的實(shí)現(xiàn)。它使用I_CANPUT ioctl來測試由第3個(gè)參
</p>
<p>數(shù)說明的優(yōu)先波段是否是可寫的?如果該ioctl執(zhí)行成功,則它對所涉及的流并末
</p>
<p>作任何改變。 </p>
<p>#include <stropts.h> </p>
<p>#include <unistd.h> </p>
<p>int </p>
<p>isastream(int fd) </p>
<p>{ </p>
<p>return(ioctl(fd, I_CANPUT, 0) != -1); </p>
<p>} </p>
<p>程序12.8 檢查描述符是否引用流設(shè)備 </p>
<p>程序12.9 可用于測試此函數(shù)。 </p>
<p>#include <sys/types.h> </p>
<p>#include <sys/fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(int argc, char *argv[]) </p>
<p>{ </p>
<p>int i, fd; </p>
<p>for (i = 1; i < argc; i++) { </p>
<p>printf("%s: ", argv[i]); </p>
<p>if ( (fd = open(argv[i], O_RDONLY)) < 0) { </p>
<p>err_ret("%s: can't open", argv[i]); </p>
<p>continue; </p>
<p>} </p>
<p>if (isastream(fd) == 0) </p>
<p>err_ret("%s: not a stream", argv[i]); </p>
<p>else </p>
<p>err_msg("%s: streams device", argv[i]); </p>
<p>} </p>
<p>exit(0); </p>
<p>} </p>
<p>程序12.9 測試isasteam函數(shù) </p>
<p>運(yùn)行此程序,得到很多由ioctl函數(shù)返回的出錯(cuò)信息。 </p>
<p>$ a.out /dev/tty /dev/vidadm /dev/null /etc/motd </p>
<p>/dev/tty:/dev/tty:streams device </p>
<p>/dev/vidadm:/dev/vidadm:not a stream:Invalid argument </p>
<p>/dev/null:/dev/null:not a stream:No suck device </p>
<p>/etc/motd: /etc/motd:not a stream:Not a typewriter </p>
<p>/dev/tty在SVR之下是個(gè)流設(shè)備,這與我們所期望的一致。/dev/vidadm不是一個(gè)流
</p>
<p>設(shè)備,但是它是支持其它ioctl請求的字符特殊文件。對于不知道這種ioctl請求的
</p>
<p>設(shè)備,它返回EINVAL。/dev/null是一種不支持任何ioctl操作的字符特殊文件,所
</p>
<p>以ioctl返回ENODEV。最后,/etc/motd是一個(gè)普通文件,而不是字符特殊文件,所
</p>
<p>以返回ENOTTY(這是在這種情況下的典型返回值)。 </p>
<p>ENOTTY("不是打字機(jī)")是個(gè)歷史產(chǎn)物,當(dāng)ioctl企圖對并不引用字符特
</p>
<p>殊設(shè)備 </p>
<p>的描述符進(jìn)行操作時(shí),Unix系統(tǒng)核都返回ENOTTY。 </p>
<p>實(shí)例 </p>
<p>如果ioctl參數(shù)request是I_LIST,則系統(tǒng)返回該流上所有模塊的名字,包括最頂端
</p>
<p>的驅(qū)動(dòng)程序。其第3個(gè)參數(shù)應(yīng)當(dāng)是指向str_list結(jié)構(gòu)是指針。 </p>
<p>struct str_list{ </p>
<p>int sl_nmods; 數(shù)組中項(xiàng)的數(shù)目 </p>
<p>struct str_modlist *sl_modlist; 指向數(shù)組中第一個(gè)元素的指針 </p>
<p>}; </p>
<p>應(yīng)將sl_modlist設(shè)置為指向str_mlist結(jié)構(gòu)數(shù)組的第一個(gè)元素,將sl_nmods設(shè)置為
</p>
<p>該數(shù)組中的項(xiàng)數(shù)。 </p>
<p>struct str_mlist { </p>
<p>char l_name[FMNAMESZ+1]; 以null結(jié)尾的模塊名字 </p>
<p>}; </p>
<p>常數(shù)FMNAMESZ定義在頭文件<sys/conf.h>中,其值常常是8。l_name的實(shí)際長度是
</p>
<p>FMNAMESZ+1,增加1個(gè)字節(jié)是為了存放null終止符。 </p>
<p>如果ioctl的第3個(gè)參數(shù)是0,則該函數(shù)返回的是模塊數(shù),而不是模塊名。我們將先
</p>
<p>用這種ioctl調(diào)用確定模塊數(shù),然后再分配所要求的str_mlist結(jié)構(gòu)數(shù)。 </p>
<p>程序12.10例示了I_LIST操作。由ioctl返回的名字列表并不對模塊和驅(qū)動(dòng)程序進(jìn)行
</p>
<p>區(qū)分,因?yàn)樵谠摿斜碇械淖詈笠豁?xiàng)是處于流底部的驅(qū)動(dòng)程序,所以在打印時(shí)將其標(biāo)
</p>
<p>明為驅(qū)動(dòng)程序。 </p>
<p>#include <sys/conf.h> </p>
<p>#include <sys/types.h> </p>
<p>#include <fcntl.h> </p>
<p>#include <stropts.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(int argc, char *argv[]) </p>
<p>{ </p>
<p>int fd, i, nmods; </p>
<p>struct str_list list; </p>
<p>if (argc != 2) </p>
<p>err_quit("usage: a.out <pathname>"); </p>
<p>if ( (fd = open(argv[1], O_RDONLY)) < 0) </p>
<p>err_sys("can't open %s", argv[1]); </p>
<p>if (isastream(fd) == 0) </p>
<p>err_quit("%s is not a stream", argv[1]); </p>
<p>/* fetch number of modules */ </p>
<p>if ( (nmods = ioctl(fd, I_LIST, (void *) 0)) < 0) </p>
<p>err_sys("I_LIST error for nmods"); </p>
<p>printf("#modules = %d\n", nmods); </p>
<p>/* allocate storage for all the module names */ </p>
<p>list.sl_modlist = calloc(nmods, sizeof(struct str_mlist)); </p>
<p>if (list.sl_modlist == NULL) </p>
<p>err_sys("calloc error"); </p>
<p>list.sl_nmods = nmods; </p>
<p>/* and fetch the module names */ </p>
<p>if (ioctl(fd, I_LIST, &list) < 0) </p>
<p>err_sys("I_LIST error for list"); </p>
<p>/* print the module names */ </p>
<p>for (i = 1; i <= nmods; i++) </p>
<p>printf(" %s: %s\n", (i == nmods) ? "driver" : "module", </p>
<p>list.sl_modlist++); </p>
<p>exit(0); </p>
<p>} </p>
<p>程序12.10 打印流中的模塊名 </p>
<p>$ who </p>
<p>stevens console sep 25 06:12 </p>
<p>stevens pts001 Oct 12 07:12 </p>
<p>$ a.out /dev/pts001 </p>
<p>#modules=4 </p>
<p>module:ttcompat </p>
<p>module:ldterm </p>
<p>module:ptem </p>
<p>driver:pts </p>
<p>$ a.out /dev/console </p>
<p>#modules=5 </p>
<p>module:ttcompat </p>
<p>module:ldterm </p>
<p>module:ansi </p>
<p>module:char </p>
<p>driver:cmux </p>
<p>在這兩種情形中,頂上的兩個(gè)流模塊都是一樣的(ttcompac和ldterm),但是余下
</p>
<p>的模塊和驅(qū)動(dòng)程序則不同。在第十九章中我們將說明偽終端情形。
</p>
<p>write至流設(shè)備 </p>
<p>在圖12.10中可以看到write至流設(shè)備產(chǎn)生一個(gè)M_DATA消息。一般而言,這確實(shí)如此
</p>
<p>,但是也還有一些情況需要考慮。首先,流中頂部的一個(gè)處理模塊規(guī)定了可順流傳
</p>
<p>送的最小、最大數(shù)據(jù)包長度。(無法查詢該模塊中規(guī)定的這些值。)如果write的
</p>
<p>數(shù)據(jù)長度超過最大值,則流首將這一數(shù)據(jù)分解成最大長度的若干數(shù)據(jù)包。最后一個(gè)
</p>
<p>數(shù)據(jù)包的長度則小于最大值。 </p>
<p>接著要考慮的是:如果向流write 0個(gè)字節(jié),又將如何呢?除非流涉及管道或FIFO
</p>
<p>,否則就順流發(fā)送0長消息。對于管道FIFO,為與以前版本兼容,系統(tǒng)的默認(rèn)處理
</p>
<p>方式是忽略0長write。可以用ioctl設(shè)置實(shí)現(xiàn)管道和FIFO的流寫方式以更改這種默
</p>
<p>認(rèn)處理方式。 </p>
<p>寫方式 </p>
<p>可以用ioctl取得和設(shè)置一個(gè)流的寫方式。如果將request設(shè)置為I_GWROPT,第三個(gè)
</p>
<p>參數(shù)為指向一個(gè)整型變量的指針,則該流的當(dāng)前寫方式就在該整型量中返回。如果
</p>
<p>將request設(shè)置為I_SWROPT,第3個(gè)參數(shù)是一個(gè)整型,則其值就成為該流新的寫方式。
</p>
<p>如同處理文件描述符標(biāo)志和文件狀態(tài)標(biāo)志(3.13節(jié))一樣,總是應(yīng)當(dāng)先取當(dāng)前寫方式
</p>
<p>值,然后修改它,而不只是將寫方式設(shè)置為某個(gè)絕對值(很可能要關(guān)閉某些原來打開
</p>
<p>的位)。 </p>
<p>目前,只定義了兩個(gè)寫方式值。 </p>
<p>SNDZERO 對管道和FIFO的0長寫會造成順流傳送一個(gè)0長消息。按系統(tǒng)默認(rèn),0長寫
</p>
<p>不發(fā)送消息。 </p>
<p>SNPIPE 在流上已出錯(cuò)后,若調(diào)用write或putmsg,則向調(diào)用進(jìn)程發(fā)送SIGPIPE信
</p>
<p>號。 </p>
<p>一個(gè)流也有讀方式,我們先說明getmsg和getpmsg函數(shù),然后再說明讀方式。
</p>
<p>_______________________________________________________________________ </p>
<p>____ </p>
<p>#include <stropts.h> </p>
<p>int getmsg(int filedes,struct strbuf *ctlptr, </p>
<p>struct strbuf *dataptr,int *flagptr); </p>
<p>int getpmsg(int filedes,struct strbuf *ctlptr, </p>
<p>struct strbuf *dataptr, int *bandptr, int *flagptr); </p>
<p>兩個(gè)函數(shù)返回:若成功為非負(fù)值,出錯(cuò)為-1 </p>
<p>_____________________________________________________________________ </p>
<p>________ </p>
<p>getmsg和getpmsg函數(shù) </p>
<p>用read,getmsg和getpmsg從流首讀流消息。 </p>
<p>注意,flagptr和bandptr是指向整型的指針。在調(diào)用之前,這兩個(gè)指針?biāo)赶虻恼?
</p>
<p>型單元中應(yīng)設(shè)置成所希望的消息類型,在返回時(shí),此整型量設(shè)置為所讀到的消息的
</p>
<p>類型。 </p>
<p>如果flagptr指向的整型單元的值是0,則getmsg返回在流首讀隊(duì)列中的下一個(gè)消息
</p>
<p>。如果下一個(gè)消息是高優(yōu)先權(quán)消息,則在返回時(shí),flagptr所指向的整型單元設(shè)置
</p>
<p>為RS_HIPRI。如果希望只接收高優(yōu)先權(quán)消息,則在調(diào)用getmsg之前必須將flagptr
</p>
<p>所指向的整型單元設(shè)置為RS_HIPRI。 </p>
<p>這兩個(gè)函數(shù)有很多條件來確定返回給調(diào)用者何種消息:(a)flagptr和bandptr所指
</p>
<p>向的值,(b)ctlptr->maxlen和dataptr->maxlen的值。對使用getmsg而言,并不需
</p>
<p>要了解所有這些細(xì)節(jié)。如欲了解這些細(xì)節(jié)請參閱getmsg(2)手冊頁。
</p>
<p>讀方式 </p>
<p>如果read流設(shè)備會發(fā)生些什么呢?有兩個(gè)潛在的問題:(1)如果讀到流中消息的記
</p>
<p>錄邊界將會怎樣?(2)如果調(diào)用read,而流中下一個(gè)消息有控制信息又將如何?對
</p>
<p>第一種情況的默認(rèn)處理方式被稱為字節(jié)流方式。在這種方式中,read從流中取數(shù)據(jù)
</p>
<p>直至滿足了要求,或者已經(jīng)沒有數(shù)據(jù)。在這種方式中,忽略流中消息的邊界。對第
</p>
<p>二種情況的默認(rèn)處理是,如果在隊(duì)列的前端有控制消息,則read出錯(cuò)返回。可以改
</p>
<p>變這兩種默認(rèn)處理方式。 </p>
<p>在調(diào)用ioctl時(shí),若將request設(shè)置為I_GRDOPT,第3個(gè)參數(shù)又是指向一個(gè)整型單元
</p>
<p>的指針,則對該流的當(dāng)前讀方式在該整型單元中返回。如果將request設(shè)置為I_SR
</p>
<p>DOPT,第3個(gè)參數(shù)是整型值,則該流的讀方式設(shè)置為該值。讀方式值有下列三個(gè):
</p>
<p>RNORM 普通,字節(jié)流方式(與上面說明的相同)。這是默認(rèn)方式。 </p>
<p>RMSGN
消息不刪除方式。讀從流中取數(shù)據(jù)直至讀到所要求的字節(jié)數(shù),或者到達(dá)消
</p>
<p>息邊界。如果某次讀只用了一個(gè)消息的一部分,則其余下部分仍留在流中,以供下
</p>
<p>一個(gè)讀取。 </p>
<p>RMSGD
消息刪除方式。這與不刪除方式的區(qū)別是,如果某次讀只用一個(gè)消息的一
</p>
<p>部分。則余下部分就被刪除,不再使用。 </p>
<p>在讀方式中還可指定另外三個(gè)常數(shù),以便設(shè)置在讀到流中包含協(xié)議信息的消息時(shí)r
</p>
<p>ead的處理方法: </p>
<p>RPROTNORM 協(xié)議-普通方式:read出錯(cuò)返回,errno設(shè)置為EBADMSG。這是默認(rèn)方式
</p>
<p>。 </p>
<p>RPROTDAT 協(xié)議-數(shù)據(jù)方式:read將控制部分作為數(shù)據(jù)返回給調(diào)用者。 </p>
<p>RPROTDIS 協(xié)議-刪除方式:read刪除消息中的控制信息,但是返回消息中的數(shù)
</p>
<p>據(jù)。 </p>
<p>實(shí)例 </p>
<p>程序12.11是在程序3.3的基礎(chǔ)上改寫
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -