Открыть список
Как стать автором
Обновить

Комментарии 40

Хотелось бы добавить еще небольшое уточнение по коду.
TraceSize = backtrace(Trace, 16);
этим мы получаем не весь стек вызовов, а последние 16, чего вполне достаточно чтобы локализовать проблему, если конечно речь идет не о рекурсивных функциях
что то не вижу где можно скачать исходный код…
если надо, то могу выложить на pastebin
да, если не сложно…
Недавно писал демона для приложения на Erlang. Вышло все гораздо проще и быстрее.
Стандартные gen_server + gen_tcp (для управления) + application решают. Можно управлять и через веб-интерфейс с помощью web machine. К тому же решение будет кросс-платформенным.
Ну это чуть разные тематики выходят. Одни используют Erlang, другие .net, третьи java, а кто-то чистый Си. Кому что по вкусу, то и используют. К тому же данный код очень легко расширятся для работы с BSD.
И к тому же демон может быть не сетевой и использоваться для других целей (к примеру конвертация чего-либо и прочие)
да, тематики разные) просто не удержался – так меня поразил эрланг))
основная беда — стоит кому-то поразиться очередной интересной штуке, как он начинает использовать эту штуку везде где только можно. Причём не всегда там где она и правда требуется. Ладно хоть шелл-скрипты на Эрланге вроде как не пишут, хотя не удивлюсь если и такой изврат кто-то практикует.
Недавно имел дело с сервером, написанным на эрланге. Расплевался из-за того, что эрланговцы считают, что весь мир должен вокруг их VM крутиться. Долго и нудно закрывал лишние порты от всяких beam.smp, боролся с уродливыми конфигами на самом эрланге… Общее впечатление — поменьше программ на подобном.
Ну, для определённых задач Эрланг и впрям хорош. Но вот когда я небольшой и ненагруженый сокет-сервер сделал на Node.js, эрланговцы меня заплевали. Такое ощущение что все делают только чаты с многосоттысячными коннектами и видеосерверы с дикой посещаемостью.
Спасибо огромное за статью. Она как никогда кстати. Как раз подвернулась работка на эту тематику
man daemon.

Всю задачу «корректно форкнуться» сделали уже за вас.

Кроме того, вовсе не обязтельно самим хранить pid — масса приложений использует обвязку из init.d для работы с pid'ом.
Можно и daemon использовать, но хотелось показать именно как всё это работает.
Обвязка из init.d не всегда есть.
В Debian, например, есть start-stop-daemon, но в том же Arch — нету вообще ничего подобного.
скрипт в init.d поставляется мейнтейнером и является конфигурационным файлом. В нормальном режиме любой уважающий себя дистриубив о подобных проблемах должен думать и должен их решать нормально.

Как в арче не знаю, мне вполне хватает того, что большая тройка (RHEL/Centos, Debian и SUSE) эту задачу решила успешно.
А какой смысл надеятся, на встроенные механизмы, которые не факт, что будут работать корректно во всех дистрибутивах, когда кода тут кот наплакал.
Кода кот наплакал если писать его как попало. В статье PID файл записывается без обработки ошибок, например.
Никакого смысла нет. Весь чужой код может оказаться нерабочим или неправильным. Именно этим принципом руководствуются те, кто пишут свои карманные ОС.
Не знаю что она там решила, но в CentOS 6.9 start-stop-daemon так и нету.
А это уже на любителя — как реализовать.
Я, например, вообще «тройную проверку» делаю: flock на бинарник, проверку pidfile и проверку дерева процессов в /proc. Хотя, возможно, это и излишне…
НЛО прилетело и опубликовало эту надпись здесь
как вариант если нужен дамп процесса, то в обработке сигнала можно запустить gdb с указанием своего pid'a и пусть он сгенерирует дамп. Конечно костыль, но всё же идея имеет право на жизнь
Или еще проще — использовать gcore. Если надо тащить в себе весь функционал, то достаточно взять его из того же gcore
И что мы получим? Корку когда мы находимся в обработчике сигнала? Она же практически бесполезна. Разве не так? Нам нужна корка в месте, где сигнал произошел.
Самое простое — делать abort() и, если ядро настроено соответсвующим образом, SIGALRM вызовет coredump.
А вообще есть еще вот такое code.google.com/p/google-coredumper/
сорри, имел в виду SIGABRT, my bad
Я обычно делаю в конфиге параметр коркаться всегда, 1 раз или никогда. Выглядит примерно так.
После 1 форка, но до запуска процесса мониторинга.

