多進(jìn)程之間的文件鎖
這些日子用到了這些知識點(diǎn),所以來記錄一下.
文章轉(zhuǎn)載自這里:http://liwei.life/2016/07/31/file_and_filelock/
但是添加了自己的理解和一些新的內(nèi)容.
下面的例子都省略了錯誤處理,為了使代碼更加簡潔明了.
競爭條件
我們的第一個例子是多個進(jìn)程寫文件的例子恩闻,雖然還沒做到通信挪挤,但是這比較方便的說明一個通信時經(jīng)常出現(xiàn)的情況:競爭條件纵竖。假設(shè)我們要并發(fā)100
個進(jìn)程,這些進(jìn)程約定好一個文件波丰,這個文件初始值內(nèi)容寫0
,每一個進(jìn)程都要打開這個文件讀出當(dāng)前的數(shù)字,加一之后將結(jié)果寫回去硕噩。在理想狀態(tài)下骇径,這個文件最后寫的數(shù)字應(yīng)該是100
躯肌,因?yàn)橛?code>100個進(jìn)程打開、讀數(shù)破衔、加1
清女、寫回,自然是有多少個進(jìn)程最后文件中的數(shù)字結(jié)果就應(yīng)該是多少运敢。但是實(shí)際上并非如此校仑,可以看一下這個例子:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#define COUNT 100
#define NUM 64
#define FILEPATH "/tmp/count"
int do_child(const char *path) {
/*-
* 當(dāng)多個進(jìn)程同時執(zhí)行這個過程的時候,就會出現(xiàn)racing:競爭條件传惠,
* 多個進(jìn)程可能同時從文件獨(dú)到同一個數(shù)字迄沫,并且分別對同一個數(shù)字加1并寫回,
* 導(dǎo)致多次寫回的結(jié)果并不是我們最終想要的累積結(jié)果卦方。
*/
int fd;
int ret, count;
char buf[NUM];
fd = open(path, O_RDWR); /* 1. 打開FILEPATH路徑的文件 */
ret = read(fd, buf, NUM); /* 2. 讀出文件中的當(dāng)前數(shù)字 */
buf[ret] = '\0';
count = atoi(buf); /* 3. 將字符串轉(zhuǎn)成整數(shù) */
++count; /* 4. 整數(shù)自增加1 */
sprintf(buf, "%d", count); /* 5. 將整數(shù)轉(zhuǎn)成字符串 */
lseek(fd, 0, SEEK_SET); /* 6. lseek調(diào)整文件當(dāng)前的偏移量到文件頭 */
ret = write(fd, buf, strlen(buf)); /* 7. 將字符串寫會文件 */
close(fd);
exit(0);
}
int main() {
pid_t pid;
int count;
for (count=0;count<COUNT;count++)
{
pid = fork();
if (pid == 0) {
do_child(FILEPATH);
}
}
for (count=0;count<COUNT;count++) {
wait(NULL);
}
}
這個程序做后執(zhí)行的效果如下:
[zorro@zorrozou-pc0 process]$ echo 0 > /tmp/count
[zorro@zorrozou-pc0 process]$ ./racing
[zorro@zorrozou-pc0 process]$ cat /tmp/count
71
[zorro@zorrozou-pc0 process]$
[zorro@zorrozou-pc0 process]$ echo 0 > /tmp/count
[zorro@zorrozou-pc0 process]$ ./racing
[zorro@zorrozou-pc0 process]$ cat /tmp/count
61
[zorro@zorrozou-pc0 process]$
[zorro@zorrozou-pc0 process]$ echo 0 > /tmp/count
[zorro@zorrozou-pc0 process]$ ./racing
[zorro@zorrozou-pc0 process]$ cat /tmp/count
64
[zorro@zorrozou-pc0 process]$
我們執(zhí)行了三次這個程序羊瘩,每次結(jié)果都不太一樣,第一次是71
盼砍,第二次是61
尘吗,第三次是64
,全都沒有得到預(yù)期結(jié)果浇坐,這就是競爭條件(racing
)引入的問題睬捶。仔細(xì)分析這個進(jìn)程我們可以發(fā)現(xiàn)這個競爭條件是如何發(fā)生的:
最開始文件內(nèi)容是0
,假設(shè)此時同時打開了3
個進(jìn)程近刘,那么他們分別讀文件的時候擒贸,這個過程是可能并發(fā)的,于是每個進(jìn)程讀到的數(shù)組可能都是0
觉渴,因?yàn)樗麄兌荚趧e的進(jìn)程沒寫入1
之前就開始讀了文件介劫。于是三個進(jìn)程都是給0
加1
,然后寫了個1回到文件案淋。其他進(jìn)程以此類推座韵,每次100
個進(jìn)程的執(zhí)行順序可能不一樣,于是結(jié)果是每次得到的值都可能不太一樣踢京,但是一定都少于產(chǎn)生的實(shí)際進(jìn)程個數(shù)誉碴。于是我們把這種多個執(zhí)行過程(如進(jìn)程或線程)中訪問同一個共享資源宦棺,而這些共享資源又有無法被多個執(zhí)行過程存取的的程序片段,叫做臨界區(qū)代碼翔烁。
那么該如何解決這個racing
的問題呢渺氧?對于這個例子來說,可以用文件鎖的方式解決這個問題蹬屹。就是說侣背,對臨界區(qū)代碼進(jìn)行加鎖,來解決競爭條件的問題慨默。哪段是臨界區(qū)代碼贩耐?在這個例子中,兩端/ /之間的部分就是臨界區(qū)代碼厦取。一個正確的例子是:
...
flock(fd, LOCK_EX); /* 對整個文件加鎖 */
read(fd, buf, NUM);
buf[ret] = '\0';
count = atoi(buf);
++count;
sprintf(buf, "%d", count);
lseek(fd, 0, SEEK_SET);
write(fd, buf, strlen(buf));
flock(fd, LOCK_UN); /* 解鎖 */
...
我們將臨界區(qū)部分代碼前后都使用了flock
的互斥鎖潮太,防止了臨界區(qū)的racing
。這個例子雖然并沒有真正達(dá)到讓多個進(jìn)程通過文件進(jìn)行通信虾攻,解決某種協(xié)同工作問題的目的铡买,但是足以表現(xiàn)出進(jìn)程間通信機(jī)制的一些問題了。當(dāng)涉及到數(shù)據(jù)在多個進(jìn)程間進(jìn)行共享的時候霎箍,僅僅只實(shí)現(xiàn)數(shù)據(jù)通信或共享機(jī)制本身是不夠的奇钞,還需要實(shí)現(xiàn)相關(guān)的同步或異步機(jī)制來控制多個進(jìn)程,達(dá)到保護(hù)臨界區(qū)或其他讓進(jìn)程可以處理同步或異步事件的能力漂坏。我們可以認(rèn)為文件鎖是可以實(shí)現(xiàn)這樣一種多進(jìn)程的協(xié)調(diào)同步能力的機(jī)制景埃,而除了文件鎖以外,還有其他機(jī)制可以達(dá)到相同或者不同的功能顶别,我們會在下文中繼續(xù)詳細(xì)解釋谷徙。
再次,我們并不對flock
這個方法本身進(jìn)行功能性講解驯绎。這種功能性講解大家可以很輕易的在網(wǎng)上或者通過別的書籍得到相關(guān)內(nèi)容完慧。本文更加偏重的是Linux
環(huán)境提供了多少種文件鎖以及他們的區(qū)別是什么?
flock和lockf
從底層的實(shí)現(xiàn)來說剩失,Linux
的文件鎖主要有兩種:flock
和lockf
骗随。需要額外對lockf
說明的是,它只是fcntl系統(tǒng)調(diào)用的一個封裝赴叹。從使用角度講,lockf
或fcntl
實(shí)現(xiàn)了更細(xì)粒度文件鎖指蚜,即:記錄鎖乞巧。我們可以使用lockf
或fcntl
對文件的部分字節(jié)上鎖,而flock
只能對整個文件加鎖摊鸡。這兩種文件鎖是從歷史上不同的標(biāo)準(zhǔn)中起源的绽媒,flock
來自BSD
,而lockf
來自POSIX
蚕冬,所以lockf
或fcntl
實(shí)現(xiàn)的鎖在類型上又叫做POSIX
鎖。
除了這個區(qū)別外是辕,fcntl
系統(tǒng)調(diào)用還可以支持強(qiáng)制鎖(Mandatory locking
)囤热。強(qiáng)制鎖的概念是傳統(tǒng)UNIX
為了強(qiáng)制應(yīng)用程序遵守鎖規(guī)則而引入的一個概念,與之對應(yīng)的概念就是建議鎖(Advisory locking
)获三。我們?nèi)粘J褂玫幕径际墙ㄗh鎖旁蔼,它并不強(qiáng)制生效。這里的不強(qiáng)制生效的意思是疙教,如果某一個進(jìn)程對一個文件持有一把鎖之后棺聊,其他進(jìn)程仍然可以直接對文件進(jìn)行各種操作的,比如open
贞谓、read
限佩、write
。只有當(dāng)多個進(jìn)程在操作文件前都去檢查和對相關(guān)鎖進(jìn)行鎖操作的時候裸弦,文件鎖的規(guī)則才會生效祟同。這就是一般建議鎖的行為。而強(qiáng)制性鎖試圖實(shí)現(xiàn)一套內(nèi)核級的鎖操作理疙。當(dāng)有進(jìn)程對某個文件上鎖之后晕城,其他進(jìn)程即使不在操作文件之前檢查鎖,也會在open
沪斟、read
或write
等文件操作時發(fā)生錯誤广辰。內(nèi)核將對有鎖的文件在任何情況下的鎖規(guī)則都生效,這就是強(qiáng)制鎖的行為主之。由此可以理解择吊,如果內(nèi)核想要支持強(qiáng)制鎖,將需要在內(nèi)核實(shí)現(xiàn)open
槽奕、read
几睛、write
等系統(tǒng)調(diào)用內(nèi)部進(jìn)行支持。
從應(yīng)用的角度來說粤攒,Linux
內(nèi)核雖然號稱具備了強(qiáng)制鎖的能力所森,但其對強(qiáng)制性鎖的實(shí)現(xiàn)是不可靠的,建議大家還是不要在Linux下使用強(qiáng)制鎖夯接。事實(shí)上焕济,在我目前手頭正在使用的Linux
環(huán)境上,一個系統(tǒng)在mount -o mand
分區(qū)的時候報(bào)錯(archlinux kernel 4.5
)盔几,而另一個系統(tǒng)雖然可以以強(qiáng)制鎖方式mount
上分區(qū)晴弃,但是功能實(shí)現(xiàn)卻不完整,主要表現(xiàn)在只有在加鎖后產(chǎn)生的子進(jìn)程中open
才會報(bào)錯,如果直接write
是沒問題的上鞠,而且其他進(jìn)程無論open
還是read
际邻、write
都沒問題(Centos 7 kernel 3.10
)。鑒于此芍阎,我們就不在此介紹如何在Linux
環(huán)境中打開所謂的強(qiáng)制鎖支持了世曾。我們只需知道,在Linux
環(huán)境下的應(yīng)用程序谴咸,flock
和lockf
在是鎖類型方面沒有本質(zhì)差別轮听,他們都是建議鎖,而非強(qiáng)制鎖寿冕。
flock
和lockf
另外一個差別是它們實(shí)現(xiàn)鎖的方式不同蕊程。這在應(yīng)用的時候表現(xiàn)在flock
的語義是針對文件的鎖,而lockf
是針對文件描述符(fd
)的鎖驼唱。我們用一個例子來觀察這個區(qū)別:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <wait.h>
#define PATH "/tmp/lock"
int main()
{
int fd;
pid_t pid;
fd = open(PATH, O_RDWR | O_CREAT | O_TRUNC, 0644);
flock(fd, LOCK_EX); /* 對整個文件加鎖 */
printf("%d: locked!\n", getpid());
pid = fork();
if (pid == 0) { /* 子進(jìn)程 */
/*
fd = open(PATH, O_RDWR|O_CREAT|O_TRUNC, 0644); /* 重新打開文件 */
*/
flock(fd, LOCK_EX); /* 加鎖 */
printf("%d: locked!\n", getpid());
exit(0);
}
wait(NULL);
unlink(PATH);
exit(0);
}
上面代碼是一個flock
的例子藻茂,其作用也很簡單:
- 打開
/tmp/lock
文件。 - 使用flock對其加互斥鎖玫恳。
- 打印“
PID:locked辨赐!
”表示加鎖成功。 - 打開一個子進(jìn)程京办,在子進(jìn)程中使用
flock
對同一個文件加互斥鎖掀序。 - 子進(jìn)程打印“
PID:locked!
”表示加鎖成功惭婿。如果沒加鎖成功子進(jìn)程會推出不恭,不顯示相關(guān)內(nèi)容。 - 父進(jìn)程回收子進(jìn)程并退出财饥。
這個程序直接編譯執(zhí)行的結(jié)果是:
[zorro@zorrozou-pc0 locktest]$ ./flock
23279: locked!
23280: locked!
父子進(jìn)程都加鎖成功了换吧。這個結(jié)果似乎并不符合我們對文件加鎖的本意。按照我們對互斥鎖的理解钥星,子進(jìn)程對父進(jìn)程已經(jīng)加鎖過的文件應(yīng)該加鎖失敗才對沾瓦。我們可以稍微修改一下上面程序讓它達(dá)到預(yù)期效果,將子進(jìn)程代碼段中的注釋取消掉重新編譯即可:
...
/*
// 重新打開的文件會丟失以前的鎖,同時對已經(jīng)持有鎖的文件再加鎖也會成功
fd = open(PATH, O_RDWR|O_CREAT|O_TRUNC, 0644);
*/
...
將這段代碼上下的/ /刪除重新編譯谦炒。之后執(zhí)行的效果如下:
[zorro@zorrozou-pc0 locktest]$ ./flock
23437: locked!
此時子進(jìn)程flock
的時候會阻塞贯莺,讓進(jìn)程的執(zhí)行一直停在這。這才是我們使用文件鎖之后預(yù)期該有的效果宁改。而相同的程序使用lockf
卻不會這樣缕探。這個原因在于flock
和lockf
的語義是不同的。使用lockf
或fcntl
的鎖还蹲,在實(shí)現(xiàn)上關(guān)聯(lián)到文件結(jié)構(gòu)體撕蔼,這樣的實(shí)現(xiàn)導(dǎo)致鎖不會在fork
之后被子進(jìn)程繼承豁鲤。而flock
在實(shí)現(xiàn)上關(guān)聯(lián)到的是文件描述符,這就意味著如果我們在進(jìn)程中復(fù)制了一個文件描述符鲸沮,那么使用flock
對這個描述符加的鎖也會在新復(fù)制出的描述符中繼續(xù)引用。在進(jìn)程fork
的時候锅论,新產(chǎn)生的子進(jìn)程的描述符也是從父進(jìn)程繼承(復(fù)制)來的讼溺。在子進(jìn)程剛開始執(zhí)行的時候,父子進(jìn)程的描述符關(guān)系實(shí)際上跟在一個進(jìn)程中使用dup
復(fù)制文件描述符的狀態(tài)一樣(參見《UNIX環(huán)境高級編程》8.3節(jié)的文件共享部分)最易。這就可能造成上述例子的情況怒坯,通過fork
產(chǎn)生的多個進(jìn)程,因?yàn)樽舆M(jìn)程的文件描述符是復(fù)制的父進(jìn)程的文件描述符藻懒,所以導(dǎo)致父子進(jìn)程同時持有對同一個文件的互斥鎖剔猿,導(dǎo)致第一個例子中的子進(jìn)程仍然可以加鎖成功。這個文件共享的現(xiàn)象在子進(jìn)程使用open
重新打開文件之后就不再存在了嬉荆,所以重新對同一文件open
之后归敬,子進(jìn)程再使用flock
進(jìn)行加鎖的時候會阻塞。另外要注意:除非文件描述符被標(biāo)記了close-on-exec
標(biāo)記鄙早,flock
鎖和lockf
鎖都可以穿越exec
汪茧,在當(dāng)前進(jìn)程變成另一個執(zhí)行鏡像之后仍然保留。
上面的例子中只演示了fork
所產(chǎn)生的文件共享對flock
互斥鎖的影響限番,同樣原因也會導(dǎo)致dup
或dup2
所產(chǎn)生的文件描述符對flock
在一個進(jìn)程內(nèi)產(chǎn)生相同的影響舱污。dup
造成的鎖問題一般只有在多線程情況下才會產(chǎn)生影響,所以應(yīng)該避免在多線程場景下使用flock
對文件加鎖弥虐,而lockf/fcntl
則沒有這個問題扩灯。
為了對比flock
的行為,我們在此列出使用lockf
的相同例子霜瘪,來演示一下它們的不同:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <wait.h>
#define PATH "/tmp/lock"
int main()
{
int fd;
pid_t pid;
fd = open(PATH, O_RDWR | O_CREAT | O_TRUNC, 0644);
lockf(fd, F_LOCK, 0)
printf("%d: locked!\n", getpid());
pid = fork();
if (pid == 0) {
/*
fd = open(PATH, O_RDWR|O_CREAT|O_TRUNC, 0644);
*/
lockf(fd, F_LOCK, 0);
printf("%d: locked!\n", getpid());
exit(0);
}
wait(NULL);
unlink(PATH);
exit(0);
}
編譯執(zhí)行的結(jié)果是:
[zorro@zorrozou-pc0 locktest]$ ./lockf
27262: locked!
在子進(jìn)程不用open
重新打開文件的情況下珠插,進(jìn)程執(zhí)行仍然被阻塞在子進(jìn)程lockf
加鎖的操作上。關(guān)于fcntl
對文件實(shí)現(xiàn)記錄鎖的詳細(xì)內(nèi)容粥庄,大家可以參考《UNIX環(huán)境高級編程》中關(guān)于記錄鎖的14.3章節(jié)丧失。
fcntl鎖
既然都扯到了鎖這一塊了,就不得不說一下用fcntl
這個函數(shù)對文件進(jìn)行加鎖,正如前面所提到的,fcntl
可以實(shí)現(xiàn)顆粒度更小的加鎖.
在UNP
第二卷中給我們封裝了這樣一系列函數(shù):
int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) {
struct flock lock;
lock.l_type = type;
lock.l_start = offset;
lock.l_whence = whence;
lock.l_len = len;
return (fcntl(fd, cmd, &lock));
}
/* 讀鎖 */
#define read_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len)
/* 阻塞版本的讀鎖 */
#define readw_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len)
/* 寫鎖 */
#define write_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len)
/* 阻塞版本的寫鎖 */
#define writew_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len)
/* 用于解鎖 */
#define un_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len)
關(guān)于這個struct flock
,我這里稍微摘抄一下UNP v2
上面的一些解釋:
l_type
可以取3
種值,F_RDLCK
表示讀鎖,F_WRLCK
表示寫鎖,F_UNLCK
表示解鎖.
l_start
伴隨著l_whence
來解釋:
SEEK_SET
:l_start
相對于文件的開頭解釋.
SEEK_CUR
:l_start
相對于文件的當(dāng)前字節(jié)偏移(即當(dāng)前的讀寫指針的位置)解釋.
SEEK_END
:l_start
相對于文件的末尾解釋.
l_len
成員指定從該偏移開始的連續(xù)字節(jié)數(shù),長度為0
代表"從起始偏移到文件偏移的最大可能值".因此鎖住整個文件有兩種方式:
- 指定
l_whence
成員為SEEK_SET
,l_start
成員為0
,l_len
成員為0.
- 使用
lseek
將讀寫指針定位到文件頭,然后指定l_whence
成員為SEEK_CUR
,l _start
為0
,l_len
為0
.
fcntl
加鎖的規(guī)則是什么呢?
- 對于一個文件的任意字節(jié),最多只能存在一種類型的鎖(讀鎖或者寫鎖);
- 而且,一個給定字節(jié)可以有多個讀鎖,但是只能有一個寫鎖;
- 當(dāng)一個文件描述符不是打開來用于讀時,如果我們對它請求一個讀鎖,會出錯,同理,如果一個描述符不是打開用于寫時,如果我們對它請求一個寫鎖,也會出錯;
- 正如前面所講的,鎖不能通過
fork
由子進(jìn)程繼承.
這樣說起可能不形象,我給一個很小的例子:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <wait.h>
#include <string.h>
#include "fileLocker.h"
#define PATH "/tmp/count"
int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) {
struct flock lock;
lock.l_type = type;
lock.l_start = offset;
lock.l_whence = whence;
lock.l_len = len;
return (fcntl(fd, cmd, &lock));
}
#define read_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len)
#define readw_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len)
#define write_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len)
#define writew_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len)
#define un_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len)
int main(int argc, char *argv[])
{
int fd = open(PATH, O_RDONLY, NULL); /* 打開文件 */
readw_lock(fd, 0, SEEK_SET, 0); /* 加讀鎖 */
char buf[512] = { 0 };
read(fd, buf, sizeof(buf));
printf("%s\n", buf);
sleep(10);
un_lock(fd, 0, SEEK_SET, 0); /* 解鎖 */
return 0;
}
這個例子讀取/temp/count
文件中的內(nèi)容,然后打印出來,假設(shè)count
里面是1
,那么我們先運(yùn)行這段程序的一個實(shí)例,很好,這個程序加了寫鎖,輸出了1
,2
~3
秒后我們運(yùn)行這段程序的另外一個實(shí)例,然后也輸出了1
,這代表加鎖也成功了,所以可以一個字節(jié)上可以加多個讀鎖.
我們可以對寫鎖也進(jìn)行相同的一個實(shí)驗(yàn):
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <wait.h>
#include <string.h>
#include "fileLocker.h"
int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) {
struct flock lock;
lock.l_type = type;
lock.l_start = offset;
lock.l_whence = whence;
lock.l_len = len;
return (fcntl(fd, cmd, &lock));
}
#define read_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len)
#define readw_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len)
#define write_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len)
#define writew_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len)
#define un_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len)
#define PATH "/tmp/count"
int main() {
int fd = open(PATH, O_RDWR, NULL); /* 打開文件 */
writew_lock(fd, 0, SEEK_SET, 0); /* 對整個文件加寫鎖 */
char line[512] = { 0 };
int seqno; /* 序列號 */
lseek(fd, 0L, SEEK_SET);
int n = read(fd, line, sizeof line);
printf("line : %s", line);
sscanf(line, "%d", &seqno);
printf("seqno : %d\n", seqno);
seqno++;
snprintf(line, sizeof line, "%d\n", seqno);
lseek(fd, 0L, SEEK_SET); /* 讀寫指針移到文件頭部 */
write(fd, line, strlen(line));
sleep(25); /* 等待25秒 */
un_lock(fd, 0, SEEK_SET, 0); /* 解鎖 */
close(fd);
return 0;
}
實(shí)驗(yàn)很簡單,直接說結(jié)論吧:
加了讀鎖的話,不能加寫鎖,但可以繼續(xù)加讀鎖.
加了寫鎖的話,不能加讀鎖,同時也不能繼續(xù)加寫鎖了,除非這個寫鎖釋放.
一般來說,使用UNP v2
上給我們封裝好的函數(shù),一切就都夠用了.