mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* 2.6.14-rc1 wait()/SIG_CHILD bevahiour
@ 2005-09-19 17:39 Badari Pulavarty
  2005-09-19 18:01 ` Kyle Moffett
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Badari Pulavarty @ 2005-09-19 17:39 UTC (permalink / raw)
  To: lkml

[-- Attachment #1: Type: text/plain, Size: 4431 bytes --]

Hi,

I am looking at a problem where the parent process doesn't seem 
to cleanup the exited children (with a webserver). We narrowed it
down to a simple testcase. Seems more like a lost SIG_CHILD.

I can easily reproduce the problem on my AMD64 machine. 
Any thoughts on why this is happening ? Any known issues/fixes ?

Thanks,
Badari

elm3b29:~ # ./proctest 10 30
Parent process id: 30007
Spawned 10 children
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Waiting for 10 children to exit
Child 0 exiting. Executed 3682 forks
Child 3 exiting. Executed 3677 forks
Waiting for 8 children to exit
Waiting for 8 children to exit
Waiting for 8 children to exit
Waiting for 8 children to exit
Waiting for 8 children to exit
Waiting for 8 children to exit
Waiting for 8 children to exit
Waiting for 8 children to exit
Waiting for 8 children to exit
Waiting for 8 children to exit
...

#ps -aef 
....
root     30007 20480  0 02:35 pts/1    00:00:00 ./proctest 10 30
root     30009 30007  0 02:35 pts/1    00:00:00 ./proctest 10 30
root     30011 30007  0 02:35 pts/1    00:00:00 ./proctest 10 30
root     30015 30007  0 02:35 pts/1    00:00:00 ./proctest 10 30
root     30017 30007  0 02:35 pts/1    00:00:00 ./proctest 10 30
root     30019 30007  0 02:35 pts/1    00:00:00 ./proctest 10 30
root     30023 30007  0 02:35 pts/1    00:00:00 ./proctest 10 30
root     30026 30007  0 02:35 pts/1    00:00:00 ./proctest 10 30
root     30031 30007  0 02:35 pts/1    00:00:00 ./proctest 10 30
root       698 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       704 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       724 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       730 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       738 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       754 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       761 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       766 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       781 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       786 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       792 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       814 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       820 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       833 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       840 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       859 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       868 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       876 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       890 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       900 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       903 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       911 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       924 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       930 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       935 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root       939 30023  0 02:35 pts/1    00:00:00 [child] <defunct>
root      2822 30017  0 02:35 pts/1    00:00:00 [child] <defunct>
root      2826 30017  0 02:35 pts/1    00:00:00 [child] <defunct>
root      2834 30017  0 02:35 pts/1    00:00:00 [child] <defunct>
root      2842 30017  0 02:35 pts/1    00:00:00 [child] <defunct>
...
...

elm3b29:~ # strace -p 30023
Process 30023 attached - interrupt to quit
futex(0x2aaaaaddf118, FUTEX_WAIT, 2, NULL


[-- Attachment #2: proctest.c --]
[-- Type: text/x-csrc, Size: 2548 bytes --]

/* proctest.c
 * Test the destruction of zombie processes
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/resource.h>

#define MIN_DELAY 500000
#define MAX_DELAY 2000000
#define MAX_RAND_DELAY MAX_DELAY-MIN_DELAY

static int child_count;
static int *pids;

void usage()
{
    printf("USAGE: proctest [child count] [duration in sec]\n");
}

int rand_delay()
{
    int delay = (int)(MAX_DELAY*rand()/(RAND_MAX+1.0));
    return delay;
}

int child_process(int i, int duration)
{
	time_t tt;
	struct timeval tv;
	struct timezone tz;
	int cnt=0;
	gettimeofday(&tv,&tz);
	tt=tv.tv_sec;
	while(tv.tv_sec - tt < duration ) {
		cnt++;
		if (!fork()) {
		    execve("./child",NULL,NULL);
		    _exit(0);
		}
    		usleep(rand_delay()); 
		gettimeofday(&tv,&tz);
	}

    fprintf(stderr,"Child %d exiting. Executed %d forks \n",i,cnt);
    return 0;
}

int pids_remaining()
{
    int i, count = 0;
    for (i = 0; i < child_count; i++)
    {
        if (pids[i] != 0)
            count++;
    }
    return count;
}

void pid_exited(int pid)
{
    int i;
    for (i = 0; i < child_count; i++) {
        if (pids[i] == pid) {
            pids[i] = 0;
            break;
        }
    }
}

void sigchld_handler(int signo)
{
    int status;
    struct rusage ru;
    int pid;

    /* this delay forces some children into the zombie state temporarily */
    usleep(rand_delay()); 

    while(1) {
        pid = wait4(-1, &status, WNOHANG, &ru);
        if (pid <= 0) break;
//	printf("SIGCHLD received for pid %d\n", pid);
        pid_exited(pid); 
    }
}


int main(int argc, char* argv[])
{
    int i, pid, delay, rem, duration;

    if (argc != 3) {
	usage();
	exit(1);
    }

    printf("Parent process id: %d\n", getpid());

    signal(SIGCHLD, sigchld_handler);

    srand(time(NULL));

    child_count = atoi(argv[1]);
    duration = atoi(argv[2]);

    /* storage for the children pids */
    pids = (int *)calloc(child_count, sizeof(int));

    for (i = 0; i < child_count; i++) {
        pid = fork();
        if (pid) {
            pids[i] = pid;
        } else {
            child_process(i, duration);
            _exit(0);
        }
    }
    fprintf(stderr,"Spawned %d children\n", child_count);

    while ((rem = pids_remaining()) > 0) {
        fprintf(stderr,"Waiting for %d children to exit\n", rem);
        usleep(1000000); /* 1 HZ */
    }

    fprintf(stderr,"All children have exited and been cleaned up\n");
    return 0;
}


[-- Attachment #3: child.c --]
[-- Type: text/x-csrc, Size: 80 bytes --]

#include<stdio.h>
main()
{
// printf("PID is %d\n",getpid());
usleep(200000);
}

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.6.14-rc1 wait()/SIG_CHILD bevahiour
  2005-09-19 17:39 2.6.14-rc1 wait()/SIG_CHILD bevahiour Badari Pulavarty
@ 2005-09-19 18:01 ` Kyle Moffett
  2005-09-19 18:19   ` Badari Pulavarty
  2005-09-19 19:14 ` Linus Torvalds
  2005-09-19 20:53 ` Edgar Toernig
  2 siblings, 1 reply; 7+ messages in thread
From: Kyle Moffett @ 2005-09-19 18:01 UTC (permalink / raw)
  To: Badari Pulavarty; +Cc: lkml

On Sep 19, 2005, at 13:39:33, Badari Pulavarty wrote:
> Hi,
>
> I am looking at a problem where the parent process doesn't seem to  
> cleanup the exited children (with a webserver). We narrowed it down  
> to a simple testcase. Seems more like a lost SIG_CHILD.

You don't get one SIG_CHLD per child that quits.  The kernel may and  
probably will merge SIG_CHLD signals together if it has several  
queued before it gets a chance to deliver them to your process.  This  
is true of _all_ signals.  If you "kill -STOP 1234", then "kill -QUIT  
1234", "kill -QUIT 1234", "kill -QUIT 1234", "kill -CONT 1234", the  
PID 1234 will have 3 signals delivered:  The original untrappable  
SIGSTOP, the SIGCONT that causes it to resume, and a single SIGQUIT  
immediately following it.  The correct and portable way to handle  
this is to put a loop in your SIGCHLD signal handler:

#include <sys/types.h>
#include <sys/wait.h>

void sigchld_handler(int signal) {
     pid_t pid;
     int status;

     while( -1 != (child = waitpid(-1, &status, WNOHANG)) ) {
         /*
          * Now "status" is the exit status of the child and
          * "pid" is its pid.  See the waitpid() manpage for
          * macros you can use to get information from the
          * status variable.
          */
         do_some_processing_of_exited_child(pid,status);
     }
}

Cheers,
Kyle Moffett

--
There is no way to make Linux robust with unreliable memory  
subsystems, sorry.  It would be like trying to make a human more  
robust with an unreliable O2 supply. Memory just has to work.
   -- Andi Kleen



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.6.14-rc1 wait()/SIG_CHILD bevahiour
  2005-09-19 18:01 ` Kyle Moffett
