Multitasking
Multitasking
Until now....
We understand how a program runs on a computer
system.
In reality, multiple programs (tasks) run concurrently on
multiple resources.
Processor Memory
2
Until now....
We understand how a program runs on a computer
system.
In reality, multiple programs (tasks) run concurrently on
multiple resources.
interrupt
handler Processor Memory
3
Interleaving in time
timer interrupt
Keyboard Timer Monitor
disk data
Logical
control
flow
4
Control Flow
Computers do Only One Thing
– From startup to shutdown, a CPU simply reads and executes
(interprets) a sequence of instructions, one at a time.
– This sequence is the system’s physical control flow (or flow
of control).
5
How the Control Flow Changes
Up to Now: two mechanisms for changing control flow:
– Jumps and branches
– Call and return using the stack discipline.
– Both react to changes in program state.
Insufficient for a useful system
– Difficult for the CPU to react to changes in system state.
• data arrives from a disk or a network adapter.
• Instruction divides by zero
• User hits ctl-c at the keyboard
• System timer expires
System needs mechanisms for “exceptional control flow”
Supporting “Exceptional control flow” is the basic mechanism
with which OS serve multiple concurrent tasks controlling
multiple resources.
6
Exceptional Control Flow
– Mechanisms for exceptional control flow exists at all levels of
a computer system.
Low level Mechanism
– exceptions
• change in control flow in response to a system event (i.e.,
change in system state)
– Combination of hardware and OS software
Higher Level Mechanisms
– Process context switch
– Signals
– Nonlocal jumps (setjmp/longjmp)
– Implemented by either:
• OS software (context switch and signals).
• C language runtime library: nonlocal jumps.
7
System context for exceptions
Local/IO Bus
SCSI bus
disk Display Network
disk CDROM
8
Exceptions
9
Interrupt Vectors
Exception
numbers
– Each type of event has a
code for unique exception number k
exception handler 0 – Index into jump table (a.k.a.,
interrupt
code for interrupt vector)
vector
exception handler 1 – Jump table entry k points to
0
1 code for a function (exception
2 exception handler 2 handler).
...
– Handler k is called each
n-1 ...
time exception k occurs.
code for
exception handler n-1
10
Exception Types
Asynchronous Exceptions (Interrupts)
Synchronous Exceptions
– trap (e.g., system call)
– fault (e.g., page fault)
– abort (e.g., parity error)
11
Asynchronous Exceptions (Interrupts)
Caused by events external to the processor
– Indicated by setting the processor’s interrupt pin
– handler returns to “next” instruction.
Examples:
– I/O interrupts
• hitting ctl-c at the keyboard
• arrival of a packet from a network
• arrival of a data sector from a disk
– Hard reset interrupt
• hitting the reset button
– Soft reset interrupt
• hitting ctl-alt-delete on a PC
12
Synchronous Exceptions
Caused by events that occur as a result of executing
an instruction:
– Traps
• Intentional
• Examples: system calls, breakpoint traps, special instructions
• Returns control to “next” instruction
– Faults
• Unintentional but possibly recoverable
• Examples: page faults (recoverable), protection faults
(unrecoverable).
• Either re-executes faulting (“current”) instruction or aborts.
– Aborts
• unintentional and unrecoverable
• Examples: parity error, machine check.
• Aborts current program
13
Trap Example
Opening a File
– User calls open(filename, options)
0804d070 <__libc_open>:
. . .
804d082: cd 80 int $0x80
804d084: 5b pop %ebx
. . .
User Process OS
int exception
pop Open file
return
14
Fault Example #1
int a[1000];
Memory Reference main ()
– User writes to memory location {
a[500] = 13;
– That portion (page) of user’s memory is }
currently on disk
80483b7: c7 05 10 9d 04 08 0d movl $0xd,0x8049d10
– Page handler must load page into
physical memory (This gives the task
the illusion of exclusive use of memory)
– Returns to faulting instruction
– Successful on second try
User Process OS
User Process OS
17
Logical Control Flows
Time
18
Concurrent Processes
Two processes run concurrently (are concurrent) if
their flows overlap in time.
Otherwise, they are sequential.
Examples:
– Concurrent: A & B, A & C
– Sequential: B & C
Time
19
User View of Concurrent Processes
Control flows for concurrent processes are physically
disjoint in time. (Because CPU can run only a single
instruction at a time)
However, we can think of concurrent processes are
running in parallel with each other.
Time
20
Context Switching
Processes are managed by a shared chunk of OS
code called the kernel
– Important: the kernel is not a separate process, but rather
runs as part of some user process
Control flow passes from one process to another via
a context switch.
Process A Process B
code code
user code
context switch
kernel code
Time
user code
21
Private Address Spaces
Each process has its own private address space.
0xffffffff
kernel virtual memory memory
(code, data, heap, stack) invisible to
0xc0000000 user code
user stack
(created at runtime)
%esp (stack pointer)
brk
run-time heap
(managed by malloc)
read/write segment
(.data, .bss)
loaded from the
read-only segment executable file
(.init, .text, .rodata)
0x08048000
unused
0
22
Process Related System Calls
Now, we understand how multiple processes run
concurrently
How multiple processes can be created?
How existing processes can be removed from the
system?
OS provides system calls to do this
– fork
– exit
23
fork: Creating new processes
int fork(void)
– creates a new process (child process) that is identical to the
calling process (parent process)
– returns 0 to the child process
– returns child’s pid to the parent process
if (fork() == 0) {
printf("hello from child\n"); Fork is interesting
} else { (and often confusing)
printf("hello from parent\n"); because it is called
} once but returns twice
24
Kernel Data Structure for Processes
Process Table (Array of PCB)
– Save information of active processes
– Display process information using a PCB(Process Control
Block) as a table entry
25
PCB for each process
PCB
Process state
Process classification
Exit code
and state information
Process ID, Parent process ID
Scheduling priority
Time slice Scheduling information
Resister set
User and group information of
User ID, Group ID executable file
26
Fork Example #1
Key Points
– Parent and child both run same code
• Distinguish parent from child by return value from fork
– Start with same state (e.g., stack, registers, program counter,
environment variables, and open file descriptors)
– But, each has private copy and thus can evolve separately
void fork1()
{ Relative ordering of their print
int x = 1; statements undefined
pid_t pid = fork();
if (pid == 0) {
printf("Child has x = %d\n", ++x);
} else {
printf("Parent has x = %d\n", --x);
}
printf("Bye from process %d with x = %d\n", getpid(), x);
}
27
Fork Example #2
Key Points
– Both parent and child can continue forking
void fork2()
{
printf("L0\n");
Bye
fork();
L1 Bye
printf("L1\n");
fork(); Bye
printf("Bye\n"); L0 L1 Bye
}
28
Fork Example #3
Key Points
– Both parent and child can continue forking
void fork3()
{
Bye
printf("L0\n");
fork(); L2 Bye
printf("L1\n"); Bye
fork(); L1 L2 Bye
printf("L2\n"); Bye
fork();
L2 Bye
printf("Bye\n");
} Bye
L0 L1 L2 Bye
29
Fork Example #4
Key Points
– Both parent and child can continue forking
void fork4()
{
printf("L0\n");
if (fork() != 0) {
printf("L1\n");
if (fork() != 0) { Bye
printf("L2\n");
fork(); Bye
} Bye
} L0 L1 L2 Bye
printf("Bye\n");
}
30
Fork Example #5
Key Points
– Both parent and child can continue forking
void fork5()
{
printf("L0\n");
if (fork() == 0) {
printf("L1\n");
if (fork() == 0) { Bye
printf("L2\n"); L2 Bye
fork();
} L1 Bye
} L0 Bye
printf("Bye\n");
}
31
exit: Destroying Process
void exit(int status)
– exits a process
• Normally return with status 0
– atexit() registers functions to be executed upon exit
void cleanup(void) {
printf("cleaning up\n");
}
void fork6() {
atexit(cleanup);
fork();
exit(0);
}
32
Zombies
Idea
– When process terminates, still consumes system resources
• Various tables maintained by OS
– Called a “zombie”
• Living corpse, half alive and half dead
Reaping
– Performed by parent on terminated child
– Parent is given exit status information
– Kernel discards process
What if Parent Doesn’t Reap?
– If any parent terminates without reaping a child, then child
will be reaped by init process
– Only need explicit reaping for long-running processes
• E.g., shells and servers
33
void fork7()
Zombie {
if (fork() == 0) {
Example /* Child */
printf("Terminating Child, PID = %d\n",
getpid());
exit(0);
} else {
printf("Running Parent, PID = %d\n",
getpid());
linux> ./forks 7 &
while (1)
[1] 6639 ; /* Infinite loop */
Running Parent, PID = 6639 }
Terminating Child, PID = 6640 }
linux> ps
PID TTY TIME CMD – ps shows child
6585 ttyp9 00:00:00 tcsh process as “defunct”
6639 ttyp9 00:00:03 forks
6640 ttyp9 00:00:00 forks <defunct> – Killing parent allows
6641 ttyp9 00:00:00 ps child to be reaped
linux> kill 6639
[1] Terminated
linux> ps
PID TTY TIME CMD
6585 ttyp9 00:00:00 tcsh
6642 ttyp9 00:00:00 ps
34
void fork8()
Nonterminating {
if (fork() == 0) {
Child /* Child */
printf("Running Child, PID = %d\n",
Example getpid());
while (1)
; /* Infinite loop */
} else {
printf("Terminating Parent, PID = %d\n",
getpid());
linux> ./forks 8 exit(0);
Terminating Parent, PID = 6675 }
Running Child, PID = 6676 }
linux> ps
PID TTY TIME CMD
6585 ttyp9 00:00:00 tcsh – Child process still active
6676 ttyp9 00:00:06 forks
6677 ttyp9 00:00:00 ps
even though parent has
linux> kill 6676 terminated
linux> ps – Must kill explicitly, or else
PID TTY TIME CMD
6585 ttyp9 00:00:00 tcsh
will keep running indefinitely
6678 ttyp9 00:00:00 ps
35
wait: Synchronizing with children
and Reaping zombies
36
wait: Synchronizing with children
void fork9() {
int child_status;
if (fork() == 0) {
printf("HC: hello from child\n");
}
else {
printf("HP: hello from parent\n");
wait(&child_status);
printf("CT: child has terminated\n");
}
printf("Bye\n"); HC Bye
exit();
}
HP CT Bye
37
Wait Example
– If multiple children completed, will take in arbitrary order
– Can use macros WIFEXITED and WEXITSTATUS to get
information about exit status
void fork10()
{
pid_t pid[N];
int i;
int child_status;
for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0)
exit(100+i); /* Child */
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 terminate abnormally\n", wpid);
}
}
38
Waitpid
– waitpid(pid, &status, options)
• Can wait for specific process
• Various options
void fork11()
{
pid_t pid[N];
int i;
int child_status;
for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0)
exit(100+i); /* Child */
for (i = 0; i < N; 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);
}
39
Wait/Waitpid Example Outputs
Using wait (fork10)
Child 3565 terminated with exit status 103
Child 3564 terminated with exit status 102
Child 3563 terminated with exit status 101
Child 3562 terminated with exit status 100
Child 3566 terminated with exit status 104
40
exec: Running new programs
int execl(char *path, char *arg0, char *arg1, …, 0)
– loads and runs executable at path with args arg0, arg1, …
• path is the complete path of an executable
• arg0 becomes the name of the process
– typically arg0 is either identical to path, or else it contains only the
executable filename from path
• “real” arguments to the executable start with arg1, etc.
• list of args is terminated by a (char *)0 argument
– returns -1 if error, otherwise doesn’t return!
main() {
if (fork() == 0) {
execl("/usr/bin/cp", "cp", "foo", "bar", 0);
}
wait(NULL);
printf("copy completed\n");
exit();
}
41
The World of Multitasking
42
Programmer’s Model of Multitasking
Basic Functions
– fork() spawns new process
• Called once, returns twice
– exit() terminates own process
• Called once, never returns
• Puts it into “zombie” status
– wait() and waitpid() wait for and reap terminated
children
– execl() and execve() run a new program in an existing
process •can give env variables
• Called once, (normally) never returns
Programming Challenge
– Understanding the nonstandard semantics of the functions
– Avoiding improper use of system resources
• E.g. “Fork bombs” can disable a system.
43
Unix Process Hierarchy
Now, we are ready to understand how UNIX starts up
and run many user application programs
[0]
init [1]
Grandchild Grandchild
44
Unix Startup: Step 1
1. Pushing reset button loads the PC with the address of a small
bootstrap program.
2. Bootstrap program loads the boot block (disk block 0).
3. Boot block program loads kernel binary (e.g., /boot/vmlinux)
4. Boot block program passes control to kernel.
5. Kernel handcrafts the data structures for process 0.
45
Unix Startup: Step 2
[0]
46
Unix Startup: Step 3
[0]
init [1]
47
Unix Startup: Step 4
[0]
init [1]
48
Shell Programs
int main()
{
char cmdline[MAXLINE];
while (1) {
/* read */
printf("> "); Execution is a sequence
Fgets(cmdline, MAXLINE, stdin); of read/evaluate steps
if (feof(stdin))
exit(0);
/* evaluate */
eval(cmdline);
}
} 49
Simple Shell eval Function
void eval(char *cmdline)
{
char *argv[MAXARGS]; /* argv for execve() */
int bg; /* should the job run in bg or fg? */
pid_t pid; /* process id */
bg = parseline(cmdline, argv);
if (!builtin_command(argv)) {
if ((pid = Fork()) == 0) { /* child runs user job */
if (execve(argv[0], argv, environ) < 0) {
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}