2024年PHP开发者大会日本站

posix_kill

(PHP 4, PHP 5, PHP 7, PHP 8)

posix_kill向进程发送信号

描述

posix_kill(int $process_id, int $signal): bool

将信号 signal 发送到进程标识符为 process_id 的进程。

参数

process_id

进程标识符。

signal

一个PCNTL信号常量

返回值

成功返回 true,失败返回 false

参见

  • POSIX系统的kill(2)手册页,其中包含关于负进程标识符、特殊pid 0、特殊pid -1和信号编号0的附加信息。

添加注释

用户贡献的注释 12条注释

codeslinger at compsalot dot com
19年前
检测另一个程序副本是否正在运行(*NIX专用)

一个巧妙的技巧是发送信号0来查看另一个进程是否正在运行。信号0实际上不会被发送,但kill会检查是否可以发送信号。请注意,这只有在您有权向该进程发送信号的情况下才有效。

此技术的实际用途是避免运行同一程序的多个副本。您以通常的方式将PID保存到文件中……然后在启动期间,您检查PID文件的值,并查看该进程当前是否存在。

这并非完全万无一失。在极少数情况下,不相关的程序可能具有相同的已回收PID。但是,另一个程序很可能不会接受来自您的程序的信号(除非您的程序是root)。

为了使其尽可能可靠,您希望您的程序在关闭时删除其PID文件(参见register_shutdown_function)。这样,只有当您的程序崩溃并且另一个程序碰巧使用相同的PID并且另一个程序愿意接受来自您的程序的信号时,您才会得到错误的结果。这将是极其罕见的情况。这也假设PID文件没有被篡改(所有依赖PID文件的程序都是如此……)。

也可以使用“ps x”来检测这一点,但是使用kill效率更高。

这是核心程序

$PrevPid = file_get_contents($PathToPidFile);

if(($PrevPid !== FALSE) && posix_kill(rtrim($PrevPid),0)) {
echo "错误:服务器已使用PID:$PrevPid运行\n";
exit(-99);
} else {
echo "正在启动服务器…";
}

嗯……如果您想要完全100%的可靠性以及效率。您可以做的是使用kill进行初始检查。如果它说没有运行,那么您就可以准备运行了。但是,如果kill说已经在运行,那么您可以使用

//您可以从$argv[0]获取$ProgramName
$Result = shell_exec('ps x | grep "' . $PrevPid . '" | grep "' . $ProgramName . '" | grep -v "grep"');

假设您的程序有权执行此操作。如果您执行该命令并返回空字符串,则另一个程序是使用已回收PID的冒名顶替者,您可以继续运行。

-- Erik
php at tla-sys dot com
3年前
尽管posix_kill()似乎内置于所有现代版本的PHP中,但它实际上需要“process”扩展。否则,调用它时会收到“未定义函数”错误。您可以尝试以下方法来提高代码在Linux上的健壮性
<?php
function send_signal(int $pid, int $sig_num) : bool {
if (
function_exists("posix_kill") ) return posix_kill($pid, $sig_num);
exec("/usr/bin/kill -s $sig_num $pid 2>&1", $junk, $return_code);
return !
$return_code;
}
?>
是的,当$sig_num = 0时它可以正常工作。
Skippy
15年前
为了检查posix_kill()的结果,您可以使用posix_get_last_error(),它在成功的情况下返回0(零),否则返回非零错误代码。使用posix_get_last_error()返回的数字作为posix_strerror()的参数,以获取与该错误代码相对应的可读错误消息。

请注意,来自posix_get_last_error()的非零代码并不意味着pid不存在;它只意味着posix_kill()在向该进程发出信号时遇到了麻烦。例如,pid可能存在,但进程由您用于运行代码的用户以外的用户拥有,而您不是root;在这种情况下,您会收到一条错误消息,说明您无权向该进程发出信号(操作不被允许)。

因此,Jille之前发布的代码是错误的。根据POSIX规范(请参阅您系统上的errno.h),EPERM表示“操作不被允许”。这不应被视为pid不存在的指示,它仅仅意味着posix_kill()无法向该进程发出信号。如果有什么不同的话,它应该暗示具有该pid的进程正在运行。

errno.h常量名称定义
http://www.opengroup.org/onlinepubs/009695399/basedefs/errno.h.html

不幸的是,PHP目前没有定义这些名称的常量(例如EPERM、ENOENT、ESRCH等)。为套接字操作定义了一个不完整的子集(例如SOCKET_EPERM),但它并不包含所有可能的POSIX错误常量;例如,ESRCH对于posix_kill()特别重要,但SOCKET_ESRCH不存在,因为它意味着“没有这样的进程”,对套接字没有意义。

