Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erläuterung der Daemon-Daemon-Implementierung in PHP7

Detaillierte Erläuterung der Daemon-Daemon-Implementierung in PHP7

little bottle
little bottlenach vorne
2019-04-25 14:11:473364Durchsuche

In diesem Artikel geht es hauptsächlich um die Verwendung von PHP7 zur Implementierung des Daemon-Prozesses. Interessierte Freunde können mehr darüber erfahren.

In einem Multitasking-Computerbetriebssystem ist ein Daemon-Prozess ein Computerprogramm, das im Hintergrund ausgeführt wird. Solche Programme werden als Prozesse initialisiert. Die Namen von Daemon-Programmen enden normalerweise mit dem Buchstaben „d“: syslogd bezieht sich beispielsweise auf den Daemon, der Systemprotokolle verwaltet.

Das Daemon-Programm ist ein Serverprogramm, das immer ausgeführt wird und auch als Daemon-Prozess bezeichnet wird. Es läuft normalerweise im Hintergrund des Systems und interagiert nicht mit dem Vordergrund, ohne dass ein Steuerterminal vorhanden ist. Das Daemon-Programm wird im Allgemeinen als Systemdienst verwendet. Ein Daemon ist ein Prozess mit langer Laufzeit, der normalerweise nach dem Systemstart ausgeführt wird und endet, wenn das System heruntergefahren wird. Im Allgemeinen läuft das Daemon-Programm im Hintergrund, da es kein Steuerungsterminal hat und nicht mit Benutzern im Vordergrund interagieren kann. Daemon-Programme werden im Allgemeinen als Dienstprogramme verwendet und warten darauf, dass Client-Programme mit ihnen kommunizieren. Wir bezeichnen das laufende Daemon-Programm auch als Daemon-Prozess.

Normalerweise verfügt der Daemon-Prozess über keinen vorhandenen übergeordneten Prozess (d. h. PPID=1) und befindet sich direkt unter init in der Prozesshierarchie des UNIX-Systems. Ein Daemon-Prozess macht sich normalerweise durch die folgende Methode selbst zu einem Daemon: führt fork auf einem untergeordneten Prozess aus und beendet dann seinen übergeordneten Prozess sofort, sodass der untergeordnete Prozess in init Ausführen unter . Diese Methode wird oft als „Beschuss“ bezeichnet.

Das System startet normalerweise den Daemon-Prozess beim Start. Daemons bieten Unterstützung für die Reaktion auf Netzwerkanfragen, Hardwareaktivitäten oder andere Anfragen von anderen Anwendungen durch bestimmte Aufgaben. Daemons können auch Hardware konfigurieren (z. B. devfsd auf einigen Linux-Systemen), geplante Aufgaben ausführen (z. B. cron) und andere Aufgaben ausführen. Jeder Prozess hat einen übergeordneten Prozess. Wenn der untergeordnete Prozess beendet wird, kann der übergeordnete Prozess den Beendigungsstatus des untergeordneten Prozesses erhalten.

Ein Daemon-Prozess ist einfach ein Prozess, der im Hintergrund ohne das Terminal ausgeführt werden kann. Dies ist ein sehr häufiger Prozess unter Linux, beispielsweise nach einem Dienst wie Apache oder MySQL Wenn der Prozess gestartet wird, wird er als Daemon-Prozess im Speicher gespeichert. Daemons sind Anwendungen, die im Hintergrund laufen und nicht direkt vom Benutzer bedient werden. Beispiele für Daemons sind Cron und MySQL. Die Verwendung des PHP-Daemons ist sehr einfach und erfordert Kompilierungsparameter mit PHP 4.1 oder höher: --enable-pcntl

Angenommen, es gibt eine zeitaufwändige Aufgabe, die im Hintergrund ausgeführt werden muss: Alle 20 Millionen Benutzer in der Benutzertabelle in ganz MySQL in Redis importieren, um den Cache vorzuwärmen, dann wird diese Aufgabe wahrscheinlich noch eine Weile dauern Zu diesem Zeitpunkt müssen Sie ein PHP-Skript schreiben, das als Daemon im System ausgeführt wird, und es nach Abschluss automatisch starten.

Unter Linux gibt es drei Möglichkeiten, Skripte im Hintergrund zu erstellen:

1. Fügen Sie nach dem Befehl

Zum Beispiel php task.php &. Der Nachteil dieser Methode besteht darin, dass der PHP-Prozess geschlossen wird, wenn das Terminal geschlossen wird, unabhängig davon, ob es normal oder abnormal geschlossen ist Zweitens wird der Code im aktuellen Terminalfenster ausgegeben, wenn er Ausgabetext wie echo oder print_r enthält.

2. Verwenden Sie den nohup-Befehl

, z. B.

