即使write()
是完全線程安全的(禁止執行錯誤...),理論上這並不安全。 (強調我的): 。
The write()
function shall attempt to writenbyte
bytes from the buffer pointed to by buf
to the file associated with the open file descriptor, fildes
.
...
RETURN VALUE
Upon successful completion, these functions shall return the number of bytes actually written ...
誰也不能保證,你不會得到部分write()
,所以即使每個人write()
調用是原子,它不一定完整,所以你仍然可以得到交錯的數據,因爲它可能需要更多比撥打write()
要完全寫入所有數據。在實踐中,如果你只做相對較小的調用write()
,你可能永遠不會看到局部的write()
,「小」和「可能」是取決於你的實現的不確定值。
我經常發表使用與O_APPEND
打開普通文件解鎖單write()
調用,以提高記錄的性能的代碼 - 建立一個日誌條目,然後write()
一次調用整個條目。我已經從來沒有在Linux和Solaris系統上做了幾乎幾十年的部分或交錯的write()
結果,即使許多進程寫入相同的日誌文件。但是再次,它是一個文本日誌文件,如果發生部分或交錯的write()
,則不會造成真正的損壞甚至數據丟失。
但是,在這種情況下,您正在將少量字節「寫入」內核結構。您可以在Illumos.org上查看Solaris /dev/poll
內核驅動程序源代碼,並查看部分write()
的可能性。我懷疑這實際上是不可能的 - 因爲我回去看了十年前爲我公司的軟件庫編寫的多平臺調查班。在Solaris上,它使用/dev/poll
和解鎖來自多個線程的write()
調用。它已經工作正常了十年......
的Solaris的/ dev /泳池設備驅動程序源代碼分析
的(開放)的Solaris源代碼可以在這裏找到:http://src.illumos.org/source/xref/illumos-gate/usr/src/uts/common/io/devpoll.c#628
的dpwrite()
函數是實際執行「寫入」操作的/dev/poll
驅動程序中的代碼。我使用引號是因爲它根本不是一個寫操作 - 數據不會像內核中的數據那樣傳輸,而是代表被輪詢的文件描述符集被更新。
將數據從用戶空間複製到內核空間 - 到使用kmem_alloc()
獲取的內存緩衝區。我沒有看到任何可能的方式,可以是部分副本。分配成功或者不成功。在執行任何操作之前,代碼可能會被中斷,因爲它等待對內核結構的獨佔訪問權限write()
。
之後,最後返回調用是在結束 - 如果沒有錯誤,則整個呼叫被標記爲成功或任何錯誤的整個調用失敗:
995 if (error == 0) {
996 /*
997 * The state of uio_resid is updated only after the pollcache
998 * is successfully modified.
999 */
1000 uioskip(uiop, copysize);
1001 }
1002 return (error);
1003}
如果你挖通的Solaris內核代碼,你會看到uio_resid
是成功調用後返回值爲write()
的結果。
因此,這個呼叫肯定看起來是全有或全無。儘管在傳入多個描述符成功處理較早的描述符後,代碼似乎可以在文件描述符上返回錯誤,但代碼似乎沒有返回任何部分成功指示。
如果您一次只處理一個文件描述符,我會說write()操作是完全線程安全的,並且它幾乎可以確保線程安全,可以將多個文件描述符「寫入」更新爲駕駛員沒有明顯的方式返回部分write()
的結果。
我很肯定你需要做你自己的同步。對於使用多線程應用程序的I/O,我的經驗是I/O不是線程安全的。 http://stackoverflow.com/questions/19974548/are-functions-in-the-c-standard-library-thread-safe有關於線程安全和C標準庫的一些討論,儘管它沒有討論'write()'特別是。然後有這篇文章,write(),線程安全和POSIX https://lwn.net/Articles/180387/ –
'write()'是線程安全的。但是,是否將從多個線程到同一個文件描述符的'write()'併發原子化是另一個問題。假設寫入文件不是太大,用'pwrite()'來分隔文件的大部分部分,'writev()'是可以的。 – EOF
'write()'是POSIX的函數列表,如果「兩個線程分別調用其中一個函數,每個調用將看到另一個調用的所有指定的效果,或者它們都不是。 http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_07 –