@ 2005-09-19 18:19   ` Badari Pulavarty
  0 siblings, 0 replies; 7+ messages in thread
From: Badari Pulavarty @ 2005-09-19 18:19 UTC (permalink / raw)
  To: Kyle Moffett; +Cc: lkml

On Mon, 2005-09-19 at 14:01 -0400, Kyle Moffett wrote:
> On Sep 19, 2005, at 13:39:33, Badari Pulavarty wrote:
> > Hi,
> >
> > I am looking at a problem where the parent process doesn't seem to  
> > cleanup the exited children (with a webserver). We narrowed it down  
> > to a simple testcase. Seems more like a lost SIG_CHILD.
> 
> You don't get one SIG_CHLD per child that quits.  The kernel may and  
> probably will merge SIG_CHLD signals together if it has several  
> queued before it gets a chance to deliver them to your process.  This  
> is true of _all_ signals.  If you "kill -STOP 1234", then "kill -QUIT  
> 1234", "kill -QUIT 1234", "kill -QUIT 1234", "kill -CONT 1234", the  
> PID 1234 will have 3 signals delivered:  The original untrappable  
> SIGSTOP, the SIGCONT that causes it to resume, and a single SIGQUIT  

True.

> immediately following it.  The correct and portable way to handle  
> this is to put a loop in your SIGCHLD signal handler:
> 
> #include <sys/types.h>
> #include <sys/wait.h>
> 
> void sigchld_handler(int signal) {
>      pid_t pid;
>      int status;
> 
>      while( -1 != (child = waitpid(-1, &status, WNOHANG)) ) {
>          /*
>           * Now "status" is the exit status of the child and
>           * "pid" is its pid.  See the waitpid() manpage for
>           * macros you can use to get information from the
>           * status variable.
>           */
>          do_some_processing_of_exited_child(pid,status);
>      }
> }


Thats what I already do, in my current testcase.

    while(1) {
        pid = wait4(-1, &status, WNOHANG, &ru);
        if (pid <= 0) break;
//        printf("SIGCHLD received for pid %d\n", pid);
        pid_exited(pid);
    }


Thanks,
Badari


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.6.14-rc1 wait()/SIG_CHILD bevahiour
  2005-09-19 17:39 2.6.14-rc1 wait()/SIG_CHILD bevahiour Badari Pulavarty
  2005-09-19 18:01 ` Kyle Moffett
