Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game. |
Notices |
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
Are you new to LinuxQuestions.org? Visit the following links:
Site Howto |
Site FAQ |
Sitemap |
Register Now
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
|
 |
03-16-2011, 10:31 AM
|
#1
|
LQ Newbie
Registered: Mar 2011
Distribution: Gentoo
Posts: 13
Rep:
|
Why does a child exit when the parent receives a signal?
Hi,
I have a problem with the last point of some homework I have for an OS class.
I need the program to print the pid upon execution, then for 10 seconds react
to SIGUSR1, SIGUSR2 and SIGTERM. If either USR1 or USR2 is received, the 10 seconds are reset.
Some of the functions I'm told to use are alarm(), pause() and signal().
The nicest way I found was to handle SIGUSR1, SIGUSR2 and SIGALRM in the parent process, with SIGALRM killing the child (with a SIGTERM). In the child a pause() (or for(;  pause()  would be enough for it to be alive until killed from the SIGALRM handler in the parent.
What I found is that I can't just do a wait() in the parent process, waiting for the child to be killed, since whenever the parent receives a SIGUSR1 or SIGUSR2, the right handler is called but the child exits nicely.
I tried blocking/ignoring those signals in the child, since the default behaviour for SIGUSR1 and SIGUSR2 is to terminate the process, but the result is the same.
The only way I could do it is using a waitpid() within a do { } while(!WIFSIGNALED(status)) since when the child gets killed with the
parent's kill(), it's WIFSIGNALED, whereas when it exits after the parent handles a SIGUSR1/2, it's WIFEXITED.
Is this the expected behavior? Is there any 'standard/recommended' way?
Any help appreciated
P.S. What I'm doing to achieve the expected 10s window is calling alarm(10) in the parent process and again within the SIGUSR1/2 handlers. In the SIGALRM handler I kill the child and within the child I simply do a for(;  pause()
|
|
|
03-16-2011, 12:01 PM
|
#2
|
Senior Member
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,541
|
I don't quite understand what you are attempting to achieve (i.e. your requirements are vague).
If you setup the appropriate signal handlers in your main process, then fork a child process, the signals sent to the main process should not affect the child process.
Be aware that waitpid() will return with a status of -1 when a signal is delivered to the main process; you should verify that the errno reason is EINTR, and if so, merely return back to monitoring the child process to determine when it exits. You can also reset your alarm() if/when this condition occurs -- if that is what you want.
You should avoid the use of signal(); it pretty much has been deprecated. Use sigaction() instead; something like:
Code:
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = signal_handler;
sigaction(SIGUSR1, &act);
You can reuse the "act" variable if you require multiple signal handlers to be setup.
|
|
|
03-16-2011, 02:14 PM
|
#3
|
LQ Newbie
Registered: Mar 2011
Distribution: Gentoo
Posts: 13
Original Poster
Rep:
|
Thanks for your reply.
Let's see. I do indeed use sigaction instead of signal since every updated reference I find says
signal is non portable. But I have to ask whether it is ok to use it. That's not the problem I have, though.
Part of my code is below.
I'll try to paraphrase.
The program has to listen to USR1, USR2 and TERM signals for 10 seconds after being launched.
Whenever USR1 or USR2 is received, the 10s counter is reset again to 10s, that is, there has
to be a 10s space with no signals received.
Those signals increment/decrement some offset for other computation the program later has to do.
The code shows pretty well what I got to, including some debugging code which helped me find the
reason whenever a USR1/USR2 signal was handled, instead of reseting the 10s timer, it exited.
I had to wrap the waitpid within an if which checks its exit status. That way, waitpid returns on
each USR1/USR2 signal received, but, unless the child is killed from the ALRM handler, waitpid
is called again.
In essence, yes, it seems the child never dies, it's the waitpid call which returns on every signal
received. Maybe this is normal, expected behavior. That's what I was asking.
Code:
sig_atomic_t offset = 13;
pid_t child_pid;
void handler (int signal_number)
{
switch (signal_number) {
case SIGUSR1:
offset--;
alarm(10);
break;
case SIGUSR2:
offset++;
alarm(10);
break;
case SIGALRM:
kill(child_pid, SIGTERM);
break;
}
}
int main (int argc, char *argv[])
{
char s[100];
int status;
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &handler;
sigaction( SIGUSR1, &sa, NULL);
sigaction( SIGUSR2, &sa, NULL);
sigaction( SIGALRM, &sa, NULL);
sprintf (s, "My pid is: %ld\n", (long) getpid());
write (2, s, strlen (s));
if ( (child_pid=fork()) == -1) {
error("forking child");
}
else if (child_pid == 0) {
for(;;)
pause();
} else {
alarm(10);
do {
waitpid(child_pid, &status, 0);
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFSIGNALED(status));
...
exit (0);
}
|
|
|
03-16-2011, 02:36 PM
|
#4
|
Member
Registered: Sep 2007
Location: Mariposa
Distribution: FreeBSD,Debian wheezy
Posts: 811
Rep: 
|
Quote:
Originally Posted by jmarcet
In essence, yes, it seems the child never dies, it's the waitpid call which returns on every signal
received. Maybe this is normal, expected behavior. That's what I was asking.
|
-------->8-------->8-------->8-------->8-------->8-------->8----------
Quote:
Originally Posted by jmarcet
Code:
do {
waitpid(child_pid, &status, 0);
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFSIGNALED(status));
|
Ouch. There's your problem. - When using any library call, read the man page. In this case, use the following shell command:
If you do not have man pages on your system, go here. Then type waitpid into the box at the top and click the search button. It will give you a choice of a man page from section 2, or a man page from section 3. In this case, it doesn't matter. With experience, you'll get to know which to choose if you have a choice.
- When reading the man page, be sure to see what the function returns, if anything. Search the page for RETURN VALUE, and read that section carefully. In this case, as in many others, the function returns -1 as its value in case of error. If so, as the man page states for waitpid(), you need to check the value of the integer errno. To make that possible, you'll need to
in your code. If the value of the integer is EINTR, you'll need to do the waitpid() over again, because (as you've discovered) a signal will interrupt a waitpid() call. Thus says the man page. The man page speaks truly.
|
|
|
03-16-2011, 06:45 PM
|
#5
|
LQ Newbie
Registered: Mar 2011
Distribution: Gentoo
Posts: 13
Original Poster
Rep:
|
Thanks for the info.
I had read the man pages (I've been a Gentoo user for years).
I was using wait() in my original code. It was after reading a couple books
on the matter (ALP and the recent, wonderful The Linux Programming Interface)
that I replaced it with waitpid() to read the return status and debug my problem.
I sure did not read wait's man page carefully.
The errno variable was the key. Now I do:
Code:
do {
ret=wait(NULL);
} while (ret==-1 && errno == EINTR);
which not only works fine, it looks good to my eyes.
Thanks again for your help 
|
|
|
All times are GMT -5. The time now is 10:14 PM.
|
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.
|
Latest Threads
LQ News
|
|