gpt4 book ai didi

c - c linux中的管道连接3个进程以执行命令

转载 作者:行者123 更新时间:2023-11-30 19:10:01 25 4
gpt4 key购买 nike

我的任务是编写一个执行命令“ ls -l / bin / ?? | grep rwxr-xr-x | sort”的C程序。有3个子进程,其中每个子进程分别执行一个命令,并将结果通过管道发送到下一个子进程。我使用的是debian的瑞典语修改版本,因此错误消息是瑞典语,但我将翻译错误信息,其内容类似于:排序:失败至状态-:未知fileidentifier。

也许是我的管道无法正常工作,我不太了解close()命令。我很确定错误来自管道。如果有人可以运行该程序并获得英语错误消息,将不胜感激。

#include <stdio.h> 
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <string.h>


int main()
{
int ret;
int fds1[2], fds2[2], fds3[2];
char buf[20];

pid_t pid;

///initiating pipes
ret=pipe(fds1);

if(ret == -1){
perror("could not pipe");
exit(1);
}

ret=pipe(fds2);

if( ret == -1){
perror("could not pipe");
exit(1);
}

ret=pipe(fds3);

if (ret == -1){
perror("could not pipe");
exit(1);
}

pid=fork();

if(pid==-1){

fprintf(stderr,"fork failed");
exit(0);
}

if(pid==0){
///CHILD 1

close(1);
dup(fds1[1]);

close(fds1[0]);
close(fds1[1]);

close(0);
execlp("/bin/sh","bin/sh", "ls-l /bin/??", (char *)NULL);
}
else{
wait(0);
}

pid=fork();

if(pid==-1){

fprintf(stderr,"fork failed");
exit(0);
}

if(pid==0){

close(0);
dup(fds1[0]);

close(fds1[0]);
close(fds1[1]);

close(1);
dup(fds2[1]);

close(fds2[0]);
close(fds2[1]);

execlp("/usr/share/grep/", "grep", "rwxr-xr-x", NULL);
}
else{
wait(0);
}

close(fds1[0]);
close(fds1[1]);

pid=fork();

if(pid==-1){

fprintf(stderr,"fork failed");
exit(0);
}

if(pid==0){

close(0);
dup(fds2[0]);
close(fds2[0]);
close(fds2[1]);

execlp("sort", "sort", NULL);
}
else{
wait(0);
}

close(fds2[0]);
close(fds2[1]);
}

最佳答案

您的代码有几个问题,但是在讨论它们之前,让我向您介绍我最喜欢的预处理器宏之一:

#define DO_OR_DIE(x, s) do { \
if ((x) < 0) { \
perror(s); \
exit(1); \
} \
} while (0)


在适当的地方使用此宏可以通过替换所有样板错误检查来澄清代码。例如,这:


ret=pipe(fds1); 

if(ret == -1){
perror("could not pipe");
exit(1);
}



变得公正

DO_OR_DIE(pipe(fds1), "pipe");


这使得查看和关注代码的关键部分变得容易得多,并且键入起来也更加容易。结果,它也减少了跳过错误检查的诱惑,例如对 dup()的调用。



现在,关于您的代码。对我来说,它不仅表现出您现在在问题中描述的一种不当行为,还表现出三种:


它发出错误消息“ bin / sh:ls-1 / bin / ??:没有这样的文件或目录”。
它发出您描述的错误消息,“ sort:stat失败:-:错误的文件描述符”。
它不会终止。


第一条错误消息与您第一次 execlp()调用的参数中的多个问题有关。如果要启动外壳并指定要运行的命令(而不是从中读取命令的文件),则必须将 -c选项传递给它。此外,您还省略了 ls及其参数之间的强制空格。看起来您想要这样:

execlp("/bin/sh","sh", "-c", "ls -l /bin/??", (char *)NULL); 


暂时不考虑第二个问题,让我们谈谈终止失败的问题。您在这方面有几个问题,属于以下类别:


保持管道末端敞开,应确保其封闭
在错误的地方调用 wait()


在两个进程之间建立管道时,通常要确保管道的任一端都没有打开的文件描述符,只有一个进程所拥有的写端上有一个打开文件描述符,而一个进程所拥有的读端上没有打开的文件描述符。其他过程。每一端应在一个过程中完全打开一次。由于连接的进程总是从其父进程继承这些文件描述符,因此父进程必须关闭其副本(除非如果父进程本身是正在通信的进程之一,否则父进程希望保持打开状态)。

在关闭写入端上所有打开的文件描述符之前,管道读取端上的进程将看不到该管道上的EOF。如果未完全关闭管道的写入端,则正在运行诸如 grepsort之类的程序的子进程将无限期地挂起这些程序,例如 wait()execlp()

当读取管道的孩子也拥有该管道写端的副本(未使用),或者如果其兄弟姐妹拥有副本时,这可能是一个特别不正常的问题。

另外,流水线的全部要点是所涉及的进程可以同时运行。如果在启动下一个之后先 if,然后再启动下一个,则至少可以防止此类并发。但是,更糟糕的是,这也可能导致程序挂起,因为管道的缓冲区容量有限。如果子进程正在将输出写入管道,但没有人正在读取该输出,则管道的缓冲区可以填满容量,此时子进程将阻塞。如果父母在启动将排空管道的过程之前等待孩子完成,那么您将陷入僵局。因此,您应该首先启动管道中的所有进程,然后等待所有进程。

在您的代码中修复了此类问题后,我发现该程序为我发出了另一个错误:


  execlp:没有这样的文件或目录


(此消息的详细信息来自于我的修复程序的性质。)这尤其应引起关注,因为如果 execlp()失败,则它将在调用它的过程中返回。在您的情况下,控件将直接从您的 exit()语句中落入仅用于父级执行的代码中。因此,必须处理 _Exit()中的错误。至少,此后立即添加对 grep"/usr/share/grep/"的呼叫。

但是出什么事了?好吧,这次是 /。请注意,您指定了以 /usr/bin/grep执行的命令-尾随 execlp是错误的,并且路径本身是可疑的。在我的系统上,正确的路径是 dup(),但是由于我们使用的是 dup2(),它可以解析路径中的可执行文件,因此我们也可以完全省略该路径:

execlp("grep", "grep", "rwxr-xr-x", (char *) NULL);


等等!进行更正后,您的程序会为我运行。



附加建议:当您关心要复制的文件描述符号是什么(例如,当您尝试复制到标准流之一)时,请不要使用 。为此,使用 ,它的另一个优点是您不需要先关闭指定的文件描述符。

关于c - c linux中的管道连接3个进程以执行命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42009856/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com