os模块中的fork方法可以创建一个子进程。相当于克隆了父进程
os.fork()
子进程运行时,os.fork方法会返回0;
而父进程运行时,os.fork方法会返回子进程的PID号。
所以可以使用PID来区分两个进程:
1
|
2
3
4
5
6
7
8
9
10
11
12
|
#!/usr/bin/env python
#coding=utf8
from time import sleep
import os
try :
pid = os.fork()
except OSError, e:
pass
sleep( 30 )
|
运行代码,查看进程:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[root@localhost ~] # python test2.py &
[1] 2464
[root@localhost ~] # ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 2379 2377 0 80 0 - 28879 wait pts /1 00:00:00 bash
0 S 0 2464 2379 0 80 0 - 31318 poll_s pts /1 00:00:00 python
1 S 0 2465 2464 0 80 0 - 31318 poll_s pts /1 00:00:00 python
0 R 0 2466 2379 0 80 0 - 37227 - pts /1 00:00:00 ps
|
可以看出第二条python进程就是第一条的子进程。
如刚刚所说os.fork()方法区分子进程和父进程
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#-*- coding:utf-8 -*-
from time import sleep
import os
print( 'start+++++++++++++' )
#创建子进程之前声明的变量
source = 10
try:
pid = os.fork()
print( 'pid=' ,pid)
if pid == 0: #子进程
print( "this is child process." )
source = source - 1 #在子进程中source减1
else : #父进程
print( "this is parent process." )
print( source )
except (OSError,e):
pass
print( 'END---------------' )
|
面代码中,在子进程创建前,声明了一个变量source,然后在子进程中减1,最后打印出source的值,显然父进程打印出来的值应该为10,子进程打印出来的值应该为9。
1
|
2
3
4
5
6
7
8
9
10
11
|
[root@localhost ~] # python test3.py
start+++++++++++++
pid= 2550
this is parent process.
10
END---------------
pid= 0
this is child process.
9
END---------------
|
简单守护进程例子:
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
|
def main():
''' 程序要执行的逻辑代码 '''
pass
# 创建守护进程函数
def createDaemon():
''' 第一块(创建第一个子进程) '''
# fork 第一个子进程(如果fork成功,父进程自杀,只留下第一个子进程继续向下运行)
try :
if os.fork() > 0 :
sys.exit( 0 )
except OSError, error:
print '(fork第一个子进程失败)fork #1 failed: %d (%s)' % (error.errno, error.strerror)
sys.exit( 1 )
''' 第一块结束 '''
###### 第一个进程创建成功后,它的ppid = 1,已是一个守护里程了,但有些功能上还是有被限制。
###### 所以下面再来创建一个子进程。第二次创建的子进程限制就没那多了,有可能没有,所以最安全。
###### 下面来创建第二个子进程。
os.chdir( '/' ) # 把第一个子进程的工作目录切换到 / (根目录)
os.setsid() # 第一个子进程取得程序的权限
os.umask( 0 ) # 第一个子进程取得工作目录的所有操作(目录的rwx)
''' 第二块(创建第二个子进程) '''
# fork 第二个子进程(如果fork成功,第一个子进程自杀,只留下新创建的第二个子进程)
try :
pid = os.fork()
if pid > 0 :
print 'Daemon PID %d' % pid
sys.exit( 0 )
except OSError, error:
print '(fork第二个子进程失败)fork #2 failed: %d (%s)' % (error.errno, error.strerror)
sys.exit( 1 )
''' 第二块结束 '''
####### 通过上面两个 try 语句块,只留下了第二个子进程在运行了。这时第二个子进程的ppid=1。
####### 创建的第二个子进程,可以说是一个不受限的守护进程了。
# 重定向标准IO(因为只有第二个子进程在运行了,所以也就是指定整个程序的输入、输出、错误流)
# sys.stdout.flush() # 清除程序运行空间的输出流
# sys.stderr.flush() # 清除程序运行空间的错误流
# inputS = file("/dev/null", 'r') # 定义一个 inputS 文件对象
# outputS = file("/dev/null", 'a+') # 定义一个 outputS 文件对象
# errorS = file("/dev/null", 'a+', 0) # 定义一个 errorS 文件对象
# os.dup2(inputS.fileno(), sys.stdin.fileno()) # 把程序的输入流重定向到上面定义的 inputS 文件对象上。
# os.dup2(so.fileno(), sys.stdout.fileno()) # 把程序的 输出流 重定向到上面定义的 outputS 文件对象上。
# os.dup2(se.fileno(), sys.stderr.fileno()) # 把程序的 错误流 重定向到上面定义的 errorS 文件对象上。
main() # main函数为真正程序逻辑代码
if __name__ = = "__main__" :
if platform.system() = = "Linux" :
createDaemon()
else :
sys.exit()
|
带控制参数的例子:
编写守护进程的基类,用于继承:
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
|
# 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)
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)
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
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
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
|
编写自己的类:
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
|
#导入刚刚编写的基类
from daemon import Daemon
#继承
class MyTestDaemon(Daemon):
#重写run方法,就是你要后台运行的函数
def run( self ):
#后台运行的函数,比如shell输出到自己定义的文件
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 )
|
关于两次fork
第二个fork不是必须的,只是为了防止进程打开控制终端。
打开一个控制终端的条件是该进程必须是session leader。第一次fork,setsid之后,子进程成为session leader,进程可以打开终端;第二次fork产生的进程,不再是session leader,进程则无法打开终端。
也就是说,只要程序实现得好,控制程序不主动打开终端,无第二次fork亦可。
代码实现
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
|
# 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)
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)
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
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
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
|
使用测试
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
|
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
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
[daemon] python test.py start 23:45:42
[daemon] cat /tmp/daemon-example.pid 23:45:49
8532
[daemon] ps -ef|grep 8532 | grep -v grep 23:46:07
502 8532 1 0 11:45下午 ?? 0:00.00 python test.py start
[daemon] tail -f /tmp/daemon-example.log 23:46:20
Daemon started with pid 8532
Daemon Alive! Fri Dec 2 23:45:49 2016
Daemon Alive! Fri Dec 2 23:45:54 2016
Daemon Alive! Fri Dec 2 23:45:59 2016
Daemon Alive! Fri Dec 2 23:46:04 2016
Daemon Alive! Fri Dec 2 23:46:09 2016
Daemon Alive! Fri Dec 2 23:46:14 2016
Daemon Alive! Fri Dec 2 23:46:19 2016
Daemon Alive! Fri Dec 2 23:46:24 2016
Daemon Alive! Fri Dec 2 23:46:29 2016
Daemon Alive! Fri Dec 2 23:46:34 2016
[daemon] python test.py stop 23:46:36
[daemon] ps -ef|grep 8532 | grep -v grep 23:46:43
|
原文链接:http://www.cnblogs.com/zimsan/p/7840306.html