@ 2005-09-19 19:14 ` Linus Torvalds
  2005-09-19 23:34   ` Roland McGrath
  2005-09-19 20:53 ` Edgar Toernig
  2 siblings, 1 reply; 7+ messages in thread
From: Linus Torvalds @ 2005-09-19 19:14 UTC (permalink / raw)
  To: Badari Pulavarty, Ingo Molnar, Roland McGrath, Andrew Morton; +Cc: lkml



On Mon, 19 Sep 2005, Badari Pulavarty wrote:
> 
> I can easily reproduce the problem on my AMD64 machine. 
> Any thoughts on why this is happening ? Any known issues/fixes ?

Interesting.

I don't think it is SIGCHLD, because you can "strace" the waiter in one 
window, and send it a SIGCHLD _by_hand_ in the other window, and it will 
do a new "wait4()", but still not pick up its zombie children.

So it looks like wait() itself is broken, and doesn't pick up the children 
for some reason. It just returns 0.

Ingo, Roland - Badari included a test-program in his post on lkml, and I
can trigger the behaviour on ppc64, even if I can't see what's wrong yet.  
Mind taking a look?

			Linus

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.6.14-rc1 wait()/SIG_CHILD bevahiour
  2005-09-19 17:39 2.6.14-rc1 wait()/SIG_CHILD bevahiour Badari Pulavarty
  2005-09-19 18:01 ` Kyle Moffett
  2005-09-19 19:14 ` Linus Torvalds
@ 2005-09-19 20:53 ` Edgar Toernig
  2 siblings, 0 replies; 7+ messages in thread
From: Edgar Toernig @ 2005-09-19 20:53 UTC (permalink / raw)
  To: Badari Pulavarty; +Cc: lkml

