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
|
守护进程的编写步骤:
1
、fork子进程,然后父进程退出,此时子进程会被init进程接管。
2
、修改子进程的工作目录,创建新进程组合新会话,修改umask。
3
、子进程再次fork一个进程,这个进程可以称为孙子进程,然后子进程退出。
4
、重定向孙子进程的标准输入流,标准输出流,标准错误到
/
dev
/
null
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import
sys, os
'''将当前进程fork为一个守护进程
注意:如果你的守护进程是由inetd启动的,不要这样做!inetd完成了
所有需要做的事情,包括重定向标准文件描述符,需要做的事情只有chdir()和umask()了
'''
def
daemon(stdin
=
'/dev/null'
, stdout
=
'/dev/null'
, stderr
=
'/dev/null'
):
# 重定向标准文件描述符(默认情况下定向到/dev/null)
try
:
pid
=
os.fork()
# 父进程(会话组头领进程)退出,这意味着一个非会话组头领进程永远不能重新获得控制终端。
if
pid >
0
:
sys.exit(
0
)
# 父进程退出
except
OSError, e:
sys.stderr.write(
"fork #1 failed: (%d) %s\n"
%
(e.errno, e.strerror))
sys.exit(
1
)
# 从母体环境脱离
os.chdir(
"/"
)
# chdir确认进程不保持任何目录于使用状态,否则不能umount一个文件系统。也可以改变到对于守护程序运行重要的文件所在目录
os.umask(
0
)
# 调用umask(0)以便拥有对于写的任何东西的完全控制,因为有时不知道继承了什么样的umask。
os.setsid()
# setsid调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。
# 执行第二次fork
try
:
pid
=
os.fork()
if
pid >
0
:
sys.exit(
0
)
# 第二个父进程退出
except
OSError, e:
sys.stderr.write(
"fork #2 failed: (%d) %s\n"
%
(e.errno, e.strerror))
sys.exit(
1
)
# 进程已经是守护进程了,重定向标准文件描述符
for
f
in
sys.stdout, sys.stderr: f.flush()
si
=
open
(stdin,
'r'
)
so
=
open
(stdout,
'a+'
)
se
=
open
(stderr,
'a+'
)
os.dup2(si.fileno(), sys.stdin.fileno())
# dup2函数原子化关闭和复制文件描述符
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# 示例函数:每秒打印一个数字和时间戳
def
main():
import
time
sys.stdout.write(
'Daemon started with pid %d\n'
%
os.getpid())
sys.stdout.write(
'Daemon stdout output\n'
)
sys.stderr.write(
'Daemon stderr output\n'
)
c
=
0
while
True
:
sys.stdout.write(
'%d: %s\n'
%
(c, time.ctime()))
sys.stdout.flush()
c
=
c
+
1
time.sleep(
1
)
if
__name__
=
=
"__main__"
:
daemone(
'/dev/null'
,
'/tmp/daemon_stdout.log'
,
'/tmp/daemon_error.log'
)
main()
# 可以通过命令ps -ef | grep daemon.py查看后台运行的继承
# 在/tmp/daemon_error.log会记录错误运行日志
# 在/tmp/daemon_stdout.log会记录标准输出日志。
|
1、fork子进程,父进程退出
通常,我们执行服务端程序的时候都会通过终端连接到服务器,成功连接后会加载shell环境,终端盒shell都是进程,shell进程是终端进程的子进程,通过ps命令可以很容易的查看到,在这个shell环境下一开始执行的程序都是shell进程的子进程,自然会受到shell进程的影响,在程序里fork子进程后,父进程退出,对于shell进程来说,这个父进程就算执行完毕,而产生的子进程会被init进程接管,从而也就脱离了终端控制。
2.修改子进程的工作目录
子进程在创建的时候会继承父进程的工作目录,如果执行的程序是在U盘里面,就会导致U盘不能卸载。
3.创建新会话
使用setsid后,子进程就会成为新会话的首进程,子进程会成为新进程组的组长进程,子进程没有控制终端。
4.修改umask
由于umask会屏蔽权限,所有设定为0,这样可以避免读写文件时碰到权限问题
5.fork孙子进程,子进程退出
经过上面几个步骤后,子进程会成为新的进程组老大,可以重新申请打开终端,为了避免这个问题,fork孙子进程处理,
6.重定向孙子进程的标准输入流,标准输出流,标准错误流到/dev/null
因为是守护进程,本身已经脱离了终端,那么标准输入流,标准输入流,标准错误流就没有什么意义了,所以都转向到/dev/null,就是丢弃的意思
本文转自 baby神 51CTO博客,原文链接:http://blog.51cto.com/babyshen/1888273,如需转载请自行联系原作者