Обработка сигнала SIGHUP для деамонизации команды в системном программировании Unix - PullRequest
0 голосов
/ 07 декабря 2018

Я читаю книгу о системном программировании Unix.В книге есть функция для создания процесса-демона.

Часть кода мне не очень понятна, особенно следующее:

struct sigaction    sa;
....
/* *Become a session leader to lose controlling TTY. */
if ((pid = fork()) < 0)
{
    err_quit("%s: can’t fork", cmd);
}
else if (pid != 0) /* parent */
{
    exit(0); //the parent will exit
}
setsid();

/* *Ensure future opens won’t allocate controlling TTYs. */
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
{
    err_quit("%s: can’t ignore SIGHUP", cmd);
}

где

SIGHUP - это сигнал, отправляемый управляющему процессу (руководителю сеанса), связанному с управляющим терминалом, если интерфейс терминала обнаруживает разрыв.

Таким образом, в основном, родительский процесс вызывает fork и затем выход.Таким образом, нам гарантирован ребенок, а не лидер группы.Ребенок становится лидером сеанса с setsid.

Я не понимаю, когда генерируется сигнал SIG_UP: из определения кажется, что он генерируется при закрытии окна терминала, но из комментария в коде

/* *Ensure future opens won’t allocate controlling TTYs. */

этокажется, что он генерируется в другой ситуации: когда он генерируется?

Во-вторых, он хочет игнорировать этот сигнал, поэтому устанавливает sa.sa_handler = SIG_IGN и затем вызывает sigaction.Если он игнорирует настройку сигнала SIG_IGN в качестве своего обработчика, почему он устанавливает маску, переданную в sigaction как sigemptyset(&sa.sa_mask);?Я имею в виду, что если нет обработчика, маска, установленная перед выполнением обработчика, не используется:

Полная функция следующая:

void daemonize(const char *cmd)
{
    int i, fd0, fd1, fd2;
    pid_t pid;
    struct rlimit       rl;
    struct sigaction    sa;
    /* *Clear file creation mask.*/
    umask(0);
    /* *Get maximum number of file descriptors. */
    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
    {
        err_quit("%s: can’t get file limit", cmd);
    }
    /* *Become a session leader to lose controlling TTY. */
    if ((pid = fork()) < 0)
    {
        err_quit("%s: can’t fork", cmd);
    }
    else if (pid != 0) /* parent */
    {
        exit(0); //the parent will exit
    }
    setsid();
    /* *Ensure future opens won’t allocate controlling TTYs. */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGHUP, &sa, NULL) < 0)
    {
        err_quit("%s: can’t ignore SIGHUP", cmd);
    }
    if ((pid = fork()) < 0)
    {
        err_quit("%s: can’t fork", cmd);
    }
    else if (pid != 0) /* parent */
    {
        exit(0);
    }
    /*
    *Change the current working directory to the root so
    * we won’t prevent file systems from being unmounted.
    */
    if (chdir("/") < 0)
    {
        err_quit("%s: can’t change directory to /", cmd);
    }
    /*
    *Close all open file descriptors.
    */
    if (rl.rlim_max == RLIM_INFINITY)
    {
        rl.rlim_max = 1024;
    }
    for (i = 0; i < rl.rlim_max; i++)
    {
        close(i);
    }
    /*
    *Attach file descriptors 0, 1, and 2 to /dev/null.
    */
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);
    /*
    *Initialize the log file.
    */
    openlog(cmd, LOG_CONS, LOG_DAEMON);
    if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
        exit(1);
    }
}

РЕДАКТИРОВАТЬ

Также у меня есть дополнительный вопрос.Почему fork вызывается дважды в функции?

1 Ответ

0 голосов
/ 09 декабря 2018

Так что в основном ...

Да, родительский процесс разветвляет дочерний процесс, и этот дочерний процесс делает setsid(), так что он будет лидером группы процессов (и единственнымProcess) в новой группе процессов, и не имеют управляющего терминала .Эта последняя часть является ключом.

(Если была причина, по которой дочерний процесс должен выполняться в той же группе процессов, что и родительский процесс, можно использовать int fd = open("/dev/tty", O_RDWR); if (fd != -1) ioctl(fd, TIOCNOTTY); для отсоединения от управляющего терминала. setsid() проще, и, как правило, в любом случае предпочтительно, чтобы дочерний элемент запускался в новой группе процессов, поскольку ему и его дочерним элементам можно посылать сигнал, не затрагивая другие процессы.)

Теперь, когда процесс, которыйесли управляющий терминал не открывает терминальное устройство (tty или псевдо-tty), это устройство станет его управляющим терминалом (если только при открытии устройства не использовался флаг O_NOCTTY).

Всякий раз, когда управляющий терминалотключен, сигнал SIGHUP доставляется каждому процессу, имеющему этот терминал в качестве управляющего терминала.(Это SIG_UP - просто опечатка. Имена сигналов не имеют подчеркивания, только специальные обработчики SIG_DFL, SIG_IGN и SIG_ERR do.)

Если процесс-демон открывает терминальное устройствопо любой причине - например, из-за того, что библиотека хочет напечатать сообщение об ошибке на консоли и открывает для этого /dev/tty1 или что-то подобное - демон случайно получит управляющий терминал.Помимо вставки open(), fopen(), opendir() и т. Д., Чтобы гарантировать, что их базовые флаги open() будут включать O_NOCTTY, демон не может сделать много, чтобы гарантировать, что он случайно не получит управляющийТерминал.Вместо этого, более простой вариант - просто предположить, что это возможно, и просто убедиться, что это не вызывает особых проблем.Чтобы избежать наиболее типичной проблемы, умирающей от SIGHUP, когда управляющий терминал отключен, процесс-демон может просто игнорировать выдачу сигнала SIGHUP.

Короче говоря, это поясподтяжки подходят.setsid() отсоединяет процесс от управляющего терминала;и SIGHUP игнорируется, если демон по неосторожности получает управляющий терминал, открывая tty-устройство без использования флага O_NOCTTY.

...