390 likes | 521 Views
This document provides an in-depth examination of process and signal management in Unix systems. It covers the fundamentals of process creation using fork, handling execution with exec, and the various termination methods of processes, including normal and abnormal exit strategies. Additionally, it discusses the SIGCHLD signal, process states (including zombie processes), and memory management for processes, such as shared libraries and memory structures. This comprehensive overview is essential for anyone looking to understand Unix process handling and signal mechanisms.
E N D
Unix System Interface Programming Part 6.3 – Process and Signal Prepared by Xu Zhenya( xzy@buaa.edu.cn ) Draft – Xu Zhenya( 2002/10/01 ) Rev1.0 – Xu Zhenya( 2002/10/10 )
Agenda • 1. Process • 2. Signal
Modeling the Concepts ( Sequence ) Parent Child fork() exec() exit() abort() • The parent ignores/catches SIGCHLD. • The parent = “init” Notification by SIGCHLD Zombie wait(), waitpid(), wait3(), wait4()
Process Context process-specific data structures (page tables, task and mm structs) physical memory kernel code/data/stack kernel VM 0xc0 demand-zero stack %esp process VM Memory mapped region for shared libraries .data .text libc.so brk runtime heap (via malloc) demand-zero uninitialized data (.bss) initialized data (.data) .data program text (.text) .text p forbidden 0
Create a process • fork, vfork & system: textbook, p162 • Notes: • 1. Two generic use for fork: fork & no exec, fork + exec (spawn) • 2. The two main reasons for fork to fail: • 3. Why the child’s PID is returned to the parent process, and a zero to the child? • 4. The child process is a replica of the parent. • 5. Copy-on-write(COW) • 6. The interaction of fork with the I/O functions: stdio functions • 7. File Sharing • 8. Inherited properties from the parent process • 9. Properties Different from the parent process • 10. Fork() Safety in the multithreaded environment
execlp execl execle build argv build argv build argv execvp execv execve Try each PATH prefix Use Environ Running a program • Notes: • In execlp/execvp, when a filename argument is specified, If filename contains a slash, it is taken as a pathname • For execl, execle, and execlp: • char *arg0, char *arg1, …, char *argn, (char *)0 • the total size of the argument list and the environment : sysconf( ARG_MAX )
exec() in the kernel • To run a new program p in the current process using exec(): • free vm_area_struct’s and page tables for old areas. • create new vm_area_struct’s and page tables for new areas. • stack, bss, data, text, shared libs. • text and data backed by ELF executable object file. • bss and stack initialized to zero. • set PC to entry point in .text • The kernel will swap in code and data pages as needed. process-specific data structures (page tables, task and mm structs) physical memory same for each process kernel code/data/stack kernel VM 0xc0 demand-zero stack %esp process VM Memory mapped region for shared libraries .data .text libc.so brk runtime heap (via malloc) demand-zero uninitialized data (.bss) initialized data (.data) .data program text (.text) .text p forbidden 0
Running a program • Semantic of the exec family • File descriptors open in the calling process image: close-on-exec • Directory streams open in the calling process image • Signals set to the default action (SIG_DFL) • Signals set to be caught by the calling process image: SIG_DFL • Signals set to be ignored (SIG_IGN) by the calling process image • In a shell which does not support job control, run “cc xxx &” • any functions previously registered by atexit() • If the set-user-ID mode bit of the new process image • Any shared memory segments attached to the calling process image
Terminate a process (1) • Ways for a process to terminate: • Normal termination & Abnormal termination • Notify its parent how it terminated • Exit status & termination status • signal SIGCHLD: SA_NOCLDWAIT set by signaction • “zombie”: • What happened if the parent terminates before the child? • Explanations for exit(): atexit() and tmpfile()
Terminate a process (2) 53 /* 54 * Exit, flushing stdio buffers if necessary. 55 */ 56 void 57 exit( status ) 58 int status; 59 { 60 register struct atexit *p; 61 register int n; 62 63 #ifdef _THREAD_SAFE 64 extern int _thread_autoinit_dummy_decl; 65 /* Ensure that the auto-initialization routine is linked in: */ 66 _thread_autoinit_dummy_decl = 1; 67 #endif 68 69 for (p = __atexit; p; p = p->next) 70 for (n = p->ind; --n >= 0;) 71 (*p->fns[n])(); 72 if (__cleanup) 73 (*__cleanup)(); 74 _exit(status); 75 }
Terminate a process (3) 49 void 50 abort() 51 { 52 sigset_t mask; 53 54 /* 55 * POSIX requires we flush stdio buffers on abort 56 */ 57 if ( __cleanup ) 58 (*__cleanup)(); 59 60 sigfillset( &mask ); 61 /* 62 * don't block SIGABRT to give any handler a chance; we ignore 63 * any errors -- X311J doesn't allow abort to return anyway. 64 */ 65 sigdelset( &mask, SIGABRT ); 66 #ifdef _THREAD_SAFE 67 _thread_sys_sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); 68 #else 69 (void)sigprocmask( SIG_SETMASK, &mask, (sigset_t *)NULL ); 70 #endif 71 (void)kill( getpid(), SIGABRT ); 72 73 exit( 1 ); 74 }
Cleaning up terminated process (3) • The waitpid() function provides three features that are not provided by the wait function: • waitpid() lets us wait for one particular process (whereas wait() returns the status of any terminated child). • waitpid() provides a nonblocking version of wait(). There are times where we want to fetch a child’s status, but we don’t want to block. For example, receiving the signal SIGCHLD. • waitpid() supports job control(with the WUNTRACED option). #define WIFEXITED(stat) ((int)((stat)&0xFF) == 0) #define WIFSIGNALED(stat) ((int)((stat)&0xFF) > 0 && \ (int)((stat)&0xFF00) == 0) #define WIFSTOPPED(stat) ((int)((stat)&0xFF) == 0177 && \ (int)((stat)&0xFF00) != 0) #define WEXITSTATUS(stat) ((int)(((stat)>>8)&0xFF)) #define WTERMSIG(stat) ((int)((stat)&0x7F)) #define WSTOPSIG(stat) ((int)(((stat)>>8)&0xFF))
/* the init process */ handle( sig_t handler, ... ) { int sig; struct sigaction sa; sigset_t mask_everything; va_list ap; va_start( ap, handler ); sa.sa_handler = handler; sigfillset( &mask_everything ); while ( ( sig = va_arg(ap, int ) ) != NULL ) { sa.sa_mask = mask_everything; /* XXX SA_RESTART? */ sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; sigaction( sig, &sa, (struct sigaction *) 0 ); } va_end( ap ); } Cleaning up terminated process (4)
Signal Overview • Unix are designed around a virtual-machine model, in which system calls are considered to be the parallel of machines’ hardware instruction set. Signals are the software equivalent of traps or interrupt, a signal-handling routine perform the equivalent function of interrupt or trap service routines. • How does the hardware interrupt work? • Interruption source -> 8259 controller->cpu->interrupt vector table->interrupt service routines • How to write a interrupt handler? • Save the context, set the interrupt mask • May change another stack
Signal Overview • => signal generation, • The kernel is the interrupt controller, • The destination process is the CPU: mask register, handler vector, etc
Signal Overview • Signal Generation • Exceptions • Other processes: sigsend() • Terminal interrupts: CTRL+c • Job control: stop and continue, notify their parent process • Quotas • Notifications: aio • Alarms: setitimer(), generated by the callout thread • Actions for signal • Default, IGN, Specific Handler
Signal overview • How does the kernel send a signal to the destination process? • 1. get the signal number and the process control block • 2. check PCB’s signal block mask, and ignored mask • 3. if PCB blocked this signal, then pending • 4. if PCB ignored this signal, then discarded now. • *** in Solaris, semantics of SIGCHLD • 5. if PCB has a signal hander installed for this signal • What is the destination process doing now? • If sleeping for a system call, then wakeup it now. • Put the signal into the process’ signal-q.
Signal overview • Signal handling: • only be taken by the receiving process itself, so the process must be scheduled to run. • The receiving process becomes aware of the signal when the kernel does check for pending signal. The kernel checks at the following times: • Before returning to user mode from a system call or interrupt. • Just before blocking on an interruptible event. • Immediately after waking up from an interruptible event. • How does the kernel prepare the context for the signal handler? • Stack frame: signum, siginfo_t, ucontext *, and a instruction which will re-enter the kernel again • Nested signal handling??? Very Difficult.
Signal overview • Conclusions: • Signals generated by asynchronous events may occur after any instruction in the code path of the process. • We need mechanisms to block the specific signals during running. The most progress in O.S. design • Book, p6-7 • In multithreaded program, signal handling becomes rather difficult there. • In Solaris’s implementation, signal handling is difficult too. • Generation -> notification -> redirect -> delivered • There is a LWP to wait for signals, and then dispatch to a thread.
History about signal handling • unreliable signal: See two classical code: race condition example 1: int sig_int(); /* singal hander */ int main( int argc, char **argv ) { ... signal( SIGINT, sig_int ); /* install my own signal handler */ .... } void sig_int( int signum ) { /* here the kernel sends a signal again, I don’t get it here.. */ signal( SIGINT, sig_int ); /* reinstall the signal handler */ }
History about signal handling Example 2: static volatile sig_atomic_t sig_int_flag; /* set nonzero when signal occurs */ int main( int argc, char **argv ) { int sig_int(); signal( SIGINT, sig_int ); while ( sig_int_flag == 0 ) /* I may lose the signal here */ pause(); /* waiting for a incoming signal */ ... } void sig_int( int signum ) { signal( SIGINT, sig_int ); sig_int_flag = 1; }
History about signal handling • Reliable signal handling • 1. permanent signal handling • 2. masking to protect the critical area: masked = blocked • 3. atomic unblocked and waiting operation: sigsuspend() • 4. for performance, put signal masks into PCB, so we need not wakeup some process now
Issues about signal handling • Restartable system calls • Waiting for a device operation of slow speed: socket, terminal • errno == EINTR • Reentrant functions • Called by both the main program and signal handler • stdio, malloc/free, routines using global variables • I/O operations with timeout • Use longjmp() & setjmp(), but signal interaction • Use select()/poll() • There is no other good means for this requirements now.
Functions about signal (1) • 1. sending signals • kill( pid_t pid, int sig ): pid, see waitpid() • sigsend( idtype_t, id_t, int ): idtype_t, see waitid() • 2. using signal sets • sigset_t: p6-11 • sigprocmask(): p6-12, 13, operating the process’ blocked mask • Remember: restore the old mask: SIG_SETMASK • Catching signals: p6-15, 16, 17, 18, • void ( *signal( int signum, void (*sighandler)(int) ) )(int); • Using sigaction() is the best choice. • Flag SA_SIGINFO • See the examples sigaction.c: use workshop to evaluate siginfo_t
Functions about signal (2) • Catching SIGCHLD • 1. in the main program, invoke wait()/waitpid() • 2. install a SIGCHLD handler, and in which we should use wait • 3. ignore SIGCHLD • 4. use sigaction() and the flag SA_NOCLDWAIT • 5. fork() and fork(): ask the init process reap my sons
Example - SIGCHLD • void childhandler( int signo ) • { … • saveerrno = errno; • /* get all outstanding terminated children */ • for ( ;; ) • { • pid = waitpid( -1, &status, WNOHANG ); • if ( pid == 0 ) { • /* 1. no dead children, but some live ones */ • break; • } else if ( pid == -1 && errno == ECHILD ) { • /* 2. no more children, dead or running */ • break; • } else if (pid == -1) { • /* should not get this */ • perror("waitpid"); • abort(); • } • /* 3. status contains the reaped status of one child */ • /* If desired, save status for main program. */ • } • errno = saveerrno; • return; • } • // sig_atomic_t
Functions about signal (3) • Using alarm signals • alarm(), Textbook, p167: timeout.c • setitimer() & getitimer(): • setitimer(): itimerval.it-value, current value? • !!!! Read man setitimer carefully, specially used in the multithreaded environment • Notes: • Timer set by setitimer() is permanent. • There is only 4 timers for one process. So we need implement our own software timeout mechanism sometime. • Hint: Data-link timer queue management • sigsetjmp() & siglongjmp() & timeout => timed system call
Functions about signal (4) • Waiting for a signal: • sigpause( int signum ); a particular signal • Used in the synchronization between parent & child processes • sigsuspend( sigset_t *set ); /****** important *******/ • Atomic unblock and waiting: reliable signal • Example: sigprocmask( SIG_BLOCK, &newset, &oldmask ); /* critical region */ if ( condition ) /* may while ( flag ), */ sigsuspend( &set ); sigprocmask( SIG_SETMASK, &oldmask, NULL ); • Remember: during waiting for a signal, and at the same time invoking a system call, no reliable ways now.
Exercise • 1. Notes: • The child process's tms structure is cleared: tms_utime , stime , cutime , and cstime are set to 0 (using times(2)). • The child processes resource utilizations are set to 0. The it_value and it_interval values for the ITIMER_REAL timer are reset to 0. • The set of signals pending for the child process is initialized to the empty set. • Suggestions: • Take into more and more careful consideration: interactions • Use the traditional ways to handle signal • Separate the signal handling/critical region from the remainder
Process Relationship (3) • History: System V & BSD => Session( job control, login ) • The init process
System and Process Information • Host information: Book, p3-4, • System variables • File and directory limits • Machine time • Converting time • Time usage