/* 
 *  linux/kernel/exit.c 
 * 
 *  Copyright (C) 1991, 1992  Linus Torvalds 
 */ 

 

#undef DEBUG_PROC_TREE 
 
#include <linux/wait.h> 
#include <linux/errno.h> 
#include <linux/signal.h>
#include <linux/sched.h> 
#include <linux/kernel.h> 
#include <linux/resource.h> 
#include <linux/mm.h> 
#include <linux/tty.h> 
#include <linux/malloc.h> 
#include <linux/interrupt.h> 
#include <asm/segment.h> 
#include <asm/pgtable.h> 


 
extern void sem_exit (void); 
extern int acct_process (long exitcode); 
extern void kerneld_exit(void);
 
int getrusage(struct task_struct *, int, struct rusage *);

/* Poniższa procedura generuje sygnał, tzn. ustawia odpowiedni bit w polu signal w task_struct procesu p. Sygnał nie zostanie wysłany, jeżeli proces p go ignoruje (z wyjątkiem SIGCHLD, gdyż nie jest on tak naprawdę ignorowany przy SIG_IGN). W przeciwnym przypadku proces odebrałby sygnał i tak czy tak by nic nie zrobił. Parametry:

static inline void generate(unsigned long sig, struct task_struct * p)
{
/* mask jest słowem z sig-tym bitem ustawionym na 1, a resztš wyzerowanš */
        unsigned long mask = 1 << (sig-1);

/*sa opisuje obsługę "naszego" sygnału przez proces p. W szczególności sa->sa_handler mówi nam, czy sygnał jest ignorowany. Notabene: pole sa_handler zawiera albo wskaźnik do procedury, albo jedną ze stałych (liczbowych) SIG_IGN, SIG_DFL, SIG_ERR */

        struct sigaction * sa = sig + p->sig->action - 1;

/*
* Optimize away the signal, if it's a signal that can
* be handled immediately (ie non-blocked and untraced)
* and that is ignored (either explicitly or by default)
*//* Jeżeli sygnał jest ignorowany i nie jest blokowany, to proces go nie dostanie. Wyjątkiem jest tutaj SIGCHLD. SIGCHLD zachowuje się trochę dziwacznie - mimo, że ma sa
_handler ustawione na SIG_IGN, to jest on obsługiwany (p. do_signal). Procesy œśledzone (tzn. mające ustawiony bit PF_TRACED w polu p->flags) zawsze dostają sygnały*/

        if (!(mask & p->blocked) && !(p->flags & PF_PTRACED)) {
  /* don't bother with ignored signals (but SIGCHLD is special) */
                if (sa->sa_handler == SIG_IGN && sig != SIGCHLD)
                        return;

/* some signals are ignored by default.. (but SIGCONT already did its deed) *//*To dotyczy sygnałów, dla których zachowaniem domyślnym (SIG_DFL) jest ignorowanie (czyli zgodnie wyłuszczonymi powyżej racjami powinniśmy je ignorować) */

                if ((sa->sa_handler == SIG_DFL) &&
                    (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH || sig == SIGURG))
                        return;
        }

/*Tutaj odbywa się "prawdziwe" wysłanie sygnału - zmiana pola p->signal. UWAGA: sygnały blokowane rejestrowane! Gdy zostaną odblokowane będą obsłużone. Procesy uśpione TASK_INTERRUPTIBLE są budzone (o ile istnieją sygnału odebrane, a nie blokowane) */

        p->signal |= mask;
        if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
                wake_up_process(p);
}

/* * Force a signal that the process can't ignore: if necessary * we unblock the signal and change any SIG_IGN to SIG_DFL.*//*Procedura wymuszająca sygnał - robi to co powyższa, z następującymi różnicami:

  1. Nie pozwala na blokowanie sygnału (odblokowuje sygnał, jeżeli był blokowany)
  2. Nie pozwala na ignorowanie sygnału (w razie czego zmienia pole sa_handler z SIG_IGN na SIG_DFL.)*/
void force_sig(unsigned long sig, struct task_struct * p)
{
        sig--;
        if (p->sig) {
                unsigned long mask = 1UL << sig;
                struct sigaction *sa = p->sig->action + sig;
                p->signal |= mask;
                p->blocked &= ~mask;
                if (sa->sa_handler == SIG_IGN)
                        sa->sa_handler = SIG_DFL;
                if (p->state == TASK_INTERRUPTIBLE)
                        wake_up_process(p);
        }
}

/* To jest "prawdziwa" procedura wysyłająca sygnał. Z niej korzystają np. notify_parent, oraz - przez pośrednictwo kill_proc i sys_kill - funkcja systemowa kill. Parametry to:

int send_sig(unsigned long sig,struct task_struct * p,int priv)
{
        if (!p || sig > 32)
                return -EINVAL;

/* Sygnał można wysłać (tzn. mamy uprawnienie), jeżeli:

        if (!priv && ((sig != SIGCONT) || (current->session != p->session)) &&
            (current->euid ^ p->suid) && (current->euid ^ p->uid) &&
            (current->uid ^ p->suid) && (current->uid ^ p->uid) &&
            !suser())
                return -EPERM;

/* Wysyłanie sygnału zerowego (a takiego nie ma) nie kończy wywołania send_sig. Być może sygnał ten służy do sprawdzenia, czy proces ma uprawnienia do wysyłania sygnałów: wywołanie send_sig(0,p,0) zwróci -EPERM, jeżeli nie można wysłać sygnału, 0 w.p.p.*/

        if (!sig)
                return 0;

/* Jeżeli proces nie ma wskaźnika do signal_struct (zasadniczo tablicy z rekordem sigaction na każdy sygnał), to nie warto go wysyłać. Dzieje się tak, gdy odbiorca jest w stanie ZOMBIE*/

        if (!p->sig)
                return 0;

/*Jeżeli proces dostaje sygnał SIGCONT, automatycznie kasowane jest odebranie sygnałów, które mogły go zatrzymać w oczekiwaniu na wznowienie, a nie zostały jeszcze obsłużone. To samo dotyczy SIGKILL. */

        if ((sig == SIGKILL) || (sig == SIGCONT)) {
                if (p->state == TASK_STOPPED)
                        wake_up_process(p);
                p->exit_code = 0;
                p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) |
                                (1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) );
        }

/* Sytuacja odwrotna - jeżeli SIGCONT (wznowienie) nie zostało jeszcze obsłużone, a proces dostał sygnał zatrzymujący, jest ono kasowane */

        if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
                p->signal &= ~(1<<(SIGCONT-1));

/* Actually generate the signal *//* Wywołanie samego sygnału, za pomocą wyżej opisanej procedury */

        generate(sig,p);
        return 0;
}

/* Procedura ta informuje rodzica o zakończeniu działania potomka. Rodzic dostanie sygnał niezależnie od uprawnień potomka (trzeci argument funkcji send_sig, czyli priv, jest różny od 0). Następnie budzone są procesy, które wykonały wait.*/

void notify_parent(struct task_struct * tsk, int signal)
{
        send_sig(signal, tsk->p_pptr, 1);
        wake_up_interruptible(&tsk->p_pptr->wait_chldexit);
}
/*Komentował Mikołaj Bojańczyk */


/* zwalnia miejsce w tablicy procesów po zakończonym procesie i zwalnia te struktury
   danych używane przez proces, które nie były zwolnione przy wywołaniu exit */
