天天看點

python 守護程序(daemon)

<code>守護程序的編寫步驟:</code>

<code>1</code><code>、fork子程序,然後父程序退出,此時子程序會被init程序接管。</code>

<code>2</code><code>、修改子程序的工作目錄,建立新程序組合新會話,修改umask。</code>

<code>3</code><code>、子程序再次fork一個程序,這個程序可以稱為孫子程序,然後子程序退出。</code>

<code>4</code><code>、重定向孫子程序的标準輸入流,标準輸出流,标準錯誤到</code><code>/</code><code>dev</code><code>/</code><code>null</code>

<code>#!/usr/bin/env python</code>

<code># -*- coding:utf-8 -*-</code>

<code>import</code> <code>sys, os</code>

<code>'''将目前程序fork為一個守護程序</code>

<code>   </code><code>注意:如果你的守護程序是由inetd啟動的,不要這樣做!inetd完成了</code>

<code>   </code><code>所有需要做的事情,包括重定向标準檔案描述符,需要做的事情隻有chdir()和umask()了</code>

<code>'''</code>

<code>def</code> <code>daemon(stdin</code><code>=</code><code>'/dev/null'</code><code>, stdout</code><code>=</code><code>'/dev/null'</code><code>, stderr</code><code>=</code><code>'/dev/null'</code><code>):</code>

<code>    </code><code># 重定向标準檔案描述符(預設情況下定向到/dev/null)  </code>

<code>    </code><code>try</code><code>:</code>

<code>        </code><code>pid </code><code>=</code> <code>os.fork()</code>

<code>        </code><code># 父程序(會話組頭領程序)退出,這意味着一個非會話組頭領程序永遠不能重新獲得控制終端。  </code>

<code>        </code><code>if</code> <code>pid &gt; </code><code>0</code><code>:</code>

<code>            </code><code>sys.exit(</code><code>0</code><code>)  </code><code># 父程序退出  </code>

<code>    </code><code>except</code> <code>OSError, e:</code>

<code>        </code><code>sys.stderr.write(</code><code>"fork #1 failed: (%d) %s\n"</code> <code>%</code> <code>(e.errno, e.strerror))</code>

<code>        </code><code>sys.exit(</code><code>1</code><code>)</code>

<code>        </code><code># 從母體環境脫離  </code>

<code>    </code><code>os.chdir(</code><code>"/"</code><code>)  </code><code># chdir确認程序不保持任何目錄于使用狀态,否則不能umount一個檔案系統。也可以改變到對于守護程式運作重要的檔案所在目錄  </code>

<code>    </code><code>os.umask(</code><code>0</code><code>)  </code><code># 調用umask(0)以便擁有對于寫的任何東西的完全控制,因為有時不知道繼承了什麼樣的umask。  </code>

<code>    </code><code>os.setsid()  </code><code># setsid調用成功後,程序成為新的會話組長和新的程序組長,并與原來的登入會話和程序組脫離。  </code>

<code>    </code><code># 執行第二次fork  </code>

<code>            </code><code>sys.exit(</code><code>0</code><code>)  </code><code># 第二個父程序退出  </code>

<code>        </code><code>sys.stderr.write(</code><code>"fork #2 failed: (%d) %s\n"</code> <code>%</code> <code>(e.errno, e.strerror))</code>

<code>        </code><code># 程序已經是守護程序了,重定向标準檔案描述符  </code>

<code>    </code><code>for</code> <code>f </code><code>in</code> <code>sys.stdout, sys.stderr: f.flush()</code>

<code>    </code><code>si </code><code>=</code> <code>open</code><code>(stdin, </code><code>'r'</code><code>)</code>

<code>    </code><code>so </code><code>=</code> <code>open</code><code>(stdout, </code><code>'a+'</code><code>)</code>

<code>    </code><code>se </code><code>=</code> <code>open</code><code>(stderr, </code><code>'a+'</code><code>)</code>

<code>    </code><code>os.dup2(si.fileno(), sys.stdin.fileno())  </code><code># dup2函數原子化關閉和複制檔案描述符  </code>

<code>    </code><code>os.dup2(so.fileno(), sys.stdout.fileno())</code>

<code>    </code><code>os.dup2(se.fileno(), sys.stderr.fileno())</code>

<code># 示例函數:每秒列印一個數字和時間戳  </code>

<code>def</code> <code>main():</code>

<code>    </code><code>import</code> <code>time</code>

<code>    </code><code>sys.stdout.write(</code><code>'Daemon started with pid %d\n'</code> <code>%</code> <code>os.getpid())</code>

<code>    </code><code>sys.stdout.write(</code><code>'Daemon stdout output\n'</code><code>)</code>

<code>    </code><code>sys.stderr.write(</code><code>'Daemon stderr output\n'</code><code>)</code>

<code>    </code><code>c </code><code>=</code> <code>0</code>

<code>    </code><code>while</code> <code>True</code><code>:</code>

<code>        </code><code>sys.stdout.write(</code><code>'%d: %s\n'</code> <code>%</code> <code>(c, time.ctime()))</code>

<code>        </code><code>sys.stdout.flush()</code>

<code>        </code><code>c </code><code>=</code> <code>c </code><code>+</code> <code>1</code>

<code>        </code><code>time.sleep(</code><code>1</code><code>)</code>

<code>if</code> <code>__name__ </code><code>=</code><code>=</code> <code>"__main__"</code><code>:</code>

<code>    </code><code>daemone(</code><code>'/dev/null'</code><code>, </code><code>'/tmp/daemon_stdout.log'</code><code>, </code><code>'/tmp/daemon_error.log'</code><code>)</code>

<code>    </code><code>main()</code>

<code># 可以通過指令ps -ef | grep daemon.py檢視背景運作的繼承</code>

<code># 在/tmp/daemon_error.log會記錄錯誤運作日志</code>

<code># 在/tmp/daemon_stdout.log會記錄标準輸出日志。</code>

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,如需轉載請自行聯系原作者