![通过PID获取进程的返回值](https://linux22.com/image/45748/%E9%80%9A%E8%BF%87PID%E8%8E%B7%E5%8F%96%E8%BF%9B%E7%A8%8B%E7%9A%84%E8%BF%94%E5%9B%9E%E5%80%BC.png)
如果我有一个被杀死的进程的 PID,我如何获得该进程的返回状态?
我特别想知道返回状态是 143 ( kill
) 还是 137 ( kill -9
),因为我不知道是什么终止了该进程。它是一个 Python 脚本,从我没有 root 访问权限的服务器上的 PHP 脚本运行。 Python 脚本运行几分钟,但 PHP 仅限运行 30 秒。 PHP 具有获取返回状态的功能,exec()
但是如果 PHP 脚本超时,则该功能将不起作用!
答案1
这几乎肯定是由(默认)php.ini
或等效设置决定的:
max_execution_time = 30
TLDR:如果您确定 PHP 确实首先退出,并且某些东西正在杀死您的脚本,那么您可以使用守护进程包装并监视Python进程(在非重新启动模式下,即没有--respawn
),或添加信号处理机到 Python 脚本。
一般来说,如果您能够作为脚本的用户 ID 运行 shell,那么您应该能够strace
或truss
进程,例如在 Linux 上您可以相当有效地执行此操作:
$ sleep 60 &
[1] 10873
$ strace -e trace=signal,process -p $!
Process 10873 attached - interrupt to quit
+++ killed by SIGKILL +++
Process 10873 detached
该sleep
进程由另一个终端的 终止kill -9
。kill -9
但这并不常见,因为进程无法捕获它并干净地退出。
要daemon
使用以下变体进行监控:
daemon -i --errlog=/tmp/mypy.log -- /usr/bin/python [...]
这将记录任何与终止相关的信号。如果您想无论如何都记录每个退出,请添加--dbglog
和。--debug=2
正如其他地方所指出的,如果该过程已经终止,那么它就消失了。只有父级(或 init)才能获取其退出代码,除非将其记录(可能使用进程记帐或审计),否则退出代码将丢失。
为了在 *nix 平台上进行内部超时处理,PHP 设置了一个SIGALARM
或SIGPROF
一个指定的超时。该信号处理程序只是调用内部zend_error()
函数。然而,它也调用任何注册回调和错误处理程序,这可能对您有用。
如果默认的错误处理程序启动,我相信 PHP 退出代码将是 255,因为超时是一个E_ERROR
.
另请注意,退出代码约定为 128+N(其中 N 是信号编号)是由于某些 shell(包括bash
)和许多 API 的行为造成的。信号后的实际进程退出代码取决于系统。这是大概只是信号编号,Linux 上通常是这种情况。这wait()
组系统调用提供了进程退出的更好详细信息。 PHP 遵循 shell 约定,例如,如果您正在使用popen()
,pclose()
当信号终止进程时,您将看到 128+N 作为退出代码。
这里的另一个考虑因素是 PHP 时间限制的行为,如果您检查set_time_limit()
您将看到的文档
set_time_limit() 函数和配置指令
max_execution_time
仅影响脚本本身的执行时间。system()
在确定脚本运行的最长时间时,不包括在脚本执行之外发生的任何活动所花费的时间,例如使用 的系统调用、流操作、数据库查询等。在 Windows 上情况并非如此,因为测量的时间是真实的。
您可以用一个简短的脚本来证明这一点:
<?php
# for (;;);
$exe="sleep 20";
print "exit code: " . pclose(popen($exe, 'r'));
?>
调用与time php -d max_execution_time=5 -f sleep3.php
.该脚本将运行20秒,不会出现错误。如果您终止睡眠进程,例如kill -USR1 $(pgrep sleep)
在另一个终端中,您将看到退出代码 138 (128+SIGUSR1)。
如果取消无限循环的注释for(;;)
,脚本将在 5 秒后终止。
答案2
已被终止的进程不再存在于系统中。事实上,在繁忙的主机上,一个长时间运行且被杀死的进程很有可能其 PID 已被其他进程重用,因此您最终会看到一些完全不相关的东西!
当一个进程被杀死时,它不返回任何内容。它根本不复存在。这与可捕获信号(例如 HUP 或 USR1)形成鲜明对比,该信号处理能(但不一定)以任何它认为合适的方式捕获和处理(包括使用定义的退出代码退出)。
如果您有足够的访问权限,您也许可以安装某种库或内核模块来拦截并记录系统调用,最终导致相关进程接收信号,但似乎更容易正如 pjc50 建议的那样只是添加包装脚本并在某处记录退出状态。
另一种选择是不让用户等待,而是让用户等待转向异步架构无论你正在做什么,都需要很长时间才能完成。
答案3
你要等待进程(),但我不知道 PHP 中是否可用。如果您不希望它被阻止,请不要忘记添加 WNOHANG。然而,只有当 PID 是调用进程的子进程时,这才有效。
最简单的方法可能是在 python 脚本周围放置一个包装脚本,将其终止状态记录在日志文件中的某个位置。
答案4
另一种选择:
来自 PHP:
exec("./wrapper.sh");
在wrapper.sh
:
#!/bin/bash
./wrapperlogger.sh >/dev/null 2>&1 &
在wrapperlogger.sh
:
./yourPythonScript
echo $? > exit_status.log
第一个包装器是该过程的后台。第二个包装器监视退出代码并将其记录到文件中。请注意,该脚本每次执行时都会覆盖相同的日志文件。