原文地址: http://www.01happy.com/linux-python-daemon/
守護進程英文為daemon,像httpd、mysqld、vsftpd最后個字母d其實就是表示daemon的意思。
守護進程的編寫步驟:
- fork子進程,而后父進程退出,此時子進程會被init進程接管。
- 修改子進程的工作目錄、創建新進程組和新會話、修改umask。
- 子進程再次fork一個進程,這個進程可以稱為孫子進程,而后子進程退出。
- 重定向孫子進程的標准輸入流、標准輸出流、標准錯誤流到/dev/null。
完成上面的4個步驟,那么最終的孫子進程就稱為守護進程。先看下代碼,后面再分析下每個步驟的原因。
|
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
|
#!/usr/bin/env python
#coding=utf8
import
os, sys, time
#產生子進程,而后父進程退出
pid
=
os.fork()
if
pid >
0
:
sys.exit(
0
)
#修改子進程工作目錄
os.chdir(
"/"
)
#創建新的會話,子進程成為會話的首進程
os.setsid()
#修改工作目錄的umask
os.umask(
0
)
#創建孫子進程,而后子進程退出
pid
=
os.fork()
if
pid >
0
:
sys.exit(
0
)
#重定向標准輸入流、標准輸出流、標准錯誤
sys.stdout.flush()
sys.stderr.flush()
si
=
file
(
"/dev/null"
,
'r'
)
so
=
file
(
"/dev/null"
,
'a+'
)
se
=
file
(
"/dev/null"
,
'a+'
,
0
)
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(
10
)
f
=
open
(
'/home/test.txt'
,
'a'
)
f.write(
'hello'
)
|
上面的程序沒有任何錯誤處理,但是不影響原理分析。如果要應用到項目里,還需完善。下面筆者談下自己對每個步驟的理解。
1、fork子進程,父進程退出
通常,我們執行服務端程序的時候都會通過終端連接到服務器,成功連接后會加載shell環境,終端和shell都是進程,shell進程是終端進程的子進程,通過ps命令可以很容易的查看到。在這個shell環境下一開始執行的程序都是shell進程的子進程,自然會受到shell進程的影響。在程序里fork子進程后,父進程退出,對了shell進程來說,這個父進程就算執行完了,而產生的子進程會被init進程接管,從而也就脫離了終端的控制。
2、修改子進程的工作目錄
子進程在創建的時候會繼承父進程的工作目錄,如果執行的程序是在u盤里的,就會導致u盤不能卸載。
3、創建新會話
使用setsid后,子進程就會成為新會話的首進程(session leader);子進程會成為新進程組的組長進程;子進程沒有控制終端。
4、修改umask
由於umask會屏蔽權限,所以設定為0,這樣可以避免讀寫文件時碰到權限問題。
5、fork孫子進程,子進程退出
經過上面幾個步驟后,子進程會成為新的進程組老大,可以重新申請打開終端,為了避免這個問題,fork孫子進程出來。
6、重定向孫子進程的標准輸入流、標准輸出流、標准錯誤流到/dev/null
因為是守護進程,本身已經脫離了終端,那么標准輸入流、標准輸出流、標准錯誤流就沒有什么意義了。所以都轉向到/dev/null,就是都丟棄的意思。
