如何在Linux上同步终止一个进程?

5
当我调用 kill() 函数杀死一个进程时,它会立即返回,因为它只是发送了一个信号。我的代码在一个无限循环中检查一些(外部的,不由我编写或修改的)进程,如果它们超过一些限制(如占用太多内存等),就会杀死它们(同时写入 syslog 等)。问题是当进程大量使用交换空间时,需要许多秒才能将它们杀死,因此我的进程会执行相同的检查多次,并尝试向同一进程发送多次信号,也写入 syslog。(这不是故意的,这是我正在尝试解决的副作用)
我不关心它向进程发送信号的次数,但我关心它写入 syslog 的次数。我可以保持已经发送过 kill 信号的 PID 列表,但理论上,即使概率很低,可能有另一个进程生成了与先前被杀死的进程相同的 pid,该进程也可能需要被杀死,在这种情况下,日志将不完整。
我不知道是否存在任何进程的唯一标识符,但我怀疑不存在。那么我应该如何同步地杀死一个进程,或者跟踪已经收到信号不需要再记录的进程?

也许可以在即将被杀死的进程中设置一个信号处理程序,当接收到 SIGINT 信号时写入文件,然后自行终止进程? - C.B.
但是这个过程正在监视外部进程,不仅限于我编写并可以修改的进程。 - Petr
5个回答

6
即使您可以进行“同步杀”,仍然存在竞争条件,您可能会杀死错误的进程。每当您想要杀死的进程自愿退出或第三方操作之后,在您杀死它之前,都可能发生这种情况。在此间隔期间,PID 可能被分配给新进程。基本上没有解决这个问题的方法。PIDs 本质上是属于已识别进程的父进程的本地资源;任何其他进程使用 PID 都是竞争条件。
如果您对系统有更多的控制(例如,控制您想要终止的进程的父进程),那么可能会有特殊情况的解决方案。还可能有基于使用 /proc 中的某些机制来避免竞争的(仅限于 Linux 的)解决方案,尽管我不知道有哪些。
另一个解决方法可能是像调试一样使用 ptrace 在目标进程上。这允许您部分“窃取”父级角色,避免 PID 失效,同时您仍在使用它,并允许您在进程终止时获得通知。您可以执行以下操作:
1.检查进程信息(例如从/proc中)以确定您要终止的进程。
2.对其进行ptrace,暂时停止它。
3.重新检查进程信息,以确保您获得了要终止的进程。
4.恢复跟踪的进程。
5.kill它。
6.等待(通过 waitpid)通知进程已退出。

2
该死...这一切都是真的。你没有让我开心 :) 但还是谢谢你的好回答。 - Petr

1
这将使脚本等待进程终止。
kill $PID
while [ kill -0 $PID 2>/dev/null  ]
do
     sleep 1
done

kill -0 [pid] 测试进程是否存在


1
你测试过了吗?我认为你不想在kill命令周围加上[和]。我非常确定你只需要使用while kill -0 $PID 2>/dev/null - Zan Lynx
当进程可以杀死多个PID时,它是否应该阻塞进程? - C.B.
2
如果使用旧的pid生成另一个进程,则可能会挂起。 - tmyklebu
谢谢你的回答,我说的是C语言,但这段代码也可以,只是它不能百分之百地工作——如果在两次kill调用之间有另一个使用相同pid的进程被生成,它会继续检查,但不是同一个进程。 - Petr
如果进程存在且不响应SIGTERM(第一个kill),则此操作将陷入无限循环。 - codeforester

1
以下解决方案适用于大多数不是调试器或在调试器中被调试的进程。
  • 使用带有参数PTRACE_ATTACHptrace附加到进程。这会停止您想要终止的进程。此时,您应该验证是否已附加到正确的进程。
  • 使用SIGKILL杀死目标。现在它已经消失了。
  • 我记不清楚进程现在是否是一个需要收割的僵尸进程,还是您需要先使用PTRACE_CONT。无论哪种情况,您最终都必须调用waitpid来收割它,这时您就知道它已经死了。

0
如果您正在使用C语言编写此代码,则可以使用kill系统调用发送信号。与其重复发送终止信号,不如只发送一次,然后循环(或以某种方式定期检查)使用kill(pid, 0);。信号的零值只会告诉您进程是否仍在运行,并且您可以采取适当的措施。当进程终止时,kill将返回ESRCH

1
如果在两次调用kill(pid,0)之间进程死亡并且另一个进程使用相同的pid生成,会发生什么?这种情况极不可能,但是有可能发生。在这种情况下,将不会返回ESRCH。 - Petr
1
你现在是如何通过向进程发送信号来避免这种情况的? - Duck
我并不是在回避它,我想知道如何避免它 :) 所以我在这里问... 顺便说一下,“轰炸”只是由于整个问题引起的副作用,如果有其他方法可以解决它,我会很乐意尝试。 - Petr
我的意思是,它并不是有意地被重复杀死。这是因为该进程的死亡速度比我的程序在循环中执行检查的速度慢。由于我的程序无法知道是否已经向该进程发送了信号(在Linux中不存在真正的唯一标识符),并且自上次收到sigkill以来它没有死亡,因此它会接收到另一个sigkill……然后再次,再次……这是一个错误,而不是一个功能。 - Petr
你的选择是创建一堆毫无意义的日志消息(和杀死信号),或者为了少量额外的代码成本做出合理的努力(例如,进程是否具有最初使您想要终止它的资源使用特征),并记录您可以记录的内容。 - Duck
显示剩余5条评论

0
  • 当您生成这些进程时,可以使用经典的waitpid(2)家族

  • 如果在其他地方没有使用,您可以将要被杀死的进程移动到自己的cgroup中;这些cgroup上可以有通知器,在进程退出时触发。

  • 要找出进程是否已被杀死,可以chdir(2)进入/proc/<pid>open(2)此目录。进程终止后,那里的状态文件将无法访问。这种方法是竞争的(在您的检查和操作之间,进程可能会终止并生成一个具有相同pid的新进程)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接