Что такого особенного в файловом дескрипторе 3 в Linux? - PullRequest
19 голосов
/ 21 августа 2010

Я работаю над серверным приложением, которое будет работать на Linux и Mac OS X. Это выглядит так:

  • запуск основного приложения
  • форк контроллера процесса
  • вызов lock_down () в процессе контроллера
  • прекратить основное приложение
  • процесс контроллера снова разветвляется, создавая рабочий процесс
  • со временем контроллер продолжает разветвлять больше рабочих процессов

Я могу войти, используя несколько методов (например, syslog или файл), но сейчас я размышляю о syslog. Самое смешное, что в процессе контроллера не выводится никакой вывод системного журнала, если я не включу раздел #ifdef ниже.

Рабочий безошибочно обрабатывает журналы в Mac OS X и linux с разделом ifdef ниже или без него. Контроллер также безупречно ведет журнал в Mac OS X без раздела # ifdef, но в linux необходим ifdef, если я хочу увидеть какие-либо выходные данные в syslog (или файл журнала в этом отношении) из процесса контроллера.

Итак, почему это так?

static int
lock_down(void)
{
    struct rlimit rl;
    unsigned int n;
    int fd0;
    int fd1;
    int fd2;

    // Reset file mode mask
    umask(0);

    // change the working directory
    if ((chdir("/")) < 0)
        return EXIT_FAILURE;

    // close any and all open file descriptors
    if (getrlimit(RLIMIT_NOFILE, &rl))
        return EXIT_FAILURE;
    if (RLIM_INFINITY == rl.rlim_max)
        rl.rlim_max = 1024;

    for (n = 0; n < rl.rlim_max; n++) {
#ifdef __linux__        
        if (3 == n) // deep magic...
            continue;
#endif
        if (close(n) && (EBADF != errno))
            return EXIT_FAILURE;
    }

    // attach file descriptors 0, 1 and 2 to /dev/null
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup2(fd0, 1);
    fd2 = dup2(fd0, 2);
    if (0 != fd0)
        return EXIT_FAILURE;

    return EXIT_SUCCESS;
}

camh был близок, но использование closelog () было идеей, которая добилась цели, поэтому честь досталась Джиллесу. Что-то еще, кроме закрытия файлового дескриптора из-под ног системного журнала, должно продолжаться. Чтобы код работал, я добавил вызов closelog () непосредственно перед циклом:

closelog();
for (n = 0; n < rl.rlim_max; n++) {
    if (close(n) && (EBADF != errno))
        return EXIT_FAILURE;
}

Я полагался на дословное понимание страницы руководства, говоря:

Использование openlog () необязательно; при необходимости он будет автоматически вызываться syslog () ...

Я интерпретировал это как высказывание, что syslog обнаружит, если дескриптор файла закрыт под ним. Видимо это не так. Явный closelog () в linux был необходим, чтобы сообщить системному журналу, что дескриптор закрыт.

Еще одна вещь, которая все еще вызывает недоумение, заключается в том, что не использование closelog () не позволяло первому разветвленному процессу (контроллеру) даже открывать и использовать файл журнала. Следующие разветвленные процессы могут без проблем использовать системный журнал или файл журнала. Может быть, в файловой системе есть какой-то эффект кэширования, который делает первый разветвленный процесс ненадежным «представлением» о том, какие файловые дескрипторы доступны, в то время как следующий набор разветвленных процессов достаточно задерживается, чтобы не зависеть от этого?

Ответы [ 4 ]

14 голосов
/ 21 августа 2010

Особенность файлового дескриптора 3 заключается в том, что обычно это будет первый файловый дескриптор, возвращаемый системным вызовом, который выделяет новый файловый дескриптор, учитывая, что 0, 1 и 2 обычно устанавливаются для stdin, stdout и stderr.

Это означает, что если любая вызванная вами библиотечная функция выделяет файловый дескриптор для своих внутренних целей для выполнения своих функций, она получит fd 3.

Для вызова библиотеки openlog (3) нужно открыть /dev/log для связи с демоном syslog. Если впоследствии вы закроете все файловые дескрипторы, вы можете нарушить функции библиотеки syslog, если они написаны не для того, чтобы справиться с этим.

8 голосов
/ 21 августа 2010

Способ отладки в Linux заключается в использовании strace для отслеживания текущих системных вызовов; тогда использование файлового дескриптора для системного журнала становится очевидным:

$ cat syslog_test.c
#include <stdio.h>
#include <syslog.h>

int main(void)
{
    openlog("test", LOG_PID, LOG_LOCAL0);
    syslog(LOG_ERR, "waaaaaah");
    closelog();
    return 0;
}
$ gcc -W -Wall -o syslog_test syslog_test.c
$ strace ./syslog_test
...
socket(PF_FILE, SOCK_DGRAM, 0)          = 3
fcntl64(3, F_SETFD, FD_CLOEXEC)         = 0
connect(3, {sa_family=AF_FILE, path="/dev/log"}, 16) = 0
send(3, "<131>Aug 21 00:47:52 test[24264]"..., 42, MSG_NOSIGNAL) = 42
close(3)                                = 0
exit_group(0)                           = ?
Process 24264 detached
6 голосов
/ 21 августа 2010

syslog (3) может оставить дескриптор файла открытым для сокета syslogd;закрытие этого под ногами может вызвать проблемы.Вызов closelog (3) может помочь.

1 голос
/ 21 августа 2010

Syslog связывается с данным дескриптором при запуске.В большинстве случаев дескриптор 3. Если вы закрываете его, журналы отсутствуют.

syslog-ng -d -v

Дает вам больше информации о том, что он делает за кулисами.

Вывод должен выглядеть примерно так:

binding fd 3, inetaddr: 0.0.0.0, port: 514
io.c: Preparing fd 3 for reading
io.c: Preparing fd 4 for reading
binding fd 5, unixaddr: /dev/log
io.c: listening on fd 5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...