Syllabus   Blank Homework   Quizzes  
Notes   Labs   Scores   Blank

Lecture Notes
Dr. Tong Lai Yu, 2010

  1. Introduction
  2. Processes
  3. Inter Process Communication
  4. Deadlocks
  5. Memory Management
 
  1. File Systems
  2. Protection and Security
  3. I/O Systems


I know of no more encouraging fact,
than the unquestionable ability of man
to elevate his life by conscious endeavor.

			Henry David Thoreau
Processes
  1. Introduction

    process ( tasks, jobs ) ~ program in execution + stack, registers + temporary data ( such as return addresses, subroutine parameters ) + program counter + other activities

    process ≠ program

    e.g. run same program editor, can have several processes

    process model

    Conceptually, each process has its own virtual CPU. In reality, the real CPU switches from process to process.


    Figure (Textbook, P. 85):

    1. Multiporgramming of four programs.
    2. Conceptual model of four independent, sequential processes.
    3. Only one program is active at one time.

    process states

  2. new: the process is being created
  3. ready: process waiting to be assigned to a processor
  4. running: instructions being executed
  5. blocked: waiting for some events to happen ( e.g. I/O or reception of signal )
  6. terminate: process has finished execution
  7. Note: At any instant, only one process can be running; others are in ready or waiting ( blocked ) state

    process table ( PT ) saves Process Control Blocks ( PCB )
    each process has a PCB with the information:

  8. process state
  9. program counter ( PC )
  10. CPU registers
  11. memory-management information
  12. accounting information ( e.g. time limits, process #, ... )
  13. I/O status information ( e.g. list of open files by the process )

  14. Typical Process Table.

  15. Process Creation

    When a process creates a new process, two possibilities exist in terms of execution

    1. The parent continues to execute concurrently with its children
    2. The parent waits until some or all of its children have terminated

    There are two possiblilities in terms of the address space of the new process

    1. the child process is a duplicate of the parent
    2. the child process has a program loaded into it

    process tree

    In Unix, a new process is started with the fork() command. It starts a new process running in the same program, starting at the statement immediately following the fork() call. After the call, both the parent (the process that called fork()) and the child are both executing at the same point in the program. The child is given its own memory space, which is initialized with an exact copy of the memory space (globals, stack, heap objects) of the parent. Thus the child looks like an exact clone of the parent, and indeed, it's hard to tell them apart. The only difference is that fork() returns 0 in the child , but returns a non-zero value in the parent.

    Example
    #include <iostream>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    
    void main()
    {
      int pid;      	//process id
    
      pid = fork(); 	//create another process
    
      if ( pid < 0 ) {  	//fail
        cout << "\nFork failed" << endl;
        exit ( -1 );
      } else if ( pid == 0 ) {      //child
        execlp ( "/bin/ls", "ls", "-l", NULL );	//execute ls
      } else {      		//parent
        wait ( NULL );              //wait for child
        cout << "\nchild complete" << endl;
        exit ( 0 );
      }
    }
    

  16. CPU Scheduling

    OS makes two related kinds of decisions about resources:

  17. Allocation: who gets what. Given a set of requests for resources, which processes should be given which resources in order to make most efficient use of the resources?
  18. Scheduling: how long can they keep it. When more resources are requested than can be granted immediately, in which order should they be serviced? Examples are processor scheduling (one processor, many processes), memory scheduling in virtual memory systems.
  19. Resource #1: the processor.

    Processes may be in any one of three general scheduling states:

  20. Running
  21. Ready
  22. Blocked
  23. There are two parts of CPU scheduling:

  24. The scheduler is a piece of OS code that decides the priorities of processes and how long each will run.
  25. The dispatcher provides the basic mechanism for running processes.
  26. This is an example of policy/mechanism separation.

    Goals for Scheduling Disciplines

  27. CPU utilization -- keep CPU as busy as possible
  28. Fairness -- each process has fair share of CPU
  29. Minimize response time -- time from the submission of a request until the first response is produced
  30. Minimize waiting time -- total time spent waiting in the ready queue
  31. Minimize overhead (context swaps)
  32. Minimize turnaround time -- the interval from the time of submission to the time of completion: waiting time to get into memory + waiting in ready queue + execution on CPU + I/O time
  33. Maximize throughput -- the number of processes that are completed per unit time
  34. Algorithms:

  35. First-come First-served ( FCFS ): run until finished

    • In the simplest case this means uniprogramming.
    • Usually, "finished" means "blocked". One process can use CPU while another waits for an event. Go to back of run queue when ready.
    • Need an FIFO queue.
    • Problem: one process can monopolize CPU; waiting could be long.

  36. Round-Robin

    • Similar to FCFS but preemption is added to switch between processes.
    • Run process for one time slice ( quantum ), then move to back of queue ( context switch ).
    • Each process gets equal share of the CPU. Most systems use some variant of this; time quantum ~ 10 - 100 ms
    • May produce poor turnaround time.
    Example:
      if quantum = 20 ms
      switching time = 5 ms
      % of CPU time 'wasted' = 5 / ( 20 + 5 ) = 20%

      if quantum = 500 ms, waste < 1%

      faster CPU can increase efficiency

  37. Shortest Job First
    • shortest time to completion first. This minimizes the average response time.
    • SJF works quite nicely; unfortunately, it requires knowledge of the future. Instead, we can use past performance to predict future performance.

    Example:

      Process burst time
      P16
      P28
      P37
      P43
      Gantt Chart

      SJF:
      P4P1 P3P2  
      0 3 9 16 24

      Average Turnaround time = ( 3 + 9 + 16 + 24 ) / 4 = 13

      Average waiting time = ( 0 + 3 + 9 + 16 ) / 4 = 7

    SJF is optimal

      first job burst time: a
      second job burst time: b
      third job burst time: c
      fourth job burst time: d

      mean turnaround time
          T = ( a + ( a + b ) + ( a + b + c ) + ( a + b + c + d )) /4
             = ( 4a + 3b + 2c + d ) / 4

      To minimize T, we need a ≤ b ≤ c ≤ d

    For interactive jobs, we can regard execution of each command as a job.

    For a certain terminal

      T -- predicted time
      t -- measured time
    Next predicted time:
      Tn+1 = tn + ( 1 - ) Tn
      if = 1, Tn+1 = tn,         only most recent history matters
      if = 0, Tn+1 = Tn = Tn-1 = ... = T0     recent history has no effect

      = 1 / 2
      Tn+1 = tn / 2   +   Tn / 2
      T1 = t0 / 2   +   T0 / 2
      T2 = t1 / 2   +   ( T0 / 2 + T1/2 ) / 2
        = t1 / 2   +   t0 / 4 + T0 / 4
      T3 = t2/2   +   t1 / 4   +   t0 / 8 + T0 / 8
      exponential decay -- More recent events has larger weights

    SJF can be preemptive or nonpreemptive

      nonpreemptive -- when shortest job comes in, it has to wait until the currently-executed job has finished

      preemptive -- when shortest job comes in, currently-executed job will be suspended
      then, shortest-remaining-time-first

    Example

      Process   Arrival time   Predicted time to finish
      P1 0 8
      P2 1 4
      P3 2 9
      P4 3 5

    nonpreemptive : P1, P2, P4, P3,

    Average waiting time = ( 0 + 7 + 9 + 15 ) /4 = 7.75

    preemptive
    P1 P2 P4 P1 P3
    0 1 5 10 17 26

    Average waiting time = ( 0 + ( 5 - 3 ) + ( 10 - 1 ) + ( 17 - 2 ) ) / 4 = 26 /4 = 6.5

  38. Multilevel Queue ( exponential queue ) Scheduling
    • Attach priority to each process ( e.g. faculty processes has higher priorites than students ).
    • In general, give newly runnable process a high priority and a very short time slice. If process uses up the time slice without blocking then decrease priority by 1 and double time slice for next time.
    • A process running too long may have priority adjusted.
    • Techniques like this one are called adaptive. They are common in interactive systems.
    • The CTSS system (MIT, early 1960's) was the first to use exponential queues.

  39. Lottery Scheduling
      Processes pick tickets and scheduler randomly select a winning ticket.

  40. Fair-Share Scheduling

    Each user is allocated some fraction of the CPU.

    For example, the system has only 2 users, each promised 50% use of CPU.
    User 1 has 4 processes: A, B, C, D.
    User 2 has only 1 process: a
    If round-robbin scheduling is used, then

      A a B a C a D a A a B a ...
    If User 1 owns 2/3 of CPU and User 1/3, then
      A B a C D a A B a ....

  41. Two-level Scheduling

    if insufficient main memory, some processes are kept in disk

      Processes in
      main memory
      Processes in
      disk
      a     b
      c     d
      e     f
      g     h
      e     f
      g     h
      a     b
      c     d
      b     c
      f     g
      a     d
      e     h

      level 1 scheduler -- schedule process in main memory

      level 2 scheduler -- shuffle process between memory and disk

  42. Multi-processor scheduling

    asymmetric multiporcessing -- has master-slave relation; master server does the scheduling

    symmetric multiprocessing:

    • one common ready queue
    • each processor is self-scheduling
    • each processor examines the common ready queue and selects a process to execute

  43. Real-time scheduling

    in hard real time systems, critical tasks are guanranteed, i.e. to be completed within a time limit:

    • a process is submitted along with a statement -- time to finish
    • scheduler either admits or rejects the process

    in soft real-time system, critical processes receive higher priority

    dispatcher -- context switch

    dispatch latency -- time to switch context

    In UNIX and many OS, to do context switch, it needs to wait for a system call to complete or wait for an I/O block → latency can be long

    to keep dispatch latency low, need to allow system calls preemptive:

    1. check at preemptive points to see if requests are of higher priority

      or

    2. make the entire kernel preemptive -- in this case, all kernel data must be protected

      if the high-priority process needs to read or modify kernel data that are currently accessed by another lower-priority process, → priority inversion; chain of processes --> priority-inheritance protocol

      • A higher-priority task H expects to be run as soon as it is ready. However, a lower-priority task L may be in a critical section (CS) that H needs. Then H must wait for L to finish.
      • If a medium-priority task M that does not need the CS arrives, it will be run; we say that a priority inversion has occurred.
      • This dangerous sequence of events is illustrated in above Figure.
      • Low-priority Task L and high-priority Task H share a resource.
      • Shortly after Task L takes the resource, Task H becomes ready to run. However, Task H must wait for Task L to finish with the resource, so it pends.
      • Before Task L finishes with the resource, Task M becomes ready to run, preempting Task L.
      • While Task M (and perhaps additional intermediate-priority tasks) runs, Task H, the highest-priority task in the system, remains in a pending state.

      e.g. Mars Pathfinder ( robot rover ) in 1997

  44. Threads

    process ~ program in execution + resources needed

    A thread ( light weight process ( LWP ) ) is a basic unit of CPU utilization ~ PC + registers + stack space

    a thread shares with peer threads its coded section, data section, and OS resources ( such as open files and signals ) ~ task

    traditionally heavy weight process = thread + task

    CPU switching much easier


    a) Three processes with one thread. b) One process with three threads.

    Also, some systems implement user-level threads in user-level libraries, rather than via system calls, so thread switching does not need to call the OS and to cause an interrupt to the kernel. This makes the switching to be done quickly.
    Thus, blocking a thread and switching to another thread is a reasonable solution to the problem of how a server can handle many requests efficiently.

    The benefits of using Threads:


    Each thread has its own stack.

    Comparison of multiple-thread and multiple-process control:

    multiple processesmultiple threads
    has states new, ready, running, blocked, terminated like a process, a thread has states new, ready, running, blocked, terminated
    processes can create child processes threads can create child threads
    each process operates independently of the others, has its own PC, stack pointer and address space threads are not independent of each other; threads can read or write over any other's stack

    Examples of Thread Applications:


    A word processor with three threads.


    A multithreaded Web server
  45. The dispatcher thread reads incoming requests from network.
  46. Chooses an idle (blocked) worker thread and passes the request.
  47. Dispatcher wakes up the worker thread to do the work.
  48. Implementing Threads:

    In user space
    In kernel space


    a) A user-level threads package. b) A threads package managed by the kernel.

    Hybrid Implementation


    Multiplexing user-kernel threads onto kernel-level threads.

    Pop-up Threads


    Creationg of a new thread upon arrival of a message. a) Before the message arrives. b) After the message arrives.

    Multithreading with SDL

    Simple DirectMedia Layer ( SDL ) is a cross-platform multimedia library with low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer. It is used by MPEG playback software, emulators, and many popular games.

  49. Start new thread with SDL_CreateThread():
      SDL_Thread *SDL_CreateThread ( int (*) func, void *data )
    • func -- Entry point ( function ) for the new thread; it takes one void * argument and returns an integer
    • data -- to be passed to func, could be NULL.
    • Returns -- pointer to an SDL_Thread structure that represents the newly created process.

  50. Terminate an SDL thread by SDL_KillThread():
      void SDL_KillThread ( SDL_Thread *id )

  51. Joining a thread ( waits for an SDL thread to terminate ) by SDL_WaitThread()
      void SDL_WaitThread ( SDL_Thread *id )

  52. Create a new mutex ( mutual exclusion flag ):
      SDL_mutex * SDL_CreateMutex()

      The mutex thus created is initially unlocked.

  53. Frees a mutex:
      void SDL_DestroyMutex( SDL_mutex *mutex )

  54. Locks a mutex:
      void SDL_mutexP ( SDL_mutex *mutex )

  55. Unlocks a mutex:
      void SDL_mutexV ( SDL_mutex *mutex )

  56. An Example
    //thread1.cpp
    // Example of SDL's portable threading API.
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <SDL/SDL.h>
     
    /* We must include SDL_thread.h separately. */
    #include <SDL/SDL_thread.h>
     
    static int counter = 0;
    SDL_mutex *counter_mutex;
     
    /* The three threads will run until this flag is set. */
    static int exit_flag = 0;
     
    /* This function is a thread entry point. */
    int ThreadEntryPoint(void *data)
    {
        char *threadname;
     
        /* Anything can be passed as thread data.
           We will use it as a thread name. */
        threadname = (char *) data;
                                                                                      
        /* Loop until main() sets the exit flag. */
        while (exit_flag == 0) {
            printf("This is %s! ", threadname);
                                                                                      
            /* Get a lock on the counter variable. */
            SDL_mutexP(counter_mutex);
                                                                                      
            /* We can now safely modify the counter. */
            printf("The counter is currently %i\n", counter);
            counter++;
                                                                                      
            /* Release the lock on the counter variable. */
            SDL_mutexV(counter_mutex);
                                                                                      
            /* Delay for a random amount of time. */
            SDL_Delay(rand() % 3000);
        }
                                                                                      
        printf("%s is now exiting.\n", threadname);
                                                                                      
        return 0;
    }
                                                                                      
    int main()
    {
        SDL_Thread *thread1, *thread2, *thread3;
                                                                                      
        /* Create a mutex to protect the counter. */
        counter_mutex = SDL_CreateMutex();
                                                                                      
        printf("Press Ctrl-C to exit the program.\n");
    
        /* Create three threads. Give each thread a name
           as its data. */
                                                                                      
        thread1 = SDL_CreateThread( ThreadEntryPoint, ( void *) "Thread 1");
        thread2 = SDL_CreateThread(ThreadEntryPoint, ( void *) "Thread 2");
        thread3 = SDL_CreateThread(ThreadEntryPoint, ( void *) "Thread 3");
                                                                                      
        /* Let the threads run until the counter reaches 20. */
        while (counter < 20)
            SDL_Delay(1000);
                                                                                      
        /* Signal the threads to exit. */
        exit_flag = 1;
        printf("exit_flag has been set by main().\n");
                                                                                      
        /* Give them time to notice the flag and exit. */
        SDL_Delay(3500);
                                                                                      
        /* Destroy the counter mutex. */
        SDL_DestroyMutex(counter_mutex);
                                                                                      
        return 0;
    }
    /*
    #Makefile
    CC = g++
    PROG    = thread1
    LIBSDL =  -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lSDL -lpthread
    INCLS   =  -I/usr/local/include/SDL
    #source codes
    SRCS = $(PROG).cpp
    #substitute .c by .o to obtain object filenames
    OBJS = $(SRCS:.cpp=.o)
     
    #$< evaluates to the target's dependencies,
    #$@ evaluates to the target
     
    $(PROG): $(OBJS)
            $(CC) -o $@ $(OBJS)   $(LIBSDL)
     
    $(OBJS):
            $(CC) -c  $*.cpp $(INCLS)
     
    clean:
            rm $(OBJS)
    */
    
    Dot Product by Thread Example
  57. Class Exercises

    1. At any given time, only one process can be executing instructions on a computer. True or False?
    2. What is the difference between processes that are awake and those that are asleep?
    3. A process may have zero parent processes. True or false?
    4. From where does an operating system load the execution for the process to be dispatched during a context switch.
    5. What is an alternative to interrupts and why is it rarely used?
    6. Why do distributed systems rely on message passing instead of signals?