void release(struct task_struct * p)
{
        int i;

       
/* jeżeli nie podano żadnego procesu zakończ funkcję */
        if (!p)
                return;
        /* jeżeli argument funkcji dotyczy aktualnego procesu to zakończ funkcję
           wypisując stosowny komunikat */
 
        if (p == current) {
                printk("task releasing itself\n");
                return;
        }
         /* znajdź pozycję w tablicy procesów zajmowaną przez proces wskazywany przez p */  
        for (i=1 ; i<NR_TASKS ; i++)
                if (task[i] == p) {
                         /* zwolnij miejsce w tablicy procesów i zmniejsz aktualną
                            liczbę procesów w systemie */
                        nr_tasks--;
                        task[i] = NULL;
                        /* uaktualnij dowiązania wskazujące na proces wyznaczony przez p */
                        REMOVE_LINKS(p);
                        /* w aktualnej implementacji systemu kolejna funkcja nie 
                           robi nic */
                        release_thread(p);
                        if (STACK_MAGIC != *(unsigned long *)p->kernel_stack_page)
                                printk(KERN_ALERT "release: %s kernel stack corruption. Aiee\n", p->comm);
                        /* zwolnij stos jądra. Zwolnienie stosu jądra znajduje się
                           w tej funkcji, a nie w funkcji exit, ponieważ funkcja ta 
                           używana jest również do zwalniania miejsca w tablicy procesów
                           po wątkach jądra. Stos jądra dla takiego wątku nie może być
                           zwolniny wcześniej, bo inny wątek może posiadać wskaźnik 
                           do tego stosu.     */
                  
                        free_kernel_stack(p->kernel_stack_page);
                        /* zwiększ liczbę błędów braku strony i liczbę wymian stron 
                           procesów potomnych o takie wartości zapamiętane w 
                           zakończonym procesie */
                        current->cmin_flt += p->min_flt + p->cmin_flt;
                        current->cmaj_flt += p->maj_flt + p->cmaj_flt;
                        current->cnswap += p->nswap + p->cnswap;
                        /* zwolnij pamięc przeznaczoną na strukturę task_struct */ 
                        kfree(p);
                        return;
                }
        panic("trying to release non-existent task");
}



 Komentowała Małgorzata Górbiel 


 
 
#ifdef DEBUG_PROC_TREE 
 
 /* 
 * Check to see if a task_struct pointer is present in the task[] array 
 * Return 0 if found, and 1 if not found. 
 */ 
int bad_task_ptr(struct task_struct *p) 
 /* Funkcja bad_task_ptr sprawdza czy adres p struktury task_struct będący parametrem
  * funkcji znajduje się w  tablicy  procesów 
task[]  zdefinowanej  w  pliku  sched.h.
  * W przypadku gdy adresu nie ma w tablicy zwracane jest 0, gdy jest  -  1.
  */

        int     i; 
 
        if (!p) 
                return 0; 
        for (i=0 ; i<NR_TASKS ; i++) 
                if (task[i] == p) 
                        return 0; 
        return 1; 

 
/* 
 * This routine scans the pid tree and makes sure the rep invariant still 
 * holds.  Used for debugging only, since it's very slow.... 
 * 
 * It looks a lot scarier than it really is.... we're doing nothing more 
 * than verifying the doubly-linked list found in p_ysptr and p_osptr,  
 * and checking it corresponds with the process tree defined by p_cptr and  
 * p_pptr; 
 */
void audit_ptree(void) 
 /* Funkcja audit_ptree sprawdza poprawność drzewa procesów.
  * W przypadku spełnienia dowolnego ze sprawdzanych warunków 
  * wyświetlane są na konsoli ostrzeżenia.
  */
 

        int     i; 
 
        for (i=1 ; i<NR_TASKS ; i++) { 
                if (!task[i]) 
                        continue; 
                 /* Dla każdego procesu z tablicy procesów  sprawdzane są następujące
                  * warunki:
                  */
                if (bad_task_ptr(task[i]->p_pptr)) 
                 /* Czy nie istnieje ojciec procesu w tablicy procesów */
                        printk("Warning, pid %d's parent link is bad\n", 
                                task[i]->pid); 
                if (bad_task_ptr(task[i]->p_cptr)) 
                 /* Czy nie istnieje najmłodsze dziecko w tablicy procesów */
                        printk("Warning, pid %d's child link is bad\n", 
                                task[i]->pid); 
                if (bad_task_ptr(task[i]->p_ysptr)) 
                 /* Czy nie istnieje młodsze rodzeństwo w tablicy procesów */
                        printk("Warning, pid %d's ys link is bad\n", 
                                task[i]->pid); 
                if (bad_task_ptr(task[i]->p_osptr)) 
                 /* Czy nie istnieje starsze rodzeństwo w tablicy procesów */
                        printk("Warning, pid %d's os link is bad\n", 
                                task[i]->pid); 
                if (task[i]->p_pptr == task[i]) 
                 /* Czy wskaźnik na ojca wskazuje na sam proces */
                        printk("Warning, pid %d parent link points to self\n", 
                                task[i]->pid); 
                if (task[i]->p_cptr == task[i]) 
                 /* Czy wskaźnik na najmłodsze dziecko wskazuje na sam proces */
                        printk("Warning, pid %d child link points to self\n", 
                                task[i]->pid); 
                if (task[i]->p_ysptr == task[i]) 
                 /* Czy wskaźnik na młodsze rodzeństwo wskazuje na sam proces */
                        printk("Warning, pid %d ys link points to self\n", 
                                task[i]->pid); 
                if (task[i]->p_osptr == task[i]) 
                 /* Czy wskaźnik na starsze rodzeństwo wskazuje na sam proces */
                        printk("Warning, pid %d os link points to self\n", 
                                task[i]->pid); 
                if (task[i]->p_osptr) { 
                 /* Jeżeli wskaźnik na starsze rodzeństwo nie jest NULL'em sprawdzane jest:
                  * - czy wskaźnik na ojca procesu wskazuje na ojca starszego rodzeństwa;
                  * - czy wskaźnik na młodsze rodzeństwo  starszego  rodzeństwa  procesu 
                  *   wskazuje na proces.
                  */
                        if (task[i]->p_pptr != task[i]->p_osptr->p_pptr) 
                                printk( 
                        "Warning, pid %d older sibling %d parent is %d\n", 
                                task[i]->pid, task[i]->p_osptr->pid, 
                                task[i]->p_osptr->p_pptr->pid); 
                        if (task[i]->p_osptr->p_ysptr != task[i]) 
                                printk( 
                "Warning, pid %d older sibling %d has mismatched ys link\n", 
                                task[i]->pid, task[i]->p_osptr->pid); 
                } 
                if (task[i]->p_ysptr) { 
                 /* Podobne testy jak poprzednio z tą różnicą, że wykonane dla 
                  * młodszego rodzeństwa 
                  */
                        if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr) 
                                printk( 
                        "Warning, pid %d younger sibling %d parent is %d\n", 
                                task[i]->pid, task[i]->p_osptr->pid, 
                                task[i]->p_osptr->p_pptr->pid); 
                        if (task[i]->p_ysptr->p_osptr != task[i]) 
                                printk( 
                "Warning, pid %d younger sibling %d has mismatched os link\n", 
                                task[i]->pid, task[i]->p_ysptr->pid); 
                } 
                if (task[i]->p_cptr) { 
                 /* Jeżeli wskaźnik na najmłodsze dziecko  procesu  nie  jest  NULL'em,
                  * wtedy sprawdzene jest:
                  * - czy wskaźnik na ojca najmłodszego dziecka wskazuje na proces; 
                  * - czy wskaźnik na młodsze rodzeństwo najmłodszego dziecka procesu
                  *   nie jest NULL'em.
                  */
                        if (task[i]->p_cptr->p_pptr != task[i]) 
                                printk( 
                        "Warning, pid %d youngest child %d has mismatched parent link\n", 
                                task[i]->pid, task[i]->p_cptr->pid); 
                        if (task[i]->p_cptr->p_ysptr) 
                                printk( 
                        "Warning, pid %d youngest child %d has non-NULL ys link\n", 
                                task[i]->pid, task[i]->p_cptr->pid); 
                } 
        } 


