Linux daemon double fork

Linux daemon double fork

Something that I have been asked many times is why do Linux / Unix daemons double-fork to start a daemon? Now that I am blogging for HP’s Advanced Technology Group I can try to explain this as simply as I can.

Daemons double-fork

For those who were not aware (and hopefully are now by the title) almost every Linux / Unix service that daemonizes does so by using a double-fork technique. This means that the application performs the following:

  1. The application (parent) forks a child process
  2. The parent process terminates
  3. The child forks a grandchild process
  4. The child process terminates
  5. The grandchild process is now the daemon

The technical reason

First I’ll give the technical documented reason for this and then I’ll break this down into something a bit more consumable. POSIX.1-2008 Section 11.1.3, «The Controlling Terminal» states the following:

The controlling terminal for a session is allocated by the session leader in an implementation-defined manner. If a session leader has no controlling terminal, and opens a terminal device file that is not already associated with a session without using the O_NOCTTY option (see open()), it is implementation-defined whether the terminal becomes the controlling terminal of the session leader. If a process which is not a session leader opens a terminal file, or the O_NOCTTY option is used on open(), then that terminal shall not become the controlling terminal of the calling process.

Источник

Двойной fork()

У Стивенсона в книге есть пример функции daemon_init(), которая создает демона. В ней используется используется двойной вызов fork(), якобы для надежности, что бы наверняка :).

Меня интересует насколько оправдана такая реализация с двумя вызовами fork()? Спасибо.

Re: Двойной fork()

Ну, типа, все так делают.

А как ты иначе передашь процесс init’у (если не хочешь убивать породивший процесс)?

Re: Двойной fork()

Хм. наверное я все-таки чего-то недопонимаю, ибо вот, например, здесь: http://www.linuxprofilm.com/articles/linux-daemon-howto.html используется исключительно один fork(), и я так тоже до этого делал — правда в теле main(), а не из сторонней функции.

Ведь тут тоже процесс передается init’у, imho.

Re: Двойной fork()

> Ведь тут тоже процесс передается init’у, imho.

Угу, после смерти папы.

А если папе надо дальше жить?

Re: Двойной fork()

Осмелюсь немного поправить 😉

Смысл double fork в том, чтобы процесс-демон никогда бы не смог
получить controlling terminal.
Честно говоря лень самому описывать, краткое изложение имеется в
Unix Programmin FAQ:
http://www.erlenstar.demon.co.uk/unix/faq_toc.html
1.7 How do I get my program to act like a daemon?

Читайте также:  Windows 10 stopcode 0x000021a

У Стивенса в APUE (AFAIK русского перевода увы нет) это тоже
описано (книжка на работе, так что точную цитату привести не могу).
Возможно и в Linux Application Development это описано (опять-таки
книжка на работе, проверить не могу).

Этот второй fork не обязателен в том смысле, что если ты сам пишешь
этого демона, то ты и так знаешь — будет твой демон пытаться
получить controlling terminal или нет.

Re: Двойной fork()

To Onanim:
Благодарю. Вроде бы понял. Жаль, что APUE не переведена, иначе бы приобрел в бумажном виде. Спасибо еще раз.

Re: Двойной fork()

BTW в прошлом году вышло второе издание APUE (увы, естественно, уже
без Стивенса). Впрочем Stephen Rago (соавтор нового издания) тоже
IMHO вполне авторитетная личность.
Первому изданию уже лет наверное 15, давно пора было обновить.
Я сам нового издания не видел, но «судя по каментам» — там все как надо :-)))

Re: Двойной fork()

Ну да, про это я забыл.

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

Только сейчас дошло, что это моя специфика, и демоны, вообще говоря, не совсем для этого 🙂

Источник

mynameisrufus / .gitignore

