Heim  >  Artikel  >  Backend-Entwicklung  >  Eine vorläufige Einführung in die PHP-Multiprozessprogrammierung

Eine vorläufige Einführung in die PHP-Multiprozessprogrammierung

不言
不言Original
2018-07-05 10:37:061209Durchsuche

Dieser Artikel stellt hauptsächlich die erste Einführung in die PHP-Multiprozessprogrammierung dar. Er hat einen gewissen Referenzwert. Jetzt kann ich ihn mit allen teilen, die ihn brauchen.

Ich beneide Naruto um den Shadow-Klon ? Ja, PHP-Programme können Schattenklone aktivieren! Wenn Sie eine Aufgabe erledigen möchten, aber das Gefühl haben, dass ein Prozess zu langsam ist, versuchen Sie es mit mehreren Prozessen. In diesem Artikel werden die grundlegenden Anforderungen von PHP-Multiprozessen, die Erstellung von Multiprozessen und die grundlegende Signalsteuerung vorgestellt. Vorerst erfahren Sie jedoch nicht, wie Sie Informationen zwischen Prozessen kommunizieren und austauschen.

1. Vorbereitung

Bevor Sie beginnen, stellen Sie bitte sicher, dass Sie nicht die M$ Windows-Plattform verwenden (da ich sie nicht habe). Windows). Linux/BSD/Unix sollte kein Problem sein. Nachdem wir die Arbeitsumgebung bestätigt haben, prüfen wir, ob die von uns benötigten PHP-Module verfügbar sind. Öffnen Sie das Terminal und geben Sie den folgenden Befehl ein:

$ php -m

Dieser Befehl prüft und druckt alle aktuell aktivierten PHP-Erweiterungen, um zu sehen, ob pcntl und posix in der Ausgabeliste sind.

1.1. pcntl

Wenn pcntl nicht gefunden werden kann, wurde diese Erweiterung wahrscheinlich während der Kompilierung nicht kompiliert. Wenn Sie wie ich PHP kompiliert und installiert haben, müssen Sie PHP neu kompilieren und installieren. Denken Sie daran, bei der Konfiguration den Parameter --enable-pcntl hinzuzufügen.

$ cd /path/to/php_source_code_dir 
$ ./configure [some other options] --enable-pcntl
$ make && make install

1.2. posix

wird normalerweise standardmäßig installiert, solange Sie beim Kompilieren nicht --disable-posix hinzufügen.

2. Vorkenntnisse

Bevor Sie fortfahren, müssen Sie auch ein wenig Verständnis für Linux-Multiprozesse haben. Was ist mit Multi-Processing los? Dies unterscheidet sich geringfügig vom Schattenklon in Naruto. Erstens ist Naruto schon in jungen Jahren aufgewachsen, etwa 16 Jahre alt, hust. Eines Tages aktivierte er seinen Schattenklon und teilte sich in fünf Teile auf. Offensichtlich handelt es sich bei diesen Klonen auch um 16-jährige Naruto und nicht um Babys, die bei der Geburt weinen und nichts verstehen (das nennt man Klonen). Dann kommt hier der Unterschied: Die Klone sind zu unabhängigen Menschen geworden, die ihre eigenen Dinge tun, und sie wissen nicht mehr, was andere Klone und der ursprüngliche Körper getan haben (natürlich sammeln sie keine Erfahrungen wie im Cartoon). Zur ursprünglichen Person) . Sofern sie nicht miteinander kommunizieren, sind nur Dinge vor dem 16. Lebensjahr ihre gemeinsamen Erinnerungen.

Ein Klassenkamerad sagte: „Boss, betrügst du mich nicht?“ Ich habe Naruto nicht gesehen! Dann sollten Sie einen Blick darauf werfen...

Endlich ist das vorbereitende Wissen vorbei, Sie müssen nur noch ein allgemeines Verständnis für die vom Hauptprozess gestarteten untergeordneten Prozesse haben. Der Code des untergeordneten Prozesses ist genau derselbe wie der des Hauptprozesses, und der andere Teil desselben besteht darin, dass alles ausgeführt wird, bis der Schattenklon gestartet wird. Einzelheiten entnehmen Sie bitte dem Kurs „Betriebssystem“.