#endif  /* DEBUG_PROC_TREE */ 
 
/* 
 * This checks not only the pgrp, but falls back on the pid if no 
 * satisfactory pgrp is found. I dunno - gdb doesn't work correctly 
 * without this... 
 */
int session_of_pgrp(int pgrp) 

        struct task_struct *p; 
        int fallback; 
 
        fallback = -1; 
        for_each_task(p) { 
                if (p->session <= 0) 
                        continue; 
                if (p->pgrp == pgrp) 
                        return p->session; 
                if (p->pid == pgrp) 
                        fallback = p->session; 
        } 
        return fallback; 

 
 
/* 
 * kill_pg() sends a signal to a process group: this is what the tty 
 * control characters do (^C, ^Z etc) 
 */
/* * kill_pg() sends a signal to a process group: this is what the tty * control characters do (^C, ^Z etc) *//* Kill_pg wysyła sygnał sig do wszystkich procesów, które spełniają warunek p->pgrp=pgrp (czyli należą do grupy procesów pgrp). Argument priv ma takie samo znaczenie jak w funkcji send_sig, tzn. jeżeli priv!=0, sygnał jest wysyłany bez względu na uprawnienia procesu wysyłającego. ^C, ^Z powodują wywołanie tej funkcji dla grupy związanej danym terminalem (tty) */

int kill_pg(int pgrp, int sig, int priv)
{
        struct task_struct *p;
        int err,retval = -ESRCH;
        int found = 0;

        if (sig<0 || sig>32 || pgrp<=0)
                return -EINVAL;

/* for_each_task(p){BLOK(p)} jest makrem, które wywołuje BLOK(p) dla każdego działającego procesu. W tym przypadku po prostu wykonujemy dla odpowiednich procesów send_sig. */

        for_each_task(p) {
                if (p->pgrp == pgrp) {
                        if ((err = send_sig(sig,p,priv)) != 0)
                                retval = err;
                        else
                                found++;
                }
        }

/* Jeżeli przy wysyłaniu sygnału do wszystkich procesów powstał błąd ( -EPERM), błąd ten zostanie zwrócony. Jeżeli w ogóle nie ma procesów w tej grupie, zwracamy -ESRCH. W przeciwnym przypadku zwracane jest 0. */

        return(found ? 0 : retval);
}

/* * kill_sl() sends a signal to the session leader: this is used * to send SIGHUP to the controlling process of a terminal when * the connection is lost. *//* Procedura poniższa zachowuje się podobnie jak kill_pg, tyle że wysyła sygnał do 'lidera' sesji (np. proces kontrolujący terminal dostaje w ten sposób SIGHUP kiedy utracone jest połączenie z terminalem) */

int kill_sl(int sess, int sig, int priv)
{
        struct task_struct *p;
        int err,retval = -ESRCH;
        int found = 0;

        if (sig<0 || sig>32 || sess<=0)
                return -EINVAL;
        for_each_task(p) {
                if (p->session == sess && p->leader) {
                        if ((err = send_sig(sig,p,priv)) != 0)
                                retval = err;
                        else
                                found++;
                }
        }
        return(found ? 0 : retval);
}

/* Funkcja wysyła sygnał do procesu o danym pid. Zupełnie analogiczna do powyższych*/

int kill_proc(int pid, int sig, int priv)
{
        struct task_struct *p;

        if (sig<0 || sig>32)
                return -EINVAL;
        for_each_task(p) {
                if (p && p->pid == pid)
                        return send_sig(sig,p,priv);
        }
        return(-ESRCH);
}

/* * POSIX specifies that kill(-1,sig) is unspecified, but what we have * is probably wrong. Should make it like BSD or SYSV. */ /* Funkcja systemowa. Wysyła sygnał, przy czym pid jest rozumiany nastepująco:

Jest to ta sama konwencja co przy argumentach dla funkcji wait. Wszystkie sygnały są wysłane z argumentem priv=0, czyli wysyłanie nieuprzywilejowane (tzn. można nie mieć uprawnien do wysłania sygnału)*/

asmlinkage int sys_kill(int pid,int sig)
{
        int err, retval = 0, count = 0;

        if (!pid)
                return(kill_pg(current->pgrp,sig,0));

/* Błąd to:

         if (pid == -1) {
                struct task_struct * p;
                for_each_task(p) {
                        if (p->pid > 1 && p != current) {
                                ++count;
                                if ((err = send_sig(sig,p,0)) != -EPERM)
                                        retval = err;
                        }
                }
                return(count ? retval : -ESRCH);
        }
        if (pid < 0) 
                return(kill_pg(-pid,sig,0));

/* Normal kill *//*Normalne wysłanie sygnału do procesy */

        return(kill_proc(pid,sig,0));
}
/* 
 * Determine if a process group is "orphaned", according to the POSIX 
 * definition in 2.2.2.52.  Orphaned process groups are not to be affected 
 * by terminal-generated stop signals.  Newly orphaned process groups are  
 * to receive a SIGHUP and a SIGCONT. 
 *  
 * "I ask yoU, have you ever known what it is to be an orphan?" 
 */ 

static int will_become_orphaned_pgrp(int pgrp, struct task_struct * ignored_task) 
 /* Funkcja ta sprawdza czy grupa procesów podana parametrem  pgrp  (bez uwzględnienia
  * procesu, na którego task struct wskazuje parametr ignored_task) jest 'osierocona'.
  * Przez grupę 'osieroconą' uważać będziemy taką grupę, w której wszystkie procesy są 
  * ZOMBI lub gdy jakiś proces nie jest ZOMBI, to jego ojciec należy do tej grupy. 
  * Gdy grupa z wyłączeniem procesu, na którego strukturę task_struct wskazuje parametr
  * ignored_task, jest 'osierocona' wówczas zwracana jest wartość 1, w p.p. 0.
  * Działąnie pętli for_each_task(p) omówione jest przy funkcji forget_original_parent().
  */
{ 
        struct task_struct *p; 
 
        for_each_task(p) { 
                if ((p == ignored_task) || (p->pgrp != pgrp) ||  
                    (p->state == TASK_ZOMBIE) || 
                    (p->p_pptr->pid == 1)) 
                        continue; 
                if ((p->p_pptr->pgrp != pgrp) && 
                    (p->p_pptr->session == p->session)) 
                        return 0; 
        } 
        return(1);        /* (sighing) "Often!" */ 
} 

