Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 使用每个进程的等待返回代码创建多个管道_C_Unix_Pipe_Fork_Dup - Fatal编程技术网

C 使用每个进程的等待返回代码创建多个管道

C 使用每个进程的等待返回代码创建多个管道,c,unix,pipe,fork,dup,C,Unix,Pipe,Fork,Dup,我想使用execve、dup2、fork、waitpid&pipe函数在C中重现UNIX Shell的管道系统 右,例如,此命令:/bin/ls-l |/usr/bin/head-2 |/usr/bin/wc由以下程序复制: #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> in

我想使用execvedup2forkwaitpid&pipe函数在C中重现UNIX Shell的管道系统

右,例如,此命令:
/bin/ls-l |/usr/bin/head-2 |/usr/bin/wc
由以下程序复制:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int add_child_process(char **argv, int in, int out, char **envp)
{
  int pid;

  pid = fork();
  if (pid == 0)
    {
      if (in != 0)
    {
      dup2(in, 0);
    }
      if (out != 1)
    {
      dup2(out, 1);
    }
      if (execve(argv[0], argv, envp) == -1)
        perror("Execve failed");
    }
  else
    return (pid);


}

int main(int av, char **ag, char **envp)
{
  int in;
  int pipes[2];
  char *argv[3];
  int pids[3];

  /** Launch first process that read on original stdin (0) and write on out of pipe **/
  pipe(pipes);
  argv[0] = "/bin/ls";
  argv[1] = "-l";
  argv[2] = NULL;
  pids[0] = add_child_process(argv, 0, pipes[1], envp);
  close(pipes[1]);
  in = pipes[0];

  /** Launch second process that read on in of old pipe and write on out of new pipe **/
  pipe(pipes);
  argv[0] = "/usr/bin/head";
  argv[1] = "-2";
  argv[2] = NULL;
  pids[1] = add_child_process(argv, in, pipes[1], envp);
  close(in);
  close(pipes[1]);
  in = pipes[0];

  /** Launch last process that read on in of old pipe and write on original stdout (1) **/
  argv[0] = "/usr/bin/wc";
  argv[1] = NULL;
  pids[2] = add_child_process(argv, in, 1, envp);
  close(in);

  /** Wait for all process end to catch all return codes **/
  int return_code;
  waitpid(pids[0], &return_code, 0);
  printf("Process 0 return : %d\n", return_code);
  waitpid(pids[1], &return_code, 0);
  printf("Process 1 return : %d\n", return_code);
  waitpid(pids[2], &return_code, 0);
  printf("Process 2 return : %d\n", return_code);
} 
echo”/bin/ls-l |/usr/bin/head-2 |/usr/bin/wc“| bash

但是对于像ping google.com | head-2 | wc这样的阻塞命令,显示如下:

      2      16     145
并且仍然被封锁

以下是使用此命令更新的main:

int main(int av, char **ag, char **envp)
{
  int in;
  int pipes[2];
  char *argv[3];
  int pids[3];

  /** Launch first process that read on original in (0) and write on out of pipe **/
  pipe(pipes);
  argv[0] = "/bin/ping";
  argv[1] = "google.com";
  argv[2] = NULL;
  pids[0] = add_child_process(argv, 0, pipes[1], envp);
  close(pipes[1]);
  in = pipes[0];

  /** Launch second process that read on in of old pipe and write on out of new pipe **/
  pipe(pipes);
  argv[0] = "/usr/bin/head";
  argv[1] = "-2";
  argv[2] = NULL;
  pids[1] = add_child_process(argv, in, pipes[1], envp);
  close(in);
  close(pipes[1]);
  in = pipes[0];

  /** Launch last process that read on in of old pipe and write on original stdout (1) **/
  argv[0] = "/usr/bin/wc";
  argv[1] = NULL;
  pids[2] = add_child_process(argv, in, 1, envp);
  close(in);

  /** Wait for all process end to catch all return codes **/
  int return_code;
  waitpid(pids[0], &return_code, 0);
  printf("Process 0 return : %d\n", return_code);
  waitpid(pids[1], &return_code, 0);
  printf("Process 1 return : %d\n", return_code);
  waitpid(pids[2], &return_code, 0);
  printf("Process 2 return : %d\n", return_code);
} 
我真的不明白为什么会出现这种行为,例如在Bash中:
echo“ping google.com | head-2 | wc”| Bash
show
2 16 145
,不要卡住


如何处理阻塞命令?我需要根据上一个错误代码获取我的进程的所有返回代码以进行返回。

您的
add\u child\u process()
函数应该返回PID;没有。如果程序无法执行,它还应该在执行execve()之后进行错误处理

第一个解决方案-不够好 有了这些修复(以及其他各种更改,以超过我的最低编译警告级别,例如
#include
,以便在使用
printf()
之前有一个声明),我大致得到了预期的输出

我使用此代码(源文件
pc67.c
)测试:

这样,我就得到了输出:

