-
设计一种直接将文件-数据流图转换为模块结构图的方法
资源介绍
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。
3 . 11.2 创建一个文件
在对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");
第 3章 文 件 I/O 4 5
- 上一篇: 操作系统-处理机管理
- 下一篇: 多处理机--多核操作系统原理