int is_orphaned_pgrp(int pgrp) 
 /* Działa identycznie jak funkcja  will_become_orphaned_pgrp  z tą różnicą, że główna
  * pętla dotyczy wszystkich procesów (tzn. nie ma procesu, którego można pominąć).  
  */ 
{ 
        return will_become_orphaned_pgrp(pgrp, 0); 
} 

static inline int has_stopped_jobs(int pgrp) 
 /* Funkcja ta sprawdza, czy w grupie podanej parametrem  pgrp  są jakieś zatrzymane
  * zadania (tzn. pole state w strukturze task_struct jest równe TASK_STOPPED).
  * Zwraca 1 jeżeli taki jest oraz 0 gdy takiego procesu nie ma.
  * Pętla for_each_task(p) omówiona została przy funkcji forget_original_parent().
  */
{ 
        struct task_struct * p; 
 
        for_each_task(p) { 
                if (p->pgrp != pgrp) 
                        continue; 
                if (p->state == TASK_STOPPED) 
                        return(1); 
        } 
        return(0); 
} 

static inline void forget_original_parent(struct task_struct * father) 
 /* Funkcja forget_original_parent() dla każdego procesu, którego pole p_opptr (original  
  * parent)  w strukturze  task_struct  wskazuje na strukturę wskazywaną przez parametr
  * funkcji   - father -   zmienia  wskaźnik  ten  tak,   aby  wskazywał  na  strukturę 
  * task_struct  procesu  INIT.
  * W procesach, którym zmieniany jest ten wskaźnik, ustawiany jest również sygnał końca     
  * (przesyłany ojcu po śmierci dziecka) na SIGCHLD. Procesy te będą zaadoptowane
  * przez init, a procesy któych ojcem jest init, o śmierci informują go
  * zawsze sygnałem SIGCHLD.
  * Pętla for_each_task(p) użyta w tej funkcji, jest makrem zdefiniowanym w pliku sched.h.
  * Jest to pętla for() przechodząca całą listę procesów (określoną polami next_task i
  * prev_taskw strukturze task_struct procesów) poczynając od procesu INIT.
  */
{ 
        struct task_struct * p; 
 
        for_each_task(p) { 
                if (p->p_opptr == father) 
                        p->exit_signal = SIGCHLD;
                        p->p_opptr = task[smp_num_cpus] ? : task[0];  /* init */ 
        } 
} 

static inline void close_files(struct files_struct * files) 
 /* Funkcja ta zamyka otwarte deskryptory wskazywane przez parametr files. */
{ 
        int i, j; 
 
        j = 0; 
        for (;;) { 
                unsigned long set = files->open_fds.fds_bits[j]; 
                i = j * __NFDBITS; 
                j++; 
                if (i = NR_OPEN) 
                        break; 
                while (set) { 
                        if (set & 1) 
                                close_fp(files->fd[i]); 
                                 /* Funkcja close_fp() zdefiniowana jest w pliku open.c
                                  * Zamyka ona deskryptor będący w tablicy otwartych 
                                  * plików ze struktury files_struct. 
                                  */
                        i++;  
                        set >>= 1; 
                } 
        } 
} 

static inline void __exit_files(struct task_struct *tsk) 
 /* Funkcja ta zamyka otwarte przez proces (na którego strukturę  task_struct  wskazuje
  * parametr tsk)  pliki.
  */
{ 
        struct files_struct * files = tsk->files; 
 
        if (files) { 
                 /* Jeżeli pole files ze struktury task_struct procesu
                  * jest NULL'em wtedy funkcja kończy działanie. W przeciwnym 
                  * przypadku wykonywane są następujące operacje:
                  */
                tsk->files = NULL; 
                   /* Ustawienie pola files ze struktury task_struct procesu
                    * na NULL. Wartość tego pola zachowana została na 
                    * lokalnej zmiennej files. 
                    */ 
                if (!--files->count) { 
                         /* Zmniejszony  zostaje  licznik  odwołań  do  struktury 
                          * zapamiętanej na zmiennej files. Jeżeli po zmniejszeniu ma  
                          * on wartość 0 (tzn. struktura ta nie jest dzielona z innymi 
                          * procesami), wtedy wykonywane są następujące funkcje:
                          */
                        close_files(files); 
                          /* Funkcja close_files() zaimplementowana jest w bieżącym 
                           * pliku. Zamyka otwarte deskryptory.
                           */
                        kfree(files); 
                          /* Funkcja kfree() zaimplementowana jest w pliku kmalloc.c.
                           * Zwalnia pamięć przedzieloną na strukturę danych 
                           * wskazywaną przez parametr.
                           */
                } 
        } 
} 

  
void exit_files(struct task_struct *tsk) 
{ 
 __exit_files(tsk); 
} 

static inline void __exit_fs(struct task_struct *tsk) 
 /* Funkcja ta zwalnia i-węzły katalogu bieżącego i katalogu głównego 
  * wskazane przez pole fs ze struktury task_struct procesu.
  */
{ 
        struct fs_struct * fs = tsk->fs; 
 
        if (fs) { 
         /* Jeżeli pole fs ze struktury task_struct procesu
          * nie jest NULL'em wtedy wykonywane są następujące operacje:   
          */
                tsk->fs = NULL; 
                 /* Na to pole przypisany zostaje NULL, poprzednia wartość
                  * tego pola została zapamiętana na lokalnej zmiennej fs.
                  */
                if (!--fs->count) { 
                         /* Zmniejszony zostaje licznik odwołań do struktury, na którą
                          * wskazuje zmienna fs. Jeżeli jest on zerem (tzn. struktura ta 
                          * nie jest dzielona z innymi procesami) wtedy:
                          * - zwolnione zostają i-węzły wskazywane przez pola root 
                          * i pwd struktury wskazywanej przez fs funkcją iput() 
                          * zdefiniowaną w pliku inode.c;
                          * - funkcja kfree() (zdefiniowana w pliku kmalloc.c) zwalnia 
                          * pamięć przydzieloną na strukturę wskazywaną przez zmienną fs.
                          */
                        iput(fs->root); 
                        iput(fs->pwd); 
                        kfree(fs); 
                } 
        } 
} 

 
void exit_fs(struct task_struct *tsk) 
{ 
  __exit_fs(tsk); 
} 
 
static inline void __exit_sighand(struct task_struct *tsk) 
 /* Zwalnianie pamięci przydzielonej procesowi na tablicę obsługi sygnałów. */ 
{ 
        struct signal_struct * sig = tsk->sig; 
 
        if (sig) { 
                  /* Jeżeli pole sig ze struktury task_struct procesu nie wskazuje 
                   * na NULL, wtedy przypisany zostaje mu NULL. Poprzednia wartość tego
                   * pola zachowana jest na lokalnej zmiennej sig. 
                   */ 
                tsk->sig = NULL; 
                if (!--sig->count) { 
                          /* Zmniejszony zostaje licznik odwołań do struktury, na którą 
                           * wskazuje zmienna sig. Jeżeli jest on równy 0 (tzn. struktura
                           * ta nie jest dzielona z innymi procesami),  wtedy  funkcją 
                           * kfree()  (zaimplementowaną  w  pliku  kmalloc.c) 
                           * zwolniona zostaje pamięć przydzielona na tą strukturę.
                           */ 
                        kfree(sig); 
                } 
        } 
} 
 
   
void exit_sighand(struct task_struct *tsk) 
{ 
        __exit_sighand(tsk); 
}


