C中的不定管道
我不太清楚为什么waitpid()会永久挂起。我假设fork应该在execvp()完成后终止并返回到父进程。这并没有发生。如果注释掉waitpid(),则初始输出是正确的,但程序开始出现意外行为 编辑C中的不定管道,c,linux,piping,C,Linux,Piping,我不太清楚为什么waitpid()会永久挂起。我假设fork应该在execvp()完成后终止并返回到父进程。这并没有发生。如果注释掉waitpid(),则初始输出是正确的,但程序开始出现意外行为 编辑 -execFirst()处理列表中第一个命令的执行 -execMid()处理列表中不是列表中第一个或最后一个命令的任何命令的执行 -execLast()处理列表中最后一个命令的执行 // Closes pipe void closePipe(int *pPipe){ close(pPipe
-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;
}
我假设fork应该在execvp()完成后终止并返回到父进程
如果你是像我想的那样,那么你错了。exec
函数系列不会破坏子进程。它们将在子进程中执行的程序换成另一个,但进程保持不变。特别是,在由exec
调用的程序终止之前,该进程的waitpid
不会返回
如果您只是创建一个子进程,而该子进程将在没有父进程进一步帮助的情况下运行到完成,那么这将是很好的。但你想建立一条管道。您不能期望管道中的任何进程在全部完成之前终止。尤其不能期望管道中的第一个进程在创建第二个进程之前终止。但这正是您的代码所做的:它等待第一个进程终止,然后再创建第二个进程——同时,第一个进程已经填满了管道,正在等待第二个进程读取一些数据,然后才能继续。僵局
相反,您需要做的是在等待任何进程之前创建所有进程。您已经获得了所需的两个循环中的一个。不要在需要调用的地方调用waitpid
,而是将进程ID保存在数据结构中,然后转到下一个迭代。然后,在消耗完整个“命令列表”后的第二个循环中,在循环中调用waitpid
,而不指定要等待的任何特定进程ID;当每个进程终止时,从保存它们的数据结构中删除其ID;当数据结构为空时,就完成了
此外,您还需要检查每个系统调用是否有错误,除了close
。(POSIX中的一个错误是,close
被允许失败;Unix的合理实现只有在传递了一个一开始没有打开的文件描述符编号时才会返回错误,而且我还没有遇到需要关心这个问题的程序。如果您需要确保数据已经到达磁盘,则需要在之前调用fsync
)eclose
;但是在管道上执行fsync
是没有意义的。)
在这里,我对良好的编程风格进行了一些观察:(1)如果停止使用匈牙利语前缀,代码将更易于阅读。(1a)按照惯例,标识符末尾的无意义下划线保留给宏中定义的变量,C没有宏。(1b)C标准保留以下划线开头的函数名供C库内部使用;您的
fork
包装器应该命名为forkhithsettings
——更具描述性的东西会更好,但不知道这个设置对象中的所有内容,我不能提出任何建议。(2) 在你放花括号的宏伟计划中,这并不重要,但出于对Ghu的热爱,选择一种样式并在整个文件中始终如一地使用它。你能为我们提供一个带有示例输入的主函数,以便我们可以立即开始测试吗?您也可以发布您的结构sCommandList吗?只是为了清楚起见。另外,请确保检查所有系统调用是否有错误:pipe()、fork()、waitpid()等-这些调用中可能存在错误。仅保留以下划线开头,后跟大写字母或另一个下划线的标识符。@dreamlax所有以下划线开头的标识符都保留用作外部符号(例如函数)。您认为只有\u[\ u A-Z].
是无条件保留的,这是正确的。