CSE 451
Operating Systems
L6 - Concurrency: Processes and Threads
Slides by: Tom Anderson
Baris Kasikci
Process Lifetime
C program -> process
To create a new process to run a program
Example process control block (proc.h)
Windows CreateProcess API (simplified)
if (!CreateProcess(
NULL,
argv[1], NULL, NULL, FALSE, 0, NULL, NULL,
&si,
&pi )
// No module name (use command line)
// Command line
// Process handle not inheritable
// Thread handle not inheritable
// Set handle inheritance to FALSE
// No creation flags
// Use parent's environment block
// Use parent's starting directory
// Pointer to STARTUPINFO structure
// Pointer to PROCESS_INFORMATION structure
)
UNIX Process API
UNIX Process Management
Question: What does this code print?
int child_pid = fork();
if (child_pid == 0) { // I'm the child process printf("I am process #%d\n", getpid());
return 0;
} else { // I'm the parent process printf("I am parent of process #%d\n", child_pid); return 0;
}
UNIX shell
A shell is a user application that runs other programs
% grep “To be or not” Shakespeare.txt
% grep “To be or not” Shakespeare.txt > logfile
% grep “To be or not” Shakespeare.txt > logfile &
% grep “To be or not” Shakespeare.txt | wc
// run grep, output to stdout
// run grep, output to logfile
// same, but do it in the background
// run grep and wc, output of grep
// goes to input of wc
How can the shell use fork/exec to do these?
Grep has no knowledge of where its output is going
Base case: create a process with arguments
// grep “To be or not” Shakespeare.txt char *prog, **args;
int child_pid;
while (readAndParseCmdLine(&prog, &args)) {// Read and parse the input a line at a time
// create a child process
// I'm the child process. Run prog
if ((child_pid = fork()) == 0) { exec(prog, args);
} else {
wait(child_pid); return 0;
}
}
// I'm the parent, wait for child
Redirect to file “logfile”
// grep “To be or not” Shakespeare.txt > logfile
if ((child_pid = fork()) == 0) { int fd = open(“logfile”); dup2(fd, stdout);
exec(“grep”, args);
} else {
wait(child_pid); return 0;
}
}
// create a child process
// replace stdout with fd
// Then run grep
// I'm the parent, wait for child
Connect two processes with a pipe
// grep “To be or not” Shakespeare.txt | wc pipe(&fd[2]);
if ((child1 = fork()) == 0) {
dup2(fd[1], stdout);
exec(“grep”, grepargs);
} else if (child2 = fork()) == 0) { dup2(fd[0], stdin);
exec(“wc”, wcargs);
} else {
wait(child1); wait(child2);
}}
// create the pipe
// create one child process
// replace stdout with one end of the pipe
// Then run grep
// create the second child process
// replace stdin with other end of the pipe
// wait for both children to finish
Questions
Implementing UNIX fork/exec
Steps to implement UNIX fork
space of the parent
Steps to implement UNIX exec
Is UNIX fork too slow?
Concurrency: Threads
Definitions
Multithreaded OS Kernel
Multithreaded User Processes
Process Thread Lifetime
Thread Operations
Déjà vu?
Thread Abstraction
Question
Why do threads execute at variable speed?
Programmer vs. Processor View
Possible Executions
Example: threadHello
#define NTHREADS 10 thread_t threads[NTHREADS]; main() {
for (i = 0; i < NTHREADS; i++) thread_create(&threads[i], &go, i); for (i = 0; i < NTHREADS; i++) {
exitValue = thread_join(threads[i]);
printf("Thread %d returned with %ld\n", i, exitValue);
}
printf("Main thread done.\n");
}
void go (int n) {
printf("Hello from thread %d\n", n); thread_exit(100 + n);
// REACHED?
}
Implementing threads
Thread Stack
xk swtch (swtch.S)
swtch:
// callee save registers already saved
// ptr to old PCB is in rdi push %rbp
push %rbx
push %r11 push %r12 push %r13 push %r14 push %r15
mov %rsp, (%rdi)
// ptr to new PCB is in rsi mov %rsi, %rsp
pop %r15 pop %r14 pop %r13 pop %r12 pop %r11 pop %rbx pop %rbp ret
A Subtlety
=> Set up newly created thread so that swtch will ”resume” at start of
thread
Stack Progression
Timer Interrupt -> swtch
Two Threads Call Yield