2016-12-28 2 views
0

У меня есть сервер C. Этот сервер должен обрабатывать несколько подключений и пользовательский ввод (через простой графический интерфейс ncurses). Поэтому я создал двух детей.
Моя проблема возникает, когда из главного меню пользовательского интерфейса мне нужно exit программы (затем завершите второй дочерний процесс , который обрабатывает соединения - из первого дочернего процесса).exit() программа от родителя до завершения дочернего процесса

Я попытаюсь объяснить себя с небольшой пример:

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; 
} 

Итак, как я могу kill, если я не знаю второго ребенка pid от первого ребенка?

+0

Ребенок не должен убивать своих родителей. Ваша проблема в дизайне. Вы должны использовать 'thread' или' select' (если вы используете linux, вы можете добавить 0, чтобы выбрать, а не переносить). – Stargateur

+0

'select'? Я использую его для обработки сокетов, что он имеет отношение к моей проблеме? Вы говорите об использовании 'select' способом, связанным с' fork'? – UrbiJr

+0

Я хочу сказать, что ваше использование 'fork()' не является целью этого. 'fork()' создать новый ** процесс **, который выполняет задание, которое вы хотите. Здесь вы хотите, чтобы ваш ребенок убил вашего родителя. Это не цель 'fork()'. Вы должны использовать поток, который обрабатывает ncurses. При переменной 'running', установите значение false при остановке потока. Основная программа остановится, когда эта переменная будет ложной. Или используйте select для обработки '0'. Таким образом, вы можете прочитать ввод пользователя с помощью ncurses. – Stargateur

ответ

3

В вашем коде вы повторно используете переменную pid, но, к счастью, ненужный 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() должна быть избыточна для ребенка, но родительский процесс может иметь детей не знают о некоторых обстоятельствах - маловероятные, но не невозможные обстоятельства.

Использование во втором ребенке pause() - просто написать код; это не полезно и поэтому не будет тем, что вы там пишете. Написание комментария /* action 1 */ - также фиктивный код; вы замените его кодом, который сделает что-то полезное. У меня, вероятно, были бы функции для вызова первого и второго потомков, а не вложения большого количества кода в 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 

Все это выглядит так, как можно было бы ожидать. Вы можете видеть смерть от SIGTERM в статусе 0x000F (SIGTERM обычно составляет 15 и 15 на macOS Sierra, хотя AFAIK не требует стандартных требований, что это 15). Вы можете видеть, что первый ребенок вышел из состояния со статусом 2 из 0x0200. Вы можете видеть, что родитель начал ждать, пока дети ничего не сделают. И вы можете увидеть методы отладки - обильная печать и в том числе PID большую часть времени.

+0

Да, я создал этот пример, чтобы избежать копирования моего длинного кода. Спасибо за ваше время. Я попробую это позже, и я дам вам знать. – UrbiJr

+0

Эй, @ JonathanLeffler Я только что протестировал его, и кажется, что он не работает ... По сути, он копирует * первый детский * процесс, я не знаю. Я думаю, что он выполняет свои инструкции 2 раза и одновременно. Я говорю это, потому что, когда я запускаю его, он повторяет printfs (* первого ребенка *), и когда он запускает ncurses, он в основном падает. – UrbiJr

+0

Однако второй ребенок * отлично работает, и теперь уже нет процесса зомби, который держит мой файл открытым. – UrbiJr

Смежные вопросы