Badari Pulavarty wrote:
>
> I am looking at a problem where the parent process doesn't seem 
> to cleanup the exited children (with a webserver). We narrowed it
> down to a simple testcase. Seems more like a lost SIG_CHILD.

You are aware that the defunct processes are all grandchildren
and the sigchild handler of the children is the one inherited from
the main process?  That sighandler is pretty bogus for the children,
i.e. pid_exited never does anything useful.  Further, the children
terminate unconditionally after some time - all still present grand-
children are inherited by init (pid 1) who does the clean up.

But I think the major problem is that the main-loop of the children
and the signal handler both call usleep(rand()) - I wouldn't be
surprised if this combo isn't reentrant, especially from signal
handlers ...

Ciao, ET.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.6.14-rc1 wait()/SIG_CHILD bevahiour
  2005-09-19 19:14 ` Linus Torvalds
@ 2005-09-19 23:34   ` Roland McGrath
  2005-09-20 16:49     ` Badari Pulavarty
  0 siblings, 1 reply; 7+ messages in thread
From: Roland McGrath @ 2005-09-19 23:34 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Badari Pulavarty, Ingo Molnar, Andrew Morton, lkml

The test program is buggy.  Here is one clue:

	elm3b29:~ # strace -p 30023
	Process 30023 attached - interrupt to quit
	futex(0x2aaaaaddf118, FUTEX_WAIT, 2, NULL

It's not anywhere near wait4.  It's deadlocked in the rand() call inside
rand_delay, called from sigchld_handler.  You cannot safely call rand
inside a signal handler, for exactly this reason.  The signal came during
another rand call and attempted to reenter.  If this sort of deadlock is
the failure mode of your real-world case, then it is probably an
application bug.  If this deadlock is just a mistake in your test program
here, then you'll need to give us a corrected test program to pursue
whatever real kernel issue you may have.


Thanks,
Roland

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.6.14-rc1 wait()/SIG_CHILD bevahiour
  2005-09-19 23:34   ` Roland McGrath
@ 2005-09-20 16:49     ` Badari Pulavarty
  0 siblings, 0 replies; 7+ messages in thread
From: Badari Pulavarty @ 2005-09-20 16:49 UTC (permalink / raw)
  To: Roland McGrath; +Cc: Linus Torvalds, Ingo Molnar, Andrew Morton, lkml

On Mon, 2005-09-19 at 16:34 -0700, Roland McGrath wrote:
> The test program is buggy.  Here is one clue:
> 
> 	elm3b29:~ # strace -p 30023
> 	Process 30023 attached - interrupt to quit
> 	futex(0x2aaaaaddf118, FUTEX_WAIT, 2, NULL
> 
> It's not anywhere near wait4.  It's deadlocked in the rand() call inside
> rand_delay, called from sigchld_handler.  You cannot safely call rand
> inside a signal handler, for exactly this reason.  The signal came during
> another rand call and attempted to reenter.  If this sort of deadlock is
> the failure mode of your real-world case, then it is probably an
> application bug.  If this deadlock is just a mistake in your test program
> here, then you'll need to give us a corrected test program to pursue
> whatever real kernel issue you may have.

Thank you for pointing out the problem with the testcase.

While running webserving benchmark (Zeus), we noticed that process is
not cleaning up its zombie childern. We end up with thousands of them
and eventually fork fails. Interesting thing to note in that case was,
moment we do strace -p <parent>, it suddenly seem to cleanup all its
zombie childern. I don't have access to the webserver source code, so we
tried the behaviour simulate it with a simple testcase. 

I will let you know, if I can come up with a *better* testcase to
reproduce the problem.

Thanks,
Badari


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2005-09-20 16:50 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-09-19 17:39 2.6.14-rc1 wait()/SIG_CHILD bevahiour Badari Pulavarty
2005-09-19 18:01 ` Kyle Moffett
2005-09-19 18:19   ` Badari Pulavarty
2005-09-19 19:14 ` Linus Torvalds
2005-09-19 23:34   ` Roland McGrath
2005-09-20 16:49     ` Badari Pulavarty
2005-09-19 20:53 ` Edgar Toernig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®