?? 12.htm
字號:
<p>err_sys("write_lock error"); </p>
<p>TELL_CHILD(pid); </p>
<p>if (waitpid(pid, NULL, 0) < 0) </p>
<p>err_sys("waitpid error"); </p>
<p>} else { /* child */ </p>
<p>WAIT_PARENT(); /* wait for parent to set lock */ </p>
<p>set_fl(fd, O_NONBLOCK); </p>
<p>/* first let's see what error we get if region is locked */ </p>
<p>if (read_lock(fd, 0, SEEK_SET, 0) != -1) /* no wait */ </p>
<p>err_sys("child: read_lock succeeded"); </p>
<p>printf("read_lock of already-locked region returns %d\n", errno); </p>
<p>/* now try to read the mandatory locked file */ </p>
<p>if (lseek(fd, 0, SEEK_SET) == -1) </p>
<p>err_sys("lseek error"); </p>
<p>if (read(fd, buff, 2) < 0) </p>
<p>err_ret("read failed (mandatory locking works)"); </p>
<p>else </p>
<p>printf("read OK (no mandatory locking), buff = %2.2s\n", buff); </p>
<p>} </p>
<p>exit(0); </p>
<p>} </p>
<p>程序12.7 檢查是否支持強(qiáng)制性鎖 </p>
<p>此程序首先創(chuàng)建一個文件,并使強(qiáng)制性鎖機(jī)制對其起作用。然后fork一個子進(jìn)程。
</p>
<p>父進(jìn)程對整個文件設(shè)置一把寫鎖,子進(jìn)程則將該文件的描述符設(shè)置為非阻塞的,然
</p>
<p>后企圖對該文件設(shè)置一把讀鎖,我們期望這會出錯返回,并希望看到系統(tǒng)返回值是
</p>
<p>EACCES或EAGAIN。接著,子進(jìn)程將文件讀、寫位置調(diào)整到文件起點(diǎn),并試圖讀該文
</p>
<p>件。如果系統(tǒng)提供強(qiáng)制性鎖機(jī)制,則read應(yīng)返回EACCES或EAGAIN(因為該描述符是
</p>
<p>非阻塞的)。否則read返回所讀的數(shù)據(jù)。在SVR4中運(yùn)行此程序(該系統(tǒng)支持強(qiáng)制性
</p>
<p>鎖機(jī)制),得到: </p>
<p>$ a.out </p>
<p>read_lock of already-locked region returns 13 </p>
<p>read failed (mandatory locking works):No more processes </p>
<p>查看系統(tǒng)頭文件或intro(2)手冊頁,可以看到錯誤13對應(yīng)于EACCES。從例子中還可
</p>
<p>以看到,在read出錯返回信息部分中包含有"No more processes"。這通常來自于
</p>
<p>fork,表示已用完了進(jìn)程表項。 </p>
<p>$ a.out </p>
<p>read_lock of already_locked region returns 35 </p>
<p>read OK (no mandatory locking),buff=ab </p>
<p>其中,errno35對應(yīng)于EAGAIN。該系統(tǒng)不支持強(qiáng)制性鎖。 </p>
<p>實例 </p>
<p>讓我們回到本節(jié)的第一個問題:當(dāng)兩個人同時編輯同一個文件將會怎樣呢?一般的
</p>
<p>UNIX文本編輯器并不使用記錄鎖,所以對此問題的回答仍然是:該文件的最后結(jié)果
</p>
<p>取決于寫該文件的最后一個進(jìn)程。(4.3+BSD vi編輯器確實有一個編譯時選擇項使
</p>
<p>運(yùn)行時建議性記錄鎖起作用,但是這一選擇項并不是缺省可用的。)即使我們在一
</p>
<p>個編輯器,例如vi中使用了建議性鎖,可是這把鎖并不能阻止其他用戶使用另一個
</p>
<p>編輯器,該編輯器沒有使用建議性記錄鎖。 </p>
<p>若系統(tǒng)提供強(qiáng)制性記錄鎖,那么我們可以修改常用的編輯器(如果我們有該編輯器
</p>
<p>的源代碼。)如若我們沒有該編輯器的源代碼,那么我們可以試一試下述方法。編
</p>
<p>寫一個vi的前端程序。該程序立即調(diào)用fork,然后父進(jìn)程等待子進(jìn)程終止,子進(jìn)程
</p>
<p>打開在命令行中指定的文件,使強(qiáng)制性鎖起作用,對整個文件設(shè)置一把寫鎖,然后
</p>
<p>exec vi。在vi運(yùn)行時,該文件是加了寫鎖的,所以其他用戶不能修改它。當(dāng)vi結(jié)
</p>
<p>束時,父進(jìn)程從wait返回,此時我們自編的前端程序也就結(jié)束。在本例中假定鎖能
</p>
<p>跨越exec,這正是我們前面所說的SVR4的情況(SVR4是我們說過的提供強(qiáng)制性鎖的
</p>
<p>唯一系統(tǒng))。 </p>
<p>這種類型的前端程序是可以編寫的,但卻往往不能起作用。問題出在大多數(shù)編輯器
</p>
<p>(至少是vi和ed)讀它們的輸入文件,然后關(guān)閉它。只要引用被編輯文件的描述符
</p>
<p>關(guān)閉了,那么加在該文件上的鎖就被釋放了。這意味著,在編輯器讀了該文件的內(nèi)
</p>
<p>容,然后關(guān)閉了它,那么鎖也就不存在了。在前端程序中沒有任何方法可以阻止這
</p>
<p>一點(diǎn)。 </p>
<p>在第十六章的數(shù)據(jù)庫函數(shù)庫中,我們使用了記錄鎖以阻止多個進(jìn)程的并發(fā)存取。在
</p>
<p>本章,我們提供了時間測量以觀察記錄鎖對進(jìn)程的影響。 </p>
<p>12.4流(Streams) </p>
<p>流是系統(tǒng)V提供的構(gòu)造內(nèi)核設(shè)備驅(qū)動程序和網(wǎng)絡(luò)協(xié)議包等的一種通用方法,我們對
</p>
<p>流進(jìn)行討論的目的是理解下列各點(diǎn): </p>
<p>(a) 系統(tǒng)V的終端界面。 </p>
<p>(b) I/O多路復(fù)用中輪詢函數(shù)的使用(12.5.2節(jié))。 </p>
<p>(c) 基于流的管道和命名流管道的實現(xiàn)(15.2和12.5.2節(jié))。 </p>
<p>流機(jī)制是由Dennis Ritchie 發(fā)展起來的[Ritchie 1984],其目的是用通用、靈活的
</p>
<p>方法改寫傳統(tǒng)的字符I/O系統(tǒng)并適應(yīng)網(wǎng)絡(luò)協(xié)議的需要,后來流機(jī)制被加入SVR3。SV
</p>
<p>R4則提供了對流(基于流的終端I/O系統(tǒng))的全面支持。[AT&T 1990d]對SVR4實現(xiàn)
</p>
<p>進(jìn)行了說明。 </p>
<p>請注意不要將本章說明的流與標(biāo)準(zhǔn)I/O庫(5.2節(jié))中使用的流相混淆。
</p>
<p>一個流在用戶進(jìn)程和設(shè)備驅(qū)動程序之間提供了一條全雙工通路。流無需和實際硬件
</p>
<p>設(shè)備直接對話-流也可以用作為偽設(shè)備驅(qū)動程序。圖12.8示出了一個簡單流的基本
</p>
<p>結(jié)構(gòu)。 </p>
<p>圖12.8 一個簡單流 </p>
<p>在流首之下可以壓入處理模塊。這可以用ioctl實現(xiàn)。圖12.9示出了一個包含一個
</p>
<p>處理模塊的流。各方框之間用兩根帶箭頭的線連接,以強(qiáng)調(diào)流的全雙工特征。
</p>
<p>任意個數(shù)的處理模塊可以壓入流。我們使用術(shù)語"壓入",這是因為每一新模塊總是
</p>
<p>插到流首之下,而將以前壓入的模塊下壓。(這類似于后進(jìn)、先出的棧。)在圖1
</p>
<p>2.9中,我們標(biāo)出了流的兩側(cè),分別稱為順流(downstream)和逆流(upstream)
</p>
<p>。寫到流首的數(shù)據(jù)將順流而下傳送。由設(shè)備驅(qū)動程序讀到的數(shù)據(jù)則逆流向上傳送。
</p>
<p>圖12.9 具有處理模塊的流 </p>
<p>流模塊是作為核心部分執(zhí)行的,這類似于設(shè)備驅(qū)動程序,當(dāng)構(gòu)造核心時,流模塊連
</p>
<p>編進(jìn)入核心。大多數(shù)系統(tǒng)不允許將末連編進(jìn)入核心的流模塊壓入流。
</p>
<p>在圖11.2中示出了基于流的終端系統(tǒng)的一般結(jié)構(gòu)。圖中標(biāo)出的"讀、寫"函數(shù)是流首
</p>
<p>。標(biāo)注為"終端行規(guī)程"的框是一個流處理模塊。該處理模塊的實際名稱是ldterm。
</p>
<p>(各種流模塊的手冊頁在[AT&T 1990d]的第7節(jié)和[AT&T 1991]的第7節(jié)中。
</p>
<p>用第三章中說明的函數(shù)存取流,它們是:open、close、read、write和ioctl。另
</p>
<p>外,在SVR3核中增加了3個支持流的新函數(shù)(getmsg、putmsg、和poll),在SVR4
</p>
<p>中又加了兩個處理流不同優(yōu)先級波段消息的函數(shù)(getpmsg和putpmsg)。本節(jié)將說
</p>
<p>明這些新函數(shù),我們?yōu)榱鞔蜷_的路徑名通常在/dev目錄之下。用ls -l查看設(shè)備名
</p>
<p>,就能判斷該設(shè)備是否為流設(shè)備。所有流設(shè)備都是字符特殊文件。
</p>
<p>雖然某些有關(guān)流的文獻(xiàn)暗示我們可以編寫處理模塊,并將它們壓入流中,但是編寫
</p>
<p>這些模塊如同編寫設(shè)備驅(qū)動程序一樣,需要專門的技術(shù)。 </p>
<p>在流機(jī)制之前,終端是用現(xiàn)存的clist機(jī)制處理的。(Bach [1986]的10.3.1節(jié)和
L </p>
<p>effler et al[1989]的9.6節(jié))分別說明SVR2和4.3BSD中的clist
機(jī)制。將基于字符 </p>
<p>的設(shè)備添加到核心中通常涉及編寫設(shè)備驅(qū)動程序,將所有有關(guān)部分都安排在驅(qū)動程
</p>
<p>序中。對新設(shè)備的存取典型地通過原始設(shè)備進(jìn)行,這意味著每個用戶的read,writ
</p>
<p>e都直通進(jìn)入設(shè)備驅(qū)動程序。流機(jī)制使這種交互作用方式更加靈活,條理清晰,使
</p>
<p>得數(shù)據(jù)可以用流消息方式在流首和驅(qū)動程序之間傳送,并使任意數(shù)的中間處理模塊
</p>
<p>可對數(shù)據(jù)進(jìn)行操作。 </p>
<p>流消息 </p>
<p>流的所有輸入和輸出都基于消息。流首和用戶使用read、write、getmsg、getpms
</p>
<p>g、putmsg和putpmsg交換消息。在流首、各處理模塊和設(shè)備驅(qū)動程序之間,消息可
</p>
<p>以順流而下,也可以逆流而上。 </p>
<p>在用戶進(jìn)程和流首之間,消息由下列幾部分組成:消息類型、可選擇的控制信息,
</p>
<p>以及可選擇的數(shù)據(jù)。在圖12.10中示出了對應(yīng)于write、putmsg和putpmsg的不同參
</p>
<p>數(shù),所產(chǎn)生的不同消息類型。控制信息和數(shù)據(jù)存放在strbuf結(jié)構(gòu)中:
</p>
<p>struct strbuf { </p>
<p>int maxlen; 緩存大小 </p>
<p>int Len; 當(dāng)前在緩存中的字節(jié)數(shù) </p>
<p>char *buf 緩存指針 </p>
<p>}; </p>
<p>圖12.10 為write、putmsg和putpmsg產(chǎn)生的流消息的類型 </p>
<p>當(dāng)用putmsg或putpmsg發(fā)送消息時,len指定緩存中數(shù)據(jù)的字節(jié)數(shù)。當(dāng)用getmsg或g
</p>
<p>etpmsg接收消息時,maxlen
指定緩存長度(使核心不會溢出緩存),而len則由核 </p>
<p>心設(shè)置,說明存放在緩存中的數(shù)據(jù)量。0長消息是允許的,len為-1說明沒有控制信
</p>
<p>息或數(shù)據(jù)。 </p>
<p>為什么我們需要傳送控制信息和數(shù)據(jù)兩者呢?提供這兩者使我們可以實現(xiàn)用戶進(jìn)程
</p>
<p>和流之間的界面。Olander,McGrath和Israel[1986]說明了系統(tǒng)V服務(wù)界面的原先實
</p>
<p>現(xiàn)。AT&T[1990d]第五章詳細(xì)說明了服務(wù)界面,還使用了一個簡單的實例。可能最為
</p>
<p>人了解的服務(wù)界面是系統(tǒng)V的傳輸層界面(TLI),它提供了網(wǎng)絡(luò)系統(tǒng)界面,Stevens
</p>
<p>[1990]第七章對此進(jìn)行了說明。 </p>
<p>控制信息的另一個例子是發(fā)送一個無連接的網(wǎng)絡(luò)消息(數(shù)據(jù)報)。為了發(fā)送該消息
</p>
<p>,我們需要說明消息的內(nèi)容(數(shù)據(jù))和該消息的目的地址(控制信息)。如果我們
</p>
<p>不能將數(shù)據(jù)和控制一起發(fā)送,那么就要某種專門設(shè)計的方案。例如,我們可以用i
</p>
<p>octl說明地址,然后用write發(fā)送數(shù)據(jù)。另一種技術(shù)可能要求:地址占用數(shù)據(jù)的前
</p>
<p>N個字節(jié),用write寫數(shù)據(jù)。將控制信息與數(shù)據(jù)分開,并且提供處理兩者的函數(shù)(p
</p>
<p>utmsg和getmsg)是處理這種問題的較清晰的方法。 </p>
<p>有約25種不同類型的消息,但是只有少數(shù)幾種用于用戶進(jìn)程和流首之間。其余的則
</p>
<p>只在核心中順流、逆流傳送。(對于編寫流處理模塊的人員而言,這些消息是非常
</p>
<p>有用的,但是對編寫用戶級代碼的人員而言,則可忽略它們。)在我們所使用的函
</p>
<p>數(shù)(read、write、getmsg、getpmsg、putmsg和putpmsg)中,只涉及三種消息類
</p>
<p>型,它們是: </p>
<p>l M_DATA(I/O的用戶數(shù)據(jù)); </p>
<p>l M_PROTO(協(xié)議控制信息); </p>
<p>l M_PCPROTO(高優(yōu)先級協(xié)議控制信息)。 </p>
<p>流中的消息都有一個排隊優(yōu)先級; </p>
<p>l 高優(yōu)先級消息(最高優(yōu)先級); </p>
<p>l 優(yōu)先波段消息; </p>
<p>l 普通消息(最低優(yōu)先級)。 </p>
<p>普通消息是優(yōu)先波段為0的消息。優(yōu)先波段消息的波段可在1-255之間,波段愈高,
</p>
<p>優(yōu)先級也愈高。 </p>
<p>每個流模塊有兩個輸入隊列。一個接收來自它上面模塊的消息(這種消息從流首向
</p>
<p>驅(qū)動程序順流傳送)。另一個接收來自它下面模塊的消息(這種消息從驅(qū)動程序向
</p>
<p>流首逆流傳送)。在輸入隊列中的消息按優(yōu)先級從高到低排列。在圖12.10中,我
</p>
<p>們示出了針對write、putmsg和putpmsg的不同參數(shù),產(chǎn)生不同優(yōu)先級的消息。
</p>
<p>有一些消息我們未加考慮。例如,若流首從它下面接收到M_SIG消息,則產(chǎn)生
</p>
<p>一信號。這種方法用于終端行規(guī)程模塊向相關(guān)前臺進(jìn)程組發(fā)送終端產(chǎn)生的信號
</p>
<p>putmsg和putpmsg函數(shù) </p>
<p>putmsg和putpmsg函數(shù)用于將流消息(控制信息或數(shù)據(jù),或兩者)寫至流中。這兩
</p>
<p>個函數(shù)的區(qū)別是后者允許對消息指定一個優(yōu)先波段。 </p>
<p>_______________________________________________________________________ </p>
<p>_______ </p>
<p>#include <stropts.h> </p>
<p>int putmsg(int filedes,const struct strbug *ctlptr, </p>
<p>const struct strbuf *dataptr,int flag); </p>
<p>int uptpmsg(int filedes,const struct strbuf *ctlptr, </p>
<p>const struct strbuf *dataptr,int band,int flag); </p>
<p>兩個函數(shù)返回:若成功為0,出錯為-1 </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>對流也可以使用write函數(shù),它等效于不帶任何控制信息,flag為0的putmsg。
</p>
<p>這兩個函數(shù)可以產(chǎn)生三種不同優(yōu)先級的消息:普通、優(yōu)先波段和高優(yōu)先級。圖12.
</p>
<p>10詳細(xì)列出了這兩個函數(shù)中幾個參數(shù)的各種可能組合,以及所產(chǎn)生的不同類型的消
</p>
<p>息。 </p>
<p>圖12.10 write,putmsg和putpmsg所產(chǎn)生的流消息類型 </p>
<p>在此圖中,消息控制列中的"否"(no)對應(yīng)于空ctlptr參數(shù),或ctlptr->len為-1
</p>
<p>。該列中的"是"對應(yīng)于ctlptr是非空,以及ctlptr->len大于等于0。這些說明同樣
</p>
<p>適用于消息的數(shù)據(jù)部分(用dataptr代替ctlptr)。 </p>
<p>流 ioct l操作 </p>
<p>在SVR4中,使用ioctl可對流執(zhí)行29種不同的操作。關(guān)于這些操作的說明請見stre
</p>
<p>amio(7)手冊頁(AT&T1990d),頭文件<stropts.h>應(yīng)包括在使用這些操作的C代碼
</p>
<p>中。ioctl的第2個參數(shù)request說明執(zhí)行29個操作中的那一個。所有request都以I
</p>
<p>_開始。第3個參數(shù)則與request有關(guān)。有時第3個參數(shù)是一個整型值,有時它是指向
</p>
<p>一個整型或一個數(shù)據(jù)結(jié)構(gòu)的指針。 </p>
<p>實例-isastream函數(shù) </p>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -