CS5460: Operating Systems Lecture 5: Processes and Threads (Chapters 3-4)
Context Switch Results lab2-15 gamow home 3.8 us 1.6 us 1.0 us VirtualBox on lab2-25 VirtualBox on gamow VirtualBox on home 170 us 4.6 us 42 us
Important From Last Time Process state machine Interaction with OS invariants Process control block Presence on OS queues Process creation UNIX vs. Windows style Process termination create process New Ready I/O done schedule deschedule Waiting Terminated exit process Running block on timer, I/O, page fault,
What does this program print? #include <stdio.h> #include <unistd.h> int main (void) { int x = 1000; fork(); printf ( %d\n, x++); printf ( %d\n, x++); return 0;
Termination When process dies, OS reclaims its resources On Unix: A process can terminate itself using exit() system call A process can kill another process using the kill() system call #include <signal.h> #include <unistd.h> #include <stdio.h> int main(void) { int cid = fork(); if (cid == 0) { sleep(10); printf( Child exiting!\n ); exit(0); else { printf( Type to kill child\n ); char answer[10]; gets(answer); if (!kill(cid,sigkill)) { printf( Child dead!\n );
pid_t wait(int *status): Parent process use wait() to request notification when a child dies Returns PID of dead child; sets status to be child s exit code Works regardless of whether child dies before/ after call What does this program do? What happens when a process dies? Do we reclaim all resources? #include <signal.h> #include <unistd.h> #include <stdio.h> int main(void) { int ret, cid; cid = fork(); if (cid == 0) { /* CHILD*/ printf( Child exiting.\n ); exit(100); else { /* PARENT */ wait(&ret); printf( Status: %d\n, ret);
Context switch: Switch from one process/thread running on CPU to another When can it happen? Which process/thread should run next? How is it accomplished? When? Preemptive vs non-preemptive scheduling Can occur whenever the kernel (scheduler) gains control of CPU Which? Scheduler decides based on its policies How? Save state of old process Restore state of new process Question: What constitutes the process s state?
Timeline of a Context Switch P 1 Scheduler (P 1 ) Scheduler (P 1 ) P 1 Interrupt or trap One instr later! int/trap handler Save/restore cpu state Interrupt or trap Save/restore cpu state Scheduler (P 2 ) Return from trap/int (P2) P 2 Question: What parts of timeline execute in kernel mode? P N int/trap handler Scheduler (P N ) TIME
Introducing Threads Processes are heavyweight Memory mappings: may be expensive to swap Cache/TLB state: flushing expensive, especially side effects Lots of kernel state Context switching between processes is expensive Threads are lightweight Multiple threads share process state» Same address space» Same open file/socket tables Goal: make context switching between threads cheap Not all OSes support threads efficiently (e.g., older Unixes)» Had to fake it using things like select() and signal()
What Are Threads Good For? 1. Making apps speed up on a multicore (Bad) alternative to using threads: Use multiple single-threaded processes 2. Creating servers with a lot of internal concurrency 3. Letting programmers express somewhat independent computations within an address space 4. Letting programmers create the hardest debugging programs EVER Race conditions Deadlocks Etc.
Threads separate process into two abstractions: Shared resources: address space, kernel resources, privileges Execution state: PC, SP, PSW, other registers, stack, Address space Thread Add multiprogramming Add Multithreading DOS, small embedded systems Older (unthreaded) UNIX Real-time OSes, Java Multihreaded OSes (Win7, OS X, Linux)
Implementing Threads Address space shared by threads Code Data and heap Thread private state: Registers (inc. pc, sp, psw) Stack Key issue: How do you safely access shared state?» Read-only (e.g., code) à easy» Writable à hard Whole section of course dedicated to this» Synchronization» Concurrency control Context switch between threads Save/restore registers (tricky) SP0 SP1 HP PC1 PC0 Stack: Stack: Data: Code:
Kernel Threads vs User Threads Kernel threads: OS supports threads directly Each thread has separate OS state (ready, blocked, ) Kernel schedules threads rather than processes Kernel context switches between threads (and processes)» Thread switch: save/restore registers (inc. PC and SP)» Process switch: above plus MMU/TLB state» Thread switches cheaper than process (~10us vs ~30us) User threads: OS does not directly support threads User-level thread library implements threads Thread context switch performed entirely within user space Cannot get parallelism on a multicore! Solaris operating system uses both
Kernel Threads vs User Threads Advantages of kernel threads Threads can exploit multiprocessors Blocking I/O does not stall non-blocked threads Advantages of user threads Faster to create, manipulate, and synchronize In theory, can tailor scheduling policy to match application need Alternative to threads à event-based programming See Ousterhout s paper: Why Threads Are a Bad Idea Getting concurrency right is hard à events simplify model» Race conditions, deadlocks, livelocks, Downside: events are also difficult, and prevent running on multiple processors
Cooperating Processes vs.threads Cooperating processes can provide benefits: Improved performance by overlapping activities (parallelism) Simpler program structure by separating activities (web server) Fault isolation (e.g., shell, CGI subsystem of web server)» Example: Apache, Google Chrome Problems arise: New failure modes introduced à concurrency control Errors may be harder to debug Cleanup complicated à cannot just destroy single process Questions to consider: How do cooperating processes communicate? Do the processes need to share the same {language, machine, OS?
IPC via Message Passing Option 1: processes exchange messages Need to be able to name other process (e.g., PID) Some form of msgsnd() and msgrecv() supported by OS main() { if (!fork()) producer(); else consumer(); producer() { while (1) { produce item nextp; msgsnd(nextp, conspid); consumer() { while (1) { msgrcv(nextc {, prodpid); consume item nextc;
IPC via Shared Memory Option 2: processes directly access same memory Need mechanism to establish shared mappings (e.g., threads in same process, mmap(), shmget()). main() { buffer = shmget( ); in = out = 0; if (!fork()) producer(); else consumer(); Shared also (not shown) producer() { while (1) { produce item nextp; while ((in+1)mod N)== out); buffer[in] = nextp; in = (in+1) mod N; consumer() { while (1) { while (in == out); nextc = buffer[out]; out = (out+1) mod N; consume item nextc; Which is more efficient, shared memory or message passing? What are possible problems with this solution?
Important From Today Processes and threads: Encapsulate resources and accounting Threads separate execution context from process abstraction Typical address space layout Context switch Kernel threads versus User threads How do threads communicate (within a process)? How to cooperating processes communicate? Synchronization is hard and important Next up: Scheduling How to decide what to run next