Unix C管道问题

4
我正在尝试理解管道的用法。
父进程将创建管道,如果父进程进行分叉,子进程将继承该管道。现在我们拥有了与子进程的直接链接,它们可以通信?
当我们开始关闭管道和重定向管道时,我感到困惑。有没有人能够对关闭管道和重定向管道给出一个很好的解释?
提前致谢。
5个回答

6

以下是内容翻译:

  1. 管道是一个软件抽象,有两个端点,每个端点都是fd。你在一个端点上写入(write fd),就可以从另一个端点上读取(read fd)。
  2. 使用管道的技巧是将某个进程的stdin与管道的read fd交换。现在,你可以向管道的write fd写入数据,当另一个进程读取他认为是自己的stdin时,实际上它读取了管道的read fd(并获取了你的数据)。
  3. 为了进行双向通信,你可以再获取一个管道,并将进程的stdout与管道的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
}

4

请参见管道

Simple example

ls -l | less

In this example, ls is the Unix directory lister, and less is an interactive text pager with searching capabilities. The pipeline lets the user scroll up and down a directory listing that may not fit on the screen.

Creating pipelines programmatically

Pipelines can be created under program control. The Unix pipe() system call asks the operating system to construct a new anonymous pipe object. This results in two new, opened file descriptors in the process: the read-only end of the pipe, and the write-only end. The pipe ends appear to be normal, anonymous file descriptors, except that they have no ability to seek.

To avoid deadlock and exploit parallelism, the Unix process with one or more new pipes will then, generally, call fork() to create new processes. Each process will then close the end(s) of the pipe that it will not be using before producing or consuming any data.

alt text


3
您可以使用dup系统调用系列将新的文件描述符更改为前三个系统调用之一。
int new_stdout = open("filename", O_WRONLY);
/* ... error check ... */
if (!fork()) {
    /* in child */
    dup2(new_stdout, 1);
    execve("program", argv, envp);

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

1

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

这里是两个命令的示例。我不确定对于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"]


1

子进程继承了父进程的打开描述符。默认的描述符是stdin、stdout和stderr。它们不能提供子进程直接与父进程通信的方式,因为它们实际上是到控制台的。所以通常你会关闭或重定向它们,这样就不会污染父进程的控制台I/O。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接