信号与进程
Opened this issue · 0 comments
信号与进程
信号量 socket 消息队列 共享内存
信号是一种用来控制进程的机制。进程是指正在执行的程序,它可以在后台运行或在前台与用户交互。
通过发送信号,您可以控制进程的执行,例如终止进程、暂停和恢复进程等。
信号是通过 PHP 的 pcntl_signal()
函数来处理的。
pcntl_signal_dispatch(
举个例子,如果您希望在进程执行到特定的位置时终止进程,您可以
使用 pcntl_signal() 函数注册一个信号处理器,当信号被触发时,
该处理器就会执行指定的动作,例如终止进程。
简而言之,信号与进程是 PHP 中的重要概念,它们可用来控制程序的执行。
通过使用 pcntl_signal() 函数,您可以创建信号
处理器来响应信号,从而控制进程的执行。
PHP信号是一种操作系统的机制,用于在进程之间进行通信。
它可以通过终止程序、触发自定义事件等方式来传递信息。
PHP进程是一个独立的执行单元,拥有自己的地址空间、数据和指令集。
它可以用来执行不同的任务,以便于多线程并发执行。
使用信号与进程可以提高PHP程序的性能,同时也可以实现复杂的逻辑。
但要注意,由于信号与进程都是操作系统级别的机制,
在 PHP 中,pcntl_signal()
函数可以用来处理进程中的信号。
信号是一种轻量级的进程间通信方式,它可以在进程之间传递一个特定的消息,
表示某种状态发生了改变。例如,当一个进程收到 SIGINT 信号(即中断信号)时,它可能会停止执行并退出。
使用 pcntl_signal() 函数,你可以设置一个处理程序来响应特定的信号。
这样,当进程收到指定的信号时,处理程序就会被执行。这个函数接受两个参数:
要处理的信号编号,以及一个用于处理该信号的 PHP 函数。
例如,下面的代码演示了如何使用 pcntl_signal() 函数来处理 SIGINT 信号:
// 设置处理程序
function handle_sigint() {
echo "Caught SIGINT...\n";
exit;
}
// 使用 pcntl_signal() 函数处理信号
pcntl_signal(SIGINT, 'handle_sigint');
// 执行一些操作
while (true) {
echo "Hello, world!\n";
sleep(1);
}
在这个例子中,当进程收到 SIGINT 信号时,handle_sigint() 函数就
会被执行,并打印一条消息,然后退出进程。
需要注意的是,使用 pcntl_signal() 函数处理信号需要 PHP 的 pcntl 扩展支持。
pcntl_signal_dispatch() 函数是用来处理信号的队列的。当 PHP
脚本收到一个信号时,它会将这个信号放到一个队列中,等待处理。
pcntl_signal_dispatch() 函数用来检查这个队列,并执行队列中的信号处理程序。
例如,假设我们已经设置了一个处理程序来响应 SIGINT 信号,
我们可以使用 pcntl_signal_dispatch() 函数来处理信号队列:
// 设置处理程序
function handle_sigint() {
echo "Caught SIGINT...\n";
exit;
}
// 使用 pcntl_signal() 函数处理信号
pcntl_signal(SIGINT, 'handle_sigint');
// 执行一些操作
while (true) {
echo "Hello, world!\n";
sleep(1);
// 检查信号队列,并执行处理程序
pcntl_signal_dispatch();
}
在这个例子中,我们每次循环都会调用 pcntl_signal_dispatch() 函数,
来检查信号队列,并执行处理程序。
workerman
protected static function installSignal()
{
if (static::$_OS !== \OS_TYPE_LINUX) {
return;
}
$signalHandler = '\Workerman\Worker::signalHandler';
// stop
\pcntl_signal(\SIGINT, $signalHandler, false);
// graceful stop
\pcntl_signal(\SIGTERM, $signalHandler, false);
// reload
\pcntl_signal(\SIGUSR1, $signalHandler, false);
// graceful reload
\pcntl_signal(\SIGQUIT, $signalHandler, false);
// status
\pcntl_signal(\SIGUSR2, $signalHandler, false);
// connection status
\pcntl_signal(\SIGIO, $signalHandler, false);
// ignore
\pcntl_signal(\SIGPIPE, \SIG_IGN, false);
}
protected static function reinstallSignal()
{
if (static::$_OS !== \OS_TYPE_LINUX) {
return;
}
$signalHandler = '\Workerman\Worker::signalHandler';
// uninstall stop signal handler
\pcntl_signal(\SIGINT, \SIG_IGN, false);
// uninstall graceful stop signal handler
\pcntl_signal(\SIGTERM, \SIG_IGN, false);
// uninstall reload signal handler
\pcntl_signal(\SIGUSR1, \SIG_IGN, false);
// uninstall graceful reload signal handler
\pcntl_signal(\SIGQUIT, \SIG_IGN, false);
// uninstall status signal handler
\pcntl_signal(\SIGUSR2, \SIG_IGN, false);
// uninstall connections status signal handler
\pcntl_signal(\SIGIO, \SIG_IGN, false);
// reinstall stop signal handler
static::$globalEvent->add(\SIGINT, EventInterface::EV_SIGNAL, $signalHandler);
// reinstall graceful stop signal handler
static::$globalEvent->add(\SIGTERM, EventInterface::EV_SIGNAL, $signalHandler);
// reinstall reload signal handler
static::$globalEvent->add(\SIGUSR1, EventInterface::EV_SIGNAL, $signalHandler);
// reinstall graceful reload signal handler
static::$globalEvent->add(\SIGQUIT, EventInterface::EV_SIGNAL, $signalHandler);
// reinstall status signal handler
static::$globalEvent->add(\SIGUSR2, EventInterface::EV_SIGNAL, $signalHandler);
// reinstall connection status signal handler
static::$globalEvent->add(\SIGIO, EventInterface::EV_SIGNAL, $signalHandler);
}
protected static function monitorWorkersForLinux()
{
static::$_status = static::STATUS_RUNNING;
while (1) {
// Calls signal handlers for pending signals.
\pcntl_signal_dispatch();
// Suspends execution of the current process until a child has exited, or until a signal is delivered
$status = 0;
$pid = \pcntl_wait($status, \WUNTRACED);
// Calls signal handlers for pending signals again.
\pcntl_signal_dispatch();
// If a child has already exited.
if ($pid > 0) {
// Find out which worker process exited.
foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
if (isset($worker_pid_array[$pid])) {
$worker = static::$_workers[$worker_id];
// Exit status.
if ($status !== 0) {
static::log("worker[" . $worker->name . ":$pid] exit with status $status");
}
// For Statistics.
if (!isset(static::$_globalStatistics['worker_exit_info'][$worker_id][$status])) {
static::$_globalStatistics['worker_exit_info'][$worker_id][$status] = 0;
}
++static::$_globalStatistics['worker_exit_info'][$worker_id][$status];
// Clear process data.
unset(static::$_pidMap[$worker_id][$pid]);
// Mark id is available.
$id = static::getId($worker_id, $pid);
static::$_idMap[$worker_id][$id] = 0;
break;
}
}
// Is still running state then fork a new worker process.
if (static::$_status !== static::STATUS_SHUTDOWN) {
static::forkWorkers();
// If reloading continue.
if (isset(static::$_pidsToRestart[$pid])) {
unset(static::$_pidsToRestart[$pid]);
static::reload();
}
}
}
// If shutdown state and all child processes exited then master process exit.
if (static::$_status === static::STATUS_SHUTDOWN && !static::getAllWorkerPids()) {
static::exitAndClearAll();
}
}
}
问:关于信号阻塞实现reload功能,如何保证「完成当前进程内任务后再退出」
答:在看这个解答前,先看下第二个问题,然后再回来。这个非常简单,
就是说reload只是参数,TA背后应该是收到这个参数后向某个进程发送
某个信号比如SIGUSR1。进程提前设置了SIGUSR1的mask,所以进程会首先
「兜住」SIGUSR1信号暂时不理会SIGUSR1(注意不是一直不理会),
一旦进程中UNBLOCK了这个信号,进程立马会相应这个信号,你让进程相应
该信号时候退出进程即可。顺序上就是先设置屏蔽SIGUSR1信号,进程开始
处理常规业务流程,处理完毕后解除屏蔽SIGUSR1信号。
有一定应用场景限制,适合进程中是一次性的任务。