添加至一个文件
if (lseek(fd, 0L, 2) < 0) /*position to EOF*/
err_sys("lseek error");
if (write(fd, buff, 100) != 100) /*and write*/
err_sys("write error");
对单个进程而言,这段程序能正常工作,但若有多个进程时,则会产生问题。(如果此程序由多个进程同时执行,各自将消息添加到一个日记文件中,就会产生这种情况。)
假定有两个独立的进程A和B,都对同一文件进行追尾的添加操作。每个进程都已打开了该文件,但未使用O _ A P P E N D标志。每个进程都有它自己的文件表项,但是共享一个v节点表项。假定进程A调用了l s e e k,它将对于进程A的该文件
的当前位移量设置为1 5 0 0字节(当前文件尾端处)。然后内核切换进程使进程B运行。进程B执行l s e e k,也将其对该文件的当前位移量设置为1 5 0 0字节(当前文件尾端处)。然后B调用w r i t e,它将B的该文件的当前文件位移量增至1 6 0 0。因为该文件的长度已经增加了,所以内核对v节点中的当前文件长度更新为1 6 0 0。然后,内核又进行进程切换使进程A恢复运行。当A调用w r i t e时,就从其当前文件位移量( 1 5 0 0 )处将数据写到文件中去。这样也就代换了进程B刚写到该文件中的数据。
这里的问题出在逻辑操作“定位游标到了文件尾端处,然后写”使用了两个分开的函数调用。解决问题的方法是使这两个操作(定位 , 写 )对于其他进程而言成为一个原子操作,任何时候不能被打断成分成两步执行.
为此, U N I X 提供了一种方法使这种操作成为原子操作,其方法就是在打开文件时设置O _ A P P E N D标志。正如前一节中所述,这就使内核每次对这种文件进行写之前,都将进程的当前位移量设置为该文件的最新的尾端处,于是在每次写之前就不再需要人为的调用l s e e k。
创建一个文件
在对o p e n函数的O _ C R E AT和O _ E X C L选择项进行说明时,我们已见到了另一个有关原子操作的例子。当同时指定这两个选择项,而该文件又已经存在时, o p e 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");
如果在打开和创建之间,另一个进程创建了该文件,那么就会发生问题。如果在这两个函数调用之间,另一个进程创建了该文件,而且又向该文件写进了一些数据,那么执行这段程序中的c r e a t 时,刚写上去的数据就会被擦去。将这两者合并在一个原子操作中,此种问题也就不会产生。
一般而言,原子操作(atomic operation)指的是由多步组成的操作。如果该操作原子地执行,则或者执行完所有步,或者一步也不执行,不可能只执行所有步的一个子集.
posted on 2009-10-06 14:07
月光记忆 阅读(696)
评论(0) 编辑 收藏 所属分类:
c进程间通信