惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
InfoQ
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
云风的 BLOG
云风的 BLOG
S
Secure Thoughts
Stack Overflow Blog
Stack Overflow Blog
G
Google Developers Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
SegmentFault 最新的问题
博客园 - Franky
T
Tenable Blog
T
The Blog of Author Tim Ferriss
博客园 - 三生石上(FineUI控件)
V
V2EX
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
Troy Hunt's Blog
罗磊的独立博客
WordPress大学
WordPress大学
SecWiki News
SecWiki News
The Cloudflare Blog
S
Securelist
小众软件
小众软件
Schneier on Security
Schneier on Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 叶小钗
Google Online Security Blog
Google Online Security Blog
Forbes - Security
Forbes - Security
阮一峰的网络日志
阮一峰的网络日志
量子位
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
W
WeLiveSecurity
A
Arctic Wolf
大猫的无限游戏
大猫的无限游戏
The Last Watchdog
The Last Watchdog
C
Cybersecurity and Infrastructure Security Agency CISA
宝玉的分享
宝玉的分享
MyScale Blog
MyScale Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
博客园 - 司徒正美
Vercel News
Vercel News
H
Help Net Security
Y
Y Combinator Blog
PCI Perspectives
PCI Perspectives
L
LINUX DO - 最新话题
H
Hackread – Cybersecurity News, Data Breaches, AI and More

WangDeer

非机动车交通规则测验 微软 50 周年壁纸是在 macOS上设计的? 「转」西二旗折叠:蜂巢经济学启示录 难得空明 Ubuntu Clean ApidocJs apigroup Support Chinese int(10)里面的10是什么意思? 5-3 会话 2021-03-25 「PHP架构师」面试准备 Laravel 上传 Docx 文件,表单验证不通过 4-4 发送信号 4-3 信号集 4-2 编写中断信号处理程序 2022-03-10 面试复盘 4-1 什么是中断信号(软中断) 3-7 进程查看 3-6 SUID特权进程 3-5多进程编写 3-4进程调度 3-3进程exec 3-1进程标识与fork 2-3解释器文件 2-2命令行参数与环境参数表 2-1程序与进程 1-1课程介绍 终极复盘 第八章 传承篇 第七章 自由篇 第六章 品牌篇 第五章 作品篇 第四章 定位篇 第三章 金钱篇 第二章 富人篇 第一章 财务自由篇 财富思维觉醒课前作业 This Set Cookie Was Blocked Because It Was Not Sent Over a Secure Connection 「转」Windows11右键恢复至Windows10模式 Ecshop 4.0 微信登录 一直循环授权登录 PHP判断指定日期是否为工作日 PHP Ajax 跨域请求允许多个域名 返回键劫持,强制跳转到指定链接 Chromium Adobe Flash Player下载 批量检查并清除BOM头 Ecshop之smarty与js冲突问题 Ecshop 广告位宽度值必须在1 1024之间 修改方法 PHP获取指定时区当前时间-PHP获取世界时钟 phpcms 更新栏目缓存 `PHP Has Encountered a Stack Overflow` 解决办法 php判断是否手机访问-php判断访问终端类型 丢了 如果昨天是明天的话就好了,这样今天就周五了 Ecshop自定义调用指定广告位 Destoon修改商务便签文字 Ecshop首页获取指定分类的推荐商品 QQ空间如何屏蔽好友刷屏广告 Css实现文字竖排 英尺(inch)、厘米(cm) JS转换 网页禁止鼠标右键 Destoon 选择支付方式 可单独关闭对联广告两枚 网页打开不是在最顶部,而是跳到中间位置解决方法 Ecmall iis 伪静态规则 Destoon 复制商城模块 QQ在线客服设置 QQ开启临时会话教程 Ecshop加入购物车js报错 IE浏览器“浏览器模式”和“文本模式” 网站图片在IE浏览器下不显示 Ecshop 去掉头部版权信息 echsop “transport.js/run() error:'process_request' 未定义”-解决方法 Ecshop Deprecated:Assigning the Return Value of New by Reference Is Deprecated in 报错解决方法 PHPCMS全局循环调用二级类目导航 如何使EditPlus打开tpl文件高亮显示代码 Ecshop前台英文后台中文实现方法 Ecshop安装报错warning:date_default_timezone_get() 1970年1月1日(00:00:00 GMT)Unix时间戳(Unix Timestamp) 黑莓bb微信 2.6 for os5 黑莓微信内测版问题总结及开发建议 360安全卫士卸载过程 管理距离AD值(Administrative Distance) 思科在中国的总代及金牌、银牌代理商 黑莓7100T拆机全程实录 CCNA静态路由配置实例 8700G国产壳白色,太粗糙了 黑莓7100T玉照曝光 黑莓手机电池的正确使用和保养 51啦统计不能打开,求分析,求解释 处女作,黑莓8707G4.2主题出炉 我的2011 Xweibo手机版底部版权修改 常用ping地址,逗逗搜索引擎,SEO必备 飞飞影视系统快播播放列表前缀修改 飞飞影视系统title修改 这才整明白啥叫被跨省,上网有风险,发帖需谨慎 为emlog添加Gravatar头像服务功能 三层交换机实现VLAN间通信 路由器的基本配置 路由器接口类型及连接方式 19岁双IE 谈Cisco认证 Secondary IP-Address 用途 儿童节NA战报 CCNA疑难解析之network x.x.x.x
3-2进程退出和回收
王二 · 2022-03-04 · via WangDeer

