文件描述符分配问题

2020/05/09 操作系统

在 OSTEP 上看到一段有意思的代码:

// p4.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>

int main() {
    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
        open("./p4.error", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);

        char *myargs[3];
        myargs[0] = strdup("wc");
        myargs[1] = strdup("p4.c"); // 如果将 p4.c 改成不存在的文件,标准错误就会写入到 p4.error 文件中
        myargs[2] = NULL;

        execvp(myargs[0], myargs);
    } else {
        int rc_wait = wait(NULL);
    }

    return 0;
}

以上程序执行结果如下:

$ gcc p4.c
$ ./a.out
$ cat p4.output
      31      69     681 p4.c

可以发现以上程序并没有显式的将结果输出到屏幕,而是写入到 p4.output 文件了;同理,将程序中的 p4.c 改成不存在的文件,错误信息会写入到 p4.error 文件中。

跟以下 fprintf 这种显式指定输出文件的方式不一样,以上程序并没有显式指定标准输出为 p4.output、标准错误为 p4.error。

#include <stdio.h>

int main() {
    FILE *fp = fopen("Hello", "w");
    fprintf(fp, "Hello world");

    return 0;
}

这是由于 STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO 的默认值分别为 0,1,2。而 Unix 系统从 0 开始查找自由的文件描述符,当 close(STDOUT_FILENO) 之后,1 变得不可用的,而通过 open() 调用打开 p4.output 文件后,该文件描述符就会对应首先被 close 的 STDOUT_FILENO;同理,p4.error 文件的文件描述符对应 STDERR_FILENO。因此,执行后续程序时,标准输出和标准错误就会分别重定向到 p4.output 和 p4.error 文件中。

Unix 管道就是以类似的方式实现的,但它用的是 pipe() 系统调用。这种情况下,一个进程的输出连接到一个内核 pipe(也就是队列);另一个进程的输入连接到同一个管道;因此,一个进程的输出会无缝地作为下一个(进程)的输入。

Search

    Table of Contents