-
I guess that makes sense. it still doesn't make any sense why the signals wouldn't get passed when you kill the child, versus when the child terminates normally. I suspect it has to do with something you're doing in the handle_command() function, but I can't imagine what that might be.
-
Well, I can post more code. I just have to make you all aware that I am seeking help elsewhere (otherwise I would have been more noisy here).
-
OK, now I am confused. It seems I do not fully understand how signals work. Consider the example:
Code:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void child_status_change_handler(int signum)
{
fprintf(stderr, "Signal recieved!\n");
}
int main()
{
signal(SIGCHLD, &child_status_change_handler);
if (fork() == 0)
execlp("./idler", "idler", "5", NULL);
if (fork() == 0)
execlp("./idler", "idler", "10", NULL);
char buf[1024];
fprintf(stderr, "Press any key to quit...\n");
while (fgets(buf, sizeof(buf), stdin) == NULL);
printf("Terminating...\n");
}
I will receive only ONE signal. The other signal is lost in the void. Why?
Source for idler:
Code:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv)
{
fprintf(stderr, "Idler will idle for %s seconds...\n", argv[1]);
sleep(strtol(argv[1], NULL, 10));
fprintf(stderr, "Idler terminating...\n");
}
-
is it possible that one of the fork() calls is failing? you can check this by testing its return value < 0.
-
No, I get the output:
Press any key to quit...
Idler will idle for 5 seconds...
Idler will idle for 10 seconds...
Idler terminating...
Signal recieved!
Idler terminating...
gg
Terminating...
-
I think the received signal is canceling your request for console input. this is normal behavior for unix/linux systems. if you use sigaction() instead of signal(), you can specify that blocking system calls like that should resume instead of returning.
-
Imagine that!
Using sigaction instead of signal, the code now mysteriously works.
I simply changed
signal(SIGCHLD, &child_status_change_handler);
to
Code:
typedef struct sigaction sigaction_t;
sigaction_t sigdata;
memset(&sigdata, sizeof(sigdata), 0);
sigdata.sa_handler = &child_status_change_handler;
sigdata.sa_flags = SA_NOCLDSTOP | SA_RESTART;
sigaction(SIGCHLD, &sigdata, NULL);
What is even better is that fgets is resumed (or restarted?) after the signal, hence it keeps blocking.
I will now have to incorporate that into the main program and see what happens.
EDIT:
Wow! I couldn't ask for it to work more perfectly!
You may not have produced the solution, but you did tread me down on the right path. This saves me a lot of frustration! So thanks a million.
The ability to resume sys calls is also extremely useful!
-
the SA_RESTART flag means to restart any system calls in progress, which is more or less exactly what I told you :P
-
Yeah, which is one of the reasons I tried using it.
-
well I'm glad I could help. did it have the desired effect when you put it in your main program?
-
Yeah, it did. Hence my edit.
-
Auto-restart of syscalls is only useful in some very specific situations... Normally, you want a signal to abort any blocking syscall you might be inside, so that you can actually respond in some way to the signal. It's difficult to safely do very much inside a signal handler -- most functions are not async signal safe, and even for those that are, you need to remember that you are executing in an asynchronous context and must therefore interlock with the main execution. Signal handlers should ideally just set a flag and then return -- the main program does the heavy lifting. For this to work, the syscall MUST be interrupted by the signal, otherwise you'll just sit there blocked when you actually have something you should be responding to.
It's sort of a pain in the ass, but wrapper like the following can be helpful:
Code:
// auto-restarting wrapper around the 'foobar()' system call
int foobar_wrapper(int arg)
{
int ret = foobar(arg);
while (ret < 0)
{
if (errno == EINTR)
HandlePendingSignals(); // take care of anything queued up by the sighandlers
else
break;
}
return ret;
}