PHP Conference Japan 2024

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
函式 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 操作定義了一個非完整子集(例如 SOCKET_EPERM),但它沒有包含所有可能的 POSIX 錯誤常數;例如,ESRCH 對 posix_kill() 特別感興趣,但 SOCKET_ESRCH 不存在,因為它表示「沒有此類程序」並且對 socket 沒有意義。

解決方案
* 讓 PHP 開發人員在未來的 PHP 版本中定義這些常數。
* 在您的系統上查找 errno.h 並定義您自己的常數。您可以使用腳本來解析 errno.h,並即時定義常數或生成一次定義它們的 PHP 代碼。

但是,請注意,依賴於特定的 errno.h 是不可移植的。不同的系統可能對這些常數具有不同的數值。這就是為什麼 PHP 應該在編譯時定義常數,並且代碼應該能夠依賴常數名稱;只有名稱是可移植的,實際值則不是。
gid at gifpaste dot net
21 年前
對於那些想要終止符合特定模式的所有程序的人(類似於 Linux 中的 killall),請嘗試以下操作。請注意,為了跨平台相容性,執行此類操作是一個好主意,而不是執行 killall,因為其他 UNIX 系統的 killall 只會終止所有程序。

函式 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 {
返回 "$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 年前
請記住,您只能將 kill 訊號傳送到您 UID 所擁有的行程。

如果您以 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
函式 win_kill($pid){
$wmi=新 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
函式 killProcessAndChilds($pid,$signal) {
exec("ps -ef| awk '\$3 == '$pid' { print \$2 }'", $output, $ret);
if(
$ret) return '您需要 ps、grep 和 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