gpt4 book ai didi

c - Linux shell 中的管道函数用 C 编写

转载 作者:太空狗 更新时间:2023-10-29 12:12:49 24 4
gpt4 key购买 nike

我的迷你 shell 程序接受管道命令,例如 ls -l | wc -l 并使用 excevp 来执行这些命令。

我的问题是,如果没有用于 execvp 的 fork(),管道命令运行良好,但 shell 随后终止。如果有 fork() for execvp,就会发生死循环。我无法修复它。

代码:

void run_pipe(char **args){
int ps[2];
pipe(ps);

pid_t pid = fork();
pid_t child_pid;
int child_status;

if(pid == 0){ // child process

close(1);
close(ps[0]);
dup2(ps[1], 1);

//e.g. cmd[0] = "ls", cmd[1] = "-l"
char ** cmd = split(args[index], " \t");

//if fork here, program cannot continue with infinite loop somewhere
if(fork()==0){
if (execvp(cmd[0],cmd)==-1){
printf("%s: Command not found.\n", args[0]);
}
}
wait(0);
}
else{ // parent process

close(0);
close(ps[1]);
dup2(ps[0],0);

//e.g. cmd[0] = "wc", cmd[1] = "-l"
char ** cmd = split(args[index+1], " \t");

//if fork here, program cannot continue with infinite loop somewhere
if(fork()==0){
if (execvp(cmd[0],cmd)==-1){
printf("%s: Command not found.\n", args[0]);
}
}
wait(0);
waitpid(pid, &child_status, 0);
}
}

我知道 excevp 需要 fork() 才能终止 shell 程序,但我仍然无法修复它。任何帮助将不胜感激,谢谢!


我应该如何让两个 child 平行?

pid = fork(); 
if( pid == 0){
// child
} else{ // parent
pid1 = fork();
if(pid1 == 0){
// second child
} else // parent

}

这是正确的吗?

最佳答案

是的,execvp() 用不同的程序替换了调用它的程序。如果你想在不结束执行生成的程序(即 shell)的情况下生成另一个程序,那么该程序必须 fork() 来创建一个新进程,并让新进程执行 execvp()

您的程序源代码表现出错误的并行性,可能使您感到困惑或反射(reflect)出更深层次的困惑。您以与 fork 后父进程的行为相同的方式构造 fork 的第一个子进程的行为,但是应该平行的是第一个子进程的行为和第二个子进程的行为 child 。

一个结果是您的程序有太多分支。初始进程应该恰好 fork 两次——针对它想要生成的每个子进程一次——并且两个子进程都不应该 fork ,因为它已经是一个专用于您要运行的命令之一的进程。然而,在您的实际程序中,第一个 child 确实会 fork 。那个案子可能是被 child 救出来的,也是 wait()ing for the grandchild,但它很乱,形式也很差。

另一个结果是,当您设置第二个 child 的文件描述符时,您在 fork 之前操纵父文件描述符,而不是在 fork 之后操纵子文件描述符。这些更改将持续存在于父进程中,我非常有信心这不是您想要的。这可能是 shell 似乎挂起的原因:当 run_pipe() 返回时(shell 的标准输入已更改为管道的读取端)。

此外,父进程应该在子进程都被 fork 后关闭管道的两端,这与子进程必须各自关闭它们不使用的一端的原因大致相同。最后,管道的每一端都有一个文件描述符的打开副本,一个在一个 child 中,另一个在另一个中。如果未能正确执行此操作,在某些情况下也可能导致挂起,因为您 fork 的进程可能不会终止。

以下是您希望程序执行的操作的摘要:

  • 原始进程设置管道。
  • 原始进程 fork 两次,每个命令一次。
  • 每个子进程都操纵自己的文件描述符,以使用管道的正确一端作为适当的标准 FD,并关闭管道的另一端。
  • 每个子进程都使用 execvp()(或该系列中的其他函数之一)来运行请求的程序
  • 父级关闭管道两端的文件描述符副本
  • parent 使用wait()waitpid() 收集两个 child 。

另请注意,您应该检查所有函数调用的返回值并为错误提供适当的处理

关于c - Linux shell 中的管道函数用 C 编写,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36156341/

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