I have been working on signal handling on Linux lately, and have read all the concepts related to signal handling. One question that's tinkering my head is that why the signal in the set of sigtimedwait() doesn't get delivered while the process is unblocked. My code is as follows :-
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
void sighandler1(int sig)
{
printf("SIGINT caught\n");
}
void sighandler2(int sig)
{
printf("SIGTSTP caught\n");
}
void sighandler3(int sig)
{
printf("SIGALRM caught\n");
}
int main()
{
sigset_t s1,s2;
struct sigaction act1,act2,act3;
int ret;
sigemptyset(&s1);// The bit-mask s1 is cleared
sigaddset(&s1,SIGINT);//Add SIGINT to the bit-mask s1
sigemptyset(&s2);// The bit-mask s2 is cleared
sigaddset(&s2,SIGALRM);//Add SIGALRM to the bit-mask s2
sigprocmask(SIG_BLOCK,&s2,NULL);//Signal(s) in s2 blocked
act1.sa_handler = sighandler1; //function pointer pointing to the signal handler
act1.sa_flags = 0;
sigaction(SIGINT,&act1,NULL); // installing the action
// for SIGINT
act2.sa_handler = sighandler2; //function pointer pointing to another signal handler
act2.sa_flags = 0; // no flags
sigaction(SIGTSTP,&act2,NULL); // installing the action
// for SIGTSTP
act3.sa_handler = sighandler3; //function pointer pointing to another signal handler
act3.sa_flags = 0; // no flags
sigaction(SIGALRM,&act3,NULL); // installing the action for SIGALRM
sigprocmask(SIG_SETMASK,&s1,NULL); //Signals in s1 blocked and other signals unblocked
printf("sigprocmask() called with SIG_SETMASK on s1,which contains SIGINT\n");
printf("Blocked on sigtimedwait() with s1\n");
if(sigtimedwait(&s1,NULL,NULL) < 0)
{
if(errno == EINTR)
printf("Some other signal caught\n");
}
printf("This is a process. You can pass signal to it\n");
while(1);
}
To be more clear with the question, I have called sigtimedwait in the above code with "s1" as the "set "parameter . This set contains only the signal SIGINT. As per the man page, sigtimedwait() blocks the process , until one of the signals in its set is delivered. I am all okay with that statement. But Why is the SIGINT handler not called when I pass SIGINT to unblock the process? On the other hand, when I pass SIGALRM or SIGTSTP, which are not there in the set, EINTR is returned as expected, and also the signal handler gets called.
For anyone, who wants to observe the scenario, can execute the above code and then pass SIGINT to it. They will observe that the process is unblocked without the handler being called . Why is the handler not called? Am I misinterpreting any part of the man page of sigtimedwait()??
sigtimedwait seems to return the signal value, instead of the signal handler getting caught:
switch(sigtimedwait(&s1,NULL,NULL))
{
default:
printf ("Some other signal???");
break;
case SIGINT:
printf ("We got SIGINT\n");
break;
case -1:
perror ("sigtimedwait");
break;
}
Related
I'm not new to programming, but pretty new to Linux. I'm trying to use signals to asynchronously catch a push on a button, like this:
Run a worker thread which raises SIGUSR1 when the button is pushed.
Run a loop (main thread) around sigtimedwait() that will rotate info every two seconds (as long as the button is not pushed) or break (when the button is pushed).
According to the notes on sigtimedwait(), one should block the signals you want to wait for, then call sigtimedwait(). But I never see sigtimedwait() catching the blocked signals. I have run the code below in a few ways to see what happens with different scenarios:
Call to pthread_sigmask() disabled, call to signal() disabled,
result: programs exits with message "User defined signal 1".
Call to pthread_sigmask() disabled, call to signal() enabled, result:
message "Button 1 pressed sync1 hit" appears, sigtimedwait() always
returns EAGAIN.
Call to pthread_sigmask() enabled, call to signal() disabled, result:
message "Button 1 pressed" appears, sigtimedwait() always returns
EAGAIN.
Call to pthread_sigmask() enabled, call to signal() enabled, result
of course same as previous because the handler will not be called.
All as expected, except for the fact that sigtimedwait() doesn't seem to catch the signal when it's pending.
I've looked into similar code, e.g. this. But I don't understand how that particular code could work: SIGUSR1 isn't blocked, so raising that should immediately terminate the program (the default action for SIGUSR1).
It looks like I'm missing something here. What am I doing wrong? Or is the whole idea of using raise() in a worker thread wrong? I'm running this on a Raspberry Pi 3 with Raspbian Stretch (Debian 9.1), could there be a problem in that?
[I know printf() shouldn't be used in a signal handler, but for this purpose it works]
Any help appreciated, thx!
#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#define PIN_BUTTON1 RPI_V2_GPIO_P1_22 // GPIO #24
// Thread function
void* check_button1(void* param)
{
while (true)
{
if (bcm2835_gpio_lev(PIN_BUTTON1) == HIGH)
{
printf("Button 1 pressed ");
raise(SIGUSR1);
}
delay(250);
}
}
// Signal handler, if applied
volatile sig_atomic_t usr_interrupt = 0;
void sync1(int sig)
{
printf("sync1 hit ... ");
usr_interrupt = 1;
}
int main(int argc, char** argv)
{
if (!bcm2835_init())
{
printf("Failed to initialize BCM2835 GPIO library.");
return 1;
}
bcm2835_gpio_fsel(PIN_BUTTON1, BCM2835_GPIO_FSEL_INPT);
sigset_t sigusr;
sigemptyset(&sigusr);
sigaddset(&sigusr, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigusr, NULL);
signal(SIGUSR1, sync1);
// Start the threads to read the button pin state
pthread_t th1;
pthread_create(&th1, NULL, check_button1, NULL);
// Create a two second loop
struct timespec timeout = { 0 };
timeout.tv_sec = 2;
usr_interrupt = 0;
int nLoopCount = 0;
while (true)
{
printf("Loop %d, waiting %d seconds ... ", ++nLoopCount, timeout.tv_sec);
int nResult = sigtimedwait(&sigusr, NULL, &timeout);
if (nResult < 0)
{
switch (errno)
{
case EAGAIN: printf("EAGAIN "); break; // Time out, no signal raised, next loop
case EINTR: printf("EINTR "); break; // Interrupted by a signal other than SIGCHLD.
case EINVAL: printf("EINVAL "); exit(1); // Invalid timeout
default: printf("Result=%d Error=%d ", nResult, errno); break;
}
printf("\n");
continue;
}
printf("Signal %d caught\n", nResult);
}
return 0;
}
ADDENDUM: In the meantime, I got this working by replacing raise(SIGUSR1) by kill(getpid(), SIGUSR1). Strange, because according to the manual raise(x) is equivalent to kill(getpid, x) in single-threaded programs and to pthread_kill(pthread_self(), x) in multi-threaded ones. Replacing raise(SIGUSR1) by pthread_kill(pthread_self, SIGUSR1) has no effect. If anyone could explain this to me ...
I'm sending say 100 signal (SIGINT) from terminal in while loop continuously, as i have register signal handler for this signal, so any signal comes either in auxiliary thread or main thread (as signal disposition is shared by all threads) should print "caught signal: ..." message as output every time i send the signal. but i observed out of 100, some random less no of times say 95, 84 times being this message printed. can some one please explain why all 100 signal is not being printed and how to print all 100 signal with below code.
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
// Signal Handler for entire Process (shared by all threads)
void signalHandler(int param)
{
printf("Caught signal: %d ", param);
}
void *childFun(void *arg)
{
// Register signal Handler here
struct sigaction childpsa;
childpsa.sa_handler = signalHandler;
sigaction(SIGTERM, &childpsa, NULL);
sigaction(SIGHUP, &childpsa, NULL);
sigaction(SIGINT, &childpsa, NULL);
sigaction(SIGCONT, &childpsa, NULL);
sigaction(SIGTSTP, &childpsa, NULL);
while (1) {
// doSomething in while loop
}
}
int main(void)
{
// create a auxiliary thread here
pthread_t child_threadId;
int err = pthread_create(&child_threadId, NULL, &childFun, NULL);
while (1) {
// main program do something
}
return 1;
}
Most of Linux implementation doesn't support queueing of signal, so if a process receives same signal 'x' number of times, it will be called only once.
Also you shouldn't use function like printf inside signal handler(instead use some counters(an integer) that increments for every invocation )
Except
POSIX.1 allows the system to deliver the signal either once or more than once in case a blocked signal is generated more than once before the process unblocks the signal. If the system delivers the signal more than once, we say that the signals are queued. Most UNIX systems, however, do not queue signals unless they support the real-time extensions to POSIX.1. Instead, the UNIX kernel simply delivers the signal once. [p336]
https://notes.shichao.io/apue/ch10/#reliable-signal-terminology-and-semantics
Do you know where I can see a list of signals and functions that cannot be used alongside sleep() command?
For example, you can see this code:
// this program presents how to block signal SIGINT
// while running in critical region
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
static void sig_int(int);
int main(void) //6
{
sigset_t newmask, oldmask, zeromask;
if (signal(SIGINT, sig_int) == SIG_ERR)
fprintf(stderr,"signal(SIGINT) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/* block SIGINT and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
fprintf(stderr,"SIG_BLOCK error");
/* critical region of code */
printf("In critical region: SIGINT will be blocked for 3 sec.\n");
printf("Type Ctrl-C in first 3 secs and see what happens.\n");
printf("Then run this program again and type Ctrl-C when 3 secs elapsed.\n");
fflush(stdout);
sleep(3);
printf("end sleep");
/* allow all signals and pause */
if (sigsuspend(&zeromask) != -1)
fprintf(stderr,"sigsuspend error");
printf("after return from sigsuspend: ");
/* reset signal mask which unblocks SIGINT */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
fprintf(stderr,"SIG_SETMASK error");
/* and continue processing ... */
exit(0);
}
static void sig_int(int signo)
{
printf("\nIn sig_int: SIGINT\n"); fflush(stdout);
return;
}
The program doesn't wake up after the sleep(). Do you know why?
If you use strace, you can see what your program actually does
strace ./my-sig-program
If sleep never returns, I guess that task has received a SIGSTOP (this one can not be intercepted) or SIGTSTP (this one you can intercept with a signal handler), causing the OS the halt the entire process, until a SIGCONT is received.
How can I cancel a sent signal to a process which is still not delivered?
Consider the scenario where I have sent a signal to a process, but the process was in un-interruptible state.
I am doing a conditional wait for the signal to be handled. But since it did not I want to continue executing further. In that case, is there a way that I can cancel the sent signal(it is not yet delivered)
A pending signal is canceled if that signal is ignored before the signal is delivered. You just have to ignore the signal. You can do this with sigaction() by setting the sa_handler field in struct sigaction to SIG_IGN.
Here's some example code that illustrates this and shows that it works. The code does the following:
Blocks SIGINT so that we have a window of time to send it SIGINT when it is blocked - this will generate a pending SIGINT that will be delivered when the process signal mask is changed to a mask that does not include SIGINT (and will be canceled if the signal is ignored).
Waits for you to send SIGINT
Ignores SIGINT after knowing that a SIGINT is pending. This has the effect of canceling the pending signal.
Restores the original default action for SIGINT, which is to terminate the process
Restores the original process signal mask, that does not block SIGINT.
Waits for you to enter any character to terminate.
You can see that the process does not terminate after step 5 and waits for your input, which means that the pending signal was canceled.
Here's the code that illustrates this:
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
sigset_t block_sigint, prev_mask;
sigemptyset(&block_sigint);
sigaddset(&block_sigint, SIGINT);
if (sigprocmask(SIG_SETMASK, &block_sigint, &prev_mask) < 0) {
perror("Couldn't block SIGINT");
return 0;
}
printf("SIGINT blocked: kill -SIGINT %ld to generate a pending SIGINT. Press return when done.\n", (long) getpid());
/* Now, open a new terminal and send SIGINT to this process.
*
* After doing that, the signal will be pending delivery because it is currently blocked.
*
* Now, if we ignore SIGINT, the pending signal will be cancelled
*/
getchar();
struct sigaction ign_sigint, prev;
ign_sigint.sa_handler = SIG_IGN;
ign_sigint.sa_flags = 0;
sigemptyset(&ign_sigint.sa_mask);
if (sigaction(SIGINT, &ign_sigint, &prev) < 0) {
perror("Couldn't ignore SIGINT");
return 0;
}
printf("SIGINT ignored - pending SIGINT was canceled.\n");
/* Now, restore the default action for SIGINT */
if (sigaction(SIGINT, &prev, NULL) < 0) {
perror("Couldn't restore default SIGINT behavior");
return 0;
}
/* And also restore the process's original sigmask, which does not block SIGINT */
if (sigprocmask(SIG_SETMASK, &prev_mask, NULL) < 0) {
perror("Couldn't restore original process sigmask");
return 0;
}
printf("Process sigmask and action for SIGINT are now restored\n");
/* We will not receive SIGINT at this point because it was canceled
* So the process will block on getchar() here instead of terminating
*/
getchar();
return 0;
}
In Linux, I am emulating an embedded system that has one thread that gets messages delivered to the outside world. If some thread detects an insurmountable problem, my goal is to stop all the other threads in their tracks (leaving useful stack traces) and allow only the message delivery thread to continue. So in my emulation environment, I want to "pthread_kill(tid, SIGnal)" each "tid". (I have a list. I'm using SIGTSTP.) Unfortunately, only one thread is getting the signal. "sigprocmask()" is not able to unmask the signal. Here is my current (non-working) handler:
void
wait_until_death(int sig)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, sig);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
for (;;)
pause();
}
I get verification that all the pthread_kill()'s get invoked, but only one thread has the handler in the stack trace. Can this be done?
This minimal example seems to function in the manner you want - all the threads except the main thread end up waiting in wait_until_death():
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#define NTHREADS 10
pthread_barrier_t barrier;
void
wait_until_death(int sig)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, sig);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
for (;;)
pause();
}
void *thread_func(void *arg)
{
pthread_barrier_wait(&barrier);
for (;;)
pause();
}
int main(int argc, char *argv[])
{
const int thread_signal = SIGTSTP;
const struct sigaction sa = { .sa_handler = wait_until_death };
int i;
pthread_t thread[NTHREADS];
pthread_barrier_init(&barrier, NULL, NTHREADS + 1);
sigaction(thread_signal, &sa, NULL);
for (i = 0; i < NTHREADS; i++)
pthread_create(&thread[i], NULL, thread_func, NULL);
pthread_barrier_wait(&barrier);
for (i = 0; i < NTHREADS; i++)
pthread_kill(thread[i], thread_signal);
fprintf(stderr, "All threads signalled.\n");
for (;;)
pause();
return 0;
}
Note that unblocking the signal in the wait_until_death() isn't required: the signal mask is per-thread, and the thread that is executing the signal handler isn't going to be signalled again.
Presumably the problem is in how you are installing the signal handler, or setting up thread signal masks.
This is impossible. The problem is that some of the threads you stop may hold locks that the thread you want to continue running requires in order to continue making forward progress. Just abandon this idea entirely. Trust me, this will only cause you great pain.
If you literally must do it, have all the other threads call a conditional yielding point at known safe places where they hold no lock that can prevent any other thread from reaching its next conditional yielding point. But this is very difficult to get right and is very prone to deadlock and I strongly advise not trying it.