在 child 进程终止之前从 parent 退出()程序
exit() the program from parent before child process has terminated
我有一个C服务器。此服务器必须处理多个连接 和 用户的输入(通过简单的 ncurses GUI)。所以我创建了两个 childs.
我的问题来自用户界面的主菜单,我需要 exit
程序(然后终止第二个 child 进程 -处理连接-来自第一个 child 进程)。
我将尝试用一个小例子来解释我自己:
int main(){
pid_t pid;
int status1, status2;
if((pid = fork()) < 0){
perror("main fork failure:");
exit(1);
}
if(pid == 0){
pid = fork();
if(pid == 0){
/*
some stuff the second child does while
the first child is already running
*/
}
/* this is the first child */
int choice;
choice = menu();
switch(choice){
case 1:
break;
case 2:
/*
HERE I have to exit (from the first child first,
and from the program then): how can I kill the
second child that is running to prevent
zombie processes?
*/
// kill() which pid?
exit(2);
break;
}
wait(&status2);
}
wait(&status1);
return 0;
}
那么,如果我不知道第一个 child 中的第二个 child pid
,我怎么能 kill
呢?
在您的代码中,您重用了变量 pid
,但幸运的是,non-zero pid
是您需要发出信号的变量。
因此:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
extern int menu(void);
static void wait_for_pid(int pid)
{
int status;
int corpse;
while ((corpse = wait(&status)) >= 0 && corpse != pid)
printf("Unexpected child %d exited with status 0x%.4X\n", corpse, status);
if (corpse == pid)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
else
printf("Child %d died without its death being tracked\n", pid);
}
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
perror("main fork failure:");
exit(1);
}
if (pid == 0)
{
if ((pid = fork()) < 0)
{
perror("child fork failure:");
exit(1);
}
if (pid == 0)
{
pause(); /* Do nothing until signalled */
exit(0);
}
/* this is the first child */
int choice = menu();
switch (choice)
{
case 1:
/* action 1 */
break;
case 2:
kill(pid, SIGTERM);
exit(2);
/*NOTREACHED*/
}
wait_for_pid(pid);
exit(0);
}
wait_for_pid(pid);
return 0;
}
wait_for_pid()
函数中的循环对于 child 来说应该是矫枉过正,但是 parent 进程可能有 children 它在某些情况下不知道情况 - 不太可能但并非不可能的情况。
第二个child中pause()
的使用,就是简单的写一些代码;它没有用,因此不会是你在那里写的东西。写注释 /* action 1 */
同样是伪代码;您会用有用的代码替换它。我可能会调用第一个 child 和第二个 child 的函数,而不是在 main()
中嵌入很多代码。我假设它是如图所示编写的以创建 MCVE (Minimal, Complete, Verifiable Example);感谢您让代码保持简洁。
上面的代码未经测试,因为没有 menu()
函数。下面的代码有一个菜单功能——但不是很互动。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
extern int menu(void);
int menu(void)
{
printf("Dozing...\n");
sleep(1);
printf("Menu option 2 chosen\n");
return 2;
}
static void wait_for_pid(int pid)
{
int status;
int corpse;
int curpid = getpid();
printf("%d: waiting for children to die\n", curpid);
while ((corpse = wait(&status)) >= 0 && corpse != pid)
printf("%d: Unexpected child %d exited with status 0x%.4X\n", curpid, corpse, status);
if (corpse == pid)
printf("%d: Child %d exited with status 0x%.4X\n", curpid, corpse, status);
else
printf("%d: Child %d died without its death being tracked\n", curpid, pid);
}
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
perror("main fork failure:");
exit(1);
}
if (pid == 0)
{
if ((pid = fork()) < 0)
{
perror("child fork failure:");
exit(1);
}
if (pid == 0)
{
printf("Second child (%d) - pausing\n", (int)getpid());
pause(); /* Do nothing until signalled */
printf("Second child (%d) - awake despite no signal handling\n", (int)getpid());
exit(0);
}
/* this is the first child */
printf("First child (%d) - menuing\n", (int)getpid());
int choice = menu();
switch (choice)
{
case 1:
/* action 1 */
break;
case 2:
printf("kill(%d, SIGTERM)\n", pid);
kill(pid, SIGTERM);
wait_for_pid(pid);
exit(2);
/*NOTREACHED*/
}
/* Reached on menu choices != 2 */
/* Probably needs a loop around the menu() - end loop before wait_for_pid() */
wait_for_pid(pid);
exit(0);
}
wait_for_pid(pid);
return 0;
}
当 运行 时,示例输出序列为:
19489: waiting for children to die
First child (19490) - menuing
Dozing...
Second child (19491) - pausing
Menu option 2 chosen
kill(19491, SIGTERM)
19490: waiting for children to die
19490: Child 19491 exited with status 0x000F
19489: Child 19490 exited with status 0x0200
所有这些看起来都符合预期。您可以在状态 0x000F 中看到来自 SIGTERM 的死亡(SIGTERM 通常是 15,在 macOS Sierra 上是 15,尽管 AFAIK 没有标准要求它是 15)。您可以从 0x0200 看到第一个 child 以状态 2 正常退出。你可以看到 parent 在 children 做任何事情之前就开始等待了。您还可以看到调试技术——大量打印,大部分时间都包括 PID。
我有一个C服务器。此服务器必须处理多个连接 和 用户的输入(通过简单的 ncurses GUI)。所以我创建了两个 childs.
我的问题来自用户界面的主菜单,我需要 exit
程序(然后终止第二个 child 进程 -处理连接-来自第一个 child 进程)。
我将尝试用一个小例子来解释我自己:
int main(){
pid_t pid;
int status1, status2;
if((pid = fork()) < 0){
perror("main fork failure:");
exit(1);
}
if(pid == 0){
pid = fork();
if(pid == 0){
/*
some stuff the second child does while
the first child is already running
*/
}
/* this is the first child */
int choice;
choice = menu();
switch(choice){
case 1:
break;
case 2:
/*
HERE I have to exit (from the first child first,
and from the program then): how can I kill the
second child that is running to prevent
zombie processes?
*/
// kill() which pid?
exit(2);
break;
}
wait(&status2);
}
wait(&status1);
return 0;
}
那么,如果我不知道第一个 child 中的第二个 child pid
,我怎么能 kill
呢?
在您的代码中,您重用了变量 pid
,但幸运的是,non-zero pid
是您需要发出信号的变量。
因此:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
extern int menu(void);
static void wait_for_pid(int pid)
{
int status;
int corpse;
while ((corpse = wait(&status)) >= 0 && corpse != pid)
printf("Unexpected child %d exited with status 0x%.4X\n", corpse, status);
if (corpse == pid)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
else
printf("Child %d died without its death being tracked\n", pid);
}
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
perror("main fork failure:");
exit(1);
}
if (pid == 0)
{
if ((pid = fork()) < 0)
{
perror("child fork failure:");
exit(1);
}
if (pid == 0)
{
pause(); /* Do nothing until signalled */
exit(0);
}
/* this is the first child */
int choice = menu();
switch (choice)
{
case 1:
/* action 1 */
break;
case 2:
kill(pid, SIGTERM);
exit(2);
/*NOTREACHED*/
}
wait_for_pid(pid);
exit(0);
}
wait_for_pid(pid);
return 0;
}
wait_for_pid()
函数中的循环对于 child 来说应该是矫枉过正,但是 parent 进程可能有 children 它在某些情况下不知道情况 - 不太可能但并非不可能的情况。
第二个child中pause()
的使用,就是简单的写一些代码;它没有用,因此不会是你在那里写的东西。写注释 /* action 1 */
同样是伪代码;您会用有用的代码替换它。我可能会调用第一个 child 和第二个 child 的函数,而不是在 main()
中嵌入很多代码。我假设它是如图所示编写的以创建 MCVE (Minimal, Complete, Verifiable Example);感谢您让代码保持简洁。
上面的代码未经测试,因为没有 menu()
函数。下面的代码有一个菜单功能——但不是很互动。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
extern int menu(void);
int menu(void)
{
printf("Dozing...\n");
sleep(1);
printf("Menu option 2 chosen\n");
return 2;
}
static void wait_for_pid(int pid)
{
int status;
int corpse;
int curpid = getpid();
printf("%d: waiting for children to die\n", curpid);
while ((corpse = wait(&status)) >= 0 && corpse != pid)
printf("%d: Unexpected child %d exited with status 0x%.4X\n", curpid, corpse, status);
if (corpse == pid)
printf("%d: Child %d exited with status 0x%.4X\n", curpid, corpse, status);
else
printf("%d: Child %d died without its death being tracked\n", curpid, pid);
}
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
perror("main fork failure:");
exit(1);
}
if (pid == 0)
{
if ((pid = fork()) < 0)
{
perror("child fork failure:");
exit(1);
}
if (pid == 0)
{
printf("Second child (%d) - pausing\n", (int)getpid());
pause(); /* Do nothing until signalled */
printf("Second child (%d) - awake despite no signal handling\n", (int)getpid());
exit(0);
}
/* this is the first child */
printf("First child (%d) - menuing\n", (int)getpid());
int choice = menu();
switch (choice)
{
case 1:
/* action 1 */
break;
case 2:
printf("kill(%d, SIGTERM)\n", pid);
kill(pid, SIGTERM);
wait_for_pid(pid);
exit(2);
/*NOTREACHED*/
}
/* Reached on menu choices != 2 */
/* Probably needs a loop around the menu() - end loop before wait_for_pid() */
wait_for_pid(pid);
exit(0);
}
wait_for_pid(pid);
return 0;
}
当 运行 时,示例输出序列为:
19489: waiting for children to die
First child (19490) - menuing
Dozing...
Second child (19491) - pausing
Menu option 2 chosen
kill(19491, SIGTERM)
19490: waiting for children to die
19490: Child 19491 exited with status 0x000F
19489: Child 19490 exited with status 0x0200
所有这些看起来都符合预期。您可以在状态 0x000F 中看到来自 SIGTERM 的死亡(SIGTERM 通常是 15,在 macOS Sierra 上是 15,尽管 AFAIK 没有标准要求它是 15)。您可以从 0x0200 看到第一个 child 以状态 2 正常退出。你可以看到 parent 在 children 做任何事情之前就开始等待了。您还可以看到调试技术——大量打印,大部分时间都包括 PID。