260 likes | 268 Views
Explore the structure of process groups, signal management, and concurrency control in computer systems. Learn about terminal access, reentrancy, and accessing data structures in a concurrent environment.
E N D
CS 3214Introduction to Computer Systems Godmar Back Lecture 14
Announcements • Read Chapter 8 & 11 • Exercise 9 due Oct 19 • Project 3 has been published • See forum – due Oct 30 • Midterm: Tuesday Oct 20 CS 3214 Fall 2009
Part 3 Threads and Processes CS 3214 Fall 2009
Signals – Summary • Universal mechanism to notify a process of events • Internal events (memory access violation, process-internal timers, …) • External events • User-driven: ^C, ^Z • Resulting from other processes: explicit kill(2), or SIGCHLD • Resulting from kernel event: e.g., SIGTTOU, SIGTTIN • Signal handler can change program state before returning • Extremely powerful CS 3214 Fall 2009
Process Groups • Every process belongs to exactly one process group Shell pid=10 pgid=10 Fore- ground job Back- ground job #1 Back- ground job #2 pid=20 pgid=20 pid=32 pgid=32 pid=40 pgid=40 Background process group 32 Background process group 40 Child Child getpgrp() – Return process group of current process setpgid() – Change process group of a process pid=21 pgid=20 pid=22 pgid=20 Foreground process group 20 CS 3214 Fall 2009
Process Groups, cont. • Every process can form a new process group by declaring themselves a leader • setpgid(0, 0) • But, this is not necessary – process groups can also be formed by having a parent place a process in its own or an existing group • E.g., shell places all children belonging to the same pipeline into the same group • Process groups are populated simply by adding processes to them • Restriction: process group must be part of the same “session” – a concept that groups multiple procgroup’s. • See /proc/<pid>/stat to learn pgid of a process CS 3214 Fall 2009
Managing Terminal Access • What if multiple processes wish to read from the terminal? • (default behavior: undefined) • Use process groups • One ‘foreground pgroup’ per terminal • Kernel will suspend (via SIGTTOU/TTIN) any process in background pgroup that attempts to read from terminal • Try: “vim &” • It’s up to shell to manage access to the terminal • Use tcsetpgrp() CS 3214 Fall 2009
Signals & Concurrency • Signal handlers for external events can occur during *anytime* • Unless blocked – must think of signal handler as concurrent flow of control regular program handler signal delivered Signal handler returns sigreturn() user mode kernel mode CS 3214 Fall 2009
Signals & Concurrency • Blocking a signal guarantees that signal handler execution will not occur even when signal is delivered • Will occur as soon as the signal is unblocked unblock(SIGNAL) block(SIGNAL) signal pending handler signal sent Signal handler returns sigreturn() user mode kernel mode CS 3214 Fall 2009
Reentrancy • A function is said to be reentrant if it can be safely called again even while a call is still in progress (i.e., has not returned) • Could be on a regular control flow path, e.g. recursion • Or 2nd call could be from signal handler • Or (discuss this later in more detail) from another thread • Examples of functions that are not reentrant • inet_ntoa(), strtok() – uses private buffer • printf() – takes a lock • You cannot call non-reentrant functions from a signal handler for signal ‘s’ • Unless you prevent the delivery of ‘s’ during calls in your main program via { block(s); ….; unblock(s); } CS 3214 Fall 2009
Async-Signal Safety • ‘async-signal safe functions’ - safe to call from a signal handler • Provide the signal is allowed to occur (i.e., is not blocked) while calls to these functions are in progress – else no issue arises • See list in man 2 signal. Includes waitpid(), etc. • The kicker: printf() is not safe to call in a signal handler • Frequent source of bugs (even in some textbook sample code….!) • Can use ‘snprintf() + write(1, …)’ if needed CS 3214 Fall 2009
Concurrent Accesses To Data Structures • Consider shell maintaining a list of jobs • Main program forks, adds jobs • SIGCHLD handler may reap jobs, perhaps remove jobs from joblist void list_insert (structlist_elem *before, structlist_elem *elem) { elem->prev = before->prev; elem->next = before; before->prev->next = elem; before->prev = elem; } If signal arrives inside the instructions doing the list manipulation, signal handler will see inconsistent – calls to list_insert will lead to havoc CS 3214 Fall 2009
Strategy • Identify data structures shared between signal handler and main program • E.g., everything the signal handler (or functions called from it) accesses • Then protect accesses to those data structures by blocking the signal around the access • Use ‘assert()’ • assert(esh_signal_is_blocked(SIGCHLD)); • Aside: the technique of delaying such interrupts is used inside OS in a very similar way, e.g. when devices trigger interrupts CS 3214 Fall 2009
Nonlocal Jumps: setjmp/longjmp • Powerful (but dangerous) user-level mechanism for transferring control to an arbitrary location. • Controlled to way to break the procedure call/return discipline • Useful for error recovery and signal handling • intsetjmp(jmp_buf j) • Must be called before longjmp • Identifies a return site for a subsequent longjmp. • Called once, returns one or more times • Implementation: • Remember where you are by storing the current register context, stack pointer, and PC value in jmp_buf. • Return 0 CS 3214 Fall 2009
setjmp/longjmp (cont) • void longjmp(jmp_buf j, inti) • Meaning: • return from the setjmp remembered by jump buffer j again... • …this time returningi instead of 0 • Called after setjmp • Called once, but never returns • longjmp Implementation: • Restore register context from jump buffer j • Set %eax(the return value) to i • Jump to the location indicated by the PC stored in jump bufj. CS 3214 Fall 2009
setjmp/longjmp Example #include <setjmp.h> jmp_bufbuf; main() { if (setjmp(buf) != 0) { printf("back in main due to an error\n"); else printf("first time through\n"); p1(); /* p1 calls p2, which calls p3 */ } ... p3() { <error checking code> if (error) longjmp(buf, 1) } CS 3214 Fall 2009
A Program That Restarts Itself When ctrl-c’d #include <stdio.h> #include <signal.h> #include <setjmp.h> sigjmp_buf buf; void handler(int sig) { siglongjmp(buf, 1); } main() { signal(SIGINT, handler); if (!sigsetjmp(buf, 1)) printf("starting\n"); else printf("restarting\n"); while(1) { sleep(1); printf("processing...\n"); } } bass> a.out starting processing... processing... restarting processing... processing... processing... restarting processing... restarting processing... processing... Ctrl-c Ctrl-c Ctrl-c CS 3214 Fall 2009
Limitation of setjmp/longjmp • Longjmp restores stack pointer • Thus activates a new stack frame • Stack frame must still be valid • Consequence: • Can only longjmp “up the stack” to functions that haven’t yet returned when longjmp() is called • repositioning the stack pointer automatically “destroys” intermediate stack frames • But does not call cleanup functions provided in some languages (e.g. C++ destructors or Java ‘finally’ clauses) • Longjmp’ing “down the stack” would “reactivate” already destroyed stack frames • Does not necessarily crash, but leads to unpredictable results • Think of setjmp/longjmp as a low-level mechanism to implement a variant of C++/Java style exceptions CS 3214 Fall 2009
Summary • Signals provide process-level exception handling • Can generate from user programs • Can define effect by declaring signal handler • Some caveats • Very high overhead • >10,000 clock cycles • Only use for exceptional conditions • Don’t have queues (exception: “real-time signals”) • Just one bit for each pending signal type • Nonlocal jumps provide exceptional control flow within process • Within constraints of stack discipline CS 3214 Fall 2009
Unix File Descriptors • Unix provides a file descriptor abstraction • File descriptors are • Small integers that have a local meaning within one process • Can be obtained from kernel • Several functions create them, e.g. open() • Can refer to various kernel objects (not just files) • Can be passed to a standard set of functions: • read, write, close, lseek, (and more) • Can be inherited when a process forks a child CS 3214 Fall 2009
Examples • 0-2 are initially assigned • 0 – stdin • 1 – stdout • 2 – stderr • But this assignment is not fixed – process can change it via syscalls • int fd = open(“file”, O_RDONLY); • int fd = creat(“file”, 0600); CS 3214 Fall 2009
Implementing I/O Redirection • dup and dup2() system call • pipes: pipe(2) CS 3214 Fall 2009
dup2 #include <stdio.h> #include <stdlib.h> // redirect stdout to a file int main(int ac, char *av[]) { int c; intfd = creat(av[1], 0600); if (fd == -1) perror("creat"), exit(-1); if (dup2(fd, 1) == -1) perror("dup2"), exit(-1); while ((c = fgetc(stdin)) != EOF) fputc(c, stdout); } CS 3214 Fall 2009
user view kernel view The Big Picture Process 1 0 1 2 Terminal Device 3 open(“x”) 4 File Descriptor Open File x open(“x”) close(4) Process 2 0 File Descriptor 1 2 3 dup2(3,0) CS 3214 Fall 2009
Reference Counting • Multiple file descriptors may refer to same open file • Within the same process: • fd = open(“file”); fd2 = dup(fd); • Across anchestor processes: • fd = open(“file”); fork(); • But can also open a file multiple times: • fd = open(“file”); fd2 = open(“file”); • In this case, fd and fd2 have different read/write offsets • In both cases, closing fd does not affect fd2 • Reference Counting at 2 Levels: • Kernel keeps track of how many processes refer to a file descriptor –fork() and dup() may add refs • And keeps track of how many file descriptors refer to open file • close(fd) removes reference in current process CS 3214 Fall 2009
Practical Implications • Number of simultaneously open file descriptors per process is limited • 1024 on current Linux, for instance • Must make sure fd’s are closed • Else ‘open()’ may fail • Number space is reused • “double-close” error may inadvertently close a new file descriptor assigned the same number CS 3214 Fall 2009