首先給出一段程式,這段程式的本來目的是為了驗證用O_APPEND標誌開啟檔案後,調用lseek(fd, 0, SEEK_CUR)應該返迴文件長度,但是運行程式後發現錯了。注意加紅的兩句,本來希望分別輸出0和49,但實際輸出都是零。
#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>int main(void){ int fd; off_t off; printf("Not using O_APPEND:\n"); fd = open("./test.c", O_WRONLY); off = lseek(fd, 0, SEEK_SET); printf("The offset of SEEK_SET is %d.\n", off); close(fd); fd = open("./test.c", O_WRONLY); off = lseek(fd, 0, SEEK_END); printf("The offset of SEEK_END is %d.\n", off); close(fd); fd = open("./test.c", O_WRONLY); off = lseek(fd, 0, SEEK_CUR); printf("The offset of SEEK_CUR is %d.\n", off); close(fd); printf("Using O_APPEND:\n"); fd = open("./test.c", O_WRONLY | O_APPEND); off = lseek(fd, 0, SEEK_SET); printf("The offset of SEEK_SET is %d.\n", off); close(fd); fd = open("./test.c", O_WRONLY | O_APPEND); off = lseek(fd, 0, SEEK_END); printf("The offset of SEEK_END is %d.\n", off); close(fd); fd = open("./test.c", O_WRONLY | O_APPEND); off = lseek(fd, 0, SEEK_CUR); printf("The offset of SEEK_CUR is %d.\n", off); close(fd); exit(0);}
輸出為:
Not using O_APPEND:
The offset of SEEK_SET is 0.
The offset of SEEK_END is 49.
The offset of SEEK_CUR is 0.
Using O_APPEND:
The offset of SEEK_SET is 0.
The offset of SEEK_END is 49.
The offset of SEEK_CUR is 0.
原本以為O_APPEND把檔案指標移到了檔案尾,以方便從檔案尾添加資料,但這麼看來不對,檔案指標並沒有移動。問題出在哪裡呢?
從APUE中得到答案:
每個進程有一個開啟檔案描述符表,而該表的表項中有一個檔案狀態標誌,當前檔案位移量和一個v節點指標,v節點指標指向一個v節點表,而該表中的i節點資訊中有一個當前檔案長度。
對於lseek來說,它直接修改檔案描述符表項中的當前檔案位移量,並返回當前的檔案位移量,而對於O_APPEND標誌,則只是將其設定到了檔案表項的檔案狀態標誌中,此時當前檔案位移量並未修改,仍然為0,只有當使用者企圖寫入檔案時,才將檔案當前位移量置為檔案長度。這從另一個角度說明,如果檔案以O_APPEND標誌開啟,則lseek對該檔案的寫將不起作用,因為無論lseek怎樣調整當前檔案位移量,在寫入時仍然會被設為檔案長度而將內容添加在檔案尾。例如如下程式:
#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>int main(void){ int fd; off_t off; fd = open("./test.c", O_WRONLY | O_APPEND); off = lseek(fd, 3, SEEK_SET); write(fd, "a", 1); printf("The offset of SEEK_SET is %d.\n", off); close(fd); exit(0);}
輸出為:
The offset of SEEK_SET is 3.
可見,lseek修改了當前檔案位移量,但是,查看test.c檔案,a卻添加在了檔案尾,驗證了上面的內容。但是,對於讀來說,O_APPEND就不起作用了,如下面的程式:
#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>int main(void){ int fd; char buf[3]; off_t off; fd = open("./test.c", O_RDONLY | O_APPEND); off = lseek(fd, 3, SEEK_SET); read(fd, buf, 2); buf[2] = '\0'; printf("%s\n", buf); printf("The offset of SEEK_SET is %d.\n", off); close(fd); exit(0);}
輸出為:
cl
The offset of SEEK_SET is 3.
程式讀到了檔案位移量為3處的兩個字元。
可見,OAPPEND標誌的主要作用就是保證在多進程寫入時,保證每個進程寫入的內容都的確添加在了檔案尾,因為不管有幾個進程,在他們寫同一個檔案時,都會首先將當前檔案位移量修改為此時此刻的檔案長度,這就保證了檔案寫入的原子性。