[轉]Python 守護進程

守護進程:通常被定義爲一個後臺進程,而且它不屬於任何一個終端會話(terminal session)。許多系統服務由守護程序實施;如網絡服務,打印等。 
下面是轉自一位網友寫的編寫守護進程的步驟:
1. 調用fork()以便父進程可以退出,這樣就將控制權歸還給運行你程序的
    命令行或shell程序。需要這一步以便保證新進程不是一個進程組頭領進程(process
    group leader)。下一步,‘setsid()’,會因爲你是進程組頭領進程而失敗。
2. 調用‘setsid()’ 以便成爲一個進程組和會話組的頭領進程。由於一個控制終端
    與一個會話相關聯,而且這個新會話還沒有獲得一個控制終端,我們的進程沒
    有控制終端,這對於守護程序來說是一件好事。
3. 再次調用‘fork()’所以父進程(會話組頭領進程)可以退出。這意味着我們,一
    個非會話組頭領進程永遠不能重新獲得控制終端。
4. 調用‘chdir("/")’確認我們的進程不保持任何目錄於使用狀態。不做這個會導
    致系統管理員不能卸裝(umount)一個文件系統,因爲它是我們的當前工作目錄。
    [類似的,我們可以改變當前目錄至對於守護程序運行重要的文件所在目錄]
5. 調用‘umask(0)’以便我們擁有對於我們寫的任何東西的完全控制。我們不知
    道我們繼承了什麼樣的umask。
    [這一步是可選的](譯者注:這裏指步驟5,因爲守護程序不一定需要寫文件)
6. 調用‘close()’關閉文件描述符0,1和2。這樣我們釋放了從父進程繼承的標
    準輸入,標準輸出,和標準錯誤輸出。我們沒辦法知道這些文描述符符可能
    已經被重定向去哪裏。注意到許多守護程序使用‘sysconf()’來確認
    ‘_SC_OPEN_MAX’的限制。‘_SC_OPEN_MAX’告訴你每個進程能夠打
    開的最多文件數。然後使用一個循環,守護程序可以關閉所有可能的文件描
    述符。你必須決定你需要做這個或不做。如果你認爲有可能有打開的文件描
    述符,你需要關閉它們,因爲系統有一個同時打開文件數的限制。
7. 爲標準輸入,標準輸出和標準錯誤輸出建立新的文件描述符。即使你不打算
    使用它們,打開着它們不失爲一個好主意。準確操作這些描述符是基於各自
    愛好;比如說,如果你有一個日誌文件,你可能希望把它作爲標準輸出和標
    準錯誤輸出打開,而把‘/dev/null’作爲標準輸入打開;作爲替代方法,你可
    以將‘/dev/console’作爲標準錯誤輸出和/或標準輸出打開,而‘/dev/null’作
    爲標準輸入,或者任何其它對你的守護程序有意義的結合方法。(譯者注:一
    般使用dup2函數原子化關閉和複製文件描述符。
說實話,上面這段文字看着有點雲裏霧裏,下面看個具體的代碼(我只粘貼了函數的第一部分,也是最重要的一部分,要查看整個代碼,請移步到這http://www.pythonid.com/bbs/redirect.php?tid=239&goto=lastpost&highlight=自行查看):

def daemonize(stdout='/dev/null', stderr=None, stdin='/dev/null',
              pidfile=None, startmsg = 'started with pid %s' ):
    '''
         This forks the current process into a daemon.
         The stdin, stdout, and stderr arguments are file names that
         will be opened and be used to replace the standard file descriptors
         in sys.stdin, sys.stdout, and sys.stderr.
         These arguments are optional and default to /dev/null.
        Note that stderr is opened unbuffered, so
        if it shares a file with stdout then interleaved output
         may not appear in the order that you expect.
     '''
    # flush io
    sys.stdout.flush()
    sys.stderr.flush()
    # Do first fork.
    try:
        pid = os.fork()
        if pid > 0: sys.exit(0) # Exit first parent.
    except OSError, e:
        sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
        sys.exit(1)      
    # Decouple from parent environment.
    os.chdir("/")
    os.umask(0)
    os.setsid()
    # Do second fork.
    try:
        pid = os.fork()
        if pid > 0: sys.exit(0) # Exit second parent.
    except OSError, e:
        sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
        sys.exit(1)
    # Open file descriptors and print start message
    if not stderr: stderr = stdout
        si = file(stdin, 'r')
        so = file(stdout, 'a+')
        se = file(stderr, 'a+', 0)  #unbuffered
        pid = str(os.getpid())
        sys.stderr.write("\n%s\n" % startmsg % pid)
        sys.stderr.flush()
    if pidfile: file(pidfile,'w+').write("%s\n" % pid)
    # Redirect standard file descriptors.
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

仔細比對前面說的步驟和代碼,大體上是沒有什麼問題了,但是疑問就來了,os.fork()到底是怎麼工作的呢,GOOGLE了個遍,最後的結論是這樣:
  父進程執行代碼到os.fork()處時,會將自己整個拷貝一份(即子進程)這時候父進程os.fork()的返回值大於零(即子進程的PID),子進程os.fork()的返回值等於零,父進程結束,子進程繼續執行,這時候又遇到第二個os.fork(),如上次一樣,原來的子進程變成了父進程,又產生新的子進程,之後父進程就結束。這就能夠說通第一次是避免process group leader,第二次是避免session group leader。子進程就變成了一個無終端,無會話的完全自我掌控的後臺進程了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章