Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erläuterung des Daemon-Prozesses zur Implementierung der Systemprogrammierung in PHP

Detaillierte Erläuterung des Daemon-Prozesses zur Implementierung der Systemprogrammierung in PHP

不言
不言Original
2018-04-13 10:48:051225Durchsuche

Der Inhalt dieses Artikels besteht darin, Ihnen eine detaillierte Erklärung zum Schreiben von Daemons für die Systemprogrammierung in PHP zu geben. Er hat einen gewissen Referenzwert Gruppe, Konzepte wie Sitzung, Steuerterminal und Steuerprozess

Prozessgruppe

: Jeder Prozess hat eine Prozessgruppe (Prozessgruppe), zu der er gehört, und die Prozessgruppe Hat einen Prozessleiter (Prozessgruppenleiter), ist die Prozessgruppen-ID die Prozessnummer des Prozessgruppenleiters. Um also festzustellen, ob ein Prozess der Prozessgruppenleiter ist, müssen Sie nur vergleichen, ob der Prozessname mit seinem Prozess übereinstimmt Gruppen-ID. Sie können es in PHP verwenden. Die Funktion posix_getpgrp() ruft die Prozessgruppen-ID des aktuellen Prozesses ab, und posix_getpid() wird verwendet, um die Prozess-ID des aktuellen Prozesses zu erhalten.


Die obige Routine gibt Folgendes aus:

<?php

function isGroupLeader() {
    return posix_getpgrp() == posix_getpid();
}

$pid = pcntl_fork();

if($pid == 0) {
    echo &#39;子进程:&#39; . PHP_EOL;
}
elseif($pid > 0) {
    sleep(2);
    echo &#39;父进程:&#39; . PHP_EOL;
}

echo "当前进程组gid:" . posix_getpgrp() . PHP_EOL;
echo "当前进程号pid:" . posix_getpid() . PHP_EOL;

if (isGroupLeader()) {
    echo &#39;Is a process group leader&#39; . PHP_EOL;
}
else {
    echo &#39;Is not a process group leader&#39; . PHP_EOL;
}



Sitzung
子进程:
当前进程组gid:15827
当前进程号pid:15828
Is not a process group leader
父进程:
当前进程组gid:15827
当前进程号pid:15827
Is a process group leader
: Eine Sitzung ist eine Sammlung mehrerer Prozessgruppen in der Sitzung, und die Sitzungs-ID ist die Prozessgruppen-ID des Sitzungsleiters Funktion posix_getsid(int $pid), um die Sitzungs-ID des angegebenen Prozesses abzurufen, oder Sie können die Funktion posix_setsid() verwenden, um eine neue Sitzung zu erstellen. Zu diesem Zeitpunkt wird der Prozess zum Sitzungsleiter der neuen Sitzung Gibt die neu erstellte Sitzungs-ID erfolgreich zurück, oder -1, wenn ein Fehler auftritt.

Beachten Sie, dass posix_setsid() unter Linux aufgerufen wird Der Prozess der Funktion darf nicht der Prozessgruppenleiter sein , sonst schlägt der Aufruf fehl. Dies liegt daran, dass ein Prozess in einer Prozessgruppe nicht mehrere Sitzungen gleichzeitig umfassen kann .

Dokumenteinführung zu Setsid unter Linux:


Die obige Routine gibt Folgendes aus:
setsid()  creates  a  new  session  if  the calling process is not a process group leader.  The calling process is the leader of the new session, the process group
       leader of the new process group, and has no controlling tty.  The process group ID and session ID of the calling process are set to the PID of the calling process.
       The calling process will be the only process in this new process group and in this new session.
<?php

function isGroupLeader() {
    return posix_getpgrp() == posix_getpid();
}

echo "当前会话id: " . posix_getsid(0) . PHP_EOL; //传0表示获取当前进程的会话id

if (isGroupLeader()) {
    echo "当前进程是进程组长\n";
}

$ret = posix_setsid();  //创建一个新会话
var_dump($ret);  //由于当前进程是进程组长,此处会返回-1, 表示调用失败



Wie erstelle ich dann eine neue Sitzung? Nachdem wir pcntl_fork() zum Erstellen eines untergeordneten Prozesses verwendet haben, ist dies der Fall Der untergeordnete Prozess ist nicht der Prozessleiter, daher können Sie den untergeordneten Prozess zum Erstellen einer neuen Sitzung verwenden.

当前会话id: 13000
当前进程是进程组长
int(-1)



Die obige Routine gibt Folgendes aus:

<?php
function isGroupLeader()
{
     return posix_getpgrp() == posix_getpid();
}
echo "当前进程所属会话ID:" . posix_getsid(0) . PHP_EOL;

$pid = pcntl_fork();