$ ./pc67
       2      11      70
Process 0 return : 0
Process 1 return : 0
Process 2 return : 0
$
这11个字是“总计”和块数,然后是一行
ls-l
输出的9个字。我已在命令前后重新运行检查
ps
的输出;没有正在运行的游离进程

第二种解决方案——规避而非治愈 谢谢,但与Bash
echo“ping google.com | head-2 | wc”| Bash
不同,它仍然在等待中被阻止,并且不会终止程序

我不清楚你的替代命令行和你的问题有什么关系。然而,当您引入
ping google.com
而不是
ls-l
作为命令时,可能会发生很多事情。你不能这样合法地改变你问题的参数。这完全是一个新问题

在shell代理中,您没有指定程序的路径;你的代码无法处理这个问题。(如果您使用
execvp()
而不是
execve()
——当您只是中继继承的环境时,使用
execve()
是毫无意义的;环境仍然是继承的——那么到命令的路径将是无关的)

然而,使用
“/sbin/ping”
“google.com”
(在程序
pc23.c
的副本中)似乎是挂起的。从另一个终端查看设置,我看到:

$ ps -ftttys000
  UID   PID  PPID   C STIME   TTY           TIME CMD
    0 51551 51550   0 10:13AM ttys000    0:00.50 login -pf jleffler
  502 51553 51551   0 10:13AM ttys000    0:00.14 -bash
  502 54866 51553   0 11:10AM ttys000    0:00.01 pc23
  502 54867 54866   0 11:10AM ttys000    0:00.00 /sbin/ping google.com
  502 54868 54866   0 11:10AM ttys000    0:00.00 (head)
  502 54869 54866   0 11:10AM ttys000    0:00.00 (wc)
$
head
wc
进程都已终止(这些是僵尸的条目),但
ping
进程并未消亡。它似乎做的不多,但它也不会死

一段时间后,我得到:

$ ps -ftttys000
  UID   PID  PPID   C STIME   TTY           TIME CMD
    0 51551 51550   0 10:13AM ttys000    0:00.50 login -pf jleffler
  502 51553 51551   0 10:13AM ttys000    0:00.14 -bash
  502 54866 51553   0 11:10AM ttys000    0:00.01 pc23
  502 54867 54866   0 11:10AM ttys000    0:00.09 /sbin/ping google.com
  502 54868 54866   0 11:10AM ttys000    0:00.00 (head)
  502 54869 54866   0 11:10AM ttys000    0:00.00 (wc)
$
它设法使用了9个CPU秒。因此,在此上下文中,出于某种原因,
ping
不关注
SIGPIPE
信号

如何修复?这需要实验,可能更复杂。最简单的修复方法是添加对我有效的选项,如
-c
3
。我现在没有动力去寻找替代的修复方法

ping
的macOS手册页部分说明:

-c计数

发送(和接收)计数回显响应数据包后停止。如果未指定此选项,
ping
将一直运行,直到中断。如果此选项与
ping
sweep一起指定,则每次扫描将由计数数据包组成

推测“中断”一词的精确性是很有趣的。如果发送了实际(Control-C)中断或
SIGTERM
信号(
kill 54867
),则程序终止。奇怪的是,它有时会在SIGPIPE上停止,有时则不会

