1 / 83

System Programming

System Programming. Chapter 10. Signals. Objectives: An overview of signals Related function libraries and problems, e.g., reliability & incompatibility. What is a signal? Software interrupts A way of handling asynchronous events Asynchronous means random time occurrences

jagger
Download Presentation

System Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. System Programming Chapter 10

  2. Signals • Objectives: • An overview of signals • Related function libraries and problems, e.g., reliability & incompatibility. • What is a signal? • Software interrupts • A way of handling asynchronous events • Asynchronous means random time occurrences • e.g., SIGABRT, SIGALRM. • 15 signals for Version 7, 31 signals for SVR4 & 4.3+BSD – <signal.h> (# > 0)

  3. What conditions generate signals to a process? • Terminal-generated signals • DELETE key, ^c  SIGINT • Hardware exceptions • SIGFPE ~ divided-by-0, SIGSEGV ~ illegal memory access, etc. • Function kill: one process sends a signal to another process • Owner or superuser • Shell command kill, e.g., kill –9 pid • Software conditions • SIGPIPE ~ reader of the pipe terminated • SIGALRM ~ expiration of an alarm clock

  4. The disposition of a signal (the action) • Tell the kernel what to do when a signal occurs • Ignore signals • SIGKILL and SIGSTOP can not be ignored -> process termination • Some undefined behaviors for ignoring signals, such as SIGFPE. • Catch signals • Provide a signal handler • e.g., calling waitpid() when a process receives SIGCHLD • Apply the default action – Figure 10.1

  5. Signals • How to read Figure 10.1? • ISO C / SUS: signals are defined in ISO C POSX.1 / XSI • Default action: Terminate w/core – not POSIX.1 • Core = memory of image is left in the file named core in the current working directory of the process -> for debugging • When core file is not generated? • Non-owner setuid process • Non-grp-owner setgid process • No access rights at the working dir • File is too big (RLIMIT_CORE) • Description: hardware faults • Implementation-defined faults

  6. Signals • 41 UNIX signals in figure 10.1: let’s go through them! • SIGABRT (abort?) – terminate w/core • Call abort() • SIGALRM (alarm?) – terminate • Call setitimer() • SIGBUS – terminate w/core • Implementation-defined HW fault • SIGCHLD (child?) – ignore • It was sent whenever a process terminates or stops

  7. More Signals • SIGCONT – continue/ignore • Continue a stopped process, e.g., vi – redraw the terminal screen • SIGEMT – terminate w/core • Implementation-defined HW fault • SIGFPE (floating point error?) – terminate w/core • Divid-by-0, floating point overflow, etc. • SIGHUP (hung up?) – terminate • Disconnection is detected by the terminal interface (no daemons)  controlling process of a terminal • Triggering of the rereading of the config files (daemons)

  8. Signals • SIGILL (illegal instruction?) – terminate w/core • Illegal hardware instruction (4.3BSD do it for abort() in the past) • SIGINFO – ignore (BSD4.3+) • Status request for fg processes (^T) • SIGINT (interrupt?) – terminate • DELETE key or ^C • SIGIO – terminate/ignore • Indicate an asynchronous I/O event (SIGIO=SIGPOLL, Terminate on SVR4)

  9. Signals • SIGIOT – terminate w/core • Implementation-defined HW fault (System V did it for abort() in the past) • SIGKILL – terminate • Could not be ignored or caught! • SIGLWP – ignore • Used internally by Solaris thread library • SIGPIPE – terminate • reader of the pipe/socket terminated • SIGPOLL – terminate (SVR4) • A specific event happens on a pollable device. • Use with poll() function

  10. Signals • SIGPROF – terminate • A profiling timer expires (setitimer) • SIGPWR (power) – ignore (SVR4) • System dependent on SVR4 • UPS  init shutdowns the system • SIGQUIT – terminate w/core • ^\ triggers the terminal driver to send the signal to all foreground processes. • SIGSEGV – terminate w/core • Invalid memory access

  11. Signals • SIGSTOP – stop process (like SIGTSTP) • Can not be caught or ignored • SIGSYS – terminate w/core • Invalid system call • SIGTERM – terminate • Termination signal sent by kill command • SIGTRAP – terminate w/core • Implementation-defined HW fault • SIGTSTP – stop process • Terminal stop signal (^Z) to all foreground processes

  12. Signals • SIGTTIN – stop process • Generated when bg processes try to read from the controlling terminal • SIGTTOU – stop process • Generated when bg processes try to write to the controlling terminal • Could be generated by terminal operations, e.g., tcflush • SIGURG – ignore • Urgent condition (e.g., receiving of out-of-band data on a network connection) • out-of-band data = data delivered with high priority (not received in order)

  13. Signals • SIGUSR1 – terminate • User-defined • SIGUSR2 – terminate • User-defined • SIGVTALRM – terminate • A virtual timer expires (setitimer) • SIGWINCH – ignore • Changing of a terminal window size

  14. Signals • SIGXCPU – terminate w/core • Exceed the soft CPU time limit • SIGXFSZ – terminate w/core • Exceed the soft file size (max size of a file created)!

  15. Catching a signal • Specify a function (signal handler) to be called when a signal occurs. #include <signal.h> void (*signal(int signo, void (*func)(int)))(int); • signo – Figure 10.1 • func: SIG_IGN, SIG_DFL, the address of the signal handler/ signal-catching function • SIGKILL & SIGSTOP • Returned value: the address of the previous signal handler.

  16. static void sig_usr(int); /* one handler for both signals */ int main(void) { if (signal(SIGUSR1, sig_usr) == SIG_ERR) err_sys("can't catch SIGUSR1"); if (signal(SIGUSR2, sig_usr) == SIG_ERR) err_sys("can't catch SIGUSR2"); for ( ; ; ) pause(); } static void sig_usr(int signo) /* argument is signal number */ { if (signo == SIGUSR1) printf("received SIGUSR1\n"); else if (signo == SIGUSR2) printf("received SIGUSR2\n"); else err_dump("received signal %d\n", signo); } linux1:~/sys_prog_06/test> fig10.2.exe Suspended linux1:~/sys_prog_06/test> bg [1] fig10.2.exe & linux1:~/sys_prog_06/test> ps PID TTY TIME CMD 1735 pts/7 00:00:00 tcsh 1793 pts/7 00:00:00 fig10.2.exe 1798 pts/7 00:00:00 ps linux1:~/sys_prog_06/test> kill -USR1 1793 received SIGUSR1 linux1:~/sys_prog_06/test> kill 1793

  17. signal() • Remark: • SVR4: signal function – unreliable signal semantics • Unreliable = signals can be lost & a process never knows it • 4.3+BSD: defined in terms of sigaction function – reliable signal semantics • typedef void Sigfunc(int) • Sigfunc *signal(int, sigfunc *); • Constants: #define SIG_ERR (void (*)())-1 #define SIG_DFL (void (*)())0 #define SIG_IGN (void (*)())1

  18. Program Start-up through exec • All signals are set to their default actions unless some are ignored. • The exec functions change the disposition of any signals that are being caught to their default action. • Why? • The address of the signal handler no longer exists in the new program • How about Fork()? • Child inherits the parent’s signal dispositions • The shells automatically set the disposition of the interrupt and quit signals of background processes to “ignored”. • Type ^C: does not terminate the background processes?

  19. In earlier version of UNIX: unreliable Signals Signals could get lost & a process never knows it Action of a signal was reset to its default each time the signal occurred Does this prevent process termination? What if another signal (SIGINT) occurs before the call to signal in the signal handler? Easy to debug? int sig_int(); // my signal handler routine … signal(SIGINT, sig_int); // establish the handler … sig_int() { signal(SIGINT, sig_int); // re-establish handler for next time … // process the signal } Unreliable Signals: Problem 1

  20. Catch a signal or ignore the signal Cannot turn off when the process does not want it to occur Code: prevent the following signals from occurring, but remember if they do occur What happens if signal occurs before after comparison and before pause()? Process sleeps forever int sig_int_flag; main() { int sig_int(); … signal(SIGINT, sig_int); … while (sig_int_flag == 0) pause(); } sig_int() { signal(SIGINT, sig_int); sig_int_flag = 1; } Unreliable Signals: Problem 2

  21. Interrupted System Calls • What if a signal occurs in a system call? • When to deliver the signal to the process? • Immediate: Interrupt the system call & deliver the signal • Wait: till the completion of the system call & then deliver the signal • How to choose between them? • Slow system calls vs. others.

  22. Slow system calls & Slow devices • “Slow” System Calls • Reads from or writes to files that can block the caller forever (e.g., pipes, terminal devices, and network devices) • Opens of files (e.g., terminal device) that block until some conditions occurs. • pause, wait, certain ioctl operations • Some IPC functions • Disk IO system calls are not slow (blocked only temporarily) • Traditional Approach • “Slow” system calls could be interrupted  errno = EINTR • Interactive devices = slow devices

  23. Dealing with Interrupted System Calls • A typical code sequence again: if ((n = read(fd, buff, BUFFSIZE)) < 0) { if (errno == EINTR) goto again; } • Restarting of interrupted system calls – since 4.2BSD • ioctl, read, readv, write, writev, wait, and waitpid. • What if restarting is not wanted? • 4.3BSD allows a process to disable the restarting on a per-signal basis.

  24. Different signal implementations • Scan Figure 10.3 on page 305

  25. Reentrant Functions • When interrupts occur, a function could be called twice at the same time and causes problems. • Example: calling a function from a signal handler (figure 10.5) • Potential Problem: • In the signal handler, we can’t tell where the process was executing when the signal was caught! • Example: may be calling malloc() and in the middle of changing a linked list

  26. static void my_alarm(int signo) { struct passwd *rootptr; printf("in signal handler\n"); if ((rootptr = getpwnam("root")) == NULL) err_sys("getpwnam(root) error"); alarm(1); } int main(void) { struct passwd *ptr; signal(SIGALRM, my_alarm); alarm(1); for ( ; ; ) { if ((ptr = getpwnam("sar")) == NULL) err_sys("getpwnam error"); if (strcmp(ptr->pw_name, "sar") != 0) printf("return value corrupted!, pw_name = %s\n", ptr->pw_name); } } Calling a function (getpwnam) twice

  27. Non-Reentrant Functions /* non-reentrant function */ char *strtoupper(char *string) { static char buffer[MAX_STRING_SIZE]; int index; for (index = 0; string[index]; index++) buffer[index] = toupper(string[index]); buffer[index] = 0 ; return buffer; } Where/what is the problem? overwriting the static buffer in the 2nd call static variable should not be used

  28. Reentrant Functions /* reentrant function (a poor solution) */ char *strtoupper(char *string) { char *buffer; int index; /* error-checking should be performed! */ buffer = malloc(MAX_STRING_SIZE); for (index = 0; string[index]; index++) buffer[index] = toupper(string[index]); buffer[index] = 0 ; return buffer; } Where/what is the problem? malloc() is not reentrant safe.

  29. Better Solution /* reentrant function (a better solution) */ char *strtoupper_r(char *in_str, char *out_str) { int index; for (index = 0; in_str[index]; index++) out_str[index] = toupper(in_str[index]); out_str[index] = 0 ; return out_str; } From “General Programming Concepts: Writing and Debugging Programs”

  30. Some Non-reentrant Functions • The POSIX.1 process environment functions getlogin(), ttyname() (see ISO/IEC 9945:1-1996, §4.2.4 and 4.7.2) • getlogin() returns a pointer to static data whose content is overwritten by each call. • The C-language functions asctime(), ctime(), gmtime() and localtime() (see ISO/IEC 9945:1-1996, §8.3.4-8.3.7) • The POSIX.1 system database functions getgrgid(), getgrnam(), getpwuid() and getpwnam() (see ISO/IEC 9945:1-1996, §9.2.1 a

  31. Reentrant Functions • Figure 10.4 – reentrant functions • *-marked functions – not in POSIX.1, but in SVR4 • Non-Reentrant Functions • Those which use static data structures • Those which call malloc or free • Those which are part of the standard I/O library – usage of global data structures

  32. Reentrant Functions • One Errno variable per thread • Cause problems for reentrance functions? • Example: main calling read() & setting errno; signal occurs, signal handling calling read() overwriting errno. • Solution: save & restore errno inside a handler • longjmp() is not reentrance because of updating a global data structure • How to make it reentrance safe?

  33. SIGCLD vs SIGCHLD • Recall zombie process • A process has terminated, but its parent has not waited for it. • SIGCLD (SV) vs SIGCHLD (BSD, POSIX) • SIGCHLD • Handling is similar to those of other signals. • SIGCLD: Use signal() or sigset() to set its disposition  • The children of the calling process which sets its disposition to SIG_IGN will not generate zombie processes (not for BSD). • wait() returns –1 with errno = ECHILD until all children terminate.

  34. SIGCLD Semantics • When SIGCLD is set to be caught, the kernel checks if there is any child ready to be waited. • If yes, call SIGCLD handler immediately! • Problem in Program 10.3 – Page 309

  35. static void sig_cld(int); int main() { pid_t pid; if (signal(SIGCLD, sig_cld) == SIG_ERR) perror("signal error"); if ((pid = fork()) < 0) { perror("fork error"); } else if (pid == 0) { /* child */ sleep(2); _exit(0); } pause(); /* parent */ exit(0); } static void sig_cld(int signo) /* interrupts pause() */ { pid_t pid; int status; printf("SIGCLD received\n"); if (signal(SIGCLD, sig_cld) == SIG_ERR) /* reestablish handler */ perror("signal error"); if ((pid = wait(&status)) < 0) /* fetch child status */ perror("wait error"); printf("pid = %d\n", pid); } continual string of SIGCHL How to fix this? move signal() after wait()

  36. Reliable Signals Signal generated Signal delivered Pending • A signal is generated when • the event that causes the signal occurs! • A flag is set in the process table. • A signal is delivered when • the action for the signal is taken. • A signal is pending during • the time between its delivery and generation. set flag sig_int() Time Divided by 0

  37. Reliable Signals • A signal is blocked until • the process unblock the signal, or • The corresponding action become “ignore”. • (if the action is either default or a handler) • A process can set which signals are blocked and pending! • sigpending() • Set signal mask for each process – sigprocmask() • 1 bit per signal • More details later

  38. More than one signal is ready for delivery? • Signals are queued when • a blocked signal is generated more than once. • POSIX.1 (but not over many Unix) allows the system to deliver the signal either once or more than once. • Delivery order of signals • No order under POSIX.1, but its Rationale states that signals related to the current process state, e.g., SIGSEGV, should be delivered first.

  39. Send a signal to a process or a process group #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int signo); int raise(int signo); // itself • raise(signo) = kill(getpid(), signo) • Return 0 if okay, -1 on error • pid > 0  to the process • pid == 0  to “all” processes with the same gid of the sender (excluding proc 0, 1, 2) • pid < 0  to “all” processes with gid == |pid| • pid == -1  broadcast signals under SVR4 and 4.3+BSD

  40. kill and raise • Right permissions must be applied! • Superuser is mighty! • Real or effective uid of the sender == that of the receiver • _POSIX_SAVED_IDS  receiver’s saved set-uid is checked up, instead of effective uid • SIGCONT  member of the session • signo == 0 ~ a null signal • Normal error checking is performed by kill() to see if a specific process exists. • kill() returns –1, and errno == ESRCH

  41. Setting a timer (SIGALRM) #include <unistd.h> unsigned int alarm(unsigned int secs); • Set a timer that will expire at a specified time in the future • A previously registered alarm is replaced by the new value – the left seconds is returned! • There could be a delay in calling handler because of processor scheduling delays. • alarm(0) resets (cancels) the alarm. • Default: termination

  42. Suspend a process till a signal is caught #include <unistd.h> int pause(void); • Return if a signal handler is executed. • Returns –1 with errno = EINTR

  43. Implement sleep1() with alarm() & pause(): 3 potential problems static void sig_alrm(int signo) { /* nothing to do, just return to wake up the pause */ } unsigned int sleep1(unsigned int nsecs) { if (signal(SIGALRM, sig_alrm) == SIG_ERR) return(nsecs); alarm(nsecs); /* start the timer */ pause(); /* next caught signal wakes us up */ return(alarm(0)); /* turn off timer, return unslept time */ }

  44. Three potential problems • Any previous alarm? • What should be done if old alarm < nsecs? • What should be done if old alarm > nsecs? • The lost of the previous SIGALRM handler • A race condition • signal between alarm() & pause() -> sleep in pause() forever

  45. Any previous alarm - solution if ( (oldalarm = alarm(nsecs)) > 0) // get the old alarm value if (oldalarm < nsecs) { alarm(0); alarm( oldalarm ); pause(); return(alarm(0));} else { pause(); return( alarm( oldalarm – nsecs) ); }

  46. Previous SIGALRM handler - solution if ( (oldhandler = signal(SIGALRM, sig_alrm)) == SIG_ERR) return (nsecs); // lines 15-16 here. signal(SIGALRM, oldhandler); return( alarm(0) );

  47. race condition - solution • What is problem? • pause() may not be executed after signal() • How to fix it? • The trick is in the signal handler: jump past pause().

  48. race condition – SV2 solution static jmp_buf env_alrm; static void sig_alrm(int signo) { longjmp(env_alrm, 1); } unsigned int sleep2(unsigned int nsecs) { if (signal(SIGALRM, sig_alrm) == SIG_ERR) return(nsecs); if (setjmp(env_alrm) == 0) { alarm(nsecs); /* start the timer */ pause(); /* next caught signal wakes us up */ } return(alarm(0)); /* turn off timer, return unslept time */ }

  49. problem in SV2 solution • What if SIGALRM interrupts another signal handler? • longjmp() will abort that signal handler.

  50. unsigned int sleep2(unsigned int); static void sig_int(int); int main(void) { unsigned int unslept; if (signal(SIGINT, sig_int) == SIG_ERR) err_sys("signal(SIGINT) error"); unslept = sleep2(5); printf("sleep2 returned: %u\n", unslept); exit(0); } static void sig_int(int signo) { int i, j; volatile int k; /* * Tune these loops to run for more than 5 seconds * on whatever system this test program is run. */ printf("\nsig_int starting\n"); for (i = 0; i < 300000; i++) for (j = 0; j < 4000; j++) k += i * j; printf("sig_int finished\n"); // does not finish } static jmp_buf env_alrm; static void sig_alrm(int signo) { longjmp(env_alrm, 1); } unsigned int sleep2(unsigned int nsecs) { if (signal(SIGALRM, sig_alrm) == SIG_ERR) return(nsecs); if (setjmp(env_alrm) == 0) { alarm(nsecs); /* start the timer */ pause(); /* next caught signal wakes us up */ } return(alarm(0)); /* turn off timer, return unslept time */ }

More Related