首先,所有的系統調用都是原子性的。這句話來自TLPI:
"All system calls are executed atomically. By this, we mean that the kernel guarantees that all of the steps in a system call are completed as a single operation, without being interrupted by another process of thread."
太好了!於是當兩個線程或進程在同一時間寫同一文件時,我們可以肯定他們的數據不會交錯插入。這使調用write(2)不需要互斥鎖,而且還能保證原子性,因為內核已經幫我們做到那個了。
但是還有一個問題,一般寫文件時,你需要找到你想插入的具體的位置,然后再進行真正的寫入操作。不幸的是,這會涉及到兩個系統調用,lseek(2)和write(2).他們各自是原子性的,但是兩個操作作為一個整體就不是原子性的。讓我們來證明下:
用下面的偽代碼表示寫文件的過程:
lseek(fd,0,SEEK_END);// seek to the end of the file
write(fd,"log message",len);// perform the write
在多線程環境下,這種操作是不安全的。通過這兩個系統調用不能保證原子性,例如下面的情況:
d'oh 線程A不在指向文件的真正末尾位置,它指向文件先前的默認位置,線程B寫入信息的位置。當線程A寫入文件時,可能被線程B寫入的信息覆蓋。
通過這種方法寫文件,有可能會丟失數據。
但是還有一種方法。
為了能保證seek和write兩個操作發生的原子性,你可以設置O_APPEND標志,當你在打開一個文件時。然后,任何往文件追加的寫操作都能保證是原子性的。這正是我們在寫要共享日志文件時想要的情況。Logger和MonoLogger都是用這個標志來打開文件。
open(filename,(FILE::WRONLY|FILE::APPEND))
因此MonoLogger可以原子性的追加到日志系統,而不需要互斥信號。
轉載:https://www.jianshu.com/p/b5a731940ff9