2011-09-06 44 views
4

我有不同的進程在Linux中同時訪問命名管道,我想讓這個訪問互斥。如何在Linux和C中使用文件作爲互斥體?

我知道有可能實現使用共享內存區域中的互斥鎖,但這是一種家庭作業,我有一些限制。

因此,我想到的是在文件上使用鎖定原語來實現互斥;我做了一些嘗試,但我無法使它工作。

這是我的嘗試:

flock(lock_file, LOCK_EX) 

// critic section 

flock(lock_file, LOCK_UN) 

不同的項目將使用不同的文件描述符,但指的是同一個文件。 是否有可能實現類似的目標?你能舉一些例子嗎?

+2

不。使用互斥鎖。 –

+0

我無法使用互斥體!我不想讓事情複雜化,但是這必須以這種方式完成 – Simone

+2

也許你應該解釋爲什麼要使用文件而不是真正的互斥體...... – Macmade

回答

3

你的例子和你使用flock (2)一樣好(畢竟,這只是一個「顧問」鎖(也就是說根本不是鎖))。在我的Mac OS X系統上的手冊頁有幾個可能重要的附帶條件:

鎖在文件上,而不是文件描述符。也就是說,通過dup(2)或fork(2)複製的文件描述符不會導致鎖的多個實例,而是多次引用單個鎖。如果一個進程上的文件叉持有鎖和孩子明確地解鎖文件,該 母公司就失去了鎖定

進程阻塞,正在等待一個鎖可以通過信號喚醒。

兩者都提出了可能失敗的方法。


//本來是一個評論,但我想在一些長度引述該名男子頁

4

標準鎖文件技術使用O_EXCL等選項調用open()來嘗試創建文件。您使用鎖存儲進程的PID,因此您可以確定進程是否仍然存在(使用kill()進行測試)。你必須擔心併發 - 很多。

步驟:

  • 基於FIFO的名稱確定鎖定文件的名稱
  • 如果使用如果其他進程存在,它存在
    • 存在
    • 檢查是否處理
    • 打開鎖文件,它已控制(退出時出錯,或等待退出)
    • 如果沒有其他進程,則刪除鎖定文件
  • 此時,鎖定文件在上次檢查時不存在。
  • 嘗試使用其他選項中的open()O_EXCL來創建它。
  • 如果有效,您的流程創建了文件 - 您有權繼續。
  • 將您的PID寫入文件;關閉它。
  • 打開FIFO - 使用它。
  • 完成後(atexit()?)刪除鎖定文件。

擔心如果打開鎖定文件並且不讀取PID會發生什麼......是另一個進程是否創建了它並且尚未將其PID寫入其中,還是在此之前死亡?可能最好退後一步 - 關閉文件並重試(可能在隨機化後的nanosleep())。如果您多次獲取空文件(比如說連續3次),則假定進程已死,並刪除鎖定文件。

當FIFO打開時,您可以考慮擁有該文件的進程在該文件上保留一個諮詢鎖。如果鎖不存在,則該過程已經死亡。在打開文件和應用鎖之間仍然存在TOCTOU(檢查時間,使用時間)漏洞窗口。

仔細查看系統上的open()手冊頁,查看是否有任何其他選項可以幫助您。有時,進程使用目錄(mkdir())而不是文件,因爲甚至root也不能創建給定目錄名稱的第二個實例,但是如果知道資源打開的進程的PID,則會遇到問題等。

+0

另一種方式?我也考慮過使用第三個fifo,因爲fifo阻止讀者在空的時候......這可能嗎? – Simone

+0

我不會說這是不可能的,但是使用FIFO來獲得對另一個FIFO的獨佔訪問感覺不對,我不知道它是如何工作的。但我沒有花太多時間思考它是如何工作的。 –

+0

這種方法是bugprone:PID不能保證是唯一的,可以重複使用。我不明白爲什麼在打開文件和鎖定文件之間會出現TUCTOU。爲什麼不使用在程序退出時自動釋放的'flock'? –

4

我肯定會推薦使用一個實際的互斥(正如在評論中有人建議) ;例如,pthread庫提供an implementation。但是如果你想自己做一個用於教育目的的文件,我建議看一下前面發佈的this answer,它描述了在Python中這樣做的方法。轉換爲C,它應該看起來像這樣(警告:未經測試的代碼,使用風險自負;我的C是生鏽的):

// each instance of the process should have a different filename here 
char* process_lockfile = "/path/to/hostname.pid.lock"; 
// all processes should have the same filename here 
char* global_lockfile = "/path/to/lockfile"; 
// create the file if necessary (only once, at the beginning of each process) 
FILE* f = fopen(process_lockfile, "w"); 
fprintf(f, "\n"); // or maybe write the hostname and pid 
fclose(f); 

// now, each time you have to lock the file: 
int lock_acquired = 0; 
while (!lock_acquired) { 
    int r = link(process_lockfile, global_lockfile); 
    if (r == 0) { 
     lock_acquired = 1; 
    } 
    else { 
     struct stat buf; 
     stat(process_lockfile, &buf); 
     lock_acquired = (buf.st_nlink == 2); 
    } 
} 
// do your writing 
unlink(global_lockfile); 
lock_acquired = 0; 
+0

不錯,但應該避免忙碌的等待。在BSD/Darwin上,可以使用'kqueue','EVFILT_VNODE'和'EVFILT_PROC'。一些方法也可能在Linux上存在。 –

+0

'link'方法的主要問題是,如果程序以異常方式終止(bug/crash/signal/system shutdown),鎖將保持不變。 'flock'沒有這個問題。 –