?? 12.htm
字號:
該區域可以在當前文件尾端處開始或超過其尾端處開始,但是不能在文件起始位
</p>
<p>置之前開始或越過該起始位置。 </p>
<p>l 如若l_len為0,則表示鎖的區域從其起點(由l_start和l_whence決定)開始直
</p>
<p>至最大可能位置為止。也就是不管添寫到該文件中多少數據,它都處于鎖的范圍。
</p>
<p>l 為了鎖整個文件,通常的方法是將l_start說明為0,l_whence說明為SEEK_SET,
</p>
<p>l_len說明為0。 </p>
<p>上面提到了兩個鎖類型:共享讀鎖(l_type為F_RDLCK)和獨占寫瑣(F_WRLCK)。
</p>
<p>基本規則是:多個進程在一個給定的字節上可以有一把共享的讀鎖,但是在一個給
</p>
<p>定字節上的寫鎖則只能由一個進程獨用。更進一步而言,如果在一個給定加字節上
</p>
<p>已經有一把或多把讀鎖,則不能在該字節上再加寫鎖;如果在一個字節上已經有一
</p>
<p>把獨占性的寫鎖,則不能再對它加任何讀鎖。在圖12.2中示出了這些規則。
</p>
<p>圖12.2 不同類型鎖之間的兼容性 </p>
<p>為了加讀鎖,該描述符必須是讀打開,為了加寫鎖,該描述符必須是寫打開。
</p>
<p>現在說明fcntl函數的三種命令。 </p>
<p>F_GETLK 決定由flockptr所說明的鎖是否被另外一把鎖所排斥(阻塞)。如
</p>
<p>果存在一把鎖,它阻止創建由flockptr所描述符的鎖,則這把現存的鎖的信息寫到
</p>
<p>flockptr指向的結構中;如果不存在這種情況,則除l_type設置為F_UNLCK之外,
</p>
<p>flockptr所指向結構中的其它信息、保持不變。 </p>
<p>F_SETLK 設置由flockptr所描述的鎖。如果試圖建立一把按上述兼容性規則
</p>
<p>并不允許的鎖,則fcntl立即出錯返回,此時errno設置為EACCES或EAGAIN。
</p>
<p>SVR2和SVR4返回EACCES,但手冊頁警告將來返回EAGAIN。4.3+BS </p>
<p>D則返回EAGAIN。POSIX.1允許這兩種情況。 </p>
<p>此命令也用來清除由flockptr說明的鎖(l_type為F_UNLCK)。 </p>
<p>F_SETLKW 這是F_SETLK的阻塞版本(命令名中的W表示等待(wait))。如果
</p>
<p>由于存在其它鎖,那么按兼容性規則由flockptr所要求的鎖不能被創建,則調用進
</p>
<p>程睡眠。如果捕捉到信號則睡眠中斷。 </p>
<p>應當了解,用F_GETLK測試能否建立一把鎖,然后用F_SETLK和F_SETLKW企圖建立一
</p>
<p>把鎖,這兩者不是一個原子操作。在這兩個操作之間可能會有另一個進程插入并建
</p>
<p>立一把相關的鎖,使原來測試到的情況發生變化,如果不希望在建立鎖時可能產生
</p>
<p>的長期阻塞,則應使用F_SETLK,并對返回結果進行測試,以判別是否成功地建立
</p>
<p>了所要求的鎖。 </p>
<p>在設置或釋放在一個文件上的一把鎖時。系統按需組合或裂開相鄰區。例如若100
</p>
<p>-199字節是加鎖的區,然后解鎖第150字節,則系統核將維持兩把鎖,一把是從10
</p>
<p>0-149字節,另一把是從151-199字節。 </p>
<p>實例-要求和釋放一把鎖 </p>
<p>為了免于每次分配flock結構,然后又填入各項信息,可以用程序12.2中的函數lo
</p>
<p>ck_reg來處理這些細節。 </p>
<p>#include <sys/types.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len </p>
<p>) </p>
<p>{ </p>
<p>struct flock lock; </p>
<p>lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */ </p>
<p>lock.l_start = offset; /* byte offset, relative to l_whence */ </p>
<p>lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ </p>
<p>lock.l_len = len; /* #bytes (0 means to EOF) */ </p>
<p>return( fcntl(fd, cmd, &lock) ); </p>
<p>} </p>
<p>程序12.2 鎖和解鎖一個文件區域的函數 </p>
<p>因為大多數鎖調用是鎖或解鎖一個文件區域(命令F_GETLK很少使用)。我們通常
</p>
<p>使用下列五個宏,它們都定義在ourhdr.h中(附錄B)。 </p>
<p>#define read_lock(fd,offset,whence,len) </p>
<p>lock_reg(fd,F_SETLK,F_RDLCK,offset,whence,len) </p>
<p>#define needw_lock(fd,offset,whence,len) </p>
<p>lock_reg(fd,F_SETLKW,F_RDLCK,offset,whence,len) </p>
<p>#define write_lock(fd,offset,whence,len) </p>
<p>lock_reg(fd,F_SETLK,F_WRLCK,offset,whence,len) </p>
<p>#define writew_lock(fd,offset,whence,len) </p>
<p>lock_reg(fd,F_SETLKW,F_WRLCK,offset,whence,len) </p>
<p>#define un_lock(fd,offset,whence,len) </p>
<p>lock_reg(fd,F_SETLK,F_UNLCK,offset,whence,len) </p>
<p>我們以lseek函數中的同樣順序定義了這些宏中的三個參數。 </p>
<p>實例-測試一把鎖 </p>
<p>程序12.3定義了一個函數lock_test,可用其測試一把鎖。 </p>
<p>#include <sys/types.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>pid_t </p>
<p>lock_test(int fd, int type, off_t offset, int whence, off_t len) </p>
<p>{ </p>
<p>struct flock lock; </p>
<p>lock.l_type = type; /* F_RDLCK or F_WRLCK */ </p>
<p>lock.l_start = offset; /* byte offset, relative to l_whence */ </p>
<p>lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ </p>
<p>lock.l_len = len; /* #bytes (0 means to EOF) */ </p>
<p>if (fcntl(fd, F_GETLK, &lock) < 0) </p>
<p>err_sys("fcntl error"); </p>
<p>if (lock.l_type == F_UNLCK) </p>
<p>return(0); /* false, region is not locked by anothe </p>
<p>proc */ </p>
<p>return(lock.l_pid); /* true, return pid of lock owner */ </p>
<p>} </p>
<p>程序12.3 測試一個鎖條件的函數 </p>
<p>如果存在一把鎖,它阻塞由參數說明的鎖,則此函數返回持有這把現存鎖的進程的
</p>
<p>ID,否則此函數返回0。通常用下面兩個宏來調用此函數(它們也定義在ourhdr.h
</p>
<p>)。 </p>
<p>#define is_read_lockable(fd,offset,whence,len) </p>
<p>lock_test(fd,F_RDLCK,offset,whence,len) </p>
<p>#define is_write_lockable(fd,offset,whence,len) </p>
<p>lock_test(fd,F_WRLCK,offset,whence,len) </p>
<p>實例-死鎖 </p>
<p>如果兩個進程相互等待對方持有并且不釋放(鎖定)的資源時,則這兩個進程就處
</p>
<p>于死鎖狀態。如果一個進程已經控制了一個文件中的一個加鎖區域,然后它又試圖
</p>
<p>對另一個進程控制的區域加鎖,則它就會睡眠,在這種情況下,有發生死鎖的可能
</p>
<p>性。 </p>
<p>程序12.4示出了一個死鎖的例子。子進程鎖字節0,父進程鎖字節1。然后,它們中
</p>
<p>的每一個又試圖鎖對方已經加了鎖的字節。在該程序中使用了8.8節中介紹的父-子
</p>
<p>進程同步例程(TELL_xxx,WAIT_xxx),使得對方都能建立第一把鎖。運行程序12
</p>
<p>..4得到: </p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>static void lockabyte(const char *, int, off_t); </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int fd; </p>
<p>pid_t pid; </p>
<p>/* Create a file and write two bytes to it */ </p>
<p>if ( (fd = creat("templock", FILE_MODE)) < 0) </p>
<p>err_sys("creat error"); </p>
<p>if (write(fd, "ab", 2) != 2) </p>
<p>err_sys("write error"); </p>
<p>TELL_WAIT(); </p>
<p>if ( (pid = fork()) < 0) </p>
<p>err_sys("fork error"); </p>
<p>else if (pid == 0) { /* child */ </p>
<p>lockabyte("child", fd, 0); </p>
<p>TELL_PARENT(getppid()); </p>
<p>WAIT_PARENT(); </p>
<p>lockabyte("child", fd, 1); </p>
<p>} else { /* parent */ </p>
<p>lockabyte("parent", fd, 1); </p>
<p>TELL_CHILD(pid); </p>
<p>WAIT_CHILD(); </p>
<p>lockabyte("parent", fd, 0); </p>
<p>} </p>
<p>exit(0); </p>
<p>} </p>
<p>static void </p>
<p>lockabyte(const char *name, int fd, off_t offset) </p>
<p>{ </p>
<p>if (writew_lock(fd, offset, SEEK_SET, 1) < 0) </p>
<p>err_sys("%s: writew_lock error", name); </p>
<p>printf("%s: got the lock, byte %d\n", name, offset); </p>
<p>} </p>
<p>程序12.4 死鎖檢測實例 </p>
<p>$ a.out </p>
<p>child:got the lock,byte 0 </p>
<p>parent:got the lock,byte 1 </p>
<p>child:writew_lock error:Deadlock situation detected/avoided </p>
<p>parent:got the lock,byte 0 </p>
<p>檢測到死鎖時,系統核必須選擇一個進程收到出錯返回。在本實例中,選擇了子進
</p>
<p>程,這是一個實現細節。當此程序在另一個系統上運行時,一半次數是子進程接到
</p>
<p>出錯信息,另一半則是父進程。 </p>
<p>鎖的隱含繼承和釋放 </p>
<p>關于記錄鎖的自動繼承和釋放有三條規則: </p>
<p>1.
鎖與一個進程、一個文件兩方面有關。這有兩重含意。第一重是很明顯的,當
</p>
<p>一個進程終止時,它所建立的鎖全部釋放。第二重意思就不很明顯,任何時候關閉
</p>
<p>一個描述符時,則該進程通過這一描述符可以存訪的文件上的任何一把鎖都被釋放
</p>
<p>(這些鎖都是該進程設置的)。這就意味著如果執行下列四步: </p>
<p>fd1=open(pathname,…); </p>
<p>read_lock(fd1,…); </p>
<p>fd2=dup(fd1); </p>
<p>close(fd2); </p>
<p>則在close(fd2)后,在fd1上設置的鎖被釋放。如果將dup代換為open </p>
<p>,其效果也一樣: </p>
<p>fd1=open(palhname,…); </p>
<p>read_lock(fd1,…); </p>
<p>fd2=open(palhname,…); </p>
<p>close(fd2); </p>
<p>2. 由fork產生的子程序不繼承父進程所設置的鎖。這意味著,若一個進程得到一
</p>
<p>把鎖,然后調用fork,那么對于父進程獲得的鎖而言,子進程被視為另一個進程,
</p>
<p>對于從父進程處繼承過來的任一描述符,子進程要調用fcntl以獲得它自己的鎖。
</p>
<p>這與鎖的作用是相一致的。鎖的作用是阻止多個進程同時寫同一個文件(或同一文
</p>
<p>件區域)。如果子進程繼承父進程的鎖,則父、子進程就可以同時寫同一個文件。
</p>
<p>3. 在執行exec后,新程序可以繼承原執行程序的鎖。 </p>
<p>POSIX.1沒有要求這一點。但是,SVR4和4.3+BSD都支持這一點 </p>
<p>4.3+BSD的實現 </p>
<p>先簡要地觀察4.3+BSD實現中使用的數據結構,從中可以看到鎖是與一個進程、一
</p>
<p>個文件相關聯的。 </p>
<p>考慮一個進程,它執行下列語句(忽略出錯返回): </p>
<p>fd1 = open(pathname, … ); </p>
<p>write_lock(fd1, 0, SEEK_SET, 1); 父進程在字節0寫? </p>
<p>if (fork() > 0) { </p>
<p>fd2 = dup(fdl); </p>
<p>fd3 = open(pathname, …); </p>
<p>pause; </p>
<p>} else { </p>
<p>read_lock(fd1, 1, SEEK_SET, 1); 子進程在字節1讀? </p>
<p>pause; </p>
<p>} </p>
<p>圖12.3 顯示了父、子進程暫停(執行pause( ))后的數據結構情況。 </p>
<p>圖12.3 關于記錄鎖的4.3+BSD數據結構 </p>
<p>在以前的圖3.4和8.1中已顯示了open、fork以及dup后的數據結構有了記錄鎖后,
</p>
<p>在原來的這些圖上新加了flock結構,它們由i_node結構開始相互連接起來。注意
</p>
<p>,每個flock結構說明了一個給定進程的一個加鎖區域。在圖中顯示了兩個flock結
</p>
<p>構,一個是由父進程調用write_lock形成的,另一個則由子進程調用read_lock形
</p>
<p>成的。每一個結構都包含了相應進程ID。 </p>
<p>在父進程中,關閉fd1、fd2和fd3中的任何一個都釋放由父進程設置的寫鎖。在關
</p>
<p>閉這三個描述符中的任何一個時,系統核會從該描述符所關連的i_node開始,逐個
</p>
<p>檢查flock連接表中各項,釋放由調用進程持有的各把鎖。系統核并不清楚也不關
</p>
<p>心父進程是用哪一個描述符來設置這把鎖的。 </p>
<p>實例: </p>
<p>建議性鎖可由精靈進程使用以保證該精靈進程只有一個副本在運行。在起動時,很
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -