I'm trying to use the signal SIGRTMIN, in order to do_stuff when it gives me permission. It's my first time working with signals. The problem is that I'm stuck in
while (go == 0){printf("Waiting\n");}
This is my code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile sig_atomic_t go = 0;
void handler (int sig)
{
go = 1;
signal (sig, handler);
}
void do_stuff (void)
{
puts ("Doing stuff while waiting for alarm....");
}
int main (void)
{
int i=0;
signal (SIGRTMIN, handler);
while(i<5){
while (go == 0){printf("Waiting\n");}
do_stuff();
i++;
}
return EXIT_SUCCESS;
}
Can you help? Thanks in advance!
You need to get the SIGRTMIN signals sent from somewhere; it won't respond to the signal until it is sent. The simplest thing is to have it send itself signals, so here's a mildly revised version of your code.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#ifndef SIGRTMIN
#define SIGRTMIN SIGUSR1
#endif
volatile sig_atomic_t go = 0;
static
void handler(int sig)
{
go = 1;
signal(sig, handler);
}
static
void do_stuff(void)
{
puts("Doing stuff while waiting for alarm....");
go = 0;
}
int main(void)
{
signal(SIGRTMIN, handler);
for (int i = 0; i < 5; i++)
{
while (go == 0)
{
puts("Waiting...");
struct timespec t = { .tv_sec = 0, .tv_nsec = 300000000 };
nanosleep(&t, 0);
kill(getpid(), SIGRTMIN);
}
do_stuff();
}
return EXIT_SUCCESS;
}
Example run:
Waiting...
Doing stuff while waiting for alarm....
Waiting...
Doing stuff while waiting for alarm....
Waiting...
Doing stuff while waiting for alarm....
Waiting...
Doing stuff while waiting for alarm....
Waiting...
Doing stuff while waiting for alarm....
Not very exciting output, but it is at least working. The nanosleep() sleeps for 0.3 seconds.
Related
I have this code:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int cpt = 0;
void handler (int sig) {
cpt++ ;
}
int main() {
int i;
signal(SIGCHLD, handler);
for (i = 0; i < 5; i++) {
if (fork() == 0) {
exit(0);
}
}
while (wait(NULL) != -1) ;
printf("cpt = %d\n", cpt);
return 0;
}
this program to my understanding should always print cpt = 5
but when i run it on my machine it returns different values (3,4,5) why is that?
The SIGCHLD signal is a little funny and doesn't work like you'd expect: we think we should get one signal per child death, but that's not it.
Instead, it's a kind of level-triggered thing where at some unknown intervals it sends the signal if there are any un-waited-for children.
In the loop you provided that burns through the wait(), this loop is consuming multiple children before the signal handler gets around to it, hence less trips through the handler.
Others have pointed out that you should be using a volatile sig_atomic_t variable, and though this is a good idea, it's not why you're seeing this behavior.
I believe the only way to get a guaranteed one-signal-per-child is to actually wait for the child in the signal handler - this makes it appear more like an edge-triggered signal.
Of course, you're pretty limited to what you can do in the signal handler, so if your application already has a good regimen for waiting for child processes, you likely don't need a SIGCHLD handler.
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
static volatile sig_atomic_t cpt = 0;
static void handler(int sig) {
cpt++;
wait(NULL); // ADD ME
}
int main() {
int i;
signal(SIGCHLD, handler);
for (i = 0; i < 5; i++) {
if (fork() == 0) {
exit(0);
}
}
while (wait(NULL) != -1) ;
printf("cpt=%d\n", cpt);
return 0;
}
As an alternative, if the while() loop were not so tight and had other processing (or even an explicit delay), there would not be a race condition and you'd see all five SIGCHLD delivered.
This is probably a very basic question, but I'm using the code below to run a simple alarm. It works as I want it to, but I'm wondering if it's at all possible to run multiple alarms simultaneously that each trigger a different function when complete. Is there a way to do that?
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <time.h>
void alarm_handler(int signum){
printf("five seconds passed!!\n");
}
int main(){
signal(SIGALRM, alarm_handler);
alarm(5);
pause();
return 0;
}
No. According to this source:
Alarm requests are not stacked; only one SIGALRM generation can be scheduled in this manner. If the SIGALRM signal has not yet been generated, the call shall result in rescheduling the time at which the SIGALRM signal is generated.
One alternative is to create a priority queue in which you put your tasks and then always schedule your alarm for the time difference between the current time and the task at the top of the queue.
But be sure to look at this SO question: you're limited in the kinds of things you can do inside your signal handler.
Not with alarm(2), however, you can use POSIX timers to achieve your goal.
Either you can set each timer to run a different signal when it expires or you can use a single signal and pass a pointer to the timer with it via siginfo_t, based on which you can then decide what to do in the handler.
Example:
#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
static timer_t tmid0, tmid1;
static void hndlr(int Sig, siginfo_t *Info, void *Ptr)
{
if(Info->si_value.sival_ptr == &tmid0)
write(2, "tmid0\n", 6);
else{
write(2, "tmid1\n", 6);
_exit(0);
}
}
int main()
{
int r = EXIT_SUCCESS;
sigaction(SIGALRM, &(struct sigaction){ .sa_sigaction = hndlr, .sa_flags=SA_SIGINFO }, 0);
printf("%p %p\n", (void*)&tmid0, (void*)&tmid1);
struct sigevent sev = { .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGALRM };
sev.sigev_value.sival_ptr = &tmid0;
if(0>timer_create(CLOCK_REALTIME,&sev,&tmid0))
{ r=EXIT_FAILURE; goto out; }
sev.sigev_value.sival_ptr = &tmid1;
if(0>timer_create(CLOCK_REALTIME,&sev,&tmid1))
{ r=EXIT_FAILURE; goto out; }
if(0>timer_settime(tmid0, 0, &(struct itimerspec const){ .it_value={1,0} } , NULL) )
{ r=EXIT_FAILURE; goto out; }
//tmid0 expires after 1 second
if(0>timer_settime(tmid1, 0, &(struct itimerspec const){ .it_value={3,0} } , NULL) )
{ r=EXIT_FAILURE; goto out; }
//tmid1 expires after 3 seconds
for(;;)
pause();
out:
if(r)
perror(0);
return r;
}
I'm trying to do something simple with alarms, however the printf is never executing after I do the alarm, why's that?
#include <stdio.h>
#include <signal.h>
int main() {
alarm(3);
printf("Hello...\n");
alarm(6);
while(1);
printf("Hello2\n");
}
I want hello and hello2 to be printed, only hello is being printed for now
You didn't specify a handler for SIGALRM, and its default behavior (per man 7 signal) is to terminate the program. Even if you did specify a handler, after it ran, you'd still be in the while(1) loop.
Here's how you'd modify your program to fix both of those problems:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
volatile sig_atomic_t got_sigalrm = 0;
void handle_sigalrm(int signum) {
got_sigalrm = 1;
}
int main() {
struct sigaction act = { .sa_handler = handle_sigalrm };
sigaction(SIGALRM, &act, NULL);
alarm(3);
printf("Hello...\n");
alarm(6);
while(!got_sigalrm);
printf("Hello2\n");
}
This is probably a very basic question, but I'm using the code below to run a simple alarm. It works as I want it to, but I'm wondering if it's at all possible to run multiple alarms simultaneously that each trigger a different function when complete. Is there a way to do that?
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <time.h>
void alarm_handler(int signum){
printf("five seconds passed!!\n");
}
int main(){
signal(SIGALRM, alarm_handler);
alarm(5);
pause();
return 0;
}
No. According to this source:
Alarm requests are not stacked; only one SIGALRM generation can be scheduled in this manner. If the SIGALRM signal has not yet been generated, the call shall result in rescheduling the time at which the SIGALRM signal is generated.
One alternative is to create a priority queue in which you put your tasks and then always schedule your alarm for the time difference between the current time and the task at the top of the queue.
But be sure to look at this SO question: you're limited in the kinds of things you can do inside your signal handler.
Not with alarm(2), however, you can use POSIX timers to achieve your goal.
Either you can set each timer to run a different signal when it expires or you can use a single signal and pass a pointer to the timer with it via siginfo_t, based on which you can then decide what to do in the handler.
Example:
#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
static timer_t tmid0, tmid1;
static void hndlr(int Sig, siginfo_t *Info, void *Ptr)
{
if(Info->si_value.sival_ptr == &tmid0)
write(2, "tmid0\n", 6);
else{
write(2, "tmid1\n", 6);
_exit(0);
}
}
int main()
{
int r = EXIT_SUCCESS;
sigaction(SIGALRM, &(struct sigaction){ .sa_sigaction = hndlr, .sa_flags=SA_SIGINFO }, 0);
printf("%p %p\n", (void*)&tmid0, (void*)&tmid1);
struct sigevent sev = { .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGALRM };
sev.sigev_value.sival_ptr = &tmid0;
if(0>timer_create(CLOCK_REALTIME,&sev,&tmid0))
{ r=EXIT_FAILURE; goto out; }
sev.sigev_value.sival_ptr = &tmid1;
if(0>timer_create(CLOCK_REALTIME,&sev,&tmid1))
{ r=EXIT_FAILURE; goto out; }
if(0>timer_settime(tmid0, 0, &(struct itimerspec const){ .it_value={1,0} } , NULL) )
{ r=EXIT_FAILURE; goto out; }
//tmid0 expires after 1 second
if(0>timer_settime(tmid1, 0, &(struct itimerspec const){ .it_value={3,0} } , NULL) )
{ r=EXIT_FAILURE; goto out; }
//tmid1 expires after 3 seconds
for(;;)
pause();
out:
if(r)
perror(0);
return r;
}
I have this code:
#include <stdio.h>
#include <signal.h>
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
}
int main(void) {
int s;
signal(SIGTSTP, signal_handler);
while(1){
printf("%s#%s/# ",getlogin(),get_current_dir_name());
scanf("%d",&s);
}
return 0;
}
when i run the code it prints:
something: ^ZCaught signal in CHILD.
As far i understand that the scanf doesn't execute when i press the ctr-z. Although after the printf inside my function it goes straight to the scanf, waits for input and then starts the loop again.Is there any way to avoid scanf when i press ctr-z and start the while loop again? I tried something like that
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
printf("%s#%s/# ",getlogin(),get_current_dir_name());
}
but it didn't work. After the second printf goes straight to the scanf, waits for input and then starts the loop again. Can i, somehow, start the loop again?
The signal handler is interrupting scanf during its read of STDIN. However, because of the way you set signal disposition, the read system call restarts immediately upon return of the signal handler. That's why you are "stuck" in the scanf rather than back at the top of your loop.
One important thing you can do is to use sigaction rather than signal. This will force you to specify the behavior of interrupted calls: restart them or not?
The next thing to do is to limit your signal handlers to functions that are async-signal-safe, lest you risk misery.
As an aside, another change to make is to give us all the required includes (<unistd.h>?) and defines (_GNU_SOURCE ?) to make your program work.
As commented the worst solution should be:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
exit(1);
}
int main(void) {
int s;
signal(SIGTSTP, signal_handler);
while(1){
printf("test\n");
scanf("%d",&s);
}
return 0;
}
Better solution
#include <stdio.h>
#include <signal.h>
static volatile int keepRunning = 1;
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
keepRunning = 0;
}
int main(void) {
int s;
signal(SIGTSTP, signal_handler);
while(keepRunning){
printf("test\n");
scanf("%d",&s);
}
return 0;
}
EDIT after comments
#include <stdio.h>
#include <signal.h>
static volatile int skipPrintf= 1;
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
skipPrintf= 1;
}
int main(void) {
int s;
signal(SIGTSTP, signal_handler);
while(1){
if (skipPrintf == 0)
{
printf("test\n");
}
else
{
skipPrintf = 0;
}
scanf("%d",&s);
}
return 0;
}