if (prctl(PR_SET_DUMPABLE, config.coredump != COREDUMP_OFF) < 0) {
err = -errno;
log_sys_err(«prctl(PR_SET_DUMPABLE, ...)»);
return err;
}

if(config.respawn) { // как раз и означает запуск этого процесса
while(true) {
pid_t pid = fork();
if (pid < 0) {
err = -errno;
log_sys_err(«error in fork»);
return err;
}

if (pid == 0)
break;

int status = 0;
if (waitpid(pid, &status, 0) < 0) {
err = -errno;
log_sys_err(«error in waitpid»);
return err;
}
int sig = WTERMSIG(status);
if (!sig)
_exit(0);

int core = WCOREDUMP(status);

log_err(«daemon killed: %s (core %sdumped)»,
strsignal(sig), (core? "": «not „));
if(core && config.coredump == COREDUMP_ONCE)
if(prctl(PR_SET_DUMPABLE, 0) < 0)
log_sys_err(“prctl(PR_SET_DUMPABLE, 0)»);
}
Огромное спасибо за статью.

Я до сих пор оказывается в разработке под *nix дуб дубом был. Таких элементарных вещей не знал.

Единственный вопрос возник — как решить проблему крашащегося при запуске основной рабочей части демона? Он в приведенном коде будет циклически перезапускаться. Я думаю, это не очень хорошо. Можно ли как то это решить?
Можно перед стартом потомка запомнить время, после завершения потомка проверить если он умер слишком рано (либо последние n раз он умерал слишком рано), ругнуться в лог и подождать к примеру минуту.
В принципе логично. Как я сам не догадался) Спасибо.
Спасибо за статью. Я новичек в разработке под *nix, но этот код уже нагуглил)
А вот за программный разбор бектрейса — огромнейшее спасибо. Недавно искал причину рандомного SIGSEGV в своем демоне… нашел… но сразу выведенный бектрейс сэкономил бы мне пару дней)
Если мы закрыли stdin/stdout/stderr, то нужно открыть их хотя бы в /dev/null, иначе при следующем open() мы попадем на стандартный дескриптор, а это вроде как не есть хорошо.
В данном случае я закрывал, чтобы показать что они не нужны, т.к. в коде нигде не используются printf и прочие функции работы с терминалом. Зато 3 дескриптора освобождается, и даже если open вернет их, то проблем не будет, если сразу предусмотреть это.

А вообще в таком случае в больших проектах лучше действовать по такой схеме:
1) если это дебаг версия, то ничего не закрывать и для логирования использовать printf и получать на экран весь лог
2) если это релиз версия, то закрывать всё, а потом открывать файл лога и связывать его с STDOUT_FILENO чтобы также прозрачно (через printf) писать лог.
Лет 10 нечто подобное встраиваю во все свои резидентные демоны.
А идею подглядел в книге Робачевский А.М. «Операционная система UNIX», где коротко описано, как открепить демона от родителя-стартера.
У вас плохая обработка ошибок. Часть сисколов может вернуть EINTR. Нет проверки wait() и т.п., chdir("/"), если свалились chroot() или close(0/1/2) и ошибка не обработана — ай-ай-ай.

Что будет при ошибках в SetPidFile()? Должен быть аналог abort(). Внутри должны проверяться fprintf(), close(), иначе пид файл недозаписан. Также если вы сами решили обрабатывать пид файл, то вы должны обрабатывать ситуации с параллельной попыткой записи в пид файл (т.е. одновременно был сделан /etc/init.d/your_daemon start) и fopen() vs. unlink() race.
Такой вопрос:
Существует ли утилита, демонизирующая любой процесс, даже не демонизирущая его, а отвязывающая от текущей сессии (чтобы можно было удаленно запустить, например, cloud-mail.ru, затем закрыть сессию)?

Подсказка — есть такая утилита, называется spawn-fcgi, но она делает еще плюс ко всем другие действия, а мне надо просто отвязать процесс от сессии.
Если говорить по факту, то отвязка от сессии — это и есть демон. Такая утилита есть и называется start-stop-daemon.
Обычно ее используют в управляющих скриптах, чтобы запускать программу как сервис при загрузке, и которая сама не становится демоном.
У демона в целом есть еще ряд действий, но в целом для задачи, я думаю, неважно.
Оо, ништяк, спасибо, попробую сча.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.