tmp
log
doc
daemon
* .swp
#!/usr/bin/env ruby
# == Double-forking Unix daemon
#
# Author:: Rufus Post (mailto:rufuspost@gmail.com)
#
# === How does it work?
#
# According to Stevens’s Advanced Programming in the UNIX Environment
# chapter 13, this is the procedure to make a well-behaved Unix daemon:
#
# Fork and have the parent exit. This makes the shell or boot script
# think the command is done. Also, the child process is guaranteed not
# to be a process group leader (a prerequisite for setsid next)
# Call setsid to create a new session. This does three things:
# * The process becomes a session leader of a new session
# * The process becomes the process group leader of a new process group
# * The process has no controlling terminal
#
# Optionally fork again and have the parent exit. This guarantes that
# the daemon is not a session leader nor can it acquire a controlling
# terminal (under SVR4)
#
# grandparent — the current process
# \_ parent — exits immediately
# \_ simple daemon — writes out its pid to file
#
# Change the current working directory to / to avoid interfering with
# mounting and unmounting. By default don’t bother to chdir(«/») here
# because we might to run inside APP_ROOT.
#
# Set file mode creation mask to 000 to allow creation of files with any
# required permission later. By default umask is whatever was set by the
# parent process at startup and can be set in config.ru and config_file,
# so making it 0000 and potentially exposing sensitive log data can be
# bad policy.
#
# Close unneeded file descriptors inherited from the parent (there is no
# controlling terminal anyway): stdout, stderr, and stdin.
#
# Nowadays there is a file to track the PID which is used heavily by
# Linux distribution boot scripts. Be sure to write out the PID of the
# grandchild, either the return value of the second fork (step 3) or the
# value of getpid() after step 3.
#
class SimpleDaemon
# In the directory where you want your daemon add a git submodule to
# your project and create a daemon launcher script:
#
# #!/usr/bin/env ruby
#
# require ‘simple_daemon’
#
# $0 = «my daemon»
#
# SimpleDaemon.daemonize! ARGV[0], ARGV[1], ARGV[2]
#
# loop do
# sleep 5
# puts «tick»
# sleep 5
# puts «tock»
# end
#
# make your script executable and run:
#
# $ chmod +x launcher
# $ launcher tmp/daemon.pid log/daemon.stdout.log log/daemon.stderr.log
#
# check that it is running by with the following:
#
# $ ps aux | grep «my daemon»
#
#
def self . daemonize! pidfile , out = ‘/dev/null’ , err = ‘/dev/null’ , safe = true
raise ‘First fork failed’ if ( pid = fork ) == — 1
exit unless pid . nil?
Process . setsid
raise ‘Second fork failed’ if ( pid = fork ) == — 1
exit unless pid . nil?
kill pidfile
write Process . pid , pidfile
unless safe
Dir . chdir ‘/’
File . umask 0000
end
redirect out , err
end
# Attempts to write the pid of the forked process to the pid file.
# Kills process if write unsuccesfull.
def self . write pid , pidfile
File . open pidfile , «w» do | f |
f . write pid
f . close
end
$stdout . puts «Daemon running with pid: # < pid >«
rescue :: Exception => e
raise «While writing the PID to file, unexpected # < e . class >: # < e >«
end
# Try and read the existing pid from the pid file and signal HUP to
# process.
def self . kill pidfile
opid = open ( pidfile ) . read . strip . to_i
Process . kill «HUP» , opid
rescue TypeError
$stdout . puts » # < pidfile >was empty: TypeError»
rescue Errno :: ENOENT
$stdout . puts » # < pidfile >did not exist: Errno::ENOENT»
rescue Errno :: ESRCH
$stdout . puts «The process # < opid >did not exist: Errno::ESRCH»
rescue Errno :: EPERM
raise «Lack of privileges to manage the process # < opid >: Errno::EPERM»
rescue :: Exception => e
raise «While signaling the PID, unexpected # < e . class >: # < e >«
end
# Redirect file descriptors inherited from the parent.
def self . redirect out , err
$stdin . reopen ‘/dev/null’
$stdout . reopen File . new ( out , «a» )
$stderr . reopen File . new ( err , «a» )
$stdout . sync = $stderr . sync = true
end
end
Читайте также:  При установке mac os пишет не могу установить

You can’t perform that action at this time.

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.

Источник

Один экземпляр python-daemon

Пишу демон на питоне, необходимо чтобы он не давал запустить копию себя, если он уже работает. При этом, нужно чтобы он правильно обрабатывал ситуацию, когда он был экстренно завершен.

Для демонизации использую python-daemon из PEP-3143. В общем случае, демон выглядит так:

Но, несмотря на pid-файл второй демон запускается спокойно.

Самое простое что пришло в голову — проверять os.path.exists(PIDFILE) , а потом try os.kill(pidfile.read_pid(), 0) и ловить except OSError

Но, выглядит как-то костылевато.

Но, ума не приложу как ей пользоваться, она у меня валится на sock.bind

Еще я нашел такую проверку (средствами самого python-daemon):

Как правильно ограничивать количество копий процесса? Можете просто ткнуть в доки или бросить ссылкой.

Всегда пользовался этим вариантом, простой double fork.

Python-daemon из https://pypi.python.org/pypi/python-daemon тоже делает double-fork, всё по-пацански, как у Стивенса.

Я посмотрел Вами приведенную ссылку. Там ловится except OSError , но мне кажется, это немного похоже на костыль.

Вот бы понять, что как работает встроенная daemon.runner.is_pidfile_stale(pidfile) .

А можете объяснить, какой смысл в pid-файлах, если его можно удалить? Просто не понимаю.

Вот идея с сокетом мне нравится (только у меня не получилось её повторить).

Пид-файл это дешего и сердито. Нет никакого смысла изобретать железобетонные схемы для обычных систем.

В общем, сделал как посоветовал bj, спасибо ему!

Но, вопрос про сокет остается актуальным — я не понимаю как использовать эту функцию:

Читайте также:  Как активировать windows профессиональная

Там на сайте парень даёт пояснение:

On linux (at least) there’s one nice trick to get a single-instance program. Create a unix domain socket, and bind it to an address that begins with the null character ‘\0’. You can bind the same address a second time, and if the process dies the socket is automatically destroyed. It will not leave anything on the filesystem.

.. создайте сокет и подключитесь к адресу, начинающемуся с нулевого символа. Вы можете подключится к этому адресу второй раз и если процесс умрет, сокет автоматически разрушится.

Кто знает, как это можно использовать для запуска одной копии демона?

Кстати, о птичках. Зачем демонезировать средствами питона, когда для этого дела есть куча классных тулз? Можно, скажем, отталкиваться от доки по gunicorn-у ( http://gunicorn-docs.readthedocs.org/en/latest/deploy.html ), где есть и supervisord, и системные runit, systemd и upstart.

Вот убунтовый upstart:

Просто, понятно. Если конфиг называется mydaemon.conf, то запуск start mydaemon, а остановка — stop mydaemon

Логи, единственность демона, перезапуск при падениях должны сработаться upstart-ом, для того он и сделан.

Кто знает, как это можно использовать для запуска одной копии демона?

Очень просто. Биндишь порт, например (возможно, твой демон и так должен это делать). Если bind() фейлится с EBUSY, или как там — значит, данный процесс должен завершиться. В итоге, у тебя никогда не будет двух процессов демона, раз уж демон биндит некий порт.

Источник

Оцените статью