在多进程以同样方式同时打开同一个文件写的条件下:
1.如果以追加 方式写,会将所有进程所写的内容全部保存进文件,也就是说进程间对彼此已经完成的写入操作是可见的;
2.如果以覆盖 方式写,则只有最后一个执行写入操作的进程会将其内容保存进文件,也就是说进程间对彼此已经完成的写入操作是不可见的。
1) O_WRONLY
write 函数是这样处理的,比如写入的当前位置为 5, 写入长度为 10,文件长度为20。则write 只管更新内容,不会更改文件长度。如果写入长度为 16,则除更新内容外,还要更新文件长度。当前位置为每个进程私有,但文件长度为多个进程共享。换而言之,一 个进程打开一个文件后,别的进程更改了该文件的长度,原来的进程并不知道。
在这用方式下,多个进程写入的内容会相互覆盖。
2) O_WRONLY|O_APPAND
这时 write 写入方式与1)不同,写入前,进程先将写入点定位到文件末尾,然后写入,这是一个原子操作,所以必定不会覆盖其他进程写入的内容。
3 . 11.1 添加至一个文件
考虑一个进程,它要将数据添加到一个文件尾端。早期的U N I X版本并不支持o p e n的
O _ A P P E N D选择项,所以程序被编写成下列形式:
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标志。此时各数据结构之间的关系如图3 - 2中所示一样。每个进程都有它
自己的文件表项,但是共享一个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刚写到该文
件中的数据。
这里的问题出在逻辑操作“定位档到文件尾端处,然后写”使用了两个分开的函数调用。
解决问题的方法是使这两个操作对于其他进程而言成为一个原子操作。任何一个要求多于1个
函数调用的操作都不能成为原子操作,因为在两个函数调用之间,内核有可能会临时挂起该进
程(正如我们前面所假定的)。
U N I X 提供了一种方法使这种操作成为原子操作,其方法就是在打开文件时设置
O _ A P P E N D标志。正如前一节中所述,这就使内核每次对这种文件进行写之前,都将进程的
当前位移量设置到该文件的尾端处,于是在每次写之前就不再需要调用l s e e k。