Operating Systems


Chapter 2: Process Management

Tanenbaum's chapter title is ``processes''. I prefer process management. The subject matter is processes, process scheduling, interrupt handling, and IPC (Interprocess communication--and coordination).

2.1: Processes

Definition: A process is a program in execution.

2.1.1: The Process Model

Even though in actuality there are many processes running at once, the OS gives each process the illusion that it is running alone.

Virtual time and virtual memory are examples of abstractions provided by the operating system to the user processes so that the latter ``sees'' a more pleasant virtual machine than actually exists.

Process Hierarchies

Modern general purpose operating systems permit a user to create and destroy processes.

Old or primitive operating system like MS-DOS are not multiprogrammed so when one process starts another, the first process is automatically blocked and waits until the second is finished.

Process states and transitions

The above diagram contains a great deal of information.


================ Start Lecture #4 ================

One can organize an OS around the scheduler.

2.1.3: Implementation of Processes

The OS organizes the data about each process in a table naturally called the process table. Each entry in this table is called a process table entry or PTE.

An aside on Interrupts (will be done again here) and here.

In a well defined location in memory (specified by the hardware) the OS stores an interrupt vector, which contains the address of the (first level) interrupt handler.

Assume a process P is running and a disk interrupt occurs for the completion of a disk read previously issued by process Q, which is currently blocked. Note that interrupts are unlikely to be for the currently running process (because the process waiting for the interrupt is likely blocked).

  1. The hardware saves the program counter and some other registers (or switches to using another set of registers, the exact mechanism is machine dependent).
  2. Hardware loads new program counter from the interrupt vector.
  3. Assembly language routine saves registers.
  4. Assembly routine sets up new stack.
  5. Assembly routine calls C procedure (tanenbaum forgot this one).
  6. C procedure does the real work.
  7. The C procedure (that did the real work in the interrupt processing) continues and returns to the assembly code.
  8. Assembly language restores P's state (e.g., registers) and starts P at the point it was when the interrupt occurred.

2.2: Interprocess Communication (IPC) and Process Coordination and Synchronization

2.2.1: Race Conditions

A race condition occurs when two processes can interact and the outcome depends on the order in which the processes execute.

Homework: 2

2.2.2: Critical sections

We must prevent interleaving sections of code that need to be atomic with respect to each other. That is, the conflicting sections need mutual exclusion. If process A is executing its critical section, it excludes process B from executing its critical section. Conversely if process B is executing is critical section, it excludes process A from executing its critical section.

Requirements for a critical section implementation.

  1. No two processes may be simultaneously inside their critical section.
  2. No assumption may be made about the speeds or the number of CPUs.
  3. No process outside its critical section may block other processes.
  4. No process should have to wait forever to enter its critical section.

2.2.3 Mutual exclusion with busy waiting

The operating system can choose not to preempt itself. That is, no preemption for system processes (if the OS is client server) or for processes running in system mode (if the OS is self service). Forbidding preemption for system processes would prevent the problem above where x<--x+1 not being atomic crashed the printer spooler if the spooler is part of the OS.

But simply forbidding preemption while in system mode is not sufficient.

Software solutions for two processes

Initially P1wants=P2wants=false

Code for P1                             Code for P2

Loop forever {                          Loop forever {
    P1wants <-- true         ENTRY          P2wants <-- true
    while (P2wants) {}       ENTRY          while (P1wants) {}
    critical-section                        critical-section
    P1wants <-- false        EXIT           P2wants <-- false
    non-critical-section }                  non-critical-section }

Explain why this works.

But it is wrong! Why?


================ Start Lecture #5 ================

Let's try again. The trouble was that setting want before the loop permitted us to get stuck. We had them in the wrong order!

Initially P1wants=P2wants=false

Code for P1                             Code for P2

Loop forever {                          Loop forever {
    while (P2wants) {}       ENTRY          while (P1wants) {}
    P1wants <-- true         ENTRY          P2wants <-- true
    critical-section                        critical-section
    P1wants <-- false        EXIT           P2wants <-- false
    non-critical-section }                  non-critical-section }

