一、什麼是檔案鎖定
對於鎖這個字,大家一定不會陌生,因為我們生活中就存在著大量的鎖,它們各個方面發揮著它的作用,現在世界中的鎖的功能都可歸結為一句話,就是阻止某些人做某些事,例如,門鎖就是阻止除了屋主之外的人進入這個房子,你進入不到這個房子,也就不能使用房子裡面的東西。
而因為程式經常需要共用資料,而這通常又是通過檔案來實現的,試想一個情況,A進程正在對一個檔案進行寫操作,而另一個程式B需要對同一個檔案進行讀操作,並以讀取到的資料作為自己程式運行時所需要的資料,這會發生什麼情況呢?進程B可能會讀到錯亂的資料,因為它並不知道另一個進程A正在改寫這個檔案中的資料。
為瞭解決類似的問題,就出現了檔案鎖定,簡單點來說,這是檔案的一種安全的更新方式,當一個程式正在對檔案進行寫操作時,檔案就會進入一種暫時狀態,在這個狀態下,如果另一個程式嘗試讀這個檔案,它就會自動停下來等待這個狀態結束。Linux系統提供了很多特性來實現檔案鎖定,其中最簡單的方法就是以原子操作的方式建立鎖檔案。
用回之前的例子就是,檔案鎖就是當檔案在寫的時候,阻止其他的需要寫或者要讀檔案的進程來操作這個檔案。
二、建立鎖檔案
建立一個鎖檔案是非常簡單的,我們可以使用open系統調用來建立一個鎖檔案,在調用open時oflags參數要增加參數O_CREAT和O_EXCL標誌,如file_desc = open("/tmp/LCK.test", O_RDWR|O_CREAT|O_EXCL, 0444);就可以建立一個鎖檔案/tmp/LCK.test。O_CREAT|O_EXCL,可以確保調用者可以建立出檔案,使用這個模式可以防止兩個程式同時建立同一個檔案,如果檔案(/tmp/LCK.test)已經存在,則open調用就會失敗,返回-1。
如果一個程式在它執行時,只需要獨佔某個資源一段很短的時間,這個時間段(或代碼區)通常被叫做臨界區,我們需要在進入臨界區之前使用open系統調用建立鎖檔案,然後在退出臨界區時用unlink系統調用刪除這個鎖檔案。
注意:鎖檔案只是充當一個指標的角色,程式間需要通過相互協作來使用它們,也就是說鎖檔案只是建議鎖,而不是強制鎖,並不會真正阻止你讀寫檔案中的資料。
可以看看下面的例子:源檔案檔案名稱為filelock1.c,代碼如下:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <errno.h> int main() { const char *lock_file = "/tmp/LCK.test1"; int n_fd = -1; int n_tries = 10; while(n_tries--) { //建立鎖檔案 n_fd = open(lock_file, O_RDWR|O_CREAT|O_EXCL, 0444); if(n_fd == -1) { //建立失敗 printf("%d - Lock already present\n", getpid()); sleep(2); } else { //建立成功 printf("%d - I have exclusive access\n", getpid()); sleep(1); close(n_fd); //刪除鎖檔案,釋放鎖 unlink(lock_file); sleep(2); } } return 0; }
同時運行同一個程式的兩個執行個體,運行結果為:
從啟動並執行結果可以看出兩個程式交叉地對對檔案進行鎖定,但是真實的操作卻是,每次調用open函數去檢查/tmp/LCK.test1這個檔案是否存在,如果存在open調用就失敗,顯示有進程已經把這個檔案鎖定了,如果這個檔案不存在,就建立這個檔案,並顯示許可資訊。但是這種做法有一定的缺憾,我們可以看到檔案/tmp/LCK.test1被建立了很多次,也被unlink刪除了很多次,也就是說我們不能使用已經事先有資料的檔案作為這種鎖檔案,因為如果檔案已經存在,則open調用總是失敗。
給我的感覺是,這更像是一種對進程工作的協調性安排,更像是二進位訊號量的作用,檔案存在為0,不存在為1,而不是真正的檔案鎖定。