Operating Systems: Threads, SMP, and Microkernels
Operating Systems: Threads, SMP, and Microkernels
Lecture 4
Threads, SMP, and Microkernels
Dr. S. S. Shehaby
Processes versus Threads
• Processes have two characteristics treated independently by the
operating system :
• Resource ownership - process includes a virtual address space to hold the
process image
• Scheduling/execution - follows an execution path that may be interleaved
with other processes
• Historically: The unit of dispatching is referred to as a thread or
lightweight process, the unit of resource ownership is referred to as a
process or task
• Multithreading: The ability of an OS to support multiple, concurrent
paths of execution within a single process.
2
Process versus Threads
• Threads operate in the common address space, so in-memory data structures can
be shared without any restrictions (assuming, of course, that proper
synchronization has been put in place). One can pass an address of an in memory
data structure to another thread in order for the thread to be able to access that
data structure. Processes, on the other hand, operate in separate memory
spaces, so they use one of several approaches:
• Serialized data exchange - this can be implemented on top of pipes or other in-memory
exchange data protocols (e.g. connecting via TCP to a server running on localhost). The data
needs to be serialized and de-serialized, making this approach less practical as the volume of
data goes up. Note that this is not precisely sharing, because all the data is copied both ways.
• Shared Memory - this lets you exchange large amounts of data without serialization. This is a
true data sharing, because no copying is done. However, ensuring proper synchronization
becomes a responsibility of your application. Memory-mapped files is another variation of
shared memory, retaining all the advantages and the disadvantages of the approach.
3
fork(), exec(), spawn()
int spawn (char* program, char** arg_list) {
pid_t child_pid; int main ()
{
child_pid = fork ();
/* The argument list to pass to the “ls” command. */
if (child_pid != 0)
char* arg_list[] = {“ls”, “-l”,”/”,NULL} ;
/* This is the parent process. */
/* argv[0], the name of the program.
return child_pid;
The argument list must end with a NULL. */
else { /* Spawn a child process running the “ls” command.
/* Now execute PROGRAM, searching for it in the path. Ignore the returned child process ID. */
*/ spawn (“ls”, arg_list);
execvp (program, arg_list); printf (“done with main program\n”);
/* The execvp function returns only if an error occurs. */ // why printed only once ???
fprintf (stderr, “an error occurred in execvp\n”); return 0;
abort (); }
}
}
4
fork(), exec(), spawn()
int spawn (char* program, char** arg_list) {
pid_t child_pid; int main ()
PARENT
{
child_pid = fork ();
int spawn () { /* The argument list to pass to the “ls” command. */
if (child_pid != 0)
char* arg_list[] = {“ls”, “-l”,”/”,NULL} ;
/* This is the parent process. */ Child (pid=xxx)
child_pid = fork (); /* argv[0], the name of the program.
return child_pid;
// pid=xxx The argument list must end with a NULL. */
else { If (child_process!=0) /*int spawn
Spawn () { process running the “ls” command.
a child
/* Now execute PROGRAM, searchingreturn
for it inchild_pid;
the path. Ignore the returned child process ID. */
*/ child_pid
spawn = fork ();
(“ls”, arg_list);
execvp (program, arg_list); If (child_process!=0)
printf (“done with main program\n”);
/* The execvp function returns only if an error occurs. */ // why printed only once ???
else0;{exec()….
return
fprintf (stderr, “an error occurred in execvp\n”);
} Homework: send to child process
abort ();
several lines, child has to exec()
} sort to sort them, use dup() or
} dup2(), understand redirection
and piping ( cat| sort >file )
5
fork(), exec(), spawn()
int spawn (char* program, char** arg_list) {
pid_t child_pid; int main ()
PARENT
{
child_pid = fork ();
int spawn () { /* The argument list to pass to the “ls” command. */
if (child_pid != 0)
char* arg_list[] = {“ls”, “-l”,”/”,NULL} ;
/* This is the parent process. */ Ls (pid=xxx)
child_pid = fork (); /* argv[0], the name of the program.
return child_pid;
// pid=xxx The argument list must end with a NULL. */
else { If (child_process!=0) /*….. ls –la/child
Spawn … process running the “ls” command.
/* Now execute PROGRAM, searchingreturn
for it inchild_pid;
the path. Ignore the returned child process ID. */
*/ spawn (“ls”, arg_list);
execvp (program, arg_list); printf (“done with main program\n”);
/* The execvp function returns only if an error occurs. */ // why printed only once ???
fprintf (stderr, “an error occurred in execvp\n”); return 0;
abort (); }
}
}
6
IPC
• Pipes: Processes must be on same machine , One process spawns the
other ,Used mostly for filters
• A filter is a process that reads from stdin and writes to stdout
• Sockets : Processes can be on any machine , Processes can be created
independently , Used for clients/servers, distributed systems, etc.
• Other: Shared memory, Named pipelines (FIFO), Semaphores, Mapped
memory, Locks, Signals
• The combination of the following features gives Unix incredible power and
flexibility:
• Standard I/O -> File Descriptor:
• 0=standard input, 1: std output, 2: std error
• I/O redirection
• Pipes
• Shell>> cat < foo | grep bar | sort > save
7
8
PARENT
10
A Thread and Thread Control Block (TCB)
• Has an execution state (running, ready, etc.)
• Saves thread context when not running
• Has an execution stack
• Has some per-thread static storage for local variables
• Has access to the memory and resources of its process
• all threads of a process share this
11
Threads example
#include <pthread.h> int main ()
#include <stdio.h> {
pthread_t thread1_id;
/* Parameters to print_function. */ pthread_t thread2_id;
struct parms struct parms thread1;
struct parms thread2;
{char character; int count;};
/* Create a new thread to print 30,000 ’x’s. */
/* Prints a number of characters to stderr, which is a thread1.character = ’x’;
pointer to a struct char_print_parms. */ thread1.count = 30000;
void* char_print (void* parameters) pthread_create (&thread1_id, NULL, &char_print,
&thread1);
{ /* Create a new thread to print 20,000 o’s. */
/* Cast the cookie pointer to the right type. */ thread2.character = ’o’;
thread2.count = 20000;
struct parms* p = (struct parms*) parameters; int i;
pthread_create (&thread2_id, NULL, &char_print,
for (i = 0; i < p->count; ++i) fputc (p->character, stderr); &thread2);
return NULL; return 0;
}
}
12
Threads and Processes
• All threads in a program must run the same executable. A child process, on the
other hand, may run a different executable by calling an exec function.
• A thread can harm other threads in the same process because threads share the
same virtual memory space and other resources. For instance, a wild memory
write through an uninitialized pointer in one thread can corrupt memory visible
to another thread. A process, cannot do so because each process has a copy of
the program’s memory space.
• Copying memory for a new process adds an additional performance overhead
relative to creating a new thread. However, the copy is performed only when the
memory is changed(Copy on write)
• Threads should be used for programs that need fine-grained parallelism.
Processes should be used for programs that need coarser parallelism.
• Sharing data among threads is trivial because threads share the same memory.
(However, great care must be taken to avoid race conditions)
• Sharing data among processes requires the use of IPC, which makes multiple
processes less likely to suffer from concurrency bugs.
13
Thread States
• States associated with a change in thread state
• Spawn (another thread)
• Block
• Issue: will blocking a thread block other, or all, threads
• Unblock
• Finish (thread)
• Deallocate register context and stacks
14
Kernel-Level Threads
• To make concurrency cheaper, the execution aspect of process is separated out into
threads. As such, the OS now manages threads and processes. All thread operations
are implemented in the kernel and the OS schedules all threads in the system. OS
managed threads are called kernel-level threads or light weight processes.
• NT: Threads
• Solaris: Lightweight processes(LWP).
• In this method, the kernel knows about and manages the threads. No runtime system
is needed in this case. Instead of thread table in each process, the kernel has a thread
table that keeps track of all threads in the system. In addition, the kernel also
maintains the traditional process table to keep track of processes. Operating Systems
kernel provides system call to create and manage threads.
• Advantages:
• Because kernel has full knowledge of all threads, Scheduler may decide to give more time to a
process having large number of threads than process having small number of threads.
• Kernel-level threads are especially good for applications that frequently block.
• Disadvantages:
• The kernel-level threads are slow and inefficient. For instance, threads operations are hundreds of
times slower than that of user-level threads.
• Since kernel must manage and schedule threads as well as processes. It require a full thread
control block (TCB) for each thread to maintain information about threads. As a result there is
15
significant overhead and increased in kernel complexity.
User-Level Threads (ULT)
• Kernel-Level threads make concurrency much cheaper than process because,
much less state to allocate and initialize. However, for fine-grained concurrency,
kernel-level threads still suffer from too much overhead. Thread operations still
require system calls. Ideally, we require thread operations to be as fast as a
procedure call. Kernel-Level threads have to be general to support the needs of
all programmers, languages, runtimes, etc. For such fine grained concurrency we
need still "cheaper" threads.
• To make threads cheap and fast, they need to be implemented at user level.
User-Level threads are managed entirely by the run-time system (user-level
library).The kernel knows nothing about user-level threads and manages them as
if they were single-threaded processes. User-Level threads are small and fast,
each thread is represented by a PC, register, stack, and small thread control block.
Creating a new thread, switching between threads, and synchronizing threads are
done via procedure call. i.e no kernel involvement. User-Level threads are
hundred times faster than Kernel-Level threads.
16
ULT
• Advantages:
• The most obvious advantage of this technique is that a user-level threads package can be implemented on
an Operating System that does not support threads.
• User-level threads does not require modification to operating systems.
• Simple Representation: Each thread is represented simply by a PC, registers, stack and a small control
block, all stored in the user process address space.
• Simple Management: This simply means that creating a thread, switching between threads and
synchronization between threads can all be done without intervention of the kernel.
• Fast and Efficient: Thread switching is not much more expensive than a procedure call.
• Disadvantages:
• User-Level threads are not a perfect solution as with everything else, they are a trade off. Since, User-Level
threads are invisible to the OS they are not well integrated with the OS. As a result, Os can make poor
decisions like scheduling a process with idle threads, blocking a process whose thread initiated an I/O even
though the process has other threads that can run and unscheduling a process with a thread holding a
lock. Solving this requires communication between kernel and user-level thread manager.
• There is a lack of coordination between threads and operating system kernel. Therefore, process as whole
gets one time slice irrespect of whether process has one thread or 1000 threads within. It is up to each
thread to relinquish control to other threads.
• User-level threads requires non-blocking systems call i.e., a multithreaded kernel. Otherwise, entire
process will blocked in the kernel, even if there are runable threads left in the processes. For example, if
one thread causes a page fault, the process blocks.
17
Pthreads implementation: KLT and ULT
ULT KLT
18
Light Weight Process
• LWP is a kernel-supported
user thread.
• It belongs to a user process.
• Independently scheduled.
• Share the address space and
other resources of the
process.
• LWP should be synchronized
on shared data.
• Blocking an LWP is expensive
19
Creation time Synchronization
Time
Latency User thread 52 66
LWP 350 390
Process 1700 200
halted and can only
resume by positive
action from another
process The process has
been terminated
but, for some
reason, still must
have its TCB
Linux
Thread States
A blocked state, in which the
process is waiting for an event,
such as the end of an I/O
operation, the availability of a Another blocked state. A
resource, or a signal from process is waiting directly
another process on hardware conditions and
therefore will not handle
any signals.
20
MultiProcessing Reminder
MISD
?????
21
SMP
• There are multiple processors, each
of which contains its own control
unit, arithmetic-logic unit, and
registers.
• Each processor has access to a
shared main memory and the I/O
devices through some form of
interconnection mechanism; a
shared bus is a common facility.
• The processors can communicate
with each other through memory
(messages and status information
left in shared address spaces).
• It may also be possible for
processors to exchange signals
directly. The memory is often
organized so that multiple
simultaneous accesses to separate
blocks of memory are possible.
22
Microkernel (Reminder)
• only absolutely essential core OS functions should be in the kernel.
• Less essential services and applications are built on the microkernel
and execute in user mode.
23
Microkernel: examples
• MMU: Low-level memory management - Mapping each virtual page to a
physical page frame, most memory management tasks occur in user space
• Communication between processes or threads in a microkernel OS is via
messages:
• A header that identifies the sending and receiving process and
• A body that contains direct data, a pointer to a block of data, or some control
information about the process.
• Interrupt handlers: it is possible to handle hardware interrupts as
messages and to include I/O ports in address spaces, a particular user-level
process is assigned to the interrupt and the kernel maintains the mapping.
24
Thanks
25