Jos*_*osh 0 c fork exec waitpid
由于某种原因,此代码立即执行父命令,终止我的信号量并搞砸了我对其他程序的流控制.谁能告诉我为什么waitpid()不起作用?
//Create child processes
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork Failed.\n");
exit(1);
return;
}else if(pid==0){
if(execl("/home/tropix/hw11-2","/home/tropix/hw11-2",semarg,pipe_to_p3,pipe_to_p4,(char*)0)){
fprintf(stderr, "File Exexecution of hw11-2 failed.\n");
exit(1);
}
} else {
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork Failed.\n");
exit(1);
return;
} else if(pid==0){
if(execl("/home/tropix/hw11-3","/home/tropix/hw11-3",shmarg,semarg,pipe_from_p2,pipe_to_p5_1, (char*)0)){
fprintf(stderr, "File Execution of hw11-3 failed.\n");
exit(1);
}
} else {
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork Failed.\n");
exit(1);
return;
} else if (pid == 0){
if(execl("/home/tropix/hw11-4","/home/tropix/hw11-4",shmarg,semarg,pipe_from_p2_2,pipe_to_p5_2, (char*)0)){
fprintf(stderr, "File Execution of hw11-4 failed.\n");
exit(1);
}
} else {
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork Failed.\n");
exit(1);
return;
} else if (pid == 0){
if(execl("/home/tropix/hw11-5","/home/tropix/hw11-5",semarg,pipe_from_p3,pipe_from_p4,(char*)0)){
fprintf(stderr, "File Execution of hw11-5 failed.\n");
exit(1);
}
} else if (pid > 0) {
}
}
}
//Closing Pipes
close(pipe1[1]);
close(pipe2[1]);
close(pipe3[1]);
close(pipe4[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
close(pipe4[0]);
//Wait for child process completetion
waitpid(pid,NULL,0);
printf("Child Processes Complete.\n");
//Terminate Semaphores
semctl(sem_id,0,IPC_RMID);
//Terminate Shared Memory Segement
shmctl(shmid, IPC_RMID, NULL);
}
Run Code Online (Sandbox Code Playgroud)
}
谢谢!
编辑:好的,我替换了waitpid:
while (pid = waitpid(-1, NULL, 0)) {
if (errno == ECHILD) {
break;
}
}
Run Code Online (Sandbox Code Playgroud)
这让我成为那里的一部分.它不是立即执行父母控制,但它似乎永远不会执行.就你所谈到的管道问题而言,程序1(这一个)应该终止所有IPC元素,包括管道.如果有更好的方式,我很乐意听到它.
谢谢@Jonathan
您只需等待一个进程完成 - 而不是等待所有进程完成.这可能是一个问题.修复循环,waitpid()
直到它返回'不再是孩子'.
代码的结构留下了一些需要的东西 - 它是一只兔子的嵌套if; 麻烦!
我担心在执行其他命令之前你没有关闭足够的管道.如果命令不依赖于检测管道上的EOF,则可能没问题; 否则,你需要等待很长时间.
你需要一个像这样的功能:
#include <stdarg.h>
static void err_exit(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)
这简化了您的错误处理.如果您愿意,您还可以执行自动添加正在死亡的PID或触发退出的错误等操作.
我们还可以创建一个函数来运行另一个命令:
static pid_t run_command(const char *cmd, const char *shmarg, const char *semarg,
const char *fdarg1, const char *fdarg2)
{
pid_t pid = fork();
if (pid < 0)
err_exit("Failed to fork\n");
else if (pid == 0)
{
execl(cmd, cmd, shmarg, semarg, fdarg1, fdarg2, (char *)0);
err_exit("Failed to exec %s\n", cmd);
}
return pid;
}
Run Code Online (Sandbox Code Playgroud)
有了这些,我们可以将代码缩减到这个......
// Create child processes
pid_t pid1 = run_command("/home/tropix/hw11-2", semarg, pipe_to_p3, pipe_to_p4);
pid_t pid2 = run_command("/home/tropix/hw11-3", shmarg, semarg, pipe_from_p2, pipe_to_p5_1);
pid_t pid3 = run_command("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2);
pid_t pid4 = run_command("/home/tropix/hw11-5", semarg, pipe_from_p3, pipe_from_p4);
Run Code Online (Sandbox Code Playgroud)
嗯...其中一些有shmarg
,有些不 - 有意或无意的不一致?我们假设有意,所以我们需要两个版本的'run_command()':
static pid_t run_cmd4(const char *cmd, const char *shmarg, const char *semarg,
const char *fdarg1, const char *fdarg2)
{
pid_t pid = fork();
if (pid < 0)
err_exit("Failed to fork\n");
else if (pid == 0)
{
execl(cmd, cmd, shmarg, semarg, fdarg1, fdarg2, (char *)0);
err_exit("Failed to exec %s\n", cmd);
}
return pid;
}
static pid_t run_cmd3(const char *cmd, const char *semarg,
const char *fdarg1, const char *fdarg2)
{
pid_t pid = fork();
if (pid < 0)
err_exit("Failed to fork\n");
else if (pid == 0)
{
execl(cmd, cmd, semarg, fdarg1, fdarg2, (char *)0);
err_exit("Failed to exec %s\n", cmd);
}
return pid;
}
Run Code Online (Sandbox Code Playgroud)
然后:
// Create child processes
pid_t pid1 = run_cmd3("/home/tropix/hw11-2", semarg, pipe_to_p3, pipe_to_p4);
pid_t pid2 = run_cmd4("/home/tropix/hw11-3", shmarg, semarg, pipe_from_p2, pipe_to_p5_1);
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2);
pid_t pid4 = run_cmd3("/home/tropix/hw11-5", semarg, pipe_from_p3, pipe_from_p4);
Run Code Online (Sandbox Code Playgroud)
如果它是我的代码,变量的名称将更加统一 - 并且可能在数组中:
// Create child processes
pid_t pid1 = run_cmd3("/home/tropix/hw11-2", semarg, pipearg[0], pipearg[1]);
pid_t pid2 = run_cmd4("/home/tropix/hw11-3", shmarg, semarg, pipearg[2], pipearg[3]);
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipearg[4], pipearg[5]);
pid_t pid4 = run_cmd3("/home/tropix/hw11-5", semarg, pipearg[6], pipearg[7]);
Run Code Online (Sandbox Code Playgroud)
然后,最后,你有代码:
// Closing Pipes
close(pipe1[1]);
close(pipe2[1]);
close(pipe3[1]);
close(pipe4[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
close(pipe4[0]);
//Wait for child process completion
while (waitpid(0, NULL, 0) != 0)
;
printf("Child Processes Complete.\n");
// Remove Semaphores and Shared Memory
semctl(sem_id,0,IPC_RMID);
shmctl(shmid, IPC_RMID, NULL);
Run Code Online (Sandbox Code Playgroud)
我非常怀疑这些run_cmdX()
功能还需要关闭大量的管道 - 至少管道的每个描述符都不打算与子流程进行通信.
干净利落地组织起来比较棘手 - 但可以小心翼翼地完成.我可能会在一个数组中创建管道:
if (pipe(&pipes[0]) != 0 || pipe(&pipes[2]) != 0 ||
pipe(&pipes[4]) != 0 || pipe(&pipes[6]) != 0)
err_exit("Failed to create a pipe\n");
Run Code Online (Sandbox Code Playgroud)
然后我会创建一个函数:
void pipe_closer(int *pipes, int close_mask)
{
for (i = 0; i < 8; i++)
{
if ((mask & (1 << i)) != 0)
close(pipes[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
然后可以调用它来关闭不需要的管道:
pipe_closer(pipes, 0xFF); // Close them all - main()
pipe_closer(pipes, 0xFC); // All except 0, 1
pipe_closer(pipes, 0xF3); // All except 2, 3
pipe_closer(pipes, 0xCF); // All except 4, 5
pipe_closer(pipes, 0x3F); // All except 6, 7
Run Code Online (Sandbox Code Playgroud)
您只需安排正确的掩码与每个run_cmdN()
函数一起传递,并进行正确的调用.如果pipes
数组不是全局的,那么也需要传递.我还将看看如何整齐地编码数据,以便调用run_cmdN()
尽可能规则和对称.
Kernighan&Plauger的" 编程风格的元素 "(第二个Edn,1978;很难找到,我怀疑)包含许多华丽的引用.立即适用的是(大胆强调添加,原始斜体):
- 子程序调用允许我们总结参数列表中的不规则性,我们可以快速查看正在发生的事情.
- 子程序本身总结了代码的规律性,因此不需要使用重复的模式.
这可以被视为编程的DRY(不要重复自己)原则的一部分.该err_exit()
函数调用封装的代码三四线-打印和出口加括号,这取决于您的首选布局.这些run_command()
功能是DRY的主要案例.提议pipe_closer()
是另一个.