进程的退出

PHP 进程退出的几种情况

  1. 运行到最后一行语句

  2. 运行时遇到 return

  3. 运行时遇到 exit() 函数的时候

  4. 程序异常的时候

  5. 进程接收到中断信号

正常结束、异常结束(跟信号有关),不管以何种方式退出,都有一个终止状态码。

僵尸进程

僵尸进程(zombie process):指子进程已结束,但是父进程还没有使用 waitpcntl_wait)/pcntl_waitpidwaitpid) 来回收。

进程结束时并不会真的退出,还会驻留在内存中,父进程需要通过 waitpcntl_wait」函数来获取进程的终止状态码,同时该函数会释放终止进程的内存空间。否则容易造成僵尸进程过多,占用大量内存空间。

wait 函数挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将被释放。关于 wait 在您系统上工作的详细规范请查看您系统的 wait(2)手册。

——pcntl_wait

示例:

<?php

$pid = pcntl_fork();

if (0 === $pid) {
    fprintf(STDOUT, "我是子进程,pid = %d,运行完我就没事啦。\n", posix_getpid());
} else {
    fprintf(STDOUT, "我是父进程,pid = %d。\n", posix_getpid());
    sleep(1);
    while (1) {
        # code...
        ;
    }
}

运行结果:


php demo5.php

我是父进程,pid = 1282。
我是子进程,pid = 1283,运行完我就没事啦。

进程状态:

laradock@3a6c2da5a07b:/var/www$ ps exj
   PPID     PID    PGID     SID TTY        TPGID STAT   UID   TIME COMMAND
    807    1282    1282     807 pts/2       1282 R+    1000   0:15 php demo5.php LC_ALL=en_US.UTF-8 NVM_DIR=/home/laradock/.nvm LS_COLORS=no=00:fi=00:
   1282    1283    1282     807 pts/2       1282 Z+    1000   0:00 [php] <defunct> // 僵尸进程

进程产生的文件

一个进程运行时,会在 /proc/PID 目录产生文件。

