Как повторно запустить программу через сигнал - PullRequest
2 голосов
/ 15 марта 2020

Я хотел удобный способ перезапустить программу. Я думал, что смогу просто поймать сигнал (в примере USR1) и вызвать exe c.

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

char* const* args;
void restart() {
    printf("restarting\n");
    execv(args[0], args);
}

int main(int argc, char* const argv[]) {
    printf("Starting\n");
    args = argv;
    signal(SIGUSR1, restart);
    raise(SIGUSR1); // could use pkill -SIGUSR1 file-name instead
    pause();
    printf("Terminated normally\n");
    return 0;
}

Пример выше работает. Вывод

Starting
restarting
Starting

и затем зависает. Другие сигналы все еще могут быть получены.

Полагаю, мне просто не удается сбросить сигнал. Ожидаемое поведение - программа будет перезапускаться бесконечно.

Ответы [ 3 ]

1 голос
/ 15 марта 2020

На вашей платформе SIGUSR1 маскируется внутри обработчика сигнала, что является типичным, но не обязательным поведением для signal (см., Напротив, SA_NODEFER и sigaction).

Эта маска наследуется через execve, таким образом, SIGUSR1 удерживается в ожидании во время второго выполнения и никогда не доставляется.

Попробуйте что-то подобное в верхней части main(), и возможно, внутри обработчика, чтобы увидеть, что происходит:

static int
is_blocked(int sig) {
  sigset_t ss;
  sigemptyset(&ss);
  (void)sigprocmask(SIG_BLOCK, NULL, &ss);
  return sigismember(&ss, sig);
}
1 голос
/ 15 марта 2020

Не уверен, почему это работает - замените сигнал на sigset

Сначала определите __USER_XOPEN_EXTENDED, иначе sigset не определен

#define __USE_XOPEN_EXTENDED
#include <signal.h>

Измените сигнал на sigset

...
sigset(SIGUSR1, restart);
...

It затем продолжает перезапуск, пока не будет убит. Возможно, кто-то может объяснить, почему sigset работает, а сигнал - нет.

1 голос
/ 15 марта 2020

Linux man 2 signal объясняет, что происходит, когда вы устанавливаете обработчик сигнала с помощью signal. Есть две возможности:

  1. Расположение сигнала сбрасывается до SIG_DFL, и затем вызывается обработчик. Чтобы снова обработать этот сигнал, вам нужно переустановить sh обработчик сигнала. Это сработало бы так, как вы ожидали.

Или:

Сигнал блокируется, и затем вызывается обработчик. Когда обработчик возвращается, сигнал разблокируется. Если обработчик не возвращается, сигнал все еще блокируется, что и происходит с вами.

Так что один работает с вашим кодом, а другой - нет. Но какое из двух применимо? Нет надежного способа узнать. Posix допускает обе возможности, и обе возможности существуют на разных платформах. По этой причине справочная страница Linux рекомендует:

Единственное переносимое использование signal () - это установить расположение сигнала на SIG_DFL или SIG_IGN ... [D] o не используйте его для [установки обработчика сигнала].

POSIX.1 решил проблему переносимости, указав sigaction(2), которая обеспечивает явный контроль семантики при вызове обработчика сигнала; используйте этот интерфейс вместо signal().

Это хороший совет. Когда вы перейдете на использование sigaction, вы, вероятно, захотите go для первого варианта выше, который требует:

sa.sa_flags = SA_RESETHAND | SA_NODEFER

Это также происходит из справочной страницы Linux, что стоит читать полностью. (Конечно, справочная страница sigaction еще более актуальна.)

...