任何人都可以在fork()之后解释关于'文件描述符'的简单描述吗?

时间:2012-07-31 05:11:38

标签: c unix fork file-descriptor

在“Unix环境中的高级编程”,第2版,作者:W。Richard Stevens。

第8.3节fork函数。

以下是描述:

父项和子项共享相同的文件偏移量非常重要。

考虑一个分叉孩子的过程,然后等待孩子完成。假设两个进程都写入标准输出作为其正常处理的一部分。如果父级的标准输出重定向(可能是shell),则当子级写入标准输出时,子级必须更新父级的文件偏移量。

[1。这是什么意思?例如,如果父项的std输出被重定向到'file1',那么子项写入后子项应该更新什么? parent的原始std输出偏移或重定向输出(即file1)偏移?不能晚于,对吗?]

[2。更新是如何完成的?由子显式,由OS隐式地,由文件描述符本身?在fork之后,我认为父母和孩子走自己的路,并拥有自己的文件描述符COPY。那么孩子如何更新偏移到父方?]

在这种情况下,孩子可以在父母等待时写入标准输出;在孩子完成后,父母可以继续写入标准输出,知道其输出将附加到孩子写的任何内容。如果父级和子级没有共享相同的文件偏移量,则此类交互将更难实现,并且需要父级的明确操作。

如果父节点和子节点都写入相同的描述符,没有任何形式的同步,例如让父节点等待子节点,则它们的输出将被混合(假设它是在fork之前打开的描述符)。虽然这是可能的,但这不是正常的操作模式。

在fork之后处理描述符有两种正常情况。

  1. 父母等待孩子完成。在这种情况下,父级不需要对其描述符执行任何操作。当子进程终止时,子进程读取或写入的任何共享描述符都会相应地更新其文件偏移量。

  2. 父母和孩子都有自己的方式。这里,在fork之后,父关闭它不需要的描述符,并且子进行相同的操作。这样,既不会干扰对方的开放描述符。网络服务器通常就是这种情况。“

  3. [3。当调用fork()时,我理解的是那个子得到了父有什么的COPY,在这种情况下是文件描述符,并做了它的事情。如果任何偏移更改为父和子共享的文件描述符,则只能因为描述符记住偏移本身。我是对的吗?]

    对不起,我对这些概念不熟悉。

    有任何帮助吗?感谢。

2 个答案:

答案 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参考。
现在,在创建子项时,将为子项复制文件描述符表。 因此,文件表条目(对于该打开的描述符)中的引用计数增加,这意味着现在对同一文件表条目有两个引用。

此描述符现在在父项和子项中均可用,指向相同的文件表条目,因此共享偏移量。 现在有了这个背景,让我们看看你的问题,

  1. 这是什么意思?例如,如果父项的std输出被重定向到'file1',那么子项写入后子项应该更新什么?父的原始标准输出偏移量或 重定向输出(即file1)偏移量?不能是后来的,对吗?]
  2. 孩子显然不需要更新任何东西。这本书的作者正在努力 告诉我们,假设父母的标准输出被重定向到一个文件并进行了一个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, 在这种情况下,文件描述符,并做它的事情。如果任何偏移更改为父和子共享的文件描述符,则只能因为描述符记住偏移本身。我是对的吗?]

    这也应该是清楚的。用户文件表的条目指向文件表 表条目(包含偏移量)。

    换句话说,系统调用可以从描述符中获取偏移量。