laradock@3a6c2da5a07b:/proc/1282$ ll
total 0
dr-xr-xr-x   9 laradock laradock 0 Mar  4 09:00 ./
dr-xr-xr-x 447 root     root     0 Mar  4 07:56 ../
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 arch_status
dr-xr-xr-x   2 laradock laradock 0 Mar  4 09:00 attr/
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 autogroup
-r--------   1 laradock laradock 0 Mar  4 09:00 auxv
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 cgroup
--w-------   1 laradock laradock 0 Mar  4 09:00 clear_refs
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 cmdline
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 comm
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 coredump_filter
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 cpu_resctrl_groups
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 cpuset
lrwxrwxrwx   1 laradock laradock 0 Mar  4 09:00 cwd -> /var/www/hugoBlog/content/posts/dev/liunx下php多进程编程/code/
-r--------   1 laradock laradock 0 Mar  4 09:00 environ
lrwxrwxrwx   1 laradock laradock 0 Mar  4 09:00 exe -> /usr/bin/php7.4*
dr-x------   2 laradock laradock 0 Mar  4 09:00 fd/
dr-x------   2 laradock laradock 0 Mar  4 09:00 fdinfo/
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 gid_map
-r--------   1 laradock laradock 0 Mar  4 09:00 io
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 limits
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 loginuid
dr-x------   2 laradock laradock 0 Mar  4 09:00 map_files/
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 maps
-rw-------   1 laradock laradock 0 Mar  4 09:00 mem
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 mountinfo
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 mounts
-r--------   1 laradock laradock 0 Mar  4 09:00 mountstats
dr-xr-xr-x  53 laradock laradock 0 Mar  4 09:00 net/
dr-x--x--x   2 laradock laradock 0 Mar  4 09:00 ns/
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 numa_maps
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 oom_adj
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 oom_score
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 oom_score_adj
-r--------   1 laradock laradock 0 Mar  4 09:00 pagemap
-r--------   1 laradock laradock 0 Mar  4 09:00 patch_state
-r--------   1 laradock laradock 0 Mar  4 09:00 personality
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 projid_map
lrwxrwxrwx   1 laradock laradock 0 Mar  4 09:00 root -> //
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 sched
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 schedstat
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 sessionid
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 setgroups
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 smaps
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 smaps_rollup
-r--------   1 laradock laradock 0 Mar  4 09:00 stack
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 stat
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 statm
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 status
-r--------   1 laradock laradock 0 Mar  4 09:00 syscall
dr-xr-xr-x   3 laradock laradock 0 Mar  4 09:00 task/
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 timens_offsets
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 timers
-rw-rw-rw-   1 laradock laradock 0 Mar  4 09:00 timerslack_ns
-rw-r--r--   1 laradock laradock 0 Mar  4 09:00 uid_map
-r--r--r--   1 laradock laradock 0 Mar  4 09:00 wchan

如果我们开发一个守护进程的web项目,开启了大量的子进程,并且没有回收,那么服务器的内存和存储空间可能会被挤满,所以必须回收。

进程的回收

调用 pcntl_wait 函数会产生的几种情况

  1. 如果没有子进程,可能会返回错误

  2. 如果子进程还没有结束,就会阻塞父进程

    示例:

    <?php
    
    $pid = pcntl_fork();
    
    if (0 === $pid) {
        fprintf(STDOUT, "我是子进程,pid = %d,运行完我就没事啦。\n", posix_getpid());
        while (1) {
            # code...
        }
        // exit(256); // 0 = 成功、-1 = 失败(返回255)、最大值 255、超过 255 返回 0
    } else {
        fprintf(STDOUT, "我是父进程,pid = %d。\n", posix_getpid());
    
        $exitPid = pcntl_wait($status);
        if ($exitPid > 0){
            fprintf(STDOUT,"pid=%d,子进程已经挂了,它的终止状态码:%d,并且已经完全释放了它所占用的资源...\n", $pid, pcntl_wexitstatus($status));
        }else{
            fprintf(STDOUT,"wait error...\n");
        }
        while(1){
            fprintf(STDOUT,"我在打印...\n");
            sleep(3);
        }
    }
    

    运行结果:

    laradock@3a6c2da5a07b:$ php demo5.php
    我是父进程,pid = 1652。
    我是子进程,pid = 1653,运行完我就没事啦
    ... // 子进程没结束,父进程被阻塞
    

    进程状态:

    laradock@3a6c2da5a07b:/proc$ ps -exj
        PPID     PID    PGID     SID TTY        TPGID STAT   UID   TIME COMMAND
           0     333      333     333 pts/1       1655 Ss    1000   0:00 bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/larado
           0     807      807     807 pts/2       1652 Ss    1000   0:00 bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/larado
         807     1652     1652    807 pts/2       1652 S+    1000   0:00 php demo5.php LC_ALL=en_US.UTF-8 NVM_DIR=/home/laradock/.nvm LS_COLORS=no=00:fi=00:
        1652     1653     1652    807 pts/2       1652 R+    1000   0:08 php demo5.php LC_ALL=en_US.UTF-8 NVM_DIR=/home/laradock/.nvm LS_COLORS=no=00:fi=00:
    
  3. 给函数传递第三个参数 option 可以让父进程不阻塞

