I have a single thread program that sleeps to conserve battery power while waiting for hardware events. main() registers a signal handler, then sleeps. Hardware interrupt handlers may wake main by calling a function which calls raise(), and seeing any signal should wake main from sleep.
Now, for development purposes, I am running on a desktop, and instead of getting true hardware events, there are a couple of other processes which send fake hardware events. Instead of the interrupt handler making the aforementioned function calls, these tasks do.
A very simple demo case works fine, I can provide that code if needed, but don't want this post to be too long.
I have declared this signal handler:
void sig_handler( int signo )
{
//To confirm that this process is the same one that is sleeping
printf("sig_handler: my PID is %d\n", getpid());
if ( SIGUSR1 == signo)
{
printf("received SIGUSR1\n");
}
else
{
printf("received some other signal\n");
}
}
There is lot of other code, but the bit in question is that during init, I call:
sSigAction.sa_handler = sig_handler;
sigemptyset( &sSigAction.sa_mask );
sigaction( SIGUSR1, &sSigAction, NULL );
In a function called by another process (or interrupt handler in real system):
printf( "raising signal!\n");
raise( SIGUSR1 );
In main:
printf("main: my PID is %d\n", getpid());
while(1)
{
printf("sleeping forever\n");
usleep(-1);
printf("main is awake"\n);
//Do work we were waiting to do
}
The output looks like this:
main: my PID is 17250
sleeping forever
raising signal!
sig_handler: my PID is 17250
received SIGUSR1
Note the critical lack of "main is awake". As I said, doing these same steps does work, in a simple demo case. Since I am doing the same setup, I don't understand why it's not working. In both cases, I perform the same signal handler registration, usleep(), and raise(), but the demo case works and this does not.
Any ideas of what I could be missing?
Follow-up: I have found that if I use $kill -s USR1 then the task wakes as expected. So the mechanism is sound, but why does $kill work and raise() not?
Follow-up 2: I now have the function calling raise() also print getpid() and it matches the PID of main().
Resolution: While in the end I will have a single thread, and raise() would be appropriate right now I have multiple, and so must use kill(). It's that simple.
Related
I got this issue:
I made a program in c, where the main process creates some child process, and these, after a while, are able to send a signal to the main process:
the signal is sent with this code:
kill(getppid(), SIGUSR1);
and the main process, in the while loop is waiting the SIGUSR1 message...
everything is fine, but if I increase the child number and automatically the possibility to have more signals in the same time, the program crash printing the message:
User defined signal 1
the main code is like this:
void signalHandler(int sig, siginfo_t* info, void* vp) {
if (sig == SIGUSR1) {
printf("SIGUSR1 has arrived\n");
} else if (sig == SIGUSR2) {
printf("SIGUSR2 has arrived\n");
}
}
int main(int argc, char const *argv[]) {
struct sigaction action, old_action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_sigaction = signalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART | SA_NODEFER;
while (1) {
sigaction(SIGUSR1, &action, &old_action);
sigaction(SIGUSR2, &action, &old_action);
}
}
I think the problem is that the signal is sent when the master is still working on the previous signal...but how can I do to fix this thing
thank you very much
It means that the child is sending the signal before the parent process was able to call sigaction() to configure the signal handler. When this happens, the default signal reaction to SIGUSR1 terminates the program:
SIGUSR1 P1990 Term User-defined signal 1
https://man7.org/linux/man-pages/man7/signal.7.html
However, there are many problems with your code. printf() is not safe to be called inside a signal handler (it's AS-Unsafe as defined by POSIX):
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_03
Also, using SA_NODEFER may create nested signals (another signal handler is called while some signal handler is running) but your program does not protect against a flood. Given enough children this will generate a stack overflow. Finally, the main program keeps running a non-stop infinite loop reconfiguring the signals, while it should have configured them only once outside the loop and blocked inside the loop (for example sigwait() or pselect()):
https://man7.org/linux/man-pages/man2/select.2.html
Finally, if you expect to run a large number of children that might flood the parent with signals, then it would be better to use the real time signal generation function (sigqueue()) rather than kill(). The difference is that with sigqueue(), all signals are queued and SA_NODEFER is not necessary to avoid discarding signals while some other signal handler is running:
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_02
Final conclusion: the code should be completely rewritten.
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
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.
I have a parent process that manages a child (fork, execve). I created a handler in the parent to catch SIGCHLD signals from the child in order to call waitpid() and take appropriate action such as restarting the child.
I understood from the manual page for sigaction() that, while inside a signal handler, further signals of the same type would be blocked by default. I definitely wish for this behaviour so I decided to test it.
I put a sleep (my own implementation using clock_nanosleep() in a loop which resumes when interrupted) at the end of the signal handler and sent a SIGINT to the child. This duly made it quit and sent SIGCHLD to the parent. I logged the fact and started my sleep for 10 seconds. Now, I sent another SIGINT to the new child (sighandler restarted it first time) and was surprised to see another log and sleep happen.
How can this be? When I attached using a debugger to the parent it clearly showed two different threads interrupted to call my signal handler, both now sat in sleep. If that keeps up I will run out of threads!
I understand putting long sleeps into a signal handler is a daft thing to do but it does illustrate the point; I expected to see the second signal marked as pending in /proc/[PID]/status but instead it's delivered.
Here's the relevant bits of my code:
Set up the SIGCHLD handler:
typedef struct SigActType {
struct sigaction act;
int retval;
void (*func)(int);
}SigActType;
static SigActType sigActList[64];
public void setChildHandler(void (*func)(int)) {
SigActType *sat = &sigActList[SIGCHLD];
sat->act.sa_sigaction = sigchldHandler;
sigemptyset(&sat->act.sa_mask);
sigaddset (&sat->act.sa_mask, SIGTERM);
sigaddset (&sat->act.sa_mask, SIGINT);
sigaddset (&sat->act.sa_mask, SIGCHLD);
sat->act.sa_flags = SA_SIGINFO;
sat->retval = 0;
sat->func = func;
sigaction(SIGCHLD, &sat->act, NULL);
}
static void sigchldHandler(int sig, siginfo_t *si, void *thing) {
SigActType *sat = &sigActList[SIGCHLD];
if (sat->func) {
sat->func(si->si_pid);
}
}
and using this:
int main(int argc, char **argv) {
setChildHandler(manageChildSignals);
...
}
static void manageChildSignals(int d) {
if ((pid = waitpid(-1, &stat, WAIT_MYPGRP)) > 0) {
... restart child if appropriate
}
printf("start of pause...\n");
mySleep(10);
printf("end of pause...\n");
}
Stdout clearly shows:
(when I type kill -2 [PID]
start of pause
(when the new child is started and I type kill -2 [NEWPID]
start of pause
...10 seconds slide past...
end of pause
end of pause
I am puzzled as to why this happens. As you can see I even added SIGCHLD to the block mask for sigaction() to try to encourage it to do the right thing.
Any pointers most welcome!
signals of the same type would be blocked by default.
Yes, but only for the thread sigaction() is called from.
From man sigaction (bold emphasis by me):
sa_mask specifies a mask of signals which should be blocked (i.e.,
added to the signal mask of the thread in which the signal handler is
invoked) during execution of the signal handler.
As signal dispostion is per process any other thread not blocking the signal in question might receive it, that is get interupted and process it.
If this behaviour is not what you want you should perhaps modify the design of the way your program handles signals in such a way that per default all signals are blocked for each thread, and only one specifiy thread has signal reception unblocked.
Update:
Signals masks are inherited from the parent thread by the child thread.
If signal handling shall be done by one specific thread only, have the main thread block all signals prior to creating any other thread. Then create one specfic thread to do the signal handling, and have this thread unblock the signals to be handled. This concept also allows models like one thread per signal.
In a mutlithreaded environment use pthread_sigmask() to mask signals on a per thread base.
Please note that the behaviour of sigprocmask() in a multithreaded process is unspecified, use pthread_sigmask() then.
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.