if ($pid > 0) {
    exit(0); // 让父进程退出
}
elseif ($pid == 0) {
    if (isGroupLeader()) {
        echo "是进程组组长\n";
    } else {
        echo "不是进程组组长\n";
    }
    echo "进程组ID:" . posix_getpgrp() . PHP_EOL;  
    echo "进程号pid: " . posix_getpid() . PHP_EOL;

    $ret = posix_setsid();
    var_dump($ret);

    echo "当前进程所属会话ID:" . posix_getsid(0) . PHP_EOL;
}



Mit dem untergeordneten Prozess wurde erfolgreich eine neue Sitzung erstellt.

当前进程所属会话ID:13000
[root@localhost php]# 不是进程组组长
进程组ID:15856
进程号pid: 15857
int(15857)
当前进程所属会话ID:15857


Steuerterminal
und Steuerprozess: (Terminal ist der allgemeine Begriff für alle Eingabe- und Ausgabegeräte, wie z. B. Tastatur, Maus und Monitor ein Terminal) Eine Sitzung Es kann ein Steuerterminal geben, und ein Steuerterminal ist exklusiv für eine Sitzung. Wenn die Sitzung zum ersten Mal erstellt wird, gibt es kein Kontrollterminal, aber der Sitzungsleiter kann die Eröffnung eines Terminals beantragen. Wenn dieses Terminal nicht das Kontrollterminal anderer Sitzungen ist, wird das Terminal zu diesem Zeitpunkt zum Kontrollterminal der Sitzung. Der Sitzungsleiter wird als Kontrollprozess bezeichnet.

Um festzustellen, ob eine Sitzung unter Linux über ein Kontrollterminal verfügt, können wir versuchen, eine spezielle Datei /dev/tty zu öffnen, die auf das verweist Echtes Steuerterminal. Wenn es erfolgreich geöffnet wird, bedeutet dies, dass Sie über ein Steuerterminal verfügen. Andernfalls gibt es kein Steuerterminal.


Der untergeordnete Prozess der obigen Routine erstellt eine neue Sitzung, und dann versuchen sowohl der übergeordnete als auch der untergeordnete Prozess, die Datei /dev/tty zu öffnen der Routine ist wie folgt:

<?php
function isGroupLeader()
{
     return posix_getpgrp() == posix_getpid();
}

$pid = pcntl_fork();

if ($pid > 0) {
        sleep(1);
        $fp = fopen("/dev/tty", "rb");
        if ($fp) {
            echo "父进程会话 " . posix_getsid(0) . " 拥有控制终端\n";
        } else {
            echo "父进程会话 " . posix_getsid(0) . " 不拥有控制终端\n";
        }

    exit(0); // 让父进程退出
}
elseif ($pid == 0) {
    if (isGroupLeader()) {
        echo "是进程组组长\n";
    } else {
        echo "不是进程组组长\n";
    }

    $ret = posix_setsid();
    var_dump($ret);

    $fp = fopen("/dev/tty", "rb");
    if ($fp) {
            echo "子进程会话 " . posix_getsid(0) . " 拥有控制终端\n";
    }   else {
            echo "子进程会话 " . posix_getsid(0) . " 不拥有控制终端\n";
    }
}



SIGHUP-Signal generieren
不是进程组组长
int(15906)
PHP Warning:  fopen(/dev/tty): failed to open stream: No such device or address in /root/php/setsid.php on line 30

Warning: fopen(/dev/tty): failed to open stream: No such device or address in /root/php/setsid.php on line 30
子进程会话 15906 不拥有控制终端
父进程会话 13000 拥有控制终端

1 Wenn eine Sitzung die Kontrolle über das Terminal verliert, sendet der Kernel ein SIGHUP-Signal, und normalerweise ist der steuernde Prozess der Sitzung der Shell-Prozess SIGHUP-Signal an alle von ihm erstellten Prozessgruppen (Vordergrund- oder Hintergrundprozessgruppen) und dann beenden. Die Standardverarbeitungsmethode für einen Prozess, der ein SIGHUP-Signal empfängt, ist natürlich auch das Beenden des Prozesses ignoriere es.

2. Wenn der Steuerungsprozess beendet wird, sendet der Kernel außerdem ein SIGHUP-Signal an alle Mitglieder der Vordergrundprozessgruppe des Terminals.


Verwenden Sie php sighup.php, um das Programm auszuführen, schließen Sie dann direkt das Terminal und melden Sie sich erneut bei der Shell an. Sie werden feststellen, dass das Programm noch läuft , daemon.txt-Datei Das erfasste SIGHUP-Signal wird aufgezeichnet.

<?php
$callback = function($signo){
        $sigstr = &#39;unkown signal&#39;;
        switch($signo) {
        case SIGINT:
            $sigstr = &#39;SIGINT&#39;;
            break;
        case SIGHUP:
            $sigstr = &#39;SIGHUP&#39;;
            break;
        case SIGTSTP:
            $sigstr = &#39;SIGTSTP&#39;;
            break;
        }
       file_put_contents("daemon.txt", "catch signal $sigstr\n", FILE_APPEND);
};

