/* 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 . */ #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 #include #include #include #include #include #include #include #include #include /* 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(¤t->vm); node_release(current->wd); string_table_destroy(¤t->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); }