Clase de teoría sem. 11 · Deadlock (lo que añade al libro)
🎯 Al terminar podrás
- Distinguir starvation, livelock y deadlock con un ejemplo de cada uno.
- Encontrar el deadlock en código real (productor–consumidor con los P() invertidos, transferencias con dos locks) y corregirlo.
- Decidir con el grafo de asignación si un ciclo es deadlock según las instancias, y construir el grafo de espera.
- Ejecutar a mano el banquero (Need, seguridad y peticiones) y el algoritmo de detección.
Antes necesitas: Coffman y los tres enfoques
- Detective escribe tu hipótesis del bug antes de abrir el diagnóstico
- Laboratorio sube la escalera: resuelto → completa → solo → variante
- Examen cronométrate y no abras la solución hasta terminar
Starvation, livelock y deadlock
Sección titulada «Starvation, livelock y deadlock»| Qué pasa | Ejemplo de la clase | |
|---|---|---|
| Starvation | Un hilo espera indefinidamente, aunque el sistema sigue avanzando | hilos de baja prioridad que nunca corren porque siempre hay otros de alta |
| Livelock | Un caso especial de starvation: los estados de los procesos cambian todo el tiempo, pero ninguno progresa | dos hilos que se ceden el paso eternamente (como el trylock + reintentar del lab 11) |
| Deadlock | Espera circular por recursos: nadie se mueve | el hilo A tiene Res 1 y espera Res 2; el hilo B tiene Res 2 y espera Res 1 |
Relación clave: todo deadlock es starvation, pero no al revés. En un deadlock los procesos involucrados esperan para siempre, pero puede haber starvation sin ningún ciclo.
Modelo del sistema: tipos de recurso
Sección titulada «Modelo del sistema: tipos de recurso»- Los recursos son de tipos R1 … Rm, y cada tipo Ri tiene Wi instancias.
- Cada proceso usa un recurso en tres pasos: 1) pedir (request), 2) usar (use), 3) liberar (release).
| Clasificación | Qué significa | Ejemplos |
|---|---|---|
| Expropiable (preemptible) | el SO puede quitarlo sin problema | CPU |
| No expropiable | no se le puede quitar al hilo | espacio en disco, impresora, derecho a entrar a una sección crítica |
| Compartible vs exclusivo | ¿varios lo pueden usar a la vez? | archivo de solo lectura (compartible) vs impresora (exclusivo) |
| Reutilizable vs consumible | como en el libro | memoria y semáforos vs mensajes y señales |
Expropiar (preemption) es interrumpir temporalmente lo que hace una tarea, con la intención de reanudarla después.
Ejemplo 1 · Productor–consumidor con los P() invertidos
Sección titulada «Ejemplo 1 · Productor–consumidor con los P() invertidos»La versión correcta pide primero el semáforo de conteo y después el mutex:
Producer(item) { Consumer() { emptySlots.P(); fullSlots.P(); mutex.P(); mutex.P(); Enqueue(item); item = Dequeue(); mutex.V(); mutex.V(); fullSlots.V(); emptySlots.V();} return item; }La clase cambia el orden solo en el productor: primero mutex.P() y después emptySlots.P().
Ejemplo 2 · Deadlock por orden de locks
Sección titulada «Ejemplo 2 · Deadlock por orden de locks»Dos hilos que toman dos mutex en orden opuesto (recurso reutilizable):
void *do_work_one(void *param) { void *do_work_two(void *param) { pthread_mutex_lock(&first_mutex); pthread_mutex_lock(&second_mutex); pthread_mutex_lock(&second_mutex); pthread_mutex_lock(&first_mutex); ... ...} }Si el hilo 1 toma first_mutex y el hilo 2 toma second_mutex, cada uno espera el del otro.
Transferencias bancarias (lock ordering deadlock): aquí el orden lo deciden los argumentos.
void transaction(Account from, Account to, double amount) { lock1 = get_lock(from); lock2 = get_lock(to); acquire(lock1); acquire(lock2); withdraw(from, amount); deposit(to, amount); release(lock2); release(lock1);}La transacción 1 hace A → B (50), las dos al mismo tiempo.
Prevención: los detalles de la clase
Sección titulada «Prevención: los detalles de la clase»- Exclusión mutua: no hace falta para recursos compartibles (archivos de solo lectura); sí para los no compartibles.
- Hold and wait, dos opciones:
- pedir y recibir todos los recursos antes de empezar;
- pedir recursos solo cuando no se tiene ninguno.
- Desventajas: baja utilización de los recursos y posible starvation.
- No expropiación: si un proceso que tiene recursos pide uno que no se le puede dar ya, libera todo lo que tiene. Lo liberado pasa a su lista de espera y el proceso se reinicia solo cuando puede recuperar lo viejo y lo nuevo.
- Espera circular: un orden total de los tipos de recurso, pidiéndolos en orden creciente.
Grafo de asignación y cuándo un ciclo es deadlock
Sección titulada «Grafo de asignación y cuándo un ciclo es deadlock»- Arista de petición: Pi → Rj.
- Arista de asignación: Rj → Pi.
- Arista de reclamo (claim): Pi ⇢ Rj, punteada. Indica que Pi podría pedir Rj en el futuro; se usa en la evitación.
| El grafo… | Conclusión |
|---|---|
| No tiene ciclos | no hay deadlock |
| Tiene un ciclo y una instancia por tipo | hay deadlock |
| Tiene un ciclo y varias instancias por tipo | posibilidad de deadlock: hay que analizar |
El ejercicio de la clase («¿este grafo representa un deadlock?»): no lo hay si se puede ir liberando.
- T3 termina y libera R4 y R2.
- Con eso, T2 termina y libera R1, R2 y R3.
- Luego T1 termina.
Si existe un orden así, no hay deadlock aunque haya un ciclo. En los dos grafos siguientes de la clase, uno sí tiene deadlock (nadie puede terminar) y el otro no, porque un hilo fuera del ciclo (T2 o T4) libera una instancia.
Algoritmos según el número de instancias
Sección titulada «Algoritmos según el número de instancias»| Instancias | Evitación | Detección |
|---|---|---|
| Una por tipo | analizar el grafo de asignación | grafo de espera (wait-for): los nodos son solo procesos, con Pi → Pj si Pi espera a Pj. Hay deadlock si y solo si hay un ciclo. Buscarlo cuesta O(n²) |
| Varias por tipo | banquero | algoritmo con Available, Allocation y Request, de costo O(m × n²) |
El grafo de espera se obtiene del grafo de asignación eliminando los nodos de recurso y uniendo directamente al que espera con el que tiene el recurso.
El banquero en notación Silberschatz (como en la clase)
Sección titulada «El banquero en notación Silberschatz (como en la clase)»- Need = Max − Allocation.
- Petición de Pi:
- si Request ≤ Need, ir al paso 2; si no, error (superó su máximo);
- si Request ≤ Available, ir al paso 3; si no, esperar;
- simular: Available −= Request, Allocation += Request, Need −= Request. Si el estado es seguro se asigna; si no, se restaura y Pi espera.
- Seguridad:
- Work = Available y Finish = false;
- buscar un i con Finish[i] = false y Need_i ≤ Work, hacer Work += Allocation_i y Finish[i] = true, y repetir;
- si todos tienen Finish = true, el estado es seguro.
- Estado seguro (definición de la clase): existe una secuencia ⟨P1 … Pn⟩ con todos los procesos en la que lo que Pi aún puede pedir se cubre con lo disponible más lo que tienen todos los Pj con j < i.
- Seguro ⇒ no hay deadlock. Inseguro ⇒ posibilidad de deadlock. La evitación garantiza no entrar nunca en un estado inseguro.
Ejemplo clásico de Silberschatz: 5 procesos y recursos A = 10, B = 5, C = 7.
| Allocation (A B C) | Max (A B C) | Need = Max − Alloc | |
|---|---|---|---|
| P0 | 0 1 0 | 7 5 3 | 7 4 3 |
| P1 | 2 0 0 | 3 2 2 | 1 2 2 |
| P2 | 3 0 2 | 9 0 2 | 6 0 0 |
| P3 | 2 1 1 | 2 2 2 | 0 1 1 |
| P4 | 0 0 2 | 4 3 3 | 4 3 1 |
Available = (10, 5, 7) − la suma de Allocation = (3, 3, 2).
Peldaño 1 · Resueltolee cada paso y su porqué
Resuelto: ¿el estado es seguro?
| Paso | ¿Quién? | Need ≤ Work | Work después (+ Allocation) |
|---|---|---|---|
| 0 | — | — | 3 3 2 |
| 1 | P1 | 1 2 2 ≤ 3 3 2 ✓ | 5 3 2 |
| 2 | P3 | 0 1 1 ≤ 5 3 2 ✓ | 7 4 3 |
| 3 | P0 | 7 4 3 ≤ 7 4 3 ✓ | 7 5 3 |
| 4 | P2 | 6 0 0 ≤ 7 5 3 ✓ | 10 5 5 |
| 5 | P4 | 4 3 1 ≤ 10 5 5 ✓ | 10 5 7 |
Todos terminan: el estado es seguro, con la secuencia ⟨P1, P3, P0, P2, P4⟩. Hay otras válidas, como ⟨P1, P3, P4, P2, P0⟩, la del libro de Silberschatz. Al terminar, Work debe volver al total (10, 5, 7): es una buena forma de comprobar las sumas.
Peldaño 2 · Complétalofaltan pasos clave: llénalos tú
Complétalo: P1 pide (1, 0, 2)
Los pasos 1 y 2 de la petición ya están: Request (1 0 2) ≤ Need de P1 (1 2 2) ✓, y Request ≤ Available (3 3 2) ✓. Completa el paso 3: simula la asignación (nuevo Available, nuevo Allocation y nuevo Need de P1) y corre el algoritmo de seguridad. ¿Se concede?
Comparar (verificado con un script)
- Available = 3 3 2 − 1 0 2 = 2 3 0. Allocation de P1 = 3 0 2. Need de P1 = 0 2 0.
- Seguridad: P1 (0 2 0 ≤ 2 3 0) → Work 5 3 2 → P3 → 7 4 3 → P0 → 7 5 3 → P2 → 10 5 5 → P4 → 10 5 7.
- Es seguro: se concede.
Peldaño 3 · Solosin ayuda; luego compara
Solo: después de conceder lo anterior, llegan dos peticiones: P4 pide (3, 3, 0) y P0 pide (0, 2, 0)
Comparar (verificado con un script)
- P4 (3 3 0): es ≤ su Need (4 3 1), pero no es ≤ Available (2 3 0), porque falta A. P4 espera, sin simular nada.
- P0 (0 2 0): es ≤ su Need (7 4 3) y ≤ Available. Se simula: Available queda en 2 1 0. Ahora nadie cumple Need ≤ Work: P1 necesita 2 de B, P3 necesita 1 de C y los demás necesitan más A. El estado es inseguro, así que se deniega, se restaura el estado y P0 espera.
Peldaño 4 · Variantecambia una condición: ¿sigue valiendo lo anterior?
Variante: la misma petición de P0 (0, 2, 0), pero en el estado original (antes de conceder lo de P1)
Comparar
Ahora sí se concede. Available queda en 3 1 2 y existe la secuencia segura ⟨P3, P1, P0, P2, P4⟩ (verificada con un script). Lección: el banquero no juzga la petición en sí, sino el estado al que lleva. La misma petición puede ser segura o insegura según lo que ya se haya asignado. Que se deniegue no significa que habría habido deadlock: el banquero es conservador.
Ejemplo de detección con 5 procesos
Sección titulada «Ejemplo de detección con 5 procesos»Hay 5 procesos y 3 recursos: A = 7, B = 2 y C = 6. Available = (0, 0, 0).
| Allocation (A B C) | Request (A B C) | |
|---|---|---|
| P0 | 0 1 0 | 0 0 0 |
| P1 | 2 0 0 | 2 0 2 |
| P2 | 3 0 3 | 0 0 0 |
| P3 | 2 1 1 | 1 0 0 |
| P4 | 0 0 2 | 0 0 2 |
¿Hay deadlock? ¿Y si P2 pide una C más?
Estado inicial: no hay deadlock. La clase da la secuencia ⟨P0, P2, P3, P1, P4⟩, con Finish = true para todos. Por ejemplo, P0 no pide nada y libera (0, 1, 0); P2 tampoco pide y libera (3, 0, 3), y así sucesivamente. El script encontró otra secuencia válida, ⟨P0, P2, P1, P3, P4⟩: puede haber varias.
P2 pide una C más (su fila de Request pasa a 0 0 1):
- Solo P0 puede terminar (no pide nada), y entonces Work = (0, 1, 0).
- Con eso nadie más puede: P1 pide 2 A, P2 pide 1 C, P3 pide 1 A y P4 pide 2 C.
- Hay deadlock entre P1, P2, P3 y P4.
Las dos respuestas están verificadas con un script del algoritmo de detección.
Ojo con el paso 1: a los procesos con Allocation = 0 se les pone Finish = true desde el inicio (en Stallings: «marcar los que tienen la fila en ceros»).
¿Cada cuánto correrlo? Si se invoca de forma arbitraria puede haber muchos ciclos en el grafo, y no se sabría cuál proceso «causó» el deadlock.
Recuperación (la lista de la clase)
Sección titulada «Recuperación (la lista de la clase)»Terminando procesos: abortar todos los que están en deadlock, o uno a la vez hasta romper el ciclo. Para elegir a quién:
- la prioridad del proceso;
- cuánto ha computado y cuánto le falta;
- los recursos que ya usó;
- los recursos que necesita para terminar;
- cuántos procesos habrá que terminar;
- si es interactivo o batch.
Expropiando recursos:
- Elegir víctima minimizando el costo.
- Rollback: volver a un estado seguro y reiniciar desde ahí.
- Starvation: puede salir siempre la misma víctima. La solución es incluir el número de rollbacks en el costo.
«Ignorar el problema» y lockdep
Sección titulada ««Ignorar el problema» y lockdep»- No hay una estrategia que sirva para todo tipo de deadlock.
- Otra solución es ignorarlo (la «del avestruz»). La usan la mayoría de los SO, como UNIX y Linux, excepto en las funciones del kernel.
- Dentro del kernel, Linux usa lockdep (Lock Dependency Validator, en
kernel/locking/lockdep.c):- detecta de forma dinámica y proactiva los deadlocks de spinlocks y mutex;
- verifica que los locks se tomen siempre en un orden consistente en todo el código del kernel (es la prevención de la espera circular, comprobada en ejecución);
- Linux propone además un mecanismo nuevo: DEPT (Dependency Tracker).
Autoevaluación
Sección titulada «Autoevaluación»Clase de teoría sem. 11
0/12 correctas · 0 respondidas📈 ¿Qué tan bien lo dominas?
Elige pensando en lo que hiciste, no en lo que leíste. Para marcar 3 tienes que haber resuelto un ejercicio nuevo sin pistas y haberlo justificado. Si solo lo leíste y sientes que «ya entendí», todavía es un 1.
Fuente ·sem_11/Sem11_11-OST-Deadlock.pdf (clase de teoría 11) · Stallings 9e, cap. 6 · Ejemplos verificados con scripts propios: recorrido de todos los estados del productor–consumidor y algoritmo de detección · La corrección de la transferencia es una solución propia