I read about add signal() function in the signal handler function can over write the default behaviour:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signalHandler();
int main(void) {
signal(SIGUSR1, signalHandler);
sleep(60);
printf("I wake up");
return 0;
}
void signalHandler() {
signal(SIGUSR1, signalHandler);// I add this line to overwrite the default behaviour
printf("I received the signal");
}
And I trigger it with another process
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
kill(5061, SIGUSR1); // 5061 is the receiver pid, kill(argv[1], SIGUSR1) doesn't working, when I get 5061 as parameter
puts("send the signal ");
return 0;
}
The receiver process wake up as soon as it receiver the SIGUSR1 signal. How can I make the receiver continue sleep even when it receive the signal from other process?
BTW why kill(5061, SIGUSR1); 5061 is the receiver pid, kill(argv[1], SIGUSR1) doesn't work, when I get 5061 as parameter?
From the sleep(3) manual page:
Return Value
Zero if the requested time has elapsed, or the number of seconds left to sleep, if the call was interrupted by a signal handler.
So instead of just calling sleep you have to check the return value and call in a loop:
int sleep_time = 60;
while ((sleep_time = sleep(sleep_time)) > 0)
;
Your question (why kill(argv[1], SIGUSR1) does not work) actually is not related to signals, but to basic C programming. Please compile with gcc -Wall -g (i.e. all warnings and debugging information) on Linux, and improve your code till no warning is given.
Please read carefully the kill(2) man page (on Linux, after installing packages like manpages and manpages-dev and man-db on Ubuntu or Debian, you can type man 2 kill to read it on your computer). Read also carefully signal(2) and signal(7) man pages. Read these man pages several times.
Then, understand that the kill syscall is declared as
int kill(pid_t pid, int sig);
and since pid_t is some integral type, you need to convert argv[1] (which is a string, i.e. a char*) to some integer (and kill(argv[1], SIGUSR1) should not even compile without errors, since argv[1] is not some integer but a string). So please use:
kill((pid_t) atoi(argv[1]), SIGUSR1);
The man page says also that you should use signal(SIGUSR1, SIG_DFL) to restore the default behavior, and signal(SIGUSR1, SIG_IGN) to ignore that signal.
Of course, you should better use sigaction(2) as the man page of signal(2) tells you.
At last, please take the habit of reading man pages and spend hours to read good books like Advanced Linux Programming and Advanced Unix Programming. They explain things much better than we can explain in a few minutes. If you are not familiar with C programming, read also some good book on it.
I haven't tried it before, but you could use sigaction(2) instead and set the SA_RESTART flag, this should make syscalls restartable.
Edit:
Actually this won't work, according to the man page of signal(7) not all system calls are restartable:
The sleep(3) function is also never restarted if interrupted by a
handler, but gives a success return: the number of seconds remaining
to sleep.
So you should call the sleep function again with the remaining time instead.
Related
I have found a problem where it asks to explain the behaviour of the following program:
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sig_alrm(int n) {
write(2, "ALARM!\n", 7);
return;
}
int main() {
int fd[2], n;
char message[6], *s;
s = "HELLO\n";
signal(SIGALRM, sig_alrm);
pipe(fd);
if (fork() == 0) {
close(fd[1]);
alarm(3);
while ((n = read(fd[0], message, 6)) > 0);
alarm(0);
exit(0);
}
close(1);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
while (1)
write(1, s, 6);
}
It's basically a parent process with a shared pipe sending HELLO\n constantly via pipe to a child. The child sets up a SIGALRM in three seconds which will be caught by the sig_alrm function, and then proceeds to read indefinitely from the pipe.
If I understand correctly, SIGALRM should interrupt the read() system call, causing it to error out upon arriving, which would in turn cause the child to exit with an instant alarm with default behavior, and the parent to end too due to a SIGPIPE.
The problem is that I attempted to run the code, and both processes continue to read and write from the pipe happily after the SIGALRM arrives to the child.
Is there something I misunderstood from signal behaviors?
The behavior of signal varies from platform to platform, and you should instead use its successor sigaction, which was standardized in POSIX-1.1988.
On your platform and with your build instructions, signal() installs user handlers in a "restartable" fashion, meaning that interruptable system calls will simply be restarted rather than fail with EINTR. Your read is interrupted by the handler, but then resumes. Some platforms and/or build semantics do not do this.
sigaction resolves this ambiguity by providing a flag, SA_RESTART, which controls whether or not interrupted syscalls are, in fact, restarted. It standardizes other historically divergent behaviors, too.
For what it's worth, when I gcc-compile your code on an older Linux system without specifying any feature macros, an strace reveals that signal is, in fact, implemented in terms of a (real-time) sigaction call with SA_RESTART, which explains the behavior you see:
$ strace -fe trace=\!write,read ./so65742182
....
munmap(0x7f8ddffaf000, 70990) = 0
rt_sigaction(SIGALRM, {0x013370, [ALRM], SA_RESTORER|SA_RESTART, ...
# ^^^^^^^^^^
pipe([3, 4])
....
Some time ago I found this exercise in C: without changing the main function, so that receiving a SIGUSR1 signal output is directed and added to a file in append mode as the first parameter. At the reception of another SIGUSR1 the output is directed to the console, and so on. How do such an exercise?
#include <stdio.h>
#include <time.h>
void redirectsetup (char *s){
}
int main (int argc, char *argv[]){
redirect setup(argv[1]);
while(1){
time_t now = time(NULL);
printf("%s",ctime(&now));
sleep(1);
}
}
I'll give you some general pointers.
You need to install a signal handler. Try typing man signal or googling "signal handler linux". As a rule your signal handler should do the minimum possible, many things are illegal in signal handlers including much of the C library. In this case, you could have it set a variable which tells you where to direct output and leave it at that.
Your main program needs to loop, sending characters to wherever the variable tells it.
One thing you might need to be careful of is a signal interrupting a system call, requiring it to be retried.
Have a go and see if you can make it work.
I have a signal handling snippet but it is somehow malfunctioning on my Mac and virtual Linux box at koding.com but on my office Linux PC it is working..Can someone please tell me why..
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void my_isr(int n){
printf("Hello World");
signal(SIGINT, SIG_DFL);
}
int main(){
signal(SIGINT, my_isr);
printf("pid = %d\n", getpid());
while(1);
return 0;
}
When I am pressing Ctrl+C it is not printing Hello World on the first time but it is re-modifying the SIGINT signal action & hence it is exiting the program when I press Ctrl+C second time. Can someone explain me why?
You are not allowed to call every function in a signal handler.
Read signal(7). Only async signal safe functions can be called (directly or indirectly) from a signal handler, and printf is not such a function. If you really want to reliably "print" something from inside a signal handler (which I don't recommend), you can only use the low-level write(2) syscall (it is async signal safe).
So you've got undefined behavior. This explains why it is so bad.
The recommended way is to set a volatile sigatomic_t flag in your signal handler, and to test it outside of it (e.g. in your while loop...).
And you forgot to call fflush(3). You might be more lucky by ending your printf format string with \n since stdout is line-buffered!
Of course, changing your printf inside your signal handler is still UB, even with a \n, but very often it would appear to work.
Here is a conforming version of your program....
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
volatile sig_atomic_t got_signal;
void my_sigint_handler (int signum) {
if (signum == SIGINT) // this is always true!
got_signal = 1;
#define INTERRUPT_MESSAGE "Interrupted!\n"
write(STDOUT_FILENO, INTERRUPT_MESSAGE, strlen(INTERRUPT_MESSAGE));
};
int main(int argc, char**argv) {
struct sigaction act_int;
memset (&act_int, 0, sizeof(act_int));
act_int.sa_handler = my_sigint_handler;
if (sigaction(SIGINT, &act_int, NULL)) {
perror("sigaction"); exit(EXIT_FAILURE);
};
printf ("start %s pid %d\n", argv[0], (int)getpid());
while (!got_signal) {
};
printf ("ended %s after signal\n", argv[0]);
return 0;
}
A useful (and permissible) trick could be to write(2) a single byte -inside your signal handler- on a pipe(7) to self (you set up that pipe using pipe(2) early at program initialization), and in your event loop poll(2) the read end of that pipe.
printf is the culprit just use counter in handler and print outside handler its value it will work.
use sigaction instead of signal
I am a newbie in Linux programming.I copied the code below from a book:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void ouch (int sig)
{
printf("OUCH! - I got signal %d\n", sig);
(void) signal(SIGINT, SIG_DFL);
}
int main ()
{
(void) signal(SIGINT, ouch);
while(1)
{
printf("Hello World!\n");
sleep(1);
}
}
It was expected to print something when Ctrl+C was entered.But it do nothing but print Hello World!.
EDIT:
I am so sorry that I have binded the Ctrl+C as a short-cut key for copy.
Sorry for trouble caused.
My Suggestion is don't use printf in siginal handler (ouch), it may be undefined behavior. Async-signal-safe functions: The list of safe functions that can be call in signal handler man page.
It is not safe to call all functions, such as printf, from within a signal handler.
A useful technique is to use a signal handler to set a flag and then check that flag
from the main program and print a message if required.
Reference: Beginning Linux Programming, 4th Edition,In this book exactly your code is explained, Chapter 11: Processes and Signals, page 484
An additional helpful link:
Explanation: Use reentrant functions for safer signal handling
Sorry, I can't see a question here... but I can guess what you are interested in.
printf() is a stateful function, thus not reentrant. It uses a FILE structure (variable name is 'stdin') to keep it's state. (It is like calling fprintf(stdin,format,...)).
That means, dependant on implementation and 'luck', calling printf() from a signal handler may print what you expect, but also may print nothing or may even crash or worse, smash your memory! Anything could happen.
So, just don't call functions from within a signal handler that are not explicitely marked 'signal-safe'. You will avoid lot's of headaches in the long term.
Put an fflush(stdout) in your signal handler. It was just buffered, then the second SIGINT exited the program before the buffer could be flushed.
Basically,expected output of is that it catches KeyboardInterrupt 5 times and exits the 6th time.(If 1st line of handler() is un-commented)
Now, if i comment that line too, then
also the behavior of program doesnt
change even though I am using
unreliable API.
As I have used signal() function, this is unreliable bcos after the first call to the handler(), SIGINT will then have the default behavior, thats is of exiting the a.out..
The program still quits after 5 ^C.. WHY?
**
The code works even without
reinstating the handler(). WHy?
**
/* ursig1.c */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static int count = 0;
void handler(int signo) {
// signal(SIGINT,handler);
/* Re-instate handler */
++count;
/* Increment count */
write(1,"Got SIGINT\n",11); /* Write message */
}
int
main(int argc,char **argv) {
signal(SIGINT,handler);
/* Register function */
while ( count < 5 ) {
puts("Waiting for SIGINT..");
sleep(4);
/* Snooze */
}
puts("End.");
return 0;
}
Have a read of the Linux man page for signal(2), under the section Portability, where it discusses the varying behaviour of signal(2) across the many different versions of Unix. In particular,
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 is the behaviour you are expecting, but it is not what Linux provides, as allowed by POSIX.1. You should be using sigaction(2) to install your signal handlers to get portable and defined behaviour.