本篇文章跟大家分享的內容是PHP實現系統程式設計之多進程程式設計介紹及孤兒進程、殭屍進程,有著一定的參考價值,有需要的朋友可以參考一下
多進程程式設計也是系統程式設計的重要方面,但PHP程式設計師通常不需要關心多進程的問題,因為web伺服器或PHP-FPM已經幫我們管理好進程方面的問題了,但是如果我們想要用PHP來開發CLI程序,多進程編程是不可或缺的基本技術。
PHP中關於進程控制的方法主要使用到PCNTL(Process Control)擴充功能, 所以,在進行多進程程式設計之前,首先要確保你的PHP已經安裝了最新的PCNTL擴展,可以輸入php -m指令來查看目前已安裝的擴充功能:
#PCNTL 函数
pcntl_alarm — 为进程设置一个alarm闹钟信号
pcntl_errno — 别名 pcntl_get_last_error
pcntl_exec — 在当前进程空间执行指定程序
pcntl_fork — 在当前进程当前位置产生分支(子进程)。
pcntl_get_last_error — Retrieve the error number set by the last pcntl function which failed
pcntl_getpriority — 获取任意进程的优先级
pcntl_setpriority — 修改任意进程的优先级
pcntl_signal_dispatch — 调用等待信号的处理器
pcntl_signal_get_handler — Get the current handler for specified signal
pcntl_signal — 安装一个信号处理器
pcntl_sigprocmask — 设置或检索阻塞信号
pcntl_sigtimedwait — 带超时机制的信号等待
pcntl_sigwaitinfo — 等待信号
pcntl_strerror — Retrieve the system error message associated with the given errno
pcntl_wait — 等待或返回fork的子进程状态
pcntl_waitpid — 等待或返回fork的子进程状态
pcntl_wexitstatus — 返回一个中断的子进程的返回代码
pcntl_wifexited — 检查状态代码是否代表一个正常的退出。
pcntl_wifsignaled — 检查子进程状态码是否代表由于某个信号而中断
pcntl_wifstopped — 检查子进程当前是否已经停止
pcntl_wstopsig — 返回导致子进程停止的信号
pcntl_wtermsig — 返回导致子进程中断的信号
pcntl_fork
—
在目前進程目前位置產生分支(子進程) 。譯註:fork是創建了一個子進程,父進程和子進程
都從fork的位置開始向下繼續執行,不同的是父進程執行過程中,得到的fork回傳值為子進程號,而子進程得到的是0。
fork出的子進程幾近於完全的複製了父進程,父子進程共享程式碼段,雖然父子進程的資料段、堆、棧是相互獨立的,但在一開始,子進程完全複製了父進程的這些數據,但之後的修改互不影響。
#
int pcntl_fork ( void )
<?php for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); //创建子进程,子进程也是从这里开始执行。 if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环,否则子进程又会创建自己的子进程。 } } sleep($i); //第一个创建的子进程将睡眠0秒,第二个将睡眠1s,依次类推...主进程会睡眠5秒 if ($i < 5) { exit("第 " . ($i+1) . " 个子进程退出..." . time() . PHP_EOL); } else { exit("父进程退出..." . time() . PHP_EOL); }
##建立5個子進程碼示範:
[root@localhost process]# php process.php 第 1 个子进程退出...1503322773 第 2 个子进程退出...1503322774 第 3 个子进程退出...1503322775 第 4 个子进程退出...1503322776 第 5 个子进程退出...1503322777 父进程退出...1503322778
執行結果:
##
<?php for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } sleep($i); //第一个创建的子进程将睡眠0秒,第二个将睡眠1s,依次类推...主进程会睡眠5秒 /* if ($i < 5) { exit("第 " . ($i+1) . " 个子进程退出..." . time() . PHP_EOL); } else { exit("父进程退出..." . time() . PHP_EOL); } */ while(1) { sleep(1); //执行死循环不退出 }
對於pcntl_fork函數要重點理解:「
fork是創建了一個子進程,父進程和子進程都從fork的位置開始向下繼續執行,不同的是父進程執行過程中,得到的fork回傳值為子進程號,而子進程得到的是0”
把上面的程式碼稍作修改,不讓行程退出,然後利用ps指令查看系統狀態:
[root@localhost ~]# ps -ef | grep php
root 3670 3609 0 21:54 pts/0 00:00:00 php process.php
root 3671 3670 0 21:54 pts/0 00:00:00 php process.php
root 3672 3670 0 21:54 pts/0 00:00:00 php process.php
root 3673 3670 0 21:54 pts/0 00:00:00 php process.php
root 3674 3670 0 21:54 pts/0 00:00:00 php process.php
root 3675 3670 0 21:54 pts/0 00:00:00 php process.php
root 3677 3646 0 21:54 pts/1 00:00:00 grep php
執行後輸入ps -ef | grep php 檢視系統程序
##
<?php $ppid = posix_getpid(); //记录父进程的进程号 for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } if ($ppid == posix_getpid()) { //父进程 while(1) { sleep(1); } } else { //子进程 for($i = 0; $i < 100; $i ++) { echo "子进程" . posix_getpid() . " 循环 $i ...\n"; sleep(1); } }
可以看到6個php process.php 進程,其中第二列是進程號,第三列是進程的父進程號,可以看到後面五個進程的父進程號都是第一個行程的進程號。
[root@localhost process]# php process.php 子进程6677 循环 0 ... 子进程6676 循环 0 ... 子进程6678 循环 0 ... 子进程6680 循环 0 ... 子进程6679 循环 0 ... 子进程6677 循环 1 ... 子进程6676 循环 1 ... 子进程6678 循环 1 ... 子进程6680 循环 1 ... 子进程6679 循环 1 ... 子进程6677 循环 2 ... 子进程6676 循环 2 ... 子进程6678 循环 2 ... 子进程6680 循环 2 ...###### ###
void pcntl_exec ( string $path [, array $args [, array $envs ]] )#########其實上面的程式父子進程還是執行了相同的程式碼,只是進入的if分支不一樣,而pcntl_exec則可以讓子進程完全脫離父進程的影響,去執行新的程序。 ##################
pcntl_exec — 在当前进程空间执行指定程序
void pcntl_exec ( string $path [, array $args [, array $envs ]] )
path
path必须时可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本(比如文件第一行是#!/usr/local/bin/perl的perl脚本)。 更多的信息请查看您系统的execve(2)手册。
args
args是一个要传递给程序的参数的字符串数组。
envs
envs是一个要传递给程序作为环境变量的字符串数组。这个数组是 key => value格式的,key代表要传递的环境变量的名称,value代表该环境变量值。
注意该方法的返回值比较特殊:当发生错误时返回 FALSE ,没有错误时没有返回,因为pcntl_exec调用成功,子进程就去运行新的程序 从父进程继承的代码段、数据段、堆、栈等信息全部被替换成新的,此时的pcntl_exec函数调用栈已经不存在了,所以也就没有返回了。代码示例:
<?php for($i = 0; $i < 3; $i++) { $pid = pcntl_fork(); if($pid == 0) { echo "子进程pid = " . posix_getpid() . PHP_EOL; $ret = pcntl_exec('/bin/ls'); //执行 ls 命令, 此处调用成功子进程将不会再回来执行下面的任何代码 var_dump($ret); // 此处的代码不会再执行 } } sleep(5); //睡眠5秒以确保子进程执行完毕,原因后面会说 exit( "主进程退出...\n");
运行结果:
[root@localhost process]# php pcntl_exec.php 子进程pid = 6728 子进程pid = 6729 子进程pid = 6727 pcntl_exec.php process.php pcntl_exec.php process.php pcntl_exec.php process.php 主进程退出... [root@localhost process]# ls pcntl_exec.php process.php
以上就是对PHP多进程开发的简单介绍,对于子进程不同的存续状态,引出孤儿进程和僵尸进程的概念,在linux系统中,init进程(1号进程)是所有进程的祖先,其他进程要么是该进程的子进程,要么是子进程的子进程,子进程的子进程的子进程...,linux系统中可以用 pstree 命令查看进程树结构:
在多进程程序中,如果父进程先于子进程退出,那么子进程将会被init进程收养,成为init进程的子进程,这种进程被称为孤儿进程,我们可以把上面的代码稍作修改来演示这种情况:
<?php $ppid = posix_getpid(); //记录父进程的进程号 for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } if ($ppid == posix_getpid()) { //父进程直接退出,它的子进程都会成为孤儿进程 exit(0); } else { //子进程 for($i = 0; $i < 100; $i ++) { echo "子进程" . posix_getpid() . " 循环 $i ...\n"; sleep(1); } }
运行该程序,然后查看进程状态:
[root@localhost ~]# ps -ef | grep php root 2903 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2904 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2905 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2906 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2907 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2935 2912 0 12:10 pts/1 00:00:00 grep php
可以看到五个子进程的父进程号都是1了,并且这时控制台不再被程序占用,子进程转到了后台运行,这种孤儿进程被init进程收养的机制是实现后面将要介绍的守护进程的必要条件之一。
子进程还有一种状态叫僵尸进程,子进程结束时并不是完全退出,内核进程表中仍旧保有该进程的记录,这样做的目的是能够让父进程可以得知子进程的退出状态,以及子进程是自杀(调用exit或代码执行完毕)还是他杀(被信号终止),父进程可以调用pcntl_wait 或 pcntl_waitpid 方法来回收子进程(收尸),释放子进程占用的所有资源,并获得子进程的退出状态,如果父进程不做回收,则僵尸进程一直存在,如果这时父进程也退出了,则这些僵尸进程会被init进程接管并自动回收。
对于linux系统来说,一个长时间运行的多进程程序一定要回收子进程,因为系统的进程资源是有限的,僵尸进程会让系统的可用资源减少。
代码演示僵尸进程的产生:
<?php $ppid = posix_getpid(); //记录父进程的进程号 for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } if ($ppid == posix_getpid()) { //父进程不退出,也不回收子进程 while(1) { sleep(1); } } else { //子进程退出,会成为僵尸进程 exit("子进程退出 $ppid ...\n"); }
运行之后查看进程状态:
[root@localhost ~]# ps -ef | grep php root 2971 2864 0 14:13 pts/0 00:00:00 php pcntl.fork.php root 2972 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2973 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2974 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2975 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2976 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2978 2912 0 14:13 pts/1 00:00:00 grep php
僵尸进程会用
PHP的pcntl扩展提供了两个回收子进程的方法供我们调用:
int pcntl_wait ( int &$status [, int $options = 0 ] ) int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )
pcntl_wait函数挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。 如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将被释放。
关于wait在您系统上工作的详细规范请查看您系统的wait(2)手册。这个函数等同于以-1作为参数pid 的值并且没有options参数来调用pcntl_waitpid() 函数。
代码示例:
<?php $ppid = posix_getpid(); //记录父进程的进程号 for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } if ($ppid == posix_getpid()) { //父进程循环回收收子进程 while(($id = pcntl_wait($status)) > 0) //如果没有子进程退出, pcntl_wait 会一直阻塞 { echo "回收子进程:$id, 子进程退出状态值: $status...\n"; } exit("父进程退出 $id....\n"); //当子进程全部结束 pcntl_wait 返回-1 } else { //子进程退出,会成为僵尸进程 sleep($i); exit($i); }
运行结果:
[root@localhost php]# php pcntl.fork.php 回收子进程:3043, 子进程退出状态值: 0... 回收子进程:3044, 子进程退出状态值: 256... 回收子进程:3045, 子进程退出状态值: 512... 回收子进程:3046, 子进程退出状态值: 768... 回收子进程:3047, 子进程退出状态值: 1024... 父进程退出 -1....
这里只是对PHP多进程编程做了基本的介绍,后面会结合 信号、进程间通信以及守护进程 做更进一步的介绍,欢迎大家关注后续文章。
PHP是世界上最好的语言 That's all :)
相关推荐:
以上是PHP實作系統程式設計之 多進程程式設計介紹及孤兒流程、殭屍流程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

方法:1、用“str_replace(" ","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\ \;||\xc2\xa0)/","其他字符",$str)”语句。

查找方法:1、用strpos(),语法“strpos("字符串值","查找子串")+1”;2、用stripos(),语法“strpos("字符串值","查找子串")+1”。因为字符串是从0开始计数的,因此两个函数获取的位置需要进行加1处理。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

SublimeText3 Linux新版
SublimeText3 Linux最新版