My current program is creating child processes and giving them work (CPU intensive work). The main() sits there and waits for the child processes to send data via pipes (using select).
What I wanted to do is when the program is processing data I could press CTRL+C to stop the child processes from working and asking the user if he wants to quit or resume work.
If user wants to quit, the program would kill all the processes. If user wants to resume work, it would tell the child processes to resume the computation.
I already have the code in place but it's not quite working right.
In main I have signal(SIGINT, pausar);
to handle SIGINT (CTRL+C).
Thi开发者_如何学JAVAs is the pausar() function:
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 resumiu\n", pid[i]);
}
}
}
The problem is that sometimes I press CTRL+C and nothing shows in the console (but the processes STOP because I'm paying attention to the process manager). The other problem is that after I enter 'C' to resume work, I get errors in select() and the children never resume work.
Using select()
and signal-handler at the same time is prone to race conditions - a signal could occur during the select()
call, but also in every other line of code.
If your are on linux: create an event socket with signalfd()
and add this socket to the read set passed to select()
. Signals are then handled at a fixed point in your code and you do not need to worry about race conditions.
First, for what you're trying to-do, your signal handler is way too complex. Secondly, calling signal()
inside your signal handler is not a good idea ... it's not an asynchronous signal-safe function.
What you can do is the following:
- In your main, set the signal handler function using
signal()
like you've done. - Block the SIGINT signal via
sigprocmask()
. This prevents a spurious signal from arriving before the call topselect()
. - Inside your signal handler only set a simple global flag that is a
sig_atomic_t
- Use
pselect()
instead ofselect()
. This will allow you to change the process signal mask to allow a SIGINT signal to arrive, and it will do-so in an atomic manner with respect to signals. Otherwise, you could have your SIGINT arrive before the call toselect()
, and then you have "lost" that signal, even though it does set the flag in the handler. - When the
pselect()
call returns, detect whether the flag has been set. - If the global
sig_atomic_t
flag was set, and you returned frompselect
because of a caught signal, then launch another function that will actually do all the ending of the child-processes and prompt the user, etc.
Doing these steps will simplify your signal-handling code and reduce the chances of race-conditions or other unexpected results because of the asynchronous nature of signal arrival.
If you'd like some more information on pselect()
, you there is a nice article on that here.
精彩评论