Erstellen und Beenden von ProzessenErzeugen eines ProzessesUnter Linux gibt es drei verschiedene Arten von Prozessen:- idle thread(s) - kernel threads - user task Der erste idle thread wird beim Kompilieren erstellt, dann zur Boot-Zeit für jede CPU jeweils ein Thread. Alle idle threads haben die PID 0. Sie sind die einzigen, die das CLONE_PID Flag in clone() benutzen dürfen.Kernel Threads zeichnen sich dadurch aus, daß sie keinen Speicher im Benutzer Adressbereich haben. Das Feld p->mm ist auf NULL gesetzt. Stattdessen arbeiten sie direkt auf dem vom Kernel allozierten Speicher. Kernel Threads haben alle I/O Privilegien, d.h. sie dürfen direkt auf die Kernelroutinen zugreifen und können vom Scheduler nicht unterbrochen werden. Sie werden mittels dem clone() Systemaufruf erzeugt.Benutzer Tasks werden mit clone() oder fork() Systemaufrufen erzeugt. fork() erzeugt einen Kindprozess, der sich lediglich in der PID und in der PPID vom Elternprozess unterscheidet, Filetable werden ebenfalls nicht vererbt. Dabei benutzt er die copy-on-write Technik, d.h. der Kindprozess benutzt den Speicher des Elternprozesses solange mit, bis eine Schreibaktivität erfolgt. Erst dann wird die entsprechende Speicherseite kopiert und dem Kindprozess exklusiv zur Verfügung gestellt.clone() implementiert unter Linux sogenannte Lightweight Processes. Hiermit ist gemeint, daß das Kind sich den Usermode Adressbereich, sowie die Filetables mit seinem Elternprozess teilt. Die Absicht dieser Systemroutine ist Threads zu erzeugen, die dann gemeinsam auf einem Datenbereich arbeiten. Der clone() Befehl hat, unter anderem, einen Parameter fn, der eine Funktion darstellt, die bei Erzeugen des Threads ausgeführt wird, z.B. ein eigenes C-Programm. Ist diese Funktion beendet, so wird auch der Prozess beendet. Der Rückgabewert des Prozesses ist der Rückgabewert der Funktion.Als dritte Möglichkeit einen Prozess zu erzeugen gibt es noch den POSIX konformen Aufruf vfork . Auch bei diesem teilen sich Eltern- und Kindprozess einen gemeinsamen Speicherraum. Um zu verhindern, daß sich die beiden in die Quere kommen, wird der Elternprozess solange suspendiert bis der Kindprozess beendet wurde oder er wiederum einen Kindprozess erzeugt hat.Bei allen drei Möglichkeiten wird allerdings unter Linux eine Routine aufgerufen, die den neuen Prozess initialisiert: do_fork() .
Wir wollen nun die wesentlichen Schritte betrachten, die in dieser Prozedur ausgeführt werden:
retval = -EPERM; /* * CLONE_PID is only allowed for the initial SMP swapper * calls */ if (clone_flags & CLONE_PID) { if (current->pid) goto fork_out; } CLONE_PID Flag gesetzt ist, falls dies der Fall ist und falls der Prozess kein idle Thread ist (PID == 0) wird als Fehlermeldung EPERM zurückgeliefert.
retval = -ENOMEM; p = alloc_task_struct(); if (!p) goto fork_out; task_struct zu reservieren, falls dieses fehlschlägt wird ENOMEM als Fehlercode zurückgeliefert.
*p = *current; retval = -EAGAIN; if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur) goto bad_fork_free; atomic_inc(&p->user->__count); atomic_inc(&p->user->processes); if (nr_threads >= max_threads) goto bad_fork_cleanup_count; ... p->pid = get_pid(clone_flags); clone_flags generiert.
p->run_list.next = NULL; p->run_list.prev = NULL; run_list wird initialisiert.
... if (clone_flags & CLONE_VFORK) { p->vfork_done = &vfork; init_completion(&vfork); } retval = p->pid; p->tgid = retval; INIT_LIST_HEAD(&p->thread_group); /* Need tasklist lock for parent etc handling! */ write_lock_irq(&tasklist_lock); /* CLONE_PARENT and CLONE_THREAD re-use the old parent */ p->p_opptr = current->p_opptr; p->p_pptr = current->p_pptr; if (!(clone_flags & (CLONE_PARENT | CLONE_THREAD))) { p->p_opptr = current; if (!(p->ptrace & PT_PTRACED)) p->p_pptr = current; } if (clone_flags & CLONE_THREAD) { p->tgid = current->tgid; list_add(&p->thread_group, ¤t->thread_group); } SET_LINKS(p); hash_pid(p); nr_threads++; write_unlock_irq(&tasklist_lock); if (p->ptrace & PT_PTRACED) send_sig(SIGSTOP, p, 1); wake_up_process(p); /* do this last */ ++total_forks; if (clone_flags & CLONE_VFORK) wait_for_completion(&vfork); SET_LINKS in die Prozessliste eingefügt, in die Hashtable eingetragen und zum Schluß aufgeweckt.
Beenden eines ProzessesZum Beenden eines Prozesses gibt des den Systemcallsys_exit , dieser ruft allerdings systemspezifisch do_exit in exit.c auf. Diese Funktion führt folgende Schritte aus:
|