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


Life consists not in holding good cards but in playing those you hold well.

							Josh Billings

Deadlocks
  1. Introduction

    processes are blocked, waiting on events that could never happen

    different from starvation ( the associated resource is in continuous use )

  2. System Model
  3. A system consists of a finite number of resources to be distributed among a number of competing processes.
  4. Resources can only be used by a single process at any single instance of time
  5. Resources can be of different types, e.g. memory space, CPU cycles, files, I/O devices
  6. A process must request a resource before using it, and must release the resource after using it
  7. A normal operation consists of a sequence of events:
    • Request: if request cannot be granted immediately, process must wait until it can acquire the resource
    • Use: operate on the resource ( e.g. printing )
    • Release: release the resource

      Examples:

      open file, read file, close file
      allocate memory, use memory, free memory
  8. A set of processes is in a deadlock state when every process in the set is waiting for an event that can be caused by onother process in the set.
  9. Deadlock modeling

    Four necessary conditions for deadlock:

    e.g. Resources are two books: Java Programming, OS Principles,
           need the two books to finish project

    Resource-allocation graphs


    the process is holding the resource
     
    the process is requesting the resource

    cycle ~ deadlock

    Deadlock

    if graph contains no cycles => no process is in deadlock

    if graph contains cycles => deadlock may exist

    Examples

    Example 1

  10. Concurrent processes: A, B, C

  11. Shared resources: R, S, T

  12. Left to run on their own, each process might issue requests as follows:

    PROCESSABC
    requestRST
    requestSTR
    releaseRST
    releaseSTR

  13. Depending on how the OS schedules the processes A, B and C, this may or may not cause deadlock

  14. If the OS runs A to completion, then B, then C, there is no problem, but this is not regularly the case (efficiency)

  15. OS blocks a process waiting for a resource that is not available and runs some other process

  16. A deadlock can occur if OS scheduling causes requests to occur as follows:

    1A → R
    2B → S
    3C → T
    4A → S
    5B → T
    6C → R

  17. After request 4 has been made, process A blocks waiting for S which is used by process B

  18. In the next two steps B and C also block, as they request resources held by C and A, respectively

  19. A cycle A-S-B-T-C-R-A is formed as shown in figure, implying DEADLOCK

  20. Could OS avoid deadlock in the example?

    • OS can schedule A, B and C in a different order based on some analysis of the allocation graph

    • OS could run only A and C together, avoiding closed loop and hence the deadlock

    • Afterwards, B can be run on its own, with no danger of deadlock
  21. Reduction of Resource Graph

    If a process's resource requests can be granted, then we say that the graph can be reduced by that process
    ( i.e. the process is allowed to complete its execution and return all its resources )

    If a graph can be reduced by all the processes, then there is no deadlock.
    If a graph cannot be reduced by all the processes, the irreducible processes constitute the set of deadlock processes in the graph
    An example of reduction of an RAG ( Resource Allocation Graph )

    Example of RAG with cycle but no deadlock

    Examples

    Code with potential deadlock:

    /*
      deadlock.cpp
      A simple example demonstrating the occurrence of deadlock.
      Compile: g++ -o deadlock deadlock.cpp -lSDL -lpthread
      Execute: ./deadlock
    */
                                                                                   
    #include <SDL/SDL.h>
    #include <SDL/SDL_thread.h>
    #include <stdlib.h>
                                                                                   
    using namespace std;
                                                                                   
    bool value_consumed = true;
    SDL_mutex *mutexa, *mutexb;     //mutexes for accessing a, b
    bool quit = false;
    int a = 0, b = 0;
    int thread1 ( void *data )
    {
      char *tname = ( char * )data;
                                                                                   
      while ( !quit ) {
        printf("I am %s. ", tname );
        SDL_mutexP ( mutexa );      //lock before processing a
        printf("%s has locked a\n", tname);
        ++a;                        //dummy instruction
        //do somehting
        SDL_Delay( 10 );
                                                                                   
        //also need b value
        SDL_mutexP ( mutexb );      //lock before accessing b
        printf("%s has locked b\n", tname);
        a += b++;                   //dummy instruction
                                                                                   
        //release lock b
        SDL_mutexV ( mutexb );
        //release lock a
        SDL_mutexV ( mutexa );
                                                                                   
        //delay for a random amount of time
        SDL_Delay ( rand() % 3000 );
      }
      printf("%s quits\n", tname );
                                                                                   
      return 0;
    }
                                                                                   
    int thread2 ( void *data )
    {
      char *tname = ( char * )data;
                                                                                   
      while ( !quit ) {
        printf("I am %s. ", tname );
        SDL_mutexP ( mutexb );      //lock before processing b
        printf("%s has locked b\n", tname);
        ++b;                        //dummy instruction
        //do somehting
        SDL_Delay( 10 );
                                                                                   
        //also need a value
        SDL_mutexP ( mutexa );      //lock before accessing a
        printf("%s has locked b\n", tname);
        a += b++;                   //dummy instruction
                                                                                   
        //release the lock a
        SDL_mutexV ( mutexa );
        //release the lock b
        SDL_mutexV ( mutexb );
                                                                                   
        //delay for a random amount of time
        SDL_Delay ( rand() % 3000 );
      }
      printf("%s quits\n", tname);
                                                                                   
      return 0;
    }
                                                                                   
    int main ()
    {
      SDL_Thread *id1, *id2;                //thread identifiers
      char *tnames[2] = { "Arnold", "Groper" };     //names of threads
                                                                                   
      mutexa = SDL_CreateMutex();
      mutexb = SDL_CreateMutex();
                                                                                   
      //create the threads
      id1 = SDL_CreateThread ( thread1, tnames[0] );
      id2 = SDL_CreateThread ( thread2, tnames[1] );
                                                                                   
      //experiment with 20 seconds
      for ( int i = 0; i < 10; ++i )
          SDL_Delay ( 2000 );
                                                                                   
      quit = true;                  //signal the threads to return
                                                                                   
      //wait for the threads to exit
      SDL_WaitThread ( id1, NULL );
      SDL_WaitThread ( id2, NULL );
                                                                                   
      return 0;
    }
       

    Deadlock-free code:

    /*
      deadlock1.cpp
      A simple example demonstrating  deadlock-free case.
      Compile: g++ -o deadlock1 deadlock1.cpp -lSDL -lpthread
      Execute: ./deadlock
    */
                                                                                   
    #include <SDL/SDL.h>
    #include <SDL/SDL_thread.h>
    #include <stdlib.h>
                                                                                   
    using namespace std;
                                                                                   
    bool value_consumed = true;
    SDL_mutex *mutexa, *mutexb;     //mutexes for accessing a, b
    bool quit = false;
    int a = 0, b = 0;
    int thread1 ( void *data )
    {
      char *tname = ( char * )data;
                                                                                   
      while ( !quit ) {
        printf("I am %s. ", tname );
        SDL_mutexP ( mutexa );      //lock before processing a
        printf("%s has locked a\n", tname);
        ++a;                        //dummy instruction
        //do somehting
        SDL_Delay( 10 );
                                                                                   
        //also need b value
        SDL_mutexP ( mutexb );      //lock before accessing b
        printf("%s has locked b\n", tname);
        a += b++;                   //dummy instruction
                                                                                   
        //release lock b
        SDL_mutexV ( mutexb );
        //release lock a
        SDL_mutexV ( mutexa );
                                                                                   
        //delay for a random amount of time
        SDL_Delay ( rand() % 3000 );
      }
      printf("%s quits\n", tname );
                                                                                   
      return 0;
    }
                                                                                   
    int thread2 ( void *data )
    {
      char *tname = ( char * )data;
                                                                                   
      while ( !quit ) {
        printf("I am %s. ", tname );
        SDL_mutexP ( mutexa );      //lock before accessing a
        printf("%s has locked b\n", tname);
        a += b;                   //dummy instruction
        
        SDL_mutexP ( mutexb );      //lock before processing b
        printf("%s has locked b\n", tname);
        ++b;                        //dummy instruction
        //do somehting
        SDL_Delay( 10 );
                                                                                   
                                                                                   
        //release the lock b
        SDL_mutexV ( mutexb );
        //release the lock a
        SDL_mutexV ( mutexa );
                                                                                   
        //delay for a random amount of time
        SDL_Delay ( rand() % 3000 );
      }
      printf("%s quits\n", tname);
                                                                                   
      return 0;
    }
                                                                                   
    int main ()
    {
      SDL_Thread *id1, *id2;                //thread identifiers
      char *tnames[2] = { "Arnold", "Groper" };     //names of threads
                                                                                   
      mutexa = SDL_CreateMutex();
      mutexb = SDL_CreateMutex();
                                                                                   
      //create the threads
      id1 = SDL_CreateThread ( thread1, tnames[0] );
      id2 = SDL_CreateThread ( thread2, tnames[1] );
                                                                                   
      //experiment with 20 seconds
      for ( int i = 0; i < 10; ++i )
          SDL_Delay ( 2000 );
                                                                                   
      quit = true;                  //signal the threads to return
                                                                                   
      //wait for the threads to exit
      SDL_WaitThread ( id1, NULL );
      SDL_WaitThread ( id2, NULL );
                                                                                   
      return 0;
    }
    

    A deadlock example in Java:

    DeadlockExamp.java

  22. Deadlock handling strategies

  23. Ignore the problem altogether
  24. Prevention -- use a protocol to ensure that the system will never enter a deadlock state
  25. Detection and recovery -- allow the system to enter a deadlock state and then recover
  26. Dynamic avoidance -- by careful resource allocation
  27. a) The Ostrich Algorithm

    pretend there's no problem ( Windows, UNIX )
    undetected deadlock may result in deterioration of the system performance; eventually, the system will stop functioning and will need to be restarted manually

    b) Detection and Recovery

    system monitors requests and releases resources

    check resource graph to see if cycle exists; kill a process if necessary

    or if a process has been blocked for, say one hour, kill it

    c) Deadlock Prevention

  28. Mutual exclusion
    printer cannot be shared by 2 processes
    read-only file -- sharable
    A process never has to wait for a sharable resource
    In general, it is not possible to prevent deadlocks by denying mutual-exclusion condition

  29. Hold and wait
    have all processes request all their resources before start execution -- inefficient, also, process don't know in advance resources needed

    before a process can request any additional resources, it must release temporarily all the resources that it currently holds, if successful, it can get the original resources back
    may have starvation

  30. No preemption
    allows preemption
    OK, what if its printer

  31. Circular wait

    order resource types
    process requests resources in an increasing order

    Let R = { R1, R2, ..., Rm } be the set of resource types

    F: R → N ( set of natural numbers )

    e.g.

      F ( tape drive ) = 1
      F ( disk drive ) = 5
      F ( printer ) = 12

    Suppose a process first requests Ri, it can request Rj only if F(Rj) > F(Ri)

    Under this constraint, circular-wait condition cannot hold

    Proof

    by contradiction

    Suppose circular wait holds for
    { P0, P1, ..., Pn }

    Pi is waiting for Ri which is held by Pi+1

    Because Pi+1 is holding Ri while requesting Ri+1, we have

      F( Ri ) < F( Ri+1 ) for all i
      => F( R0 ) < F( R1 ) < ... < F( Rn ) < F( R0 )
      => F( R0 ) < F( R0 )
    which is impossible.
    Thus circular wait does not exist.
  32. d) Deadlock Avoidance

    In prevention strategy, we restrain how request can be made.

    Here, we want to develop an algorithm to avoid deadlock by making the right choice all the time

  33. Banker's Algorithm for Single Resource Type

    Dijkstra's Banker's Algorithm is an approach to trying to give processes as much as is possible, while guaranteeing no deadlock.

    safe state -- a state is safe if the system can allocate resources to each process in some order and still avoid a deadlock.

    bank:

      credits available = 10 units
      Process   Using
      ( allocated )  
      Maximum needs
      P0 06
      P1 05
      P2 04
      P3 07

    At time t0, the system is safe.

    At time t1:

      credits available = 2 units
      Process   Using
      ( allocated )  
      Maximum
      needs
        Maximum
      requests
      P0 165
      P1 154
      P2 242
      P3 473

    credits left = 2 ( i.e. available = 2 ), state is still safe

    delay any requests from P0, P1, P3

    can grant further requests to P2, why?


      credits available = 1 unit
      Process   Using
      ( allocated )  
      Maximum
      needs
        Maximum
      requests
      P0 165
      P1 253
      P2 242
      P3 473

    This is an unsafe state; with only one credit left, if all processes request their maximum units, all need to be blocked and wait → deadlock

    Of course, unsafe state may not lead to deadlock but the bank cannot count on this.

    The banker's algorithm is to consider each request as it occurs,
    if granting the request would result in a safe state, request is granted
    otherwise, it is postponed

  34. Banker's Algorithm for Multiple Resource Type

    Let

    n = # of processes in system
    m = # of resource types

    Example

      a b c d
    EXISTING = ( 6 3 4 2 )
    POSSESSED = ( 5 3 2 2 )
    AVAILABLE = ( 1 0 2 0 )

    Notation

    for two vectors X, Y of length m,
    X ≤ Y if and only if X[i] ≤ Y[i]   for all i = 1, 2, ..., m

    e.g. A = ( 1, 7, 3, 2 ), and B = ( 0, 3, 1, 1 ) then B ≤ A

    X < Y if X ≤ Y and X ≠ Y
    i.e. at least one element of X is strictly smaller than the corresponding element of Y

    Safe Algorithm

    1. Assume m resource types, n processes
      define two matrices
        AVAILABLE = ( ... )   of length m

        TERMINATE = ( ... )   of length n

    2. initialize
        AVAILABLE = available resources

        TERMINATE[i] = false   for i = 1, 2, .. n ( i.e. no process is terminated )

    3. look for an i such that
        a. TERMINATE[i] = false     ( consider one element of TERMINATE )
        b. NEED[i] ≤ AVAILABLE     ( consider one row of NEED )

      if no such i exists, ( i.e. either all terminated or may lead to deadlock ), go to step 5

    4. Set
        TERMINATE[i] = ture     ( i.e. we can run the process to the end )
        AVAILABLE = AVAILABLE + ALLOCATED[i]     ( the terminated process can return all the resources )
      go to step 3

    5. if TERMINATE[i] == true for all i, the system is safe, otherwise it is not safe

    In our example, is the state safe ?

    Now suppose P5 wants 2 c ( e.g. printers )

    Suppose we grant the 2 c to P5, will the state be safe? Let's check:
    ALLOCATED=
    3 0 1 1
    0 1 0 0
    1 1 1 0
    1 1 0 1
    0 0 2 0
     NEED =
    1 1 0 0
    0 1 1 2
    3 1 0 0
    0 0 1 0
    2 1 0 0

    Available = ( 1000 )

    Then the remaining resources ( AVAILABLE ) are not sufficient to satisfy the max need of any process to run to the end. Thus the state is not safe. Therefore the request of P5 must not be granted at this moment.

    Note that an unsafe state does not necesarily lead to deadlock.

    Weaknesses of the Banker's Algorithm

  35. requires users to state their maximum needs in advance

  36. required fixed number of resources to allocate

  37. requires population of users fixed

  38. requires all tasks return all resources within a finite time
  39. Resource Trajectories

    A deadlock path

    An avoidance path

  40. Class Exercises

    1. Processes do not deadlock as a result of contending for the processor. True or false?
    2. Nonpreemptible resources must be hardware. True or false?
    3. Compare and contrast deadlock prevention and deadlock avoidance.
    4. An unsafe state is a deadlocked state. True or false?
    5. Two processes, A and B, each needs three records, 1, 2, and 3 in a database. Is deadlock possible, if both processes request the records in the order 1, 2, 3. How about if they both request the records in the order 2, 1, 3? How about A requests in order 1, 2, 3 and B requests in order 3, 2, 1?