[英]forking, waitpid problems in c
由於某種原因,此代碼會立即執行父母命令,從而終止我的信號量並破壞我對其他程序的流控制。 誰能告訴我為什么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);
}
}
謝謝!
編輯:好的,我用以下方式替換了waitpid:
while (pid = waitpid(-1, NULL, 0)) {
if (errno == ECHILD) {
break;
}
}
那使我成為那里的一部分。 它不會立即執行父母控制,但現在似乎永遠不會執行。 就您所討論的管道問題而言,程序1(該程序)應該終止所有IPC元素,包括管道。 如果有更好的方法,我很想聽聽。
謝謝@喬納森
您僅等待一個過程完成,而不必等待所有過程完成。 那可能是一個問題。 在waitpid()
上循環修復,直到返回“沒有更多的孩子”。
代碼的結構還有一些不足之處-它是嵌套if的兔子。 ick!
我擔心您在執行其他命令之前沒有關閉足夠的管道。 如果這些命令不依賴於檢測管道上的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);
}
這簡化了錯誤處理。 如果願意,您還可以執行一些操作,例如自動添加即將終止的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;
}
有了這些之后,我們可以考慮將您的代碼減少到此程度。
// 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);
嗯...其中有些帶有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;
}
接着:
// 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);
如果這是我的代碼,則變量名將更統一-並可能以數組形式出現:
// 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]);
然后,終於有了代碼:
// 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_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");
然后,我將創建一個函數:
void pipe_closer(int *pipes, int close_mask)
{
for (i = 0; i < 8; i++)
{
if ((mask & (1 << i)) != 0)
close(pipes[i]);
}
}
然后可以調用它關閉不需要的管道:
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_cmdN()
函數一起傳遞,並進行正確的調用即可。 如果pipes
數組不是全局數組,則也需要傳遞該數組。 我還將研究如何整齊地編碼數據,以便對run_cmdN()
的調用盡可能規則且對稱。
克尼根與普拉格(Kernighan&Plauger)的“ 編程風格要素 ”(第二版,1978年,我很難找到,我懷疑)包含許多引人注目的引號。 緊隨其后的是(粗體字加粗斜體)。
- 子例程調用使我們可以總結參數列表中的不規則之處 ,從而可以快速了解發生了什么情況。
- 子例程本身總結了代碼的規則性 ,因此無需使用重復的模式。
可以將其視為DRY(請勿重復自己)編程原理的一部分。 err_exit()
函數調用封裝三到四行代碼-打印,出口和花括號,具體取決於您的首選布局。 run_command()
函數是DRY的主要情況。 建議的pipe_closer()
是另一個。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.