解决方案
* 让PHP开发人员在未来的PHP版本中定义这些常量。
* 查看您系统上的errno.h并定义您自己的常量。您可以使用脚本来解析errno.h,并动态定义常量或生成一次PHP代码来定义它们。

但是,请注意,依赖于特定的errno.h不可移植。不同的系统可能对这些常量具有不同的数值。这就是为什么PHP应该在编译时定义常量,并且代码应该能够依赖常量名称;只有名称是可移植的,值本身不可移植。
gid at gifpaste dot net
21年前
对于那些想要杀死所有匹配特定模式的进程(类似于linux的killall)的人,请尝试以下方法。请注意,为了实现跨平台兼容性,执行此操作是个好主意,而不是执行killall,因为其他UNIX的killall就是这样做的,杀死所有东西。 :)

function killall($match) {
if($match=='') return '未指定模式';
$match = escapeshellarg($match);
exec("ps x|grep $match|grep -v grep|awk '{print $1}'", $output, $ret);
if($ret) return '你需要安装ps、grep和awk才能运行此程序';
while(list(,$t) = each($output)) {
if(preg_match('/^([0-9]+)/', $t, $r)) {
system('kill '. $r[1], $k);
if(!$k) $killed = 1;
}
}
if($killed) {
return '';
} else {
return "$match:没有杀死任何进程";
}
}
Martin
9年前
警告!

如果您从浏览器运行脚本(例如作为Apache进程),则$sig参数不能是PCNTL信号常量之一。奇怪的是,它似乎允许从命令行(“php -r …”)使用这些常量,但是如果您的脚本在Apache内部运行,则调用posix_kill($pid, SIGINT)将返回FALSE(并且posix_get_last_error()将返回0)。
eddi13
9年前
检查进程是否正在运行。Windows-Linux
<?php
function isRunning($pid) {
$isRunning = false;
if(
strncasecmp(PHP_OS, "win", 3) == 0) {
$out = [];
exec("TASKLIST /FO LIST /FI \"PID eq $pid\"", $out);
if(
count($out) > 1) {
$isRunning = true;
}
}
elseif(
posix_kill(intval($prevPid), 0)) {
$isRunning = true;
}
return
$isRunning;
}
?>
Jacques Manukyan
16年前
请记住,你只能向你的UID拥有的进程发送kill信号。

如果你的程序以root身份运行,那么你可以向所有进程发送kill信号。
Mauro
10年前
Posix_kill不太可靠(它似乎总是传递信号——即使是0到一个不存在的进程——并且不会产生错误)。
我找到了这种方法来检查进程的存在,使用/proc

if(!file_exists("/proc/$pid/cmdline"

显然要检查权限。
unsure at tactileint dot com
13年前
就像错误代码(EPERM、EEXIST等)一样,信号编号在不同的平台上也不同。例如,在macos上,SIGCONT是19;在Linux上,SIGSTOP是19。区别很大。

如果你的PCNTL已编译,你可以使用SIGCONT之类的常量;我相信它们都是正确的。

如果没有,请查看/usr/include/signal.h;现在有很多ifdef的冗余代码,但你可以去/usr/include/bits或/usr/include/sys之类的目录,查找名为sig*.h的文件;其中一个会列出它们。
匿名
13年前
这是一个用于Windows的进程终止函数

<?php
function win_kill($pid){
$wmi=new COM("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2");
$procs=$wmi->ExecQuery("SELECT * FROM Win32_Process WHERE ProcessId='".$pid."'");
foreach(
$procs as $proc)
$proc->Terminate();
}
?>
regis dot fr dot php dot net at tornad dot net
15年前
一个小的递归函数,用于终止进程及其子进程。
它对我来说运行良好,我没有找到其他方法来做到这一点。
这是我找到的各种脚本的混合。

<?php
function killProcessAndChilds($pid,$signal) {
exec("ps -ef| awk '\$3 == '$pid' { print \$2 }'", $output, $ret);
if(
$ret) return 'you need ps, grep, and awk';
while(list(,
$t) = each($output)) {
if (
$t != $pid ) {
killProcessAndChilds($t,$signal);
}
}
//echo "killing ".$pid."\n";
posix_kill($pid, 9);
}
?>
Jille at mydevnull dot quis dot cx
16年前
如果你想测试其他用户拥有的进程是否正在运行,你可以使用

<?php
$running
=posix_kill($pid, 0);
if(
posix_get_last_error()==1) /* EPERM */
$running=true;
?>

如果进程由其他人拥有(并且你不是root),你将得到一个EPERM错误。
在我的系统(FreeBSD)上,它被定义为1。

你应该测试EPERM在你的系统上的值。
To Top