文件鎖 python 進程間鎖 fcntl


http://blog.csdn.net/jianhong1990/article/details/26370519

http://yunjianfei.iteye.com/blog/2061756

http://zhou123.blog.51cto.com/4355617/1650185

https://blog.jamespan.me/posts/deadlock-with-python-fcntl-flock

 

 

python的文件鎖目前使用的是fcntl這個庫,它實際上為 Unix上的ioctlflock和fcntl 函數提供了一個接口。

1.fcntl庫的簡單使用

  1. import fcntl  
  2. import os, time  
  3.   
  4. FILE = "counter.txt"  
  5.   
  6. if not os.path.exists(FILE):  
  7.     # create the counter file if it doesn't exist  
  8.     file = open(FILE, "w")  
  9.     file.write("0")  
  10.     file.close()  
  11.   
  12. for i in range(20):  
  13.     file = open(FILE, "r+")     #由於flock生成的是勸告鎖,不能阻止進程對文件的操作,所以這里可以正常打開文件  
  14.     fcntl.flock(file.fileno(), fcntl.LOCK_EX)   #為了避免同時操作文件,需要程序自己來檢查該文件是否已經被加鎖。這里如果檢查到加鎖了,進程會被阻塞      
  15.     print 'acquire lock'  
  16.     counter = int(file.readline()) + 1  
  17.     file.seek(0)  
  18.     file.write(str(counter))  
  19.     print os.getpid(), "=>", counter  
  20.     time.sleep(10)  
  21.     file.close() # unlocks the file  
  22.     print 'release lock'  
  23.     time.sleep(3)  

 

分別啟動2個進程來同時運行這個腳本,我們可以很明顯的看到2者互相之間交替阻塞。同一時刻只有一個進程能夠對counter.txt文件進行操作。

2.對fcntl.flock()函數的說明:

linux的flock() 的函數原型如下所示:
int flock(int fd, int operation);
其中,參數 fd 表示文件描述符;參數 operation 指定要進行的鎖操作,該參數的取值有如下幾種:
LOCK_SH:表示要創建一個共享鎖,在任意時間內,一個文件的共享鎖可以被多個進程擁有;
LOCK_EX:表示創建一個排他鎖,在任意時間內,一個文件的排他鎖只能被一個進程擁有;
LOCK_UN:表示刪除該進程創建的鎖;
LOCK_MAND:它主要是用於共享模式強制鎖,它可以與 LOCK_READ 或者 LOCK_WRITE聯合起來使用,從而表示是否允許並發的讀操作或者並發的寫操作;
    
通常情況下,如果加鎖請求不能被立即滿足,那么系統調用 flock()會阻塞當前進程。比如,進程想要請求一個排他鎖,但此時,已經由其他進程獲取了這個鎖,那么該進程將會被阻塞。如果想要在沒有獲得這個排他鎖的情況下不阻塞該進程,可以將LOCK_NB 和 LOCK_SH 或者 LOCK_EX 聯合使用,那么系統就不會阻塞該進程。flock()所加的鎖會對整個文件起作用。
注意:
1. 對於文件的 close() 操作會使文件鎖失效;
2. 同理,進程結束后文件鎖失效;

3. flock() 的 LOCK_EX是“勸告鎖”,系統內核不會強制檢查鎖的狀態,需要在代碼中進行文件操作的地方顯式檢查才能生效。

3.相關資料

