ESTUDIO DE CASOS: LINUX En este capítulo se estudia el sistema operativo Linux. Se trata de un sistema operativo de libre distribución que proporciona una interfaz POSIX. Actualmente Linux es ampliamente utilizado y se ha convertido en uno de los pocos sistemas que puede competir hasta cierto punto con los sistemas Windows por lograr una cuota del mercado de los sistemas operativos. De hecho, no sólo se trata de dos sistemas operativos diferentes sino de dos maneras distintas de entender la informática. Windows es un sistema cerrado y "secreto" creado por una gran multinacional, Microsoft, cuyo único objetivo es vender su producto sea como sea. Linux es un sistema abierto y público que inicialmente surgió de un proyecto personal sin ánimo de lucro, pero que se ha convertido en la actualidad en un sistema desarrollado conjuntamente por gente de todo el mundo gracias al auge de Internet. En este capítulo se da una visión general de Linux. La exposición se organiza en las siguientes secciones: • Historia • Características generales y arquitectura • Gestión de procesos • Gestión de memoria • Entrada/salida • Sistema de archivos 1 HISTORIA DE LINUX El origen de Linux se encuentra en el sistema operativo MINIX. MINIX fue desarrollado por Andrew S. Tanenbaum con el objetivo de que sirviera de apoyo para la enseñanza de sistemas operativos. De hecho, en su clásico libro "Operating Systems: Design and Implementation" [Tanenbaum 1987], se utilizaba este sistema operativo para explicar los diferentes conceptos de esta materia, incluyéndose además en un apéndice un listado completo de su código escrito en lenguaje C. Además de por su carácter pedagógico, MINIX se caracterizaba por tener una estructura basada en un microkernel. El autor intentaba demostrar al crear MINIX que se podía construir un sistema operativo más sencillo y fiable, pero a la vez eficiente, usando este tipo de organización, que era novedosa en aquel momento. Algunas otras características positivas de MINIX eran las siguientes: • • • Ofrecía una interfaz basada en la de UNIX Versión 7. El grupo de trabajo de POSIX todavía no había terminado su labor en esa época. Tenía un tamaño relativamente pequeño. Constaba de aproximadamente 12.000 líneas de código. Podía trabajar en equipos que disponían de unos recursos hardware muy limitados. De hecho, incluso podía usarse en máquinas que no disponían de disco duro. Sin embargo, también presentaba algunas deficiencias y limitaciones. • • La gestión de memoria era muy primitiva. No había ni memoria virtual ni intercambio. Además, no se aprovechaba adecuadamente el mecanismo de paginación del procesador. El autor justificaba esta limitación argumentando que la inclusión de estos mecanismos complicaría considerablemente el código del sistema operativo. Aunque esta opinión es razonable desde el punto de vista pedagógico, tenía como consecuencia que MINIX no se utilizara como un sistema para el desarrollo de aplicaciones de cierta entidad. Por simplicidad, algunas partes del sistema operativo, como por ejemplo el sistema de archivos, no eran concurrentes lo que limitaba considerablemente el rendimiento del sistema. 1 A pesar de estos defectos, MINIX atrajo la atención de muchos usuarios de todo el mundo, que usaban el grupo de noticias comp.os.minix como punto de encuentro. Algunos de estos usuarios se ofrecían a mejorar partes del sistema o a incluir nuevas funciones al mismo. Sin embargo, el autor siempre fue bastante reacio a estas ofertas. Entre los interesados en MINIX se encontraba un estudiante finlandés llamado Linus Torvalds. Fue en el año 1990 cuando este estudiante envió un mensaje a este grupo de noticias comentando que por curiosidad y ganas de ampliar sus conocimientos estaba desarrollando un nuevo sistema operativo tomando como base MINIX. Se estaba produciendo el humilde nacimiento de Linux cuya primera versión (numerada como 0.01) vio la luz a mediados del año 1991. Es importante recalcar que en su concepción inicial Linux tomaba prestadas numerosas características de MINIX (por ejemplo, el sistema de archivos). De hecho, Linux se desarrolló usando como plataforma de trabajo MINIX y las primeras versiones de Linux no eran autónomas sino que tenían que arrancarse desde MINIX. Sin embargo, a pesar de esta herencia inicial, MINIX y Linux son radicalmente diferentes. Por un lado, Linux solventa muchas de las deficiencias de MINIX como, por ejemplo, la carencia de memoria virtual. Estas mejoras permiten que se trate de un sistema adecuado para trabajar de manera profesional, no limitándose su uso a un entorno académico. Pero la diferencia más importante entre ellos está en su organización interna. Mientras que MINIX tiene una estructura moderna basada en un microkernel, Linux posee una estructura monolítica más clásica. Este diseño conservador tuvo como consecuencia que el autor de MINIX no diera el visto bueno a Linux ya que consideraba que este sistema suponía un paso atrás en la evolución de los sistemas operativos. Desde su lanzamiento público en 1991, Linux ha ido evolucionando e incorporando nuevas características. Además, se ha transportado a otros procesadores como SPARC, Alpha y MIPS. En la actualidad es un sistema con unas características y prestaciones comparables a las de cualquier sistema operativo comercial. Puesto que en esta sección se ha presentado la historia de Linux, parece el lugar adecuado para explicar cuál es el convenio usado para numerar las sucesivas versiones del sistema. El número de la versión contiene un número primario y uno secundario separados por un punto (por ejemplo, versión 2.2). Un incremento en el número primario corresponde con una nueva versión que incluye cambios significativos en el sistema. En el caso de una modificación de menor impacto, sólo se modifica el número de versión secundario. Además, un número secundario de versión que sea impar (como, por ejemplo, la versión 2.1) indica que se trata de una versión inestable en la que se han incluido nuevas características que todavía hay que probar y depurar. Evidentemente, un usuario normal debería instalarse una versión con un número secundario par. 2 CARACTERÍSTICAS Y ESTRUCTURA DE LINUX Linux es un sistema UNIX y, por tanto, posee las características típicas de los sistemas UNIX. Se trata de un sistema multiusuario y multitarea de propósito general. Algunas de sus características específicas más relevantes son las siguientes: • • • • • Proporciona una interfaz POSIX. Tiene un código independiente del procesador en la medida de lo posible. Aunque inicialmente se desarrolló pera procesadores Intel, se ha transportado a otras arquitecturas con un esfuerzo relativamente pequeño. Puede adaptarse a máquinas de muy diversas características. Como el desarrollo inicial se realizó en máquinas con recursos limitados, ha resultado un sistema que puede trabajar en máquinas con prestaciones muy diferentes. Permite incluir de forma dinámica nuevas funcionalidades al núcleo del sistema operativo gracias al mecanismo de los módulos. Proporciona soporte para una gran variedad de tipos de sistemas de archivos, entre ellos los utilizados en Windows. También es capaz de manejar distintos formatos de archivos ejecutables. 2 • Proporciona soporte para multiprocesadores utilizando un esquema de multiproceso simétrico. Para aprovechar al máximo el paralelismo del hardware, se ha ido modificando progresivamente el núcleo con el objetivo de aumentar su concurrencia interna. En cuanto a la estructura de Linux, como se comentó previamente, tiene una organización monolítica al igual que ocurre con la mayoría de las implementaciones de UNIX. A pesar de este carácter monolítico, el núcleo no es algo estático y cerrado sino que se pueden añadir y quitar módulos de código en tiempo de ejecución. Se trata de un mecanismo similar al de las bibliotecas dinámicas pero aplicado al propio sistema operativo. Se pueden añadir módulos que correspondan con nuevos tipos de sistemas de archivos, nuevos manejadores de dispositivos o gestores de nuevos formatos de ejecutables. Un sistema Linux completo no sólo está formado por el núcleo monolítico sino también incluye programas del sistema (como, por ejemplo, demonios) y bibliotecas del sistema. Debido a las dificultades que hay para instalar y configurar el sistema, existen diversas distribuciones de Linux que incluyen el núcleo, los programas y bibliotecas del sistema, así como un conjunto de herramientas de instalación y configuración que facilitan considerablemente esta ardua labor. Hay distribuciones tanto de carácter comercial como gratuitas. Algunas de las distribuciones más populares son Slackware, Debian, Suse y Red Hat. 3 GESTIÓN DE PROCESOS La gestión de procesos en Linux es básicamente igual que en cualquier otra variedad de UNIX. Un aspecto original de Linux es el servicio clone que es una extensión del clásico fork. Este nuevo servicio permite crear un proceso que comparta con el padre su mapa de memoria, sus rutinas de manejo de señales y sus descriptores de archivos. Aunque Linux no implementa threads en el núcleo, se pueden construir bibliotecas de threads usando este nuevo servicio. En cuanto a la sincronización dentro del núcleo, siguiendo la tradición de UNIX, Linux no permite que haya llamadas concurrentes activas dentro del sistema operativo. Así, si se produce un evento que causa un cambio de proceso mientras se está ejecutando una llamada al sistema (por ejemplo, una interrupción de reloj), el cambio se difiere hasta que la llamada termina o se bloquea. Para evitar las condiciones de carrera entre la ejecución de una llamada y el tratamiento de una interrupción, se prohíben las interrupciones en pequeñas zonas del código del sistema. Puesto que el código de una rutina de interrupción ejecuta con una prioridad alta bloqueando el tratamiento de las interrupciones, es importante que estas rutinas sean muy cortas. Para ayudar a lograr este objetivo, Linux ofrece un mecanismo que permite dividir las operaciones asociadas a una interrupción en dos partes: • • Las operaciones de carácter más urgente se ejecutan en el entorno de la rutina de interrupción, denominada mitad superior en la nomenclatura de Linux. Las operaciones menos urgentes las ejecuta una rutina del núcleo que tiene una prioridad inferior a la de los dispositivos. Esta rutina se denomina en Linux mitad inferior y durante su ejecución no estarán bloqueadas las interrupciones de otros dispositivos. La propia mitad superior se encarga de fijar un determinado valor en una estructura de datos para indicar que la mitad inferior está activada. Justo antes de retornar a modo usuario, el sistema operativo comprueba dicha estructura y si encuentra que hay alguna una mitad inferior activada, la ejecutará. La figura 1 muestra cuáles son los niveles de prioridad de las diversas partes del núcleo. Nótese que sólo se ejecutará una rutina de un determinado nivel si no está activa ninguna rutina de un nivel superior. 3 Figura 1 Niveles de prioridad dentro del núcleo de Linux En cuanto a la planificación, Linux soporta tres clases de planificación: un algoritmo de tiempo compartido y dos algoritmos de planificación de tiempo real que se corresponden con los definidos por POSIX. El servicio sched_setscheduler permite definir la clase de planificación del proceso que la invoca. Esta llamada sólo puede hacerla un proceso privilegiado. Cada proceso de tiempo real tiene asociada una prioridad y un tipo de planificación que puede ser FIFO o Round-Robin. El planificador selecciona en cada momento el proceso listo para ejecutar que tenga mayor prioridad. Si el proceso es de tipo FIFO seguirá ejecutando hasta que se bloquee. Si es de tipo Round-Robin, cuando termine su rodaja el proceso pasará al final de la cola de procesos listos para ejecutar de su misma prioridad. Los procesos de tiempo compartido sólo pueden ejecutar cuando no hay ningún proceso de tiempo real listo para ejecutar. El algoritmo de planificación para este tipo de procesos intenta conjugar la prioridad del proceso con su perfil de ejecución, favoreciendo a los procesos que realizan más operaciones de entrada/salida. A continuación se describe este algoritmo. Todo proceso tiene asociada una prioridad base. Inicialmente la prioridad del proceso es igual a su prioridad base. Cada vez que se produce una interrupción de reloj se resta una unidad a la prioridad del proceso que estaba ejecutando. El algoritmo de planificación está basado en la prioridad del proceso y tiene carácter exclusivo: el planificador elige el proceso listo para ejecutar que tenga mayor prioridad. Cuando se produce una situación en la que todos los procesos listos para ejecutar tienen una prioridad igual a cero (todos ellos han usado el procesador un tiempo suficiente para que su prioridad haya caído a 0), se produce un reajuste de las prioridades de todos los procesos sea cuál sea su estado. la nueva prioridad se calcula dividiendo por 2 la actual y sumando la prioridad base (prioridad = prioridad/2 + prioridad base). Nótese que los procesos listos para ejecutar simplemente recuperan su prioridad base ya que su prioridad actual es igual a 0. Sin embargo, los procesos bloqueados obtienen una nueva prioridad mayor que la base puesto que su actual prioridad es mayor que cero. Se trata de una fórmula de tipo exponencial. Con ella un proceso que estuviese bloqueado mucho tiempo puede llegar a tener una prioridad con un valor del doble de la prioridad base. 4 4 GESTIÓN DE MEMORIA Linux tiene un sistema de memoria que incluye todas las características habituales en los sistemas modernos. Estas características ya fueron discutidas en el capítulo dedicado a este tema, por lo que en esta sección se presentan aquellos aspectos específicos de Linux: • • • • Se utiliza un modelo de memoria independiente del procesador. Utiliza un esquema de paginación con tres niveles. Existe una capa de software de bajo nivel que se encarga de adaptar este modelo abstracto al hardware de gestión de memoria real. Permite utilizar tanto dispositivos como archivos para soporte de la memoria secundaria. Se utiliza una versión modificada del algoritmo del reloj como algoritmo de reemplazo. Gestiona la memoria dinámica del propio sistema operativo usando un algoritmo inspirado en el sistema buddy. 5 ENTRADA/SALIDA La entrada/salida en Linux es muy similar a la de cualquier otro sistema UNIX. Se distinguen, por tanto, dos tipos de dispositivos: dispositivos de bloques y dispositivos de caracteres. Como el resto de los sistemas UNIX, se utiliza una caché común para todos los dispositivos de bloques. El tamaño de la caché es dinámico y crece de acuerdo a las necesidades de memoria del resto del sistema. Para gestionarla se usa básicamente una política de reemplazo LRU. En las últimas versiones esta caché trabaja coordinadamente con la utilizada por el gestor de memoria. En cuanto al acceso a los discos, se utiliza el algoritmo del ascensor con un único sentido de servicio. Siguiendo el modelo de UNIX, en Linux los usuarios ven los dispositivos como archivos y utilizan los servicios destinados a trabajar con archivos para acceder a los dispositivos. La red, sin embargo, es un dispositivo que tiene un tratamiento un poco diferente. El usuario no puede acceder a este dispositivo de la misma manera que a un archivo. La parte del sistema operativo que trata la red está dividida en tres niveles: • • • En el nivel inferior está el manejador del dispositivo al que el usuario no puede acceder directamente. En el nivel intermedio está el software que implementa la pila de protocolos (por ejemplo, TCP e IP). En el nivel superior está la interfaz del programador que corresponde con la de los sockets definidos en el UNIX BSD. 6 SISTEMA DE ARCHIVOS Linux da soporte a una gran variedad de tipos de sistemas de archivos entre los que se incluyen los distintos sistemas de archivos de Windows y de otros sistemas UNIX. Además, cualquier usuario puede programar un manejador de un nuevo tipo de sistema de archivos e incluirlo en el sistema como un módulo. Esta coexistencia de distintos tipos de sistemas de archivos la posibilita el VFS (Virtual File System, Sistema Virtual de Archivos) presente en la mayoría de los sistemas UNIX actuales y suficientemente analizado en el capítulo dedicado a los archivos. Aunque admite muy diferentes tipos de sistemas de archivos, Linux tiene sus propio sistema de archivos que se denomina ext2fs. Este sistema evolucionó desde el sistema de archivos de MINIX. Se le fueron añadiendo nuevas características al sistema de archivos de MINIX hasta llegar al sistema extfs. 5 Posteriormente, se rediseñó dando lugar al ext2fs actual. Se trata de un sistema basado en el FFS (Fast File System, Sistema de Archivos Rápido) del UNIX BSD, que ya se estudió adecuadamente en el capítulo dedicado a los archivos. Merece mención aparte un tipo de sistema de archivos muy especial: el sistema de archivos proc. Este sistema de archivos no tiene soporte en ningún dispositivo. Su objetivo es poner a disposición del usuario datos del estado del sistema en la forma de archivos. Esta idea no es original de Linux ya que casi todos los sistemas UNIX la incluyen. Sin embargo, Linux se caracteriza por ofrecer más información del sistema que el resto de variedades de UNIX. En este sistema de archivos se puede acceder a información general sobre características y estadísticas del sistema, así como a información sobre los distintos procesos existentes. La figura 2 muestra cómo se relacionan las distintas partes del sistema de archivos. Figura 2 Niveles del sistema de archivos 7 PUNTOS A RECORDAR El origen del Linux está en MINIX que es un sistema operativo de carácter pedagógico basado en un microkernel. Linux supera muchas de las limitaciones de MINIX y tiene una organización monolítica más convencional. Linux comenzó en 1991 como proyecto personal de Linus Torvalds. Gracias al auge de Internet, han colaborado numerosas personas en su desarrollo. Proporciona una interfaz POSIX. Está diseñado para facilitar su transporte a distintos procesadores. Puede ejecutar en máquinas de muy distintas prestaciones. Da soporte a una gran variedad de tipos de sistemas de archivos. Proporciona soporte para un esquema de multiproceso simétrico. 6 La gestión de procesos en Linux es muy similar a la realizada en otros sistemas UNIX. El núcleo de Linux no es reentrante. El tratamiento de un interrupción puede dividirse en una mitad superior, que ejecuta con alta prioridad, y una mitad inferior menos prioritaria. Linux soporta las clases de planificación de tiempo real definidas en POSIX. La planificación de procesos de tiempo compartido intenta conjugar la prioridad del proceso y su perfil de ejecución. La gestión de memoria en Linux incluye todas las características presentes en cualquier sistema operativo moderno. La entrada/salida es similar a la existente en cualquier otro sistema UNIX. Linux da soporte a una gran variedad de tipos de sistemas de archivos gracias al VFS. El sistema de archivos nativo de Linux es ext2fs basado en el FFS. El sistema de archivos proc ofrece mucha información sobre el propio sistema y los procesos existentes en el mismo. Bibliografía [Beck 1996] M. Beck, H. Bohme, M. Dziadzka, U. Kunitz, R. Magnus y D. Verworner, Linux Kernel Internals, Addison-Wesley, 1996. [Cornes 1997] P. Cornes, The Linux A-Z, Prentice-Hall, 1997. [Silberschatz 1998] A. Silberschatz and P.B. Galvin, Operating System Concepts, 5th ed., Addison-Wesley, 1998. [Tanenbaum 1987] A. S. Tanenbaum, Operating Systems: Design and Implementation, Primera edición, Prentice-Hall, 1987. [Tanenbaum 1997] A. S. Tanenbaum y A. Woodhull, Operating Systems: Design and Implementation, Segunda edición, Prentice Hall, 1997. 7