第三种解决方案-关闭文件描述符 进一步考虑,问题是代码没有关闭足够的文件描述符。这是管道未终止时最常见的问题。当
ls
作为第一个进程时,问题就隐藏了,因为
ls
会自动终止,关闭散乱的文件描述符。 更改为
ping
会暴露问题。这是代码的另一个修订版。它关闭
dup2()
描述符(oops;将self拍打在手腕上)。它还确保通过
add\u child\u process()
的新
tbc
(待关闭)文件描述符参数关闭另一个管道描述符。我还选择使用
execv()
而不是
execve()
,从
add\u child\u process()
函数中删除一个参数,并允许
int main(void)
,因为程序不注意其参数(这允许我丢失
assert()
,只要您没有使用
-DNDEBUG
或等效工具进行编译,它就可以“确保”参数计数和向量参数被“使用”

我还调整了命令构建代码,以便可以轻松地从
ping
添加/删除
-c
3
参数,或者从其他命令添加/删除参数

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

static
int add_child_process(char **argv, int in, int out, int tbc)
{
    int pid;

    pid = fork();
    if (pid == 0)
    {
        if (tbc >= 0)
            close(tbc);
        if (in != 0)
        {
            dup2(in, 0);
            close(in);
        }
        if (out != 1)
        {
            dup2(out, 1);
            close(out);
        }
        execv(argv[0], argv);
        abort();
    }
    return pid;
}

int main(void)
{
    int in;
    int pipes[2];
    char *argv[10];
    int pids[3];

    /** Launch first process that read on original stdin (0) and write on out of pipe **/
    pipe(pipes);
    int argn = 0;
    argv[argn++] = "/sbin/ping";
    //argv[argn++] = "-c";
    //argv[argn++] = "3";
    argv[argn++] = "google.com";
    argv[argn++] = NULL;
    pids[0] = add_child_process(argv, 0, pipes[1], pipes[0]);
    close(pipes[1]);
    in = pipes[0];

    /** Launch second process that read on in of old pipe and write on out of new pipe **/
    pipe(pipes);
    argn = 0;
    argv[argn++] = "/usr/bin/head";
    argv[argn++] = "-2";
    argv[argn++] = NULL;
    pids[1] = add_child_process(argv, in, pipes[1], pipes[0]);
    close(in);
    close(pipes[1]);
    in = pipes[0];

    /** Launch last process that read on in of old pipe and write on original stdout (1) **/
    argn = 0;
    argv[argn++] = "/usr/bin/wc";
    argv[argn++] = NULL;
    pids[2] = add_child_process(argv, in, 1, -1);
    close(in);

    /** Wait for all process end to catch all return codes **/
    int return_code;
    waitpid(pids[0], &return_code, 0);
    printf("Process 0 return : %d\n", return_code);
    waitpid(pids[1], &return_code, 0);
    printf("Process 1 return : %d\n", return_code);
    waitpid(pids[2], &return_code, 0);
    printf("Process 2 return : %d\n", return_code);
}
出现的
wc
输出和另一个输出之间有一个短的、亚秒的暂停;
ping
命令在尝试再次写入之前等待一秒钟。
13
的退出状态表明
ping
确实是由
SIGPIPE
信号造成的。前面的问题是,
ping
仍然打开管道的读取端,因此它没有获得
SIGPIPE
$ ps -ftttys000
  UID   PID  PPID   C STIME   TTY           TIME CMD
    0 51551 51550   0 10:13AM ttys000    0:00.50 login -pf jleffler
  502 51553 51551   0 10:13AM ttys000    0:00.14 -bash
  502 54866 51553   0 11:10AM ttys000    0:00.01 pc23
  502 54867 54866   0 11:10AM ttys000    0:00.00 /sbin/ping google.com
  502 54868 54866   0 11:10AM ttys000    0:00.00 (head)
  502 54869 54866   0 11:10AM ttys000    0:00.00 (wc)
$
$ ps -ftttys000
  UID   PID  PPID   C STIME   TTY           TIME CMD
    0 51551 51550   0 10:13AM ttys000    0:00.50 login -pf jleffler
  502 51553 51551   0 10:13AM ttys000    0:00.14 -bash
  502 54866 51553   0 11:10AM ttys000    0:00.01 pc23
  502 54867 54866   0 11:10AM ttys000    0:00.09 /sbin/ping google.com
  502 54868 54866   0 11:10AM ttys000    0:00.00 (head)
  502 54869 54866   0 11:10AM ttys000    0:00.00 (wc)
$
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

static
int add_child_process(char **argv, int in, int out, int tbc)
{
    int pid;

    pid = fork();
    if (pid == 0)
    {
        if (tbc >= 0)
            close(tbc);
        if (in != 0)
        {
            dup2(in, 0);
            close(in);
        }
        if (out != 1)
        {
            dup2(out, 1);
            close(out);
        }
        execv(argv[0], argv);
        abort();
    }
    return pid;
}

int main(void)
{
    int in;
    int pipes[2];
    char *argv[10];
    int pids[3];

    /** Launch first process that read on original stdin (0) and write on out of pipe **/
    pipe(pipes);
    int argn = 0;
    argv[argn++] = "/sbin/ping";
    //argv[argn++] = "-c";
    //argv[argn++] = "3";
    argv[argn++] = "google.com";
    argv[argn++] = NULL;
    pids[0] = add_child_process(argv, 0, pipes[1], pipes[0]);
    close(pipes[1]);
    in = pipes[0];

    /** Launch second process that read on in of old pipe and write on out of new pipe **/
    pipe(pipes);
    argn = 0;
    argv[argn++] = "/usr/bin/head";
    argv[argn++] = "-2";
    argv[argn++] = NULL;
    pids[1] = add_child_process(argv, in, pipes[1], pipes[0]);
    close(in);
    close(pipes[1]);
    in = pipes[0];

    /** Launch last process that read on in of old pipe and write on original stdout (1) **/
    argn = 0;
    argv[argn++] = "/usr/bin/wc";
    argv[argn++] = NULL;
    pids[2] = add_child_process(argv, in, 1, -1);
    close(in);

    /** Wait for all process end to catch all return codes **/
    int return_code;
    waitpid(pids[0], &return_code, 0);
    printf("Process 0 return : %d\n", return_code);
    waitpid(pids[1], &return_code, 0);
    printf("Process 1 return : %d\n", return_code);
    waitpid(pids[2], &return_code, 0);
    printf("Process 2 return : %d\n", return_code);
}
$ ./pc73
       2      14     109
Process 0 return : 13
Process 1 return : 0
Process 2 return : 0
$