3. Die Kunst des Schattenklons

Wie kann man also den Inhalt der Schriftrolle ohne Grundkenntnisse verstehen? Als ich die Schriftrolle öffnete, sah ich zum ersten Mal ein Wort: Gabel.

3.1. Gabel

Gabel? Die Gabeln sind gegabelt, aus einer wird eine Mehrfache! Das ist so ziemlich das, was es bedeutet. Verwenden Sie diesen Befehl, um einen untergeordneten Prozess zu erstellen. Hier müssen Sie die Funktion pcntl_fork() verwenden. (Sie können zunächst einen kurzen Blick auf die Einführung dieser Funktion im PHP-Handbuch werfen.) Erstellen Sie ein PHP-Skript: Die Funktion

$pid = pcntl_fork(); // 一旦调用成功,事情就变得有些不同了
if ($pid == -1) {    
die('fork failed');
} else if ($pid == 0) {} else {}

pcntl_fork() erstellt einen untergeordneten Prozess Der übergeordnete Prozess unterscheidet sich von der PPID (übergeordnete Prozess-ID) durch die PID (Prozess-ID). Um den Vorgang im Terminal anzuzeigen, verwenden Sie den Befehl ps (fragen Sie man, wie man ps verwendet: man ps). Wenn die Funktion -1 zurückgibt, bedeutet dies, dass der Fork fehlgeschlagen ist. Versuchen Sie, den Satz if vor echo $pid . PHP_EOL; hinzuzufügen. Führen Sie Ihr Skript aus. Die Ausgabe sieht möglicherweise wie folgt aus (das Ergebnis zeigt, dass der Code des untergeordneten Prozesses und des übergeordneten Prozesses identisch ist):

67789 # 这个是父进程打印的
0     # 这个是子进程打印的

pcntl_fork()Nachdem der Funktionsaufruf erfolgreich war, wird die PID angezeigt des untergeordneten Prozesses wird im übergeordneten Prozess zurückgegeben, und 0 wird im untergeordneten Prozess zurückgegeben. Daher verwenden wir den Zweig if direkt, um den übergeordneten Prozess und den untergeordneten Prozess so zu steuern, dass er unterschiedliche Aufgaben erledigt.

3.2. Aufgaben zuweisen

Dann lasst uns über Narutos Schattenklon sprechen, als er 16 Jahre alt war, und dem ursprünglichen Körper und Klon zwei einfache Ausgabeaufgaben zuweisen:

