390 likes | 506 Views
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( Relationship ). Modeling the Concepts ( Sequence ). Parent.
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