pcntl_signal(SIGINT, $callback);
pcntl_signal(SIGHUP, $callback);
pcntl_signal(SIGTSTP, $callback);

while(1)
{
    sleep(100);
    pcntl_signal_dispatch();
}


Gleichzeitig stellt Linux einen Nohup-Befehl zur Verfügung, der es dem Prozess ermöglicht, alle SIGHUP-Signale wie

[root@localhost php]# cat daemon.txt 
catch signal SIGHUP
[root@localhost php]# ps aux | grep sighup.php 
root     18438  0.0  0.4 191600  8996 ?        S    16:48   0:00 php sighup.php
root     18443  0.0  0.0 103328   896 pts/0    S+   16:53   0:00 grep sighup.php

zu ignorieren


[root@localhost php]# nohup php sighup.php 
nohup: 忽略输入并把输出追加到"nohup.out"
(2)

Standardeingabe, Standardausgabe, Standardfehlerausgabe
Es gibt drei Dateihandles Standardmäßig in PHP geöffnet STDIN, STDOUT und STDERR entsprechen den oben genannten drei Dateideskriptoren Da die Standardeingabe und -ausgabe sich auf das Terminal beziehen, sind sie für den Daemon-Prozess nicht von Nutzen. Sie können sie jedoch direkt schließen direkt ein Problem verursachen kann. Bitte beachten Sie den folgenden Code


Wenn Sie den obigen Code ausführen, werden auf dem Bildschirm keine Echoinformationen ausgegeben, sondern ins Freie geschrieben Dies ist auf das Herunterfahren zurückzuführen. Nach dem STDOUT-Dateihandle wird der entsprechende Dateideskriptor freigegeben, und Linux verwendet immer den kleinsten verfügbaren Dateideskriptor, um eine Datei zu öffnen, sodass dieser Dateideskriptor nun auf die von fopen geöffnete Datei verweist Informationen, die ursprünglich in die Standardausgabe geschrieben wurden und jetzt geschrieben werden sollen, sind in der Datei angekommen. Um dieses seltsame Verhalten zu vermeiden, können wir die von Linux bereitgestellte Black-Hole-Datei /dev/null sofort öffnen, nachdem wir diese drei Dateihandles geschlossen haben, wie zum Beispiel:

<?php
fclose(STDOUT);

$fp = fopen("stdout.log", "a");

echo "hello world\n";

<?php
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
fopen(&#39;/dev/null&#39;, &#39;r&#39;);
fopen(&#39;/dev/null&#39;, &#39;w&#39;);
fopen(&#39;/dev/null&#39;, &#39;w&#39;);

$fp = fopen("stdout.log", "a");

echo "hello world\n";

上面这个例程关闭STDIN,STDOUT, STDERR立马打开 /dev/null 三次,这样echo的信息会直接写到黑洞中,避免了前面出现的怪异的问题。


(三)编写守护进程涉及的其他问题

编写守护进程还涉及工作目录、文件掩码、信号处理、热更新、安全的启动停止等等问题,这里先留给大家自己百度,后期有空再来补充。


(四)一个守护进程的示例

<?php

//由于进程组长无法创建会话,fork一个子进程并让父进程退出,以便可以创建新会话
switch(pcntl_fork()) {
    case -1:
            exit("fork error");
            break;
    case 0: 
            break;
    default:
            exit(0); //父进程退出
}

posix_setsid();  //创建新会话,脱离原来的控制终端

//再次fork并让父进程退出, 子进程不再是会话首进程,让其永远无法打开一个控制终端
switch(pcntl_fork()) {
    case -1:
        exit("fork error");
        break;
    case 0:
        break;
    default:
        exit(0); //父进程退出
}

//关闭标准输入输出
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
fopen(&#39;/dev/null&#39;, &#39;r&#39;);
fopen(&#39;/dev/null&#39;, &#39;w&#39;);
fopen(&#39;/dev/null&#39;, &#39;w&#39;);

//切换工作目录
chdir(&#39;/&#39;);

//清除文件掩码
umask(0);

//由于内核不会再为进程产生SIGHUP信号,我们可以使用该信号来实现热重启
pcntl_signal(SIGHUP, function($signo){
    //重新加载配置文件,重新打开日志文件等等
});

for(;;)
{
     pcntl_signal_dispatch();  //处理信号回调
    //实现业务逻辑
}



to be continue!

相关推荐:

PHP实现系统编程之网络Socket及IO多路复用

PHP实现系统编程之本地套接字(Unix Domain Socket)

PHP实现系统编程之 多进程编程介绍及孤儿进程、僵尸进程




Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des Daemon-Prozesses zur Implementierung der Systemprogrammierung in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn