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
2 年前
虽然 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
8 年前
检查进程是否正在运行。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 的文件;其中一个将为您列出它们。
Anonymous
13 年前
这是一个用于 Windows 的进程 kill 函数

<?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