Подтвердить что ты не робот

Что такого особенного в файловом дескрипторе 3 на linux?

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

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

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

Рабочий обрабатывает журналы безупречно в Mac OS X и Linux с или без раздела ifdef'ed ниже. Контроллер также безукоризненно работает в 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() было идеей, которая сделала трюк, чтобы честь пошла в jilles. Что-то еще, кроме закрытия файлового дескриптора из-под syslogs, ноги должны продолжаться. Чтобы сделать работу с кодом, я добавил вызов closelog() непосредственно перед циклом:

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

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

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

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

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

4b9b3361

Ответ 1

syslog (3) может хранить дескриптор файла в syslogd socket open; закрытие этого под ногами, вероятно, вызовет проблемы. Вы можете обратиться за помощью к телефону (3).

Ответ 2

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

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

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

Ответ 3

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

$ 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

Ответ 4

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