View of xos/kernel/exit.c


XOS | Parent Directory | View | Download

/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
#include "make_status.h"
#include "status.h"
#include "signal.h"
#include "sched.h"
#include "proc.h"
#include "alloc_proc.h"
#include "idle.h"
#include "proc_struct.h"
 
#include <fd.h>
#include <node.h>
#include <vm.h>
#include <string_table.h>
#include <paging.h>
#include <asm.h>
#include <errno.h>
#include <consts.h>
#include <enums.h>
#include <stddef.h>
 
/* Groupe de processus orphelin */
 
/* A process group is considered to be orphaned if it is not under the control
 * of a job control shell. More precisely, a process group is orphaned when
 * none of its members has a parent process that is in the same session as the
 * group, but is in a different process group. Note that when a process exits,
 * the parent process for its children is changed to be init, which is in a
 * separate session. Not all members of an orphaned process group are
 * necessarily orphaned processes (those whose creating process has exited).
 * The process group of a session leader is orphaned by definition. */
 
/* Dit si un processus contribue a ce que son groupe devienne orphelin,
 * c'est-a-dire s'il n'est pas sous controle d'un processus d'un autre groupe.
 * Peut etre legitimement appele si proc est zombie. */
static int is_orphaned(const struct proc_struct *proc)
{
  return is_orphan(proc) || proc->parent->group == proc->group;
}
 
/* Dit si un groupe de processus est orphelin. */
int is_orphaned_proc_group(const struct proc_group_struct *proc_group)
{
  struct proc_struct *proc;
 
  for (proc = proc_group->first_proc; proc; proc = proc->group_next) {
    if (proc->state == PS_ZOMBIE)
      continue; /* les processus zombies sont ignores */
    if (!is_orphaned(proc))
      return 0;
  }
  return 1;
}
 
/* Dit si le groupe d'un processus devient orphelin si le processus devient
 * zombie ou orphelin. */
static int become_orphaned_proc_group(const struct proc_struct *proc)
{
  struct proc_struct *p;
 
  if (is_orphaned(proc))
    return 0;
  for (p = proc->group->first_proc; p; p = p->group_next) {
    if (p == proc)
      continue;
    if (p->state == PS_ZOMBIE)
      continue; /* les processus zombies sont ignores */
    if (!is_orphaned(p))
      return 0;
  }
  return 1;
}
 
/* Dit si un groupe de processus a des membres stoppes. */
static int has_stopped_member(const struct proc_group_struct *proc_group)
{
  struct proc_struct *proc;
 
  for (proc = proc_group->first_proc; proc; proc = proc->group_next)
    if (proc->state == PS_STOPPED)
      return 1;
  return 0;
}
 
/* Terminaison du processus courant */
 
static void close_file_descrs(struct file_descr_struct *file_descr_table[])
{
  struct file_descr_struct **file_descr;
 
  for (file_descr = file_descr_table; file_descr < file_descr_table + OPEN_MAX; file_descr++)
    if (*file_descr)
      fd_close(*file_descr);
}
 
static void hangup_newly_orphaned_process_group(const struct proc_struct *proc)
{
  if (!become_orphaned_proc_group(proc))
    return;
  if (has_stopped_member(proc->group)) {
    signal_group(proc->group, SIGHUP);
    signal_group(proc->group, SIGCONT);
  }
}
 
/* Si la fin du processus rend orphelin un groupe de processus, et si certain
 * membres de ce groupe sont arretes, alors tous les processus du groupe vont
 * recevoir SIGHUP suivi de SIGCONT. */
