Prevent orphan processes from becoming zombies.

This commit is contained in:
Jonas 'Sortie' Termansen 2015-07-31 21:59:53 +02:00
parent 44d4807fc4
commit fc637c8880
3 changed files with 26 additions and 67 deletions

View File

@ -469,7 +469,7 @@ void set_hostname()
free(hostname); free(hostname);
} }
int init_main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
if ( 3 <= argc && !strcmp(argv[1], "--chain") ) if ( 3 <= argc && !strcmp(argv[1], "--chain") )
return chain_boot_device(argv[2]); return chain_boot_device(argv[2]);
@ -513,33 +513,3 @@ int init_main(int argc, char* argv[])
return chain_boot_uuid(root_uuid); return chain_boot_uuid(root_uuid);
} }
int main(int argc, char* argv[])
{
if ( getpid() == 1 )
{
pid_t direct_child_pid = fork();
if ( direct_child_pid < 0 )
error(2, errno, "fork");
if ( direct_child_pid )
{
int status;
while ( true )
{
pid_t child_pid = waitpid(-1, &status, 0);
if ( child_pid < 0 )
error(2, errno, "waitpid");
if ( child_pid == direct_child_pid )
break;
}
int exit_value = WEXITSTATUS(status);
// TODO: Broadcast SIGKILL and wait for all processes to finish.
while ( 0 < waitpid(-1, &status, WNOHANG) )
{
}
return exit_value;
}
}
return init_main(argc, argv);
}

View File

@ -129,7 +129,6 @@ private:
kthread_mutex_t childlock; kthread_mutex_t childlock;
kthread_mutex_t parentlock; kthread_mutex_t parentlock;
kthread_cond_t zombiecond; kthread_cond_t zombiecond;
size_t zombiewaiting;
bool iszombie; bool iszombie;
bool nozombify; bool nozombify;
int exit_code; int exit_code;
@ -190,6 +189,7 @@ public:
private: private:
void OnLastThreadExit(); void OnLastThreadExit();
void LastPrayer(); void LastPrayer();
void WaitedFor();
void NotifyMemberExit(Process* child); void NotifyMemberExit(Process* child);
void NotifyChildExit(Process* child, bool zombify); void NotifyChildExit(Process* child, bool zombify);
void NotifyNewZombies(); void NotifyNewZombies();

View File

