在“Unix环境中的高级编程”,第2版,作者:W。Richard Stevens。
第8.3节fork函数。
以下是描述:
父项和子项共享相同的文件偏移量非常重要。
考虑一个分叉孩子的过程,然后等待孩子完成。假设两个进程都写入标准输出作为其正常处理的一部分。如果父级的标准输出重定向(可能是shell),则当子级写入标准输出时,子级必须更新父级的文件偏移量。
的 [1。这是什么意思?例如,如果父项的std输出被重定向到'file1',那么子项写入后子项应该更新什么? parent的原始std输出偏移或重定向输出(即file1)偏移?不能晚于,对吗?]
的 [2。更新是如何完成的?由子显式,由OS隐式地,由文件描述符本身?在fork之后,我认为父母和孩子走自己的路,并拥有自己的文件描述符COPY。那么孩子如何更新偏移到父方?]
在这种情况下,孩子可以在父母等待时写入标准输出;在孩子完成后,父母可以继续写入标准输出,知道其输出将附加到孩子写的任何内容。如果父级和子级没有共享相同的文件偏移量,则此类交互将更难实现,并且需要父级的明确操作。
如果父节点和子节点都写入相同的描述符,没有任何形式的同步,例如让父节点等待子节点,则它们的输出将被混合(假设它是在fork之前打开的描述符)。虽然这是可能的,但这不是正常的操作模式。
在fork之后处理描述符有两种正常情况。
父母等待孩子完成。在这种情况下,父级不需要对其描述符执行任何操作。当子进程终止时,子进程读取或写入的任何共享描述符都会相应地更新其文件偏移量。
父母和孩子都有自己的方式。这里,在fork之后,父关闭它不需要的描述符,并且子进行相同的操作。这样,既不会干扰对方的开放描述符。网络服务器通常就是这种情况。“
的 [3。当调用fork()时,我理解的是那个子得到了父有什么的COPY,在这种情况下是文件描述符,并做了它的事情。如果任何偏移更改为父和子共享的文件描述符,则只能因为描述符记住偏移本身。我是对的吗?]
对不起,我对这些概念不熟悉。
有任何帮助吗?感谢。
答案 0 :(得分:75)
区分文件描述符非常重要,文件描述符是进程在其读写调用中用于标识文件的小整数,以及文件描述 ,这是内核中的一个结构。文件偏移量是文件描述的一部分。它存在于内核中。
举个例子,让我们使用这个程序:
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(void)
{
int fd;
fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666);
if(!fork()) {
/* child */
write(fd, "hello ", 6);
_exit(0);
} else {
/* parent */
int status;
wait(&status);
write(fd, "world\n", 6);
}
}
(省略了所有错误检查)
如果我们编译此程序,请将其命名为hello
,并按以下方式运行:
./hello
这是发生的事情:
程序打开output
文件,如果它不存在则创建它,或者如果确实存在则将其截断为零。内核创建文件描述(在Linux内核中为struct file
)并将其与调用进程的文件描述符相关联(该进程的文件描述符表中尚未使用的最低非负整数)。返回文件描述符并将其分配给程序中的fd
。为了论证,假设fd
是3。
程序执行fork()。新的子进程获取其父文件描述符表的副本,但不复制文件描述。两个进程文件表中的条目号3指向相同的struct file
。
父进程在子进程写入时等待。孩子的写入会导致"hello world\n"
的前半部分存储在文件中,并使文件偏移量增加6.文件偏移量位于struct file
!
子进程退出,父进程wait()
完成,父进程使用fd 3进行写入,fd 3仍然与相同的文件描述相关联,文件描述的文件偏移由子进程write()
更新。所以消息的后半部分存储在之后第一部分,而不是覆盖它,如果父文件的文件偏移量为零,则会覆盖它,如果文件描述不是这样的话共享。
最后父进程退出,内核看到struct file
不再使用并释放它。
答案 1 :(得分:4)
在本书的同一部分中,有一个图表显示了打开文件时存在的三个表。
用户filedescriptor表(进程表条目的一部分),filetable和inode表(v-node表)。
现在,filedescriptor(它是文件描述符表的索引)条目指向文件表条目,该条目指向inode表条目。
现在文件表格中存在文件偏移量(下次读/写发生的位置)。
所以说你在父母中打开了一个文件,这意味着它有一个描述符,一个文件
表条目和inode参考。
现在,在创建子项时,将为子项复制文件描述符表。
因此,文件表条目(对于该打开的描述符)中的引用计数增加,这意味着现在对同一文件表条目有两个引用。
此描述符现在在父项和子项中均可用,指向相同的文件表条目,因此共享偏移量。 现在有了这个背景,让我们看看你的问题,
孩子显然不需要更新任何东西。这本书的作者正在努力 告诉我们,假设父母的标准输出被重定向到一个文件并进行了一个fork调用。之后父进程正在进行。所以描述符现在是重复的,即文件偏移量也是共享的。现在,只要现在孩子将任何内容写入标准输出,写入的数据就会保存在重定向的文件中。 写入调用会自动增加偏移量。
现在说孩子退出了。 所以父母出来等待并在标准上写出一些东西(这是 重定向)。现在将放置父母的写呼叫输出 - &gt;在数据之后,由孩子写的。为什么 - &gt;因为在孩子写完后,偏移的当前值现在已经改变了。
Parent ( )
{
open a file for writing, that is get the
descriptor( say fd);
close(1);//Closing stdout
dup(fd); //Now writing to stdout means writing to the file
close(fd)
//Create a child that is do a fork call.
ret = fork();
if ( 0 == ret )
{
write(1, "Child", strlen("Child");
exit ..
}
wait(); //Parent waits till child exit.
write(1, "Parent", strlen("Parent");
exit ..
}
PL。看到上面的伪代码,打开的文件包含的最终数据将是ChildParent。所以你看到当孩子写完文件时,文件偏移量已经改变了,而且这个文件偏移量可以用于父母的写入调用,因为共享的是offest。
2.更新如何完成?由子显式,由OS隐式地,由文件描述符本身?在分叉后,我认为父母和孩子都走自己的路 有自己的文件描述符COPY。那么孩子如何更新偏移到父方?]
Now I think the answer is clear-> by the system call that is by the OS.
[3。当调用fork()时,我理解的是那个孩子得到了父母的COPY, 在这种情况下,文件描述符,并做它的事情。如果任何偏移更改为父和子共享的文件描述符,则只能因为描述符记住偏移本身。我是对的吗?]
这也应该是清楚的。用户文件表的条目指向文件表 表条目(包含偏移量)。
换句话说,系统调用可以从描述符中获取偏移量。