Explain why this works.

But it is wrong again! Why?

So let's be polite and really take turns. None of this wanting stuff.

Initially turn=1

Code for P1                      Code for P2

Loop forever {                   Loop forever {
    while (turn = 2) {}              while (turn = 1) {}
    critical-section                 critical-section
    turn <-- 2                       turn <-- 1
    non-critical-section }           non-critical-section }

This one forces alternation, so is not general enough. Specifically, it does not satisfy condition three, which requires that no process in its non-critical section can stop another process from entering its critical section. With alternation, if one process is in its non-critical section (NCS) then the other can enter the CS once but not again.

In fact, it took years (way back when) to find a correct solution. Many earlier ``solutions'' were found and several were published, but all were wrong. The first true solution was found by Dekker. It is very clever, but I am skipping it (I cover it when I teach masters level OS II). Subsequently, algorithms with better fairness properties were found (e.g., no task has to wait for another task to enter the CS twice).

What follows is Peterson's solution. When it was published, it was a surprise to see such a simple soluntion. In fact Peterson gave a solution for any number of processes. A proof that the algorithm for any number of processes satisfies our properties (including a strong fairness condition) can be found in Operating Systems Review Jan 1990, pp. 18-22.

Initially P1wants=P2wants=false  and  turn=1

Code for P1                        Code for P2

Loop forever {                     Loop forever {
    P1wants <-- true                   P2wants <-- true
    turn <-- 2                         turn <-- 1
    while (P2wants and turn=2) {}      while (P1wants and turn=1) {}
    critical-section                   critical-section
    P1wants <-- false                  P2wants <-- false
    non-critical-section               non-critical-section

Hardware assist (test and set)

TAS(b) where b is a binary variable ATOMICALLY sets b<--true and returns the OLD value of b. Of course it would be silly to return the new value of b since we know the new value is true

Now implementing a critical section for any number of processes is trivial.

loop forever {
    while (TAS(s)) {}   ENTRY
    CS
    s<--false           EXIT
    NCS

P and V and Semaphores

Note: Tanenbaum does both busy waiting (like above) and blocking (process switching) solutions. We will only do busy waiting.

Homework: 3

The entry code is often called P and the exit code V (Tanenbaum only uses P and V for blocking, but we use it for busy waiting). So the critical section problem is to write P and V so that

loop forever
    P
    critical-section
    V
    non-critical-section
satisfies
  1. Mutual exclusion
  2. No speed assumptions
  3. No blocking by processes in NCS
  4. Forward progress (my weakened version of Tanenbaum's last condition

Note that I use indenting carefully and hence do not need (and sometimes omit) the braces {}

A binary semaphore abstracts the TAS solution we gave for the critical section problem.

The above code is not real, i.e., it is not an implementation of P. It is, instead, a definition of the effect P is to have.

To repeat: for any number of processes, the critical section problem can be solved by

loop forever
    P(S)
    CS
    V(S)
    NCS

The only specific solution we have seen for an arbitrary number of processes is the one just above with P(S) implemented via test and set.

Remark: Peterson's solution requires each process to know its processor number. The TAS soluton does not. Moreover the definition of P and V does not permit use of the processor number. Thus, strictly speaking Peterson did not provide an implementation of P and V. He did solve the critical section problem.

To solve other coordination problems we want to extend binary semaphores.

The solution to both of these shortcomings is to remove the restriction to a binary variable and define a generalized or counting semaphore.

These counting semaphores can solve what I call the semi-critical-section problem, where you premit up to k processes in the section. When k=1 we have the original critical-section problem.

initially S=k

loop forever
    P(S)
    SCS   <== semi-critical-section
    V(S)
    NCS


================ Start Lecture #6 ================

Producer-consumer problem

Initially e=k, f=0 (counting semaphore); b=open (binary semaphore)

Producer                         Consumer

loop forever                     loop forever
    produce-item                     P(f)
    P(e)                             P(b); take item from buf; V(b)
    P(b); add item to buf; V(b)      V(e)
    V(f)                             consume-item

Dining Philosophers

A classical problem from Dijkstra

What algorithm do you use for access to the shared resource (the forks)?

The purpose of mentioning the Dining Philosophers problem without giving the solution is to give a feel of what coordination problems are like. The book gives others as well. We are skipping these (again this material would be covered in a sequel course). If you are interested look, for example, here.

Homework: 14,15 (these have short answers but are not easy).

Readers and writers

Quite useful in multiprocessor operating systems. The ``easy way out'' is to treat all processes as writers in which case the problem reduces to mutual exclusion (P and V). The disadvantage of the easy way out is that you give up reader concurrency. Again for more information see the web page referenced above.


================ Start Lecture #7 ================

2.4: Process Scheduling

Scheduling the processor is often called ``process scheduling'' or simply ``scheduling''.

The objectives of a good scheduling policy include

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.

Preemption

It is important to distinguish preemptive from non-preemptive scheduling algorithms.

Deadline scheduling

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 in advance.

Actually it is more complicated.

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.

Round Robbin (RR, RR, RR, RR)

Homework: 9, 19, 20, 21, and the following (remind me to discuss this last one in class next time):

Consider the set of processes in the table below. When does each process finish if RR scheduling is used with q=1, if q=2, if q=3, if q=100. First assume (unrealistically) that context switch time is zero. Then assume it is .1. Each process performs no I/O (i.e., no process ever blocks). All times are in milliseconds. The CPU time is the total time required for the process (excluding context switch time). The creation time is the time when the process is created. So P1 is created when the problem begins and P2 is created 5 miliseconds later.

ProcessCPU TimeCreation Time
P1200
P233
P325

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.

Homework: 18. (part of homework #8 as #7 has enough)

Variants of Round Robbin

Priority Scheduling

Each job is assigned a priority (externally, perhaps by charging more for higher priority) and the highest priority ready job is run.

Priority aging

As a job is waiting, raise its priority so eventually it will have the maximum priority.


================ Start Lecture #8 ================

Homework: 22, 23

Selfish RR (SRR, **, SRR, **)

Shortest Job First (SPN, SJF, SJF, SJF)

Sort jobs by total execution time needed and run the shortest first.

Preemptive Shortest Job First (PSPN, SRT, PSJF/SRTF, --)

Preemptive version of above

Highest Penalty Ratio Next (HPRN, HRN, **, **)

Run the process that has been ``hurt'' the most.

Multilevel Queues (**, **, MLQ, **)

Put different classes of processs in different queues

Multilevel Feedback Queues (FB, MFQ, MLFBQ, MQ)

Many queues and processs move from queue to queue in an attempt to dynamically separate ``batch-like'' from interactive processs.

Theoretical Issues

Considerable theory has been developed.

Medium Term scheduling

Decisions made at a coarser time scale.

Long Term Scheduling


================ Start Lecture #9 ================

Notes on lab (scheduling)

  1. If several processes are waiting on I/O, you may assume noninterference. For example, assume that on cycle 100 process A flips a coin and decides its wait is 6 units (i.e., during cycles 101-106 A will be blocked. Assume B begins running at cycle 101 for a burst of 1 cycle. So during 101 process B flips a coin and decides its wait is 3 units. You do NOT have to alter process A. That is, Process A will become ready after cycle 106 (100+6) so enters the ready list cycle 107 and process B becomes ready after cycle 104 (101+3) and enters ready list cycle 105.

  2. For processor sharing (PS), which is part of the extra credit:
    PS (processor sharing). Every cycle you see how many jobs are in the ready Q. Say there are 7. Then during this cycle (an exception will be described below) each process gets 1/7 of a cycle.
    EXCEPTION: Assume there are exactly 2 jobs in RQ, one needs 1/3 cycle and one needs 1/2 cycle. The process needing only 1/3 gets only 1/3, i.e. it is finished after 2/3 cycle. So the other process gets 1/3 cycle during the first 2/3 cycle and then starts to get all the CPU. Hence it finishes after 2/3 + 1/6 = 5/6 cycle. The last 1/6 cycle is not used by any process.


Allan Gottlieb