@ -127,7 +127,6 @@ Process::Process()
childlock = KTHREAD_MUTEX_INITIALIZER; childlock = KTHREAD_MUTEX_INITIALIZER;
parentlock = KTHREAD_MUTEX_INITIALIZER; parentlock = KTHREAD_MUTEX_INITIALIZER;
zombiecond = KTHREAD_COND_INITIALIZER; zombiecond = KTHREAD_COND_INITIALIZER;
zombiewaiting = 0;
iszombie = false; iszombie = false;
nozombify = false; nozombify = false;
exit_code = -1; exit_code = -1;
@ -323,29 +322,24 @@ void Process::LastPrayer()
if ( init->firstchild ) if ( init->firstchild )
init->firstchild->prevsibling = process; init->firstchild->prevsibling = process;
init->firstchild = process; init->firstchild = process;
process->nozombify = true;
} }
// Since we have no more children (they are with init now), we don't // Since we have no more children (they are with init now), we don't
// have to worry about new zombie processes showing up, so just collect // have to worry about new zombie processes showing up, so just collect
// those that are left. Then we satisfiy the invariant !zombiechild that // those that are left. Then we satisfiy the invariant !zombiechild that
// applies on process termination. // applies on process termination.
bool hadzombies = zombiechild;
while ( zombiechild ) while ( zombiechild )
{ {
ScopedLock zombiechildlock(&zombiechild->parentlock);
ScopedLock initlock(&init->childlock);
Process* zombie = zombiechild; Process* zombie = zombiechild;
zombiechild = zombie->nextsibling; zombiechild = zombie->nextsibling;
zombie->prevsibling = NULL; zombie->nextsibling = NULL;
zombie->nextsibling = init->zombiechild; if ( zombiechild )
if ( init->zombiechild ) zombiechild->prevsibling = NULL;
init->zombiechild->prevsibling = zombie; zombie->nozombify = true;
init->zombiechild = zombie; zombie->WaitedFor();
} }
kthread_mutex_unlock(&childlock); kthread_mutex_unlock(&childlock);
if ( hadzombies )
init->NotifyNewZombies();
iszombie = true; iszombie = true;
bool zombify = !nozombify; bool zombify = !nozombify;
@ -365,14 +359,19 @@ void Process::LastPrayer()
// If nobody is waiting for us, then simply commit suicide. // If nobody is waiting for us, then simply commit suicide.
if ( !zombify ) if ( !zombify )
{ WaitedFor();
kthread_mutex_lock(&groupparentlock); }
bool in_limbo = groupfirst && (grouplimbo = true);
kthread_mutex_unlock(&groupparentlock);
if ( !in_limbo ) void Process::WaitedFor()
delete this; {
} kthread_mutex_lock(&parentlock);
parent = NULL;
kthread_mutex_unlock(&parentlock);
kthread_mutex_lock(&groupparentlock);
bool in_limbo = groupfirst && (grouplimbo = true);
kthread_mutex_unlock(&groupparentlock);
if ( !in_limbo )
delete this;
} }
void Process::ResetAddressSpace() void Process::ResetAddressSpace()
@ -451,8 +450,7 @@ void Process::NotifyNewZombies()
{ {
ScopedLock lock(&childlock); ScopedLock lock(&childlock);
DeliverSignal(SIGCHLD); DeliverSignal(SIGCHLD);
if ( zombiewaiting ) kthread_cond_broadcast(&zombiecond);
kthread_cond_broadcast(&zombiecond);
} }
pid_t Process::Wait(pid_t thepid, int* status_ptr, int options) pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
@ -474,10 +472,10 @@ pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
// target process has the correct parent. // target process has the correct parent.
bool found = false; bool found = false;
for ( Process* p = firstchild; !found && p; p = p->nextsibling ) for ( Process* p = firstchild; !found && p; p = p->nextsibling )
if ( p->pid == thepid ) if ( p->pid == thepid && !p->nozombify )
found = true; found = true;
for ( Process* p = zombiechild; !found && p; p = p->nextsibling ) for ( Process* p = zombiechild; !found && p; p = p->nextsibling )
if ( p->pid == thepid ) if ( p->pid == thepid && !p->nozombify )
found = true; found = true;
if ( !found ) if ( !found )
return errno = ECHILD, -1; return errno = ECHILD, -1;
@ -487,16 +485,13 @@ pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
while ( !zombie ) while ( !zombie )
{ {
for ( zombie = zombiechild; zombie; zombie = zombie->nextsibling ) for ( zombie = zombiechild; zombie; zombie = zombie->nextsibling )
if ( thepid == -1 || thepid == zombie->pid ) if ( (thepid == -1 || thepid == zombie->pid) && !zombie->nozombify )
break; break;
if ( zombie ) if ( zombie )
break; break;
if ( options & WNOHANG ) if ( options & WNOHANG )
return 0; return 0;
zombiewaiting++; if ( !kthread_cond_wait_signal(&zombiecond, &childlock) )
unsigned long r = kthread_cond_wait_signal(&zombiecond, &childlock);
zombiewaiting--;
if ( !r )
return errno = EINTR, -1; return errno = EINTR, -1;
} }
@ -521,13 +516,7 @@ pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
if ( status < 0 ) if ( status < 0 )
status = WCONSTRUCT(WNATURE_SIGNALED, 128 + SIGKILL, SIGKILL); status = WCONSTRUCT(WNATURE_SIGNALED, 128 + SIGKILL, SIGKILL);
kthread_mutex_lock(&zombie->groupparentlock); zombie->WaitedFor();
bool in_limbo = zombie->groupfirst && (zombie->grouplimbo = true);
kthread_mutex_unlock(&zombie->groupparentlock);
// And so, the process was fully deleted.
if ( !in_limbo )
delete zombie;
if ( status_ptr ) if ( status_ptr )
*status_ptr = status; *status_ptr = status;