本篇文章给大家分享的内容是PHP实现系统编程之 编写守护进程详解,有着一定的参考价值,有需要的朋友可以参考一下
(一)进程组、会话、控制终端、控制进程等概念
进程组:每个进程都有一个所属的进程组 (process group),进程组有一个进程组长(process group leader),进程组ID即为这个进程组长的进程号,所以判断一个进程是否为进程组组长,只需比较该进称号是否和它的进程组id相等即可,PHP中可以用函数 posix_getpgrp() 获取当前进程的进程组id,用 posix_getpid() 获取当前进程的进程号。
<?php function isGroupLeader() { return posix_getpgrp() == posix_getpid(); } $pid = pcntl_fork(); if($pid == 0) { echo '子进程:' . PHP_EOL; } elseif($pid > 0) { sleep(2); echo '父进程:' . PHP_EOL; } echo "当前进程组gid:" . posix_getpgrp() . PHP_EOL; echo "当前进程号pid:" . posix_getpid() . PHP_EOL; if (isGroupLeader()) { echo 'Is a process group leader' . PHP_EOL; } else { echo 'Is not a process group leader' . PHP_EOL; }
以上例程会输出:
子进程: 当前进程组gid:15827 当前进程号pid:15828 Is not a process group leader 父进程: 当前进程组gid:15827 当前进程号pid:15827 Is a process group leader
会话:会话(session)是若干进程组的集合,会话中的一个进程组为会话组长(session leader),会话ID即为这个会话组长的进程组id,PHP中可以使用函数 posix_getsid(int $pid) 来获取指定进程的会话id,也可以使用函数 posix_setsid() 来创建一个新的会话,此时该进程成为新会话的会话组长,该函数调用成功返回新创建的会话ID,或者在失败出错时返回-1,注意linux中调用 posix_setsid() 函数的进程不能是进程组长,否则会调用失败,这是由于一个进程组中的进程不能同时跨多个会话。
linux 中关于setsid的文档介绍:
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, 表示调用失败
以上例程会输出:
当前会话id: 13000 当前进程是进程组长 int(-1)
那该如何新建会话呢,我们注意到前面使用pcntl_fork() 创建了一个子进程后,这个子进程就不是进程组长,所以可以利用子进程来创建新会话。
<?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; }
以上例程会输出:
当前进程所属会话ID:13000 [root@localhost php]# 不是进程组组长 进程组ID:15856 进程号pid: 15857 int(15857) 当前进程所属会话ID:15857
利用子进程成功创建了新的会话。
控制终端和控制进程:(终端是所有输入输出设备的总称,比如键盘,鼠标,显示器都是一个终端)一个会话可以有一个控制终端,一个控制终端被一个会话独占。会话刚创建的时候是没有控制终端的,但会话组长可以申请打开一个终端,如果这个终端不是其他会话的控制终端,这时的终端将会成为会话的控制终端,会话组长叫做控制进程。
linux下判断一个会话是否拥有控制终端,我们可以尝试打开一个特殊的文件 /dev/tty , 他指向了真实的控制终端,如果打开成功说明拥有控制终端,反之则没有控制终端。
<?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"; } }
上述例程子进程新建了一个会话,然后父子进程都尝试打开文件 /dev/tty,例程输出如下:
不是进程组组长 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 拥有控制终端
产生SIGHUP信号
1、当一个会话失去控制终端时,内核会向该会话的控制进程发送一个 SIGHUP 信号,而通常会话的控制进程是shell进程,shell在收到一个 SIGHUP 信号时,会向由它创建的所有进程组(前台或后台进程组)也发送一个SIGHUP信号,然后退出,进程收到一个SIGHUP信号的默认处理方式就是退出进程,当然进程也可以自定义信号处理或者忽略它。
2、另外,当控制进程终止时,内核也会向终端的前台进程组的所有成员发送SIGHUP信号。
<?php $callback = function($signo){ $sigstr = 'unkown signal'; switch($signo) { case SIGINT: $sigstr = 'SIGINT'; break; case SIGHUP: $sigstr = 'SIGHUP'; break; case SIGTSTP: $sigstr = 'SIGTSTP'; 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(); }
使用 php sighup.php运行起该程序,然后直接关掉终端,重新登录shell,会发现该程序仍在运行,daemon.txt 文件中会 记录捕获到的SIGHUP信号。
[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
同时linux下提供了一个nohup命令,可以让进程忽略所有的SIGHUP信号,例如
[root@localhost php]# nohup php sighup.php nohup: 忽略输入并把输出追加到"nohup.out"
(二)标准输入、标准输出、标准错误输出
php中有三个默认打开的文件句柄 STDIN,STDOUT, STDERR 分别对应上述三个文件描述符,而由于标准输入输出是和终端相关的,对于守护进程来说并没有什么用,可以直接关闭,但是直接关闭可能会造成一个问题,请看下面这段代码
<?php fclose(STDOUT); $fp = fopen("stdout.log", "a"); echo "hello world\n";
运行上述代码时,屏幕不会输出echo的信息,而是写入到打开的文件中了,这是由于关闭STDOUT文件句柄后,释放了对应的文件描述符,而linux打开文件总是使用最小的可用文件描述符,所以这个文件描述符现在指向fopen打开的文件了,导致原本写到标准输出的信息现在写到了文件里。为了避免这种怪异的行为,我们在关闭这三个文件句柄之后可以立即打开 linux提供的黑洞文件 /dev/null,比如:
<?php fclose(STDIN); fclose(STDOUT); fclose(STDERR); fopen('/dev/null', 'r'); fopen('/dev/null', 'w'); fopen('/dev/null', 'w'); $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('/dev/null', 'r'); fopen('/dev/null', 'w'); fopen('/dev/null', 'w'); //切换工作目录 chdir('/'); //清除文件掩码 umask(0); //由于内核不会再为进程产生SIGHUP信号,我们可以使用该信号来实现热重启 pcntl_signal(SIGHUP, function($signo){ //重新加载配置文件,重新打开日志文件等等 }); for(;;) { pcntl_signal_dispatch(); //处理信号回调 //实现业务逻辑 }
to be continue!
相关推荐:
PHP实现系统编程之本地套接字(Unix Domain Socket)
以上是PHP实现系统编程之 编写守护进程详解的详细内容。更多信息请关注PHP中文网其他相关文章!

tomodifyDataNaphPsession,startTheSessionWithSession_start(),然后使用$ _sessionToset,修改,orremovevariables.1)startThesession.2)setthesession.2)使用$ _session.3)setormodifysessessvariables.3)emovervariableswithunset()

