I want to write a program in c for linux that catchs the first SIGUSR1 signal, ignores the second one and continue in this behaviour (catch-ignore) for the successive SIGUSR1 signals.
I wonder how to keep alternating between the two handlers, because once i set the handler to SIG_IGN, the signal will be ignored and I won't be able to detect it and act accordingly.
This is the code i have tried:
int tst;
void sigusr1_handler(){
if(tst==0){
signal(SIGUSR1,SIG_IGN);
tst=1;
}
else tst= 0;
}
int main(){
signal(SIGUSR1, sigusr1_handler);
tst=1;
while(1){}
return 0;
}
You do not.
What you can do, is have your signal handler decide whether to do something or not -- for example, whether to call an another function or not. This is not completely reliable, however, because standard POSIX signals like SIGUSR1 are not queued. If two or more signals are sent at (nearly) the same time, only one is actually delivered. POSIX realtime signals (SIGRTMIN+0 to SIGRTMAX-0 -- from the programmer's point of view, only the number and semantics differ) are queued, but even they can be dropped in some situations.
In all cases, remember that a signal handler function can only use async-signal safe functions. If you need to be able to use all functions, you should instead block the signals in all threads, and have a dedicated thread receive the signals using e.g. sigwaitinfo(). In this case, you don't have signal handler functions, but instead a dedicated thread that receives the signals, and is thus free to use any functions it wants.
If we reword the question into "How do I alternate the handling of a delivered signal in a single-threaded program?", the answer is simple: you use a volatile sig_atomic_t counter.
For alternating between two choices, do_something_odd() first:
#include <signal.h>
void my_signal_handler(int signum)
{
static volatile sig_atomic_t count = 0;
switch (++count) {
case 1:
do_something_odd();
break;
default:
count = 0;
do_something_even();
break;
}
}
For alternating between three cases, you add more case statements as necessary:
#include <signal.h>
void my_signal_handler(int signum)
{
static volatile sig_atomic_t count = 0;
switch (++count) {
case 2:
do_something_2_of_3();
break;
case 1:
do_something_1_of_3();
break;
default:
count = 0;
do_something_3_of_3()
break;
}
}
The above assumes you install the signal handler using sigaction() without SA_SIGINFO in .sa_flags.
The POSIX standard says you can do up to 128 cases (as sig_atomic_t is guaranteed to be able to represent values 0 to 127, inclusive) this way.
You can do larger sets using an unsigned int or unsigned long, but then you must NOT have SA_NODEFER in the .sa_flags, and if the same handler is used for multiple signals, the .sa_mask must have all the other signals handled by the same handler set. This ensures that the signal handler is not interrupted by another signal that is delivered to the same handler, and allows us to use non-atomic counter variable.
In a multithreaded program one must rely on compiler-provided atomic operations, either legacy __sync built-ins, or __atomic built-ins. They are not GCC-specific; at least Intel Compiler Collection and clang provide them also. For alternating between two options, our = __atomic_xor_fetch(&counter, 1, __ATOMIC_SEQ_CST); or our = __sync_xor_and_fetch(&counter, 1); can be used to obtain and flip a counter between 0 and 1 atomically. For a non-power-of-two number of options, a compare-and-swap loop is typically used.
Related
I'm learning to write a signal handler in C for a Linux system. Here's my code:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signum){
//Return type of the handler function should be void
printf("\nInside handler function\n");
}
int main(){
signal(SIGINT,sig_handler); // Register signal handler
for(int i=1;;i++){ //Infinite loop
printf("%d : Inside main function\n",i);
sleep(1); // Delay for 1 second
}
return 0;
}
My question is, why when I hit Ctrl+C twice, the program stops? Shouldn't it be that everytime I hit Ctrl+C the signal handler runs, so effectively the program should run forever?
In reality, this is my output, the signal handler is only called in the first Ctrl+C, not the second time:
1 : Inside main function
2 : Inside main function
3 : Inside main function
4 : Inside main function
^C
Inside handler function
5 : Inside main function
6 : Inside main function
7 : Inside main function
8 : Inside main function
9 : Inside main function
10 : Inside main function
^C
On Linux, a number of factors contribute to the behaviour of signal. Depending on the version of glibc, the defined feature_test_macros(7), and whether or not the kernel version of the function is used, the results will differ between two polarizing behaviours, described as System V semantics and BSD semantics:
With System V semantics, when a signal handler is invoked, the disposition of the signal is reset to its default behaviour (SIG_DFL). Additionally, further delivery of the signal is not blocked during the execution of the signal handler.
With BSD semantics, when a signal handler is invoked, the disposition of the signal is not reset, and further delivery of the signal is blocked during the execution of the signal handler. Additionally, certain system calls will be automatically be restarted if interrupted by the signal handler.
Your version of signal appears to be supplying System V semantics. The signal disposition is reset after the first signal handler, and the following SIGINT terminates the program.
See signal(2) and its notes on portability for more details.
See signal-safety(7) for a list of functions that are safe to call from within a signal handler. printf is not an async-signal-safe function, and should not be called from within a signal handler.
Use write(2) instead.
The quickest fix is to call signal within the signal handler to once again set the signal disposition to the signal handler.
void sig_handler(int signum)
{
(void) signum;
char msg[] = "Signal handler called!\n";
write(STDOUT_FILENO, msg, strlen(msg));
signal(SIGINT, sig_handler);
}
The more robust solution is to use sigaction(2) to establish signal handlers, as its behaviour is more consistent and provides better portability.
A basic example:
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
volatile sig_atomic_t sig_count = 0;
void sig_handler(int signum)
{
(void) signum;
sig_count++;
char msg[] = "Signal handler called!\n";
write(STDOUT_FILENO, msg, strlen(msg));
}
int main(void)
{
struct sigaction sa = { .sa_handler = sig_handler };
sigaction(SIGINT, &sa, NULL);
while (sig_count < 5);
}
The default behaviour of sigaction is similar to that described by BSD semantics, with the exception that certain system calls will not restart when interrupted by the signal handler.
To enable this behaviour, the .sa_flags member of the struct sigaction should contain the SA_RESTART flag.
To mimic System V semantics, the .sa_flags member of the struct sigaction should contain the SA_RESETHAND and SA_NODEFER flags.
TL;DR: use sigaction(), not signal(), to install signal handlers.
As comments and other answers have observed, your signal handler has undefined behavior as a result of its call to printf(). In the event that the signal handler is ever triggered, that gives the whole program UB, which makes it very difficult to reason about its observed behavior.
But consider this variation instead:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signum){
static const char msg[] = "\nInside handler function\n";
write(1, msg, sizeof(msg) - 1);
}
int main(){
signal(SIGINT,sig_handler); // Register signal handler
for(int i=1;;i++){ //Infinite loop
printf("%d : Inside main function\n",i);
sleep(1); // Delay for 1 second
}
return 0;
}
I have switched from printf to write inside the signal handler, thereby removing the UB. And if I compile it with
gcc -std=c11 htest.c -o htest
then the resulting executable still exhibits the behavior you describe: the first Ctrl-C is handled, but the second is not.
HOWEVER, if I instead compile with
gcc htest.c -o htest
then the resulting program intercepts every Ctrl-C I type, as I guess you were expecting. So what's going on?
The problem revolves around the fact that the details of the behavior of the signal() function have varied historically. For more detail, see the portability notes in the signal(2) manual page, but here's a brief rundown:
In the original System V UNIX, the custom signal handlers installed via signal() were one-shots: when such a handler was triggered, the disposition for the signal was reset to its default, and the signal was not blocked during execution of the handler.
The System V behavior has some issues, so BSD implemented signal() differently in these respects: signal disposition is not automatically reset, and the signal is blocked during execution of the handler. And additionally, in BSD, certain blocking system calls are automatically restarted if interrupted by a signal that does not result in the program terminating.
These differences mean that the only portable uses for the signal() function are for setting signal disposition to SIG_DFL or SIG_IGN.
Glibc supports both alternatives, and which one you get is controlled by feature test macros, which can be influenced by compiler command-line options. It defaults to BSD semantics as long as the _DEFAULT_SOURCE macro is defined (_BSD_SOURCE prior to glibc 2.19). Gcc defines that macro by default, but some command line options, notably the strict-conformance -std options, cause Gcc not to define it. And that's why I could get different behavior depending on how I compiled the program.
On POSIX systems, the solution is to use the sigaction() function instead of signal() to register signal handlers. This function has well-defined semantics for all the areas of behavioral difference described above, including a well-defined means to choose among them. However, to get its declaration included in all cases, you will need to ensure that a different feature-test macro is defined. For example:
// Manipulation of feature-test macros should precede all header inclusions
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 1
#endif
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sig_handler(int signum) {
static const char msg[] = "\nInside handler function\n";
write(1, msg, sizeof(msg) - 1);
}
int main(void) {
// Register signal handler
struct sigaction sa = { .sa_handler = sig_handler /* default sa_mask and sa_flags */ };
sigaction(SIGINT, &sa, NULL);
while (int i = 1; ; i++) {
printf("%d : Inside main function\n",i);
sleep(1);
}
return 0;
}
That will reliably get you a handler that is not reset when triggered, does have SIGINT blocked while it is being handled, and does not automatically restart system calls interrupted by the signal. That will prevent the program from being killed via a SIGINT, though there are other ways it can be killed, such as via a SIGKILL (which cannot be blocked or handled).
My question is, why when I hit Ctrl+C twice, the program stops?
Undefined behaviour:
Your code invokes undefined behaviour because it calls a function (printf) that isn't async-signal-safe.¹
From C11:
If the signal occurs other than as the result of calling the abort or
raise function, the behavior is undefined if the signal handler refers
to any object with static or thread storage duration that is not a
lock-free atomic object other than by assigning a value to an object
declared as volatile sig_atomic_t, or the signal handler calls any
function in the standard library other than the abort function, the
_Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the
signal that caused the invocation of the handler.
As per the C Standard, you can only call:
abort()
_Exit()
quick_exit()
signal() /* With the first argument equal to the signal number the handler caught */
safely inside a signal handler.
The POSIX standard, however, specifies many more functions. So you can have write(), but not printf().
[1] — An async-signal-safe function is one that can be safely called from within a signal handler. (Linux man page)
Don't use signal(). Instead use sigaction().
Per the spec, [w]hen a signal occurs, and [the second argument to signal] points to a function, it is implementation-defined whether the equivalent of a
signal(sig, SIG_DFL); is executed...
Historically, some UNIX flavors reset the signal disposition to the default behavior upon entry into the user-defined signal handler. That meant that your first SIGFOO would invoke your handler, but your next one would trigger SIG_DFL behavior. Importantly, this is consistent with what you observe.
Yes, printf() is unsafe (undefined behavior) to call in a signal handler. In the code you provide, it might conceivably interrupt itself, which can lead to all sorts of nastiness. In practice, people call printf() all the time in trivial programs to no great harm. The fact that your behavior is repeatable suggests that you are getting deterministic behavior, and not dreadful eldritch magic undefined behavior.
sigaction() forces you to specify what kind of behavior you want when the user-supplied handler is invoked, and is thus superior to signal().
i have an assignment to make, for university, it is almost done, most thing working, there is just one aspect that is not working and i'm not quite sure how to fix it..
The objetivo is to make the problem wait for 2 ctrl+C and close.. But if he catch a first ctrl+C and pass more then 3 seconds the program must forget about it and wait again for another 2 ctrl+C. This is how i'm doing it:
/*Problem 2. Write a program that sleeps forever until the user interrupts it twice with a Ctrl-C, and
then exits. Once the first interrupt is received, tell the user: “Interrupt again to exit.”. The first
interrupt should be forgotten 3 seconds after it has occurred. Additionally, the program should block
the SIGQUIT signal, and ignore the SIGTSTP signal. The program should start by printing “Interrupt
twice with Ctrl-C to quit.” on the screen.*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
//handler to catch the first ctrl_c and ask user to do it another time(no reference to time limit)
void ctrl_c(int sig){
signal(sig, SIG_IGN);
printf("\nInterrupt again to exit.\n");
}
//handler for second ctrl_c. If called, program will end
void second_catch(int sig){
if(sig == SIGINT){
printf("\n");
exit(0);
}
}
//handler to always ignore ctrl_z
void ctrl_z(int sig){
signal(sig, SIG_IGN);
}
int main(){
//blocking SIQUIT (Ctrl+\) using series of command to change the mask value of SIGQUIT
sigset_t sg;
sigemptyset (&sg);
sigaddset(&sg, SIGQUIT);
sigprocmask(SIG_BLOCK, &sg, NULL);
//installing handler to ignore SIGTSTP (Ctrl+Z)
signal(SIGTSTP, ctrl_z);
//two part SIGINT handling
printf("\nInterrupt twice with Ctrl+C to quit.\n");
signal(SIGINT, ctrl_c); //first handler install
do{ //cycle for second hanler install and 3 second timer
if(sleep(3) == 0){
main(); //if second_catch handler is not called within 3 seconds, program will restart
}
else {
signal(SIGINT, second_catch); //upon call, program will end
}
}while(1);
return 0;
}
What's happening is that it keeps reseting after 3 seconds, in a loop.. But i want to reset only 1 time after i click ctrl+c and 3 seconds passed..
What must i change?
Your approach is unlikely to lead to a working program.
First, use a signal handler that only sets a global variable (of volatile sig_atomic_t type) whenever a SIGINT signal is caught. Do not try to print anything from the signal handler, as standard I/O is not async-signal safe.
Second, use sigaction() to install the signal handler. Use zero flags. In other words, do NOT use SA_RESTART flag when installing the handler. This way, when a signal is delivered to your handler, it will interrupt most syscalls (including sleeps). (The functions will return -1 with errno == EINTR.)
This way, after your main() has installed the signal handler, you can have it print the instruction, and enter into a loop.
In the loop, clear the interrupt flag, and sleep for a few seconds. It does not matter how long. If the interrupt flag is not set after the sleep completes, continue (at the beginning of the loop).
Otherwise, you know that the user has pressed Ctrl+C. So, clear the interrupt flag, and sleep for another three seconds. If the flag is set after the sleep completes, you know the user supplied another Ctrl+C, and you can break out of the loop. Otherwise, you just continue the loop again.
Technically, there is a race condition here, as the user might press Ctrl+C twice in a row, rapidly enough so that the main() only sees one.
Unfortunately, increments (flag++) are not atomic; the compiler or the hardware may actually do temp = flag; temp = temp + 1; flag = temp; and the signal may be delivered just before the third step, leading to the signal handler and main() seeing different values of flag.
One way around that is to use C11 atomics (if the architecture and C library provides them, in <stdatomic.h>, with macro ATOMIC_INT_LOCK_FREE defined): volatile atomic_int flag; for the flag, __atomic_add_fetch(&flag, 1, __ATOMIC_SEQ_CST) to increment it, and __atomic_sub_fetch(&flag, 1, __ATOMIC_SEQ_CST) to decrement it.
Another way would be to use a POSIX semaphore. The signal handler can increment it (using sem_post()) safely. In main(), you can use sem_timedwait() to wait for the signal for a limited time, and sem_trywait() to decrement it.
A third way would be to use sigtimedwait() to catch the signal in main() with a timeout, without any signal handlers. This last one is, I believe, the most robust and simple to implement, so that's what I'd use in a real application.
It turns out that there is another way to achieve this, one that responds to two consecutive Ctrl+C presses within three seconds, without leaving any nasty corner cases.
This is NOT exactly what was asked of OP, and as such is not a valid answer to their exercise, but this would be a good approach otherwise.
The idea is to use alarm() and a SIGALRM handler, and two sig_atomic_t flags: one that counts the Ctrl+C keypresses, and one that flags the case when there have been two in a three-second period.
Unfortunately, sleep() cannot be used in this case -- you have to use nanosleep() instead --, as sleep(), alarm(), and SIGALRM signal handling may interfere with each other.
Essentially, we use
#define INTR_SECONDS 3
static volatile sig_atomic_t done = 0;
static volatile sig_atomic_t interrupted = 0;
static void handle_sigalrm(int signum)
{
if (interrupted > 1)
done = 1;
interrupted = 0;
}
static void handle_sigint(int signum)
{
interrupted++;
if (interrupted > 1) {
done = 1;
alarm(1);
} else
alarm(INTR_SECONDS);
}
handle_sigalrm() is installed as the SIGALRM handler, with SIGINT in its signal mask; handle_sigint() is installed as the SIGINT handler, with SIGALRM in its signal mask. This way the two signal handlers block each other, and won't be interrupted by each other.
When a first SIGINT is received, the alarm is primed. If this is the second (or third etc.) SIGINT without an intervening SIGALRM, we also set the done flag, and prime the alarm to occur in one second, to ensure we catch the state change in at most one second.
When a SIGALRM is received, the interrupt count is zeroed. If it was two or more, the done flag is also set.
In main(), we only check done and interrupted, never modify them. This avoids the corner cases I was worried about.
In the worst case, there is one second delay to quitting, if the second Ctrl+C is delivered after we check, but just before we sleep. The alarm(1) in handle_sigint() is for just that case.
The loop in main is then just
while (!done) {
while (!done && !interrupted)
nanosleep(&naptime, NULL);
if (done)
break;
printf("Ctrl+C again to quit!\n");
fflush(stdout);
while (interrupted == 1 && !done)
nanosleep(&naptime, NULL);
}
The first inner loop only sleeps when it has been over three seconds since the last SIGINT (or we never received one). It will be interrupted by both SIGINT and SIGALRM, so the duration does not matter.
The if (done) break; case just avoids printing anything if the user had lightning hands and typed Ctrl+C twice really fast.
The second inner loop only sleep when we are waiting for a second Ctrl+C. It too will be interrupted by both signals, so the duration here does not matter either. Note, however, that we do wish to check interrupted first, to ensure we catch all changes reliably. (If we checked done first, we might be interrupted before we check interrupted, and it is possible, in theory, that done changes to nonzero and interrupt to zero and then to 1 in the mean time. But, if we check interrupted first, and it is 1, any additional interrupts will just set done, which we'll catch. So, interrupted == 1 && done == 0 is the correct check in the correct order here.)
As noted above, the duration specified for nanosleep() does not actually matter, as it will be interrupted by the signal delivery anyway. Something like ten seconds should be fine,
struct timespec naptime = { .tv_sec = 10, .tv_nsec = 0L };
If the lecturer had recommended POSIX.1 functions (sigaction(), nanosleep()), this would have been surprisingly interesting exercise.
First, I am aware that the mutexes are not considered async-safe normally. This question concerns the use of sigprocmask to make mutexes safe in a multithreaded program with async signals and signal handlers.
I have some code conceptually like the following:
struct { int a, b; } gvars;
void sigfoo_handler(int signo, siginfo_t *info, void *context) {
if(gvars.a == 42 || gvars.b == 13) {
/* run a chained signal handler */
}
}
/* called from normal code */
void update_gvars(int a, int b) {
gvars.a = a;
gvars.b = b;
}
gvars is a global variable which is too large to fit in a single sig_atomic_t. It is updated by normal code and read from the signal handler. The controlled code is a chained signal handler, and so it must run in signal handler context (it may use info or context). Consequently, all accesses to gvars have to be controlled via some sort of synchronization mechanism. Complicating matters, the program is multithreaded, and any thread may receive a SIGFOO.
Question: By combining sigprocmask (or pthread_sigmask) and pthread_mutex_t, is it possible to guarantee synchronization, using code like the following?
struct { int a, b; } gvars;
pthread_mutex_t gvars_mutex;
void sigfoo_handler(int signo, siginfo_t *info, void *context) {
/* Assume SIGFOO's handler does not have NODEFER set, i.e. it is automatically blocked upon entry */
pthread_mutex_lock(&gvars_mutex);
int cond = gvars.a == 42 || gvars.b == 13;
pthread_mutex_unlock(&gvars_mutex);
if(cond) {
/* run a chained signal handler */
}
}
/* called from normal code */
void update_gvars(int a, int b) {
sigset_t set, oset;
sigemptyset(&set);
sigaddset(&set, SIGFOO);
pthread_sigmask(SIG_BLOCK, &set, &oset);
pthread_mutex_lock(&gvars_mutex);
gvars.a = a;
gvars.b = b;
pthread_mutex_unlock(&gvars_mutex);
pthread_sigmask(SIG_SETMASK, &oset, NULL);
}
The logic goes as following: within sigfoo_handler, SIGFOO is blocked so it cannot interrupt the pthread_mutex_lock. Within update_gvars, SIGFOO cannot be raised in the current thread during the pthread_sigmask-protected critical region, and so it can't interrupt the pthread_mutex_lock either. Assuming no other signals (and we can always block any other signals that could be problematic), the lock/unlocks should always proceed in a normal, uninterruptible fashion on the current thread, and the use of lock/unlock should ensure that other threads don't interfere. Am I right, or should I avoid this approach?
I found this paper https://www.cs.purdue.edu/homes/rego/cs543/threads/signals.pdf that discusses running AS-unsafe code in sig handlers safely by
masking out signals in AS-unsafe blocks of normal-context code (explored as less efficient) OR
protecting AS-unsafe blocks of normal-context code with a global sig_atomic volatile flag that prevents the AS-unsafe code in the handler from being entered if set (explored as efficient)
This approach satisfies the part of the POSIX standard that says that
calling AS-unsafe functions in sig-handlers is only deemed unsafe if the sighandler interrupts an AS-unsafe function (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03_03 : scroll down to the 1st paragraph after the function list)
I think what you're toying with here is essentially a more fine-grained version of this idea, since you're not trying to prevent
pthread_mutex_lock(&gvars_mutex);
int cond = gvars.a == 42 || gvars.b == 13;
pthread_mutex_unlock(&gvars_mutex);
run from a sig-handler from clashing with any AS-unsafe code but rather just with this same/similar AS-unsafe code dealing with this mutex and these variables.
Unfortunately, POSIX only seems to have a code-only concept of signal-safety: a function is either safe or unsafe, regardless of its arguments.
However, IMO, a semaphores/mutex has no good reason to operate on any data or OS handles other than those contained in the mutex/semaphore they're passed, so I think calling sem_wait(&sem)/pthread_mutex_lock(&mx); from a signal handler ought to be safe if it's guaranteed to never clash with a sem_wait/pthread_mutex_lock to the same mutex, even though the POSIX standard technically says it shouldn't be safe (counter-arguments more than welcome).
You obviously know you're into undefined behavior territory with your mention of sig_atomic_t. That being said, the only way I can see this exact example not working on modern unix-like systems is if the signal was set up with SA_NODEFER.
The mutex is enough to ensure proper synchronization between different threads (including the signal handler being run in another thread) and the sigmask will prevent the signal handler in this thread recursing the mutex.
That being said, you're in deep water with locks inside signal handlers. One signal handler might be safe enough, but if you had two signal handlers doing the same trick with different locks you end up with lock ordering deadlocks. This can be somewhat mitigated by applying process sigmasks instead of thread sigmasks. A simple debugging fprintf in the signal handler will definitely violate lock ordering, for example.
I would back away and redesign my application because stuff like this in a signal handler is a sign it's getting too complex and too easy to break. Signal handlers touching one sig_atomic_t are the only defined thing in the C standard because of the exploding complexity of getting anything else right.
Doing homework with signals and fork and have a problem with the signal.
I've created the function:
void trata_sinal_int() {
char op[2];
printf("\nTerminate? (y/n)\n");
scanf("%s", op);
if (op[0] == 'y') {
printf("Bye Bye\n");
exit(0);
}
}
And in main I have:
signal(SIGINT, trata_sinal_int);
When I run this, and press CTRL ^C the function void trata_sinal_int() is called and I got the message.
If I press y program ends as expected but if I press n program still ends.
It is not returning to were he was before pressing CTRL ^C.
Is this supposed to happen?
It depends on which standard you are adhering to, but Standard C doesn't allow you to do much more than modify a variable of type volatile sig_atomic_t or call _Exit (or abort() or signal()) from a signal handler. POSIX is a lot more lenient. Your code in your signal handler, replete with user interaction, is pushing beyond the limits of what even POSIX allows. Normally, you want your signal handler function to be small and svelte.
Note that the signal handler function should be:
void trata_sinal_int(int signum)
{
This allows you to compile without casts or compiler warnings about type mismatches.
The signal() function may reset the signal handler back to default behaviour when it is invoked; classically, it is necessary to reinstate the signal handler inside the signal handler:
signal(signum, trata_sinal_int);
So far, that's all pretty generic and semi-trivial.
When you type the Control-C, the system does go back to roughly where it was when the signal was originally received. However, what happens next depends on where it was (one of the reasons you have to be so very careful inside the handler). For example, if it was in the middle of manipulating the free list pointers inside malloc(), it would return there, but if you'd reinvoked malloc() inside the handler, all hell might be breaking loose. If you were inside a system call, then your call may be interrupted (return with an error indication and errno == EINTR), or it may resume where it left off. Otherwise, it should go back to where the calculation was running.
Here's (a fixed up version of) your code built into a test rig. The pause() function waits for a signal before returning.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
static void trata_sinal_int(int signum)
{
char op[2];
signal(signum, trata_sinal_int);
printf("\nTerminate? (y/n)\n");
scanf("%s", op);
if (op[0] == 'y')
{
printf("Bye Bye\n");
exit(0);
}
}
int main(void)
{
signal(SIGINT, trata_sinal_int);
for (int i = 0; i < 3; i++)
{
printf("Pausing\n");
pause();
printf("Continuing\n");
}
printf("Exiting\n");
return(0);
}
I should really point out that the scanf() is not very safe at all; a buffer of size 2 is an open invitation to buffer overflow. I'm also not error checking system calls.
I tested on Mac OS X 10.7.5, a BSD derivative. The chance are good that the resetting of signal() would be unnecessary on this platform, because BSD introduced 'reliable signals' a long time ago (pre-POSIX).
ISO/IEC 9899:2011 §7.14.1.1 The signal function
¶5 If the signal occurs other than as the result of calling the abort or raise function, the
behavior is undefined if the signal handler refers to any object with static or thread
storage duration that is not a lock-free atomic object other than by assigning a value to an
object declared as volatile sig_atomic_t, or the signal handler calls any function
in the standard library other than the abort function, the _Exit function, the
quick_exit function, or the signal function with the first argument equal to the
signal number corresponding to the signal that caused the invocation of the handler.
Furthermore, if such a call to the signal function results in a SIG_ERR return, the
value of errno is indeterminate.252)
252) If any signal is generated by an asynchronous signal handler, the behavior is undefined.
The references to quick_exit() are new in C2011; they were not present in C1999.
POSIX 2008
The section on Signal Concepts goes through what is and is not allowed inside a signal handler under POSIX in considerable detail.
First, your signal handler is not exactly async signal safe. In practice this is probably not a problem in your case, since I assume the main() is basically doing nothing while it is waiting for the signal. But it is definately not correct anyway.
As for why the program exits, not counting segfault:s in the signal handler due to invalid use of FILE* functions such as printf, sscanf etc, when the signal is received any system calls you are doing (or, well, most) will be interreupted with EAGAIN.
If you are using something like sleep() in main to wait for the signal to occur it will be interrupted. You are expected to restart it manually.
To avoid this you probably want to use the significantly more portable sigaction interface instead of signal. If nothing else this allows you to indicate that you want system calls to be restarted.
The reason that FILE * functions (and most other functions that use global state such as malloc and free) is not allowed in signal handlers is that you might be in the middle of another operation on the same state when the signal arrives.
This can cause segfaults or other undefined operations.
The normal 'trick' to implement this is to have a self-pipe: The signal handler will write a byte to the pipe, and your main loop will see this (usually by waiting in poll or something similar) and then act on it.
If you absolutely want to do user interaction in the signal handler you have to use write() and read(), not the FILE* functions.
I have the following code. When I compile it with the gnu extensions (-std=gnu99), the program will catch 5 SIGINT before ending (which I would expect). When compiled without it (-std=c99) ends after the second (and only outputs one line).
What am I missing?
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
int int_stage = 0;
int got_signal = 0;
void sigint(int parameter)
{
(void)parameter;
got_signal = 1;
int_stage++;
}
int main()
{
signal(SIGINT,sigint);
while(1)
{
if (got_signal)
{
got_signal = 0;
puts("still alive");
if (int_stage >= 5) exit(1);
}
}
return 0;
}
Use sigaction(2) rather than signal(2).
The Linux man page has this, in particular, in the Portability section:
In the original UNIX systems, when a handler that was established using signal() was invoked by the
delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did
not block delivery of further instances of the signal. System V also provides these semantics for
signal(). This was bad because the signal might be delivered again before the handler had a chance
to reestablish itself. Furthermore, rapid deliveries of the same signal could result in recursive
invocations of the handler.
BSD improved on this situation by changing the semantics of signal handling (but, unfortunately,
silently changed the semantics when establishing a handler with signal()). On BSD, when a signal
handler is invoked, the signal disposition is not reset, and further instances of the signal are
blocked from being delivered while the handler is executing.
The situation on Linux is as follows:
The kernel's signal() system call provides System V semantics.
By default, in glibc 2 and later, the signal() wrapper function does not invoke the kernel system
call. Instead, it calls sigaction(2) using flags that supply BSD semantics. This default behav‐
ior is provided as long as the _BSD_SOURCE feature test macro is defined. By default, _BSD_SOURCE
is defined; it is also implicitly defined if one defines _GNU_SOURCE, and can of course be explic‐
itly defined.
On glibc 2 and later, if the _BSD_SOURCE feature test macro is not defined, then signal() provides
System V semantics. (The default implicit definition of _BSD_SOURCE is not provided if one
invokes gcc(1) in one of its standard modes (-std=xxx or -ansi) or defines various other feature
test macros such as _POSIX_SOURCE, _XOPEN_SOURCE, or _SVID_SOURCE; see feature_test_macros(7).)
Using std=gnu99, you're getting BSD semantics. Using -std=c99, you're getting System V semantics. So the signal handler is "reinstalled" in one case (BSD), and the signal disposition is reset back to SIG_DFL in the other (System V).
The problem is that signal also resets the signal handling mechanism, you have to reset sigint as the signal handler. From the manual
In the original UNIX systems, when a handler that was established using signal() was invoked by the delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did not block delivery of further instances of the signal. System V also provides these semantics for signal(). This was bad because the signal might be delivered again before the handler had a chance to reestablish itself. Furthermore, rapid deliveries of the same signal could result in recursive invocations of the handler.
This is how to do it with the old antiquated signal() call.
Note how int_stage and got_signal have to be sig_atomic_t.
You can also only call async safe functions, look at here for a list.
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
sig_atomic_t int_stage = 0;
sig_atomic_t got_signal = 0;
void sigint(int parameter)
{
(void)parameter;
got_signal = 1;
int_stage++;
}
int main()
{
signal(SIGINT,sigint);
while(1)
{
if (got_signal)
{
signal(SIGINT,sigint);
got_signal = 0;
puts("still alive");
if (int_stage >= 5) exit(1);
}
}
return 0;
}
Please consider either using sigaction, or sigwait.
Sigaction would have practically the same idea, but no nonsense with re-initializing the signal handler. Sigwait would stop your thread until a signal is received. So, for sigwait, you can call any function or deal with any data. I can show you example code if you desire.
I agree with Ethan Steinberg - the "busy wait" is So Wrong...
But the problem is that you're failing to reset the signal handler. AFAIK, you must do this (call "signal(SIGINT,sigint)" again) with any version of C.