考慮如下場景:你編寫了乙個python服務程式,並且在命令列下啟動,而你的命令行會話又被終端所控制,python服務成了終端程式的乙個子程序。因此如果你關閉了終端,這個命令列程式也會隨之關閉。
要使你的python服務不受終端影響而常駐系統,就需要將它變成守護程序。
守護程序就是daemon程式,是一種在系統後台執行的程式,它獨立於控制終端並且執行一些週期任務或觸發事件,通常被命名為"d"字母結尾,如常見的httpd、syslogd、systemd和dockerd等。
python可以很簡潔地實現守護程序,下面先給出**和相應注釋:
# coding=utf8import os
import sys
import atexit
def daemonize(pid_file=none):
"""建立守護程序
:param pid_file: 儲存程序id的檔案
:return:
"""# 從父程序fork乙個子程序出來
pid = os.fork()
# 子程序的pid一定為0,父程序大於0
if pid:
# 退出父程序,sys.exit()方法比os._exit()方法會多執行一些重新整理緩衝工作
sys.exit(0)
# 子程序預設繼承父程序的工作目錄,最好是變更到根目錄,否則回影響檔案系統的解除安裝
os.chdir('/')
# 子程序預設繼承父程序的umask(檔案許可權掩碼),重設為0(完全控制),以免影響程式讀寫檔案
os.umask(0)
# 讓子程序成為新的會話組長和程序組長
os.setsid()
# 注意了,這裡是第2次fork,也就是子程序的子程序,我們把它叫為孫子程序
_pid = os.fork()
if _pid:
# 退出子程序
sys.exit(0)
# 此時,孫子程序已經是守護程序了,接下來重定向標準輸入、輸出、錯誤的描述符(是重定向而不是關閉, 這樣可以避免程式在 print 的時候出錯)
# 重新整理緩衝區先,小心使得萬年船
sys.stdout.flush()
sys.stderr.flush()
# dup2函式原子化地關閉和複製檔案描述符,重定向到/dev/nul,即丟棄所有輸入輸出
with open('/dev/null') as read_null, open('/dev/null', 'w') as write_null:
os.dup2(read_null.fileno(), sys.stdin.fileno())
os.dup2(write_null.fileno(), sys.stdout.fileno())
os.dup2(write_null.fileno(), sys.stderr.fileno())
# 寫入pid檔案
if pid_file:
with open(pid_file, 'w+') as f:
f.write(str(os.getpid()))
# 註冊退出函式,程序異常退出時移除pid檔案
atexit.register(os.remove, pid_file)
概括一下守護程序的編寫步驟:
fork出子程序,退出父程序
子程序變更工作目錄(chdir)、檔案許可權掩碼(umask)、程序組和會話組(setsid)
子程序fork孫子程序,退出子程序
孫子程序重新整理緩衝,重定向標準輸入/輸出/錯誤(一般到/dev/null,意即丟棄)
(可選)pid寫入檔案
為什麼要fork兩次
第一次fork,是為了脫離終端控制的魔爪。父程序之所以退出,是因為終端敲擊鍵盤、或者關閉時給它傳送了訊號;而fork出來的子程序,在父程序自殺後成為孤兒程序,進而被作業系統的init程序接管,因此脫離終端控制。
所以其實,第二次fork並不是必須的(很多開源專案裡的**就沒有fork兩次)。只不過出於謹慎考慮,防止程序再次開啟乙個控制終端。因為子程序現在是會話組長了(對話期的首次程序),有能力開啟控制終端,再fork一次,孫子程序就不能開啟控制終端了。
檔案描述符
linux是「一切皆檔案」,檔案描述符是核心為已開啟的檔案所建立的索引,通常是非負整數。程序通過檔案描述符執行io操作。
預設情況下,0代表標準輸入,1代表標準輸出,2代表標準錯誤。
umask許可權掩碼
我們知道,在linux中,任何乙個檔案都有讀(read)、寫(write)和執行(execute)的三種使用許可權。其中,讀的許可權用數字4代表,寫許可權是2,執行許可權是1。命令ls -l可以檢視檔案許可權,r/w/x分別表示具有讀/寫/執行許可權。
任何檔案,也都有使用者(user),使用者組(group),其他組(others)三種身份許可權。一般用3個數字表示檔案許可權,例如754:
而umask是為了控制預設許可權,防止新建檔案或資料夾具有全權。
系統一般預設為022(使用命令umask檢視),表示預設建立檔案的許可權是644,資料夾是755。你應該可以看出它們的規律,就是檔案許可權和umask的相加結果為666(笑),資料夾許可權和umask的相加結果為777。
程序組每個程序都屬於乙個程序組(pg,process group),程序組可以包含多個程序。
程序組有乙個程序組長(leader),程序組長的id(pid, process id)就作為整個程序組的id(pgid,process groupd id)。
會話組登陸終端時,就會創造乙個會話,多個程序組可以包含在乙個會話中。而建立會話的程序,就是會話組長。
已經是會話組長的程序,不可以再呼叫setsid()方法建立會話。因此,上面**中,子程序可以呼叫setsid(),而父程序不能,因為它本身就是會話組長。
另外,sh(bourne shell)不支援會話機制,因為會話機制需要shell支援工作控制(job control)。
守護程序與後台程序
通過&符號,可以把命令放到後台執行。它與守護程序是不同的:
守護程序與終端無關,是被init程序收養的孤兒程序;而後台程序的父程序是終端,仍然可以在終端列印
守護程序在關閉終端時依然堅挺;而後台程序會隨使用者退出而停止,除非加上nohup
守護程序改變了會話、程序組、工作目錄和檔案描述符,後台程序直接繼承父程序(shell)的
換句話說:守護程序就是默默地奮鬥打拼的有為青年,而後台程序是默默繼承老爸資產的富二代。
Python 建立守護程序的示例
考慮如下場景 你編寫了乙個python服務程式,並且在命令列下啟動,而你的命令行會話又被終端所控制,python服務成了終端程式的乙個子程序。因此如果你關閉了終端,這個命令列程式也會隨之關閉。要使你的python服務不受終端影響而常駐系統,就需要將它變成守護程序。守護程序就是daemon程式,是一種...
linux 守護程序詳解及建立守護程序
linux 守護程序詳解及建立守護程序 守護程序是一種後台執行並且獨立於所有終端控制之外的程序。守護程序的啟動 要啟動乙個守護程序,可以採取一下幾種方式 守護程序的建立 先來看乙個守護程序建立的例子 include include include include define maxfd 64 vo...
Linux 守護程序建立
守護程序是在後台執行,不受使用者的控制 守護程序沒有任何存在的父程序。如果乙個程序想成為守護程序,有fork 建立 然後終止父程序,脫離資源。例子 void init daemon int pid,i pid fork if pid exit 0 結束父程序 else exit 1 失敗退出 是子程...