static inline void __exit_mm(struct task_struct * tsk) 
 /* Zwalnianie pamięci przydzielonej procesowi na tablicę stron, segment kodu 
  * i segment danych. 
  */ 
{ 
        struct mm_struct * mm = tsk->mm; 
  
        /* Set us up to use the kernel mm state */ 
        if (mm != &init_mm) {
                 /* Jeżeli pole  mm  ze stuktury  task_struct  procesu nie wskazuje na 
                  * strukturę mm_struct procesu INIT  (która jest zdefiniowana w pliku 
                  * sched.h), wtedy wykonywane są następujące czynności:
                  */ 
                flush_cache_mm(mm);
                flush_tlb_mm(mm); 
                 /* Funkcje flush_cache_mm i flush_tlb_mmm zdefiniowane są
                  * w pliku init.c i szerzej omówione powinny być w rozdziale 
                  * dotyczącym zarządzaniem pamięcią.
                  */ 
                tsk->mm = &init_mm; 
                tsk->swappable = 0; 
                 /* Na pole  mm  w strukturze  task_struct  procesu przypisany 
                  * zostaje  adres  zmiennej  init_mm  zdefiniowanej  w  pliku 
                  * sched.h.  Jest on tym samym adresem,  który przypisany jest 
                  * polu mm w strukturze task_struct procesu  INIT. Poprzednia 
                  * wartość pola mm dla procesu wykonującego 'exit' pamiętana jest
                  * na lokalnej zmiennej mm. Następnie polu swappable ze struktury 
                  * task_struct bieżącego procesu (domyślnie ustawionym na 1) 
                  * przypisane zostaje 0.
                  */ 
                SET_PAGE_DIR(tsk, swapper_pg_dir); 
 
                /* free the old state - not used any more */ 
                if (!--mm->count) { 
                         /* Licznik odwołań  do  struktury  mm_struct  (zapamietanej na 
                          * zmiennej  mm) zostaje zmniejszony.  Jeżeli jest on równy  0 
                          * ( tzn.  struktura ta nie jest dzielona z innymi procesami ) 
                          * zwolniona  zostaje  pamięć przydzielona  na  pola struktury 
                          * mm_struct  oraz  na  samą  strukturę.
                          */ 
                        exit_mmap(mm); 
                        free_page_tables(mm); 
                        kfree(mm); 
                } 
        } 
} 
 

void exit_mm(struct task_struct *tsk) 
{ 
        __exit_mm(tsk); 
} 
 
 
/*  
 * Send signals to all our closest relatives so that they know 
 * to properly mourn us.. 
 */  

