C using Signals to stop child processes - c

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).
This 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 to pselect().
Inside your signal handler only set a simple global flag that is a sig_atomic_t
Use pselect() instead of select(). 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 to select(), 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 from pselect 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.

Related

Ambiguous behaviour of kill system call in C

I have made this program and the output so far doesn't make much sense to me. Can someone please explain what is going on?
void handler1a(int x){
printf("A\n");
}
int main(){
signal(SIGUSR1, handler1a);
int p = fork();
if(p==0)
{
sleep(5);
printf("L \n");
}
else
{
kill(0,SIGUSR1);
kill(0,SIGUSR1);
kill(0,SIGUSR1);
//kill(0,SIGUSR1);
wait(NULL);
}
}
With 3 kill signals, my output is- 5A and 1L. With 2 kill signals, the output is- 4A and 1L. With 4 kill signals, output is- 6A and 1L. It seems like upto 2 kill signals, both parent and child process are using my custom handler but somehow one of them isn't using the handler or isn't getting the kill signal after receiving the signal it twice already (it would explain why only a single A is printed when I add another kill system call after 2 kill system calls).
Signals aren't queued. So if you send the same signal to a process multiple times, it may get processed any number of times from 1 to the number of times you sent the signal.
Or, to put it another way, for each combination of process and signal, the signal can be in the signaled state. If you send a signal to a process when that process is already in the signaled state for that signal, nothing happens.
A process cannot have two SIGUSR1 signals pending. Either SIGUSR1 is pending or it isn't.
Here kill is called by the parent process P with 0 as its pid value which means that all the processes in its group (P itself, as well as the child C) will get the signal and the same custom signal handler will be used to process it.
If you are sending multiple signals to the process C that are of same type (SIGUSR1 here), they will not be queued because that signal will be blocked until the first one received is handled, and they will be discarded.
Only when the signal handler for C returns, C is ready to process the signal again. This explains why there are fewer "A"s in the output from C's invocation of the handler.
You can see which process has called the handler by adding printf("A %d\n", getpid()) inside the handler.

Nested signal handlers in C

