用PHP寫一個Daemon守護進程_PHP教程

來源:互聯網
上載者:User

Daemon守護進程

這又是一個有趣的概念,daemon在英語中是"精靈"的意思,就像我們經常在迪斯尼動畫裡見到的那些,有些會飛,有些不會,經常圍著動畫片的主人公轉來轉去,囉裡囉唆地提一些忠告,時不時倒黴地撞在柱子上,有時候還會想出一些小小的花招,把主人公從敵人手中救出來,正因如此,daemon有時也被譯作"守護神"。所以,daemon進程在國內也有兩種譯法,有些人譯作"精靈進程",有些人譯作"守護進程",這兩種稱呼的出現頻率都很高。

與真正的daemon相似,daemon進程也習慣於把自己隱藏在人們的視線之外,默默為系統做出貢獻,有時人們也把它們稱作"後台服務進程"。daemon進程的壽命很長,一般來說,從它們一被執行開始,直到整個系統關閉,它們才會退出。幾乎所有的伺服器程式,包括我們熟知的Apache和wu-FTP,都用daemon進程的形式實現。很多Linux下常見的命令如inetd和ftpd,末尾的字母d就是指daemon。

為什麼一定要使用daemon進程呢?Linux中每一個系統與使用者進行交流的介面稱為終端(terminal),每一個從此終端開始啟動並執行進程都會依附於這個終端,這個終端就稱為這些進程的控制終端(Controlling terminal),當控制終端被關閉時,相應的進程都會被自動關閉。關於這點,讀者可以用X-Window中的XTerm實驗一下,(每一個XTerm就是一個開啟的終端,)我們可以通過鍵入命令啟動應用程式,比如:$netscape 然後我們關閉XTerm視窗,剛剛啟動的netscape視窗也會隨之一同突然蒸發。但是daemon進程卻能夠突破這種限制,即使對應的終端關閉,它也能在系統中長久地存在下去,如果我們想讓某個進程長命百歲,不因為使用者或終端或其他的變化而受到影響,就必須把這個進程變成一個daemon進程。

Daemon進程的編程規則

如果想把自己的進程變成daemon進程,我們必須嚴格按照以下步驟進行:

1. 調用fork產生一個子進程,同時父進程退出。我們所有後續工作都在子進程中完成。這樣做我們可以:

  • 如果我們是從命令列執行的該程式,這可以造成程式執行完畢的假象,shell會回去等待下一條命令;
  • 剛剛通過fork產生的新進程一定不會是一個進程組的組長,這為第2步的執行提供了前提保障。

這樣做還會出現一種很有趣的現象:由於父進程已經先於子進程退出,會造成子進程沒有父進程,變成一個孤兒進程(orphan)。每當系統發現一個孤兒進程,就會自動由1號進程收養它,這樣,原先的子進程就會變成1號進程的子進程。

2. 調用setsid系統調用。這是整個過程中最重要的一步。它的作用是建立一個新的會話(session),並自任該會話的組長(session leader)。如果調用進程是一個進程組的組長,調用就會失敗,但這已經在第1步得到了保證。調用setsid有3個作用:

  • 讓進程擺脫原會話的控制;
  • 讓進程擺脫原進程組的控制;
  • 讓進程擺脫原控制終端的控制;

總之,就是讓調用進程完全獨立出來,脫離所有其他進程的控制。

3. 把當前工作目錄切換到根目錄。

如果我們是在一個臨時載入的檔案系統上執行這個進程的,比如:/mnt/floppy/,該進程的當前工作目錄就會是/mnt/floppy/。在整個進程運行期間該檔案系統都無法被卸下(umount),而無論我們是否在使用這個檔案系統,這會給我們帶來很多不便。解決的方法是使用chdir系統調用把當前工作目錄變為根目錄,應該不會有人想把根目錄卸下吧。

當然,在這一步裡,如果有特殊的需要,我們也可以把當前工作目錄換成其他的路徑,比如/tmp。

4. 將檔案許可權掩碼設為0。

這需要調用系統調用umask,參見附錄3。每個進程都會從父進程那裡繼承一個檔案許可權掩碼,當建立新檔案時,這個掩碼被用於設定檔案的預設存取權限,屏蔽掉某些許可權,如一般使用者的寫入權限。當另一個進程用exec調用我們編寫的daemon程式時,由於我們不知道那個進程的檔案許可權掩碼是什麼,這樣在我們建立新檔案時,就會帶來一些麻煩。所以,我們應該重新設定檔案許可權掩碼,我們可以設成任何我們想要的值,但一般情況下,大家都把它設為0,這樣,它就不會屏蔽使用者的任何操作。

