Python 筆記 – 分享 Python Daemon 範例
如果需要實作一個 Python Daemon,你會怎麼做?
也許會找找看套件,例如 python-daemon,但如果還需要進一步細微調整,也許套件不是那麼好調整。
這裡推薦一個很棒的 Python Daemon 範例,請參考這篇 A simple unix/linux daemon in Python
Python 2 環境
這篇範例是 Python2 的,如果你是 Python 2 環境,按照他的範例把 class Daemon 複製一份,再按照他下面的使用範例 MyDaemon 那樣,基本上就可以用了。
這邊 Base Class init 的 argument 可指定 pidfile、stdin、stdout、stderr 的路徑,如果還需要擴充其他的 argument ,就可以自行撰寫。
跑起來以後,你就可以看到你指定的 pid file 內會儲存一個數字,那個就是 daemon 的 pid。
Python 3 調整
以下是必要修改項目
-
- print ” 改為 print(”) (注意有的是寫 print str(xxx),沒有引號)
- Exception, e 要改寫為 Exception as (至少 L29, L44, L109 這三行要)
- file() 改為 open() (L51, L52, L53, L61, L72, L93)
- 承上,file() 改為 open() 之後 第 53 行的參數需要修改, a+ 要改 ab+
- se = file(self.stderr, ‘a+’, 0) -> se = open(self.stderr, ‘ab+’, 0)
其他的話,建議可以 follow 一下 A simple unix/linux daemon in Python 下方的 Comments,看看其他人遇到的問題或者情境,是否有符合你的情境。
原理研讀(2023updated)
可以從 def daemonize(self) 看一下。
他以 fork-twice 的方式,產出一個 Grandchild Process,並把 pid 以檔案的方式記錄下來,這樣就會有個 Process 常駐了。
關於要用 fork-twice 的方式來產生一個 Daemon,可以參考這些文章:
目前讀到的目的,大致就是為了要讓 Process 擺脫 Terminal,並希望 Daemon 的 Process 要成為 INIT Process 的 Child。
其中,fork 出 Child 後結束 Parent,就可以讓 INIT Process 接管此 Process。
如果 fork 兩次(產出 Child 與 Grandchild,並且立刻結束掉 Child)。其優點是可以讓 Parent 不必 wait Child,可以去做自己要做的事,然後 Grandchild 會因為 Child 的結束,而被 INIT Process 接管。
當然如果你的 Parent 沒有什麼特別要做的事情,那麼 fork 出一個 Child 後,立刻結束 Parent 就已經達成這個目的了,若是這麼操作,此時 Child 的 Parent 會變成 INIT Process。
至於擺脫 Terminal 的部分,通常我們可能是在某個 Terminal 啟動 Parent Process,如果不做任何處理,那麼 Terminal 結束時,也會要求 Parent Process 結束 (也就是收到 SIGHUP 信號),這是我們要想辦法擺脫的。
若在 fork 出的 Child 上執行 setsid,可以讓其變成 Session Leader,這就初步擺脫了原本啟動時的那個 Terminal 。
不過這時候 Child 自己是 Session Leader,仍允許建立與 Terminal 的連接,而若連上 Terminal,就又可能受到 Terminal 影響。
所以,再 fork 出一個 Grandchild,並且 Grandchild 不再執行 setsid,之後結束 Child。此時 Grandchild 既沒有 Terminal 也沒有 Session Leader,讓整個擺脫更完美、更保險一點。
關於 Terminal, Session 以及 Session Leader 相關知識,可參考以下資料:
- 行程群組(process group)、作業階段(session)和工作控制 (job control)
- Process Relationship 的『0x03 Sessions』以及『0x04 Controlling Terminal』
Leave a Reply