管道 bash 命令 echo 和 bc 进入 C 程序
Pipeline bash commands echo and bc into a C program
我正在尝试编写一个小 C 程序来实现两个 bash 命令的管道:echo $arithmeticOperation |公元前
$arithmeticOperation 是一个字符串作为输入。
程序在执行第一个命令时运行良好,但是当我 运行 第二个命令时,我得到了正确的输出,但是执行 bc 的子进程仍然卡住,阻止子进程结束。
所以在这一行中父进程被阻塞了:
waitpid(pid2,NULL,0);
您认为问题出在哪里?
抱歉,如果我问错了问题,这是我的第一个问题。谢谢
#define SYSCALL(r,c,e) if((r=c)==-1) { perror(e);exit(EXIT_FAILURE);}
int main(){
char buf[128];
int pfd[2],err;
pid_t pid1,pid2;
SYSCALL(err,pipe(pfd),"pipe");
switch (pid1=fork()) {
case -1: { perror("fork"); exit(EXIT_FAILURE);}
case 0 : {
scanf("%s",buf);
SYSCALL(err,dup2(pfd[1],1),"dup");
close(pfd[1]);
close(pfd[0]);
execl("/bin/echo","echo",buf,(char *)NULL);
return 1;
}
}
switch (pid2=fork() ){
case -1 : { perror("fork"); exit(EXIT_FAILURE);}
case 0 : {
SYSCALL(err,dup2(pfd[0],0),"dup");
close(pfd[1]);
close(pfd[0]);
// execl("/usr/bin/bc","bc",(char *)NULL);
execlp("bc","bc",(char *)NULL);
return 1;
}
}
printf("waiting . . . \n");
waitpid(pid1,NULL,0);
printf("wait\n");
waitpid(pid2,NULL,0);
close(pfd[1]);
close(pfd[0]);
return 0;
}
因此,如果我将数字“1+1”作为输入字符串,我会得到正确的输出,但是执行 bc 的进程永远不会退出
正如我在 中指出的那样,您的父进程必须在 等待 bc
之前关闭管道 的文件描述符(并且您已经同意这可以解决问题)。
这是因为 bc
为读取打开了管道,而父级为写入打开了管道,内核认为父级因此可以将数据发送到 bc
。它不会,但它可以。
管理管道时必须非常小心。您小心地避免了未关闭子项中足够多的文件描述符的常见问题。
经验法则:如果你
dup2()
管道的一端连接到标准输入或标准输出,同时关闭
返回的原始文件描述符
pipe()
尽早。
特别是,您应该在使用任何
exec*()
函数族。
如果您使用以下任一方式复制描述符,则该规则也适用
dup()
要么
fcntl()
F_DUPFD
我需要扩展它以涵盖父进程。
如果父进程不打算通过管道与其任何子进程通信,它必须确保它关闭管道的两端,以便其子进程可以在读取时接收到 EOF 指示(或获得 SIGPIPE 信号或写入错误),而不是无限期地阻塞。父级通常应该至少关闭管道的一端——程序在单个管道的两端读取和写入是非常不寻常的。
我正在尝试编写一个小 C 程序来实现两个 bash 命令的管道:echo $arithmeticOperation |公元前
$arithmeticOperation 是一个字符串作为输入。
程序在执行第一个命令时运行良好,但是当我 运行 第二个命令时,我得到了正确的输出,但是执行 bc 的子进程仍然卡住,阻止子进程结束。
所以在这一行中父进程被阻塞了: waitpid(pid2,NULL,0);
您认为问题出在哪里?
抱歉,如果我问错了问题,这是我的第一个问题。谢谢
#define SYSCALL(r,c,e) if((r=c)==-1) { perror(e);exit(EXIT_FAILURE);}
int main(){
char buf[128];
int pfd[2],err;
pid_t pid1,pid2;
SYSCALL(err,pipe(pfd),"pipe");
switch (pid1=fork()) {
case -1: { perror("fork"); exit(EXIT_FAILURE);}
case 0 : {
scanf("%s",buf);
SYSCALL(err,dup2(pfd[1],1),"dup");
close(pfd[1]);
close(pfd[0]);
execl("/bin/echo","echo",buf,(char *)NULL);
return 1;
}
}
switch (pid2=fork() ){
case -1 : { perror("fork"); exit(EXIT_FAILURE);}
case 0 : {
SYSCALL(err,dup2(pfd[0],0),"dup");
close(pfd[1]);
close(pfd[0]);
// execl("/usr/bin/bc","bc",(char *)NULL);
execlp("bc","bc",(char *)NULL);
return 1;
}
}
printf("waiting . . . \n");
waitpid(pid1,NULL,0);
printf("wait\n");
waitpid(pid2,NULL,0);
close(pfd[1]);
close(pfd[0]);
return 0;
}
因此,如果我将数字“1+1”作为输入字符串,我会得到正确的输出,但是执行 bc 的进程永远不会退出
正如我在 bc
之前关闭管道 的文件描述符(并且您已经同意这可以解决问题)。
这是因为 bc
为读取打开了管道,而父级为写入打开了管道,内核认为父级因此可以将数据发送到 bc
。它不会,但它可以。
管理管道时必须非常小心。您小心地避免了未关闭子项中足够多的文件描述符的常见问题。
经验法则:如果你
dup2()
管道的一端连接到标准输入或标准输出,同时关闭
返回的原始文件描述符
pipe()
尽早。
特别是,您应该在使用任何
exec*()
函数族。
如果您使用以下任一方式复制描述符,则该规则也适用
dup()
要么
fcntl()
F_DUPFD
我需要扩展它以涵盖父进程。
如果父进程不打算通过管道与其任何子进程通信,它必须确保它关闭管道的两端,以便其子进程可以在读取时接收到 EOF 指示(或获得 SIGPIPE 信号或写入错误),而不是无限期地阻塞。父级通常应该至少关闭管道的一端——程序在单个管道的两端读取和写入是非常不寻常的。