APUE - File I/O (4) - Atomic Operations
Posted on 2007-08-21 14:45 ZelluX 閱讀(523) 評論(0) 編輯 收藏 所屬分類: Linux 、C/C++Appending to a File
考慮某個單一進程試圖在文件追加寫入的操作。由于早期的UNIX系統不支持O_APPEND選項,因此得先用lseek將offset置于文件尾部。
if (lseek(fd, 0L, 2) < 0)
err_sys("lseek error");
if (write(fd, buf, 100) != 100)
err_sys("write error");
注意這里的lseek函數的第三個參數2等于SEEK_END常量,但是早期UNIX并沒有這個常量名(System V中才引進)
這樣的處理在多進程試圖在同一文件尾部追加寫入時就有可能出現問題。
假設兩個獨立線程A B要在某個文件尾部追加寫入,使用了上述方法,并沒有使用O_APPEND開關。
首 先A指向了該文件尾部(假設是offset=1500的地方),接著內核切換到B進程,B也指向了該尾部,然后寫入了100字節,此時內核把v-node 中當前文件的大小改為了1600。內核再次切換進程,A繼續運行,調用write方法,結果就在offset=1500的地方寫入了,覆蓋了B進程寫入的 內容。
這個問題其實和Java中的多線程差不多,指向文件尾部和寫入應該作為一個原子操作執行,就像Java中使用synchronized塊保護原子操作。使用O_APPEND選項就是一種解決方法。
另一種解決方法時使用XSI extension中的pread和pwrite函數。
#include <unistd.h>
ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset);
// Returns: number of bytes read, 0 if end of file, -1 on error
ssize_t pwrite(int filedes, const void *buf, size_t nbytes, off_t offset);
// Returns: number of bytes read, 0 if end of file, -1 on error
調用pread與調用lseek后再調用read等價,以下情況除外:
1. pread的兩個步驟無法被中斷。
2. 文件指針尚未被更新時。
pwrite與lseek+write的差別也相似。
Creating a File
當使用O_CREAT和O_EXCL開關調用open函數時,如果文件已經存在,則open函數返回失敗值。如果不使用這兩個開關,可以這樣寫:
當open函數執行后creat函數執行前另一個進程創建了同名文件的話,該數據就會被擦除。
考慮某個單一進程試圖在文件追加寫入的操作。由于早期的UNIX系統不支持O_APPEND選項,因此得先用lseek將offset置于文件尾部。
if (lseek(fd, 0L, 2) < 0)
err_sys("lseek error");
if (write(fd, buf, 100) != 100)
err_sys("write error");
注意這里的lseek函數的第三個參數2等于SEEK_END常量,但是早期UNIX并沒有這個常量名(System V中才引進)
這樣的處理在多進程試圖在同一文件尾部追加寫入時就有可能出現問題。
假設兩個獨立線程A B要在某個文件尾部追加寫入,使用了上述方法,并沒有使用O_APPEND開關。
首 先A指向了該文件尾部(假設是offset=1500的地方),接著內核切換到B進程,B也指向了該尾部,然后寫入了100字節,此時內核把v-node 中當前文件的大小改為了1600。內核再次切換進程,A繼續運行,調用write方法,結果就在offset=1500的地方寫入了,覆蓋了B進程寫入的 內容。
這個問題其實和Java中的多線程差不多,指向文件尾部和寫入應該作為一個原子操作執行,就像Java中使用synchronized塊保護原子操作。使用O_APPEND選項就是一種解決方法。
另一種解決方法時使用XSI extension中的pread和pwrite函數。
#include <unistd.h>
ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset);
// Returns: number of bytes read, 0 if end of file, -1 on error
ssize_t pwrite(int filedes, const void *buf, size_t nbytes, off_t offset);
// Returns: number of bytes read, 0 if end of file, -1 on error
調用pread與調用lseek后再調用read等價,以下情況除外:
1. pread的兩個步驟無法被中斷。
2. 文件指針尚未被更新時。
pwrite與lseek+write的差別也相似。
Creating a File
當使用O_CREAT和O_EXCL開關調用open函數時,如果文件已經存在,則open函數返回失敗值。如果不使用這兩個開關,可以這樣寫:
if ((fd = open(pathname, O_WRONLY)) < 0) {
if (errno == ENOENT) {
if ((fd = creat(pathname, mode)) < 0)
err_sys("creat error");
} else {
err_sys("open error");
}
}
if (errno == ENOENT) {
if ((fd = creat(pathname, mode)) < 0)
err_sys("creat error");
} else {
err_sys("open error");
}
}
當open函數執行后creat函數執行前另一個進程創建了同名文件的話,該數據就會被擦除。