How it works¶
Daemonization is a little tricky to get right (and very difficult to test). Cross-platform “daemonization” is even less straightforward.
On Unix, daemonization with Daemoniker performs the following steps:
- Create a PID file, failing if it already exists
- Register cleanup of the PID file for program exit
- Double-fork, dissociating itself from the original process group and any possible control terminal
umaskand change current working directory
- Write its PID to the PID file
- Close file descriptors
To be considered a “well-behaved daemon”, applications should also, at the
least, handle termination through a
SIGTERM handler (see
Signal handling API for using Daemoniker for this purpose).
On Windows, “daemonization” with Daemoniker performs the following steps:
- Find the currently-running Python interpreter and file.
- Search for a copy of
pythonw.exewithin the same directory.
- Create a PID file, failing if it already exists.
- Save the current namespace and re-launch the script using
- Bypass any already-completed code using an environment variable “switch”.
- Change current working directory.
- Write its process handle to the PID file and register the file’s cleanup for program exit.
- Extract the old namespace.
- Return the old namespace into the resumed “daemonized” process and allow the original process to exit.
Due to the implementation of signals on Windows (as well as their use
within the CPython interpreter), any signals sent to this daughter process
will result in its immediate termination, without any cleanup. That
atexit calls, no
finally: blocks, etc. See
Signals: Windows below for more information, or see
Signal handling API for using Daemoniker as a workaround.
Signal handling on Unix is very straightforward. The signal handler provided
SigHandler1 provides a thin wrapper around the built-in
signal.signal functionality. To maintain uniform cross-platform behavior,
frame argument typically passed to
signal.signal callbacks is
removed, but otherwise, Daemoniker is simply a convenience wrapper around
signal.signal that includes several default signal handlers.
Signals on Windows are not natively supported by the operating system. They are
included in the C runtime environment provided by Windows, but their role is
substantially different than that in Unix systems. Furthermore, these signals
are largely limited to transmission between parent/child processes, and because
the “daemonization” process creates a fully-independent process group, every
available signal (including the Windows-specific
CTRL_BREAK_EVENT) result in immediate termination of the daughter process
To avoid this thoroughly undesirable behavior, Daemoniker uses the following workaround:
- From the main thread of the (daemonized) Python script, launch a daughter thread devoted to signal handling.
- From that daughter thread, launch a sleep-loop-forever daughter process.
- Overwrite the PID file with the PID of the daughter process.
- Wait for the daughter process to complete. If it was killed by a signal, its return code equals the number of the signal. Handle it accordingly.
- For every signal received, create a new daughter process.
Additionally, to mimic the behavior of
signal.signal and replicate Unix
behavior, the default Daemoniker signal handlers call a
ctypes API to raise
an exception in the main thread of the parent script.