1 ===============================
2 Sistemas Distribuidos I (75.74)
3 ===============================
5 ----------------------------------
6 Práctica 2: Repaso de concurrencia
7 ----------------------------------
9 :Author: Leandro Lucarella (77891)
19 Problema de los jardines con shared memory
20 ------------------------------------------
22 Como implementa el mutex con el algoritmo de Dekker con turnos sólo funciona
23 para 2 procesos y hay que numerarlos a mano. Se corre con::
25 $ ./P02e1101 0 & ./P02e1101 1 &
27 El primer parámetro es el número de proceso, debe ser 0 o 1, se puede pasar un
28 segundo parametro opcional con la cantidad de iteraciones a realizar. El proceso
29 0 debe ejecutarse primero porque es el que inicializa las estructuras
32 Se provee un script lanzador::
36 Recibe un parámetro opcional con la cantidad de iteraciones.
38 Código fuente (P02e1101.cpp)
39 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40 .. include:: P02e1101.cpp
43 Código fuente (P02e1101.sh)
44 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
45 .. include:: P02e1101.sh
48 Corrida de ./P02e1101.sh
49 ^^^^^^^^^^^^^^^^^^^^^^^^
50 .. include:: P02e1101.corrida
54 Problema de los jardines con semáforos y shared memory
55 ------------------------------------------------------
57 Muy similar al anterior pero se puede correr con N procesos porque se usan
58 semáforos para hacer exclusión mutua. Por ejemplo::
60 $ ./P02e1201 0 & ./P02e1201 1 & ./P02e1201 2 & ./P02e1201 3 & ./P02e1201 4
62 También acepta un párametro extra para la cantidad de iteraciones.
64 Se provee un script lanzador::
68 Recibe un parámetro opcional con la cantidad de iteraciones, este lanzador
69 corre 5 instancias como en la línea de ejecución de ejemplo.
71 Código fuente (P02e1201.cpp)
72 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
73 .. include:: P02e1201.cpp
76 Código fuente (P02e1201.sh)
77 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
78 .. include:: P02e1201.sh
81 Corrida de ./P02e1201.sh
82 ^^^^^^^^^^^^^^^^^^^^^^^^
83 .. include:: P02e1201.corrida
87 Análisis de entrelazado, interbloqueo y espera activa
88 -----------------------------------------------------
89 En el primer programa no hay posibilidad de interbloqueo ya que se utiliza el
90 algoritmo de Dekker que es un algoritmo muy conocido y probado que garantiza
91 tanto la ausencia de interbloqueo como *fairness*. Sin embargo sí hay espera
92 activa (el algoritmo se basa en espera activa sobre variables compartidas). El
93 entrelazado puede observarse gracias a la llamada a sched_yield(). De no
94 utilizarse, el entrelazado se ve de manera mucho menos frecuente, ya que hay que
95 esperar que se agote el *timeslice* del proceso en ejecución (que se queda
96 ejecutando la espera activa) para que el kernel ponga a ejecutar otro proceso.
98 En el segundo programa, con el entrelazado pasa exactamente lo mismo, porque los
99 semáforos se utilizan como mutex, de la misma manera que fue usado el mutex
100 construido con el algoritmo de Dekker. Tampoco hay posibilidad de interbloqueo
101 porque no hay anidamiento de mutex ni ninguna otra condición que pueda llevar a
102 esto, es un uso muy básico y simple de un sólo semáforo a modo de mutex. Sin
103 embargo, la espera activa desaparece gracias a la utilización de los semáforos,
104 ya que si un proceso hace el wait y el semáforo está en 0, el kernel pone el
105 proceso en pausa y continúa ejecutando otros procesos hasta que el estado del
106 semáforo cambie con un signal.
114 Usando semáforos binarios y shared memory
115 -----------------------------------------
117 Esquema productor-consumidor
118 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
119 Productor-consumidor usando semáforos binarios y shared memory. Debe coincidir
120 la cantidad de cosas a producir y a consumir, y debe correrse primero el
121 productor porque inicializa las estructuras compartidas. Por ejemplo::
123 $ ./P02e2111 0 & ./P02e2111 1 &
125 También acepta un parámetro extra para la cantidad de iteraciones. 0 es
126 productor, 1 es consumidor.
128 Otra forma de probar con varios consumidores es::
130 $ ./P02e2111 0 20 & ./P02e2111 1 12 & ./P02e2111 1 8 &
132 Se provee un script lanzador::
136 Recibe un parámetro opcional con la cantidad de iteraciones, este lanzador
137 corre 1 productor y 1 consumidor.
139 Código fuente (P02e2111.cpp)
140 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
141 .. include:: P02e2111.cpp
144 Código fuente (P02e2111.sh)
145 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
146 .. include:: P02e2111.sh
149 Corrida de ./P02e2111.sh
150 ~~~~~~~~~~~~~~~~~~~~~~~~
151 .. include:: P02e2111.corrida
155 Esquema productor-consumidor con 3 productores y 2 consumidores
156 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
157 Productor-consumidor usando semáforos binarios y shared memory pero con 3
158 productores que producen parcialmente y 2 consumidores que consumen todo.
159 Debe correr primero el primer productor porque inicializa las estructuras
160 compartidas. Por ejemplo::
162 $ ./P02e2121 0 & ./P02e2121 1 & ./P02e2121 2 & ./P02e2121 3 & ./P02e2121 4 &
164 El proceso 0 a 2 son productores, el 3 y 4 consumidores.
166 Se provee un script lanzador::
170 Recibe un parámetro opcional con la cantidad de iteraciones.
172 Código fuente (P02e2121.cpp)
173 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
174 .. include:: P02e2121.cpp
177 Código fuente (P02e2121.sh)
178 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
179 .. include:: P02e2121.sh
182 Corrida de ./P02e2121.sh
183 ~~~~~~~~~~~~~~~~~~~~~~~~
184 .. include:: P02e2121.corrida
188 Usando colas de mensajes
189 ------------------------
191 Esquema productor-consumidor
192 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
193 Productor-consumidor usando colas de mensajes. Debe coincidir la cantidad de
194 cosas a producir y a consumir, y puede haber sólo un productor (que debe
195 correr primero) y un solo consumidor. Por ejemplo::
197 $ ./P02e2211 0 & ./P02e2211 1 &
199 También acepta un parámetro extra para la cantidad de iteraciones. 0 es
200 productor, 1 es consumidor.
202 Se provee un script lanzador::
206 Recibe un parámetro opcional con la cantidad de iteraciones.
208 Código fuente (P02e2211.cpp)
209 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
210 .. include:: P02e2211.cpp
213 Código fuente (P02e2211.sh)
214 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
215 .. include:: P02e2211.sh
218 Corrida de ./P02e2211.sh
219 ~~~~~~~~~~~~~~~~~~~~~~~~
220 .. include:: P02e2211.corrida
224 Esquema productor-consumidor con 3 productores y 2 consumidores
225 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
226 Productor-consumidor usando colas de mensajes pero con 3 productores que
227 producen parcialmente y 2 consumidores que consumen todo. Debe correr
228 primero el primer productor porque inicializa las estructuras compartidas.
231 $ ./P02e2221 0 & ./P02e2221 1 & ./P02e2221 2 & ./P02e2221 3 & ./P02e2221 4 &
233 El proceso 0 a 2 son productores, el 3 y 4 consumidores.
235 Se provee un script lanzador::
239 Recibe un parámetro opcional con la cantidad de iteraciones.
241 Código fuente (P02e2221.cpp)
242 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
243 .. include:: P02e2221.cpp
246 Código fuente (P02e2221.sh)
247 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
248 .. include:: P02e2221.sh
251 Corrida de ./P02e2221.sh
252 ~~~~~~~~~~~~~~~~~~~~~~~~
253 .. include:: P02e2221.corrida
260 Esquema productor-consumidor
261 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
262 Productor-consumidor usando pipes. Debe coincidir la cantidad de cosas a
263 producir y a consumir, y puede haber sólo un productor (que debe correr
264 primero) y un sólo consumidor. Por ejemplo::
266 $ ./P02e2311 0 & ./P02e2311 1 &
268 También acepta un parámetro extra para la cantidad de iteraciones. 0 es
269 productor, 1 es consumidor.
271 Se provee un script lanzador::
275 Recibe un parámetro opcional con la cantidad de iteraciones.
277 Código fuente (P02e2311.cpp)
278 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
279 .. include:: P02e2311.cpp
282 Código fuente (P02e2311.sh)
283 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
284 .. include:: P02e2311.sh
287 Corrida de ./P02e2311.sh
288 ~~~~~~~~~~~~~~~~~~~~~~~~
289 .. include:: P02e2311.corrida
293 Esquema productor-consumidor con 3 productores y 2 consumidores
294 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
295 Productor-consumidor usando pipes pero con 3 productores que producen
296 parcialmente y 2 consumidores que consumen todo. Debe correr primero el
297 primer productor porque inicializa las estructuras compartidas. Por ejemplo::
299 $ ./P02e2321 0 & ./P02e2321 1 & ./P02e2321 2 & ./P02e2321 3 & ./P02e2321 4 &
301 El proceso 0 a 2 son productores, el 3 y 4 consumidores.
303 Se provee un script lanzador::
307 Recibe un parámetro opcional con la cantidad de iteraciones.
309 Código fuente (P02e2321.cpp)
310 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
311 .. include:: P02e2321.cpp
314 Código fuente (P02e2321.sh)
315 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
316 .. include:: P02e2321.sh
319 Corrida de ./P02e2321.sh
320 ~~~~~~~~~~~~~~~~~~~~~~~~
321 .. include:: P02e2321.corrida
325 Análisis de entrelazado, interbloqueo y espera activa
326 -----------------------------------------------------
327 En el punto 2.1.2 es el único ejemplo en donde puede observarse espera activa,
328 ya que se verifica si un flag está seteado. Este flag indica si la parte
329 correspondiente al productor en ejecución ya fue producida, de ser así espera
330 (activamente) que los demás productores terminen de producir lo que les
331 corresponda para luego pasar el control a los consumidores. La espera activa es
332 realmente **muy** notoria.
334 Con respecto a interbloqueo, a menos que haya algún bug, no debería haber en
335 ninguno de los 3 casos, porque los ejercicios fueron pensados para que no hayan
336 2 procesos esperando condiciones complementarias.
338 Finalmente con el entrelazado pasa algo similar a lo comentado en el punto 1.3,
339 excepto (nuevamente) por el ejericio 2.1.2, que por la forma en la que está
340 resuelto, obliga a la existencia de entrelazado entre cada proceso, porque cada
341 productor espera a que el resto termine, y ambos consumidores esperan a que
342 todas las partes estén producidas. Hay libertad sobre la forma que pueden
343 entrelazarse los productores entre sí, y los consumidores entre sí (aunque no se
344 aprecie en la corrida), pero debe haber entrelazado entre el conjunto de
345 productores y consumidores 1 a 1. En los otros ejemplo sí pasa lo descripto en
346 el punto 1.3, que, si no se utilizara la llamada a sched_yield(), no se vería
347 entrelazado entre los consumidores y productores a menos que se haga una corrida
348 mucho más larga (más iteraciones) de forma que se agote el *timeslice* (o que se
349 acabe el *buffer* de las colas o pipes y se ponga a dormir el proceso) de los
350 procesos antes de que estos finalicen.