PHP万字码出完美守护进程详解
简介
本攻略的目的是为了帮助 PHP 开发者了解如何实现 PHP 守护进程,主要包括以下内容:
- 什么是守护进程
- 为什么需要守护进程
- PHP 实现守护进程的方法
- 守护进程实现注意事项
- 示例:守护进程监控文件变化
- 示例:守护进程定时任务
什么是守护进程
守护进程是在后台运行的进程。与其他后台进程不同的是,守护进程在系统启动时就会自动启动,而且不会和任何控制终端连接,这意味着不会受到 Ctrl+C 的影响。同时,守护进程也会自动重新启动,这在服务器端十分重要。
为什么需要守护进程
通常情况下,PHP 脚本只有在通过命令行方式启动时才会执行,如果需要在后台常驻运行,则需要使用守护进程。比如需要定时执行一些任务、监控一些事物等。
PHP 实现守护进程的方法
基本方法
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die('无法创建子进程');
} else if ($pid) {
exit(); // 父进程,直接退出
}
// 子进程,开启守护进程
posix_setsid(); // 创建新的会话,让进程脱离终端
chdir('/'); // 更改工作目录
umask(0); // 设置权限掩码,避免因为继承的环境导致权限问题
// 守护进程主体代码
这是一个比较基础的创建守护进程的方法。首先使用 pcntl_fork() 创建子进程,父进程直接退出,子进程通过 posix_setsid() 创建新会话,脱离终端并成为新的进程组和会话组的首进程。然后更改工作目录和设置权限掩码等操作,最后在子进程中执行守护进程主体代码。
守护进程生命周期控制方法
<?php
class Daemon {
private static function signalHandler($signal) {
switch ($signal) {
case SIGTERM:
case SIGHUP:
// 处理结束信号
exit();
break;
case SIGCHLD:
// 子进程退出处理
break;
}
}
public static function start() {
$pid = pcntl_fork();
if ($pid == -1) {
die('无法创建子进程');
} else if ($pid) {
exit(); // 父进程,直接退出
}
// 子进程,开启守护进程
posix_setsid(); // 创建新的会话,让进程脱离终端
chdir('/'); // 更改工作目录
umask(0); // 设置权限掩码,避免因为继承的环境导致权限问题
// 注册信号处理函数
pcntl_signal(SIGTERM, array(__CLASS__, 'signalHandler')); // 结束信号
pcntl_signal(SIGHUP, array(__CLASS__, 'signalHandler')); // 重新加载配置信号
pcntl_signal(SIGCHLD, array(__CLASS__, 'signalHandler')); // 子进程退出信号
// 守护进程主体代码
}
public static function stop($pidFile) {
// 读取 PID 文件中的 PID
$pid = file_get_contents($pidFile);
if (!$pid) {
echo 'PID 文件不存在';
return;
}
// 发送结束进程信号
posix_kill($pid, SIGTERM);
unlink($pidFile);
echo '进程已结束';
}
}
这是一个控制守护进程生命周期的方法。加入信号处理函数,守护进程在收到 SIGTERM 或 SIGHUP 信号时将会结束进程,可以通过监测是否存在 PID 文件来判断进程是否正在运行。
守护进程实现注意事项
- 文件打开和日志处理要使用绝对路径,避免进程环境发生变化。
- 要注意 PID 文件的管理,避免多个进程同时运行。
- 在子进程中设置资源限制,防止因为进程过多导致系统崩溃。
- 在处理子进程退出信号时,要使用 pcntl_waitpid() 函数避免成为僵尸进程。
示例:守护进程监控文件变化
<?php
class Monitor {
private $lastMTime;
private $fileName;
public function __construct($fileName) {
$this->init($fileName);
}
public function run() {
while (true) {
clearstatcache(); // 清空文件状态缓存
if (file_exists($this->fileName)) {
$mtime = filemtime($this->fileName);
if ($mtime !== $this->lastMTime) {
$this->lastMTime = $mtime;
// 文件发生变化,触发回调函数
$this->onChange();
}
}
sleep(1); // 秒级轮询
}
}
private function init($fileName) {
$this->lastMTime = filemtime($fileName);
$this->fileName = $fileName;
}
public function onChange() {
// 文件变化回调函数
}
}
Daemon::start();
$monitor = new Monitor('/path/to/monitor/file');
$monitor->run();
这是一个示例守护进程用于监控文件变化的代码。在初始化时读取文件的 mtime 作为上次修改时间,然后通过内置的 run() 方法进行循环读取文件状态信息,如果监测到文件的 mtime 发生变化则触发回调函数。在使用时需要将此代码作为守护进程程序运行。
示例:守护进程定时任务
<?php
class Job {
public function run() {
// 定时任务逻辑代码
}
}
class Scheduler {
private $jobs;
public function __construct() {
$this->jobs = array();
}
public function addJob(Job $job, $interval = 60) {
$this->jobs[] = array(
'job' => $job,
'interval' => $interval,
'lastRunTime' => 0,
);
}
public function run() {
while (true) {
$runTime = time(); // 计算本次循环的运行时间
foreach ($this->jobs as &$job) {
if ($runTime - $job['lastRunTime'] >= $job['interval']) {
$job['lastRunTime'] = $runTime;
// 调用定时任务的 run() 方法
$job['job']->run();
}
}
sleep(1); // 秒级轮询
}
}
}
Daemon::start();
$scheduler = new Scheduler();
$job = new Job();
$scheduler->addJob($job, 5 * 60); // 每隔 5 分钟运行一次
$scheduler->run();
这是一个示例守护进程用于定时任务的代码。在使用时,需要将定时任务的逻辑代码写在 Job 类的 run() 方法中,并将其添加到 Scheduler 中,设置定时任务的间隔时间(单位:秒),最后调用 Scheduler 的 run() 方法启动定时任务守护进程。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:php万字码出完美守护进程详解 - Python技术站