我目前正在编写shell。我执行进程并利用SIGCHLD
信号处理程序在它们完成时清理(等待它们)。
一切都在运作 - 除非我执行使用sudo
升级权限的流程。在这些情况下,我从未得到SIGCHLD
信号 - 因此我永远不知道该过程已完成执行。
当我收到诸如sudo ls
之类的命令时,我执行程序sudo
,然后提供ls
作为参数。我使用execvp
执行此执行。
如果我在shell执行ps -aux
后查看sudo ls
,我会看到以下内容:
root 4795 0.0 0.0 4496 1160 pts/29 S+ 16:51 0:00 sudo ls
root 4796 0.0 0.0 0 0 pts/29 Z+ 16:51 0:00 [ls] <defunct>
因此,sudo
运行并被分配pid = 4795
,并为子(ls)分配了4796
。孩子已经完成了任务,现在正处于一个僵尸状态。 sudo
似乎不想收获僵尸进程而只是坐在那里。
我想知道导致此行为的原因 - 我尝试了不同的技术来清理这些僵尸进程,例如在sudo
下运行我的shell并直接在sudo
和{ {1}} PID
执行的{1}}(上例中为4796)。这些技术都没有奏效。
与往常一样,任何建议都表示赞赏。
答案 0 :(得分:4)
我的第一个想法是错误的信号处理,但是你的帖子中没有足够的信息来编写测试代码来复制你的失败。但我可以给你一些看的地方。如果我为未来的读者介绍一些你已经知道的信号基础,请原谅我。
首先,我不知道您是使用传统信号()还是新的POSIX sigaction()信号例程来捕获信号。 sigset()在GNU之间是有用的。
传统信号 - 信号()
即使不是不可能,也几乎不可能保证在所有环境中使用原始信号处理器的气密信号处理器。
while( ( pid = waitpid( -1, &signal, WNOHANG ) ) > 0 )
循环,
直到找不到更多信号,因为传统信号设置了bool条件
指示至少一个信号未完成。
实际计数未知。
建议,抓住你的鼻子,逃离传统信号。
传统处理程序和多个SIGCHILD中缺少while()循环,一个来自你的sudo,一个或多个来自意外的孙子孙女被sudo解雇。如果孙子信号首先进入时只处理一个SIGCHILD,则不会捕获预期程序的信号。
POSIX信号 - sigaction()
POSIX信号可以清除传统信号的所有故障。
如果在SIGCHILD处理程序中获得SIGCHILD,缺少掩码会导致丢失信号跟踪等奇怪的事情。
GNU - sigset()
GNU提供了一个有用的中间版本,它具有与signal()相同的调用签名,但删除了大多数问题。还提供一些额外的控制功能。使用sigset()可以轻松解决许多信号问题。
提醒的
将信号处理程序视为程序中的线程,
即使你没有在代码中使用线程。
在旧的时候你需要在信号处理程序中完成最少的处理......不需要调用库代码, 如printf,有副作用。 当我不得不使用传统信号处理程序时,我仍然遵循这一点,并且总是在新的处理程序中使用多线程注意事项。