Linux 檔案鎖學習筆記

來源:互聯網
上載者:User

之前曾經讀過《UNIX環境進階編程》,但是對其中的檔案鎖的概念沒有深入的瞭解與學習,近日在操MySQL資料庫時,遇到死結現象。順便聯想到了Linux下的檔案鎖,風聞資料庫中的庫是需要OS下的檔案來支撐的,為此更要來對Linux檔案鎖來深入學習一下。

Linux系統上的檔案鎖主要分為建議鎖(advisory lock)和強制鎖(mandatory lock)。在Linux上使用的檔案鎖大部分為建議鎖,而且使用強制鎖的時候也要檢查系統是否支援強制鎖(http://www.ibm.com/developerworks/cn/linux/l-cn-filelock/index.html,這裡有份代碼傳說可以檢查是否支援強制鎖)。根據查看的資料顯示, OpenSuse11.1,
CentOS5.3等系統不支援強制檔案鎖。

下面分別對建議鎖和強制鎖做簡要的介紹:

1、             
建議鎖又稱協同鎖。對於這種類型的鎖,核心只是提供加減鎖以及檢測是否加鎖的操作,但是不提供鎖的控制與協調工作。也就是說,如果應用程式對某個檔案進行操作時,沒有檢測是否加鎖或者無視加鎖而直接向檔案寫入資料,核心是不會加以阻攔控制的。因此,建議鎖,不能阻止進程對檔案的操作,而只能依賴於大家自覺的去檢測是否加鎖然後約束自己的行為;

2、             
強制鎖,是OS核心的檔案鎖。每個對檔案操作時,例如執行open、read、write等操作時,OS內部檢測該檔案是否被加了強制鎖,如果加鎖導致這些檔案操作失敗。也就是核心強制應用程式來遵守遊戲規則;

 

 

對檔案鎖操作的相關函數簡介:

Flock和fcntl,flock主要完成的是建議鎖,當然也可以用fcntl來完成建議鎖。Fcntl除了可以完成建議鎖之外,還可以完成記錄鎖,即對檔案的一部分進行加鎖操作;

 

鎖的隱含繼承和釋放

關於記錄鎖的自動繼承和釋放規則:

1、             
鎖與進程和檔案兩方面相關。兩層含義:當一個進程終止時,它所建立的鎖全部釋放;第二,任何時候關閉一個描述符時,則該進程通過這一描述符可以引用的檔案上的任何一把鎖都被釋放(這些鎖都是被該進程設定的)。這意味著:

Fd1=open(pathname,….);

Read_lock(fd1,…);

Fd2=dup(fd1);

Close(fd2);

則在close(fd2)後,fd1上設定的鎖都被釋放。如果將dup換為open,以開啟另一描述符上的同一檔案,其效果也是一樣的;

2、             
由fork產生的子進程不繼承父進程所設定的鎖。意味著,若一個進程得到一把鎖,然後調用fork,那麼對於父進程獲得鎖而言,子進程被視為另一個進程,對於從父進程處繼承過來的任一描述符,子進程需要調用fcntl才能獲得它自己的鎖。這與鎖的作用是一致的。鎖的作用是阻止多個進程同時操作同一個檔案,如果子進程繼承父進程的鎖,則父子進程操作同一個檔案,這與鎖的初衷相違背;

3、             
在執行exec後,新程式可以繼承原執行程式的鎖。但是注意,如果對一個檔案描述符設定了close-on-exec標誌,那麼當作為exec的一部分關閉該檔案描述符時,對相應檔案的所有鎖都被釋放啦。

 

附:檢測系統是否支援強制鎖的代碼(《Unix環境進階編程》);

 

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/wait.h>

#include <stdarg.h>

#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>

#include <stdlib.h>

#include <fcntl.h>

#include <signal.h>

#include <sys/wait.h>

 

volatile sig_atomic_t sigflag;

int pfd1[2], pfd2[2];

sigset_t   
newmask, oldmask, zeromask;

 

void err_doit(int errnoflag, const char *fmt,
va_list ap)

{

   
int    errno_save;

   
char    buf[4096];

   

   
errno_save = errno;

   
vsprintf(buf, fmt, ap);

   
if (errnoflag)

      
sprintf(buf+strlen(buf), ": %s", strerror(errno_save));

   
strcat(buf, "/n");

   
fflush(stdout);

   
fputs(buf, stderr);

   
fflush(stderr);

   
return;

}

 

void err_sys(const char *fmt, ...)

{

   
va_list        ap;

   
va_start(ap, fmt);

   
err_doit(1, fmt, ap);

   
va_end(ap);

   
exit(1);

}

void sig_usr(int signo)

{

   
sigflag = 1;

   
return;

}

TELL_WAIT()

{

   
if (signal(SIGUSR1, sig_usr) == SIG_ERR)         /*SIGUSR1 為使用者自訂訊號*/

       
err_sys("signal(SIGINT) error");

   
if (signal(SIGUSR2, sig_usr) == SIG_ERR)

       
err_sys("signal(SIGQUIT) error");

   

   
sigemptyset(&zeromask);

   

   
sigemptyset(&newmask);

   
sigaddset(&newmask, SIGUSR1);

   
sigaddset(&newmask, SIGUSR2);

       
/*阻塞 SIGUSR1 和 SIGUSR2 並且儲存當前訊號掩碼*/

   
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)

       
err_sys("SIG_BLOCK error");

}   

 

void WAIT_PARENT(void)

{

   
while( sigflag == 0)

       
sigsuspend(&zeromask);   
/*suspend()取消了所有訊號屏蔽,等待父進程發來訊號*/

   
sigflag = 0;

       
/*恢複訊號掩碼*/

   
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)

       
err_sys("SIG_SETMASK error");

}

 

 

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) );

}

 

void set_fl(int fd, int flags)

{

   
int     val;

   
if ( (val = fcntl(fd, F_GETFL, 0)) < 0)

       
err_sys("fcntl F_GETFL error");

 

   
val |= flags;        /*置標誌*/

 

   
if (fcntl(fd, F_SETFL, val) < 0)

       
err_sys("fcntl F_SETFL error");

}

 

void err_ret(const char *fmt, ...)

{

   
va_list        ap;

   
va_start(ap, fmt);

   
err_doit(1, fmt, ap);

    va_end(ap);

   
return;

}

 

 

int main(void)

{

   
int        fd;

   
pid_t        pid;

   
char        buff[5];

   
struct stat    statbuf;

   

   
if ( (fd = open("templock", O_RDWR | O_CREAT | O_TRUNC, 0644))
< 0)

       
err_sys("open error",buff);

   

   
if (write(fd, "abcdef", 6) != 6)

       
err_sys("write error");

 

   
/*開啟
set-group-ID(s-gid),並關閉組執行許可權*/

   
if (fstat(fd, &statbuf) < 0)

       
err_sys("fstat error");

   

   
if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)

       
err_sys("fork error");

   

   
TELL_WAIT();

   
if ( (pid = fork()) < 0) {

       
err_sys("fork error");

    }
else if (pid > 0) {    /*父進程*/

                /*整個檔案寫鎖*/

   
if (lock_reg(fd, F_SETLK, F_WRLCK, 0, SEEK_SET, 0) < 0)

       
err_sys("write_lock error");

   
kill(pid, SIGUSR1);    /*給子進程發送訊號告知鎖完成*/

   

   
if (waitpid(pid, NULL, 0) < 0)

       
err_sys("waitpid error");

    }
else {

           
WAIT_PARENT();    /*等待父進程設定鎖*/

   

   
set_fl(fd, O_NONBLOCK);   

   

   
if (lock_reg(fd, F_SETLK, F_RDLCK, 0, SEEK_SET, 0) != -1)

       
err_sys("child: read_lock succeeded");

   
printf("read_lock of already-locked region return %d/n",
errno);

 

   
if (lseek(fd, 0, SEEK_SET) == -1)

       
err_sys("lseek, error");

 

   
if (read(fd, buff, 2) < 0)

    err_ret("read
failed (mandatory locking wroks)");

   
else

       
printf("read OK (no mandatory locking), buff = %2.2s/n",
buff);

    }

   
exit(0);

}

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.