我必须将一个C程序从OpenVMS迁移到Linux,现在生成子进程的程序有困难。生成一个子进程(fork工作正常),但execve失败(这是正确的,因为给出了错误的程序名)。
但是为了重置活动子进程的数量,我之后调用了一个不返回的wait()。当我通过ps查看进程时,我发现没有更多的子进程,但是wait()没有像我想象的那样返回ECHILD。
while (jobs_to_be_done)
{
if (running_process_cnt < max_process_cnt)
{
if ((pid = vfork()) == 0)
{
params[0] = param1 ;
params[1] = NULL ;
if ((cstatus = execv(command, params)) == -1)
{
perror("Child - Exec failed") ; // this happens
exit(EXIT_FAILURE) ;
}
}
else if (pid < 0)
{
printf("\nMain - Child process failed") ;
}
else
{
running_process_cnt++ ;
}
}
else // no more free process slot, wait
{
if ((pid = wait(&cstatus)) == -1) // does not return from this statement
{
if (errno != ECHILD)
{
perror("Main: Wait failed") ;
}
anz_sub = 0 ;
}
else
{
...
}
}
}
是否需要做任何事情来告诉wait-command没有更多的子进程? 使用OpenVMS,程序运行正常。
非常感谢您的帮助
答案 0 :(得分:2)
我建议不要在Linux上使用vfork
,因为fork(2)足够有效,这要归功于Linux内核中的懒惰copy-on-write技术。
您应该检查fork
的结果。除非它失败,否则会创建一个流程,wait
(或waitpid(2),或许WNOHANG
,如果您不想真正等待,但只是发现已经结束子进程...)不应该失败(即使子进程中的exec
函数失败,fork也会成功)。
您也可能小心使用SIGCHLD
信号,请参阅signal(7)。使用信号的防御方法是在信号处理程序中设置一些volatile sigatomic_t
标志,并在循环内测试并清除这些标志。回想一下,只有异步信号安全函数(并且它们中很少有)可以在信号处理程序中间接调用。另请阅读POSIX signals。
花些时间阅读Advanced Linux Programming,以便在您的脑海中获得更广阔的视野。不要尝试在POSIX上模仿OpenVMS,但要以POSIX或Linux的方式思考!
您可能希望在循环中始终waitpid
,也许(有时或始终)使用WNOHANG
。所以waitpid
不应仅在if (running_process_cnt < max_process_cnt)
的else部分调用,而应在循环的每次迭代中调用。
你可能想要编译所有警告&amp;调试信息(gcc -Wall -Wextra -g
)然后使用gdb
调试器。你也可以strace(1)你的程序(可能是-f
)
您可能想了解memory overcommitment。我不喜欢这个功能并且通常会禁用它(例如以root身份运行echo 0 > /proc/sys/vm/overcommit_memory
)。另见proc(5) - 这对于了解...非常有用。
答案 1 :(得分:1)
来自man vfork
:
子项不能从当前函数返回或调用exit(3),但可以调用_exit(2)
当致电exit()
(execv
之后)失败时,您不得致电vfork
- 您必须使用_exit()
。很可能仅凭这一点导致wait
无法返回的问题。
我建议您使用fork
代替vfork
。它使用起来更容易,更安全。
如果仅靠这一点无法解决问题,则需要进行一些调试或减少代码,直至找到原因。例如,以下内容应该不挂起:
#include <sys/wait.h>
int main(int argc, char ** argv)
{
pid_t pid;
int cstatus;
pid = wait(&cstatus);
return 0;
}
如果您可以验证此程序没有挂起,则必须是导致挂起的您的程序的某些方面。我建议在调用wait
之前和之后输入打印语句。