管道系统调用和 reading/writing 到 stdin/out 之间的区别

Difference between pipe system call and reading/writing to stdin/out

A pipe connects the stdout of one process to the stdin of another: https://superuser.com/a/277327

这是一个从 stdin 获取输入并打印它的简单程序:

int main( ) {
   char str[100];
   gets( str );
   puts( str );
   return 0;
}

我可以使用 unix 管道传递来自另一个进程的输入:

echo "hi" | ./a.out 

我的问题是,上面的简单代码和使用pipe()系统调用有什么区别?系统调用本质上是否在不写入终端的情况下完成相同的工作?有关管道的更多信息:https://tldp.org/LDP/lpg/node11.html

管道是一种利用 I/O 重定向的进程间通信机制。但是,管道并不参与所有 I/O 重定向。

由于子进程可能会从其父进程继承文件描述符,因此父进程可能会更改子进程的标准流指向的文件,而子进程并不知道。这是 I/O 重定向。

pipe() 系统调用允许您获取允许通过多个进程流式传输字节的通道(管道)的文件描述符(一个用于读取,一个用于写入)。这是一个父进程创建管道并且其子进程写入管道以便父进程可以从中读取的示例:

int main() {
    int fd[2];
    pipe(fd);
    int pid = fork();
    if (pid == 0) { // Child:
        close(fd[0]); // Close reading descriptor as it's not needed
        write(fd[1], "Hello", 5);
    } else { // Parent:
        char buf[5];
        close(fd[1]); // Close writing descriptor as it's not needed
        read(fd[0], buf, 5); // Read the data sent by the child through the pipe
        write(1, buf, 5); // print the data that's been read to stdout
    }
}

当 shell 遇到管道 (|) 运算符时,它确实使用了 pipe() 系统调用,但也做了额外的事情,以便重定向左操作数的标准输出和右操作数的标准输入到管道。这是 shell 对命令 echo "hi" | ./a.out 的作用的简化示例(请记住,在复制文件描述符时,它会复制到进程的打开文件结构中可用的第一个索引):

int main() {
    int fd[2];
    pipe(fd);
    int pid_echo = fork();
    if (pid_echo == 0) {
        // Close reading descriptor as it's not needed
        close(fd[0]);
        // Close standard output
        close(1);
        // Replace standard output with the pipe by duplicating its writing descriptor
        dup(fd[1]);
        // Execute echo;
        // now when echo prints to stdout it will actually print to the pipe
        // because now file descriptor 1 belongs to the pipe
        execlp("echo", "echo", "hi", (char*)NULL);
        exit(-1);
    }
    int pid_aout = fork();
    if (pid_aout == 0) {
        // Close standard input
        close(0);
        // Replace standard input with the pipe by duplicating its reading descriptor
        dup(fd[0]);
        // Execute a.out;
        // Now when a.out reads from stdin it will actually read from the pipe
        // because now file descriptor 0 belongs to the pipe
        execl("./a.out", "./a.out", (char*)NULL);
        exit(-1);
    }
}