?? 12.htm
字號:
</p>
<p>多精靈進程都把它們的進程ID寫到一個它們各自專用的一個PID文件上。當系統停
</p>
<p>機時,可以從這些文件中取用這些精靈進程的進程ID。防止一個精靈進程有多份副
</p>
<p>本同時運行的方法是:在精靈進程開始運行時,在它的進程ID文件上企圖設置一把
</p>
<p>寫鎖。如果在它運行時一直保持這把鎖,則就不可能再起動它的其它副本。程序1
</p>
<p>2.5實現了這一技術。 </p>
<p>因為進程ID文件可能包含以前的精靈進程ID,而且其長度還可能長于當前進程的I
</p>
<p>D,例如該文件中以前的內容可能是12345\n,而現在的進程ID是654,我們希望該
</p>
<p>文件現在只包含654\n,而不是654\n5,所以在寫該文件時,先將其截短為0。注意
</p>
<p>,要在設置了鎖之后再調用截短文件長度的函數ftruncate。在調用open時不能指
</p>
<p>定O_TRUNC,因為這樣做會在有一個這種精靈進程運行并對該文件加了鎖時也會使
</p>
<p>該文件截短為0。(如果使用強制性鎖而不是建議性鎖,則可使用O_TRUNC。在本節
</p>
<p>最后部分將討論強制性鎖。) </p>
<p>在本實例中,也對該描述符設置exec時關閉(close-on-exec)標志。這是因為精
</p>
<p>靈進程常常fork并exec其它進程,無需在另一個進程中使該文件也處在打開狀態。
</p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <errno.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>#define PIDFILE "daemon.pid" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int fd, val; </p>
<p>char buf[10]; </p>
<p>if ( (fd = open(PIDFILE, O_WRONLY | O_CREAT, FILE_MODE)) < 0) </p>
<p>err_sys("open error"); </p>
<p>/* try and set a write lock on the entire file */ </p>
<p>if (write_lock(fd, 0, SEEK_SET, 0) < 0) { </p>
<p>if (errno == EACCES || errno == EAGAIN) </p>
<p>exit(0); /* gracefully exit, daemon is already ru </p>
<p>ning */ </p>
<p>else </p>
<p>err_sys("write_lock error"); </p>
<p>} </p>
<p>/* truncate to zero length, now that we have the lock */ </p>
<p>if (ftruncate(fd, 0) < 0) </p>
<p>err_sys("ftruncate error"); </p>
<p>/* and write our process ID */ </p>
<p>sprintf(buf, "%d\n", getpid()); </p>
<p>if (write(fd, buf, strlen(buf)) != strlen(buf)) </p>
<p>err_sys("write error"); </p>
<p>/* set close-on-exec flag for descriptor */ </p>
<p>if ( (val = fcntl(fd, F_GETFD, 0)) < 0) </p>
<p>err_sys("fcntl F_GETFD error"); </p>
<p>val |= FD_CLOEXEC; </p>
<p>if (fcntl(fd, F_SETFD, val) < 0) </p>
<p>err_sys("fcntl F_SETFD error"); </p>
<p>/* leave file open until we terminate: lock will be held */ </p>
<p>/* do whatever ... */ </p>
<p>exit(0); </p>
<p>} </p>
<p>程序12.5 精靈進程阻止其多份副本同時運行的起動代碼 </p>
<p>實例 </p>
<p>在相對文件尾端加鎖或解鎖時需要特別小心。大多數實現按照I_whence的SEEK_CU
</p>
<p>R或SEEN_END值,用文件當前位置或當前長度以及l_start得到絕對的文件位移量。
</p>
<p>但是,通常我們需要相對于文件的當前位置或當前長度指定一把鎖。
</p>
<p>程序12.6寫一個文件,一次一個字節。每次循環中,從文件當前尾端開始處加鎖直到
</p>
<p>將來可能擴充到的尾端為止(最后一個參數,長度,指定為0),然后寫1個字節。
</p>
<p>然后解除這把鎖,寫另一個字節。如果系統用"從當前尾端開始,直到將來可能擴
</p>
<p>充的尾端"這種記法來跟蹤鎖,那么這段程序能夠正常工作。但是如果系統將相對
</p>
<p>位移量變換成絕對位移量就會有問題。在SVR4中運行此程序的確會發生問題:
</p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>/* set close-on-exec flag for descriptor */ </p>
<p>if ( (val = fcntl(fd, F_GETFD, 0)) < 0) </p>
<p>err_sys("fcntl F_GETFD error"); </p>
<p>val |= FD_CLOEXEC; </p>
<p>if (fcntl(fd, F_SETFD, val) < 0) </p>
<p>err_sys("fcntl F_SETFD error"); </p>
<p>/* leave file open until we terminate: lock will be held */ </p>
<p>/* do whatever ... */ </p>
<p>exit(0); </p>
<p>} </p>
<p>程序12.5 精靈進程阻止其多份副本同時運行的起動代碼 </p>
<p>實例 </p>
<p>在相對文件尾端加鎖或解鎖時需要特別小心。大多數實現按照I_whence的SEEK_CU
</p>
<p>R或SEEN_END值,用文件當前位置或當前長度以及l_start得到絕對的文件位移量。
</p>
<p>但是,通常我們需要相對于文件的當前位置或當前長度指定一把鎖。
</p>
<p>程序12.6寫一個文件,一次一個字節。每次循環中,從文件當前尾端開始處加鎖直到
</p>
<p>將來可能擴充到的尾端為止(最后一個參數,長度,指定為0),然后寫1個字節。
</p>
<p>然后解除這把鎖,寫另一個字節。如果系統用"從當前尾端開始,直到將來可能擴
</p>
<p>充的尾端"這種記法來跟蹤鎖,那么這段程序能夠正常工作。但是如果系統將相對
</p>
<p>位移量變換成絕對位移量就會有問題。在SVR4中運行此程序的確會發生問題:
</p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int i, fd; </p>
<p>if ( (fd = open("temp.lock", O_RDWR | O_CREAT | O_TRUNC, </p>
<p>FILE_MODE)) < 0) </p>
<p>err_sys("open error"); </p>
<p>for (i = 0; i < 1000000; i++) { /* try to write 2 Mbytes */ </p>
<p>/* lock from current EOF to EOF */ </p>
<p>if (writew_lock(fd, 0, SEEK_END, 0) < 0) </p>
<p>err_sys("writew_lock error"); </p>
<p>if (write(fd, &fd, 1) != 1) </p>
<p>err_sys("write error"); </p>
<p>if (un_lock(fd, 0, SEEK_END, 0) < 0) </p>
<p>err_sys("un_lock error"); </p>
<p>if (write(fd, &fd, 1) != 1) </p>
<p>err_sys("write error"); </p>
<p>} </p>
<p>exit(0); </p>
<p>賦與l_start)改換成所寫字節數的負值(在本程序中是-1)。這就使得un_lock去
</p>
<p>除上次加的鎖。 </p>
<p>建議性鎖和強制性鎖 </p>
<p>考慮數據庫存取例程序。如果該庫中所有函數都以一致的方法處理記錄鎖,則我們
</p>
<p>稱使用這些函數存取數據庫的任何進程集為合作進程。如果這些函數是唯一的用來
</p>
<p>存取數據庫的函數,那么它們使用建議性鎖是可行的。但是建議性鎖并不能阻止對
</p>
<p>數據庫文件有寫許可權的任何其它進程寫數據庫文件。不使用協同一致的方法(數
</p>
<p>據庫存取例程庫)來存取數據庫的進程是一個非合作進程。 </p>
<p>強制性鎖機制中,系統核對每一個open、read和write都要檢查調用進程對正在存
</p>
<p>取的文件是否違背了某一把鎖的作用。 </p>
<p>對一個特定文件打開其設置_組_ID位,關閉其組_執行位則對該文件啟動了強制性
</p>
<p>鎖機制。(回憶程序4.4)。因為當組_ 執行位關閉時,設置_組_ID位不再有意義
</p>
<p>,所以SVR3的設計者借用兩者的這種組合來指定對一個文件的鎖是強制性的而非建
</p>
<p>議性的。 </p>
<p>如果一個進程試圖讀、寫一個強制性鎖起作用的文件,而欲讀、寫的部分又由其它
</p>
<p>進程加上了讀、寫鎖,此時會發生什么呢?對這一問題的回答取決于三方面的因素
</p>
<p>:操作類型(read或write),其它進程保有的鎖的類型(讀鎖或寫鎖),以及有
</p>
<p>關描述符是阻塞還是非阻塞的。圖12.7顯示了這八種可能性。 </p>
<p>圖12.7 強制性鎖對其它進程讀、寫的影響 </p>
<p>除了圖12.7中的read,write函數,其它進程的強制性鎖也會對open函數產生影響。
</p>
<p>通常,即使正在打開的文件具有強制性記錄鎖,該打開操作也會成功。下面的rea
</p>
<p>d或write依從于圖12.7中所示的規則。但是,如果欲打開的文件具有強制性鎖(讀
</p>
<p>鎖或寫鎖),而且open調用中的flag為O_TRUNC或O_CREAT,則不論是否指定O_NON
</p>
<p>BLOCK,open都立即出錯返回,erron設置為EAGAIN。(對O_TRUNC情況出錯返回是有
</p>
<p>意義的,因為其它進程對該文件持有讀、寫鎖,所以不能將其截短為0。對O_CREA
</p>
<p>T情況在返回時也設置erron則無意義,因為該標志的意義是如果該文件不存在則創
</p>
<p>建,由于其它進程對該文件持有記錄鎖,因而該文件肯定是存在的。)
</p>
<p>這種處理方式可能導致令人驚異的結果。我們曾編寫過一個程序,它打開一個文件
</p>
<p>(其mode指定為強制性鎖),然后對該文件的整體設置一把讀鎖,然后進入睡眠一
</p>
<p>段時間。在這段睡眠時間內,用某些常規的Unix程序和操作符對該文件進行處理,
</p>
<p>發現下列情況: </p>
<p>l 可用ed編輯程序對該文件進行編輯操作,而且編輯結果寫回磁盤!強制性記錄鎖
</p>
<p>對此毫無影響。對ed操作進行跟蹤分析發現,ed將新內容寫到一個臨時文件中,然
</p>
<p>后刪除原文件,最后將臨時文件名改名為原文件名。于是,發現強制性鎖機制對u
</p>
<p>nlink函數沒有影響。 </p>
<p>在SVR4中,用truss(1)命令可以得到一個進程的系統調用跟蹤信息,在4.3+BSD中
</p>
<p>,則使用ktrace(1)和kdump(1)命令。 </p>
<p>l 不能用vi編輯程序編輯該文件。vi可以讀該文件,但是如果試圖將新的數據寫到
</p>
<p>該文件中,則出錯返回(EAGAIN)。如果試圖將新數據添加到該文件中,則write
</p>
<p>阻塞。vi的這種行為與所希望的一樣。 </p>
<p>l 使用KornShell的>和》算符重寫或添寫到該文件中,產生出錯信息"cannot
cre </p>
<p>at"。 </p>
<p>l 在Bourne Shell下使用>算符出錯,但是使用》算符則阻塞,在刪除了強制性鎖
</p>
<p>后再繼續進行處理。(執行添加操作所產生的區別是因為:Korn
Shell以O_CREAT </p>
<p>和O_APPEND標志打開文件,而上面已提及指定O_CREAT會產生出錯返回。但是,Bo
</p>
<p>urne Shell在該文件已存在時并不指定O_CREAT,所以open成功,而下一個write則
</p>
<p>阻塞。) </p>
<p>從這樣一個例子中可見,在使用強制性鎖時還需有所警惕。 </p>
<p>一個別有用心的用戶可以對大家都可讀的文件加一把讀鎖(強制性),這樣就能阻
</p>
<p>止任何其它人寫該文件(當然,該文件應當是強制性鎖機制起作用的,這可能要求
</p>
<p>該用戶能夠更改該文件的許可權位。)考慮一個數據庫文件,它是大家都可讀的,
</p>
<p>并且是強制性鎖機制起作用的。如果一個別有用心的用戶對該整個文件保有一把讀
</p>
<p>鎖,則其它進程不能再寫該文件。 </p>
<p>實例 </p>
<p>程序12.7 檢查一個系統是否支持強制性鎖機制。 </p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <sys/wait.h> </p>
<p>#include <errno.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int fd; </p>
<p>pid_t pid; </p>
<p>char buff[5]; </p>
<p>struct stat statbuf; </p>
<p>if ( (fd = open("templock", O_RDWR | O_CREAT | O_TRUNC, </p>
<p>FILE_MODE)) < 0) </p>
<p>err_sys("open error"); </p>
<p>if (write(fd, "abcdef", 6) != 6) </p>
<p>err_sys("write error"); </p>
<p>/* turn on set-group-ID and turn off group-execute */ </p>
<p>if (fstat(fd, &statbuf) < 0) </p>
<p>err_sys("fstat error"); </p>
<p>if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0) </p>
<p>err_sys("fchmod error"); </p>
<p>TELL_WAIT(); </p>
<p>if ( (pid = fork()) < 0) { </p>
<p>err_sys("fork error"); </p>
<p>} else if (pid > 0) { /* parent */ </p>
<p>/* write lock entire file */ </p>
<p>if (write_lock(fd, 0, SEEK_SET, 0) < 0) </p>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -