本文目录导读:

在 PHP 中,孤儿进程 指的是:父进程先于子进程退出,导致子进程被 init 进程(PID 1,通常是 systemd)收养的进程。
下面详细说明 PHP 中如何处理和利用孤儿进程。
PHP 如何创建子进程
PHP 主要通过 pcntl_fork() 函数创建子进程:
$pid = pcntl_fork();
if ($pid == -1) {
// 创建失败
die('fork failed');
} elseif ($pid == 0) {
// 子进程执行的代码
// 子进程的 PID 是 0(实际是子进程本身)
// 父进程 ID (PPID) 是创建它的父进程
while (true) {
echo "子进程运行中,父进程 PID: " . posix_getppid() . "\n";
sleep(1);
}
} else {
// 父进程执行的代码
// $pid 是子进程的 PID
echo "父进程结束\n";
exit(0); // 父进程先退出
}
孤儿进程的产生
上面的代码中,父进程 exit(0) 先退出,而子进程还在循环运行。
- 子进程的 PPID(父进程 ID)会变成 1(init / systemd)
- 这个子进程就成了 孤儿进程
验证方法:在父进程退出后,用 ps -ef | grep php 可以看到子进程的 PPID = 1。
处理孤儿进程的方式
不处理(默认)
$pid = pcntl_fork();
if ($pid == 0) {
sleep(10);
// 子进程继续运行,父进程已退出
}
// 父进程立即退出
exit(0);
子进程将由 init 进程接管,自动回收资源(不会变成僵尸进程)。
父进程等待子进程(避免产生孤儿)
使用 pcntl_wait() 或 pcntl_waitpid():
$pid = pcntl_fork();
if ($pid == 0) {
sleep(5);
exit(0);
}
// 父进程等待子进程结束
pcntl_wait($status);
// 此时子进程已经结束,不会产生孤儿
exit(0);
通过 signal 处理孤儿进程的退出状态
更常见的是,父进程先退出,但希望知道子进程什么时候结束:
// 父进程设置信号处理
pcntl_signal(SIGCHLD, function ($signo) {
// 子进程退出时触发
while (($pid = pcntl_waitpid(-1, $status, WNOHANG)) > 0) {
echo "子进程 $pid 已退出\n";
}
});
$pid = pcntl_fork();
if ($pid == 0) {
sleep(5);
exit(0);
}
// 父进程可以做其他事情
sleep(10);
exit(0);
故意使用孤儿进程的场景
守护进程 (Daemon) 创建
常见模式:父进程 fork 后立即退出,让子进程被 init 接管,脱离终端控制。
function daemonize() {
$pid = pcntl_fork();
if ($pid < 0) {
die('fork failed');
}
if ($pid > 0) {
// 父进程退出
exit(0);
}
// 子进程(孤儿进程)继续运行
// 此时子进程被 init 收养
// 创建新会话,脱离终端
posix_setsid();
// 可以再 fork 一次,确保不是会话领导者
$pid = pcntl_fork();
if ($pid < 0) {
die('fork failed');
}
if ($pid > 0) {
exit(0);
}
// 子进程(真正的守护进程)运行在此
while (true) {
// 守护进程的工作
sleep(60);
}
}
与僵尸进程的区别
| 类型 | 产生原因 | 影响 | 处理方式 |
|---|---|---|---|
| 孤儿进程 | 父进程先退出 | 无害,被 init 收养并回收 | 自动处理 |
| 僵尸进程 | 子进程先退出,父进程未调用 wait() | 占用进程表项,有害 | 父进程必须 wait() 或杀死父进程 |
完整示例
// 创建孤儿进程的完整示例
$pid = pcntl_fork();
if ($pid == -1) {
die("fork failed");
}
if ($pid == 0) {
// 子进程
echo sprintf("子进程 PID: %d, 父进程 PID: %d\n",
posix_getpid(), posix_getppid());
// 模拟子进程工作
for ($i = 0; $i < 10; $i++) {
sleep(1);
// 理想情况下,父进程退出后,PPID 会变成 1
echo "工作 $i 次, 当前父 PID: " . posix_getppid() . "\n";
}
exit(0);
} else {
// 父进程
echo "父进程 PID: " . posix_getpid() . ", 子进程 PID: $pid\n";
sleep(2); // 等待 2 秒
echo "父进程即将退出...\n";
exit(0); // 父进程退出,子进程变为孤儿
}
运行结果示例:
父进程 PID: 12345, 子进程 PID: 12346
子进程 PID: 12346, 父进程 PID: 12345
工作 0 次, 当前父 PID: 12345
工作 1 次, 当前父 PID: 12345
父进程即将退出...
工作 2 次, 当前父 PID: 1 ← 变成孤儿,被 init 收养
工作 3 次, 当前父 PID: 1
...
- PHP 创建孤儿进程:父进程 fork 后先 exit()
- 孤儿进程无害:被 PID 1 的进程自动收养
- 用途:常用于创建守护进程(daemon)
- 避免僵尸进程:父进程应调用
pcntl_wait()或 catchSIGCHLD - 注意事项:PHP 需启用
pcntl和posix扩展(CLI 模式)
在实际应用中,孤儿进程的 "父进程" 变成了 init,这通常是安全的,但如果需要精确控制子进程的生命周期,建议让父进程等待子进程结束。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播