Snowdream

I'm awake but my world is half asleep
posts - 403, comments - 310, trackbacks - 0, articles - 7
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

APUE - File I/O (4) - Atomic Operations

Posted on 2007-08-21 14:45 ZelluX 阅读(409) 评论(0)  编辑  收藏 所属分类: LinuxC/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函数返回失败值。如果不使用这两个开关,可以这样写:
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");
    }
}

当open函数执行后creat函数执行前另一个进程创建了同名文件的话,该数据就会被擦除。

只有注册用户登录后才能发表评论。


网站导航: