#c #handler #signals
#c #обработчик #сигналы
Вопрос:
Моя текущая программа создает дочерние процессы и дает им работу (интенсивная работа с процессором). Main() сидит там и ждет, пока дочерние процессы отправят данные по каналам (используя select).
Что я хотел сделать, так это когда программа обрабатывает данные, я мог бы нажать CTRL C, чтобы остановить работу дочерних процессов и спросить пользователя, хочет ли он прекратить или возобновить работу.
Если пользователь захочет выйти, программа уничтожит все процессы. Если пользователь хочет возобновить работу, он сообщит дочерним процессам возобновить вычисления.
У меня уже есть код на месте, но он работает не совсем правильно.
В основном я должен signal(SIGINT, pausar);
обрабатывать SIGINT (CTRL C).
Это функция pausar():
void pausar(int signum){
signal(SIGINT, pausar);
int i;
// pid[] contains all the child processes
for(i = 0; i<CORES; i )
{
kill(pid[i], SIGSTOP);
}
char option[2];
printf("n Computacao pausada.n'S' para sair ou 'C' para continuar: ");
scanf("%1s", option);
if (option[0] == 's' || option[0] == 'S') {
printf("A desligar...n");
//if user wants to quit, kill all the child processes
for(i = 0; i<CORES; i )
{
kill(pid[i], SIGKILL);
}
exit(0);
}
else
{
printf("[%d] A resumir computacao...n",getpid());
kill(getpid(), SIGCONT);
//if user wants to resume work, send signal to continue
for(i = 0; i<CORES; i )
{
kill(pid[i], SIGCONT);
printf("%d resumiun", pid[i]);
}
}
}
Проблема в том, что иногда я нажимаю CTRL C, и в консоли ничего не отображается (но процессы ОСТАНАВЛИВАЮТСЯ, потому что я обращаю внимание на диспетчер процессов). Другая проблема заключается в том, что после ввода ‘C’ для возобновления работы я получаю ошибки в select (), и дочерние элементы никогда не возобновляют работу.
Ответ №1:
Одновременное использование select()
и обработчика сигналов может привести к возникновению условий гонки — сигнал может возникнуть во время select()
вызова, но также и в любой другой строке кода.
Если вы используете Linux: создайте сокет события с signalfd()
помощью и добавьте этот сокет в набор чтения, переданный select()
. Затем сигналы обрабатываются в фиксированной точке вашего кода, и вам не нужно беспокоиться об условиях гонки.
Комментарии:
1. Может быть, вы имеете в виду
signalfd()
вместоeventfd()
?
Ответ №2:
Во-первых, для того, что вы пытаетесь сделать, ваш обработчик сигналов слишком сложен. Во-вторых, вызов signal()
внутри вашего обработчика сигналов — не очень хорошая идея… это не асинхронная функция, защищенная от сигналов.
Что вы можете сделать, так это следующее:
- В вашем основном, установите функцию обработчика сигналов, используя
signal()
, как вы это сделали. - Заблокируйте сигнал SIGINT через
sigprocmask()
. Это предотвращает поступление ложного сигнала до вызоваpselect()
. - Внутри вашего обработчика сигналов установите только простой глобальный флаг, который является
sig_atomic_t
- Используйте
pselect()
вместоselect()
. Это позволит вам изменить маску сигнала процесса, чтобы разрешить поступление сигнала SIGINT, и это будет сделано атомарным образом по отношению к сигналам. В противном случае ваш SIGINT может появиться до вызова toselect()
, и тогда вы «потеряете» этот сигнал, даже если он устанавливает флаг в обработчике. - Когда
pselect()
вызов возвращается, определите, был ли установлен флаг. - Если
sig_atomic_t
был установлен глобальный флаг, и вы вернулись изpselect
-за перехваченного сигнала, затем запустите другую функцию, которая фактически выполнит все завершение дочерних процессов и запросит пользователя и т. Д.
Выполнение этих шагов упростит ваш код обработки сигналов и уменьшит вероятность возникновения условий гонки или других неожиданных результатов из-за асинхронного характера поступления сигнала.
Если вам нужна дополнительная информация pselect()
, здесь есть хорошая статья об этом.
Комментарии:
1. это решение без
signalfd()
, хотя это невозможно сделать безопасно, неpselect()
включивSIGINT
только во времяpselect()
вызова, потому что всегда будет время непосредственно передselect()
вызовом, но глобальный флаг уже установлен. если сигнал возникает в это время, Strg C не будет обрабатываться доselect()
возврата.2. Хорошо, я обновлю вышеприведенный пост… спасибо, что указали на это