310 likes | 545 Views
Deadlock. What is deadlock? A group of processes permanently stuck waiting for each other Deadlock Example Process P Process Q (1) Get A (5) Get B (2) Get B (6) Get A (3) Release B (7) Release A (4) Release A (8) Release B Possible execution paths 5, 6, 7, 8, … – No problem
E N D
Deadlock • What is deadlock? • A group of processes permanently stuck waiting for each other • Deadlock Example Process P Process Q (1) Get A (5) Get B (2) Get B (6) Get A (3) Release B (7) Release A (4) Release A (8) Release B • Possible execution paths • 5, 6, 7, 8, … – No problem • 5, 6, 1, … – P blocks, but will finish • 5, 1, … – Deadlock inevitable • 1, 5, … – Deadlock inevitable • 1, 2, 5, … – Q blocks, but will finish • 1, 2, 3, 4, … – No problem
Deadlock • Reusable Resources • Can only be used by one process at a time. After use, can be reassigned to another process (printer, memory, files, etc.) • In previous example, if “A” = file, and “B” = tape drive, deadlock can occur • Can occur with memory allocation if we don’t do swapping • 200K available, both processes want 80K, then make second request for 70K • Consumable Resources • Can be created or destroyed (signals, messages) • No fixed limit on # of resources • Can deadlock if both waiting for a message from the other
Conditions for Deadlock • All must hold for deadlock to occur • Mutual Exclusion • Only one process can hold the resource at any given time • Hold and Wait • A process may hold resources while requesting others • No Preemption • No resource may be forcibly removed from a process • Circular Wait (event) • A closed chain of process exists, each waiting for a resource held by the next process in the chain • Figure 6.5, page 273:
Deadlock Prevention • Can prevent deadlock by forbidding one of the conditions • Forbid Mutual Exclusion • Generally not reasonable • Multiple read-only access often ok • Forbid Hold and Wait • Ask for all resources at one time • May block a process for a long time when it can be making progress • Wait for resources it doesn’t yet need • May tie up unneeded resources • May hold resources that are not needed immediately • May not know what to request • Especially true for interactive processes • May request resources because they are needed in some instances, but not always
Deadlock Prevention • Forbid No Preemption • Take resources away from waiting processes • Only feasible if state can be saved • Examples: CPU, Memory • Not usable for Files, Printer, etc. • Forbid Circular Wait • Define a linear order on items • If it needs resources 3, 15, 6, 9, and 4 then must request in the order 3, 4, 6, 9, 15 • Cannot have circular wait because a process cannot have 9 and request 5 • May not be easy to define the order (files) • May force an awkward order of requesting resources • May have to request resource 3 in order to request 4, even when 4 is needed now but 3 is not needed until much later
Deadlock Avoidance • Allow general requests, but make choices to avoid deadlock • Assume we know the maximum requests (claims) for each process • Process must state it needs a max of 5 A objects, 3 B objects, 2 C objects. • Do not need to use its max claims • Ok to set max=5 and only use 3 • Can make requests at any time and in any order • Process Initiation Denial • Track current allocations • Assume all processes may make maximum requests at the same time • Only start process if it can’t result in deadlock regardless of allocations
Resource Allocation Denial • Also called banker’s algorithm • safe state – We can finish all processes by some scheduling sequence • Example: Finish P1, P4, P2, P5, P3 • Reject a request if it exceeds the processes’ declared maximum claims • Grant a request if the new state would be safe • Determining if a state is safe • Find any process Pi for which we can meet it’s maximum requests • Don't forget already allocated resources • Mark Pi as “done”, add its resources to available resource pool • State is safe if we can mark all processes as “done” • Block a process if the resources are not currently available or the new state is not safe
Avoidance Example • Figure 6.6, page 277 • Claim Matrix: A B C P1 3 2 2 P2 6 1 3 P3 3 1 4 P4 4 2 2 • Allocation Matrix: A B C P1 1 0 0 P2 5 1 1 P3 2 1 1 P4 0 0 2 • Total Resources: 9 3 6 • Available: 1 1 2 • P1 wants 1 A and 1 C, is it ok? • P4 wants 1 B, is it ok? • Still requires the process to declare its max request at start
Deadlock Detection • Avoidance methods tend to limit access to resources • Instead, grant arbitrary requests and note when deadlock happens • Can vary how often we check • Early detection vs. overhead of checks • Algorithm (Page 280) • Preparation: • Create table of process requests, current allocations • Note available resources • Mark processes with no resources • Mark any process whose requests can be met (requests £ available resources) • Include resources from that process as ‘available’ (this process can finish) • If multiple processes available, pick any • If any processes cannot be marked, they are part of a deadlock
Detection Example • Figure 6.9, page 281 • Requests: A B C D E P1 0 1 0 0 1 P2 0 0 1 0 1 P3 0 0 0 0 1 P4 1 0 1 0 1 • Allocation: A B C D E P1 1 0 1 1 0 P2 1 1 0 0 0 P3 0 0 0 1 0 P4 0 0 0 0 0 • Available 0 0 0 0 1 • Mark P4 (nothing allocated) • Mark P3, new available: 0 0 0 1 1 • Cannot mark P1 or P2, so we are deadlocked
Detection Example 2 • Requests: A B C P1 0 0 0 P2 2 0 2 P3 0 0 0 P4 1 0 0 P5 0 0 2 • Allocation: A B C P1 0 1 0 P2 2 0 0 P3 3 0 3 P4 2 1 1 P5 0 0 2 • Available 0 0 0 • Is this system deadlocked?
Deadlock Recovery • Several possible approaches • Abort all deadlocked processes • Simple but common • Back up processes to a previously saved checkpoint, then restart • Assumes we have checkpoints and a rollback mechanism • Runs risk of repeating deadlock • Assumes that the deadlock has enough timing dependencies it won’t happen • Selectively abort processes until deadlock broken • Preempt resources until deadlock broken • Must roll back process to checkpoint prior to acquiring key resource
Mixed Strategy • May group resources into classes, have a different deadlock strategy for each class • Swap Space • Prevent Deadlocks by requiring all space to be allocated at once • Avoidance also possible • Tapes/Files • Avoidance can be effective here • Prevention by ordering resources also possible • Main Memory • Preemption a good approach • Internal Resources (channels, etc.) • Prevention by ordering resources • Can use linear ordering between classes
Dining Philosophers • Five philosophers who alternately think and eat • Shares a fork with each neighbor • Assume each philosopher picks up left fork, then right fork, then eats • Deadlock if all enter at once • Figure 6.10, page 283
Solving Dining Phil. • Buy more Forks • Equivalent to increasing resources • Put fork down if 2nd fork busy • Can produce “livelock” if philosophers stay synchronized • Room Attendant • Only let 4 of the philosophers into the room at once • May have 4 philosophers in room, but only 1 can eat • Left-Handed Philosophers • Grab forks in the other order (right fork, then left fork) • Any mix will avoid deadlock (effective linear ordering on forks)
Linux Fork() • fork() • returns 0 to child • return child_pid to parent • Parent is responsible to look after children • Failure to do so can create zombies • pid = wait( &status ) to explicitly wait for the child to terminate • signal(SIGCHLD, SIG_IGN) to ignore child-termination signals • May also set SIGCHLD to call a specified subroutine when a child dies
#include <stdio.h> #include <signal.h> void main() { int cnt = 15; int pid; char buff[100]; /* create a new process */ if ((pid = fork()) == -1) { perror("Fork failed, call system manager!"); exit(1); } if (pid) { /* we are the parent, tell system to ignore child termination messages */ signal(SIGCHLD, SIG_IGN); while (cnt--) { /* lets look for the child */ printf("The child has a pid of %d, my pid is %d\n", pid, getpid()); printf("----------------------\n"); sprintf(buff, "%s %d", "ps -aux | grep ", pid); system(buff); /* execute the command */ printf("----------------------\n"); sleep(1); /* let some time pass… */ } exit(0); } else { /* here is the child code */ int cnt = 8; while (cnt) { printf("[child] I'm alive...cnt = %d\n", cnt--); sleep(1); } printf("[child] Preparing to exit with status 7\n"); exit(7); /* quit */ } } /* end of program */ Fork() Example
Linux Semaphores • Key - 4 byte value for the “name” • Create/access semaphores • id = semget( KEY, Count, IPC_CREAT | PERM ) • id = semget( KEY, 0, 0 ) • Perm = file permission bits (0666) • to create only (fails if exists) use IPC_CREAT|IPC_EXCL|PERM • Count = # of semaphores to create • Working with semaphore value • i = semctl( id, num, SETVAL, v ) • val= semctl( id, num, GETVAL, 0 ) • Deleting semaphores • i = semctl( id, 0, IPC_RMID, 0 ) • ipcs – Program to list semaphores/etc. • ipcrm sem id – Program to delete a semaphore
Semaphore Operations • Semaphore Structure struct sembuf { short sem_num; (semaphore ID) short sem_op; (change amount) short sem_flg; (wait/don’t wait) } op; • sem_flg can be 0 or IPC_NOWAIT • Wait(): • op.sem_op = -1 (decrement) • res = semop( id, &op, 1 ) • Signal(): • op.sem_op = 1 (increment) • res = semop( id, &op, 1 ) • Can do multiple signal/wait ops • res = semop( id, oparray, opcount )
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <errno.h> #define PERM 0666 #define KEY 0x12348765 main() {int id, result; id = semget(KEY,1, IPC_CREAT|IPC_EXCL|PERM); printf("status of create = %d\n", id); if(id == -1){perror("Bad Semaphore Create"); exit(0);} /* set the value to one */ result = semctl(id,0,SETVAL,1); if(result == -1){ perror("Bad SetVal call");exit(0);} printf("Semaphore created with an id of %d\n",id); } Create Example
Wait Example #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <errno.h> #define KEY 0x12348765 main() {int id, result; struct sembuf op[1]; id = semget(KEY,0,0); printf("status of get key = %d\n",id); if(id == -1) {perror("Bad Semaphore ID fetch"); exit();} /* initialize the op data structure */ op[0].sem_num = 0; /* the first one */ op[0].sem_op = -1; /* wait until we can add 1 */ op[0].sem_flg = 0; /* no options */ /* now try to get past the semaphore */ printf("Ready to test semaphore\n"); result = semop(id,op,1); if(result == -1){ perror("Bad Wait"); exit();} printf("Got past semaphore\n"); }
Shared Memory • Create/Access Shared Memory • id = shmget( KEY, Size, IPC_CREAT | PERM ) • id = shmget( KEY, 0, 0 ) • Deleting Shared Memory • i = shmctl( id, IPC_RMID, 0 ) • Or use ipcrm • Accessing Shared Memory • memaddr = shmat( id, 0, 0 ) • memaddr = shmat( id, addr, 0 ) • Addr should be multiple of SHMLBA • memaddr = shmat( id, 0, SHM_READONLY ) • System will decide address to place the memory at • shmdt( memaddr ) • Detach from shared memory
Creating Shared Memory #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <errno.h> #define SIZE 512 /* shared mem buffer size */ #define PERM 0666 /* all can read write */ #define KEY 'JHL ' /* our key */ main() { int id; id = shmget(KEY,SIZE, IPC_CREAT|IPC_EXCL|PERM ); printf("status of create = %d\n",id); if(id == -1) perror("Bad Create"); }
Writing Shared Memory #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <string.h> #include <errno.h> #include <stdio.h> #define SIZE 512 /* shared memory buffer size */ #define KEY 'JHL ' /* our key */ main() { key_t key; int memoryid; char * location; /* the shmget will fail if the segment has not been created */ memoryid = shmget(KEY,SIZE,0); printf("status of key retrieval = %d\n",memoryid); if(memoryid == -1) {perror("Bad Key");exit();} /* we got the key now attach to the segment we will allow the system to chose the location (second parm) and we will want read/write access (third parm) */ location = (char *)shmat(memoryid,0,0); /* we want read write access */ printf("status of attach = %d\n",location); if (location == (char*)-1) {perror("Bad Attach");exit();} /* memory has been attached to,now copy string into the location */ printf("Enter string -> "); gets(location); printf("location contains: %s\n",location); }
Unix Pipes • Creates a one-way connection between processes • Created using pipe() call • Returns a pair of file descriptors • pend[0] = read end • pend[1] = write end • Use dup() call to convert to stdin/stdout • Example: int pend[2] pipe( pend ) fork() parentchild close(pend[1]) close(pend[0]) write(pend[1],…) read(pend[0],…) close(pend[0]) close(pend[1])
Unix Pipe Example /* A simple "pipe" demo --- Author: Jerry LeVan */ #include <stdio.h> #define DATA "Sample Data Sent Through A Pipe" main() { int sockets[2], child; /* create a pipe; doing it here assures that it will be open in the child */ if(pipe(sockets) <0){ perror("opening stream socket pair"); exit(10); } if((child = fork()) == -1) /* fork the child */ perror("fork"); else if (child) { char buf[1024]; /* this is the parent */ sleep(4); /* let the child do its stuff */ close(sockets[1]); /* close "write" side */ if ( read(sockets[0],buf,1024) < 0) perror("reading message"); printf("-->%s\n",buf); /* Do you know why this works? */ close(sockets[0]); /* shut "read" side */ }else { /* this is the child. It writes to the parent */ close(sockets[0]); /* close "read" side */ if ( write(sockets[1],DATA,sizeof(DATA)) < 0) perror("writing message"); close(sockets[1]); /* close "write" side */ printf("[child] I am outa here...\n"); exit(0); } }
Unix Messages • Send information (type + data) between processes • Message Structure • long type • char text[] • Functions: • id = msgget( KEY, IPC_CREAT|…) • id = msgget( KEY, 0 ) • msgctl( id, IPC_RMID ) • msgsnd( id, buf, text_size, flags ) • msgrcv( id, buf, max_size, flags ) • Useful Flags • IPC_NOWAIT • MSG_NOERROR (truncate long messages to max_size)
Other Unix Synchronization • Semaphores (discussed earlier) • Shared Memory (discussed earlier) • Signals • Used to inform processes of asynchronous events • Set up handler using • void handler( int sig_num ) { … } • sig_num is signal number so one handler can respond to several signals • signal( SIGxxx, handler) • One-time event – Must reset within handler if you want to keep handling this signal (Linux) • special value for handler • SIG_IGN – Ignore signal • SIG_DFL – Default action • Partial list (Table 6.2, page 288) • SIGHUP, SIGQUIT, SIGKILL, SIGALARM, SIGUSR1, SIGCLD • Send signal using kill command/call
Solaris • Both Kernel and User-level calls • Mutual Exclusion Lock • mutex_enter() – Waiting processes can busy-wait or be suspended • mutex_exit() – Release lock • mutex_tryenter() – Get lock if not held • Condition Variables • Used in conjunction with a mutex lock • cv_wait() –The process will release the associated mutex before waiting, reacquire it before continuing • cv_signal() – Wake up one thread waiting on a given condition • cv_broadcast() – Wake up all threads • Mutex + Condition Variables can be used to implement monitors
Solaris • Semaphores • sema_p() – Traditional Wait() • sema_v() – Traditional Signal() • sema_tryp() • Reader/Writer Lock • rw_enter() – Get lock as a reader or writer • rw_exit() – Release a lock • rw_tryenter() – Try to get a lock, but don’t wait if it isn’t available • rw_downgrade() – Convert a write lock to a read lock • rw_tryupgrade() – Try to convert a read lock into a write lock
Windows 2000 • Synchronization Objects (table 6.3, page 292) • Process – Signals on termination • Thread – Signals on termination • File – Signal on I/O operation complete • Console Input – Signal on data ready • File Change Notification – Signal when a file system change matching a filter happens • Mutex – Provides mutual exclusion • Semaphore – Counting semaphore • Event – Announcement a system event has happened • Waitable Timer – Counter that records the passage of time