I want to work on signal handlers in the context of two independent processes namely writer and reader for notification. The writer sends a first signal SIGUSR1 to the reader which loops till it hears the second signal SIGUSR2 from the writer.
reader.c
static volatile sig_atomic_t done_waiting;
int handler1(int signal){
done_waiting = 0;
while( !done_waiting ){
(void)fprintf(stdout, " reader waiting for sigusr2: done_waiting = %d\n", done_waiting );
}
(void)fprintf(stdout, " reader received sigusr2 \n);
}
int handler2 (int signal){
done_waiting = 1;
}
main(){
signal(SIGUSR1, handler1);
signal(SIGUSR2, handler2);
sleep(5); // sleep till we start worker
}
In writer.c, signals are sent to the reader as
main(){
kill(pid_reader, SIGUSR1);
sleep(5);
kill (pid_reader, SIGUSR2);
}
When I execute reader first followed by worker, the program quits at the while loop. And the writer prints that "No matching processes belonging to you were found".
Is nesting signal handlers allowed and if yes, is it recommended? Also, is there any another alternative mechanism for writer to notify reader that it is ready?
Is maybe nested signals actually what you meant, not nested signal handlers ? To clarify, what will happen if a SIGUSR2 is received while the handler for SIGUSR1 is executing, is that what you mean ? I assume so,
I tested your code, with some modifications, to get the pid for the reader process into the writer process I used the args to main.
The results I get is.
First reader is quiet
After receiving SIGUSR1 it starts continuously writing that it waits for SIGUSR2
When receiving SIGUSR2, it prints "reader received SIGUSR2"
This indicates that it is possible to have nested signals. However I would not say it is recommended as an intentional design.
As mentioned in the comments, you should do as little as possible in the signal handlers, definitely not loop in a while-loop.
And as also mentioned in the comments, be very careful what functions you call in signal-context, printf() is not OK, even though it may seem to work fine.
Tested on Linux, with the ancient kernel 3.16 and gcc 4.9

In C, linux, about kill signal and sleep() in loop

I run my C program on Mac OS. Part of my program is as following. This code runs well on sigint signal but can't work on sigkill signal.
void sigkill(int sig){
/*some code neglected*/
exit(0);
}
void sigint(int sig){
flag=1;
}
void alive(void) {
signal(SIGINT, sigint);
signal(SIGKILL, sigkill);
alarm(10);
while(1){
//printf("%d\n",flag);
sleep(1);
if(flag==1){
printf("no\n");
flag=0;
}
}
}
I have four questions:
At first I didn't write sleep(1), It can enter the function sigint(), and change the flag value, I can see from the printf. However, no "no" output as I expected.
After I added sleep function, it works well. I suppose the while loop would check flag every 1 second, and output "no" if flag=1. However, it seems the "no" output everytime when I press ctrl+c. Why it don't wait for one second?
The question said "You should not use 'sleep()' to wait 10 seconds. Use alarm(), coupled with a loop." I want to know how to implement this without sleep().
The kill command can't invoke sigkill function, how to fix this?
In general, signals can only be "caught" by your application when it makes a system call into the kernel. If you do a plain while(1) { if (flag==1){...} }, that will never call into the kernel. Theoretically, when you do your outer printf in the while(1) loop, that should call into the kernel and so the signal could be caught.
sleep() is interrupted by any signal. Check the man page for sleep(3).
check the man page for alarm(2).
You cannot change the signal handler for SIGKILL nor for SIGSTOP. These signal effects are hardcoded into the kernel. From sigaction(2):
signum specifies the signal and can be any valid signal except SIGKILL and SIGSTOP.
The kill command with no arguments does not generate a SIGKILL signal; it generates SIGTERM.

Gracefully Exiting a C Application

I currently have a program I have written in C on a server that has an infinite loop that processes information, each loop takes about 5 minutes to complete. I would like to have the following functionality in a shell script:
Terminate C program
Make source
Run program
The problem is, I don't know how to tell my C program to exit without doing something like ctrl+c, I would rather it finished processing the information it is currently working on before terminating itself.
The POSIX standard way to tell a process to finish its business and exit cleanly is to send it a SIGTERM signal. Depending on your application it may or may not be appropriate to exit on SIGINT, which is meant to interrupt a process, not terminate it. (Control-c sends SIGINT.)
Try putting a flag in your tight loop; check the flag at a time when it is easy to exit, but still frequently enough to exit promptly. In your case, receipt of a SIGTERM might put a message on the system log right away, then promise to exit within the next 5 minutes.
Your signal handler will look like this:
static int signalled; // if nonzero, what signal have we been sent?
static void SignalHandler(int signum) {
signalled = signum;
}
I check the global static variable signalled after every I/O operation, which means many times per second.
Here's my code to catch and restore signals:
static __sighandler_t sh, si, st;
static void catch_signals(void) {
if ((sh = signal(SIGHUP, SignalHandler)) == SIG_IGN) signal(SIGHUP, SIG_IGN);
if ((si = signal(SIGINT, SignalHandler)) == SIG_IGN) signal(SIGINT, SIG_IGN);
if ((st = signal(SIGTERM, SignalHandler)) == SIG_IGN) signal(SIGTERM, SIG_IGN);
signalled = 0;
}
static void restore_signals(void) {
signal(SIGHUP, sh);
signal(SIGINT, si);
signal(SIGTERM, st);
}
(This code is from a library, so I'm being extra careful to leave things the way I found them.)
Bonus trick: when time expires (this is a TV recording library), the timer just sets signalled = SIGTERM, and the same logic is used to exit the recorder normally.
like ctrl+c, I would rather it finished processing the information it
is currently working on before terminating itsel
Establish a signal handler for SIGINT or whatever you want and do your cleanup after you receive it. You shouldn't do the cleanup in the handler itself however.
volatile sig_atomic_t do_cleanup = 0;
void handler(int sig)
{
do_cleanup = 1;
}
Then in your main loop you just have to test do_cleanup and exit when you please. You must also be careful in properly treating EINTR errors if you're not already doing so.
Here is how to send signal from shell:
http://bash.cyberciti.biz/guide/Sending_signal_to_Processes
or simply man kill
Here is how to react to signal:
http://www.cs.cf.ac.uk/Dave/C/node24.html#SECTION002400000000000000000

Signal handling in C - interrupt in interrupt

I was wondering if it is possible to be interrupted by a signal when my program is handling other signal at the same time, I tried to simulate it with:
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
void sig_output()
{
sigset_t set;
sigprocmask(0,NULL,&set);
printf("currently blocking:");
if (sigismember(&set,SIGUSR1))
printf("\nSIGUSR1");
if(sigismember(&set,SIGUSR2))
printf("\nSIGUSR2");
printf("\n");
return ;
}
void sig_handler(int sig)
{
raise(SIGUSR1);
printf("start\n");
if (sig==SIGUSR1)
printf("SIGUSR1\n");
else if (sig==SIGUSR2)
printf("SIGUSR2\n");
printf("end\n");
return ;
}
void other_sig_handler(int sig)
{
printf("start - other\n");
if (sig==SIGUSR1)
printf("SIGUSR1\n");
else if (sig==SIGUSR2)
printf("SIGUSR2\n");
printf("end - other\n");
return ;
}
int main()
{
sig_output();
struct sigaction a;
a.sa_handler=sig_handler;
a.sa_flags=0;
sigset_t set,old;
//blocking SIGUSR1,SIGUSR2
sigemptyset(&set);
sigaddset(&set,SIGUSR1);
sigaddset(&set,SIGUSR2);
printf("blocking SIGUSR1, SIGUSR2\n");
sigprocmask(SIG_SETMASK,&set,&old);
sig_output();
//adding handles for SIGUSR1,SIGUSR2
sigemptyset(&(a.sa_mask));
sigaction(SIGUSR1,&a,NULL);
a.sa_handler=other_sig_handler;
sigaction(SIGUSR2,&a,NULL);
printf("poczatek wysylania \n");
raise(SIGUSR1);
raise(SIGUSR2);
raise(SIGUSR1);
printf("using sigsuspend\n");
sigsuspend(&old);
printf("end of program\n");
return 0;
}
and everytime I run this program I get
currently blocking:
blocking SIGUSR1, SIGUSR2
currently blocking:
SIGUSR1
SIGUSR2
raising
using sigsuspend
start - other
SIGUSR2
end - other
start
SIGUSR1
end
end of program
is it always like that?
Quoting the sigaction(2) manpage:
Signal routines normally execute with the signal that caused their
invocation blocked, but other signals may yet occur. A global signal mask
defines the set of signals currently blocked from delivery to a process.
The signal mask for a process is initialized from that of its parent
(normally empty). It may be changed with a sigprocmask(2) call, or when
a signal is delivered to the process.
You can control whether the signal is automatically blocked in its signal handler with the SA_NODEFER flag.
The order in which these particular pending signals are delivered is not, as far as I know, defined. However, signals are (mostly; there's an exception for SIGCLD, which is traditionally done by "cheating") "non-queueing", except for real-time signals. The non-queuing aspect means that if you have signal X blocked, and then raise it twice (as you do above for SIGUSR1), you only get it delivered once.
The only ordering documented on at least one system (MacOS) is:
If multiple signals are ready to be delivered at the same time, any signals that
could be caused by traps are delivered first.
(These are things like SIGSEGV and SIGBUS.) In general, you can control the order of delivery by use of the signal blocking masks: unblock any particular signal(s) at some point and those are the ones that can be delivered at that point.
If you do not set SA_NODEFER, the blocking mask at the entry to your handler will always block whatever signal your handler is handling, so that you won't have to worry about recursion.
The special case for SIGCLD comes from System V, which originally implemented this by resetting the handler to SIG_DFL on each SIGCLD delivery. (In fact, SysV did this with all signals, effectively implementing SA_RESETHAND whether you wanted it or not.) The default action was to discard the signal, as if the handler were SIG_IGN. This of course created race conditions when multiple child processes finished before the handler could do its thing. Instead of a block/unblock model, though, the SysV folks put in a hack: at the end of your SIGCLD handler, you would call signal(SIGCLD, handler); to fix up the handler. At that point, if there were any exited children that had not yet been wait-ed for, SysV would immediately generate a new SIGCLD, and your handler would be entered recursively. This made it look as though the signals were queued, without actually queueing them.
For more on Linux signals, see (eg) http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html.

Resources