示例:

<?php

$pid = pcntl_fork();

if (0 === $pid) {
    fprintf(STDOUT, "我是子进程,pid = %d,运行完我就没事啦。\n", posix_getpid());

    sleep(3);

    exit(10);
}

while(1){
    fprintf(STDOUT, "我是父进程,pid = %d。\n", posix_getpid());

    $exitPid = pcntl_wait($status, WNOHANG);

    if ($exitPid > 0){
        fprintf(STDOUT,"pid=%d,子进程已经挂了,它的终止状态码:%d,并且已经完全释放了它所占用的资源...\n", $pid, pcntl_wexitstatus($status));
        break;
    }else if (0 === $exitPid) {
        fprintf(STDOUT,"我在打印1...\n");
    } else {
        $no = posix_errno();
        fprintf(STDOUT,"wait error... %s\n", posix_strerror($no));
    }
    fprintf(STDOUT,"我在打印2...\n");
    sleep(1);
}

fprintf(STDOUT, "父进程,pid = %d,结束。\n", posix_getpid());

运行结果:

laradock@3a6c2da5a07b:$ php demo5.php
我是父进程,pid = 1952。
我在打印1...
我在打印2...
我是子进程,pid = 1953,运行完我就没事啦。
我是父进程,pid = 1952。
我在打印1...
我在打印2...
我是父进程,pid = 1952。
我在打印1...
我在打印2...
我是父进程,pid = 1952。
我在打印1...
我在打印2...
我是父进程,pid = 1952。
pid=1953,子进程已经挂了,它的终止状态码:10,并且已经完全释放了它所占用的资源...
父进程,pid = 1952,结束。

判断进程的退出方式

可以通过函数判断进程的退出方式,获取终止状态码和中断信号编号。

posix_killkill):是用来发送一个中断信号(给进程或者一个进程组)。

示例:

<?php

echo posix_getpid();

$pid = pcntl_fork();

if ($pid==0){

    while(1){
        fprintf(STDOUT,"pid=%d child process do...\n",posix_getpid());
        sleep(2);
        // return;
    }
}

while (1){

    $pid = pcntl_wait($status,WNOHANG);//让父进程以非阻塞方式运行

    fprintf(STDOUT,"exit pid=%d\n",$pid);

    if ($pid>0){

        // 正常退出
        if(pcntl_wifexited($status)){
            fprintf(STDOUT,"正常退出:exit pid=%d,exit-status=%d\n",$pid,pcntl_wexitstatus($status));

        }

        //中断退出
        else if (pcntl_wifsignaled($status)){

            fprintf(STDOUT,"中断退出1:exit pid=%d,SIGNUM=%d\n",$pid,pcntl_wtermsig($status));
        }

        // 一般是发送SIGSTOP SIGTSTP 要让进程停止
        // 以阻塞方式回收,才能生效 $pid = pcntl_wait($status,WNOHANG);
        else if (pcntl_wifstopped($status)){
            fprintf(STDOUT,"中断退出2:exit pid=%d,SIGNUM=%d\n",$pid,pcntl_wstopsig($status));
        }

    }

    fprintf(STDOUT,"PID=%d father process...\n",posix_getpid());

    sleep(3);
}

中断信号有自己的信号编号和对应的信号名字,信号编号是以非负数值来表示,信号名字是以SIG开头的,可以通过 kill -l 查看中断信号列表。