static void exit_notify(void) 
 /* Funkcja exit_notify służy do usunięcia procesu wykonującego 'exit' 
  * z drzewa procesów, oraz wysłania sygnałów do odpowiednich procesów.
  */
{ 
        struct task_struct * p; 
 
        forget_original_parent(current); 
         /* Funkcja forget_original_parent służy do zmiany pola p_opptr w strukturze
          * task_struct tak aby wskazywało na strukturę  task_struct  procesu  INIT. 
          * Zmiana  ta  dotyczy  tych  procesów,  dla  których  pole  to  wskazywało
          *  na strukturę  task_struct  procesu wykonującego 'exit'.
          */

        /* 
         * Check to see if any process groups have become orphaned 
         * as a result of our exiting, and if they have any stopped 
         * jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2) 
         * 
         * Case i: Our father is in a different pgrp than we are 
         * and we were the only connection outside, so our pgrp 
         * is about to become orphaned. 
         */ 

        if ((current->p_pptr->pgrp != current->pgrp) && 
            (current->p_pptr->session == current->session) && 
            will_become_orphaned_pgrp(current->pgrp, current) && 
            has_stopped_jobs(current->pgrp)) { 
                /* Jeżeli proces wykonujący 'exit'  jest w innej grupie niż jego rodzic,
                 * oraz grupa ta nie zostanie 'osierocona' po śmierci bieżącego procesu,
                 * oraz  istnieje  w  tej  grupie  proces  zatrzymany,  wtedy grupie tej
                 * wysyłane są sygnały SIGHUP oraz SIGCONT, opisane w temacie dotyczącym
                 * sygnałów.
                 * (za grupę 'osieroconą' uważamy taką, w której wszystkie procesy są
                 *  ZOMBI lub jeżeli jakiś proces nie jest ZOBMI to jego
                 *  ojciec należy do tej grupy)
                 */
                kill_pg(current->pgrp,SIGHUP,1); 
                kill_pg(current->pgrp,SIGCONT,1); 
        } 
 
        /* Let father know we died */ 

        notify_parent(current,current->exit_signal); 
         /* Funkcja notify_parent odpowiada za wysłanie 'ojcu' (procesowi, którego 
          * struktura  task_struct  wskazywana jest przez  pole  p_pptr  struktury 
          * task_struct  procesu  wykonującego  'exit')  sygnału o śmierci dziecka.
          */  
        /* 
         * This loop does two things: 
         *  
         * A.  Make init inherit all the child processes 
         * B.  Check to see if any process groups have become orphaned 
         *      as a result of our exiting, and if they have any stopped 
         *      jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2) 
         */ 
 
        while ((p = current->p_cptr) != NULL) { 
                 /* Dopoki wskaźnik na strukturę task_struct dziecka nie jest NULL'em,
                  * w pętli wykonywane są następujące czynności:
                  * - na zmiennej p zachowywany jest wskaźnik do struktury  task_struct 
                  *   najmłodszego dziecka;
                  * - wskaźnik do struktury task_struct najmłodszego dziecka przekierowany 
                  *   zostaje na strukturę task_struct starszego dziecka;
                  * - jako ojciec procesu P, na którego strukturę task_struct  wskazuje
                  *    p, przypisany zostaje proces wskazywany przez pole o_pptr
                  *   (jeżeli pole to wskazywało na proces wykonujący exit, to po wywołaniu funkcji
                  *   forget_original_parent, wskazuje ono na proces INIT);
                  * - proces P zostaje odpowiednio umieszczony na liście dzieci nowego
                  *   ojca (tzn. dowiązanie do najmłodszego dziecka nowego ojca zostaje 
                  *    przekierowane na strukturę  task_struct  procesu P,  wskaźnik do
                  *    młodszego rodzeństwa procesu  P  staje się  NULL'em, a starszego
                  *    rodzeństwa wskaźnikiem do struktury task_struct  dotychczasowego 
                  *    najmłodszego dziecka nowego ojca, itd.);
                  * - jeżeli proces P jest ZOMBI, wtedy funkcją notify_parent() powiadomiony
                  *   zostaje jego nowy rodzic o śmierci dziecka;
                  * - wysłane zostają odpowiednie sygnały do grupy  procesu  P  jeżeli
                  *   ojciec jest w innej grupie oraz funkcje is_orphaned_pgrp()
                  *    i has_stopped_jobs() zwrócą 1.
                  */
                current->p_cptr = p->p_osptr; 
                p->p_ysptr = NULL; 
                p->flags &= ~(PF_PTRACED|PF_TRACESYS); 
                p->p_pptr = p->p_opptr; 
                p->p_osptr = p->p_pptr->p_cptr; 
                p->p_osptr->p_ysptr = p; 
                if (p->p_osptr)
                        p->p_pptr->p_cptr = p; 
                if (p->state == TASK_ZOMBIE) 
                        notify_parent(p); 
 
                 /*
                 * process group orphan check 
                 * Case ii: Our child is in a different pgrp  
                 * than we are, and it was the only connection 
                 * outside, so the child pgrp is now orphaned. 
                 */ 
 
                if ((p->pgrp != current->pgrp) && 
                    (p->session == current->session) && 
                    is_orphaned_pgrp(p->pgrp)&& 
                    has_stopped_jobs(p->pgrp)) { 
                        kill_pg(p->pgrp,SIGHUP,1); 
                        kill_pg(p->pgrp,SIGCONT,1); 
                } 
        } 
        if (current->leader) 
                 /* Jeżeli proces wykonujący 'exit' jest leaderem grupy związanej z
                  *  pewnym terminalem, wtedy wywoływana jest funkcja disassociate_ctty()
                  *  zdefiniowana w pliku tty_io.c, która wykonuje następujące operacje:
                  * - wysyła sygnały: SIGCONT, SIGHUP procesom z grupy; 
                  * - 'uwalnia' terminal od 'wpływów' procesów wykonujących się na nim 
                  *   (tzn. wstawia w polu session struktury tty bieżącego trminala
                  *    wartość 0, zaś w polu pgrp tej struktury wartość -1).
                  */  
                disassociate_ctty(1); 
} 


 
NORET_TYPE void do_exit(long code) 
 /* Jest to główna funkcja wykonująca algorytm 'exit'.
  * Wywoływana jest w funkcji sys_exit.  
  */  
{ 
        if (intr_count) { 
                printk("Aiee, killing interrupt handler\n"); 
                intr_count = 0; 
        }
          /* Zmienna intr_count zadeklarowana jest w interrupt.h. Jest ona flagą
           * wskazującą na to, czy proces jest w trakcie  obsługi przerwania czy nie. 
           * Wykonanie algorytmu różni się dla  obu  przypadków  jedynie  pojawieniem 
           * się komunikatu na konsoli. Wyświetlanie komunikatów na konsoli obsługuje
           * funkcja printk() zdefiniowana w pliku printk.c.
           */ 
fake_volatile: 
 /* Etykieta fake_volatile wykorzystana jest do skoku z końca programu 
  * w przypadku niespodziewanego powrotu z funkcji schedule(). 
  */
        acct_process(code); 
         /* Funkcja acct_process() zdefiniowana jest w pliku sys.c.
          * Służy do zapisania danych o pracy procesu do pliku rozliczeniowego.
          * Flaga acct_active, zdefinoiowana w plklu sys.c odpowiada za to,
          * czy włączona jest opcja rozliczania procesów. Jeżeli flaga ta jest 
          * niezerowa, wtedy na lokalną zmienną typu struct acct zapisywane
          * są następujące dane o procesie pobrane lub wyliczone z pól struktury
          * task_struct procesu:
            - nazwa programu pobrana z pola com;
            - czas pracy procesu w trybach jądra i użytkownika pobrane odpowidednio
              z pól stime i utame;
            - czas pracy i rozpoczęcia pracy procesu; 
            - identyfikatory użytkownika, grupy, terminala procesu pobrane z pól uid, gid 
              i tty;
            - flaga opisująca cechy pracy procesu (takie jak: czy wokonany został zrzut
              pamięci, czy wywołał funkcję fork "bez funkcji exec", itp) pobrane z pola 
              flags.
          * Następnie dopisane zostają te dane do pliku rozliczeniowego, którego parametry
          * określone są na zmiennej acct_file. 
          */
        current->flags |= PF_EXITING;
          /* W strukturze task_struct procesu pole flags
           * jest ustawiane na PF_EXITING.
           */
        del_timer(&current->real_timer); 
         /* Funkcja del_timer() zdefiniowana jest w pliku shed.c. Służy do usunięcia 
          * prywatnego zegara procesu. Zegar ten znajduje się w stukturze timer_list 
          * zadeklarowanej w pliku time.h. Wskaźnik do zegara procesu wykonującego 'exit'
          * przechowywany jest w polu real_timer w strukturze task_struct procesu.
          */
        sem_exit();
         /* Funkcja sem_exit() zdefiniowana jest w pliku sem.c. Najpierw usuwa ona proces 
          * wykonujący  'exit'  z listy procesów czekających na semaforze.  Wskaźnik do  
          * elementu tej listy  przechowywany  jest w  polu  semsleeping  w  strukturze 
          * task_struct  procesu. Następnie cofane są operacje semaforowe wykonywane ze 
          * znacznikiem  SEM_UNDO.  Lista  tych  operacji  przechowywana  jest  w  polu 
          * semundo w strukturze task_struct procesu. 
          */
        kerneld_exit();
         /* Funkcja kerneled_exit() zdefiniowana  jest w pliku  msg.c.  Obsługuje  ona
          * odłączenie procasu od kolejki komunikatów jądra. Jeżeli istnieje ta kolejka 
          * (tzn. kerneld_msqid!=0)  oraz  proces  korzysta  z  niej  (tzn. istnieje i: 
          * 0 <= i <= MAX_KERNELDS takie, że kerneld_arr[i] = current->pid),  wówczas 
          * odłączany jest proces od kolejki komunikatow jądra (tzn. kerneld_arr[i]=0) 
          * oraz zminiejszany jest licznik odwołań (tzn. --n_kernelds).  W  przypadku, 
          * gdy  nie  ma  procesu  korzystającego z kolejki  komunikatow  jądra  (tzn. 
          * n_kernelds == 0),  kolejka ta  zostaje  usunięta.  Zmienne  kerneld_msqid, 
          * kerneld_arr[]n_kernelds  i  MAX_KERNELDS zdefiniowane są w pliku msg.c.
          */
        __exit_mm(current);
         /* Funkcja __exit_mm()  zdefiniowana jest w bieżącym pliku.  Jej  celem  jest 
          * zwolnienie  pamieci  przydzielonej  procesowi  na  segmenty  danych,  kodu 
          * i tablice stron.
          */
        __exit_files(current)          /* Funkcja __exit_files() zdefiniowana jest w bieżącym pliku. Zadaniem jej jest
           * zamknięcie (zwolnienie) otwartych przez proces wykonujący 'exit' plików.
           */
        __exit_fs(current);
          /* Funkcja __exit_fs() zdefiniowana jest w bieżącym pliku.
           * Zamyka (zwalnia) ona i-wezly katalogu głównego oraz katalogu bieżącego.
           */
        __exit_sighand(current)          /* Funkcja __exit_sighand() zdefiniowana jest w bieżącym pliku. Odpowiada ona
           * za zwalnianie pamięci przydzielonej na tablicę obsługi sygnałów.
           */
        exit_thread(); 
          /* Funkcja exit_thread() zdefiniowana jest w pliku process.c. Odpowiada ona
           * między inymi za zwolnienie pamięci przydzielonej procesowi na pole ldt 
           * struktury task_struct.
           */
        current->state = TASK_ZOMBIE; 
          /* Ustawienie pola state w strukturze task_struct procesu na TASK_ZOMBIE.
           */
        current->exit_code = code;
          /* Ustawienie pola exit_code (kodu wyjścia) w stukturze  task_struct 
           * procesu na parametr wywołania funkcji exit.
           */
        exit_notify()          /* Funkcja exit_notify  (zdefiniowana  w  bieżącym  pliku)  modyfikuje
           * drzewo procesów, wysyła sygnał do procesu macierzystego o swojej śmierci.
           * W przypadku gdy proces jest liderem grupy procesów to wysyła grupie
           * odpowiedni sygnał.
           */
#ifdef DEBUG_PROC_TREE
        audit_ptree()          /* Funkcja audit_ptree  (zdefiniowana w bieżącym pliku)  jest wywoływana 
           * jedynie wtedy, gdy zdefiniowana jest flaga DEBUG_PROC_TREE. Funkcja ta 
           * sprawdza poprawność drzewa procesów. W razie odkrycia błędu wyświetlane
           * są odpowiednie komunikaty na konsoli.
           */
#endif 
        if (current->exec_domain && current->exec_domain->use_count) 
                (*current->exec_domain->use_count)--; 
          /* W przypadku gdy proces korzysta ze struktury exec_domain  (tzn. pole 
           * exec_domain ze sruktury task_struct procesu - wskaźnik do struktury 
           * exex_domain - nie jest NULL), oraz licznik odwołań do tej struktury nie 
           * jest równy 0, wtedy zmniejszany jest ten licznik.
           */
        if (current->binfmt && current->binfmt->use_count) 
                (*current->binfmt->use_count)--; 
          /* W przypadku, gdy proces korzysta ze struktury linux_binfmt (tzn. pole
           * binfmt ze struktury task_struct procesu - wskaznik do struktury 
           * linux_binfmt - nie jest NULL), oraz licznik odwołań do tej struktury 
           * nie jest równy 0, wtedy zmniejszany jest ten licznik.
           */
        schedule(); 
          /* Funkcja schedule() zdefiniowana jest w pliku shed.c. Odpowiada ona za
           * szeregowanie  procesow.  Szerzej omowiona jest w  rozdziale dotyczącym 
           * szeregowania.
           */
/* 
 * In order to get rid of the "volatile function does return" message 
 * I did this little loop that confuses gcc to think do_exit really 
 * is volatile. In fact it's schedule() that is volatile in some 
 * circumstances: when current-state = ZOMBIE, schedule() never 
 * returns. 
 * 
 * In fact the natural way to do all this is to have the label and the 
 * goto right after each other, but I put the fake_volatile label at 
 * the start of the function just in case something /really/ bad 
 * happens, and the schedule returns. This way we can try again. I'm 
 * not paranoid: it's just that everybody is out to get me. 
 */ 

        goto fake_volatile          /* Z  funkcji  schedule()  proces  nie  powinien  wrócić.  Jeżeli  zdażył się
           * jakiś nieprzewidziany wypadek i proces znalazł sie w tym miejscu programu,
           * powinien powtórzyć wykonanie powyższych funkcji.
           */

 Komentował Jacek Drzycimski   
    asmlinkage int sys_exit(int error_code)  {         do_exit((error_code&0xff)<<8);  }  asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru) {         int flag, retval;         struct wait_queue wait = { current, NULL };         struct task_struct *p;         /* Argument funkcji stat_addr określa adres, pod który ma zostać wpisana             przyczyna zakończenia procesu. Wyjątkiem jest stat_addr równe 0             i oznacza, że nie interesuje nas kod zakończenia procesu potomnego.*/          if (stat_addr) {                 /* sprawdź, czy adres podany jako argument stat_addr jest poprawny,                    tzn. czy należy do przestrzeni, w której proces może pisać */                       flag = verify_area(VERIFY_WRITE, stat_addr, sizeof(*stat_addr));                 /* jeżeli argument nie jest poprawny, to zakończ funkcję i zwróć                     błąd EFAULT */                       if (flag)                         return flag;         }                   /* Argument ru oznacza wskaźnik do struktury rusage, w której             pamiętane są informacje o korzystaniu z zasobów takie jak np.            czas w trybie użytkownika i czas w trybie jądra,liczba błędów             braku strony wymagających korzystania z urządzeń wymiany i             i nie wymagających, liczba wymian stron i inne.Podanie jako tego             argumentu wartości NULL oznacza, że nie interesują nas takie informacje*/                          if (ru) {                 /* sprawdź czy argument podany jako argument ru jest poprawny,                    tzn. czy należy do przestrzeni, w której proces może pisać */                       flag = verify_area(VERIFY_WRITE, ru, sizeof(*ru));                 /* jeśli argument niepoprawny zwróć błąd EFAULT */                       if (flag)                         return flag;         }                   /* Argument options określa różne opcje wywołania sys_wait4.Możemy podać             dowolną alternatywę bitową flag:WNOHANG, WUNTRACED, _WCLONE lub 0.            Jeżeli argument options jest inny to zwróć błąd EINVAL */               if (options & ~(WNOHANG|WUNTRACED|__WCLONE))             return -EINVAL;                     /* Proces wkłada siebie do kolejki wait_chldexit.Mógłby to zrobić dopiero            przed zaśnięciem, czyli zmianą swojego stanu na TASK_INTERRUPTIBLE,            ale wymagałoby to wkładania się do tej kolejki po każdym obudzeniu.            Efektywniejsze wydaje się włożenie siebie do kolejki tylko raz, zwłaszcza,            że procesy przebywające w kolejce typu wait_queue mające stan inny niż            TASK_INTERRUPTIBLE lub TASK_UNINTERRUPTIBLE są ignorawane przez takie             operacje na kolejce jak wake_up czy wake_up_ineterruptible.Jeśli jakiś            proces potomny zakończy się w chwili gdy proces macierzysty śpi w kolejce            wait_chlexit, zbudzi śpiący proces wywołując funkcję notify_parent.*/                  add_wait_queue(&current->wait_chldexit,&wait); repeat:          /* zmienna flag określa, czy znaleźliśmy jakiś proces potomny określony przez            argument pid */                 flag=0;          /* przeglądamy wszystkie swoje dzieci w poszukiwaniu procesu spełniajacego             ograniczenie narzucone przez pid oraz będącego w stanie TASK_STOPPED lub            TASK_ZOMBIE */               for (p = current->p_cptr ; p ; p = p->p_osptr) {                 if (pid>0) {                         /* jeżeli argument pid jest większy od zera i identyfikator                            aktualnie rozpatrywanego potomka jest różny od argumentu                             pid to przejdź do następnego potomka */                                if (p->pid != pid)                                 continue;                 } else if (!pid) {                         /* jeżeli argument pid jest równy 0 i aktualnie rozpatrywany                             potomek ma identyfikator grupy różny od identyfikatora                            grupy procesu macierzystego to przejdź do następnego                             potomka */                               if (p->pgrp != current->pgrp)                                 continue;                 } else if (pid != -1) {                         /* jeżeli argument pid jest mniejszy od -1 i identyfikator                            grupy rozpatrywanego potomka jest różny od wartości                             bezwzględnej pid to przejdź do następnego potomka */                                if (p->pgrp != -pid)                                 continue;                 }                 
/* wait for cloned processes iff the __WCLONE flag is set */                        /* opcja _WCLONE wywołania sys_wait4 jest używana do czekanie na                      wątki jądra ( zobacz funkcje pthread.c i manager.c w katalogu                      linuxthread-0.7 ).Przy tworzeniu takiego wątku jako sygnał                      wysyłany po śmierci do procesu macierzystego ustawiana jest                      stała PTHREAD_SIG_RESTART, której wartość równa jest numerowi                     sygnału SIGUSR1.                  */                         /* jeżeli ustawiona jest opcja _WCLONE i exit_signal procesu wynosi                     SIGCHLD przejdź do następnego potomka.Analogicznie jeśli opcja                     ta nie jest ustawiona i exit_signal procesu jest różny od SIGCHLD                     omiń tego potomka */                         if ((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))                         continue;                 /* zaznacz, że znaleziono jakiś proces potomny spełniający ograniczenia                    narzucone przez pid */                       flag = 1;                 switch (p->state) {                         case TASK_STOPPED:                                 /* proces przechodzi w stan TASK_STOPPED po otrzymaniu                                    sygnału: SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU.                                    Wówczas w polu exit_code procesu jest wpisany numer                                    sygnału, który zatrzymał proces.Ojciec sprawdza,                                    czy w polu tym jest O. Jeśli tak oznacza to, że                                     ojciec dowiedział się już o zatrzymaniu procesu                                    potomka i szuka następnego potomka. */                                        if (!p->exit_code)                                         continue;                                  /* w polu exit_code znajduje się numer sygnału, który                                     zatrzymał proces */                                       /* argument options wynoszący WUNTRACED powoduje                                    branie pod uwagę wszystkich procesów potomnych                                    w stanie TASK_STOPPED.Jeżeli opcja ta nie                                     jest ustawiona brane pod uwagę są tylko te                                     procesy potomne w stanie TASK_STOPPED, które                                     ustawiły flagę śledzenia PF_PTRACE, wywołując                                    funkcję ptrace(0) */                                       if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))                                         continue;                                 if (ru != NULL)                                         /* wywołanie funkcji getrusage z parametrem                                             RUSAGE_BOTH powoduje zapisanie do struktury                                            wskazywanej przez wskaźnik ru, dla każdego                                             zasobu łączne wykorzystanie zasobów przez                                            proces i jego procesy potomne */                                                                                          getrusage(p, RUSAGE_BOTH, ru);                                 if (stat_addr)                                         /* do zmiennej wskazywanej przez wskaźnik                                            stat_addr wpisz numer sygnału, który                                             spowodował zatrzymanie procesu potomnego                                            przesunięty o osiem bitów w lewo */                                                put_user((p->exit_code << 8) | 0x7f,                                                 stat_addr);                                 /* ustal wartość pola exit_code w zatrzymanym procesi                                    na 0, aby zaznaczyć, że proces macierzysty                                    dowiedział się już o zatrzymaniu procesu potomnego */                                        p->exit_code = 0;                                 /* do zmiennej oznaczającej wartość zwracaną przez                                    funkcję wpisz identyfikator zatrzymanego procesu                                     potomnego */                                           retval = p->pid;                                 goto end_wait4;                         case TASK_ZOMBIE:                                 /* proces może osiągnać ten stan na dwa sposoby:                                    1) wywołując funkcję exit i wówczas do pola                                       exit_code procesu wpisywany jest argument dla                                       exit przesunięty o 8 bitów w lewo;                                    2) otrzymując sygnał, dla którego czynnością domyślną                                       jest zakończenie procesu,o ile proces nie                                        zmienił obsługi takiego sygnału.Wówczas pole                                       exit_code procesu zawiera numer sygnału, który                                        spowodował śmierć procesu (ewentualnie ze znacznikiem                                       zrzutu pamięci ).                                  */                                        /* uaktualnij czas w trybie jądra i w trybie                                      użytkownika o czasy zużyte przez procesy potomne*/                                          current->cutime += p->utime + p->cutime;                                 current->cstime += p->stime + p->cstime;                                 if (ru != NULL)                                  /* uaktualnij strukturę wskazywaną przez ru */                                               getrusage(p, RUSAGE_BOTH, ru);                                 if (stat_addr)                                         /* do zmiennej wskazywanej przez wskaźnik                                            stat_addr wpisz wartość exit_code                                             zakończonego procesu bądź numer sygnału,                                            gdy proces zakończył się z powodu nadejścia                                             sygnału */                                               put_user(p->exit_code, stat_addr);                                 /* jako wartość zwracaną przez funkcję ustaw                                     identyfikator zakończonego procesu potomnego */                                       retval = p->pid;                                 if (p->p_opptr != p->p_pptr) {                                         /* Zazwyczaj wskaźnik do ojca i orginalnego                                             ojca procesu wskazują na ten sam proces.                                            Jedynie w funkcji sys_ptrace (zobacz plik                                             ptrace.c w katalogu /arch/i386 ) z parametrem                                            request wynoszącym PTRACE_ATTACH zmieniany                                             jest wskaźnik do ojca procesu.Wskaźnik                                            ten ustawiany jest z powrotem na pierwotną                                             wartość przy wywołaniu funkcji sys_ptrace                                             z argumentem request równym PTRACE_DETACH.                                            Zatem jeżeli proces taki zrobił exit, to                                             wywołując notify_parent powiadomił nie tego                                             ojca co powinien. Musi zatem z powrotem                                             powiadomić o swojej śmierci prawdziwego ojca */                                               REMOVE_LINKS(p);                                         p->p_pptr = p->p_opptr;                                         SET_LINKS(p);                                         notify_parent(p, p->exit_signal);                                 } else                                    /* zwolnij pozycję w tablicy procesów po zakończonym                                       procesie */                                                    release(p); #ifdef DEBUG_PROC_TREE                                 audit_ptree(); #endif                                 goto end_wait4;                         default:                                 continue;                 }         }         if (flag) {                 /* nie ma żadnego procesu potomnego, spełniającego ograniczenia                    wynikające z argumentów funkcji, który przestał działać */                                                    /* jeżeli ustawiona jest opcja WNOHANG to zwróć 0 */                       retval = 0;                 if (options & WNOHANG)                         goto end_wait4;                 /* jeżeli w trakcie wykonywania funkcji nadszedł jakiś nieoczekiwany                    sygnał i nie zablokowany to zwróć bład ERASTARTSYS */                         retval = -ERESTARTSYS;                 if (current->signal & ~current->blocked)                         goto end_wait4;                 /* zaśnij dopuszczając przerwania */                        current->state=TAST_INTERRUPTIBLE;                  schedule();                 /* po obudzeniu na skutek nadejścia sygnału przejdź do ponownego                     przeglądania listy dzieci */                       goto repeat;         }         retval = -ECHILD; end_wait4:          /* usuń się z kolejki wait_chldexit i zwróć jako wynik funkcji wartość             zapamiętaną w zmiennaj retval */                  remove_wait_queue(&current->wait_chldexit,&wait);         return retval; }
 Komentowała Małgorzata Górbiel 
  #ifndef __alpha__
/*  * sys_waitpid() remains for compatibility. waitpid() should be  * implemented by calling sys_wait4() from libc.a.  */       asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options) {         return sys_wait4(pid, stat_addr, options, NULL); } #endif