我在<defunct>
(和top
)中有一些显示为ps
的流程。我从真实的脚本和程序中汲取了一些东西。
在我的crontab
:
* * * * * /tmp/launcher.sh /tmp/tester.sh
launcher.sh
的内容(当然标记为可执行文件):
#!/bin/bash
# the real script does a little argument processing here
"$@"
tester.sh
的内容(当然标记为可执行文件):
#!/bin/bash
sleep 27 & # the real script launches a compiled C program in the background
ps
显示以下内容:
user 24257 24256 0 18:32 ? 00:00:00 [launcher.sh] <defunct>
user 24259 1 0 18:32 ? 00:00:00 sleep 27
请注意,tester.sh
没有出现 - 它在启动后台作业后退出。
为什么launcher.sh
会留下来,标记为<defunct>
?它似乎只是在由cron
启动时执行此操作 - 而不是在我自己运行它时。
附加说明:launcher.sh
是运行的系统中的常见脚本,不容易修改。其他事情(crontab
,tester.sh
,甚至是我运行的程序而不是sleep
)都可以更容易地修改。
答案 0 :(得分:11)
因为它们不是wait(2)
系统调用的主题。
由于某人可能会在将来等待这些进程,因此内核无法完全摆脱它们,或者它无法执行wait
系统调用,因为它不具有退出状态或其存在的证据。
当你从shell启动一个shell时,你的shell会捕获SIGCHLD并进行各种等待操作,所以没有任何东西可以长时间停止运行。
但是cron没有处于等待状态,它正在睡觉,所以已经不复存在的孩子可能会坚持一段时间,直到cron醒来。
更新:回复评论... 嗯。我确实设法复制了这个问题:
PPID PID PGID SESS COMMAND
1 3562 3562 3562 cron
3562 1629 3562 3562 \_ cron
1629 1636 1636 1636 \_ sh <defunct>
1 1639 1636 1636 sleep
所以,发生了什么事,我想:
daemon_fork()
期间所见libdaemon installs a SIGCHLD handler,这可能会干扰1629中间快速退出的信号传递现在,我甚至不知道我的Ubuntu系统上是否有vixie cron甚至用libdaemon构建,但至少我有一个新的理论。 :-)
答案 1 :(得分:4)
我怀疑cron正在等待会话中的所有子进程终止。有关负pid参数,请参阅wait(2)。你可以看到SESS:
ps faxo stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
这是我看到的(编辑过的):
STAT EUID RUID TT TPGID SESS PGRP PPID PID %CPU COMMAND
Ss 0 0 ? -1 3197 3197 1 3197 0.0 cron
S 0 0 ? -1 3197 3197 3197 18825 0.0 \_ cron
Zs 1000 1000 ? -1 18832 18832 18825 18832 0.0 \_ sh <defunct>
S 1000 1000 ? -1 18832 18832 1 18836 0.0 sleep
请注意,sh和sleep在同一个SESS中。
使用命令setsid(1)。这是tester.sh:
#!/bin/bash
setsid sleep 27 # the real script launches a compiled C program in the background
请注意,您不需要&
,setsid会将其置于后台。
答案 2 :(得分:4)
我认为它是由进程CROND(由crond为每个任务生成)等待stdin上的输入引起的,该stdin被传送到crontab中命令的stdout / stderr。这样做是因为cron能够通过邮件将结果输出发送给用户。
因此,CROND正在等待EOF,直到用户命令及其生成的所有子进程已关闭管道。如果这样做,CROND继续使用wait语句,然后defunct user命令消失。
所以我认为你必须明确地将脚本中的每个衍生子进程从管道中断开(例如通过将其重定向到文件或/ dev / null。
所以以下行应该在crontab中起作用:
* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & )
答案 3 :(得分:3)
我建议您通过简单地没有两个单独的流程来解决问题:让launcher.sh
在最后一行执行此操作:
exec "$@"
这将消除多余的过程。
答案 4 :(得分:1)
我在寻找具有类似问题的解决方案时发现了这个问题。不幸的是,这个问题的答案并没有解决我的问题。
杀死已解决的进程不是一个选项,因为您需要查找并终止其父进程。我最终以下列方式杀死了已解散的进程:
ps -ef | grep '<defunct>' | grep -v grep | awk '{print "kill -9 ",$3}' | sh
在“grep''”中,您可以将搜索范围缩小到您所追求的特定已解散的过程。
答案 5 :(得分:-2)
我已经多次测试了同样的问题。 最后我得到了解决方案。 只需在bash脚本之前指定'/ bin / bash',如下所示。
* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh