PHP守護進程化


什么是守護進程?

一個守護進程通常補認為是一個不對終端進行控制的后台任務。它有三個很顯著的特征:在后台運行,與啟動他的進程脫離,無須控制終端。常用的實現方式是fork() -> setsid() -> fork()

在glibc里有一個函數daemon。調用此函數,就可使當前進程脫離終端變成一個守護進程,具體內容參見man daemon。PHP中暫時沒有此函數,PHP程序實現守護進程化有2種方法:

1.使用系統命令nohup

nohup php myprog.php > log.txt &

&,這樣執行程序雖然也是轉為后台運行,但實際上是依賴終端的,當用戶退出終端時進程就會被殺掉。需要使用nohup來實現

2.使用supervisor工具 (推薦此方案)

  http://www.cnblogs.com/loveyouyou616/p/7028257.html

3.當然也可以用程序實現(不建議生產環境使用)

C程序實現:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4 #include <sys/types.h>
 5 #include <sys/stat.h>
 6 #include <fcntl.h>
 7 
 8 //實現守護進程步驟
 9 void crete_daemon(void)
10 {
11     pid_t pid = 0;
12     pid = fork();
13     if (pid<0)
14     {
15         perror("fork");
16         exit(-1);
17     }
18 
19     if (pid > 0)
20     {
21         //1.父進程直接退出
22         exit(0);
23     }
24 
25     //2.
26     //執行到這里就是子進程
27     //setsid 將當前進程設置為一個新的會話期session,目的就是
28     //讓當前進程脫離控制台,成為守護進程。
29     pid = setsid();
30     if (pid < 0)
31     {
32         perror("setsid");
33         exit(-1);
34     }
35 
36     //3.設置當前進程的工作目錄為根目錄,不依賴於其他
37     chdir("/");
38 
39     //4.umask設置為0確保將來進程有最大的文件操作權限
40     umask(0);
41 
42     //5.關閉文件描述符
43     //先要獲取當前系統中所允許打開的最大文件描述符數目
44     int i = 0;
45     int cnt = sysconf(_SC_OPEN_MAX);
46     for (i=0;i<cnt;i++)
47     {
48         close(i);
49     }
50 
51     //將0,1,2定位到 /dev/null
52     open("/dev/null",O_RDWR);
53     open("/dev/null",O_RDWR);
54     open("/dev/null",O_RDWR);
55 
56 }
57 
58 int main(void)
59 {
60 
61     crete_daemon();
62 
63     while(1)
64     {
65         printf("I am running.\n");
66         sleep(1);
67     }
68 
69     return 0;
70 }

PHP腳本函數實現:

/*
 *根據c語言的實現思路即可。
 * 因為需要關閉 標准io,所以這里使用redis方便測試。
 */
<?php
//php代碼實現守護進程
function daemon(){
    $pid = pcntl_fork();
    if($pid < 0){
        die("fork(1) failed!\n");
    }elseif($pid > 0){
        //1.父進程直接退出
        exit;
    }

    //執行到這里就是子進程
    //2.建立一個有別於終端的新session以脫離終端
    $sid = posix_setsid();
    if (!$sid) {
        die("setsid failed!\n");
    }

    //這一部不是必須的
    $pid = pcntl_fork();
    if($pid < 0){
        die("fork(1) failed!\n");
    }elseif($pid > 0){
        exit; //父進程退出, 剩下子進程成為最終的獨立進程
    }


    //3.設置當前進程的工作目錄為根目錄,不依賴於其他
    chdir("/");
    //4.umask設置為0確保將來進程有最大的文件操作權限
    umask(0);


    //5.關閉標准I/O流
    if (defined('STDIN'))
        fclose(STDIN);
    if (defined('STDOUT'))
        fclose(STDOUT);
    if (defined('STDERR'))
        fclose(STDERR);
}

daemon();

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

while (true) {
    //echo 1; 不要任何輸出echo 因為標准輸入流關閉了,會異常導致進程終止
    $redis->set("name", "lemon".mt_rand());
    sleep(3);
}

測試結果:

守護進程:

 

這里較為關鍵的二個php函數是pcntl_fork()和posix_setsid()

  • fork()一個進程,則表示創建了一個運行進程的副本,副本被認為是子進程,而原始進程被認為是父進程。當fork()運行之后,則可以脫離啟動他的進程與終端控制等,也意味着父進程可以自由退出。
  • setsid(),它首先使新進程成為一個新會話的“領導者”,最后使該進程不再控制終端,這也是成為守護進程最關鍵的一步,這意味着,不會隨着終端關閉而強制退出進程。對於一個不會被中斷的常駐進程來說,這是很關鍵的一步。
  • 進行最后一次fork(),這一步不是必須的,但通常都這么做,它最大的意義是防止獲得控制終端。(在直接打開一個終端設備,而且沒有使用O_NOCTTY標志的情況下, 會獲得控制終端)

其它事項說明:

  • chdir() 守護進程默認繼承了父進程的當前工作目錄,當系統磁盤發生umount時將造成諸多的麻煩,通常將”/” 作為守護進程的當前工作目錄,可以避免上述的問題
  • umask() 守護進程默認繼承了父進程的文件權限掩碼,這就給該子進程使用文件帶來了諸多的麻煩。因此,把文件權限掩碼設置為0,可以大大增強該守護進程的靈活性
  • fclose(STDIN), fclose(STDOUT), fclose(STDERR) 關閉標准I/O流。用fork函數新建的子進程會從父進程那里繼承一些已經打開了的文件。這些被打開的文件可能永遠不會被守護進程讀寫,但它們一樣消耗系統資源,而且可能導致所在的文件系統無法卸下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM