使用 sudo 执行的进程未收到 SIGCHLD

发布于 2024-12-17 06:15:19 字数 900 浏览 0 评论 0原文

我目前正在编写 shell。我执行进程并利用 SIGCHLD 信号处理程序在进程完成时进行清理(等待它们)。

一切都正常 - 除非我执行使用 sudo 升级权限的进程。在这些情况下,我永远不会收到 SIGCHLD 信号 - 所以我永远不知道该进程已完成执行。

当我收到诸如 sudo ls 之类的命令时,我会执行程序 sudo,然后提供 ls 作为参数。我使用 execvp 执行此执行。

如果我在 shell 执行 sudo ls 后查看 ps -aux,我会看到以下内容:

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以及 sudo 执行的 PID(上例中的 4796)。这些技术都不起作用。

一如既往,如有任何建议,我们将不胜感激。

I'm currently in the process of writing a shell. I execute processes and utilize a SIGCHLD signal handler to clean up (wait on them) when they are complete.

Everything has been working -- except when I execute processes which escalate privileges with sudo. In these cases, I never get a SIGCHLD signal -- so I never know that the process has completed executing.

When I receive a command such as sudo ls, I execute the program sudo and then provide ls as a parameter. I perform this execution with execvp.

If I take a look at ps -aux after my shell has executed sudo ls, I see the following:

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>

So, sudo ran and got assigned pid = 4795, with the child (ls) being assigned 4796. The child has completed its task and is now sitting in a zombie state. sudo doesn't seem to want to reap the zombie process and just sits there.

I would like to know what is causing this behavior -- I've tried different techniques to cleanup these zombie processes, such as running my shell under sudo and waiting directly on sudo and the PID which sudo executes (4796 in the above example). None of these techniques have worked.

As always, any advise is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

怀里藏娇 2024-12-24 06:15:19

我的第一个想法是信号处理不正确,但您的帖子中没有足够的信息来编写测试代码来复制您的故障。但我可以给你一些地方可以看看。如果我为未来的读者介绍一些您已经了解的信号基础知识,请原谅我。

首先,我不知道您是否使用旧的 signal() 还是新的 POSIX sigaction() 信号例程来捕获信号。 sigset() 是 GNU 中的一个有用的工具。

传统信号——signal()

要在所有环境中使用原始信号处理器来保证信号处理器的气密性,即使不是不可能,也几乎是不可能的。

  • 在某些 UNIX 系统上,输入信号处理程序可以将处理程序重置为默认状态。除非处理程序显式重置信号,否则后续信号肯定会丢失。
  • signal() 处理程序不得假设每个信号都会调用一次它们。
    • 处理程序必须执行 while( ( pid = waitpid( -1, &signal, WNOHANG ) ) > 0 ) 循环,
      直到找不到更多信号,因为传统信号设置了布尔条件
      表明至少有一个信号未完成。
      实际人数未知。
    • 处理程序必须允许在处理先前的 while() 循环时找不到信号
      信号。
  • 允许来自未知进程的信号...如果您启动的程序也启动
    如果您的子进程快速退出,您可以继承该孙进程。

建议,捂住鼻子,逃离遗留信号。

遗留处理程序中缺少 while() 循环和多个 SIGCHILD,其中一个来自您的 sudo,一个或多个来自 sudo 意外触发的孙子。如果当孙子信号首先到来时只处理一个 SIGCHILD,则预期程序的信号将不会被捕获。

POSIX 信号 -- sigaction()

POSIX 信号可以清除遗留信号的所有故障。

  • 设置处理程序,无需恢复(恢复不是 POSIX 信号的一部分,而且通常是,
    至少在我看来,当你可能会得到多个以相同方式处理的信号时,这是邪恶的)。
  • sigaction() 信号是粘性的......它们一直存在直到明确改变(太棒了!)。
    不再需要再次重置信号处理程序这一麻烦的要求
    在处理程序中。
  • 设置掩码,在处理信号时屏蔽掉当前信号。偏执狂还会屏蔽传递给同一处理程序的任何其他信号。

缺少掩码可能会导致奇怪的事情,例如如果您在 SIGCHILD 处理程序中收到 SIGCHILD,则丢失信号的跟踪。

GNU -- sigset()

GNU 提供了一个有用的中间方案,它具有与 signal() 相同的调用签名,但消除了大部分问题。还提供一些附加控制功能。使用 sigset() 可以轻松解决许多信号问题。

提醒

将信号处理程序视为程序中的线程,
即使您没有在代码中使用线程。

在过去,您需要在信号处理程序中进行绝对最少的处理......无需调用库代码,
例如 printf,它有副作用。
当必须使用旧的信号处理程序时,我仍然遵循这一点,并且始终在新的处理程序中使用多线程警告。

My first thought is incorrect signal processing but there is not enough information in your post to write test code to replicate your failure. But I can give you some places to look. Pardon me if I cover a few signal basics you already know for future readers.

First of all I do not know if you are using the legacy signal() or the new POSIX sigaction() signal routines to catch signals. sigset() is a useful in between from GNU.

Legacy Signals -- signal()

It's near impossible, if not impossible, to guarantee an air-tight signal processor using the original signal processor in all environments.

  • On some UNIX systems entering the signal handler can reset the handler to the default condition. Subsequent signals are guaranteed to be lost unless the handler explicitly reset the signal.
  • signal() handlers must not assume they get called once for each signal.
    • Handlers must do a while( ( pid = waitpid( -1, &signal, WNOHANG ) ) > 0 ) loop,
      until no more signals are found as legacy signals set a bool condition
      indicating at least one signal is outstanding.
      The actual count is unknown.

    • Handlers must allow for no signals being found if a prior while() loop processed
      the signal.
  • Allow for signals from unknown processes... if the program you start also starts
    a grandchild process you may inherit that process if your child exits quickly.

Advice, hold your nose and flee from legacy signals.

Lack of a while() loop in a legacy handler and multiple SIGCHILDs, one from your sudo and one or more from unexpected grandchildren fired off by sudo. If only one SIGCHILD is handled when a grandchild signal comes in first, the expected program's signal will not be caught.

POSIX Signals -- sigaction()

POSIX signals can clean up all of the failures of legacy signals.

  • Set a handler, without a restore (restore is NOT part of POSIX signals and is often,
    at least in my mind, evil when you might get more than one signal to handle in the same way).

  • sigaction() signals are sticky... they live until expressly changed (wonderful!).
    None of this troublesome requirement of having to reset the signal handler again
    in the handler.

  • Set a mask to mask out the current signal when processing the signal. Paranoids will also mask any other signal passed to the same handler.

Lack of a mask can cause weird stuff like loosing track of a signal if you get a SIGCHILD while in a SIGCHILD handler.

GNU -- sigset()

GNU provides an useful in-between that has the same calling signatures as signal() but removes most of the problems. Some additional control functions are also available. Using sigset() is an easy fix for many signal problems.

Reminders

Think of signal handlers as threads in your program,
even if you are not otherwise using threads in the code.

In days of old you needed to do absolutely minimal processing in signal handlers... no calling of library code,
such as printf, that have side effects.
I still follow this when having to use legacy signal handlers and always use multithread cautions in newer handlers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文