$parentPid = getmypid(); // 这就是传说中16岁之前的记忆
$pid = pcntl_fork(); // 一旦调用成功,事情就变得有些不同了
if ($pid == -1) {    
die('fork failed');
} else if ($pid == 0) {    
$mypid = getmypid(); // 用getmypid()函数获取当前进程的PID    
echo 'I am child process. My PID is ' . $mypid . ' and my father's PID is ' . $parentPid . PHP_EOL;
} else {    
echo 'Oh my god! I am a father now! My child's PID is ' . $pid . ' and mine is ' . $parentPid . PHP_EOL;}

Das Ausgabeergebnis könnte wie folgt aussehen:

Oh my god! I am a father now! My child's PID is 68066 and mine is 68065
I am child process. My PID is 68066 and my father's PID is 68065

Lassen Sie mich noch einmal betonen, dass nach erfolgreichem Aufruf von pcntl_fork() aus einem Programm zwei Programme werden: Der von einem Programm erhaltene $pid-Variablenwert ist 0. und Es handelt sich um einen untergeordneten Prozess. Der von einem anderen Programm erhaltene Wert von $pid ist größer als 0. Dieser Wert ist die PID des untergeordneten Prozesses, der der übergeordnete Prozess ist. In der folgenden Verzweigungsanweisung werden aufgrund unterschiedlicher Werte von $pid unterschiedliche Codes ausgeführt. Lassen Sie mich noch einmal betonen: Der Code des untergeordneten Prozesses ist der gleiche wie der des übergeordneten Prozesses. Daher müssen ihnen durch Branch-Anweisungen unterschiedliche Aufgaben zugewiesen werden.

3.3. 子进程回收

刚刚有man ps么?一般我习惯用ps aux加上grep命令来查找运行着的后台进程。其中有一列STAT,标识了每个进程的运行状态。这里,我们关注状态Z:僵尸(Zombie)。当子进程比父进程先退出,而父进程没对其做任何处理的时候,子进程将会变成僵尸进程。Oops,又跟火影里的影分身不一样了。鸣人的影分身被干死了以后就自动消失了,但是这里的子进程分身死了话还留着一个空壳在,直到父进程回收它。僵尸进程虽然不占什么内存,但是很碍眼,院子里一堆躺着的僵尸怎么都觉得怪怪的。(别忘了它们还占用着PID)

一般来说,在父进程结束之前回收挂掉的子进程就可以了。在pcntl扩展里面有一个pcntl_wait()函数,它会将父进程挂起,直到有一个子进程退出为止。如果有一个子进程变成了僵尸的话,它会立即返回。所有的子进程都要回收,所以多等等也没关系啦!

3.4. 父进程先挂了

如果父进程先挂了怎么办?会发生什么?什么也不会发生,子进程依旧还在运行。但是这个时候,子进程会被交给1号进程,1号进程成为了这些子进程的继父。1号进程会很好地处理这些进程的资源,当它们结束时1号进程会自动回收资源。所以,另一种处理僵尸进程的临时办法是关闭它们的父进程。

4. 信号

一般多进程的事儿讲到上面就完了,可是信号在系统中确实是一个非常重要的东西。信号就是信号灯,点亮一个信号灯,程序就会做出反应。这个你一定用过,比如说在终端下运行某个程序,等了半天也没什么反应,可能你会按 Ctrl+C 来关闭这个程序。实际上,这里就是通过键盘向程序发送了一个中断的信号:SIGINT。有时候进程失去响应了还会执行kill [PID]命令,未加任何其他参数的话,程序会接收到一个SIGTERM信号。程序收到上面两个信号的时候,默认都会结束执行,那么是否有可能改变这种默认行为呢?必须能啊!

4.1. 注册信号

人是活的程序也是活的,只不过程序需要遵循人制定的规则来运行。现在开始给信号重新设定规则,这里用到的函数是pcntl_signal()(继续之前为啥不先查查PHP手册呢?)。下面这段程序将给SIGINT重新定义行为,注意看好:

// 定义一个处理器,接收到SIGINT信号后只输出一行信息
function signalHandler($signal) {    
if ($signal == SIGINT) {        
echo 'signal received' . PHP_EOL;    }}
// 信号注册:当接收到SIGINT信号时,调用signalHandler()函数pcntl_signal(SIGINT, 'signalHandler');while (true) {    sleep(1);    // do something    pcntl_signal_dispatch(); // 接收到信号时,调用注册的signalHandler()}

执行一下,随时按下 Ctrl+C 看看会发生什么事。

4.2. 信号分发

说明一下:pcntl_signal()函数仅仅是注册信号和它的处理方法,真正接收到信号并调用其处理方法的是pcntl_signal_dispatch()函数。试试把// do something替换成下面这段代码:

for ($i = 0; $i < 1000000; $i++) {    
echo $i . PHP_EOL;    
usleep(100000);}

在终端下执行这个脚本,当它不停输出数字的时候尝试按下 Ctrl+C 。看看程序有什么响应?嗯……什么都没有,除了屏幕可能多了个^C以外,程序一直在不停地输出数字。因为程序一直没有执行到pcntl_signal_dispatch(),所以就并没有调用signalHandler(),所以就没有输出signal received

4.3. 版本问题

如果认真看了PHP文档,会发现pcntl_signal_dispatch()这个函数是PHP 5.3以上才支持的,如果你的PHP版本大于5.3,建议使用这个方法调用信号处理器。5.3以下的版本需要在注册信号之前加一句:declare(ticks = 1);表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器。想想就挺不爽的,干嘛一直都检查?还是在我们指定的地方检查一下就好。

4.4. 感受僵尸进程

现在我们回到子进程回收的问题上(差点忘了= =")。当你的一个子进程挂了(或者说是结束了),但是父进程还在运行中并且可能很长一段时间不会退出。一个僵尸进程从此站起来了!这时,保护伞公司(内核)发现它的地盘里出现了一个僵尸,这个僵尸是谁儿子呢?看一下PPID就知道了。然后,内核给PPID这个进程(也就是僵尸进程的父进程)发送一个信号:SIGCHLD。然后,你知道怎么在父进程中回收这个子进程了么?提示一下,用pcntl_wait()函数。

4.5. Signal senden

Ich hoffe, Sie haben den kill-Befehl gerade sorgfältig besetzt. Es sendet tatsächlich ein Signal an den Prozess. In PHP können Sie auch die Funktion posix_kill() aufrufen, um den gleichen Effekt zu erzielen. Damit können Sie die Ausführung anderer untergeordneter Prozesse im übergeordneten Prozess steuern. Um beispielsweise alle untergeordneten Prozesse zu schließen, bevor der übergeordnete Prozess endet, zeichnen Sie dann die PIDs aller untergeordneten Prozesse im übergeordneten Prozess unter fork auf und senden Sie nacheinander Endsignale an die untergeordneten Prozesse, bevor der übergeordnete Prozess endet.

5. Üben

Der Multiprozess von PHP ist dem von C ziemlich ähnlich. Sobald Sie es verstanden haben, wird es ähnlich sein, wenn Sie es in anderen Sprachen schreiben. Wenn Sie Zeit haben, versuchen Sie, ein kleines Programm zu schreiben und erleben Sie es persönlich:

  1. Der 16-jährige Naruto schickte Schattenklone und teilte sich in 5 Klone auf

  2. Jeder Klon überlebt zufällig 10 bis 30 Sekunden und gibt jede Sekunde etwas aus

  3. Stellen Sie sicher, dass der ursprüngliche Klon das Ende des Klons spüren kann, und aktivieren Sie dann einen anderen Klon Es gibt höchstens 5 Klone

  4. Nicht nohup verwenden, damit der ursprüngliche Klon auch nach dem Schließen des Terminals noch ausgeführt werden kann

  5. Erhöhen Sie die Anzahl der Klone (5). Schreiben Sie es in eine Konfigurationsdatei (erwägen Sie die Verwendung von SIGUSR1 oder SIGUSR2), liest der Originalkörper die Konfigurationsdatei und aktualisiert die maximale Anzahl zulässiger Klone

  6. Wenn es zu viele Klone gibt, schließen Sie ein paar; wenn es zu wenige sind, teilen Sie ein paar mehr auf

Tipps:

  1. Verwenden Sie die while Schleifengarantie. Der Prozess läuft. Seien Sie vorsichtig sleep, um eine 100-prozentige CPU-Auslastung zu vermeiden.

  2. Wenn das Terminal, auf dem der Prozess ausgeführt wird, geschlossen ist, wird der Das Programm empfängt ein SIGHUP-Signal

  3. Sie können die Funktion parse_ini_file() verwenden, um die INI-Konfigurationsdatei zu analysieren

Das Obige ist der gesamte Inhalt Ich hoffe, dass er für das Lernen aller hilfreich ist. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website.

Verwandte Empfehlungen:

Verständnis des PHP-Polymorphismus

Einführung in die PHP-Dateiprogrammierung

Das obige ist der detaillierte Inhalt vonEine vorläufige Einführung in die PHP-Multiprozessprogrammierung. 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
Vorheriger Artikel:Einführung in PHP-NamespacesNächster Artikel:Einführung in PHP-Namespaces