多進(jìn)程之間的文件鎖

多進(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)程都是給01,然后寫了個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的文件鎖主要有兩種:flocklockf骗随。需要額外對lockf說明的是,它只是fcntl系統(tǒng)調(diào)用的一個封裝赴叹。從使用角度講,lockffcntl實(shí)現(xiàn)了更細(xì)粒度文件鎖指蚜,即:記錄鎖乞巧。我們可以使用lockffcntl對文件的部分字節(jié)上鎖,flock只能對整個文件加鎖摊鸡。這兩種文件鎖是從歷史上不同的標(biāo)準(zhǔn)中起源的绽媒,flock來自BSD,而lockf來自POSIX蚕冬,所以lockffcntl實(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沪斟、readwrite等文件操作時發(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)用程序谴咸,flocklockf在是鎖類型方面沒有本質(zhì)差別轮听,他們都是建議鎖,而非強(qiáng)制鎖寿冕。
flocklockf另外一個差別是它們實(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卻不會這樣缕探。這個原因在于flocklockf的語義是不同的。使用lockffcntl的鎖还蹲,在實(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)致dupdup2所產(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代表"從起始偏移到文件偏移的最大可能值".因此鎖住整個文件有兩種方式:

  1. 指定l_whence成員為SEEK_SET, l_start成員為0,l_len成員為0.
  1. 使用lseek將讀寫指針定位到文件頭,然后指定l_whence成員為SEEK_CUR, l _start0,l_len0.

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ù),一切就都夠用了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惜互,隨后出現(xiàn)的幾起案子布讹,更是在濱河造成了極大的恐慌,老刑警劉巖训堆,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件描验,死亡現(xiàn)場離奇詭異,居然都是意外死亡坑鱼,警方通過查閱死者的電腦和手機(jī)膘流,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門絮缅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呼股,你說我怎么就攤上這事耕魄。” “怎么了彭谁?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵吸奴,是天一觀的道長。 經(jīng)常有香客問我缠局,道長则奥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任狭园,我火速辦了婚禮读处,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唱矛。我一直安慰自己罚舱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布揖赴。 她就那樣靜靜地躺著馆匿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪燥滑。 梳的紋絲不亂的頭發(fā)上渐北,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音铭拧,去河邊找鬼赃蛛。 笑死,一個胖子當(dāng)著我的面吹牛搀菩,可吹牛的內(nèi)容都是我干的呕臂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼肪跋,長吁一口氣:“原來是場噩夢啊……” “哼歧蒋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起州既,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤谜洽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吴叶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阐虚,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年蚌卤,在試婚紗的時候發(fā)現(xiàn)自己被綠了实束。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奥秆。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咸灿,靈堂內(nèi)的尸體忽然破棺而出构订,到底是詐尸還是另有隱情,我是刑警寧澤析显,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布鲫咽,位于F島的核電站,受9級特大地震影響谷异,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锦聊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一歹嘹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧孔庭,春花似錦尺上、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芽淡,卻和暖如春马绝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挣菲。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工富稻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人白胀。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓椭赋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親或杠。 傳聞我的和親對象是個殘疾皇子哪怔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容