APUE - File I/O (4) - Atomic Operations
Posted on 2007-08-21 14:45 ZelluX 閱讀(528) 評論(0) 編輯 收藏 所屬分類: Linux 、C/C++Appending to a File
考慮某個單一進(jìn)程試圖在文件追加寫入的操作。由于早期的UNIX系統(tǒng)不支持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函數(shù)的第三個參數(shù)2等于SEEK_END常量,但是早期UNIX并沒有這個常量名(System V中才引進(jìn))
這樣的處理在多進(jìn)程試圖在同一文件尾部追加寫入時就有可能出現(xiàn)問題。
假設(shè)兩個獨立線程A B要在某個文件尾部追加寫入,使用了上述方法,并沒有使用O_APPEND開關(guān)。
首 先A指向了該文件尾部(假設(shè)是offset=1500的地方),接著內(nèi)核切換到B進(jìn)程,B也指向了該尾部,然后寫入了100字節(jié),此時內(nèi)核把v-node 中當(dāng)前文件的大小改為了1600。內(nèi)核再次切換進(jìn)程,A繼續(xù)運行,調(diào)用write方法,結(jié)果就在offset=1500的地方寫入了,覆蓋了B進(jìn)程寫入的 內(nèi)容。
這個問題其實和Java中的多線程差不多,指向文件尾部和寫入應(yīng)該作為一個原子操作執(zhí)行,就像Java中使用synchronized塊保護(hù)原子操作。使用O_APPEND選項就是一種解決方法。
另一種解決方法時使用XSI extension中的pread和pwrite函數(shù)。
#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
調(diào)用pread與調(diào)用lseek后再調(diào)用read等價,以下情況除外:
1. pread的兩個步驟無法被中斷。
2. 文件指針尚未被更新時。
pwrite與lseek+write的差別也相似。
Creating a File
當(dāng)使用O_CREAT和O_EXCL開關(guān)調(diào)用open函數(shù)時,如果文件已經(jīng)存在,則open函數(shù)返回失敗值。如果不使用這兩個開關(guān),可以這樣寫:
當(dāng)open函數(shù)執(zhí)行后creat函數(shù)執(zhí)行前另一個進(jìn)程創(chuàng)建了同名文件的話,該數(shù)據(jù)就會被擦除。
考慮某個單一進(jìn)程試圖在文件追加寫入的操作。由于早期的UNIX系統(tǒng)不支持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函數(shù)的第三個參數(shù)2等于SEEK_END常量,但是早期UNIX并沒有這個常量名(System V中才引進(jìn))
這樣的處理在多進(jìn)程試圖在同一文件尾部追加寫入時就有可能出現(xiàn)問題。
假設(shè)兩個獨立線程A B要在某個文件尾部追加寫入,使用了上述方法,并沒有使用O_APPEND開關(guān)。
首 先A指向了該文件尾部(假設(shè)是offset=1500的地方),接著內(nèi)核切換到B進(jìn)程,B也指向了該尾部,然后寫入了100字節(jié),此時內(nèi)核把v-node 中當(dāng)前文件的大小改為了1600。內(nèi)核再次切換進(jìn)程,A繼續(xù)運行,調(diào)用write方法,結(jié)果就在offset=1500的地方寫入了,覆蓋了B進(jìn)程寫入的 內(nèi)容。
這個問題其實和Java中的多線程差不多,指向文件尾部和寫入應(yīng)該作為一個原子操作執(zhí)行,就像Java中使用synchronized塊保護(hù)原子操作。使用O_APPEND選項就是一種解決方法。
另一種解決方法時使用XSI extension中的pread和pwrite函數(shù)。
#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
調(diào)用pread與調(diào)用lseek后再調(diào)用read等價,以下情況除外:
1. pread的兩個步驟無法被中斷。
2. 文件指針尚未被更新時。
pwrite與lseek+write的差別也相似。
Creating a File
當(dāng)使用O_CREAT和O_EXCL開關(guān)調(diào)用open函數(shù)時,如果文件已經(jīng)存在,則open函數(shù)返回失敗值。如果不使用這兩個開關(guān),可以這樣寫:
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");
}
}
當(dāng)open函數(shù)執(zhí)行后creat函數(shù)執(zhí)行前另一個進(jìn)程創(chuàng)建了同名文件的話,該數(shù)據(jù)就會被擦除。