Внешнее отключение сигналов для программы Linux
В Linux можно каким-то образом отключить сигнализацию для программ снаружи... то есть, не изменяя их исходный код?
Контекст:
Я вызываю программу C (, а также Java) из bash script в Linux. Мне не нужны перерывы для моего bash script, а для других программ, запускаемых script (в качестве процессов переднего плана).
Пока я могу использовать...
trap '' INT
... в моем bash script, чтобы отключить сигнал Ctrl C, это работает только тогда, когда управление программой происходит в коде bash. То есть, если я нажимаю Ctrl C во время работы программы C, программа C прерывается и выходит! Эта программа C выполняет некоторую критическую операцию, из-за которой я не хочу, чтобы она прерывалась. У меня нет доступа к исходному коду этой C-программы, поэтому обработка сигнала внутри программы C не может быть и речи.
#!/bin/bash
trap 'echo You pressed Ctrl C' INT
# A C program to emulate a real-world, long-running program,
# which I don't want to be interrupted, and for which I
# don't have the source code!
#
# File: y.c
# To build: gcc -o y y.c
#
# #include <stdio.h>
# int main(int argc, char *argv[]) {
# printf("Performing a critical operation...\n");
# for(;;); // Do nothing forever.
# printf("Performing a critical operation... done.\n");
# }
./y
Привет,
/HS
Ответы
Ответ 1
Маска сигнала процесса наследуется через exec
, поэтому вы можете просто написать небольшую программу-оболочку, которая блокирует SIGINT
и выполняет цель:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGINT);
sigprocmask(SIG_BLOCK, &sigs, 0);
if (argc > 1) {
execvp(argv[1], argv + 1);
perror("execv");
} else {
fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
}
return 1;
}
Если вы скомпилируете эту программу на noint
, вы просто выполните ./noint ./y
.
Как эфемерные примечания в комментариях, расположение сигнала также унаследовано, поэтому вы можете заставить обертку игнорировать сигнал, а не блокировать его:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
struct sigaction sa = { 0 };
sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &sa, 0);
if (argc > 1) {
execvp(argv[1], argv + 1);
perror("execv");
} else {
fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
}
return 1;
}
(и, конечно, для подхода с поясом и фигурными скобками, вы можете сделать и то, и другое).
Ответ 2
Команда "trap" является локальной для этого процесса, никогда не относится к дочерним элементам.
Чтобы действительно захватить сигнал, вы должны взломать его, используя крюк LD_PRELOAD
. Это непрозрачная задача (вы должны скомпилировать загружаемый с _init()
, sigaction()
внутри), поэтому здесь я не буду включать полный код. Вы можете найти пример для SIGSEGV на Phack Volume 0x0b, Issue 0x3a, Phile # 0x03.
Альтернативно попробуйте трюк nohup
и tail
.
nohup your_command &
tail -F nohup.out
Ответ 3
Я бы предположил, что ваше приложение C (и Java) нуждается в перезаписи, чтобы оно могло обрабатывать исключение, что произойдет, если его действительно нужно прервать, сбой питания и т.д.
I, который терпит неудачу, J-16 прав на деньги. Нужно ли пользователю переписываться с процессом или просто видеть результат (нужно ли даже видеть выход?)
Ответ 4
Решения, описанные выше, не работают для меня, даже связывая обе команды, предложенные Caf.
Однако мне, наконец, удалось получить ожидаемое поведение следующим образом:
#!/bin/zsh
setopt MONITOR
TRAPINT() { print AAA }
print 1
( ./child & ; wait)
print 2
Если я нажму Ctrl-C, когда выполняется child
, он будет ожидать, что он выйдет, а затем напечатает AAA и 2. child
не получит никаких сигналов.
Подлеум используется для предотвращения отображения PID.
И жаль... это для zsh, но вопрос в том, что для bash, но я не знаю bash, чтобы обеспечить эквивалент script.
Ответ 5
Это пример кода включения таких сигналов, как Ctrl + C, для программ, которые его блокируют.
fixControlC.c
#include <stdio.h>
#include <signal.h>
int sigaddset(sigset_t *set, int signo) {
printf("int sigaddset(sigset_t *set=%p, int signo=%d)\n", set, signo);
return 0;
}
Скомпилируйте его:
gcc -fPIC -shared -o fixControlC.so fixControlC.c
Запустите его:
LD_LIBRARY_PATH=. LD_PRELOAD=fixControlC.so mysqld