C中的无限管道

时间:2015-05-10 01:36:22

标签: c linux piping

我不确定为什么waitpid()只是永久挂起。我假设在execvp()完成后,fork应该终止并返回到父进程。这不会发生。如果我注释掉waitpid(),初始输出是正确的,但程序开始出现意外行为。

修改
- execFirst()处理列表中第一个命令的执行 - execMid()处理列表中不是列表中第一个或最后一个命令的任何命令的执行
- execLast()处理列表中最后一个命令的执行

// Closes pipe
void closePipe(int *pPipe){
    close(pPipe[0]);
    close(pPipe[1]);

    return;
}

// Opens the read end of pPipe and closes the write end
// @Private
void readFromPipe(int *pPipe){
    dup2(pPipe[0],0);
    close(pPipe[1]);
    close(pPipe[0]);

    return;
}

// Opens the write end of pPipe and closes the read end
// @Private
void writeToPipe(int *pPipe){
    dup2(pPipe[1],1);
    close(pPipe[0]);
    close(pPipe[1]);

    return;
}
// @Private
void execLast(sSettings *pSettings_, sCommand *pCommand, int iPipe[]){
    readFromPipe(iPipe);
    _execvp(pSettings_, pCommand);
}

// @Private
void execMid(sSettings *pSettings_, sCommand *pCommand, int iPipe1[], int iPipe2[]){
    readFromPipe(iPipe1);
    writeToPipe(iPipe2);
    _execvp(pSettings_, pCommand);
}

// @Private
void execFirst(sSettings *pSettings_, sCommand *pCommand, int iPipe[]){
    writeToPipe(iPipe);
    _execvp(pSettings_, pCommand);
}

void execIndefDepthPipe(sSettings *pSettings_, sCommandList *pCommandList_){
    int iPipe1[2];
    int iPipe2[2];

    int iStatus, iProcessId, iNumCommands = pCommandList_->iSize;
    _pipe(pSettings_, iPipe1);
    _pipe(pSettings_, iPipe2);

    sCommand *pCommand;
    do{
        pCommand    = popHeadNode(pCommandList_);
        iProcessId  = _fork(pSettings_);

        if(iProcessId){ // If Parent
            // continue; // Do nothing
            waitpid(iProcessId, &iStatus, WUNTRACED); // It waits forever. I don't know why.
        }
        else { // Execute the commands as a child
            if(pCommand != NULL) {
                if(pCommandList_->iSize == (iNumCommands-1)) { // Exec First
                    //closePipe(iPipe2);
                    execFirst(pSettings_, pCommand, iPipe1);
                }
                else if(pCommandList_->iSize == 0) { // Exec Last
                    closePipe(iPipe1);
                    execLast(pSettings_, pCommand, iPipe2);
                }
                else { // Exec mid
                    execMid(pSettings_, pCommand, iPipe1, iPipe2);
                }
            }
            _exit(0);
        }
    } while(pCommand != NULL);

    return;
}

1 个答案:

答案 0 :(得分:0)

  

我的假设是,在execvp()完成后,forks应该终止并返回到父进程

如果你的意思是我认为你的方式,那你就错了。 exec函数族不会破坏子进程。他们将在子进程中执行的程序替换为另一个,但过程保持不变。特别是,在waitpid 调用的程序终止之前,该进程的exec不会返回。

如果你只是创建一个子进程而没有来自父进程的进一步帮助就可以运行完成,那就没问题了。但是你正试图建立一个管道。在所有完成之前,您不能指望管道中的任何进程终止。您尤其不能指望管道中的第一个进程在第二个进程已经创建之前终止。但这正是你的代码所做的:它等待第一个进程在创建第二个进程之前终止 - 同时,第一个进程已经填满了它的管道并等待第二个进程在它之前读取一些数据可以继续死锁。

您需要做的是在等待任何之前创建所有进程。您已经拥有了所需的两个循环之一。而不是在您执行的地方调用waitpid,而是将进程ID保存在数据结构中并转到下一次迭代。然后,在使用完整个“命令列表”后的第二个循环中,在循环中调用waitpid,而不用指定要等待的任何特定进程ID ;当每个进程终止时,从保存它们的数据结构中删除它的ID;当数据结构为空时,你就完成了。

此外,您需要检查每个系统调用以查找错误,close除外。 (POSIX中的一个错误是close被允许失败;如果传递了一个首先未打开的文件描述符号,Unix的合理实现只返回错误,我还没有遇到一个程序需要关心那个。如果你需要确保数据已经到达磁盘,你需要在 fsync之前调用close ;但是要做管道上的fsync毫无意义。)

当我在这里时,对良好编程风格的一些观察:(1)如果您停止使用匈牙利语前缀,您的代码将更容易阅读。 (1a)标识符末尾的无意义下划线按惯例保留用于宏内定义的变量,C没有宏观卫生。 (1b)具有下划线的开始的函数名称由C标准保留用于C库的内部使用; fork周围的包装应该命名为forkWithSettings - 更具描述性的东西会更好,但不知道这个设置对象中的所有内容我无法提出任何建议。 (2)在你用花括号的东西的宏观方案中并没有那么重要,但是对于Ghu的爱,选择一种风格并在整个文件中一致地使用它。