Linux shell中的管道函数用C

时间:2016-03-22 13:55:27

标签: c linux shell pipe execvp

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

我的问题是如果execvp没有fork(),则pipe命令运行良好,但shell会在之后终止。如果execvp有fork(),则会发生死循环。我无法解决它。

代码:

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程序,但我仍然无法修复它。任何帮助将不胜感激,谢谢!

我应该如何让两个孩子平行?

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

} 

这是对的吗?

1 个答案:

答案 0 :(得分:2)

是的,execvp() 替换用不同的程序调用它的程序。如果要生成另一个程序而不结束执行产生的程序(即shell),则该程序必须fork()创建一个新进程,并让新进程执行execvp()

您的程序源表现出错误的并行性,可能会让您感到困惑或反映出更深层次的混淆。您构造第一个子分叉的行为的方式与fork之后的父进程的行为相同,但并行的是第一个子进程的行为和第二个子进程的行为子。

一个结果是你的程序有太多的分叉。初始进程应该分叉两次 - 对于每个要生成的子进程一次 - 并且两个子进程都不应该进行分叉,因为它已经是一个专门用于运行其中一个命令的进程。然而,在你的实际程序中,第一个孩子会做分叉。这个案子很可能是由孩子wait()为孙子救出的,但它的形式很混乱。

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

此外,父进程应在子进程分叉后关闭管道的两个端,这或多或少与出现子进程必须关闭其未使用的结尾的原因相同。最后,管道的每一端都会有一个文件描述符的打开副本,一个在一个孩子中,另一个在另一个孩子中。在某些情况下,如果无法正确执行此操作也会导致挂起,因为您分叉的进程可能无法终止。

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

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

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