Явно вызывать обработчики SIG_DFL / SIG_IGN в Linux - PullRequest
1 голос
/ 30 июня 2010

Я заблокировал, а затем ждал сигнала с помощью следующего кода:

sigset_t set;
sigfillset(&set); // all signals
sigprocmask(SIG_SETMASK, &set, NULL); // block all signals
siginfo_t info;
int signum = sigwaitinfo(&set, &info); // wait for next signal
struct sigaction act;
sigaction(signum, NULL, &act); // get the current handler for the signal
act.sa_handler(signum); // invoke it

Последняя строка генерирует ошибку сегментации, поскольку обработчик установлен на SIG_DFL (определяется как 0).Как я могу вручную вызвать обработчик по умолчанию, если он установлен на SIG_DFL или SIG_IGN?Также обратите внимание, что SIG_IGN определяется как 1.

Ответы [ 3 ]

2 голосов
/ 13 июля 2010

Как вы обнаружили, вы не можете вызывать SIG_DFL и SIG_IGN как таковые . Однако вы можете более или менее имитировать их поведение.

Вкратце, имитация нормального расположения сигнала будет:

  • довольно легко для пользователя sa_handler s
  • достаточно просто для SIG_IGN, с оговоркой, что вам нужно будет waitpid () в случае CHLD
  • прямолинейно, но неприятно для SIG_DFL, ре-рейз, чтобы ядро ​​могло творить чудеса.

Делает ли это то, что вы хотите?

#include <signal.h>
#include <stdlib.h>

/* Manually dispose of a signal, mimicking the behavior of current
 * signal dispositions as best we can.  We won't cause EINTR, for
 * instance.
 *
 * FIXME:  save and restore errno around the SIG_DFL logic and
 *         SIG_IGN/CHLD logic.
 */
void dispatch_signal(const int signo) {
    int stop = 0;
    sigset_t oset;
    struct sigaction curact;

    sigaction(signo, NULL, &curact);

    /* SIG_IGN => noop or soak up child term/stop signals (for CHLD) */
    if (SIG_IGN == curact.sa_handler) {
        if (SIGCHLD == signo) {
          int status;
          while (waitpid(-1, &status, WNOHANG|WUNTRACED) > 0) {;}
        } 
        return;
    }

    /* user defined => invoke it */
    if (SIG_DFL != curact.sa_handler) {
        curact.sa_handler(signo);
        return;
    }

    /* SIG_DFL => let kernel handle it (mostly).
     *
     *  We handle noop signals ourselves -- "Ign" and "Cont", which we
     *  can never intercept while stopped.
     */
    if (SIGURG == signo || SIGWINCH == signo || SIGCONT == signo) return;

    /*  Unblock CONT if this is a "Stop" signal, so that we may later be
     *  woken up.
     */
    stop = (SIGTSTP == signo || SIGTTIN == signo || SIGTTOU == signo);
    if (stop) {
        sigset_t sig_cont;

        sigemptyset(&sig_cont);
        sigaddset(&sig_cont, SIGCONT);
        sigprocmask(SIG_UNBLOCK, &sig_cont, &oset);
    }

    /*  Re-raise, letting the kernel do the work:
     *     - Set exit codes and corefiles for "Term" and "Core"
     *     - Halt us and signal WUNTRACED'ing parents for "Stop"
     *     - Do the right thing if we forgot to handle any special
     *       signals or signals yet to be introduced
     */
    kill(getpid(), signo);

    /* Re-block CONT, if needed */
    if (stop) sigprocmask(SIG_SETMASK, &oset, NULL);
}
<Ч />

UPDATE (в ответ на отличные вопросы ОП)

1: идет ли этот слот после sigwaitinfo?

Да. Что-то вроде:

... block signals ...
signo = sigwaitinfo(&set, &info);
dispatch_signal(signo);

2: Почему бы не поднять эти сигналы, обрабатываемые SIG_IGN, они все равно будут игнорироваться

Несколько более эффективно выполнять noop в пользовательском пространстве, чем с помощью трех системных вызовов (повторное повышение, снятие маски, повторная маска). Более того, CHLD имеет особую семантику, когда SIG_IGNored.

3: Зачем особенно обращаться с SIGCHLD?

Первоначально (проверьте редактирование ответа) я не сделал этого - повторно поднял его в случае SIG_IGN, потому что сигналы IGNored CHLD сообщают ядру, чтобы оно автоматически получало дочерние элементы .

Однако я изменил его, потому что "естественные" сигналы CHLD несут информацию о завершенный процесс (по крайней мере, PID, статус и реальный UID). Сгенерированные пользователем сигналы CHLD не имеют той же семантики, и, в моем тестировании, IGNoring их не заставляет 2.6 автоматически собирать в очередь зомби, чей SIGCHLD был "пропущен". Итак, я делаю это сам.

4: Почему сигналы, связанные с остановкой, разблокируются. CONT. Не вызовет ли обработчик по умолчанию для CONT прерывание процесса?

Если мы остановлены (не выполняемся) и CONT заблокированы, мы никогда не получим сигнал разбудить нас!

5: Почему бы не вызвать рейз вместо линии убийства, которую вы дали?

Личные предпочтения; raise() тоже сработает.

1 голос
/ 30 июня 2010

Я не знаю, как это сделать.

Единственное, что я могу предложить, - это посмотреть на man 7 signal и выполнить вручную действие в соответствии с таблицей, которую вы там видите. Ign это ничто. Core это вызов abort(). Term - это _exit().

Конечно, вы также можете установить обработчик сигнала обратно на SIG_DFL, а затем kill(getpid(),THE_SIG) (или его эквивалент raise(THE_SIG)). (Лично мне не нравится raise, потому что в некоторых системах он может выдавать некоторые сообщения на stderr.)

1 голос
/ 30 июня 2010

Я вижу 2 ошибки в вашем коде:1) Вы должны обратить последние две строки следующим образом:

act.sa_handler(signum);
sigaction(signum, NULL, &act);

2) Вы должны передать обработчик функции в fiedl sa_handler вместо int.Прототип функции должен выглядеть следующим образом:

   /**
    *some where in you code
     */
     void handler (int signal){ /*your code*/}
   /**
    *
    */      
    act.sa_handler = handler;

Если вы хотите, чтобы вызывался обработчик по умолчанию, вы должны установить для поля sa_handler значение SIG_DFL иэто должно работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...