1.Linux中的文件鎖的概念及其實現(http://blog.csdn.net/jianhong1990/article/details/26369465)

2.fcntl模塊的官方文檔(https://docs.python.org/2/library/fcntl.html#fcntl.flock)

 

 

===============================================

值得注意的是,在給文件加鎖之前,一定要保證文件以相應的訪問模式打開,例如要對一個文件加上共享鎖,一定要首先按讀模式打開文件,若要給文件加上排他鎖,則首先要按寫模式打開對應文件;若想加兩種鎖,則需要按讀寫模式打開.

跨進程鎖的實現方式中,基於文件鎖的方式相對來說好一點。以下貼出一個簡單的代碼:

  1. import os  
  2. import fcntl  
  3.   
  4. class Lock:   
  5.     def __init__(self, filename):  
  6.         self.filename = filename  
  7.         # This will create it if it does not exist already  
  8.         self.handle = open(filename, 'w')  
  9.       
  10.     # Bitwise OR fcntl.LOCK_NB if you need a non-blocking lock   
  11.     def acquire(self):  
  12.         fcntl.flock(self.handle, fcntl.LOCK_EX)  
  13.           
  14.     def release(self):  
  15.         fcntl.flock(self.handle, fcntl.LOCK_UN)            //最好改成fcntl.lockf
  16.           
  17.     def __del__(self):  
  18.         self.handle.close()  
  19.   
  20. # Usage  
  21. try:  
  22.     lock = Lock("/tmp/lock_name.tmp")  
  23.     lock.acquire()  
  24.     # Do important stuff that needs to be synchronized  
  25.   
  26. finally:   
  27.     lock.release()  

可以同時運行多份該程序來進行試驗。

 

這種方式的鎖有以下特點:

1. 鎖文件只在第一次調用的時候創建。

2. 鎖是通過kernel來控制的,不消耗磁盤IO

3. 這種方式的鎖只對同一個OS中的進程有效。跨服務器、OS是無效的,這時候需要選用分布式鎖,比如Elock  http://dustin.sallings.org/elock/

 

 

==============================

一不小心被文件鎖坑了然后就死鎖了

文件鎖我用的是 fcntl.flock,通過 strace 命令查看進程正在進行的系統調用,確實能看到進程阻塞在 flock 函數上,cat /proc/locks 能看到目標進程除了正在持有一個 FLOCK,還有另外兩個嘗試獲取鎖的操作,只不過很不幸地被正在持有的鎖阻塞了。

文件鎖這種東西就是比較坑,沒有什么方法能夠從外部讓進程主動釋放,除非殺死進程,或者換一個文件。但是出乎我意料的是,即便我把目標進程重啟了,它還是會阻塞在獲取鎖的操作上,從 /proc/locks 來看,鎖確實沒有被釋放,而持有鎖的進程,則是一個早已不存在的進程。

靈異事件!不存在的進程居然還能持有文件鎖!

一番探尋之后,用 lsof 發現了真相:持有文件鎖的,不是所謂的不存在的進程,而是這個不存在的進程的子進程。在我貧瘠的知識中,只知道大多數時候 fork 出來的子進程會持有父進程持有的文件描述符(aka fd),但是文件鎖這種東西也會被繼承?

上網查詢了相關資料,我才發現有一種坑爹的文件鎖,是真的有可能伴隨着 fd 一起被子進程繼承的,非常不幸,這種文件鎖恰好就是我用的那個,fcntl.flock

既然知道了原因,解決起來就容易了,要么把文件鎖換成 fcntl.lockf,要么在用 subprocess.Popen 創建子進程的時候,帶上參數 close_fds=True,讓創建出來的子進程把除了 stdin,stdout,stderr 之外的 fd 全關掉,別從父進程帶一大堆有的沒的文件過來。

 

https://stackoverflow.com/questions/22409780/flock-vs-lockf-on-linux

In Linux, lockf() is just a wrapper around fcntl(), while flock() locks are separate (and will only work on local filesystems, not on e.g. NFS mounts). That is, one process can have an advisory exclusive flock() lock on a file, while another process has an advisory exclusive fcntl() lock on that same file. Both are advisory locks, but they do not interact.

POSIX does not explicitly specify how lockf()/flock()/fcntl() locks should interact, and there have been differences in the past. Now, the situation has calmed down a bit, and one can approximately say that

  1. fcntl() locks are the most reliable

    Across architectures, they have the best chance of working right on e.g. shared filesystems -- NFS and CIFS mounts, for example.

  2. Most often, lockf() is implemented as "shorthand" for fcntl()

    The other alternative, as "shorthand" for flock(), is possible, but nowadays rare.

  3. fcntl() and flock() have different semantics wrt. inheritance and automatic releases

    fcntl() locks are preserved across an exec(), but not inherited across a fork()[fcntl/lockf的文件鎖不會被子進程繼承]. The locks are released when the owning process closes any descriptor referring to the same file.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM