Как убить только дочерний процесс на переднем плане? - PullRequest
0 голосов
/ 10 мая 2018

Я пытаюсь создать оболочку, и мне удалось закодировать большую часть функциональности, однако у меня небольшая проблема.

Скажем, я набираю firefox &.Firefox откроется как фоновый процесс.& активирует флаг BG, который заставляет родителя не ждать дочернего процесса.

Затем я набираю gedit.Gedit откроется как передний план процесса.Это означает, что в настоящий момент родитель ожидает завершения процесса.

На данный момент родитель имеет два процесса - firefox и gedit.Firefox не ожидался, и в настоящее время находится в фоновом режиме, в то время как мы в настоящее время ждем, пока Gedit завершит работу.Пока все хорошо.

Однако, если я решу отправить сигнал SIGINT нажатием ctrl-c, оба firefox и gedit закроются.Не хорошо, только gedit должно закрываться.

Вот моя функция обработчика сигнала:

pid_t suspended_process[10];
int last_suspended = -1;

void signal_handler(int signo){
    pid_t process = currentpid();

    // Catches interrupt signal
    if(signo == SIGINT){
        int success = kill(process, SIGINT);
    }

    // Catches suspend signal
    else if(signo == SIGTSTP){
        int success = kill(process, SIGTSTP);
        resuspended = 1;
        suspended_process[last_suspended+1] = process;
        last_suspended++;
    }
}

А вот часть в коде fork-exec, которая либо ожидает процесс, либопродолжает идти.

  else if(pid > 0){ //Parent
    current_pid = pid;

    // Waits if background flag not activated.
    if(BG == 0){
      // WUNTRACED used to stop waiting when suspended
      waitpid(current_pid, &status, WUNTRACED);

        if(WIFEXITED(status)){
          setExitcode(WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status)){
          printf("Process received SIGNAL %d\n", WTERMSIG(status));
        }
    }
  }

Это также происходит, если я заранее приостановил процесс.Например, я запускаю firefox и затем нажимаю ctrl-z, чтобы приостановить его.Затем я запускаю gedit и нажимаю ctrl-c, чтобы закрыть его.Сразу после этого, если я нажимаю fg для восстановления приостановленного firefox, он немедленно закрывается.

Я не могу найти способ только отправить сигнал SIGINT процессу переднего плана, он всегда отправляет сигнал ВСЕМдочерние элементы, кроме родителя, независимо от того, находятся ли они в фоновом режиме или приостановлены.

На всякий случай, это функция, которая инициализирует обработчик сигнала:

void init_handler(){
    struct sigaction sa;

    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;

    // If conditions for signal handling.
    // Also creates 2 signal handlers in memory for the SIGINT and SIGTSTP
    if(sigaction(SIGINT, &sa, NULL) == -1)
        printf("Couldn't catch SIGINT - Interrupt Signal\n");
    if(sigaction(SIGTSTP, &sa, NULL) == -1)
        printf("Couldn't catch SIGTSTP - Suspension Signal\n");
}

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

С помощью Антти мне удалось найти проблему. Я добавил одну строку в код fork-exec:

  else if(pid > 0){ //Parent
    current_pid = pid;

    if(setpgid(pid, pid) == 0) perror("setpid");

    // Waits if background flag not activated.
    if(BG == 0){
      // WUNTRACED used to stop waiting when suspended
      waitpid(current_pid, &status, WUNTRACED);

        if(WIFEXITED(status)){
          setExitcode(WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status)){
          printf("Process received SIGNAL %d\n", WTERMSIG(status));
        }
    }
  }

if(setpgid(pid, pid) == 0) perror("setpid");

Из того, что я мог собрать, setpgid устанавливает идентификатор группы процессов для процесса. Это означает, что в строке выше, Я устанавливаю pgid процесса с pid pid равным pid.

Я могу ошибаться, я до сих пор не до конца понимаю процесс, но причина, по которой это работает, заключается в том, что сигнал SIGINT отправляется процессу только с pgid pid. То есть раньше, поскольку я не устанавливал pgid каждого процесса, у них у всех был один и тот же pgid, следовательно, они все получили бы сигнал. Однако, как только я установлю pgid для каждого процесса, если я нажму CTRL-C в середине процесса переднего плана, он только выйдет из этого запущенного процесса.

По крайней мере, это то, что я мог собрать. Я до сих пор не до конца понимаю tcsetpgrp, особенно то, что я мог бы установить в качестве первого параметра, который является дескриптором файла. Добавление этой строки сразу после setpgid:

tcsetpgrp(STDIN_FILENO, pid)

Просто запускает всю программу в фоновом режиме, когда я exec запускаю команду. Вместо запуска firefox, и он появляется, я запускаю firefox, и вся программа получает stopped (согласно тому, что терминал говорит по крайней мере) . Я понятия не имею, почему это происходит.

Тем не менее, спасибо Антти!

0 голосов
/ 10 мая 2018

Это довольно просто, но это не сделано с сигналами .Вместо этого вы должны использовать функцию под названием группы процессов .Каждое отдельное задание (исполняемое или конвейерное или около того) будет отдельной группой процессов .Вы можете создавать группы процессов с setpgid (или в некоторых системах с setpgrp).Вы можете просто установить группу процессов дочернего процесса после fork, но до exec, а затем сохранить идентификатор группы процессов этого задания в таблице заданий.

Теперь группа процессов, которая находится впередний план устанавливается как активная группа процессов для терминала (/dev/tty оболочки) с помощью tcsetpgrp - это группа процессов, которая получит CTRL + C .Те группы процессов, которые принадлежат одному и тому же сеансу, но не относятся к группе, установленной на передний план с tcsetpgrp, будут полностью игнорировать CTRL + C .

...