Tuesday, November 27, 2007

Chapter 5 - Lecture

Chapter 5: Threads
􀂄 Overview
􀂄 Multithreading Models
􀂄 Threading Issues
􀂄 Linux Threads
􀂄 Pthreads

Threads Overview
􀂄 A process can contain multiple threads of control
􀂄 A thread (or lightweight process) contains:
􀀩 A thread ID
􀀩 A program counter
􀀩 A register set
􀀩 A stack
􀂄 Threads belonging to the same process share:
􀀩 Code section
􀀩 Data section
􀀩 Open files
􀂄 A process with multiple threads of control can do multiple tasks at a time

Single and Multithreaded Processes

Benefits of Threads
􀂄 Increase responsiveness to user
􀂄 Thread creation faster than process creation
􀂄 Thread context switch faster than process context switch
􀂄 Utilization of multiprocessor architectures: threads belonging to the same process may run in parallel on different processors

User Threads
􀂄 Supported by a user-level thread library
􀂄 Library handles thread creation, scheduling, and management with no support from kernel
􀂄 Advantage: user-level threads are fast to create and manage
􀂄 Drawback: a user thread performing a blocking system call will cause entire process to block
􀂄 Example user thread libraries: POSIX Pthreads, Mach Cthreads, Solaris 2 UI-threads

Kernel Threads
􀂄 Supported by the operating system
􀂄 Kernel handles thread creation, scheduling, and management
􀂄 Advantages:
􀀩 If a thread performs a blocking system call, the kernel can schedule another thread in the application for execution
􀀩 Kernel can schedule threads on different processors in a multiprocessor system
􀂄 Drawback: slower to create and manage than user threads
􀂄 Most modern operating systems support kernel threads

Multithreading Models
􀂄 Many systems support both user level and kernel level threads
􀂄 How to map user level threads to kernel level threads?
􀀩 Many-to-One
􀀩 One-to-One
􀀩 Many-to-Many

Many-to-One
􀂄 Many user-level threads mapped to one kernel thread.
􀂄 Allow the developer to create as many user threads as she wishes
􀂄 Entire process will block if a thread makes a blocking system call
􀂄 Multiple threads can’t run in parallel on multiprocessors
􀂄 Example: green threads (a user-level thread library) in Solaris 2

Many-to-One Model

One-to-One
􀂄 Each user-level thread mapped to a kernel thread
􀂄 Allow another thread to run when a thread blocks
􀂄 Allow multiple threads to run in parallel on multiprocessors
􀂄 Most systems limit the number of threads supported due to overhead of creating kernel level threads
􀂄 Examples: Windows NT/2000, OS/2

One-to-one Model

Many-to-Many Model
􀂄 Many user level threads mapped to a smaller or equal number of kernel threads.
􀂄 Avoid limitations of both many-to-one and one-to-one models
􀀩 Developers can create as many user threads as necessary,
􀀩 Kernel threads corresponding to the user threads can run in parallel on multiprocessors
􀀩 Kernel can schedule another thread for execution if a thread blocks
􀂄 Examples: Solaris 2, Windows NT/2000 with the fiber library

Many-to-Many Model


Threading Issues: fork and exec
􀂄 What if one thread in a program calls fork?
Some UNIX systems have two versions of fork:
􀀩 The new process duplicates all threads
􀀩 The new process duplicates only the thread that invoked fork
􀂄 What if one thread in a program calls exec?
􀀩 The new program replaces the entire process (including all threads)
􀀩 If exec is called immediately after fork, duplicating all threads is unnecessary

Threading Issues: Cancellation
􀂄 Thread cancellation: killing a thread before it has completed
􀂄 Asynchronous cancellation: the target thread is immediately cancelled
􀀩 Can cause problems if the target thread is in the middle of allocating resources, performing I/O, or updating shared data
􀂄 Deferred cancellation: the target thread periodically checks if it should terminate
􀀩 Allow cancellation at safe points
􀀩 Pthreads refer to safe points as cancellation points

Threading Issues: Signal Handling
􀂄 A signal is used to notify a process about the occurrence of an event
􀂄 Synchronous signal: sent from a process to itself
􀀩 E.g., division by 0, illegal memory access
􀂄 Asynchronous signal: sent from external source
􀀩 E.g., timer expire, ctrl-c
􀂄 A signal may be handled by
􀀩 Default signal handler:
􀀗 Every signal has one
􀀗 Run by kernel
􀀩 User-defined signal handler: override the default signal handler

Threading Issues: Signal Handling
􀂄 If a process has several threads, where to deliver a signal?
􀀩 To the thread to which the signal applies (e.g. division by 0)
􀀩 To all the threads in the process (e.g. ctrl-c)
􀀩 To certain threads in the process, i.e., those that are not blocking the signal
􀀗 Typically a signal is delivered only to the first thread that is not blocking the signal
􀀩 Assign a thread to receive and handle all signals for the process

Threading Issues: Thread Pools
􀂄 Motivating example: a web server creates a new thread to service each request
Two concerns:
􀀩 Overhead to create thread; thread discarded after it has completed its work
􀀩 No limit on the number of threads created, may exhaust system resources
􀂄 One solution: thread pools
􀀩 Create a pool of threads at process startup
􀀩 Request comes in: wake up a thread from pool, assign the request to it. If no thread available, server waits until one is free
􀀩 Service completed: thread returns to pool
􀂄 Benefits:
􀀩 Faster to wake up an existing thread than creating a new thread
􀀩 Pool limits the number of threads in system

Linux Threads
􀂄 clone system call used to create kernel level threads
􀀩 Allow child to share resources with parent (e.g. memory space, open files, signal handlers)
􀀗 Parameter ‘flags’ indicates the extent of sharing
􀀩 How to allow sharing?
􀀗 Kernel data structure for a process created by clone contains pointers to the data structures of the parent
process

Pthreads
􀂄 The POSIX standard (IEEE 1003.1c) API for thread creation and synchronization.
􀂄 The standard specifies interface only, implementation is up to developer.
􀂄 Common in UNIX operating systems.
􀂄 On Linux machines
􀀩 #include
􀀩 Link with the pthread library
g++ threads.cc -lpthread

Pthreads: Creating
int pthread_create(pthread_t * thread, pthread_attr_t * attr,
void * (* start_routine) (void *), void * arg);
􀂄 On success
􀀩 A new thread is created with attributes “attr” (NULL=default), id of new thread is placed in “thread”.
􀀩 New thread starts executing function “start_routine” with parameter “arg”
􀀩 New thread executes concurrently with the calling thread
􀀩 New thread runs until it returns from “start_routine” or it calls “pthread_exit”
􀀩 Return 0
􀂄 On failure
􀀩 Return non-zero error code

pthread_create Example
void* my_thread (void *arg)
{
char *msg = (char *) arg;
cout << “Thread says “ << msg <<“\n”;
}
int main()
{
pthread_t t;
char *msg = “Hello World”;
pthread_create(&t, NULL, my_thread, msg);
return 0;
}

Pthreads: Waiting
int pthread_join(pthread_t th, void **thread_return); //wait for
a thread to terminate
􀂄 th: the thread to wait for
􀂄 thread_return: NULL or address of buffer to store return value of th
􀂄 When a thread terminates, its memory resources (thread ID and stack) are not deallocated until another thread performs pthread_join on it.
􀂄 At most one thread can wait for the termination of a given thread.
􀂄 Return 0 on success, non-zero on failure

No comments: