Sistemas operativos modernos
CPUs, habrá largas colas de procesadores esperando el bloqueo. Por suerte, es fácil mejorar la situación. Muchas partes del sistema operativo son independientes entre sí. Por ejemplo, no hay problema si una CPU ejecuta el calendarizador mientras otra maneja una llamada al sistema de archivos y una tercera está procesando un fallo de página. Esta observación lleva a dividir el sistema operativo en regiones críticas independientes que no interactúan entre sí. Cada región crítica está protegida por su propio mutex, de modo que só lo una CPU podrá ejecutarla a la vez. De este modo puede lograrse un paralelismo mucho ma yor. Sin embargo, bien podría suceder que algunas tablas, como la de procesos, se usen en varias regiones críticas. Por ejemplo, la tabla de procesos se necesita para calendarizar, pero también para la llamada al sistema fork y para manejar señales. Toda tabla que pueda ser utili zada por múltiples regiones críticas necesitará su propio mutex. Así, cada región crítica sólo podrá ser ejecutada por una CPU a la vez y cada tabla crítica sólo podrá ser utilizada por una CPU a la vez. Casi todos los multiprocesadores modernos adoptan esta organización. La dificultad de es cribir un sistema operativo para una máquina así no radica en que el código en sí sea muy di ferente del de un sistema operativo normal, pues no lo es. La parte difícil consiste en dividirlo en regiones críticas que puedan ejecutarse de forma concurrente en diferentes CPUs sin que in terfieran entre sí, ni siquiera de formas sufiles e indirectas. Además, todas las tablas empleadas por dos o más regiones críticas deberán protegerse de manera individual con un mutex, y todo el código que use la tabla deberá manejar el mutex en forma correcta. Además, debe tenerse mucho cuidado para evitar los bloqueos irreversibles. Si dos regio nes críticas necesitan tanto la tabla A como la B, y una de ellas se adueña primero de A y la otra se adueña primero de B, tarde o temprano se presentará un bloqueo irreversible y nadie sabrá por qué. En teoría, podrían asignarse valores enteros a todas las tablas y podría exigirse a las regiones críficas obtener tablas en orden ascendente. Tal estrategia evita bloqueos irreversibles, pero obliga al programador a elegir con detenimiento las tablas que necesita cada región crífi- ca, a fin de emifir las solicitudes en el orden correcto. A medida que el código evoluciona con el paso dei tiempo, una región crítica podría nece sitar una nueva tabla que no requería antes. Si el programador es nuevo y no entiende en for ma cabal la lógica del sistema, se senfirá tentado a apoderarse simplemente del mutex de la tabla cuando la necesite y a liberarlo cuando ya no la necesite. Por más razonable que parezca esto, podría llevar a bloqueos irreversibles, que el usuario percibirá como un congelamiento del sistema. No es fácil encontrar la forma correcta de hacerlo, y mantenerla correcta durante un periodo de años mientras cambian los programas es más difícil aún. 8.1.3 Sincronización de multiprocesadores Las CPUs de un multiprocesador a menudo necesitan sincronizarse. Acabamos de ver el caso en que regiones críticas y tablas del kemel necesitan protegerse con mutexes. Ahora veamos más de cerca cómo funciona en realidad esta sincronización en un mulfiprocesador. El asunto no es nada trivial, como veremos. Por principio de cuentas, en realidad se necesitan primifivas de sincronización. Si un pro ceso de un uniprocesador efectúa una llamada al sistema que requiere acceso a una tabla críti
RkJQdWJsaXNoZXIy MjI4NDcx