Как предотвратить распространение SIGINT в дочернем процессе на родительский процесс и его уничтожение?

Недавно я научился делать всю эту штуку fork/exec/wait и в итоге написал некоторый код, который выглядел урезанным следующим образом:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
  pid_t x = fork();
  if (x == 0) {
    execlp("sqlite3", "sqlite3");
    printf("exec failed\n");
  } else {
    wait(NULL);
    printf("Hi\n");
  }
}

Это работает довольно хорошо и фактически приводит к открытию оболочки sqlite3, но есть одна проблема. Если вы Ctrl + C выходите из оболочки sqlite3, то она также завершает родительский процесс, и строка printf("Hi\n") никогда не запускается.

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

Я попытался исправить это, пытаясь вызвать setpgid следующим образом:

int main() {
  pid_t x = fork();
  if (x == 0) {
    setpgid(getpid(), getpid()); // <-- Added in this line
    execlp("sqlite3", "sqlite3");
    printf("exec failed\n");
  } else {
    wait(NULL);
    printf("Hi\n");
  }
}

который работал нормально, но это сломало подсказку sqlite3. Более того, Ctrl + C все равно убил родителя в этом случае. Я решил еще больше сузить круг, а потом нашел кое-что странное и запутался. Если просто поместить вызов getpid() в вилку, вызов execlp() потерпит неудачу.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
  pid_t x = fork();
  if (x == 0) {
    getpid(); // <--- just adding this in makes it fail
    execlp("sqlite3", "sqlite3");
    printf("exec failed\n");
  } else {
    wait(NULL);
    printf("Hi\n");
  }
}

и даже не только для sqlite3, но и для простых вещей, таких как execlp("ls", "ls") (хотя это может быть не связано с исходным вопросом).

Я немного не в себе, и мне было интересно, может ли кто-нибудь указать мне правильное направление. Я не совсем уверен, что делать дальше.

  • Я на OSX 10.11 и компилирую с clang.
  • clang --version: Apple LLVM версии 8.0.0 (clang-800.0.42.1)

person m0meni    schedule 07.11.2016    source источник
comment
Если вы нажмете Ctrl + C вне оболочки sqlite3, то он также завершит родительский процесс, потому что Ctrl+C заставит Bash отправить SIGINT процессу, который он породил (т.е. вашей программе C), а не какому-либо другому процесс в это время читает со стандартного ввода.   -  person Colonel Thirty Two    schedule 08.11.2016
comment
@ColonelThirtyTwo хм, понятно! Итак, мне нужно отправить Ctrl + C на передний план? Кроме того, почему это не так, когда я делаю execlp("bash", "bash")? Это прекрасно справляется с Ctrl + C.   -  person m0meni    schedule 08.11.2016
comment
Вы уже есть. Что касается bash, ваш процесс является процессом переднего плана, а sqlite3 — это просто какой-то другой процесс, который использует тот же канал стандартного ввода. Вы можете обработать ctrl+c в своей программе C и отправить sqlite3 SIGTERM или использовать Ctrl+D для отправки EOF на стандартный ввод, который sqlite3 подберет и интерпретирует как конец потока стандартного ввода.   -  person Colonel Thirty Two    schedule 08.11.2016
comment
@ColonelThirtyTwo эй, я добавил быстрое редактирование к исходному комментарию, но повторюсь. Также почему этого не происходит, когда я делаю execlp("bash", "bash")? Это прекрасно справляется с Ctrl + C.   -  person m0meni    schedule 08.11.2016
comment
Сначала исправьте наихудшую ошибку: это должно быть execlp("bash", "bash", NULL); -- NULL требуется для завершения списка аргументов для execl(), execlp() и execle(). Затем, если вы хотите обрабатывать Ctrl+C в родительском процессе, просто установите обработчик сигнала, который будет пересылать сигнал SIGINT дочернему процессу.   -  person Nominal Animal    schedule 08.11.2016
comment
Я предполагаю, что bash делает какую-то специальную настройку с терминалом для захвата и интерпретации Ctrl + C, чтобы он мог отправлять SIGINT своим дочерним элементам. Дочерний процесс bash перезаписывает настройку родительского.   -  person Colonel Thirty Two    schedule 08.11.2016
comment
@ColonelThirtyTwo Мне бы очень не хотелось не отдать вам должное за все ваши комментарии. Не могли бы вы просто скопировать и вставить их все в поле для ответов целиком, чтобы я мог проголосовать и принять?   -  person m0meni    schedule 08.11.2016
comment
Разве сигналы клавиатуры не отправляются всей группе процессов переднего плана, а не только одному процессу?   -  person Barmar    schedule 08.11.2016
comment
@Barmar после цепочки комментариев выше и моих собственных экспериментов, я так думаю. Я бы пометил этот вопрос как решенный каким-то образом, но никто не ответил.   -  person m0meni    schedule 08.11.2016
comment
Вы можете просто удалить его, если он больше не нуждается в ответе.   -  person Barmar    schedule 08.11.2016
comment
@Barmar хорошо, я связался с ним отсюда: stackoverflow.com/questions/40477988/, и, кроме того, я предпочитаю создавать вики сообщества с содержимым комментарии, но я ждал, чтобы увидеть, опубликуют ли они ответ сами, прежде чем я сделаю это для них.   -  person m0meni    schedule 08.11.2016


Ответы (1)


вы можете поймать SIGINT или написать обработчик сигнала. используйте код ниже:

signal(SIGINT, sig_handler);
void sig_handler(int signo)
{
  if (signo == SIGINT)
    printf("received SIGINT\n");
}
person Darshan b    schedule 08.11.2016