2012-03-26 65 views
8

多進程同時訪問(寫入)同一文件是否正常?使用下面的代碼,它似乎工作,但我有我的懷疑。在實例訪問同一文件的多個進程

使用的情況是,被稱爲每一個接收到電子郵件,並記錄其輸出到中央文件時的可執行程序。

if (freopen(console_logfile, "a+", stdout) == NULL || freopen(error_logfile, "a+", stderr) == NULL) { 
    perror("freopen"); 
} 
printf("Hello World!"); 

這是在CentOS運行和編譯爲C.

+0

可能(更好)重複[fopen兩個進程](http://stackoverflow.com/questions/1842909/fopen-two-processes)。 – blahdiblah 2012-03-26 22:45:17

+0

另請參見[多個進程可以附加到文件使用fopen沒有任何併發​​問題?](http://stackoverflow.com/questions/7552451/can-multiple-processes-append-to-a-file-using-fopen-沒有,任何併發-概率)。 – blahdiblah 2012-03-26 22:47:37

+2

我不知道日誌的使用情況,但我建議看一下'syslog'。也許它適合你。使用它非常簡單。 http://www.gnu.org/software/libc/manual/html_node/Submitting-Syslog-Messages.html – 2012-03-26 22:57:52

回答

8

使用標準C IO設備引入了複雜的新層;該文件僅通過write(2)修改 - 系統調用族(或內存映射,但在本例中未使用) - C標準IO包裝可能會推遲寫入文件一段時間,並且可能不會在一個系統中提交完整的請求呼叫。

write(2)調用本身應該表現良好:

[...] If the file was 
    open(2)ed with O_APPEND, the file offset is first set to the 
    end of the file before writing. The adjustment of the file 
    offset and the write operation are performed as an atomic 
    step. 

    POSIX requires that a read(2) which can be proved to occur 
    after a write() has returned returns the new data. Note that 
    not all file systems are POSIX conforming. 

因此您的基礎write(2)通話將表現正常。

對於更高級別的C標準IO流,您還需要照顧緩衝。 setvbuf(3)函數可用於請求無緩衝輸出,行緩衝輸出或塊緩衝輸出。從流到流的默認行爲發生變化 - 如果標準輸出和標準錯誤正在寫入終端,則默認情況下它們是行緩衝和無緩衝的。否則,塊緩衝是默認設置。

您可能希望手動選擇行緩衝如果您的數據自然是面向行的,以防止交織的數據。如果你的數據是而不是面向行的,你可能希望使用un-buffered或者讓它進行塊緩衝,但是每當你累積了一個單位的輸出時手動刷新數據。

如果您一次寫入超過BUFSIZ字節,則寫入操作可能會交錯。 setvbuf(3)函數可以幫助防止交織。

談論性能可能爲時過早,但行緩衝將比塊緩衝慢。如果您的日誌速度接近磁盤的速度,則可能希望完全採用另一種方法來確保寫入不會交錯。

+0

關於'setvbuf()'及其變體'setbuf()','setbuffer()'和'setlinebuf()'的大提示。他們正是我需要的。謝謝,@sarnold。 – 2012-03-29 17:15:10

+0

感謝您指出需要O_APPEND來保證文件偏移量的原子調整。我從第一個打開文件的進程中省略了它,因爲它也創建了它(所以'append'似乎不合適..) – RobM 2012-10-26 20:12:50

1

這個答案是不正確的。它的工作:

所以競爭條件是:

  1. 過程1打開它進行追加,然後
  2. 後處理2打開它進行追加,然後
  3. 後來還是1個寫入並關閉,然後
  4. 最後2寫和關閉。

如果那個'工作',我會留下深刻的印象,因爲我不清楚什麼 工作意味着什麼。我假設「工作」是指由兩個進程寫入的所有字節 都在日誌文件中?我期望他們都從相同的字節偏移開始寫入 ,所以一個將替換其他的 字節。這一切都會好起來,包括第3步,並且只在步驟4中出現 作爲問題,看起來像是一個簡單的測試寫:打開getchar ...寫封閉。

他們可以同時打開文件至關重要嗎? A 更明顯的解決辦法如果寫得很快,就是打開專屬。

爲了您系統上的快速檢查,請嘗試:

/* write the first command line argument to a file called foo 
* stackoverflow topic 9880935 
*/ 

#include <stdio.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 

int main (int argc, const char * argv[]) { 
    if (argc <2) { 
     fprintf(stderr, "Error: need some text to write to the file Foo\n"); 
     exit(1); 
    } 

    FILE* fp = freopen("foo", "a+", stdout); 

    if (fp == NULL) { 
     perror("Error failed to open file\n"); 
     exit(1); 
    } 

    fprintf(stderr, "Press a key to continue\n"); 
    (void) getchar();  /* Yes, I really mean to ignore the character */ 

    if (printf("%s\n", argv[1]) < 0) { 
     perror("Error failed to write to file: "); 
     exit(1);   
    } 

    fclose(fp); 

    return 0; 
} 
+0

它們不會相互覆蓋,從'man freopen':'a + ...隨後寫入文件將始終在文件的當前結尾處結束。 – blahdiblah 2012-03-26 22:42:21

+0

@blahdiblah - 也許我錯過了一些東西,但是他們怎麼能**在我的例子中不覆蓋?這兩個進程都打開進行追加,但是在這個階段都沒有寫入任何字節,所以這兩個打開文件的長度都是相同的。然後他們都寫。文件是不是偏移了fd的屬性,而不是文件? – gbulmer 2012-03-26 22:49:33

+0

我僅報告手冊頁中的信息以及我自己的測試結果。我無法說明底層的實現細節。 – blahdiblah 2012-03-26 22:53:00