在PHP会话中可以存储数组。1.启动会话,使用session_start()。2.创建数组并存储在$_SESSION中。3.通过$_SESSION检索数组。4.优化会话数据以提升性能。

PHP会话垃圾回收通过概率机制触发,清理过期会话数据。1)配置文件中设置触发概率和会话生命周期;2)可使用cron任务优化高负载应用;3)需平衡垃圾回收频率与性能,避免数据丢失。

PHP中追踪用户会话活动通过会话管理实现。1)使用session_start()启动会话。2)通过$_SESSION数组存储和访问数据。3)调用session_destroy()结束会话。会话追踪用于用户行为分析、安全监控和性能优化。

利用数据库存储PHP会话数据可以提高性能和可扩展性。1)配置MySQL存储会话数据:在php.ini或PHP代码中设置会话处理器。2)实现自定义会话处理器:定义open、close、read、write等函数与数据库交互。3)优化和最佳实践:使用索引、缓存、数据压缩和分布式存储来提升性能。

phpsessionstrackuserdataacrossmultiplepagerequestsusingauniqueIdStoredInacookie.here'showtomanageThemeffectionaly:1)startAsessionWithSessionwwithSession_start()和stordoredAtain $ _session.2)

在PHP中,遍历会话数据可以通过以下步骤实现:1.使用session_start()启动会话。2.通过foreach循环遍历$_SESSION数组中的所有键值对。3.处理复杂数据结构时,使用is_array()或is_object()函数,并用print_r()输出详细信息。4.优化遍历时,可采用分页处理,避免一次性处理大量数据。这将帮助你在实际项目中更有效地管理和使用PHP会话数据。

会话通过服务器端的状态管理机制实现用户认证。1)会话创建并生成唯一ID,2)ID通过cookies传递,3)服务器存储并通过ID访问会话数据,4)实现用户认证和状态管理,提升应用安全性和用户体验。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

Atom编辑器mac版下载
最流行的的开源编辑器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

Dreamweaver Mac版
视觉化网页开发工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

WebStorm Mac版
好用的JavaScript开发工具