Unix C 管道问题

Posted

技术标签:

【中文标题】Unix C 管道问题【英文标题】:Unix C pipe Question 【发布时间】:2010-10-12 20:58:25 【问题描述】:

我正在尝试了解管道的用法。

父进程将使用管道,如果父进程分叉,子进程将继承管道。所以现在我们有一个直接链接到子进程并且他们可以通信?

当我们开始关闭管道和重定向管道时,我迷路了。有人对关闭管道和重定向管道有很好的解释吗?

提前感谢您。

【问题讨论】:

【参考方案1】:

这是交易。

    管道是一种软件抽象,它有两端,每端一个 fd。无论您在一端写入什么(写入 fd),您都可以从另一端读取(读取 fd)。 使用管道的诀窍是用管道的读取 fd 切换某些进程的标准输入。现在,您可以写入管道的写入 fd,当其他进程读取他认为是他的标准输入的内容时,他实际上会读取管道的读取 fd(并获取您的数据)。 对于双向通信,你可以得到另一个管道,并用管道的write fd切换进程的stdout。现在,无论进程向其标准输出写入什么内容,您都可以读取管道的 read fd。

这是它的样子:

P1=[WR FD]===========[RD FD]=[STDIN]=P2
P1=[RD FD]===========[WR FD]=[STDOUT]=P2

P1 and P2 are processes. And "===" depict the pipes.

您的问题是关于关闭和重定向的。当您执行我之前提到的切换时,它就会发挥作用。假设您通过使用 pipe() 系统调用获得了一个管道。

int fd[2];
pipe(fd);

现在您创建一个子进程,并在该进程中执行切换,如下所示:

if (!fork()) // 0 return value indicates that you are in the child process

    dup2(fd[0], 0); // dup2 first closes 0 in the child process, then duplicates
                    // fd[0] as 0; keep in mind that 0 is stdin and fd[0] is the
                    // read end of the pipe

    exec(something); // start the new process, which when it reads stdin, will be
                     // reading the pipe instead

【讨论】:

【参考方案2】:

见Pipeline。

简单示例

ls -l | less

在此示例中,ls 是 Unix 目录列表器,而 less 是具有搜索功能的交互式文本分页器。管道允许用户上下滚动可能无法在屏幕上显示的目录列表。

以编程方式创建管道

可以在程序控制下创建管道。 Unix pipe() 系统调用要求操作系统构造一个新的匿名管道对象。这会在进程中产生两个新的打开文件描述符:管道的只读端和只写端。管道末端似乎是正常的匿名文件描述符,只是它们无法查找。

为避免死锁和利用并行性,具有一个或多个新管道的 Unix 进程通常会调用 fork() 来创建新进程。然后,每个进程将在生成或使用任何数据之前关闭它不会使用的管道末端。

【讨论】:

【参考方案3】:

您使用dup 系列系统调用将新文件描述符更改为前三个系统调用之一。

int new_stdout = open("filename", O_WRONLY);
/* ... error check ... */
if (!fork()) 
    /* in child */
    dup2(new_stdout, 1);
    execve("program", argv, envp);

这使得“文件名”将其标准写入“文件名”。

【讨论】:

【参考方案4】:

子进程继承父进程的打开描述符。默认的是标准输入、标准输出、标准错误。它们没有为孩子提供直接与父母交流的方式,因为他们实际上是在控制台上。所以通常你关闭或重定向那些,这样你就不会污染父级的控制台 I/O。

但是,如果您打开了一对管道,则可以使用子进程继承的它们的描述符在两个进程之间进行双向通信。每个方向使用一个描述符。

【讨论】:

【参考方案5】:

我们经常使用管道。正如 Amardeep 所说,子进程继承了父进程的描述符,包括管道。

这是 2 个命令的示例。我不确定我的 n 个命令的算法:-)

void do__pipe(char** cmd1, char** cmd2)

    int fd[2]; /* fd[0] for reading fd[1] for writting */

    if (pipe(fd) == -1)
    
       perror("pipe");
    

    switch (fork())
    
       case -1:
      perror("fork"); exit (1);
       case 0:
      close (fd[0]);
      dup2 (fd[1], STDOUT_FILENO);
      close (fd[1]);
      execvp (cmd1[0], cmd1);
      exit (1);
    

    dup2(STDIN_FILENO, STDIN_FILENO);

    switch (fork())
    
       case -1:
      perror("fork (2)"); exit (1);
       case 0:
      close (fd[1]);
      dup2 (fd[0], STDIN_FILENO);
      close (fd[0]);
      execvp (cmd2[0], cmd2);
      exit (1);
    u

    wait((int*)NULL);

这段代码取自我为学习而编写的一个小 shell,所以 char** cmd1 可能类似于 ["cat", "/etc/passwd"],而 cmd2 可能是 ["wc", "-l"]

【讨论】:

以上是关于Unix C 管道问题的主要内容,如果未能解决你的问题,请参考以下文章

unix 管道是不是仅限于在 2 个进程之间使用?

[Linux 高并发服务器] 管道

Unix管道的有效调试技术?

Unix上用C程序实现pipe管道命令“ | “(pipe,fork,dup2,close,execl,重定向)

如何跨(计算)节点使用 unix 管道?

C Minishell - 需要为管道实现杀死僵尸