static void __attribute__ ((noreturn)) terminate(int status)
{
  struct proc_struct *child;
 
  cli();
 
  /* liberation des ressources */
  sched_remove(current);
  close_file_descrs(current->file_descr_table);
  vm_free(&current->vm);
  node_release(current->wd);
  string_table_destroy(&current->cmdline);
 
  /* passage dans l'etat ZOMBIE et deconnection de l'arborescence */
  current->state = PS_ZOMBIE;
  /* On regarde si le groupe du processus devient orphelin, ce qui est le cas
     si le processus etait le dernier encore sous controle. */
  hangup_newly_orphaned_process_group(current);
  /* On regarde si le groupe de chaque fils devient orphelin, ce qui est le cas
     si le processus etait le dernier qui controlait un membre de ce groupe. */
  for (child = current->first_child; child; child = child->next_sibling)
    hangup_newly_orphaned_process_group(child);
  proc_tree_dishinerit_children(current);
  set_status(status);
 
  /* schedule */
  schedule();
  while (1)
    hlt();
}
 
/* Terminaison normale, demandee par le processus lui-meme. */
void __attribute__ ((noreturn)) exit(int code)
{
  terminate(make_status(0, code, 0));
}
 
/* Terminaison anormale, provoquee par une faute du processus ou un signal. */
void __attribute__ ((noreturn)) abort(int sig)
{
  terminate(make_status(sig, 0, 0));
}
 
/* Attente de la fin d'un processus */
 
static void destroy_proc(struct proc_struct *proc)
{
  proc_group_remove(proc);
  proc_tree_remove(proc);
  proc_list_remove(proc);
  proc_table_remove(proc);
}
 
/* Detruit et libere definitivement un processus. */
static void discard_proc(struct proc_struct *proc)
{
  destroy_proc(proc);
  free_proc(proc);
}
 
/* Recoit l'information d'un fils s'il a change d'etat.
 * Libere les ressources du fils si celui-ci est termine.
 * Retourne le pid du fils en cas de succes, 0 en cas d'echec. */
static int reap(struct proc_struct *child, int *status, int options)
{
  int pid;
 
  if (!report_status(child, status))
    return 0; /* information non disponible */
  pid = child->id;
  switch (child->state) {
  case PS_STOPPED:
    if (!(options & WUNTRACED))
      return 0;
    break;
  case PS_ZOMBIE:
    discard_proc(child);
    break;
  default:
    return 0;
  }
  return pid;
}
 
static int reap_any(struct proc_struct *child, struct proc_group_struct *proc_group, int *status, int options)
{
  int pid;
  int child_exists;
 
  if (child)
    return reap(child, status, options);
  child_exists = 0;
  for (child = current->first_child; child; child = child->next_sibling) {
    if (proc_group && child->group != proc_group)
      continue;
    child_exists = 1;
    if ((pid = reap(child, status, options)))
      return pid;
  }
  return child_exists ? 0 : -ECHILD;
}
 
static int do_wait(struct proc_struct *child, struct proc_group_struct *proc_group, int *status, int options)
{
  int retval;
  int intr;
 
  disable_intr(intr);
  while (!(retval = reap_any(child, proc_group, status, options))) {
    if (options & WNOHANG) {
      retval = 0;
      goto restore;
    }
    if (!wait_child_status()) {
      retval = -ERESTARTSYS;
      goto restore;
    }
  }
 restore:
  restore_intr(intr);
  return retval;
}
 
int wait(struct proc_struct *child, int *status, int options)
{
  return do_wait(child, NULL, status, options);
}
 
int wait_group_any(struct proc_group_struct *proc_group, int *status, int options)
{
  return do_wait(NULL, proc_group, status, options);
}
 
int wait_any(int *status, int options)
{
  return do_wait(NULL, NULL, status, options);
}
 
void reap_orphan_zombies()
{
  int intr;
  struct proc_struct *orphan, *next;
 
  disable_intr(intr);
  if (idle->first_child) {
    for (orphan = idle->first_child; orphan; orphan = next) {
      next = orphan->next_sibling;
      if (orphan->state == PS_ZOMBIE)
        discard_proc(orphan);
    }
    if (!idle->first_child) /* on a detruit init */
      check_paging_empty(); /* test de consistence */
  }
  restore_intr(intr);
}