如果你的應用程式根本就不涉及建立新檔案或是檔案存取權限的設定,你也完全可以把檔案許可權掩碼一腳踢開,跳過這一步。

5. 關閉所有不需要的檔案。

同檔案許可權掩碼一樣,我們的新進程會從父進程那裡繼承一些已經開啟了的檔案。這些被開啟的檔案可能永遠不被我們的daemon進程讀或寫,但它們一樣消耗系統資源,而且可能導致所在的檔案系統無法卸下。需要指出的是,檔案描述符為0、1和2的三個檔案(檔案描述符的概念將在下一章介紹),也就是我們常說的輸入、輸出和報錯這三個檔案也需要被關閉。很可能不少讀者會對此感到奇怪,難道我們不需要輸入輸出嗎?但事實是,在上面的第2步後,我們的daemon進程已經與所屬的控制終端失去了聯絡,我們從終端輸入的字元不可能達到daemon進程,daemon進程用常規的方法(如printf)輸出的字元也不可能在我們的終端上顯示出來。所以這三個檔案已經失去了存在的價值,也應該被關閉。

編寫守護進程

在我之前的文章中,介紹過Gearman的使用。在我的項目中,我使用了PHP來編寫一直啟動並執行Worker。如果按照Gearman官方推薦的例子,只是簡單的一個迴圈來等待任務,會有一些問題,包括:1、當代碼進行過修改之後,如何讓代碼的修改生效;2、重啟Worker的時候,如何保證當前的任務處理完成才重啟。

針對這個問題,我考慮了以下的解決方案:

  1. 每次修改完代碼後,Worker需要手工重啟(先殺死然後啟動)。這個只能解決重新載入設定檔的問題。
  2. 在Worker中設定,單次任務迴圈完成後,就對Worker進行重啟。這個方案的問題在於消耗比較大。
  3. 在Worker中添加一個退出函數,如果需要Worker退出的時候,在Client端發送一個優先順序比較高的退出調用。這個需要用戶端配合,在使用後台類任務時,不太適合。
  4. 在Worker中檢查檔案是否發生變化,如果發生了變化,退出並重啟自身。
  5. 為Worker編寫訊號控制,接受重啟指令,類似於 http restart graceful 指令。

最後,結合4和5兩種方法,可以實現這樣一個Daemon,如果設定檔發生了變化,他就會自動重啟;如果接受到了使用者的 kill -1 pid 訊號,也會重新啟動。

代碼如下:

 at 2011-12-04$init_md5 = md5_file( 'config.php');// register signal handlerpcntl_signal( SIGALRM, "signal_handler", true );pcntl_signal( SIGHUP, 'signal_handler', TRUE );$job_flag = FALSE;    //Job status flag, to justify if the job has been finished$signal_flag = FALSE;    //Signal status flag, to justify whether we received the kill -1 signalwhile( 1 ){    $job_flag = FALSE;    //Job status flag    print "Worker start running ... n";    sleep(5);    print "Worker's task done ... n";    $flag = TRUE;    //Job status flag    AutoStart( $signal_flag );}function signal_handler( $signal ) {    global $job_flag;    global $signal_flag;    switch( $signal ){        case SIGQUIT:            print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGQUIT - No : $signal n";            exit(0);            break;        case SIGSTOP:            print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGSTOP - No : $signal n";            break;        case SIGHUP:            print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGHUP - No : $signal n";            if( $flag === TRUE ){                AutoStart( TRUE );            }else{                $signal_flag = TRUE;            }            break;        case SIGALRM:            print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGALRM - No : $signal n";            //pcntl_exec( '/bin/ls' );            pcntl_alarm( 5 );            break;        default:            break;    }}function AutoStart( $signal = FALSE, $filename = 'config.php' ){    global $init_md5;    if( $signal || md5_file( $filename ) != $init_md5 ){        print "The config file has been changed, we are going to restart. n";        $pid = pcntl_fork();        if( $pid == -1 ){            print "Fork error n";        }else if( $pid > 0 ){            print "Parent exit n";            exit(0);        }else{            $init_md5 = md5_file( $filename );            print "Child continue to run n";        }    }}?>

http://www.bkjia.com/PHPjc/752556.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/752556.htmlTechArticleDaemon守護進程 這又是一個有趣的概念,daemon在英語中是"精靈"的意思,就像我們經常在迪斯尼動畫裡見到的那些,有些會飛,有些不會,經...

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.