April 24th Lecture ==================== On process creation: fork (copy everything), then exec() (wipe everything) * waste of time copying vfork() * uses parentt's memory until execve() or exit() * parent suspended until child execs or exits * gotchas: - don't return while in child context -- clears stack frame and parent returns to non-existent frame - use _exit() (system call) rather than exit() (library call) so buffers aren't closed * in general, don't use; to quote Linux manpage: "It is rather unfortunate that Linux revived this spectre from the past." rfork(flags) * another fork call; flags indicates selective sharing of memory between processes At fork(): * allocate and init new process structure * duplicate context of parent process * schedule new process to run In proc.h: ... #define p_startzero p_oppid pid_t p_oppid ... #define p_endzero p_magic #define p_startcopy p_magic u_int p_magic ... #define p_endcopy p_xstat u_short p_xstat ... First block is zeroed upon process creation, second block is copied from parent process, everything else is uninitialized (could be garbage). New PID selection: * early versions: linear search for unused PID * now: track lastpid and pidchecked - use lastpid++ - when lastpid = pidchecked, find new range - lastpid+1 to pidchecked-1 are free - optional: add random "jump" to make PID's unpredictable for security Process completion: * voluntarily (exit(), fall through bottom of main()) * involuntary (signal, exception) * returns exit status to parent; parent gets status through wait(), waitpid(), ... * can also exit() inside the kernel - kills other threads first > any thread entering kernel from userspace will execute thread_exit() > any thread sleeping in the kernel will return immediately with EINTR or EAGAIN which forces them out to userspace and frees kernel resources; on reentry, they'll execute thread_exit() - clean up kernel state > cancel timers > release virtual memory > close open files > handle stopped/traced child processes > move from allprocs list to zombprocs list - record termination status - notify parent * zombies get freed when processed by parent's wait*() Signals: * defined for software+hardware conditions that may arise * may cause default action or invoke a custom signal handler * software equivalent of hardware interrupts * for multithreaded apps, usually one thread handles signals and others mask them out * possible defaul actions: - ignore - terminate all threads - terminate all threads and generate core dump - stop/suspend all threads in the process - resume execution of process * SIGSTOP and SIGKILL are special -- can't be masked, ignored, or caught * signals can also be sent manually from other processes with the kill() system call; can only send signals to processes owned by the same userid (unless you're root) * mask signals => delay processing until they're unmasked * sigsuspend() - causes process to sleep until a signal is received void hand(int signnum) { /* signal handler */ } main () { struct sigaction sa; sa.sa_handler = hand; sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL); ... return(0); } This will cause Ctrl+C to be caught and handled specially. Scheduling an alarm signal x seconds in the future: alarm(x); Add a handler for SIGALRM to handle it (useful for timeouts)