/* * linux/kernel/fork.c * * Copyright (C) 1991, 1992 Linus Torvalds */ /* * 'fork.c' contains the help-routines for the 'fork' system call * (see also system_call.s). * Fork is rather simple, once you get the hang of it, but the memory * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' */ /* Autor komentarzy w języku polskim: Michał Smoktunowicz */ #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> #include <linux/malloc.h> #include <linux/ldt.h> #include <linux/smp.h> #include <asm/segment.h> #include <asm/system.h> #include <asm/pgtable.h> int nr_tasks=1; int nr_running=1; unsigned long int total_forks=0; /* Handle normal Linux uptimes. */ int last_pid=0; static inline int find_empty_process(void) { int i; if (nr_tasks >= NR_TASKS - MIN_TASKS_LEFT_FOR_ROOT) { if (current->uid) return -EAGAIN; } if (current->uid) { long max_tasks = current->rlim[RLIMIT_NPROC].rlim_cur; max_tasks--; /* count the new process.. */ if (max_tasks < nr_tasks) { struct task_struct *p; for_each_task (p) { if (p->uid == current->uid) if (--max_tasks < 0) return -EAGAIN; } } } for (i = 0 ; i < NR_TASKS ; i++) { if (!task[i]) return i; } return -EAGAIN; } static int get_pid(unsigned long flags) { struct task_struct *p; if (flags & CLONE_PID) return current->pid; repeat: if ((++last_pid) & 0xffff8000) last_pid=1; for_each_task (p) { if (p->pid == last_pid || p->pgrp == last_pid || p->session == last_pid) goto repeat; } return last_pid; } /* Funkcja wywoływana przez copy_mm * Duplikacja struktur vm_area_struct opisującej pamięć wirtualną procesu * parametr: * mm - struktura mm_struct nowego procesu * wynik: 0 w przypadku sukcesu * -ENOMEM w przypadku błędu */ static inline int dup_mmap(struct mm_struct * mm) { struct vm_area_struct * mpnt, **p, *tmp; mm->mmap = NULL; p = &mm->mmap; /* utworzenie nowej listy obszarów pamięci wirtualnej dla procesu-potomka przez przejście listy aktualnego procesu (pole mmap w strukturze mm_struct) i skopiowanie odpowiednich informacji */ for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { /* utworzenie nowego elementu nowej listy, płytkie skopiowanie pól z listy procesu-rodzica oraz zablokowanie dostępu do tej struktury */ tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); if (!tmp) { exit_mmap(mm); return -ENOMEM; } *tmp = *mpnt; tmp->vm_flags &= ~VM_LOCKED; tmp->vm_mm = mm; tmp->vm_next = NULL; if (tmp->vm_inode) { /* w przypadku gdy obszar pochodzi z odwzorowania i-węzeła zwiększamy tylko licznik odwołań do tego obszaru */ tmp->vm_inode->i_count++; /* insert tmp into the share list, just after mpnt */ /* dodanie obszaru do listy cyklicznej obszarów pamięci dzielonej przez różne procesy */ tmp->vm_next_share->vm_prev_share = tmp; mpnt->vm_next_share = tmp; tmp->vm_prev_share = mpnt; } /* kopiowanie jednego obszaru pamięci wirtualnej. Uaktualnienie katalogu stron w nowym procesie. Skopiowanie wszystkich pozycji pte_t (czyli opisów stron) w katalogu stron dla obszaru tmp Ustawienie flagi "read-olny" dla wszystkich stron, w przypadku obszaru do zapisu i który nie jest dzielony. */ if (copy_page_range(mm, current->mm, tmp)) { if (mpnt->vm_next_share == tmp) { tmp->vm_prev_share->vm_next_share = tmp->vm_next_share; tmp->vm_next_share->vm_prev_share = tmp->vm_prev_share; } kfree(tmp); exit_mmap(mm); return -ENOMEM; } /* odblokowanie obszaru */ if (tmp->vm_ops && tmp->vm_ops->open) tmp->vm_ops->open(tmp); *p = tmp; p = &tmp->vm_next; } /* zbudownie drzewa AVL obiektów vm_area na podstawie listy wolnych obszarów */ build_mmap_avl(mm); flush_tlb_mm(current->mm); return 0; } /* funkcja wywoływana przez do_fork * * Kopiowanie lub klonowanie struktur opisujących pamięć wirtualną. * parametry: * clone_flags - jeśli ustawiona flaga CLONE_VM - tylko klonowanie, czyli * zwiększenie ilości odwołań do struktury mm_struct * tsk - stuktura opisująca proces-potomek. * wynik: 0 w przypadku sukcesu * -ENOMEM w przypadku niepowodzenia. */ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk) { if (!(clone_flags & CLONE_VM)) { /* gdy nie jest ustawiona flaga CLONE_VM: tworzona jest nowa struktura mm_struct dla procesu opisanego przez tsk - czyli dla potomka i są ustawione odpowiednie pola na domyślną wartość */ struct mm_struct * mm = kmalloc(sizeof(*tsk->mm), GFP_KERNEL); if (!mm) return -ENOMEM; *mm = *current->mm; mm->count = 1; mm->def_flags = 0; mm->mmap_sem = MUTEX; tsk->mm = mm; tsk->min_flt = tsk->maj_flt = 0; tsk->cmin_flt = tsk->cmaj_flt = 0; tsk->nswap = tsk->cnswap = 0; /* utworzenie nowego katalogu stron procesu*/ if (new_page_tables(tsk)) { tsk->mm = NULL; exit_mmap(mm); goto free_mm; } /* skopiowanie struktur vm_area_struct w przypadku błędu: zwolnienie katalogu stron procesu */ if (dup_mmap(mm)) { tsk->mm = NULL; exit_mmap(mm); free_page_tables(mm); free_mm: kfree(mm); return -ENOMEM; } return 0; } /* gdy flaga CLONE_VM jest ustawiona: ustawienie katalogu stron procesu na katalog rodzica, zwiększenie licznika odwołań do struktury mm_struct - bedzie dzielona przez dwa procesy */ SET_PAGE_DIR(tsk, current->mm->pgd); current->mm->count++; return 0; } static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) { if (clone_flags & CLONE_FS) { current->fs->count++; return 0; } tsk->fs = kmalloc(sizeof(*tsk->fs), GFP_KERNEL); if (!tsk->fs) return -1; tsk->fs->count = 1; tsk->fs->umask = current->fs->umask; if ((tsk->fs->root = current->fs->root)) tsk->fs->root->i_count++; if ((tsk->fs->pwd = current->fs->pwd)) tsk->fs->pwd->i_count++; return 0; } static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk) { int i; struct files_struct *oldf, *newf; struct file **old_fds, **new_fds; oldf = current->files; if (clone_flags & CLONE_FILES) { oldf->count++; return 0; } newf = kmalloc(sizeof(*newf), GFP_KERNEL); tsk->files = newf; if (!newf) return -1; newf->count = 1; newf->close_on_exec = oldf->close_on_exec; newf->open_fds = oldf->open_fds; old_fds = oldf->fd; new_fds = newf->fd; for (i = NR_OPEN; i != 0; i--) { struct file * f = *old_fds; old_fds++; *new_fds = f; new_fds++; if (f) f->f_count++; } return 0; } static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) { if (clone_flags & CLONE_SIGHAND) { current->sig->count++; return 0; } tsk->sig = kmalloc(sizeof(*tsk->sig), GFP_KERNEL); if (!tsk->sig) return -1; tsk->sig->count = 1; memcpy(tsk->sig->action, current->sig->action, sizeof(tsk->sig->action)); return 0; } /* * Ok, this is the main fork-routine. It copies the system process * information (task[nr]) and sets up the necessary registers. It * also copies the data segment in its entirety. */ /* * Głowna funkcja wywolywana przez sys_fork z pliku process.c * parametry: * clone_flags - co ma byc kopiowane a co tylko klonowane w nowym procesie, * oraz jaki sygnał ma być przekazany w przypadku śmierci potomka. * usp - gdy wskaźnik ten jest różny od NULL, to jest używany jako początkowy * wskaźnik stosu potomka * regs - rejestry procesora * wynik: * identyfikator nowego procesu w procesie-rodzicu, * 0 w procesie potomnym. * kod błędu w przypadku niepowodzenia. */ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) { int nr; int error = -ENOMEM; unsigned long new_stack; struct task_struct *p; /* tu odbywają się czynności przygotowujące struktury dla nowego procesu; nowa struktura task_struct która będzie dołączona do tablicy procesów, nowy stos kontekstu jądra. */ p = (struct task_struct *) kmalloc(sizeof(*p), GFP_KERNEL); if (!p) goto bad_fork; new_stack = alloc_kernel_stack(); if (!new_stack) goto bad_fork_free_p; error = -EAGAIN; nr = find_empty_process(); if (nr < 0) goto bad_fork_free_stack; *p = *current; if (p->exec_domain && p->exec_domain->use_count) (*p->exec_domain->use_count)++; if (p->binfmt && p->binfmt->use_count) (*p->binfmt->use_count)++; p->did_exec = 0; p->swappable = 0; p->kernel_stack_page = new_stack; *(unsigned long *) p->kernel_stack_page = STACK_MAGIC; p->state = TASK_UNINTERRUPTIBLE; p->flags &= ~(PF_PTRACED|PF_TRACESYS|PF_SUPERPRIV); p->flags |= PF_FORKNOEXEC; p->pid = get_pid(clone_flags); p->next_run = NULL; p->prev_run = NULL; p->p_pptr = p->p_opptr = current; p->p_cptr = NULL; init_waitqueue(&p->wait_chldexit); p->signal = 0; p->it_real_value = p->it_virt_value = p->it_prof_value = 0; p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0; init_timer(&p->real_timer); p->real_timer.data = (unsigned long) p; p->leader = 0; /* session leadership doesn't inherit */ p->tty_old_pgrp = 0; p->utime = p->stime = 0; p->cutime = p->cstime = 0; #ifdef __SMP__ p->processor = NO_PROC_ID; p->lock_depth = 1; #endif p->start_time = jiffies; task[nr] = p; SET_LINKS(p); nr_tasks++; error = -ENOMEM; /* copy all the process information */ /* kopiowanie lub klonowanie następujących danych: informacji o otwartych plikach, informacji o systemie plików, informacji o procedurach obsługi sygnałów, struktur pamięci wirtualnej. */ if (copy_files(clone_flags, p)) goto bad_fork_cleanup; if (copy_fs(clone_flags, p)) goto bad_fork_cleanup_files; if (copy_sighand(clone_flags, p)) goto bad_fork_cleanup_fs; if (copy_mm(clone_flags, p)) goto bad_fork_cleanup_sighand; copy_thread(nr, clone_flags, usp, p, regs); p->semundo = NULL; /* ok, now we should be set up.. */ p->swappable = 1; p->exit_signal = clone_flags & CSIGNAL; p->counter = (current->counter >>= 1); wake_up_process(p); /* do this last, just in case */ ++total_forks; return p->pid; bad_fork_cleanup_sighand: exit_sighand(p); bad_fork_cleanup_fs: exit_fs(p); bad_fork_cleanup_files: exit_files(p); bad_fork_cleanup: if (p->exec_domain && p->exec_domain->use_count) (*p->exec_domain->use_count)--; if (p->binfmt && p->binfmt->use_count) (*p->binfmt->use_count)--; task[nr] = NULL; REMOVE_LINKS(p); nr_tasks--; bad_fork_free_stack: free_kernel_stack(new_stack); bad_fork_free_p: kfree(p); bad_fork: return error; }