Не получать SIGCHLD для процессов, выполняемых с помощью sudo

В настоящее время я пишу оболочку. Я выполняю процессы и использую обработчик сигнала SIGCHLD для очистки (ждут на них), когда они будут завершены.

Все работает, за исключением случаев, когда я выполняю процессы, которые повышают привилегии с помощью sudo. В этих случаях я никогда не получаю сигнал SIGCHLD, поэтому я никогда не знаю, что процесс завершил выполнение.

Когда я получаю команду, например sudo ls, я запускаю программу sudo, а затем предоставляю ls в качестве параметра. Я выполняю это выполнение с execvp.

Если я посмотрю 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 и ожидание непосредственно на sudo и PID, которые sudo выполняется (4796 в приведенном выше примере). Ни один из этих методов не работал.

Как всегда, любой совет приветствуется.

Ответ 1

Моя первая мысль - некорректная обработка сигнала, но в вашем сообщении недостаточно информации для написания тестового кода для репликации вашего сбоя. Но я могу дать вам несколько мест, чтобы посмотреть. Простите меня, если я расскажу о нескольких основах сигнала, которые вы уже знаете для будущих читателей.

Прежде всего, я не знаю, используете ли вы устаревший сигнал() или новые сигнальные процедуры POSIX sigaction(), чтобы поймать сигналы. sigset() полезен между GNU.

Устаревшие сигналы - сигнал()
Это почти невозможно, если не невозможно, гарантировать герметичный процессор сигналов с использованием исходного процессора сигналов во всех средах.

  • В некоторых системах UNIX, входящих в обработчик сигнала, может reset обработчик к состоянию по умолчанию. Последующие сигналы гарантированно теряются, если обработчик явно не примет сигнал reset.
  • Обработчики сигналов() не должны предполагать, что они вызываются один раз для каждого сигнала.  
    • Обработчики должны выполнять цикл while( ( pid = waitpid( -1, &signal, WNOHANG ) ) > 0 ),     пока не будет найдено больше сигналов, поскольку устаревшие сигналы устанавливают условие bool     показывая хотя бы один сигнал.     Фактический счет неизвестен.
    • Обработчики должны допускать, чтобы сигналы не были обнаружены, если обработанный ранее цикл while()     сигнал.
  • Разрешить сигналы от неизвестных процессов... если запущенная программа также запускается процесс внука вы можете наследовать этот процесс, если ваш ребенок быстро выйдет.

Совет, держите нос и убегайте от устаревших сигналов.

Отсутствие цикла while() в устаревшем обработчике и нескольких SIGCHILD, один из вашего sudo и один или несколько из неожиданных внуков, выпущенных sudo. Если обрабатывается только один SIGCHILD, когда сигнал внука входит первым, ожидаемый программный сигнал не будет улавливаться.

Сигналы POSIX - sigaction() Сигналы POSIX могут очищать все сбои устаревших сигналов.

  • Установить обработчик без восстановления (восстановление НЕ является частью сигналов POSIX и часто, по крайней мере, на мой взгляд, зло, когда вы можете получить более одного сигнала для обработки таким же образом).
  • Сигналы sigaction() являются липкими... они живут, пока явно не изменились (замечательно!). Ничего из этого сложного требования, связанного с необходимостью reset обработчика сигнала в обработчике.
  • Установите маску для маскировки текущего сигнала при обработке сигнала. Параноиды также маскируют любой другой сигнал, передаваемый тому же обработчику.

Недостаток маски может вызвать странные вещи, такие как потеря трека сигнала, если вы получаете SIGCHILD, находясь в обработчике SIGCHILD.

GNU - sigset()
GNU обеспечивает полезную промежуточную связь, которая имеет те же сигнатуры вызова, что и signal(), но устраняет большинство проблем. Также доступны некоторые дополнительные функции управления. Использование sigset() является простым решением для многих проблем с сигналами.

Напоминания
Подумайте о обработчиках сигналов в виде потоков в вашей программе, даже если вы в противном случае не используете потоки в коде.

В старые времена вам нужно было сделать абсолютно минимальную обработку обработчиков сигналов... отсутствие вызова библиотечного кода, таких как printf, которые имеют побочные эффекты. Я по-прежнему придерживаюсь этого, когда нужно использовать устаревшие обработчики сигналов и всегда использовать многопоточные предупреждения в новых обработчиках.