Somebody PLEASE try this... Normally, when a process dies it becomes a zombie. If the parent dies (before or after the child), the child is adopted by init. Init will reap the child.
The program included below DOES NOT get reaped. Do like so: gcc -m32 -O2 -std=gnu99 -o foo foo.c while true; do killall -9 foo; ./foo; sleep 1; done BTW, it gets even better if you start playing with ptrace. Use the "strace" program (following children) and/or start sending rapid-fire SIGKILL to all the various _threads_ in the processes. You can get processes wedged in a wide variety of interesting states. I've seen "X" state, processes sitting around with pending SIGKILL, a process stuck in "D" state supposedly core dumping despite ulimit 0 on the core size, etc. ///////////////////////////////// #include <sys/mman.h> #include <signal.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #include <asm/unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdbool.h> static void early_write(int fd, const void *buf, size_t count) { #if 0 unsigned long eax = __NR_write; /* push and pop because -fPIC probably needs ebx for the GOT base pointer */ __asm__ __volatile__( "push %%ebx ; " "push %1 ; pop %%ebx ; int $0x80" "; pop %%ebx" :"=a"(eax) :"r"(fd),"c"(buf),"d"(count),"0"(eax) :"memory" ); #endif } static void p_str(char *s) { size_t count = strlen(s); early_write(STDERR_FILENO,s,count); } static void p_hex(unsigned long u) { char buf[9]; char x[] = "0123456789abcdef"; char *s = buf; s[8] = '\0'; int i = 8; while(i--) buf[7-i] = x[(u>>(i*4))&15]; early_write(STDERR_FILENO,buf,8); } static void p_dec(unsigned long u) { char buf[11]; char *s = buf+10; *s-- = '\0'; int count = 0; while(u || !count) { *s-- = u%10 + '0'; u /= 10; count++; } early_write(STDERR_FILENO,s+1,count); } #define FUTEX_WAIT 0 #define FUTEX_WAKE 1 typedef int lock_t; #define LOCK_INITIALIZER 0 static inline void init_lock(lock_t* l) { *l = 0; } // lock_add performs an atomic add // and returns the resulting value static inline int lock_add(lock_t* l, int val) { int result = val; __asm__ __volatile__ ( "lock; xaddl %1, %0;" : "=m" (*l), "=r" (result) : "1" (result), "m" (*l) : "memory"); return result + val; // Returns the value written to memory } // lock_bts_high_bit atomically tests and // sets the high bit and returns // true if the bit was clear initially static inline bool lock_bts_high_bit(lock_t* l) { bool result; __asm__ __volatile__ ( "lock; btsl $31, %0;\n\t" "setnc %1;" : "=m" (*l), "=q" (result) : "m" (*l) : "memory"); return result; } static int futex(int* uaddr, int op, int val, const struct timespec*timeout, int*uaddr2, int val3) { (void)timeout; (void)uaddr2; (void)val3; int eax = __NR_futex; __asm__ __volatile__( "push %%ebx ; push %1 ; pop %%ebx" " ; int $0x80; pop %%ebx" :"=a"(eax) :"r"(uaddr),"c"(op),"d"(val),"0"(eax) :"memory" ); return eax; } // lock will wait for and lock a mutex static void lock(lock_t* l) { // Check the mutex and set held bit if (lock_bts_high_bit(l)) { // Got the mutex return; } // Increment wait count lock_add(l, 1); while (true) { // Check the mutex and set held bit if (lock_bts_high_bit(l)) { // Got mutex, decrement wait count lock_add(l, -1); return; } int val = *l; // Ensure mutex not given up since check if (!(val & 0x80000000)) continue; // Wait for the mutex futex(l, FUTEX_WAIT, val, NULL, NULL, 0); } } // unlock will release a mutex static void unlock(lock_t* l) { // Turn off lock held bit and check for waiters if (lock_add(l, 0x80000000) == 0) { // No waiters return; } // Waiters found, wake up one of them futex(l, FUTEX_WAKE, 1, NULL, NULL, 0); } unsigned toomany = 42; struct data { unsigned nprocs; lock_t lock; unsigned count; }; struct data *data; static struct data *get_shm(void) { void *addr; int shmid; // create shmid = shmget(IPC_PRIVATE,42,IPC_CREAT|0666); // attach addr = shmat(shmid, NULL, 0); // don't want it to stay around shmctl(shmid, IPC_RMID, NULL); return addr; } static int __attribute__((noreturn,regparm(3),used,unused)) do_stuff(void *arg) { lock(&data->lock); data->nprocs++; unlock(&data->lock); srand((unsigned long)arg); int time_to_die; for(;;) { time_to_die = 0; lock(&data->lock); if(data->nprocs > toomany) { data->nprocs--; time_to_die = 1; } unlock(&data->lock); if(time_to_die) { p_str("exiting\n"); /* don't even think of getting hit * by a signal while the stack is * getting freed */ __asm__ __volatile__( "mov %%esp,%%ebx;" "andl $0xfffff000,%%ebx;" "int $0x80;" "mov %0,%%eax;" "int $0x80" : :"n"(__NR_exit),"c"(4096),"a"(__NR_munmap) :"memory" ); } char *msg = "cloning\n"; int clone_flags = CLONE_VM|CLONE_FS|CLONE_FILES; switch((int) (10.0 * (rand() / (RAND_MAX + 1.0)))) { int ret; default: sched_yield(); break; case 1 ... 3: p_str("forking\n"); __asm__ __volatile__( "int $0x80" :"=a"(ret) :"0"(__NR_fork) :"memory" ); if(!ret) { // child of a fork lock(&data->lock); data->nprocs++; unlock(&data->lock); unsigned t1,t2; __asm__ __volatile__( "rdtsc" :"=a"(t1),"=d"(t2) ); srand(t1^t2); continue; } if(ret<0) { char ec[80]; snprintf( ec, sizeof ec, "fork error %d (%s)\n", -ret, strerror(-ret) ); p_str(ec); } break; case 4 ... 5: msg = "threading\n"; clone_flags |= (CLONE_THREAD|CLONE_SIGHAND); // FALL THROUGH case 6 ... 9: p_str(msg); ; char *stack = mmap( 0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0 ); __asm__ __volatile__( "pushl %%ebx\n\t" "movl %[clone_flags],%%ebx\n\t" "int $0x80\n\t" "mov %%eax,%%ecx\n\t" "jecxz 1f\n\t" "jmp 2f\n" "1:\n\t" // child (ecx is 0) "rdtsc\n\t" "xorl %%edx,%%eax\n\t" "jmp *%[do_stuff]\n" // parent "2:\n\t" "popl %%ebx\n" :"=c"(ret) :"a"(__NR_clone) ,"0"(stack+4096) ,[do_stuff]"D"(do_stuff) ,[clone_flags]"d"(clone_flags) :"memory" ); if(ret<0) { munmap(stack,4096); char ec[80]; snprintf( ec, sizeof ec, "thread error %d (%s)\n", -ret, strerror(-ret) ); p_str(ec); } break; } } } extern const char * const sys_siglist[]; static void signal_handler(int signo){ char mb[80]; snprintf( mb, sizeof mb, "dying with signal %d (%s)\n", signo, sys_siglist[signo] ); p_str(mb); __asm__ __volatile__( "mov %0,%%ebx; int $0x80" : :"r"(signo+128),"a"(__NR_exit) :"memory" ); } static char stack[10240]; int main(int argc, char *argv[]) { if(sizeof(void*)>4) return 7; nice(19); #if 0 stack_t ss = { .ss_sp = stack, .ss_flags = 0, .ss_size = sizeof stack, }; sigaltstack(&ss,NULL); struct sigaction sa; int i = 32; memset(&sa, 0, sizeof(sa)); sa.sa_handler = signal_handler; sigfillset(&sa.sa_mask); while(i--) switch(i){ default: sigaction(i,&sa,NULL); case 0: case SIGINT: /* ^C */ case SIGTSTP: /* ^Z */ case SIGTTOU: /* see stty(1) man page */ case SIGQUIT: /* ^\ */ case SIGPROF: /* profiling */ case SIGKILL: /* can not catch */ case SIGSTOP: /* can not catch */ case SIGWINCH: /* don't care if window size changes */ ; } #endif data = get_shm(); data->nprocs = 1; signal(SIGCHLD,SIG_IGN); char *stack = mmap( 0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0 ); __asm__ __volatile__( "mov %0, %%esp ; jmp *%1" : :"r"(stack+4096), "r"(do_stuff) :"memory" ); return 0; } - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/