NOTE: These notes are adapted from those of
Allan Gottlieb, and are
reproduced here with his permission.
================ Start Lecture #3
Transfer of Control : Version 1
More details will be added when we study memory management
and more again when we study interrupts.
Procedure f calls g(a,b,c) in process P.
Steps when f carries out the call:
1. Complete all previous instructions in f. Therefore, the only
registers important for the state of f are the stack pointer (SP)
and the program counter (PC)
2. Push arguments c,b,a onto P's stack. Note: Stacks usually
grow downward from the top of P's segment, so pushing
an item onto the stack actually involves decrementing SP.
3. Execute PUSHJ < start-address of g >. This instruction
pushes PC onto the stack, and then jumps to the start address
4. The first step in g is to allocate space for its own local
variables by suitably decrementing SP.
g now starts its execution from the beginning. This may involve
calling other procedures, possibly including recursive calls to
Steps when g returns control to f:
5. At the end of g: Undo step (4) and deallocate its
local variables by incrementing the SP.
6. Last step of g: POPJ has the effect PC = pop(stack)
7. We are now at the step in f immediately following the call
to g. Pop the arguments a,b,c off the stack and continue the
execution of f.
Features of procedure call
- Predictable: f knows when the call is coming and can
make sure that it is in a good state for the transfer.
- LIFO structure of control: we can be sure that control
will return to f when this call to g exits. (Excluding language
features such as "throwing" and "catching" exceptions.)
- Recursive: g may (directly or indirectly) create a new
instance of f during the course of execution, but this must
end before g does.
- Entirely in user mode, user space.
System Call: non-blocking.
Function f in process P calls a non-blocking system routine.
1. Procedure call to library routine in user space; just as above.
2. In library routine: put system call in specified location
3. Put PC in specified location LPC (e.g. register or top of user stack).
4. TRAP: Jump to location in kernel and switch to kernel mode.
5. In kernel: read system call, and go to that routine.
6. Execute body of system call. Kernel does not encounter any need
to block P.
7. Jump to location saved in LPC (next step in library routine)
and switch to user mode.
8. Library routine: Library procedure returns to f, as above.
- Immediate return from kernel to P. No other processes become active.
- Kernel call.
System call: Blocking
Function f in process P calls a blocking system routine.
Steps when P makes call:
Steps 1-5 are the same as for a non-blocking system call. (Necessarily,
as there is no way to know whether P will block until step 6.)
6. Execute body of system call. Kernel realizes that P has to block.
7. Kernel determines that process P is running from corresponding
flag in kernel.
8. Save next address for P from LPC into process table entry (PTE) for P.
9. Save SP in PTE for P.
10. Save other dynamic information in PTE for P. Some of this may
be in registers, some in kernel space, some in P's user space.
11. Mark P as blocked in PTE.
12. Call the scheduler to choose a ready process Q.
13. Load Q's dynamic information from PTE for Q into wherever it goes.
14. Mark Q as the running process.
15. Jump to the next address of Q as recorded in Q's PTE. Switch to user mode.
16. In Q: Continue.
When P unblocks
When an event occurs to unblock P, mark P as ready.
When the scheduler choose to run P
Carry out steps 12-15 as above for P.
16. We are at the next step of the library routine in P. Return to f.
- Other processes become active before return to P.
- Kernel call.
Interrupts (including clock interrupts)
P is tootling along when suddenly an interrupt occurs.
Steps when interrupts occur
1. I/O device notifies interrupt controller.
2. Interrupt controller places index of device and event
into address lines.
3. Interrupt controller issues interrupt to CPU.
4. Hardware saves PC (next address in P) in LPC, switch to kernel
5. Hardware load PC from interrupt vector [ index of device,event].
6. Assembly language procedure saves registers (expressing state of P)
into PTE of P.
7. Assembly language procedure sets up new stack.
8. C interrupt server runs.
Continue as in steps 7-16 of "call to blocking routine," except that
P will generally be marked as "ready" rather than blocked.
When the scheduler decides to run P again
Same as in call to blocking routine, except that registers have to
be reloaded from PTE for P.
- Unpredictable. The interrupt can come at any time, so the
process P can't "straighten things up" so as to be in a simple
state. If the interrupt occurs in the middle of
executing a source language instruction, important parts of
the process state may be in registers. For example, the
instruction "x = y+z" may compile into something like
131: LOAD R1, Y
132: LOAD R2, Z
133: ADD R1,R2,R3
134: STORE R3, X
If the interrupt occurs after 132 has executed, then the values of R1 and R2
need to be saved and restored before P resumes at 133. Otherwise, when
P resumes at 133, R1 and R2 will contain some garbage put there by some
entirely different process.
If the machine uses pipelining or a super-scalar architecture, in which several
instructions are executing simultaneously, then the problem of saving state
become much hairier.
- Other processes become active before returning to P.
- Kernel is activated by interrupt from I/O device.
Very similar to interrupts, but activated by P itself, and always block P.
We'll talk about these when we do memory management.
2.4: Process Scheduling
Scheduling processes on the processor is often called ``process
scheduling'' or simply ``scheduling''.
The objectives of a good scheduling policy include
- Low response time (important for interactive jobs).
- Low turnaround time (important for batch jobs).
- High throughput [the above are from Tanenbaum].
- Repeatability. Dartmouth (DTSS) ``wasted cycles'' and limited
logins for repeatability.
- Fair across projects.
- ``Cheating'' in unix by using multiple processes.
- Fair share research project.
- Degrade gracefully under load.
Recall the basic diagram describing process states
For now we are discussing short-term scheduling, i.e., the arcs
connecting running <--> ready.
Medium term scheduling is discussed later.
It is important to distinguish preemptive from non-preemptive
- Preemption means the operating system moves a process from running
to ready without the process requesting it.
- Without preemption, the system implements ``run to completion (or
yield or block)''.
- The ``preempt'' arc in the diagram.
- We do not consider yield (a solid arrow from running to ready).
- Preemption needs a clock interrupt (or equivalent).
- Preemption is needed to guarantee fairness.
- Found in all modern general purpose operating systems.
- Even non preemptive systems can be multiprogrammed (e.g., when processes
block for I/O).
This is used for real time systems. The objective of the scheduler is
to find a schedule for all the tasks (there are a fixed set of tasks)
so that each meets its deadline. The run time of each task is known
Actually it is more complicated.
- Periodic tasks
- What if we can't schedule all task so that each meets its deadline
(i.e., what should be the penalty function)?
- What if the run-time is not constant but has a known probability
We do not cover deadline scheduling in this course.
The name game
There is an amazing inconsistency in naming the different
(short-term) scheduling algorithms. Over the years I have used
primarily 4 books: In chronological order they are Finkel, Deitel,
Silberschatz, and Tanenbaum. The table just below illustrates the
name game for these four books. After the table we discuss each
scheduling policy in turn.
Finkel Deitel Silbershatz Tanenbaum
FCFS FIFO FCFS -- unnamed in tanenbaum
RR RR RR RR
PS ** PS PS
SRR ** SRR ** not in tanenbaum
SPN SJF SJF SJF
PSPN SRT PSJF/SRTF -- unnamed in tanenbaum
HPRN HRN ** ** not in tanenbaum
** ** MLQ ** only in silbershatz
FB MLFQ MLFQ MQ
First Come First Served (FCFS, FIFO, FCFS, --)
If the OS ``doesn't'' schedule, it still needs to store the PTEs
somewhere. If it is a queue you get FCFS. If it is a stack
(strange), you get LCFS. Perhaps you could get some sort of random
policy as well.
- Only FCFS is considered.
- The simplist scheduling policy.
Round Robin (RR, RR, RR, RR)
- An important preemptive policy.
- Essentially the preemptive version of FCFS.
- The key parameter is the quantum size q.
- When a process is put into the running state a timer is set to q.
- If the timer goes off and the process is still running, the OS
preempts the process.
- This process is moved to the ready state (the
preempt arc in the diagram), where it is placed at the
rear of the ready list (a queue).
- The process at the front of the ready list is removed from
the ready list and run (i.e., moves to state running).
- When a process is created, it is placed at the rear of the ready list.
- As q gets large, RR approaches FCFS
- As q gets small, RR approaches PS (Processor Sharing, described next)
- What value of q should we choose?
- Small q makes system more responsive.
- Large q makes system more efficient since less process switching.
Processor Sharing (PS, **, PS, PS)
Merge the ready and running states and permit all ready jobs to be run
at once. However, the processor slows down so that when n jobs are
running at once each progresses at a speed 1/n as fast as it would if
it were running alone.
- Clearly impossible as stated due to the overhead of process
- Of theoretical interest (easy to analyze).
- Approximated by RR when the quantum is small. Make
sure you understand this last point. For example,
consider the last homework assignment (with zero context switch time)
and consider q=1, q=.1, q=.01, etc.
Variants of Round Robbin
- State dependent RR
- Same as RR but q is varied dynamically depending on the state
of the system.
- Favor processes holding important resources.
- For example, non-swappable memory.
- Perhaps this should be considered medium term scheduling
since you probably do not recalculate q each time.
- External priorities: RR but a user can pay more and get
bigger q. That is one process can be given a higher priority than
another. But this is not an absolute priority, i.e., the lower priority
(i.e., less important) process does get to run, but not as much as the
high priority process.
Each job is assigned a priority (externally, perhaps by charging
more for higher priority) and the highest priority ready job is run.
- Similar to ``External priorities'' above
- If many processes have the highest priority, use RR among them.
- Can easily starve processes (see aging below for fix).
- Can have the priorities changed dynamically to favor processes
holding important resources (similar to state dependent RR).
- Many policies can be thought of as priority scheduling in
which we run the job with the highest priority (with different notions
of priority for different policies).
As a job is waiting, raise its priority so eventually it will have the
- This prevents starvation (assuming all jobs terminate).
policy is preemptive).
- There may be many processes with the maximum priority.
- If so, can use fifo among those with max priority (risks
starvation if a job doesn't terminate) or can use RR.
- Can apply priority aging to many policies, in particular to priority
scheduling described above.
Selfish RR (SRR, **, SRR, **)
- Perhaps it should be called ``snobbish RR''.
- ``Accepted processes'' run RR.
- Accepted process have their priority increase at rate b>=0.
- A new process starts at priority 0; its priority increases at rate a>=0.
- A new process becomes an accepted process when its priority
reaches that of an accepted process (or until there are no accepted
- Note that at any time all accepted processes have same priority.
- If b>=a, get FCFS.
- If b=0, get RR.
- If a>b>0, it is interesting.
Shortest Job First (SPN, SJF, SJF, SJF)
Sort jobs by total execution time needed and run the shortest first.
- First consider a static situation where all jobs are available in
the beginning, we know how long each one takes to run, and we
implement ``run-to-completion'' (i.e., we don't even switch to another
process on I/O). In this situation, SJF has the shortest average
- Assume you have a schedule with a long job right before a
- Consider swapping the two jobs.
- This decreases the wait for
the short by the length of the long job and increases the wait of the
long job by the length of the short job.
- This decreases the total waiting time for these two.
- Hence decreases the total waiting for all jobs and hence decreases
the average waiting time as well.
- Hence, whenever a long job is right before a short job, we can
swap them and decrease the average waiting time.
- Thus the lowest average waiting time occurs when there are no
short jobs right before long jobs.
- This is SJF.
- In the more realistic case where the scheduler switches to a new
process when the currently running process blocks (say for I/O), we
should call the policy shortest next-CPU-burst first.
- The difficulty is predicting the future (i.e., knowing in advance
the time required for the job or next-CPU-burst).
- This is an example of priority scheduling.
Preemptive Shortest Job First (PSPN, SRT, PSJF/SRTF, --)
Preemptive version of above
- Permit a process that enters the ready list to preempt the running
process if the time for the new process (or for its next burst) is
less than the remaining time for the running process (or for
its current burst).
- It will never happen that a process in the ready list
will require less time than the remaining time for the currently
running process. Why?
Ans: When the process joined the ready list it would have started
running if the current process had more time remaining. Since
that didn't happen the current job had less time remaining and now
it has even less.
- Can starve processs that require a long burst.
- This is fixed by the standard technique.
- What is that technique?
Ans: Priority aging.
- Another example of priority scheduling.