nohup php task.php & Der Code Oder der von print_r ausgegebene Text wird in die Datei nohup.out im selben Verzeichnis wie der PHP-Code ausgegeben. Wenn Sie das Terminal mit normalen Mitteln wie dem Exit-Befehl oder der Schaltfläche „Schließen“ schließen, wird der Prozess nicht geschlossen und wird weiterhin im Hintergrund ausgeführt. Wenn das Terminal jedoch auf einen abnormalen Exit oder Abbruch stößt, wird der PHP-Prozess ebenfalls sofort beendet.

3. Implementierung von pcntlposix durch und

Erweiterungen sind zu beachten :

  • Lassen Sie den Hauptprozess das Terminal über Sekundär pcntl_fork() verlassen und posix_setsid
  • Ignorieren oder verarbeiten Sie das pcntl_signal()-Signal SIGHUP bis
  • Mehrprozessprogramme müssen zweimal durchlaufen pcntl_fork() oder pcntl_signal() ignorieren das SIGCHLD-Signal, um zu verhindern, dass der untergeordnete Prozess zu einem Zombie-Prozess wird
  • bestanden umask() Legen Sie die Dateiberechtigungsmaske fest, um den Einfluss von Berechtigungen zu verhindern, die von Dateiberechtigungen geerbt wurden
  • Leiten Sie den laufenden Prozess STDIN/STDOUT/STDERR zu /dev/null oder anderen Streams

Daemon hat die folgenden Eigenschaften:

  • Kein Terminal
  • Wird im Hintergrund ausgeführt
  • Die übergeordnete Prozess-PID ist 1

Wenn Sie den laufenden Daemon anzeigen möchten Prozess können Sie oder ps -ax View übergeben, wobei ps -ef bedeutet, dass Prozesse ohne steuernde Terminals aufgelistet werden. -x

Fork-Systemaufruf

Der Fork-Systemaufruf wird verwendet, um einen Prozess zu kopieren, der nahezu identisch mit dem übergeordneten Prozess ist Der Prozess ist anders. Das Problem besteht darin, dass er eine andere PID und einen anderen Speicherplatz als der übergeordnete Prozess hat. Gemäß der Codelogik können der übergeordnete und untergeordnete Prozess die gleiche Arbeit ausführen oder sich unterscheiden. Der untergeordnete Prozess erbt Ressourcen wie Dateideskriptoren vom übergeordneten Prozess.

Die -Erweiterung in PHP implementiert die pcntl-Funktion, die zum Forken eines neuen Prozesses in PHP verwendet wird. pcntl_fork()

setsid-Systemaufruf

setsid-Systemaufruf wird verwendet, um eine neue Sitzung zu erstellen und die Prozessgruppen-ID festzulegen. Hier gibt es mehrere Konzepte: 会话, 进程组.

Unter Linux generiert die Benutzeranmeldung eine Sitzung. Eine Sitzung enthält eine oder mehrere Prozessgruppen, und eine Prozessgruppe enthält mehrere Prozesse. Jede Prozessgruppe hat einen Gruppenleiter (Sitzungsleiter) und seine PID ist die Gruppen-ID der Prozessgruppe. Sobald der Prozessleiter ein Terminal öffnet, wird dieses Terminal als steuerndes Terminal bezeichnet. Sobald im Steuerterminal eine Ausnahme auftritt (Verbindungsunterbrechung, Hardwarefehler usw.), wird ein Signal an den Prozessgruppenleiter gesendet.

Im Hintergrund laufende Programme (z. B. Ausführungsanweisungen, die mit in der Shell enden) werden auch nach dem Schließen des Terminals beendet, d Wenn die Verbindung getrennt wird, wird das Signal >& nicht ordnungsgemäß verarbeitet, und das Standardverhalten des Signals SIGHUP für einen Prozess besteht darin, den Prozess zu beenden. SIGHUP

Aufruf setsid Nach dem Systemaufruf erstellt der aktuelle Prozess eine neue Prozessgruppe. Wenn das Terminal im aktuellen Prozess nicht geöffnet ist Dies hier Es wird kein steuerndes Terminal in der Prozessgruppe geben und es wird kein Problem geben, den Prozess durch Schließen des Terminals abzubrechen.

Die

-Erweiterung in PHP implementiert die -Funktion, die zum Festlegen einer neuen Prozessgruppe in PHP verwendet wird. posixposix_setsid()

Die Rolle der sekundären Gabelung

Erstens kann der

Systemaufruf nicht vom Prozessgruppenleiter aufgerufen werden und wird zurückgegeben -1. setsid

Der Beispielcode für die sekundäre Fork-Operation lautet wie folgt:

<span style="font-size: 16px;">$pid1 = pcntl_fork();

if ($pid1 > 0) {</span><br/><span style="font-size: 16px;">    // 父进程会得到子进程号,所以这里是父进程执行的逻辑
    exit('parent process. 1'."\n");
} else if ($pid1 < 0) {
    exit("Failed to fork 1\n");
}

if (-1 == posix_setsid()) {
    exit("Failed to setsid\n");
}

