查错逻辑和代码输出包括dup()dup2()和fork()

时间:2019-06-22 10:45:48

标签: c fork dup

在操作系统考试中的一个问题中,我一直试图跟踪以下代码,但均未成功。

问题表明,这些假设是:

  • 至少STDOUT已打开。

  • foo.txt具有字符串“ abcdef” 6个字节

  • bar.txt具有字符串“ 567”

  • 答案中的输出为a567b。

有人可以跟踪这段代码并为我绘制文件描述符数组吗? 预先感谢..

main() {
    char buf[1024];
    int fd_foo = open("foo.txt", O_RDONLY);
    if (fd_foo != 4) {
        dup2(fd_foo, 4);

        close(fd_foo);
    }
    int fd_bar = open("bar.txt", O_RDONLY);
    if (fd_bar != 0) {
        close(0);
        dup(fd_bar);
        close(fd_bar);
    }
    switch (fork()) {
    case -1: exit(1);
    case 0:
        dup2(4, 5);
        close(4);
        execl("child", "child", (char *)NULL);
        break;
    default:
        wait(NULL);
        read(4, buf, 1);
        write(1, buf, 1);
    }
} // main

子源文件内容。

int main() {
    char buf[3];
    read(5, buf, 1);
    write(1, buf, 1);
    read(0, buf, 3);

    write(1, buf, 3);
}

1 个答案:

答案 0 :(得分:1)

让我们首先查看主文件中的main(),然后再查看子文件中main()的流程。

在开始之前,让我们回顾一下stdout(3) - Linux man page下在Linux下启动的C应用程序的标准文件句柄分配。

  

在程序启动时,与   流stdin,stdout和stderr分别为0、1和2。的   预处理程序符号STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO   在中用这些值定义。 (将freopen(3)应用于   这些流之一可以更改关联的文件描述符号   与流。)

接下来,我们将从DUP(2) Linux Programmer's Manual回顾dup()系统调用的作用。

  

dup()系统调用创建文件描述符oldfd的副本,   使用新编号最小的未使用文件描述符   描述符。

     

成功返回后,旧文件描述符和新文件描述符可能是   可互换使用。它们引用相同的打开文件描述   (请参阅open(2)),从而共享文件偏移量和文件状态标志;对于   例如,如果文件偏移量是通过在以下其中一项上使用lseek(2)修改的   文件描述符,其他文件的偏移量也会更改。

主文件中的main()如下所示,带有注释作为注释:

main() {
    char buf[1024];

    // open the file foo.txt and then dup() the file handle received from the open()
    // to be file handle number 4. Close the original file handle received.
    int fd_foo = open("foo.txt", O_RDONLY);
    if (fd_foo != 4) {
        dup2(fd_foo, 4);

        close(fd_foo);
    }
    // at this point the file handle 4 refers to the file foo.txt

    // open the file bar.txt and make sure that the file handle received is file handle
    // handle 0. if not then we close file handle 0 and dup the file handle to bar.txt
    // File handle 0 is Standard Input or STDIN.
    int fd_bar = open("bar.txt", O_RDONLY);
    if (fd_bar != 0) {
        close(0);
        dup(fd_bar);
        close(fd_bar);
        // Since dup() looks for the lowest numbered file descriptor and we have
        // just closed file descriptor 0, the result of dup() is to now have the
        // file bar.txt to also be accessed through file handle 0.
    }
    // at this point we have the following file assignments:
    //  - file handle 0 which was to Standard In is now file bar.txt
    //  - file handle 1 is to Standard Out
    //  - file handle 2 is to Standard Error
    //  - file handle 4 is to file foo.txt

    // now do a fork and the forked process will then execute the program whose
    // source code is in the child source file. the child process will
    // inherit our open file handles since we did not specify otherwise.
    switch (fork()) {
    case -1: exit(1);       // if error just exit.
    case 0:
        // we be the forked process so we now
        //  - dup file handle 4 to file handle 5 and close 4
        //  - load in the child process on top of ourselves
        //  - loaded child process will inherit our open file handles
        dup2(4, 5);
        close(4);
        execl("child", "child", (char *)NULL);
        // at this point we now jump to the source code of the child source file
        break;
    default:
        // we are the parent process so now lets just wait for the child to
        // finish. Once it has finished we will then do some final file I/O
        // then exit.
        // Note: while the forked process closed file handle 4, the parent process
        //       has not so file handle 4 is still valid for the parent.
        wait(NULL);
        read(4, buf, 1);     // read 1 character from file foo.txt
        write(1, buf, 1);    // write it to Standard Output
    }
} // main

已启动的子进程。

首先查看在子应用程序加载execl()之前,由分叉的子进程设置的子进程的环境。

  • 文件句柄5附加到打开文件foo.txt
  • 文件句柄0附加到打开文件bar.txt
  • 文件句柄1附加到标准输出

子文件的源代码为

int main() {
    char buf[3];
    read(5, buf, 1);    // read one character from foo.txt, the letter "a" from the string "abcdef"
    write(1, buf, 1);   // write it to Standard Out
    read(0, buf, 3);    // read three characters from bar.txt, the string "567"

    write(1, buf, 3);   // write them to Standard out
}

所有这些操作的结果是以下I / O。

  • 主进程启动,设置文件描述符,派生并加载子进程

  • 主进程等待子进程完成

  • 子进程从文件foo.txt中读取“ a”,而未读取“ bcdef”。

  • 子进程将“ a”写入标准输出

  • 子进程从文件bar.txt中读取“ 567”,没有任何未读内容

  • 子进程将“ 567”写入标准输出

  • 子进程退出

  • 主进程继续运行

  • 主进程从文件foo.txt中读取“ b”,而未读取“ cdef”

  • 主进程将“ b”写入标准输出

  • 主流程退出

这的结果是通过这两个合作过程将“ a567b”写入标准输出。它们共享相同的两个文件,尽管foo.txt被两个不同的文件描述符访问,并且共享相同的标准输出。