Python daemonize

The process of daemonization in a Linux system always goes through the same process regardless of the development language.

In general, the ID of the parent process is 1 (init process id) through a double fork processing. Also, since the daemon needs to operate regardless of input/output, it dedirects stdout, stderr, and stdin to /dev/null.

Here is a very simple Python code. It simply displays Hello World in the loop statement. The only part I care about is adding code for handling the termination signal.

Normal Python Program

import time
import os, sys
import signal


'''
For Smooth Closing
'''
def stop(sig, frame):
    print('Process Exit')
    sys.exit(0)


signal.signal(signal.SIGINT, stop)
signal.signal(signal.SIGTERM, stop)

while True:
    time.sleep(1)
    print('Hello World')
<normal.py>


If you run the code, you may see this screen output.

[root@gcloud-seoul-a093086b769c683771dee9c71e694a73 python_daemon]# python3 normal.py
Hello World
Hello World
Hello World
Hello World
^CProcess Exit

Python Daemonized Program

Let's turn this simple program into a daemon. As mentioned earlier, you can use the double fork method.

import time
import os, sys
import signal


'''
For Smooth Closing
'''
def stop(sig, frame):
    print('Process Exit')
    sys.exit(0)


signal.signal(signal.SIGINT, stop)
signal.signal(signal.SIGTERM, stop)

# double fork, first fork
pid = os.fork()
if pid > 0:
    # parent procrss
    # just exit
    exit(0)
else:
    # decouple from parent envronment
    os.chdir('/')
    os.setsid()
    os.umask(0)

    # second fork
    pid = os.fork()
    if pid > 0:
        exit(0)
    else:
        # daemon process does not use stdout, stderr, stdin. So redirect them to dev/null
        sys.stdout.flush()
        sys.stderr.flush()
        si = open(os.devnull, 'r')
        so = open(os.devnull, 'a+')
        se = open(os.devnull, 'a+')
        os.dup2(si.fileno(), sys.stdin.fileno())
        os.dup2(so.fileno(), sys.stdout.fileno())
        os.dup2(se.fileno(), sys.stderr.fileno())

while True:
    time.sleep(1)
    print('Hello World')
<daemon1.py>


If you run the code, you may see this screen output.

[root@gcloud-seoul-a093086b769c683771dee9c71e694a73 python_daemon]# python3 daemon1.py
[root@gcloud-seoul-a093086b769c683771dee9c71e694a73 python_daemon]# ps -ef|grep python3
root      7058     1  0 11:38 ?        00:00:00 python3 daemon1.py
root      7062  3507  0 11:38 pts/0    00:00:00 grep --color=auto python3

As soon as you run the program, it returns to the prompt state. And if we look for a Python 3 program using the ps command, we can see that process ID 7058 is the process we ran. Also, you can see that the parent process ID has been changed to 1.
This python program is now a daemon and runs in the background, and there is no more screen I/O.

Python Daemonized other Program

Finally, let's work on making a process a daemon other than itself. This process is almost the same as the previous process. You can change the child proess daemonized through doule fork to another process using the execv function. The execv function is a system function available in most languages.

import time
import os, sys
import signal


'''
For Smooth Closing
'''
def stop(sig, frame):
    print('Process Exit')
    sys.exit(0)


signal.signal(signal.SIGINT, stop)
signal.signal(signal.SIGTERM, stop)

'''
start other process and make it daemon
'''

# double fork, first fork
pid = os.fork()
if pid > 0:
    # parent procrss
    # just exit
    exit(0)
else:
    # decouple from parent envronment
    os.chdir('/')
    os.setsid()
    os.umask(0)

    # second fork
    pid = os.fork()
    if pid > 0:
        exit(0)
    else:
        # daemon process does not use stdout, stderr, stdin. So redirect them to dev/null
        sys.stdout.flush()
        sys.stderr.flush()
        si = open(os.devnull, 'r')
        so = open(os.devnull, 'a+')
        se = open(os.devnull, 'a+')
        os.dup2(si.fileno(), sys.stdin.fileno())
        os.dup2(so.fileno(), sys.stdout.fileno())
        os.dup2(se.fileno(), sys.stderr.fileno())
        exec_str =  ['/usr/bin/python3', '/usr/local/src/study/python_daemon/normal.py']
        os.execv('/usr/bin/python3',exec_str)

while True:
    time.sleep(1)
    print('Hello World')
<daemon2.py>


This program runs normal.py, which we used as the first example, and daemonizes this process.

[root@gcloud-seoul-a093086b769c683771dee9c71e694a73 python_daemon]# python3 daemon2.py
[root@gcloud-seoul-a093086b769c683771dee9c71e694a73 python_daemon]# ps -ef|grep python3 root 8027 1 7 11:46 ? 00:00:00 /usr/bin/python3 /usr/local/src/study/python_daemon/normal.py root 8029 3507 0 11:46 pts/0 00:00:00 grep --color=auto python3


Wrapping up

The method introduced above can be applied in the same way regardless of the language used, such as C/C++, Python, or Java. Only if your operating system is a Linux system.













댓글

이 블로그의 인기 게시물

MQTT - C/C++ Client

RabbitMQ - C++ Client #1 : Installing C/C++ Libraries

C/C++ - Everything about time, date