python-daemon

守护进程

守护进程是生存期长的一种进程。它们独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。

守护进程特性:

  1. 在后台运行
  2. 与其运行前的环境隔离开来,这些环境包括未关闭的文件描述符、控制终端、会话和进程组、工作目录以及文件创建掩码等
  3. 启动方式特殊

守护进程编写规则:

  1. 在后台运行,调用fork(),使父进程exit
  2. 脱离控制终端,登录会话和进程组,调用setsid()使进程变成会话组长
  3. 禁止进程重新打开控制终端
  4. 关闭已打开的文件描述符,调用fclose()
  5. 将当前工作目录更改为根目录
  6. 重设文件创建掩码为0
  7. 处理SIGEHLD信号
Daemon

创建daemon.py文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# coding: utf-8

import os
import sys
import time
import atexit
import signal


class Daemon:
def __init__(self, pidfile='/tmp/daemon.pid', stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile

def daemonize(self):
if os.path.exists(self.pidfile):
raise RuntimeError('Already running.')

# First fork (detaches from parent)
# 第一次fork(脱离父进程)
try:
if os.fork() > 0:
raise SystemExit(0)
except OSError as e:
raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror))

os.chdir('/')
os.setsid()
os.umask(0o22)

# Second fork (relinquish session leadership)
# 第二次fork(提升进程为会话组长)
try:
if os.fork() > 0:
raise SystemExit(0)
except OSError as e:
raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror))

# Flush I/O buffers
# 刷新IO缓存
sys.stdout.flush()
sys.stderr.flush()

# Replace file descriptors for stdin, stdout, and stderr
# 重置文件描述标准输入、输出、错误
with open(self.stdin, 'rb', 0) as f:
os.dup2(f.fileno(), sys.stdin.fileno())
with open(self.stdout, 'ab', 0) as f:
os.dup2(f.fileno(), sys.stdout.fileno())
with open(self.stderr, 'ab', 0) as f:
os.dup2(f.fileno(), sys.stderr.fileno())

# Write the PID file
# 写pid文件
with open(self.pidfile, 'w') as f:
print(os.getpid(), file=f)

# Arrange to have the PID file removed on exit/signal
atexit.register(lambda: os.remove(self.pidfile))

signal.signal(signal.SIGTERM, self.__sigterm_handler)

# Signal handler for termination (required)
# 终止信号处理
@staticmethod
def __sigterm_handler(signo, frame):
raise SystemExit(1)

def start(self):
try:
self.daemonize()
except RuntimeError as e:
print(e, file=sys.stderr)
raise SystemExit(1)

self.run()

def stop(self):
try:
if os.path.exists(self.pidfile):
with open(self.pidfile) as f:
os.kill(int(f.read()), signal.SIGTERM)
else:
print('Not running.', file=sys.stderr)
raise SystemExit(1)
except OSError as e:
if 'No such process' in str(e) and os.path.exists(self.pidfile):
os.remove(self.pidfile)

def restart(self):
self.stop()
self.start()

def run(self):
pass

创建一个test.py,对继承daemon,对run函数修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# -*- coding: UTF-8 -*-
import os
import sys
import time

from daemon import Daemon

class MyTestDaemon(Daemon):
def run(self):
sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid()))
while True:
sys.stdout.write('Daemon Alive! {}\n'.format(time.ctime()))
sys.stdout.flush()

time.sleep(5)

if __name__ == '__main__':
PIDFILE = '/tmp/daemon-example.pid'
LOG = '/tmp/daemon-example.log'
daemon = MyTestDaemon(pidfile=PIDFILE, stdout=LOG, stderr=LOG)

if len(sys.argv) != 2:
print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr)
raise SystemExit(1)

if 'start' == sys.argv[1]:
daemon.start()
elif 'stop' == sys.argv[1]:
daemon.stop()
elif 'restart' == sys.argv[1]:
daemon.restart()
else:
print('Unknown command {!r}'.format(sys.argv[1]), file=sys.stderr)
raise SystemExit(1)

运行程序

1
python3 test.py  start

查看pid号

​ cat /tmp/daemon-example.pid
​ 16385

查看日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tail -f /tmp/daemon-example.log 
Daemon Alive! Tue Apr 9 18:09:36 2019
Daemon Alive! Tue Apr 9 18:09:41 2019
Daemon Alive! Tue Apr 9 18:09:46 2019
Daemon Alive! Tue Apr 9 18:09:51 2019
Daemon Alive! Tue Apr 9 18:09:56 2019
Daemon Alive! Tue Apr 9 18:10:01 2019
Daemon Alive! Tue Apr 9 18:10:06 2019
Daemon Alive! Tue Apr 9 18:10:11 2019
Daemon Alive! Tue Apr 9 18:10:16 2019
Daemon Alive! Tue Apr 9 18:10:21 2019
Daemon Alive! Tue Apr 9 18:10:26 2019
Daemon Alive! Tue Apr 9 18:10:31 2019
Daemon Alive! Tue Apr 9 18:10:36 2019
Daemon Alive! Tue Apr 9 18:10:41 2019
-------------本文结束-------------