UNIS Template
Processes and Threads
Shuaiwen Leon Song
USYD Future System Architecture Lab (FSA)
https://shuaiwen-leon-song.github.io/
https://shuaiwen-leon-song.github.io/
• To understand what a process is
• To learn the basics of exceptional control flow
• To learn how to control child processes
• To learn about other process related
functions
• To learn how to load and execute programs
• To learn about signals
• To learn about pipes
Objectives
How to run programs?
› How does the operating system run programs?
3
How to run programs?
› How does the operating system run programs?
› Need abstraction of executing program
4
How to run programs?
› How does the operating system run programs?
› Need abstraction of executing program
› process = memory state + machine state
5
The OS must handle multiple processes
in execution at any given time…
How do we do this?
Multi-Processing
What is a process?
› An independent logical control flow that provides the illusion that our
program has exclusive use of the processor
› A private address space that provides the illusion that our program has
exclusive use of the memory system
7
How do we control process?
›We need to identify process:
› Get process id use getpid() function
8
#include
#include
int main(int argc, char const *argv[])
{
printf(“pid = %d\n”, getpid());
return 0;
}
https://linux.die.net/man/3/getpid
How do we control process?
›Need a way to create and destroy processes
9
A process is in one of three states
Running Stopped Terminated
Create & Terminate Processes
A process is in one of three states
Running Stopped Terminated
In the running state it is either executing on the CPU or
is waiting to be executed and eventually scheduled by the kernel
Create & Terminate Processes
A process is in one of three states
Running Stopped Terminated
In the stopped state the execution of the process is suspended
and will not be scheduled. A process stops if it receives a
SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU signal
(more on signals later)
Create & Terminate Processes
More detailed POSIX signals refer to : https://en.wikipedia.org/wiki/Signal_(IPC)
A process is in one of three states
Running Stopped Terminated
In the terminated state the process is stopped permanently. A
process becomes terminated for one of three reasons: (1)
receiving a signal to terminate, (2) returning from main, or (3)
calling the exit function.
Create & Terminate Processes
A process is in one of three states
Running Stopped Terminated
So, how do we create a running process?
Create & Terminate Processes
We use the fork() function to
create a new process.
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
Create & Terminate Processes
We use the fork() function to
create a new process.
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
fork() creates a new process.
It is almost identical to the parent – but, has a different PID.
It gets a copy of the parent’s virtualaddress space,
both heap and stack.
Create & Terminate Processes
We use the fork() function to
create a new process.
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
fork() returns different results to the parent and child:
Parent gets the pid of the new child
Child gets 0 (so it can easily know it’s the child) -‐
if it needs its own pid it can simply call getpid to obtain it
Create & Terminate Processes
We use the fork() function to
create a new process.
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
Parent
Child
Create & Terminate Processes
We use the fork() function to
create a new process.
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
Parent
Child
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
Create & Terminate Processes
We use the fork() function to
create a new process.
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
Parent
Child
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
Create & Terminate Processes
We use the fork() function to
create a new process.
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
pid_t pid = fork();
if (pid == 0) {
printf(“hello from child\n”);
} else {
printf(“hello from parent\n”);
}
Create & Terminate Processes
After forking, parent and child process share file descriptors but not virtual memory (although
it gets a copy of the parent’s virtualaddress space, both heap and stack. )
Sharing Between Two Processes
› Can two processes share the same shared memory segment?
› Yes and no. Typically with modern operating systems, when another process
is forked from the first, they share the same memory space with a copy-on-
write set on all pages. Any updates made to any of the read-write memory pages
causes a copy to be made for the page so there will be two copies and the memory
page will no longer be shared between the parent and child process. This means
that only read-only pages or pages that have not been written to will be shared.
› If a process has not been forked from another then they typically do not share any
memory. One exception is if you are running two instances of the same program
then they may share code and maybe even static data segments but no other
pages will be shared.
22
http://linux.die.net/man/2/fork
http://en.wikipedia.org/wiki/Copy-on-write
http://en.wikipedia.org/wiki/Data_segment#Program_memory
What does this print out?
void main()
{
printf(“L0\n”);
fork();
printf(“L1\n”);
fork();
printf(“Bye\n”);
}
L1 Bye
Bye
L0 L1 Bye
Bye
Create & Terminate Processes
What does this print out?
void main()
{
printf(“L0\n”);
fork();
printf(“L1\n”);
fork();
printf(“L2\n”);
fork();
printf(“Bye\n”);
}
Bye
L2 Bye
Bye
L1 L2 Bye
Bye
L2 Bye
Bye
L0 L1 L2 Bye
Does it always print inorder?
Create & Terminate Processes
What does this print out?
void main()
{
printf(“L0\n”);
if (fork() != 0) {
printf(“L1\n”);
if (fork() != 0) {
printf(“L2\n”);
fork();
}
}
printf(“Bye\n”);
}
How many linesof output will it produce?
A) 4
B) 5
C) 6
D) 7
E) 8
Create & Terminate Processes
What does this print out?
void main()
{
printf(“L0\n”);
if (fork() != 0) {
printf(“L1\n”);
if (fork() != 0) {
printf(“L2\n”);
fork();
}
}
printf(“Bye\n”);
}
Bye
Bye
Bye
L0 L1 L2 Bye
Create & Terminate Processes
What happens here?
void main()
{
if (fork() == 0) {
/* Child */
printf(“Terminating Child, PID = %d\n”,
getpid());
exit(0);
} else {
printf(“Running Parent, PID =
%d\n”, getpid());
while (1)
; /* Infinite loop */
}
}
Zombie!
Create & Terminate Processes
Linux
donglin@ubuntu:~/ctest$ ./main
Running Parent, PID = 24753
Terminating Child, PID = 24754
donglin@ubuntu:~/ctest$ kill -9 24754
donglin@ubuntu:~/ctest$ ps
PID TTY TIME CMD
24721 pts/4 00:00:00 bash
24753 pts/4 00:00:04 main
24754 pts/4 00:00:00 main
24755 pts/4 00:00:00 ps
Create & Terminate Processes
Linux
Zombie process cannot be killed, kill the
parent process instead.
Create & Terminate Processes
What about this one?
Zombie?
void main()
{
if (fork() == 0) {
/* Child */
printf(“Running Child, PID =
%d\n”, getpid());
while (1)
; /* Infinite loop */
} else {
printf(“Terminating Parent, PID = %d\n”,
getpid());
exit(0);
}
}
Create & Terminate Processes
donglin@ubuntu:~/ctest$ ./main
Terminating Parent, PID = 24950
Running Child, PID = 24951
donglin@ubuntu :~/ctest$ ps -f
UID PID PPID C STIME TTY TIME CMD
donglin 24911 24910 0 13:39 pts/5 00:00:00 -bash
donglin 24951 1 99 13:40 pts/5 00:00:27 ./main
donglin 24957 24911 0 13:40 pts/5 00:00:00 ps -f
Create & Terminate Processes
Note: PPID=1, this is not a zombie (first
process started on Linux at boot)
Need a way to get rid of(kill)
the zombies!
This is called
reaping!
How do we control processes?
So, how do we “reap” a child process
programmatically?
wait()
waitpid()
Zombie?
Create & Terminate Processes
kill -s SIGCHLD pid
int wait(int *child_status)
void main()
{
pid_t pid[N];
int i;
int child_status;
for (i = 0; i < N; i++) { if ((pid[i] = fork()) == 0) { // Child exit(100+i); } } for (i = 0; i < N; i++) { pid_t wpid = wait(&child_status); if (WIFEXITED(child_status)) { printf("Child %d terminated with exit status %d\n", wpid, WEXITSTATUS(child_status)); } else { printf("Child %d terminated abnormally\n", wpid); } } } fork a childprocesses Create & Terminate Processes int wait(int *child_status) void main() { pid_t pid[N]; int i; int child_status; for (i = 0; i < N; i++) { if ((pid[i] = fork()) == 0) { // Child exit(100+i); } } for (i = 0; i < N; i++) { pid_t wpid = wait(&child_status); if (WIFEXITED(child_status)) { printf("Child %d terminated with exit status %d\n", wpid, WEXITSTATUS(child_status)); } else { printf("Child %d terminated abnormally\n", wpid); } } } wait for each toterminate Create & Terminate Processes int wait(int *child_status) void main() { pid_t pid[N]; int i; int child_status; for (i = 0; i < N; i++) { if ((pid[i] = fork()) == 0) { // Child exit(100+i); } } for (i = 0; i < N; i++) { pid_t wpid = wait(&child_status); if (WIFEXITED(child_status)) { printf("Child %d terminated with exit status %d\n", wpid, WEXITSTATUS(child_status)); } else { printf("Child %d terminated abnormally\n", wpid); } } } wait for each toterminate Get Info on Status Create & Terminate Processes int waitpid(pid, &status, options void main() { pid_t pid[N]; int i; int child_status; for (i = 0; i < N; i++) { if ((pid[i] = fork()) == 0) { // Child exit(100+i); } } for (i = N-1; i >= 0; i–) {
pid_t wpid = waitpid(pid[i], &child_status, 0);
if (WIFEXITED(child_status)) {
printf(“Child %d terminated with exit status %d\n”,
wpid, WEXITSTATUS(child_status));
} else {
printf(“Child %d terminated abnormally\n”, wpid);
} } }
wait for specific child to terminate
Create & Terminate Processes
int waitpid(-1, &status, 0)
is the same as…
int wait(&status)
Create & Terminate Processes
void main()
{
if (fork() == 0) {
printf(“a”);
}
else {
printf(“b”);
waitpid(-1, NULL, 0);
}
printf(“c”);
exit(0);
}
What is the output of the programon
the left?
A. acbc
B. bcac
C. abcc
D. bacc
E. A or C orD
Activity
void main()
{
if (fork() == 0) {
printf(“a”);
}
else {
printf(“b”);
waitpid(-1, NULL, 0);
}
printf(“c”);
exit(0);
}
What is the output of the programon
the left?
A. acbc
B. bcac
C. abcc
D. bacc
E. A orC or D
Activity
Putting a process to sleep: zzzzz…
› unsigned int sleep(unsigned int seconds);
41
#include
void main()
{
int amt = sleep(5);
}
Returns the number of seconds left to
sleep if a signal woke up the
process.
Returns 0 if time has elapsed.
Putting a process to sleep: zzzzz…
› int pause(void);
42
#include
void main()
{
int amt = pause();
}
pause() returns only when a signal was caught and
the signal-catching function returned. In this
case, pause() returns -1
Loading and Running Programs
43
What does a shell program do?
A shell program is a special kind of program whose
primary responsibility is to run otherprograms.
ls
ps
emacs
vim
cd
int execve(const char* filename,
const
const
char*
char*
argv[],
envp[]);
Loading and Running Programs
In computing, exec is a functionality of an operating system that runs an executable
file in the context of an already existing process, replacing the previous executable.
This act is also referred to as an overlay. It is especially important in Unix-
like systems, although exists elsewhere. As a new process is not created,
the process identifier (PID) does not change, but the machine code, data, heap,
and stack of the process are replaced by those of the new program.
https://en.wikipedia.org/wiki/Exec_(system_call)
https://en.wikipedia.org/wiki/Computing
https://en.wikipedia.org/wiki/Operating_system
https://en.wikipedia.org/wiki/Executable_file
https://en.wikipedia.org/wiki/Process_(computing)
https://en.wikipedia.org/wiki/Unix-like
https://en.wikipedia.org/wiki/Process_identifier
https://en.wikipedia.org/wiki/Machine_code
https://en.wikipedia.org/wiki/Data_(computing)
https://en.wikipedia.org/wiki/Heap_(programming)
https://en.wikipedia.org/wiki/Run-time_stack
We know what these are…
Loading and Running Programs
int execve(const char* filename,
const
const
char*
char*
argv[],
envp[]);
We know what these are…
But, what is this?
Loading and Running Programs
int execve(const char* filename,
const
const
char*
char*
argv[],
envp[]);
Every program runs in an “environment”. The environment
is customized using environment variables: PATH, EDITOR, …
envp[0]
envp[1]
…
envp[n-‐1]
NULL
envp “USER=richards”
“SHELL=/bin/bash”
“EDITOR=emacs”
Loading and Running Programs
int execve(const char* filename,
const
const
char*
char*
argv[],
envp[]);
It turns out that the more general form for main is:
int main(int argc,
const char* argv[],
const char* envp[]);
Loading and Running Programs
int execve(const char* filename,
const
const
char*
char*
argv[],
envp[]);
Loading and Running Programs
49
› Where are argv and envp in memory when a process executes?
50
Loading and Running Programs
Where are argvand envp
In memory when a process
executes?
Null-‐terminated environment variable strings
Null-‐terminated command-‐line argument strings
unused space
envp[n] == NULL
envp[n-‐1]
…
envp[0]
argv[argc] ==NULL
argv[argc-‐1]
…
argv[0]
(Dynamic Linker Variables)
envp
argv
argc
Stack frame for main
Bottomof stack
Top of stack
51
Loading and Running Programs
Where are argvand envp
In memory when a process
executes?
So, how do we accessthe
environment variables?
Null-‐terminated environment variable strings
Null-‐terminated command-‐line argument strings
unused space
envp[n] == NULL
envp[n-‐1]
…
envp[0]
argv[argc] ==NULL
argv[argc-‐1]
…
argv[0]
(Dynamic Linker Variables)
envp
argv
argc
Stack frame for main
Bottomof stack
Top of stack
int* getenv(const char* name);
Returns pointer to value if there is an entry of the form
“name=value”, and NULL if there is not
int setenv(const char* name,
const char* newval,
int overwrite);
Returns 0 on success, -1 on error.
void unsetenv(const char* name);
Removing the name of the environmental variable. Upon
successful completion, zero shall be returned. Otherwise, -1
shall be returned, errno set to indicate the error, and the
environment shall be unchanged.
Loading and Running Programs
How do we communicate to processes?
We send them messages
called signals
A signal is an event of
some type that has
occurred in the system.
It allows the OS to
communicate to aprocess
AND
user processes to
communicate to eachother
Signals
How do we communicate to processes?
We send them messages
called signals
A signal is an event of
some type that has
occurred in the system.
It allows the OS to
communicate to aprocess
AND
user processes to
communicate to eachother
If a process tries to divide by 0:
OS sends it a SIGFPE signal
If a process executesan
illegal instruction:
OS sends it a SIGILL signal
If a process makes illegal memory reference:
OS sends it a SIGSEGV signal
Signals
How do we communicate to processes?
We send them messages
called signals
A signal is an event of
some type that has
occurred in the system.
It allows the OS to
communicate to aprocess
AND
user processes to
communicate to eachother
If you type ctrl-‐c:during processexec
OS sends it a SIGINT signal
A process can kill another process:
P1 sends P2 a SIGKILL signal
When a child terminates:
OS sends the parent a SIGCHLDsignal
Signals
How do we communicate to processes?
The transfer of a signal to a
destination occurs in 2 steps
Sending a Signal Receiving a Signal
Signals
How do we communicate to processes?
Sending a Signal
The kernel sends a signal to a destination process by
updating some state in the context of that process.
This occurs when the kernel detects a system event
(div by 0) or when a process invokes the kill system call
to explicitly request the kernel to send a signal to the
destination process.
Signals
› How do we communicate to processes?
› Receiving a Signal
› A destination process receives a signal when it is forced by
the kernel to react in some way.
› The process can either ignore the signal, terminate, or
catch the signal by executing user‐level functions called
signal handlers.
Signals
How do we communicate to processes?
• A signal is pending if sent but not yet received
– There can be at most 1 pending signal for a type
– Important: signals are not queued
• If a process has a pending signal of type k, then
subsequent signals of type k that are sent to that
process are discarded.
• A process can block the receipt of certain
signals
– Blocked signals can be delivered, but will not be
received until the signal is unblocked.
Signals
So, how are they implemented?
Process 1
0 0 0 0 … 0
Pending
0 0 0 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
0 0 0 0 … 0
Blocked
Kernel
Signals
So, how are they implemented?
Process 1
0 0 0 0 … 0
Pending
0 0 0 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
0 0 0 0 … 0
Blocked
Kernel
Each process structurecontainsa pending and blocked bit vector.
Each entry corresponds to a specific signal.
Signals
So, how are they implemented?
Process 1
0 0 0 0 … 0
Pending
0 0 1 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
1 0 0 0 … 0
Blocked
Kernel
A process can decideto block a signal by setting
the correspondingbit in the blockedbit vector.
Signals
So, how are they implemented?
Process 1
0 0 0 0 … 0
Pending
0 0 1 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
1 0 0 0 … 0
Blocked
Kernel
Not all signals can be blocked:
SIGKILL can’t be blocked or handled by the process.
Signals
So, how are they implemented?
Process 1
0 0 0 0 … 0
Pending
0 0 1 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
1 0 0 0 … 0
Blocked
Kernel
Imagine the processorencounters an illegal instruction
during the execution of Process 1
SIGILL
Signals
So, how are they implemented?
Process 1
0 0 0 1 … 0
Pending
0 0 1 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
1 0 0 0 … 0
Blocked
Kernel
The kernel will set the correspondingbit in the Pending bit vector
SIGILL
SIGILL
Signals
So, how are they implemented?
Process 1
0 0 0 1 … 0
Pending
0 0 1 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
1 0 0 0 … 0
Blocked
Kernel
Just before Process 1 resumes execution the kernel will check
to see if the process has any pending signals that are not blocked
SIGILL
?
Signals
So, how are they implemented?
Process 1
0 0 0 1 … 0
Pending
0 0 1 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
1 0 0 0 … 0
Blocked
Kernel
If it does it will check if the process has defined a signal handler
for the signal or execute default behavior
SIGILL
?
Signals
So, how are they implemented?
Process 1
0 0 0 1 … 0
Pending
0 0 1 0 … 0
Blocked
Process 2
0 0 0 0 … 0
Pending
1 0 0 0 … 0
Blocked
Kernel
The default behavior for SIGILL is to terminate the process
SIGILL
Signals
0 0 0 0 … 0
0 0 1 0 … 0
Blocked
0 0 0 0 … 0
1 0 0 0 … 0
Blocked
Process 1 Process 2
Kernel
Pending Pending
A process can send another process
a signal using the kill system call
So, how are they implemented?
#include
int kill(pid_t pid, int sig);
Signals
0 0 0 0 … 1
0 0 1 0 … 0
Blocked
0 0 0 0 … 0
1 0 0 0 … 0
Blocked
Process 1 Process 2
Kernel
Pending Pending
A process can send another process
a signal using the kill system call
So, how are they implemented?
#include
int kill(pid_t pid, int sig);
SIGINT
Signals
0 0 0 0 … 1
0 0 1 0 … 0
Blocked
0 0 0 0 … 0
1 0 0 0 … 0
Blocked
Process 1 Process 2
Kernel
Pending Pending
Again, the kernel will check for pending signals,
check for a handler or execute default behavior
So, how are they implemented?
#include
int kill(pid_t pid, int sig);
SIGINT
Signals
0 0 0 0 … 1
0 0 1 0 … 0
Blocked
0 0 0 0 … 0
1 0 0 0 … 0
Blocked
Process 1 Process 2
Kernel
Pending Pending
Again, the kernel will check for pending signals,
check for a handler or execute default behavior
So, how are they implemented?
#include
int kill(pid_t pid, int sig);
SIGINT
Signals
We can send signals from the keyboard
ctrl-c
Typing ctrl-c from the keyboard sends a SIGINT signal to the shell.
The shell catches the signal and then sends a SIGINT to every process
in the foreground processgroup.
ctrl-z
Typing ctrl-‐zfrom the keyboard sends a SIGTSTP signal to the shell.
The shell catches the signal and then sends a SIGTSTP to every process
in the foreground processgroup.
Signals
Ok, so how do we send signals programmatically?
#include
#include
int kill(pid_t pid, int sig);
Signals
And how do we catch a signal?
#include
unsigned int alarm(unsigned int secs);
Generates a SIGALRM signal to the
calling process aftersecs seconds.
Need a handler to “catch” the signal
and do something interesting.
Signals
Register signal handler
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t
handler);
The signal
Sets the disposition of the signal
signum to handler
Signals
Signals
77
#include
#include
#include
#include
void sig_alarm(int signal)
{
printf(“Receive alarm signal = %d\n”,
signal);
}
int main(int argc, char *argv[])
{
signal(SIGALRM /*14*/, sig_alarm);
alarm(1);
printf(“program end!\n”);
return 0;
}
Output:
Receive alarm signal = 14
program end!
Tell the OS to send a signal
to a process with a certain
delay.
• Clean Sharing Model
– Global variables (address spaces) are not shared
– File tables (file descriptors) are shared
• Simple and straightforward
– Forking a child process is easy
– Reaping child processes is simple
– Processes are isolated for protection
Process Pros
• Forking a process is expensive
– Need to clone parent’s memory space
– Need to setup a separate context for child
Process Cons
Note: the concept of context switch and why it has high overhead:
https://en.wikipedia.org/wiki/Context_switch#:~:text=In%20computing%2C%20
a%20context%20switch,of%20a%20multitasking%20operating%20system.
• Forking a process is expensive
– Need to clone parent’s memory space
– Need to setup a separate context for child
• Non-trivial to share data between processes
– Need to use files/pipes to share data
– Can use shared memory
– Lots of overhead!
Process Cons
• Process = context + code, data, and stack
shared libraries
0
Program context:
Data registers
Condition codes
Stack pointer (SP)
Program counter (PC)
Kernel context:
VM structures
Descriptor table
Code, data, and stack
run-time heap
read/write data
read-only code/data
stack
SP
PC
Process context
View of a Process
• Process = context + code, data, and stack
shared libraries
0
Program context:
Data registers
Condition codes
Stack pointer (SP)
Program counter (PC)
Kernel context:
VM structures
Descriptor table
Code, data, and stack
run-time heap
read/write data
read-only code/data
stack
SP
PC
Process context
View of a Process
0
Thread context:
Data registers
Condition codes
Stack pointer (SP)
Program counter (PC)
run-time heap
read/write data
read-only code/data
stack
SP
PC
• Process = thread + code, data, and kernel context
Thread (main thread) Code and Data
shared libraries
Kernel context:
VM structures
Descriptor table
Alternate View of a Process
• Multiple threads can be associated with a process
– Each thread has its own logical control flow
shared libraries
0
Thread 1 context:
Data registers
Condition codes
SP1
PC1
Shared code and data
run-time heap
read/write data
read-only code/data
stack 1
Thread 1 (main thread)
Kernel context:
VM structures
Descriptor table
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Thread 2 (peer thread)
A Process With Multiple Threads
• Multiple threads can be associated with a process
– Each thread has its own logical control flow
– Each thread shares the same code, data, and kernel context
shared libraries
0
Thread 1 context:
Data registers
Condition codes
SP1
PC1
Shared code and data
run-time heap
read/write data
read-only code/data
stack 1
Thread 1 (main thread)
Kernel context:
VM structures
Descriptor table
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Thread 2 (peer thread)
A Process With Multiple Threads
• Multiple threads can be associated with a process
– Each thread has its own logical control flow
– Each thread shares the same code, data, and kernel context
– Each thread has its own stack for local variables
shared libraries
0
Thread 1 context:
Data registers
Condition codes
SP1
PC1
Shared code and data
run-time heap
read/write data
read-only code/data
stack 1
Thread 1 (main thread)
Kernel context:
VM structures
Descriptor table
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Thread 2 (peer thread)
A Process With Multiple Threads
• Multiple threads can be associated with a process
– Each thread has its own logical control flow
– Each thread shares the same code, data, and kernel context
– Each thread has its own stack for local variables
• but not protected from other threads
shared libraries
0
Thread 1 context:
Data registers
Condition codes
SP1
PC1
Shared code and data
run-time heap
read/write data
read-only code/data
stack 1
Thread 1 (main thread)
Kernel context:
VM structures
Descriptor table
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Thread 2 (peer thread)
A Process With Multiple Threads
• Multiple threads can be associated with a process
– Each thread has its own logical control flow
– Each thread shares the same code, data, and kernel context
– Each thread has its own stack for local variables
• but not protected from other threads
– Each thread has its own thread id (TID)
0
Thread 1 context:
Data registers
Condition codes
SP1
PC1
run-time heap
read/write data
read-only code/data
stack 1
Thread 1 (main thread)
Kernel context:
VM structures
Descriptor table
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Shared code and data Thread 2 (peer thread)
shared libraries
A Process With Multiple Threads
(typically functions)
Process
• Processes
– Represent an entire program
• Threads
– Represents smaller chunks of code
Threads
OS: Concept of a Thread
(typically functions)
Process
• Processes
– Represent an entire program
– High Overhead context switch
• Threads
– Represents smaller chunks of code
– Lower Overhead context switch
Threads
OS: Concept of a Thread
• Threads associated with process form a pool of peers
– Unlike processes which form a tree hierarchy
P0
P1
sh sh sh
foo
bar
T1
Process hierarchyThreads associated with process foo
T2
T4
T5 T3
shared code, data
and kernel context
Logical View of Threads
• How threads and processes are similar
– Each has its own logical control flow
– Each can run concurrently with others
– Each is context switched
Threads vs. Processes
• How threads and processes are similar
– Each has its own logical control flow
– Each can run concurrently with others
– Each is context switched
• How threads and processes are different
– Threads share code and some data
• Processes (typically) do not
Threads vs. Processes
• How threads and processes are similar
– Each has its own logical control flow
– Each can run concurrently with others
– Each is context switched
• How threads and processes are different
– Threads share code and some data
• Processes (typically) do not
– Threads are somewhat less expensive than processes
• Process control is twice as expensive as thread control
• Linux numbers:
– ~20K cycles to create and reap a process
– ~10K cycles (or less) to create and reap a thread
Threads vs. Processes