【问题标题】:Pipe function in Linux shell write in CLinux shell中的管道函数用C编写
【发布时间】:2016-07-09 10:55:47
【问题描述】:

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

我的问题是,如果没有用于 execvp 的 fork(),管道命令运行良好,但 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 

} 

这是正确的吗?

【问题讨论】:

  • 你是fork()ing 两次。不清楚第一个孩子(第二个孩子的父母)在wait()s 之后对其孩子做了什么,因为它只是从这个函数返回。发布更多代码或解释为什么你fork() 两次。
  • 注意dup2()会自动关闭新的文件描述符,如果它已经打开,所以没有必要先close()那个FD。如果你知道 FD 是打开的,那么这样做并没有错,但首先显式关闭它确实会插入一个额外的函数调用,你应该检查它的返回值。

标签: c linux shell pipe execvp


【解决方案1】:

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

您的程序源代码表现出错误的并行性,这可能使您感到困惑或反映出更深层次的困惑。您构建第一个子进程的行为的方式与分叉后父进程的行为相同,但应该平行的是第一个子进程的行为和第二个子进程的行为孩子。

一个结果是你的程序有太多的分叉。初始进程应该恰好分叉两次——对于它想要生成的每个子进程一次——并且两个子进程都不应该分叉,因为它已经是一个专用于您要运行的命令之一的进程。但是,在您的实际程序中,第一个孩子会分叉。那个案子可能是孩子也wait()ing为孙子救了,但它是凌乱和糟糕的形式。

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

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

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

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

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

【讨论】:

  • 我已经修改了问题。请再看一遍。非常感谢。
猜你喜欢
  • 2016-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-03
  • 1970-01-01
  • 2011-10-01
  • 1970-01-01
相关资源
最近更新 更多