260 likes | 275 Views
15-213 Recitation 7 – 3/18/02. Outline Fork() Signals setjmp() and longjmp() Exception handling. Mengzhi Wang e-mail: mzwang@cs.cmu.edu Office Hours: Thursday 1:30 – 3:00 Wean Hall 3108. Reminders Lab 5 Due: March 24. What output does the following have?.
E N D
15-213 Recitation 7 – 3/18/02 Outline • Fork() • Signals • setjmp() and longjmp() • Exception handling Mengzhi Wang e-mail: mzwang@cs.cmu.edu Office Hours: Thursday 1:30 – 3:00 Wean Hall 3108 • Reminders • Lab 5 Due: March 24
What output does the following have? int main(int argc, char *argv[]) { int i = 0; printf("%d\n", i); if(fork()) { ++i; printf("%d\n", i); if(fork()) { ++i; printf("%d\n", i); } else { printf("%d\n", i); } } else { printf("%d\n", i); } return 0; }
Scheduling Dependent Output • Parents always first produces: • 0, 1, 2, 0, 1 • Lowest level child always first: • 0, 0, 1, 1, 2 • A good number more versions…
What’s wrong with the code? int main(int argc, char *argv[]) { int i = 0; printf("%d\n", i); if(fork()) { ++i; printf("%d\n", i); if(fork()) { ++i; printf("%d\n", i); } else { printf("%d\n", i); } } else { printf("%d\n", i); } return 0; }
Need to add wait() int main(int argc, char *argv[]) { int i = 0; printf("%d\n", i); if(fork()) { ++i; printf("%d\n", i); if(fork()) { ++i; printf("%d\n", i); } else { printf("%d\n", i); wait(); } } else { printf("%d\n", i); wait(); } return 0; }
Signals • Software events generated by OS and processes • an OS abstraction for exceptions and interrupts • Sent from the kernel or a process to other processes. • Different signals are identified by small integer ID’s • Only information in a signal is its ID and the fact that it arrived.
Signals #include <stdio.h> #include <malloc.h> #include <signal.h> // Signal handler // Called when process receives signal // Just exits static void sig_handler ( int sig ) { printf( "Segmentation fault caught, exiting gracefully.\n" ); exit ( 16 ); }
Signals int main() { float *f, t1, t2; signal ( SIGSEGV, sig_handler ); // set function to handle signals f = (float*) malloc ( 3 * sizeof ( float ) ); f[0] = 1; f[1] = 8; f[2] = 7; t1 = *(f+0) + *(f+1); printf ( "%f\n", t1 ); free ( f ); f = NULL; // set f to NULL t2 = *(f+1) + *(f+2); // accessing it generates SIGSEGV printf ( "%f", t2 ); // which is caught by process and return 0; // and calls handling function }
Signals • Output without signal handling: % ./signal 9.000000 Segmentation fault • Output with signal handling: % ./signal 9.000000 Segmentation fault caught, exiting gracefully. • Provides programmer with method to clean up program (free all used blocks of memory, unlock mutex, etc..) if a kill signal is sent
Setjmp, Longjmp int setjmp(jmp_buf env); void longjmp(jmp_buf env, int val); • Useful functions for dealing with errors and interrupts • setjmp saves its environment (i.e. registers) in env for later use by longjmp • After longjmp completes, program starts after call to setjmp, as if setjmp had returned val.
Setjmp, Longjmp: Example 1 Consider writing a project that will fail if it ever segfaults. Instead just gracefully restart it. How would you go about doing this? Catch the SIGSEGV, and use setjmp/longjmp to jump back to the beginning.
Setjmp, Longjmp: Example 1 con’t switch(opt) { case 1: /* FindPerson(); */ printf("Finding a person\n"); break; case 2: /* AddPerson(); */ printf("Adding a person\n"); break; case 3: /* DeletePerson(); */ printf("Deleting a person\n"); break; case 4: exit(0); default: printf("Invalid Menu option\n"); break; } } /* end while loop */ return; } #include <stdio.h> int main() { int opt; while(1) { printf("Directory Main Menu\n"); printf("-------------------\n"); printf("1. Find Person\n"); printf("2. Add Person\n"); printf("3. Delete Person\n"); printf("4. Exit\n"); printf("Enter your menu option: "); scanf("%d", &opt); printf(“\n”);
Setjmp, Longjmp: Example 1 con’t printf("1. Find Person\n"); printf("2. Add Person\n"); printf("3. Delete Person\n"); printf("4. Exit\n"); printf("Enter your menu option: "); scanf("%d", &opt); switch(opt) { case 1: /* FindPerson(); */ printf(“\n Finding a person\n"); break; case 2: /* AddPerson(); */ printf("\n Adding a person\n"); break; case 3: /* DeletePerson(); */ printf("\n Deleting a person\n"); break; case 4: exit(0); default: printf("\n Invalid Menu option\n"); break; } } /* end while loop */ return; } #include <setjmp.h> #include <signal.h> #include <stdio.h> jmp_buf Env; void handler(int sig) { /* Can do clean up here */ longjmp(Env, 1); } int main() { int opt; signal(SIGSEGV, handler); if(setjmp(Env) != 0) { printf("\n\n Returning to Main Menu \n"); } while(1) { printf("Directory Main Menu\n"); printf("-------------------\n");
Setjmp, Longjmp: Example 2 What is the output of the following program -- sj.c? #include <setjmp.h> #include <stdio.h> int a(char *s, jmp_buf env) { int i; i = setjmp(env); printf("Setjmp returned -- %d\n", i); printf("s = %s\n", s); return i; } int b(int i, jmp_buf env) { printf("In b: i = %d, Calling longjmp\n", i); longjmp(env, i); } int main() { jmp_buf env; if(a("Bob", env) != 0) exit(0); b(3,env); return 0; }
Setjmp, Longjmp: Example 2 con’t Output: ??? UNIX> sj Setjmp returned -- 0 s = Bob In b: I = 3, Calling longjmp Setjmp returned -- 3 Segmentation Fault UNIX> sj Setjmp returned -- 0 s = Bob In b: I = 3, Calling longjmp Setjmp returned -- 3 s = Bob
Setjmp, Longjmp: Example 2 con’t Let’s take a look at the stack to see why we’re segfaulting. old %ebp %ebp First, main( ) looks like this. %eip --> in main env[8] …. env[0] %esp
Setjmp, Longjmp: Example 2 con’t old %ebp env[8] Then main( ) calls a( ). %eip --> in a …. env[0] s = “Bob” Rtn Addr %ebp old %ebp i %esp
Setjmp, Longjmp: Example 2 con’t old %ebp Then main( ) calls a( ). %eip --> in a Then a( ) calls setjmp( ). This saves the current state of the registers. env[8] …. env[0] s = “Bob” Rtn Addr %ebp old %ebp i %esp
Setjmp, Longjmp: Example 2 con’t old %ebp env[8] …. Returned to main( ), and main( ) calls b( ). %eip --> in b env[0] i = 3 Rtn Addr %ebp old %ebp i %esp
Setjmp, Longjmp: Example 2 con’t old %ebp env[8] longjmp( ) is called, and the regs are restored to their values of when a( ) was called. %eip --> in a …. env[0] s =?? 3 Rtn Addr %ebp old %ebp i %esp Why the segfault?? --> the stack is in a bad state. a( ) expects a (char *) instead of the int value 3. This a common bug with setjmp/longjmp ---> You CANNOT return from a function that calls setjmp!
C++ exception handling introduction • A more general and powerful error handling mechanism than return values • Simplified syntax: class A { }; try { do_something(); } catch (const A&) { // handle exception A } • The function do_something can throw/raise exceptions with: throw A();
C++ exception handling introduction • Exceptions can be arbitrary types in C++, including objects with constructors • C++ allows for nested try blocks; exceptions that are uncaught at one level percolate up to the next • Uncaught exceptions terminate the program, but this behavior can be changed • The full syntax and semantics, like most of C++, is complicated • Read Stroustrup, chapter 14
Emulating exception handling in C • setjmp/longjmp mechanism allows for some limited emulation of C++ exceptions • Use setjmp to set up a try block and the exception handlers • Exceptions are thrown using longjmp with an appropriate exception code
#include <stdio.h> #include <math.h> #include <unistd.h> class Range { }; class Close { }; void sqrt_loop(void) { double d; printf("Enter a number between 0 and 100: "); if (scanf("%lf",&d) == EOF) throw Close(); if (d<0 || d>100) throw Range(); printf("Square root of %f is %f\n",d,sqrt(d)); } int main(void) { try { while(1) sqrt_loop(); } catch (const Range &) { fprintf(stderr,"Number out of range\n"); exit(0); } catch (...) { fprintf(stderr,"Uncaught exception\n"); exit(1); } return 0; } C++ exception handling example
#include <setjmp.h> #include <stdio.h> #include <math.h> #include <unistd.h> jmp_buf ex_buf; #define EX_RANGE 1 #define EX_CLOSE 2 void sqrt_loop(void) { double d; printf("Enter a number between 0 and 100: "); if (scanf("%lf",&d) == EOF) longjmp(ex_buf,EX_CLOSE); if (d<0 || d>100) longjmp(ex_buf,EX_RANGE); printf("Square root of %f is %f\n",d,sqrt(d)); } int main(void) { int ex_no; switch (ex_no = setjmp(ex_buf)) { case 0: while(1) sqrt_loop(); break; case EX_RANGE: fprintf(stderr,"Number out of range\n"); exit(0); default: fprintf(stderr,"Uncaught exception %d\n",ex_no); exit(ex_no); } return 0; } Emulating exception handling in C example
Limitations • We can only throw an integer error code to the exception catcher. C++ allows arbitrary objects to be thrown • You can create a pointer for an “exception object,” but this is not type-safe and allocation/disposal is not automatic • Need a stack of jmp_buf structures to emulate an exception stack • Otherwise, storing and restoring the exception jmp_buf gets messy • Uncaught exceptions are not automatically rethrown • Even with a stack, the above implementation is not thread-safe • Each thread needs a separate exception stack