Sincronización entre procesos Problemas clásicos Rodolfo Baader Departamento de Computación, FCEyN, Universidad de Buenos Aires, Buenos Aires, Argentina Sistemas Operativos, segundo cuatrimestre de 2014 Rodolfo Baader Problemas clásicos de sincronización entre procesos (2) Siempre los mismos problemas... En muchas áreas de la computación hay problemas clásicos. Eso significa que aparecen una y otra vez bajo diferentes formas, pero son básicamente el mismo problema. En el caso de sincronización entre procesos también sucede que muchos de estos problemas nos suelen inducir a pensar en “soluciones” incorrectas. Por eso está bueno estudiarlos y conocer buenas formas de solucionarlos. Además, analizarlos suele aportar una mirada bastante profunda sobre problemas fundamentales de la computación, y a veces, del razonamiento humano. OJO: conocer los problemas clásicos y sus soluciones no debe ser una excusa para la pereza. Los problemas concretos tienen particularidades y contextos que hacen necesario evaluarlos de manera individual y pensar en cada caso. ∆ ! Dicho de otra forma, el conocimiento no reemplaza a la inteligencia. Ambos se complementan. Rodolfo Baader Problemas clásicos de sincronización entre procesos (3) Los turnos Problema: Tenemos una serie de procesos Pi , i ∈ {1 . . . N} que se están ejecutando en simultáneo (supongamos que tenemos N CPUs). Cada proceso i tiene una sentencia si. Queremos que en el sistema global se ejecuten s1, s2, . . . , sN. ¿De qué nos disfrazamos? Podemos utilizar semáforos. Rodolfo Baader Problemas clásicos de sincronización entre procesos (4) Los turnos (cont.) Listing 1: Inicialización proc i n i t () { f o r ( i= 0 ; i <N+1; i ++) s e m a f o r o s [ i ]= new S e má f o r o ( 0 ) ; f o r ( i= 0 ; i <N ; i ++) f o r k P( i ) ; semaforos [ 0 ] . s i g n a l ( ) ; } Listing 2: Proceso Pi p r o c P( i ) { s e m a n t= s e m a f o r o s [ i ] ; s e m s i g= s e m a f o r o s [ i + 1 ] ; sem ant . wait ( ) ; s( i ); sem sig . s i g n a l ( ) ; } Rodolfo Baader Problemas clásicos de sincronización entre procesos (5) Barrera - Rendezvous Otro problema muy común es el de rendezvous (punto de encuentro), o barrera de sincronización. Dados Pi = [a(i); b(i)], i ∈ {1 . . . N}, asegurarse que los b(i) se ejecuten recién después de que se ejecutaron todos los a(i). Es decir, queremos poner una barrera entre los a y los b. Sin embargo, no hay que restringir de más: no hay que imponer ningún orden entre los a(i) ni entre los b(i). Rodolfo Baader Problemas clásicos de sincronización entre procesos (6) Rendezvous (cont.) Listing 3: Inicialización proc i n i t () { e n b a r r e r a= 0 ; // Cant . de p r o c s en l a b a r r e r a . mutex= new S e má f o r o ( 1 ) ; // P e r m i t o empezar de i n m e d i a t o . b a r r e r a= new S e má f o r o ( 0 ) ; f o r ( i= 0 ; i <N ; i ++) f o r k P( i ) ; } Rodolfo Baader Problemas clásicos de sincronización entre procesos (7) Rendezvous (cont.) Listing 4: Proceso Pi p r o c P( i ) { a( i ); mutex . w a i t ( ) ; // A c c e s o a v a r . c o m p a r t i d a . e n b a r r e r a ++; b o o l t o d o s e n b a r r e r a= ( e n b a r r e r a==N ) ; mutex . s i g n a l ( ) ; i f ( todos en barrera ) // Todos e s t á n l i s t o s p a r a c o n t i n u a r . Que s i g a n . barrera . signal (); barrera . wait ( ) ; b( i ); } Esta solución no es correcta. ¿Qué está mal? Rodolfo Baader Problemas clásicos de sincronización entre procesos (8) Rendezvous (cont.) En el ejemplo anterior habı́a deadlock. ∆ ! Listing 5: Proceso Pi (correcto) p r o c P( i ) { a( i ); mutex . w a i t ( ) ; // A c c e s o a v a r . c o m p a r t i d a . e n b a r r e r a ++; b o o l t o d o s e n b a r r e r a= ( e n b a r r e r a==N ) ; mutex . s i g n a l ( ) ; i f ( todos en barrera ) // Todos e s t á n l i s t o s p a r a c o n t i n u a r . Que s i g a n . barrera . signal (); barrera . wait ( ) ; barrera . signal (); b( i ); } Notar que hay un signal() de más. Rodolfo Baader Problemas clásicos de sincronización entre procesos (9) Rendezvous (cont.) ¿Qué pasa si contamos con la primitiva broadcast()?. Listing 6: Proceso Pi p r o c P( i ) { a( i ); mutex . w a i t ( ) ; // A c c e s o a v a r . c o m p a r t i d a . e n b a r r e r a ++; b o o l t o d o s e n b a r r e r a= ( e n b a r r e r a==N ) ; mutex . s i g n a l ( ) ; i f ( todos en barrera ) // Todos e s t á n l i s t o s p a r a c o n t i n u a r . Que s i g a n . barrera . broadcast ( ) ; else barrera . wait ( ) ; b( i ); } Esta solución no es correcta. ¿Qué está mal? Rodolfo Baader Problemas clásicos de sincronización entre procesos (10) Sección crı́tica de a k Siguiente problema. N procesos, sólo k ejecutan a la vez la sección crı́tica sc(i). Es fácil: Listing 7: Inicialización proc i n i t () { sem= new S e má f o r o ( k ) ; f o r ( i= 0 ; i <N ; i ++) f o r k P( i ) ; } Listing 8: Proceso Pi p r o c P( i ) { sem . w a i t ( ) ; sc ( i ) ; sem . s i g n a l ( ) ; } Rodolfo Baader Problemas clásicos de sincronización entre procesos (11) Lectores/escritores Otro problema más. Se da mucho en bases de datos. Hay una variable compartida. Los escritores necesitan acceso exclusivo. Pero los lectores pueden convivir. ¿Cómo podrı́a solucionarse? Rodolfo Baader Problemas clásicos de sincronización entre procesos (12) Lectores/escritores (cont.) Listing 9: Inicialización proc i n i t () { v a r i a b l e c o m p a r t i d a ; // É s t a e s l a que t o d o s s e d i s p u t a n . mutex= new S e má f o r o ( 1 ) ; a c c e s o e x c l u s i v o= new S e má f o r o ( 1 ) ; i n t l e c t o r e s= 0 ; f o r ( i= 0 ; i <N ; i ++) f o r k L e c t o r ( i ) ; f o r ( i= 0 ; i <M; i ++) f o r k E s c r i t o r ( i ) ; } Los escritores necesitan acceso exclusivo, sin vueltas: Listing 10: Escritores proc E s c r i t o r ( i ) { a c c e s o e x c l u s i v o . wait ( ) ; E s c r i b i r ( compartida ) ; acceso exclusivo . signal (); } Rodolfo Baader Problemas clásicos de sincronización entre procesos (13) Lectores/escritores (cont.) Listing 11: Lectores proc Lector ( i ) { mutex . w a i t ( ) ; l e c t o r e s ++; i f ( l e c t o r e s ==1) // Soy e l p r i m e r o , r e c l a m o a c c e s o ” e x c l u s i v o ” p a r a // t o d o s l o s l e c t o r e s . a c c e s o e x c l u s i v o . wait ( ) ; mutex . s i g n a l ( ) ; Leer ( compartida ) ; mutex . w a i t ( ) ; l e c t o r e s −−; i f ( l e c t o r e s ==0) // Soy e l ú l t i m o , d e j o que e n t r e n l o s acceso exclusivo . signal (); mutex . s i g n a l ( ) ; escritores . } Rodolfo Baader Problemas clásicos de sincronización entre procesos (14) Lectores/escritores (cont.) ¿Puede haber deadlock? No, pero puede haber inanición de escritores. Tarea: pensar cómo evitarlo. Rodolfo Baader Problemas clásicos de sincronización entre procesos (15) Filósofos que cenan (Dining Quintuple/Philosophers) Este problema lo inventó Dijkstra en 1965. Tenemos 5 filósofos en una mesa circular, con 5 platos de fideos y un tenedor entre cada plato. Para comer necesitan dos tenedores. Los filósofos hacen: p r o c F i l ó s o f o ( i ) while ( true ) { pensar ( ) ; conseguir tenedores ( i ); comer ( ) ; dejar tenedores ( i ); } Problema: programar conseguir tenedores() y dejar tenedores() de manera tal que: EXCL) DEAD) INAN) CONC) Sólo un filósofo tenga cada tenedor en cada momento. No haya deadlock. No haya inanición. Más de un filósofo esté comiendo a la vez. Rodolfo Baader Problemas clásicos de sincronización entre procesos (16) Gráficamente Rodolfo Baader Problemas clásicos de sincronización entre procesos (17) Filósofos que cenan Empecemos por algunas definiciones básicas: Macros cómodas para los tenedores: izq(i) = i der(i) = (i + 1) mod 5 ¿Qué son los tenedores? Seguro que hay que garantizar acceso exclusivo a ellos: Un arreglo: tenedores[i]= new Semáforo(1) Rodolfo Baader Problemas clásicos de sincronización entre procesos (18) Solución ingénua Empecemos por la idea más elemental: func c o n s e g u i r t e n e d o re s ( i ) { tenedores [ izq ( i ) ] . wait ( ) ; tenedores [ der ( i ) ] . wait ( ) ; } func d e j a r t e n e d o r e s ( i ) { tenedores [ izq ( i ) ] . signal (); tenedores [ der ( i ) ] . s i g n a l ( ) ; } ¿Está bien? Esta solución garantiza EXCL, pero falla con DEAD. ¿Cómo rompemos el deadlock? Pensemos en las condiciones. Tarea: pensar en soluciones para este problema. Buscar las ya existentes. Rodolfo Baader Problemas clásicos de sincronización entre procesos (19) Barbero Otro problema clásico: En una peluquerı́a hay dos salas, una de espera, con n sillas y otra donde está la única silla donde el único peluquero corta el pelo. Si no hay clientes, el peluquero se duerme una siesta. Si entra un cliente, y no se puede sentar a esperar, se va. Si el peluquero está dormido, lo despierta. Rodolfo Baader Problemas clásicos de sincronización entre procesos (20) Barbero (cont.) Vamos a usar tres semáforos: a l g u n c l i e n t e= new S e má f o r o ( 0 ) ; p a s a r= new S e má f o r o ( 0 ) ; mutex= new S e má f o r o ( 1 ) ; c l i e n t e s= 0 ; El peluquero es sencillo: proc Peluquero while ( true ) { a l g u n c l i e n t e . wait ( ) ; pasar . s i g n a l ( ) ; cortar pelo (); } Rodolfo Baader Problemas clásicos de sincronización entre procesos (21) Barbero (cont.) Veamos a los clientes: proc C l i e n t e () // ¿Hay l u g a r ? C a p a c i d a d = n + 1 ( a l que l e c o r t a n ) mutex . w a i t ( ) ; i f ( c l i e n t e s==n+1) // No , me v o y . mutex . s i g n a l ( ) ; r e t i r a r s e ( ) ; // No v u e l v e // Sı́ hay l u g a r , e n t r o . c l i e n t e s ++; mutex . s i g n a l ( ) ; // ”A v i s o ” que l l e g u é . algun cliente . signal (); // E s p e r o a que me d e j e n p a s a r . pasar . wait ( ) ; e n t r a r a c o r t a r s e e l p e l o ( ) ; // ¡Todo p a r a e s t o ! // S a l g o . mutex . w a i t ( ) ; c l i e n t e s −−; mutex . s i g n a l ( ) ; Rodolfo Baader Problemas clásicos de sincronización entre procesos (22) Bibliografı́a “The Little Book of Semaphores”, Second Edition. Allen B. Downey. http://greenteapress.com/semaphores/ “Cooperating sequential processes”. Edgar W. Dijkstra. Technical Report 123, Univ. Texas. http://www.cs.utexas. edu/users/EWD/transcriptions/EWD01xx/EWD123.html. Rodolfo Baader Problemas clásicos de sincronización entre procesos