$pid2 = pcntl_fork();

if ($pid2 > 0) {
    exit('parent process. 2'."\n");
} else if ($pid2 < 0) {
    exit("Failed to fork 2\n");
}</span>

Die Funktion pcntl_fork() erstellt einen untergeordneten Prozess Untergeordneter Prozess Nur die PID (Prozess-ID) und die PPID (übergeordnete Prozess-ID) unterscheiden sich vom übergeordneten Prozess.

Rückgabewert

Bei Erfolg wird die PID des generierten untergeordneten Prozesses im Ausführungsthread des übergeordneten Prozesses zurückgegeben Im untergeordneten Prozess ausgeführt

0 wird innerhalb des Threads zurückgegeben. Wenn -1 im Kontext des übergeordneten Prozesses zurückgegeben wird, wird kein PHP-Fehler erstellt wird erhöht.

假定我们在终端中执行应用程序,进程为 a,第一次 fork 会生成子进程 b,如果 fork 成功,父进程 a 退出。b 作为孤儿进程,被 init 进程托管。

此时,进程 b 处于进程组 a 中,进程 b 调用 posix_setsid 要求生成新的进程组,调用成功后当前进程组变为 b。


php fork2.php 
parent process. 1
parent process. 2

此时进程 b 事实上已经脱离任何的控制终端,例程:


cli_set_process_title('process_a');

$pidA = pcntl_fork();

if ($pidA > 0) {
    exit(0);
} else if ($pidA < 0) {
    exit(1);
}

cli_set_process_title('process_b');

if (-1 === posix_setsid()) {
    exit(2);
}

while(true) {
    sleep(1);
}

执行程序之后:  


$ php cli-title.php 
$ ps ax | grep -v grep | grep -E 'process_|PID'
  PID TTY      STAT   TIME COMMAND
15725 ?        Ss     0:00 process_b

重新打开一个shell窗口,效果一样,都在呢

从 ps 的结果来看,process_b 的 TTY 已经变成了 ,即没有对应的控制终端。

代码走到这里,似乎已经完成了功能,关闭终端之后 process_b 也没有被杀死,但是为什么还要进行第二次 fork 操作呢?

StackOverflow 上的一个回答写的很好:

The second fork(2) is there to ensure that the new process is not a session leader, so it won’t be able to (accidentally) allocate a controlling terminal, since daemons are not supposed to ever have a controlling terminal.

这是为了防止实际的工作的进程主动关联或者意外关联控制终端,再次 fork 之后生成的新进程由于不是进程组组长,是不能申请关联控制终端的。

综上,二次 fork 与 setsid 的作用是生成新的进程组,防止工作进程关联控制终端。 

写一个demo测试下


<?php
// 第一次fork系统调用
$pid_A = pcntl_fork();

// 父进程 和 子进程 都会执行下面代码
if ($pid_A < 0) {
    // 错误处理: 创建子进程失败时返回-1.
    exit('A fork error ');
} else if ($pid_A > 0) {
     // 父进程会得到子进程号,所以这里是父进程执行的逻辑
    exit("A parent process exit \n");
}

// B 作为孤儿进程,被 init 进程托管,此时,进程 B 处于进程组 A 中

// 子进程得到的$pid为0, 所以以下是子进程执行的逻辑,受控制终端的影响,控制终端关闭则这里也会退出

// [子进程] 控制终端未关闭前,将当前子进程提升会会话组组长,及进程组的leader
// 进程 B 调用 posix_setsid 要求生成新的进程组,调用成功后当前进程组变为 B
if (-1 == posix_setsid()) {
    exit("Failed to setsid\n");
}

// 此时进程 B 已经脱离任何的控制终端

// [子进程]  这时候在【进程组B】中,重新fork系统调用(二次fork)
$pid_B = pcntl_fork();
if ($pid_B < 0) {
    exit('B fork error ');
} else if ($pid_B > 0) {
    exit("B parent process exit \n");
}

// [新子进程] 这里是新生成的进程组,不受控制终端的影响,写写自己的业务逻辑代码
for ($i = 1; $i <= 100; $i++) {
    sleep(1);
    file_put_contents('daemon.log',$i . "--" . date("Y-m-d H:i:s", time()) . "\n",FILE_APPEND);
}

Window 下跑回直接抛出异常


php runtime\daemon.php
PHP Fatal error:  Uncaught Error: Call to undefined function pcntl_fork() in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php:13
Stack trace:
#0 {main}
  thrown in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php on line 13

Linux 下执行,输出结果


<span style="font-size: 16px;">php daemon.php</span><br/><span style="font-size: 16px;">...
97--2018-09-07 03:50:09
98--2018-09-07 03:50:10
99--2018-09-07 03:50:11
100--2018-09-07 03:50:12</span>

所以,现在即使关闭了终端,改脚本任然在后台守护进程运行

相关教程:PHP视频教程

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

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen

In Verbindung stehende Artikel

Mehr sehen