15 213 Recitation 7 31802 Outline fork and

  • Slides: 23
Download presentation
15 -213 Recitation 7 – 3/18/02 Outline • fork and scheduling • Signals •

15 -213 Recitation 7 – 3/18/02 Outline • fork and scheduling • Signals • setjmp/longjmp – Emulating C++ exceptions in C James Wilson e-mail: wilson 2@andrew. cmu. edu Office Hours: Friday 1: 30 – 3: 00 Wean Cluster 520 x

What output does the following have? int main(int argc, char *argv[]) { int i

What output does the following have? int main(int argc, char *argv[]) { int i = 0; else { printf("%dn", i); } return 0; printf("%dn", i); if(fork()) { ++i; printf("%dn", i); } else { printf("%dn", i); } } }

Scheduling Dependent Output • Parents always first produces: – 0, 1, 2, 0, 1

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 =

What’s wrong with the code? int main(int argc, char *argv[]) { int i = 0; else { printf("%dn", i); } return 0; printf("%dn", i); if(fork()) { ++i; printf("%dn", i); } else { printf("%dn", i); } } }

Need to add wait() int main(int argc, char *argv[]) { int i = 0;

Need to add wait() int main(int argc, char *argv[]) { int i = 0; else { printf("%dn", i); } return 0; printf("%dn", i); if(fork()) { ++i; printf("%dn", i); wait(); } else { printf("%dn", i); } wait(); } }

Signals • Software events generated by OS and processes – an OS abstraction for

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. Num. Name 2 SIGINT Default Description Terminate Interrupt from keyboard (cntl-c) Terminate Kill program (cannot override or ignore) 11 SIGSEGV Terminate & Dump Segmentation violation 14 SIGALRM 17 SIGCHLD Terminate Timer signal Ignore Child stopped or terminated 9 SIGKILL

Signals #include <stdio. h> #include <malloc. h> #include <signal. h> // Signal handler //

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, t 1, t 2; signal ( SIGSEGV, sig_handler

Signals int main() { float *f, t 1, t 2; signal ( SIGSEGV, sig_handler ); // set function to handle signals f = (float*) malloc ( 3 * sizeof ( float ) ); f[0] = 1; f[1] = 8; f[2] = 7; t 1 = *(f+0) + *(f+1); printf ( "%fn", t 1 ); } free ( f ); f = NULL; // set f to NULL t 2 = *(f+1) + *(f+2); printf ( "%f", t 2 ); return 0; // accessing it generates SIGSEGV // which is caught by process and // and calls handling function

Signals • Output without signal handling: • %. /signal • 9. 000000 • Segmentation

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

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 What is the output of the following program? #include <setjmp. h>

Setjmp, Longjmp: Example What is the output of the following program? #include <setjmp. h> #include <stdio. h> int a(char *s, jmp_buf env) { int i; i = setjmp(env); printf("Setjmp returned -- %dn", i); printf("s = %sn", s); int main() { jmp_buf env; if(a("Bob", env) != 0) exit(0); b(3, env); return i; } int b(int i, jmp_buf env) { printf("In b: i = %d, Calling longjmpn", i); longjmp(env, i); } return 0; }

Setjmp, Longjmp: Example con’t UNIX> sj Setjmp returned -- 0 s = Bob In

Setjmp, Longjmp: Example con’t UNIX> sj Setjmp returned -- 0 s = Bob In b: I = 3, Calling longjmp Setjmp returned -- 3 s = Bob UNIX> sj Setjmp returned -- 0 s = Bob In b: I = 3, Calling longjmp Setjmp returned -- 3 Segmentation Fault

Setjmp, Longjmp: Example con’t Let’s take a look at the stack to see why

Setjmp, Longjmp: Example con’t Let’s take a look at the stack to see why we’re segfaulting. %ebp old %ebp First, main( ) looks like this. env[8]. . . env[0] %esp %eip --> in main

Setjmp, Longjmp: Example con’t old %ebp env[8]. . . Then main( ) calls a(

Setjmp, Longjmp: Example con’t old %ebp env[8]. . . Then main( ) calls a( ). env[0] s = “Bob” Rtn Addr %ebp old %ebp i %esp %eip --> in a

Setjmp, Longjmp: Example con’t old %ebp env[8] Then main( ) calls a( ). ….

Setjmp, Longjmp: Example con’t old %ebp env[8] Then main( ) calls a( ). …. env[0] s = “Bob” Rtn Addr %ebp old %ebp i %esp %eip --> in a Then a( ) calls setjmp( ). This saves the current state of the registers.

Setjmp, Longjmp: Example con’t old %ebp env[8] …. env[0] Returned to main( ), and

Setjmp, Longjmp: Example con’t old %ebp env[8] …. env[0] Returned to main( ), and main( ) calls b( ). i = 3 Rtn Addr %ebp old %ebp %esp i %eip --> in b

Setjmp, Longjmp: Example con’t old %ebp env[8] …. env[0] s =? ? 3 longjmp(

Setjmp, Longjmp: Example con’t old %ebp env[8] …. env[0] s =? ? 3 longjmp( ) is called, and the regs are restored to their values of when a( ) was called. Rtn Addr %ebp old %ebp %esp i %eip --> in a 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

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

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

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

C++ exception handling example #include <stdio. h> #include <math. h> #include <unistd. h> class

C++ exception handling example #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 %fn", d, sqrt(d)); } int main(void) { try { while(1) sqrt_loop(); } catch (const Range &) { fprintf(stderr, "Number out of rangen"); exit(0); } catch (. . . ) { fprintf(stderr, "Uncaught exceptionn"); exit(1); } return 0; }

Emulating exception handling in C example #include <setjmp. h> <stdio. h> <math. h> <unistd.

Emulating exception handling in C example #include <setjmp. h> <stdio. h> <math. h> <unistd. h> 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 rangen"); exit(0); default: fprintf(stderr, "Uncaught exception %dn", ex_no); exit(ex_no); } return 0; 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 %fn", d, sqrt(d)); } }

Limitations • We can only throw an integer error code to the exception catcher.

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