AMPLIACIÓN DE SISTEMAS OPERATIVOS , AMPLIACION DE SISTEMAS OPERATIVOS José Manuel Díaz Martínez sanz y torres MARCAS REGISTRADAS. Los nombres de los productos (hardware, software, sistemas operativos, etc.) que aparecen en este libro pueden ser marcas registradas por sus fabricantes. El autor ha utilizado exclusivamente estos nombres con el propósito de identificar los productos y sin intención de infringir las marcas ni perjudicar a los propietarios de sus derechos. AMPLIACIÓN DE SISTEMAS OPERATIVOS Todos los derechos reservados. Queda prohibida, salvo excepción prevista en la ley, cualquier forma de reproducción, distribución, comunicación pública y transformación de esta obra sin contar con la autorización de los autores yjo editores. La infracción de los derechos mencionados puede ser cons­ titutiva de delito contra la propiedad intelectual. © José Manuel Díaz Martínez © EDITORIAL SANZ Y TORRES, S. L. cj Pinos Alta, 49 - 28029 Madrid � 902 400 415 - 91 314 55 99 www.sanzytorres.com libreria@sanzytorres.com www.sanzytorres.com/editorial editorial@sanzytorres.com ISBN: 978-84-15550-17-4 Depósito legal: M-26492-2012 Portada: Masterline. S. L. cj Las Minas, 1, 28250 Torrelodones (Madrid) Composición: Autor Impresión: Jacaryan, S. A., Avda. Pedro Díez, 3, 28019 Madrid Encuadernación: Felipe Méndez, S. A., cj Del Carbón, 6 y 8, Poi. lnd. San José de Valderas 2, 28918 Leganés (Madrid) " Indice general Prefacio XIII Lista de acrónimos y abreviaturas XIX l. Sistemas Operativos Basados en UNIX (SOBUNIX): introducción general 1.1. 1 .2. 1 .3. 1 .4. 1 .5 . 1 .6. 1 .7 . 1 .8 . l. 9. 1 . 1 O. 1.11. Introducción . . . . . . . . . . . . . . . Cronología histórica de los SOBUNIX . . . . Compatibilidad entre los SOBUNIX . . . . . Características principales de los SOBUNIX . Interfaces con los usuarios disponibles e n SOBUNIX 1 .5. 1 . Intérpretes de comandos . . . 1 .5.2. Interfaces de usuario gráficas . . . . . . . . 1 . 5 . 3 . Llamadas al sistema . . . . . . . . . . . . Introducción a la gestión de archivos en SOBUNIX Seguridad y protección en SOBUNIX . l.7 .l. Identificadores reales . . . . . . 1 .7 .2. Máscara de modo de un archivo 1 .7 . 3 . Identificadores efectivos . . . . l.7 .4. Implementación de la seguridad Resumen . . . . . . . Lecturas recomendadas Autoevaluación Ejercicios . . . . . . . 2. SOBUNIX: implementación y control de procesos multihilos 2. 1 . Introducción . . . . . . . . . . . . . . . . . . . . . . . . . 2.2. Implementación de los procesos multihilos en SOBUNIX . 2.2. 1 . Conceptos fundamentales . . . . . . . . . . . . . 2.2.2. Estructuras de datos asociadas a los procesos multihilos 2.3. Control de procesos multihilos en SOBUNIX . . . . . . . . . . VII 1 2 2 5 5 7 7 19 22 23 25 25 27 32 33 35 36 37 40 45 46 46 46 49 56 Ampliación de sistemas operativos vm 20 30 1 . Creación de procesos e invocación de otros programas 20 3020 Terminación de procesos 20 30 30 Notificación de eventos: señales 20 3 .40 Control de hilos de usuario: librerías de hilos 20 3050 Grupos de procesos y sesiones 20 3060 El sistema de archivos procfs Resumen Lecturas recomendadas Autoevaluación Ejercicios o o o o o o o o o o o o o o o o o o 56 63 67 76 79 81 84 85 85 88 o 2.40 20 50 2060 2070 o o o o o o o o o o o o o o 3. SOBUNIX: planificación, sincronización y mecanismos IPC 93 30 1 . Introducción 3020 Planificación de procesos multihilos en SOBUNIX 3020 1 . Características generales 302020 Dos ejemplos ilustrativos de la planificación en SOBUNIX 302030 Expropiación del procesador 30 30 Mecanismos de sincronización del núcleo en SOBUNIX 30 30 1 . Cerrojos 30 3020 Semáforos 3.40 Dormir/despertar y colas de hilos dormidos en S OBUNIX 3050 Mecanismos de comunicación entre procesos en SOBUNIX 3050 1 . Mecanismos IPC del System V 30 5020 Otros mecanismos IPC 3060 Resumen 3 7 Lecturas recomendadas 3080 Autoevaluación 3090 Ejercicios o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o 94 94 94 95 1 09 1 10 111 1 13 1 16 1 18 1 19 133 135 136 136 1 38 o o 4. SOBUNIX: administración de memoria 143 40 1 . Introducción 4020 Gestión del espacio de direcciones virtuales de un proceso en SOBUNIX 40 20 1 . Organización del espacio de direcciones virtuales de un proceso 402020 Mapeado de archivos en memoria 402030 Estructuras de datos gestionadas por el núcleo asociadas al espacio de direcciones virtuales de un proceso 402.40 Memoria anónima 402050 Memoria compartida 40 20 60 Operaciones sobre el espacio de direcciones virtuales de un proceso 402070 Creación del espacio de direcciones virtuales de un proceso o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o 1 44 1 45 1 45 1 48 1 52 1 55 1 57 1 59 1 60 Índice general 4.3. Traducción de direcciones virtuales a direcciones físicas en SOBUNIX . . . . . . . . . . . . . . . . . . 4. 3 . 1 . Tablas de páginas y MMU . . . . . 4.3.2. Tratamiento de los fallos de página 4.4. Gestión de la memoria física en SOBUNIX 4.4. 1 . Estructuras de datos utilizadas en la gestión de la memoria física . 4.4.2. Reemplazamiento de páginas . . . 4.4. 3 . Intercambio de procesos . . . . . . . . 4.4.4. Asignación de memoria principal . . . 4. 5 . Gestión del área d e intercambio en SOBUNIX . 4. 6. Gestión de la memoria perteneciente al núcleo en SOBUNIX 4.6. 1 . Espacio de direcciones virtuales del núcleo 4.6.2. Asignación de la memoria del núcleo 4.7. Resumen . . . . . . . 4.8 . Lecturas recomendadas 4. 9. Autoevaluación 4. 1 0. Ejercicios . . . . . . . 161 161 1 65 1 66 1 66 1 68 171 1 72 173 1 77 1 77 1 78 18 1 182 18 2 184 5. SOBUNIX: gestión de archivos y gestión de la E/S 5. 1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2. Gestión de archivos en SOBUNIX: características principales y llamadas al sistema 5 .2. 1 . Archivos . 5. 2.2. Directorios 5 . 2 . 3 . Enlaces . . 5 .2.4. Sistemas de archivos 5. 3 . Capa nodo virtual/sistema de archivos virtual 5 . 3 . 1 . Nodos virtuales . . . . . . . . . . . . 5 . 3 .2. S istemas de archivos virtuales . . . . 5 . 3 . 3 . Análisis d e nombres de rutas en SOBUNIX 5.4. El sistema de archivos UFS . . . . . . . . . . . . 5 .4. 1 . Características principales . . . . . . . . 5 .4.2. Estructura de un sistema de archivos UFS 5 .4. 3 . Implementación de directorios e n UFS . 5 .4.4. Gestión de un sistema UFS . 5 . 5 . Gestión d e la E/ S e n SOBUNIX 5 . 5 . 1. Perspectiva general . . . 5.5.2. Archivos de dispositivos 5 . 5 . 3 . Subsistema de E/ S . . 5 .5.4. Drivers de los dispositivos de E/ S 5 . 5 . 5 . Tratamiento d e las interrupciones . IX 187 • • o o o o o o 187 18 8 188 1 95 1 97 200 202 203 206 208 209 209 210 21 1 212 216 216 217 219 220 22 1 x Ampliación de sistemas operativos 5.6. 5. 7. 5.8 . 5.9. 5. 10. Conectores . . . . . . Resumen . . . . . . . Lecturas recomendadas Autoevaluación Ejercicios . . . . . . . 6. SOBUNIX: el sistema operativo Linux 6. 1 . Introducción . . . . . . . . . . . . . 6.2. Consideraciones generales sobre Linux . 6.2. 1 . Orígenes de Linux . . . . . . 6.2.2. Versiones del núcleo de Linux 6.2.3. Distribuciones Linux . . . . . 6 . 3 . Gestión de procesos e hilos en Linux . 6.3. 1 . Estructuras del núcleo asociadas a las tareas . 6.3.2. Creación de procesos e hilos en Linux 6 . 3 . 3 . Hilos del núcleo en Linux . . . . . . . . . . 6.4. Planificación en Linux . . . . . . . . . . . . . . . . 6.4. 1 . Planificador usado en las versiones del núcleo de Linux anteriores a la 2.6.23 6.4.2. Planificador utilizado a partir de la versión 2.6.23 del núcleo de Linux 6.5. Gestión de memoria en Linux . . . . . . . . . . . . 6.5. 1 . Gestión del espacio de direcciones virtuales 6.5.2. Gestión de la memoria física . . . . . . . . 6.6. Gestión de archivos en Linux . . . . . . . . . . . . 6.6. 1 . Implementación de la capa nodo virtual/sistema de archivos virtual en Linux 6.6.2. El sistema de archivos EXT2 . 6.6. 3 . E l sistema d e archivos EXT3 . 6.6.4. El sistema de archivos EXT4 . 6. 7. Resumen . . . . . . . 6.8 . Lecturas recomendadas 6.9. Autoevaluación 6. 1 O. Ejercicios . . . . . . . 7. El sistema operativo MS-DOS 7 . 1 . Introducción . . . . . . . . . . . . . . . . . 7.2. Consideraciones generales sobre MS-DOS . 7.2. 1 . Cronología histórica . . . . . . . . 7.2.2. Características principales de MS-DOS 7.2.3. Estructura del sistema operativo MS-DOS . 7.2.4. Llamadas al sistema . 7.2.5. Arranque de MS-DOS 7.2.6. Interfaz con el usuario 222 224 226 226 229 231 23 1 232 232 232 233 233 234 239 240 24 1 242 247 250 250 252 26 1 26 1 263 267 268 269 270 270 272 273 274 274 274 275 276 277 278 279 Índice general 7.3. Implementación y control de procesos en MS-DOS 7 . 3 . 1 . Creación y ejecución de un proceso 7.3 .2. Terminación de un proceso . 7.4. Gestión de memoria en MS-DOS . 7.5. Gestión de archivos en MS-DOS 7 . 5 . 1 . Archivos . . . . . . 7.5.2. Directorios . . . . . 7 . 5 . 3 . Sistemas de archivos 7.6. El sistema de archivos FAT . 7.6. 1 . Estructura de un sistema FAT 7.6.2. Implementación de directorios en un sistema FAT . 7 . 6 . 3 . Localización d e los clusters de datos d e u n archivo en u n sistema FAT 7.7. Gestión de la E/S en MS-DOS . . . 7.7. 1 . Subsistema de E/S . . . . . . 7.7.2. Drivers de dispositivos E/S . . 7 . 7 . 3 . Manej adores de interrupciones 7.8 . Resumen . . . . . . . 7.9. Lecturas recomendadas 7. 1 O. Autoevaluación 7. 1 1 . Ejercicios . . . . . . . 8. El sistema operativo Windows 8 . 1 . Introducción . . . . . . . . 8 .2. Consideraciones generales sobre Windows . 8 .2. 1 . Cronología histórica . . . . . . . . 8 .2.2. Características principales de Windows 8 .2.3. Estructura del sistema operativo Windows . 8 .2.4. Implementación de la API Win32 . 8 .2.5. Interfaz con el usuario de Windows 8 .2.6. Llamadas al sistema en Windows 8 .2.7. El registro de Windows . 8 .2.8 . Arranque de Windows 8 .2.9. Objetos del núcleo . . . 8 .2. 1 0. Seguridad en Windows . 8 .3 . Implementación, control y planificación de procesos multihilos en Windows 8 . 3 . 1 . Implementación de los procesos multihilos en Windows 8 .3.2. Control de procesos multihilos en Windows . . . . . . . . . . 8 . 3 . 3 . Planificación de procesos multihilos en Windows . . . . . . . 8 .4. Sincronización y comunicación entre procesos multihilos en Windows 8 .4. 1 . Mecanismos de sincronización usados por el sistema operativo 8 .4.2. Mecanismos de sincronización entre procesos multihilos disponibles en Windows XI 28 3 28 3 28 4 28 4 28 7 28 7 28 9 28 9 290 290 29 1 292 292 292 294 294 295 2 96 296 297 299 300 300 300 302 303 305 306 307 308 3 10 3 10 311 313 313 316 319 325 325 325 XII Ampliación de sistemas operativos 8 .4.3. Mecanismos de comunicación entre procesos multihilos disponibles en Windows 8 .5 . Gestión de memoria en Windows . . . 8 .5 . 1 . Administrador de memoria . . 8 . 5.2. Gestión de la memoria virtual 8 .5 . 3 . Traducción de direcciones . . 8 .5.4. Gestión de la memoria física . 8 .5 . 5 . Respaldo d e páginas e n memoria secundaria . 8 .6. Gestión de archivos en Windows . . . . . . . . . . . 8 . 6. 1 . Funciones de l a API Win32 asociadas a l a gestión de archivos y directorios 8 .6.2. Sistemas de archivos . . . 8 .7. El sistema de archivos NTFS . . . . . . . . . . . . 8 .7. 1 . Características principales . . . . . . . . . 8 .7.2. Estructura en el disco de un sistema NTFS 8 .7 . 3 . Estructura d e un directorio e n NTFS . . . . 8 .7 .4. Localización de archivos en un sistema NTFS . 8 .8 . Gestión de E/S en Windows . . . . . . . . 8 .8 . 1 . Subsistema de E/S . . . . . . . . . . . 8 .8 .2. Drivers de dispositivos en Windows . . 8 .8 . 3 . Gestión d e interrupciones e n Windows . 8 .9. Resumen . . . . . . . 8 . 10. Lecturas recomendadas 8 . 1 1 . Autoevaluación 8 . 1 2. Ejercicios . . . . . . . 327 328 328 329 332 335 338 339 339 340 341 341 344 348 349 35 1 35 1 352 354 356 358 358 361 A. Soluciones completas de los ejercicios A . 1 . Soluciones ejercicios capítulo 1 . A.2. Soluciones ejercicios capítulo 2 . A . 3 . Soluciones ejercicios capítulo 3 . A.4. Soluciones ejercicios capítulo 4 . A.5. Soluciones ejercicios capítulo 5 . A.6. Soluciones ejercicios capítulo 6 . A.7. Soluciones ejercicios capítulo 7 . A.8 . Soluciones ejercicios capítulo 8 . 363 Bibliografía 401 Índice alfabético 403 363 375 38 0 38 7 38 9 392 395 397 Prefacio Sobre el autor José Manuel Díaz Martínez es profesor titular de universidad en el Departamento de Informática y Automática de la Escuela Técnica Superior de Ingeniería Informática de la Universidad Nacional de Educación a Distancia (UNED). Desde el año 2001 imparte docencia en asignaturas relacionadas con la materia Sistemas Operativos. Asimismo ha escrito diferentes materiales para el aprendizaje de esta materia entre ellos el libro [Díaz et al., 2011]. A quién va dirigido este libro Este libro está concebido para ser la bibliografía básica de la asignatura Ampliación de Sistemas Operativos que se imparte en el tercer curso del Grado en Ingeniería en Tecnologías de la Información de la UNED. Asimismo puede resultar de utilidad a estudiantes de Escuelas de Ingenierías o Facultades de Ciencia que tengan en sus planes de estudio asignaturas relacionadas con los sistemas operativos. Por supuesto este libro también puede ser utilizado por cualquier persona interesada en esta materia. Objetivos En el Grado en Ingeniería en Tecnologías de la Información de la UNED la materia Sistemas Opera­ tivos se diversifica en dos asignaturas: Sistemas Operativos y Ampliación de Sistemas Operativos. En la asignatura Sistemas Operativos que se imparte en el segundo curso se adquieren los fundamentos básicos de los sistemas operativos: descripción y control de procesos, planificación de procesos, comunicación y sincronización de procesos, interbloqueo, administración de memoria, memoria virtual, gestión de la E/S, gestión de archivos y seguridad y protección. Por otra parte en la asignatura Ampliación de Sistemas Operativos que se imparte en el tercer curso se pretende consolidar y practicar con los fundamentos básicos de los sistemas operativos adquiridos en la asignatura Sistemas Operativos. Para lograr este objetivo en esta asignatura se estudian los sistemas operativos basados en UNIX (BSD, System V, Solaris, Linux, etc) y los sistemas operativos MS-DOS y Windows. Este estudio se realiza tanto desde un punto de vista interno, describiendo las características principales del núcleo de estos sistemas operativos, como desde un punto de vista externo, describiendo y practicando con las llamadas al sistema y comandos disponibles en estos sistemas operativos. xm Ampliación de sistemas operativos XIV _ __ ___ ---------- Este libro tiene como objetivo fundamental explicar, de la forma más clara posible, las características internas y externas de los Sistemas Operativos Basados en UNIX (SOBUNIX) y de los sistemas opera­ tivos MS-DOS y Windows. Como resultado del estudio y aprendizaje de los contenidos de este libro el estudiante será capaz de: • Conocer las características generales de los SOBUNIX. • Conocer los comandos básicos de los intérpretes de comandos de los SOBUNIX. • Conocer las principales llamadas al sistema disponibles en los SOBUNIX. • Conocer la implementación y control de los procesos multihilos de los SOBUNIX. • Conocer la planificación de procesos multihilos, los mecanismos de sincronización del núcleo y los mecanismos IPC de los SOBUNIX. • Saber cómo se realiza la administración de memoria en los SOBUNIX. • Saber cómo se realiza la gestión de archivos y la gestión de la E/S en los SOBUNIX. • Conocer las particularidades del sistema operativo Linux. • Conocer las características generales del sistema operativo MS-DOS . • Conocer los comandos básicos del intérprete de comandos de MS-DOS . • Conocer la implementación y control de los procesos en MS-DOS . • Saber cómo se realiza la administración de memoria en MS-DOS . • Conocer cómo se realiza la gestión de archivos y la gestión de la E/S en MS-DOS . • Conocer las características generales del sistema operativo Windows. • Conocer los principales llamadas al sistema del sistema Windows. • Conocer la implementación, control y planificación de los procesos multihilos en Windows. • Conocer los mecanismos de sincronización del núcleo y los mecanismos de comunicación entre procesos de Windows. • Saber cómo se realiza la administración de memoria en Windows. • Saber cómo se realiza la gestión de archivos y la gestión de la E/ S en Windows. Prefacio xv (�onocimientos previos necesarios Para comprender adecuadamente los contenidos de este libro es necesario conocer los fundamentos básicos de la materia Sistemas Operativos: [Silberschatz et. al, 2002] , [Stallings, 2005] , [Tanenbaum , 2009 ] y [Díaz et al., 20 1 1 ] . También es necesario tener conocimientos básicos de programación en C : 1 Kernighan y Ritchie, 199 1 ] y [Gottfried, 2005] . Organización Los contenidos de este libro se organizan en ocho capítulos. Conviene señalar que en la organización propuesta se ha tenido en cuenta el hecho de que muchos SOBUNIX son de código abierto a diferencia de MS-DOS y Windows que son de código cerrado. Por ello se ha preferido dedicar más de la mitad del libro al estudio de los S OBUNIX, ya que el estudiante interesado podrá acceder al código fuente de diversos miembros de esta familia de sistemas operativos. En el capítulo 1 se realiza una introducción a los SOBUNIX. En primer lugar se comentan la cro­ nología histórica , la compatibilidad y las características principales de los SOBUNIX. En segundo lugar se describen los interfaces con los usuarios disponibles en los SOBUNIX. En tercer lugar se realiza una introducción a la gestión de archivos en SOBUNIX. Finalmente, se describe como se implementa la seguridad y la protección en los SOBUNIX. El capítulo 2 está dedicado la implementación y control de procesos multihilos en los SOBUNIX . En la primera parte del capítulo se trata la implementación de los procesos multihilos: conceptos fundamen­ tales y principales estructuras de datos. En la segunda y última parte se trata el control de los procesos multihilos: creación, invocación y terminación de procesos, notificación de eventos mediante señales, librerías de hilos, grupos de procesos y sesiones, y el sistema de archivos procfs. El capítulo 3 está dedicado a la planificación, sincronización y mecanismos IPC de los SOBUNIX. En la primera parte del capítulo se describen las características generales de la planificación de procesos multihilos. En esta parte, a modo de ejemplo, se estudia la implementación de la planificación de dos SOBUNIX ilustrativos : BSD 4.3 y Solaris. Posteriormente se incluyen diferentes consideraciones sobre la expropiación del procesador. En la segunda parte del capítulo se describen los principales mecanismos de sincronización del núcleo de los SOBUNIX: cerrojos y semáforos. También se incluyen algunas consideraciones sobre la implementación de las operaciones de dormir y despertar y sobre las colas de procesos o hilos dormidos. En la tercera y última parte se describen los principales mecanismos IPC disponibles en los SOBUNIX: los mecanismos IPC del System V (semáforos, colas de mensajes y memoria compartida), las señales y las tuberías. El capítulo 4 está dedicado a la administración de memoria en los SOBUNIX. En primer lugar se estudia la gestión del espacio de direcciones virtuales de un proceso. En segundo lugar se describe la tra­ ducción de direcciones virtuales a direcciones físicas. En tercer lugar se analiza la gestión de la memoria física . En cuarto lugar se describe la gestión del área de intercambio. Finalmente se describe la gestión de la memoria del propio núcleo. El capítulo 5 está dedicado al estudio de la gestión de archivos y la gestión de la E/S en los SO­ BUNIX. En primer lugar se explican las características principales de los archivos, los directorios, los XVI Ampliación de sistemas operativos enlaces y los sistemas de archivos en los SOBUNIX. Asimismo se enumeran y describen las principales llamadas al sistema disponibles en los SOBUNIX para la gestión de estos elementos. En segundo lugar se describe la capa nodo virtual/sistema de archivos virtual utilizada en la mayoría de los SOBUNIX modernos para poder soportar diferentes tipos de sistemas de archivos. En tercer lugar se explica cómo se analizan los nombres de rutas en los SOBUNIX. En cuarto lugar se describe el sistema de archivos UFS , el cual es utilizado en diversos SOBUNIX. En cuarto lugar se describe la gestión de la E/S en los SOBUNIX. El capítulo finaliza con una introducción a los conectores que son uno de los principales mecanismos utilizados en S OBUNIX para implementar las conexiones en red. El capítulo 6 está dedicado al estudio de las particularidades del sistema operativo Linux, uno de los miembros más populares de la familia de SOBUNIX. En primer lugar se incluyen una serie de conside­ raciones generales sobre Linux. En segundo lugar se describe el modelo de proceso multihilo utilizado en Linux. En tercer lugar se estudia la planificación de procesos multihilos en Linux. En cuarto lugar se estudia la implementación de la gestión de memoria. Finalmente se estudian los sistemas archivos nativos de Linux: EXT2, EXT3 y EXT4. El capítulo 7 está dedicado al estudio del sistema operativo MS-DOS . En primer lugar se realizan una serie de consideraciones generales. En segundo lugar se describen la implementación y el control de procesos. En tercer lugar se estudia la gestión de memoria. En cuarto lugar se estudia la gestión de archivos y el sistema de archivos FAT- 1 2. Finalmente se estudia la gestión de la E/S. El capítulo 8 está dedicado al estudio del sistema operativo Windows. En primer lugar se realizan una serie de consideraciones generales sobre Windows. En segundo lugar se describe la implementación, control y planificación de procesos multihilos. En tercer lugar se estudian los mecanismos de sincroniza­ ción del núcleo y los mecanismos IPC. En cuarto lugar se estudia la gestión de memoria. En quinto lugar se describe la gestión de archivos y el sistema de archivos NTFS . Finalmente se estudia la gestión de la E/S . E l libro también contiene u n apéndice A en el que s e incluyen las soluciones completas de los ejer­ cicios incluidos al final de cada capítulo, la mayoría de los cuales han sido propuestos en exámenes. Cómo usar el libro Este libro está pensado para la educación a distancia, por ello sus contenidos han sido organizados y seleccionados para un aprendizaje progresivo y secuencial. Además se incluyen bastantes figuras y ejemplos que ayudan a comprender los contenidos expuestos. Por otra parte, el estudiante dispone de cuestiones de autoevaluación y de las soluciones de todos los ejercicios para poder comprobar si efectivamente ha asimilado los contenidos y ha alcanzado los objetivos marcados. Todos los capítulos tienen una estructura uniforme. En primer lugar, se enumeran los objetivos do­ centes del capítulo. En segundo lugar, se realiza una introducción a los contenidos del capítulo. En tercer lugar, se incluyen los contenidos propiamente dichos. En cuarto lugar, se realiza un resumen de los con­ tenidos, el cual ayuda a fijar los contenidos más importantes. En quinto lugar, se incluyen las lecturas recomendadas. En sexto lugar, se incluyen las cuestiones de autoevaluación, a través de las cuales el estu­ diante puede establecer el grado de asimilación de los contenidos y deducir qué contenidos debe repasar. Prefacio XVII Jiinalmente, se plantean varios ejercicios con los que practicar o ampliar los contenidos aprendidos . Se recomienda intentar hacer cada ejercicio antes de mirar su solución en el apéndice A. Sobre las unidades de almacenamiento de la información usadas en este libro La unidades básicas de almacenamiento u organización de la información son el bit (símbolo b) y el O o un 1 , que se utiliza para representar una pareja de posibles estados, por ejemplo: encendido y apagado, presente y ausente, etc. Un byte son ocho bits contiguos. En la siguiente tabla se incluyen los prefijos que se utilizan en este texto para especificar los múltiplos de las unidades anteriores, de acuerdo con el SI y el estándar 1 54 1 del IEEE. byte (símbolo B). Un bit es un dígito binario, es decir, un Prefijo kibi kilo mebi mega gibi giga tebi tera pebi peta exbi exa Símbolo Ki k Mi M Gi G Ti T Pi Valor 21U 10 3 220 1 06 230 10 9 240 1 01 2 250 p 1 01 5 Ei E 1 10 8 260 Sistema de unidades IEEE 1 54 1 SI IEEE 1 54 1 SI IEEE 1 54 1 SI IEEE 1 54 1 SI IEEE 1 54 1 SI IEEE 1 54 1 SI Contacto con el autor: envío de erratas, comentarios y sugerencias Pese al esfuerzo realizado por explicar lo mej or posible los contenidos incluidos en este libro y evitar la existencia de erratas el autor es consciente de que seguramente habrá fracaso en su objetivo, por este motivo estará encantado de recibir en la dirección a s o@di a . uned . e s todas las erratas, comentarios y sugerencias que los lectores deseen enviar. Además en la página web h t tp: / / www . uned . e s / 7 1 0 2 3 0 1 6 /ma t e r i a l e s . html se irán recopilando todas las erratas que se vayan detectando así como las aclaraciones a los contenidos del libro que sean necesarias. Lista de acrónimos y abreviaturas ALPCs APC API ASCII BCPL BIOS BSD BSS CD CDE CFS CPU DACL DLL DPC DVD E/S EFS EGID EMS EUID EXT EXT2 EXT3 EXT4 FAT FCB FFS FIFO FSD GDI Advanced Local Procedure Calls Asynchronous Procedure Call Application Program Interface American Standard Code for Information Interchange Basic Combined Programming Language Basic Input-Output System Berkeley Software Distribution Block Started by Symbol Compact Disc Common Desktop Environment Completely Fair Scheduling Central Processing Unit Discretionary Access Control List Dynamic Link Library Deferred Procedure Call Digital Versatile Disc Encryption File System Effective Group IDentifier Expanded Memory Specifications Effective U ser IDentifier EXTended filesystem Second EXTended filesystem Third EXTended filesystem Fourth EXTended filesystem File Allocation Table File Control B lock Fast File System First Input - First Output File System Driver Graphics Device Interface XIX Llamadas a procedimientos locales avanzados Llamadas a procedimientos asíncronos Interfaz de programas de aplicación Código estándar americano para el intercam­ bio de información Lenguaje de programación combinado básico Sistema básico de Entrada/Salida Distribución de software de B erkeley B loque inicializado con símbolos Disco compacto Entorno de escritorio común Planificación completamente j usta Unidad central de procesamiento Lista de acceso discrecional Librería de enlace dinámico Llamadas a procedimientos aplazados Disco versátil digital Entrada/Salida Encriptación del sistema de archivos Identificador de grupo efectivo Especificaciones de memoria expandida Identificador de usuario efectivo Sistema de archivos extendido Segundo sistema de archivos extendido Tercer sistema de archivos extendido Cuarto sistema de archivos extendido Tabla de asignación de archivos B loque de control del archivo S istema de archivos rápido Primero en entrar primero en salir Driver del sistema de archivos Interfaz de dispositivos gráficos XX Ampliación de sistemas operativos GID GNU GPL GUI HAL HAT IFS IP IPC IRP IRQL KDE LPC LRU LSI LWPID MFT MIT MMU M S-DOS msgid MULTICS mutex NFS NTFS NUMA Nodo-i Nodo-s Nodo-v npi OSF PAE PCs PEB PID PFRA PGID PnP POS IX procfs PSP R/W RAM RFS Group IDentifier GNU is Not Unix General Public License Graphical U ser Interface Hardware Abstraction Layer Hardware Address Translation Installable File System Internet Protocol InterProcess Communications 1/0 Request Packet Interrupt Request Level K Desktop Environment Local Procedure Call Least Recently U sed Large S cale lntegration LightWeight Process IDentifier Master File Table Massachusetts Institute of Technology Memory Management Unit MicroSoft-Disk Operating System Message queue identification MULTiplexed Information and Computing Service mutual exclusion Network File System New Technology FileSystem Non-Uniform Memory Access Open Software Foundation Physical Address Extension Personal Computers Process Environment Block Process IDentifier Page Frame Reclaiming Algoritm Process Group IDentifier Plug-and-Play Portable Operating System Interface based on UNIX process file system Program Segment Prefix Reader/Wri ter Random Access Memory Remote File Sharing Identificador de grupo GNU No es Unix Licencia pública general Interfaz de usuario gráfica Capa de abstracción del hardware Traducción de direcciones hardware Sistemas de archivos instalables Protocolo de Internet Comunicaciones entre procesos Paquete de petición de E/ S Nivel de petición de interrupción Entorno de escritorio K Llamada a procedimiento local Usada menos recientemente Integración a gran escala Identificación de proceso ligero Tabla de archivos maestra Instituto Tecnológico de Massachusetts Unidad de gestión de memoria Sistema operativo de disco de Microsoft Identificación de la cola de mensajes Servicio de cálculo e información multiplexada exclusión mutua Sistema de archivos en red Sistema de archivos de tecnología nueva Acceso a memoria no uniforme Nodo índice Nodo sombra Nodo virtual Nivel de prioridad de la interrupción Fundación de software abierto Extensión de dirección física Computadores personales Bloque de entorno del proceso Identificador de proceso Algoritmo de reclamo de marcos de página Identificador de grupo de procesos Conectar y usar Interfaz de sistema operativo portable basada en UNIX sistema de archivos de procesos Prefijo del segmento del programa Lector/Escritor Memoria de acceso aleatorio Compartición de archivos remotos Lista de acrónimos y abreviaturas ROM RPCs S5FS SAV SAM SCB semid SFT shmid SI SID SOBUNIX SUA Read Only Memory Remote Procedure Calls System V FileSystem sus Single UNIX Specification Transmission Control Protocol Thread Environment Block Thread Group IDentifier Task IDentifier Translation Lookaside Buffer Terminate and Stay Resident U ser Datagram Protocol UNIX File System User IDentifier Uniform Memory Access Universal Serial Bus Virtual Address Descriptor Virtual File System Virtual Memory Windows Driver Model Extended Memory Specifications TCP TEB TGID TID TLB TSR UDP UFS UID UMA USB VAD VFS VM WDM XMS Security Account Manager Stream Control Block Semaphore identification System File Table Shared memory identification Le Systéme International d'Unités Session IDentifier Subsystem for Unix-based Applications XXI Memoria de sólo lectura Llamadas a procedimientos remotos Sistema de archivos del S ystem V Sistema de Archivos Virtual Administrador de cuentas de seguridad B loque de control del flujo Identificación del semáforo Tabla de archivo del sistema Identificación de la memoria compartida S istema Internacional de unidades Identificador de sesión Sistemas Operativos Basados en UNIX Subsistema para aplicaciones basadas en UNIX Especificación única de UNIX Protocolo de control de transmisión Bloque de entorno del hilo Identificador de grupo de hilos Identificador de tarea Buffer de traducción de vista lateral Terminar y permanecer residente Protocolo datagrama de usuario Sistema de archivos UNIX Identificador de usuario Acceso a memoria uniforme Bus universal en serie Descriptor de direcciones virtuales Sistema de archivos virtual Memoria virtual Modelo de drivers de Windows Especificaciones de memoria extendida Capítulo 1 Sistemas Operativos Basados en UNIX (SOBUNIX): introducción general Objetivos docentes Los objetivos docentes de este capítulo son los siguientes : • Conocer el origen y la evolución del sistema operativo UNIX y de los principales Sistemas Ope­ rativos Basados en UNIX (SOBUNIX). • Saber cuáles son los principales estándares de compatibilidad soportados por los SOBUNIX. • Conocer las características principales de los SOBUNIX. • Conocer el funcionamiento y la implementación de las interfaces de usuario de los SOBUNIX: interfaz de línea de comandos (intérpretes de comandos) e interfaz gráfica. • Conocer las principales acciones que de forma general realiza el núcleo de un SOBUNIX para atender una llamada al sistema. • Conocer los comandos básicos asociados a la gestión de archivos y directorios disponibles en los intérpretes de comandos de los SOBUNIX. • Conocer la estructura básica y las características de los archivos de comandos (shell scripts) de los SOBUNIX. • Conocer los conceptos de objeto de archivo abierto, descriptor de archivo y nodo virtual (nodo-v). • Saber cómo se implementa la seguridad y la protección en los SOBUNIX. 1 2 Ampliación de sistemas operativos 1.1. Introducción El sistema operativo UNIX nacido a principios de los años setenta ha sido tomado como base de un gran número de sistemas operativos. En este texto se usará el término SOBUNIX para referirse tanto al UNIX original como a los diferentes Sistemas Operativos Basados en UNIX. En este primer capítulo se realiza una introducción general a los SOBUNIX. En primer lugar se describen la cronología histórica, los estándares de compatibilidad y las principales características de los S OBUNIX. En segundo lugar se describe el funcionamiento y la implementación de los dos tipos de interfaz de usuario disponibles en los SOBUNIX: interfaz de línea de comandos e interfaz gráfica. Además se comentan las acciones que de forma general realiza el núcleo de un SOBUNIX para atender las llamadas al sistema. En el caso de la interfaz de línea de comandos se define qué es un intérprete de comandos, se enu­ meran los principales tipos de intérpretes de comandos, se analiza la estructura de los comandos, se introducen algunos comandos básicos asociados a la gestión de archivos y directorios, se explica qué son las variables del intérprete de comandos y las variables de entorno, y se explica qué son, cómo se construyen y cómo se ejecutan los archivos de comandos (shell scripts). En el caso de las interfaces de usuario gráficas (Graphical U ser Interfaces, GUis) de los SOBUNIX, se describe el sistema X Windows, las librerías widgets toolkits y los entornos de escritorio. En tercer lugar se realiza una breve introducción a la gestión de archivos en los SOBUNIX para presentar los conceptos de objeto de archivo abierto, descriptor de archivo y nodo virtual (nodo-v), los cuales se utilizan en los siguientes capítulos. Finalmente se describen los elementos que de forma general utilizan los SOBUNIX para implementar la seguridad y la protección de los datos de los usuarios: los identificadores reales, los identificadores efectivos y la máscara de modo de un archivo. 1.2. Cronología histórica de los SOBUNIX A finales de los años 60 los laboratorios Bell propiedad de AT&T, General Electric y el MIT cola­ boraron para desarrollar el sistema operativo de tiempo compartido MULTIC S . Las primeras versiones de este sistema operativo tuvieron un rendimiento deficiente y su éxito comercial fue escaso. Por estos motivos, entre otros, los laboratorios Bell decidieron desvincularse del proyecto; aunque mantuvieron a Ken Thompson, uno de los programadores de Bell que había colaborado en MULTICS, pendiente del proyecto. Thompson por iniciativa propia decidió escribir en lenguaje ensamblador una versión reducida de MULTICS para el minicomputador PDP-7. Brian Kernighan, compañero de Thompson en Bell, con­ sideraba al sistema operativo desarrollado por Thompson una versión capada de MULTICS . Por ello, haciendo gala de un gran sentido del humor, lo bautizó inicialmente con el nombre de UNICS . Nótese que UNICS en inglés suena igual que la palabra inglesa eunuchs, que significa eunucos en español. Por simplificar su escritura el nombre de UNICS fue sustituido finalmente por el nombre de UNIX. Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 3 Cuando los responsables de los laboratorios Bell vieron el trabajo de Ken Thompson quedaron gra­ tamente impresionados y decidieron darle apoyo económico para que portara UNIX a la familia de mini­ computadores PDP- 1 1 , bastante popular por aquella época. Puesto que programar en lenguaje ensambla­ dor es bastante tedioso, Thompson decidió que para portar UNIX a la familia PDP- 1 1 reescribiría UNIX en un lenguaje de alto nivel. En primer lugar se decantó por utilizar el lenguaje B que el mismo había desarrollado basándose en BCPL; pero este lenguaje tenía algunas deficiencias importantes, por ejemplo no soportaba estructuras de datos, por lo que pronto se dio cuenta que no era el lenguaj e más apropiado. Dennis Ritchie, otro programador de laboratorios Bell, desarrolló en 1 972 un lenguaje de progra­ mación de alto nivel al que llamó C. Además escribió un compilador de C para los minicomputadores PDP- 1 1 . El lenguaje C [Kemighan y Ritchie, 1 99 1 ] es un lenguaje muy flexible que crea un código bastante eficiente. Ante sus notables ventajas, Thompson decidió reescribir UNIX en C. Para esta tarea contó con la ayuda de Ritchie. Escrito en C el código de UNIX era mucho más conciso y compacto, además posibilitaba su portabilidad no solo a la familia PDP- 1 1 sino también a otras arquitecturas. En 1 974 se dio a conocer públicamente la nueva versión de UNIX [Ritchie y Thompson, 1 974] . Ello propició que muchas universidades se interesaran por este sistema operativo. Como la legislación esta­ dounidense vigente no permitía a AT &T comercializar directamente UNIX, AT &T no tuvo inconveniente en otorgar licencias de UNIX a las universidades con fines educativos y de investigación. Las copias de UNIX que AT &T distribuyó a las universidades incluían también su código fuente lo que propició que la comunidad universitaria aportara sugerencias e ideas para mej orar UNIX. El departamento de computación de la Universidad de California en Berkeley, fue mucho más lejos, ya que modificó tanto el código fuente que desarrolló y distribuyó su propio UNIX al que llamó BSD (Berkeley Software Distribution). Las versiones BSD 1 .0 y BSD 2.0 no modificaban el UNIX original de AT&T simplemente consistían en aplicaciones y utilidades. Es la versión BSD 3 . 0 lanzada en 1 979 la que se considera un sistema operativo completo como tal y la que hizo que BSD se convirtiera en un fuerte competidor del UNIX original de AT&T. Las siguientes versiones de BSD fueron BSD 4.0 en 1 98 0, BSD 4. 1 en 1 98 1 , BSD 4.2 en 1 98 3 , BSD 4.3 en 1 98 6 y BSD 4.4 en 1 993 . Por su parte AT&T sacó la versión 7 de UNIX en 1 979, que sería la última del UNIX original con una amplia distribución, ya que la versión 8 que se liberó en 1 98 5 y las versiones 9 y 1 O que se liberaron a finales de los 8 0 sólo fueron adquiridas por unas pocas universidades. Aparte de las universidades, algunas empresas fabricantes de computadores y de software también obtuvieron licencias de UNIX y produjeron sus propias distribuciones. Un ejemplo es el sistema Xenix, desarrollado por Microsoft Corporation y Santa Cruz Operation (SCO) a principios de los años 8 0, que era una adaptación del UNIX versión 7 a las máquinas lntel 8 08 6. Xenix fue una de las primeras distri­ buciones comerciales de UNIX. Señalar que Microsoft centró sus esfuerzos en otros sistemas operativos y vendió sus derechos de Xenix a SCO, con lo que el sistema operativo pasó a llamarse SCO Xenix. Debido a la proliferación de distribuciones de UNIX, AT &T también decidió entrar en el negocio a través de una compañía filial. Así en 1 98 1 lanzó el sistema System III, la variante comercial del UNIX versión 7. Poco después, en 1 98 3, lanzaría el sistema System V Release 1 , o abreviadamente SVR l . Las siguientes versiones fueron SVR2 en 1 98 4, SVR3 en 1 98 7 y SVR4 en 1 98 8 . Señalar que el sistema System V incorporó características de otras distribuciones de UNIX, como BSD. 4 Ampliación de sistemas operativos El conjunto de las principales distribuciones comerciales de UNIX aparecidas en la década de los ochenta lo completan SunOS , AIX y HP-UX. El sistema SunOS de la compañía Sun Microsystems era una adaptación de BSD para sus máquinas Sun. El que SunOS se basará en BSD y no en el UNIX de AT&T no es casual ya que Bill Joy, uno de los socios fundadores en 1 98 2 de Sun Microsystems, era también uno de los creadores de BSD. Conviene señalar que a partir de su versión 4.0 de 1 98 8 , debido a un acuerdo entre S un Microsystems y AT&T, S unOS también pasó a incorporar características del System V. De hecho, la versión 5 . 0 de SunOS , comercializada en 1 992 con el nombre de Solaris 2, estaba basada exclusivamente en SVR4, en cuyo desarrollo también había colaborado Sun. El sistema AIX fue distribuido por IBM y el sistema HP-UX por Hewlett-Packard. Ambos esta­ ban basados inicialmente en el sistema System III de AT&T, aunque luego se basaron en el System V incluyendo también características de BSD. Otros proyectos y sistemas basados en UNIX aparecidos en la década de los 8 0 y principios de los 90 que merecen ser mencionados por su importancia y porque el objetivo de su desarrollo no era el beneficio económico son: GNU, MINIX y Linux. El proyecto GNU fue propuesto en 1 98 3 por Richard Stallman y pretendía crear un sistema parecido a UNIX que pudiese ser distribuido libremente y que hiciese resurgir el espíritu de colaboración desinteresada que existió a fi nales de los 70. Aunque el proyecto GNU nunca ha llegado a crear un sistema operativo como tal, ya que en sus inicios no se llegó a escribir un núcleo adecuado y luego optaron por Linux, si que generó diversas aplicaciones, como el compilador de C gcc o el editor emacs, que han sido incorporadas en otros SOBUNIX. Actualmente el proyecto GNU sigue vivo y cuenta con una amplia comunidad de colaboradores. El sistema MINIX basado en el funcionamiento del UNIX versión 7 fue desarrollado en 1 98 7 por Andrew S. Tanenbaum con fines educativos; por ello su código es abierto y no muy extenso. Por su parte, el sistema Linux, también de código abierto fue desarrollado a principios de los 90 por Linus B. Torvalds (ver sección 6.2. 1 ) . Volviendo al origen d e todo, e n 1 98 9 AT&T creó la división Unix Systems Laboratories (USL) y en ella delegó el desarrolló y la comercialización del System V. En 1 99 1 , la compañía Novell adquirió parte de USL y creo la filial Univel. Univel lanzó en 1 992 el sistema UnixWare para computadores personales. UnixWare estaba basado en SVR4 y Netware (un sistema operativo de red). En 1 993 Novell se hizo con el resto de USL, aunque en 1 995 la vendió junto con Univel a SCO la cual continuó con el desarrollo del System V. De hecho la versión 7.0 de su sistema UnixWare lanzada en 1 997 estaba basada en SVR5 . SCO también basó su sistema OpenServer en SVR5 . Con respecto al sistema BSD su desarrolló llegó a su fin con la distribución B S D 4.4-Lite Release 2 aparecida en 1 995. Pese a ello han seguido desarrollándose sistemas operativos basados total o parcial­ mente en BSD. Algunos de estos sistemas son libres y de código abierto, como por ejemplo: NetBSD, OpenBSD y FreeBSD. Otros son sistemas comerciales, como por ejemplo, Mac OS X de Apple. En la actualidad la familia de SOBUNIX sigue muy viva y es muy amplia. Algunos son sistemas comerciales, como por ejemplo: Solaris 1 0 de Sun aparecido en 2005 contando como núcleo con SunOS 5 . 1 0, AIX 7. 1 de IBM aparecido en 20 1 1 , HP-UX 1 1 .3 1 de HP aparecido en 2009 y Mac OS X 1 0.8 de Apple aparecido en 20 1 2. Otros son sistemas libres como MINIX 3 . 2 de 20 1 2, OpenSolaris 2009.06 de 2009, NetBSD 5 . 1 .2 de 20 1 2, OpenBSD 5.0 de 20 1 1 , FreeB SD 9.0 de 20 1 2 y Linux 3 . 3 . 6 de 20 1 2 . Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 1.3. 5 Compatibilidad entre los SOBUNIX La gran variedad de SOBUNIX resultaba un serio problema para los desarrolladores de aplicaciones software. Las diferencias existentes entre unos SOBUNIX y otros propiciaban que una misma aplicación pudiera no funcionar correctamente en todos los sistemas. Para intentar resolver este problema se han creado diferentes estándares, también denominados nor­ mas o especificaciones, para definir una API para los SOBUNIX. Una aplicación que es desarrollada usando una API definida por un determinado estándar tiene garantizado que puede ser ejecutada en todos los sistema operativos que soporten dicho estándar. Una API queda definida, entre otros elementos, por el conjunto de llamadas al sistema que pueden invocarse. Aparte del API los estándares también definen las reglas que deben seguir los intérpretes de comandos (shells) y los archivos de órdenes para los intérpretes (shell scripts). Entre los diferentes estándares que aparecieron destaca el estándar POSIX que es soportado por bastantes sistemas operativos. Este estándar fue desarrollado en 1 98 8 por IEEE teniendo en cuenta las características comunes existentes en los sistemas System V y BSD. POSIX consta de diferentes docu­ mentos o extensiones los cuales se conocen con el nombre de familia IEEE Std 1 003, siendo el IEEE Std 1003 . 1 su documento base. También POS IX dispone de un conjunto de tests que permiten certificar que un sistema cumple el estándar. Otro estándar que cada vez está más en auge es el estándar SUS, sobre todo desde que IEEE cobra una cantidad no despreciable por proporcionar la documentación del estándar y los test de POSIX. Este estándar fue desarrollado en 1 997 por The Open Group, un consorcio formado por empresas y agencias gubernamentales que se creó en 1 996 con la unión de X/Open y OSF. Este consorcio también certifica que un sistema cumple con el estándar SUS . Dicha certificación identifica y aprueba a un sistema basado en UNIX. Desde su versión 3 de 200 1 , SUS es compatible con POSIX, ya que el documento base de ambos estándares ha sido desarrollado por The Austin Group un grupo de trabajo en el que participan el IEEE y The Open Group, entre otros. Ejemplos de sistemas operativos que cumplen con SUS versión 3 (equi­ valentemente también se dice que son sistemas UNIX 03) son: AIX 6. 1 , HP-UX 1 1 . 3 1 , Mac OS X 1 0 . 6 y Solaris 10. Los sistemas basados en UNIX de distribución libre no suelen pasar procesos de certificación pero suelen soportar algún estándar. Por ejemplo, Linux y MINIX son plenamente compatibles con alguna versión de POSIX. 1.4. Características principales de los SOBUNIX Las características principales de los SOBUNIX son: • Son sistemas de tiempo compartido, también denominados sistemas interactivos. Aunque algunos incluso soportan también aplicaciones de tiempo real. • Son sistemas multiprogramados y multiusuario, con soporte para multiprocesamiento. 6 Ampliación de sistemas operativos • El núcleo está escrito en su mayor parte en lenguaje C, lo que facilita su portabilidad a distintas arquitecturas. • El núcleo posee una estructura de tipo monolítica o simple. • La planificación se realiza a nivel de procesos o de hilos del núcleo usando un algoritmo de plani­ ficación basada en múltiples colas de planificación y realimentación. • Disponen entre otros de los siguientes mecanismos de comunicación y sincronización entre proce­ sos: tuberías, semáforos, memoria compartida y colas de mensajes. • No suelen implementar ninguna estrategia de tratamiento de interbloqueos. Dichas estrategias si se quieren implementar deben hacerse a nivel de los programas de aplicación. • Administran la memoria principal e implementan la memoria virtual usando la técnica de demanda de página. • Consideran a los archivos como una secuencia de bits y delegan en las aplicaciones la interpreta­ ción de los mismos. • Soportan tanto el acceso secuencial como el acceso aleatorio a los archivos . • E n los nombres d e archivos distinguen entre minúsculas y mayúsculas. • Soportan una estructura de directorios de gráfica acíclica. El directorio raíz se denota con el sím­ bolo' 1 ', que además se usa para separar los componentes del nombre de ruta de un archivo. • Los SOBUNIX modernos implementan una capa nodo virtual/sistema de archivos virtual para poder soportar diferentes tipos de sistemas de archivos. • Cada sistema de archivos se monta en un directorio del sistema de archivos principal. Con lo que todos los sistemas de archivos quedan integrados dentro de una única estructura de directorios. • Los dispositivos de E/S se integran dentro del sistema de archivos mediante el uso de archivos de dispositivos modo bloque y archivos de dispositivos modo carácter. Lo que permite operar sobre ellos con las mismas llamadas al sistema que se utilizan para operar sobre los archivos. • Permiten especificar los permisos de acceso (lectura, escritura y ejecución) a un archivo o directo­ rio de forma independiente para el usuario propietario del archivo, el grupo de usuarios propietario y el resto de usuarios. Conviene señalar que cada SOBUNIX puede implementar estas características de distinta forma. Además cada SOBUNIX puede poseer características particulares no disponibles en el resto de SOBU­ NIX. Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 1.5. 7 Interfaces con los usuarios disponibles en SOBUNIX Los SOBUNIX soportan dos posibles tipos de interfaz de usuario (ver Figura 1 . 1 ) : inteifaz de línea de comandos implementada mediante un intérprete de comandos e inteifaz de usuario gráfica (GUI) . Nótese que desde una interfaz gráfica también se puede disponer de una interfaz de línea de comandos. A través de estos interfaces un usuario puede invocar a otros programas o aplicaciones. Desde el punto de vista de su ejecución tanto los interfaces como los programas tendrán asociado cada uno al menos un proceso monohilo o multihilo. Un proceso (o un hilo) para solicitar los servicios del núcleo del sistema operativo tiene que realizar las llamadas al sistema correspondientes. Típicamente una llamada al sistema se realiza mediante la invocación de una función de la librería estándar C de llamadas al sistema. Modo Usuario } Il Usuarios Otros programas Librería estándar C de llamadas al sistema f------1 Núcleo del sistema operativo } Modo Núcleo Hardware Figura 1.1 - Interacción de los usuarios con un S OBUNIX En las siguientes secciones se describen los intérpretes de comandos que implementan las interfaces de línea de comandos, la implementación de las interfaces gráficas y el tratamiento de las llamadas al sistema. 1.5.1. Intérpretes de comandos Definición e intérpretes más conocidos Un intérprete de comandos (shell) es un programa que acepta un comando 1 introducido por un usua­ rio a través del dispositivo de entrada estándar (por defecto el teclado) y ejecuta su código asociado. El resultado de la ejecución se muestra, generalmente, en el dispositivo de salida estándar (por defecto la pantalla) . Una vez finalizada la ejecución del comando el intérprete queda a la espera de recibir otra orden por parte del usuario. Cada SOBUNIX suele incluir por defecto en su distribución un determinado intérprete de comandos, pero el usuario si así lo desea puede utilizar otro. Entre los intérpretes de comandos más conocidos se encuentran los siguientes: 1 0tro término sinónimo de comando es orden. En este texto se utilizan ambos términos indistintamente. 8 Ampliación de sistemas operativos • Bourne shell (sh). Este intérprete fue escrito a finales de los setenta por Steve B ourne. Se distribuyó inicialmente con el UNIX version 7 de AT &T. • e shell (c sh) . Intérprete escrito a finales de los setenta por Bill Joy. Se distribuyó inicialmente con BSD 2.0. • TENEX e shell ( t c sh) . Este intérprete se basa en C shell y supone una mej ora del mismo. La pri­ mera versión de este intérprete fue escrita Ken Greer a finales de los setenta. Aunque en los ochenta y en los noventa continuaron su desarrollo, Paul Placeway y Wilfredo S ánchez, respectivamente. Este intérprete se distribuye, entre otros sistemas, en FreeB SD y Mac OS X. • Korn shell (ksh) . Este intérprete fue escrito a principios de los ochenta por David Korn. Es com­ patible con B ourne shell e incluye características de C shell. Se ha distribuido, entre otros, en los sistemas SVR4, AIX y HP-UX. • Bourne-Again shell (ba sh) . Fue escrito a finales de los ochenta por Brian Fax para el proyecto GNU. Este intérprete se distribuye, entre otros sistemas, en Linux. • A lmquist shell (ash). Fue escrita por Kenneth Almquist a finales de los ochenta. Se distribuyó inicialmente con BSD 4.3. • Z shell ( z sh) . Fue escrito por Paul Falstad en 1 990. Se considera un intérprete muy completo ya que incluye características de TENEX C shell, B ourne-Again shell y Korn shell, entre otros. La mayoría de los intérpretes enumerados se han ido mejorando posteriormente y han incluido tam­ bién, por motivos de compatibilidad, las especificaciones POSIX. El usar un intérprete u otro suele ser muchas veces una cuestión de gustos, ya que todos incluyen unas características básicas similares. Las principales diferencias entre los intérpretes se encuentran en las características adicionales y avanzadas que pueden soportar y cómo las implementan, como por ejemplo: la corrección automática de la sintaxis de los comandos, el histórico de comandos, el autocomplementado de órdenes y el lenguaje soportado para la escritura de archivos de comandos (shell scripts). Estructura de los comandos Un comando es una cadena de caracteres que de forma general tiene la siguiente estructura: nombre_c omando - op_l - op_2 . . . - op_M arg_l arg_2 . . . arg_N En dicha estructura nombre_c omando es el nombre del comando que se desea ejecutar, - op_j con j = 1 , 2, . . . ,M son opciones que permiten configurar el comportamiento del programa, y arg_i con i = 1 , 2, . . . ,N son los argumentos que acepta el programa. Cada opción se escribe con el prefijo' -', aunque existe la posibilidad de agrupar todas las opciones y usar un único prefijo: nombre_c omando - op_l op_2 . . . op_M arg_l arg_2 . . . arg_N Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 9 Si se desea conocer el significado, las opciones y los argumentos de un determinado comando, se puede consultar el manual de ayuda del sistema desde el propio intérprete mediante la orden: man nombre_c omando El manual de ayuda se divide en secciones. Cada sección se identifica mediante un entero positivo y está asociada a un determinado tipo de recurso: comandos, llamadas al sistema, funciones de librerías, archivos del sistema, macros, etc. A su vez cada sección se divide en páginas, donde cada página está asociada a un determinado recurso identificado por su nombre. Conviene señalar que no todos los re­ cursos disponibles están documentados en el manual de ayuda. Para acceder a una página dentro de una determinada sección se debe especificar el número de la sección antes del nombre del recurso: man número s e c c i ón nombre recurso En ocasiones un mismo nombre designa a diferentes recursos. Por defecto se muestra la primera página que se encuentra en el manual asociada a dicho nombre. Para mostrar todas las páginas asociadas a un mismo nombre hay que usar la opción - a : man - a nombre Para ir avanzando por la información de una página del manual se debe ir presionando la tecla [ intro ] o [ e spac i o ] . Asimismo para volver hacia atrás se debe presionar la tecla [ b ] . Si se desea salir del manual se debe presionar la tecla [ q l . Para conocer las opciones y el funcionamiento del manual de ayuda se puede consultar su propia página del manual: man man Aparte de este manual de ayuda principal existen otros manuales de ayuda específicos de ciertos programas que se pueden consultar usando el comando i n f o . e Ejemplo 1 . 1 Supóngase que e l directorio d e trabajo actual e s 1 expo r t / home 1 j m y que en e l mismo existen, entre otros, el subdirectorio Documento s . Para conocer el nombre de ruta absoluta del directorio de trabaj o actual s e puede usar e l comando pwd. Luego s i e n la línea d e comandos del intérprete s e escribe l a orden pwd en la pantalla aparece como respuesta / expo r t / home / j m Nótese que en este caso el comando ha sido escrito sin opciones ni argumentos. Para acceder a otro directorio, es decir, para cambiar el directorio de trabajo actual, se puede usar el comando cd, el cual admite como argumento el nombre de ruta absoluto o relativo del directorio. Por ejemplo, si se desea acceder al subdirectorio Document o s se puede escribir la orden: 10 Ampliación de sistemas operativos cd / expo rt / home / j m / Documen t o s o equivalentemente: c d Do cumen t o s En el primer caso s e ha utilizado e l nombre d e ruta absoluto del directorio y e n e l segundo e l nombre de ruta relativo. Para regresar al directorio de trabaj o anterior se puede escribir la orden cd / expo r t / home / j m o equivalentemente la orden cd . . Donde " . . " hace referencia el directorio padre del directorio actual. Para mostrar en pantalla un listado de los nombres de los archivos y directorios contenidos en el directorio de trabajo actual se puede usar el comando l s . Este comando admite diferentes opciones que permiten establecer la información que se mostrará sobre los archivos y directorios listados. Por ejemplo, la orden ls -F introduce al final del nombre de los directorios e l símbolo 1 para diferenciarlos del resto de archivos. 1 1 Otro ejemplo, la orden ls -i muestra el número de nodo-i asociado al archivo o directorio correspondiente. Las dos órdenes anteriores se pueden escribir en una sola orden de la siguiente forma: ls -Fi Para conocer todas las opciones que acepta e l comando l s s e puede consultar su página del manual de ayuda man l s Para borrar l a pantalla del intérprete de comandos se puede usar l a orden c l ear. S i se desea cerrar la ventana del intérprete se puede pulsar la combinación de teclas [ c ontro l ] + [ dl o usar el comando exi t . • Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 11 E l número d e comandos soportados por los intérpretes de los SOBUNIX e s elevado. En l a Tabla 1 . 1 se recopilan algunos comandos básicos asociados principalmente a la gestión de archivos y directorios. A lo largo de éste y los restantes capítulos se irán presentando más comandos para la realización de otras tareas. Comando cat cd cp f ind grep ls more mkdir mv mvdi r pwd rm rmdir Utilidad Encadenar y mostrar el contenido de archivos Cambiar de directorio de trabaj o Copiar archivos o directorios B uscar un archivo B uscar una cadena de texto en varios archivos Listar los archivos y directorios de un directorio Mostrar página a página el contenido de un archivo Crear directorio Mover de directorio o renombrar archivos Cambiar de ubicación u n directorio Mostrar el nombre de ruta del directorio de trabaj o actual Borrar archivos o directorios B orrar directorio Tabla 1.1 - Algunos comandos básicos asociados a la gestión de archivos y directorios en SOBUNIX Con el fin de reducir la longitud de la cadena de caracteres asociada a una orden, la mayoría de los intérpretes de comandos de SOBUNIX soportan el uso de comodines (wild cards) en los argumentos de las órdenes. Un comodín es un carácter especial que hace que el intérprete trate el argumento de una determinada forma. Entre los comodines más frecuentemente soportados se encuentran los siguientes : • e ' *' . Este comodín sustituye a cualquier cadena de caracteres. • ' ? ' . Este comodín sustituye a cualquier carácter. • " [ c l c 2 . . . eN ] " . Este comodín significa que se debe sustituir por uno solo de los caracteres c j j = 1 , . . . , N incluidos dentro de los corchetes. Ejemplo 1.2 Supóngase que en el directorio de trabajo actual existen los archivos prueba . e, t raba j o l . txt y t raba j o 2 . txt . Supóngase además que se desea copiar en el subdirectorio Document o s los archivos con extensión txt. Para realizar esta acción se podría usar la siguiente orden: . cp t raba j o l . txt t rabaj o 2 . txt Documen t o s También s e podría reducir la longitud d e l a orden haciendo u s o del comodín' *' : 12 Ampliación de sistemas operativos cp * . txt Document o s En este caso e l comodín 1 * 1 sustituye a todas las cadenas de caracteres posibles, por lo que el intérprete busca en el directorio de trabajo todos los archivos cuyo nombre terminan en 11 • txt 11 • Asimismo si se sabe que el subdirectorio Document o s es el único que empieza por el carácter 1 D 1 enton­ ces la orden se podría haber escrito aún de forma más reducida: cp * . txt D * Usando e l mismo ejemplo s e puede ilustrar el uso de los comodines 1 ? 1 y 11 [ l 11 : cp trabaj o ? . txt Documen t o s cp traba j o [ 1 2 ] . txt Documentos En la primera orden el comodín 1 ? 1 permite sustituir a los caracteres 1 1 1 y 1 2 1 • En la segunda orden se fuerza a que se sustituya por los caracteres 1 1 1 y 1 2 1 • • En los SOBUNIX cuando se ejecuta un programa se crea un proceso para el cual se abren por defecto tres archivos (ver sección 5 . 2 . 1 ) : el archivo de entrada estándar, el archivo de salida estándar y el archivo de salida de errores estándar. El archivo de entrada estándar está asociado por defecto al teclado (ver sección 5 .5), mientras que los archivos de salida estándar y salida de errores estándar están asociados por defecto a la pantalla. Los intérpretes de comandos en los SOBUNIX soportan el redireccionamiento de la E/S, es decir, permite cambiar los archivos de entrada y salida estándar de los comandos que ejecutan. Para ello se pueden usar los siguientes operadores : • Operador 1 < 1 • Permite redireccionar la entrada estándar hacia otro archivo. • Operador 1 > 1 • Permite redireccionar la salida estándar hacia otro archivo. La salida se empieza a almacenar desde el comienzo del archivo, con lo que se sobreescribe su contenido. • Operador 1 > > 1 • Permite redireccionar la salida estándar hacia otro archivo. La salida se empieza a almacenar a partir del final del archivo, con lo que no se eliminan los datos que ya existieran en el mismo. • Operador 1 2 > 1 • Permite redireccionar la salida de errores estándar hacia otro archivo. La salida se empieza a almacenar desde el comienzo del archivo, con lo que se sobreescribe su contenido. • Operador 1 2 > > 1 • Permite redireccionar la salida de errores estándar hacia otro archivo. La salida se empieza a almacenar a partir del final del archivo, con lo que no se eliminan los datos que ya existieran en el mismo. Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 13 También es posible encadenar órdenes de tal forma que la salida de una orden se convierta en la entrada de otra orden. Para ello hay que utilizar una tubería (ver sección 3.5 .2) la cual se representa mediante el carácter ' 1 '. e Ejemplo 1.3 Supóngase que se escribe la siguiente orden: s o r t < da t o s . da t > o rdenado s . txt Esta orden invoca al comando s o r t que ordena por orden alfabético las líneas del archivo da t o s . da t pasado como entrada. L a salida del comando s e guarda e n e l archivo o rdenado s . t x t . Por s u parte l a orden l s -F 1 sort utiliza una tubería para que la salida d e la orden l s - F s e convierta e n la entrada d e la orden s o r t cuya salida se mostrará en la salida estándar, en este caso la pantalla. • Otra característica de los intérpretes de comandos de los SOBUNIX que resulta muy útil para aho­ rrarse escribir ordenes largas o complicadas de recordar que se usen frecuentemente, es la posibilidad de asignar un nombre o alias a una determinada orden o cadena de órdenes. De esta forma, las invocaciones de dicha orden se realizan usando su alias correspondiente. Para asignar un alias a una orden o cadena de órdenes se usa el comando a l i a s . También es posible deshacer la asignación de un alias, para ello se debe usar el comando una l i a s . Es importante tener en cuenta que por defecto la asignación de un alias tiene un carácter temporal, es decir, desaparece al cerrarse el intérprete de comandos. Para hacer la asignación de un alias persistente, es decir, para que se pueda utilizar un alias sin ser definido previamente cada vez que se abra el intérprete, se debe incluir su asignación en los archivos de configuración del intérprete. e Ejemplo 1 .4 Supóngase que un usuario tiene que escribir con frecuencia la siguiente secuencia de órdenes: cp * . txt Do cumen t o s ; cd Document o s ; l s - F Para ahorrarse s u escritura puede asignarles u n alias, al que s e va a llamar por ejemplo c op i ar_txt, de la siguiente forma: a l i as copi ar_txt = ' cp * . txt Document o s ; cd Document o s ; l s - F ' Si se desea quitar este alias simplemente habría que usar la orden: una l i a s c op i ar_txt • 14 Ampliación de sistemas operativos Funcionamiento Un intérprete de comandos de SOBUNIX es un archivo ejecutable que reside en un cierto directorio del sistema de archivos, típicamente en el directorio / b i n . Cuando se invoca a un intérprete se crea un proceso asociado a su ejecución. Este proceso en primer lugar ejecuta las órdenes establecidas en los diferentes archivos de configuración del intérprete (ver la página del manual de ayuda del intérprete correspondiente) con el objetivo de configurar las variables del intérprete de comandos. A continuación muestra el apuntador (prompt) en pantalla que es un carácter o cadena de caracteres cuya aparición en la pantalla sirve para indicar al usuario que ya puede escribir una orden. El funcionamiento del intérprete depende del tipo de comando que se escriba. En general se distin­ guen dos tipos de comandos: externos e internos. Señalar que el comando type permite conocer el tipo (interno o externo) de un comando. Un comando externo es aquél cuyo código de ejecución se encuentra en un archivo ejecutable que debe ser buscado por el intérprete. En general un comando externo es cualquier archivo ejecutable, como por ejemplo: los programas de utilidad y aplicaciones suministrados en la distribución del S OBUNIX y los programas de aplicación descargados o creados por los usuarios. También entran en esta categoría los archivos de comandos (shell scripts) . Un comando interno (built-in command) e s aquél cuyo código d e ejecución forma parte del código del intérprete de comandos . En consecuencia no requiere ser buscado en un archivo ejecutable por el intérprete, puesto que ya se encuentra cargado en su espacio de direcciones. Ejemplos de comandos internos son cd, pwd y a l i a s . Cuando s e teclea u n comando el intérprete lee el nombre d e l a orden y comprueba s i e s una orden interna. En dicho caso analiza la sintaxis de la orden y si es correcta la ejecuta, en caso contrario muestra un mensaje de error en la salida estándar. Si no es un comando interno, entonces el intérprete busca el archivo ejecutable asociado al comando externo. En el caso de que no lo localice mostrará un mensaje de error en la salida estándar. Si lo en­ cuentra crea un nuevo proceso asociado a la ejecución del archivo ejecutable y le pasa los indicadores y argumentos del comando como parámetros. En los SOBUNIX se suele usar el término trabajo (job) para designar al proceso o grupo de procesos que se crean asociados a la ejecución de un comando introducido por el usuario en un intérprete de comandos. Un trabajo se puede ejecutar en primer plano (foreground) o en segundo plano (background). Si un trabaj o se ejecuta en primer plano entonces puede interactuar con el usuario esperando por la recepción de entradas y mostrando salidas. Por el contrario un trabaj o ejecutándose en segundo plano no puede interactuar con el usuario. En un intérprete de comandos, en un determinado instante de tiempo, solamente puede existir un trabaj o ejecutándose en primer plano. Por el contrario pueden existir múltiples trabajos ejecutándose en segundo plano. Si un usuario escribe una orden en el intérprete de comandos, el trabajo asociado a dicha orden se ejecuta por defecto en ptimer plano. Si se desea que el trabajo se ejecute en segundo plano la orden se debe escribir seguida de un espacio y del carácter'&' , es decir: Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 15 c omando & Un trabajo puede ser pasado de segundo plano a primer plano usando el comando f g . Asimismo el trabajo en primer plano puede ser pasado a segundo plano usando el comando bg. Nótese que para poder realizar este cambio el trabajo en primer plano debe ser previamente parado pulsando las teclas [ c ontr o l ] + [ z ] . Para obtener información sobre los trabajos que se están ejecutando en un intérprete se puede usar el comando j obs . Si se desea terminar el trabajo que se está ejecutando en primer plano se deben pulsar las teclas [ c ontro l ] + [ e ] . Variables y entorno Cuando se inicia la ejecución de un intérprete de comandos se crean y se inicializan un conjunto de variables denominadas variables del intérprete de comandos a partir de los archivos de configuración del intérprete. Estas variables son locales al proceso asociado a la ejecución del intérprete y pueden ser utilizadas por los comandos internos. El comando s e t permite visualizar un listado de todas las variables del intérprete. En la página del manual de ayuda asociada al intérprete se describe el significado de cada una de estas variables. Un usuario puede cambiar el valor de algunas de las variables del intérprete. Otras variables, sin embargo, no pueden ser modificadas. Además un usuario también puede definir nuevas variables. Si se desea que los cambios que se realicen en las variables o que las nuevas variables definidas sean persistentes se deben definir en los archivos de configuración del intérprete. e Ejemplo 1.5 Supóngase que se ha iniciado una sesión de trabaj o con un determinado intérprete de comandos. Si se teclea el comando: set s e mostrará en l a salida estándar e l listado de todas las variables del intérprete. Si se desea conocer el valor de una determinada variable se puede usar el comando echo. Por ejemplo, para conocer el valor de la variable PATH, la cual contiene los nombres de ruta de los directorios donde el intérprete busca los comandos externos, se debe escribir la siguiente orden: echo $ PATH Nótese que el valor de una variable se referencia escribiendo ' $ ' delante del nombre de la variable. Si se desea añadir un directorio a la variable PATH, por ejemplo el directorio de trabaj o actual, se puede usar la siguiente orden: PATH= $ PATH : . 16 Ampliación de sistemas operativos ----��--------------------------------------------------------- Recuérdese que el nombre de ruta 1 1 hace referencia al directorio de trabajo actual. • Se puede comprobar que la variable ha sido cambiada escribiendo de nuevo la orden: echo $ PATH También se puede definir una nueva variable MI_VARIABLE y asignarle un cierto valor, de la siguiente forma: MI_VARIABLE= l O Si se escribe de nuevo la orden set s e puede comprobar que la nueva variable h a sido incorporada al listado de variables del intérprete y que la variable PATH contiene los cambios realizados. Señalar que si se cierra el intérprete y se vuelve a iniciar se puede comprobar usando la orden set que los cambios realizados no son persistentes . • Como se describirá en la sección 2.3 . 1 , cuando se invoca a otro programa desde un proceso ya creado se le puede pasar como argumentos una serie de cadenas de caracteres asociadas a variables inicializadas a un cierto valor. A dicho conjunto de variables se le denomina entorno del proceso, y a cada variable del conjunto se le denomina variable de entorno. Las variables de entorno pueden ser leídas o escritas durante la ejecución del proceso. Un intérprete de comandos, como cualquier otro programa, tiene asociado un proceso a su ejecución y en consecuencia dispone de un entorno de ejecución. Para conocer las variables de entorno de un intérprete se puede usar el comando env. Algunas variables del intérprete proceden del entorno del proceso asociado al intérprete. Asimismo también es posible exportar una variable del intérprete a su entorno; para ello se debe usar el comando exp o r t . Si se desea eliminar una variable del intérprete se debe usar el comando uns e t . Este comando también borra a la variable del entorno, si había sido exportada allí. e Ejemplo 1.6 Si se teclea el comando: env 1 s o r t s e mostrará e n l a salida estándar e l listado d e todas las variables d e entorno del proceso asociado al intérprete ordenadas alfabéticamente. Para exportar al entorno las variables PATH y MI_VARIABLE del ejemplo anterior se debe escribir la siguiente orden: Sistemas Operativos Basados en UNIX (SOBUNIX): introducción general 17 export PATH MI_VARIABLE Si se teclea de nuevo el comando env 1 s o r t se puede comprobar que dichas variables han sido in­ cluidas en el entorno. Si se desea eliminar la variable MI_VARIABLE se debe usar la orden: uns e t MI_VARIABLE Usando el comando s e t se puede comprobar que MI_VARIABLE ha sido eliminada. Además usando el comando env se puede comprobar que también ha desaparecido del entorno. • Cuando se ejecuta un comando externo en un intérprete, el proceso asociado al intérprete crea un proceso hijo asociado al comando que recibe como entorno una copia del entorno del intérprete. Los cambios que realice el nuevo proceso en su copia del entorno no afectan al entorno del proceso padre ya que en los SOBUNIX los procesos poseen espacios de direcciones independientes. Obviamente si se desea que el proceso asociado al comando externo pueda hacer uso de alguna variable del intérprete ésta debe ser exportada al entorno del intérprete antes de ejecutar el comando. Recuérdese que las variables del intérprete son locales al proceso asociado a la ejecución del intérprete. Shell scripts Usualmente los intérpretes de comandos, aparte de ser capaces de ejecutar comandos internos y externos, también disponen de un completo lenguaje de programación con sentencias del tipo i f , f o r , whi l e , etc. Además también soportan la definición de funciones. Para conocer los detalles de la sintaxis del lenguaje de programación de un intérprete se debe consultar su página del manual de ayuda. Un shell script es un archivo ASCII que contiene comandos y sentencias del lenguaje de programa­ ción del intérprete. Para crear un shell script se puede usar cualquier editor de texto. Una vez escrito el código del shell script se debe guardar en un archivo, al que hay que proporcionarle permiso de ejecución (ver sección 1 .7.2) . Además hay que incluir (si no lo estaba ya) dentro de la variable PATH el nombre de ruta del directorio donde reside el shell script. La ejecución de un shell script se puede realizar de dos formas. La primera forma consiste en pasar el nombre de ruta del shell script como argumento de entrada del comando s ourc e : s ource s he l l_s c r i p t Dicho comando carga el shell script en el espacio de direcciones del intérprete y lo ejecuta. Nótese que en este caso la ejecución del shell script es similar a la de un comando interno, por lo que no hay que crear un nuevo proceso. La segunda forma de ejecución de un shell script consiste en invocarlo como un comando externo: s he l l_s c r i p t 18 Ampliación de sistemas operativos En este caso el intérprete invoca a otro intérprete, a veces denominado subintérprete (subshell), para que ejecute el shell script. Nótese que el subintérprete recibe una copia del entorno del intérprete, por lo que las variables del intérprete que no hubieran sido exportadas a su entorno no serán heredadas por el subintérprete. También se puede invocar directamente el subintérprete y pasarle como argumento de entrada el nombre de ruta del shell script que se desea ejecutar. Por ejemplo si el shell script se desea ejecutar en un subintérprete bash entonces la orden sería: bash she l l_s c r i p t L a utilidad d e los shell scripts e s evidente. Por u n lado, s i u n usuario ejecuta frecuentemente la misma secuencia de comandos puede evitarse su escritura creando un shell script que contenga dicha secuencia. De esta forma solo tendría que escribir el nombre del shell script. Por otra parte, gracias a que un intérprete soporta un lenguaje de programación, se pueden escribir shell scripts para la realización de todo tipo de tareas. e Ejemplo 1.7 En la Figura 1 .2 se muestra el código de un shell script sencillo que únicamente contiene comentarios y algunos comandos descritos en las secciones anteriores. # ! / b i n / ba s h # Mo s t rar ordenadame n t e a l f abé t i c amen te y p á g i n a a p á g i n a l a s var i ab l e s # de l e n t o rno de l i n t érpr e t e env 1 sort 1 mo r e # C r e a r u n a nueva var i ab l e M I V MIV= l O #Ver e l va l o r de l a var i ab l e echo $MIV # Expo r t a r l a a l entorno exp o r t MIV Figura 1.2 - Ejemplo de shell script Los comentarios se introducen en el código del shell script precedidos del símbolo' # ' . El primer comen­ tario indica al intérprete el nombre de ruta del subintérprete que debe invocar para ejecutar este shell script. En este caso es un intérprete bash. Nótese que si se omite esta orden el subintérprete que se invoca es del mismo tipo que el intérprete. Este comentario es importante ya que un shell script escrito con el lenguaje de programación de un cierto tipo de intérprete puede que produzca errores si se ejecuta con otro tipo de intérprete. Supóngase que el shell script de la Figura 1 .2 se encuentra almacenado en el archivo e j _s c r i p t . Para poder ejecutarlo se debe habilitar el permiso de ejecución del archivo (ver sección 1 .7 . 2), usando la siguiente orden: chmod 0 7 4 4 e j ernp l o_s c r i p t Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 19 Además hay que asegurarse que el directorio donde se encuentra el shell script se encuentra añadido en la variable PATH. Si se cumplen estas dos condiciones, el shell script se puede invocar, por ejemplo, mediante su nombre de ruta: e j emp l o_s c r i p t En este caso se ha supuesto que el shell script se encuentra en el directorio de trabaj o actual, por ello se ha podido invocar mediante su nombre de ruta relativa. • 1.5.2. Interfaces de usuario gráficas El sistema X Window Las interfaces de usuario gráficas (GUis) de la mayoría de SOBUNIX se basan en el sistema X Window, también conocido abreviadamente como X. El sistema X Window fue desarrollado a mediados de los años ochenta por el MIT. La primera versión, denomina X 1 apareció en 1 984. En el momento de escribir este texto la última versión de X Window es la X 1 1 R7.7 de 20 1 2 . Nótese que la versión 1 1 (X 1 1 ) aparecida en 1 987 h a sido sobre l a que s e han ido haciendo las sucesivas modificaciones. El sistema X Window es un sistema de ventanas (windowing system), es decir, la capa de software más interna de una GUI que se encarga de enviar y recibir datos del hardware (pantalla, teclado y ra­ tón). Además contiene las funciones primitivas básicas para la representación gráfica de líneas y fuentes asociadas a las ventanas y al puntero del ratón. El sistema X Window se caracteriza por su alta portabilidad ya que se ejecuta fuera del espacio de direcciones del núcleo. Está diseñado siguiendo una arquitectura del tipo servidor-cliente (ver Figura 1 . 3). El servidor X es la parte del software del sistema X Window que se ejecuta en la máquina del usuario y se encarga de leer datos procedentes del teclado y del ratón, así como de escribir mapas de bits en la pantalla. Además se comunica con los clientes X. Un cliente X es un determinado programa (gestor de ventanas, navegador web, gestor de correo electrónico, emulador de terminal, etc) que invoca (bien directamente o a través de librerías gráficas) funciones de la librería Xl ib para comunicarse con el servidor X. Un cliente X puede estar ejecutándose en la misma máquina que el servidor X o en una máquina remota. En este último caso la comunicación se realiza a través de la red. En una misma máquina pueden existir varios clientes X. Cada cliente X envía al servidor X la información que debe mostrar en la pantalla. Además recibe del servidor X información procedente del teclado y el ratón. Nótese que el servidor X hace un seguimiento del puntero del ratón y la posición de las ventanas para determinar qué ventana es la que se encuentra activa y saber a qué cliente X debe enviar la información procedente del teclado y el ratón. Además el servidor X almacena en unas estructuras de datos denominadas recursos información sobre diferentes objetos: ventanas, fuentes, paletas de colores, cursor, etc. Cada recurso tiene un identificador numérico entero que lo identifica de forma única. 20 Ampliación de sistemas operativos Protocolo X Cliente X Figura 1.3 - Cliente X Arquitectura cliente-servidor del sistema X Window El intercambio de información entre un servidor X y un cliente X sigue el protocolo de comunicación denominado protocolo X (Xprotocol). El cliente X solicita la conexión con el servidor X enviándole un primer mensaje. El servidor X responde con otro mensaje para aceptar o rechazar la conexión, o para obtener una comprobación de la identidad del cliente X. El mensaje de aceptación de la conexión por parte del servidor X contiene información que el cliente X utilizará en las siguientes comunicaciones con el servidor X. Una vez que la conexión se ha establecido, el servidor X y el cliente X pueden intercambiar cuatro tipos de mensajes: • Mensajes de petición. Son enviados por el cliente X para pedir información al servidor X o para solicitarle que dibuje algo en la pantalla. • Mensajes de respuesta. Son enviados por el servidor X en respuesta a algunos mensajes de petición del cliente X. • Mensajes de aparición de eventos. Son enviados por el servidor X a un cliente X cuando se produce algún evento asociada a la ventana asociada al cliente X: entrada de ratón o teclado, cambio de posición o de tamaño de la ventana, etc. • Mensajes de error. Por ejemplo, si un cliente X desea crear una ventana, enviará un mensaje de petición al servidor X. É ste creará la ventana y su recurso asociado, y le asignará un identificador que enviará en el mensaje de respuesta al cliente X. De esta forma el cliente X utilizará este identificador para referirse a la ventana en posteriores mensajes de petición. Los mensajes son cadenas de bytes que deben ser interpretados. Por ejemplo los mensaj es de apari­ ción de eventos tienen una longitud de 32 bytes. El byte más significativo, es decir, el situado más a la izquierda, especifica el tipo de evento y los 31 bytes restantes especifican información adicional asociada al evento. Obviamente desde el punto de vista del programador de aplicaciones trabajar directamente con cadenas de bytes es muy tedioso. Afortunadamente el sistema X Window incluye una librería de primitivas básicas de manej o del protocolo X denominada X l ib. Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 21 Widgets toolkits Aparte del sistema de ventanas otra parte fundamental de una GUI son las librerías denominadas widgets toolkits, que proporcionan al programador de aplicaciones funciones para crear y manipular desde un nivel de abstracción superior los objetos gráficos típicos de una interfaz gráfica (ventanas, menús, botones, barras de desplazamientos, etc) a los cuales se les denomina de forma general widgets. En las interfaces gráficas basadas en el sistema X Window las widget toolkits se ejecutan por encima de la librería Xl ib. Es decir, un programa de aplicación (cliente X) invoca a una función de una widgets toolkit que a su vez invoca a una función de la librería Xl ib. Entre las librerías widgets toolkits más conocidas que se ejecutan sobre el sistema X Window se encuentran Mo t i f , Q t y GTK + . El sistema X Window también incluye una librería widgets toolkit básica denominada X Too/kit Intrinsics (o más abreviadamente Xt o X toolkit). Motif se ejecuta sobre Xt. En la Figura 1 .4 se muestran las librerías gráficas más comunes de un cliente X. Programa de aplicación 1 1 1 Qt GTK+ 1 1 Motif 1 Widgets toolkits Xt 1 Xlib ¡ Protocolo X Figura 1.4 - Librerías gráficas más comunes de un cliente X Entornos de escritorio Cada aplicación define en su ventana asociada la interfaz gráfica específica que le desea ofrecer al usuario. Sin embargo, la apariencia de los marcos de las ventanas y la interacción del usuario con los mismos son controladas por una capa de software que se denomina entorno de escritorio. Algunos de los entornos de escritorio más usados en SOBUNIX son: CDE que usa la librería Motif, KDE que usa Qt y GNOME que usa GTK+. Un entorno de escritorio también se encarga de controlar el fondo, la barra de tareas y los iconos del escritorio. Consta, entre otros, de los siguientes componentes: 22 Ampliación de sistemas operativos • Gestor de ventanas (window manager). Es un cliente X que se encarga, entre otras tareas, de posicionar las ventanas, colocar los marcos alrededor de las ventanas y atender la interacción del usuario con los marcos de las ventanas, con los iconos y con la barra de tareas. También se encarga de tratar los clics del ratón y las pulsaciones de teclas fuera de una ventana determinada, es decir, no asociadas a un cliente X determinado. Por otra parte, ciertas peticiones de los clientes X al servidor X son redireccionadas al gestor de ventanas, como por ejemplo, la creación o la eliminación de ventanas. • Gestor de archivos (file manager), también denominado explorador de archivos (file browser). Es un cliente X que suministra al usuario una interfaz gráfica amigable para la realización de operaciones básicas con archivos y directorios: creación, eliminación, copia, visualización, etc. 1 .5.3. Llamadas al sistema Los procesos de los usuarios, tanto los que se crean para implementar las interfaces con el usuario como los que se crean al interactuar el usuario con las interfaces, tienen que realizar llamadas al sistema para solicitar los servicios del sistema operativo. De esta forma, las llamadas al sistema disponibles en un sistema operativo establecen la interfaz de programación entre los procesos de los usuarios y el núcleo del sistema operativo. Cada llamada al sistema tiene asociado un número entero positivo para poder ser identificada por el núcleo. En los SOBUNIX un proceso para hacer una llamada al sistema invoca a una función de envoltura o 2 redireccionamiento (wrapper function) de la librería e estándar, también denominada abreviadamente l ib e . Cada llamada al sistema tiene asociada al menos una función de envoltura. Varias funciones de envoltura pueden estar asociadas a la misma llamada al sistema, la diferencia entre ellas radica en los argumentos de entrada que reciben y que pasan a la llamada. Por ejemplo, las funciones de envoltura execv y execve invocan ambas a la llamada exe c . La diferencia entre ambas funciones es que execve admite un mayor número de argumentos de entrada que execv. La función de envoltura, cuando se ejecuta, copia el identificador de la llamada al sistema a la que está asociada en algún registro o en la pila de usuario. A continuación ejecuta una trampa (trap) que provoca la conmutación hardware de modo usuario a modo supervisor y transfiere el control al núcleo. É ste invoca al manejador de llamadas al sistema, el cual se implementa mediante una rutina denominada sys c a l l ( ) en la mayoría de SOBUNIX. El manejador de llamadas al sistema, sys c a l l ( ) , realiza, entre otras, las siguientes acciones : l . Copia los argumentos de la llamada al sistema en el área de usuario del proceso (ver sección 2.2.2) y guarda el contexto hardware del proceso, es decir, los valores actuales de los registros del procesador, en la pila del núcleo asociada al proceso. 2. Utiliza el identificador de la llamada para acceder a la entrada adecuada de la tabla de llamadas al sistema del núcleo. Cada entrada de esta tabla contiene, entre otros datos, la dirección de comienzo 2En programación una función función. de envoltura o redireccionamiento es aquella cuya principal finalidad es invocar a otra S istemas Operativos Basados en UNIX (SOBUNIX): introducción general 23 de la rutina del núcleo específica que hay que ejecutar para atender una determinada llamada al sistema. 3. Cuando termina la ejecución de la rutina del núcleo que atiende a la llamada al sistema, entonces sys c a l l ( ) copia los valores de retomo (o los códigos de error si se produjo algún error) en los registros oportunos. A continuación restaura el contexto hardware del proceso y conmuta a modo usuario devolviendo el control a la función de envoltura. Cuando se devuelve el control a la función de envoltura, ya en modo usuario, ésta comprueba leyendo los valores almacenados en los registros oportunos si la llamada al sistema se ejecutó correctamente o si se produjeron errores. Si la llamada se ejecutó con éxito la función de envoltura retoma y se continúa con la ejecución de la siguiente instrucción del proceso. Si se produjeron errores la función de envoltura, antes de retomar, invoca a una función de notificación de errores, la cual almacena un identificador numérico del error producido, en la forma de constante simbólica, en una variable global denominada generalmente errno 3 y establece que el valor de retomo de la función de envoltura sea el valor - l . Por ejemplo si una llamada al sistema es interrumpida por la recepción de una señal entonces en la variable e rrno se almacena la constante E INTR. Cada sistema operativo establece las llamadas al sistema que soporta, las cuales se pueden agrupar en diferentes categorías: control de procesos, comunicación de procesos, gestión de archivos y directorios, gestión de dispositivos, etc. Afortunadamente para los programadores de aplicaciones si un programa utilizada las funciones de envoltura definidas en las especificaciones POSIX, se podrá ejecutar sin pro­ blemas en todos los sistemas operativos que cumplan con estas especificaciones. 1 .6. Introducción a la gestión de archivos en SOBUNIX La gestión de archivos en los SOBUNIX será descrita en detalle en el capítulo 5, en esta sección únicamente se introducen aquellos conceptos que requieren ser conocidos antes de llegar a este capítulo. En los SOBUNIX la información contenida en un archivo se estructura como una secuencia de bytes. El núcleo no interpreta dicha información, simplemente se limita a operar con ella a nivel de byte. La interpretación de los bytes recae sobre los procesos que solicitan al sistema operativo las operaciones sobre los archivos mediante la realización de las llamadas al sistema oportunas. Por ejemplo, para leer y escribir el contenido de un archivo se utilizan las llamadas al sistema read y wr i t e , respectivamente (ver sección 5.2. 1 ) . E l nombre de u n archivo e s una cadena d e caracteres ASCII. En los SOBUNIX s e pueden usar todos los caracteres excepto los caracteres 1 y \ O Además se distingue entre caracteres en mayúsculas y en minúsculas. La longitud máxima de un nombre depende del tipo de sistema de archivos al que pertenezca el archivo. Los SOBUNIX modernos soportan el uso de diferentes tipos de sistemas de archivos. A diferencia de otros sistemas operativos, en los SOBUNIX los nombres de archivos no tienen por qué tener una extensión ya que el núcleo no las interpreta. De nuevo, el uso de extensiones en el nombre 1 3En el fichero de cabecera errno. errno . 1 1 1• h puede encontrarse una descripción de los posibles valores que puede tomar la variable Ampliación de sistemas operativos 24 de los archivos y su interpretación recae en los procesos. En este sentido, conviene señalar que un nombre puede tener más de una extensión. Teniendo en cuenta lo anterior los nombres prueba, prueba . e y prueba . e . g z serían nombres de archivos válidos en SOBUNIX. Para que un proceso pueda operar sobre un archivo, éste primero tiene que ser abierto mediante la invocación de la llamada al sistema open. La rutina del núcleo que trata esta llamada al sistema crea en la memoria principal una estructura de datos denominada objeto de archivo abierto. Además asigna a dicha estructura un número entero positivo pequeño denominado descriptor de archivo para identificarla. Nótese que si un proceso abre el mismo archivo dos veces, o si dos procesos abren el mismo archivo, el núcleo creará una estructura de objeto abierto y un descriptor de archivo en cada invocación de la llamada al sistema open. Luego se tendrían dos objetos de archivo abierto, cada uno con su correspondiente descriptor de archivo. La llamada al sistema open devuelve el descriptor del archivo abierto al proceso que invocó la llamada. Para poder operar sobre un archivo abierto el proceso debe pasar el descriptor del archivo como argumento de las posteriores llamadas al sistema asociadas a las operaciones que desee realizar sobre el archivo. Todos los descriptores de archivos asociados a un mismo proceso se indexan en una tabla denomina tabla de descriptores de archivos. Esta tabla es local a cada proceso, es decir, cada proceso tiene su propia tabla de descriptores de archivos. Luego un mismo descriptor de archivo puede referirse en dos procesos distintos a archivos diferentes. Cada entrada de una tabla de descriptores de archivos contiene un puntero a la estructura de objeto de archivo abierto a la que está asociado el descriptor. También contiene una serie de indicadores que especifican el tratamiento que debe dar al descriptor ante la aparición de determinados eventos. Por su parte, un objeto de archivo abierto contiene, entre otros, los siguientes datos: • Puntero de lectura/ escritura. Indica el byte del archivo donde se realizará la próxima operación de lectura o de escritura. Se especifica como un desplazamiento desde el origen del archivo. En SOBUNIX el acceso al contenido de un archivo se realiza por defecto de forma secuencial, aunque es fácil implementar el acceso directo o aleatorio a un determinado byte del archivo usando la llamada al sistema l s e e k (ver sección 5.2. 1 ) . • Puntero a l nodo virtual del archivo. L a mayoría d e S OBUNIX son capaces de soportar y mani­ pular diferentes tipos de sistemas de archivos. Para implementar esta propiedad el subsistema de archivos del núcleo dispone de una capa de software denominada capa nodo virtual/sistema de archivo virtual que se encarga de encaminar cada operación sobre un archivo perteneciente a un determinado tipo de sistema de archivos hacia la función dependiente del sistema de archivos que implementa dicha operación. Un nodo virtual, o más abreviadamente nodo-v (ver sección 5 . 3 . 1 ), es una estructura de datos que el núcleo crea en memoria principal para cada archivo distinto abier­ to. En un nodo-v se almacena información que el núcleo necesita conocer para operar sobre un archivo. Los S OBUNIX implementan una estructura de directorios de gráfica acíclica. El directorio raíz es el directorio 1 '. Este carácter también se utiliza para separar cada componente del nombre de ruta ' Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 25 absoluta o relativa de un archivo. Dentro del directorio raíz suelen existir, entre otros, los directorios que se muestran en la Tabla 1 .2. Directorio /bin /boot / dev / etc / home / l ib /mnt / ro o t / sbin / tmp /usr Contenido Archivos binarios asociados a comandos y programas esenciales del sistema Archivos necesarios para el arranque del sistema Archivos de dispositivos Diferentes archivos de configuración del sistema Cuentas de los usuarios ordinarios Diferentes librerías compartidas Montaje de dispositivos temporales Directorio borne del superusuario Archivos binarios fundamentales del sistema Archivos temporales Aplicaciones d e usuario Tabla 1.2 - Algunos directorios existentes en el sistema de archivos de los SOBUNIX 1 .7. Seguridad y protección en SOBUNIX Los SOBUNIX son sistemas multiusuario, es decir, pueden atender simultáneamente a múltiples usuarios. Además también tienen la capacidad de gestionar grupos de usuarios. En las siguientes sec­ ciones se describen los elementos que de forma general se usan en los SOBUNIX para implementar la seguridad y la protección de los datos de los usuarios : los identificadores reales, la máscara de modo de un archivo y los identificadores efectivos. También se describe cómo se implementa la seguridad y la protección haciendo uso de estos elementos. Señalar que cada SOBUNIX puede implementar mecanismos de seguridad adicionales a los meca­ nismos generales que se describen en las siguientes secciones. 1.7. 1 . Identificadores reales Cada usuario tiene asignado un identificador de usuario (User IDentifier, UID) que es un número entero positivo que lo identifica de manera unívoca. El identificador de usuario UID=O está reservado para designar a un usuario privilegiado conocido como superusuario o root, el cual dispone de privilegios que no tienen el resto de usuarios, como por ejemplo: leer, escribir o ejecutar cualquier archivo. Por otra parte, en los SOBUNIX cada usuario pertenece a uno o varios grupos de usuarios. Cada grupo de usuarios tiene asignado un identificador de grupo (Group IDentifier, GID) que es otro número entero positivo. En los SOBUNIX a cada usuario reconocido en el sistema se le asigna un UID y un GID cuando se le crea una cuenta de trabaj o en el sistema. A este par de valores se les denomina de forma general 26 Ampliación de sistemas operativos como identificadores reales. En el caso del superusuario sus identificadores reales son UID=O y GID=O. Nótese que aunque un usuario solo tiene asociado un GID puede pertenecer a varios grupos. El núcleo mantiene los identificadores reales de los usuarios en un archivo cuya nombre de ruta absoluta normalmente es / e t c /pas swd. Además en este archivo también se mantiene la siguiente in­ formación sobre cada usuario: nombre de usuario, contraseña de acceso encriptada, nombre completo del usuario, nombre de ruta absoluta del directorio de trabaj o inicial y el nombre de ruta absoluta del intérprete de comandos que utiliza por defecto. El archivo pas swd puede ser leído por todos los usuarios, sin embargo sólo puede ser modificado por el superusuario. Para crear un nuevo usuario, el superusuario puede usar, por ejemplo, el coman­ do u s eradd o el comando addu s e r . Para eliminar un usuario puede usar el comando u s erde l o el comando de l u s e r . Para modificar los datos de un usuario puede usar el comando u s e rmod. También, es posible impedir el acceso a un usuario sin borrar sus datos. Para ello el superusuario solo tiene que escribir, en la línea asociada al usuario en el archivo pas swd, el carácter 1 * 1 delante de la x asociada a la contraseña encriptada del usuario cuyo acceso se desea impedir. e Ejemplo 1.8 Supóngase que en un cierto S OB UNIX pueden acceder los siguientes usuarios : roo t , pedro 9 0 , j uan y mar i . Si el usuario pedro 9 0 ejecuta la orden cat / e t c / p a s swd en la pantalla se mostrará el contenido del archivo p a s swd. Este archivo contiene una línea por cada usuario reconocido por el sistema operativo. Supóngase que la línea asociada al usuario pedro 9 O es la siguiente: p edro 9 0 : x : 1 0 1 : 1 0 : Pedro B l anco : / exp o r t / home /pedro 6 9 : / b i n / ba s h Los datos del usuario s e separan por e l carácter 1 : 1 De izquierda a derecha s e muestran l o s siguientes datos: pedr o 9 O es el nombre de usuario, x indica que la contraseña se encuentra encriptada dentro del archivo cuyo nombre de ruta absoluta suele ser / e t c / s hadow, 1 0 1 es el UID, 1 0 es el GID, Pedro B l anco es el nombre completo del usuario, / expo r t / home / pedro 6 9 es el nombre de ruta absoluta del directorio de trabaj o inicial, y / b i n / bash es el nombre de ruta absoluta del intérprete de comandos que utiliza por defecto el usuario. • Por ejemplo si se desea impedir el acceso al usuario pedr o 9 O su línea del archivo debería modificarse de la siguiente forma: pedro 9 0 : * x : 1 0 1 : 1 0 : Pedro B l anc o : / expo r t / home /pedro 6 9 : / b i n / ba s h Nótese que s i s e examinan detenidamente las líneas del archivo pas swd s e observará que existen más líneas que las cuatro esperadas en principio, una por cada usuario habilitado. Esto es así porque en SOBUNIX la ejecución de ciertos programas requiere de la asignación de un usuario ficticio. • Sistemas Operativos B asados en UNIX (SOBUNIX) : introducción general 27 Asimismo, la información sobre la composición de los grupos, es decir, los usuarios que forman parte de cada grupo, es almacenada por el sistema operativo en un archivo cuyo nombre de ruta absoluta suele ser 1 e t c 1 group. El archivo 1 e t c / group puede ser leído por todos los usuarios, sin embargo sólo puede ser modifica­ do por el superusuario. Para crear un nuevo grupo, el superusuario puede usar, por ejemplo, el comando groupadd o el comando addgroup . Para eliminar un grupo puede usar el comando g roupde l . Si se desea modificar la composición de un grupo el superusuario puede usar el comando g roupmod. Otra opción sería editar directamente el contenido del archivo 1 e t c 1 group . e Ejemplo 1.9 Considérese el sistema del ejemplo anterior, si un usuario ejecuta la orden: c a t / e t c / group en la pantalla se mostrará el contenido del archivo group. Este archivo contiene una línea por cada grupo de usuarios reconocido por el sistema operativo. Dentro de una línea los datos asociados a un grupo se separan por el carácter : Supóngase que este archivo contiene, entre otras, la siguiente línea: 1 1 • s t a f f : : 1 0 : mar i , j uan Los datos, de izquierda a derecha, contenidos en esta línea son los siguientes: s t a f f es el nombre del grupo, : : indica que no existe especificada una contraseña de acceso al grupo, 1 0 es el GID, y mar i y j uan son los nombres de dos usuarios añadidos a este grupo. De acuerdo con esta información al grupo s t a f f pertenecen todos los usuarios que tengan asignado el GID= 1 0, como era el caso del usuario pedro 9 0 , y los usuarios mar i y j uan. Señalar que al igual que sucedía con / e t c /pas swd en / e t c / group existen líneas asociadas a gru­ pos ficticios, creados durante la instalación de ciertos programas y cuya existencia es necesaria para su correcta ejecución. • l. 7 2 . . Máscara de modo de un archivo Representación binaria En los SOBUNIX cada archivo tiene asociado, entre otros atributos. (ver sección 5 . 2 . 1 ) , un número binario de 1 6 bits denominado máscara de modo (ver Figura 1 .5). Si un bit de la máscara está a 1 se considera activado, en caso contrario estará desactivado. La información contenida en la máscara de modo es la siguiente: • Tipo de archivo (B 1 5 B 14B 1 3 B 1 2 ). Algunos de los tipos de archivos más comunes soportados en los SOBUNIX son: archivo ordinario ( 1 000), directorio (0 1 00), archivo asociado a dispositivo modo carácter (00 1 0), archivo asociado a dispositivo modo bloque (O 1 1 0), tubería (000 1 ), enlace simbólico ( 1 0 1 0) y conector (socket) ( 1 1 00). Ampliación de sistemas operativos 28 • • • • • • Bit s_I SUID (B u ). Si este bit se encuentra activado entonces cuando un usuario ejecute este archivo su identificador efectivo de usuario (ver sección 1 .7 . 3 ) se hará igual al UID del propietario del archivo. Bit S_I SGID (B 1 0 ). Tiene una utilidad semejante al bit S_I SUID pero a nivel de grupo. Es decir, si este bit se encuentra activado entonces cuando un usuario ejecute este archivo su identificador efectivo de grupo (ver sección 1 .7.3) se hará igual al GID del propietario del archivo. Bit S_I SVTX (B 9 ), también denominado bit pegajoso (sticky bit). La activación de este bit indica al sistema operativo que la región de código del archivo ejecutable va a ser compartida por varios procesos y en consecuencia debe mantenerse en la memoria principal aunque termine el primer proceso cuya ejecución produj o que se cargará dicha región de código. En el caso de programas utilizados por distintos usuarios (editores, compiladores, etc) esta técnica permite ahorrar memoria. Permisos de acceso del usuario propietario del archivo (BsB 7 B 6 ). Se puede establecer de forma independiente tres tipos de permisos : lectura (B8), escritura (B 7 ) y ejecución (B 6 ). Permisos de acceso para los usuarios pertenecientes al grupo propietario del archivo (BsB 4 B 3 ). Se pueden establecer de forma independiente tres tipos de permisos: lectura (Bs), escritura (B 4 ) y ejecución (B 3 ). Por defecto el grupo propietario es aquel cuyo GID coincide con el GID asignado al usuario propietario del archivo. Tanto el usuario propietario como el grupo propietario pueden ser cambiados usando la llamada al sistema chown o el comando homónimo. Permisos de acceso para el resto de usuarios (B 2 B 1 Bo). Es decir, para aquellos usuarios que no son ni el propietario ni pertenecen al grupo propietario. Se pueden establecer de forma independiente tres tipos de permisos : lectura (B 2 ), escritura (B 1 ) y ejecución (Bo). Representación binaria Tipo de archivo s_r s G I D + S I SU I D S Permisos propietario I SVTX Permisos otros usuarios Permisos grupo propietario Representación octal Figura 1.5 - Máscara de modo de un archivo en representación binaria y en representación octal Sistemas Operativos B asados en UNIX (SOBUNIX) : introducción general 29 Nótese que en el caso de que el archivo sea un directorio, entonces el permiso de lectura permite leer las entradas del directorio, el permiso de escritura permite añadir o borrar una entrada al directorio y el permiso de ejecución permite buscar una entrada en el directorio. Representación octal De los 1 6 bits que componen la máscara de modo de una archivo solo los bits B 1 1 a Bo pueden ser modificados por el superusuario o el propietario. Estos doce bits se suelen representar mediante cuatro cifras octales 0 3 0 2 0 1 00 (ver Figura 1 . 5) que tienen el siguiente significado: • 0 3 . Establece el valor de los bits S_I SUID, s_I SGID y s_I SVTX, ya que es la codificación en octal de los bits B l 1 B 10B 9 . • 0 2 . Establece los permisos de acceso del usuario propietario del archivo ya que es la codificación en octal de los bits BsB7B 6 . • 0 1 . Establece los permisos de acceso del grupo propietario del archivo ya que es la codificación en octal de los bits BsB4B 3 . • 00 . Establece los permisos de acceso al archivo del resto de usuarios ya que es la codificación en octal de los bits BzB 1 Bo . Representación simbólica La máscara de modo de un archivo también se suele representar usando una cadena de 1 O caracteres (ver Figura 1 . 6) a la que se denomina máscara simbólica. El significado y los valores de cada uno de estos caracteres es el siguiente: • c 9 . Este carácter establece el tipo de archivo. Puede tomar los siguientes valores : ordinario), 1 d 1 (directorio), 1 e 1 (archivo asociado a un dispositivo modo carácter), asociado a un dispositivo modo bloque), 1 p (tubería), 1 1 1 (enlace simbólico) y 1 s 1 • 1 - 1 (archivo 1 b 1 (archivo 1 (conector). c 8 , c5 y c2 . Estos tres caracteres establecen el permiso de lectura para el propietario, el grupo propietario y el resto de usuarios, respectivamente. Cada uno de estos bits puede tomar de forma independiente los siguientes valores : r 1 (permiso de lectura concedido) o 1 - 1 (permiso de lectura denegado) . 1 • c7 , c4 y e 1 . Estos tres caracteres establecen el permiso de escritura para el propietario, el grupo propietario y el resto de usuarios, respectivamente. Cada uno de estos bits puede tomar de forma independiente los siguientes valores : 1 w (permiso de escritura concedido) o 1 1 (permiso de escritura denegado). 1 • c 6 . Este carácter establece la activación del bit S_I SUID y el permiso de ejecución para el propie­ tario del archivo. Puede tomar los siguientes valores : 1 - 1 (bit S_I SUID desactivado y permiso de Ampliación de sistemas operativos 30 Estado S Permisos propietario I SG I D -1 o :.r::.;os Tipo de archivo Estado S S I SU I D I SVTX Figura 1.6 - Máscara de modo de un archivo en representación simbólica ejecución denegado), 1 x 1 (bit s_r sum desactivado y permiso de ejecución concedido), 1 s 1 (bit S_I SUID activado y permiso de ejecución denegado) y 1 s 1 (bit s_r sum activado y permiso de ejecución concedido). • CJ . Este carácter establece la activación del bit s_I S G I D y el permiso de ejecución para el grupo propietario del archivo. Puede tomar los siguientes valores: (bit s_I SGID desactivado y per­ miso de ejecución denegado), 1 x 1 (bit S_I SGID desactivado y permiso de ejecución concedido), 1 s 1 (bit S_I SGID activado y permiso de ejecución denegado) y 1 s (bit S_I SGID activado y permiso de ejecución concedido). 1 - 1 1 • co . Este carácter establece la activación del bit S_I SVTX y el permiso de ejecución para el resto de usuarios del archivo. Puede tomar los siguientes valores : - (bit S_I SVTX desactivado y permiso de ejecución denegado), 1 x 1 (bit s_I SVTX desactivado y permiso de ejecución concedido), 1 T 1 (bit S_I SVTX activado y permiso de ejecución denegado) y 1 t (bit S_I SVTX activado y permiso de ejecución concedido). 1 1 1 e Ejemplo 1.10 Supóngase que el archivo / heme / p rueba . txt tiene la siguiente máscara simbólica: - r - s -ws - - t El primer carácter ( - ) de la máscara indica que se trata de un archivo ordinario. Los siguientes tres carac­ teres (r - s ) especifican que el propietario tiene permisos de lectura y ejecución sobre el archivo, además el bit S_I SUID está activado. Los caracteres ( -ws ) indican que el grupo propietario únicamente tiene per­ miso de escritura, además el bit S_I SGID está activado. Los últimos tres caracteres ( - - t) especifican que el resto de usuarios tienen solo permiso de ejecución, además el bit S_I SVTX está activado. Sistemas Operativos B asados en UNIX (SOBUNIX): introducción general 31 De acuerdo con esta máscara de modo simbólica la máscara de modo binaria sería: 1000 111 101 010 001 A partir de la representación en binario es sencillo obtener la máscara octal: 7521 • Configuración de la máscara de modo de un archivo Cuando un proceso crea un archivo haciendo uso de las llamadas al sistema c r e a t u open, el sistema operativo se encarga de configurar los cuatro bits de la máscara de modo que especifican el tipo del archivo. Los doce bits restantes de la máscara son inicializados por el proceso especificando uno de los argumentos de entrada de estas llamadas al sistema (ver sección 5 . 2 . 1 ) . Posteriormente, el propietario del archivo o el superusuario pueden cambiar el valor de los 1 2 últimos bits de la máscara de modo haciendo uso de la llamada al sistema chrnod o del comando homónimo. e Ejemplo 1.11 Considérese el archivo / home / prueba . txt del ejemplo anterior. Se desea que todos los usuarios tengan permiso de lectura sobre dicho archivo. La máscara de modo, expresada en binario, que debería tener el archivo para tener estos permisos sería: 1000 111 101 110 101 Cuya máscara octal equivalente es : 7565 Para conceder estos permisos el propietario del archivo o el superusuario puede usar el comando chrnod en un interprete de comandos de la siguiente formé : chrnod 7 5 6 5 / home / prueba . txt Si se ejecuta ahora el comando l s - l / home / prueba . txt se puede comprobar que la máscara simbólica del archivo tiene los permisos deseados, es decir, que su máscara de modo simbólica es: - r - s rwS r - t • 4Si se consulta la página del comando chmod en el manual de ayuda se puede comprobar que este comando también soporta otra sintaxis basada en argumentos de tipo carácter que especifican los permisos que se quieren añadir o eliminar en la máscara de modo. De las dos sintaxis posibles se puede usar la que más se prefiera. 32 Ampliación de sistemas operativos l. 7 3 . . Identificadores efectivos En los SOBUNIX cada usuario, aparte de los identificadores reales, también tiene asignados un identificador de usuario efectivo (Effective User IDentifier, EUID) y un identificador de grupo efectivo (Effective Group IDentifier, EGID). Estos identificadores efectivos son consultados por el núcleo en diferentes circunstancias, como por ejemplo cuando un usuario intenta crear o abrir un archivo, o si un proceso desea enviar una señal a otro proceso. Los identificadores efectivos se inicializan con el valor de sus identificadores reales cuando un usua­ rio inicia una sesión de trabaj o en el sistema. Por otra parte, si un usuario intenta ej ecutar un archivo que tiene su bit S_I SUID activado entonces el EUID se configura con el valor del UID del propietario del archivo. Lo mismo ocurre a nivel de grupo si se activa el bit S_I SGID. Esta propiedad resulta muy útil para permitir a un usuario ej ecutar programas que son propiedad de usuarios más privilegiados, como el superusuario. e Ejemplo 1.12 El programa / bin /pas swd se utiliza para cambiar la contraseña de un usuario. Este programa tiene que acceder al archivo de texto / e t c / p a s swd que contiene los datos de todos los usuarios. Por motivos de seguridad este archivo solo puede ser escrito por el superusuario. Si se ejecuta el comando ls -1 / b i n / p a s swd se puede comprobar que el propietario de este archivo ejecutable es el superusuario (usuario r o o t ) y que su máscara simbólica es : - r-s r-s r-x Se observa que e l archivo puede ser leído y ejecutado por todos los usuarios. Además están activa­ dos los bits S_I SUID y S_I SGID. En consecuencia cuando un usuario ejecuta el comando pas swd, sus identificadores efectivos se hacen iguales a los identificadores reales del superusuario, es decir, EUID=O y EGID=O. Ello posibilita que durante la ejecución del comando se pueda escribir el archi­ vo / e t c /pas swd. • Un usuario puede conocer los valores de sus identificadores UID, EUID, GID, EGID usando las llamadas al sistema getu i d, g e t eu i d, getgid y g e t e g i d, respectivamente. Asimismo para cambiar los valores de estos identificadores se pueden usar las llamadas al sistema s e tu i d y s e tg i d, respec­ tivamente. Si estas llamadas las invoca el superusuario entonces podrá cambiar el valor de todos los identificadores, tanto los reales como los efectivos . Si las invoca un usuario ordinario solo podrá conse­ guir que sus identificadores efectivos tomen el valor de sus identificadores reales. También es posible comprobar, usando la llamada al sistema a c c e s s , si un usuario dispone de acceso a un archivo. Esta llamada usa para realizar esta comprobación los identificadores reales UID y GID en lugar de los efectivos EUID y EGID . Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general Utilidad Comando chown chmod groupadd groupde l groupmod pas swd su u s e radd u s e rde l u s e rmod 33 Cambiar el propietario de un archivo Cambiar la máscara de modo de un archivo Añadir un grupo de usuarios Eliminar un grupo de usuarios Modificar la cuenta de un grupo de usuarios Cambiar la contraseña de un usuario Cambiar de usuario Añadir un usuario Eliminar un usuario Modificar la cuenta de un usuario Tabla 1.3 - Algunos comandos básicos asociados a la seguridad y a la protección en S OBUNIX Llamada al sistema access chmod chown getegid g e t eu i d getgid getu i d s e tg i d s e tu i d Utilidad Comprobar s i u n usuario puede acceder a u n archivo Cambiar el propietario de un archivo Cambiar la máscara de modo de un archivo Obtener e l valor del EGID d e u n usuario Obtener e l valor del EUID d e u n usuario Obtener e l valor del GID d e u n usuario Obtener e l valor del UID d e u n usuario Configurar e l valor del GID y del EGID d e u n usuario Configurar e l valor del UID y del EUID d e u n usuario Tabla 1.4 - Algunas llamadas al sistema básicas asociadas a la seguridad y a la protección en SOBUNIX En las Tablas 1 . 3 y 1 .4 se recopilan los comandos y las llamadas al sistema más utilizadas en los S OBUNIX asociadas a la seguridad y a la protección. Señalar que algunos comandos tienen el mismo nombre que algunas llamadas al sistema, estos comandos invocan durante su ejecución a las llamadas al sistema homónimas. 1 .7.4. Implementación de la seguridad Acceso al sistema El acceso de un usuario a un SOBUNIX se realiza a través de un programa de acceso o programa de login que puede presentar una interfaz en línea de comandos o una interfaz gráfica. Este programa se ejecuta con el EUID del superusuario ya que el archivo asociado al programa tiene su bit S_I SUID activado. El programa de login en primer lugar solicita al usuario su nombre de usuario y su contraseña. A continuación encripta la contraseña y comprueba que los valores introducidos por el usuario coinciden 34 Ampliación de sistemas operativos con los almacenados en el archivo / e t c /pas swd. En caso afirmativo accede de nuevo a este archivo para leer los identificadores reales del usuario e invoca a las llamadas al sistema s e tu i d y s e tg i d. Con ello se consigue que los identificadores reales y efectivos del programa de login pasen a tomar los valores del UID y GID del usuario que desea acceder al sistema. Finalmente, el programa de login invoca a un intérprete de comandos, en concreto al especificado para el usuario en / e tc / p a s swd. Nótese que el proceso asociado al intérprete y todos los procesos creados a partir de éste heredarán los identificadores reales y efectivos del programa de login. También los heredarán los archivos u otros recursos abiertos debido a la actividad del usuario. Protección de los archivos En los S OBUNIX el núcleo implementa la protección de un archivo haciendo uso de la máscara de modo del archivo y de los identificadores efectivos. Cuando un proceso desea abrir un archivo previa­ mente creado debe invocar a la llamada al sistema open. Esta llamada analiza la mascara de modo del archivo y los identificadores efectivos para determinar si el proceso puede acceder al archivo y con que privilegios. En caso afirmativo, devuelve un descriptor de archivo. En caso contrario devuelve el valor -l. Cuando se realiza una operación sobre un archivo abierto el núcleo examina el modo de apertura del archivo almacenado en el obj eto de archivo abierto para verificar si la operación se puede realizar o debe ser denegada. El modo de apertura se especifica como un argumento de la llamada al sistema open (ver sección 5 . 2 . 1 ) . e Ejemplo 1.13 S upóngase que en el directorio de trabajo actual existe el archivo da t o s l que tiene la siguiente máscara de modo simbólica: - rw- rw- rwDe la máscara se deduce que se trata de un archivo ordinario, que los bits S_I SUID, S_I SGID y S_I SVTX están desactivados y que todos los usuarios tienen permiso de lectura y escritura sobre el archivo. Supóngase que un proceso de un usuario distinto del propietario realiza la siguiente llamada al sistema para abrir el archivo dat o s l con permiso de solo lectura ( o_RDONLY ) : fd= open ( " da t o s l " , O_RDONLY ) En este caso el permiso de apertura especificado por el proceso es más restrictivo que los permisos que especifica la máscara de modo, El núcleo consultaría la máscara de modo del archivo y los identificadores efectivos para comprobar si el proceso puede abrir el archivo. En este caso si que puede abrirlo por lo que el núcleo le asigna un descriptor de archivo que se almacena en la variable fd y un objeto de archivo abierto. • Sistemas Operativos B asados en UNIX (SOBUNIX) : introducción general 35 Señalar que el acceso a un archivo también depende de los permisos de acceso que tengan cada uno de los componentes (directorios) del nombre de la ruta absoluta de un archivo. Si los componentes de la ruta no tienen permisos de lectura y ejecución no se podrá acceder al archivo. e Ejemplo 1.14 S upóngase que el directorio de trabajo actual es / horne . Si la máscara simbólica de este directorio se configura a d rwx - - - - - entonces ningún usuario excepto el propietario del directorio podrá acceder al directorio. De esta forma el propietario no tiene necesidad de especificar los permisos de los archivos contenidos en dicho directorio ya que ningún usuario excepto él y el superusuario pueden acceder a dicho directorio. • 1 .8. Resumen El sistema operativo UNIX fue desarrollado por Ken Thompson en los laboratorios B ell de AT&T a principios de los años setenta. Puesto que debido a la legislación norteamericana vigente AT &T no podía comercializar UNIX directamente, no tuvo inconveniente en otorgar licencias de UNIX a las uni­ versidades con fines educativos y de investigación. Esto propicio que la comunidad universitaria aportara sugerencias e ideas a AT&T para mejorar UNIX. De hecho la Universidad de California en B erkeley introdujo tantos cambios en el código fuente que comercializó su propio UNIX con el nombre de B S D . Aparte d e las universidades, algunas empresas fabricantes d e computadores y software también obtuvie­ ron sus propias licencias de UNIX y produjeron sus propias distribuciones, como por ejemplo: Xenix, SunOS, AIX y HP-UX. Debido a la proliferación de distribuciones de UNIX, AT&T también decidió entrar en el negocio a través de una compañía filial, y lanzó sus propias distribuciones como : System III y System V. La gran variedad de S OBUNIX resultaba un serio problema para los desarrolladores de aplicaciones software. Las diferencias entre unos SOBUNIX y otros propiciaban que una misma aplicación pudiera no funcionar correctamente en todos los sistemas . Para intentar resolver este problema se han creado diferentes estándares o especificaciones que definen una API y las características que deben tener los intérpretes de comandos y los shell scripts . Entre los estándares definidos destacan POSIX y SUS . Los SOBUNIX comparten una serie de características comunes : son sistemas de tiempo compartido, son sistemas multiprogramados y multiusuario, soportan multiprocesamiento, el núcleo está escrito en su mayor parte en lenguaje C y posee una estructura de tipo monolítica, la planificación se realiza a nivel de procesos o de hilos del núcleo usando un algoritmo de planificación basada en múltiples colas de planificación y realimentación, administran la memoria principal e implementan la memoria virtual usando la técnica de demanda de página, soportan una estructura de directorios de gráfica acíclica, el directorio raíz se denota con el símbolo '/' que además se usa para separar los componentes del nombre de ruta de un archivo, implementan las abstracciones de nodo virtual y sistema de archivos virtual lo 36 Ampliación de sistemas operativos que les permite soportar diferentes tipos de sistemas de archivos, y los dispositivos de E/S se integran dentro del sistema de archivos mediante el uso de archivos de dispositivos modo bloque y archivos de dispositivos modo carácter. Los SOBUNIX soportan dos posibles tipos de interfaces con los usuarios : interfaz de línea de coman­ dos e interfaz de usuario gráfica. Nótese que desde una interfaz gráfica también se puede disponer de una interfaz de línea de comandos . A través de estos interfaces un usuario puede invocar a otros programas o aplicaciones. La interfaz de línea de comandos se implementa mediante un intérprete de comandos que es un pro­ grama que acepta un comando introducido por un usuario a través del dispositivo de entrada estándar (por defecto el teclado) y ejecuta su código asociado. El resultado de la ejecución se muestra, general­ mente, en el dispositivo de salida estándar (por defecto la pantalla) . Una vez finalizada la ej ecución del comando el intérprete queda a la espera de recibir otra orden por parte del usuario. Cada S OBUNIX suele incluir por defecto en su distribución un determinado intérprete de comandos, pero el usuario si así lo desea puede utilizar otro. Entre los intérpretes de comandos más conocidos se encuentran los siguientes : B oume shell ( sh), C shell ( c s h) , TENEX C shell (tc sh), Kom shell (ksh), y B oume-Again shell (bash). Las interfaces de usuario gráficas de la mayoría de SOBUNIX se basan en el sistema X Window, también denominado abreviadamente como X. El sistema X Window es un sistema de ventanas, es decir, la capa de software más interna de un GUI que se encarga de envíar y recibir datos del hardware (pantalla, teclado y ratón) y que contiene las funciones primitivas básicas para la representación gráfica de líneas y fuentes, asociadas a las ventanas y al puntero del ratón. X Windows se caracteriza por su alta portabilidad ya que se ej ecuta fuera del espacio de direcciones del núcleo. Desde el punto de vista de su ejecución tanto los interfaces como los programas tendrán asociado cada uno al menos un proceso monohilo o multihilo. Un proceso (o un hilo) para solicitar los servi­ cios del núcleo del sistema operativo tiene que realizar las llamadas al sistema correspondientes. En los S OBUNIX un proceso para hacer una llamada al sistema invoca a una función de envoltura o redireccio­ namiento de la librería C estándar, también denominada l ib e abreviadamente. Cada llamada al sistema tiene asociada al menos una función de envoltura. Los S OBUNIX son sistemas multiusuario, es decir, pueden atender simultáneamente a múltiples usuarios . Además también tienen la capacidad de gestionar grupos de usuarios . Para implementar la seguridad y la protección de los datos de los usuarios los SOBUNIX utilizan los siguientes elementos: identificadores reales (UID y GID), identificadores efectivos (EUID y EGID) y máscara de modo de un archivo. 1 .9. Lecturas recomendadas Si se desea obtener una explicación adicional de los contenidos tratados en este capítulo se pue­ den consultar, por ejemplo : el capítulo 10 de [Tanenbaum, 2009] , el capítulo 1 de [Vahalia, 1 995], los capítulos 1 a 6, 10, 1 2, 1 3 y 14 de [Love et al. , 2005 ] , y los capítulos 1 , 3 y 6 de [Márquez, 2004] . S istemas Operativos B asados en UNIX (SOBUNIX) : introducción general 1 .10. 37 Autoevaluación 1.1. ¿Cuándo se desarrollo el sistema operativo UNIX? ¿ Quién fue su creador? ¿Qué compañía era la propietaria de sus derechos? (Respuesta en sección 1 .2) 1.2. ¿Qué motivó la proliferación de diferentes distribuciones de UNIX? (Respuesta en sección 1 .2) 1.3. ¿Qué universidad desarrolló su propia distribución de UNIX? ¿Cómo se llamaba dicha distribu­ ción? (Respuesta en sección 1 . 2) 1.4. ¿Qué distribuciones de UNIX comercializó AT &T a través de una compañía filial ? (Respuesta en sección 1 .2) 1.5. Enumerar tres ejemplos actuales de distribuciones comerciales de UNIX y tres ejemplos actuales de distribuciones libres . (Respuesta en sección 1 .2) 1.6. ¿Cuáles son los estándares de compatibilidad más usados en los SOBUNIX? (Respuesta en sección 1 .3) 1.7. Enumerar las características principales de los SOBUNIX . (Respuesta en sección 1 .4) 1.8. ¿Qué es un intérprete de comandos? ¿Cuáles son los intérpretes de comandos más conocidos en los S OBUNIX? (Respuesta en sección 1 .5 . 1 ) 1.9. ¿Cuál es l a estructura general de un comando? (Respuesta en sección 1 .5 . 1 ) 1. 10. ¿Para que sirve el comando roan? ¿ Y el comando i n f o ? (Respuesta en sección 1 .5 . 1 ) 1.11. Explique l a utilidad de los siguientes comandos: cd, cp, l s , mkd i r , mv, mvd i r , pwd, rm y rmd i r . (Respuesta e n sección 1 . 5 . 1 ) 1.12. Explique la utilidad de los siguientes comandos : c a t , f i nd, grep, more y s o r t . (Respuesta e n sección 1 .5 . 1 ) 1. 13. ¿Qué es un comodín en los intérpretes de comandos de los SOBUNIX? Explique la utilidad de los siguientes comodines : ' * , ? y [ J (Respuesta en sección 1 .5 . 1 ) ' ' ' 11 11 • 1. 14. ¿Cómo se puede conseguir el redireccionamiento de l a entrada y de l a salida en un intérprete de comandos? ¿ Y el encadenamiento de órdenes ? (Respuesta en sección 1 .5 . 1 ) 1. 15. ¿Cómo se puede asignar un alias a una orden o cadena de órdenes ? ¿Cómo s e elimina dicho alias ? (Respuesta en sección 1 .5 . 1 ) 1. 16. Explicar razonadamente si la asignación de un alias es temporal o permanente. (Respuesta en sección 1 .5 . 1 ) 1. 17. ¿Qué es un comando externo? ¿ Y un comando interno? (Respuesta en sección 1 .5 . 1 ) 38 Ampliación de sistemas operativos 1. 18. ¿Qué se entiende por trabaj o (j ob) en el contexto de un intérprete de comandos? (Respuesta en sección 1 .5 . 1 ) 1. 19. ¿Cómo se debe escribir una orden para que sea ejecutada e n segundo plano? (Respuesta en sección 1 .5 . 1 ) 1 .20. Explique l a utilidad de los siguientes comandos : fg, b g y j obs . (Respuesta e n sección 1 .5 . 1 ) 1.21. Explique qué sucede e n u n intérprete de comandos s i s e pulsan las teclas : a) [contro l ] + [ z ] . b) [contro l ] + [ e ] . (Respuesta en sección 1 .5 . 1 ) 1 .22. ¿Qué son las variables del intérprete de comandos? ¿Cómo s e pueden visualizar? (Respuesta en sección 1 .5 . 1 ) 1 .23. ¿Qué información contiene l a variable del intérprete PATH? ¿Cómo se puede añadir el directorio de trabajo actual a dicha variable? (Respuesta en sección 1 .5 . 1 ) 1 .24. ¿ Qué son las variables de entorno de un proceso? ¿Cómo s e pueden visualizar las variables de entorno del intérprete de comandos? (Respuesta en sección 1 .5 . 1 ) 1 .25. ¿Cómo s e puede exportar una variable del intérprete a s u entorno? ¿Cómo se puede eliminar una variable del intérprete? (Respuesta en sección 1 .5 . 1 ) 1 .26. ¿Qué es un shell script? ¿Cómo se crea? (Respuesta e n sección 1 .5 . 1 ) 1 .27. Explicar razonadamente las diferentes formas posibles d e ejecutar u n shell script. (Respuesta en sección 1 .5 . 1 ) 1 .28. Explicar razonadamente qué es el sistema X Window y cómo funciona. (Respuesta en sección 1 .5.2) 1 .29. ¿Qué es un widget toolkit? Enumerar algunas widgets toolkits usadas en SOBUNIX. (Respuesta en sección 1 .5 .2) 1 .30. ¿Qué componentes básicos suele incluir un entorno de escritorio de los SOBUNIX? Enumerar algunos ejemplos. (Respuesta en sección 1 .5 .2) 1.31. Explicar razonadamente cómo se invocan y cómo se tratan las llamadas al sistema en los S OBU­ NIX. (Respuesta en sección 1 .5 . 3 ) 1 .32. ¿Cómo s e estructura la información contenida en los archivos e n los SOBUNIX? (Respuesta en sección 1 .6) 1 .33. Los SOBUNIX ¿distinguen entre mayúsculas y minúsculas en los nombres de los archivos ? (Respuesta e n sección 1 .6) 1 .34. Explicar razonadamente si en los SOBUNIX los nombres de los archivos pueden usar extensiones. (Respuesta en sección 1 .6) Sistemas Operativos B asados en UNIX (SOBUNIX) : introducción general 39 1.35. ¿Qué es un descriptor de archivo? ¿Y un obj eto de archivo abierto? (Respuesta en sección 1 .6) 1.36. ¿Cuántas tablas de descriptores de archivos existen en el núcléo? (Respuesta en sección 1 . 6) 1.37. ¿Qué información contiene un objeto de archivo abierto? (Respuesta en sección 1 .6) 1.38. ¿Qué tipo de estructura de directorios implementan los SOBUNIX? ¿Con que carácter se repre­ senta al directorio raíz del sistema de archivos principal? (Respuesta en sección 1 .6) 1.39. Enumerar y explicar el contenido de cinco de los directorios contenidos dentro del directorio raíz del sistema de archivos principal de los SOBUNIX. (Respuesta en sección 1 .6) 1.40. ¿Cuáles son los identificadores reales de un usuario? ¿Qué valor toman estos identificadores en el caso del superusuario? (Respuesta en sección 1 .7 . 1 ) 1.41. ¿Qué información contiene el archivo / e t c / pa s swd? ¿ Y el archivo / e t c / gr oup ? (Respuesta en sección 1 .7 . 1 ) 1.42. ¿Cómo s e pueden crear y eliminar usuarios en los SOBUNIX? ¿Es posible impedir el acceso a un usuario sin eliminar su cuenta? (Respuesta en sección 1 .7 . 1 ) 1.43. ¿Cómo s e pueden crear, modificar y eliminar grupos de usuarios en los SOBUNIX? (Respuesta en sección l . 7 . 1 ) 1.44. Explicar razonadamente qué es la máscara de modo de u n archivo y comentar e l significado de cada uno de los bits en su representación binaria. (Respuesta en sección 1 .7 .2) 1.45. Explicar razonadamente el significado de cada uno de los caracteres de la representación simbólica de la máscara de modo de un archivo. (Respuesta en sección 1 .7 .2) 1.46. ¿Qué comando se utiliza para modificar la máscara de modo de un archivo? (Respuesta en sección 1 .7.2) 1.47. ¿Cuáles son los identificadores efectivos de un usuario? ¿Cuándo son consultados por el núcleo? (Respuesta en sección 1 .7 . 3 ) 1.48. Explicar l a relación existente entre e l bit s_I SUID d e la máscara d e modo d e un archivo y el identificador EUID . (Respuesta en sección 1 .7 . 3 ) 1.49. Explicar para qué se utilizan las siguientes llamadas a l sistema: getuid, g e t eu i d, getgid, g e t e g i d, s e tu i d, s e t e u i d y acc e s s . (Respuesta en sección 1 .7.3) 1.50. Explicar cómo se realiza el acceso de un usuario a un SOBUNIX . (Respuesta en sección 1 .7 .4) 1.51. Explicar cómo se implementa la protección de archivos en los S OBUNIX. (Respuesta en sección 1 .7 .4) 40 Ampliación de sistemas operativos 1.11. Ejercicios 1.1. Supóngase que en un cierto S OBUNIX el directorio de trabajo actual de un usuario contiene los archivos da t o s , f o t o smu l , l i s t as . txt, ex l , a l umno s . da t , trabaj o s . txt y asdo, y los directorios f o t o s , v i d e o s y corre o s . Indicar la orden que se debe escribir en un intérprete de comandos para realizar cada una de las siguientes acciones : a) Visualizar la ruta absoluta del directorio de trabaj o actual . b) Visualizar un listado del contenido del directorio de trabajo que permita diferenciar entre archivos y directorios. e) Visualizar un listado que únicamente contenga los nombres de los archivos que acaban con la letra 'o' o con la letra ' t ' . d ) Mover al subdirectorio f o t o s los archivos dat o s y f o t o smu l . e) Hacer que el directorio f o t o s pase a ser el directorio de trabajo actual . 1.2. Explique razonadamente cuál sería el resultado de la ej ecución de las siguientes órdenes : a) s o r t < fA > f B b) l s -1 > asd e) l s -i > > wer t 2 3 d) more � / prueba . tx t e ) c p par t e { p r imera , s egunda } trabaj o s 1.3. Consulte l a página del manual man o l a bibliografía que considere necesarias y conteste a las siguientes cuestiones relativas al intérprete de comandos bash: a) ¿Cómo es posible conocer todos los intérpretes de comandos disponibles en un cierto SO­ BUNIX? b) ¿Cómo se puede saber dónde se aloj a el intérprete bash? e) ¿Cómo es posible saber si el intérprete de comandos que estamos utilizando es el intérprete bash? d) ¿Cómo se puede saber la versión de bash que estamos utilizando? e) ¿Qué variables son consideradas especiales en bash? f) ¿Para qué sirve la sustitución de comandos y cómo se implementa? g) Explicar la diferencia entre los siguientes entrecomillados: " c adena " y 'c adena' h) ¿Cómo se pueden introducir datos procedentes del teclado? i) Enumerar los operadores lógicos, los operadores de comparación de cadenas y los operadores de comparación de números enteros disponibles en bash. Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general 41 j) Enumerar algunos de los operadores de comparación de atributos de archivos disponibles en bash. k) Explicar el significado del operador [ expre s i ón J . 1) Explicar cómo se pueden realizar operaciones aritméticas con enteros en bash. m) Explicar cómo se declara una sentencia condicional de tipo i f. n) Explicar cómo se declara un bucle de tipo whi l e o de tipo unt i l . o) Explicar cómo s e declara un bucle de tipo f o r . p ) Explicar cómo s e declara una sentencia d e tipo c a s e . q) Explicar cómo s e declara y s e invoca una función que reciba parámetros d e entrada. Explicar también cómo se pueden retornar valores desde una función. 1.4. Explique razonadamente el funcionamiento del shell script que se muestra en la Figura 1 .7. 1.5. Explique razonadamente el funcionamiento del shell script que se muestra en la Figura 1 . 8 . 1.6. Escribir un shell script para e l intérprete b a s h que muestre l a fecha y l a hora del sistema cada segundo durante un minuto. Ayuda: para la realización de este ejercicio resulta muy útil el uso de los comandos s l e ep y da t e . 1.7. Escribir u n shell script para e l intérprete ba sh que muestre e l día d e l a semana que fue ayer. Ayuda: para la realización de este ejercicio resulta muy útil el uso del comando da t e . 1.8. Buscar l a utilidad d e l a función sys t em y escribir u n programa en C d e ejemplo d e uso d e esta función. 1.9. La máscara de modo del archivo regular resul t ado s expresada en octal es 6644 : a) Explique razonadamente el significado de todos los bits de esta máscara de modo. b) Escriba la máscara de modo simbólica asociada a este archivo. e) ¿Qué orden se debe escribir en un intérprete de comandos para que la máscara simbólica del archivo pasase a ser - rwx r- - - -T? 1.10. La máscara de modo simbólica del archivo ej ecutable s 0 7 que resulta de compilar el programa que se muestra en la Figura 1 .9 es - rwxrw- rwx y dicho archivo pertenece al usuario Wo l f (UID=50 1 , GID=202). Explique razonadamente el funcionamiento del programa en los siguientes casos: a) El programa lo ej ecuta wo l f . b) El programa lo ejecuta el usuario Fox (UID=502, GID=203) . 42 Ampliación de sistemas operativos # ! / b i n / ba s h # echo " I n t ro du c e una c adena : " read CADENA wc - e ) LONG I TUD= $ ( echo $ CADENA wh i l e [ $ L ONGI TUD - g t O do X= " $ X " $ ( e cho $ CADENA J c u t - c $ LONG I TUD ) LONG I TUD= $ ( ( $ LONG I TUD - 1) ) done echo " $X " Figura 1.7 - Shell script del Ejercicio 1 .4 # ! / b i n / ba s h # if [ $# -gt O for i ) ; then in $ * do mv $ i if [ $ HOME / p ap e l e r a 2 > $? - eq O ) ; / dev / nu l l then echo " $ i ha s i do mov i do a l a pap e l e r a con éxi t o " echo " $ i no h a p o d i do s e r movi do a l a p ap e l e ra " else fi done e ls e echo " Error : echo " $ 0 archivo 1 hay qu e e s p e c i f i c ar argumen t o s " [ ar c h i vo 2 ) . . . " exi t 1 fi ex i t O Figura 1.8 - Shell script del Ejercicio 1 .5 Sistemas Operativos Basados en UNIX (SOBUNIX) : introducción general f l o a t a l ( f l oa t ( *b ) ( float a ) ) ; f l o a t a 2 ( f l oa t a ) ; ma i n ( ) { int u [ 3 ] ; f l oa t z = O ; u [ 0 ] =getui d ( ) ; u [ l ] = g e t eu i d ( ) ; u [ 2 ] = s e tu i d ( u [ l ] ) ; if (u [ 2 ] ==0 ) { z = a l ( a2 ) ; p r i n t f ( " \ n [ %d , f l oat al ( f l oat ( *b ) !hl , !hl , % . 2f ] \n" , u [ O ] , u [ l ] , u [ 2 ] , z ) ; ( f loat a ) ) { f l oat u = 3 0 . 4 ; u= ( *b ) ( u ) ; return ( u ) ; f l oa t a 2 ( f l oa t a ) { int h ; f o r ( h= - l ; h< 3 ; h+ + ) { return ( a ) ; Figura 1.9 - Código e del programa S o 7 del Ejercicio 1 . 1 o 43 Capítulo 2 SOBUNIX: implementación y control de procesos multihilos Objetivos docentes Los objetivos docentes de este capítulo son los siguientes : • Conocer los conceptos y las estructuras de datos fundamentales que utilizan los SOBUNIX para implementar los procesos multihilos. • S aber cómo se realiza la creación de nuevos procesos y la invocación de programas en los SOBU­ NIX. • Saber cómo se realiza la terminación de procesos en los SOBUNIX. • Conocer el uso, funcionamiento e implementación de la notificación de eventos mediante señales en los S OBUNIX. • Saber cómo se realiza el control de los hilos de usuario mediante el uso de la librería de hilos Pthreads de POSIX. • Conocer qué son y para qué sirven los conceptos de grupos de procesos y sesiones en los S OBU­ NIX. • Saber qué es el sistema de archivos de procesos (procfs), conocer la información que contiene y cómo se consulta. 45 Ampliación de sistemas operativos 46 2. 1. Introducción Uno de los principales servicios que debe proporcionar un sistema operativo es posibilitar la eje­ cución de los programas de los usuarios. Para lograr este objetivo se utiliza el modelo de proceso. Un proceso es una instancia de un programa en ejecución que requiere la asignación de diferentes recursos para poder existir y progresar. El modelo de proceso utilizado en los primeros S OBUNIX consideraba que un proceso únicamente sigue una determinada traza o ruta de instrucciones en su ej ecución, es decir, tiene un único hilo de control. Sin embargo los SOBUNIX desarrollados con posterioridad comenzaron a utilizar diferentes modelos de procesos multihilos ya que permiten mejorar el rendimiento del sistema. Este capítulo está dedicado al estudio de la implementación y control de los procesos multihilos en los SOBUNIX. En primer lugar se introducen los conceptos fundamentales necesarios para entender los posibles modelos de procesos multihilos . En segundo lugar se describen las estructuras de datos que se utilizan para implementar los procesos multihilos. En tercer lugar se describe la creación de procesos y la invocación de programas. En cuarto lugar se describe la terminación de procesos. En quinto lugar se estudia la notificación de eventos mediante el uso de señales. En sexto lugar se describe el control de los hilos de usuario mediante el uso de librería de hilos, en concreto se toma como ejemplo la librería Pthreads de POSIX. En séptimo lugar se estudia los conceptos de grupos de procesos y sesiones. Finalmente se describe el sistema de archivos de procesos (procfs). 2.2. 2.2.1. Implementación de los procesos multihilos en SOBUNIX Conceptos fundamentales Un programa es un archivo ejecutable que típicamente se encuentra almacenado en memoria secun­ daria. Considerado como archivo, un programa es una entidad pasiva o estática. Cuando un programa es ej ecutado se convierte en una entidad activa o dinámica denominada proceso que va pasando por dife­ rentes estados y utiliza distintos recursos del computador. En definitiva, un proceso es un programa en ej ecución. Un proceso se puede considerar como una entidad computacional básica que tiene asignado un con­ j unto de recursos y que durante su existencia ejecuta una serie de instrucciones. Así un proceso queda caracterizado por dos elementos que se pueden tratar de forma independiente: • Conjunto de recursos asignados. Un proceso tiene asociado un espacio de direcciones de memo­ ria virtual (código, datos, pila, . . . ) y otros recursos, tales como archivos abiertos, procesos hijos, manejadores de señales, información de contabilidad, etc. • Hilos de control o simplemente hilos (thread). Un hilo es un subconjunto de instrucciones del có­ digo del proceso que se puede ej ecutar independientemente del resto de instrucciones del proceso. En un computador con N procesadores es posible ejecutar en paralelo N hilos pertenecientes a uno o varios procesos . En general se distinguen los siguientes tipos de hilos en función de sus propiedades y usos : SOBUNIX: implementación y control de procesos multihilos 47 • Hilos del núcleo (kernel threads) . Son hilos asociados al código del núcleo del sistema operativo que son creados y gestionados por el propio núcleo para la realización de funciones especificas, como por ej emplo: dar soporte a procesos, atender operaciones de E/S , tratamiento de interrup­ ciones, etc . Los hilos del núcleo no necesitan estar asociados a ningún proceso. • Hilos de usuario (user threads) . Son parte del código de un determinado proceso de usuario. El programador al diseñar un programa es el encargado de establecer el número de hilos de usuario en que se va descomponer el código del programa. Si el código de un programa consta de un único hilo de usuario entonces al proceso asociado se le denomina proceso monohilo. M ientras que si se descompone en varios hilos de usuario se denomina proceso multihilo. Para la gestión de los hilos de usuario el programador utiliza una librería de hilos, como por ej emplo la librería Pthreads de POSIX (ver sección 2 . 3 .4). Una librería de hilos contiene funciones para la creación, destrucción, planificación y sincronización de los hilos de usuario, entre otras operaciones . El sistema operativo generalmente no suele conocer la existencia de los hilos de usuario, aunque debe dar servicio a las llamadas al sistema que pueda realizar la librería de hilos para implementar la gestión de los hilos. Además internamente la librería de hilos puede crear hilos cuya existencia es desconocida por los hilos de usuario que gestiona. • Procesos ligeros (lightweight processes). También denomi nados procesadores virtuales. Son hilos de usuario cuya gestión es realizada por el núcleo 1 del sistema operativo. Un proceso ligero puede estar asociado a uno o varios hilos de usuario. Es la librería de hilos la que se encarga de multiplexar los hilos de usuario de un determinado proceso en un número menor o igual de procesos ligeros. Los procesos ligeros solo se pueden utilizar en aquellos sistemas operativos cuyo núcleo soporte hilos del núcleo, ya que cada proceso ligero tiene que tener asociado un hilo del núcleo para poder ser planificado y ej ecutado. Los SOBUNIX desarrollados hasta mediados de los ochenta usaban el proceso como unidad de planificación y ej ecución. El principal inconveniente de estos sistemas es que el bloqueo de un hilo de usuario de un proceso produce el bloqueo del proceso completo. Posteriormente se comenzaron a desarrollar SOBUNIX que usaban al hilo del núcleo como unidad básica de planificación y ejecución. Estos sistemas también pueden soportar, por lo tanto, procesos lige­ ros . La principal ventaj a de soportar hilos del núcleo es que el bloqueo de un hilo del núcleo asociado a un proceso ligero de un proceso no supone el bloqueo de todos los procesos ligeros de dicho proceso; por lo que se evita tener que realizar un cambio de proceso. B asta con realizar un cambio de hilo, el cual introduce una menor sobrecarga. Además en un computador con varios procesadores (o un procesador con varios núcleos) es posible obtener un verdadero paralelismo durante la ej ecución de un proceso, ya que cada proceso ligero de un proceso se puede planificar, a través de su hilo del núcleo asociado, en un procesador. El principal inconveniente de los hilos del núcleo es que, a diferencia de los hilos de usuario, consu­ men recursos del núcleo y su gestión implica la ej ecución del núcleo. Por este motivo, muchos sistemas 1 En algunos sistemas operativos el te1mino proceso ligero también se utiliza como sinónimo del término independientemente de si el núcleo conoce la existencia y gestiona dicho hilo. hilo de usuario 48 Ampliación de sistemas operativos reciclan los hilos de núcleo existentes . Cuando un hilo del núcleo finaliza su ejecución, se marca como no planificable, pero sus estructuras de datos asociados no son eliminadas . Cuando se tiene que crear un nuevo hilo del núcleo, lo que se hace es que se reactiva algún hilo del núcleo marcado como no planificable con el objetivo de ahorrarse la sobrecarga de asignar estructuras de datos al nuevo hilo. e Ejemplo 2. 1 Un ej emplo de SOBUNIX que soporta hilos del núcleo y procesos ligeros es el sistema operativo Solaris. Desde su versión 2.2 hasta su versión 8.0 soportaba un modelo de proceso multihilo en el cual los hilos de usuario eran multiplexados en un número menor de procesos ligeros. La gestión de los hilos de usuario se realiza mediante el uso de una librería de hilos . Solaris dispone de su propia librería de hilos l ibthread, aunque también soporta la librería p threads de POSIX. Haciendo uso de las funciones de estas librerías es posible crear, destruir, planificar y sincronizar hilos de usuario. Una librería de hilos también se encarga de crear por defecto un conj unto de procesos ligeros asociado a cada proceso, y de multiplexar los hilos de usuario sobre los procesos ligeros. El tamaño de ese conjunto depende del número de hilos de usuario y del número de procesadores del computador. Los procesos ligeros son invisibles por defecto al programador, con lo que solo se tiene que preocupar de programar los hilos de usuario. Aunque si lo desea puede especificar el número de procesos ligeros que se deben crear. También puede asociar un proceso ligero a un único hilo de usuario, garantizando de esta forma que dicho hilo siempre tendrá un proceso ligero asociado . En la Figura 2. 1 a se representa, a modo de ejemplo, un proceso multihilo que consta de cuatro hilos de usuario. Uno de los hilos está asociado a un proceso ligero. Los tres hilos restantes son multiplexados sobre dos procesos ligeros. Cada proceso ligero tiene a su vez un hilo del núcleo asociado. Usar un modelo de proceso multihilo con un mayor número de hilos de usuario que procesos ligeros resulta útil para aplicaciones en las cuales no es necesario que la mayoría de sus hilos se ejecuten concu- Proceso multihilo Proceso multihilo Hilo de usuario Proceso ligero Hilo del núcleo (a) (b) Figura 2.1 - Ejemplo de proceso multihilo soportado en: a) Solaris 2.2. b) Solaris 1 0 . SOBUNIX : implementación y control de procesos multihilos 49 rrentemente. Por ejemplo, cada elemento de la interfaz gráfica de una aplicación (ventana, iconos, barra deslizadora, menús, etc) puede ser programado como un hilo de usuario. En un cierto instante solo unos pocos de dichos elementos se encontrarán activos, luego solo los hilos de usuario asociados a dichos elementos deben estar ej ecutándose y en consecuencia necesitan tener asignado un proceso ligero. Este modelo de proceso no resulta adecuado para aplicaciones que requieren que la mayoría de sus hilos se ejecuten concurrentemente, ya que la librería de hilos gastaría un tiempo de ejecución importante en la planificación y en los cambios de hilos. Recuérdese que al existir un mayor número de hilos de usuario que de procesos ligeros, la librería de hilos debe decidir que hilos de usuarios van a tener un proceso ligero asociado. En defi nitiva, este modelo de proceso multihilo presenta como principal ventaj a que permite implementar la concurrencia sin usar demasiados recursos del núcleo. Su principal inconveniente es que la implemen­ tación de un buen planificador de hilos en la librería de hilos es compleja y costosa computacionalmente. También con este modelo resulta bastante laborioso implementar el tratamiento de las señales (ver sec­ ción 2 . 3 . 3 ) a nivel de la librería de hilos. Con el fin de evitar los inconvenientes de este modelo Solaris a partir de su versión 8.0 comenzó a soportar también un modelo de proceso multihilo donde cada hilo de usuario tiene asociado un único proceso ligero (ver Figura 2. 1 b) . Desde Solaris 10 este es el único modelo soportado. En este modelo de proceso multihilo la planificación y sincronización de los hilos de usuario, entre otras tareas de gestión, se delega en el núcleo. Ello ha permitido simplificar el código fuente de la librería de hilos l i bthread. • 2.2.2. Estructuras de datos asociadas a los procesos multihilos La implementación de un modelo de proceso multihilo requiere la creación y manejo de diferentes estructuras de datos tanto para el proceso como para los hilos de usuario. Además si el sistema operativo soporta hilos del núcleo y procesos ligeros entonces también debe mantener estructuras de datos aso­ ci adas a los mismos. En las siguientes secciones se comentan las principales estructuras de datos que generalmente se mantienen en los S OBUNIX para implementar los procesos multihilos. Estructuras asociadas a los procesos Los SOBUNIX suelen mantener en el espacio de direcciones del núcleo una estructura de datos denominada tabla de procesos que tiene una entrada por cada proceso existente en el sistema. Dicha entrada se implementa como una estructura de datos, la cual se denomina en algunos SOBUNIX como estructura pro c . Esta estructura contiene, entre otros, los siguientes datos sobre un determinado proceso : • Localización del espacio de direcciones virtuales del proceso. Se mantiene un puntero a una es­ tructura que contiene información sobre la ubicación del espacio de direcciones virtuales de un proceso (ver sección 4 . 2 . 3 ) . 50 Ampliación de sistemas operativos • Identificadores del proceso. Cada proceso tiene asociado un número entero positivo que lo identi­ fica de manera univoca. A dicho identificador se le conoce generalmente con el nombre de identi­ ficador del proceso (Process IDentifier, PID). El número máximo de procesos que pueden existir de manera simultánea está limitado y suele ser configurable por el superusuario. En consecuencia el PID puede tomar valores comprendidos entre O y un cierto valor máximo PIDmax . Este límite máximo no supone generalmente un problema ya que como el tiempo de vida de algunos procesos es corto sus PIDs pueden ser reutilizados. También un proceso tiene asociado un identificador de grupo de procesos y un identificador de sesión (ver sección 2 . 3 . 5) . • Credenciales del proceso. Generalmente quedan definidos por los identificadores d e usuario y de grupo reales y efectivos, es decir, UID, GID, EUID y EGID. Estos identificadores relacionan a un proceso con un determinado usuario y grupo. • Estado del proceso. El tiempo de vida de un proceso puede descomponerse en un conjunto de estados que definen su comportamiento. Las transiciones entre los estados se realizan debido a la aparición de diferentes eventos. El número y el nombre de los estados soportados dependen de cada S OBUNIX. Algunos estados habituales son: inicial o creado, preparado para ej ecución, ej e­ cutándose en modo usuario, ejecutándose en modo núcleo, dormido (bloqueado), parado y zombi (terminado). • Parámetros de planificación. Es la información que utiliza el planificador para decidir que proceso planificar, como por ej emplo, la prioridad de planificación del proceso o el tiempo de uso del procesador. • Información estadística. Como el tiempo de ejecución en modo usuario y el tiempo de ejecución en modo supervisor del proceso y de sus procesos hijos. • Información sobre los procesos ligeros en que se descompone el proceso. Esta información solo se mantiene si el núcleo soporta hilos del núcleo. • Información genealógica. Como el PID del proceso padre y punteros a las estructuras proc de su proceso padre, de sus procesos hijos y de sus procesos hermanos. • Información para el tratamiento de las señales. Como se verá en la sección 2 . 3 . 3 existen máscaras de bits que permiten especificar qué señales serán ignoradas, qué señales serán bloqueadas y cuáles serán capturadas . • Información para la implementación del sistema de archivos procfs. El sistema de archivos de pro­ cesos procfs es un pseudosistema de archivos existente en algunos S OBUNIX que permite acceder a los usuarios a la información que mantiene el núcleo sobre los procesos . Dicha información se almacena en archivos y se organiza en directorios. • Á rea de usuario, denominada abreviadamente área-u. Se trata de una estructura de datos que con­ tiene aquella información asociada al proceso que necesita ser consultada por el núcleo únicamente SOBUNIX: implementación y control de procesos multihilos 51 cuando el proceso está siendo ejecutado, como por ejemplo: un puntero a la tabla de descriptores de archivos abiertos por el proceso, información sobre el archivo ejecutable a partir del cual se generó el proceso, los argumentos pasados a la función principal ma in del código del proceso, los argumentos de la llamada al sistema exec (ver sección 2 . 3 . 1 ), las variables de entorno, las direcciones de los manejadores definidos para el tratamiento de las señales capturadas, etc. En los SOBUNIX que no soportan hilos del núcleo en el área-u también se suelen almacenar la pila del núcleo asociada al proceso y el contexto hardware salvado del proceso. Una pila del núcleo es una estructura que contiene los marcos de pila de las funciones invocadas por el núcleo durante la ej ecución del proceso en modo núcleo. El contexto hardware salvado son los valores que contenían los registros del computador (contador de programa, registro de pila, registro de estado del procesador, etc) cuando se interrumpió la ej ecución del proceso debido a un cambio de contexto. El contexto hardware será restaurado cuando el proceso vuelva a ser planificado y se continúe con la ej ecución del proceso. En algunos S OBUNIX el área-u de un proceso se implementa en el espacio de direcciones del proceso en una estructura externa a la estructura proc del proceso. En dicho caso en la entrada de la tabla de procesos se mantiene un puntero al área-u . • Punteros para implementar listas enlazadas de procesos. E l núcleo mantiene diferentes listas para poder consultarlas durante la planificación de los procesos o cuando se produce algún evento. Por ejemplo, pueden existir listas de procesos preparados, listas de procesos dormidos (bloqueados) por determinados eventos, etc. Estas listas se implementan mediante punteros a las estructuras proc de los procesos que forman parte de cada lista. Estructuras asociadas a los hilos de usuario En el modelo de proceso multihilo, el proceso es entendido como un contenedor de recursos y los hilos de usuarios como unas secuencias de instrucciones. Los hilos de usuario comparten el espacio de direcciones virtuales del proceso y todos sus recursos (credenciales, archivos abiertos, etc). Cada hilo de usuario tiene asignada una pila de usuario, que es una estructura que contiene los marcos de pila de las funciones invocadas por el hilo durante su ejecución en modo usuario. Además también es necesario mantener, entre otros, los siguientes datos de cada hilo de usuario : • Identificador del hilo de usuario. Es un número entero positivo que identifica de manera univoca a un hilo de usuario dentro de un proceso. • Contexto hardware salvado en modo usuario. Son los valores que contenían los registros del computador cuando se interrumpió la ejecución del hilo en modo usuario debido a una llamada al sistema o a un cambio de hilo. El contexto hardware en modo usuario será restaurado cuando se continúe con la ej ecución del hilo en modo usuario. • Prioridad. Es consultada por el planificador implementado a nivel de la librería de hilos, si existe alguno. 52 Ampliación de sistemas operativos • Máscara de señales. Especifica el tratamiento que se realizará cuando llegue una determinada señal (ver sección 2 . 3 . 3 ) . La pila d e usuario y la información asociada a u n hilo d e usuario e s mantenida e n e l espacio de direcciones del proceso por la librería de hilos de usuario. Estructuras asociadas a los procesos ligeros Los S OBUNIX que soportan procesos ligeros deben mantener en el espacio de direcciones del núcleo una estructura de datos por cada uno de los procesos ligeros en que se descompone un proceso. Dicha estructura, denominada lwp en algunos SOBUNIX, contiene entre otros los siguientes datos: contexto hardware salvado en modo usuario, un puntero a la estructura proc del proceso del que forma parte, un puntero al hilo del núcleo que tiene asociado, información estadística de uso de recursos e información sobre el tratamiento de señales. Nótese que un proceso ligero no tiene asignado un identificador numérico, esto es así porque real­ mente no le hace falta ya que queda identificado por el hilo del núcleo al que está asociado, el cual si tiene asignado un identificador numérico, como se verá en la siguiente subsección. Recuérdese también que es la librería de hilos la que asocia un proceso ligero a un determinado hilo de usuario. Por otra parte es el núcleo el que asocia un hilo del núcleo a dicho proceso ligero. Estructuras asociadas a los hilos del núcleo En los SOBUNIX que soportan hilos del núcleo el sistema operativo debe mantener en su espacio de direcciones una estructura de datos y una pila del núcleo por cada hilo del núcleo. La estructura de datos asociada a un hilo del núcleo contiene la siguiente información: • Identificador del hilo núcleo. Es un número entero positivo que identifica de manera univoca a cada hilo del núcleo. • Contexto hardware salvado. Son los valores que contenían los registros del computador cuando se interrumpió la ejecución del hilo del núcleo debido a un cambio de contexto. El contexto hardware será restaurado cuando el hilo del núcleo vuelva a ser planificado y se continúe con la ejecución del hilo. • Parámetros de planificación. Es la información que utiliza el planificador del núcleo para decidir qué hilo del núcleo planificar, como por ejemplo, la prioridad de planificación del hilo o el tiempo de uso del procesador. • Punteros para implementar diferentes colas de hilos del núcleo. Como colas de hilos preparados para ejecución, colas de hilos dormidos, etc. • Puntero a la pila del núcleo asociada al hilo. SOBUNIX : implementación y control de procesos multihilos • 53 Información sobre el proceso ligero asociado. Como por ejemplo un puntero a la estructura lwp del proceso ligero al que está asociado y un puntero a la estructura pro e del proceso del que forma parte el proceso ligero. Si el hilo del núcleo no está asociado a ningún proceso ligero, entonces estos punteros contienen valores NULL. Por su parte, una pila del núcleo es una estructura que contiene los marcos de pila de las funciones invocadas por el hilo durante su ejecución en modo núcleo. Está pila está vacía cuando el hilo del núcleo se ejecuta en modo usuario. e Ejemplo 2.2 El sistema operativo Solaris 1 0 soporta un modelo de proceso multihilo en el que cada hilo de usua­ rio tiene asociado un proceso ligero (ver Figura 2. 1 b) . El núcleo para implementar este modelo utiliza principalmente las siguientes estructuras de datos: • Estructura proc. Se utiliza para implementar una entrada de la tabla de procesos. Algunos de los campos presentes en una estructura proc son : - p_exe c . Puntero al nodo-v del archivo ejecutable a partir del cual se creó el espacio de direcciones virtuales del proceso. - p_a s . Puntero a una estructura que contiene información sobre la ubicación del espacio de direcciones virtuales del proceso. - p_c red. Puntero a una estructura que contiene los credenciales del proceso . - p_s t a t . Contiene e l estado del proceso. E n un determinado instante d e tiempo e l estado de un proceso puede tomar uno de los siguientes valores: o S I DL (creado). Estado inicial de un proceso mientras está siendo creado. o SRUN (preparado para ejecución). El proceso se encuentra preparado para ser planifica­ do. o SONPROC (ejecutándose). El proceso está siendo ejecutado en un procesador. o SSLEEP (dormido). El proceso se encuentra dormido a la espera de que ocurra un deter­ minado evento, por ejemplo, que se complete una operación de E/ S . o S STOP (parado). La ejecución del proceso h a sido detenida por u n depurador de proce­ sos. o S Z OMB (zombi) . El proceso ha terminado. Si un proceso únicamente consta de un único hilo de usuario entonces el estado del proceso coincide con el estado del hilo de usuario, el cual queda definido por el estado del hilo del núcleo asociado al proceso ligero del hilo de usuario. Si se tiene un proceso multihilo, cada hilo puede encontrarse en un estado distinto . En es­ te caso el estado del proceso se fij a al de un hilo del núcleo asociado a un proceso ligero representativo del proceso seleccionado por el núcleo. 54 Ampliación de sistemas operativos - p__pp id. PID del proceso padre. - Información genealógica del proceso. Se implementa mediante punteros a las estructuras proc de algunos familiares del proceso: puntero al proceso padre ( * p__parent ) , puntero al primer proceso hijo ( * P_chi l d) , puntero al último proceso hijo ( * P_chi l d_ns ), puntero al siguiente proceso hermano ( * p_s ibl i ng ) , etc. - p_s e s sp . Puntero a una estructura que almacena información sobre la sesión de trabaj o del terminal al que pertenece el proceso (ver sección 2.3.5). - p__p i dp. Puntero a una estructura que almacena el PID del proceso y otras informaciones relacionadas . - p__pgp i dp. Puntero a una estructura que almacena el identificador del grupo de procesos (ver sección 2.3.5) y otras informaciones relacionadas. - Información sobre los procesos ligeros asociados al proceso. Como por ejemplo el núme­ ro total de procesos ligeros asociados al proceso (p_lwpcnt ) , el número de procesos lige­ ros bloqueados (p_lwpb l o c ked) , el número de procesos ligeros preparados para ejecución (p_lwprcnt ) , etc. - uarea. Puntero al área-u la cual se implementa mediante una estructura u s e r . Contiene, entre otros, los siguientes datos: puntero a la tabla de descriptores de archivos abiertos, los argumentos pasados a la función principal ma i n del código del proceso, los argumentos de la llamada al sistema exec y las variables de entorno. - Campos para soportar el sistema de archivos procfs. • Estructura kl wp . Se utiliza para almacenar información sobre un proceso ligero. Entre la infor­ mación almacenada se encuentra la siguiente: uso de recursos, contexto hardware salvado, infor­ mación para el tratamiento de señales, puntero a la estructura proc del proceso al que pertene­ ce, puntero al hilo del núcleo asociado kthread y campos para soportar el sistema de archivos proc f s . • Estructura kthread. Se utiliza para almacenar información sobre un hilo del núcleo. Algunos de los campos presentes en esta estructura son: - t_s tack. Puntero a la pila del núcleo asociada al hilo del núcleo. - p_s t a t . Contiene el estado del hilo del núcleo. En un determinado instante de tiempo el estado de un hilo del núcleo puede tomar uno de los siguientes valores (ver Figura 2.2): o TS_RUN (preparado para ejecución). El hilo del núcleo se encuentra preparado para ser planificado. o TS_ONPROC (ejecutándose). El hilo del núcleo está siendo ejecutado en un procesador. o TS_SLEEP (dormido). El hilo del núcleo se encuentra dormido a la espera de que ocurra un determinado evento, por ejemplo, que se complete una operación de E/ S . SOBUNIX: implementación y control de procesos multihilos Detener ej ecución 55 Ej ecución finalizada Continuar Asignación del hilo Figura 2.2 - Diagrama de transición de estados de los hilos del núcleo en Solaris o TS_STOPPED (parado). La ejecución del hilo del núcleo ha sido detenida, seguramente por un depurador. o TS_Z OMB (zombi). El hilo del núcleo ha terminado, pero sus datos se encuentran disponi­ bles todavía para ser leídos por los hilos encargados de recopilar información estadística sobre el uso de recursos. Posteriormente, el hilo será recolectado por el núcleo para formar parte de la lista de hilos del núcleo libres. o TS_FREE (libre) . La estructura del hilo del núcleo se encuentra disponible para ser asig­ nada a un proceso ligero o para la realización de funciones internas del núcleo. En con­ secuencia las estructuras de datos de un hilo del núcleo son reutilizadas, ahorrándose por tanto su tiempo de su creación. t_pr i . Prioridad de planificación del hilo del núcleo. t_t id. Identificador numérico del hilo del núcleo. t_lwp . Puntero a la estructura klwp del proceso ligero al que estás asociado. Si no está asociado a ninguno, como cuando ejecuta tareas del núcleo, entonces contiene el valor NULL. - t_procp. Puntero a la estructura proc del proceso al que estás asociado a través de un proceso ligero. Si no está asociado a ninguno entonces contiene el valor NULL. - t_l ink. Punteros a estructuras kthread para implementar diferentes colas de hilos del núcleo: preparados para ejecución, dormidos y libres. - t_next . Puntero a la siguiente estructura kthread de la lista global de hilos del núcleo. El núcleo mantiene una lista enlazada con todos los hilos del núcleo existentes. - t_prev. Puntero a la anterior estructura kthread de la lista global de hilos del núcleo. 56 Ampliación de sistemas operativos En la Figura 2.3 se muestra la relación entre las estructuras de datos del núcleo de S olaris asociadas a los procesos, a los procesos ligeros y a los hilos del núcleo, que han sido descritas en los párrafos anteriores . Se ha considerado a modo de ej emplo un proceso A que consta de dos hilos de usuario y un proceso B monohilo. Cada hilo de usuario tiene asociado en el núcleo un proceso ligero . A su vez cada proceso ligero tiene asociado un hilo del núcleo de la lista global de hilos del núcleo. Señalar la existencia de hilos del núcleo que no están asociados a ningún proceso ligero. Dichos hi los del núcleo pueden estar libres o asociados a la ej ecución de funciones del núcleo . En S olaris la información sobre un determinado hilo de usuario es mantenida a nivel de la librería de hilos en una estructura u lwp . Esta estructura contiene la pila de usuario, información para la política de planificación a nivel de hilos de usuario (si existe alguna definida), mecanismos de sincronización y máscaras de señales. Otra estructura de datos importante en la implementación del modelo de proceso multihilo es uberda ta. La librería de hilos crea una estructura uberda t a por cada proceso. En ella almacena la información necesaria para gestionar y acceder a los datos de todos los hilos de usuario asociados a un proceso. • 2.3. Control de procesos multihilos en SOBUNIX 2.3.1. Creación de procesos e invocación de otros programas La llamada al sistema fork En los SOBUNIX la creación de un proceso se realiza generalmente invocando a la llamada al sistema f ork. Al proceso que invoca a f ork se le denomina proceso padre y al nuevo proceso que se crea se le denomina proceso hijo. El proceso hijo recién creado es un copia prácticamente idéntica del proceso padre y comparte el acceso a todos sus recursos. La única diferencia entre el proceso hijo recién creado y el proceso padre se encuentra en algunos campos de su estructura p r o c , es decir, de su entrada de la tabla de procesos . Por ejemplo, el PID del proceso hij o es distinto del PID del proceso padre. Además, los campos asociados al uso de recursos tienen que ser configurados a O. También los punteros para mantener la información genealógica tienen que ser adaptados adecuadamente tanto en la estructura proc del padre como en la estructura del hijo. El espacio de direcciones virtuales del proceso hij o recién creado es una copia del espacio de di­ recciones del proceso padre. Por lo tanto ambos ej ecutan el mismo programa pero en espacios indepen­ dientes . Esto significa que los cambios que realice el proceso padre en las variables de su espacio de direcciones no afectan a los valores de las variables del espacio de direcciones del proceso hijo y vice­ versa. De hecho esta característica es un mecanismo de protección característico de los SOBUNIX . Un proceso no puede acceder al espacio de direcciones de otro proceso, salvo que ambos compartan una región de memoria compartida. Para poder diferenciar, desde el punto de vista del usuario, que proceso se ej ecuta, f o rk devuelve cuando finaliza el valor O al proceso hijo y el PID del proceso hijo al proceso padre. Es el planificador SOBUNIX: implementación y control de procesos multihilos 57 Lista global de hilos del núcleo p ro e p ro e ·- - - - - - - - - - - - - - - - - - Figura 2.3 - Relación entre las estructuras de datos del núcleo de Solaris para el soporte de procesos multihilos del núcleo el que determina si debe continuar ejecutándose el proceso padre o debe iniciarse la ejecución del proceso hijo. e Ejemplo 2.3 Considérese el programa prog2 1 cuyo código en lenguaje C se muestra en la Figura 2.4. Este archivo puede ser compilado en un SOB UNIX usando por ejemplo el compilador gcc del proyecto GNU, para ello se puede teclear, la siguiente orden desde un intérprete de comandos: gcc - o prog2 1 prog2 1 . c Para ejecutar el programa compilado prog2 1 se debe escribir la siguiente orden: prog2 1 58 Ampliación de sistemas operativos rna i n ( ) { int y , x=O ; y= f o rk ( ) ; if ( y= = - 1 ) { p r i n t f ( " \nErro r " ) ; else i f ( y= = O ) { p r i n t f ( " \ nSoy e l h i j o P I D= %d , mi padre t i en e P I D= %d " , g e tp i d ( ) , g e tpp i d ( ) ) ; x=x+ 1 ; p r i n t f ( " \ nx= %d " , x ) ; else { p r i n t f ( " \ nSoy e l padre P I D= %d " , g e tp i d ( ) ) ; x=x- 1 ; p r i n t f ( " \ nx= %d " , x ) ; p r i n t f ( " \ nF in " ) ; Figura 2.4 - Código C del programa prog2 1 Nótese que para ejecutar esta orden con éxito en la variable de entorno PATH debe estar incluido el directorio de trabajo actual. Si no es así se puede incluir escribiendo en el intérprete la siguiente orden : PATH= $ PATH : . S upóngase que para ejecutar el programa prog2 1 el intérprete crea el proceso A con un PID igual a 2343 . Cuando se ejecuta este proceso en primer lugar se invoca a la llamada al sistema f o rk. Si se produjese algún error durante el tratamiento por parte del núcleo de esta llamada al sistema, f o r k devolvería el valor - l . Luego la condición del i f se cumpliría y se imprimiría en la salida estándar (la pantalla por defecto) el mensaje Error. A continuación se imprimiría el mensaje F i n y la ej ecución del proceso A finalizaría. Si f ork se ejecuta con éxito se crea un nuevo proceso B . Se va a suponer que su PID es igual 2344. Puesto que A y B tienen el mismo código, para distinguir su ejecución fork devuelve O al proceso hijo y el PID del hijo al proceso padre. S upóngase que el planificador planifica primero al proceso hij o B . En ese caso la variable y= O luego el proceso B ejecuta el conjunto de instrucciones del bloque e l s e i f . Señalar que las funciones getpid y g e tpp i d son llamadas al sistema que permiten obtener el PID de un proceso y el PID de su proceso padre, respectivamente. Así en primer lugar se imprime en pantalla el mensaje: S oy e l h i j o P I D = 2 3 4 4 , mi padre t i ene P I D= 2 3 4 3 A continuación el proceso B suma 1 a la variable x y almacena su valor en l a misma posición de memoria. SOBUNIX: implementación y control de procesos multihilos 59 Finalmente se muestra en la pantalla el valor de x (en este caso 1) y el mensaje F in, y el proceso B finaliza. Supóngase que finalizado el proceso hijo B se planifica para ej ecución el proceso A. En este caso la variable y contiene el valor 2344, es decir, el PID del proceso hijo, luego el proceso A ejecuta el conjunto de instrucciones del bloque el s e . Así en primer lugar se imprime en pantalla el mensaj e : Soy e l padre P I D = 2 3 4 3 A continuación el proceso resta 1 a la variable x y almacena su valor en la misma posición de memoria. Finalmente se muestra en la salida estándar el valor de x (en este caso - 1 ) y el mensaj e F in, y el proceso A finaliza. Nótese que el proceso padre y el proceso hijo tienen espacios de direcciones idénticos pero indepen­ dientes. Luego los cambios que realice el proceso hij o en sus variables no afectan a los valores de las variables homónimas del proceso padre y viceversa. • Las acciones que realiza la rutina del núcleo que se encarga de tratar la llamada al sistema fork dependen de cada SOBUNIX, pero en general suele realizar las siguientes acciones [Vahalia, 1 995] : • Asignar una entrada de la tabla de procesos al proceso hijo, es decir, una estructura pro c . • Rellenar los campos d e l a estructura proc del proceso hij o a partir de l a información contenida en la estructura proc del proceso padre. • Crear el área-u del proceso hij o a partir del área-u del proceso padre. • Asignar un PID al proceso hijo. • Crear el espacio de direcciones virtuales del proceso hij o a partir del espacio de direcciones del proceso padre (ver sección 4.2.7). • Crear las referencias oportunas a los recursos compartidos con el proceso padre, como por ejemplo los archivos abiertos y el directorio de trabaj o actual. • Inicializar el contexto hardware del proceso hijo con una copia del contexto hardware del proceso padre para poder iniciar la ejecución del proceso hij o cuando éste sea planificado. • Configurar el estado del proceso a preparado para ejecución y colocarlo en alguna cola de planifi­ cación en función de su prioridad. La llamada al sistema exe c El proceso hijo creado por f ork ejecuta el mismo código que el proceso padre. Disponer de diferen­ tes instancias de un mismo programa resulta útil en el diseño de algunas aplicaciones, como por ejemplo, 60 Ampliación de sistemas operativos aquellas con un esquema cliente-servidor. En otras aplicaciones interesa, sin embargo, que el proceso hijo ej ecute un programa distinto al proceso padre. Un ej emplo típico de este caso es un intérprete de comandos, desde el cual se invocan a otros programas . En los S OBUNIX un proceso puede invocar a otro programa haciendo uso de la llamada al sistema exe c . Esta llamada al sistema básicamente reemplaza las regiones del espacio de direcciones del proceso invocador por las regiones del programa invocado. Luego cuando finaliza la llamada al sistema exec el proceso invocador pasa a ej ecutar el código del programa invocado. La llamada al sistema exec se utiliza en colaboración con la llamada al sistema f ork. Por ejemplo, cuando un usuario invoca un cierto programa ej ecutable desde la línea de órdenes de un intérprete de comandos, éste crea un proceso hij o invocando a f o rk y a continuación invoca a la llamada al sistema exec para que el proceso hijo ejecute el programa invocado. La llamada al sistema exec puede ser invocada a través de diferentes funciones de envoltura como por ej emplo: exe c l , execv y exe c l e . La principal diferencia entre todas ellas radica en el número y tipo de sus argumentos de entrada. Se distinguen tres tipos de argumentos posibles : el nombre de ruta del archivo ejecutable, los argumentos del programa invocado especificado cada uno como un puntero a una cadena de caracteres y el entorno que es un array de punteros a cadenas de caracteres. A cada cadena se le conoce con el nombre de variable de entorno (ver sección 1 .5 . 1 ) . e Ejemplo 2.4 Considérense los archivos ejecutables prog2 2 y prog2 3 (ver Figura 2.5). S upóngase que prog2 2 es invocado desde la línea de órdenes de un intérprete de comandos y que el intérprete crea un proceso A para ejecutar este programa. En primer lugar el proceso A invoca una llamada al sistema f o rk. Supuesto que la llamada se ejecuta con éxito se crea un proceso B hij o de A. Qué proceso se planificará primero, el padre o el hijo, dependerá del planificador. / * C ó d i g o de prog2 2 * / ma i n ( ) { i f ( f o rk ( } = = 0 ) { execv ( " prog 3 " , 0 ) ; p r i n t f ( " \ n men s a j e l " ) ; p r i n t f ( " \ n mens a j e 2 " ) ; / * C ó d i g o de prog2 3 * / ma i n ( ) { p r i n t f ( " \ n men s a j e 3 " ) ; Figura 2.5 - Código C de los programas prog2 2 y prog2 3 SOBUNIX: implementación y control de procesos multihilos 61 Se va a suponer que se ejecuta primero el proceso padre. En dicho caso, después de f ork el proceso A evalúa la condición del i f . Como la llamada al sistema devuelve al proceso padre el PID del proceso hijo, la condición del i f no se cumple por lo que imprime en la salida estándar men s a j e2 y finaliza su ejecución. Cuando se planifica el proceso B, éste evalúa la condición del i f . Como fork devuelve O al proceso hijo, sí se cumple la condición por lo que invoca a la llamada al sistema execv. Esta llamada tiene dos parámetros de entrada el nombre de ruta del archivo ejecutable, en este caso prog2 3 , y los argumentos que se pasan al ejecutable, en este caso ninguno. Al atenderse esta llamada al sistema el espacio de direcciones del proceso B es sustituido por el espacio de direcciones construido a partir de prog2 3 . Luego, el código del proceso B pasa a ser el de prog2 3 . En consecuencia, el proceso B imprime en la salida estándar men s a j e3 y finaliza su ejecución. • Las acciones que realiza la función del núcleo que se encarga de tratar la llamada al sistema exec dependen de cada SOBUNIX, pero en general suele realizar las siguientes acciones [Vahalia, 1 995] : • Localizar el archivo ej ecutable del programa invocado. • Consultar la máscara de modo del archivo para verificar que el proceso dispone de permiso de ejecución. • Leer la cabecera del archivo para comprobar si se trata efectivamente de un archivo ejecutable y de que tipo. Si detecta que se trata de un shell script entonces invoca a un intérprete de comandos para que lo ejecute. • Configurar el valor de los identificadores efectivos EUID y EGID, de acuerdo con la activación o no de los bits S_I SUID o S_I SGID de la máscara de modo del archivo ejecutable. • Copiar los argumentos y el entorno en el espacio del núcleo. • Liberar el espacio de direcciones del proceso. • Asignar el nuevo espacio de direcciones del proceso a partir del archivo ejecutable del programa invocado. • Copiar los argumentos y el entorno en la pila de usuario. • B orrar las direcciones de los manejadores de las señales y configurar su tratamiento a las acciones por defecto. • Inicializar el contexto hardware salvado del proceso para que el proceso pueda comenzar cuando vuelva de la llamada al sistema con la ejecución del programa invocado. 62 Ampliación de sistemas operativos La llamada al sistema vfork En los primeros SOBUNIX durante el tratamiento de f ork se duplicaban la mayoría de las páginas del proceso padre cargadas en memoria para que el proceso hij o dispusiera de una copia de las mismas, independiente de si el proceso hijo las iba a utilizar o no. Esto suponía un desperdició tanto de espacio en memoria como de tiempo de uso del procesador. Posteriormente, para evitar este duplicado innecesario de páginas los SOBUNIX comenzaron a im­ plementar la técnica de copiar al escribir ( copy on write) que consiste en aplazar la copia de una página del proceso padre hasta que el proceso hijo tiene que escribir en la página. Señalar que esta estrategia no se aplica sobre las páginas pertenecientes a la región de código o las regiones de memoria compartida del proceso padre, ya que para este tipo de regiones solo se mantiene en la memoria principal una única copia de la página, la cual es compartida por el proceso padre y sus procesos hijos. Otra posible solución al problema del duplicado de páginas consiste en usar la llamada al sistema v f ork. Esta llamada, introducida en el sistema BSD y posteriormente exportada a otros SOBUNIX, bloquea al proceso padre y presta su espacio de direcciones al proceso hij o. Cuando el hij o invoca a otro programa con exec o termina su ejecución entonces el espacio de direcciones es devuelto al proceso padre y éste es desbloqueado. La llamada vf ork es mucho más rápida que f o rk ya que se ahorra los pasos de configurar un nuevo espacio de direcciones para el proceso hijo. En consecuencia, está llamada resulta muy útil cuando el proceso hijo que se crea va a invocar posteriormente a la llamada exec para ej ecutar otro programa. Consideraciones sobre el diseño de fork en procesos multihilos En un proceso multihilo la invocación de una llamada al sistema fork es realizada por alguno de los procesos ligeros en que se descompone el proceso. A la hora de tratar esta llamada por parte del núcleo se plantean dos posibles opciones : e • Duplicar en el proceso hijo todos los procesos ligeros del proceso. Esta opción resulta más conve­ niente cuando el proceso hij o que se crea no va a invocar posteriormente a la llamada exe c para ejecutar otro programa. Su implementación presenta problemas asociados con el comportamiento que deben tener los procesos ligeros del proceso hij o que sean duplicados a partir de procesos ligeros del proceso padre que estuvieran bloqueados en espera de algún evento. • Duplicar en el proceso hijo solo el procesos ligero que invocó a f ork. Esta opción resulta más conveniente cuando el proceso hijo que se crea va a invocar posteriormente a la llamada exec para ejecutar otro programa. Su implementación puede presentar problemas asociados con la sincroni­ zación de hilos y con el soporte de la librería de hilos. Ejemplo 2.5 S upóngase que la ejecución de un proceso A se ha descompuesto en dos procesos ligeros A l y A2. El proceso ligero A l ha realizado una cierta llamada al sistema y se encuentra bloqueado a la espera de recibir una entrada desde el teclado. Por su parte el proceso ligero A2 invoca a una llamada al sistema SOBUNIX: implementación y control de procesos multihilos 63 f ork. Supóngase que esta llamada al sistema duplica todos los procesos ligeros del proceso padre. En dicho caso se crea un proceso B con dos procesos ligeros B 1 y B2, que son duplicados de A 1 y A2, respectivamente. El problema surge con el proceso ligero B 1 ya que competirá con A l por obtener la entrada del teclado produciendo una condición de carrera. • e Ejemplo 2.6 Supóngase que la ejecución de un proceso A se ha descompuesto en dos procesos ligeros Al y A2. El proceso ligero Al ha realizado una operación wa i t_s em ( s ) (ver sección 3 . 3 .2) sobre un semáforo binario S . Por su parte el proceso ligero A2 invoca a una llamada al sistema f o rk. Supóngase que esta llamada al sistema duplica únicamente el proceso ligero del proceso padre que invoca la llamada al sistema. En dicho caso se crea un proceso B con un proceso ligero B2, que es un duplicado de A2. El problema surge si el proceso ligero B2 después de ej ecutar f o rk intenta adquirir el semáforo S ha­ ciendo una operación wa i t_s em ( s ) . Como no se ha duplicado en el proceso B el proceso ligero A l el semáforo S nunca será liberado con una operación s i gnal_s em ( s ) y en consecuencia B2 se quedará siempre bloqueado. • Puesto que es complicado diseñar una única llamada al sistema f o rk que trate adecuadamente ambas posibilidades, los SOBUNIX suelen soportan diferentes variantes de f o rk. Por ej emplo, en S olaris f o rk duplica únicamente el proceso ligero que invoca la llamada. Si se desean duplicar todos los procesos ligeros del proceso padre se debe utilizar la llamada al sistema f o rka l l . Cada variante trata una de las posibilidades e intenta resolver en la manera de lo posible los problemas que dicha opción puede plantear. Por ejemplo, durante el tratamiento de la llamada f o rka l l si alguno de los procesos ligeros del proceso padre estaba realizando una llamada al sistema ésta es interrumpida en el duplicado del proceso ligero del proceso hijo. Recuérdese (ver sección 1 .5 . 3 ) que si una llamada al sistema es interrumpida retorna devolviendo el valor - 1 y almacenando un error E INTR en la variable errno . La llamada al sistema interrumpida puede ser reiniciada por el proceso ligero duplicado si lo considera necesario. En general, el diseño de las llamadas al sistema en SOBUNIX que soportan hilos del núcleo debe hacerse teniendo en cuenta si los efectos de una llamada deben aplicarse únicamente al proceso ligero in­ vocador o a todos los procesos ligeros del proceso. Además en ambos casos se debe respetar la semántica del modelo tradicional de proceso monohilo. 2.3.2. Terminación de procesos En los SOBUNIX si un proceso A desea terminar su ej ecución debe invocar explícitamente a la llamada al sistema exi t. Esta llamada tiene como argumento de entrada un número entero que es puesto a disposición del proceso padre de A para que lo examine si así lo desea. Este número denominado generalmente estatus de salida (exit status), permite al proceso hijo especificar al padre la causa por la que finalizó de acuerdo con un criterio previamente establecido por el programador. Generalmente se Ampliación de sistemas operativos 64 suele considerar que si el estatus de salida es igual a O entonces el proceso termino normalmente y sin errores, mientras que el resto de valores se utilizan para especificar los diferentes errores o causas que produjeron la terminación del proceso. El sistema operativo también procede de forma implícita a la terminación de un proceso después de ejecutar la última instrucción del proceso. En este caso el núcleo también configura a un determinado valor del estatus de salida del proceso, dicho valor depende de cada SOBUNIX. Asimismo la terminación de un proceso A también se puede producir si el proceso A recibe una señal (ver sección 2 . 3 . 3 ) cuyo tratamiento especifique la finalización del proceso. En este caso el núcleo configura el valor del estatus de salida con el número que identifica a la señal que originó la terminación del proceso. Independientemente del motivo de la terminación de un proceso, el sistema operativo realiza esta tarea ej ecutando una rutina del núcleo denominada exi t ( ) en la mayoría de SOBUNIX. Las acciones que realiza exi t ( ) depende de la implementación de dicha función en cada S OBUNIX. En general exi t ( ) suele realizar, entre otras, las siguientes acciones [Vahalia, 1 995] : • Deshabilitar el tratamiento de las señales. • Cerrar los archivos abiertos por el proceso A. • Almacenar en la entrada de la tabla de proceso asignada al proceso A los datos estadísticos de uso de recursos y el estatus de salida. • Poner al proceso A en el estado zombi. • Hacer que el proceso i n i t adopte a los procesos hijos del proceso A. Conviene señalar que cuando se arranca un SOBUNIX se crean unos pocos procesos para realizar diferentes tareas asociadas al arranque del sistema. Entre estos procesos se encuentra el proceso inicial o proceso i n i t cuyo PID es igual a l . Este proceso se encarga de crear todos los demás procesos necesarios para inicializar el sistema operativo. El proceso i n i t es el primer antepasado de todos los procesos existentes, excepto aquellos creados por el sistema operativo al arrancar. Cuando un proceso termina si tiene hijos activos éstos son adoptados por ini t, que ejerce de proceso padre. • Liberar los recursos de memoria asignados al proceso. • Enviar una señal S I GCHLD al proceso padre del proceso A para notificarle la terminación de su hijo. • Despertar al proceso padre de A si éste estaba dormido esperando por la terminación de su hijo. En algunas aplicaciones el proceso padre necesita saber cuándo un proceso hijo ha terminado su ejecución. Un ejemplo típico es un intérprete de comandos cuya ejecución está asociada a un cierto proceso A. Si un usuario teclea un comando externo el proceso A crea (invocando a f ork) un proceso hij o B y le asocia (mediante ex e e ) el código de dicho comando. A continuación ejecuta una determinada llamada al sistema para entrar en el estado dormido a la espera de que termine su hijo. Cuando el proceso SOBUNIX: implementación y control de procesos multihilos 65 B termine el proceso A será despertado por el núcleo y mostrará en la pantalla el apuntador del intérprete para indicar al usuario que ya puede introducir otro comando. Existen varias llamadas al sistema que permiten a un proceso esperar por la terminación de alguno de sus proceso hijos, como por ejemplo : wa i t, wa i t 3 , wa i t 4 , wa i t p i d y wa i t i d. La principal diferencia entre todas estas llamadas al sistema se encuentra en la información sobre el hij o que devuelve cada llamada y en la posibilidad de especificar el hij o en concreto por el que se desea esperar. e Ejemplo 2.7 Supóngase que el programa prog2 4 (ver Figura 2.6) es invocado desde la línea de órdenes de un intér­ prete de comandos de la siguiente forma: prog2 4 1 0 Supóngase además que a la ejecución de este programa se le asocia un proceso A . En primer lugar el proceso A comprueba si la variable entera argc es igual a 2. En esta variable está almacenado el número de argumentos pasados al programa por la línea de comandos. En este caso la condición se cumple ya que el nombre del programa cuenta como primer argumento. En segundo lugar invoca a la función de librería a t o i que convierte la cadena de caracteres argv [ 1 ] en un número entero y almacena el resultado en la variable x . Como argv [ 1 ] contiene el segundo argumento introducido por la línea de comandos entonces x = 1 O . En tercer lugar comprueba si l a variable x es igual a 1 0. En caso afirmativo invoca a l a llamada al sistema f ork para crear un proceso hijo B . S e va a suponer que después de la llamada al sistema f o r k se continua con la ej ecución del proceso padre. Entonces como f o rk devuelve al proceso padre el PID del proceso hijo no se cumplen las dos ma in ( i nt argc , char * argv ( ] ) { i n t x = O , s = O , var = O ; if ( argc = = 2 ) x = a t o i ( argv [ l ] ) ; if ( x= = l O & & f o rk ( ) = = O ) p r i n t f ( " \ n Pr o c e s o hi j o P I D= %d \ n " , g e tp i d ( ) ) ; exi t ( 5 ) ; s = wa i t ( &var ) ; p r i n t f ( " \ n s= %d var= %d\ n " , s , var ) ; e l s e p r i n t f ( " \ n Número de paráme t r o s i n c o r r e c t o \ n " ) ; Figura 2.6 - Código C del programa prog2 4 66 Ampliación de sistemas operativos condiciones del i f, luego la siguiente sentencia que ejecuta el proceso padre es una invocación de la llamada al sistema wa i t. Esta llamada si se ejecuta con éxito devuelve en s el PID del primer hijo de A en estado zombi que encuentra. Además almacena los ocho bits menos significativos del estatus de salida en la dirección asociada a la variable var. Si el proceso A no tiene procesos hijos o si wa i t es interrumpida por una señal, entonces se considera que se ha producido un error en la ejecución y la llamada devuelve en s el valor - l . El núcleo durante el tratamiento de una llamada al sistema wa i t borra la entrada de l a tabla de procesos asociado al primer hijo en estado zombi que encuentra. En el caso de que el proceso A tenga procesos hi­ jos pero ninguno se encuentre en el estado zombi entonces pasa al proceso A al estado dormido mientras espera por la terminación de alguno de sus hijos. Cuando alguno termine la función del núcleo exi t ( ) enviará al proceso A una señal S I GCHLD para notificárselo. En este programa, supuesto que el proceso padre se ej ecuta antes que su hijo, durante el tratamiento de wa i t el proceso A entra en el estado dormido ya que su proceso hij o no está todavía en el estado zombi. Cuando el proceso B es planificado para ej ecución, supuesto que su PID= 3456, imprime en la salida estándar el mensaje: Pro c e s o hi j o P I D = 3 4 5 6 e invoca a la llamada al sistema exi t para terminar su ejecución. Nótese que el estatus de salida especi­ ficado por el proceso hijo es igual a 5 . Durante e l tratamiento de l a llamada al sistema exi t s e despierta al proceso A. Éste al ser planificado completa la llamada al sistema wa i t que almacena en la variable var el estatus de salida del proceso B y en la variable s el PID del proceso B . Señalar que en realidad, debido a la implementación que hace el núcleo de la llamada al sistema wa i t, en la variable var se almacena el estatus de salida especificado como argumento de entrada de exi t multiplicado por 256, es decir, 256·5 = 1 280. Por último el proceso A imprime en la salida estándar el mensaj e s = 3 4 5 6 var = 1 2 8 0 y termina su ej ecución. Nótese que si el proceso hijo se hubiera planificado antes que el padre cuando se tratara la llamada al sistema wa i t no hubiera hecho falta dormir al proceso A ya que el proceso B ya estaría en el estado zombi. Por otra parte, si el programa se hubiese invocado desde la línea de comandos de la forma prog2 4 5 entonces el proceso hijo nunca se hubiera creado y la llamada a wa i t hubiera devuelto en s el valor - l . En consecuencia el mensaje que se habría escrito en l a salida estándar sería: s = - 1 var = O • SOBUNIX : implementación y control de procesos multihilos 2.3.3. 67 Notificación de eventos : señales Descripción general Las señales son un mecanismo implementado por el núcleo para la notificación de eventos a proce­ sos. Cada evento notificable tiene asociado una determinada señal. A su vez cada señal tiene asignado un número entero positivo pequeño que la identifica de manera univoca. Además tiene asociada una cons­ tante simbólica que comienza con S IG y va seguida por una abreviatura o una palabra que resume el evento al que está asociada. Cada S OBUNIX establece el número de señales que soporta, así como el número entero y la constante simbólica que le asigna a cada una. En la Tabla 2. 1 se muestra a modo de ejemplo algunas de las señales definidas en las especificaciones POSIX. Los programas que hacen uso de señales POSIX tienen garantizada su portabilidad en todos los sistemas operativos que cumplan con POS IX. El núcleo envía una señal a un determinado proceso A cuando se produce algún evento que deba ser notificado. Dicho evento puede estar causado por interrupciones, por la ejecución del propio proceso A o por la ejecución de otro proceso B . Efectivamente algunas interrupciones pueden producir que el núcleo envíe una señal a un proceso. Señal Acción por defecto S I GABRT S I GALRM S I GCHLD S I GCONT S IGFPE S IGILL S I G INT S I GKILL S IGPI PE S IGQUIT S IGSEGV S I GSTOP S I GTERM S I GTSTP S I GTTIN S I GTTOU S IGUP S I GUSRl S I GUSR2 S I GXCPU Abortar Terminar Ignorar Continuar Abortar Abortar Terminar Terminar (*) Terminar Abortar Abortar Parar (*) Terminar Parar Parar Parar Terminar Terminar Terminar Abortar Tabla 2.1 - Evento asociado Abortar un proceso Alarma de tiempo real Terminación o suspensión de un proceso Continuar si estaba parado Excepción de aritmética en coma flotante Instrucción ilegal Señal de interrupción procedente del teclado ( [ c ontro l ] + [ e ] ) Finalizar un proceso Escritura en una tubería sin lectores Señal de terminación procedente del teclado ([contro l ] + [ \ ] ) Referencia a una dirección de memoria ilegal Parar proceso Finalizar un proceso Señal de parar procedente del teclado ( [ c ontro l ] + [ z ] ) Lectura del terminal por un proceso en segundo plano Escritura en el terminal por un proceso en segundo plano Desconexión del terminal Señal definida por el usuario Señal definida por el usuario Excedido el tiempo de uso de CPU Algunas señales definidas en POS IX. (*) La acción por defecto no puede ser modificada. Ampliación de sistemas operativos 68 Por ejemplo, si se está ejecutando un proceso A en primer plano en un intérprete de comandos y el usuario pulsa las teclas [cont r o l ] + [c ] se genera una interrupción hardware que al ser atendida por el núcleo produce, entre otras acciones, el envío de una señal S I GINT al proceso A. Otra de las principales fuentes de envío de señales es la ejecución de un proceso. Entre los principales eventos asociados a la ejecución de un proceso que causan que el núcleo tenga que enviarle una señal a dicho proceso se encuentran los siguientes : • Excepciones. Si durante la ejecución del proceso se produce por ejemplo una referencia a una dirección de memoria no válida o se intenta ejecutar una instrucción ilegal, se producirá una ex­ cepción que al ser tratada por el núcleo producirá entre otras acciones el envío al proceso de una señal. Las señales S IGFPE, S IGILL y S IGSEGV de la Tabla 2. 1 son ejemplos de señales asociadas a excepciones. • Alarmas. Un proceso puede solicitar al núcleo, mediante la invocación de la llamada al sistema adecuada, que le envíe una señal cuando haya transcurrido una cierta cantidad de tiempo. A este mecanismo de aviso se le denomina alarma. Por ejemplo, usando la llamada al sistema a l arm un proceso puede configurar una alarma de tiempo real, es decir, que el núcleo le envíe una señal S I GALRM cuando hayan transcurrido un número determinado de segundos de tiempo real. • Cuotas. Si un proceso supera la cuota de uso de un recurso el núcleo se lo puede notificar mediante el envío de una señal. Por ejemplo, si un proceso excede su tiempo de uso del procesador, el núcleo le envía una señal S I GXC PU. También, un proceso puede solicitar al núcleo, invocando la llamada al sistema oportuna, que envíe una señal a un determinado proceso o incluso que se la envíe a si mismo. Por ejemplo, las llamadas al sistema s i g s end y k i l l permiten enviar una determinada señal a un proceso o grupo de procesos (ver sección 2 . 3 .5) determinado. Una señal se considera que ha sido recibida o entregada, cuando se ejecuta la acción que se había establecido ante la recepción de dicha señal. Existen seis posibles acciones : • Ignorar. La recepción de la señal no produce la realización de ninguna acción. • Terminar. Se finaliza la ejecución del proceso. • Abortar. Se crea en el directorio de trabajo actual un archivo donde se almacena información (espacio de direcciones, contexto hardware, etc) sobre el proceso y a continuación se procede a la terminación del proceso. El archivo creado, denominado c o r e en algunos SOBUNIX, puede ser consultado por otros procesos, como por ejemplo, depuradores. • Parar. Se suspende la ejecución del proceso y éste pasa al estado parado. Esta acción es la que se realiza por defecto cuando se recibe una señal S IGSTOP enviada al proceso por un depurador o ante la recepción de la señal S IGTSTP generada por la pulsación de un usuario de las teclas [contro l ] + [ z ] . SOBUNIX: implementación y control de procesos multihilos 69 • Continuar. Se pasa el proceso al estado preparado para ejecución para que pueda continuar con su ej ecución cuando sea planificado. Se aplica únicamente si el proceso estaba en el estado parado. Esta acción es la que se realiza por defecto cuando se recibe una señal S I GCONT . • Capturar. Se ejecuta una determinada función definida por el usuario. A dicha función se le deno­ mina manejador de la señal (signal handler) . Cada señal tiene asociada por defecto una acción a realizar. Por ejemplo, la acción por defecto que debe realizar el núcleo cuando un proceso reciba una señal S I GUSRl es terminar la ejecución del proceso (ver Tabla 2. 1 ) . La información sobre la acción asociada a cada señal se almacena en una determinada estructura de datos dentro del área-u del proceso. En esta estructura también se almacena las direcciones de los manejadores de las señales que hayan sido especificados. Un proceso puede configurar diferentes aspectos relativos a la recepción de las señales : e • Especificar la acción asociada a la recepción de una señal. Un proceso puede especificar la acción que se desea que se realice cuando reciba una determina señal, para ello debe invocar a la llamada al sistema adecuada. Por ejemplo, se pueden usar las llamadas al sistema s i gna l , s i gac t i on o s i gvec . • Bloquear la recepción de una señal. Un proceso también puede bloquear o enmascarar l a recep­ ción de una señal con objeto de proteger una sección crítica de su código. Para ello debe invocar una llamada al sistema, como por ejemplo: s i g s e tma sk, s i gma sk y s i gbl o ck. El núcleo mantiene al menos una mascara de señales bloqueadas por proceso que es un mapa de bits que permite con­ figurar el bloqueo o no de cada tipo de señal. Existen algunas señales especiales, como S IGSTOP o S I GKILL, cuya recepción no puede ser bloqueada y cuya acción asignada por defecto tampoco puede ser cambiada. • Esperar por la recepción de una señal. Un proceso puede quedarse suspendido a la espera de la recepción de una señal que no tenga bloqueada o no ignore. Para ello debe invocar una llamada al sistema, como por ej emplo, pau s e , s i gpau s e o s i g suspend. Ejemplo 2.8 Supóngase que el programa prog2 5 (ver Figura 2.7) es invocado desde la línea de órdenes de un intér­ prete de comandos y que a la ejecución de este programa se le asocia un proceso A. En primer lugar el proceso A invoca una llamada al sistema s i gna l . Esta llamada establece la acción a realizar ante la recepción de una determinada señal. De forma general esta llamada tiene la siguiente sintaxis : s = s i gnal ( nombre_s eña l , a c c i ón_r e c epc i ón ) ; nombre_s eña l es el número entero o la constante simbólica de la señal sobre la cual se desea especi­ ficar la acción a realizar. Por su parte a c c i ón_r e c epc i ón es un número entero o constante simbólica 70 Ampliación de sistemas operativos # i n c l ude < s i gnal . h> vo i d fun { i n t s i g ) ; ma i n { ) { int r l = O , r2 = 0 ; s i gn a l { S I GUSR2 , fun ) ; i f { f o rk { ) = = O ) / * Pr o c e s o h i j o * / { s l e ep { 3 ) ; p r i n t f { " \ n Env i o s eñ a l a mi padre " ) ; k i l l { ge tpp i d { ) , S I GUSR2 ) ; e l s e / * Pr o c e s o padr e * / { r l = s l e ep ( 7 ) ; r 2 = s l e ep ( l ) ; p r i n t f { " \ n r l= %d r 2 = %d \ n " , r l , r 2 ) ; vo i d fun { i n t s i g ) s l e ep { l ) ; p r i n t f { " \ n Rec ibo s eñ a l de mi h i j o " ) ; Figura 2. 7 - Código C del programa prog2 5 asociada a la acción a realizar al recibir la señal. Puede tomar dos posibles valores : S I G_DFL o o que es­ pecifica que la acción a realizar es la establecida por defecto y S I G_IGN o 1 que especifica que la acción a realizar es ignorar la señal. También ac c i ón_r e c epc i ón puede ser la dirección de un manej ador de señal, con lo que se estaría especificando que la acción a realizar es capturar la señal. Si la llamada se ejecuta con éxito, entonces en s se devuelve la acción que se tenía asignada a la recepción de la señal antes de ejecutar esta llamada. En caso de error en s se devuelve la constante S I G_ERR asociada al valor - l . En el caso de l a llamada s i gnal ( S IGUSR2 , fun ) que realiza el proceso A s e está estableciendo que ante la recepción de una señal S IGUSR2 se ejecute el manej ador de la señal fun definido por el usuario. En segundo lugar, el proceso A invoca a la llamada al sistema f ork para crear un proceso hijo. Supóngase que el planificador planifica al proceso padre antes que al proceso hijo. Entonces el proceso A invoca a la función s l eep de la librería l ibe que permite suspender la ejecución de un proceso durante un determinado intervalo de tiempo especificado en segundos. En este caso la ej ecución del proceso A se suspende durante 7 segundos. Cuando se planifica el proceso hijo, éste en primer lugar invoca a la función s l eep para suspender su SOBUNIX: implementación y control de procesos multihilos 71 ejecución durante 3 segundos. A continuación invoca a la función de librería print f para imprimir por la salida estándar el texto: Env i o s eña l a mi padre Acto seguido invoca a la llamada al sistema k i 11 que permite enviar una señal a un determinado proceso o conjunto de procesos. De forma general esta llamada tiene la siguiente sintaxis : s =ki l l ( i d , nombre_s eña l ) ; nombre_señal es el número entero o la constante simbólica de la señal que se va enviar. i d es un número entero que identifica al proceso o grupo de procesos al que va dirigida la señal. Si i d es mayor que O, la señal es enviada al proceso cuyo PID=id. Si i d es igual a O la señal es enviada a todos lo procesos que pertenezcan al mismo grupo que el proceso que invoca la llamada. Si id es igual a - 1 , entonces la señal es enviada a todos los procesos cuyo UID sea igual al EUID del proceso que invoca la llamada. En el caso de que el EUID sea el del superusuario entonces la llamada es enviada a todos los procesos, excepto a los procesos con PID=O y con PID= l . Por último si id es menor de - 1 , entonces la señal es enviada a todos los procesos cuyo identificador de grupo (ver sección 2.3.5) es igual a l i dl . Si la llamada se ejecuta con éxito entonces en s se almacena un O. En caso de error se almacena el valor - 1 . De acuerdo, con la explicación anterior el significado de la llamada ki 1 1 ( getpp i d ( ) , S I GUSR2 ) que invoca el proceso hijo sería el de enviar la señal S I GUSR2 al proceso padre. Recuérdese que la llamada al sistema getpp i d devuelve el PID del proceso padre del proceso que la invoca. Al retornar de la llamada ki 1 1 el proceso hijo finaliza su ejecución. Por su parte el envío de la señal S I GUSR2 hace que el núcleo despierte al proceso A antes de completar sus 7 segundos de suspensión. Al ser planificado de nuevo el proceso A, se ejecuta el manej ador fun definido para la señal S IGUSR2 . Este manejador realiza dos acciones. En primer lugar invoca a la función de librería s l e ep para suspender la ejecución del proceso durante 1 segundo. A continuación invoca a la función de librería p r i n t f que imprime por la salida estándar el texto Rec ibo s eñal de mi h i j o Finaliza la ejecución del manejador, se continúa con la ejecución de la instrucción del proceso A que fue interrumpida por la recepción de la señal. Como s l e ep no se completó con éxito al ser interrumpida por la señal, entonces devuelve en rl el tiempo de suspensión que le quedaba al proceso, en este caso 4 segundos. A continuación se invoca a otra función de librería s l e ep para suspender la ejecución del proceso A durante 1 segundo. Como la función se ejecuta con éxito entonces en r2 se almacena el valor O. Finalmente se invoca a la función de librería print f que imprime por la salida estándar el texto r 1 = 4 r2 = 0 y finaliza la ejecución del proceso A . • 72 Ampliación de sistemas operativos # i n c l ude < s i gnal . h> vo i d handl e r ( i n t s i g ) ; ma i n ( ) { s i gnal ( S I GUSRl , S IG_I GN ) ; s i gnal ( S I GUSR2 , hand l e r ) ; pau s e ( ) ; p r i n t f { " \ n Mens a j e \ n " ) ; vo i d handl e r ( i n t s i g ) p r i n t f ( " \ n Señal %s capturada \ n " , s t r s i gn a l ( s i g ) ) ; Figura 2.8 - Código C del programa prog2 6 e Ejemplo 2.9 Supóngase que el programa prog2 6 (ver Figura 2.8 ) es invocado de la siguiente forma desde la línea de órdenes de un intérprete de comandos: prog2 6 & El carácter '&' al final de la orden especifica al intérprete que el programa prog2 6 debe ser ejecutado en segundo plano, con lo que el intérprete puede seguir recibiendo órdenes del usuario. Supóngase que para ejecutar este programa el intérprete crea un proceso A con PID igual a 980. En primer lugar el proceso A invoca una llamada al sistema s i gnal para especificar que la acción que se realice cuando se reciba una señal S IGUSRl sea la de ignorar ( S I G_IGN) la señal. A continuación vuelve a invocar una llamada al sistema s i gnal para especificar que la acción que se debe realizar cuando se reciba una señal S IGUSR2 sea la de capturar la señal usando el manejador hand l e r . A continuación invoca una llamada al sistema pau s e , la cual pasa al proceso al estado dormido a la espera de recibir una señal que no ignore y que no tenga bloqueada. Supóngase que se escribe la siguiente orden en la línea de comandos del intérprete: k i l l - S IGUSRl 9 8 0 El comando k i l l permite enviar una señal a un proceso. En este caso se enviaría la señal S I GUSRl al proceso con PID=980, es decir, al proceso A. Puesto que el proceso A ha especificado que esta señal sea ignorada, el proceso A permanece a la espera de recibir una señal que no ignore o que no tenga bloqueada. Se puede comprobar que el proceso A sigue ejecutándose haciendo uso del comando j obs o del comando p s (ver sección 2.3 .6) . Supóngase, ahora, que se escribe la siguiente orden: SOBUNIX : implementación y control de procesos multihilos 73 ki l l - S IGKILL 9 8 0 En este caso se envía la señal S I GKILL al proceso A, cuya acción asignada a su recepción es la termina­ ción del proceso. Luego el proceso A termina. Finalmente, supóngase que en vez de la orden anterior se hubiera escrito la siguiente orden: ki l l - S IGUSR2 9 8 0 En este caso se envía la señal S IGUSR2 al proceso A, cuya acción asignada a su recepción es capturar la señal ejecutando el manej ador handl er. Cuando se ejecuta este manejador se imprime en la salida estándar el mensaje: Señal User de f ined s i gnal 1 c apturada Nótese que la función s t r s i gnal a la que se invoca en la función print f muestra un mensaje que describe el tipo de señal, en este caso User de f ined s i gnal 1 para S I GUSRl . A continuación se imprime en la salida estándar el texto Mens a j e y finaliza la ejecución del proceso A. • Una señal que ha sido enviada pero todavía no ha sido recibida se dice que está pendiente. El núcleo mantiene una máscara de bits para especificar las señales pendientes de un proceso. También mantiene una máscara de bits para especificar las señales ignoradas por el proceso. El núcleo comprueba la existencia de señales pendientes de un proceso mediante la invocación de una rutina del núcleo que en algunos SOBUNIX recibe el nombre de i s s i g ( ) . Esta función se invoca en varios puntos del flujo de ejecución de un proceso: al retornar del tratamiento de una llamada al sistema, al retornar del tratamiento de una interrupción hardware, una trampa o una excepción; y al despertar (desbloquear) a un proceso. La función i s s i g ( ) inspecciona la máscara de señales pendientes asociada al proceso. Además consulta la máscara de señales bloqueadas y la máscara de señales ignoradas. Si encuentra una señal que está pendiente que no sea ignorada y que no esté bloqueada entonces i s s i g ( ) finaliza su ejecución devolviendo el valor TRUE . En dicho caso el núcleo invoca a otra rutina del núcleo, denominada p s i g ( ) en algunos SOBUNIX, para que ejecute la acción asociada a la recepción de la señal. En el caso de que la acción asociada a la señal sea capturar la señal entonces p s i g ( ) invoca a otra rutina del núcleo, denominada s ends i g ( ) en algunos SOBUNIX. Esta función manipula la pila de usuario para que se pueda ejecutar el manejador de la señal cuando el proceso se ejecute en modo usuario. Una vez finalizada la ejecución del manej ador se continuará con la ejecución del código del proceso justo desde la instrucción donde fue interrumpido por la señal. Si la señal produjo la interrupción de una llamada al sistema entonces la llamada devuelve el valor - 1 y almacena en la variable errno el error E INTR. Nótese que la recepción de una señal por parte de un proceso requiere que dicho proceso sea planifi­ cado para ejecución. Luego desde que se produce un evento que debe ser notificado al proceso hasta que 74 Ampliación de sistemas operativos el proceso recibe la señal puede transcurrir una cantidad importante de tiempo dependiendo del estado y de la prioridad del proceso. Consideraciones sobre la implementación de las señales en procesos multihilo En la descripción general de las señales realizada en la sección anterior se ha supuesto por simplificar que se tenía un proceso monohilo. En el caso de proceso multihilos una señal puede ser enviada a un determinado hilo o a todos los hilos de un proceso. El escoger una opción u otra depende del evento al que está asociada la señal. Generalmente, si el evento al que está asociada una señal ha sido causado por un determinado hilo de un proceso, lo más lógico es enviar la señal a dicho hilo y no a todos los hilos del proceso. Por ejemplo, si durante la ejecución de un determinado hilo de un proceso A se ha producido una excepción, lo más lógico es enviar la señal que notifica la excepción solamente a dicho hilo y no a todos los hilos del proceso, sobre todo si la acción asociada a la recepción de la señal es la de terminar o abortar. Por el contrario, si el evento está asociado a una interrupción externa no se puede asociar a un hilo en particular y por ello lo lógico es enviar la señal a todos los hilos del proceso. Por ejemplo, si en un intérprete de comandos se está ejecutando un proceso multihilo en primer plano y el usuario pulsa las teclas [contro l ] + [c] para terminar la ejecución del proceso entonces se genera una interrupción hardware que al ser tratada por el núcleo produce, entre otras acciones, el envío de la señal S I G INT cuya acción asociada por defecto es la de terminar. Obviamente para terminar con la ejecución del proceso la señal deberá ser enviada a todos los hilos del proceso no solamente a uno. Otro aspecto importante a considerar en el caso de procesos multihilos está asociado con las estruc­ turas de datos que se deben mantener para implementar el mecanismo de señales. De acuerdo con la descripción general de las señales se requiere por cada proceso al menos una estructura de datos para mantener las acciones establecidas por cada señal y los manej adores de señales, una máscara de señales pendientes, una máscara de señales ignoradas y una máscara de señales bloqueadas. En el caso de pro­ cesos multihilos surge la posibilidad de que cada hilo tenga sus propias estructuras de datos asociadas a las señales o que todos los hilos compartan las mismas estructuras. La primera opción con respecto a la segunda es más versátil aunque su gestión produce una mayor sobrecarga. En general, se suele optar por una solución híbrida. Por una parte, la estructura de datos que mantiene las acciones establecidas por cada señal y los manej adores de señales, así como la máscara de señales pendientes y la máscara de señales ignoradas se implementan a nivel de proceso y son compartidas por todos sus hilos. Mientras que la máscara de señales bloqueadas se implementa a nivel de hilo, es decir, cada hilo dispone de su propia estructura. e Ejemplo 2. 10 En el Ejemplo 2.2 se describieron las principales estructuras de datos que implementan el modelo de pro­ ceso multihilo en Solaris. Cada proceso tiene asociada una estructura proc que implementa una entrada de la tabla de procesos. Además tiene asociada una estructura denominada uarea que se implementa dentro de la estructura pro c . Cada hilo d e usuario existente e n u n proceso tiene asociado u n proceso ligero y u n hilo del núcleo. E l hilo SOBUNIX: implementación y control de procesos multihilos 75 de usuario tiene asignada una estructura u lwp en el espacio de direcciones del proceso. Por su parte, el proceso ligero tiene asignada una estructura klwp en el espacio de direcciones del núcleo. Allí también se mantiene una estructura k thread por cada hilo del núcleo. A continuación se enumeran algunos de los campos asociados al mecanismo de señales que contienen estas estructuras : • Estructura proc. - p_s ig. Máscara de señales pendientes. Se implementa como un mapa de 64 bits. Cada bit está asociado a una señal. Si el bit se activa significa que la señal asociada está pendiente para algún hilo del proceso. Nótese que en Solaris existen definidas 48 señales, luego en este mapa existen 1 6 bits no asignados a ninguna señal. - p_i gnor e . Máscara de señales ignoradas. Es un mapa de 64 bits que contiene un bit asociado a cada señal. Si un bit se activa significa que la señal asociada es ignorada. • Estructura uarea. - u_s ignal [ MAXS I G l . Establece las acciones a realizar al recibir cada señal. Cada entrada puede contener alguno de los siguientes valores : S I G_IGN (si la señal es ignorada), S I G_DEF (si se realiza la acción asignada por defecto) o la dirección del manejador definido para la señal . • Estructura u lwp . - u l_s i grna s k. Máscara de señales pendientes para el hilo. • Estructura klwp . - lwp_cur s i g. Señal tratada actualmente. • Estructura kthread. - p_s ig. Máscara de señales pendientes. Su contenido es idéntico al del campo homónimo de la estructura pro c . - p_ho l d. Máscara d e señales bloqueadas. E s u n mapa de 64 bits que contiene u n bit asociado a cada señal. Si un bit se activa significa que la señal asociada está bloqueada. Analizando la información enumerada se concluye que en el modelo de proceso multihilo de Solaris, las acciones a realizar ante la recepción de una determina señal {u_s i gnal [ MAXS I G ] ) y la mascara de señales ignoradas (p_i gno re) se mantienen a nivel de proceso, con lo que es compartida por todos los hilos. 76 Ampliación de sistemas operativos Por otra parte, la máscara de señales bloqueadas (p_h o l d) se mantiene a nivel de hilo, en concreto en el hilo del núcleo. De esta forma cada hilo tiene la posibilidad de especificar las señales cuya recepción desea bloquear. La información sobre las señales pendientes se mantiene tanto a nivel de hilo como a nivel de proceso. En la estructura ulwp se mantienen las señales pendientes para un determinado hilo. Mientras que en las estructuras proc y kthread se duplica la misma información: la existencia de señales pendientes para algún hilo del proceso. Esta duplicidad es necesaria para poder tratar de forma más eficiente todas las situaciones en que se pueden generar señales. • 2.3.4. Control de hilos de usuario: librerías de hilos En las secciones anteriores se han descrito los mecanismos de control de procesos en los SOBUNIX. Dichos mecanismos han sido explicados en su mayor parte considerando procesos monohilos, aunque se han realizado diferentes consideraciones asociadas al caso de los procesos multihilos. Queda sin embargo comentar los mecanismos de control disponibles a nivel de los hilos de usuario. A este nivel los meca­ nismos de control son proporcionados por una librería de hilos, como por ejemplo la librería Pthreads de POSIX escrita en lenguaje C . L a librería Pthreads contiene diferentes funciones para la gestión de los hilos d e usuario: control, sincronización, comunicación, planificación, etc. Cada hilo de usuario tiene asociado un número entero positivo que lo identifica de forma univoca. Además tiene asociado una serie de atributos : inicialización, tamaño y dirección de pila, herencia, parámetros y política de planificación, etc. El identificador numéri­ co del hilo de usuario se almacena en una estructura de datos p thread_t . Mientras que los atributos se almacenan en una estructura de datos pthread_a t tr _t . Las estructuras anteriores y los prototipos de las funciones incluidas en esta librería se encuentran disponibles en el archivo de cabecera p thread . h. La librería Pthreads contiene, entre otras, las siguientes funciones para el control de los hilos de usuario: • p thread_c r e a t . Crea un hilo de usuario para ejecutar una función determinada. • pthread_s e l f . Obtiene el identificador numérico asociado al hilo de usuario que ha invocado a esta función. • pthread_exi t. Termina la ejecución del hilo de usuario que la ha invocado. • pthread_j o in. Suspende la ejecución del hilo de usuario que ha invocado a esta función hasta que termine la ejecución de un determinado hilo de usuario. • pthread_k i l l . Permite enviar una determina señal a un determinado hilo de usuario. Estas funciones suelen devolver un O si se ejecutan con éxito y un valor distinto de O en caso contrario. SOBUNIX: implementación y control de procesos multihilos 77 # i nc lude < s t d i o . h> # i nc lude <p thread . h> vo i d * funl ( vo i d * i d ) ; vo i d * fun2 ( vo i d * i d ) ; rna i n ( ) / * C ó d i g o de l h i l o 1 ( h i l o principal } * / { p thread_t hu l , hu2 ; p thre ad_c r e a t e ( &hu l , NULL , * funl , NULL ) ; pthread_c rea t e ( &hu 2 , NULL , * fun2 , NULL ) ; p thre ad_j o i n ( hu l , NULL ) ; pthread_j o i n ( hu 2 , NULL ) ; vo i d * funl ( vo i d * i d ) / * C ó d i go d e l h i l o 2 * / for ( ; ; ) ; vo i d * fun2 ( vo i d * i d ) / * C ó d i g o d e l hi l o 3 * / int i = O ; for ( i = O ; i < 4 ; i + + ) p r i n t f { " \ nMen s a j e 2 " ) ; pthread_ex i t ( NULL ) ; Figura 2.9 - Código C del programa prog2 7 e Ejemplo 2. 1 1 Supóngase que e l programa prog2 7 (ver Figura 2.9) e s compilado con e l compilador g c c invocándolo de la siguiente forma desde un intérprete de comandos: g c c - o prog2 7 prog2 7 . c - lp thread Si la orden se ejecuta con éxito se genera el archivo ejecutable prog2 7. La ejecución de este archivo produce la creación de un proceso con tres hilos de usuario. El primer hilo de usuario se crea implíci­ tamente para ejecutar la función ma in. É ste se puede considerar como el hilo principal del proceso. La terminación del hilo principal produce de forma implícita la terminación del proceso y en consecuencia del resto de hilos del proceso. Afortunadamente es posible habilitar mecanismos de sincronización para conseguir que el hilo principal termine solo cuando hayan terminado el resto de los hilos creados. El segundo hilo se crea mediante la invocación por parte del hilo principal de la función 78 Ampliación de sistemas operativos pthread_c rea t e ( &hu l , NULL , * funl , NULL ) ; Se observa que esta función tiene cuatro argumentos. El primer argumento es la dirección de una estruc­ tura de tipo p thread_t donde se almacenará el identificador del hilo si la función se ejecuta con éxito. En este caso dicha estructura se denomina hu l . El segundo argumento es la dirección de la estructura de tipo pthread_at t r_t que contiene los valores de los atributos del hilo. En este caso al pasar un valor NULL la librería se encarga de asignarle unos valores por defecto a los atributos del hilo. El tercer argu­ mento es un puntero a la función cuyo código ejecutará el hilo. En este caso el hilo ejecutará la función fun l . Se observa que esta función ejecuta un bucle f o r infinito. El cuarto argumento es un puntero a los argumentos de la función cuyo código ejecutará el hilo. En este caso al pasar un valor NULL se está especificando que la función no requiere argumentos. El tercer hilo se crea también mediante la invocación de p thread_create por parte del hilo principal. El hilo creado ejecutará el código de la función fun2 que imprime cuatro veces Mens a j e 2 en la salida estándar e invoca a la función p thread_exi t para terminar la ejecución del hilo. Nótese que dicha función tiene como argumento un valor NULL. Eso significa que no hay que almacenar ningún valor para que sea consultado por otro hilo que esté esperando por la terminación de este hilo en una función pthread_j o in. Posteriormente, el hilo principal invoca a la función p thread_j o i n ( hu l , NULL ) para suspender su ejecución hasta que termine el primer hilo creado. Esta función tiene dos argumentos de entrada. El primer argumento es el identificador del hilo por el que se desea esperar. En este caso se trata del hilo cuyo identificador se encuentra almacenado en la estructura hu l . El segundo argumento es un puntero al valor devuelto por el hilo por el que se está esperando. En este caso se pasa un valor NULL luego se indica a la librería que no se desea conocer dicho valor. Finalmente el hilo principal invoca otra vez a la función p thread_j o in pero esta vez para esperar por la terminación del segundo hilo creado, cuyo identificador se encuentra almacenado en la estructura hu2 . Supóngase un SOBUNIX con un hilo del núcleo por cada hilo de usuario, por ejemplo Solaris. S i el archivo prog2 7 fuera lanzado en segundo plano prog2 7 & y a continuación se ejecutará el comando ps - L e n la salida estándar s e mostraría l a salida que s e incluye en l a Figura 2. 1 0. Se observa que la ejecución de prog2 7 tiene asociado un proceso con PID= 1 3 1 3 cuya ejecución se descompone en tres procesos ligeros (LWP) con identificadores 1 , 2 y 3. El proceso ligero 1 está asociado al hilo principal que ejecuta el código de ma in. El proceso ligero 2 está asociado al primer hilo creado que ejecuta el código de fun l . SOBUNIX: implementación y control de procesos multihilos 79 Por último el proceso ligero 3 está asociado al segundo hilo creado que ha ejecutado el código de fun2 y que ya ha terminado (de func t). Recuérdese que en Solaris cada proceso ligero está asociado a un hilo de usuario y a un hilo del núcleo. PID LWP TTY LT IME CMD 9 64 1 pts / 3 0 : 0 0 bash 1313 1 pts / 3 0 : 0 0 prog2 7 1313 2 pts / 3 0 : 0 5 prog2 7 1313 3 pts / 3 0 : 0 0 <de func t> 1316 1 pts / 3 0 : 0 0 ps Figura 2.10 - Salida del comando p s - L en el Ej emplo 2. 1 1 • 2.3.5. Grupos de procesos y sesiones Terminales Un terminal serie es un dispositivo de E/S orientado a carácter que consta de un teclado y de una pantalla y que se comunica con el computador mediante una línea serie. En un terminal se trabaja con una interfaz de línea de comandos implementada con un intérprete de comandos. Los terminales serie se utilizaban en macrocomputadores y minicomputadores. Actualmente el ter­ minal serie como dispositivo hardware ha sido sustituido por computadores personales que disponen de software para emular dichos terminales. Por ejemplo xt erm es una aplicación que trabaj a sobre X Win­ dow que emula un terminal en una ventana. Estos terminales emulados o pseudoterminales son bastante utilizados por programadores y administradores del sistema. Las operaciones de E/S con un terminal ya sea real o emulado se implementan como operaciones de E/S sobre un archivo / dev / t ty [ i d ] donde [ i d ] es algún grupo de caracteres que permite distinguir los diferentes terminales que pueden existir. El valor de [ i d ] depende de cada SOBUNIX. Grupos de procesos En los SOBUNIX los procesos son organizados en grupos de procesos. Cada grupo tiene asocia­ do un número entero positivo que lo identifica de manera univoca. A dicho identificador se le conoce generalmente con el nombre de identificador del grupo de procesos (Process Group IDentifier, PGID). Para conocer el PGID de un proceso se pueden usar las llamadas al sistema ge tpg i d y ge tpgrp o el comando p s -j (ver sección 2 . 3 . 6). Dentro de cada grupo de procesos puede existir un proceso el cual tiene asignado un PID que coincide con el PGID del grupo, a dicho proceso se le denomina líder del grupo. Normalmente el resto de procesos que pertenecen al grupo suelen ser los hijos del líder del grupo. Cuando se crea un proceso éste hereda el PGID de su proceso padre. Luego todos los procesos con relación genealógica pertenecen por defecto al 80 Ampliación de sistemas operativos mismo grupo de procesos. Para crear un nuevo grupo de procesos o para cambiar a un proceso de grupo se pueden usar las llamadas al sistema s e tpgid y s e tpgrp. De forma general un grupo de procesos puede ser creado por el núcleo o por una aplicación de usuario para la realización de un determinado trabaj o o función. La existencia de grupos de procesos facilita el envío de señales a todos los miembros del grupo, lo cual resulta de gran utilidad en ciertas aplicaciones. e Ejemplo 2. 12 La ejecución de un comando desde la línea de órdenes de un intérprete de comandos de un terminal puede producir la creación de uno o varios procesos, todos ellos pertenecientes al mismo grupo. Recuérdese que en el contexto de un intérprete de comandos a dicho grupo de procesos se le denomina trabajo o tarea. Si un proceso de un trabaj o en segundo plano intenta leer una entrada del terminal el núcleo envía una señal S IGTT IN al grupo al que pertenece el proceso. Mientras que si intenta escribir en el terminal el núcleo envía al grupo una señal S I GTTOU. La acción por defecto asociada a la recepción de estas señales (ver Tabla 2. 1 ) es parar los procesos. Conviene señalar que algunos intérpretes de comandos hacen caso omiso de la acción por defecto asociada a la señal S I GTTOU y permiten que un proceso de un trabaj o en segundo plano pueda escribir en el terminal. Por otra parte, si un usuario pulsa las teclas [ c ontro l ] + [ z l de un terminal se produce una interrupción hardware que al ser atendida por el núcleo hace que éste, entre otras acciones, envíe al grupo de procesos que forman el trabajo en primer plano una señal S IGTSTP cuya acción por defecto es parar los procesos . • Sesiones Los SOBUNIX modernos soportan la abstracción de sesión. Una sesión está formada por uno o va­ rios grupos de procesos asociados a un determinado terminal de control. Cada sesión tiene asociado un número entero positivo que la identifica de manera univoca. A dicho identificador se le conoce general­ mente con el nombre de identificador de sesión (Session IDentifier, SID). El SID de una sesión coincide con el PGID del proceso o grupo de procesos que ha iniciado la conexión al terminal. A dicho proceso o grupo de procesos se le denomina líder de la sesión. Por ejemplo, cuando un usuario accede a un SOBUNIX a través de un programa de login se crea una sesión cuyo líder es el programa de login. También cuando un usuario abre un terminal emulado se crea una sesión cuyo líder es el intérprete de comandos del terminal. Luego un determinado proceso pertenece a un grupo de procesos y a una determinada sesión, es decir, tiene asociado un PGID y un SID. Cuando se crea un proceso éste hereda el PGID y el SID de su proceso padre. Un proceso puede crear una nueva sesión usando la llamada al sistema s e t s i d. Para conocer el SID asociado a un proceso se puede usar la llamada al sistema ge t s i d. También se puede usar el comando ps - j (ver sección 2 . 3 . 6). Conviene saber que cuando se trabaja con la abstracción de sesión, un proceso solo puede cambiar de grupo si el grupo origen y el grupo destino pertenecen a la misma sesión. SOBUNIX: implementación y control de procesos multihilos 81 Por otra parte, el uso de sesiones facilita el envío de señales a todos los grupos de procesos pertene­ cientes a una misma sesión. 2.3.6. El sistema de archivos procfs El sistema de archivos de procesos (process file system, procfs) es un pseudosistema de archivos existente en algunos SOBUNIX que permite acceder a la información que mantiene el núcleo sobre los procesos. Dicha información se almacena en archivos y se organiza en directorios. El término pseu­ dosistema se aplica sobre procfs en el sentido de que no se implementa en una partición de disco en memoria secundaria, como un sistema de archivos ordinario, sino en el espacio de direcciones del núcleo en memoria principal. La implementación y la estructura del sistema procfs depende de cada SOBUNIX. Usualmente este sistema se suele montar en el directorio /pro c . Dentro de este directorio existen, entre otros directorios, un directorio por cada proceso existente en el sistema. Cada directorio asociado a un proceso lleva por nombre el PID del proceso al que está asociado y contiene diferentes archivos y directorios con infor­ mación sobre el proceso. Para conocer la información contenida en los archivos y directorios del sistema procfs soportado por un determinado SOBUNIX se recomienda consultar la página correspondiente del manual de ayuda: man proc e Ejemplo 2. 13 En la implementación que realiza Solaris del sistema procfs dentro del directorio asociado a un proceso se aloj an, entre otros los siguientes archivos: • / proc / P I D / map . Información sobre el espacio de direcciones virtuales de un proceso. Esta in­ formación puede ser consultada usando el comando pmap . • /proc / P I D / c red. Contiene los credenciales del proceso. • /proc / P I D / p s i n f o . Contiene la información asociada a un proceso que se muestra con el co­ mando p s . • /proc 1 P I D / lps i n f o . Contiene l a información asociada a los procesos ligeros d e u n proceso que se muestra con el comando p s . • /proc / P I D / s t a tu s . Contiene diferente información sobre e l proceso: e l estado, PID, PGID, SID, etc. • /proc / P I D / u sage. Contiene información sobre el uso de los recursos del computador que reali­ za el proceso. Ampliación de sistemas operativos 82 Asimismo, cada directorio /proc / P I D / contiene, entre otros directorios, un directorio lwp dentro del cual existe un directorio por cada proceso ligero asociado al proceso existente en el sistema. Cada di­ rectorio asociado a un proceso ligero lleva por nombre el identificador numérico del proceso ligero (LightWeight ?rocess IDentifier, LWPID) al que está asociado y aloja, entre otros, los siguientes archi­ vos : • /proc / P I D / lwp / LWP I D / l wp s t a t u s . Contiene, entre otros datos, el estado del proceso ligero. • /proc / P I D / lwp / LWP I D / lwp s i n f o . Contiene la información asociada al proceso ligero que se muestra con el comando p s . • e Ejemplo 2. 14 Supóngase que en un sistema Solaris se está ejecutando un intérprete de comandos bash que tiene asociado un proceso con PID=730. Si se escribe la orden l s - F /proc en la salida estándar, por defecto la pantalla, aparecerá un listado con todos los directorios contenidos en el directorio /proc, en el cual está montado el sistema procfs. Existe un directorio por cada proceso existente. Cada directorio lleva por nombre el PID del proceso al que está asociado. Si ahora se escribe la orden l s - l /proc / 7 3 0 en la pantalla aparecerá un listado detallado con todos los archivos y directorios contenidos en el direc­ torio /proc / 7 3 0 , que es el directorio asociado al intérprete de comandos. En dicho listado aparecen, entre otros, los archivos y directorios comentados en los párrafos anteriores: a s , c red, c t l , etc Si se escribe a continuación la orden l s - l /pro c / 7 3 0 / lwp en la pantalla aparecerá un listado detallado con todos los archivos y directorios contenidos en el direc­ torio /proc / 7 3 0 / lwp, que es el directorio que contiene información sobre los procesos ligeros en que se descompone el intérprete de comandos. Existe un directorio por cada proceso ligero existente. Cada directorio lleva por nombre el LWPID del proceso ligero al que está asociado. En el caso del intérprete de comandos solo tiene un único proceso ligero cuyo LWPID = l . Luego existe un único directorio 1 dentro de /pro c / 7 3 0 / lwp . Finalmente si se escribe la orden l s - l /proc / 7 3 0 / lwp / 1 SOBUNIX : implementación y control de procesos multihilos 83 en la pantalla aparecerá un listado detallado con todos los archivos y directorios contenidos en el direc­ torio /proc / 7 3 0 / lwp / 1 , que es el directorio asociado al proceso ligero LWPID= l del intérprete de comandos. En dicho listado aparecen, entre otros, los archivos y directorios comentados en los párrafos anteriores: lwp c t l , lwp i n f o y lwp s tatus . • Existen diferentes comandos que permiten extraer la información almacenada en procfs, como por ejemplo: • ps. Este comando muestra un listado con información sobre los procesos existentes en el sistema. La información que se muestra depende de cada SOBUNIX y de las opciones especificadas en el comando. • pr s t a t . Este comando muestra un listado con información sobre el uso de los recursos que están realizando los procesos existentes en el sistema. La información que se muestra se actualiza cada cinco segundos. Otros comandos con una funcionalidad similar a prs t a t son top, mp s t a t y vms t a t . e Ejemplo 2. 15 S i se escribe en un intérprete de comandos bash de Solaris la orden ps en la pantalla aparecería una salida semej ante a la siguiente: P I D TTY 1 3 2 0 pts / 3 1 1 5 9 pts / 3 T IME CMD 0 : 0 0 ps 0 : 0 0 bash Se trata de un listado que muestra información sobre los procesos asociados al intérprete de comandos: el propio intérprete y el creado para ejecutar el comando ps. La información que se muestra de cada proceso es la siguiente: PID, el terminal de control asociado TTY, el tiempo T IME de uso del procesador y el comando CMD cuya ejecución ha generado el proceso. Se puede configurar la información que se desea que aparezca por pantalla usando las opciones dispo­ nibles del comando. Para conocer las informaciones que se pueden mostrar y cómo mostrarlas se debe consultar la página asociada al comando en el manual de ayuda. Por ejemplo, si se ahora se escribe la orden ps -Al la opción -A hace que se muestren todos los procesos existentes en el sistema. Mientras que la opción - 1 hace que se muestre un listado con más información, es decir, un mayor número de columnas. Para incluir información sobre los procesos ligeros existentes en cada proceso se debe usar la opción - L . Así la orden anterior se debería escribir de la siguiente forma: 84 Ampliación de sistemas operativos ps - AlL Para incluir la información sobre el PGID y el SID de cada proceso se debe usar la opción - j . • 2.4. Resumen Los SOBUNIX modernos usan hilos del núcleo como unidad básica de planificación y ejecución. Además también pueden soportar procesos ligeros, que son hilos de usuario cuya gestión es realizada por el núcleo del sistema operativo. Un proceso ligero puede estar asociado a uno o varios hilos de usuario. Algunos SOBUNIX utilizan un modelo de proceso multihilo con un mayor número de hilos de usuario que procesos ligeros. Otros utilizan un modelo donde cada proceso ligero está asociado a un único hilo de usuario. En los SOBUNIX la creación de un proceso se realiza generalmente invocando a la llamada al sistema f o rk. Al proceso que invoca a f o rk se le denomina proceso padre y al nuevo proceso que se crea se le denomina proceso hijo. El proceso hijo recién creado es un copia prácticamente idéntica del proceso padre y comparte el acceso a todos sus recursos. La única diferencia entre el proceso hijo recién creado y el proceso padre se encuentra en algunos campos de su estructura proc, es decir, de su entrada de la tabla de procesos. En los SOBUNIX un proceso puede invocar a otro programa haciendo uso de la llamada al sistema exe c . Esta llamada al sistema básicamente sustituye las regiones del espacio de direcciones del proceso invocador por las regiones del programa invocado. Al finalizar la llamada al sistema se comienza a ejecutar en el proceso invocador el programa invocado. En los SOBUNIX si un proceso desea terminar su ejecución debe invocar explícitamente a la llamada al sistema exi t. El sistema operativo también procede de forma implícita a la terminación de un proceso después de ejecutar la última instrucción del proceso. Asimismo la terminación de un proceso también se puede producir si el proceso recibe una señal cuyo tratamiento especifique la finalización del proceso. Las señales son un mecanismo del núcleo para la notificación de eventos a procesos. Cada evento notificable tiene asociado una determinada señal. A su vez cada señal tiene asignado un número entero positivo pequeño que la identifica de manera univoca. Además tiene asociada una constante simbólica que comienza con S I G y va seguida por una abreviatura o una palabra que resume el evento al que está asociada. Cada SOBUNIX establece el número de señales que soporta, así como el número entero y la constante simbólica que le asigna a cada una. En los SOBUNIX los procesos son organizados en grupos de procesos. Asimismo los SOBUNIX modernos soportan la abstracción de sesión. Una sesión está formada por uno o varios grupos de procesos que pueden estar asociados a un determinado terminal de control. El sistema de archivos de procesos (procfs) es un pseudosistema de archivos existente en algunos SOBUNIX que permite acceder a la información que mantiene el núcleo sobre los procesos. Dicha información se almacena en archivos y se organiza en directorios. SOBUNIX: implementación y control de procesos multihilos 2.5. 85 Lecturas recomendadas Si se desea obtener una explicación adicional de los contenidos tratados en este capítulo se pueden consultar, por ejemplo: los capítulos 2, 3, 4 y 9 de [Vahalia, 1 995] , el capítulo 10 de [Tanenbaum, 2009] , el capítulo 2 de [McDougall y Mauro, 2006], y los capítulos 5, 6 y 7 de [Márquez, 2004] . 2.6. Autoevaluación 2.1. Definir y comentar las características de los siguientes elementos: a) Hilos del núcleo. b) Hilos de usuario. e) Procesos ligeros. (Respuesta en sección 2.2. 1 ) 2.2. Explicar razonadamente e l modelo de proceso multihilo utilizado en Solaris. (Respuesta en sección 2.2. 1 ) 2.3. ¿Qué e s l a tabla d e procesos? Enumere l a información que contiene una de sus entradas. (Respuesta en sección 2.2.2) 2.4. Explicar razonadamente qué información es necesario mantener por cada hilo de usuario de un proceso. ¿Quién gestiona dicha información? ¿Dónde se almacena? (Respuesta en sección 2.2.2) 2.5. Explicar razonadamente qué información es necesario mantener por cada proceso ligero. ¿Quién gestiona dicha información? ¿Dónde se almacena? (Respuesta en sección 2.2.2) 2.6. Explicar razonadamente qué información es necesario mantener por cada hilo del núcleo. ¿Quién gestiona dicha información? ¿Dónde se almacena? (Respuesta en sección 2.2.2) 2.7. Explicar razonadamente la relación existente entre procesos, procesos ligeros e hilos del núcleo. (Respuesta en sección 2.2.2) 2.8. Explicar razonadamente la función de las siguientes estructuras de datos en Solaris: pro c , klwp, kthread, ulwp y uberda ta. (Respuesta en sección 2 .2.2) 2.9. Dibujar el diagrama de transición de estados de los hilos del núcleo en Solaris. (Respuesta en sección 2.2.2) 2.10. Explicar para qué se utiliza la llamada al sistema f o rk. ¿Qué valor devuelve al proceso padre si se ejecuta con éxito? ¿ Y al proceso hijo? (Respuesta en sección 2. 3 . 1 ) 2. 11. Explicar l a relación existente entre el espacio de direcciones virtuales de un proceso padre y su proceso hijo. (Respuesta en sección 2. 3 . 1 ) 2. 12. Enumerar las principales acciones que realiza l a función del núcleo que s e encarga de tratar la llamada al sistema f o rk (Respuesta en sección 2. 3 . 1 ) 2.13. Explicar para qué s e utiliza l a llamada al sistema exe c . (Respuesta en sección 2 . 3 . 1 ) 86 Ampliación de sistemas operativos 2.14. ¿Qué tres tipos de argumentos de entrada aceptan las funciones de envo'ltura que invocan a la llamada al sistema exe c ? (Respuesta en sección 2.3 . 1 ) 2.15. Enumerar las principales acciones que realiza l a función del núcleo que s e encarga de tratar la llamada al sistema exec (Respuesta en sección 2.3 . 1 ) 2. 16. Explicar l a utilidad d e l a llamada al sistema vfork (Respuesta e n sección 2. 3 . 1 ) 2.17. Explicar razonadamente las consideraciones a realizar sobre el diseño de fork en procesos multi­ hilos. (Respuesta en sección 2 . 3 . 1 ) 2.18. Explicar para qué s e utiliza l a llamada al sistema exi t . Comentar su sintaxis. (Respuesta e n sección 2 . 3 . 2) 2.19. Enumerar las principales acciones que realiza la función del núcleo que se encarga de tratar la llamada al sistema exi t (Respuesta en sección 2.3 .2) 2.20. ¿Cuál es el PID del proceso inicial o proceso ini t ? ¿ Cuándo se crea este proceso? ¿Cuál es su principal tarea? (Respuesta en sección 2.3.2) 2.21. Explicar para qué se utiliza la llamada al sistema wa i t . Comentar su sintaxis. (Respuesta en sección 2 . 3 .2) 2.22. ¿Qué información mantiene el núcleo si un proceso se encuentra en el estado zombi? (Respuesta en sección 2 . 3 .2) 2.23. ¿Qué son las señales? ¿Cómo se identifican? (Respuesta en sección 2.3.3) 2.24. Enumerar las principales fuentes de envío de señales. (Respuesta en sección 2.3.3) 2.25. Indicar los eventos que producen el envío de las siguientes señales y su acción por defecto asociada: S IGSEGV, S I GALRM, S IGXC PU, S IGINT, S I GKILL, S I GTTIN, S IGTTOU, S I GCHLD, S IGSTOP, S I GCONT, S I GTERM, S IGSTP, S I GUSRl y S I GUSR2 . (Respuesta en sección 2.3.3) 2.26. ¿Qué llamadas al sistema permiten enviar una señal a un proceso o grupo de procesos? (Respuesta en sección 2 . 3 . 3 ) 2.27. ¿Cuándo se considera que una señal ha sido recibida o entregada? (Respuesta e n sección 2 . 3 . 3 ) 2.28. Enumerar y comentar brevemente las posibles acciones que se pueden realizar ante la recepción de una señal. (Respuesta en sección 2 . 3 . 3 ) 2.29. Enumerar qué aspectos puede configurar un proceso ante la recepción de una señal. ¿Qué llamadas al sistema se pueden utilizar para configurar dichos aspectos? (Respuesta en sección 2 . 3 . 3 ) 2.30. ¿Para qué se utiliza la llamada al sistema s i gna l ? Explicar su sintaxis. (Respuesta en sección 2.3.3) SOBUNIX: implementación y control de procesos multihilos 87 2.31. ¿Para qué se utiliza la llamada al sistema ki 1 1 ? Explicar su sintaxis. Comentar el uso del comando homónimo. (Respuesta en sección 2.3.3) 2.32. ¿Para qué se utiliza la llamada al sistema paus e ? (Respuesta en sección 2 . 3 . 3 ) 2.33. ¿Cuándo se dice que una señal está pendiente? (Respuesta e n sección 2.3.3) 2.34. Explicar las tareas que realizan las siguientes rutinas del núcleo: i s s i g ( ) , p s i g ( ) y s ends i g ( ) . (Respuesta en sección 2 . 3 . 3 ) 2.35. Explicar razonadamente las consideraciones a realizar sobre la implementación de las señales en procesos multihilos. (Respuesta en sección 2 . 3 . 3 ) 2.36. ¿Qué tareas permiten realizar las funciones de la librería Pthreads? (Respuesta en sección 2.3 .4) 2.37. ¿Qué información mantiene la librería Pthreads por cada hilo de usuario? (Respuesta en sección 2.3 .4) 2.38. Enumerar e indicar la utilidad de cinco funciones para el control de los hilos de usuario de la librería Pthreads. (Respuesta en sección 2.3 .4) 2.39. ¿Qué identificador tiene asociado cada grupo de procesos en SOBUNIX? ¿A qué proceso se le denomina líder del grupo? (Respuesta en sección 2 . 3 . 5 ) 2.40. Explicar la utilidad de las siguientes llamadas al sistema: getpg i d y s e tpgrp . (Respuesta en sección 2 . 3 . 5 ) 2.41. ¿Que elementos conforman una sesión? ¿Qué identificador tiene asociado cada sesión? ¿A qué proceso o grupo de procesos se le denomina líder de la sesión? (Respuesta en sección 2 . 3 . 5 ) 2.42. Explicar la utilidad de las siguientes llamadas al sistema: s e t s i d y ge t s i d. (Respuesta en sección 2 . 3 . 5 ) 2.43. ¿Qué comando se puede utilizar para visualizar el identificador de grupo y el identificador de sesión de un proceso? (Respuesta en sección 2 . 3 . 5 ) 2.44. ¿Para qué se utiliza el sistema de archivos procfs? (Respuesta e n sección 2 . 3 . 6) 2.45. Explicar la estructura de un sistema de archivos procfs. (Respuesta en sección 2 . 3 . 6) 2.46. Enumerar dos comandos que permiten extraer la información almacenada en procfs y comentar qué información extraen. (Respuesta en sección 2.3 .6) 88 Ampliación de sistemas operativos ma i n ( i n t argc , char * argv [ ] ) { i n t a , b= O , c , d ; if ( argc ! = 2 ) exi t ( 3 ) ; else a = a t o i ( argv [ l ] ) ; wh i l e ( f ork ( ) = = O & & b ! = a ) { p r i n t f ( " \ nMen s a j e l [ %d ] \ n " , getpi d ( ) ) ; b=b+ l ; c = wa i t ( &d ) ; p r i n t f ( " \ nMen s a j e 2 [ %d] = %d \ n " , Figura 2.1 1 2.7. - g e t p i d ( ) , g e tpp i d ( ) ) ; Código C del programa j O 9 del Ejercicio 2. 1 Ejercicios 2. 1. Supóngase que al invocar el programa j 0 9 (ver Figura 2. 1 1 ) desde un intérprete de comandos se crea un proceso con PID= 1 1 20. Supóngase además que la asignación de los PIDs de los procesos hijos, si se llegaran a crear, se realiza mediante la expresión PIDh ij o = PIDpadre + h, donde h = 1 , 2, 3 , . . . hace referencia al orden de creación del proceso hijo, es decir, h = 1 es el primer hijo creado, h = 2 es el segundo hij o creado, etc. Suponer además que el intérprete de comandos desde donde se lanza j 0 9 tiene asignado PID= 1 000. a) Explicar razonadamente el funcionamiento de este programa si se invoca desde el intérprete de comandos mediante la orden: j o 9 4 b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando este programa en un SOBUNIX. 2.2. En la Figura 2. 1 2 se muestra el código C de los programas j 0 7 y ex2 . Supóngase que al invocar este programa desde la línea de ordenes de un intérprete de comandos se crea un proceso con PID=789 y que la asignación de los PIDs de los procesos hijos, si se llegaran a crear, se realizaría incrementando en una unidad el PID del proceso padre. a) Explicar razonadamente el funcionamiento de j 0 7 si se escriben consecutivamente las si­ guientes dos órdenes : 1 ) j 0 7 2) ki l l - S IGUSRl 7 9 0 b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando estos programas en un SOBUNIX. SOBUNIX: implementación y control de procesos multihilos 89 / * C Ó d i g o del programa j 0 7 * / # i nc lude < s i gn a l . h> vo i d func ( i nt x ) ; ma in ( ) { int u= 1 5 ; s i gnal ( S I GUS R l , func ) ; u=u %2 ; if ( fork ( ) = = 0 ) { execv ( " ex2 " , O ) ; u=u + 7 ; p r i n t f ( " \ n %f \ n " , u ) ; e l s e exi t ( l ) ; vo i d func ( i n t x ) p r i n t f ( " Men s a j e 1 " ) ; / * Có d i g o de l programa ex2 * / ma in ( ) { f l oat u ; pau s e ( ) ; p r i n t f ( " \ nu= %f \ n " , u ) ; Figura 2.12 - Código C de los programas j 0 7 y ex2 del Ejercicio 2.2 2.3. En la Figura 2. 1 3 se muestra el código C del programa so 8 . Supóngase que al invocar este progra­ ma desde la línea de ordenes de un intérprete de comandos se crea un proceso con PID= 1 035 y que la asignación de los PIDs de los procesos hijos, si se llegaran a crear, se realizaría incrementando en una unidad el PID del proceso padre. a) Explicar razonadamente el funcionamiento de s O 8 si se escriben consecutivamente las si­ guientes tres órdenes : 1) sOS & 2) k i l l - S I GUSRl 1 0 3 6 3) k i l l - S I GUSR2 1 0 3 6 b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando este programa en un SOBUNIX. 90 Ampliación de sistemas operativos # i n c l ude < s i gnal . h> vo i d f 1 ( i n t s i g ) ; ma i n ( ) { int a , b ; s i gnal ( S I GUSR1 , S I G_DFL ) ; i f ( fork ( ) = = 0 ) { s i gnal ( S I GUSR1 , S I G_I GN ) ; pau s e ( ) ; exi t ( 5 ) ; else a =wa i t ( &b ) ; pr i n t f ( " \ n %1 %1 \ n " , a , b ) ; vo i d f 1 ( i n t s i g ) p r i n t f ( " \ nMens a j e 1 \ n " ) ; Figura 2.13 - Código C del programa s O 8 del Ejercicio 2.3 # i nc l ude < s i gnal . h> ma in ( ) { int a , b ; if ( ( a = f o rk ( ) ) = = O ) wh i l e ( 1 ) { a=aA 1 Q ; b = ra i s e ( S I GCHLD ) ; p r i n t f ( " \ nMens a j e 1 [ %d ] : %1 : %1 \ a \ n " , g e tp i d ( ) , b , a ) ; s l e ep ( 4 ) ; s l e ep ( 1 6 ) ; ki l l ( a , S I GUSR 1 ) ; s l e ep ( 3 ) ; k i l l ( ge tpp i d ( ) , S I GUSR2 ) ; Figura 2.14 - Código C del programa m O 9 del Ejercicio 2.4 SOBUNIX: implementación y control de procesos multihilos 91 # i nc lude < s i gna l . h> vo i d f 1 ( i n t s i g ) ; ma in ( i nt a , char * b [ ] ) { l ong c , d ; s i gnal ( S I GUS R 1 , f 1 ) ; s i gnal ( S I GUSR2 , S I G_DFL ) ; d=atoi ( b [ 1 ] ) ; if ( a = = 2 & & d= = 1 ) c = s i g s e tma s k ( s i gma s k ( S IGUSR1 ) ) ; s i gb l o c k ( s i gma s k ( S IGUSR2 ) ) ; pau s e ( ) ; s i g s e tma s k ( c ) ; else pau s e ( ) ; exi t ( 1 ) ; v o i d f 1 ( i nt s i g ) p r i n t f ( " \ n \ n Texto 1 \ n " , s t r s i gna l ( s i g ) ) ; Figura 2.15 - Código C del programa s 0 9 R del Ejercicio 2.5 2.4. En la Figura 2. 1 4 se muestra el código C del programa roO 9. Supóngase que al invocar este progra­ ma desde la línea de ordenes de un intérprete de comandos se crea un proceso con PID=2045 y que la asignación de los PIDs de los procesos hijos, si se llegaran a crear, se realizaría incrementando en una unidad el PID del proceso padre. Suponer además que el intérprete de comandos desde donde se lanza roO 9 tiene asociado PID=2000. a) Explicar razonadamente el funcionamiento de roO 9 si se invoca desde el intérprete de coman­ dos mediante la orden: roO 9 b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando este programa en un SOBUNIX. 2.5. En la Figura 2. 1 5 se muestra el código C del programa s 0 9R. Supóngase que al invocar este programa desde la línea de ordenes de un intérprete de comandos se crea un proceso con PID=245 . a) Explicar razonadamente el funcionamiento de s 0 9 R si se escriben consecutivamente las si­ guientes tres órdenes : 92 Ampliación de sistemas operativos 1) s09R 1 & 2) ki l l - S IGUSRl 2 4 5 3 ) ki l l - S IGUSR2 2 4 5 b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando este programa en un SOBUNIX. 2.6. Considérese el programa prog 2 7 del Ejemplo 2. 1 1 cuyo código C se muestra en la Figura 2.9. Invocarlo en segundo plano desde la línea de ordenes del intérprete de comandos de un SOBUNIX y a continuación escribir la orden ps j . A partir de la información que se muestra en la pantalla contestar razonadamente a las siguientes preguntas : - a) ¿Cuál es el valor del SID ? ¿Cuál es el proceso líder de la sesión? b) ¿Cuántos grupos de procesos existen? ¿Cuáles son sus PGID? ¿Cuántos miembros tiene cada grupo? Capítulo 3 SOBUNIX: planificación, sincronización y mecanismos IPC Objetivos docentes Los objetivos docentes de este capítulo son los siguientes: • Conocer las características generales de la planificación de procesos multihilos en S OBUNIX. • Conocer los detalles de la implementación de la planificación de dos ejemplos ilustrativos de SO­ BUNIX: BSD 4.3 y S olaris. • Conocer las principales llamadas al sistema disponibles en los SOBUNIX para el control de la prioridad de un proceso. • Conocer los conceptos de núcleo expropiable, núcleo no expropiable y puntos de expropiación usados en SOBUNIX. • Conocer los principales mecanismos de sincronización usados por el núcleo de los S OBUNIX: cerrojos y semáforos . • Saber e n qué consisten las operaciones d e dormir y despertar u n hilo del núcleo. • Saber qué es un canal de espera y cómo se le asocia una cola de hilos dormidos . • Saber qué son y para qué se utilizan los turnstiles. • Conocer el funcionamiento y las llamadas al sistema asociadas a los principales mecanismos de co­ municación entre procesos disponibles en la mayoría de SOBUNIX: mecanismos IPC del System V (semáforos, colas de mensajes y memoria compartida), señales y tuberías. 93 94 Ampliación de sistemas operativos 3.1. Introducción Este capítulo se divide en tres partes. La primera parte está dedicada a describir la planificación de procesos multihilos en SOBUNIX. En primer lugar se comentan las características generales. A conti­ nuación se describen los detalles de la implementación de la planificación en BSD 4.3 y Solaris, que son dos ejemplos ilustrativos de SOBUNIX clásicos y modernos, respectivamente. Posteriormente se realizan diferentes consideraciones sobre la expropiación del núcleo en los SOBUNIX. La segunda parte de este capítulo está dedicada a describir los principales mecanismos de sincroni­ zación usados por el núcleo de un SOBUNIX: los cerrojos y los semáforos . También se describen las operaciones de dormir y despertar un hilo del núcleo, y la implementación de las colas de hilos dormidos. En la última parte de este capítulo se describen los mecanismos de comunicación entre procesos disponibles en los SOBUNIX. En primer lugar se explican los mecanismos IPC del System V: semáforos, colas de mensajes y memoria compartida. Estos mecanismos aunque introducidos en el System V han sido adoptados por todos los SOBUNIX. Por último se explica el uso de las señales y las tuberías, que son otros mecanismos IPC disponibles en los SOBUNIX. 3.2. Planificación de procesos multihilos en SOBUNIX 3.2.1 . Características generales Los SOBUNIX son sistemas operativos multiprogramados, es decir, soportan la existencia de múlti­ ples programas cargados parcial o totalmente en la memoria principal. Cada programa, de acuerdo con lo estudiado en el capítulo anterior, tiene asignado un proceso monohilo o un proceso multihilo. Además si el núcleo soporta la existencia de hilos del núcleo, cada proceso tendrá asociado uno o más hilos del núcleo. Puesto que el número de procesos en el estado preparado para ejecución suele ser mucho mayor que el número de procesadores existentes en un computador, el núcleo debe decidir qué procesos (o hilos del núcleo) pasarán a ser ejecutados en los procesadores disponibles. A la parte del núcleo de un sistema operativo encargada de esta tarea se le denomina planificador (scheduler). Nótese que si el núcleo del SOBUNIX considerado soporta hilos del núcleo entonces el planificador utiliza como unidad o entidad planificable a los hilos del núcleo. En caso contrario utilizará como unidad planificable a los procesos. El planificador también se encarga de realizar los cambios de contexto, es decir, guardar el contexto de la unidad planificable que va a ser desalojada de un procesador y cargar el contexto de la unidad planificable que ha sido planificada para ser ejecutada en dicho procesador. Además el planificador se encarga de la gestión de las diferentes estructuras de datos del núcleo necesarias para implementar la pla­ nificación, como por ejemplo las colas de unidades planificables en el estado preparado para ejecución. En la mayoría de los SOBUNIX, el planificador implementa una planificación global basada en múltiples colas de prioridad y realimentación. En este tipo de planificación, cada unidad planificable tiene asignada una determinada prioridad, que es un número entero positivo comprendido entre O y un cierto valor máximo Pmax · En algunos SOBUNIX el valor O corresponde a la máxima prioridad posible, mientras que en otros la máxima prioridad corresponde a Pmax. El planificador siempre intenta que se SOBUNIX : planificación, sincronización y mecanismos IPC 95 ejecute la unidad planificable de mayor prioridad, aunque esto no siempre es posible si el núcleo es no expropiable (ver sección 3 .2.3). El valor de la prioridad de una unidad planificable se establece en función de diferentes criterios: el modo de ejecución (modo usuario o en modo núcleo), el tipo de trabajo (trabaj o interactivo, trabajo por lotes, trabajo en tiempo real, trabajo del sistema operativo e interrupciones), el tiempo de procesador consumido, el tiempo de espera en las colas, etc. El valor de la prioridad puede variar o no con el tiempo de vida de la unidad planificable, es decir, la prioridad puede ser estática o dinámica. Por ejemplo, la prioridad de las unidades planificables asociadas a las interrupciones suele ser alta y estática. Mientras que la prioridad de las unidades planificables asociadas a trabajos por lotes suele ser baj a y dinámica. En función del valor de su prioridad cada unidad planificable en el estado preparado forma parte de una determinada cola de unidades planificables en el estado preparado o cola de prioridad. Las unidades planificables que poseen una prioridad dinámica pueden pasar durante su tiempo de vida por diferentes colas. El planificador establece una determinada planificación local para cada cola, como por ejemplo: planificación PIFO, planificación de tiempo compartido, planificación basada en prioridades, etc. Algunos SOBUNIX, como SVR4 o Solaris, para dotar de mayor flexibilidad al planificador definen clases de planificación cada una de los cuales tiene asociada un rango de valores de prioridad y un mecanismo de cálculo de la prioridad. Cada unidad planificable pertenece a una determinada clase de planificación. Normalmente se dispone de una clase de planificación por cada tipo de trabaj o soportado en el sistema: clase de trabajos en tiempo real, clase de trabajos de tiempo compartido o interactivos, etc. Las clases de planificación son manipuladas por el planificador usando dos posibles tipos de funciones : • Funciones independientes de la clase. El planificador utiliza éstas funciones para la realización de servicios que son comunes a todas las clases, como por ejemplo: el cambio de contexto y la manipulación de las colas de planificación. • Funciones dependientes de la clase. El planificador utiliza éstas funciones para la realización de servicios que dependen de la clase, como por ejemplo el cálculo de la prioridad. Cada clase sumi­ nistra su propia implementación de estas funciones . Los SOBUNIX disponen de algunas llamadas al sistema para modificar, con ciertas restricciones, el valor de la prioridad de un proceso o grupo de procesos, o para cambiar su clase de planificación. Por ejemplo las llamadas al sistema n i c e y pr i o cnt l se encuentran disponibles en algunos SOBUNIX para controlar la prioridad de un proceso. 3.2.2. Dos ejemplos ilustrativos de la planificación en SOBUNIX Aunque la planificación en los SOBUNIX presenta, en la mayoría de los casos, las características generales comentadas en la sección anterior, cada SOBUNIX implementa estas características de una determinada forma. En las siguientes secciones se describen a modo de ejemplo la planificación en dos SOBUNIX diferentes : BSD 4 . 3 1 ( 1 986) y Solaris 10 (2005). 1 L a planificación e n SVR3 e s muy similar a la planificación e n BSD 4 . 3 , únicamente existen pequeñas diferencias e n el nombre de algunas funciones y variables. 96 Ampliación de sistemas operativos Planificación en BSD 4.3 BSD 4.3 es un sistema operativo de núcleo no expropiable (ver sección 3 .2.3) que utiliza como unidad de planificación a los procesos, ya que el núcleo no soporta hilos del núcleo. Cada proceso tiene asignada una prioridad de ejecución que es un número entero positivo comprendido dentro del rango de valores [0, 1 27 ] , siendo O la prioridad más alta posible y 1 27 la prioridad más baja. La prioridad de un proceso ejecutándose en modo usuario solo puede encontrarse dentro del rango [50, 1 27 ] . Los valores comprendidos dentro del rango [0, 49] están reservados para la ejecución del núcleo. El valor de la prioridad de un proceso se almacena en el campo p_pr i de su estructura pro c . Recuér­ dese (ver sección 2.2.2) que una estructura proc implementaba la entrada de un proceso en la tabla de procesos del núcleo. También en esa misma estructura se mantiene el campo p_u s rpri donde se guarda el valor de la prioridad que se le debe asignar al proceso cuando termine de ejecutarse en modo núcleo y retome a modo usuario, por ejemplo al completarse una llamada al sistema. Mientras un proceso se está ejecutando en modo usuario el valor de la prioridad almacenado en p_pri coincide con el almacenado en p_u s rpr i . 2 E l núcleo invoca a través de u n callout una vez por segundo a l a función s chedcpu ( ) para recalcu­ lar el valor de la prioridad en modo usuario de cada proceso, el cual se almacena en p_u s rpr i . Además, la prioridad en modo usuario del proceso en ejecución es recalculada por el manejador de la interrupción del reloj cada cuatro tics de reloj . En ambos casos la prioridad en modo usuario se calcula usando la siguiente fórmula [Vahalia, 1 995] : . p_usrpn = PUSER + p cpu . 4 + 2·p_mce (3 . 1 ) En la expresión anterior: • PUSER es una constante que vale 50 y que hace referencia a la prioridad en modo usuario base, es decir, la prioridad más alta que puede tener un proceso ejecutándose en modo usuario. • p_cpu es un campo de la estructura proc que almacena una medida del uso de la CPU por parte del proceso. El valor máximo de este campo está limitado a 1 27. Cuando se crea un proceso su campo p_cpu se inicializa a O. Cada tic de reloj el manej ador de la interrupción de reloj incrementa en una unidad el valor del campo p_cpu del proceso actualmente en ejecución. Un proceso que acaba de usar la CPU tendrá un valor alto de p_cpu, luego el valor de p_u s rpri aumentará con lo que su prioridad será más pequeña. Por otra parte la rutina s c hedcpu ( ) , entre otras tareas, multiplica el valor del campo p_cpu de cada proceso por un factor de decadencia (decay factor) que se calcula mediante la siguiente expresión [Vahalia, 1 995] : decay = 2 · load_average 2·load_average + 1 (3.2) 2Los callouts son un mecanismo interno del núcleo para planificar la invocación de funciones de forma automática transcu­ rrido un cierto número de tics de reloj . SOBUNIX: planificación, sincronización y mecanismos IPC 97 En la expresión anterior l o ad_average es el número medio de procesos en el estado preparado en el último segundo. Como l oad_average es un número entero positivo entonces decay es un número menor que l . Luego la rutina s chedcpu ( ) disminuye el valor de p_cpu de cada proceso. Nótese que cuanto más tiempo pasa un proceso en espera de usar el procesador, el valor de su p_cpu se hace mas pequeño debido al factor de decadencia. En consecuencia su valor p_u s rpri se va reduciendo con lo que la prioridad del proceso en modo usuario aumenta. • p_n i c e . Es un campo de la estructura proc que almacena un valor entero positivo comprendido dentro del rango [0, 39] denominado valor amable (nice value). Por defecto, el valor amable vale 20. Nótese que cuanto más pequeño sea el valor amable mayor será la prioridad en modo usuario del proceso. El valor amable de un proceso puede ser configurado mediante el comando n i c e el cual hace uso de la llamada al sistema homónima. Los usuarios pueden usar este comando para aumentar el valor amable de un proceso y en consecuencia reducir su prioridad. De esta forma estarían dejando pasar antes al procesador a otros procesos. Esto puede ser deseable por ejemplo para los procesos que se ejecuten en segundo plano asociados a trabajos por lotes. El comando ni ce también permite decrementar el valor amable para aumentar la prioridad de un proceso, sin embargo esta acción solo la puede realizar el superusuario. En modo núcleo la prioridad de un proceso no se recalcula en función del tiempo sino que se asigna de forma directa. Un proceso cuando invoca a una llamada al sistema y pasa a ejecutarse en modo núcleo continua ejecutándose en principio con la misma prioridad que tenía en modo usuario hasta que entra en el estado dormido (bloqueado) en espera de que se produzca algún evento. Cuando el núcleo despierta a un proceso en el estado dormido porque se ha producido el evento por el que se había bloqueado, el núcleo aumenta temporalmente su prioridad asignándole un determinado valor dentro del rango [0, 49] en función del evento por el que había entrado en el estado dormido. En general la prioridad asignada en modo núcleo al despertar un proceso es mayor en función de la importancia que suponga para el resto del sistema la atención rápida de dicho evento. Por ejemplo, si un proceso esperaba por la recepción de una señal le asigna una prioridad de 40, si esperaba por la terminación de un proceso hijo le asigna una prioridad de 30, si esperaba por la terminación de una operación de E/S en el disco le asigna una prioridad de 20, si esperaba por el desbloqueo de un nodo-i le asigna una prioridad de 1 O, etc. En BSD 4.3 existen 32 colas de procesos en el estado preparado para ejecución (ver Figura 3 . 1 ) . Cada cola s e implementa como una lista doblemente enlazada d e estructuras pro c . Cada cola i (i = O, 1 , 2, . . . , 3 1 ) está asociada al rango de prioridades [4· i, 4·i + 3 ] formado por cuatro valores contiguos de prioridad. Así la cola O está asociada al rango de prioridades [0, 3], la cola 1 al rango [4, 7], etc. Solamente pueden formar parte de una determinada cola aquellos procesos en el estado preparado para ejecución cuya prioridad esté dentro del rango de prioridades asociada a dicha cola. Siempre se planifica para ejecución el proceso situado más cerca de la cabecera de la cola i asociada al rango de prioridades más altas. Luego un proceso ubicado en una cola i se ejecutaría antes que uno situado en la cola i + l . Para determinar cuál es la cola i asociada al rango de prioridades más altas que no está vacía el núcleo utiliza la variable global whi chqs . Esta variable es una máscara de 32 bits. Cada bit está asociado a una 98 Ampliación de sistemas operativos cola. El bit más significativo de la máscara (el situado más a la izquierda) se toma como bit O y se asocia a la cola O. Mientras que el bit menos significativo (el situado más a la derecha) o bit 3 1 está asociado a la cola 3 1 . En general el bit i de la máscara se asocia a la cola i. Un bit i se configura a O si la cola i a la que está asociado está vacía, en caso contrario se configura a l . El planificador examina la máscara de bits de la variable whi chqs comenzando por el bit O en busca de un 1 , es decir, en busca de la cola asociada al rango de prioridades más altas que no esté vacía. Si en la cola i asociada al rango de mayor prioridad existen varios procesos, entonces se aplica sobre dicha cola un algoritmo de turno rotatorio con un cuanto fijo de 1 00 ms. En el caso de un proceso ejecutándose en modo núcleo, como el núcleo es no expropiable, su cuanto es infinito. El núcleo cuando se termina el cuanto invoca mediante un callout a la rutina r oundrobin ( ) para planificar al siguiente proceso de la cola i. Nótese que si en la cola i asociada al rango de mayor prioridad existe un único proceso entonces éste se ejecutará hasta que se complete, independientemente de que expire su cuanto. Si se está ejecutando un proceso A procedente de la cola i y entra un proceso B en una cola j con j < i, es decir, deja de estar vacía una cola asociada a un rango de prioridades más alto que el proceso actualmente en ejecución, entonces : • Si el proceso A se está ejecutando en modo usuario se le expropia el uso del procesador aunque no haya terminado su cuanto y se le cede al proceso B . • Si el proceso A s e está ejecutando en modo núcleo, como el núcleo es no expropiable n o s e l e puede expropiar al proceso A el uso del procesador. El planificador activa un indicador denominado runrun para notificar que existe un proceso en una cola j asociada a un rango de prioridades más alto que la prioridad del proceso A. Cuando el proceso vaya a retomar a modo usuario, el planificador comprobará si runrun está activado y en caso afirmativo realizará un cambio de proceso. La rutina s chedcpu ( ) invocada cada segundo a través de un callout por el núcleo, aparte de las tareas comentadas en los párrafos anteriores, también se encarga de colocar a lo procesos en las colas adecuadas de acuerdo con su nuevo valor de prioridad. e Ejemplo 3.1 Supóngase que el estado de las colas de procesos preparados de un sistema BSD 4.3 es el que se muestra en la Figura 3 . 1 . Se observa que no existe ningún proceso en la cola n° O ni en la cola no i. Por el contrario hay dos procesos en la cola n° 1 y cuatro procesos en la cola no 3 1 . Para determinar cuál será el próximo proceso que va a ser planificado el núcleo examina la variable whi chqs de izquierda a derecha en busca de un 1 , es decir, busca la cola asociada al rango de prioridades más altas que no esté vacía. Así, en primer lugar examina el bit O, este vale O ya que la cola n° O está vacía. A continuación examina el bit l . Como la cola 1 contiene dos procesos el bit 1 tiene el valor l . Por lo tanto se selecciona para ser planificado el proceso situado más cerca de la cabecera de la cola n° l . • La planificación usada en B S D 4.3 favorece a los procesos limitados por E/S frente a los procesos limitados por la CPU. En general resulta adecuada para sistemas de tiempo compartido con una carga SOBUNIX: planificación, sincronización y mecanismos IPC 99 Sentido de análisis 31 1 Prioridad más alta wh i chqs Cola no O Prioridad [0, 3 ] Cola n o 1 Prioridad [ 4, 7] Cola n° i Prioridad [4i, 4i + 3] Prioridad más baja Cola no 3 1 Prioridad [ 1 24, 1 27] Figura 3.1 - Posible estado de las colas de procesos preparados y de la estructura whi chqs de un sistema BSD 4.3 formada por trabajos interactivos y trabajos por lotes. Presenta sin embargo los siguientes inconvenientes [Vahalia, 1 995] : • Calcular la prioridad en modo usuario de todos los procesos una vez por segundo produce una gran sobrecarga si el número de procesos es elevado. • Puede presentar el problema de la inversión de prioridad debido a la no expropiabilidad del núcleo, es decir, que un hilo de mayor prioridad tenga que esperar a usar el procesador porque lo está usando una hilo de menor prioridad. • No garantiza un tiempo de respuesta determinado por lo que no sirve para aplicaciones de tiempo real. Planificación en Solaris Solaris es un sistema operativo de núcleo expropiable (ver sección 3 .2.3) que utiliza como unidad de planificación a los hilos del núcleo. Cada hilo del núcleo tiene asignado una prioridad global y una clase de planificación. Esta información se almacena en la estructura kthread que el núcleo mantiene por cada hilo del núcleo. En esta estructura existen, entre otros (ver sección 2.2.2), los siguientes campos con información que se utiliza para la planificación del hilo: 100 Ampliación de sistemas operativos Prioridad más alta 1 69 1 1 , - � Interrupciones (*) 1 60 1 59 1 1 1 1 1 1 - Clase RT 1 00 99 1 1 Clase SYS � 60 59 Clase FX Prioridad más baj a 1 1 1 1 1 - Clases F S S , IA y TS o (*) Las interrupciones ocupan el rango [ 1 00, 1 09] si la clase RT no está cargada Figura 3.2 - Prioridades de ejecución en Solaris • t__pr i . Contiene la prioridad global de ejecución del hilo que es un número entero positivo com­ prendido dentro del rango de valores [0, 1 69] , siendo 1 69 la prioridad más alta posible y O la prioridad más baja. Las diez prioridades más altas [ 1 60, 1 69] están reservadas para la ejecución de los hilos del núcleo asociadas al tratamiento de las interrupciones. Las prioridades restantes comprendidas en el rango [0, 1 59] se reparten entre las clases de planificación definidas en Solaris (ver Figura 3 .2). • t_epr i . Contiene la prioridad heredada del hilo que se utiliza temporalmente como prioridad global cuando se aplica la técnica del traspaso o herencia de prioridades para evitar el problema de la inversión de prioridades. • t_c i d. Contiene el identificador de la clase de planificación a la que pertenece el hilo. El identi­ ficador puede tomar los siguientes valores: - RT (Real Time, tiempo real). Tiene asignada el rango de prioridades [ 1 00, 1 59] . Esta clase está definida para la planificación de los hilos del núcleo asociados a trabajos de tiempo real. - SYS (SYStem, sistema). Tiene asignada el rango de prioridades [60, 99] . Esta clase está definida para la planificación de los hilos del núcleo asociados a tareas del núcleo. SOBUNIX : planificación, sincronización y mecanismos IPC 101 - FX (FiXed, fija). Tiene asignada el rango de prioridades [0, 60] . Esta clase está definida para la planificación de los hilos del núcleo asociados a trabajos cuya ejecución se desea que tenga asignada una prioridad global fij a que no varíe durante el tiempo de vida del trabajo. Nótese que un hilo perteneciente a esta clase puede ejecutarse con el valor de prioridad 60 de la clase SYS, aunque durante un tiempo corto (cuanto pequeño). - F S S (Fair Share, reparto justo). Tiene asignada el rango de prioridades [0, 59] . Esta clase está definida para la planificación de los hilos del núcleo asociados a trabajos cuya ejecución se desea que tenga asignada el mismo tiempo de uso del procesador. En esta clase los ciclos de procesador son divididos en unas unidades denominadas cuotas (shares) que son distribuidas de manera equitativa entre todos los hilos pertenecientes a la clase. - TS (Time Share, tiempo compartido). Tiene asignada el rango de prioridades [0, 59] . Esta clase está definida para la planificación de los hilos del núcleo asociados a trabajos de tiempo compartido, como por ejemplo los intérpretes de comandos. - IA (lnterActive, interactiva). Tiene asignada el rango de prioridades [0, 59] . Esta clase está definida para la planificación de los hilos del núcleo asociados a trabajos interactivos de interfaces de usuario gráficas (ventanas, menús, etc). Cuando se arranca el sistema, el núcleo carga las clases SYS y T S . El resto de clases son cargadas dinámicamente según se van necesitando o cuando se usan determinados comandos. Si la clase RT no se carga entonces las prioridades quedan restringidas al rango [0, 1 09], siendo las prioridades [ 1 00, 1 09] asociadas al tratamiento de las interrupciones. • t_c ldata. Puntero a una estructura xxproc donde xx puede ser t s , i a , f s s , fx o r t , cuya definición puede encontrarse en el archivo xx . h. Esta estructura contiene la información necesa­ ria para poder calcular la prioridad del hilo del núcleo de acuerdo con las especificaciones de la planificación propias de cada clase. Un hilo del núcleo perteneciente a la clase SYS no requiere de esta estructura ya que se puede ejecutar todo el tiempo que necesite (cuanto infinito), hasta que se complete o sea expropiado. • t_c l func s . Puntero a una estructura c l a s s func s que contiene un vector de funciones depen­ dientes de la clase de planificación para la asignación del cuanto, el cálculo de la prioridad, el tratamiento del tic de reloj , etc. El núcleo mantiene una tabla global de clases de planificación implementada como un array de estructuras s c l a s s_t . Cada estructura s c l a s s contiene, entre otras, las siguientes informaciones sobre una determinada clase: el nombre de la clase ( c l_narne ) , un puntero a la función de inicialización de la clase ( c l_ini t ) y un puntero ( c l_func s ) a una estructura c l a s s func s_t que contiene un vector de funciones dependientes de la clase. Además el núcleo mantiene una lista doblemente enlazada por cada clase de planificación con las estructuras xxproc asociadas a los hilos del núcleo pertenecientes a una determinada clase. En la Figura 3 . 3 se muestra a modo de resumen la relación entre las estructuras descritas. 102 Ampliación de sistemas operativos Tabla global de clases de planificación el name =RT selass e l init rt init ( ) e l f une s e l name = SYS selass el init el fune s sys_i n i t ( ) e l a s s fune s e l name = T S selass e l init t s init ( ) e l fune s [ kth] =kthread Figura 3.3 - Estructuras de datos asociadas a las clases de planificación en Solaris Un hilo del núcleo en el estado preparado (estado TS_RUN) es colocado en función de su prioridad global en una determinada cola de un determinado conjunto de colas de prioridad o colas de hilos en el estado preparado. Existe un conj unto de colas de prioridad por cada procesado¡-3 . Además en cada conjunto existe una cola por cada valor de prioridad global disponible en función de las clases cargadas. Por ejemplo, si estuvieran cargadas todas las clases de planificación en un conjunto de colas asociado a un determinado procesador existirían 1 10 colas de prioridad: 1 0 colas para el rango de valores de prioridad [ 1 60, 1 69] y 1 00 colas para el rango de prioridad [0, 99] . Los hilos del núcleo en el estado preparado pertenecientes a la clase de planificación RT son colo­ cados en un conjunto de colas de prioridad especial denominado conjunto de colas de expropiación del 3En los computadores actuales un chip de procesador consta de M núcleos (cores) de procesanúento, además cada núcleo tiene la capacidad de ejecutar N hilos del núcleo simultáneamente. Luego el número total de procesadores existente por chip es M·N. SOBUNIX: planificación, sincronización y mecanismos IPC 103 núcleo. Este conjunto consta de 60 colas de prioridad, una por cada valor de prioridad dentro del rango [ 1 00, 1 59] asociado a la clase RT . Los hilos pertenecientes a este conjunto de colas pueden ser ejecutados, por defecto, en cualquier procesador. Este trato preferente que reciben los hilos del núcleo pertenecientes 4 a la clase RT es para conseguir que su latencia de despacho sea lo menor posible. Recuérdese que los trabajos de tiempo real deben ejecutarse dentro de unos plazos de tiempo preestablecidos. El núcleo se encarga de establecer a qué conjunto de colas de prioridad pertenecerá cada hilo del núcleo en el estado preparado, y en consecuencia de fijar el procesador en que se ejecutará cada hilo. Se­ ñalar que un usuario puede vincular la ejecución de un determinado trabajo a un determinado procesador o grupo de procesadores. En un determinado procesador siempre se planifica para ejecución el hilo del núcleo situado al prin­ cipio de la cola asociada a la prioridad global más alta que no esté vacía. Si en dicha cola existen varios hilos del núcleo se aplica un algoritmo de planificación de turno rotatorio. El cuanto de ejecución asig­ nado a un hilo depende de la clase de planificación a la que pertenezca el hilo y en ocasiones también de su prioridad global. Los hilos del núcleo asociados a interrupciones o a la clase SYS disponen de un cuanto infinito. Los hilos asociados a la clase F S S tienen un cuanto fijo de 1 1 0 ms. Para el resto de clases el cuanto varía en función de la prioridad global del hilo. Normalmente cuanto menor es la prioridad del hilo mayor es su cuanto de ejecución. Esto puede parecer contradictorio, sin embargo con esta política se pretende compensar a los hilos de baja prioridad porque son los que más esperan para usar un procesador. Si en la cola de mayor prioridad solo existe un hilo del núcleo éste dispondrá de un cuanto infinito, es decir, se ejecutará hasta que finalice su ejecución, entre en el estado dormido o se le expropie el uso del procesador porque llegué algún hilo a una cola asociada a un valor de prioridad mayor. El núcleo mantiene por cada clase planificación (excepto la clase SYS) una tabla de despacho (dis­ patch table) en la que se almacena, entre otras informaciones, los posibles valores del cuanto. Cada entrada de esta tabla se construye mediante una estructura xxdspent donde xx puede ser t s , i a , fx, f s s o rt cuya definición puede encontrarse en el archivo xx . h . Unos campos presentes en la estructura xxdspent_t de todas las clases son: • xx_gl obpr i . Prioridad global. • xx_quantum. Valor asignado al cuanto para la prioridad global xx_g l obpr i . La información almacenada en esta tabla puede leerse haciendo uso del comando di spadmin. e Ejemplo 3.2 Si en un intérprete de comandos de Solaris se escribe la orden di spadmin -g -e RT en la pantalla aparecería la siguiente salida5 : 4La latencia de despacho es el tiempo transcurrido desde que un hilo entra en el estado preparado hasta que comienza a ser ejecutado en un procesador. 5 Se han omitido varias líneas por cuestiones de espacio. 104 Ampliación de sistemas operativos # Real T ime Di spatcher C on f i gura t i on RES = 1 0 0 0 # TIME QUANTUM # ( r t_quantum ) 1000 # PRIORITY LEVEL o 1000 800 # # 9 10 800 600 # # 19 20 600 400 # # 29 30 400 200 # # 39 40 200 100 # # 49 50 100 # 59 S i se examina l a salida se observa que en l a segunda línea se muestra el valor asignado a l a constante RES , cuyo inversa establece la unidad de tiempo del cuanto. Por defecto RES es igual a 1 000, luego la unidad de tiempo del cuanto es 1 / 1 000 = 0,00 1 segundos, es decir, milisegundos (ms). Es posible cambiar el valor de RES usando el comando di spadm i n con la opción -r. Si RES se fija a 1 00, entonces el cuanto se expresaría en tics de reloj . En Solaris, existen 1 00 tics por segundo, luego un tic ocurre cada 10 ms. En las siguientes líneas de la salida aparece el valor del cuanto r t_quantum en función del nivel de prioridad ( PRIORITY LEVEL ) , el cual se relaciona directamente con la prioridad global. Para la clase RT la prioridad global se obtiene sumando 1 00 al nivel de prioridad. Luego el valor del cuanto toma los siguientes valores : 1 000 ms si la prioridad global pertenece al rango [ 1 00, 1 09] , 800 ms si la prioridad global pertenece al rango [ 1 1 0, 1 1 9], 600 ms si la prioridad global pertenece al rango [ 1 20, 1 29], 400 ms si la prioridad global pertenece al rango [ 1 30, 1 39], 200 ms si la prioridad global pertenece al rango [ 1 40, 1 49] y 1 00 ms si la prioridad global pertenece al rango [ 1 50, 1 59] . Se observa por lo tanto que el valor del cuanto disminuye según aumenta el rango de prioridad global considerado. • SOBUNIX: planificación, sincronización y mecanismos IPC 105 Los hilos del núcleo de las clases de planificación F S S , FX, TS e IA, aparte de una prioridad global, también tienen asociada una prioridad en modo usuario. La prioridad global determina la cola de priori­ dad en que se alojará un hilo en el estado preparado y el cuanto de ejecución que se le asignará. Por su parte la prioridad en modo usuario es el valor que se asignará a la prioridad global cuando el hilo regrese a modo usuario, ya que cuando un hilo de estas clases se ejecuta en modo núcleo su prioridad global puede tomar, en algunos casos, valores de prioridad global pertenecientes a la clase de planificación SYS. En modo usuario la prioridad global coincide con la prioridad en modo usuario. Cada clase de planificación establece el mecanismo de cálculo de la prioridad en modo usuario. Normalmente suele ser la suma de varias componentes, una de las cuales es configurable por el usuario. A la componente de la prioridad en modo usuario configurable por el usuario se le denomina prioridad de usuario. Para configurar el valor de la prioridad de usuario de un hilo del núcleo se utiliza el comando pri ocnt l . Este comando también permite cambiar la clase de planificación. Además permite confi­ gurar otros parámetros relativos a la planificación de un hilo (ver la página del comando en roan ) . Solo el superusuario puede usar este comando para incrementar la prioridad de usuario de un hilo, el resto de usuarios solo pueden decrementarla. Cada clase de planificación tiene asociado un rango de valores permitidos para la prioridad de usua­ rio. Por ejemplo, en las clases F S S y FX la prioridad de usuario puede tomar valores comprendidos en el rango [0, 60] . Mientras que en las clases TS y IA el rango de valores permitidos para la prioridad de usuario es [-60, 60] . El comando p r i o c n t l - 1 permite visualizar estos rangos. La prioridad en modo usuario y la prioridad de usuario de un hilo del núcleo se almacena en los campos xx_umdp r i y xx_upr i , respectivamente, de su estructura xxproc, donde xx puede ser t s , i a , f s s o fx. e Ejemplo 3.3 En la clase de planificación TS la prioridad en modo usuario de un hilo del núcleo perteneciente a esta clase se almacena en el campo t s_umdpri de su estructura t spro c . Su valor se calcula, ante la aparición de ciertos eventos, de la siguiente forma: ts_umdpri = {� p i 59 si pri < O si O :::; pri :::; 59 si pri > 59 (3 .3) donde pri = ts_cpupri + ts_upri (3 .4) De la expresión (3 .3) se deduce que la prioridad en modo usuario t s_umdpri solo puede tomar valores comprendidos dentro del rango [0, 59] . Por otra parte, en la expresión (3 .4) t s_upri es la prioridad de usuario y t s_cpup r i es la componente de la prioridad en modo usuario controlada por el núcleo. Estas dos componentes se almacenan también en la estructura t sproc_t . 106 Ampliación de sistemas operativos Cuando se producen ciertos eventos el valor de t s_cpupri se actualiza consultando la tabla de despacho de la clase T S . Cada entrada de esta tabla se implementa mediante una estructura t s dspend_t que contiene los siguientes campos : • t s_g l obpr i . Prioridad global. • t s_quantum. Valor asignado al cuanto para la prioridad global t s_g l obpr i . • t s_tqexp . Valor que se asignará a t s_cpupri cuando el hilo consuma el cuanto asignado. • t s_s lpr e t . Valor que se asignará a t s_cpupri cuando el hilo regrese a modo usuario después de ser despertado. Tiene como efecto el aumento de la prioridad del hilo, para que pueda acceder antes a un procesador y compensar así el tiempo perdido mientras dormía. • t s_maxwa i t. Umbral de tiempo expresado en segundos que hay que esperar antes de aplicar t s lwa i t . • t s_lwa i t . Valor que s e asignará a t s_cpupri s i e l hilo h a estado esperando para consumir su cuanto un tiempo de espera superior a t s_maxwa i t . E l tiempo de espera s e mantiene en el campo t s_di spwa i t de l a estructura t sproc_t asociada a cada hilo. Este campo se inicializa a cero cuando un hilo es colocado en una cola de prioridad después de haber consumido su cuanto o despertado porque se produjo el evento por el que estaba esperando. Si el hilo fue expropiado por un hilo de mayor prioridad t s_di spwa i t no se reinicia a cero. El valor de t s_di spwa i t es incrementado una vez por segundo para cada hilo que se encuentra en una cola de prioridad o en una cola de hilos dormidos (ver sección 3 .4). S i el valor de t s_di spwa i t es mayor que t s_maxwa i t entonces se asigna a t s_cpupri el valor t s_lwai t, lo que tiene como efecto el aumento de la prioridad del hilo. De acuerdo con los campos anteriores, se deduce que el valor de t s_cpupri asociado a un hilo del núcleo se actualiza cada vez que se produce alguno de los siguientes eventos : el hilo ha consumido su cuanto, el hilo ha despertado y regresa a modo usuario, o el hilo lleva un tiempo de espera acumulado superior a t s_maxwa i t . E l valor actual de t s_cpupri especifica l a fila de l a tabla de despacho de l a clase R T que hay que consultar. Por otra parte, el evento producido especifica la columna de la tabla de despacho donde se encuentra el nuevo valor que hay que asignar a t s_cpupr i . Conocido este valor se recalcula de nuevo la prioridad en modo usuario t s_umdp r i . Por ejemplo, supóngase que un hilo del núcleo perteneciente a la clase TS tiene t s_cpup r i = 1 1 y t s_upr i = 1 0. De acuerdo con (3 .4) p r i = 1 1 + 1 0 = 2 1 , luego de acuerdo con (3 .3) t s_umdp r i = 2 1 . S i s e usa el comando di spadm i n g - e T S se puede visualizar el contenido de l a tabla despacho de la clase T S . Las primeras doce entradas contienen los siguientes valores: - SOBUNIX: planificación, sincronización y mecanismos IPC # t s_quanturn 200 200 200 200 200 200 200 200 200 200 160 160 t s_tqexp o o o o o o o o o o o 1 t s_s lpret 50 50 50 50 50 50 50 50 50 50 51 51 t s_rnaxwa i t t s lwa i t 50 o 50 o 50 o 50 o 50 o 50 o 50 o 50 o 50 o 50 o 51 o 51 o 107 PRIORITY LEVEL o # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 # 10 # 11 # Puesto que t s_cpup r i = 1 1 l a fila de l a tabla de despacho que hay que consultar, cuando s e produzca alguno de los eventos que generan un cambio de prioridad, es la fila asociada al nivel de prioridad ( PRIORITY LEVEL ) 1 1 . Se pueden producir los siguientes eventos: • Si el hilo consume su cuanto asignado entonces el valor de t s_cpupri se actualiza con el valor de t s_tqexp de la fila 1 1 . Luego t s_cpupri = l. Como t s_upri = 1 0 entonces el nuevo valor de la prioridad en modo usuario de acuerdo con (3.3) y (3 .4) será t s_urndp r i = 1 0 + 1 = 1 1 . Luego la prioridad en modo usuario del hilo se reduce. • Si el hilo es despertado, cuando regresa a modo usuario el valor de t s_cpupr i se actualiza con el valor de t s_s lpret de la fila 1 1 . Luego t s_cpup r i = 5 1 . Como t s_up r i = 10 entonces el nuevo valor de la prioridad en modo usuario de acuerdo con (3 .3) y (3 .4) será t s_urndpri = 59. Luego la prioridad en modo usuario del hilo aumenta. • Si el valor del tiempo de espera del hilo t s_di spwa i t es mayor que t s_rnaxwa i t entonces el valor de t s_cpup r i se actualiza con el valor de t s_lwa i t de la fila 1 1 . Luego t s _cpupr i = 5 1 . Como t s_upr i = 1 0 entonces el nuevo valor de l a prioridad en modo usuario de acuerdo con (3 .3) y (3.4) será t s _urndpri = 59. Luego la prioridad en modo usuario del hilo aumenta. El nuevo valor de t s_urndpr i especifica el nuevo valor de la prioridad global, ya que en modo usuario la prioridad global coincide con el valor de la prioridad en modo usuario. Además también especifica la fila de la tabla de despacho que hay que consultar para conocer el nuevo valor del cuanto t s_quanturn. Por ejemplo, si t s_urndp r i = 1 1 entonces t s_gl obpr i = 1 1 . Además hay que consultar la fila asociada al nivel de prioridad ( PRI ORITY LEVEL ) 1 1 para conocer el nuevo valor del cuanto, que es t s_quanturn = 1 60 ms (ya que RES = 1 000). • Cada clase de planificación especifica cuándo y cómo se modifican las prioridades de los hilos del núcleo que pertenecen a dicha clase. De forma general, las prioridades de un hilo (global y en modo usuario) pueden cambiar ante la aparición de determinados eventos, como por ejemplo: 108 Ampliación de sistemas operativos • El hilo ha consumido su cuanto de ejecución. • El hilo ha sido despertado, es decir, ha regresado al estado preparado para ejecución, porque ya se ha producido el evento por el que entró en el estado dormido (bloqueado). • El hilo ha sido expropiado del procesador para ejecutar un hilo de mayor prioridad. • Un usuario utiliza el comando p r i ocnt l para cambiar la prioridad de usuario o/y la clase de planificación del hilo. Las prioridades de los hilos también pueden cambiar debido a la ejecución de funciones dependientes de cada clase de planificación que son invocadas periódicamente para la realización de alguna de las siguientes operaciones : • Procesamiento del tic de reloj. Esta operación afecta únicamente a los hilos del núcleo que están ejecutándose en los procesadores, es decir, se encuentran en el estado ejecutándose ( T S_ONPROC ) . Consiste en decrementar en un tic el contador xx_t ime l e f t de la estructura xxpro c , donde xx puede ser t s , ia, f s s , fx o r t , el cual se inicializa con el valor del cuanto asignado al hilo. Esta operación también comprueba si dicho contador ha alcanzado el valor O, es decir, el hilo ha consumido su cuanto. En caso afirmativo, se modifica la prioridad del hilo de acuerdo con la tabla de despacho (si procede en función de la clase) y se le fuerza a abandonar el procesador. Esta operación es realizada por la función xx_t i c k ( ) la cual es invocada por el manejador de la interrupción de reloj que se ejecuta 1 00 veces por segundo. • Actualización. Esta operación afecta a los hilos del núcleo en el estado preparado para ejecución ( TS_RUN) o en el estado dormido (TS_SLEEP ) pertenecientes a las clases T S , IA o F S S . Esta operación es realizada por la función xx_upda t e ( ) una vez por segundo y es invocada mediante un callout. En el caso de las clases TS e IA la operación de actualización consiste en incrementar en un segundo el contador del campo xx_d i s pwa i t de la estructura xxproc_t (donde xx puede ser t s o i a ) y comprobar si el valor del tiempo de espera del hilo xx_di spwa i t es mayor que xx_maxwa i t. En caso afirmativo se aumenta el valor de la prioridad usando la tabla de despacho. B ásicamente con esta operación se pretende evitar el abandono de hilos. En el caso de la clase F S S la operación de actualización consiste en actualizar el uso de cuotas y en modificar la prioridad en función de dicho uso. De acuerdo con la descripción realizada, se puede concluir que la planificación usada en Solaris es lo suficientemente flexible para poder tratar adecuadamente en computadores con uno o varios procesadores diferentes tipos de cargas de trabajos: interactivos, por lotes y de tiempo real. Por último conviene señalar que a pesar del tratamiento privilegiado que reciben los trabajos de tiempo real, Solaris no es un sistema operativo de tiempo real propiamente dicho sino un sistema operativo de propósito general. SOBUNIX: planificación, sincronización y mecanismos IPC 3.2.3. 109 Expropiación del procesador Como ha quedado puesto de manifiesto en las secciones anteriores en la planificación utilizada en los SOBUNIX siempre tienen preferencia para ser planificadas las unidades planificables (procesos o hilos del núcleo) situadas en la cola de unidades planificables de mayor prioridad que no esté vacía. Si una unidad planificable U l se está ejecutando en un procesador P y llega una unidad planificable U2 a una cola asociada al procesador P de mayor prioridad que la de U l , entonces si U l se está ejecutando en modo usuario se le expropia el uso del procesador sin esperar a que termine su cuanto, y se planifica para ejecución a U2. En el caso de que Ul se esté ejecutando en modo núcleo, la expropiación del procesador depende de si el núcleo es no expropiable o expropiable. Un núcleo se dice que es no expropiable si a una unidad planificable ejecutándose en modo núcleo no se le puede expropiar el uso del procesador. Sólo se le puede expropiar en los siguientes casos : para tratar las interrupciones, cuando regresa a modo usuario o cuando entra en el estado dormido a la espera de que ocurra algún evento. Ejemplos de SOBUNIX de núcleo no expropiable son SVR3 y BSD 4 . 3 . Este tipo d e núcleos son más propensos a sufrir e l problema d e la inversión d e prioridad, s i n embargo reducen el número de mecanismos de sincronización usados en el núcleo (ver sección 3 . 3). Por el contrario, un núcleo se dice que expropiable si a una unidad planificable ejecutándose en modo núcleo se le puede expropiar el uso del procesador. En este caso, conviene tener presente que con objeto de garantizar la integridad de las estructuras del núcleo existen algunas regiones de código del núcleo en las que no está permitida la expropiación. A dichas regiones se les denomina puntos de no expropiación. Un ejemplo de SOBUNIX de núcleo expropiable es Solaris . La implementación de un núcleo expropiable requiere que las estructuras del núcleo estén protegidas por mecanismos de sincronización. Algunos SOBUNIX, como por ejemplo SVR4, aplican una medida intermedia que consiste en per­ mitir la expropiación del núcleo sólo en determinados puntos del código del núcleo denominados puntos de expropiación. En estos puntos se garantiza que las estructuras de datos del núcleo se encuentran en un estado consistente. En estos sistemas una unidad planificable ejecutándose en modo núcleo solo puede ser expropiada cuando alcanza un punto de expropiación. La expropiación del procesador requiere la realización de un cambio de contexto y la manipulación de las colas de prioridad asociadas al procesador P. La unidad planificable que es expropiada sin haber concluido su cuanto, normalmente para compensarla, es colocada a la cabeza de la cola de prioridad a la que pertenece. Para notificar la existencia de una unidad planificable en una cola de mayor prioridad que la unidad planificable actualmente en ejecución, es decir, para saber si hay que expropiar el procesador, el núcleo activa uno o varios indicadores que son comprobados cuando se producen determinados eventos. e Ejemplo 3.4 El núcleo de BSD 4.3 para notificar que existe un proceso en una cola asociada a un rango de prioridades más alto que la prioridad del proceso actualmente en ejecución activa un indicador denominado runrun. Como el núcleo de BSD 4.3 es no expropiable la comprobación del estado de runrun se realiza cuando el proceso va a retornar a modo usuario. Si runrun está activado se realiza un cambio de proceso. • 1 10 e Ampliación de sistemas operativos Ejemplo 3.5 El núcleo de Solaris para notificar que existe un hilo del núcleo en una cola asociada a un rango de prio­ ridades más alto que la prioridad del hilo actualmente en ejecución, activa los indicadores cpu_runrun o cpu_krunrun de la estructura cpu asociada al procesador en que se está ejecutando el hilo. Recuérdese que en Solaris cada procesador tiene asociada un conjunto de colas de prioridad independien­ tes, luego existe una estructura cpu por cada procesador. Estos indicadores son comprobados cuando se producen cambios en la prioridad de los hilos y se manipulan las colas de prioridad. Para un determinado procesador P, el indicador cpu_krunrun se activa cuando un hilo es colocado en una cola de P cuya prioridad es mayor o igual que 1 00 y además dicha prioridad es mayor que la del hilo actualmente ejecutándose en P. Por su parte, el indicador cpu_runrun se activa cuando un hilo es colocado en una cola de P cuya prioridad es menor que 1 00 y además es mayor que la del hilo actualmente ejecutándose en P. La expropiación asociada a cpu_krunrun se realiza más rápidamente que la asociada a cpu_runrun, esto es así para reducir la latencia de despacho de los hilos asociados a aplicaciones de tiempo real y de las interrupciones que son las actividades más prioritarias. En S olaris la expropiación se deshabilita temporalmente cuando un hilo ejecutándose en modo núcleo está dentro de un punto de no expropiación. • 3.3. Mecanismos de sincronización del núcleo en SOBUNIX El código del núcleo de los SOBUNIX es reentrante. Esta característica posibilita el que varios hilos del núcleo 6 ejecutándose en modo núcleo puedan estar ejecutando concurrentemente la misma región de código del núcleo. En un sistema monoprocesador solo un hilo se encontrará en el estado ejecutándose en modo núcleo. En un sistema multiprocesador pueden existir varios hilos ejecutándose en modo núcleo, uno en cada procesador existente. En el código del núcleo existen secciones críticas asociadas a diferentes recursos críticos, como por ejemplo las estructuras de datos del núcleo. Para garantizar la exclusión mutua en la ejecución de una sección crítica del núcleo asociada a un cierto recurso crítico, el núcleo utiliza diferentes mecanismos de sincronización, como por ejemplo los cerrojos y los semáforos. Estos mecanismos serán descritos en las próximas secciones. Los diseñadores del núcleo de un sistema operativo son los encargados de elegir los tipos de meca­ nismos de sincronización que utilizarán en el núcleo y dónde los aplicarán. 6 En esta sección se va a considerar, salvo que se indique lo contrario, que las unidades planificables son los hilos del núcleo. SOBUNIX : planificación, sincronización y mecanismos IPC 3.3. 1. 111 Cerrojos U n cerrojo (lock) e s una primitiva de sincronización que consiste e n u n indicador entero que puede encontrarse en dos estados: abierto o libre (valor O) y cerrado o adquirido (valor 1 ) . Sobre un cerroj o se pueden realizar dos operaciones básicas 7 : • • Cerrar o adquirir cerrojo. Esta operación lee el valor del cerrojo, si vale O (abierto) lo pone a 1 (cerrado). Por el contrario si el cerrojo vale 1 (cerrado) entonces el hilo que invoca esta operación debe esperar a que el cerrojo se ponga a O. Esta espera puede implementarse (depende del tipo de cerrojo) como una espera activa o durmiendo en una cola de hilos dormidos asociada al cerrojo. La comprobación y configuración del valor cerrojo se implementa con una instrucción máquina especial que se ejecuta de forma atómica, como por ejemplo la instrucción comprobar y configurar (test and set). Abrir o liberar cerrojo. Esta operación configura el cerrojo al valor O (abierto) . Además si el cerrojo no implementa espera activa entonces despierta, si existe alguno, a un hilo de la cola de hilos dormidos del cerrojo. Cada recurso crítico del núcleo tiene asociado su propio cerrojo. Para garantizar la exclusión mutua en el uso de un recurso crítico R del núcleo, un hilo del núcleo antes de entrar en una sección crítica asociada a R realiza una operación cerrar cerrojo para adquirir R. Cuando el hilo termina de ejecutar la sección crítica realiza una operación abrir cerrojo para liberar a R y que otro hilo pueda usar el recurso. En función de cómo se implementa la espera de un hilo del núcleo HN 1 dentro de una operación cerrar cerrojo se distinguen tres tipos de cerrojos: • Spinlock 8 • La espera del hilo es activa, es decir, el hilo comprueba el valor del cerrojo dentro de un bucle que se ejecuta mientras el cerrojo permanezca cerrado. La espera activa supone un inconveniente si se dilata mucho en el tiempo ya que la ejecución del hilo HN 1 en un procesador impide que otros hilos lo usen. La principal ventaj a de los spinlocks es la rapidez con que HN l adquiere el cerrojo. Cuando el hilo HN2 que mantiene cerrado el cerrojo lo abre, el hilo HN 1 que espera activamente lo adquiere rápidamente puesto que HN 1 ya está ejecutándose y no hay que realizar un cambio de contexto. Obviamente se está suponiendo que en el computador existente al menos dos procesadores, en uno se ejecuta HN2 y en el otro HN l . Nótese que en un computador con un único procesador si el núcleo es no expropiable y HN 1 realiza una operación cerrar cerrojo sobre un cerrojo que se encuentra cerrado entonces la espera activa será infinita. • Cerrojo con bloqueo. El hilo HN 1 se bloquea y es colocado en una cola de hilos dormidos asociada al cerrojo. Cuando el cerroj o sea abierto, se despertará al primer hilo de la cola o al hilo de mayor prioridad, el cual debe realizar otra operación cerrar cerrojo para adquirir el cerrojo. La principal 7El nombre que reciben estas operaciones varía en la literatura y en los diferentes SOBUNIX. 8 Spinlock es un término inglés que podría traducirse como cerrojo con vuelta. Esta traducción al español no está muy extendida en la literatura por ello se ha preferido mantener el término en su idioma original . 1 12 Ampliación de sistemas operativos ventaja de los cerrojos con bloqueo es que al no realizar espera activa no acaparan un procesador mientras esperan, a diferencia de los spinlocks. Su principal inconveniente es que la adquisición de un cerrojo es más lenta que en el caso de los spinlocks ya que requiere al menos la realización de dos cambios de contexto. • e Cerrojo adaptativo. El hilo HN l puede realizar una espera activa o bloquearse. El tipo de espera dependerá del estado del hilo HN2 que actualmente mantiene el cerrojo cerrado. Si HN2 se está ejecutando en un procesador P l entonces tardará poco en abrir el cerrojo, luego HN l realiza una espera activa en otro procesador P2. Por el contrario si HN2 se encuentra en el estado bloqueado puede tardar en abrir el cerrojo, luego HN l espera bloqueado. Estos cerrojos pretenden evitar los inconvenientes de los spinlocks y de los cerrojos con bloqueo. Su principal desventaj a es que su implementación es más compleja ya que hay que implementar mecanismos para detectar que hilo retiene un cerrojo y en que estado se encuentra. Ejemplo 3.6 En la Figura 3.4 se muestra, en pseudocódigo basado en C, un ejemplo de uso de un cerrojo L de tipo spinlock. Este cerrojo se usa para garantizar la exclusión mutua en el uso del campo d de la estructura de datos del núcleo da to sN. Asimismo en la Figura 3 . 5 se muestra una posible definición de las operaciones cerrar cerrojo y abrir cerrojo del spinlock. En la operación c e r rar_c erroj o se utiliza la instrucción atómica c omprobar_y_c on f i gurar que comprueba el valor del cerrojo. Si el cerrojo vale O (abierto) lo pone a ! (cerrado) y devuelve TRUE con lo que sale del bucle whi l e y se termina la espera activa. Por el contrario, si el cerrojo vale 1 (cerrado) entonces devuelve FALS E y se vuelve a ejecutar el bucle, es decir, continúa la espera activa. • s t ruc t dato s_nuc l e o { s p i n l o c k_t L ; int d ; da t o s N ; vo i d func i on ( ) c e rrar_c erro j o ( &da t o s N . L ) ; da t o s N . d= l ; / * S e c c i ón c r í t i c a * / abr i r_cerro j o ( &da t o s N . L ) ; Figura 3.4 - Ejemplo de uso de un cerrojo de tipo spinlock para garantizar la exclusión mutua SOBUNIX : planificación, sincronización y mecanismos IPC 113 vo i d c errar_ c e r ro j o ( s p i n l o c k_ t * C e rro j o ) wh i l e ( c omprobar_y_ c on f i gurar ( c e r r o j o ) ! = vo i d abr i r_c e r r o j o ( s p i n l o c k_t 0) ; * C e rro j o ) * C erroj o = O ; Figura 3.5 - Posible definición de las operaciones de tipo spinlock c e r r a r_c e r r o j o y abr i r_c err o j o de un cerrojo En general al diseñar el código del núcleo de un sistema operativo se intenta que las regiones críticas no sean muy numerosas y que éstas sean cortas. En consecuencia se suelen utilizar spinlocks para pro­ teger la mayoría de los recursos críticos del núcleo, ya que la espera activa será pequeña. Para aquellos recursos críticos cuyas secciones críticas asociadas sean más largas o puedan requerir la suspensión del hilo que se encuentra dentro de ellas entonces se utilizan cerrojos con bloqueo. Si no se sabe si el hilo puede suspenderse entonces mej or usar cerrojos adaptativos. De acuerdo con el funcionamiento de los cerrojos descrito en los párrafos anteriores, cuando un hilo cierra un cerroj o asociado a un recurso crítico R, ningún otro hilo puede acceder a R. A los cerrojos con este comportamiento en algunos sistemas se les denomina cerrojos exclusivos o cerrojos mutex. Existe otro tipo de cerroj o denominado cerrojo lector1 escritor o cerrojo R/W cuyo funcionamiento depende del tipo de acceso que se desee realizar sobre el recurso. Si el acceso es de escritura entonces solo un hilo puede acceder al recurso. Por el contrario, si el acceso es de lectura entonces pueden acceder múltiples hilos. Básicamente un cerrojo R/W resuelve uno de los problemas clásicos de sincronización de hilos concurrentes : el problema de los lectores y los escritores [Díaz et al. , 20 1 1 ] . Los cerrojos R/W a diferencia de los cerrojos mutex requieren en su implementación de dos colas de hilos dormidos: una cola para los hilos lectores y otra para cola los hilos escritores. Los lectores duermen mientras haya un escritor usando el recurso. Por su parte los escritores duermen si hay algún hilo, ya sea lector o escritor. 3.3.2. Semáforos Un semáforo binario, también denominado mutex, es una variable entera, que puede tomar los valores O o 1 y sobre la que es posible realizar las siguientes operaciones 9 : • ini t_s em ( S , va l o r ) . Inicializa el semáforo S a un determinado valor, es decir, S =va l o r, don­ de va l o r es igual a O o a l . 9EJ nombre dado a estas operaciones varía en la literatura y en Jos diferentes SOBUNIX. Aquí se utiliza el nombre propuesto en [Díaz et al . , 20 1 1 ] . Las operaciones wa i t_s em ( S ) y s i gnal_s em ( S ) equivalen, respectivamente, a las operaciones P(S) y V(S) definidas en [Dijkstra, 1 965 ] . Ampliación de sistemas operativos 114 • wa i t_s em ( S ) . Comprueba el valor del semáforo s . Si S = O , entonces el proceso pasa al estado dormido y es colocado en una cola de hilos dormidos asociada al semáforo. Si S = l , entonces pone el semáforo a O y el hilo puede continuar su ejecución. • s i gna l_s em ( s ) . Comprueba si la cola de hilos dormidos asociada al semáforo S está vacía. En caso afirmativo, pone el semáforo a 1 , y continúa su ejecución. En caso negativo, es decir, hay hilos bloqueados en el semáforo, entonces el sistema operativo elimina de la cola asociada al semáforo a uno de los hilos dormidos, y le despierta lo que hace que pase al estado preparado para ejecución. Existe, además, otro tipo de semáforo denominado semáforo general o semáforo con contador que es una variable entera que puede tomar más valores aparte de O o l . Para un semáforo general la operación ini t_s em ( s , val or ) consiste en inicializar el valor del semáforo S a un determinado valor entero positivo N, es decir, S =N. Por su parte la operación wa i t_s em ( s ) cuando se implementa para un semáforo general disminuye en una unidad el valor del semáforo. Es decir, S = S - 1 . Si el valor resultante es un número negativo entonces el hilo que ha invocado esta operación entra en el estado dormido y es añadido a la cola de hilos dormidos asociada al semáforo. En el caso de la operación s i gnal_s em ( s ) se incrementa en una unidad el valor del semáforo, es decir, S = S + l . Si el valor resultante es menor o igual a O entonces se elimina de la cola asociada al semáforo uno de los hilos dormidos, y se le despierta, lo que hace que pase al estado preparado para ejecución. La realización de las operaciones wa i t_s em y s i gna l_s em, independientemente del tipo de se­ máforo, requiere garantizar la exclusión mutua durante la comprobación y configuración del valor del semáforo. Dicha exclusión mutua se suele implementar con una instrucción máquina especial que se ejecuta de forma atómica. La principal ventaja de los semáforos es que proporcionan un mecanismo de sincronización a alto nivel que se puede aplicar para resolver diferentes problemas de sincronización. Así, los semáforos bina­ rios se utilizan para garantizar la exclusión mutua o para sincronizar la ejecución de hilos que esperan por un evento. Por su parte, los semáforos generales se pueden usar para las mismas funciones que los semá­ foros binarios. Además permiten contar el número de instancias disponibles de un determinado recurso crítico, para ello simplemente hay que inicializar el semáforo con el número de instancias existentes. El principal inconveniente de los semáforos [Vahalia, 1 995] es que son abstracciones de alto nivel que se implementan con mecanismos de sincronización de baj o nivel para poder garantizar la atomicidad y la exclusión mutua. La implementación de las operaciones wa i t_s em y s i gnal_s em requiere una operación atómica de bajo nivel que garantice el acceso con exclusión mutua a la variable semáforo. Por otra parte, el bloqueo y desbloqueo de procesos requiere la realización de cambios de contexto y la manipulación de las colas de procesos preparados y de las colas de procesos dormidos, lo que ralentiza las operaciones sobre los semáforos. Los núcleos de los primeros SOBUNIX utilizaban como mecanismo de sincronización principal­ mente semáforos. Posteriormente, debido a la relativa lentitud de sus operaciones, han sido sustituidos por cerrojos. En los núcleos de los S OBUNIX actuales los semáforos binarios se utilizan principalmente para garantizar la exclusión mutua sobre aquellos recursos críticos del núcleo que pueden ser usados SOBUNIX: planificación, sincronización y mecanismos IPC 115 sin fuertes restricciones de tiempo. También se utilizan semáforos generales para contar el número de instancias disponibles de un determinado recurso crítico del núcleo. e Ejemplo 3.7 En la Figura 3 . 6 se muestra en pseudocódigo basado en C un ejemplo de uso de un semáforo binario mu t ex l , para garantizar la exclusión mutua en el uso de una sección crítica asociada a un determinado recurso crítico del núcleo. Asimismo, en la Figura 3 .7 se muestra un ejemplo de uso de un semáforo general c on t ador para contar las instancias disponibles de un determinado recurso crítico del núcleo del que existen M instancias . • f* I n i c i a l i z a c i ón de l s emá f o ro * f s emá f o ro_binar i o mu t ex l ; i n i t_s em ( &mu t exl , 1) ; f * U s o para garant i z ar l a exc l u s i ón mu tua * 1 wa i t_s em ( &mu t ex l ) ; e j ecuc i ón s e c c i ón c r í t i c a ; s i gnal_s em ( &mu t e x l ) ; Ejemplo de uso de un semáforo binario para garantizar la exclusión mutua en el uso de una sección crítica asociada a un cierto recurso del núcleo Figura 3.6 f* - In i c i a l i z a c i ón del s emá f oro * f s emá f o ro_general c o n t a do r ; i n i t_s em ( & contador , M) ; f * U s o para contar núme r o de i n s t anc i a s d i sponi b l e s *f wa i t_s em ( &contador ) ; u s o i n s t anc i a recur s o ; s i gna l_s em ( & c on t ador ) ; Figura 3.7 - Ejemplo de uso de un semáforo general para contar el número de instancias disponibles de un recurso del núcleo 1 16 3.4. Ampliación de sistemas operativos Dormir 1 despertar y colas de hilos dormidos en SOBUNIX En los SOBUNIX cuando un hilo ejecutándose en modo núcleo tiene que esperar un tiempo que puede sea largo por un evento o recurso se invoca a una rutina del núcleo para dormir (bloquear) el hilo. Esta rutina coloca al hilo en una cola de hilos dormidos del núcleo, cambia su estado a dormido e invoca al planificador para que seleccione para ser ejecutado a otro hilo, lo cual requerirá, entre otras acciones, la realización de un cambio de contexto. Cuando se produzca el evento o se libere el recurso, el núcleo invocará una rutina para despertar (desbloquear) al hilo esperando por dicho evento o recurso. Esta rutina debe encontrar al hilo en la cola de hilos dormidos adecuada, cambiar el estado del hilo a preparado para ejecución y colocarlo en una cola de hilos preparados para ejecución en espera de ser planificado para ejecución en un procesador. Cada recurso o evento tiene asignado un canal de espera (wait channel), también denominado canal de dormir (sleep channel), que es un número de 32 bits que almacena la dirección del recurso. Nótese que todo evento también estará asociado a un recurso. Por ejemplo, el evento de que se complete una operación de E/S estará asociado a un determinado buffer del núcleo donde se encontrará el dato que desea leer o escribir. La rutina del núcleo que bloquea el hilo almacena el canal de espera asociado al recurso o evento en un campo de la estructura de datos del hilo. Por otra parte, cada canal de espera tiene asociada una cola de hilos dormidos. La asociación se realiza mediante una función hash ya que el número de colas de hilos dormidos que mantiene el núcleo es inferior al número de canales. Por lo tanto varios canales de espera pueden tener asociada la misma cola. e Ejemplo 3.8 Considérese dos estructuras de datos del núcleo Rl y R2 cuyo acceso está protegido con los cerrojos C l y C2, respectivamente. Supóngase que un hilo del núcleo HN l cierra C l y entra en el estado dormido en espera de que se complete una operación de lectura de un bloque del disco duro que se almacenará en R l . Mientras duerme HN l , otro hilo HN2 también necesita usar R l y realiza una operación cerrar sobre C l . Como C l está cerrado por HN l , entonces HN2 entra en el estado dormido en espera de que HN l abra C l . Por otra parte, supóngase que un hilo del núcleo HN3 cierra C2. Mientras HN3 opera sobre la estructura R2 otro hilo HN4, que requiere usar también R2, realiza la operación cerrar C2 y entra el estado dormido en espera de que HN3 abra C2. La dirección de cada recurso Rl y R2 constituye dos canales de espera independientes Wl y W2. En este escenario, HN l y HN2 duermen en el mismo canal de espera W l . Mientras que HN4 duerme en el canal de espera W2. En la Figura 3 . 8 se muestra un posible estado de las colas de hilos dormidos del núcleo. Se ha supuesto que la función hash ha asignado la misma cola de hilos dormidos a los canales W l y W2, aunque podría haberles asignado colas diferentes, dependerá del valor de W l , de W2 y de la función hash utilizada . • SOBUNIX : planificación, sincronización y mecanismos IPC Cerrojo _____.. 1 17 C1 Recurso Rl Canal de espera Wl HN4 C2 R2 Conjunto de colas de hilos dormidos Figura 3.8 - Asignación de colas de hilos dormidos del Ejemplo 3 . 8 L a existencia d e hilos esperando por distintos canales dentro d e una misma cola introduce retrasos a la hora de despertar a los hilos esperando en un determinado canal, ya que hay que examinar uno a uno todos los hilos de la cola. Para eliminar este retraso, podría mantenerse una cola por canal. El inconveniente de esta solución es que consume bastante memoria principal ya que se necesita un número de colas elevado. Una solución más eficiente desde el punto de vista del consumo de memoria es la utilizada en Solaris. Esta solución tiene en cuenta el hecho de que muchos de los bloqueos de los hilos en modo núcleo se producen en objetos de sincronización (cerrojos, semáforos, . . . ) que protegen los recursos críticos. Además en un determinado instante de tiempo solo se están utilizando una pequeña parte de los muchos objetos de sincronización existentes en el código del núcleo. Teniendo en cuenta las consideraciones anteriores, cada objeto de sincronización del núcleo de So­ laris tiene un campo de dos bytes que contiene un puntero a un turnstile que es una estructura de datos que contiene, entre otras informaciones, la cabecera de una cola de hilos dormidos. Los hilos en la cola de un turnstile se organizan por prioridad. Además existe la posibilidad de despertar a todos los hilos de la cola o solamente al más prioritario. El núcleo mantiene un conj unto de turnstiles de tamaño dinámico, siempre mayor que el número de hilos del núcleo activos. Cuando en un objeto de sincronización se bloquea un primer hilo el núcleo asig­ na al objeto de sincronización un turnstile para implementar una cola de hilos dormidos. En el momento que la cola se queda vacía el tumstile queda libre y puede ser asignado a otro objeto de sincronización. Por lo tanto, en Solaris conviven dos tipos de colas de hilos dormidos : las implementadas con turns­ tiles para bloqueos en objetos de sincronización y las colas de hilos dormidos en canales de espera para 1 18 Ampliación de sistemas operativos eventos o recursos no relacionados con objetos de sincronización, como por ejemplo la finalización de una operación de E/S. Obviamente el número de colas de este segundo tipo es mucho más pequeño que en otros S OBUNIX gracias al uso de turnstiles. e Ejemplo 3.9 En la Figura 3.9 se muestra el estado del conjunto de turnstiles del núcleo de un sistema Solaris para el escenario descrito en el ejemplo anterior. Se observa que los cerrojos Cl y C2, al tener hilos bloquea­ dos en ellos, tienen asignado cada uno un turnstile (TU) para implementar las colas de hilos dormidos correspondientes. Asignado a Cl Recurso Rl Canal de espera Wl Libre Asignado a C2 --------· Libre R2 HN2 1 1 8=8 TO TU HN4 1 1 TO W2 Conjunto de tumstiles Figura 3.9 - Ejemplo de uso de tumstiles • 3.5. Mecanismos de comunicación entre procesos en SOBUNIX Los SOBUNIX ponen a disposición de los programadores de aplicaciones diferentes mecanismos para garantizar la exclusión mutua en el uso de recursos críticos, sincronizar su ejecución, esperar por la aparición de algún evento o intercambiar información. A este conjunto de mecanismos se les cono­ ce de forma general como mecanismos de comunicación entre procesos, o más abreviadamente como mecanismos IPC(InterProcess Communications). SOBUNIX: planificación, sincronización y mecanismos IPC 1 19 Es el programador de aplicaciones el que decide qué mecanismos IPC utilizar y dónde utilizarlos dentro del código de sus aplicaciones. El uso de estos mecanismos requiere la invocación de las lla­ madas al sistema oportunas. El núcleo se encarga de atender dichas llamadas al sistema y mantener las estructuras de datos necesarios para soportar dichos mecanismos IPC. En las siguientes secciones se describen los mecanismos IPC introducidos por primera vez en 1983 en el System V Release 1 de AT&T y posteriormente adoptados por el resto de SOBUNIX: los semáforos, las colas de mensajes y la memoria compartida. A estos tres mecanismos se les conoce de forma general como mecanismos IPC del System V. También se describen otros mecanismos IPC como las señales y las tuberías, usados principalmente en los intérpretes de comandos. 3.5.1. Mecanismos IPC del System V Los tres mecanismos IPC del System V: semáforos, colas de mensajes y memoria compartida, aun­ que son mecanismos distintos son soportados por el núcleo de forma muy similar. El núcleo mantiene una tabla por cada tipo de mecanismo IPC. Una entrada en una de estas tablas se implementa mediante una estructura de datos que contiene información de administración y control sobre un determinado recurso IPC, es decir, sobre una instancia de un determinado mecanismo IPC. Entre la información contenida en una entrada se encuentra la siguiente: • Creador del recurso IPC. El UID y el GID del proceso que solicita la creación el recurso. • Propietario del recurso IPC. El UID y el GID del proceso que actualmente es el propietario del recurso. Inicialmente, el creador es el propietario del recurso, aunque el mismo o el superusuario pueden transferir la propiedad del recurso a otro proceso. • Permisos del recurso IPC. Mascara de bits, similar a la máscara de modo de un archivo, que permite establecer los permisos de acceso (lectura y/o escritura) al recurso para el propietario del recurso, el grupo propietario y el resto de usuarios. • Llave o clave del recurso IPC . Número entero facilitado al núcleo por el proceso que solicita la creación o el acceso al recurso IPC. Solo los procesos que conozcan dicha llave podrán acceder al recurso. Luego se puede considerar como una especie de contraseña de acceso. La llamada al sistema f t ok permite crear una llave de acceso. • Información propia de cada tipo de recurso IPC. Como por ejemplo punteros que permiten locali­ zar a los diferentes componentes de un determinado recurso IPC. El número máximo de entradas que puede tener la tabla asociada a un determinado tipo de mecanismo IPC, o lo que es lo mismo el número de recursos IPC de un determinado tipo, es un parámetro que puede ser configurado por el superusuario. Por otra parte, el núcleo asigna a cada recurso IPC creado un determinado identificador numérico entero que lo identifica de manera unívoca. El núcleo usa este identificador para localizar la entrada asociada al recurso en la tabla correspondiente. La asignación del identificador se realiza en el momento Ampliación de sistemas operativos 120 de la creación o asignación del recurso. El núcleo devuelve este identificador al proceso para que lo utilice como argumento de entrada en las posteriores llamadas al sistema que vaya a realizar para operar sobre el recurso, y así agilizar su localización. Semáforos La implementación que hace el núcleo de los semáforos como mecanismo IPC es distinta a la im­ plementación de los semáforos como mecanismo de sincronización del núcleo estudiada en la sección 3 . 3 .2, y no deben ser confundidas. Un semáforo implementado como mecanismo IPC es una ampliación del concepto de semáforo general que consiste en un conjunto de N semáforos (N = 1 , 2, 3, . . . ) sobre el cual es posible realizar diferentes operaciones, ya sea sobre todos o algunos de sus elementos. Para gestionar los conjuntos de semáforos existentes el núcleo mantiene una tabla. Cada entrada de esta tabla contiene, entre otros, los siguientes datos sobre un determinado conjunto de semáforos : creador, propietario, llave, permisos de acceso, puntero al array de semáforos que forma parte del conjunto y número N de semáforos que forman parte del conjunto. El núcleo asocia a cada conj unto de semáforos un identificador numérico entero positivo s emid que lo identifica de manera univoca y que le permite localizar rápidamente su entrada asociada en la tabla que mantiene para soportar los semáforos. Por cada semáforo j (j = O, . . . , N - 1 ) perteneciente al conjunto de semáforos s emid el núcleo mantiene una estructura con los siguientes datos: el valor actual del semáforo, que es un número entero positivo o cero, el número de procesos esperando a que el semáforo valga cero, el número de procesos esperando a que el semáforo se incremente y el PID del proceso que hizo la última operación sobre el semáforo. También mantiene una cola de procesos dormidos en el semáforo. El núcleo soporta las siguientes llamadas al sistema asociadas con los semáforos : • s emi d= s emge t ( l l ave , N , i nd ) . Esta llamada a l sistema permite crear u n conjunto d e semá­ foros u obtener el uso de un conjunto ya creado. Requiere como argumentos de entrada la llave numérica ( l l ave) que se quiere asociar al conjunto de semáforos o que ya tiene asociada (si ya está creado), el número N de semáforos que tendrá el conjunto y una serie de indicadores ( i nd) que permiten especificar, entre otras cosas, los permisos de acceso al conjunto de semáforos. Un indicador utilizado frecuentemente es I PC_CREAT que fuerza a crear un conjunto nuevo si no existe uno ya creado. Si la llamada al sistema se ejecuta con éxito entonces guarda en s emid el identificador del conjunto de semáforos. En caso contrario almacena en s emid el valor -l. • r = s emop ( s emi d , oper , M ) . Esta llamada al sistema permite realizar operaciones sobre los semá­ foros de un determinado conjunto de semáforos. Requiere como argumentos de entrada el identifi­ cador s emid del conjunto de semáforos, un puntero oper a un array de estructuras de tipo s embu f y el número de elementos M de dicho array. Una estructura s embu f contiene la siguiente información: - El identificador j (j = O, . . . , N - 1 ) del semáforo del conjunto s emid sobre el que se quiere realizar la operación. SOBUNIX: planificación, sincronización y mecanismos IPC 121 El tipo de operación s em_op que se quiere realizar sobre el semáforo j del conjunto s emid. Si s em_op es un entero positivo mayor que cero entonces se añade s em_op al valor del semáforo y se despiertan a los procesos que estaban dormidos en espera de que el valor del semáforo se incrementase. Si s em_op es un entero negativo entonces se comprueba si el valor del semáforo es mayor o igual que el valor absoluto de s em_op. En caso afirmativo se resta s em_op del valor del semáforo. En caso negativo el proceso entra en el estado dormido en espera de que se cumpla la condición. Si s em_op es igual a cero se comprueba si el valor del semáforo es igual O. En caso afirmativo no se realiza ninguna modificación sobre el valor del semáforo. En caso contrario el proceso entra en el estado dormido en espera de que se cumpla la condición. Indicadores. El indicador I PC_NOWAIT establece que en caso de que no se pueda realizar la operación correspondiente no se bloquee el proceso y se devuelva un mensaje de error. Por defecto todas las operaciones son bloqueantes. Por su parte el indicador S EM_UNDO indica al núcleo que la operación debe ser deshecha cuando el proceso termine. Esta opción permite evitar que un semáforo quede permanente bloqueado por un proceso que finalice antes de poder liberar el recurso. El núcleo mantiene por cada semáforo una lista con las operaciones que tienen que ser deshechas. Cada operación sobre un semáforo j definida en una estructura s embu f es realizada de forma atómica por el núcleo. De esta forma, hasta que una operación no se completa no se puede realizar otra. Si la llamada al sistema s emop se ejecuta con éxito entonces en r se almacena el valor O . En caso de error se almacena - l . • r = s emc t l ( s emi d , j , o rden , arg ) . Esta llamada permite leer y configurar los datos de control que el núcleo mantiene en la entrada asociada a un determinado conjunto de semáforos en la tabla de semáforos. También permite leer y configurar los valores de todos o alguno de los semáforos de un determinado conjunto. Esta llamada requiere como parámetros de entrada el identificador del conjunto de semáforos s emi d, el identificador j de un semáforo del conjunto, un número entero o constante simbólica orden que especifica la operación que se quiere realizar sobre los datos de administración y control del semáforo, y los argumentos arg asociados a dicha operación. Algunos de los posibles valores de orden son: I PC_RMID. Especifica que el recurso IPC, en este caso el conjunto de semáforos s emid debe ser eliminado. Además despierta a todos los procesos dormidos esperando en los semáforos del conjunto. SETALL . Configura el valor de todos los semáforos del conjunto s emid al valor especificado en arg. GETALL. Lee el valor de todos los semáforos del conjunto s emi d y los almacena en arg. 122 Ampliación de sistemas operativos SETVAL. Configura el valor del semáforo j del conjunto s emid al valor especificado en arg. GETVAL. Lee el valor del semáforo j del conjunto s emi d y lo almacena en r. Si la llamada al sistema se ejecuta con éxito entonces en r se almacena O o un valor que depende de o rden. En caso contrario en r se almacena - l . # i n c lude < s td i o . h> # i n c lude < sys / typ e s . h> # i n c l ude < sys / ipc . h> # i n c lude < sys / s em . h> #de f ine CLAVE 3 4 2 1 L 1 * L l ave * 1 #de f i n e PERMI SOS 0 6 6 6 1 * L e c tura y e s c r i tura para t o d o s l o s usuar i o s *1 s t ruc t s embu f decremen t a r [ 1 ] = { 0 , - 1 , 0 } ; s t ruc t s embu f increment a r [ 1 ] = { 0 , 1 , 0 } ; i n t s em i d ; i n t r e s u l t ado ; vo i d i n i t_s em ( i nt val ) 1 * C r e a r u obtener un c o n j u n t o de s emá f o r o s de un s emá f o r o I ( N= 1 ) *1 s em i d= s emge t ( CLAVE , 1 , I PC_CREAT PERM I S O S ) ; if ( s em i d= = - 1 ) 1 * C omprobar s i ha hab ido a l gún e r r o r * 1 p r i n t f ( " \ nErro r " ) ; ex i t ( - 1 ) ; 1* I n i c i a l i z a r e l s emá f o r o a 1 *1 r e s u l tado= s emc t l ( s emi d , O , S E TVAL , va l ) ; vo i d wa i t_s em ( ) r e s u l tado = s emop ( s emi d , & d e c rementar [ 0 ] , 1 ) ; vo i d s i gna l_s em ( ) r e s u l tado = s emop ( s em i d , & i n c r ementar [ 0 ] , 1 ) ; Figura 3.10 - Código C del archivo de cabecera c omun . h del Ejemplo 3 . 1 0 SOBUNIX: planificación, sincronización y mecanismos IPC # i nc lude 123 " c omun . h " i n t ma i n ( } int N= 2 ; / * Nume ro de p r o c e s o s hi j o s a c r e ar * / i n i t_s em ( l } ; wh i l e ( N> O } { N=N- 1 ; i f ( fork ( } = = 0 } { p r i n t f ( " P I D= %d An t e s de l a r e g i ó n c r i t i c a \ n " , g e t p i d ( } } ; wa i t_s em ( } ; p r i n t f ( " P I D= %d Dentro de la regi ón c r i t i c a \ n " , getp i d ( } } ; s l e ep ( S } ; p r i n t f ( " P I D= %d D e s p i e r t o y s a l go de l a reg i ón c r i t i c a \ n " , g e t p i d ( } } ; s i gnal_s em ( } ; exi t ( 0 } ; re turn O ; Figura 3 . 1 1 - Código C del programa prog3 1 del Ejemplo 3 . 1 0 e Ejemplo 3.10 En la Figura 3 . 1 0 se muestra el código C de un archivo de cabecera c omun . h que hace uso de las lla­ madas al sistema s emg e t , s emop y s emc t l sobre un conjunto s emid que consta de un único semáforo (N= 1 ) que se utiliza para implementar un semáforo binario (ver sección 3 . 3 .2) y sus operaciones básicas ini t_s em, wa i t_s em y s i gna l_s em. En la Figura 3 . 1 1 se muestra el código C del programa prog3 1 que utiliza el semáforo binario definido en c omun . h para garantizar la exclusión mutua en el uso de una cierta región critica. S upóngase que se invoca a prog3 1 desde la línea de órdenes de un intérprete de comandos y que éste crea al proceso A para ejecutar este programa. El proceso A crea dos procesos hijos: B y C, y termina su ejecución. Se va suponer que B tiene un PID=995 y que C tiene un PID=996. Cuando se planifica para ejecución al proceso hij o B en primer lugar imprime en pantalla el mensaje P I D= 9 9 5 Ant e s de la región c r i t i c a E n segundo lugar invoca a la función wa i t_s em ( ) que decrementa en una unidad e l valor del semáforo. Este semáforo estaba inicializado a 1 , tras la operación toma el valor O. En tercer lugar imprime en pantalla el mensaje 124 Ampliación de sistemas operativos P I D= 9 9 5 Dentro de l a regi ón c r i t i c a e invoca a la llamada al sistema s l e ep ( 5 ) que suspende la ejecución del proceso B durante 5 segundos. Mientras B está suspendido se ejecuta el proceso hijo C, que imprime en pantalla P I D= 9 9 6 Ant e s de la reg i ón c r i t i c a e invoca a la función wa i t_s em ( ) . Como el semáforo tiene el valor O el proceso C entra en el estado dormido y es colocado en cola de procesos dormidos asociada al semáforo. Una vez transcurridos los 5 segundos se despierta al proceso B . Cuando se planifica para ejecución en primer lugar imprime en pantalla PID= 9 9 5 D e sp i e r t o y salgo de la r e g i ón c r i t i c a E n segundo lugar invoca a l a función s i gnal_sem ( ) que despierta a l proceso C. Finalmente e l proceso B invoca a la llamada al sistema exi t para terminar su ejecución. Cuando C es planificado para ejecución imprime en pantalla el mensaje P I D= 9 9 6 Dentro de l a regi ón c r i t i c a e invoca a la llamada al sistema s l e ep ( 5 ) que suspende la ejecución del proceso C durante 5 segundos. A continuación imprime en pantalla PID= 9 9 6 D e sp i e r t o y s a l go de la r e g i ón c r i t i c a Por último ejecuta l a operación s em_s i gna l ( ) que incrementa e n una unidad e l valor del semáforo, ahora toma el valor 1 , e invoca a la llamada al sistema exi t para terminar su ejecución. • Colas de mensajes Las colas de mensajes son buzones que posibilitan la realización de una comunicación indirecta entre procesos mediante el paso de mensajes. Un proceso que adquiera el uso de una cola de mensajes puede enviar (insertar) o recibir (extraer) mensajes de la cola. Por defecto, los mensajes son insertados y extraídos por orden de llegada, es decir, usando un algoritmo PIFO. Aunque también es posible extraer un mensaje determinado de una cola haciendo uso de un identificador entero positivo denominado tipo del mensaje definido por el programador. Cada mensaje enviado o recibido por un proceso se implementa como una estructura de datos que contiene el tipo de mensaje y el cuerpo del mensaje. El tamaño máximo en bytes y el número máximo de mensajes de una cola de mensajes son paráme­ tros configurables por el superusuario. Por defecto si un proceso envía un mensaje a una cola que está llena entra en el estado dormido en espera de que el mensaje pueda ser enviado. Asimismo si un proceso desea recibir un mensaje de una cola que está vacía entra en el estado dormido hasta que llegue el mensaje. SOBUNIX: planificación, sincronización y mecanismos IPC 125 El núcleo implementa una cola de mensajes mediante una lista enlazada de mensajes que se almacena en el espacio de direcciones del núcleo. Para gestionar las colas de mensajes existentes el núcleo mantiene una tabla de colas de mensajes. Cada entrada de esta tabla contiene, entre otros, los siguientes datos sobre una determinada cola: creador, propietario, llave, permisos de acceso, puntero al primer mensaje de la cola, puntero al último mensaj e de la cola, número de bytes almacenados en la cola, tamaño máximo en bytes que puede tener la cola y número de mensajes actualmente en la cola. Además el núcleo tiene que mantener las colas de procesos dormidos en operaciones de envío o recepción sobre una determinada cola de mensajes. El núcleo asocia a cada cola de mensajes un identificador numérico entero positivo rnsgqi d que la identifica de manera univoca y que le permite localizar rápidamente su entrada asociada en la tabla de colas de mensajes. Por cada mensaje en una cola el núcleo mantiene una estructura de datos con la siguiente informa­ ción: un puntero al siguiente mensaj e de la cola, el tipo del mensaje, el tamaño del cuerpo del mensaje y la dirección de comienzo del cuerpo del mensaje. El núcleo soporta las siguientes llamadas al sistema asociadas con las colas de mensajes: • rnsgqi d=rns gget ( l l ave , ind ) . Esta llamada al sistema permite crear una cola de mensajes nue­ va u obtener acceso a una cola de mensajes ya creada. Requiere como argumentos de entrada la l l ave que se va asignar a la nueva cola o la llave de la cola ya creada, y una serie de indicadores ind que permiten especificar, entre otras cosas, los permisos de acceso a la cola. Si la llamada se ejecuta con éxito devuelve en rnsgqi d el identificador de la cola. En caso contrario devuelve - 1 . • r=rnsgsnd ( rns gqi d , rnen , t arn , ind ) . Esta llamada al sistema permite enviar un mensaje a una determinada cola. Requiere como argumentos de entrada el identificador de la cola de mensajes rnsgqi d, un puntero rnen al buffer que contiene el mensaje que se desea enviar, el tamaño t arn en bytes del mensaje, y un indicador ind que permite especificar el comportamiento de la llamada. Cuando un proceso invoca a la llamada rnsgsnd el núcleo comprueba si existe espacio en la cola para poder enviar el mensaje. Si la cola está llena el núcleo bloquea al proceso hasta que exista espacio en la cola y el mensaje pueda ser enviado. Para evitar el bloqueo del proceso se debe especificar en ind el indicador I PC_NOWAIT. Si la llamada se ejecuta con éxito devuelve O en caso contrario devuelve - l . • r=rnsgrcv ( rns gqi d , rnen , rnax , t ipornen , ind ) . Esta llamada al sistema permite recibir un men­ saje de una determinada cola. Requiere como argumentos de entrada el identificador de la cola de mensajes rnsgqid, un puntero rnen al buffer donde se almacenará el mensaje que se desea re­ cibir, el número máximo rnax de bytes que serán almacenados del mensaje, el tipo del mensaje t ipornen y un indicador ind que permite especificar el comportamiento de la llamada. Por defec­ to si la cola está vacía o no existe un mensaje del tipo especificado el núcleo bloquea al proceso que invoca esta llamada. Para evitar el bloqueo del proceso se debe especificar en i nd el indicador I PC_NOWAIT. Si la llamada se ejecuta con éxito devuelve el número de bytes escritos en el buffer, en caso contrario devuelve - 1 . Ampliación de sistemas operativos 126 El parámetro t ipomen de esta llamada permite seleccionar el mensaje que será extraído de la cola de mensajes. Si t ipomen es mayor que cero se extrae el mensaje que lleva más tiempo en la cola cuyo tipo sea igual a t ip omen. Si t ipomen es igual a cero se extrae el mensaje que lleva más tiempo en la cola, es decir, el primer mensaje. Por último si t ipomen es menor que cero se buscan aquellos mensajes cuyo tipo sea menor o igual al valor absoluto de t ipomen, de todos los mensajes que cumplen esta condición se elige para ser extraído al mensaje que lleva más tiempo en la cola cuyo tipo sea el más pequeño de todos. • r=msgc t l ( msgqi d , o rden , arg ) . Esta llamada permite leer y configurar los datos de control que el núcleo mantiene en la entrada asociada a una determinada cola de mensajes en la tabla de colas de mensajes. Requiere como argumentos de entrada el identificador de la cola de mensajes msgqi d, un número entero o constante simbólica orden que especifica la operación que se quiere realizar sobre los datos de administración y control de la cola, y los argumentos arg asociados a dicha operación. Los posibles valores de o rden son: I PC_RMID. Especifica que el recurso IPC debe ser eliminado. Además despierta a todos los procesos dormidos esperando por una operación de envío o recepción en dicha cola de mensajes. I PC_STAT. Lee el contenido de la estructura de datos asociada al recurso IPC y lo almacena en estructura apuntada en arg. I PC_SET. Configura la estructura de datos asociada al recurso IPC con los valores almace­ nados en la estructura apuntada en arg. Si la llamada al sistema se ejecuta con éxito entonces devuelve O. En caso contrario devuelve - l . e Ejemplo 3 . 1 1 En la Figura 3 . 1 2 se muestra el código del archivo de cabecera de f_c o l a . h donde se usa la llamada al sistema msgget para crear una cola de mensajes con permisos de lectura y escritura para todos los usua­ rios. Se observa que la llave del recurso IPC es creada mediante la llamada al sistema f tok, que recibe como argumentos la ruta de un archivo " a s d . txt " que debe estar previamente creado y un carácter K ' '. Por otra parte, en las Figuras 3 . 1 3 y 3 . 1 4 se muestra el código de los programas prog 3 2 y prog3 3 . El programa prog3 2 crea u obtiene la cola de mensajes id invocando a la función obtener_c o l a ( ) y a continuación envía un mensaje x de tipo 1 invocando a la llamada msgsnd. Por su parte el programa prog3 3 invoca a obt ener_c o l a ( ) para acceder a la cola id y a continuación invoca a la llamada msgrcv para solicitar la recepción del primer mensaje de tipo 1 que exista en la cola. Si dicho mensaje existe se eliminará de la cola y se copiará en la estructura Y. Si no existe dicho mensaje el proceso receptor se bloqueará hasta que llegue a la cola un mensaje del tipo especificado. Cuando llegue el mensaj e se despertará al proceso receptor y cuando sea planificado para continuar su ejecución mostrará en la pantalla el texto: E s t e e s e l mens a j e 1 SOBUNIX: planificación, sincronización y mecanismos IPC # i nc l ude < sys / ipc . h> # i nc l ude < sys /msg . h> int i d ; key_t l l ave ; obt ener_c o l a ( ) { 1 * Creac i ón l l ave * 1 l l ave= f t o k ( " a s d . tx t " , ' K ' ) ; 1 * Ob t enc i ón de una c o l a de mens a j e s I *1 i d=ms gge t ( l l ave , I PC_CREAT 0 6 6 6 ) ; i f ( i d= = - 1 ) { p r i n t f ( " Error al obtener la c o l a de mens a j e s \ n " ) ; ex i t ( 2 ) ; Figura 3.12 - Código C del archivo de cabecera def_c o l a . h del Ejemplo 3 . 1 1 # i nc lude " de f_c o l a . h " ma in ( ) { int r ; 1* E s t ruc tura d e l mens a j e qu e s e va env i a r *1 s t ruc t { l ong t ipo ; char cuerpo [ 5 0 ] ; X; 1* Tamaño en byt e s d e l cuerpo del men s a j e X * 1 i n t L = s i z e o f ( X ) - s i z e o f ( X . t ipo ) ; 1* In i c i a l i z ac i ón de l men s a j e X * 1 X . t ip o = 1 ; s t rcpy ( X . cuerp o , " E s t e es e l men s a j e 1 " ) ; obtener_c o l a ( ) ; 1 * Enviar men s a j e a l a c o l a * 1 r=msgsnd ( i d , &X , L , O ) ; i f ( r= = - 1 ) p r i n t f ( " Er r o r a l enviar e l men s a j e X " ) ; Figura 3.13 - Código C del archivo del programa prog3 2 del Ejemplo 3 . 1 1 127 128 Ampliación de sistemas operativos # i n c l ude " de f_c o l a . h " ma i n { ) { int r ; 1 * E s t ru c tura del mens a j e que s e va r e c i b i r * 1 s t ruc t { l ong t ipo ; char cuerpo [ 5 0 ] ; Y; 1 * Tamaño en byt e s de l cuerp o del mens a j e Y * 1 i n t L = s i z e o f { Y ) - s i z e o f { Y . t i po ) ; / * I n i c i a l i z a c i ón de l men s a j e Y * / s t rcpy { Y . cuerp o , " \ 0 " ) ; obt ener_c o l a { ) ; 1 * Re c i b i r el pr imer men s a j e de t ipo 1 de l a c o l a * 1 r=msgrcv { i d , &Y , L , 1 , 0 ) ; i f { r= = - 1 ) p r i n t f { " Error a l r e c i b i r mens a j e \ n " ) ; el se p r i n t f { " \ n %s \ n " , Y . cuerp o ) ; Figura 3.14 - Código C del archivo del programa prog3 3 del Ejemplo 3 . 1 1 Señalar que la cola de mensajes i d sigue existiendo cuando finaliza la ejecución del proceso emisor y del proceso receptor. Esto puede comprobarse invocando al comando ipc s . Para eliminarla se puede usar el comando ipcrm. También se podría modificar los programas incluyendo al final la llamada al sistema msgc t l ( i d , I PC_RMI D , O ) . • Memoria compartida En los SOBUNIX un proceso no puede acceder al espacio de direcciones virtuales de otro proceso. Cuando se crea un proceso hij o con la llamada al sistema f o rk, el hijo hereda una copia del espacio de direcciones virtuales del padre. Los cambios que haga el hij o sobre su espacio de direcciones no serán visualizados por el padre, y viceversa. Esta característica es un mecanismo de protección muy eficiente. Sin embargo, en determinadas aplicaciones interesa que diferentes procesos tengan acceso a una misma región de memoria compartida, de tal forma que los cambios que haga un proceso sobre los datos almacenados en dicha región de memoria sean visibles al resto de procesos. Esta funcionalidad es SOBUNIX: planificación, sincronización y mecanismos IPC 129 la que proporciona el mecanismo IPC del System V conocido como memoria compartida. En definitiva una memoria compartida es una región de memoria que puede ser leída o escrita por varios procesos. Para operar sobre una región de memoria compartida un proceso primero tiene que crear la memoria u obtener acceso a la misma, si ya estaba creada, y a continuación adjuntarla a su espacio de direcciones virtuales. Por otra parte, si un proceso ya no va acceder más a una región de memoria compartida debe eliminarla de su espacio de direcciones virtuales. Para gestionar las regiones de memoria compartida existentes el núcleo mantiene una tabla. Cada entrada de esta tabla contiene, entre otros, los siguientes datos sobre una determinada región: creador, propietario, llave, permisos de acceso, tamaño en bytes de la región, puntero a la estructura que permite localizar la tabla de páginas asociada a la región y número de procesos que tienen adjuntada la región. El núcleo asocia a cada región de memoria compartida un identificador numérico entero positivo shmid que la identifica de manera univoca y que le permite localizar rápidamente su entrada asociada en la tabla de memoria compartida. El núcleo soporta las siguientes llamadas al sistema asociadas con las regiones de memoria compar­ tida: • shmid= shmge t ( l l ave , t amaño , ind ) . Esta llamada al sistema permite crear una nueva región de memoria compartida u obtener acceso a una región de memoria compartida ya creada. Requiere como argumentos de entrada la l l ave que se va asignar a la nueva región o la llave de la región ya creada, el tamaño en bytes de la región y una serie de indicadores ind que permiten especificar, entre otras cosas, los permisos de acceso a la región. Si la llamada se ejecuta con éxito devuelve el identificador shm i d de la región de memoria compartida. En caso contrario devuelve - l . • r= shmat ( shmi d , d i r , ind ) . Esta llamada al sistema permite adjuntar una región de memoria compartida en el espacio de direcciones virtuales del proceso invocante. Requiere como argumen­ tos de entrada: el identificador shmid de la región de memoria compartida, la dirección virtual del espacio del proceso d i r a partir de la cual se desearía que la región de memoria compartida fuese adjuntada, y una serie de indicadores i nd que permiten establecer el modo de acceso del proceso a la región de memoria. Por ejemplo, el indicador SHM_RDONLY establece que la región de memoria será accedida para solo lectura por el proceso. Si la llamada se ejecuta con éxito devuelve un puntero a la dirección virtual de inicio Dirvo de la región de memoria compartida. En caso de error devuelve el valor - l . Cuando se utiliza esta llamada al sistema lo más cómodo para el programador es que el núcleo sea el encargado de elegir la dirección Dirvo del espacio de direcciones virtuales del proceso a partir de la cual se adjuntará la región de memoria compartida. Para ello el argumento d i r se debe fijar a O. Por otra parte, si se fija d i r a un valor distinto de O, el núcleo intentará adjuntar la región de memoria compartida a partir de la dirección d i r deseada pero debe tenerse en cuenta que no siempre es posible. Conviene señalar que para una determinada región de memoria compartida por varios procesos el valor de Dirvo suele ser distinto en cada proceso. Ampliación de sistemas operativos 130 e • r = shmdt ( D i rvo ) . Esta llamada al sistema desajunta una región de memoria compartida del espa­ cio de direcciones virtuales de un proceso. Requiere como único parámetro de entrada la dirección virtual de inicio D i rvo de la región de memoria compartida. Si la llamada se ejecuta correctamente devuelve O. En caso contrario devuelve - l . • r = s hmc t l ( shmi d , o rden , arg ) . Esta llamada permite leer y configurar los datos de control que el núcleo mantiene sobre una región de memoria compartida. Su sintaxis es muy similar a la de las llamadas s emc t l y ms gqc t l . Ejemplo 3.12 Supóngase que se invoca al programa prog3 4 (ver Figura 3 . 1 5) desde la línea de órdenes de un intérprete de comandos, y que éste crea al proceso A para ejecutar este programa. El proceso A en primer lugar invoca a la función ini t_s em para inicializar con el valor O el semáforo creado en c omun . h (ver Figura 3 . 1 0) . En segundo lugar invoca a la llamada al sistema shmg e t para crear una región de memoria compartida y comprueba que la llamada se ha ejecutado correctamente. En tercer lugar invoca a la llamada al sistema shma t para adjuntarla en su espacio de direcciones a partir de la dirección contenida en el puntero contador. Además comprueba que la llamada se ha ejecutado sin errores. En cuarto lugar almacena en la región compartida el valor O. En quinto lugar invoca a la llamada al sistema f o r k para crear un proceso hijo B, el cual comparte el acceso a la región con su proceso padre. En sexto lugar el proceso A invoca a la función wa i t_s em ( ) para esperar a que termine de ejecutarse su hijo. Cuando se ejecuta el proceso B , éste incrementa en una unidad el valor almacenado en la región compar­ tida, muestra en pantalla el texto HIJO c ontado r = l realiza una operación s i gnal_s em ( ) para desbloquear a s u proceso padre y termina s u ejecución. Cuando el proceso A es despertado y planificado para ejecución muestra en pantalla el mensaje PADRE c ontado r = l invoca a l a llamada a l sistema shmdt para desajuntar la región compartida de s u espacio d e direcciones, invoca a la llamada al sistema shmc t l para borrar el recurso IPC y finaliza. Nótese que si el proceso A se planifica después que el proceso B entonces no se bloquea en el semáforo. En la Figura 3 . 1 6 se muestra el código del programa prog3 5 similar a prog3 4 donde no existe una región de memoria compartida. En dicho caso las modificaciones que hace el proceso B en su espacio de direcciones no son visibles por el proceso A, con lo que la traza de ejecución de este programa es : HIJO contado r = l PADRE c ontador= O • SOBUNIX: planificación, sincronización y mecanismos IPC # i nc lude " c omun . h " ma in ( ) { int i d ; i n t * C on t ador=NULL ; i n i t_s em ( O ) ; 1 * Crear una r e g i ó n de memo r i a c ompar t i da * 1 I i d= shmge t ( I PC_PRIVATE , s i z e o f ( i nt ) , I PC_CREAT 0 6 0 0 ) ; if ( id== - 1 ) p r i n t f ( " Error a l c r ear l a r e g i ón de memo r i a c ompar t i da \ n " ) ; ex i t ( 2 ) ; 1 * Adj untar la reg i ón al c ontador= ( i nt if e spac i o de d i r e c c i on e s v i r tua l e s del p ro c e s o * 1 * ) shma t ( i d , O , O ) ; ( c on t ado r = =NULL ) { p r i n t f ( " Error a l l i gar la reg i ón de memo r i a \ n " ) ; ex i t ( 2 ) ; * C On t ador= O ; i f ( f o rk ( ) = = O ) { * C on t ador= * c on t a do r + l ; p r i n t f ( " H IJO c ontado= %d\ n " , * C Ontado r ) ; s i gnal_s em ( ) ; exi t ( 0 ) ; wa i t _ s em ( ) ; p r i n t f ( " PADRE c on t ado= %d \ n " , * C On t ado r ) ; 1 * D e s a j un tar la r e g i ó n de memo r i a c ompar t ida d e l e spac i o * 1 shmdt ( c ontador ) ; 1 * Borrar l a r e g i ó n de memo r i a c ompar t i da * 1 s hmc t l ( i d , I PC_RMI D , O ) ; Figura 3.15 - Código C del archivo del programa prog3 4 del Ejemplo 3 . 1 2 131 132 Ampliación de sistemas operativos # i nc lude " c omun . h " ma i n { ) { i n t c ontador= O ; i f { fork { ) = = 0 ) { c ontador=c ontador+ l ; p r i n t f { " H IJO c o n t ado= %d \ n " , contador ) ; s i gnal_s em { ) ; exi t { 0 ) ; wa i t_s em { ) ; p r i n t f { " PADRE c o n t ado= %d \ n " , c ontado r ) ; Figura 3.16 - Código C del archivo del programa prog3 5 del Ejemplo 3 . 1 2 Ventajas e inconvenientes de los mecanismos IPC del System V Los mecanismos IPC del System V poseen una gran versatilidad ya que pueden usarse para diferentes funciones : • Los semáforos se pueden usar para garantizar la exclusión mutua en el uso de un recurso crítico o para sincronización de procesos. Su principal inconveniente es que si no se colocan adecuadamente en el código de la aplicación o se realiza una operación incorrecta pueden producir condiciones de carrera e interbloqueos. En aplicaciones con un código largo y con decenas de semáforos, incurrir en estas equivocaciones es relativamente fácil. • Las colas de mensajes se pueden usar para las mismas funciones que los semáforos y además para el intercambio de pequeñas cantidades de datos. Su principal inconveniente es que la transferencia de un mensaje requiere de dos copias en memoria lo que ralentiza la operación. En primer lugar el mensaje enviado por un proceso emisor a una cola de mensajes es copiado desde el espacio de direcciones del proceso en un buffer del núcleo. Posteriormente cuando el mensaje es extraído de la cola se copia desde el buffer del núcleo al espacio de direcciones del proceso receptor. Otro inconveniente de las colas de mensajes es que no es posible enviar un mensaje a varios receptores, ya que cuando un receptor recibe un mensaje éste es extraído de la cola con lo que ya no se encuentra disponible para otros receptores. Otra forma más eficiente y versátil de implementar el paso de mensajes es mediante el uso de STREAMS [Vahalia, 1 995] los cuales fueron desarrollados para soportar las comunicaciones en redes de computadores, aunque pueden ser utilizados también para paso de mensajes entre proce­ sos ejecutándose en una misma máquina. SOBUNIX: planificación, sincronización y mecanismos IPC • 133 Las regiones de memoria compartida se pueden usar para el intercambio rápido de pequeñas o grandes cantidades de datos. Su principal desventaja es que para evitar condiciones de carrera es necesario utilizar algún mecanismo de sincronización, como por ejemplo semáforos o el paso de mensajes. Cada tipo x con x= { s em, msg, shrn} de mecanismos IPC dispone de una llamada al sistema xget para crear o acceder a un recurso IPC del tipo x y de una llamada al sistema xc t l para leer o configurar los datos de administración y control que el núcleo mantiene sobre un determinado recurso IPC del tipo x. Estos datos también pueden ser visualizados en un intérprete de comandos usando el comando ipc s . Una vez creado u n determinado recurso IPC del tipo x éste perdura incluso aunque todos los procesos que lo estuvieran utilizando ya hubieran terminado. Esta característica en determinados escenarios puede ser realmente útil. Sin embargo también puede suponer un desperdicio de memoria. Debe tenerse en cuenta que el núcleo no implementa mecanismos para controlar si un determinado recurso IPC ya no va a ser utilizado nunca más. Es responsabilidad del programador eliminar dicho recurso. Un proceso perteneciente al usuario propietario, al usuario creador o al superusuario para eliminar un recurso IPC del tipo x debe invocar a la llamada al sistema xc t l con el argumento I PC_RMID. También se puede utilizar el comando ipcrm desde la línea de órdenes de un intérprete de comandos. La eliminación del recurso IPC supone liberar el espacio ocupado por las estructuras de datos asociadas al recurso y despertar a los procesos dormidos en el recurso. Otro de los problemas que presentan los mecanismos IPC del System V es en relación con la segu­ ridad. Normalmente los recursos IPC son utilizados por aplicaciones que permiten el acceso a procesos de diferentes usuarios, con lo que los permisos de acceso del recurso no suelen ser muy restrictivos. En consecuencia un proceso no autorizado podría acceder al recurso si consigue adivinar la llave o el identificador asociado al recurso. 3.5.2. Otros mecanismos IPC Señales Las señales son un mecanismo muy útil de notificación de eventos (ver sección 2 . 3 . 3 ) que también pueden usarse como mecanismo IPC para la sincronización de procesos. Por ejemplo, un proceso A puede invocar a una llamada al sistema pau s e para suspender su ejecución mientras otro proceso B realiza una determinada tarea. Cuando B haya finalizado la tarea invocará una llamada al sistema ki 1 1 para enviar una determinada señal al proceso A para que despierte y pueda continuar su ejecución. El principal inconveniente de usar las señales como mecanismo IPC es el retardo que existe desde que el emisor genera una señal hasta que es recibida por el receptor. Para enviar una señal el proceso emisor debe ser planificado para ejecución e invocar la llamada al sistema adecuada, por ejemplo ki l l . Posteriormente el núcleo debe interrumpir al proceso receptor, s i éste estaba ejecutándose, y manipular su pila de usuario para poder ejecutar el manipulador de la señal cuando el proceso retorne a modo usuario. 134 Ampliación de sistemas operativos Thberías Una tubería es un canal de comunicación unidireccional por el que se puede transmitir flujos de datos no estructurados con un tamaño máximo fijo. Un proceso emisor puede enviar (escribir) datos por un extremo de la tubería y un proceso receptor puede recibir (leer) los datos por el otro extremo. Los datos son recibidos por orden de envío, es decir, siguiendo una organización de tipo FIFO. Una vez que los datos son leídos de la tubería por un proceso receptor dej an de estar disponibles para ser leídos por otros procesos receptores. Si un proceso emisor intenta escribir en una tubería que está llena se bloquea hasta que exista espacio en la tubería. Asimismo un proceso receptor que intente leer en una tubería que esté vacía se bloqueará hasta que existan datos en la tubería. Usadas como mecanismo IPC las tuberías resultan bastante eficientes aunque presentan los siguientes inconvenientes [Vahalia, 1 995] : • No permiten el envío de información a varios receptores, ya que cuando se lee los datos de la tubería estos son borrados de la misma. • Un proceso que escribe en la tubería no puede especificar que receptor debe leer los datos. • Si un proceso emisor transmite por la tubería varios mensajes de diferente longitud, como los datos en la tubería son un flujo de bytes no estructurado, el proceso receptor no puede saber donde termina un mensaje y donde empieza otro. En los SOBUNIX las tuberías son gestionadas como los archivos (ver sección 5.2. 1 ), aunque no requieren la realización de operaciones de E/S a disco, ya que los datos de la tubería no se mantienen en memoria secundaria sino en la memoria principal. Cuando se crea una tubería el núcleo le asigna un par de descriptores de archivos Do y D 1 • El descriptor Do se utiliza como argumento de la llamada al sistema read para leer en la tubería. Mientras que D1 se utiliza como argumento de wr i te para escribir en la tubería. Existen dos posibles tipos de tuberías: • Tuberías sin nombre. Se crean haciendo uso de la llamada al sistema r=pipe ( f i l d e s ) . Esta llamada necesita como argumento de entrada la dirección de un array entero f i l d e s de dos ele­ mentos para almacenar los descriptores de archivos Do y D 1 • Si la llamada al sistema se ejecuta con éxito devuelve O en caso contrario devuelve - 1 . Los intérpretes de comandos utilizan estas tuberías para hacer que la salida de un programa sea la entrada de otro programa (ver sección 1 .5 . 1 ) . Es el propio intérprete el que se encarga de manipular los descriptores de archivos de la tubería devueltos por la llamada al sistema pipe para hacer que un programa sea el emisor y otro el receptor. Las tuberías sin nombre se caracterizan porque solo pueden ser utilizadas por procesos relacio­ nados genealógicamente. Además no son persistentes, es decir, son eliminadas al finalizar los procesos que las utilizan. La implementación de las tuberías sin nombre depende de cada SOBUNIX. Algunos SOBUNIX las implementan usando las mismas estructuras de datos que utilizan para la gestión de archivos. SOBUNIX: planificación, sincronización y mecanismos IPC 135 Otros SOBUNIX las implementan usando conectores (sockets) (ver sección 5 .6) o STREAMS [Vahalia, 1 995 ] . • Tuberías con nombre, también denominadas archivos PIFOs o simplemente PIFOs. Se crean me­ diante la llamadas al sistema rnknod (ver sección 5 . 5 .2) o rnk f i f o , o desde un intérprete de co­ mandos usando los comandos homónimos. Las tuberías con nombre se caracterizan porque se les puede asignar una ruta dentro del sistema de archivos. Cualquier proceso aunque no sea hij o del proceso creador de la tubería puede acceder a la tubería si conoce la ruta de la tubería y dispone de los permisos adecuados. Asimismo, los tuberías con nombre son persistentes, es decir, continúan existiendo aunque todos los procesos que lo estaban utilizando ya hayan terminado. Para eliminarlas se debe usar la llamada al sistema unl ink o el comando homónimo. 3.6. Resumen El planificador es la parte del núcleo de un sistema operativo encargado de decidir qué procesos (o hilos del núcleo) pasarán a ser ejecutados en los procesadores disponibles. Nótese que si el núcleo del SOBUNIX considerado soporta hilos del núcleo entonces el planificador utiliza como unidad o entidad planificable a los hilos del núcleo. En caso contrario utilizará como unidad planificable a los procesos. En la mayoría de los SOBUNIX, el planificador implementa una planificación global basada en múltiples colas de prioridad y realimentación. En este tipo de planificación, cada unidad planificable tiene asignada una determinada prioridad que es un número entero positivo comprendido entre O y un cierto valor máximo. En algunos SOBUNIX la prioridad más alta se asocia con el valor O de prioridad, mientras que en otros se toma j ustamente el criterio contrario. El planificador siempre intenta que se ejecute la unidad planificable de mayor prioridad, aunque esto no siempre es posible si el núcleo es no expropiable . El código del núcleo de los SOBUNIX es reentrante esta característica posibilita el que varias uni­ dades planificables (procesos o hilos del núcleo) ejecutándose en modo núcleo puedan estar ejecutando concurrentemente la misma región de código del núcleo. Obviamente en el código del núcleo existen secciones críticas asociadas a diferentes recursos críticos, como por ejemplo las estructuras de datos del núcleo. Para garantizar la exclusión mutua en la ejecución de una sección crítica del núcleo asociada a un cierto recurso crítico, el núcleo utiliza diferentes mecanismos de sincronización, entre los más utilizados se encuentran los cerrojos y los semáforos. Por otra parte, los SOBUNIX ponen a disposición de los programadores de aplicaciones diferentes mecanismos para garantizar la exclusión mutua en el uso de recursos críticos, sincronizar su ejecución, esperar por la aparición de algún evento o intercambiar información. A este conjunto de mecanismos se les conoce de forma general como mecanismos de comunicación entre procesos, o más abreviada­ mente como mecanismos IPC. Es el programador de aplicaciones el que decide qué mecanismos IPC utilizar y dónde utilizarlos dentro del código de sus aplicaciones. El uso de estos mecanismos requiere la invocación de las llamadas al sistema oportunas. El núcleo se encarga de atender dichas llamadas al 136 Ampliación de sistemas operativos sistema y mantener las estructuras de datos necesarios para soportar estos mecanismos IPC . Entre los mecanismos IPC soportados en la mayoría de SOBUNIX se encuentran los mecanismos IPC del System V (semáforos, colas de mensajes y memoria compartida), las señales y las tuberías. 3. 7. Lecturas recomendadas Si se desea obtener una explicación adicional de los contenidos tratados en este capítulo se pueden consultar, por ejemplo: los capítulos 5, 6 y 7 de [Vahalia, 1 995] , los capítulos 9 y 1 0 de [Márquez, 2004], los capítulos 3 , 4 y 1 7 de [McDougall y Mauro, 2006] y el capítulo 1 0 de [Tanenbaum, 2009] . 3.8. Autoevaluación 3.1. Explicar razonadamente las características generales de la planificación de procesos multihilos en SOBUNIX. (Respuesta en sección 3 .2. 1 ) 3.2. ¿Qué especificaciones tiene asociada cada clase de planificación? ¿Qué dos tipos de funciones utiliza el planificador para manipular las clases de planificación? (Respuesta en sección 3 .2. 1 ) 3.3. ¿Qué llamadas al sistema s e encuentran disponibles en algunos SOBUNIX para controlar la prio­ ridad de un proceso? (Respuesta en sección 3 .2. 1 ) 3.4. Explicar razonadamente l a planificación usada en B S D 4 . 3 . (Respuesta en sección 3 .2.2) 3.5. Explicar razonadamente la planificación usada en Solaris. (Respuesta en sección 3 . 2.2) 3.6. Dibujar un diagrama con la distribución de las prioridades de ejecución usadas en Solaris en fun­ ción del tipo de clases de planificación y las interrupciones. (Respuesta en sección 3 .2.2) 3.7. ¿Qué información se mantiene en una tabla de despacho de Solaris? (Respuesta en sección 3 .2.2) 3.8. ¿Qué información permite visualizar el comando di spadm i n en Solaris? ¿ Y pr i o cn t l ? (Respuesta en sección 3 . 2.2) 3.9. ¿Qué es la prioridad en modo usuario en Solaris? ¿De qué componentes consta? (Respuesta en sección 3 .2.2) 3.10. ¿Cómo se puede configurar la prioridad de usuario en Solaris ? (Respuesta en sección 3 .2.2) 3. 1 1 . ¿En Solaris qué eventos producen que se modifique la prioridad (global y en modo usuario)? (Respuesta en sección 3 . 2.2) 3. 12. ¿Qué operaciones realizadas periódicamente por las funciones dependientes de las clases de pla­ nificación pueden cambiar las prioridades de los hilos en S olaris? (Respuesta en sección 3 .2.2) SOBUNIX: planificación, sincronización y mecanismos IPC 137 3.13. Defina los siguientes conceptos : núcleo no expropiable, núcleo expropiable y puntos de expropia­ ción. (Respuesta en sección 3 .2.3) 3. 14. ¿Por qué es necesaria la existencia de mecanismos de sincronización en el núcleo de un SOBU­ NIX? ¿Cuáles son los mecanismos de sincronización más utilizados? (Respuesta en sección 3 . 3 ) 3. 15. Explicar qué es un cerrojo y qué operaciones básicas soporta. (Respuesta e n sección 3 . 3 . 1 ) 3. 16. Explicar qué es un spinlock, u n cerrojo con bloqueo, un cerrojo adaptativo, un cerrojo mutex y un cerrojo R/W. (Respuesta en sección 3 . 3 . 1 ) 3. 17. Explicar cuándo se realizan las operaciones de dormir y despertar un hilo y en qué consisten. (Respuesta en sección 3 .4) 3.18. ¿Qué es un canal de espera? ¿Cómo se le asocia una cola de hilos dormidos ? ¿Qué problemas presenta dicha asociación? (Respuesta en sección 3 .4) 3.19. ¿Qué es un turnstile? ¿Cómo gestiona el núcleo de Solaris los turnstiles? (Respuesta en sección 3 .4) 3.20. Enumerar los principales mecanismos IPC disponibles en los SOBUNIX. (Respuesta en sección 3 . 5 ) 3.21. ¿Qué estructuras mantiene el núcleo para soportar los mecanismos IPC del System V? ¿Qué infor­ mación contiene dichas estructuras por cada recurso IPC? (Respuesta en sección 3 . 5 . 1 ) 3.22. ¿Para qué utiliza el núcleo el identificador numérico de un recurso IPC? ¿Cuándo se l o asigna? (Respuesta en sección 3 . 5 . 1 ) 3.23. Explicar la implementación de los semáforos como mecanismos IPC del System V. (Respuesta en sección 3 . 5 . 1 ) 3.24. Explicar la utilidad y l a sintaxis de las siguientes llamadas al sistema: s emg e t , s emop y s emc t l . (Respuesta en sección 3 . 5 . 1 ) 3.25. Explicar la gestión e implementación de las colas de mensajes. (Respuesta en sección 3 . 5 . 1 ) 3.26. Explicar la utilidad y l a sintaxis de las siguientes llamadas al sistema: msgge t , msgsnd, msgrcv y msgc t l . (Respuesta en sección 3 . 5 . 1 ) 3.27. Explicar qué e s y cómo s e gestiona una región d e memoria compartida. (Respuesta e n sección 3 .5 . 1 ) 3.28. Explicar l a utilidad y l a sintaxis de las siguientes llamadas al sistema: shmg e t , s hma t , shmdt y shmc t l . (Respuesta en sección 3 . 5 . 1 ) 3.29. Explicar razonadamente las ventajas y los inconvenientes de los mecanismos IPC del System V. (Respuesta en sección 3 . 5 . 1 ) 138 Ampliación de sistemas operativos 3.30. ¿Cuál es el principal inconveniente de usar las señales como mecanismo IPC? (Respuesta en sección 3 . 5 .2) 3.31. Explicar qué es y cómo funciona una tubería. (Respuesta en sección 3 . 5 .2) 3.32. Explicar los principales inconvenientes que presenta el uso de las tuberías como mecanismo IPC. (Respuesta en sección 3 . 5 .2) 3.33. Explicar cómo gestiona el núcleo las tuberías. (Respuesta en sección 3 . 5 .2) 3.34. Explicar cómo se crean y las principales características de las tuberías sin nombre y de los archivos PIFOs. (Respuesta en sección 3 . 5 .2) 3.9. Ejercicios 3.1. En un sistema BSD 4.3 las 32 colas de procesos preparados se gestionan con una algoritmo de turno rotatorio con un cuanto de 1 00 ms ¿Qué valor toma el cuanto en el caso de las colas asociadas al rango de prioridades [0, 49] ? ¿Por qué? ¿De forma efectiva qué algoritmo se estaría aplicando realmente en dichas colas? 3.2. En un sistema BSD 4.3 se han creado en el instante t = O ms tres procesos A, B y C de forma prácticamente simultánea. Los procesos han sido colocados en la misma cola de procesos en el estado preparado para ejecución en el siguiente orden: primero A, luego B y después C. Los tres procesos tienen una prioridad inicial en modo usuario igual a 90 y un factor de amabilidad igual 20. Determinar cómo evoluciona el valor de la prioridad en modo usuario para cada proceso en el intervalo de tiempo [0, 1 000] ms. Suponer que el tic de reloj se produce cada 1 0 ms, un cuanto de 1 00 ms, que la prioridad en modo usuario base es igual a 50, y que el factor de decadencia es constante e igual a 0,5 . Además se debe suponer que el tiempo de ejecución del manipulador de la interrupción de reloj , la rutina roundrob i n ( ) , la rutina s chedcpu ( ) y el tiempo de un cambio de contexto es despreciable. Suponer también que los tres procesos durante su ejecución no invocan a ninguna llamada al sistema y que no existe en el sistema ningún otro proceso de mayor prioridad en el estado preparado para ejecución. 3.3. En la Tabla 3 . 1 se muestra la tabla de despacho de la clase TS del planificador de un sistema SVR4, en el cual se basa el planificador de Solaris. Supuesto un proceso con t s_cpupr i = 1 y t s_upr i = 1 4 determinar el valor que se le asigna a t s_cpup r i y a t s_umdpri en los siguientes casos: a) Expira el cuanto asignado al proceso. b) El proceso ha estado esperando para poder con­ sumir su cuanto más de 5 segundos. e) El proceso retorna a modo usuario tras haberse bloqueado en espera de un evento durante la realización de una llamada al sistema. 3.4. En la Figura 3 . 1 7 se muestra el código C del programa j l O . Supóngase que en el directorio de trabaj o actual residen los archivos wert . txt y j 1 0 , y que el archivo wert . txt puede ser leído por cualquier usuario. SOBUNIX: planificación, sincronización y mecanismos IPC 139 a) Explicar razonadamente el funcionamiento de j 1 o si se invoca desde el intérprete de coman­ dos mediante la orden: j 1 o b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando este programa en un SOBUNIX. 3.5. En la Figura 3 . 1 8 se muestra el código C del programa s 1 0 . a) Explicar razonadamente el funcionamiento de s 1 0 si se invoca desde el intérprete de coman­ dos mediante la orden: s l O s i s t emas_opera t i vo s_2 b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando este programa en un SOBUNIX. 3.6. En la Figura 3 . 1 9 se muestra el código C de los programas j O S a y j 0 8b. a) Explicar razonadamente el funcionamiento de estos programas si un usuario, que dispone de los permisos adecuados, escribe primero en un intérprete de comandos la orden j O Ba & y al cabo de 1 0 segundos la orden j o Bb. b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando estos programas en un SOBUNIX. ts_quantum 1 00 1 00 . . 80 ... 20 ... 10 . ts_tqexp o o ... 7 ... 30 ... 49 ts_slpret 10 11 ... 25 ... 50 ... 59 ts_maxwait 5 5 ... 5 ... 5 ... 5 ts_lwait 10 11 ... 25 ... 50 ... 59 PRIORITY LEVEL Tabla 3.1 - Tabla de despacho de la clase TS del Ejercicio 3.3 o 1 ... 15 ... 40 . . . 59 Ampliación de sistemas operativos 140 # i nc lude < s t dl ib . h> # i nc lude < s td i o . h> # i n c l ude < sys / ipc . h> # i nc lude < sys / s em . h> ma i n ( ) { int x , u ; key_t y ; s t ruc t s embu f z [ 2 ] ; z [ O ] . s em_ f l g= O ; z [ 1 ] . s em_f l g= O ; y= f t ok ( " we r t . tx t " , ' x ' ) ; if ( y = = ( key_t ) - 1 ) p r in t f ( " mens a j e wert " ) ; exi t ( 3 ) ; I x= s emge t ( y , 2 , I PC_CREAT 0 6 0 0 ) ; u= s emc t l ( x , O , S ETALL , O ) ; i f ( f o rk ( ) = = 0 ) { z [ O ] . s em_num= O ; z [ O ] . s em_op = 1 ; u = s emop ( x , z , 1 ) ; z [ O ] . s em_num= 1 ; z [ O ] . s em_op= - 1 ; u = s emop ( x , z , 1 ) ; p r i n t f ( " \ nmens a j e a s d \ n " , u ) ; el se i f ( f o rk ( ) = = O ) { s l e ep ( 2 ) ; z [ O ] . s em_num= O ; z [ O ] . s em_op= - 1 ; u = s emop ( x , z , 1 ) ; p r i n t f ( " \ nmen s a j e f gh \ n " , u ) ; z [ O ] . s em_num= 1 ; z [ O ] . s em_op= 1 ; u = s emop ( x , z , 1 ) ; Figura 3.17 - Código C del programa j 1 0 del Ejercicio 3 .4 SOBUNIX : planificación, sincronización y mecanismos IPC # i nc lude < s td l i b . h> # i nc lude < s t di o . h> ma i n { i n t b , char * C [ ] ) { int p f d [ 2 ] ; int a ; char bu f ; if {b == 2) { if { pipe { p f d ) == - 1 ) perror { " E l " ) ; exi t { - 1 ) ; a = f o rk { ) ; if {a == 0 ) close ( p fd [ l ] ) ; wh i l e { read { p f d [ O ] , &bu f , l ) > 0) { wr i t e { l , &bu f , l ) ; wr i t e { l , " \n " , 1) ; close { p fd [ O ] ) ; els e c l o s e { p fd [ O ] ) ; wr i t e { p f d [ l ] , e [ 1 ] , 10 ) ; c l o s e { p fd [ l ] ) ; p r i n t f { " \ nMen s a j e \ n " ) ; exi t { - 2 ) ; Figura 3.18 - Código C del programa s l O del Ejercicio 3 . 5 141 Ampliación de sistemas operativos 142 1 * C ó d i g o del programa j O S a *1 # i nc lude < sys / s t a t . h> # i n c l ude < f c n t l . h> ma i n ( ) { int r , h ; char * b ; b= ( char * ) ma l l o c ( 9 * s i z e o f ( char ) ) ; f o r ( h = O ; h< 9 ; h+ + ) { * ( b+ h ) = ' \ o ' ; 1 mknod ( " a s d " , S_I F I FO 0 6 6 6 , O ) ; r = op en ( " a s d " , 0 6 6 6 ) ; read ( r , b , 7 ) ; p r i n t f ( " \ n %s \ n " , b ) ; close ( r ) ; unl ink ( " a s d " ) ; 1 * C ó d i g o de l programa j 0 8 b * 1 # i nc lude < sys / s t a t . h> # i nc lude < f c n t l . h> ma i n ( ) { int r ; char b [ ) = " Texto 1 " ; r = op en ( " a s d " , 0 6 6 6 ) ; wr i t e ( r , b , 8 ) ; close ( r ) ; Figura 3.19 - Código C de los programas j O S a y j 0 8b del Ejercicio 3 . 6 Capítulo 4 SOBUNIX: administración de memoria Objetivos docentes Los objetivos docentes de este capítulo son los siguientes: • Conocer cómo se realiza en los SOBUNIX la gestión del espacio de direcciones virtuales de un proceso. • Saber cómo se realiza en los SOBUNIX el mapeo de archivos en la memoria virtual de un proceso. • Saber qué es y cómo se gestiona la memoria anónima en los SOBUNIX. • Conocer cómo se implementa la memoria compartida en los SOBUNIX. • Conocer las características más relevantes de la traducción de direcciones físicas a virtuales en los SOBUNIX. • Saber cómo se realiza en los SOBUNIX la gestión de la memoria física. • Conocer cómo se gestiona el área de intercambio en los SOBUNIX. • Conocer las características más importantes de la gestión de la memoria perteneciente al núcleo en los SOBUNIX. • Conocer las principales llamadas al sistema y los comandos disponibles en los SOBUNIX para el control de la memoria virtual. 1 43 Ampliación de sistemas operativos 144 4. 1. Introducción El subsistema de administración de memoria es el componente del núcleo de un sistema operativo encargado de administrar, en colaboración con el hardware, la memoria principal entre los restantes subsistemas del núcleo y entre todos los procesos de usuario que se ejecutan en el computador. Para realizar esta tarea se pueden usar diferentes técnicas de administración de memoria. En los SOBUNIX se utiliza la técnica de paginación por demanda la cual posibilita la implementación de la memoria virtual. El uso de la memoria virtual evita tener cargado en la memoria principal todo el espacio de direc­ ciones virtuales de un proceso. Solo se cargan las partes del espacio de direcciones virtuales del proceso que se van referenciando durante su ejecución. Ello posibilita tener cargados parcialmente en memoria principal un número mayor de procesos en el estado preparado, es decir, aumentar el grado de multipro­ gramación. Además el uso de la memoria virtual también permite ejecutar procesos cuyos espacios de direcciones virtuales sean mayores que el tamaño de la memoria principal. En la técnica de demanda de página la memoria física se divide en bloques de tamaño fijo Sp de­ nominados marcos de página. Además el espacio de direcciones virtuales de un proceso se divide en bloques del mismo tamaño Sp denominados páginas. En un determinado instante de tiempo un marco de página j puede estar libre u ocupado por una página i de un cierto proceso. Una página solo es cargada en memoria cuando se produce un fallo de página, es decir, se referencia a una dirección virtual contenida en una página i que no está cargada en ningún marco j de memoria principal. La página se construirá a partir del archivo ejecutable ubicado en memoria secundaria o se cargará desde el área de intercambio si tuvo que ser intercambiada fuera de memoria principal. Si cuando se produce un fallo de página no existe espacio libre en memoria principal, se debe seleccionar alguna página k para ser reemplazada por la página j. Si la página k fue modificada, antes de ser reemplazada deberá ser copiada en la memoria secundaria. La implementación de la memoria virtual mediante demanda de página requiere de ciertos requisitos en el hardware del computador. En primer lugar el procesador debe soportar el reinicio de instrucciones después de que la página cuya referencia produjo un fallo de página sea cargada en memoria principal. Por otra parte se requiere de un componente hardware denominado unidad de gestión de memoria (Me­ mory Management Unit, MMU), que se encarga de realizar la traducción de una dirección virtual e una dirección física. También el hardware suele suministrar mecanismos de protección de las páginas. Si se intenta realizar un acceso no autorizado el hardware produce una excepción que debe ser atendida por el núcleo. La implementación de la memoria virtual mediante demanda de página requiere que el subsistema de gestión de memoria realice, entre otras, las siguientes tareas : • Gestionar el espacio de direcciones virtuales de los procesos. • Crear y mantener los mapas de traducción de memoria virtual a memoria física, es decir, las tablas de páginas. • Tratamiento de los fallos de página. • Reemplazamiento de páginas. SOBUNIX: administración de memoria 145 • Copia de las páginas modificadas a memoria secundaria. • Asignación de páginas de memoria principal a los subsistemas del núcleo y a los procesos. • Control del número de procesos cargados en memoria principal, es decir, del grado de multipro­ gramación. Este capitulo está dedicado a describir la administración de memoria en los S OBUNIX. En primer lugar se estudia la gestión del espacio de direcciones virtuales de un proceso. En segundo lugar se analiza la traducción de direcciones virtuales a direcciones físicas. En tercer lugar se describe la gestión de la memoria física. En cuarto lugar se estudia la gestión del área de intercambio. Finalmente se describe la gestión de la memoria perteneciente al núcleo. 4.2. Gestión del espacio de direcciones virtuales de un proceso en SOBUNIX 4.2.1. Organización del espacio de direcciones virtuales de un proceso En el espacio de direcciones virtuales de un proceso se pueden distinguir diferentes regiones o seg­ mentos 1 , cada una de las cuales delimita un área contigua de memoria virtual : • Región de código, también denominada región de texto. En la mayoría de arquitecturas comien­ za en la primera dirección virtual del espacio de direcciones virtuales del proceso. Contiene las instrucciones máquina del código del programa para cuya ejecución se ha creado el proceso. El contenido y el tamaño de la región de código no varían durante la ejecución del proceso. • Región de datos. Se sitúa a continuación de la región de código. Contiene las constantes y variables usadas en el programa. Esta región se divide en dos partes: - Región de datos inicializados. Contiene aquellos datos que tienen asignado un valor inicial de forma explicita. - Región de datos no inicializados2 • Contiene aquellos datos que no han sido inicializados de forma explicita. Estos datos por defecto se inicializan a cero. • Montículo (heap). El tamaño de la región de datos puede ser aumentado dinámicamente durante la ejecución del proceso. Para ello se puede utilizar la función de librería ma l l o c que internamente hace uso de las llamadas al sistema brk o sbrk. Al espacio extra añadido a la región de datos con respecto a su tamaño inicial se le denomina montículo (heap). El sentido de crecimiento del 1 Aunque en la literatura reciben el mismo nombre, los segmentos o regi ones del espacio de direcciones virtuales de un proceso no deben confundirse con los segmentos utilizados en la técnica de segmentación de memoria cada uno de los cuales tiene un espacio de direcciones independiente. 2 A esta región también se le denomina en la literatura como región BSS (Block Started by Symbol, bloque inicializado con símbolos). 146 Ampliación de sistemas operativos montículo es establecido por cada SOBUNIX dependiendo del hardware. Para liberar un espacio asignado en el montículo se utiliza la llamada al sistema f r e e . • Región de pila (stack) . E n esta región s e implementa la pila de usuario del proceso, o las pilas de los hilos del proceso. Inicialmente contiene todas las variables del entorno del intérprete de co­ mandos desde el que fue invocado el programa. Además contiene la cadena de caracteres asociada a la orden con que fue invocado el programa, es decir, el nombre del programa y sus argumentos de entrada. Cada vez que se invoca a una función durante la ejecución del programa se añade un marco lógico a la pila con toda la información necesaria para poder continuar con la ejecución del programa cuando se termine de ejecutar dicha función. Cuando la región de pila crece por encima del espacio inicialmente asignado se produce una excepción hardware que tiene que ser tratada por el sistema operativo. El manipulador de esta excepción entre otras acciones aumenta el tamaño de la pila en un rango de direcciones equivalente a una página. La localización de la pila y su sentido de crecimiento son establecidos por cada SOBUNIX en función del hardware. • Región de librerías. Esta región contiene las diferentes librerías de funciones utilizadas por el pro­ grama. Estas librerías suelen ser compartidas por varios programas y son implementadas mediante el mapeo en memoria de los archivos (ver sección 4.2.2) asociados a las librerías. En una librería se distinguen dos partes : código y datos. Otros posibles tipos de regiones que se pueden distinguir en el espacio de direcciones virtuales de un proceso son las regiones de memoria compartida usadas como recurso IPC y los archivos mapeados en memoria (ver sección 4.2.2). Precisamente las librerías son un ejemplo de este último tipo de regiones. La distribución de las regiones y el tamaño máximo del espacio de direcciones virtuales de un proceso son fijados por cada SOBUNIX en función del hardware. e Ejemplo 4.1 En la Figura 4. 1 se muestra la organización del espacio de direcciones virtuales que realiza Solaris en diferentes arquitecturas. En la arquitectura SPARC V7 de 32 bits (ver Figura 4. 1 a) el espacio de direcciones virtuales puede tener un tamaño máximo de 4 GiB : 3,5 GiB están disponibles para el espacio de direcciones de un proceso y 5 1 2 MiB están reservados para el núcleo. En la arquitectura Intel x86 de 3 2 bits (ver Figura 4. 1 b), de los 4 GiB disponibles de memoria virtual, 3,75 GiB pueden ser utilizados para el espacio de direcciones de un proceso y 256 MiB están reservados para el núcleo. En la arquitectura SPARC V9 de 3 2 bits (ver Figura 4. 1 c) existen espacios de direcciones independientes para el núcleo y para los procesos. En esta arquitectura el tamaño máximo del espacio de direcciones de un proceso es de 3 ,99 GiB ya que 4,078 MiB de las direcciones más altas están reservados para el arranque. • SOBUNIX: administración de memoria OxFFFFFFFF ,....----, OxFFFFFFFF Espacio del núcleo (256 MiB) Espacio del núcleo (5 1 2 MiB) OxEO O O O O O O 147 - � - - - - - - - - - - - - - . OxFFFFF FFF 1 OxFFBEC O O O 1------1 1 Pila 1------l 1 Librerías OxDFFFE O O O OxFF 3 DC O O O Pila 1 • 1 1 1 1 1 1 1 OxDF 7 F 9 0 0 0 1------l t Librerías 1 1 1 Montículo Librerías 1 1 J 1-------; Datos 1 1 t Código Montículo 1 1 1 Montículo Ox 8 0 4 8 0 0 0 Pila Datos Código OxO O O l O O O O 1 1 1 1 OxO O O O O O O O L - - - - - - - - - - - - - - - 1 (a) 1 1 1 1 1 1 1 1 1 1 1 • Datos ' 1 1 Código OxO O O l O O O O 1 Ox O O O O O O O O L - - - - - - - - - - - - - - - 1 (b) OxO O O O O O O O 1 1 1 1 1 L - - - - - - - - - - - - - - -' (e) Espacio de direcciones virtuales de un proceso en Solaris con una arquitectura [McDougall Mauro, 2006] : (a) SPARC V7 de 32 bits (sun4d), (b) Intel x86 de 32 bits y (e) SPARC V9 de 32 bits (sun4u) Figura 4.1 - y El tipo de acceso permitido al contenido apuntando por una determinada dirección virtual depende de cada tipo de región. Al contenido de la región de código o de la parte de código de una librería solo se puede acceder para realizar operaciones de lectura y ejecución, mientras que al resto de regiones se puede acceder para realizar operaciones de lectura y escritura. Para obtener información sobre los permisos de acceso, tamaño y dirección de inicio de las regiones en que se organiza el espacio de direcciones virtuales de un proceso se puede utilizar el comando pmap. Por otra parte, en la técnica de demanda de página el espacio de direcciones virtuales de un proceso se divide en páginas del mismo tamaño Sp. Luego dependiendo del tamaño de una región y del tamaño de página, una región podrá estar contenida en una o varias páginas. Además una página puede que tenga contenido perteneciente a una o varias regiones. Una página i de una región del espacio de direcciones virtuales de un proceso no pasa a existir físicamente hasta que es referenciada por primera vez y se produce un fallo de página. Entonces al tratar 148 Ampliación de sistemas operativos el fallo se le asignará un marco j de la memoria principal el cual debe ser inicializado adecuadamente en función de la región a la que pertenezca la página i. Si la página i pertenece a la región de código o a la parte inicializada de la región de datos entonces el marco j se inicializa a partir del archivo ejecutable almacenado en la memoria secundaria asociado al proceso. Asimismo si la página i pertenece a la parte no inicializada de la región datos, al montículo o la región de pila entonces el marco j se inicializa con ceros. En el caso de que la página i pertenezca a una región de librería entonces el marco j se inicializa a partir del archivo de la librería en memoria secundaria. En el caso de las páginas de las regiones de código de un programa o de una librería como no pueden ser modificadas solamente es necesario mantener una copia de dichas páginas en la memoria principal, las cuales pueden ser compartidas por todos los procesos que hagan uso de dicho código. En el caso de las páginas de las regiones de datos inicializados de un programa o de una librería se aplica la técnica de copiar al escribir, que consiste en aplazar la copia en un marco k de una página i cargada en un marco j hasta el momento en que un proceso vaya a realizar una operación de escritura sobre dicha página. Es entonces cuando se crea una copia privada de la página i en el marco k sobre la cual se realizan las modificaciones. Nótese que mientras que no son modificadas estás páginas pueden ser compartidas por todos los procesos que hacen uso de ellas. De esta forma se consiguen ahorrar marcos de página de memoria principal ya que se evita tener cargadas en memoria varias copias de las mismas páginas. 4.2.2. Mapeado de archivos en memoria Como se estudiará en la sección 5 . 2 . 1 una operación de lectura o escritura a un determinado bloque de datos de un archivo ubicada en memoria secundaria requiere por parte de un proceso A la invocación de tres llamadas al sistema como máximo: la llamada al sistema open para abrir el archivo (si no estaba abierto), la llamada al sistema l s e ek para posicionar el puntero de lectura/escritura asociado al archivo en la posición adecuada (si no estaba ya correctamente posicionado) y la llamada al sistema read o wr i t e para leer o escribir, respectivamente, el bloque de datos. En el caso de una operación de lectura, en primer lugar hay que realizar una operación de E/ S a disco para leer el bloque en el disco y copiarlo en un buffer de la caché de buffers del núcleo en memoria principal. Posteriormente el bloque es copiado desde el buffer en el espacio de direcciones virtuales del proceso A . Supuesto que e l bloque d e datos tiene e l tamaño d e una página d e memoria, en la memoria principal existirían dos copias de la misma página, una asociada al buffer del núcleo y otra asociada al espacio del proceso A. Si otro proceso B desea leer la misma página del archivo, tras realizar las llamadas al sistema oportunas, la página será copiada en el espacio de direcciones del proceso B. Luego en la memoria principal existirán tres copias de la misma página, una en la caché de buffers, otra en el espacio de direcciones de A y otra en el espacio de direcciones de B . Obviamente esta forma tradicional de acceder a un archivo genera u n desperdicio de memoria princi­ pal y resulta lenta ya que involucra la realización de varias llamadas al sistema. Una forma más eficiente y rápida de acceder a un archivo consiste en mapearlo en memoria principal. S OBUNIX : administración de memoria 149 3 Un archivo mapeado en memoria (memory-mapped file) es una región del espacio de direcciones virtuales de un proceso en la que se establece una correspondencia byte a byte con parte o con la totalidad de un archivo. Para establecer esta correspondencia el núcleo debe configurar las estructuras de datos que mantiene para gestionar el espacio de direcciones de memoria virtual de un proceso (ver sección 4.2.3). Una vez mapeado en memoria el acceso a un determinado byte del archivo puede ser realizado como a cualquier otro contenido del espacio de direcciones virtuales, es decir, indicando la dirección vittual oportuna. Se evita así tener que realizar llamadas al sistema. Aparte del ahorro de memoria principal y de la rapidez de los accesos, otra de las ventajas de lo archivos mapeados en memoria es que se pueden usar como mecanismo IPC ya que los cambios que realice un proceso en el archivo mapeado son visibles por todos los procesos que mapeen dicho archivo en sus espacios de direcciones. El uso de archivos mapeados también presenta algunos inconvenientes. En primer lugar el tamaño de los archivos que se pueden mapear por completo queda limitado por el tamaño del espacio de di­ recciones virtuales del proceso, el cual suele menor de 4 GiB en arquitecturas de 32 bits. Para archivos que superan dicho tamaño, el archivo debe ser dividido en trozos cuyo mapeado debe irse alternando en memoria principal. También el tamaño del espacio de direcciones virtuales limita el número de archivos que pueden estar mapeados total o parcialmente simultáneamente en memoria. Otro inconveniente está relacionado al uso del mapeo de archivos como mecanismos IPC ya que al igual que sucedía con las regiones de memoria compartida es necesario utilizar algún mecanismo IPC adicional (semáforos o cola de mensajes) para sincronizar el acceso de los diferentes procesos al archivo mapeado. El mapeo de archivos puede ser utilizado tanto por el núcleo como por los procesos. Algunos SO­ BUNIX como SVR4 y Solaris generalizan el mapeo de archivos para implementar todo el espacio de direcciones virtuales, el cual se considera como una colección de objetos mapeados en memoria, siendo los archivos uno de los posibles objetos. En los SOBUNIX un proceso para mapear un archivo en memoria primero debe invocar a la llamada al sistema open para abrir el archivo y así obtener su descriptor fd. A continuación debe invocar a la llamada al sistema mmap, la cual presenta la siguiente sintaxis : pdi rv=mmap ( di rvO , l on , pro t , ind , fd , ini c i o ) ; Esta llamada tiene los siguientes argumentos de entrada: • dirvO es una recomendación para el núcleo sobre la dirección virtual de comienzo de la región del espacio de direcciones del proceso donde se va a mapear el archivo. Lo más recomendable es configurarlo a O para que sea el núcleo el que elij a la dirección que considere más oportuna. • l on es la longitud de la región en bytes. Si l on no es un múltiplo del tamaño de una página Sp entonces se redondea el múltiplo mayor más próximo. 3 0tras traducciones para este término son archivo proyectado en memoria y archivo de memoria mapeada. 150 Ampliación de sistemas operativos • prot permite establecer los permisos de acceso a la región. Se implementa como una combina­ ción de las siguientes constantes : PROT_READ (leer), PROT_WRITE (escribir) y PROT_EXECUTE (ejecutar). • i nd es un conjunto de indicadores que permiten especificar las características del mapeado. Entre los indicadores más frecuentemente utilizados se encuentran los siguientes: - MAP_SHARED. Establece que el mapeado del archivo sea de tipo compartido, con lo que las modificaciones que realice el proceso sobre el archivo pueden ser visualizadas por los proce­ sos que mapeen este archivo en sus espacios de direcciones. Además las modificaciones se realizarán directamente sobre las páginas del archivo cargadas en memoria y cuando las pá­ ginas sean reemplazadas de la memoria se deberán actualizar los bloques de dato del archivo en el disco. - MAP_PRIVA TE. Establece que el mapeado del archivo sea de tipo privado, con lo que las modificaciones que realice el proceso sobre el archivo no pueden ser visualizadas por los procesos que mapeen este archivo en sus espacios de direcciones. Cuando un proceso intenta escribir por primera vez sobre una determinada página del archivo mapeada como privada recibe una copia privada de la página sobre la que se realizan las modificaciones. Es decir se aplica la técnica de copiar al escribir. Dichas modificaciones, cuando la página sea re­ emplazada de memoria, no serán trasladadas a los bloques de datos del archivo en el disco. Nótese que mientras que una página no sea escrita puede ser compartida para lectura por varios procesos. • f d es el descriptor del archivo. • i ni c i o indica el byte del archivo a partir del cual se comenzará a mapear el archivo. Esta llamada si se ejecuta con éxito devuelve en pdi rv la dirección virtual de comienzo de la región y crea una correspondencia byte a byte entre el rango de bytes [ in i c i o , ini c i o + l on - 1 ] del archivo con descriptor f d y la región del proceso situada en el rango de direcciones virtuales [ pdirv , pdirv+ l on - 1 ] En caso de error devuelve - l . Para eliminar el mapeo de u n archivo en el espacio de direcciones virtuales de un proceso se debe invocar a la llamada al sistema r=munmap ( pd i rv , l on ) . Si se ejecuta con éxito devuelve O. En caso de error devuelve - l . e Ejemplo 4.2 Considérense el archivo ejecutable prog4 1 cuyo código fuente en C se muestra en la Figura 4.2. Supón­ gase que prog4 1 es invocado desde la línea de órdenes de un intérprete de comandos y que el intérprete S OBUNIX: administración de memoria 151 # i nc lude < f c n t l . h> # i nc lude < s td i o . h> # i nc lude <uni s t d . h> # i nc lude < sys / mman . h> ma i n ( ) { int f d , L ; c addr_t d ; char bu f f er [ 2 0 ] ; L = s i z e o f ( bu f f e r ) ; f d = open ( " t ex t o l . tx t " , 0 6 6 6 ) ; 1 * C r e a r e l roap e o d e l arch ivo e n u n a reg i ón * 1 I d=mmap ( NULL , L , PROT_WR I T E PROT_READ , MAP_S HARED , f d , ( o f f_t ) O ) ; i f ( d= =MAP_FAI LED ) p r i n t f ( " \ nErro r \ n " ) ; else { close ( fd) ; bu f f er [ O ] = * d ; * d= ' S ' ; print f ( / * L e e r en la r e g i ón * / / * E s c r i b i r en la r e g i ón * / • \ nAn t e s= %e De spue s= %c \ n • munmap ( d , L ) ; , bu f f er [ O ] , * d ) ; 1 * E l iminar el map e o de l archivo en l a r e g i ó n * 1 Figura 4.2 - Código C del programa prog4 1 crea un proceso A para ejecutar este programa. El proceso en primer lugar invoca a la función s i z e o f para determina el tamaño e n bytes que ocupa l a variable bu f f e r . Puesto que esta variable e s una cadena de 20 caracteres y supuesto que cada carácter requiere un 1 byte, entonces en la variable L se almacena el valor 20. En segundo lugar se invoca a la llamada al sistema open para abrir el archivo text o l . txt con permisos de lectura y escritura para todos los usuarios. Se va a suponer que este archivo contiene únicamente la siguiente línea de texto: E s t o e s un e j emp l o de u s o de mmap En tercer lugar se invoca a la llamada al sistema mmap para solicitar la creación de una región de me­ moria virtual de tamaño L bytes con acceso de lectura (PROT_READ) y escritura (PROT_WRI TE) que sea compartida (MAP_SHARED) en la que se van a mapear L bytes del archivo con descriptor fd comenzando desde el inicio ( ( o f f_ t ) o ) del archivo. La dirección de comienzo de esta región será elegida por el sistema operativo (NULL). Si la llamada al sistema se ejecuta con éxito entonces en d se almacenará la dirección de inicio de la región virtual creada, la cual ocupa el rango de direcciones [ d , d+L - 1 ] . En esta 152 Ampliación de sistemas operativos región se ha mapeado byte a byte los L primeros bytes del archivo con descriptor fd. A continuación se comprueba si se ha producido algún error (MAP_FAILED ) durante la ejecución de la llamada. En dicho caso se imprime en la pantalla el mensaje Error y el proceso termina su ejecución. Si la llamada se ha ejecutado con éxito entonces se invoca a la llamada al sistema c l a s e para cerrar el archivo. Posteriormente se lee el primer byte de la región en la que está mapeado el archivo, que corresponde al carácter E y se almacena en la variable bu f f e r [ o l . Luego se escribe el carácter s en el primer byte de la región en la que está mapeado el archivo. Finalmente se invoca a la función p r i n t f que muestra en la pantalla el mensaje An t e s = E De spué s = S e invoca a l a llamada al sistema rnunmap para eliminar el mapeo del archivo del espacio de direcciones del proceso. Señalar que puesto el mapeado del archivo en la región se solicitó con la opción MAP_SHARED la modi­ ficación que ha realizado el proceso A sobre la región ha sido trasladada el archivo t ext o l . txt en el disco. Esto se puede comprobar abriendo el archivo con un editor o a través de un intérprete de comandos escribiendo la orden: more text o l . txt • 4.2.3. Estructuras de datos gestionadas por el núcleo asociadas al espacio de direcciones virtuales de un proceso El núcleo mantiene diferentes estructuras para gestionar el espacio de direcciones de un proceso. El nombre y la información contenida en estas estructuras dependen de cada SOBUNIX. Generalmente a cada región existente se le asocia una estructura El que contiene, entre otros, los siguientes datos sobre una región: dirección virtual base, tamaño, permisos de acceso y un puntero a la tabla de páginas. Además existe una estructura E2 que contiene información sobre el número de regiones existentes, su tamaño total y la localización de las estructuras El asociadas a las regiones. En la entrada asociada al proceso en la tabla de procesos se suele mantener un puntero a su estructura E2. e Ejemplo 4.3 La implementación del espacio de direcciones virtuales que realiza Solaris está basada en la arquitec­ tura de gestión de memoria virtual de SunOS 4.0, también denominada abreviadamente en la literatura [Vahalia, 1 995] como arquitectura VM (Virtual Memory, memoria virtual). Debido a su eficiencia y versatilidad la arquitectura VM ha sido adoptada por diversos SOBUNIX, como por ejemplo: SVR4 y Solaris. En la arquitectura VM se utiliza el término segmento para referirse a una región del espacio de direcciones virtuales. Además se extiende el uso de los archivos mapeados para la construcción de todas las regiones del espacio de direcciones virtuales tanto de un proceso como del núcleo. Cada segmento mapea un determinado objeto de datos que puede estar asociado a: parte o la totalidad de un archivo en el sistema de archivos, bloques de datos del área de intercambio, un buffer de un dispositivo, SOBUNIX : administración de memoria 153 etc. Cada objeto de datos comprende un rango contiguo de bytes que puede ser accedido por el rango de direcciones virtuales contiguas que comprende el segmento. Por otra parte cada objeto tiene asociado un nodo-v. El tamaño de un objeto es siempre un múltiplo del tamaño de una página. Un objeto de datos constituye la fuente a partir de la cual se inicializa el contenido de los marcos de página asociados a las páginas virtuales de un segmento. Además es el almacenamiento de respaldo persistente de las páginas de dicho segmento. La memoria principal actúa como una memoria caché de las páginas de los objetos de datos. La primera vez que se referencie a una dirección virtual contenida en una página i de un segmento se producirá un fallo de página. Al tratar el fallo se asignará a la página i un marco j de la memoria principal el cual debe ser inicializado adecuadamente a partir del objeto de datos al que está mapeado el segmento al que pertenece la página i. Un objeto de datos especial es el objeto anónimo que se utiliza como fuente de páginas llenas de ceros, las cuales se utilizan, por ejemplo, en la creación del montículo y la pila de un proceso. El objeto anónimo se implementa como un puntero al nodo-v NULL o al nodo-v del archivo / dev / z er o . En función del objeto mapeado y del espacio d e direcciones vittuales (usuario o núcleo) e n que s e mapea se pueden distinguir diferentes tipos de segmentos. Entre los tipos de segmentos más frecuentemente utilizados en Solaris se encuentran los siguientes : • s eg_vn. Segmento perteneciente al espacio de direcciones virtuales de un proceso de usuario que se utiliza para mapear archivos regulares o el objeto anónimo. Todos los segmentos del espacio de direcciones de un proceso de usuario son segmentos de este tipo. Los segmentos de código y de datos inicializados se construyen mediante el mapeo del código y los datos inicializados del archivo ejecutable correspondiente. La región de datos no inicializados, el montículo y la pila se construyen mediante el mapeo del objeto anónimo. • s eg_map . Segmento petteneciente al espacio de direcciones virtuales del núcleo que se utiliza para mapear archivos regulares. Las páginas de un segmento de este tipo se alojan en marcos bloqueados de memoria principal. Este tipo de segmento se utiliza para implementar la caché de páginas de archivos del núcleo. • s e g_kmen. Segmento perteneciente al espacio de direcciones del núcleo que se utiliza para mapear archivos regulares o el objeto anónimo. Las páginas de un segmento de este tipo se alojan en marcos bloqueados de memoria principal. Los segmento de código, datos, pila y montículo del núcleo son segmentos de este tipo. Sobre los segmentos de un mismo tipo se opera con un determinado driver del segmento que queda definido por un conjunto de datos privados y rutinas. En Solaris cada proceso tiene asignada por el núcleo una estructura proc donde se mantiene diferente información relativa a un proceso. Entre la información contenida en proc existe un puntero p_a s a una estructura as que contiene, entre otros, los siguientes datos: • a_s i z e . Tamaño en bytes del espacio de direcciones virtuales. 154 Ampliación de sistemas operativos • a_n s e g s . Número de segmentos que forman parte del espacio de direcciones virtuales del proceso. • a_s egtree. Puntero a una estructura que implementa un árbol AVL4 con todos los segmentos del espacio. • a_hat . Puntero a una estructura hat (ver sección 4.3) con información sobre las tablas de páginas. s t ru c t seg s t ruct S -base S-s i z e S - as S -t r e e S -base S -dat a S- s i z e Operaciones del driver de los segmentos tipo S - as S -t r e e s e g_vn s _ops r-- seg s e gvn_map ( ) s _op s r- S -da t a s e gvn_ f ree ( ) s e gvn_ f au l t ( ) ! 1 s t ru c t s t ru c t s e gvn_da t a s e gvn_dat a Figura 4.3 - Estructuras de datos usadas en Solaris para describir un segmento de tipo s eg_vn A su vez cada segmento tiene asociada una estructura s eg que contiene, entre otros, los siguientes datos (ver Figura 4.3): • s_ba s e . Dirección virtual base de comienzo del segmento. • s_s i z e . Tamaño del segmento en bytes. • s_a s . Puntero a la estructura a s a la que pertenece. • s_t r e e . Punteros para construir el árbol AVL de segmentos del que forma parte. • s_op s . Puntero a las funciones del driver del segmento que definen las operaciones que pueden realizarse sobre el segmento una vez creado. • s_da t a . Puntero a una estructura que contiene diferentes datos sobre el segmento. Estos datos dependen del tipo de segmento y son utilizados por las funciones del driver del segmento. En la Figura 4.4 se muestra a modo de resumen un diagrama con las estructuras de datos utilizadas en Solaris para describir el espacio de direcciones virtuales de un proceso. • 4Un árbol AVL es un tipo especial de árbol binario autobalanceable [Cormen et al. , 2009] inventado por los matemáticos rusos Adelson-Velskii y Landis. SOBUNIX: administración de memoria 155 s t ru c t proc 1 1 1 1 1 1 1 1 r-- 1-p_as Librerías 1 1 1 1 1 1 1 1 1 1 t 1 1 1 1 1 1 1 1 1 1 s t ru c t s e g -r- Montículo s -b a s e s size S - as - S-t r e e s_op s .... Datos s t ru c t a s J a_s e g t r e e a- s i z e a_n s e g s a hat S - da t a s t ru c t s e g Código 1= '--- s -ba s e S- s i z e S Árbol AVL -a s S -t r e e s_op s s da t a Figura 4.4 - Estructuras de datos usadas en Solaris para describir el espacio de direcciones virtuales de un proceso 4.2.4. Memoria anónima Una página anónima es una página que antes de su creación no dispone de un almacenamiento persistente de respaldo en memoria secundaria. Las páginas anónimas son utilizadas por ejemplo, en la región de datos no inicializados, el montículo y la región de pila. También son utilizadas en las regiones asociadas a archivos mapeados en memoria con el indicador MAP_PRIVA TE, este el caso por ejemplo de la región de datos inicializados. Una página anónima solo se crea cuando se accede por primera vez a ella durante una operación de escritura. Es durante el tratamiento del fallo de página correspondiente cuando se le asigna un marco de memoria principal, se inicializa el marco con ceros o con la copia del contenido de otra página, y se le asigna espacio en el área de intercambio. Cuando se intenta escribir por primera vez en una página del segmento de datos no inicializados, el montículo o la pila se produce una excepción hardware que al ser tratada por el núcleo produce la creación de una página anónima que es inicializada con ceros. A esta operación se le denomina llenar Ampliación de sistemas operativos 156 con ceros por demanda (Zero-Fill-On-Demand, ZFOD). Cuando se intenta escribir por primera vez en una página de un segmento asociado a una archivo mapeado en memoria con el indicador MAP_PRIVATE se genera una excepción hardware que al ser tratada por el núcleo produce la creación de una página anónima en la cual se copia el contenido de la página que originó la excepción. A esta estrategia de copiar el contenido de una página únicamente cuando la página se modifica se le denomina copiar al escribir. Esta estrategia permite evitar durante la creación de un proceso hijo (ver sección 4.2.7) la duplicación de ciertos segmentos del proceso padre. El duplicado de una página de un segmento se aplaza hasta que el proceso hijo desea escribir en ella. Es en ese momento cuando se le asigna una página anónima en la que se carga una copia de la página del proceso padre. Al conjunto de páginas anónimas utilizadas por un proceso se le denomina memoria anónima del proceso. La memoria anónima se asigna por regiones del espacio de direcciones virtuales mediante el uso de diferentes estructuras de datos. El núcleo manipula estas estructuras mediante el uso de diferentes funciones. El nombre y el contenido de las estructuras de datos que implementan la memoria anónima, así como el nombre y la definición de las funciones del núcleo que operan sobre dicha memoria dependen de cada SOBUNIX. e Ejemplo 4.4 En los SOBUNIX que utilizan la arquitectura VM de gestión de memoria, como por ejemplo Solaris, una página anónima se crea la primera vez que se desea realizar una operación de escritura sobre una dirección virtual contenida en una página perteneciente a: • Un segmento que ha sido construido con un mapeado de tipo MAP_PRIVATE a un archivo. Este es el caso por ejemplo, del segmento de datos inicializados de un proceso. • Un segmento que ha sido construido con un mapeado al objeto anónimo. Este es el caso de los segmentos de pila, montículo y datos no inicializados . seg Array de referencias a estructuras anon .-----li-s da t a a non anon_map base a non s i ze cont re f = l s e gvn_dat a Estructuras de datos usadas en Solaris para describir las páginas anónimas de segmento de tipo s eg_vn Figura 4.5 - Para este tipo de segmentos la estructura s egvn_da t a (ver Figura 4.5), a la que apunta el campo s_da t a d e la estructura s eg que implementa u n segmento, tiene u n puntero amp a una estructura anon_rnap que S OBUNIX: administración de memoria 157 contiene la dirección de inicio ba s e y el tamaño s i z e de un array de referencias a estructuras anon. En este array existe una entrada por cada página del segmento. Cada entrada contiene un puntero a una estructura anon que representa a una página anónima. Esta estructura contiene información sobre la localización de la página anónima en el área de intercambio (ver sección 4.5). Además contiene un con­ tador de referencias que se incrementa en una unidad cada vez que el segmento de un proceso referencia a dicha página anónima. Sobre la memoria anónima el núcleo trabaja usando un determinado conjunto de funciones que constitu­ yen la denominada como capa anónima, entre las funciones que forman parte de esta capa se encuentran las siguientes [Vahalia, 1 995] : • anon_a l l o c . Esta función permite obtener una página anónima nueva. • anon_dup . Esta función duplica las referencias a un conjunto de páginas anónimas. Se utiliza durante el tratamiento de la llamada al sistema f o rk para duplicar en el espacio del proceso hijo un segmento del proceso padre que tiene páginas anónimas. El efecto de esta operación es incrementar en una unidad el contador de referencias de las estructuras anon de cada página anónima del segmento compartida por el padre y el hijo. • anon_ f r e e . Elimina las referencias a un conj unto de páginas anónimas. El efecto de esta ope­ ración es decrementar en una unidad el contador de referencias de las estructuras anon de cada página privada del conjunto. Solamente cuando el contador alcanza el valor O se puede eliminar dicha página anónima y dej ar la estructura anon desasignada. • anon_priva t e . Hace una copia privada de una página y le asigna una estructura anon. • anon_ z e r o . Crea una página llena de ceros y le asigna una estructura anon. • 4.2.5. Memoria compartida Una región de memoria compartida es un mecanismo IPC que permite compartir datos entre distintos procesos. Para que un proceso pueda acceder a dichos datos debe mapear la región dentro de su espacio de direcciones virtuales. En la sección 3 .5 . 1 al describir los mecanismos IPC del System V se describieron las llamadas al sistema que debe invocar un proceso para crear y poder usar una región de memoria compartida. También se comentó que el núcleo mantiene una tabla con una entrada por cada mecanismo IPC del tipo memoria compartida existente en el sistema. Aparte de esta tabla global el núcleo mantiene diferentes estructuras de datos para implementar las regiones de memoria compartida. El nombre y el contenido de estas estructuras dependen de cada SOBUNIX. e Ejemplo 4.5 En los SOBUNIX que usan la arquitectura VM de gestión de memoria, como Solaris , una región de memoria compartida se implementa como un segmento de tipo s eg_vn mapeado al obj eto anónimo con 158 Ampliación de sistemas operativos Proceso A s e gvn_dat a Proceso B Array d e referencias a estructuras anon a non cont re f = l anon_map base c o n t re f = 3 a non s egvn_da t a cont re f = l Tabla de recursos IPC del tipo memoria compartida Figura 4.6 - Estructuras de datos usadas en Solaris para implementar u n recurso IPC del tipo memoria compartida un mapeo del tipo MAP_SHARED. La primera vez que se escribe en una página del segmento de memoria compartido se crea una página anónima que tiene el área de intercambio como almacenamiento de res­ paldo persistente. La estructura anon_rnap asociada a este segmento contiene un contador de referencias que indica el número de estructuras que le referencian. En la Figura 4.6 se muestran, a modo de ejemplo, las estructuras de datos de un recurso IPC del tipo memoria compartida que está siendo compartido por dos procesos A y B. Se observa que el contador de referencias de la estructura anon_rnap contiene el valor 3, ya que son tres las estructuras de datos que la referencian: la estructura s egvn_da t a del proceso A, la estructura s egvn_da t a del proceso B y la entrada asociada al recurso de la tabla global de recursos IPC del tipo memoria compartida. Solo cuando este contador de referencias alcance el valor O, el núcleo podrá eliminar las páginas anónimas asociadas al segmento y desasignar las estructuras de datos. Nótese que aunque finalicen los procesos A y B , las páginas anónimas del segmento de memoria compartida seguirán existiendo si no se ha invocado explícitamente a la llamada al sistema shrnc t l con la orden I PC_RMID o se ejecuta el comando ipcrrn. Solo en ese caso se desasignará la entrada asociada a este mecanismo IPC en la tabla global y el contador de referencias de anon_rnap alcanzará el valor O. Nótese también que el contador de referencia de la estructura anon de una página anónima perteneciente a un recurso IPC del tipo memoria compartida contiene el valor l . Esto es así porque todos los proce­ sos que tienen mapeado en su espacio de direcciones este recurso IPC comparten la misma estructura anon_rnap y el mismo array de referencias a estructuras anon. A diferencia de lo que ocurre cuando se duplica un segmento, en cuyo caso el segmento original y el duplicado tienen cada uno su propia estruc­ tura anon_rnap y su propio array de referencias a estructuras anon. Como ambos arrays de referencias SOBUNIX : administración de memoria 159 apuntan a las mismas páginas anónimas entonces el contador de referencias de las estructuras anon de estas páginas contienen el valor 2. • 4.2.6. Operaciones sobre el espacio de direcciones virtuales de un proceso Sobre el espacio de direcciones virtuales de un proceso el núcleo puede realizar diferentes operacio­ nes. Estas operaciones se pueden clasificar en dos grandes grupos : • Operaciones que afectan a todo e l espacio de direcciones de u n proceso. Como l a creación de un nuevo espacio de direcciones, y la duplicación o eliminación de un espacio de direcciones ya existente. • Operaciones que afectan a una región o segmento del espacio de direcciones de un proceso. Como la creación de una nueva región, la eliminación de una región, la configuración y la comprobación de los permisos de acceso a una región, etc. Cada SOBUNIX define qué operaciones sobre el espacio de direcciones soporta y cómo las imple­ menta. e Ejemplo 4.6 En los SOBUNIX que utilizan la arquitectura VM de gestión de memoria, como por ejemplo Solaris, hay definidas diferentes operaciones que trabaj an sobre la estructura as que representa el espacio de direcciones virtuales de un proceso. Algunas de estas operaciones afectan a todo el espacio de direcciones virtuales del proceso, como por ejemplo: • as_a l l o c . Crea un nuevo espacio de direcciones virtuales. Esta función se invoca durante el tratamiento de las llamadas al sistema fork y exe c . • as_dup . Duplica u n espacio de direcciones . Esta función s e invoca durante el tratamiento d e la llamada al sistema f o rk. • as_f r e e . Elimina un espacio de direcciones ya existente. Esta función se invoca durante el trata­ miento de la llamada al sistema exi t . Otras operaciones trabaj an a nivel de segmento, como por ejemplo: • as_map . Crea un segmento en el espacio de direcciones, es decir, mapea un objeto en memoria. • as_unmap . Elimina un segmento del espacio de direcciones. • as_fau l t. Trata un fallo de página originado por una dirección virtual del espacio. Las operaciones que trabajan con la estructura a s a nivel de segmento suelen invocar, en la mayoría de los casos, a funciones del driver del segmento para realizar las operaciones correspondientes. Recuérdese que cada tipo de segmento tiene asignado un driver de segmento determinado. • 160 4.2. 7. Ampliación de sistemas operativos Creación del espacio de direcciones virtuales de un proceso Una de las tareas principales que debe realizar el núcleo durante el tratamiento de fork es la creación del espacio de direcciones virtuales del proceso hijo. La implementación de esta tarea depende de cada SOBUNIX, aunque de forma general puede afirmarse que en los SOBUNIX modernos la creación del espacio de direcciones virtuales del proceso hijo se realiza duplicando las estructuras de datos asociadas al espacio de direcciones virtuales del proceso padre. Por el contrario, las páginas del proceso padre no son inicialmente duplicadas en memoria durante el tratamiento de f ork, ya que así se ahorra memoria principal y se disminuye la sobrecarga. Las páginas asociadas a la región de código del proceso padre o las regiones de código de las librerías compartidas, son de solo lectura, y pueden ser compartidas por el proceso padre y el proceso hijo. También son compartidas las páginas de las regiones de memoria compartida y las regiones asociadas a archivos mapeados del tipo MAP_SHARED. Por otra parte, sobre las páginas asociadas a la región de datos, a la región de pila y a las regiones asociadas a archivos mapeados del tipo MAP_PRIVATE se aplica la técnica de copiar al escribir, es decir, la página es copiada en memoria únicamente cuando un proceso tiene que escribir por primera vez sobre la página. De esta forma dicho proceso hijo dispone de una copia privada de la página sobre la que realiza las modificaciones. e Ejemplo 4.7 En los SOBUNIX que implementan una arquitectura VM de gestión de memoria, como Solaris, la crea­ ción del espacio de direcciones virtuales de un proceso hijo durante el tratamiento de una llamada al sistema f ork se realiza siguiendo los siguientes pasos [Vahalia, 1 995] : l . Tras asignar e inicializar una estructura proc para el proceso hijo se invoca a la función as_dup . Esta función en primer lugar invoca a la función as_a l l o c para asignar una nueva estructura a s para el proceso hijo. 2. La función as_a l l o c recorre el árbol AVL de segmentos del proceso padre e invoca para cada segmento a la función dup del driver de segmento correspondiente con el objetivo de duplicar el segmento dentro de la estructura as del proceso hijo. Por ejemplo, para un segmento tipo s eg_vn se invocaría a la función s egvn_dup . 3 . Una vez que todos los segmentos han sido duplicados, la función as_dup invoca a la función hat_dup que duplica la estructura hat (ver sección 4.3) y las tablas de páginas del proceso padre . • SOBUNIX: administración de memoria 4.3. Traducción de direcciones virtuales a direcciones físicas en SOBUNIX 4.3.1. Tablas de páginas y MMU 161 Una de las principales tareas que es necesario realizar en la implementación de la técnica de gestión de memoria mediante demanda de página es la traducción de direcciones virtuales a direcciones físicas. Para su realización se utilizan dos elementos: • Tabla de página. Una tabla de páginas es una estructura de datos que contiene una entrada por cada página existente en un espacio de direcciones virtuales. El tamaño de una entrada suele ser de 32 bits o de 64 bits que se descomponen en diferentes campos. El número, orden, tamaño y contenido de los campos viene determinado por la arquitectura del computador. Entre los campos presentes habitualmente en una entrada de una tabla de páginas se encuentran los siguientes : - Número de marco de página. Contiene e l número de marco j de memoria principal donde se encuentra almacenada la página i - Validez o presencia. Este campo consta de un único bit v que se activa si la página i está cargada en el marco j. Si la página no pertenece actualmente al espacio de direcciones del proceso o no está cargada en memoria principal este bit está desactivado. - Bits de protección. Este campo en su forma más simple consta de un único bit que en función de su valor indica si la página es de solo-lectura o de lectura-escritura. - Referenciada. Este campo consta de un único bit r que se activa cuando la página i es referen­ ciada por una dirección virtual. Este bit suele ser consultado para decidir que página puede ser reemplazada (ver sección 4.4.2). - Modificada. Este campo consta de un único bit m que se activa cuando la página i es modi­ ficada en una operación de lectura. Su activación indica al núcleo que debe copiar la página en memoria secundaria antes de reemplazarla. Cuanto mayor sea el espacio de direcciones virtuales de un proceso mayor será el tamaño de su tabla de páginas y más espacio de memoria principal consumirá. Para ahorrar memoria algunas arquitecturas soportan el uso de tabla de páginas paginadas de dos o más niveles. También algunas arquitecturas trabajan con tablas de páginas invertidas. En un determinado instante de tiempo en la memoria del núcleo existen cargadas varias tablas de página. Generalmente existe una única tabla de páginas para describir las páginas del espacio de direcciones del núcleo y una o más tablas para describir las páginas del espacio de direcciones de un proceso. En algunos S OBUNIX se asocian las tablas de páginas por regiones, existiendo una tabla de páginas asociada a cada región. • MMU. Es un componente hardware que se encarga de realizar la traducción de una dirección virtual en una dirección física. Para poder realizar la traducción, en un registro de la MMU se Ampliación de sistemas operativos 162 carga la dirección física de comienzo de la tabla de páginas del proceso en ejecución. Además se carga en el TLB de la MMU una copia de un conjunto de entradas de dicha tabla de páginas. Cuando la MMU recibe del procesador una dirección virtual, la descompone en dos campos : número de página i y desplazamiento, y comprueba si la entrada de la tabla de páginas asociada a la página i está cargado en el TLB . Si no es así se produce un fallo de TLB que al ser tratado por el hardware, el núcleo, o ambos en colaboración, hace que se cargue en una entrada del TLB la entrada de la tabla de páginas que se estaba buscando. Una vez encontrada la entrada del TLB que contiene una copia de la entrada de la tabla de páginas asociada a la página i, la MMU comprueba el campo protección para verificar que no se está intentando realizar una operación no permitida sobre la página. Si se intenta realizar un tipo de operación no auto­ rizada la MMU genera una excepción hardware denominada fallo de protección. Si no existe un fallo de protección comprueba el bit de validez v y si está activado se accede al campo marco j de dicha entrada en el TLB para construir junto con el campo desplazamiento la dirección física. Si el bit de validez está Dirv N° de página i 1 6 bits 1 . .. 1 Sin usar }i 9 bits IETPN3 1 9 . .. 1 bits . .. IETPN2 9 bits 9 IETPN 1 bits . .. IETPNO 1 1 2 bits Desplazamiento 1 llicrn Registro CR3 � IrFI Marco l Marco m DirFo ! Marco m Tabla de páginas de nivel 3 w 1 Marco 1 1� 4 DirF 3 Tabla de páginas de nivel 2 ! Marco k Tabla de páginas de nivel 1 Marco k w 1 Marcoj Tabla de páginas de nivel O Marco j w Págma Figura 4.7 - Tablas de páginas paginadas de 4 niveles soportadas en la arquitectura x64 de AMO 1 S OBUNIX: administración de memoria 163 desactivado se produce una excepción denominada fallo de válidez que debe ser tratado por el núcleo. La traducción de direcciones requiere la colaboración entre el subsistema de gestión de memoria del núcleo y la MMU. El subsistema de gestión de memoria se encarga de asignar espacio, configurar y actualizar las tablas de páginas. Además se encarga de cargar los registros y el TLB de la MMU cada vez que se produce un cambio de contexto. También se encarga de atender las excepciones generadas por la MMU. Por su parte la MMU se encarga de comprobar la validez de los accesos, generar una excepción si el acceso no es válido, traducir las direcciones virtuales a direcciones físicas, y activar los bits referenciada o/y modificada. Con el objetivo de ahorrar espacio de memoria principal los SOBUNIX suelen trabaj ar con tablas de páginas paginadas, el número de niveles de paginación utilizados es fijado por cada SOBUNIX en función de la arquitectura. e Ejemplo 4.8 Solaris cuando se ejecuta en la arquitectura x64 de AMO utiliza un tamaño de página de 4 KiB y tablas de paginas paginadas de 4 niveles de paginación (ver Figura 4.7). Cuando un proceso es planificado para ejecución la dirección física Dirpo de la tabla de paginas del nivel 3 del espacio de direcciones de dicho proceso es cargada en el registro CR3 del computador. Cuando durante la ejecución de dicho proceso se referencia a una determinada dirección virtual Dirv para traducirla a la dirección física Dirp a la que hace referencia se realizan los siguientes pasos : l . Se suma Dirpo con el valor del campo índice de la entrada de la tabla de páginas del nivel 3 (IETPN3) de la dirección virtual Dirv para obtener la dirección física Dirp¡ de la entrada de la tabla de páginas de nivel 3 que almacena el número m del marco de página que contiene a la tabla de páginas del nivel 2. 2. Se accede a Dirp¡ para leer dicho número de marco m y se suma a la dirección física base del marco m el valor del campo IETPN2 de Dirv . Se obtiene así la dirección física Dirp2 de la entrada de la tabla de páginas de nivel 2 que almacena el número l del marco de página que contiene a la tabla de páginas del nivel l . 3 . Se accede a Dirp2 para leer dicho número de marco l y se suma a la dirección física base del marco l el valor del campo IETPN l de Dirv . Se obtiene así la dirección física Dirp3 de la entrada de la tabla de páginas de nivel 1 que almacena el número k del marco de página que contiene a la tabla de páginas del nivel O. 4. Se accede a DirF3 para leer dicho número de marco k y se suma a la dirección física base del marco k el valor del campo IETPNO de Dirv . Se obtiene así la dirección física Dirp4 de la entrada de la tabla de páginas de nivel O que almacena el número j del marco de página que contiene a la página i referenciada en Dirv . 5 . Se accede a Dirp4 para leer dicho número de marco j y se suma a la dirección física base del marco 164 Ampliación de sistemas operativos j el valor del campo desplazamiento de Dirv . Se obtiene así la dirección física Dirp a la que hace referencia Dirv . Señalar que aunque la arquitectura x64 de AMD soporta direcciones virtuales de 64 bits, realmente los 16 bits más significativos de una dirección virtual de 64 bits no se utilizan y por ello se configuran todos a O o todos l . Pese a ello el espacio de direcciones virtuales sigue siendo bastante grande: 24 8 bytes= 256 TiB . • El núcleo para gestionar las tablas de páginas de los distintos niveles y la MMU utiliza diferentes es­ tructuras de datos y funciones, cuyo nombre, definición e implementación dependen de cada SOBUNIX y de la arquitectura del computador donde se ejecuta. e Ejemplo 4.9 En los SOBUNIX que implementan la arquitectura VM de gestión de memoria, como Solaris, la tarea de traducción de direcciones la realiza una capa de software denominada capa HAT (Hardware Address Translation, traducción de direcciones) . Esta capa contiene todas las funciones y estructuras del núcleo que requieren conocer los detalles del hardware relativos a la traducción de direcciones. La implementa­ ción de esta capa depende de cada arquitectura. El uso de la capa HAT evita que el resto del subsistema de gestión de memoria del núcleo tenga que conocer los detalles del hardware, lo cual facilita la portabilidad del núcleo entre diferentes plataformas. Solo la capa HAT depende del hardware y en consecuencia es distinta en cada arquitectura. La capa HAT se encarga de la realización de las siguientes tareas: • Creación, configuración y destrucción de las tablas de páginas. • Control de la MMU. • Atención de las excepciones hardware provocadas por la MMU durante el proceso de traducción de direcciones. La capa HAT utiliza una estructura ha t para mantener todos los datos relativos a la traducción de direc­ ciones del espacio de direcciones de un determinado proceso, entre ellos, un puntero a la tabla de páginas de más alto nivel. Recuérdese (ver sección 4.2.3) que la estructura as asociada al espacio de direcciones virtuales de un proceso mantiene un puntero a la estructura ha t asociada al espacio del proceso. Los drivers de los segmentos invocan a una determinada función de la capa HAT para la realización de una determinada tarea, como por ejemplo: • hat_a l l o c . Asigna una estructura hat para el espacio de direcciones virtuales de un proceso. • hat_free_end. Destruye la estructura ha t de un proceso. • hat_mem l o ad. Crea la traducción de direcciones de una determinada página. SOBUNIX: administración de memoria 165 hat_unl oad. Elimina o invalida la traducción de direcciones de una determinada página. • Las funciones de la capa HAT reciben argumentos de entrada que son independientes del hardware como por ejemplo: direcciones virtuales, tamaños, punteros de página y modos de protección. • 4.3.2. Tratamiento de los fallos de página La MMU durante el proceso de traducción de una dirección virtual en una dirección física realiza diferentes comprobaciones en orden secuencial. Si el resultado de una comprobación no es satisfactorio genera una excepción denominada fallo de página que debe ser atendida por el núcleo. De forma general se distinguen dos posibles tipos de fallos de página: • Fallo de validez. Se produce cuando una dirección virtual referencia a una página i cuya entrada asociada en la tabla de páginas tiene el bit de validez desactivado (v = 0). Este bit se encuentra desactivado si la página i no está cargada en un marco j de memoria principal o si la página no pertenece al espacio de direcciones virtuales del proceso. • Fallo de protección. Se produce si la operación (lectura, escritura o ejecución) que se desea rea­ lizar sobre la página i referenciada por la dirección virtual no está autorizada por los permisos establecidos en su entrada de la tabla de páginas. La implementación del tratamiento de los fallos de página depende de cada SOBUNIX. Aunque de forma general puede afirmarse que el núcleo para tratar las excepciones producidas por la MMU ejecuta un manejador de fallos de página que realiza diferentes tareas en función del tipo de fallo de página que se haya producido. Si se ha producido un fallo de protección o si se produce un fallo de validez porque se ha intentado acceder a una página que no pertenece al espacio de direcciones del proceso, entonces el manejador se lo notifica al proceso en ejecución enviándole una señal, generalmente S IGS EGV, cuya acción establecida por defecto es la terminación del proceso . S i s e h a producido u n fallo d e validez porque l a página i n o estaba cargada en u n marco j d e memoria principal, entonces se le asigna un marco j de la lista de marcos de página libre y se carga en él la página i leyéndola del área de intercambio o desde el sistema de archivos. Una vez cargada el manej ador actualiza el contenido de la entrada asociada a la página i en la tabla de páginas y manipula el contexto hardware salvado del proceso que produjo el fallo de página para que cuando vuelva a ser planificado para ser ejecutado lo haga justo desde la instrucción cuya ejecución produjo el fallo de página. e Ejemplo 4.10 En los SOBUNIX que implementan una arquitectura VM de gestión de memoria, como S olaris, el trata­ miento de los fallos de página se realiza de la siguiente forma: l. El manipulador de excepciones del núcleo identifica el tipo de fallo, el tipo de acceso y localiza la estructura as del proceso cuya ejecución produjo el fallo de página. Una vez localizada dicha estructura invoca a la función as_faul t para tratar el fallo. 166 Ampliación de sistemas operativos 2. La función as_faul t localiza el segmento del espacio de direcciones virtuales al que pertenece la dirección virtual cuya referencia produjo el fallo de página. 3. Una vez localizado el segmento, as_faul t invoca a la función de tratamiento de fallos de página del driver del segmento. Recuérdese que cada tipo de segmento dispone de un driver distinto y en consecuencia su función de tratamiento de fallos de página también será distinta. Se va suponer que el segmento es de tipo s eg_vn, luego se invocaría a la función de tratamiento de fallos de página del driver de segmentos tipo s e g_vn, es decir, a la función segvn_faul t . Esta función realiza diferentes acciones en función del tipo de fallo de página. Si se trata de un fallo de protección o de un fallo de validez porque se ha intentado acceder a una página que no pertenece al espacio de direcciones del proceso, entonces s egvn_ f au l t envía una señal S I GSEGV al proceso que produjo el fallo de página. La acción por defecto asociada a esta señal es terminar el proceso. Por otra parte si se trata de un fallo de validez asociado a que la página i no estaba cargada en un marco j de memoria principal entonces las acciones que realiza la función s e gvn_ f au l t dependen del estado de la página: • Si la página tiene asignada una entrada en anon_map entonces invoca a anon_ge tpage para leer la página en el área de intercambio. • Si la página no tiene asignada una entrada en anon_map y el segmento está mapeado a una archivo, entonces invoca a la función ge tpage del nodo-v del archivo. Esta función si no encuentra la página cargada en memoria entonces la lee en el disco. • Si la página no tiene asignada una entrada en anon_map y el segmento está mapeado al objeto anónimo, entonces invoca a la función anon_ z e r o que crea una página privada llena de ceros. Una vez que la página está cargada en la memoria principal la función segvn_fau l t comprue­ ba si tiene que aplicar la técnica de copiar al escribir y en caso afirmativo invoca a la función anon_private para hacer una copia privada de la página referenciada. 4. Cuando termina s egvn_ f au l t la función as_faul t invoca a la función ha t_menl oad para actualizar el contenido de la entrada de la tabla de página tanto en memoria como en el TLB de la MMU. • 4.4. Gestión de la memoria física en SOBUNIX 4.4.1 . Estructuras de datos utilizadas en la gestión de la memoria física Los SOBUNIX reservan una parte de la memoria principal para almacenar el código y los datos estáticos del núcleo. El resto de la memoria principal se encuentra disponible para albergar los datos SOBUNIX: administración de memoria 167 dinámicos del núcleo y las páginas de los procesos de los usuarios. Los marcos de página ocupados por páginas del núcleo son marcados en su mayoría como marcos bloqueados, es decir, marcos cuyo contenido no puede ser intercambiado fuera de la memoria principal. Con esta medida se pretende evitar que existan fallos de página durante la ejecución del núcleo que afecten a su rendimiento. Algunos SOBUNIX permiten que algunas pequeñas partes de su núcleo se alojen en marcos no bloqueados. Por ejemplo, en Solaris las páginas de las pilas de los procesos ligeros se alojan en marcos no bloqueados. Memoria principal ------ ����?_i�Q ______ Zona reservada M arco j=N - 1 Marcoj=N Marcoj=N+ I ED j=N ED j=N+ l Zona asignable dinámicamente ED j=M- 1 Estructuras de datos (ED) asociadas a los marcos de página de la zona asignable dinámicamente Marcoj=M- 1 Figura 4.8 - Distribución típica de la memoria principal en los SOBUNIX Para gestionar la parte de memoria de la memoria principal que se asigna dinámicamente el núcleo mantiene una estructura de datos por cada marco de página existente (ver Figura 4.8). Dicha estructura mantiene información sobre la página cargada en el marco, así como diferentes punteros para implemen­ tar diferentes listas, como por ejemplo la lista de marcos de página libres que es consultada cuando se requiere asignar un marco de página libre para cargar una página en memoria. La implementación y la gestión de las estructuras de datos asociados a los marcos de página, así como de las diferentes listas mantenidas a partir de estas estructuras, dependen de cada SOBUNIX. e Ejemplo 4. 1 1 En los SOBUNIX que utilizan la arquitectura V M d e gestión d e memoria, como por ejemplo Solaris, cada marco de página de la memoria principal tiene asociado una estructura page que contiene, entre otros, los siguientes datos: • Identidad de la página cargada en el marco. Cada página cargada en un memoria principal queda identificada de manera unívoca por el par de valores [nodo-v, desplazamiento], es decir, con el Ampliación de sistemas operativos 168 nodo-v del objeto de datos a partir del cual fue creada la página y el desplazamiento (expresado en páginas) dentro del objeto. • Contador de referencias. Se incrementa en una unidad por cada proceso que comparte el acceso a la página cargada en el marco. • Información relacionada con las estructuras ha t . Como por ejemplo la localización de todas las tablas de páginas que apuntan al marco. • Punteros. Para poder implementar las siguientes listas: - Lista hash de marcos de página. El núcleo usa esta estructura para encontrar rápidamente al marco que contiene una determinada página. El par de valores [nodo-v, desplazamiento] que identifican a la página se introducen a modo de clave en una función hash que devuelve el índice de una entrada de la lista hash. Cada entrada de la tabla hash estará vacía o contendrá una cola de estructuras page. Todas las estructuras en una misma cola contienen páginas cuyo par de valores [nodo-v, desplazamiento] generan el mismo índice al ser introducidos en la función hash. Una vez conocido el índice hay que examinar la cola para ver si contiene la página buscada. Obviamente este procedimiento es más rápido que examinar una a una todas las estructuras page. - Lista de páginas del nodo-v. Cada nodo-v (ver sección 5 .3 . 1 ) mantiene una lista de todas las páginas que se encuentran cargadas en memoria principal del objeto al que hace referencia el nodo-v. Esta lista es utilizada por las rutinas del núcleo que trabajan sobre todas las páginas cargadas en memoria de un determinado objeto. Por ejemplo, cuando se borra un archivo el núcleo debe invalidar todas las páginas del archivo que están cargadas en memoria. - Lista de marcos de página libres. Forman parte de esta lista las estructuras page de los marcos de página que no contienen ninguna página o contienen páginas que no son mapeadas por ningún proceso ya que han sido liberadas por la función page_free. - Lista de E/S. Forman parte de esta lista las estructuras page de los marcos de página que contienen páginas cuyo contenido ha sido modificado y debe ser salvadas en el disco antes de reemplazar su contenido. • 4.4.2. Reemplazamiento de páginas Una de las tareas fundamentales que debe realizar un sistema operativo para implementar la técnica de demanda de página es el reemplazamiento de páginas que consiste en seleccionar una página k cargada en un marco de página j para ser reemplazada por la página i asociada a la dirección virtual que produjo el fallo de página. SOBUNIX : administración de memoria 169 Para implementar el reemplazamiento de página el núcleo debe seleccionar el conjunto de páginas candidatas a ser reemplazadas, y usar algún algoritmo de reemplazamiento que seleccione, de entre el conjunto de páginas candidatas, la página víctima que puede ser reemplazada. En los SOBUNIX generalmente el conjunto de páginas candidatas está formado por todas las páginas cargadas en la memoria principal en marcos no bloqueados, es decir, utilizan lo que en la literatura se conoce como estrategia o política de reemplazamiento global. Asimismo, el algoritmo de reemplazamiento utilizado mayoritariamente en los S OBUNIX es alguna aproximación del algoritmo LRU (Least Recently Used, usada menos recientemente). La implementa­ ción de un algoritmo LRU estricto produce bastante sobrecarga por lo que se suele usar algún algoritmo cuyo funcionamiento se aproxime al del algoritmo LRU pero que produzca menos sobrecarga. e Ejemplo 4.12 El algoritmo del reloj con dos manecillas (two-handed clock algorithm) utilizado en diversos SOBU­ NIX, como por ejemplo: BSD4.3, SunOS 4.0, SVR4 y Solaris, es una aproximación del algoritmo LRU que elige como página víctima aquella no usada recientemente, en vez de la página menos usada recien­ temente que se selecciona en un algoritmo LRU. Este algoritmo examina las estructuras de datos asociados a los marcos de página no bloqueados, a los que considera organizados en una lista circular (horas del reloj). Para examinar las estructuras utiliza dos punteros (manecillas) . Cada puntero apunta a una estructura. La separación entre los punteros, es decir, el número de estructuras entre ellos, se mantiene siempre constante, siendo esta separación un parámetro configurable del algoritmo. Ambos punteros se mueven simultáneamente en sentido horario. Para describir el funcionamiento del algoritmo se va denominar puntero 1 al puntero que va por delante y puntero 2 al que va por detrás (ver Figura 4.9). El puntero 1 pone a O el bit referenciada (r) de la entrada de la tabla de páginas que referencia a la página cargada en el marco j. Por su parte el puntero D ED x D =Estructura de datos asociada al marco Figura 4.9 - x Algoritmo del reloj de dos manecillas 170 Ampliación de sistemas operativos 2 comprueba el bit r de la página cargada en el marco k. Si r = O entonces la página puede ser elegida para ser reemplazada ya que la página no ha sido referenciada en el tiempo transcurrido desde que el puntero 1 pasó por ella. El puntero 2 también comprueba el bit modificada m de la página cargada en el marco k, si dicho bit está 1 la página es colocada en la lista de páginas que deben ser salvadas al área de intercambio. • En los SOBUNIX para mejorar el rendimiento del sistema la búsqueda de páginas víctimas se realiza antes de que ya no quede memoria libre disponible. Periódicamente, o cuando el tamaño de la lista de marcos de página libres cae por debaj o de un determinado umbral, se invoca a un proceso del sistema denominado escáner de paginas5 • Este proceso se crea cuando se arranca el sistema y generalmente tiene asignado el PID=2. El escáner de páginas, cuando es invocado, ejecuta el algoritmo de reemplazamiento para seleccionar un cierto número de marcos de página cuyo contenido puede ser reemplazado. Los marcos seleccionados cuyo contenido no ha sido modificado son colocados en la lista de marcos libres. Los marcos cuyo contenido ha sido modificado son colocados en la lista de marcos modificados, los cuales deben ser salvados en el área de intercambio. Cuando se realice esta operación los marcos serán colocados en la lista de marcos libres. Nótese que en consecuencia en la lista de marcos libres no solo hay marcos que contienen páginas que pertenecen a procesos que ya han finalizado sino también marcos que contienen páginas pertenecientes a procesos que pueden estar todavía activos pero que como no han sido recientemente referenciadas han sido seleccionados como candidatas a ser reemplazadas. Por este motivo el núcleo cuando se produce un fallo de página comprueba si dicha página se en­ cuentra en la lista de marcos de página libres y de esta forma ahorrarse tener que realizar una operación de E/ S al disco. De hecho algunos SOBUNIX, distinguen dos tipos de listas de marcos de páginas libres : en una lista se mantienen los marcos que contienen páginas que no van a volver a ser necesitadas y en otra lista se mantienen páginas que pueden ser necesitadas. Solo cuando la primera se agota se empiezan asignar marcos de la segunda. El escáner de páginas se ejecuta hasta que se cumple un determinada condición de parada: haber seleccionado para reemplazar un determinado número máximo de marcos de página, haber consumido una cantidad preestablecida de ciclos de CPU, etc. Cuando se cumple la condición de parada el escáner de páginas entra en el estado dormido hasta que vuelve a ser despertado porque haya transcurrido un cierto tiempo o porque haya caído la memoria libre por debajo de un cierto límite. Puesto que la disponibilidad de memoria libre afecta al rendimiento del sistema, la prioridad de ejecución del escáner de páginas es bastante elevada. 5 A este proceso también se le conoce con el nombre de demonio de páginas (page daemon) o ladrón de páginas (page stealer). En general el nombre de este proceso suele depender de cada SOBUNIX. En este texto se utiliza el nombre que emplea Solaris: escáner de páginas, el cual describe bastante bien la tarea que realiza el proceso. S OBUNIX : administración de memoria 4.4.3. 171 Intercambio de procesos Si el número de procesos cargados parcialmente en la memoria principal supera un cierto valor máximo, los conjuntos de trabaj o de los procesos, es decir, sus páginas referenciadas más frecuentemente, no pueden estar todos contenidos en la memoria principal con lo que comienzan a producirse fallos de página continuamente. Una posible forma de eliminar el trasiego consiste en intercambiar de memoria principal al área de intercambio todas las páginas de uno o varios procesos. En los SOBUNIX, esta tarea la realiza un pro­ ceso del sistema denominado proceso intercambiador (swapper). Este proceso tiene asignado el PID=O, ya que es el primer proceso que se crea cuando se arranca un S OBUNIX. Este proceso inicialmente crea al proceso ini t (PID= 1 ) y el escáner de páginas (PID=2), y a continuación pasa a ejecutar una fun­ ción, denominada s ched en algunos SOBUNIX, que contiene todo el código asociado al intercambio de procesos. El proceso intercambiador es invocado cada cierto tiempo o si el tamaño de la lista de marcos libres cae frecuentemente por debajo de un cierto valor límite, lo cual recuérdese propiciaba la invocación del escáner de páginas. El proceso intercambiador debe seleccionar de acuerdo con unos determinados criterios los procesos cuyas páginas van a ser intercambiadas al área de intercambio. Cuando termina de realizar su tarea el proceso intercambiador entra en el estado dormido a la espera de volver a ser invocado. La implementación y los criterios de selección que utiliza el proceso intercambiador dependen de cada SOBUNIX. e Ejemplo 4.13 En Solaris se distinguen dos posibles tipos de intercambio : • Intercambio suave. Se produce s i e l tamaño d e l a lista d e marcos d e página libre permanece por debajo de un cierto valor mínimo de s f ree durante alrededor de 30 segundos. El proceso inter­ cambiador, también denominado planificador de memoria, comienza a buscar procesos que han estado inactivos durante al menos maxs lp segundos. Para cada proceso que cumple está condi­ ción intercambia al área de intercambio todas sus páginas privadas y las estructuras de datos del núcleo asociadas a la gestión de sus hilos. • Intercambio duro. S e produce si se dan simultáneamente las siguientes condiciones: existen más de dos procesos en las colas de procesos preparados para ejecución, el tamaño de la lista de marcos de página libre permanece por debajo de un cierto valor mínimo de s f ree durante alrededor de 30 segundos, y la razón de fallos de página es mayor que maxpg i o . El intercambiador selecciona en primer lugar para ser intercambiadas todos las páginas pertenecientes a módulos del núcleo que no están activos. A continuación los procesos son intercambiados secuencialmente hasta que se consigue el espacio libre suficiente. • 172 Ampliación de sistemas operativos El proceso intercambiador también comprueba periódicamente si los procesos intercambiados pue­ den volver a ser cargados en la memoria principal. El criterio de retorno que se utiliza depende de la memoria libre disponible. 4.4.4. Asignación de memoria principal En los SOBUNIX la asignación de marcos de página de memoria principal la realiza un componente del subsistema de gestión de memoria principal denominado asignador a nivel de página. Este asignador atiende las peticiones de memoria del sistema de paginación y del asignador de la memoria del núcleo (ver Figura 4. 1 0) . Lista d e marcos libres Marco j Estructuras de datos para los subsistemas del núcleo Procesos usuarios Caché de buffers de bloques de disco Figura 4.10 - Asignación de memoria principal en SOBUNIX El sistema de paginación es la funcionalidad de la arquitectura de gestión de memoria virtual en­ cargada de asignar (y liberar) marcos de página para alojar en memoria principal las páginas virtuales referenciadas por los procesos durante su ejecución. En algunos S OBUNIX también se encarga de asig­ nar marcos para la caché de buffers de bloques de disco. El sistema de paginación solicita marcos de página al asignador a nivel de página para repartirlos entre sus clientes: los procesos de los usuarios o la caché de buffers de bloques de E/S . Por su parte, el asignador de la memoria del núcleo solicita marcos de página al asignador a nivel de página para atender las peticiones de memoria de los diferentes subsistemas del núcleo. El asignador a nivel de página trabaja sobre la lista de marcos de página libres sobre la que realiza, entre otras, las siguientes operaciones: S OBUNIX: administración de memoria 173 • Eliminar marcos. Cuando el sistema de paginación o el asignador de la memoria del núcleo nece­ sitan una determinada cantidad de memoria principal invocan al asignador a nivel de página para que les asigne marcos libres de memoria principal, los cuales son eliminados de la lista de marcos de página libres. • Añadir marcos. Cuando el sistema de paginación o el asignador de la memoria del núcleo liberan una cierta cantidad de memoria invocan al asignador a nivel de página para que añada los marcos liberados a la lista de marcos de página libres. • Buscar páginas en los marcos. Durante determinados eventos, como por ejemplo , el tratamiento de un fallo de página, el sistema de paginación puede solicitar al asignador a nivel de página que busque en la lista de marcos de página libres si se encuentra cargada en un marco de dicha lista la página cuya referencia originó el fallo de página. Puede suceder que dicha página se encuentre en esta lista por haber sido seleccionada por el escáner de página para ser reemplazada. Si la página se encuentra en la lista se elimina de la misma. Nótese que encontrar una página en esta lista evita tener que leerla del disco, con lo cual se ahorra tiempo, ya que una operación de búsqueda sobre la lista requiere de menos tiempo que una operación de lectura a disco. El asignador a nivel de página se implementa como un conjunto de rutinas que pueden ser invocadas por el sistema de paginación o el asignador de la memoria del núcleo. Dichas rutinas implementan toda las operaciones que realiza el asignador. e Ejemplo 4.14 Entre las rutinas que implementan el asignador a nivel de página de Solaris se encuentran las siguientes: • page_c reat e_va . Esta función devuelve una lista enlazada con todos los marcos que han sido extraídos de la lista de marcos de página libres de acuerdo con la cantidad de memoria solicitada. • page_f r e e . Esta función libera un marco de página y lo coloca en la lista de marcos de página libres. • page_l ookup . Esta función busca la existencia de una determinada página, identificada por su nodo-v y desplazamiento, en la lista de marcos de página libres. • 4.5. Gestión del área de intercambio en SOBUNIX La implementación de la técnica de gestión de memoria mediante demanda de página requiere la existencia de un espacio de almacenamiento en memoria secundaria en el que copiar aquellas páginas de procesos activos cargadas en la memoria principal que son seleccionadas para ser reemplazadas. Obvia­ mente las páginas pertenecientes a procesos terminados no necesitan ser copiadas. A dicho espacio se le 174 Ampliación de sistemas operativos denomina área de intercambio. En los SOBUNIX el área de intercambio está formada típicamente por una o varias particiones de discos no formateadas a alto nivel y a las que se accede mediante operaciones de E/S en bruto. Las páginas pertenecientes a la mayoría de las regiones del espacio de direcciones virtuales de un proceso (datos no inicializados, montículo, pila, archivos mapeados en memoria con el indicador MAP_PRIVATE, etc) son copiadas en el área de intercambio si son seleccionadas para ser reemplazadas. Las páginas de código de los procesos o de las librerías compartidas no son copiadas en el área de intercambio. Estas páginas son generadas desde el archivo ejecutable correspondiente si es necesario cargarlas de nuevo en la memoria principal debido a un fallo de página. Lo mismo ocurre con las páginas seleccionadas para ser reemplazadas pertenecientes a archivos mapeados con el indicador MAP_SHARED. En este caso, además, si las páginas han sido modificadas son salvadas en los bloques de disco del archivo. Cuando se crea una región del espacio de direcciones virtuales de un proceso que no sea de código se reserva el espacio suficiente en el área de intercambio para poder almacenar todas sus páginas. Una página i perteneciente a una determinada región de un proceso activo X se copia en el área de intercambio la primera vez que ésta es seleccionada por el escáner de páginas para ser reemplazada. Si posteriormente la ejecución del proceso X produce un fallo de página asociado a la página i el núcleo comprueba si existe una copia de la página en el área de intercambio. En caso afirmativo la página es copiada en memoria principal desde el área de intercambio. Más tarde si la página i vuelve a ser seleccionada para ser reemplazada de la memoria principal solo se copiará de nuevo en el área de intercambio si su contenido ha sido modificado (el bit m de su entrada de la tabla de páginas está activo), es decir, si la copia de la página i en memoria principal difiere de la copia de la página i en el área de intercambio. El núcleo mantiene una lista de espacio libre en el área de intercambio que consulta cuando necesita asignar espacio del área de intercambio. También mantiene alguna clase de mapa de intercambio para poder localizar rápidamente las páginas almacenadas en el área de intercambio. La implementación y mantenimiento de estas estructuras depende de cada SOBUNIX. e Ejemplo 4.15 En los SOBUNIX que implementan la arquitectura VM de gestión de memoria, como Solaris, el área de intercambio se utiliza para almacenar temporalmente las páginas anónimas pertenecientes a proce­ sos activos que son eliminadas de memoria principal. En consecuencia la implementación del área de intercambio está estrechamente relacionada con la capa anónima. Solaris implementa en la memoria principal un pseudosistema de archivos denominado swapfs que utiliza para asignar espacio de intercambio virtual a las páginas anónimas. En un determinado instante de tiempo el tamaño del espacio virtual definido en swapfs viene determinado por la suma del tamaño del espacio libre existente en el área de intercambio y el tamaño del espacio libre existente en la zona paginada de memoria principal. S olaris también define un conj unto de funciones denominado capa swapfs que utiliza para operar sobre el pseudosistema y sobre el área de intercambio. Las funciones de la capa swapfs son invocadas por funciones de la capa anónima cuando se requiere asignar espacio de intercambio a una página anónima. SOBUNIX: administración de memoria 175 Cada página anónima tiene asociado el nodo-v de un archivo de swapfs y un desplazamiento dentro de dicho archivo medido en páginas. Nótese que el archivo no ocupa espacio físico sino virtual. Solo cuando la página anónima es seleccionada para ser reemplazada es necesario asignarle espacio real dentro del área de intercambio, el cual queda localizado mediante el nodo-v del archivo asociado al dispositivo físico (ver sección 5 . 5 .2) donde se encuentra el espacio de intercambio real y un desplazamiento, medido en páginas, dentro de dicho archivo. Si el espacio dentro del área de intercambio se agota, entonces se utiliza como espacio de intercambio marcos libres de memoria principal, los cuales son marcados como bloqueados. La asignación de espacio virtual a través de swapfs permite no malgastar espacio de intercambio real. A diferencia de lo que sucede en otros SOBUNIX, como SVR4, que utilizan una política más conservadora consistente en reservar y asignar espacio de intercambio a todas las páginas de un segmento aunque posiblemente algunas de ellas nunca vayan a utilizarlo. Cuando se crea un segmento de memoria virtual que se mapea al objeto anónimo o a un archivo con un mapeado del tipo MAP_PRIVA TE se crea un array de referencias a estructuras anon. En este array existe una entrada por cada página virtual existente en el segmento. Cada entrada contiene un puntero a una estructura anon, la cual contiene información sobre la localización de la página anónima en el espacio de intercambio. En el caso de S olaris (ver Figura 4. 1 1 ) entre los campos presentes en una estructura anon se encuentran los siguientes: • an_vp. Contiene un puntero al nodo-v de un archivo de swapjs. • an_o f f . Contiene el desplazamiento medido en páginas desde el comienzo del archivo ubicado en swapfs. • an_pvp. Contiene el valor NULL si todavía no ha sido necesario asignar espacio de intercambio real a la página anónima. En caso contrario contiene al nodo-v del archivo asociado al dispositivo físico donde se encuentra el espacio de intercambio real. • an_po f f . Contiene el valor NULL si todavía no ha sido necesario asignar espacio de intercambio real a la página anónima. En caso contrario contiene el desplazamiento medido en páginas desde el comienzo del archivo asociado al dispositivo físico donde se encuentra el espacio de intercambio real. Luego el par de valores [an_vp, an_o f f ] permite localizar a una página anónima en swapfs, es decir, en el espacio de intercambio virtual . Mientras que el par de valores [an_pvp, an_po f f] permite localizar a una página anónima en el área de intercambio, es decir, en el espacio de intercambio real. Cuando el driver de un segmento invoca a la función anon_a l l o c para obtener una página anónima nueva, esta función invoca a su vez a la función swap f s_ge tvp para obtener un nodo-v de swapfs, y posteriormente a la función swap f s_ge tpage para crear una nueva página virtual en el archivo de swapfs. Esta función devuelve el desplazamiento dentro del archivo. Con esta información se inicializa los campos an_vp y an_o f f de una estructura anon. 176 Ampliación de sistemas operativos Nodo-v an_vp an o f f -----+--� an__pvp =NULL swapfs an__p o f f =NULL (a) Nodo-v swapfs Nodo-v Área de intercambio (b) Figura 4.11 - Asignación de espacio de intercambio virtual y real : (a) En el momento de la creación de una página anónima. (b) Cuando la página es transferida por primera vez fuera de memoria principal. Por otra parte los campos an__pvp y an__po f f de una estructura anon se inicializan con el valor NULL. Cuando la página sea seleccionada por primera vez por el escáner de páginas para ser reemplazada entonces se le asignará espacio en el área de intercambio y se configurarán adecuadamente los valores de estos campos. Para asignar espacio real de intercambio el escáner de páginas busca el nodo-v asignado a la página e invoca a una operación putpage de dicho nodo-v. En el caso de una página anónima el nodo-v apunta a un archivo de swapfs y en consecuencia se invoca a la función swap f s__pu tpage. Esta función asigna un bloque del tamaño de una página en el área de intercambio y configura adecuadamente los valores de los campos an__pvp y an__po f f de la estructura anon asociada a la página. Ahora sí la página tiene asociado espacio de intercambio real y puede ser intercambiada fuera de memoria principal. • SOBUNIX: administración de memoria 4.6. Gestión de la memoria perteneciente al núcleo en SOBUNIX 4.6.1. Espacio de direcciones virtuales del núcleo 177 El núcleo al igual que los procesos de los usuarios dispone de un espacio de direcciones virtuales. La localización de dicho espacio depende de cada SOBUNIX y de la arquitectura del computador donde se ejecute. En algunas arquitecturas existe un único espacio de direcciones virtuales el cual es compartido por el núcleo y los procesos de los usuarios. En dicho caso, generalmente, el espacio de direcciones virtuales del núcleo ocupa las direcciones virtuales más altas. En otras arquitecturas el núcleo y los procesos disponen de espacios de direcciones independientes. En ese caso el núcleo y los procesos tiene a su disposición todo el rango de direcciones virtuales, desde la dirección virtual O hasta una cierta dirección virtual máxima. El espacio de direcciones del núcleo se descompone en diferentes regiones o segmentos: región de código, región de datos, montículo, etc. Las regiones existentes en el espacio de direcciones virtuales del núcleo y su organización dependen de cada SOBUNIX y de la arquitectura del computador. El núcleo mantiene diferentes estructuras de datos para gestionar su propio espacio de direcciones virtuales. Además tiene que mantener una tabla de páginas para que la MMU pueda traducir las direc­ ciones virtuales del núcleo en direcciones físicas. e Ejemplo 4.16 En la Figura 4. 1 2 se muestra la distribución del espacio de direcciones virtuales del núcleo de 32 bits de Solaris en la arquitectura x64 de AMD. Se observan los siguientes segmentos en el espacio virtual del núcleo : datos, código, estructuras de datos para la gestión de memoria, montículo y la caché de pá­ ginas de archivos ( s eg_map) . Esta caché contiene las páginas de archivos que han sido leídas o escritas recientemente durante el tratamiento de las llamadas al sistema read o wr i te (ver sección 5 .2. 1 ) . Ini­ cialmente la dirección virtual base de la caché de archivos es OxC3 0 0 2 0 0 0 aunque puede extenderse dentro del rango de direcciones virtuales denominado zona roja. Como sucedía con el espacio de direcciones virtuales de los procesos el núcleo mantiene una estructura as para mantener su propio espacio de direcciones. La dirección de la estructura as del núcleo se man­ tiene en la variable kas . Recuérdese que una estructura as contenía información sobre los segmentos de un espacio de direcciones virtuales. En el caso del núcleo sus segmentos pueden ser, principalmente, de tres tipos: • s eg_kmen. Las páginas de un segmento de este tipo se alojan en marcos bloqueados de memoria principal. Los segmento de código, datos, pila y montículo del núcleo son segmentos de este tipo. • seg_map . Las páginas de un segmento de este tipo se alojan en marcos bloqueados de memoria principal. Este tipo de segmento se utiliza para implementar la caché de páginas de archivos del núcleo. • s eg_kp . Las páginas de un segmento de este tipo se alojan en marcos no bloqueados de memoria principal. Las pilas de los procesos ligeros se alojan en segmentos de este tipo. 178 Ampliación de sistemas operativos OxFFFFFFFF 1 1 - - - - - - - - - - - - - - - - 1 1 OxFF8 0 0 0 0 0 Datos del núcleo OxFE C O O O O O Código del núcleo O x FE 8 0 0 0 0 0 va r i ab l e Estructuras de datos de gestión de la memoria Montículo del núcleo va r i a b l e seg_map OxC3 0 0 2 0 0 0 Zona roja OxC3 0 0 0 0 0 0 Espacio de direcciones virtuales de usuario OxO O O O O O O O - Figura 4.12 Distribución del espacio de direcciones virtuales del núcleo de 32 bits de Solaris en la arquitectura x64 • 4.6.2. Asignación de la memoria del núcleo Los subsistemas del núcleo necesitan memoria principal para alojar dinámicamente diferentes es­ tructuras de datos. Generalmente el tamaño de estas estructuras suele ser más pequeño que el tamaño de una página y por ello asignarles páginas completas supone un desperdicio de memoria. Este es el motivo por el que en los SOBUNIX existe un componente denominado asignador de la memoria del núcleo que solicita marcos de página al asignador a nivel de página y los descompone en trozos más pequeños que reparte entre los subsistemas del núcleo que solicitan memoria. Este asignador es el encargado de atender las peticiones de memoria física realizadas por los subsistemas del núcleo para aloj ar diferentes estructuras de datos. El asignador de la memoria del núcleo puede implementarse de diferentes formas, las cuales pueden ser comparadas atendiendo a criterios tales como la fragmentación de memoria que producen y la rapidez SOBUNIX: administración de memoria 179 con que atienden las peticiones. Entre las implementaciones más usadas en los SOBUNIX se encuentran el sistema buddy y el asignador slab. El sistema buddy se describe en la sección 6.5.2, en la siguiente subsección se describe el asignador slab. Asignador slab El asignador slab 6 fue desarrollado por Jeff B onwick [Bonwick, 1 994] para implementar el asignador de la memoria del núcleo de Solaris 2.4. Debido a su eficiencia y rapidez ha sido exportado a otros SOBUNIX, como por ejemplo, Linux. El asignador slab utiliza tres tipos de elementos : • Objeto. Hace referencia a u n determinado tipo d e estructura d e datos. Cada objeto queda identifi­ cado por el tamaño que ocupa y el nombre que se le asigna, que es una cadena de caracteres que lo describe, por ejemplo: nodo-v, nodo-i, etc. Todos los objetos que se refieren a un mismo tipo de estructura de datos tienen el mismo tamaño y el mismo nombre. Un objeto puede estar libre o asignado • Slab. Es un conjunto de No objetos de un mismo tipo. Cada slab se construye con Np marcos de página contiguos de memoria principal que son suministradas por el asignador a nivel de página. • Cache de objetos. Es un conjunto de Ns slabs de obj etos de un mismo tipo. El asignador slab trabaj a con varias cachés de objetos. Si un subsistema del núcleo necesita crear una caché para un determinado tipo de objetos entonces invoca a la función kmem_c ache_c r e a t e . Por el contrario para eliminarla invoca a la función kmem_c ache_de s t roy. Si desea solicitar la asignación de un objeto de una caché debe invocar a la función kmem_c ache_a l l o c . Cuando termine de usar el objeto lo liberará invocando a la función kmem_c ache_f r e e . Cuando todos los objetos d e una caché están asignados entonces e l asignador slab solicita al asigna­ dar a nivel de página un conjunto de marcos de página contiguos para crear un nuevo slab en la caché. También es posible destruir un slab cuando todos sus objetos están libres y devolver al asignador a nivel de páginas las páginas que ocupa dicho slab. Dentro de una caché por cada slab existente se mantiene una estructura que contiene, entre otros, los siguientes datos: el número de objetos asignados en el slab, una lista de objetos libres y punteros para mantener una lista doblemente enlazada de slabs de la caché. El asignador slab se basa en el hecho de que los subsistemas del núcleo trabaj an siempre con es­ tructuras de datos de unos determinados tamaños que son asignadas y liberadas dinámicamente. Por ello resulta más eficiente y produce menos fragmentación mantener cachés con las estructuras que son liberadas, para que puedan ser reutilizadas, que volver asignarles memoria para construirlas desde cero. e Ejemplo 4 . 17 En la Figura 4. 1 3 se muestra en un determinado instante de tiempo el estado del asignador slab, se observa la existencia de dos cachés de objetos: una para objetos X1 de 2 KiB y otra para obj etos X2 de 6 Slab es una palabra inglesa que se puede traducir como lámina, pedazo, bloque o tajada. 180 Ampliación de sistemas operativos 3 KiB . En la Figura 4. 14 se detalla el estado de la caché de objetos X 1 , se observa la existencia de dos slabs. Cada slab consta de ocho obj etos y ocupa cinco marcos de página contiguos de 4 KiB . Nótese que de los 20 KiB ocupados por un slab, 1 6 KiB son para almacenar los ocho objetos que contiene y los restantes 4 KiB para almacenar la información de gestión del slab. • Asignador slab .................................................. Caché de objetos X l de 2 KiB Objetos X l Subsistemas del núcleo Objetos X2 ¡ .. 1 �! ; �� , Caché de o eo 2 [_ - - Páginas Páginas - Figura 4.13 - Asignador slab del Ejemplo 4.1 7 Marcos de página de memoria principal Caché de objetos X l de 2 KiB Slab . ..... ............. ...... ... . .. -- . . .. ,....- . . ...=:::._ : --, _ . . . Objeto X l de 2 KiB asignado a un subsistema del núcleo ·· ····· ···· ··· ···· Asignado LíQre Libre Asignado Libre Libre Libre Asigna o ".... ... . ..... .. .. ... ........ ...... ........ ...... . .. . . . . . . ................. ........................................ l======l ................. .................. .----- ---... ..... ......... . ..... ...... . ......................,...-----, Slab .. . ···· ···· ······ ···· I,.._____________J Figura 4.14 - Asignador a nivel se página ... . ...................... 1....-..----' .. ................ ............... ,____ .......... ____ _J Caché de objetos X 1 de 2 KiB de asignador slab del Ejemplo 4. 1 7 20 KiB SOBUNIX: administración de memoria 4. 7. 181 Resumen El subsistema de administración de memoria es el componente del núcleo de un sistema operativo encargado de administrar, en colaboración con el hardware, la memoria principal entre los restantes subsistemas del núcleo y entre todos los procesos de usuario que se ejecutan en el computador. Para realizar esta tarea se pueden usar diferentes técnicas de administración de memoria. En los SOBUNIX se utiliza la técnica de paginación por demanda la cual posibilita también la implementación de la memoria virtual. Un archivo mapeado en memoria es una región del espacio de direcciones virtuales de un proceso en la que se establece una correspondencia byte a byte con parte o con la totalidad de un archivo. Una vez mapeado en memoria, el acceso a un determinado byte del archivo puede ser realizado como a cualquier otro contenido del espacio de direcciones virtuales, es decir, indicando su dirección virtual, lo que evita tener que realizar llamadas al sistema, con lo que el acceso es mucho más rápido. Una página anónima es una página que antes de su creación no dispone de un almacenamiento persistente de respaldo en memoria secundaria. Las páginas anónimas son utilizadas por ejemplo, en la región de datos no inicializados, el montículo y la región de pila. Al conjunto de páginas anónimas utilizadas por un proceso se le denomina memoria anónima del proceso. Con el objetivo de ahorrar espacio de memoria principal los SOBUNIX suelen trabaj ar con tablas de páginas paginadas, el número de niveles de paginación utilizados es fijado por cada SOBUNIX en función de la arquitectura. En los SOBUNIX generalmente el conjunto de páginas candidatas a ser reemplazadas está formado por todas las páginas cargadas en la memoria principal en marcos no bloqueados, es decir, utilizan lo que en la literatura se conoce como estrategia o política de reemplazamiento global. Asimismo, el algoritmo de reemplazamiento utilizado mayoritariamente en los SOBUNIX es alguna aproximación del algorit­ mo LRU. La implementación de un algoritmo LRU estricto produce bastante sobrecarga por lo que se suele usar algún algoritmo cuyo funcionamiento se aproxime al del algoritmo LRU pero que produzca menos sobrecarga. Un ejemplo de un algoritmo con estas características es el algoritmo del reloj de dos manecillas. El área de intercambio en los SOBUNIX está formada típicamente por una o varias particiones de discos no formateadas a alto nivel y a las que se accede mediante operaciones de E/S en bruto. Las páginas pertenecientes a la mayoría de las regiones del espacio de direcciones virtuales de un proceso son copiadas en el área de intercambio si son seleccionadas para ser reemplazadas. En los SOBUNIX la asignación de marcos de página de memoria principal la realiza un componente del subsistema de gestión de memoria principal denominado asignador a nivel de página. Este asignador atiende las peticiones de memoria del sistema de paginación y del asignador de la memoria del núcleo. El asignador de la memoria del núcleo puede implementarse de diferentes formas, las cuales pueden ser comparadas atendiendo a criterios tales como la fragmentación de memoria que producen y la rapidez con que atienden las peticiones. Entre las implementaciones más usadas en los SOBUNIX se encuentran el sistema buddy y el asignador slab. 182 4.8. Ampliación de sistemas operativos Lecturas recomendadas Si se desea obtener una explicación adicional de los contenidos tratados en este capítulo se pueden consultar, por ejemplo: los capítulos 1 2 a 1 5 de [Vahalia, 1 995] , los capítulos 8 a 1 3 de [McDougall y Mauro, 2006] y el capítulo 1 O de [Tanenbaum, 2009] . 4.9. Autoevaluación 4 . 1 . ¿Qué técnica de administración de memoria se utiliza generalmente en los SOBUNIX? (Respuesta en sección 4. 1 ) 4 . 2. Enumerar y describir las regiones o segmentos e n que s e puede descomponer el espacio d e direc­ ciones virtuales de un proceso. (Respuesta en sección 4.2. 1 ) 4 .3 . ¿Qué comando s e puede utilizar en los SOBUNIX para obtener información sobre e l espacio de direcciones virtuales de un proceso? (Respuesta en sección 4.2. 1 ) 4 . 4 . Explicar cómo s e inicializa u n marco de página de la memoria principal en función del tipo de región a la que pertenece la página del espacio de direcciones virtuales de un proceso que se va alojar en dicho marco. (Respuesta en sección 4.2. 1 ) 4 .5 . ¿Qué e s u n archivo mapeado e n memoria? ¿Qué ventajas e inconvenientes presenta s u uso? (Respuesta en sección 4.2.2) 4 . 6 . Explicar la utilidad y la sintaxis de las llamadas al sistema rrunap y munmap. (Respuesta en sección 4.2.2) 4 .7 . Explicar cómo se implementa de forma general en los SOBUNIX el espacio de direcciones virtua­ les de un proceso. (Respuesta en sección 4.2.3) 4 . 8 . ¿Qué se entiende por objeto de datos en Solaris? ¿Qué es el objeto anónimo? (Respuesta en sección 4.2.3) 4 . 9 . Enumerar y describir brevemente los tipos de segmentos más frecuentemente utilizados en Solaris. (Respuesta en sección 4.2.3) 4 . 10. Dibuj ar un diagrama que ilustre la relación entre las estructuras de datos usadas en Solaris para describir el espacio de direcciones virtuales de un proceso. Comentar brevemente la información que contiene cada estructura. (Respuesta en sección 4.2.3) 4 . 1 1 . ¿Qué es una página anónima? ¿Cuándo se crea? ¿Qué se entiende por memoria anónima de un proceso? (Respuesta en sección 4.2.4) SOBUNIX: administración de memoria 183 4.12. Dibujar un diagrama que ilustre la relación entre las estructuras de datos usadas en Solaris para describir las páginas anónimas de un segmento de tipo seg_vn. Comentar brevemente la informa­ ción que contiene cada estructura. (Respuesta en sección 4.2.4) 4.13. ¿Qué es la capa anónima de Solaris? ¿Qué funciones desempeña? (Respuesta en sección 4.2.4) 4.14. Dibujar un diagrama que ilustre la relación entre las estructuras de datos usadas en Solaris para implementar un recurso IPC del tipo memoria compartida. Suponer que la memoria compartida es un segmento de tipo seg_vn. Comentar brevemente la información que contiene cada estructura. (Respuesta en sección 4.2.5) 4.15. ¿Qué operaciones se puede realizar sobre el espacio de direcciones virtuales de un proceso? (Respuesta en sección 4.2.6) 4.16. ¿Cómo se realiza de forma general en los SOBUNIX la creación del espacio de direcciones vir­ tuales de un proceso? (Respuesta en sección 4.2.7) 4.17. Explicar razonadamente como colaboran el hardware (MMU) y el núcleo en la traducción de direcciones virtuales a direcciones físicas en la técnica de paginación por demanda. (Respuesta en sección 4.3.1) 4.18. ¿De qué depende el número de niveles de paginación utilizado en los SOBUNIX? (Respuesta en sección 4.3.1) 4.19. Explicar qué es, para qué sirve y cómo se implementa la capa HAT utilizada en los SOBUNIX que implementan la arquitectura VM de gestión de memoria. (Respuesta en sección 4.3.1) 4.20. Explicar razonadamente cómo se realiza de forma general el tratamiento de los fallos de página en los SOBUNIX. (Respuesta en sección 4.3.2) 4.21. Describir cómo se distribuye generalmente la memoria principal en los SOBUNIX. (Respuesta en sección 4.4.1) 4.22. ¿Qué información debe mantener el núcleo para gestionar la memoria principal? (Respuesta en sección 4.4.1) 4.23. ¿Para qué utiliza Solaris una estructura page? ¿Qué información contiene? (Respuesta en sección 4.4.1) 4.24. Enumerar las listas en que organiza Solaris las estructuras page. Comentar la utilidad de dichas listas. (Respuesta en sección 4.4.1) 4.25. ¿Qué tipo de estrategia o política de reemplazamiento de páginas utilizan los SOBUNIX? ¿Cuál es el algoritmo de reemplazamiento utilizado mayoritariamente por los SOBUNIX? (Respuesta en sección 4.4.2) 184 Ampliación de sistemas operativos 4 . 26 . Describir el funcionamiento del algoritmo de reemplazamiento de páginas del reloj con dos mane­ cillas utilizado en diversos SOBUNIX. (Respuesta en sección 4.4.2) 4 . 27 . ¿Qué es y para qué se utiliza el escáner de páginas? ¿Cuándo se invoca? (Respuesta en sección 4.4.2) 4 . 28 . ¿Por qué el núcleo cuando se produce un fallo de página comprueba si la página que ha originado el fallo se encuentra en la lista de marcos de página libres? (Respuesta en sección 4.4.2) 4 . 29 . ¿Qué es y para qué se utiliza el proceso intercambiador? ¿ Cuándo se invoca? (Respuesta en sección 4.4.3) 4 .30 . ¿Qué es y a quién atiende el asignador a nivel de páginas? ¿ Qué operaciones realiza sobre la lista de marcos de página libres? (Respuesta en sección 4.4.4) 4 .31 . ¿Cómo se forma el área de intercambio en los SOBUNIX? ¿Para qué se utiliza en los SOBUNIX modernos? (Respuesta en sección 4.5) 4 .32 . Describir la gestión del área de intercambio en los SOBUNIX. (Respuesta en sección 4.5) 4 .33 . ¿Para que se utiliza el pseudosistema de archivos swapfs en Solaris? (Respuesta en sección 4.5) 4 .34 . ¿Qué es y para qué se utiliza la capa swapfs de Solaris? (Respuesta en sección 4.5) 4 .35 . ¿Qué parte del espacio de direcciones virtuales se reserva para el núcleo en los SOBUNIX si éste comparte el espacio con los procesos de usuario? ¿Qué regiones o segmentos se suelen distinguir en el espacio de direcciones virtuales del núcleo? (Respuesta en sección 4.6.1) 4 .36 . ¿Qué tareas realiza el asignador de la memoria del núcleo de los SOBUNIX? ¿Cómo se imple­ menta? (Respuesta en sección 4.6.2) 4 .37 . Explicar razonadamente la implementación y el funcionamiento del asignador slab en Solaris. (Respuesta en sección 4.6.2) 4.10. Ejercicios 4 . 1 . Modificar el programa prog4 1 de la Figura 4.2 para que el mapeado del archivo t ex t o l . txt sea de tipo privado. Explicar razonadamente los efectos de esta modificación. 4 . 2 . Considérese el programa prog4 2 cuyo código C se muestra en la Figura 4. 1 5 . Invocarlo en segun­ do plano desde la línea de órdenes del intérprete de comandos de un SOBUNIX: a) Cuando aparezca el primer mensaje en la pantalla escribir la orden prnap [ P I D ] donde [ P I D ] hace referencia al PID del proceso asociado al programa. Explicar la información que se muestra en la pantalla. , b) Repetir el apartado anterior pero ahora cuando aparezca el segundo mensaje en pantalla. SOBUNIX: administración de memoria 185 # i nc lude < s t d i o . h> ma i n ( ) { int x= O ; char * b ; x=g e tp i d ( ) ; pr i n t f ( " \ nPID= %d due rme 2 0 s \ n " , x) ; s l e ep ( 2 0 ) ; pr i n t f ( " \ n P ID= %d vu e lve a dorm i r o t r o s 2 0 s \ n " , b= ( char * ) x) ; rna l l o c ( 1 4 * s i z e o f ( char ) ) ; s l e ep ( 2 0 ) ; pr i n t f ( " \ n P ID= %d F i na l i z a \ n " , x) ; Figura 4.15 - Código C del programa prog 4 2 4 .3 . Ejecutar en u n intérprete d e comandos d e u n S OBUNIX l a orden vms tat 5 2 . Consultar el ma­ nual de ayuda man para explicar la funcionalidad de este comando y el significado de la salida que muestra en la pantalla. Capítulo 5 SOBUNIX: gestión de archivos y gestión de la E/S Objetivos docentes Los objetivos docentes de este capítulo son los siguientes: • Conocer las características principales de la gestión de archivos en los SOBUNIX, así como las principales llamadas al sistema disponibles para la realización de esta tarea. • Saber qué es, para qué se utiliza y cómo se implementa la capa nodo virtual/ sistema de archivos virtual existente en los SOBUNIX modernos. • Conocer cómo se realiza el análisis de nombres de rutas en los SOBUNIX. • Conocer las características principales, la estructura en disco y cómo se gestiona un sistema de archivos UFS . • Saber cómo se realiza la gestión de la E/S en los SOBUNIX. • Saber qué son y cómo se utilizan los conectores (sockets) en los SOBUNIX. 5.1. Introducción Este capítulo está dedicado al estudio de la gestión de archivos y la gestión de la E/S en los SO­ BUNIX. En primer lugar se explican las características principales de los archivos, los directorios, los enlaces y los sistemas de archivos en los SOBUNIX. Asimismo se enumeran y describen las principales llamadas al sistema disponibles en los SOBUNIX para la gestión de estos elementos . En segundo lugar se describe la capa nodo virtual/sistema de archivos virtual utilizada en la mayoría de los SOBUNIX modernos para poder soportar diferentes tipos de sistemas de archivos. En tercer lugar se explica cómo 1 87 188 Ampliación de sistemas operativos se analizan los nombres de rutas en los SOBUNIX. En cuarto lugar se describe el sistema de archivos UFS , el cual es utilizado en diversos SOBUNIX. En cuarto lugar se describe la gestión de la E/S en los SOBUNIX. El capítulo finaliza con una introducción a los conectores que son uno de los principales mecanismos utilizados en los SOBUNIX para implementar las conexiones en red. 5.2. Gestión de archivos en SOBUNIX: características principales y llamadas al sistema 5.2.1. Archivos En los SOBUNIX la información contenida en un archivo se estructura como una secuencia lineal de bytes. El núcleo no interpreta dicha información, simplemente se limita a operar (leer o escribir) con ella a nivel de byte. Son los procesos los encargados de interpretar la información contenida en los archivos . E l acceso a l contenido d e u n archivo s e realiza por defecto d e forma secuencial. Aunque también e s posible realizar u n acceso directo o aleatorio a u n determinado byte d e u n archivo. El nombre de un archivo es una cadena de caracteres ASCII. Se pueden usar todos los caracteres excepto los caracteres 1 y \ o La longitud máxima de un nombre depende del tipo de sistema de archivos al que pertenezca el archivo. Como se verá en la sección 5 . 2.4 los SOBUNIX soportan el uso de diferentes tipos de sistemas de archivos. A diferencia de otros sistemas operativos, en los SOBUNIX el nombre de un archivo no tiene por qué tener una extensión ya que el núcleo no la interpreta. De nuevo, el uso de extensiones y su interpretación recae en los procesos. En este sentido, conviene señalar que en los SOBUNIX un nombre puede tener más de una extensión. Teniendo en cuenta lo anterior los nombres prueba, prueba . e y prueba . e . g z serían nombres d e archivos válidos e n SOBUNIX. 1 1 1 1 • Tipos de archivos En los SOBUNIX, de forma general, se distinguen los siguientes tipos de archivos: • Archivos regulares. También denominados archivos ordinarios. Dentro de esta categoría de archi­ vos se encuentran los archivos binarios y los archivos ASCII. Un archivo binario es aquél que contiene información de un determinado tipo (código compilado, texto formateado, imágenes, vi­ deo, etc) con una estructura determinada que únicamente puede ser interpretada por los programas que los utilizan. Un archivo ASCII está compuesto de líneas de caracteres ASCII codificadas en binario que no requieren un programa que los interprete. • Directorios. Son archivos cuyos bloques de datos almacenan una lista con los archivos y subdi­ rectorios aloj ados en el directorio. Cada entrada de una lista de un directorio contiene el nombre de un archivo (o subdirectorio) alojado en el directorio y el número del nodo-i asociado a dicho archivo (o subdirectorio). SOBUNIX: gestión de archivos y gestión de la E/ S • 189 Archivos especiales. Son archivos que representan a dispositivos (ver sección 5 . 5 .2). Existen dos tipos de archivos especiales: - Archivos de dispositivos modo carácter. Se utilizan para modelar dispositivos de E/S de tipo serie como el teclado, el ratón, la impresora y el módem. - Archivos de dispositivos modo bloque. Se utilizan para modelar la E/ S en bruto a particiones de discos duros. • Tuberías. Una tubería (ver sección 3.5 .2) es un canal de comunicación unidireccional por el que se pueden transmitir flujos de datos no estructurados con un tamaño máximo fijo. • Conectores (sockets). Son uno de los principales mecanismos usados en los S OBUNIX para im­ plementar las conexiones en red (ver sección 5 .6). • Enlaces simbólicos. Un enlace simbólico es un archivo que contiene el nombre de ruta absoluta o relativa de otro archivo. Atributos de un archivo Además del nombre de un archivo, el núcleo tiene que mantener otras informaciones relativas a un archivo. A los elementos componentes de dicha información se les denomina atributos del archivo. Los atributos de un archivo se almacenan en el nodo-i asociado a dicho archivo. Entre los atributos frecuentemente mantenidos en los S OBUNIX se encuentran los siguientes: identi­ ficador del dispositivo donde reside el archivo, número del nodo-i asociado al archivo, máscara de modo del archivo, número de enlaces duros, UID, GID, número de dispositivo mayor y número de dispositivo menor (está información se almacena si se trata de un archivo de dispositivo (ver sección 5 . 5 .2)), tamaño en bytes, fecha del último acceso (lectura) al archivo, fecha de la última modificación (escritura) del archivo, y fecha de la última modificación de los atributos del archivo. Señalar que esta última fecha no tiene en cuenta las modificaciones en las fechas del último acceso o modificación del archivo. Los atrib �tos de un archivo se pueden obtener mediante la llamada al sistema s ta t 1. También el comando l s l in permite visualizar varios atributos de un archivo. - e Ejemplo 5 . 1 Supóngase que en el directorio de trabajo actual existe un archivo llamado text o l . txt. Si se ejecuta la orden l s - l in t ext o l . tx t entonces en la pantalla aparecerá una línea con algunos de los atributos del archivo t ex t o l . txt. Su­ póngase que la línea que aparece en la pantalla es la siguiente: 1En algunos SOBUNIX, como por ejemplo Linux, también existe un comando s t a t el cual internamente invoca a la llamada al sistema s t a t . 190 Ampliación de sistemas operativos 1024 - rw r - -- r -- 1 1 0 1 1 0 4 7 2 0 1 2 - 0 3 - 1 6 1 0 : 0 2 text o 1 . txt El significado de los componentes de esta línea es el siguiente: 1 0 2 4 es el número de nodo-i asociado al archivo, - r w- r --r --es la máscara de modo del archivo, 1 es el número de enlaces duros, 1 0 1 es el UID, 1 o es el GID, 4 7 es el tamaño del archivo en bytes, y 2 o 1 2 - o 3 -1 6 1 o : o 2 es la fecha de la última modificación del archivo. • Descriptores de archivos Para que un proceso pueda poder operar sobre un archivo, éste primero tiene que ser abierto mediante la invocación de la llamada al sistema open. La rutina del núcleo del sistema operativo que trata esta llamada al sistema crea en la memoria principal una estructura de datos denominada objeto de archivo abierto y asigna a dicha estructura un número entero positivo pequeño denominado descriptor de archivo para identificarla. Nótese que si un proceso abre el mismo archivo dos veces, o si dos procesos abren el mismo archivo, el núcleo creará una estructura de objeto abierto y un descriptor de archivo en cada invocación de la llamada al sistema open. Luego se tendrían dos obj etos de archivo abierto, cada una con su correspondiente descriptor de archivo. La llamada al sistema open devuelve el descriptor del archivo abierto al proceso que invocó la llamada. Para poder operar sobre un archivo abierto el proceso debe pasar el descriptor del archivo como argumento de las llamadas al sistema asociadas a las operaciones que desee realizar sobre el archivo. Todos los descriptores de archivos asociados a un proceso se indexan en una tabla denomina tabla de descriptores de archivos. Esta tabla es local a cada proceso, es decir, cada proceso tiene su propia tabla de descriptores de archivos. Luego un mismo descriptor de archivo puede referirse en dos procesos distintos a archivos diferentes. Cada entrada de una tabla de descriptores de archivos contiene un puntero a la estructura de objeto de archivo abierto a la que está asociado el descriptor, y una serie de indicadores que especifican el tratamiento que debe dar al descriptor ante la aparición de determinados eventos. Por construcción las tres primeras entradas (entradas O a 2) de la tabla de descriptores de archivos de un proceso están asociadas a los siguientes archivos: archivo estándar de entrada (descriptor 0), archivo estándar de salida (descriptor 1 ) y archivo estándar de salida de errores (descriptor 2) . Por defecto el archivo estándar de entrada está asociado al teclado y el archivo estándar de salida está asociado a la pantalla. Por su parte, un objeto de archivo abierto contiene la información necesaria para poder operar sobre un archivo: • Puntero de lectura/ escritura. Indica el byte del archivo donde se realizará la próxima operación de lectura o de escritura. Se especifica como un desplazamiento desde el origen del archivo. Re­ cuérdese que en SOBUNIX el acceso al contenido de un archivo se realiza por defecto de forma secuencial. • Contador de referencias. Especifica el número de descriptores de archivos que hacen referencia al objeto de archivo abierto. Normalmente el contador vale 1 , pero puede tomar valores superiores si SOBUNIX: gestión de archivos y gestión de la E/ S 191 se duplica el descriptor usando las llamadas al sistema dup o dup2 . También cuando un proceso padre crea un proceso hijo se duplican los descriptores de archivos del proceso padre para asignár­ selos al proceso hijo. De esta forma, el proceso hij o puede acceder a los archivos abiertos por el proceso padre. • Modo de apertura del archivo. Establece las operaciones que se pueden realizar sobre el archivo abierto. Por ejemplo, solo lectura, solo escritura, lectura y escritura, etc. El modo de apertura se especifica como un argumento de la llamada al sistema open, bien en la forma de un número octal similar en estructura a la máscara de modo de un archivo o en la forma de una constante simbólica. Cada vez que se va a realizar una operación sobre el archivo abierto el núcleo comprueba el modo de apertura del archivo para permitir o denegar la realización de la operación. • Puntero al nodo virtual del archivo. Un nodo virtual, o más abreviadamente nodo-v, es una es­ tructura de datos que el núcleo crea en memoria principal para cada archivo activo. La utilidad y contenido de esta estructura de datos se describirá en la sección 5 . 3 . 1 . La implementación de las tablas de descriptores de archivos y de los objetos de archivos abiertos depende de cada SOBUNIX. e Ejemplo 5 . 2 En Solaris la tabla de descriptores de archivos asociada a un proceso se implementa mediante un array f i_l i s t que está indexado por el número de descriptor de archivo. Cada elemento del array está for­ mado por una estructura u f_entry que contiene, entre otras informaciones, un puntero a un objeto de . archivo abierto, el cual se implementa mediante una estructura f i l e . Una estructura f i l e contiene los siguientes campos : • f_vnode . Puntero a l nodo-v asociado a l archivo abierto. • f_o f f s e t . Puntero de lectura/escritura. • f_c red. Modo de apertura del archivo. • f_c oun t . Contador de referencias. Especifica el número de descriptores de archivos que referen­ cian al objeto de archivo abierto. En la Figura 5 . 1 se muestra la relación entre estas estructuras para el caso de dos procesos A y B que han abierto el mismo archivo. Cada proceso tiene su propia tabla de descriptores de archivos. El proceso A referencia al archivo mediante un descriptor de archivo igual a 3 mientras que el proceso B lo referen­ cia mediante un descriptor de archivo igual a 4. Cada descriptor apunta a un obj eto de archivo abierto distinto. Como los dos procesos han abierto el mismo archivo ambos objetos abiertos apuntan al mismo nodo-v. • 192 Ampliación de sistemas operativos fi l i st O u f_ent ry u f_entry 2 u f_entry 3 u f_ent ry s t ru c t f i l e f vnode f off set 1 1 1 f cred f c ount ! 11 1 1 1 1 •-------------------· Objeto de archivo abierto Tabla de descriptores de archivos de un proceso A nodo-v f i l i st O uf_ent ry uf_ent ry 2 u f_entry 3 u f_entry 4 u f_ent ry f count Objeto de archivo abierto 1 1 1 1 1 1 1 •-------------------' Tabla de descriptores de archivos de un proceso B - Relación existente entre las estructuras de datos asociadas a un archivo abierto por dos procesos A y B en Solaris Figura 5.1 Llamadas al sistema Un proceso para operar sobre un archivo debe invocar a la llamada al sistema apropiada. De forma general los SOBUNIX proporcionan a los procesos de los usuarios las siguientes llamadas al sistema para poder operar sobre los archivos: • fd=creat ( ruta , modo ) . Esta llamada al sistema permite crear y abrir un archivo con el nombre de ruta especificado en ruta con la máscara de modo octal 0302010o especificada en modo . Si la llamada se ejecuta con éxito entonces en f d se guarda el descriptor del archivo. En caso de error en fd se guarda el valor -l. Si el archivo que se deseaba crear ya existía entonces se borra su contenido y su longitud se trunca a cero bytes. SOBUNIX: gestión de archivos y gestión de la E/ S • 193 fd=open ( ruta , modo_a ) . Esta llamada al sistema permite abrir el archivo localizado en ruta con el modo de apertura especificado en modo_a . El modo de apertura se especifica en la forma de un número octal similar en estructura a la máscara de modo de un archivo o en la forma de alguna de las constantes simbólicas definidas en el archivo de cabecera f cnt l . h. Por ejemplo, las cons­ tantes O_RDONLY y O_WRONLY indican que el archivo se debe abrir con permisos de solo lectura o de solo escritura, respectivamente. Mientras que la constante O_RDWR indica que el archivo se debe abrir con permisos de lectura y escritura. Si la llamada al sistema se ejecuta con éxito entonces en fd se guarda el descriptor del archivo. En caso de error en fd se guarda el valor l - . • r = c l o s e ( f d ) . Esta llamada al sistema cierra el archivo con descriptor de archivo fd. Si la lla­ mada se ejecuta con éxito en r se guarda el valor O. En caso de error en r se guarda el valor -l. • r = read ( fd , bu f f e r , N ) . Esta llamada permite leer N bytes del archivo con descriptor f d y escri­ birlos en memoria a partir de la posición apuntada por bu f f er. La operación de lectura comienza desde la posición indicada en el puntero de lectura/escritura almacenado en el objeto de archivo abierto al que apunta el descriptor de archivo fd. Si la llamada se ejecuta con éxito entonces en r se guarda el número de bytes que se han podido leer. Luego r puede ser menor o igual a N. En caso de error en r se guarda el valor 1 - . • r=wr i t e ( f d , bu f f e r , N ) . Esta llamada permite leer N bytes desde la posición de memoria apun­ tada por bu f f er y escribirlos en el archivo con descriptor fd. La operación de escritura comienza desde la posición indicada en el puntero de lectura/escritura almacenado en el objeto de archivo abierto al que apunta el descriptor de archivo fd. Si la llamada se ejecuta con éxito entonces en r se guarda el número de bytes que se han podido escribir. Luego r puede ser menor o igual a N. En caso de error en r se guarda el valor - 1 . • r = l s eek ( f d , o f f s e t , o r igen ) . Esta llamada al sistema permite desplazar o f f s e t bytes desde o r i gen el puntero de lectura/escritura del archivo con descriptor fd. El argumento o r i gen puede tomar los siguientes valores: O o SEEK_SET que indica que el desplazamiento se realizará desde el inicio del archivo, 1 o SEEK_CUR que indica que el desplazamiento se realizará desde la posición actual del puntero, y 2 o SEEK_END que indica que el desplazamiento se realizará desde el final del archivo. Por su parte, el argumento o f f s e t puede ser un número entero positivo o negativo. Si es positivo entonces el puntero avanzará, mientras que si es negativo el puntero retrocederá. Si la llamada al sistema se ejecuta con éxito entonces en r se guarda la nueva posición, medida en bytes, que toma el puntero de lectura/escritura con respecto al inicio del archivo. En caso de error en r se guarda el valor - l . • r = s tat ( ruta , &bu f f er ) . Esta llamada al sistema copia en la estructura bu f f e r de tipo predefi­ nido s ta t los atributos del archivo localizado en rut a . Si la llamada se ejecuta con éxito entonces en r se guarda el valor O, en caso contrario se guarda el valor - l . 194 Ampliación de sistemas operativos • r = f s tat ( fdl &bu f f e r ) . Esta llamada al sistema presenta la misma funcionalidad que la llamada s t a t . Se diferencia de dicha llamada en que recibe como argumento de entrada el descriptor del archivo fd en lugar del nombre de ruta. • r = f cnt l ( fdl op 1arg ) . Esta llamada al sistema permite realizar diferentes operaciones de con­ trol sobre un archivo abierto con descriptor fd. Las operaciones op disponibles se encuentran definidas en el archivo de cabecera f en t 1 h. Entre dichas operaciones una de las más útiles es la . posibilidad de bloquear o desbloquear el acceso a otros procesos a parte o la totalidad del conte­ nido de un archivo. De esta forma un proceso puede garantizarse el acceso con exclusión mutua a un archivo usado por múltiples procesos concurrentes sin necesidad de utilizar mecanismos IPC. Esta operación también se puede realizar utilizando la función de librería l o c k f . Los argumentos necesarios para cada operación s e especifican e n arg. Si l a llamada s e ejecuta con éxito en r se almacena un valor que depende de cada operación. En caso de error se almacena el valor -l. e Ejemplo 5 .3 Supóngase que en el directorio de trabajo actual no existe ningún archivo. Si un proceso A invoca a la llamada al sistema r = c reat ( " da t o s " 10 6 6 6 ) ; entonces se creará y se abrirá el archivo da t o s con la máscara de modo octal 0 6 6 6 , es decir, todos los usuarios pueden leer y escribir en el archivo. El descriptor del archivo se almacenará en r . Si a continuación e l proceso A invoca a la llamada a l sistema wr i t e ( r1o r i gen15 0 ) ; entonces se leerían los 50 primeros bytes desde la posición de memoria apuntada por o r i gen y se escribirían en el archivo con descriptor r . Supuesto que el proceso A ha terminado de usar el archivo entonces debería invocar una llamada al sistema close ( r ) ; para cerrarlo. S i posteriormente otro proceso B invoca a la llamada al sistema f = open ( " da t o s " 1O_RDONLY ) ; entonces el archivo da t o s se abrirá con permiso de solo lectura y el descriptor del archivo se almacenará en f. Señalar que la constante O_RDONLY podría haberse sustituido por el número octal 0 4 0 0 . S i el proceso B invoca a la llamada al sistema SOBUNIX: gestión de archivos y gestión de la E/ S 195 l s e ek ( f , 2 5 , 0 ) ; entonces el puntero de lectura/escritura se desplazará 25 bytes comenzando desde el origen del archivo. Es decir, supuesto que los bytes comienzan a numerarse desde O, el puntero pasa a apuntar al byte no 25 del archivo. Si a continuación el proceso B invoca a la llamada al sistema read ( f , de s t i no , l O ) ; entonces se leerían 1 O bytes en el archivo con descriptor f comenzando desde el byte no 25 y se escribirían en memoria a partir de la posición de memoria apuntada por de s t ino. Supuesto que el proceso B ha terminado de usar el archivo entonces debería invocar una llamada al sistema c lose ( f ) ; para cerrarlo. • 5.2.2. Directorios El núcleo, las aplicaciones y los usuarios organizan sus archivos en directorios. En los SOBUNIX un directorio es un archivo en cuyos bloques de datos se almacena una lista con los archivos y directorios contenidos en el directorio. En los sistemas de archivos desarrollados para SOBUNIX una entrada de una lista de un directorio contiene, entre otras informaciones, el número del nodo-i asociado a un archivo y su nombre. Además existen dos entradas especiales : la entrada punto que se refiere al propio directorio y la entrada punto­ punto " . . " que se refiere al directorio padre. Los SOBUNIX implementan una estructura de directorios de gráfica acíclica. El directorio raíz es el directorio / ' . Este carácter también se utiliza para separar cada componente del nombre de ruta absoluta o relativa de un archivo. 1 • 1 1 Número de nodo-i 1 20 52 256 350 Tabla 5.1 - Lista Nombre del archivo o o proye c to l i s tar de archivos almacenada en el directorio al ex de la Figura 5 .2 196 Ampliación de sistemas operativos Directorio 1 bin boot Directorio dev home home Directorio Directorio sara Figura e 5.2- Estructura de directorios del SOBUNIX del Ejemplo 5.2 Ejemplo 5.4 En la Figura 5.2 se muestra a modo de ejemplo un diagrama con la estructura de directorios de un cierto SOBUNIX. Asimismo en la Tabla 5 . 1 se muestra la lista de archivos aloj ada en el directorio / home / al ex. La primera entrada corresponde a la entrada punto que hace referencia al propio direc­ torio. Su número de nodo-i es 1 20. 1 • 1 La segunda entrada corresponde a la entrada punto-punto " . . " que hace referencia al directorio padre del directorio / home / al ex, en este caso el directorio / home . Su número de nodo-i es 52. La tercera entrada corresponde al archivo proyect o cuyo número de nodo-i es 256. Este archivo de acuerdo con la Figura 5.2 es un directorio, pero realmente esto no se puede saber hasta que no se consulte el tipo de archivo en el nodo-i número 256. Finalmente la cuarta entrada corresponde al archivo l i s t ar cuyo número de nodo-i es 350. • En los SOBUNIX existen varias llamadas al sistema que permiten operar a nivel de directorios, como por ejemplo: • r = chdi r ( ruta) . Esta llamada al sistema cambia el directorio de trabajo. El nuevo directorio de trabaj o es el especificado en rut a. Si la llamada al sistema se ejecuta con éxito en r se almacena el valor O, en caso de error se almacena el valor - 1 . Señalar que el comando cd que se utiliza para cambiar de directorio de trabaj o invoca internamente a esta llamada al sistema. SOBUNIX: gestión de archivos y gestión de la E/S • 197 r=mkd i r ( ruta , modo ) . Esta llamada al sistema permite crear un directorio en el nombre de ruta especificado en ruta con la máscara de modo octal Ü3ÜzÜ¡Üo especificada en modo. Si la llamada se ejecuta con éxito entonces en r se guarda el valor O, en caso de error se almacena el valor - 1 . Señalar que el comando mkd i r que permite crear un directorio invoca internamente a esta llamada al sistema. • r = rmdi r ( ruta ) . Esta llamada al sistema permite eliminar el directorio localizado en ruta. Para que pueda ser eliminado el directorio debe estar vacío. Si la llamada se ejecuta con éxito entonces en r se guarda el valor O, en caso de error se almacena el valor -l. Señalar que el comando rmdir que permite borrar un directorio invoca internamente a esta llamada al sistema. 5.2.3. Enlaces Una de las características principales de una estructura de directorios de gráfica acíclica es la posi­ bilidad de compartir archivos o directorios entre varios usuarios mediante el uso de enlaces. Un enlace permite conectar un directorio origen con un elemento (archivo o subdirectorio) ya existente en otro directorio destino. Se distinguen dos posibles tipos de enlaces: enlaces duros y enlaces simbólicos. Un enlace duro es una entrada de un directorio. En los SOBUNIX un archivo puede ser referenciado por múltiples entradas de directorios. En el nodo-i asociado al archivo, entre otras informaciones, existe un contador de enlaces duros. Cada vez que se crea una entrada de un directorio que referencia al archivo, es decir, un enlace duro, el contador se incrementa en una unidad. Cuando se crea un directorio el contador de enlaces duros de su nodo-i toma el valor 2. Un enlace corresponde a la entrada del directorio dentro de su directorio padre la cual contiene el nombre del directorio. El otro enlace corresponde a la entrada del directorio creado. Cada vez que se crea un subdirectorio dentro de un directorio A, el contador de enlaces duros del del subdirectorio creado, la nodo-i del directorio se incrementa en una unidad debido a la entrada cual referencia a su directorio padre, es decir, al directorio A. El contador se decrementa cada vez que se borra un subdirectorio. En general, el número de enlaces duros NEo de un directorio es igual al número de subdirectorios Ns más dos: NEo = Ns + 2 (5 . 1 ) 1 • 1 11 e • • 11 Ejemplo 5 .5 En la Figura 5 . 3 se muestra en detalle parte de la estructura de directorios del sistema de archivos del Ejemplo 5 .4. Se observa que cuatro entradas de directorios apuntan al directorio / home : la entrada aso­ ciada al directorio / home en el directorio raíz, la entrada del propio directorio / home , la entrada del subdirectorio / home / s ara. Luego el con­ del subdirectorio / home / a l ex y la entrada tador de enlaces duros del nodo-i del directorio / home tendrá almacenado el valor 4. Este valor podría haberse determinado también usando (5 . 1 ). 1 11 • • 11 11 • • • 1 11 • S i un archivo, excepto si es de tipo directorio, tiene NEo enlaces duros eso implica que tiene NEo nombres distintos. 198 Ampliación de sistemas operativos Directorio 1 : home : � �hh�me �� Directorio . . J al ex sara Dinxtori s a ra . . Directorio al ex : / . . : Figura 5.3 - Detalle de parte de la estructura de directorios del sistema de archivos del Ejemplo 5.5 A diferencia de los restantes tipos de archivos un directorio solo puede tener un único nombre para evitar la existencia de ciclos en el árbol de directorios. Un enlace simbólico es un archivo cuyos bloques de datos contienen el nombre de ruta absoluta o relativa de otro archivo o directorio. S i el nombre de ruta ocupa poco espacio entonces éste se puede almacenar directamente en el nodo-i del archivo que implementa el enlace simbólico sin necesidad de consumir bloques de datos. La entrada asociada a un enlace simbólico en el directorio origen no contiene un puntero al nodo-i del elemento destino, sino un puntero al nodo-i del archivo que implementa el enlace simbólico. La máscara de modo simbólica de cualquier enlace simbólico siempre es l rwxrwxrwx por convenio. Esta máscara solo es una notación y no transmite la información usual sobre los permisos de acceso del propietario, grupo propietario y otros usuarios. Es la máscara de modo del elemento destino la que será consultada para autorizar o denegar los accesos al archivo destino. De hecho cualquier operación de cambio de permisos que se realice sobre el enlace simbólico en realidad se estará realizando sobre el archivo destino. La principal ventaj a de los enlaces simbólicos es que permiten crear enlaces a directorios. S u princi­ pal inconveniente es que consumen bloques de datos de disco y que retardan el análisis de los nombres de ruta. En los SOBUNIX, cuando se invoca una llamada al sistema cre a te o mkdir para crear un nuevo archivo o directorio, respectivamente, se crea una entrada en el directorio de creación, es decir, un enlace duro. Una vez creado el elemento destino se pueden utilizar las llamadas al sistema l ink y s yml ink para crear más enlaces duros o enlaces simbólicos, respectivamente. Para eliminar un enlace se puede SOBUNIX: gestión de archivos y gestión de la E/ S 199 usar la llamada al sistema unl in k. Estas tareas también se pueden realizar con los comandos 1 ink, s yml ink y unl ink, los cuales internamente invocan a las llamadas al sistema homónimas. También el comando ln que permite crear enlaces duros y simbólicos invoca internamente a las llamadas 1 ink o syml ink. A la hora de usar estas llamadas al sistema y comandos conviene recordar que si se aplican para crear un enlace duro sobre un directorio se generará un mensaje de error, ya que los directorios solo pueden tener un nombre, el que se les asigna en el momento de su creación. e Ejemplo 5. 6 Supóngase la estructura de directorios de la Figura 5 .2. Considérese que el directorio de trabajo actual es / home / s ara y que el archivo / home / al ex/ proyect o / t empo . txt tiene asignado un nodo-i con número de nodo-i igual a 2345 . La creación del enlace duro proybp al archivo t empo . txt ha podido ser realizada por un usuario que disponga de los permisos adecuados usando el siguiente comando: ln / home / al ex / proyect o / t empo . txt proybp También se puede usar el comando: l i nk / home / al ex / proyect o / t empo . txt proybp Tras crear el enlace duro se tienen dos nombres distintos proybp y t empo . txt asociados al archivo con número de nodo-i 2345 . El contador de enlaces duros almacenado en el nodo-i ahora tendrá el valor 2. Esto se puede comprobar utilizando el comando: l s - l i proybp Cualquiera de los dos enlaces duros al archivo con número de nodo-i 2345 puede ser borrado usando el comando unl ink. Por ejemplo, el comando unl i nk / home / al e x / proyect o / t empo . txt eliminaría el primer enlace duro del archivo. Por otra parte, el enlace simbólico / home / al ex / l i s tar al archivo /bi n / l s ha podido ser creado usando el comando ln - s / b i n / l s / home / al ex / l i s tar También se puede usar el comando syml i nk /bi n / l s / home / al ex / l i s t ar Si se utiliza el comando l s - l i / b i n / l s / home / al ex / l i s tar 200 Ampliación de sistemas operativos se puede comprobar que los archivos / b in / l s y / home / a l ex / l i s tar tienen números de nodos-i distintos, a diferencia de que lo sucede con los enlaces duros. Esto es así porque un enlaces simbólico es un archivo distinto del archivo destino y en consecuencia tiene asignado otro nodo-i distinto. • 5.2.4. Sistemas de archivos Durante el proceso de instalación de un SOBUNIX se realiza un formateo a alto nivel de una partición del disco duro para crear el sistema de archivos principal donde se aloj a el directorio raíz !'. Cada SOBUNIX establece el tipo de sistema de archivos principal que crea, por ejemplo, SVR3 crea un sistema s5fs, B SD4.2 crea un sistema FFS , Linux un sistema EXT4, Solaris un sistema ZFS , etc. Aparte de este sistema de archivos principal, los SOBUNIX son capaces de reconocer y manipular otros sistemas de archivos del mismo o distinto tipo ubicados en memoria secundaria: discos duros, discos ópticos (CDs, DVDs o B lu-Ray), pendrives, etc. Por ejemplo, Solaris soporta, entre otros, los siguientes tipos de archivos : UFS , ZFS , NFS , FAT-32 e ISO 9660. Para poder acceder a los contenidos de un determinado sistema de archivos ubicado en memoria secundaria, éste debe ser previamente montado en algún directorio dentro de la estructura de directorios del sistema de archivos principal. A dicho directorio se le denomina punto de montaje. Por ejemplo, el punto de montaje del sistema de archivos principal es el directorio raíz !'. El núcleo almacena en un archivo una tabla de montaje con los sistemas de archivos que tiene que montar al arrancar el sistema y desmontar al apagarlo. Dicho archivo suele almacenarse en el directorio / e t c y su nombre depende de cada SOBUNIX. Por ejemplo, en Solaris se le denominamnt tab, mientras que en Linux se denomina mtab. En cada entrada de la tabla de montaje se suelen mantener, entre otros, los siguientes datos: dispositivo que se monta, punto de montaje y tipo de sistema de archivos. En SOBUNIX la estructura de directorios accesible por el usuario queda constituida por el sistema de archivos principal y todos los sistemas de archivos montados sobre éste. El acceso a un directorio o archivo de un sistema de archivos montado sobre el sistema de archivos principal se realiza exactamente igual que a cualquier archivo o directorio del sistema de archivos principal, es decir, especificando su nombre de ruta absoluta o relativa. Por otra parte, los permisos de acceso a un sistema de archivos montado se establecen configurando los permisos de acceso del punto de montaje. Algunos comandos útiles para trabaj ar con sistemas de archivos son los siguientes: 1 1 • moun t . Monta un cierto sistema de archivos en un determinado punto de montaje. Este comando internamente invoca a la llamada al sistema moun t . • umount . Desmonta un cierto sistema de archivos. Este comando internamente invoca a la llamada al sistema umount . • mk f s . Crea u n sistema d e archivos de u n tipo soportado por e l SOBUNIX correspondiente en un cierto dispositivo. SOBUNIX: gestión de archivos y gestión de la E/ S 201 • f s ck. Comprueba y repara la existencia de inconsistencias en un determinado sistema de archivos. • d f . Muestra datos sobre la distribución de espacio libre y asignado de un determinado sistema de archivos. e Ejemplo S.7 Supóngase que el núcleo de un cierto SOBUNIX asocia a la unidad de CD-ROM el archivo de dispositivo / dev/ s r O . Supóngase que se inserta en la unidad de CD-ROM un disco con un sistema de archivos de tipo ISO 9660 cuyo contenido es el que se muestra en la Figura 5 .4a. Para poder acceder a los contenidos del sistema de archivos del CD-ROM primero debe ser montado en el sistema de archivos principal en algún punto de montaje. Supóngase por ejemplo que se desea utilizar como punto de montaje el directorio / c drom. El montaje del sistema de archivos del CD-ROM en el punto de montaje seleccionado se puede realizar desde un intérprete de comandos mediante la siguiente orden: mount / dev / s r O / c dr om Una vez montado se puede listar el contenido del sistema de archivos del CD-ROM usando el comando: ls -1 / c drom El acceso a un archivo o directorio del CD-ROM se realiza igual que a cualquier otro archivo del sistema de archivos principal, es decir, especificando su nombre de ruta. Por ejemplo, supóngase que en el direc­ torio raíz del CD-ROM existe el archivo ASCII l e eme . txt. Para mostrar su contenido en la pantalla se puede usar la siguiente orden: mo re / c drom / l e eme . txt Cuando se termine de usar el CD-ROM para desmontar su sistema de archivos se debe usar la siguiente orden: umount / dev/ s r O Conviene señalar que cuando un directorio D s e utiliza como punto de montaje l a lista de archivos que contenía originalmente D es sustituida por la lista de archivos del directorio raíz ( 1 ) del sistema de archivos montado, y en consecuencia no es posible acceder a los archivos aloj ados originalmente en D. Por ejemplo, en la Figura 5 .4a el directorio / c drom contiene el archivo da t o s . txt. Cuando dicho directorio pasa a utilizarse como punto de montaje (ver Figura 5 .4b) ya no se puede acceder a da t o s . txt ya que la lista de archivos que contenía originalmente el directorio ha sido sustituida por la lista de archivos del directorio raíz del sistema de archivos del CD-ROM. Cuando el sistema de archivos del CD-ROM sea desmontado entonces se restaurará la lista de archivos original y podrá accederse de nuevo al archivo da t o s . txt. 1 1 • 202 Ampliación de sistemas operativos Directorio 1 del sistema de archivo principal bin boot dev Directorio home �����--��cdrom 1 cdrom datos.txt (a) Directorio 1 del sistema de archivo principal bin boot dev home cdrom Directorio cdrom leeme.txt vid.mpg (b) del sistema de archivos principal del Ejemplo 5.7 antes (a) el sistema de archivos del CD-ROM Figura 5.4 - Estado 5.3. y después (b) de montar Capa nodo virtual/sistema de archivos virtual Los primeros SOBUNIX únicamente soportaban un único tipo de sistema de archivos: S5FS en los SOBUNIX basados en el System V y FFS en los SOBUNIX basados en BSD. Posteriormente, el auge de las redes de comunicación conduj o al desarrollo de sistemas de archivos en red, como RFS de AT &T o NFS de Sun Microsystems, lo que hizo que algunos SOBUNIX fuesen adaptados para poder soportar­ los. Asimismo, también existía en la comunidad de SUBONIX el deseo de poder soportar sistemas de archivos nativos de otros sistemas operativos, como FAT de MS-DOS , para así acceder a la información de las particiones de discos duros o disquetes formateadas con estos sistemas de archivos. Con el fin de dar soporte a diferentes tipos de sistemas de archivos en 1 985 Sun Microsystems introdujo en el núcleo de Sun OS 2.0 una capa de software conocida en la literatura como sistema de archivos virtual (Virtual File S ystem, VFS) o capa nodo virtual (nodo-v)fsistema de archivos virtual (SAV). Debido a su eficiencia, flexibilidad y elegancia la capa nodo-v/SAV ha sido adoptada por muchos SOBUNIX: gestión de archivos y gestión de la E/S 203 SOBUNIX, aunque cada uno la implementa de distinta forma. La capa nodo-v/SAV permite aislar los detalles de la implementación de cada tipo de sistema de archivos del resto del núcleo, lo que simplifica su diseño. Cualquier operación sobre un archivo o sobre un sistema de archivos completo invocada por el usuario a través de una llamada al sistema o internamente por algún subsistema del núcleo es redireccionada por la capa nodo-v/SAV hacía la rutina dependiente del sistema de archivos al que pertenezca el archivo que da soporte a dicha operación. Obviamente, el núcleo de cada SOBUNIX debe disponer, por cada tipo de sistema de archivos que soporte, de las rutinas que implementan las operaciones sobre dicho sistema de archivos. En definitiva la capa nodo-v/SAV permite descomponer las operaciones sobre un archivo en dos partes : una independiente del tipo de sistema de archivos y otra dependiente del tipo de sistema de archivos. La capa nodo-v/SAV es la interfaz que permite el paso entre ambas partes (ver Figura 5 .5). Interfaz de llamadas al sistema (read ( ) Funciones independientes del tipo de sistema de archivos 1 wri te ( ) 1 open ( ) 1 mount ( ) 1 • • • ) Capa nodo-v/SA V Funciones dependientes de un sistema UFS Funciones dependientes de un sistema FAT Figura 5.5 - - - - - - - Funciones dependientes de un sistema ISO 9660 Capa nodo-v /SAV La capa nodo-v/SAV trabaj a con dos tipos de estructuras de datos básicas : los nodos-v y los sistemas de archivos virtuales. La implementación y gestión de estas estructuras depende de cada S OBUNIX. 5.3.1. Nodos virtuales Un nodo virtual o nodo-v es una estructura de datos que representa a un archivo activo. El núcleo asigna un nodo-v cuando se crea o se abre un archivo durante el tratamiento de las llamadas al sistema c r e a te u open. El descriptor del archivo tiene un puntero al objeto de archivo abierto, el cual tiene a su vez un puntero al nodo-v asociado al archivo (ver Figura 5 . 1 ) . Nótese que s i dos procesos A y B abren e l mismo archivo ( o s i el mismo proceso abre dos veces el mismo archivo) existirán dos descriptores de archivos y dos estructuras de objeto abierto distintas, pero solo una estructura nodo-v. El nodo-v mantiene un contador de referencias con el número de estructuras de datos que lo referencian. En este caso el contador de referencias contendrá el valor 2. Si un proceso ejecuta una llamada al sistema e l o s e sobre dicho archivo el contador de referencias se decrementará en una unidad. En general cada vez que el núcleo referencia, a través de alguna de sus estructuras de datos, al nodo­ Y de un archivo se incrementa en una unidad el contador de referencias del nodo-v. Por el contrario, 204 Ampliación de sistemas operativos cuando el núcleo elimina una referencia al nodo-v el contador de referencias se decrementa en una unidad. Solo cuando el contador de referencias de un nodo-v toma el valor O entonces el nodo-v puede ser desasignado (liberado) y las estructuras de datos (nodo-i) y bloques de datos del archivo cargadas en memoria principal pueden ser eliminadas. Si bien dicha eliminación no se realiza de forma inmediata sino que se demora en el tiempo por si el archivo vuelve a ser abierto en un futuro próximo. De esta forma se evita tener que leer de nuevo en el disco el nodo-i y los bloques de datos del archivo. Aparte de la apertura de un archivo existen otras circunstancias en las que es necesario asignar un nuevo nodo-v o referenciar a un nodo-v ya creado, y en consecuencia incrementar su contador de refe­ rencias [Vahalia, 1 995] : • Un proceso mantiene una referencia al nodo-v de su directorio de trabajo actual. Cuando un pro­ ceso cambia su directorio de trabajo tiene que referenciar al nodo-v del nuevo directorio de trabaj o y eliminar l a referencia a l nodo-v del directorio d e trabaj o antiguo. • Cuando se monta un nuevo sistema de archivos se crea una referencia al nodo-v del directorio utilizado como punto de montaje. • Cuando el núcleo analiza un nombre de ruta absoluta o relativa introducido como argumento de alguna llamada al sistema o comando tiene que referenciar al nodo-v de cada directorio intermedio que aparece en la ruta. Dicha referencia se crea para poder buscar en un directorio. Cuando termina la búsqueda en dicho directorio la referencia a su nodo-v se elimina. La principal función del nodo-v asociado a un archivo es la de redireccionar las operaciones que se vayan a realizar sobre el archivo hacía las rutinas dependientes del tipo de sistema de archivos al que pertenece el archivo que implementan dichas operaciones. Por ejemplo, en Solaris la invocación de una llamada al sistema read sobre un archivo previamente abierto perteneciente a un sistema de archivos UFS hace que se invoque a la operación virtual VOP_ READ ( ) del nodo-v la cual invoca a su vez a la función u f s_read ( ) . VOP_ READ ( ) es una función independiente del tipo de sistema de archivos mientras que u f s_ read ( ) es la función que implementa dicha operación en los sistemas tipo UFS . La estructura que implementa un nodo-v suele contener, entre otros, los siguientes datos: • Contador de referencias. Indica el número de estructuras de datos del núcleo que referencian al nodo-v. • Tipo de nodo-v. Coincide con el tipo de archivo al que está asociado el nodo-v: archivo regular, directorio, archivo de dispositivo modo bloque, etc. • Puntero a una estructura de datos dependiente del tipo de sistema de archivos. Dicha estructura se denomina de forma general como nodo índice (nodo-i) y la información que contiene depende de cada tipo de sistema de archivos. Por ejemplo, para un archivo perteneciente a un sistema de archivos S 5FS o UFS el nodo-i contiene los atributos del archivo y la localización en el disco de los bloques de datos del archivo. En este caso el nodo-i asociado al archivo al que apunta un nodo-v es una copia en memoria principal del nodo-i almacenado en el disco. El tamaño que ocupa la copia SOBUNIX: gestión de archivos y gestión de la E/ S 205 de un nodo-i en memoria principal es mayor que el tamaño del nodo-i en el disco, ya que contiene más información, como por ejemplo, un puntero al nodo-v y el número de nodo-i del archivo. • Puntero al vector de operaciones sobre un archivo. El vector de operaciones sobre un archivo es un array de punteros a funciones dependientes del tipo de sistema de archivos al que pertenece el archivo. Estas funciones implementan todas las posibles operaciones sobre un archivo. • Lista de páginas del archivo cargadas en la memoria principal. Esta lista se consulta cuando el contador de referencias del nodo-v toma el valor O para localizar y eliminar de memoria principal las páginas del archivo. Puesto que el archivo ya no está activo puede ser eliminado de la memoria principal. .--------------------------... - - - ... - - - -------------------------------- - -------- - ---------------- s t ruct vnode op s u f s_ c r e a t e ( ) u f s _open ( ) u f s_c l o s e ( ) u f s read ( ) u f s _wr i t e ( ) D Bloques O de datos O del archivo Vector de operaciones del archivo s t ru c t inode Partición de disco duro Sistema de archivos UFS nodo-i --------- - - - - - - - - - - - - - - - - - - - - - ---- - --------- - -- - - - - - - - - - - - - - - - - - - - - - - - - -- - --- - - - --- - - - - - - - - - J Estructuras en memoria principal Figura 5.6 - e Implementación de un nodo-v en Solaris Ejemplo 5 . 8 En Solaris un nodo-v (ver Figura 5 .6) se implementa con una estructura vnode que contiene, entre otros, los siguientes campos : • v_type . Contiene el tipo del nodo-v, como por ejemplo: VREG (archivo regular), VD I R (directorio), VBLK (dispositivo modo bloque), VCHR (dispositivo modo carácter), etc. • v_c ount. Contador de referencias. 206 Ampliación de sistemas operativos • v_op. Puntero al vector de operaciones virtuales sobre el archivo. Dicho vector se implementa con una estructura vnodeop s , entre las funciones virtuales que se pueden invocar se encuentran las siguientes: vop_open ( ) , vop_read ( ) , vop_wr i t e ( ) , vop_c l o s e ( ) , etc. • v_data. Puntero a una estructura de datos dependiente del tipo de sistema de archivos, es decir, a un nodo-i. • v_v f sp. Puntero a la estructura v f s asociada al sistema de archivos al que pertenece el archivo. • v_mount edhere. Puntero a la estructura v f s asociada al sistema de archivos montado en este directorio, si existe alguno. • v_page s . Lista de páginas del archivo cargadas en memoria. • 5.3.2. Sistemas de archivos virtuales Un sistema de archivos virtual (SAV) es una estructura de datos que representa a un sistema de archivos activo. El núcleo asigna un SAV cuando se monta un sistema de archivos y lo desasigna cuando lo desmonta. La principal función del SAV asociado a un sistema de archivos archivo es la de redireccionar las operaciones que se vayan a realizar sobre el sistema de archivo hacía las rutinas dependientes del tipo de sistema de archivos que implementan dichas operaciones. Por ejemplo, en Solaris la invocación de una llamada al sistema mount para montar un cierto sistema de archivos UFS , hace que se le asigne un SAV y que se invoque al procedimiento v f s_ mount ( ) del SAV, que es una función independiente del tipo de sistema de archivos. A su vez esta función invoca a la función u f s_mount ( ) que implementa esta operación en un sistema de archivos tipo UFS . La estructura que implementa un SAV suele contener, entre otros, los siguientes datos: • Puntero al siguiente SAV. El núcleo mantiene una lista con todos los SAV montados. • Puntero al vector de operaciones sobre el sistema de archivos. El vector de operaciones sobre un sistema de archivos es un array de punteros a funciones dependientes del tipo de sistema de archivos que implementan todas las posibles operaciones sobre dicho sistema de archivos. • Puntero a una estructura de datos dependiente del tipo de sistema de archivos. • Puntero al nodo-v del punto de montaje. • Tipo de sistema de archivos. Coincide con el tipo de sistema de archivos al que está asociado el SAV y que debe ser alguno de los soportados por el núcleo: S5FS , UFS , NFS , EXT2, FAT, etc. Recuérdese que cada SOBUNIX establece los tipos de sistemas de archivos que soporta. SOBUNIX : gestión de archivos y gestión de la E/ S e 207 Ejemplo 5 .9 En Solaris un SAV se implementa con una estructura v f s . Esta estructura, contiene entre otros, los siguientes campos : • vf s_next . Puntero a la siguiente estructura v f s de la lista de sistema de archivos montados. Esta lista se implementa como una lista enlazada de estructuras v f s . La variable global r o o t v f s apun­ ta a la primera estructura vf s de la lista que siempre corresponde al sistema de archivo principal, es decir, el que se monta sobre el directorio raíz 1 ' • '. v f s_ f s typ e . Tipo del sistema de archivos montado. E n Solaris s e soportan sistemas d e archivos en discos (UFS , ZFS, UDF, PCFS, HSFS , VxFS , QFS , . . ), sistemas de archivos en red (NFS) y pseudosistemas de archivos mantenidos en la memoria principal (procfs, swapfs, tmpfs, . . . ) . • vf s_op . Vector de operaciones sobre el sistema de archivos. • v f s_data. Puntero a una estructura de datos dependiente del sistema de archivos . • vf s_vnodec overed. Puntero a l nodo-v del directorio punto d e montaje. En la Figura 5.7 se muestra una posible configuración del inicio de la lista de sistemas de archivos montados de un sistema Solaris. La variable global r o o tv f s apunta a la primera estructura v f s de la lista que está asociada al sistema de archivos principal o raíz que es de tipo UFS . A su vez esta estructura contiene un puntero a la siguiente estructura v f s de la lista asociada a un sistema de tipo NFS . s t ruct vf s s t ru c t v f s rootvf s ...,. - v f s next v f s _ f s typ e � v f s_op v f s data v f s data - ... : SAV SAV s t ru c t vf s op s s t ruct vf sop s u f s _mount ( ) n f s _mount ( ) u f s _unmount ( ) n f s _unmount ( ) : Vector de operaciOnes del sistema de archivos principal de tipo UFS Figura 5.7 - ---- v f s _ f s type v f s _op : - v f s next : Vector de operaciones de un sistema de archivos tipo NFS Posible configuración del inicio de la lista de sistemas de archivos montados de un sistema Solaris • 208 5.3.3. Ampliación de sistemas operativos Análisis de nombres de rutas en SOBUNIX Algunas llamadas al sistema, como open, creat o exe c , tienen entre sus argumentos de entrada el nombre de ruta absoluta o relativa de un archivo. El núcleo tiene que analizar dicha ruta para poder localizar el nodo-v del archivo y así poder invocar a la función dependiente del tipo de sistema de archivos que implementa la operación de la llamada al sistema. El análisis de cada componente de la ruta requiere localizar el nodo-v de su directorio padre y buscar en su lista de archivos y subdirectorios la entrada asociada a la componente. Nótese que la búsqueda en la lista del directorio se implementa con una función dependiente del tipo de sistema de archivos por ello es necesario localizar el nodo-v del directorio padre del componente. Dicha entrada, aparte del nombre del componente, que es el que se utiliza para buscar, contiene el número del nodo-i asociado al componente. Si dicho nodo-i está cargado en memoria entonces tendrá asignado un nodo-v, en caso contrario hay que cargarlo y asignarle uno. e Ejemplo 5 . 10 El nombre de ruta absoluta 1 d i r l 1 archi vol contiene tres componentes: el directorio raíz ! el di­ rectorio di r l y el archivo archi vo l . Supóngase que el análisis de este nombre de ruta absoluta se realiza en Solaris. La función del núcleo l o okuppn ( ) es la encargada de analizar un nombre de ruta de un archivo o directorio. Esta función lee la variable r o o t d i r para localizar el nodo-v del directorio raíz e invoca una operación VOP_LOOKUP sobre el nodo-v lo que produce la invocación de la función apropiada dependiente del sistema de archivos. Supuesto que se trata de un sistema de archivos UFS se invocaría a la función u f s_l o o kup ( ) . ' ', Esta función, a partir del nodo-i del directorio raíz, localiza los bloques de datos que contienen la lista de archivos y subdirectorios del directorio, y busca en ella la existencia de una entrada para el directorio di r l . Si la encuentra, entonces dicha entrada contiene el número del nodo-i asociado a di r l . Si dicho nodo-i ya estaba cargado en memoria entonces tendrá un nodo-v asignado y se incrementa su contador de referencias. Si el nodo-i no estaba cargado en memoria entonces se creará y se le asignará un nodo-v. La función u f s_lookup ( ) devuelve a l o okuppn ( ) un puntero al nodo-v de / di r l . Si u f s_l o okup ( ) no encuentra la entrada entonces devuelve un error a l o o kuppn ( ) . A continuación l ookuppn ( ) decrementa el contador de referencias del nodo-v asociado al directo­ rio raíz, ya que ya ha terminado de trabaj ar con él, e invoca una operación VOP_LOOKUP sobre el nodo-v del directorio / di r l la cual produce la invocación de la función u f s_ l o okup ( ) para buscar la existencia de una entrada asociada al archivo archi vo l en la lista del directorio di r l . El funciona­ miento de esta función es idéntico al descrito en el párrafo anterior. Si se encuentra la entrada entonces uf s_l o o kup ( ) devuelve a l o o kuppn ( ) un puntero al nodo-v de 1 d i r l 1 archi vo l . Finalmente l o o kuppn ( ) decrementa el contador de referencias del nodo-v asociado al directorio 1 d i r l y devuelve u n puntero a l nodo-v d e 1 d i r l 1 archi vo l a la rutina del núcleo que la había invocado . • Para reducir el número de lecturas en disco y el tiempo de búsqueda de entradas en directorios, los SOBUNIX implementan una caché de búsqueda de nombres en directorios. Se trata de una caché de estructuras de datos que se implementa en el espacio de direcciones del núcleo. Cada estructura de datos SOBUNIX: gestión de archivos y gestión de la E/S 209 de la caché contiene, entre otros, los siguientes datos sobre una entrada de un directorio recientemen­ te accedida: un puntero al nodo-v del directorio y el nombre y un puntero al nodo-v de un archivo o subdirectorio contenido en dicho directorio. Cuando el núcleo tiene que buscar la entrada de un archivo o subdirectorio determinado dentro de la lista de un directorio, primero busca la existencia de dicha entrada en la caché. En caso de acierto se ahorra el tiempo de leer en disco el bloque de datos que contiene la lista del directorio y buscar dentro de la lista. En general la implementación y gestión de esta caché depende de cada S OBUNIX. El sistema de archivos UFS 5.4. 5.4.1. Características principales UFS (UNIX File System, sistema de archivos UNIX) es un sistema de archivos soportado por di­ versos SOBUNIX, como por ejemplo: Solaris, FreeBSD y A/UX. El sistema UFS deriva del sistema de archivos FFS de BSD, el cual en su momento supuso una auténtica revolución dentro de los SOBUNIX los cuales utilizaban el sistema de archivos S5FS que tenía limitaciones importantes. Las características principales de un sistema de archivos UFS son las siguientes: • Trabaj a con un tamaño de bloque de datos de 4 KiB o 8 KiB . Además para reducir la fragmentación interna cada bloque se divide en fragmentos que pueden ser asignados individualmente. De esta forma la fragmentación interna promedio pasa de ser de medio bloque a medio fragmento. Tanto el tamaño de bloque como el número de fragmentos ( 1 , 2, 4 o 8) se fij a al crear el sistema de archivos. • Tiene en cuenta la geometría del disco a la hora de alojar los nodos-i y los bloques de datos de los archivos. Con ello se consigue mejorar el rendimiento del sistema ya que se reduce el tiempo de búsqueda de las cabezas de lectura/escritura del disco, y en consecuencia, el tiempo de las operaciones de E/S . • Por motivos de seguridad mantiene varias copias del superbloque, que es una estructura de da­ tos que contiene información administrativa y estadística de todo el sistema de archivos . Si solo existiese una copia del superbloque, como pasaba en los sistemas de archivos S5FS , y ésta se corrompiera debido algún error en el disco todo el sistema de archivos quedaría inutilizado. • Las entradas de los directorios son de tamaño variable y pueden ser compactadas para reducir la fragmentación. El nombre de un archivo puede tener hasta 255 caracteres. • Soporta enlaces simbólicos los cuales pueden ser creados con la llamada al sistema syml ink. • Soporta la llamada al sistema rename que permite renombrar de forma atómica a un archivo o directorio. Con el uso de r ename se evita tener que invocar primero a la llamada al sistema l i nk para crear un enlace duro que establezca el nuevo nombre del archivo y a continuación usar la llamada al sistema un l ink para borrar el enlace duro que establecía el nombre antiguo. 210 Ampliación de sistemas operativos Conviene señalar que cada S OBUNIX implementa UFS de una determinada forma y con diversas extensiones, las cuales influyen en su portabilidad ya que dichas extensiones solo son reconocidas úni­ camente por el SOBUNIX que las introduce. Por ejemplo Solaris, implementa UFS logging que permite implementar la técnica de registro por diario (journaling) en los sistema de archivos UFS de Solaris. 5.4.2. Estructura de un sistema de archivos UFS Un sistema de archivos UFS presenta la siguiente estructura en la partición de disco donde se crea (ver Figura 5 . 8): • Á rea de arranque. Se sitúa al principio de la partición. Contiene el código necesario para arrancar el SOBUNIX si dicha partición se usa como partición activa. • Superbloque. Se sitúa a continuación del área de arranque. Es una estructura de datos que contiene información estadística y administrativa del sistema de archivo, como por ejemplo: un número mágico que identifica al sistema como de tipo UFS , el número, el tamaño y la localización de los grupos de cilindros, el tamaño de bloque de datos que se utiliza, el número total de bloques de datos y de nodos-i, etc. Esta información se configura al crear el sistema de archivos en la partición. • Grupos de cilindros. Un grupo de cilindros está formado por un conjunto de cilindros contiguos. El número de grupos de cilindros (NG) y el número de cilindros que contiene un grupo son confi­ gurados cuando se crea el sistema de archivos UFS . Cada grupo de cilindro contiene la siguiente información: - Una copia del superbloque. La información que contiene el superbloque es fundamental para poder operar con el sistema de archivos. En previsión de la aparición de posibles errores en el disco el superbloque se replica en cada grupo de cilindros. La ubicación de la copia del superbloque en cada grupo de cilindros se elige cuidadosamente para evitar que todas las copias se encuentren en la misma superficie o plato del disco. - Estructura de grupo de cilindros. Contiene información estadística y administrativa del grupo de cilindros. - Mapas de bits. Especifica cuales son los bloques, fragmentos y nodos-i libres en el grupo de cilindros. - Nodos-i del grupo de cilindros. Cada nodo-i tiene un tamaño de 128 bytes y almacena los atributos del archivo. También almacena la localización de los bloques de datos de un archivo. En concreto contiene 1 5 direcciones de bloques de disco de 32 bits cada. Las 12 primeras direcciones son las de los 1 2 primeros bloques de datos del archivo, también denominados bloques directos. Las tres direcciones restantes localizan a un bloque de indirección simple, un bloque de indirección doble y un bloque de indirección triple, respectivamente. - Bloques de datos del grupo de cilindros. Contienen los datos propiamente dichos de los archivos. SOBUNIX: gestión de archivos y gestión de la E/ S 211 Partición de disco Arranque Superbloque Grupo de cilindro NG- 1 Grupo de cilindro o - - - - - - - - - - - - - - Mapa de bits o de cil indro Figura 5.8 5.4.3. - Nodos-i Bloques de datos Estructura de un sistema de archivos UFS Implementación de directorios en UFS En un sistema de archivos UFS la lista de archivos y subdirectorios de un directorio se implementa con entradas de tamaño variable. En cada entrada se almacena la siguiente información: número de nodo-i, longitud en bytes de la entrada, longitud en bytes del nombre, el nombre del archivo, un carácter nulo para marcar el final del archivo y los caracteres nulos de relleno necesarios hasta alcanzar una frontera de 4 bytes. El nombre de archivo en UFS puede tener un tamaño máximo de 255 caracteres. Cuando se borra una entrada de un directorio, excepto si se trata de la primera, la entrada anterior se expande hasta ocupar el espacio que tenía asignado la entrada borrada. Esta forma de proceder ayuda a reducir la fragmentación. e Ejemplo 5 . 1 1 En l a Figura 5 .9a s e muestra u n ejemplo de l a implementación de u n directorio e n u n sistema UFS . El directorio consta de 4 entradas : la primera asignada a la entrada ' . ', la segunda a la entrada " . . ", la tercera asignada al archivo prueba y la cuarta asignada al archivo f o t o s . Las dos primeras entradas tienen un tamaño de 1 2 bytes, mientras que las dos últimas tienen un tamaño de 1 6 bytes. Se observa como el nombre de una entrada siempre termina con un carácter nulo \ o seguido de tantos caracteres nulos como sean necesarios hasta completar una frontera de 4 bytes. ' ' En la Figura 5 .9b se muestra el estado del directorio después de borrar el archivo f o t o s . Se observa como la entrada del archivo prueba ha sido expandida hasta ocupar el espacio que tenía asignado la entrada borrada. • 212 Ampliación de sistemas operativos Longitud entrada (2 bytes) Longitud nombre (2 bytes) N° nodo-i (4 bytes) Nombre + caracteres de relleno + \0 \0 \0 \0 \0 35 12 1 36 12 2 51 16 6 p r u e b 15 16 5 f o t o S \0 \0 \0 \0 \0 a (a) 35 12 1 36 12 2 \0 \0 \0 \0 \0 \0 \0 \0 1 \0 1 \0 1 \0 \o 1 \o \o 1 \o \0 \0 \0 \0 \0 \0 \0 \0 51 16 p 6 r u e b a (b) Figura 5.9 - Implementación de un directorio en UFS : (a) Estado inicial. (b) Después de borrar el archivo fotos 5.4.4. Gestión de un sistema UFS Nodos-i El núcleo para poder trabajar con un archivo, aparte de asignarle un nodo-v, necesita tener cargado en la memoria principal el nodo-i asociado al archivo, ya que contiene los atributos y la localización de sus bloques de datos. El núcleo copia el contenido de un nodo-i en una estructura de datos que crea en su espacio de direcciones. El nombre que recibe dicha estructura depende de cada SOBUNIX, el cual lo asigna en función del tipo de sistema de archivos. En lo que resta de sección se denotará por nodo-im a la estructura de datos de memoria principal que contiene una copia de un nodo-i del disco duro. En un nodo-im, aparte de una copia de un nodo-i, también se almacenan, entre otros, los siguientes datos: un puntero al nodo-v asociado al nodo-im, punteros para colocar al nodo-im en una de las colas hash que mantiene el núcleo en función del número de nodo-i para localizar a los nodos-im, y punteros para colocar al nodo-im en una lista de nodos-im libres. El núcleo coloca a un nodo-im en la lista de nodos-im libres si el contador de referencias de su nodo­ v asociado alcanza el valor O. La posición de la lista en que coloca al nodo-im depende de si la lista de páginas de nodo-v está vacía o contiene alguna página. Por ejemplo, dependerá de cada SOBUNIX, si SOBUNIX : gestión de archivos y gestión de la E/ S 213 está vacía entonces el núcleo coloca al nodo-im al principio de la lista de nodos-im libres. Por el contrario si contiene alguna página lo coloca al nodo-im al final de la lista. Si el núcleo no localiza a un nodo-im en la cola hash en que le correspondería estar en función de su número de nodo-i, entonces eso significa que dicho nodo-i no está cargado en memoria. En ese caso el nodo-i debe ser copiado en un nodo-im. El núcleo selecciona para ello al primer nodo-im de la lista de nodos-i libres. e Ejemplo 5 . 12 En Solaris la estructura de datos que implementa un nodo-im de un archivo de un sistema UFS se deno­ mina inode. Esta estructura contiene, entre otros, los siguientes campos : • i_forw. Puntero a l siguiente nodo-im d e la cola d e hash a l a que pertenece este nodo-im. • i_backw. Puntero al anterior nodo-im de la cola de hash a la que pettenece este nodo-im. • i_free f . Puntero al siguiente nodo-im de la lista de nodos-im libres. • i f reeb. Puntero al anterior nodo-im de la lista de nodos-im libres. • i c ommon. Estructura de datos donde se copian los datos de un nodo-i del disco. Entre otros con­ tiene los siguientes campos : - i c_smode. Máscara d e modo del archivo que establece el tipo y los permisos d e acceso. Se distinguen, entre otros, los siguientes tipos: archivo regular (I FREG), directorio ( I F D I R) , enlace simbólico ( I FLNK), dispositivo modo bloque ( I FBLK), dispositivo modo carácter ( I FCHR), tuberías ( I F I FO) y sockets ( I F SOCK). Este campo toma el valor O si el nodo-i está libre, es decir, no está asociado a ningún archivo. - i c _nl ink. Número de enlaces duros. - i c_su i d. UID del propietario del archivo. - i c_sgid. GID del grupo propietario del archivo. - i c_l s i z e . Tamaño del archivo en bytes. - i c_a t ime . Fecha y hora del último acceso al archivo. - i c_mt ime . Fecha y hora de la última modificación del archivo. - i c_c t ime . Fecha y hora de la última modificación de algún campo del nodo-i asociado al archivo. - i c_db. Direcciones de los 12 primeros bloques de datos del archivo (bloques directos). i c_ib. Direcciones de los tres bloques de indirección (simple, doble y triple). • i_vnode. Puntero al nodo-v asociado al nodo-i. • i_dev. Número de dispositivo físico en que reside el nodo-i. 214 Ampliación de sistemas operativos • i_nurnber. Número del nodo-i. En la Figura 5 . 1 0 se muestra un ejemplo de un posible estado de las colas hash de nodos-im y de la lista de nodos-im libres de un sistema UFS en Solaris. En este ejemplo se ha considerado que el número de colas hash implementadas es N=3 (j = O, 1 , 2) y que la función hash utilizada para colocar a los nodos-im en las colas hash es J= i_nurnbe r %N. Se observa que en la cola hash j = O se encuentran los nodos-im 90 y 1 32, en la cola hash j = 1 los nodos-im 1 1 5, 9 1 , 1 27 y 1 30, y en la cola hash j = 2 se encuentran los nodos-im 1 34, 1 3 1 y 86. Además los nodo-im 1 32, 1 1 5 y 1 3 1 forman parte de la lista de nodos-im libres. • Cabecera lista nodos-i libres t 1o o o • Cabecera cola hash j=O struct inode struct ino de 132 90 .. ! o ¡---------------------------J 1 .. Cabecera cola hash j=l struct inode 115 ,---------------------------o o struct ino de 91 struct ino de 127 struct ino de 130 .. 1 : ·---------------------------, 1 11--------------------------, :o o • Cabecera cola hash j=2 Figura 5.10 - struct inode 1 34 struct ino de 13 1 struct inode 86 Colas hash de nodos-im y lista de nodos-im libres de un sistema UFS en Solaris SOBUNIX: gestión de archivos y gestión de la E/S 215 Asignación de espacio En un sistema de archivos UFS el tamaño de un bloque de datos puede ser de 4 KiB o 8 KiB . Además los bloques se pueden dividir en un número prefij ado de fragmentos: 1 , 2, 4 o 8. El tamaño mínimo que puede tener un fragmento queda limitado por el tamaño de un sector del disco. Tanto el tamaño de bloque como el número de fragmentos se configuran al crear un sistema de archivos UFS . Cada fragmento de un bloque puede ser direccionado y asignado individualmente, esto implica sus­ tituir la lista de bloques libres por un mapa de bits que registre los fragmentos libres y los asignados. Todos los bloques de datos de un archivo o directorio deben ocupar bloques de datos completos en el disco. El último bloque del archivo puede estar totalmente lleno o solo ocupar uno o varios fragmentos contiguos del bloque. Los fragmentos libres del último bloque de un archivo pueden ser utilizados para alojar los fragmentos del último bloque de datos de otro archivo. Con esta forma de asignación se consigue reducir la fragmentación interna promedio a medio frag­ mento por archivo. Sin embargo pueden afectar al rendimiento del sistema ya que produce operaciones de copia adicionales cuando un archivo crece de tamaño. Por ejemplo, supóngase un bloque de disco X que tiene cuatro fragmentos, uno de ellos está asignado al último bloque de un archivo A y los otros tres al último bloque de un archivo B . Si el archivo A incrementa su tamaño en un fragmento el núcleo tendrá que buscar un bloque Y que contenga dos fragmentos consecutivos libres y copiar en él el fragmento del archivo A que estaba en el bloque X. Por otra parte, el núcleo con objeto de reducir los tiempos de búsqueda en disco sigue las siguientes reglas para ubicar en el disco los bloques de datos y los nodos-i de un archivo o directorio en un sistema de archivos UFS [Vahalia, 1 995 ] : • Los nodos-i d e todos los archivos pertenecientes a u n mismo directorio s e intentan colocar e n un mismo grupo de cilindros. Con esta medida se tiene en cuenta que los usuarios suelen trabaj ar normalmente con los archivos de su directorio de trabajo, y que algunos comandos, como por ejemplo l s , trabajan con los nodos-i de todos los archivos de un mismo directorio. • Cuando se crea un nuevo directorio éste se intenta ubicar en un grupo de cilindros diferente al de su directorio padre. De esta forma los datos se distribuyen de manera uniforme por el disco. • Los bloques de datos de un archivo se intentan colocar en el mismo grupo de cilindros donde está ubicado su nodo-i, ya que normalmente se suelen acceder de forma conjunta. • Los bloques directos de un archivo se aloj an en un grupo de cilindros y luego se va cambiando de grupo de cilindros cada M bloques. El valor M es configurado al crear el sistema de archivos. De esta forma los datos se distribuyen de manera uniforme por el disco y además se evita llenar por completo un grupo de cilindros con un archivo de gran tamaño. • Los bloques de datos de un archivo se intentan colocar en posiciones rotacionales óptimas teniendo en cuenta el factor de entrelazado del disco. 216 Ampliación de sistemas operativos 5.5. Gestión de la E/S en SOBUNIX 5.5.1. Perspectiva general En los SOBUNIX los dispositivos de E/S son clasificados en dos grandes categorías : • Dispositivos modo bloque. Son aquellos que almacenan la información en bloques de tamaño fijo, normalmente 5 1 2 bytes o una potencia de dos múltiplo de la anterior. Estos bloques pueden ser accedidos de forma secuencial o aleatoria. Ejemplos de dispositivos modo bloque son los discos (magnéticos u ópticos) y las cintas magnéticas. • Dispositivos modo carácter. Son aquellos que envían o reciben información como una secuencia o flujo lineal de bytes. En ellos la información no se organiza con una estructura concreta, por lo que no es direccionable y en consecuencia no permite la realización de operaciones de búsqueda. Ejemplos de dispositivos modo carácter son las impresoras, el ratón, el teclado y el módem. En los SOBUNIX si un dispositivo no encaj a como dispositivo modo bloque entonces se considera que es un dispositivo modo carácter, independientemente de que tenga las características de este tipo de dispositivos. Por ejemplo, el reloj hardware no tiene las características de un dispositivo modo bloque ni las de un dispositivo modo carácter. Luego de acuerdo con el criterio establecido en los SOBUNIX el reloj hardware se considera un dispositivo modo carácter. Los SOBUNIX también soportan el uso de pseudodispositivos que son aquellos que se implementan por software. Ejemplos de pseudodispositivos son el dispositivo nulo, que es un agujero de negro de bits, y el dispositivo cero, que llena de ceros las paginas de memoria cuya dirección se le suministra como entrada. Los SOBUNIX integran los dispositivos de E/S dentro del sistema de archivos principal, general­ mente en el directorio 1 dev, para ello asignan a cada dispositivo un archivo de dispositivo modo bloque o modo carácter. Esta integración permite operar con los dispositivos de E/S usando las mismas llamadas al sistema que se utilizan para operar sobre los archivos : open, r ead, wr i t e , etc. El subsistema de E/S es el componente del núcleo encargado de gestionar las operaciones con los dispositivos de E/S . Para comunicarse con el controlador de E/ S, el subsistema de E/ S utiliza un código especifico por cada dispositivo denominado driver del dispositivo. Los drivers son suministrados por los fabricantes de los dispositivos de E/S existiendo una distribución por cada sistema operativo con lo que puede funcionar el dispositivo. Los drivers envían órdenes a los controladores de E/ S de los dispositivos para especificarles la operación que desean realizar. Cuando un dispositivo termina una operación de E/S, el controlador de E/S del dispositivo avisa al driver correspondiente mediante la generación de una interrupción. Para atender a dicha interrupción el núcleo invoca al manejador de la interrupción apropiado. SOBUNIX : gestión de archivos y gestión de la E/S 5.5.2. 217 Archivos de dispositivos Número de dispositivo mayor y número de dispositivo menor En los SOBUNIX cada dispositivo de E/S tiene asignado, en función de su tipo, un archivo de dispositivo modo bloque o un archivo de dispositivo modo carácter. Recuérdese que la máscara de modo del archivo que se almacena en el nodo-i del archivo contiene el tipo del archivo. Todos los archivos de dispositivos generalmente suelen encontrarse aloj ados dentro del directorio 1 dev. El contenido del nodo-i de un archivo de dispositivo, a diferencia del nodo-i de un archivo regular o un directorio, no contiene las direcciones de los bloques de datos del archivo sino dos números enteros positivos denominados : número de dispositivo mayor (major device number) y número de dispositivo menor (minor device number) . El número de dispositivo mayor permite identificar a una cierta clase de dispositivo y el número de dispositivo menor a una instancia de dicho dispositivo. Por otra parte, cada tipo de dispositivo (bloque o carácter) dispone de un conjunto independiente de números de dispositivos mayores . Luego un mismo número de dispositivo mayor puede hacer referencia a dispositivos distintos en función de si es un dispositivo modo bloque o modo carácter. e Ejemplo 5. 13 Un número de dispositivo mayor igual a 3 dentro del conjunto de dispositivos modo bloque puede re­ ferirse, por ejemplo, a un cierto tipo de disco duro conectado al computador. Por otra parte, dentro del conj unto de dispositivos modo carácter un número de dispositivo mayor igual a 3 puede referirse a una impresora. Asimismo si existen dos discos duros iguales conectados al computador, tendrán asignado el mismo número de dispositivo mayor (en este ejemplo 3), pero diferentes números de dispositivos menor, por ejemplo: 1 y 2. • En conclusión en los SOBUNIX la tripleta [tipo de dispositivo, número mayor, número menor] iden­ tifica de manera unívoca a un dispositivo de E/S . Esta información se almacena en el nodo-i del archivo de dispositivo que representa al dispositivo junto con el resto de atributos del archivo. e Ejemplo 5. 14 Supóngase que en un cierto SOB UNIX se representa a uno de los puertos serie del computador mediante el archivo de dispositivo 1 dev 1 t tyS O . Supóngase además que si escribe en un intérprete de comandos de este SOBUNIX la orden ls -1 / dev / t tyS O se muestra en la pantalla la siguiente salida: c rw- rw- - - - 1 ro o t di a l ou t 4 , 6 4 2 0 1 1 - 0 9 - 1 4 / dev / t tyS O 218 Ampliación de sistemas operativos Analizando la información proporcionada por el comando se deduce que / dev / t tyS O es un archivo asociado a un dispositivo modo carácter, su número de dispositivo mayor es igual a 4 y su número de dispositivo menor es igual a 64. • Llamadas al sistema La integración de los dispositivos de E/S dentro del sistema de archivos mediante el uso de archivos de dispositivos permite operar con los dispositivos de E/ S usando las mismas llamadas al sistema que se utilizan para operar con los otros tipos de archivos : open, read, wri t e , etc. Esta característica de los S OBUNIX resulta de gran utilidad para los usuarios y los programadores, ya que pueden trabajar con los dispositivos como si fuesen un archivo más. De esta forma solo necesitan conocer el nombre de ruta del archivo de dispositivo y se evitan trabaj ar con la tripleta descriptiva del dispositivo que manej a el núcleo. La creación de un archivo de dispositivo, a diferencia de los archivos regulares, no se realiza con la llamada al sistema creat sino usando la llamada al sistema mknod, la cual permite crear cualquier tipo de archivo. Esta llamada tiene la siguiente sintaxis: r =mknod ( ruta , modo , dev ) ; Donde ruta es el nombre de ruta absoluta o relativa del archivo que se va a crear y modo es la máscara de modo del archivo. En el caso de que se desee crear un archivo de dispositivo hay que indicar su número de dispositivo mayor y su número de dispositivo menor los cuales se codifican haciendo uso por ejemplo de la macro makedev en una misma variable dev de tipo dev_t denominada número de dispositivo. Si la llamada al sistema se ejecuta con éxito guarda el valor O en r . En caso de error guarda -l. También existe un comando mknod que internamente utiliza la llamada al sistema homónima. Tanto la llamada al sistema como el comando únicamente pueden ser utilizados por el superusuario, excepto si se desea crear una tubería con nombre (ver sección 3 .5 .2), en cuyo caso pueden ser invocados por cualquier usuario. Existen unas pocas llamadas al sistema especiales que únicamente pueden usarse sobre los archivos de dispositivos. Este es el caso por ejemplo de i o c t l . Nodos-v y nodos-i d e archivos d e dispositivos La implementación de los nodos-v y los nodos-i asociados a los archivos de dispositivos depende de cada SOBUNIX. En algunos S OBUNIX, como por ejemplo SVR4, los nodos-i asociados a los archivos de dispositivos se almacenan dentro de la partición de disco que contiene el sistema de archivos principal, que será de un determinado tipo, por ejemplo UFS . En esta implementación cuando el núcleo realiza una operación sobre el nodo-v de un archivo de dispositivo, se redirecciona por defecto a una función dependiente del sistema de archivos principal. Por ejemplo, una operación VO P_OPEN sobre el nodo-v de un archivo de dispositivo redirecciona, en el caso de un sistema UFS , a la función u f s_open ( ) . Obviamente este no es el comportamiento deseado, sino que se debe redireccionar a la función apropiada del driver del dispositivo. SOBUNIX: gestión de archivos y gestión de la E/ S 219 Una de las soluciones adoptadas para resolver este problema consiste en usar una estructura de datos adicional denominada nodo sombra (nodo-s) que se encarga de realizar esta redirección hacia las fun­ ciones del driver. Todas las operaciones sobre un archivo de dispositivo son redireccionadas a través de su nodo-s, en vez de a través del nodo-v. Luego el nodo-s a todos los efectos oculta al nodo-v, de ahí el nombre de nodo sombra. SVR4 usa el pseudosistema de archivos specfs para implementar todos los nodos-s. En otros SOBUNIX, como Linux, los nodos-i asociados a los archivos de dispositivos se crean en tiempo de arranque y se almacenan en un pseudosistema de archivos, como por ejemplo tmpfs, que se implementa únicamente en la memoria principal sin existir almacenamiento de respaldo en el disco. En este esquema los nodos-i se integran dentro de los nodos-v. Con lo que solo es necesario mantener una única estructura de datos asociada al archivo de dispositivo: su nodo-v2 . En dicha estructura existe un puntero al vector de operaciones sobre el nodo-v el cual redirecciona dichas operaciones directamente hacía las funciones del driver adecuadas. 5.5.3. Subsistema de E/S El subsistema de E 1 S es el componente del núcleo que se encarga de efectuar todas aquellas tareas necesarias para la realización de las operaciones de E/S que son comunes a todos los dispositivos e inde­ pendientes de los mismos. Es decir, el subsistema de E/ S gestiona la parte independiente del dispositivo de todas las operaciones de E/S . Entre las tareas que realiza se encuentran las siguientes : asignación y protección de dispositivos, bloqueo de procesos en operaciones de E/S , planificación de la E/S, gestión de los errores producidos en la E/S, gestión de los drivers de los dispositivos y almacenamiento temporal de datos (buffering). Con respecto al almacenamiento temporal de datos asociados a operaciones de E/ S en los SOBUNIX se implementa una caché de buffers de bloques de disco. Se trata de un espacio reservado en la memoria principal para almacenar los bloques de datos del disco accedidos recientemente. Cada bloque de datos se almacena en un buffer de memoria principal. Para localizar rápidamente a los buffers el núcleo mantiene varias colas hash de buffers a las que se accede en función del número de dispositivo y el número de bloque. Cuando el núcleo desea leer un bloque del disco, primero comprueba si existe dicho bloque en la caché. En caso afirmativo el bloque es leído de la caché. En caso negativo, el bloque debe leerse en el disco y cargarse en un buffer libre. Si no existe ningún buffer libre, deberá seleccionarse algún buffer mediante el uso de algún algoritmo de reemplazamiento. En conclusión, con el uso de la caché de buffers se pretende minimizar las operaciones de E/S a disco, las cuales son varios órdenes de magnitud más lentas que los accesos a memoria principal. En el caso de las operaciones de escritura primero se realizan sobre la caché. Periódicamente o si el número de bloques escritos supera un cierto valor los bloques escritos son transferidos por el núcleo al disco. Normalmente existe un proceso del sistema que se encarga de realizar esta tarea. En los sistemas SOBUNIX modernos la operaciones de E/S sobre archivos se integran dentro de la 2En Linux no hacen uso de la terminología nodo-v, sino que directamente le denominan nodo-i. 220 Ampliación de sistemas operativos memoria virtual. En estos sistemas la caché de buffers de bloques de disco se utiliza generalmente para almacenar solo los bloques de datos que contienen metadatos de los sistemas de archivos o los archivos : superbloque, nodos-i, bloques d e indirección simple, doble, etc. Por otra parte, los bloques d e datos lógicos de un archivo son divididos en páginas. Cada página de un archivo puede contener, en función del tamaño de bloque y del tamaño de página, parte de un bloque, un bloque o varios bloques de datos del archivo. Estás páginas se cargan en la caché de páginas que se implementa en la memoria principal la cual es gestionada por el subsistema de gestión de memoria principal del núcleo. 5.5.4. Drivers de los dispositivos de E/S Un driver de un dispositivo contiene el código que permite al núcleo controlar un determinado tipo de dispositivo de E/S . Así, es necesario un driver para el ratón, otro para el teclado, otro para el disco duro, etc. Los drivers son diseñados por los fabricantes de los dispositivos teniendo en cuenta las especi­ ficaciones de la interfaz de drivers del subsistema de E/ S del núcleo y las características del dispositivo. Un driver de un dispositivo interactúa con el subsistema de E/S del núcleo y con el controlador de E/ S que controla el dispositivo. Un driver puede invocar a ciertas rutinas del núcleo para realizar diversas tareas, como por ejemplo, asignación de memoria y control del DMA. Además suministra al subsistema de E/S el conjunto de funciones que se pueden realizar sobre el dispositivo. Los drivers de dispositivos en SOBUNIX son parte del código del núcleo y se ejecutan en modo núcleo. El núcleo puede invocar a un driver por diferentes causas [Vahalia, 1 995] : • Configuración. Cuando se arranca el sistema operativo el núcleo invoca a los drivers de los dispo­ sitivos para inicializar los dispositivos. • E/S . El subsistema de E/S del núcleo invoca a un driver para leer o escribir datos en un cierto dispositivo. • Control. Un usuario puede solicitar realizar operaciones de control sobre un cierto dispositivo, como por ejemplo rebobinar una cinta magnética, o abrir o cerrar un cierto dispositivo. • Interrupciones. Los controladores de E/S generan interrupciones cuando se ha completado una operación de E/S sobre el dispositivo que supervisan o se ha producido algún cambio en su esta­ do. Las interrupciones son tratadas por los manipuladores de las interrupciones. Un manipulador de una interrupción, entre otras tareas, debe despertar al driver del dispositivo, si éste se había bloqueado en espera de que el controlador de E/S estuviera preparado para procesar otra petición de E/S . La invocación de operaciones del drivers debido a E/S o control se realiza de forma síncrona. Mien­ tras que la invocación de operaciones del driver debido a la aparición de interrupciones se realiza de forma asíncrona. Atendiendo a las diferentes formas de invocar a las operaciones de un driver se distinguen en su diseño dos partes : SOBUNIX: gestión de archivos y gestión de la E/S 221 • Parte superior (top half). Contiene las rutinas del driver que se invocan de forma síncrona. Estas rutinas se ejecutan en el contexto del proceso cuya ejecución ha producido la invocación del driver. Pueden acceder al espacio de direcciones y al área-u del proceso invocador, además pueden poner al proceso a dormir si fuese necesario. • Parte inferior (bottom half). Contiene las rutinas del driver que se pueden invocar de forma asín­ crona. Se ejecutan en el contexto del núcleo e interactúan con el controlador de E/ S del dispositivo cargando en sus registros diferentes órdenes para que las efectúe sobre el dispositivo, comprobando su estado e inicializándolo, si es necesario. En los SOBUNIX, el núcleo mantiene dos tablas denominadas tablas de conmutación. Cada entrada de una tabla de conmutación está asociada a un determinado driver y contiene una estructura de datos con punteros a las funciones que implementan las operaciones soportadas por dicho driver. Existe una tabla de conmutación para los drivers de los dispositivos modo bloque y otra tabla de conmutación para los drivers de los dispositivos modo carácter. Cuando el núcleo desea realizar alguna operación sobre un determinado dispositivo, en primer lugar en función del tipo de dispositivo (bloque o carácter) selecciona la tabla de conmutación adecuada. A continuación localiza la estructura de datos del driver en la tabla de conmutación usando el número mayor del dispositivo e invoca a la función apropiada del driver. Esta función recibe del núcleo, entre otros argumentos, el número mayor y el número menor del dispositivo. El driver mantiene unas tablas internas para traducir estos números a la dirección del registro de control o puerto del controlador de E/S del dispositivo especificado. Nótese que es la interfaz del subsistema de E/ S del núcleo la que define las operaciones que debe ser capaz de soportar un driver de un dispositivo, como por ejemplo: d_open ( ) , d_c l o s e ( ) , d_read ( ) , d_wr i t e ( ) y d_i o c t l ( ) . Los diseñadores del driver deben ajustarse a dicha interfaz. Ahora bien, un driver puede que no soporte todas las operaciones de la interfaz. Por ejemplo, un driver de impresora no necesita implementar una operación de lectura. En el caso de que el núcleo solicite una operación no soportada por un driver, éste le devuelve un código de error ENODEV. También puede ocurrir que una operación sobre un determinado driver no suponga la realización de ninguna acción por parte del driver. Por ejemplo, muchos drivers no realizan ninguna acción para implementar una operación d_c l o s e ( ) , en dicho caso el driver implementa una rutina genérica que devuelve un O al núcleo para indicarle que la operación se ha realizado correctamente. Cada driver recibe un prefijo identificativo que depende de cada SOBUNIX. Dicho prefijo se utiliza en todas las operaciones de dicho driver que son invocadas a través de la tabla de conmutación corres­ pondiente. Por ejemplo, en Solaris un driver del disco suele tener asociado el prefijo dk, luego el nombre de sus operaciones empezará con dicho prefijo: dkread ( ) , dkwr i te ( ) , etc. 5.5.5. Tratamiento de las interrupciones La mayoría de SOBUNIX asignan a cada tipo de interrupción un número entero positivo denominado nivel de prioridad de la interrupción (npi), que puede tomar valores comprendidos dentro del rango 222 Ampliación de sistemas operativos [npimil� > npimaxl · Generalmente npim in es igual a O y corresponde a la prioridad más baj a. Los procesos de los usuarios y la mayor parte del código del núcleo se ejecutan con este nivel de prioridad. El valor de npimax depende de cada S OBUNIX. Por ejemplo en Solaris npimax = 1 5, mientras que en BSD npimax=3 1 . La configuración del npi a un determinado valor bloquea o enmascara todas las interrupciones con npi igual o inferior. De esta forma se consigue priorizar la atención de las interrupciones. Por ejemplo, en Solaris cuando llega una interrupción de reloj el npi se configura a 1 0, ello impide que se puedan atender interrupciones con npi igual o inferior a 1 0, como es el caso, por ejemplo, de las interrupciones de disco (npi=4) o las interrupciones de dispositivos de red (npi=7). Dichas interrupciones serán atendidas cuando disminuya el valor del npi al valor apropiado. El valor del npi se almacena en el registro de estado del procesador. Cada arquitectura suele in­ cluir diferentes instrucciones máquina especiales que permiten configurar el valor del npi. Además el núcleo implementa en su núcleo diferentes funciones para configurar este valor haciendo uso de estas instrucciones máquina. Generalmente cuando se detecta una interrupción en un procesador se ejecuta una rutina genérica del núcleo de tratamiento de las interrupciones que se encarga de salvar el contexto hardware del proceso actualmente en ejecución, elevar el npi de acuerdo con el tipo de interrupción e invocar al manejador de la interrupción apropiado. Cuando el manej ador de la interrupción completa su ejecución se devuelve el control a la rutina genérica que disminuye el valor del npi al valor que tenía antes de atender la interrupción y restaura el contexto hardware del proceso que se estaba ejecutando. Puesto que el tratamiento de las interrupciones es una tarea crítica para el buen rendimiento del sistema, los manej adores de las interrupciones suelen ser tener un código pequeño que varía en función de la arquitectura hardware y que es rápido de ejecutar. Además su prioridad de ejecución es muy elevada. En algunos SOBUNIX los manejadores se implementan como rutinas del núcleo que se ejecutan en el contexto del proceso actualmente en ejecución. En otros SOBUNIX, como Solaris, los manej adores de interrupciones se implementan como hilos del núcleo independientes no asociados a ningún proceso. 5.6. Conectores Los conectores (sockets) son uno de los principales mecanismos usados en los SOBUNIX para im­ plementar las conexiones en red. Otro mecanismo muy utilizado son los STREAMS [Vahalia, 1 995] . Un conector permite establecer un canal de comunicación a través de una red (o localmente dentro de un mismo computador) entre un proceso emisor y un proceso receptor (ver Figura 5 . 1 1 ) . Los conectores son implementados mediante archivos que s e crean y s e eliminan dinámicamente. Un conector se crea invocando a la llamada al sistema r = s o c ke t ( f ami l i a , t ipo , pro t o c o l o ) ; Esta llamada presenta los siguientes parámetros de entrada [Márquez, 2004] : • f ami l i a. Especifica la familia de direcciones o protocolos que se desea emplear. Dichas familias SOBUNIX: gestión de archivos .;--v Pro � es emisor ¡.- Espacio � _...Conector . 1 y Red ---+ del usuario Espacio ¡.- del núcleo ---+ y gestión de la E/S 223 � Conecto!:... � 1 roceso receptor y 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 L--------------------------------------------------� Figura 5 . 1 1 - Comunicación en red mediante el uso de conectores suelen estar definidas generalmente en el archivo de cabecera sys 1 s o cket . h. Dos de las familias más usadas son: - AF_UNIX (o PF_UNI X ) . Protocolos internos de UNIX. Esta familia se utiliza para comunicar procesos que se ejecutan en la misma máquina. En consecuencia no requiere de la existencia de una tarjeta de red en el computador ya que no la utiliza. - AF_INET (o PF_INET ) . Protocolos de Internet, como por ejemplo TCP o UDP. • t ipo. Establece la semántica de conexión para el conector. Entre otros, puede tomar, los siguientes valores : - SOCK_STREAM. Conector con un protocolo orientado a conexión. Consiste en realizar una búsqueda de enlaces libres que unan los dos computadores que se quieren conectar. Una vez establecida la conexión se pueden enviar los datos en orden secuencial, como si fuese una tubería, ya que la conexión es permanente y fiable. - socK_DGRAM. Conector con un protocolo no orientado a conexión o datagrama. Se trata de conexiones no permanentes o fiables. La transmisión por datagramas se realiza a nivel de paquetes. Cada paquete puede seguir una ruta distinta. Además la recepción de los paquetes puede no ser secuencial. • pro t o c o l o . Permite especificar el protocolo particular que se va utilizar dentro de una determina­ da familia. Normalmente cada tipo de conector tiene asociado un determinado protocolo, pero en el caso de que existiera más de uno, se especifica el adecuado con este parámetro. Si este parámetro se configura con el valor O, entonces la elección del protocolo se delega en el núcleo. Si durante la ejecución de esta llamada al sistema se produce algún error entonces en r se almacena el valor -l. Si la llamada se ejecuta con éxito entonces en r se almacena el descriptor de archivo asociado al conector. 224 e Ampliación de sistemas operativos Ejemplo 5 . 15 La llamada al sistema f d= s o c k e t ( AF_UNIX , SOCK_STREAM , O ) ; intenta crear un conector orientado a conexión ( SOCK_STREAM) que utilizará los protocolos internos de UNIX (AF_UNIX) . El protocolo utilizado será escogido por el núcleo ( 0 ) . Si la llamada se ejecuta con éxito entonces almacena en fd el descriptor del archivo asociado al conector. En caso de error almacena el valor -l. • Antes de poder usar un conector se le debe asociar una dirección particular dentro de la familia de protocolos seleccionada. Para ello se debe utilizar la llamada al sistema bind. Una vez creado un conector orientado a conexión con una dirección particular asociada en cada uno de los dos computadores X e Y que se desean comunicar por red es posible establecer una conexión entre los dos computadores. Un proceso A en el computador X debe realizar una llamada al sistema l i s t en sobre su conector, la cual crea un buffer y bloquea al proceso hasta que llegan datos por la red. Otro proceso B en el computador Y debe realizar una llamada al sistema c onnec t pasándole como parámetros de entrada el descriptor de su conector local en Y y la dirección del conector en la compu­ tadora X. Si el proceso A acepta la conexión entonces se puede establecer una comunicación entre los conectores. El funcionamiento de una conexión entre conectores es similar al de una tubería. Un proceso puede leer o escribir de su conector local de la misma forma que a cualquier otro tipo de archivo, es decir, usando las llamadas al sistema read o wr i t e . Cuando un proceso termina de usar una conexión debe cerrarla usando la llamada al sistema e l o s e . Los conectores aparte d e read disponen de las siguientes llamadas al sistema adicionales para leer datos en ellos : r eadv, re cv, recvfrom y recvms g. Asimismo disponen de las siguientes llamadas para escribir datos: wr i t ev, s end, s endto y s endms g. 5.7. Resumen En los SOBUNIX se distinguen los siguientes tipos de archivos : ordinarios o regulares, directorios, especiales (asociados a dispositivos), tuberías, conectores y enlaces simbólicos. Para que un proceso pueda poder operar sobre un archivo, éste primero tiene que ser abierto mediante la invocación de la llamada al sistema open. La rutina del núcleo del sistema operativo que trata esta llamada al sistema crea en la memoria principal una estructura de datos denominada objeto de archivo abierto y asigna a dicha estructura un número entero positivo pequeño denominado descriptor de archivo para identificarla. La llamada al sistema open devuelve el descriptor del archivo abierto al proceso que invocó la llamada. Para poder operar sobre un archivo abierto el proceso debe pasar el descriptor del archivo como argumento de las llamadas al sistema asociadas a las operaciones que desee realizar sobre el archivo. SOBUNIX: gestión de archivos y gestión de la E/S 225 En los sistemas de archivos desarrollados para SOBUNIX una entrada de una lista de un directo­ rio contiene, entre otras informaciones, el número del nodo-i asociado a un archivo y su nombre. Los SOBUNIX implementan una estructura de directorios de gráfica acíclica. Durante el proceso de instalación de un SOBUNIX se realiza un formateo a alto nivel de una partición del disco duro para crear el sistema de archivos principal donde se aloja el directorio raíz 1 . Cada SOBUNIX establece el tipo de sistema de archivos principal que crea. Aparte de este sistema de archivos principal los SOBUNIX son capaces de reconocer y manipular otros sistemas de archivos del mismo o distinto tipo ubicados en memoria secundaria. ' ' Los SOBUNIX modernos para soportar distintos tipos de archivos implementan una capa de software denominada capa nodo-v/sistema de archivos virtual. Cualquier operación sobre un archivo o sobre un sistema de archivos completo invocada por el usuario a través de una llamada al sistema o internamente por algún subsistema del núcleo es redireccionada por la capa nodo-v 1 sistema de archivos virtual hacía la rutina dependiente del sistema de archivos al que pertenezca el archivo que da soporte a dicha operación. UFS es un sistema de archivos soportado por diversos SOBUNIX. Deriva del sistema de archivos FFS de BSD y presenta la siguiente estructura en la partición de disco donde se crea: área de arranque, superbloque y grupos de cilindros. El área de arranque contiene el código necesario para arrancar el SO­ BUNIX si dicha partición se usa como partición activa. El superbloque contiene información estadística y administrativa del sistema de archivo, como por ejemplo: un número mágico que identifica al sistema como de tipo UFS , el número, el tamaño y la localización de los grupos de cilindros, el tamaño de blo­ que de datos que se utiliza, el número total de bloques de datos y de nodos-i, etc. Un grupo de cilindros está formado por un conjunto de cilindros contiguos. El número de grupos de cilindros y el número de cilindros que contiene un grupo son configurados cuando se crea el sistema de archivos UFS . Cada gru­ po de cilindro contiene la siguiente información: una copia del superbloque, una estructura de grupo de cilindros que contiene información estadística y administrativa del grupo de cilindros, mapas de bits para conocer cuales son los bloques, fragmentos y nodos-i libres en el grupo de cilindros, y nodos-i del grupo de cilindros. Los SOBUNIX integran los dispositivos de E/S dentro del sistema de archivos principal, general­ mente en el directorio 1 dev, para ello asignan a cada dispositivo un archivo de dispositivo modo bloque o modo carácter. Esta integración permite operar con los dispositivos de E/ S usando las mismas llamadas al sistema que se utilizan para operar sobre los archivos : open, read, wr i te, etc. El contenido del nodo-i de un archivo de dispositivo, a diferencia del nodo-i de un archivo regular o un directorio, no contiene las direcciones de los bloques de datos del archivo sino dos números enteros positivos denominados: número de dispositivo mayor y número de dispositivo menor. El número de dispositivo mayor permite identificar a una cierta clase de dispositivo y el número de dispositivo menor a una instancia de dicho dispositivo. El núcleo generalmente mantiene dos tablas denominadas tablas de conmutación. Existe una tabla de conmutación para los drivers de los dispositivos modo bloque y otra tabla de conmutación para los drivers de los dispositivos modo carácter. Cada entrada de una tabla de conmutación está asociada a un determinado driver y contiene una estructura de datos con punteros a las funciones que implementan las operaciones soportadas por dicho driver. 226 Ampliación de sistemas operativos En relación con las conexiones en red, los conectores (sockets) son uno de los principales mecanis­ mos usados en los SOBUNIX para implementarlas. Un conector permite establecer un canal de comu­ nicación a través de una red (o localmente dentro de un mismo computador) entre un proceso emisor y un proceso receptor. Los conectores son implementados mediante archivos que se crean y se eliminan dinámicamente. 5.8. Lecturas recomendadas Si se desea obtener una explicación adicional de los contenidos tratados en este capítulo se pueden consultar, por ejemplo: los capítulos 8 y 9 de [Vahalia, 1 995] , los capítulos 2, 3, 4 y 1 1 de [Márquez, 2004] , los capítulos 14 y 15 de [McDougall y Mauro, 2006] y el capítulo 10 de [Tanenbaum, 2009] . 5.9. Autoevaluación 5. 1 . ¿Cómo se estructura la información contenida en un archivo en los SOBUNIX? ¿Qué tipos de acceso a los archivos soportan? (Respuesta en sección 5 . 2 . 1) 5 .2. Explicar razonadamente si en los SOBUNIX el nombre de un archivo puede tener una extensión. (Respuesta en sección 5 . 2 . 1) 5 .3. Enumerar y explicar brevemente los tipos de archivos soportados en los SOBUNIX. (Respuesta en sección 5 . 2 . 1) 5 .4 . Enumerar los atributos de un archivo soportados en los SOBUNIX. ¿Qué llamadas al sistema y comandos pueden usarse para visualizar los atributos de un archivo? (Respuesta en sección 5 . 2 . 1) 5 . 5 . ¿Qué es un descriptor de un archivo? ¿Cómo se obtiene? (Respuesta en sección 5.2.1) 5 . 6 . ¿Qué es una tabla de descriptores de archivos? ¿Qué información contiene una de sus entradas? ¿A quién están asignadas por defecto las tres primeras entradas de la tabla? (Respuesta en sección 5 . 2 . 1) 5 .7 . ¿Qué es un objeto de archivo abierto? ¿Cuándo se crea? Enumerar y explicar la información que contiene. (Respuesta en sección 5 . 2 . 1) 5 . 8 . Explicar la utilidad y la sintaxis de cada una de las siguientes llamadas al sistema: c r e a t , open, e l o s e , read, wr i te, l s e ek, s t a t , f s ta t y fcnt l . (Respuesta en sección 5 . 2 . 1) 5. 9. En los sistemas de archivos desarrollados para los SOBUNIX ¿qué información suele contener una entrada de una lista de un directorio? (Respuesta en sección 5 . 2.2) 5. 10. ¿Qué tipo de estructura de directorios suelen implementar los SOBUNIX? ¿Qué carácter se utiliza en los SOBUNIX para representar al directorio raíz y separar los componentes de un nombre de ruta? (Respuesta en sección 5 .2.2) SOBUNIX: gestión de archivos y gestión de la E/ S 227 5. 1 1 . Explicar la utilidad y la sintaxis de cada una de las siguientes llamadas al sistema: chdi r, mkd i r y rmd i r . (Respuesta e n sección 5 . 2.2) 5 . 12. ¿Qué es un enlace duro? ¿ Y un enlace simbólico?. (Respuesta en sección 5 .2.3) 5. 13. Explicar el funcionamiento del contador de enlaces duros del nodo-i de un directorio. (Respuesta en sección 5.2.3) 5. 14. ¿Cuántos nombres puede tener un archivo en SOBUNIX? ¿Y un directorio? (Respuesta en sección 5 .2.3) 5. 15. ¿Qué mascara de modo simbólica tiene asociada un enlace simbólico? (Respuesta en sección 5 .2.3) 5 . 16. ¿Qué llamadas al sistema y comandos pueden utilizarse para crear enlaces duros y simbólicos ? ¿ Y para eliminarlos? (Respuesta en sección 5 .2.3) 5 . 17. ¿Qué es un punto de montaje? ¿ Y la tabla de montaje? (Respuesta en sección 5 . 2 .4) 5. 18. ¿Cómo se realiza el acceso a un directorio o a un archivo perteneciente a un sistema de archivos montado sobre el sistema de archivos principal? (Respuesta en sección 5 . 2 .4) 5. 19. Explicar la utilidad de los siguientes comandos: moun t , umount , mk f s , f s c k y d f . (Respuesta e n sección 5 . 2 .4) 5 .20. ¿Cuál es la utilidad de la capa nodo-v/SAV? (Respuesta en sección 5.3) 5 .21 . ¿Qué es un nodo-v? ¿Cuándo se asigna? ¿Qué función realiza? (Respuesta en sección 5 . 3 . 1) 5 .22. Explicar el funcionamiento del contador de referencias de un nodo-v y enumerar las circunstancias en que se incrementa. (Respuesta en sección 5 . 3 . 1) 5 .23. Enumerar y explicar brevemente la información contenida en la estructura de datos que implementa un nodo-v (Respuesta en sección 5 . 3 . 1) 5 .24. ¿Qué es un sistema de archivos virtual? ¿Cuándo se asigna? ¿Qué función realiza? (Respuesta en sección 5 . 3 .2) 5 .25 . Enumerar y explicar brevemente la información contenida en la estructura de datos que implementa un SAV. (Respuesta en sección 5 . 3 .2) 5 .26. Explicar cómo se realiza el análisis de nombres de rutas en los SOBUNIX que implementan la capa nodo-v/SAV. (Respuesta en sección 5 . 3 . 3 ) 5 .27. Explicar la información que contiene y cómo se utiliza la caché de búsqueda de nombres en direc­ torios en los SOBUNIX. (Respuesta en sección 5 . 3 .3) 5 .28. Enumerar las principales características de un sistema de archivos UFS . (Respuesta en sección 5.4.1) 228 Ampliación de sistemas operativos 5 .29. Dibujar un diagrama que represente la estructura en una partición de disco de un sistema de archi­ vos UFS . Explicar la información contenida en cada uno de sus componentes. (Respuesta en sección 5 .4.2) 5 .30. Explicar cómo se implementan los directorios en un sistema de archivos UFS . (Respuesta en sección 5 .4.3) 5 .3 1 . ¿Qué es un nodo-im? ¿Qué información contiene? (Respuesta en sección 5 .4.4) 5 .32. ¿Cuándo se coloca un nodo-im en la lista de nodos-im libres? ¿Dé que depende la posición de un nodo-im en esta lista? (Respuesta en sección 5.4.4) 5 .33. ¿Qué sucede si el núcleo no localiza a un nodo-im en la cola hash donde le correspondería estar? (Respuesta en sección 5 .4.4) 5 .34. Explicar cómo se realiza la asignación de espacio en un sistema de archivos UFS . (Respuesta en sección 5 .4.5) 5 . 35 . Enumerar y explicar las dos grandes categorías en que se clasifican los dispositivos de E/S en los S OBUNIX. (Respuesta en sección 5 . 5 . 1) 5 .36. ¿Qué son los pseudodispositivos ? (Respuesta en sección 5 . 5 . 1) 5. 37. Explicar cómo se integran los dispositivos de E/ S dentro del sistema de archivos. (Respuesta en sección 5.5 .2) 5. 38. Explicar la utilidad del número de dispositivo mayor y del número de dispositivo menor. ¿Dónde se aloj a esta información? (Respuesta en sección 5 . 5 .2) 5 .39. Explicar la utilidad y la sintaxis de la llamada al sistema mknod (Respuesta en sección 5 . 5 .2) 5 .40. Comentar las diferentes soluciones adoptadas en los SOBUNIX para implementar los nodos-v y los nodos-i de los archivos de dispositivos. (Respuesta en sección 5 . 5 .2) 5 .41 . Explicar la información que contiene y cómo se utiliza la caché de buffers de bloques de disco en los S OBUNIX. (Respuesta en sección 5 .5 .3) 5 .42. Explicar cómo se integran las operaciones de E/S sobre archivos dentro de la memoria virtual. (Respuesta en sección 5 .5 . 3 ) 5 .43. ¿Dónde se integra y en qué modo se ejecuta un driver de un dispositivo en los SOBUNIX? (Respuesta en sección 5 .5 .4) 5 .44. Enumerar y comentar brevemente las principales causas por las que el núcleo puede invocar a un driver de un dispositivo en los SOBUNIX. (Respuesta en sección 5 . 5 .4) SOBUNIX: gestión de archivos y gestión de la E/S 229 5.45 . Explicar las partes que se distinguen en el diseño de un driver de un dispositivo en SOBUNIX atendiendo a las diferentes formas de invocar sus operaciones. (Respuesta en sección 5 . 5 .4) 5 .46. ¿Qué son las tablas de conmutación mantenidas en los SOBUNIX? ¿Para que se utilizan? (Respuesta en sección 5 . 5 .4) 5.47. ¿Quién define las operaciones que debe ser capaz de soportar un driver de un dispositivo? ¿Qué indica el código de error ENODEV? (Respuesta en sección 5 . 5 .4) 5.48. Explicar cómo se realiza el tratamiento de las interrupciones en los SOBUNIX. (Respuesta en sección 5 .5 .5) 5 .49. ¿Qué son los conectores? (Respuesta en sección 5 .6) 5 .50. Explicar la utilidad de las siguientes llamadas al sistema: s o cke t , bind, l i s t en y c onne c t . (Respuesta en sección 5 .6) 5. 51 . ¿Cómo se puede leer o escribir en un conector? (Respuesta en sección 5. 6) 5.10. Ejercicios 5. 1 Explicar el significado de las siguientes llamadas al sistema: a) fd=open ( " da t o s . txt " O_RDWR J O_APPEND ) ; 1 b) fd=open ( " prueba " O_RDONLY J O_CREAT 0 6 0 0 ) 1 1 ; 5 .2 El nodo-i de un archivo tiene el valor 1 en su contador de enlaces duros. ¿Qué sucede si se aplica sobre dicho archivo la llamada al sistema o el comando un l ink? 5 .3 Escribir un programa en C que ilustre el uso de la llamada al sistema s t a t . 5 .4 En el directorio de trabaj o actual de un usuario residen los ficheros wer t . t x t (ver Figura 5 . 1 2) y j 1 0 (ver Figura 5 . 1 3 ) . a) Explique detalladamente e l funcionamiento d e este programa s i s e invoca desde u n intérprete de comandos mediante la orden: j 1 0 2 5 wer t . txt b) Comprobar que la respuesta dada en el apartado anterior es correcta compilando y ejecutando este programa en un SOBUNIX. 5 . 5 Ejecutar en un intérprete de comandos de un SOBUNIX la orden d f . Consultar el manual de ayuda man para explicar la funcionalidad de este comando y el significado de la salida que muestra en la pantalla. 5 . 6 Determinar el tamaño máximo que en teoría podría tener un archivo en un sistema de archivos UFS si se utiliza un tamaño de bloque de 8 KiB . 230 Ampliación de sistemas operativos El s i s t ema ope r a t ivo UNIX es un s i s t ema mu l t iusuar i o y mu l t iproc e s o \ n e s c r i t o e n l engua j e C que d e s d e su nac imi ento a p r i n c i p i o s d e l a \ n déc ada de l o s s e t en t a h a i do a l c a n z ando bas tan t e éxi t o y popu l a r i da d \ n tanto en e l ámb i t o un ive r s i t ar i o c omo en e l emp r e s ar i a l . \ n Figura 5.12 - Primeras líneas del archivo wert . txt # i nc l ude < s t d l ib . h> # i n c l ude < s t d i o . h> # in c l ude < f c n t l . h> vo i d try ( i n t , ma in ( i nt np , { char * ) ; char * pe [ ] ) int x , T , h , a ; char * L ; if ( np = = 3 ) { x = a t o i ( pe [ l ] ) ; L = ( char * ) ma l l o c ( ( x& ( x - l ) ) * s i z e o f ( char ) ) ; f o r ( h = O ; h< = ( ( x& ( x - 1 ) ) - 2 ) ; h+ + ) * ( L +h ) = ' \ 0 ' ; a = open ( p e [ 2 ] , 0_RDWR ) ; t ry ( a , L ) ; l s eek ( a , x , x %2 ) ; t ry ( a , L ) ; exi t ( 3 ) ; vo i d try ( i n t y , char * U ) read ( y , u , 1 5 ) ; p r i nt f ( " \ n \ " %s \ " \ n " , u ) ; Figura 5.13 - Código C del programa j 1 0 del Ejercicio 5.4 Capítulo 6 SOBUNIX: el sistema operativo Linux Objetivos docentes Los objetivos docentes de este capítulo son los siguientes : • Conocer los orígenes y la naturaleza de Linux. • Saber cómo se implementa la gestión de procesos e hilos en Linux. • Conocer los detalles de la implementación de la planificación de tareas en Linux. • Conocer cómo se realiza la gestión de memoria en Linux. • Conocer las particularidades de la implementación de la gestión de archivos en Linux. • Conocer las principales características de los sistemas de archivos nativos de Linux: EXT2, EXT3 y EXT4. 6.1. Introducción Este capitulo está dedicado al estudio de las particularidades más reseñables de Linux, el SOBUNIX de código abierto más conocido y utilizado. En primer lugar se incluyen una serie de consideraciones generales sobre Linux. En concreto se comentan los orígenes y las diferencias entre versiones y distri­ buciones de Linux. En segundo lugar se describen las particularidades de la gestión de procesos e hilos en Linux. En tercer lugar se describe la planificación de tareas en Linux, distinguiendo entre la planifi­ cación utilizada antes y después de la versión 2.6.23 . En cuarto lugar se analiza la implementación de la gestión de memoria en Linux, describiendo tanto la gestión del espacio de direcciones virtuales como la gestión de la memoria física. La última parte del capítulo está dedicada al estudio de las particula­ ridades de la gestión de archivos en Linux. En concreto se describe cómo se implementa en Linux la 23 1 232 Ampliación de sistemas operativos capa nodo-v/sistema de archivos virtual. Además se estudian los sistemas de archivos nativos de Linux: EXT2, EXT3 y EXT4. Conviene señalar que en la exposición de los contenidos de este capítulo se presupone que el lector ya se ha leído los cinco capítulos anteriores dedicados al estudio de los SOBUNIX. También conviene tener presente que obviamente por motivos de espacio este capítulo no es más que un pequeño resumen de las particularidades del núcleo de Linux. Los lectores que deseen profundizar en su estudio pueden consultar las referencias bibliográficas reseñadas en la sección 6 . 8 . 6.2. Consideraciones generales sobre Linux 6.2.1. Orígenes de Linux Linux es un SOBUNIX cuyo núcleo en su primera versión fue escrito en 199 1 por Linus Benedict Torvalds, por aquel entonces un estudiante finlandés de veintiún años. Linus creó Linux 1 por hobby ya que no estaba satisfecho con el rendimiento del sistema operativo MS-DOS del PC Intel 386 que se había comprado; escribió Linux en C tomando como base el sistema operativo MINIX desarrollado por Andrew S. Tanenbaum. Linus deseaba compartir su trabaj o con otras personas por ello colgó en Internet el código fuente de Linux, primero con una licencia propia de tipo freeware y posteriormente con una licencia GPL del proyecto GNU. El acceso libre y gratuito al código fuente de Linux ha propiciado que miles de personas en todo el mundo colaboren con Linus desinteresadamente en el desarrollo y mejora de este sistema operativo. En la actualidad Linux ha sido portado a diferentes arquitecturas hardware y continúa evolucionando. Además cada vez interesa a más usuarios y empresas ya que, aparte de ser gratuito, es comparable en potencia y flexibilidad a otros sistemas operativos comerciales. 6.2.2. Versiones del núcleo de Linux Cada versión del núcleo de Linux2 tiene asignada un identificador numérico. Hasta la versión 2.5 el identificador podía constar de hasta tres número enteros positivos: v . T . R. Si T era un número par signi­ ficaba que se trataba de una versión estable del núcleo, mientras que si era un número impar significaba que se trataba de una versión en desarrollo. En las versiones en desarrollo se pretendía añadir nuevas funcionalidades al núcleo. Ello suponía la realización de cambios importantes sobre los algoritmos y las estructuras del núcleo, lo que hacía que estas versiones no fueran muy estables. Por ejemplo, de acuer­ do con este criterio la versión 1 .2 es una versión estable mientras que la versión 1 .3 es una versión en desarrollo. Por otra parte, el número R denota el número de lanzamiento o liberación (release) de dicha versión. Cada liberación incluye pequeñas modificaciones y corrige los errores que se hayan detectado desde la 1 El nombre de Linux surge como una contracción de las palabras Linus y UNIX. 2En la web www.kemel .org puede descargarse el código fuente de las últimas versiones aparecidas del núcleo de Linux. SOBUNIX: el sistema operativo Linux 233 liberación anterior. Por ejemplo, la versión 1 .2.2 incluye pequeños cambios y corrección de errores en relación con la versión 1 .2. 1 . Desde la versión 2.6 el identificador numérico de la versión puede constar de hasta cuatro números enteros positivos: v . T . R . x. Ahora el número T ya no denota si se trata de una versión estable o en desarrollo. Cada nueva liberación R de una versión v . T puede incluir cambios importantes incluso en componentes claves del núcleo por lo que es potencialmente inestable. Esto sucedía por ejemplo entre las versiones 2.6. 1 O y 2.6. 1 1 . Para corregir los problemas que se van encontrando en una versión y conseguir que sea estable se liberan sucesivas versiones parcheadas que se identifican con el número X. Para conocer la versión del núcleo de Linux que se está utilizando se puede usar el comando uname - r Señalar que en este capítulo s e toman como base las versiones 2.6. 1 1 y 2.6.24 del núcleo de Linux. 6.2.3. Distribuciones Linux En Internet puede encontrarse el código fuente del núcleo de Linux, así como diversos programas (intérpretes de comandos, compiladores, etc) y aplicaciones (escritorios, navegadores web, gestores del correo electrónico, etc) que se ejecutan en Linux, la mayoría de ellos procedentes del proyecto GNU y de la Free Software Foundation. La tarea de buscar, compilar e instalar un sistema Linux (núcleo + programas + aplicaciones) en un computador partiendo desde cero es una tarea bastante laboriosa que debe ser realizada por un usuario informático bien experimentado. Afortunadamente, con la idea de acercar el sistema Linux a todo tipo de usuarios, surgieron las distribuciones de Linux realizadas por diferentes organizaciones y empresas. Una distribución de Linux recopila en uno o varios CDs todos los ficheros necesarios para montar un sistema Linux en una partición del disco duro de un computador. Además incluye un programa instalador con una GUI amigable que simplifica enormemente la instalación de Linux. Entre las distribuciones más conocidas de Linux se encuentran las siguientes : Red Hat, Debian, Slackware, Fedora, Gentoo, Ubuntu, Kubuntu, Mandriva y SuSE. Las principales diferencias entre las distribuciones de Linux radican en los programas y aplicaciones que instalan, así como en la facilidad que conceden al usuario para instalar y configurar el sistema. 6.3. Gestión de procesos e hilos en Linux A diferencia de otros SOBUNIX, como por ejemplo Solaris (ver sección 2.2.2), en Linux no existe una distinción clara entre las abstracciones de proceso, proceso ligero (hilo de usuario) e hilo del núcleo. De hecho en varías partes del código del núcleo se utilizan los términos procesos, hilos y tareas de forma sinónima; debido seguramente a que en las primeras versiones del núcleo utilizaban como unidad de planificación y ejecución al proceso y no soportaban procesos ligeros ni hilos del núcleo. Para el núcleo de Linux un proceso monohilo, un proceso ligero o un hilo del núcleo es simplemente un flujo de ejecución independiente o tarea (task) que debe ser planificada y ejecutada. Linux considera 234 Ampliación de sistemas operativos a un proceso monohilo como una única tarea. Mientras que un proceso multihilo constará de un número de tareas igual al número de procesos ligeros (hilos de usuario) en que se descomponga. También cada hilo del núcleo asociado a un proceso del sistema o utilizado para ejecutar una determinada función del núcleo es considerado como una única tarea. Por otra parte, en Linux se utiliza el concepto de grupo de hilos para designar a todos los procesos ligeros que forman parte de una misma aplicación multihilo. 6.3. 1. Estructuras del núcleo asociadas a las tareas El núcleo asigna a cada tarea en el momento de su creación una estructura t a s k_s t ruc t , una pila en modo núcleo y una estructura thread_i n f o , entre otras estructuras de datos. Estructura task_struct La estructura task_s truc t , también denominada descriptor del proceso, contiene datos y punteros a otras estructuras de datos que el núcleo necesita conocer para poder gestionar una tarea. Esta estructura contiene la siguiente información: • Estado de la tarea. En Linux una tarea puede encontrarse en los siguientes estados: - TASK_RUNNING (Ejecutándose) . Una tarea se encuentra en este estado si se está ejecutando en un procesador o si está preparada para ser ejecutada cuando lo determine el planifica­ dor del núcleo. Nótese la diferencia entre Linux y otros SOBUNIX donde ejecutándose y preparado para ejecución son considerados estados independientes. - TASK_INTERRUPT I BL E (Interrumpible). La tarea se encuentra dormida a la espera de que se produzca un determinado evento, pero puede ser despertada si llega alguna señal no blo­ queada que no ignore. - TASK_UNINTERRUPT I BLE (No interrumpible). La tarea se encuentra dormida a la espera de que se produzca un evento, pero solo será despertada si se produce el evento por el que espera. Una tarea en este estado no puede ser despertada si llega alguna señal no bloqueada que no ignore. - TASK_STOPPED (Parada). La ejecución de la tarea ha sido detenida por otra tarea, por ejem­ plo un depurador de código. - TASK_TRACED (Rastreada). Es un caso especial del estado anterior. Este estado se utiliza exclusivamente para aquellas tareas cuya ejecución ha sido detenida porque está siendo ras­ treada mediante la llamada al sistema ptrace invocada por otra tarea. - EXI T_ZOMB I E (Zombi). La tarea ha terminado su ejecución pero el núcleo todavía no ha borrado el contenido de su estructura task_s tru c t ya que el padre todavía no ha invocado una llamada al sistema wai tp i d o wai t4 para obtener el estatus de salida de su hijo. Nótese que si el padre nunca invoca a esta llamada al sistema el núcleo no eliminará esta estructura hasta que el sistema no sea reiniciado. S OBUNIX : el sistema operativo Linux 235 - EXI T_DEAD (Muerta). La tarea ha terminado su ejecución y su proceso padre ya ha invocado una llamada al sistema wa i tp i d o wa i t 4 para obtener el estatus de salida de su hijo. El núcleo procederá a la eliminación de la estructura t a s k_s truc t si no existen más tareas que hayan invocado una llamada al sistema del mismo tipo wai tp i d sobre la misma tarea. En la Figura 6. 1 se muestra el diagrama de transición de estados de las tareas en Linux. Por simplificar el diagrama se ha omitido el estado rastreada. Terminación Creación Tratamiento de una llamada tipo wa it invocada por el padre un evento Figura 6.1 - Diagrama de transición de estados de las tareas en Linux • Parámetros de planificación. Entre ellos la prioridad de la tarea, el tiempo de ejecución y el tiempo promedio que duerme una tarea esperando por un evento. Como se explicará en la sección 6.4 el planificador utiliza esta información para decidir que tarea será planificada para ejecución en un determinado procesador del computador. • Identificadores. Cada tarea tiene asociada un identificador de tarea (task identifier, TID), también denominado identificador del hilo (thread Identifier), que la identifica de manera única. Este iden­ tificador se almacena en un campo de la estructura t a s k_s truc t . Contrariamente a lo que se podría pensar este campo no se denomina t i d sino p i d. Esto es así porque en sus orígenes Linux únicamente soportaba procesos monohilos y en dicho caso el valor del TID es igual al PID del proceso. En el caso de procesos multihilos cada hilo constituye una tarea y tiene asociado un TID diferente. Además cada tarea tiene asignado un identificador de grupo de tareas (Task Group IDentifier, 236 Ampliación de sistemas operativos TGID), también denominado identificador de grupo de hilos, el cual se almacena en el campo t g i d de la estructura ta s k_s t ruc t . Todas las tareas pertenecientes a un mismo grupo de tareas tienen asignado el mismo TGID, el cual coincide con el TID de la tarea líder del grupo, que es la tarea principal. El PID de un proceso multihilo en Linux coincide con el valor del TGID de sus hilos. • Información genealógica. Como por ejemplo: un puntero a la estructura t a s k_s truc t de su proceso padre, la cabecera de la lista doblemente enlazada de sus procesos hijos, y punteros para implementar la lista doblemente enlazada de sus procesos hermanos. • Localización del espacio de direcciones virtuales de la tarea. • Credenciales. Como los identificadores de usuario (UID y EUID), los identificadores de grupo (GID y EGID) y el identificador de sesión (SID). • Contexto hardware salvado. Permite continuar con la ejecución de la tarea en modo usuario tras retornar de la ejecución en modo núcleo de una llamada al sistema o de una interrupción. • Puntero a la estructura thread_i n f o . • Información relativa a l sistema de archivos. Como u n puntero a l a tabla de descriptores d e archivos abiertos y punteros al nodo-i del directorio de trabajo actual y al nodo-i del directorio raíz. • Información asociada al tratamiento de señales. Como las máscaras de señales ignoradas, blo­ queadas y pendientes. Así como las acciones establecidas para cada tipo de señal y las direcciones de los manejadores de las señales que son capturadas. • Información contable sobre uso de recursos. Como el tiempo ejecutado en modo usuario o en modo núcleo. También límites máximos a los recursos que puede utilizar: número máximo de archivos abiertos, tiempo máximo de uso del procesador, tamaño máximo de su pila o de su segmento de datos, número máximo de marcos de página que puede consumir, etc. El núcleo enlaza todas las estructuras ta s k_s truc t existentes en una lista que se denomina lista de procesos. Este nombre deriva de las primeras versiones de Linux donde únicamente se soportaban procesos monohilos y en consecuencia las tareas eran procesos. En las versiones actuales con soporte de procesos multihilos este nombre puede llevar a confusión ya que una tarea puede ser un proceso monohilo, un hilo de usuario (proceso ligero) o un hilo del núcleo. Un nombre más apropiado sería lista de tareas. Por otra parte señalar la analogía existente entre la estructura task_s truc t y la estructura proc descrita en la sección 2.2.2. e Ejemplo 6. 1 Considérense el archivo ejecutable prog 6 1 cuyo código fuente en C se muestra en la Figura 6.2. Se observa que el programa hace uso de funciones de la librería de hilos Pthreads (ver sección 2.3 .4) para implementar dos hilos de usuario. La función de cada hilo es mostrar en pantalla el valor de su PID y SOBUNIX: el sistema operativo Linux 237 # i nc lude < s td i o . h> # i nc l ude <pthread . h> # i nc lude < sys / sys c a l l . h> vo i d * funl ( vo i d * i d ) ; vo i d mo s t rar_i n f o ( char e ) ; ma i n ( ) { / * Código de l h i l o 1 ( pr i nc ipa l ) * / p thread_t hu l ; p thread_c r e a t e ( &hu l , NULL , * funl , NULL ) ; mo s t rar_i n f o ( ' l ' ) ; p thread_j o i n ( hu l , NULL ) ; p r i n t f ( " \ nH i l o 2 t e rm i nado " ) ; for ( ; ; ) ; vo i d * funl ( vo i d * i d ) / * C ó d i go del hi l o 2 * / mo s t rar_i n f o ( ' 2 ' ) ; for ( ; ; ) ; vo i d mo s t rar_i n f o ( char e ) { l ong i n t u , v ; u = g e tp i d ( ) ; v= ( l ong i n t ) sys c a l l ( SYS_g e t t i d ) ; p r i n t f ( " \ nH i l o %:::: : P I D= % l d Figura 6.2 - T I D= % l d \ n " , c , u , v ) ; Código C del programa prog 6 1 del Ejemplo 6 . 1 TID asociado. Para obtener el PID se invoca a la llamada al sistema g e t p i d . Realmente esta llamada en Linux devuelve el TGID del hilo que la invoca, recuérdese que PID=TGID. Por otra parte para obtener el TID se invoca indirectamente a la llamada al sistema g e t t i d a través de la llamada al sistema sys c a l l ya que no existe definida en 1 ibc una función de envoltura para esta llamada al sistema. Supóngase que prog 6 1 es invocado en segundo plano desde la línea de órdenes de un intérprete de comandos en Linux. En la pantalla aparecería una salida parecida a la siguiente: H i l o 1 : P I D= l 2 6 7 4 T I D= l 2 6 7 4 H i l o 2 : P I D= l 2 6 7 4 T I D= l 2 6 7 5 S e observa que el proceso asociado al programa tiene asignado un PID= 1 2674, luego su TGID = 1 2674. Este proceso consta de dos hilos : el hilo principal (hilo 1) cuyo TID= 1 2674 y el hilo 2 cuyo TID = 1 2675 . 238 Ampliación de sistemas operativos Esta información también se puede obtener usando el comando ps - L , que mostraría una salida similar a la siguiente: PID 10838 12674 12674 12 678 LWP 10838 12674 12675 12 678 TTY pt s / 1 pt s / 1 pts / 1 pts / 1 T IME 00 : 00 00 : 00 00 : 00 00 : 00 : : : : 00 00 00 00 CMD bash prog 6 1 prog 6 1 ps El valor del TID aparece en la columna LWP. • Pila en modo núcleo y estructura thread_info El núcleo en el momento de la creación de una tarea aparte de una estructura task_s truc t le asigna también, entre otras, las siguientes estructuras de datos: • Pila en modo núcleo. También denominada pila del núcleo. Es una estructura que contiene los marcos de pila de las funciones invocadas por el núcleo durante la ejecución de la tarea en modo núcleo. Está pila está vacía cuando la tarea se ejecuta en modo usuario. • Estructura thread_i n f o . Contiene la dirección de la estructura task_s t ruc t de la tarea así como otros parámetros de la misma. Estas dos estructuras son creadas conjuntamente por el núcleo en una misma región que ocupa una o dos páginas contiguas del espacio de direcciones virtuales del núcleo (ver Figura 6.3). La estructura thread_in f o se localiza siempre en las direcciones virtuales más baj as de las páginas asignadas. Esta disposición facilita al núcleo la localización de la estructura t a s k_s truc t de la tarea actualmente en ejecución en un determinado procesador. Simplemente necesita conocer el valor del registro puntero de pila del procesador. A partir del mismo puede determinar la página donde se ubica la estructura thread_in f o , la cual apunta a la estructura ta s k_s t ruc t . e Ejemplo 6.2 En la Figura 6.3 se muestra un diagrama en el que se representa la pila del núcleo y las estructuras t a s k_s t ruc t y thread_in f o de los hilos del proceso del Ejemplo 6. 1 . Se observa que las estructuras t a s k_s t ruc t se encuentran enlazadas formando parte de la lista de procesos (lista de tareas) del núcleo. Cada estructura task_s truc t tiene un puntero a su estructura thread_info asociada, la cual a su vez también tiene un puntero recíproco. La pila del núcleo de cada hilo se implementa en una página contigua a la página donde se localiza su estructura thread_in f o lo que facilita la localización de dicha estructura a partir del puntero de pila. • SOBUNIX: el sistema operativo Linux 239 � - - - - - - - - - - - - - - - , 1 1 1 1 1 � 1 1 1 1 1 i Pila del núcleo TID= l 2674 Puntero de pila ----. t a s k- s t ru c t 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 • 1 o 2 páginas contiguas thread- i n f o Pila del núcleo TID= 1 2675 Puntero de pila ___. t a s k s t ru c t � i 1 1 1 1 L--------------- 1 1 1 1 1 1 1 1 • thread- i n f o Lista de procesos (Lista de tareas) Figura 6.3 6.3.2. - Relación existente entre las estructuras del núcleo asignadas a una tarea Creación de procesos e hilos en Linux La creación de procesos en Linux, al igual que en otros SOBUNIX, se puede realizar mediante las llamadas al sistema fork y v f o rk. Además Linux dispone de la llamada al sistema c l one para crear un nuevo proceso ligero asociado al proceso invocador de la llamada. Esta llamada es bastante potente y flexible ya que permite especificar los recursos asociados al proceso creador que se desea que sean accedidos por el proceso ligero que es creado. Esta llamada también permite crear un nuevo proceso de la misma forma que las llamadas fork y vf ork. La llamada al sistema c l one tiene la siguiente sintaxis: r = c l one ( fun , u_p i l a , i nd , arg ) ; Donde fun es la función que comenzará a ejecutar el proceso ligero creado cuando sea planificado para ejecución. Dicha función recibe como argumentos los especificados en arg. El argumento u_p i l a es l a dirección virtual de comienzo de l a región de pila en modo usuario del hijo. El argumento ind es un mapa de bits que permite especificar los recursos del proceso padre que serán compartidos por el proceso hijo. Cada bit de la máscara tiene asignado una constante simbólica, por ejemplo: 240 Ampliación de sistemas operativos • CLONE_THREAD. Si este bit está activado se crea un nuevo proceso ligero (hilo de usuario). En caso contrario se crea un nuevo proceso monohilo. • CLONE_VM. Si este bit está activado entonces el proceso hijo comparte el mismo espacio de direc­ ciones virtuales del proceso invocador, excepto la región de pila. • CLONE_PARENT. Si este bit está activado el proceso hijo tendrá como proceso padre al mismo del proceso invocador. En caso contrario el proceso invocador será el padre del proceso creado. • C LONE_F I L E S . Si este bit está activado el proceso hij o comparte los descriptores de archivos del proceso padre. En caso contrario el proceso hijo recibe su copia independiente de los descriptores de archivos. • CLONE_S I GHAND. Si este bit está activado el proceso hij o comparte los manipuladores de señales del proceso padre. Los posibles cambios que se hagan en estos manipuladores son visualizados tanto por el padre como por el hijo. En caso contrario el proceso hij o recibe su copia independiente de los manipuladores de señales y los cambios que realice en ellos no son visualizados por el padre. Si la llamada se ejecuta con éxito en r se almacena el TID del proceso (o proceso ligero) hijo creado. En caso de error se almacena el valor - l . e Ejemplo 6.3 En la Figura 6.4 se muestra el código C del archivo ejecutable prog 6 2 cuya funcionalidad es parecida a la del programa prog 6 1 del Ejemplo 6. 1 . La principal diferencia es que ahora se utiliza la llamada al sistema c l one para crear un nuevo hilo ( CLONE_THREAD ) en vez de usar una función de la librería Pthreads. El hilo 2 creado comparte el espacio de direcciones virtuales ( CLONE_VM) y los manipuladores de señales ( CLONE_S I GHAND) del hilo invocador (hilo 1 ) . • El núcleo implementa el tratamiento de las llamadas c l one, f o r k y v f ork a través de la función del núcleo do_fork ( ) . La cual utiliza a su vez a la función c opy_pro c e s s ( ) para configurar el descriptor del proceso (estructura task_s t ruc t ) y otras estructuras de datos del núcleo asociadas al nuevo proceso creado. 6.3.3. Hilos del núcleo en Linux En Linux los hilos del núcleo son asociados a la ejecución de procesos del sistema, que son procesos iniciados por el propio núcleo que se encargan de realizar diferentes tareas administrativas como por ejemplo: búsqueda de marcos de página libres, intercambio de procesos, copia de buffers modificados a memoria secundaria, etc. A diferencia de otras tareas los hilos del núcleo únicamente se ejecutan en modo núcleo y se crean con la función del núcleo kerne l_ thread ( ) . Esta función para implementar la creación del hilo del núcleo también invoca a la función do_fork ( ) . Dependiendo del proceso del sistema al que esté asociado, un hilo del núcleo puede ser ejecutado cuando se produce un determinado evento o periódicamente. SOBUNIX: el sistema operativo Linux 241 # i ne lude < s t d i o . h> # i n e l ude <pthread . h> # i ne l ude < sys / sys e a l l . h> # i ne l ude <ma l l o e . h> # i ne lude < s ehed . h> vo i d funl ( ) ; v o i d mo s t rar_i n f o ( ehar e ) ; vo i d ma i n ( ) { int r ; uns i gned ehar p i l a_h i j o [ 4 0 9 6 ] ; r = e l one ( funl , p i l a_h i j o + 2 0 4 8 , CLONE_THREAD [ CLONE _VM [ CLONE_S I GHAND , NULL ) ; if ( r= = - 1 ) { p r i n t f ( " \ nError en la l l amada e l on e \ n " ) ; mo s t rar_i n f o ( ' l ' ) ; for ( ; ; ) ; v o i d funl ( ) mo s t rar_i n f o ( ' 2 ' ) ; for ( ; ; ) ; vo i d mo s t rar_i n f o ( ehar e ) l ong i n t u , v ; u=getpid ( ) ; v= ( l ong int ) sys e a l l ( SYS _ge t t i d ) ; p r i n t f ( " \ nH i l o �: P I D= % l d Figura 6.4 - 6.4. T I D= % l d \ n " , e , u , v ) ; Código C del programa prog 6 2 del Ejemplo 6.3 Planificación en Linux Linux es un sistema operativo de núcleo expropiable que utiliza como unidad de planificación a las tareas. El núcleo asigna a cada tarea una prioridad que es un número entero positivo p comprendido dentro del rango de valores [0, 1 39], siendo O la prioridad más alta posible y 1 39 la más baj a (ver Figura 6.5). El núcleo siempre planifica a la tarea en el estado ejecutándose de mayor prioridad (menor valor de p ) . Dicha tarea será expropiada si entra en el estado ejecutándose una tarea de mayor prioridad. A las tareas con prioridad p comprendida en el rango [ 1 00, 1 39] se les considera tareas convencio­ nales o tareas de tiempo compartido. A las tareas con prioridad p comprendida en el rango [0, 99] se 242 Ampliación de sistemas operativos Prioridad más alta o ,_ - 99 1 00 1- 1 1 1 1 1 Prioridad más baj a Figura 6.5 - 1 39 Tareas de tiempo real r- Tareas convencionales - Escala de prioridades de ejecución de las tareas considerada por el núcleo de Linux les considera tareas de tiempo real. Conviene señalar que el término tarea de tiempo real no debe ser entendido en el sentido usual, es decir, una tarea que debe ser ejecutada en unos plazos determinados, sino que se refiere al significado dado en las especificaciones POSIX 1 003 .4, es decir, una tarea con una prioridad más alta que las tareas convencionales. En el momento de su creación, la prioridad de una tarea es configurada al mismo valor que la priori­ dad de su padre. Posteriormente, si se trata de una tarea convencional, la prioridad de la tarea puede ser cambiada utilizando las llamadas al sistema ni ce o s e tpr i or i ty. En el caso de una tarea de tiempo real se pueden usar las llamadas s ched_se tparam o s ched_s e t s chedu l e r . 6.4.1. Planificador usado en las versiones del núcleo de Linux anteriores a la 2.6.23 El planificador implementado en las primeras versiones de Linux no estaba bien escalado ya que el tiempo empleado en seleccionar la tarea de mayor prioridad dependía del número de tareas en el estado ejecutándose y del número de procesadores existentes en el computador. Durante el desarrollo de la versión 2.5 de Linux el planificador fue rediseñado por completo con objeto de asegurar que el tiempo de selección de la tarea de mayor prioridad fuese independiente del número de tareas en el estado ejecutándose y del número de procesadores. A dicho planificador se le 3 conoce con el nombre de planificador 0 ( 1 ) . 3 La notación 0( ) se utiliza para designar la complejidad de un algoritmo matemático [Cormen et al. , 2009] . SOBUNIX: el sistema operativo Linux 243 Estructuras de datos utilizadas en su implementación El núcleo asigna a cada procesador existente en un computador una estructura de datos runqueue (ver Figura 6.6). Esta estructura contiene, en otros, los siguientes campos: • arrays . Este campo contiene un array de dos estructuras p r i o_array_t . Cada una de estas estructuras contiene las cabeceras de 140 colas PIFO de tareas en el estado ejecutándose, una por cada posible valor de la prioridad. También contiene un mapa de bits, con un bit asociado a cada cola, para saber si una cola está vacía o contiene alguna tarea. Además contiene un contador del número total de tareas existentes en las 140 colas. • a c t ive. Puntero a la estructura pri o_array_t que contiene las colas de tareas activas que son aquellas que consulta el planificador para seleccionar la próxima tarea que será planificada para ejecución en el procesador al que esté asociada la estructura runqueue. El planificador siempre selecciona para ejecutar a la primera tarea de la cola activa de mayor prioridad. • expi red. Puntero a la estructura pri o_array_t que contiene las colas de tareas expiradas que son aquellas en las que ingresan las tareas (excepto si son de tiempo real) cuyo cuanto de ejecución ha expirado en el procesador al que esté asociada la estructura runqueue. Cuando no queda ninguna tarea en las colas de tareas activas entonces el núcleo intercambia los valores de los campos ac t ive y exp i red, de tal forma que las colas de tareas expiradas pasan a ser las colas de tareas activas, y viceversa. Nótese que en un instante de tiempo determinado una tarea en el estado ejecutándose está asociada a una determinada estructura runqueue y solo puede pertenecer a una única cola de tareas activas o expiradas de entre las múltiples colas existentes en la estructura runqueue. Una cola de tareas en el estado ejecutándose asociada a una determinada prioridad se implementa como una lista doblemente enlazada de estructuras t a s k_s t ruc t . En la estructura t a s k_s truc t se almacenan, entre otros, los siguientes parámetros de planificación de la tarea: prioridad, política de planificación, tiempo de uso del procesador y tiempo promedio de espera en el estado dormido. Políticas de planificación Cada tarea tiene asignada una política de planificación que establece el algoritmo de planificación y las reglas para colocar una tarea dentro de una determinada cola de prioridad. Se distinguen tres posibles políticas de planificación4 : • SCHED_F IFO. Esta política utiliza un algoritmo PIFO para planificar a las tareas que tienen la mis­ ma prioridad. Luego las tareas son planificadas por orden de llegada y reciben un cuanto infinito. Esta política de planificación solo puede ser utilizada por las tareas de tiempo real. Esta política sigue las siguientes reglas a la hora de colocar a una tarea de tiempo real en una cola de prioridad: 4Se está tomando como base la versión 2.6. 1 1 del núcleo de Linux. 244 Ampliación de sistemas operativos runqueue (asociada a CPU l ) [A] = Colas tareas activas, seleccionables para ser planificadas. [E] = Colas tareas expiradas, solo ingresan aquí las tareas convencionales que han consumido su cuanto. a c t ive exp i red ,___,__,_..-.____..._.-.__� Cola tareas prioridad O Cola tareas prioridad l arrays [ O ) } [A ] Planificador Cola tareas prioridad 1 3 9 Cola tareas prioridad O Cola tareas prioridad l a rrays [ 1 ) }E; ' Cola tareas prioridad 1 3 9 runqueue (asociada a CPU2) a c t ive exp i red Cola tareas prioridad O Cola tareas prioridad l arrays [ O ) �-----�r=-:=;:,_,__r---... Cola tareas prioridad 1 3 9 Cola tareas prioridad O Cola tareas prioridad l a rrays [ 1 ) } [E ] Planificador 4 � } [A ] Cola tareas prioridad l 3 9 Figura 6.6 - Estructuras runqueue de un computador con dos procesadores - Una tarea X de prioridad p que ha sido expropiada por otra tarea Y de mayor prioridad será colocada al principio de la cola de tareas activas asociada a p. Si se invoca a una llamada al sistema s ched_s e t s chedu l e r o s ched_s e tparam para configurar la prioridad p de una tarea, dicha tarea será colocada, si se encuentra en el estado ejecutándose, al principio de la cola de tareas activas asociada a p. Cuando una tarea de prioridad p entra en el estado ejecutándose se coloca al final de la cola de tareas activas asociada a p. Si una tarea cede voluntariamente el uso del procesador mediante la invocación de una llama- SOBUNIX: el sistema operativo Linux 245 da al sistema s ched_yi e l d, la tarea es colocada al final de la cola de tareas activas asociada a p. Una tarea X con política SCHED_F IFO continuará ejecutándose en un procesador excepto si se producen alguno de los siguientes eventos : la tarea X se bloquea en espera de algún evento, entra en el estado ejecutándose una tarea Y de mayor prioridad, o la tarea X invoca a la llamada al sistema s ched_yi e l d para ceder el uso del procesador. • SCHED_RR. Esta política utiliza un algoritmo de turno rotatorio (round robín, RR) para planificar a las tareas que tienen la misma prioridad. La duración del cuanto depende de su prioridad. Esta política de planificación solo puede ser utilizada por las tareas de tiempo real. Las reglas que utiliza esta política para colocar a una tarea de tiempo real en una cola de prioridad son análogas a las utilizadas en la política SCHED_F I F O . Además si una tarea consume su cuanto ésta es colocada al final de la cola de tareas activas asociada a p. Nótese que a diferencia de lo que podría pensarse la tarea no es colocada en la cola de tareas expiradas, el objetivo de esta estrategia es disminuir el tiempo de espera de las tareas de tiempo real. Una tarea X con política SCHED_RR continuará ejecutándose en un procesador excepto si se pro­ ducen alguno de los siguientes eventos: la tarea X consume su cuanto de ejecución, la tarea X se bloquea en espera de algún evento, entra en el estado ejecutándose una tarea Y de mayor prioridad, o la tarea X invoca a la llamada al sistema s ched_yi e l d para ceder el uso del procesador. Si una tarea X con política SCHED_RR es expropiada por otra tarea Y de mayor prioridad, cuando X sea planificada otra vez para ejecución lo hará durante un tiempo igual al tiempo restante de cuanto que le quedaba por ejecutar antes de ser expropiada. • SCHED_NORMAL (también denominada SCHED_OTHER en otras versiones). Es la política que se aplica para planificar a las tareas convencionales. Esta política utiliza un algoritmo de turno rota­ torio para planificar a las tareas que tienen la misma prioridad. La duración del cuanto depende de su prioridad. Esta política sigue reglas similares a las otras políticas a la hora de colocar a una tarea en una cola de prioridad, la principal diferencia es que si una tarea de prioridad p1 consume su cuanto, el núcleo recalcula su prioridad (supóngase que ahora toma el valor pz) y coloca a la tarea al final de la cola de tareas expiradas asociada al nuevo valor de prioridad (p2 ). Asignación de la prioridad de una tarea En Linux cada tarea tiene asignada una prioridad estática Pe · Además las tareas convencionales también tienen asignada una prioridad dinámica Pd · El núcleo utiliza Pe para decidir qué tarea de tiempo real planificar y para colocar a una tarea de tiempo real en una determinada cola de prioridad. Por el contrario en el caso de una tarea convencional para realizar estas acciones utiliza Pd · Se cumple, por lo tanto, que p = Pe en el caso de una tarea de tiempo real y p = Pd en el caso de una tarea convencional. 246 Ampliación de sistemas operativos Si una tarea tiene asociada una política de planificación SCHED_RR o SCHED_NORMAL el núcleo utiliza la prioridad estática de la tarea para determinar la duración de su cuanto q (en milisegundos) de acuerdo con la siguiente fórmula [Bovet y Cesati, 2005] : q = { ( 1 40 - P e) · 20 ( 1 40 - P e) · 5 si P e si P e < � 1 20 1 20 (6. 1 ) Se observa que e l cuanto asignado a una tarea aumenta conforme mayor e s s u prioridad estática, es decir, más bajo es el valor de P e · Por ejemplo, para P e = 1 3 9 se obtiene q = 5 ms. Mientras que para P e = 1 00 se obtiene q = 800 ms. El máximo valor del cuanto es q = 2800 ms y se obtiene para la prioridad estática más alta P e = O. En el caso de una tarea de tiempo real el valor de su prioridad estática se encuentra dentro del rango [0, 99] y puede ser configurado, por ejemplo, mediante la llamada al sistema shed_s e tparam. En el caso de una tarea convencional su prioridad estática se encuentra dentro del rango [ 1 00, 1 39] y puede ser configurado mediante las llamadas al sistema ni c e o s e tp r i o r i ty. Estas llamadas trabaj an con el valor amable NI de una tarea que es un valor comprendido en el rango [ -20, 1 9] . La relación entre la prioridad estática y el valor amable es la siguiente: P e = NI + 1 20 (6.2) Por defecto NI= O. Conviene tener presente que únicamente el superusuario puede establecer valores negativos de NI, es decir, aumentar la prioridad estática de una tarea. Para visualizar el valor amable de una cierta tarea se puede usar el comando top. e Ejemplo 6.4 Supóngase el programa prog 6 1 del Ejemplo 6. 1 . Supóngase que una vez lanzado en segundo plano se introduce la orden t op -n4 - p 1 2 6 7 5 que muestra en pantalla diferente información sobre la tarea con TID = 1 2675 . La información se actualiza cuatro veces (opción -n4) antes de que finalice el comando. Supóngase que la última actualización que se muestra en la pantalla es la siguiente5 : P I D USER 1 2 6 7 5 j o s ema PR 20 NI o VIRT 10132 RES 280 SHR S % C PU %MEM 216 S 0 . 0 0 . 1 TIME+ COMMAND 0 : 0 0 . 0 0 prog 6 1 S e observa que el valor amable N I es igual a O . Sustituyendo este valor en l a ecuación (6.2) s e obtiene que la prioridad estática de la tarea es P e = O + 1 20 = 1 20, luego se trata de una tarea convencional. Señalar que la columna PR de la salida del comando t op también contiene información sobre la prioridad estática P e de la tarea. Ambos valores se relaciona a través de la expresión: P e = PR + 100 5 Por cuestiones de espacio se ha omitido parte de la información que realmente aparecería en la pantalla. (6.3) SOBUNIX: el sistema operativo Linux 247 Para este ejemplo se observa en la salida que PR=20, luego sustituyendo este valor en (6.3) se obtiene nuevamente que P e = 20 + 1 00 = 1 20. Igualando las ecuaciones (6.2) y (6.3) se puede obtener la siguiente relación entre PR y NI : NI = PR - 20 (6.4) Se comprueba que para PR=20 se obtiene el valor NI=O. • La prioridad dinámica de una tarea convencional es recalculada por el núcleo cuando la tarea con­ sume su cuanto. Para calcularla utiliza la siguiente expresión [Bovet y Cesati, 2005] : Pd = máx { 1 00, mín { Pe - b + 5 , 1 39} } (6.5) En la expresión anterior b es un factor denominado bonus, que puede tomar valores enteros dentro del rango [0, 1 0] . Si b < 5 entonces se penaliza a la tarea ya que se reduce su prioridad dinámica. Por el contrario si b > 5 se gratifica a la tarea ya que se aumenta su prioridad dinámica. El valor del bonus se fija en función del tiempo promedio de espera en el estado dormido a través de un conjunto de reglas heurísticas. El tiempo promedio de espera se incrementa cuando una tarea despierta y se reduce cuando la tarea consume su cuanto o es expropiada. Esta forma de calcular la prioridad dinámica favorece a las tareas limitadas por E/S frente a las tareas limitadas por CPU. Luego el planificador 0( 1 ) consigue que las aplicaciones interactivas tengan un mej or tiempo de respuesta que el que tenían con el planificador usado en las primeras versiones del núcleo de Linux. Otra funcionalidad reseñable del planificador 0( 1 ) es que monitoriza la carga de trabajo existen­ te en cada procesador y si lo considera necesario distribuye las tareas entre las estructuras runqueue existentes para equilibrar la carga de trabajo de los procesadores. Una de las principales fuentes de crítica del planificador 0( 1 ) es la utilización de reglas heurísticas para establecer el valor de diferentes parámetros de planificación, como por ejemplo, el valor del bonus. 6.4.2. Planificador utilizado a partir de la versión 2.6.23 del núcleo de Linux El planificador 0( 1 ) ha sido el planificador utilizado en Linux hasta la versión 2.6.23 donde ha vuelto a ser rediseñado. El planificador introducido en esta versión presenta un diseño mucho más modular basado en la existencia de un planificador central y clases de planificación. Cada clase de planificación encapsula los detalles de las políticas de planificación que soporta y dispone de un planificador independiente. Una clase de planificación se implementa mediante una es­ tructura de tipo s ched_c l a s s la cual contiene punteros a las funciones que implementan al planificador de la clase. Dichas funciones deben ser invocadas por el planificador central cuando se producen even­ tos relativos a la planificación que afectan a una tarea perteneciente a dicha clase, como por ejemplo: la selección de la próxima tarea perteneciente a la clase que puede ser planificada y la colocación o elimina­ ción de una tarea en la estructura de datos (cola o árbol) que mantiene las tareas en el estado ejecutándose perteneciente a dicha clase. 248 Ampliación de sistemas operativos Existen dos clases de planificación: • • Clase de tiempo real (shed_rt . e ) . A esta clase pertenecen las tareas de tiempo real. El planifica­ dor de esta clase implementa las políticas de planificación SCHED_F IFO y SCHED_RR. A diferencia del planificador 0( 1 ) la duración del cuanto de las tareas de tiempo real planificadas con política SCHED_RR es independiente de su prioridad e igual a 1 00 ms. Clase completamente justa (shed_f a i r . e ) . A esta clase pertenecen las tareas convencionales. El planificador de esta clase implementa las políticas de planificación SCHED_NORMAL, SCHED_IDLE y SCHED_BATCH. La política SCHED_BATCH fue introducida en la versión 2.6. 1 6 para dar soporte a tareas conven­ cionales de tipo batch que hacen un uso intensivo del procesador. Una tarea perteneciente a esta política de planificación nunca puede expropiar a otra tarea. Por lo tanto estas tareas no interfieren con las tareas interactivas de la clase SCHED_NORMAL . La política SCHED_IDLE, disponible desde la versión 2.6.23, se utiliza para dar soporte a tareas que pueden ser ejecutadas con la prioridad más baja. Cada tarea pertenece a una determinada clase planificación y será tratada por el planificador de la clase a la que pertenece de acuerdo con la política de planificación que tenga asignada. El planificador central (s chedu l e) se encarga de seleccionar a la próxima tarea que será ejecutada en cada procesador. Para realizar esta operación interactúa con los planificadores de cada clase invocando las funciones apropiadas. En primer lugar invoca al planificador de la clase de tiempo real para que seleccione una tarea de tiempo real para ser ejecutada. Si no existe ninguna tarea de tiempo real en el estado ejecutándose entonces invoca al planificador de la clase completamente justa para que seleccione una tarea convencional. De esta forma, el planificador central implementa un algoritmo de planificación basado en priorida­ des, ya que siempre selecciona para ser ejecutada en un procesador a la tarea de mayor prioridad asociada a dicho procesador. Aparte de su diseño modular otra de las principales novedades del planificador de la versión 2.6.23 es que el algoritmo de turno rotatorio que se utilizaba en la política SCHED_NORMAL ha sido sustituido por el algoritmo de planificación completamente justa (Completely Fair Scheduling, CFS). El algoritmo CFS también se utiliza para las políticas SCHED_BATCH y SCHED_IDLE. Estas tres políticas son imple­ mentadas por el planificador de la clase completamente justa, también denominado planificador CFS . El algoritmo CFS tiene como objetivo principal el realizar un reparto justo del tiempo de uso de un procesador entre las tareas convencionales asociadas a dicho procesador. Para ello simula la existencia de un computador ideal. En un computador ideal si se tuvieran que ejecutar N tareas en paralelo con idéntico tiempo de ser­ vicio cada tarea recibiría 1 /N de la potencia de calculo del computador y todas terminarían de ejecutarse en el mismo instante. Por ejemplo, si se tuviesen que ejecutar dos tareas en paralelo con un tiempo de servicio requerido de 5 s, en un computador ideal cada tarea recibiría el 50 % de la potencia de cálculo del computador con lo que tardaría en completarse 1 O s. Además ambas tareas finalizarán a la vez y nunca tendrían que esperar por el uso de un procesador. SOBUNIX: el sistema operativo Linux rq 249 (asociada a CPU ! ) .....----.....--.___. Cola tareas prioridad O Cola tareas prioridad 1 rt Cola tareas prioridad 99 Árbol roj o-negro de tareas convencionales cfs Figura 6.7 - Diagrama de la estructura rq asociada a un procesador En un computador real con un único procesador en un determinado instante de tiempo solo puede es­ tar ejecutándose una única tarea. La multitarea se simula conmutando rápidamente el uso del procesador entre todas las tareas existentes . La tarea que se ejecuta en el procesador está siendo favorecida frente al resto de las tareas que esperan para usarlo, las cuales son tratadas injustamente. El grado de injusticia es directamente proporcional al tiempo de espera para usar el procesador. Obviamente conforme más procesadores tenga el computador, el grado de injusticia se reduce pero no se elimina. El algoritmo CFS utiliza como parámetro de planificación el tiempo de ejecución virtual de una tarea que especifica cuando debería comenzar a ejecutarse dicha tarea si se dispusiera de un computador ideal. El tiempo de ejecución virtual se almacena en la estructura t a s k_s truc t , en concreto en el campo vrunt ime de la estructura s e . Cuanto mayor es la prioridad de la tarea menor es su vrunt ime . El planificador CFS utiliza un árbol rojo-negro 6 para ordenar a todas las tareas convencionales aso­ ciadas a un determinado procesador. Las tareas son ordenadas en el árbol en función de su vrunt ime . En el extremo del árbol situado más a la izquierda siempre se sitúa la tarea con menor valor de vrunt ime , que será la tarea convencional que será escogida para ser planificada sino existen pendientes tareas de tiempo real. Conforme se va ejecutando una tarea su tiempo de ejecución se añade a su vrun t ime que de esta forma se va incrementando. Como resultado la tarea se va moviendo en el árbol hacia la derecha. O visto de otra forma, las tareas que esperan para ejecutarse se van desplazando en el árbol hacia la izquierda. Una tarea X se ejecutará hasta que otra tarea Y se coloque en el extremo más a la izquierda del árbol. En dicho caso, se expropia el uso del procesador a X y se planifica para ejecución a Y. El planificador CFS también permite trabajar a nivel de grupos de tareas con lo que es posible distri­ buir el tiempo de uso del procesador de manera justa entre los grupos de tareas existentes. Otra novedad del planificador de la versión 2.6.23 se encuentra en la estructura runqueue, ahora denominada estructura rq, que el núcleo asociada a cada procesador existente. En esta estructura los 6Es un árbol binario de búsqueda equilibrado [Cormen et al. , 2009] . 250 Ampliación de sistemas operativos campos a c t ive, expi red y arrays han sido sustituidos por (ver Figura 6.7): • c f s . Es una estructura de tipo c f s_rq que permite implementar el árbol rojo-negro en que se organizan las tareas en el estado ejecutándose pertenecientes a la clase justa completamente. • r t . Es una estructura de tipo r t_rq que permite implementar las 100 colas FIFO, una por cada nivel de prioridad en el rango [0, 99] , en que se organizan las tareas en el estado ejecutándose pertenecientes a la clase de tiempo real. 6.5. Gestión de memoria en Linux 6.5.1. Gestión del espacio de direcciones virtuales En Linux el espacio de direcciones virtuales de un proceso comienza en la dirección O y se extiende hasta la dirección TASK_S I Z E - 1 . El resto de direcciones virtuales desde TASK_S I Z E hasta 232 , en má­ quinas de 32 bits, o hasta 264 , en máquinas de 64 bits, están reservadas para el núcleo. TASK_S I Z E es una constante cuyo valor depende de cada arquitectura. Generalmente el tamaño del espacio de direcciones virtuales de un proceso es tres veces mayor que el tamaño del espacio de direcciones del núcleo. e Ejemplo 6 .5 En la arquitectura Intel x86 de 32 bits el tamaño máximo del espacio de direcciones virtuales es de 4 GiB con páginas de 4 KiB . Los primeros 3 GiB contienen el espacio de direcciones de un proceso mientras que el espacio de direcciones del núcleo ocupa el último GiB . En la Figura 6.8 se muestra la distribución que realiza Linux del espacio de direcciones virtuales de un proceso en esta arquitectura. Las direcciones virtuales comprendidas entre O y el comienzo de la región de código se utilizan para direccionar a los punteros nulos NULL . A continuación de la región de código se ubican la región de datos y el montículo, el cual crece hacia direcciones virtuales crecientes. Los archivos mapeados en memoria comienzan a ubicarse desde la dirección mmap_ba s e hacía direcciones virtuales decrecientes. A continuación existe una región de protección entre la región de pila y la región de archivos mapeados en memoria. La región de pila se ubica en la parte alta de memoria desde la dirección TASK_S I Z E y crece hacía direcciones virtuales decrecientes. Las restantes direcciones están reservadas para el espacio de direcciones del núcleo. • Linux utiliza una estructura tipo vm_area_s t ru c t para almacenar información sobre una determi­ nada región del espacio de direcciones virtuales de un proceso. Entre la información sobre una región almacenada en esta estructura se encuentra la siguiente: permisos de acceso (solo lectura o lectura y escri­ tura), si es compartida o privada, dirección de crecimiento de la región, ubicación de su almacenamiento de respaldo persistente (sistemas de archivos en disco o área de intercambio), etc. Todas las estructuras vm_area_s t ruc t asociadas a las regiones de un mismo proceso son ordenadas de dos formas distintas : SOBUNIX: el sistema operativo Linux 251 O xFFFFFFFF Espacio de direcciones virtuales del núcleo OxCO O O O O O O TAS K S I Z E Pila 1 1 1 mmap_base 1 1 1 1 1 J • 1 1 Protección Archivos mapeados en meq¡oria + t 1 1 1 1 1 Montículo Datos Código Ox0 8 0 4 8 0 0 0 1 1 1 O L---------------1 Figura 6.8 - Distribución del espacio de direcciones virtuales de un proceso en Linux en una arquitectura lntel x86 de 32 bits • • Lista enlazada de estructuras vm_area_s tru c t . Las estructuras se ordenan en orden ascendente en función de la dirección virtual de comienzo de la región. Á rbol rojo-negro de estructuras vm_area_s t ruc t . Es creado cuando el número de regiones es superior a 32. Las búsquedas en un árbol rojo-negro son más rápidas que en una lista enlazada si el número de elementos de la lista es elevado. El núcleo consulta la lista o el árbol de estructuras vm_area_s t ruc t de un proceso cuando necesita acceder a una dirección virtual determinada, o cuando necesita asignar una nueva región de memoria 252 Ampliación de sistemas operativos virtual de un tamaño predeterminado. Además el núcleo mantiene una estructura tipo mm_s truc t , accesible a través del campo mm de la estructura tas k_s truc t asociada a un proceso, para recopilar toda la información que necesita conocer sobre el espacio de direcciones virtuales de un proceso. En esta estructura se almacena las direcciones virtuales de inicio y final de cada región. Además contiene un puntero mmap al comienzo de la lista de la estructuras vrn_area_s t ru c t asociadas a las regiones del proceso. También contiene un puntero mmap_rb al nodo inicial del árbol roj o-negro de estructuras vrn_area_s truc t (ver Figura 6.9). VM = vm a r e a s t ruct task s t ru c t mm s t ru c t --�----�---, mm ---- Árbol roj o-negro Figura 6.9 6.5.2. - Estructuras de Linux para gestionar el espacio de direcciones virtuales de un proceso Gestión de la memoria física Nodos y zonas En computadores que disponen de múltiples procesadores la memoria principal puede ser organizada de diferentes formas. Entre las organizaciones más utilizadas se encuentran las siguientes : • Organización de acceso a memoria uniforme (Uniform Memory Access, UMA). En este modelo toda la memoria principal se organiza de forma contigua y todos los procesadores del computador tardan lo mismo en acceder a cualquier posición de memoria. • Organización de acceso a memoria no uniforme (Non-Uniform Memory Access, NUMA). La memoria principal se divide entre los procesadores existentes. Un procesador accede más rápido a las posiciones de la memoria principal que tiene asignada, denominada memoria local, que a las posiciones de las memorias locales de los otros procesadores . Para dar soporte, tanto a la organización UMA como a la NUMA, Linux divide la memoria principal en nodos de memoria. El tiempo de acceso a una posición de memoria de un determinado nodo es siempre el mismo. Obviamente en una organización UMA existirá un único nodo de memoria, mientras que en una organización NUMA existirá un nodo de memoria por cada procesador. SOBUNIX: el sistema operativo Linux 253 Por otra parte, Linux considera las particularidades de direccionamiento (tamaño de registros, líneas de direccionamiento para DMA, etc) de cada arquitectura hardware descomponiendo cada nodo de me­ moria en zonas, que son regiones contiguas de memoria principal. En el caso de la arquitectura Intel x86 de 32 bits cada nodo de memoria se descompone en tres zonas: • ZONE_DMA. Corresponde a las direcciones de memoria física por debaj o de 1 6 MiB . Esta zona contiene los marcos de página que son utilizados para la realización de DMA en los antiguos buses ISA. • ZONE_NORMAL . Corresponde a las direcciones de memoria física comprendidas entre 16 MiB y 896 MiB . Esta zona contiene los marcos de página que almacenan las páginas normales, es decir, aquellas asociadas a los 4 primeros GiB del espacio de direcciones virtuales. • ZONE_H IGHMEM. Corresponde a las direcciones de memoria física por encima de 896 MiB . Esta zona contiene los marcos de página que utilizan para almacenar páginas correspondientes a di­ recciones virtuales superiores a los 4 GiB , las cuales en arquitecturas de 32 bits no pueden ser direccionados directamente. Esta zona en arquitecturas de 64 bits está vacía Estructuras de datos utilizadas para gestionar la memoria principal El núcleo numera de forma consecutiva comenzando desde O a todos los marcos de página en que se descompone la memoria principal independientemente del nodo y la zona a la que pertenezcan. En consecuencia no pueden existir dos marcos de página con el mismo número j aunque residan en nodos o zonas diferentes. El núcleo mantiene un descriptor de página por cada marco de página j existente. Cada descriptor de página se implementa mediante una estructura de tipo page de 32 bytes que contiene, entre otros, los siguientes datos sobre un marco: • Indicadores. Es una máscara de bits que se pueden configurar independientemente y que permiten especificar los atributos del marco: bloqueado, referenciado, activo, modificado, privado, etc. • Contador de referencias. Se incrementa cada vez que la página contenida en este marco es referen­ ciada por el núcleo y se decrementa cuando se elimina una referencia. La página puede ser borrada del marco cuando el contador toma el valor O. Si el contador es mayor que O la página no debería ser borrada ya que ello seguramente originará un fallo de página. • Mapeado. Permite localizar el espacio de direcciones virtuales al que pertenece la página cargada en el marco. • Punteros para formar diferentes listas de marcos. En un determinado instante de tiempo un des­ criptor de página estará formando parte de una de las siguientes listas : lista de bloques de marcos contiguos libres de tamaño 2k marcos, lista de marcos de página activos (referenciados reciente­ mente) o lista de marcos de página inactivos (no referenciados recientemente). 254 Ampliación de sistemas operativos Todos los descriptores de página de un mismo nodo se organizan en un array de descriptores de página denominado mem_map. En una organización UMA existe un único array mem_map mientras que en una organización NUMA existe un array mem_map por cada nodo en que se descompone la memoria. El núcleo mantiene también un descriptor de zona por cada zona existente en un nodo. Cada descrip­ tor de zona se implementa mediante una estructura de tipo z one que contiene, entre otros, los siguientes datos sobre una zona: • Número de marcos de página libres ( f r e e_pages). • Array de bloques de marcos de página libres ( f r e e_area). Es utilizado por el asignador a nivel de página del núcleo, el cual implementa el algoritmo buddy. El elemento free_area [ k ] de esta array apunta al descriptor de página del primer marco de página que forma parte de un bloque de marcos de página contiguos libres de tamaño 2k . A su vez este descriptor de página contiene un puntero al descriptor de página del siguiente marco que forma parte de dicho bloque. El descriptor de página del último marco incluido en dicho bloque contiene un puntero al siguiente bloque de marcos de página contiguos libres de tamaño 2k . En definitiva este array implementa las cabeceras de las listas de bloques de marcos contiguos libres de la zona. • Cabecera de la lista de marcos de página activos (ac t i ve_l i s t) . Apunta al descriptor de página del primer marco activo que forma parte de la lista. Esta lista es consultada por el algoritmo de reemplazamiento de páginas que implementa el núcleo de Linux. • Cabecera de lista de marcos de página inactivos (inac t ive_l i s t). Apunta al descriptor de pá­ gina del primer marco inactivo que forma parte de la lista. Esta lista es consultada por el algoritmo de reemplazamiento de páginas que implementa el núcleo de Linux. Finalmente, el núcleo mantiene un descriptor de nodo por cada nodo de memoria existente. Cada descriptor de nodo se implementa mediante una estructura de tipo pg_da ta_t y contiene, entre otros, los siguientes datos : e • Identificador del nodo (node_id) . Es un número entero positivo pequeño que identifica de manera única al nodo de memoria. Puede tomar valores entre O y N - 1 siendo N el número total de nodos existentes. • A rray de los descriptores de zona (node_z one s ) . El tamaño de este array será igual al número de zonas en que se descomponga el nodo. • Puntero (node_mem_map) al array mem_map que contiene los descriptores de páginas del nodo. El tamaño de este array será igual al número de marcos de página contenidos en las zonas en que se descompone el nodo de memoria. Ejemplo 6. 6 En la Figura 6. 1 0 se muestra un diagrama con las estructuras de datos usadas en Linux para gestionar la memoria principal. Se supone la existencia de un único nodo de memoria (organización UMA) con SOBUNIX: el sistema operativo Linux 255 tres zonas de memoria definidas : ZONE_DMA, ZONE_NORMAL y Z ONE_H IGHMEM. El descriptor de nodo contiene un puntero al comienzo del array de descriptores de página mem_map . Además posee un puntero a cada uno de los tres descriptores de zona. Nótese que por simplificar el diagrama únicamente se ha representado el descriptor de la zona Z ONE_NORMAL . El campo f r e e_area [ O ] del descriptor de zona apunta al descriptor de página del primer marco de página que forma parte de un bloque de marcos de página contiguos libres de tamaño 2° = 1 marco. En este ejemplo sería el descriptor de página del marco j = 40. A su vez este descriptor de página contiene un puntero al siguiente descriptor de página del marco que forma parte de esta lista, en este ejemplo el descriptor de página del marco j = 35. También se ha representado el comienzo de la lista de marcos de página activas. El campo a c t i ve_l i s t del descriptor de zona apunta al descriptor de página del primer marco de página que forma parte de esta lista, en este ejemplo el descriptor de página del marco j = 43 . A su vez este descriptor contiene un puntero al siguiente descriptor de página del marco que forma parte de esta lista, en este ejemplo el descriptor de página del marco j = 44. Descriptor de nodo (estructura pg data) node_me m_map - node_zone [ O ] r-- node_zone [ l ] Array d e descriptores de página estructura rnern rnap) Estructura page Estructura page Estructura page node_zone [ 2 ] node i : ----. Descriptor de zona (estructura z one) - f re e__pag e s f re e_area [ O ] Estructura page j =3 5 Estructura page j =4 0 Estructura page Estructura page j=43 j =44 f re e_are a [ l ] fre e_are a [ 2 ] fre e_area [ l O ] ac t ive l i st - inac t ive l i st Zona _______---- ZONE NORMAL - Principales estructuras de datos usadas en Linux para gestionar la memoria principal considerada en el Ejemplo 6.6 Figura 6.10 - • 256 Ampliación de sistemas operativos Traducción de direcciones virtuales Linux soporta tablas de páginas paginadas de hasta un máximo de cuatro niveles de paginación. El número de niveles de paginación que utiliza depende de la arquitectura del computador donde se ejecuta. Por ejemplo en la arquitectura Intel x86 se pueden utilizar hasta dos niveles de paginación. Para implementar los cuatro niveles de paginación, el núcleo utiliza los siguientes tipos de tablas : • Directorio global. Cada proceso tiene asignado un único directorio de páginas global del tamaño de una página. Si el proceso se encuentra activo entonces su directorio de páginas global reside en memoria principal. Cada entrada en el directorio global apunta a una página del directorio superior. • Directorio superior. Puede ocupar varias páginas. Cada entrada en el directorio superior apunta a una página del directorio intermedio. • Directorio intermedio. Puede ocupar varias páginas. Cada entrada en el directorio intermedio apun­ ta a una página que contiene una tabla de páginas. • Tabla de páginas. Puede ocupar varias páginas. Cada entrada de una tabla de páginas hace refe­ rencia a una página i del espacio de direcciones virtuales del procesos. De acuerdo con esta organización de cuatro niveles de paginación una dirección virtual se descom­ pone en cinco campos (ver Figura 6. 1 1 ) cuyos tamaños dependen de cada arquitectura: • • • • • Índice directorio global (IDG). Contiene el número de la entrada del directorio global donde hay que buscar el número m del marco que contiene a la página donde se ubica al directorio superior. Índice directorio superior (IDS). Contiene el número de la entrada del directorio superior don­ de hay que buscar el número l del marco que contiene a la página donde se ubica al directorio intermedio. Índice directorio intermedio (IDI). Contiene el número de la entrada del directorio intermedio donde hay que buscar el número k del marco que contiene a la página donde se ubica la tabla de páginas. Índice tabla de páginas (ITP) . Contiene el número de la entrada de la tabla de páginas donde hay que buscar el número j del marco que contiene a la página i referenciada. Desplazamiento. Contiene el desplazamiento dentro de la página i. Nótese que en el caso de implementar únicamente tres niveles de paginación el tamaño del campo IDS se configura a cero. Para implementar solo dos niveles de paginación el tamaño del directorio superior y del directorio intermedio se fij a a uno, es decir, solo constan de una única entrada. Además las referencias a esos niveles de indirección no utilizados se eliminan en tiempo de compilación por lo que usar un esquema de cuatro niveles de paginación en arquitecturas que solo soportan dos niveles no afecta al rendimiento del sistema. SOBUNIX: el sistema operativo Linux N° ,.... D irv � 1 IDG 1 D;,, 1 257 de página i ....,_ IDS IDI ITP 1 Desplazamiento 1 gistro CR3 � l rF I !Marco m JI Figura 6.1 1 1 Marco l 1� D1rectono supenor D1rectono global Marco k Marco 1 Marco m D irro 4 Dir F3 ! Marco k w D1rectono mtermed10 Marco j 1 Marco} ' $ o Tabla de pagmas Págma 1 - Tablas de páginas de cuatro niveles de paginación utilizadas por Linux Asignación de memoria El subsistema de gestión de memoria para atender las necesidades de memoria de los procesos de usuario y de los restantes subsistemas del núcleo implementa los siguientes asignadores de memoria: • Asignador a nivel de página. Asigna uno o varios marcos de página contiguos utilizando el al­ goritmo budd/ . Este algoritmo agrupa los marcos de página libres en M listas. La lista k con k = O, . . . , M - 1 contiene los grupos de 2k marcos de página contiguos libres. Por ejemplo, supues­ to un tamaño de página de 4 KiB , la lista k = O contiene los bloques libres de memoria de 4 KiB (2° = 1 marco), la lista k = 1 contiene los bloques libres de 8 KiB (2 1 = 2 marcos contiguos), la lista k = 2 contiene bloques libres de 16 KiB (2 2 = 4 marcos contiguos), etc. El funcionamiento del algoritmo buddy es el siguiente: cuando llega una petición de memoria principal contigua de H marcos de páginas contiguos lo redondea por exceso a la potencia 2k más cercana. Si la lista k no está vacía le asigna un bloque, el cual es borrado de la lista. Si la lista k está vacía entonces busca un bloque en la lista k + l. Si dicha lista no está vacía coge el primer bloque de la lista, cuyo tamaño es de 2k + I marcos, y lo divide en dos bloques de 2k marcos. U no de estos bloques se coloca al final de la lista k, el otro se asigna a la petición. 7 Buddy es una palabra inglesa que puede traducirse por amigo o compañero. 258 Ampliación de sistemas operativos Si la lista k + 1 también esta vacía, entonces busca en la lista k + 2. Si dicha lista no está vacía coge el primer bloque de la lista, cuyo tamaño es de 2k + 2 marcos, y lo divide en dos bloques de tamaño 2k+ l marcos. Uno de los bloques se coloca al final de la lista k + 1 , el otro lo divide en dos bloques de tamaño 2k marcos. Uno de estos bloques se coloca en la lista k, el otro se asigna a la petición. Si la lista k + 2 también está vacía entonces busca un bloque en la lista k + 3, repitiéndose la operación descrita, y así sucesivamente hasta encontrar un bloque libre. Si llega a la última lista y está vacía entonces el núcleo envía una señal de error. Cuando se liberan marcos de página, el algoritmo realiza la operación contraria, es decir, intenta unir bloques contiguos de tamaño 2k ubicados en la lista k en bloques de tamaño 2k+ l que coloca en la lista k + l . Si tiene éxito entonces realiza la misma operación sobre los bloques de la lista k + 1 , y así sucesivamente. A las parej as de bloques que consigue unir se les denomina bloques buddies, de ahí proviene el nombre del algoritmo. El algoritmo buddy trabaj a sobre las listas de bloques de marcos contiguos libres de una zona, cu­ yas cabeceras se encuentran en el array free_area del descriptor de zona. Dada una determinada petición de memoria es el núcleo el que determina de que zona se debe asignar el espacio, para ello invocará al algoritmo buddy sobre dicha zona. e • Asignador de la memoria del núcleo. Asigna fragmentos de memoria principal de tamaño menor a una página. Para realizar esta función implementa un asignador slab. • Asignador vma l l o c . Se utiliza para asignar grandes cantidades de espacio virtual del núcleo con­ tiguo que no requiere que esté contiguo en memoria principal, como por ejemplo cuando se cargan dinámicamente módulos del núcleo. Ejemplo 6.7 Supóngase que el núcleo de Linux tiene que atender una petición de memoria principal de tamaño H= 1 marco. Supóngase además que en las listas de bloques de marcos contiguos libres de la zona de donde se realizará la asignación únicamente existe en la lista k = 3 un bloque D 1 de 8 marcos contiguos libres (ver Figura 6 . 1 2a) . El núcleo entonces comienza a aplicar el algoritmo buddy para atender la petición de memoria. En primer lugar el algoritmo determina que la petición de memoria se puede atender con un bloque de tamaño 2° = 1 marco y busca en la lista k = O un marco libre. Como no existe ninguno busca entonces en la lista k = 1 un bloque libre de 2 marcos contiguos, pero esta lista también está vacía. Pasa entonces a buscar en la lista k = 2 un bloque libre de 4 marcos contiguos pero tampoco existe ninguno. A continuación busca en la lista k = 3 un bloque libre de 8 marcos contiguos. Selecciona entonces el bloque D l . Una vez seleccionado el bloque D l lo elimina de la lista k = 3 y lo divide en dos bloques C l y C2 de 4 marcos cada uno (ver Figura 6. 1 2b). A continuación coloca al bloque C2 en la lista k = 2 y al bloque C l lo divide en dos bloques B l y B2 de 2 marcos cada uno (ver Figura 6. 1 2c). Posteriormente coloca al bloque B2 en la lista k = 1 y al bloque B l lo divide en dos bloques A l y A2 de 1 marco cada uno (ver SOBUNIX: el sistema operativo Linux C2 (4) C2 (4) C2 (4) C2 (4) B2 (2) B2 (2) B2 (2) A2 ( l ) Al (l) A2 ( l ) Al (l) (d) (e) 259 D l (8) C l (4) B l (2) (a) (b) (e) Estado del conjunto de 8 marcos contiguos de memoria principal del Ejemplo 6.7 al ir aplicando el algoritmo buddy Figura 6.12 - Figura 6. 1 2d). Finalmente, coloca al marco A2 en la lista k Figura 6. 1 2e). = O, y asigna el marco A 1 a la petición (ver • Reemplazamiento de páginas En Linux el reemplazamiento de páginas es realizado por kswapd, un proceso del sistema. Existe una instancia de este proceso por cada nodo de memoria definido. kswapd se despierta periódicamente y comprueba si el número de marcos de página libres en cada zona de memoria se encuentra dentro de un determinado rango de valores. En caso afirmativo kswapd se vuelve a dormir. En caso negativo selecciona un determinado número de marcos de página para ser incorporados en la lista de marcos libres. El número de páginas que se seleccionan es un parámetro configurable, cuyo valor por defecto es 32. kswapd también puede ser despertado si el núcleo detecta una disminución drástica de memoria. Para seleccionar los marcos de página cuyo contenido puede ser reemplazado Linux utiliza el algo­ ritmo para reclamar marcos de página (Page Frame Reclaiming Algorithm, PFRA) que es una variante del algoritmo del reloj y una aproximación del algoritmo LRU. El algoritmo PFRA trabaj a con la lista de marcos de página activos y con la lista de marcos de página inactivos de cada zona. La lista de marcos de página activos contiene los descriptores de página de los marcos que almacenan páginas que han sido referenciadas recientemente. Por su parte, la lista de marcos de página inactivos contiene los descriptores de página de los marcos que almacenan páginas que llevan algún tiempo sin ser referenciadas. Dentro de cada lista los descriptores de página de los marcos son ordenados mediante una aproximación de la política LRU. Por ello a estas listas también se las denomina de forma global como listas LRU. Para decidir en que lista debe colocar un marco de página el algoritmo PFRA utiliza los indicadores PG_r e f erenced y PG_ac t ive existentes en el campo f l ags de la estructura page asociado a un descriptor de página. Por simplificar la notación se va a denotar a los indicadores PG_r e f erenced y PG_a c t i ve con las letras r y a, respectivamente. 260 Ampliación de sistemas operativos Cada vez que una página i cargada en un marco j es referenciada, el núcleo activa el indicador r del marco, es decir, r = l. Por otra parte, si un marco pertenece a la lista de marcos de página activos entonces a = 1 , mientras que si pertenece a la lista de marcos de página inactivos entonces a = O. Cuando se ejecuta el algoritmo PFRA éste examina el indicador r de los descriptores de página de los marcos de una determina zona. Si r estaba activado (r = 1) entonces lo desactiva (r = 0). Cuando termina de examinarlos, comienza a realizar una segunda ronda. Nótese que durante el tiempo entre la primera y la segunda comprobación de un mismo marco éste ha podido volver a ser referenciado y su indicador r estar activado. Si durante la segunda ronda el algoritmo encuentra un marco j con r = 1 y cuyo bit r hubiera sido desactivado en la primera ronda entonces el algoritmo vuelve a desactivarlo, activa a y coloca al marco en la lista de marcos de página activos . Luego para que un marco j ingrese en la lista de marcos de página activos debe encontrarse con el bit r activado en dos rondas del algoritmo PFRA. Nótese que en función del par de valores (r, a) un marco de página puede encontrarse en alguno de los siguientes estados: • Estado So: (r = O, a = 0). Un marco en este estado no ha sido referenciado recientemente y se encuentra en la lista de marcos de página inactivos. Son los mej ores candidatos para ser seleccio­ nados para reemplazar su contenido, es decir, para ingresar en la lista de marcos libres. • Estado S 1 : (r = 1, a = 0). Un marco en este estado ha sido referenciado recientemente y se encuentra en la lista de marcos de página inactivos . Puede llamar la atención que un marco que contiene una página que ha sido recientemente referenciada se incluya en la lista de marcos de página inactivos, esto se hace para tener en cuenta situaciones en las que un proceso referencia a una página y no vuelve a hacerlo hasta mucho tiempo después . Obviamente dicha página no debe figurar en la lista de marcos de página activos y puede ser sustituida. • Estado S 2 : (r = O, a = 1 ) . Un marco en este estado acaba de ingresar en la lista de marcos de página activos, o ya estaba en la lista de marcos de página activos y no ha sido referenciado recientemente. • Estado S 3 : (r = 1 , a = 1 ) . Un marco en este estado se encuentra en la lista de marcos de página activos y ha sido referenciado recientemente. Los marcos en este estado son los peores candidatos para reemplazar su contenido ya que producirán fallos de página. En definitiva si una página i es frecuentemente referenciada el estado del marco j que la contiene irá pasando por los estados S 0 , S 1 , S 2 y S 3 . Por el contrario si una página dej a de ser referenciada, entonces pasará del estado S 3 al estado S 2 , si se encontraba en la lista de marcos de página activos, o del estado S 1 al estado So, si se encontraba en la lista de marcos de página activos. También en situaciones de memoria libre muy escasa, marcos de la lista de marcos de página activos pueden ser transferidos a la lista de marcos de página inactivos . O visto en términos de estados un marco en el estado S 3 o S 2 puede pasar a los estados S 1 o S 0 , respectivamente. En la Figura 6. 1 3 se muestra el diagrama de estados de un marco de página al aplicar el algoritmo PFRA 8 . 8Conviene señalar que existen otras posibles transiciones que n o han sido comentadas en e l texto ni incluidas en este diagrama con objeto de simplificar su comprensión. SOBUNIX: el sistema operativo Linux 261 Probabilidad de ser reemplazado Alta Baj a r---------------------------------1 Memoria libre muy escasa 1 Memoria libre muy escasa Lista de marcos inactivos Figura 6.13 - Lista de marcos activos Diagrama de estados de un marco de página al aplicar el algoritmo PFRA El algoritmo PFRA selecciona de la lista de marcos de página inactivos de una zona las páginas que pueden ser reemplazadas. Primero escoge a los marcos en el estado So, ya que son los mej ores candidatos para reemplazar su contenido. Cuando éstos se acaban entonces selecciona a los marcos en el estado S 1 . 6.6. Gestión de archivos en Linux La gestión de archivos en Linux es similar a la gestión de archivos de otros SOBUNIX descrita en el capítulo 5 . En las siguientes secciones se describen las particularidades de Linux en relación con la gestión de archivos. En concreto se describe la implementación que realiza Linux de la capa nodo virtual/ sistema de archivos virtual. Además se describen las características de tres de los sistemas de archivos más conocidos desarrollados para Linux: EXT2, EXT3 y EXT4. 6.6.1. Implementación de la capa nodo virtual/sistema de archivos virtual en Linux Linux puede trabaj ar con diferentes tipos de sistemas de archivos : sistemas de archivos desarrollados para Linux (EXT2, EXT3 , EXT4, ReiserFS , . . . ), sistemas de archivos desarrollados para otros SOBUNIX (S5FS , UFS , VERITAS VxFS , . . . ), sistemas de archivos desarrollados para otros sistemas operativos no basados en UNIX (FAT, NTFS , HPFS, HFS , . . . ), sistemas de archivos desarrollados para discos ópticos (IS09660, UDF, . . . ), sistemas de archivos en red (NFS , Coda, AFS , CIFS , NCP, . . . ), y sistemas de archivos especiales, que son aquellos que no consumen espacio de disco, sino que se implementan en la memoria principal, como por ejemplo procfs ubicado en / p r o c . 262 Ampliación de sistemas operativos Para soportar todos estos tipos de sistemas de archivos Linux utiliza la capa nodo virtual/ sistema de archivos virtual. En Linux a esta capa se le denomina capa VFS y se implementa mediante las siguientes estructuras de datos: • Objeto superbloque. Es la implementación que realiza Linux de un sistema de archivos virtual. Es una estructura tipo sup e r_b l o c k que representa a un sistema de archivos activo. El núcleo mantiene una lista enlazada de todos los superbloques existentes en memoria. En el caso de los sistemas de archivos ubicados en disco, un superbloque contiene una copia del bloque de control (superbloque) del sistema de archivos al que está asociado. Además contiene otros datos adiciona­ les, entre ellos, un puntero s_op a un array de punteros a funciones dependientes del sistema de archivos que implementan todas las posibles operaciones sobre un superbloque: leer el superbloque del disco, escribir el superbloque del disco, leer un nodo-i, borrar un nodo-i, etc. • Objeto nodo-i. Es la implementación que realiza Linux de un nodo virtual. Es una estructura tipo inode que representa a un archivo activo. El núcleo mantiene una tabla de nodos-i con todos los nodos-i cargados en memoria. En el caso de los sistemas de archivos ubicados en disco, un nodo-i contiene una copia del nodo-i asociado al archivo en el disco. Además contiene otros datos adicionales, como por ejemplo: un puntero al objeto superbloque asociado al sistema de archivos al que pertenece el nodo-i, un contador de referencias, y un puntero i_op a un array de punteros a funciones dependientes del sistema de archivos que implementan todas las posibles operaciones sobre un nodo-i : crear, buscar, crear un enlace, etc. • Objeto entrada de directorio. Es una estructura tipo dent ry que representa a una entrada de un directorio, es decir, a un componente de un nombre de ruta. Contiene, entre otros, los siguientes datos: contador de referencias, puntero al objeto nodo-i asociado con el componente del nombre de ruta, nombre del archivo, puntero al objeto entrada de directorio del directorio padre, puntero al objeto superbloque asociado al sistema de archivos al que pertenece el archivo, y un puntero d_op a un array de punteros a funciones dependientes del sistema de archivos que implementan todas las posibles operaciones sobre un objeto entrada de directorios: comparar, borrar, liberar, etc. El núcleo utiliza las estructuras dentry para implementar la caché de búsqueda de nombres en directorios ( dent ry_c ache ) . Cada vez que tiene que analizar un nombre de ruta, comprueba si el objeto entrada de directorio del componente que analiza se encuentra en la caché. En caso de acierto se evita tener que acceder al disco para leer el nodo-i correspondiente. En caso de fallo tras leer el nodo-i del disco crea un objeto entrada de directorio asociada a dicho componente que incluye en la caché. Por ejemplo, si el núcleo ha tenido que analizar recientemente el nombre de ruta 1 d i r l 1 archi vo l , entonces existirán en la caché un objeto entrada de directorio para el directorio raíz 1 , otro para el directorio d i r l y otro para el archivo archi vo l . • Objeto archivo abierto. E s una estructura tipo f i l e que contiene l a información necesaria para poder operar con un archivo abierto por un proceso, como por ejemplo: un contador de referencias, el modo de apertura del archivo, el puntero de lectura/escritura, un puntero al obj eto entrada de directorio asociados al archivo, y un puntero f_op a un array de punteros a funciones dependientes SOBUNIX: el sistema operativo Linux 263 del sistema de archivos que implementan todas las posibles operaciones sobre un archivo: leer, escribir, etc. e Ejemplo 6. 8 Supóngase que en un sistema Linux dos procesos A y B abren el mismo archivo. El núcleo crea un objeto de archivo abierto para cada proceso (ver Figura 6. 14). Cada objeto de archivo abierto, contiene un puntero al objeto de entrada de directorio asociada a la entrada del directorio del archivo. Dicho objeto contiene un puntero al objeto nodo-i asociado al archivo y al objeto superbloque asociado al sistema de archivos al que pertenece el archivo. El objeto nodo-i contiene una copia del nodo-i asociado al archivo en el disco, el cual contiene la información necesaria para poder localizar en el disco los bloques de datos del archivo. s t ru c t Objeto nodo-i file s t ru c t s t ru c t dent ry s t ru c t file ,' Obj etos archivos abiertos ! Objeto entrada directorio \ : l' , !' i node ,....----l�--, s t ru c t ! l B loques O O de datos O del archivo sup e r_b l o c k :, l l :' Partición de disco duro sistema de archivos Objeto superbloque ¡ •---------------------------------------------------------------------------------------------· Estructuras en memoria principal Figura 6.14 - Estructuras de datos del Ejemplo 6.8 que implementan la capa VFS • 6.6.2. El sistema de archivos EXT2 La primera versión del núcleo de Linux utilizaba como sistema de archivos principal una adaptación directa del sistema de archivos usado en MINIX. Este sistema de archivos tenía serias limitaciones en relación al tamaño máximo de un archivo y al número de caracteres que podía tener un nombre. Por este motivo en 1 992 se desarrolló otro sistema de archivos para sustituirlo: el sistema de archivos extendido (EXTended filesystem, EXT). Sin embargo, el rendimiento y la funcionalidad del sistema EXT todavía estaba lejos de las de otros sistemas de archivos comerciales. Por ello no tardó en ser mej orado y al año siguiente se presentó el segundo sistema de archivos extendido (Second EXTended filesystem, EXT2) que estaba basado en el sistema de archivos FFS de BSD. 264 Ampliación de sistemas operativos Estructura de un sistema de archivos EXT2 Un sistema de archivos EXT2 cuando se crea en una partición de disco presenta una estructura similar a la que se muestra en la Figura 6 . 1 5 . El primer bloque de la partición es el bloque de arranque que contiene el código necesario para arrancar Linux si dicha partición se utiliza como partición activa. El resto de la partición se divide en NG grupos de bloques. El tamaño máximo de un grupo es igual a 8 · SB bloques, donde SB = 1 , 2 o 4 KiB es el tamaño de bloque, el cual se fija en el momento de la creación del sistema. En cada grupo de bloques se distinguen los siguientes componentes : • Copia del superbloque. El superbloque ocupa un bloque y contiene información estadística y ad­ ministrativa del sistema de archivos: número mágico que identifica al sistema como de tipo EXT2, tamaño de un bloque de datos, número total de bloques y de nodos-i, número de bloques y de nodos-i libres, fecha y hora de la última modificación del sistema de archivos, hora y fecha del montaje del sistema, etc. Puesto que la información contenida en el superbloque es crítica para el funcionamiento del sis­ tema el superbloque se duplica en cada grupo de bloques en previsión de posibles errores o de la corrupción del sistema. El núcleo normalmente trabaj a con el superbloque del primer grupo de bloques. • Copia de la tabla de descriptores de grupo. Un descriptor de grupo es una estructura que datos que describe a un grupo de bloques. Contiene la siguiente información sobre un grupo: la dirección del bloque que contiene el mapa de bits de bloques de datos del grupo, la dirección del bloque que contiene el mapa de bits de nodos-i del grupo, la dirección del bloque que contiene la tabla de nodos-i del grupo, contador de bloques libres, contador de nodos-i libres y contador de directorios existentes. Todos los descriptores de grupos se organizan en una tabla de descriptores de grupo, la cual ocupa varios bloques consecutivos . Al igual que sucede con el superbloque, la tabla de descriptores es una estructura fundamental para el funcionamiento del sistema por lo que es duplicada en todos los grupos de bloques, a continuación de la copia de superbloque. El núcleo normalmente trabaj a con l a tabla de descriptores d e grupo ubicada e n el primer grupo d e bloques. • Mapa de bits de bloques de datos existentes en el grupo. Ocupa un bloque. Cada bloque de datos existente en el grupo de bloques tiene asignado un bit en el mapa. Si el bit está desactivado significa que el bloque está libre. Cuando el núcleo tiene que asignar bloques de datos para un archivo consulta este mapa. • Mapa de bits de nodos-i existentes en el grupo. Ocupa un bloque. Su estructura y utilidad es similar a la descrita para el mapa de bits de bloques de datos pero aplicada a los nodos-i. • Tabla de nodos-i existentes en el grupo. Ocupa varios bloques consecutivos. En EXT2 un nodo-i tiene un tamaño de 1 28 bytes, luego en un bloque de la tabla se pueden almacenar SB / 1 28 nodos-i. • Á rea de datos. Contienen los bloques de datos de los archivos y directorios. 265 SOBUNIX : el sistema operativo Linux Partición de disco Grupo de bloques o Grupo de bloques NG- 1 Grupo de bloques 1 ��� �------�--� - - - - - - - - - - - - - - - - - - - - - - - - - Mapa de bits de nodos-i Figura 6.15 - Tabla de nodos-i � �� Á rea de datos Estructura en disco de un sistema de archivos EXT2 Implementación de directorios en EXT2 En un sistema de archivos EXT2 la lista de archivos y subdirectorios de un directorio se implementa con entradas de tamaño variable. Cada entrada presenta la siguiente estructura: • Número de nodo-i. Es un campo de 4 bytes que contiene el número del nodo-i del archivo o subdirectorio al que está asociada esta entrada del directorio. • Longitud en bytes de la entrada. Es un campo de 2 bytes que contiene la suma del tamaño de todos los campos existentes en la entrada. • Longitud en bytes del nombre. Es un campo de 1 byte que contiene el tamaño en bytes de la cadena de caracteres del nombre del archivo al que está asociada la entrada. • Tipo del archivo. Es un campo de 1 byte en el que se almacena un número entero positivo pequeño que identifica el tipo del archivo: archivo regular ( 1 ), directorio (2), dispositivo modo carácter (3), dispositivo modo bloque (4), FIFO (5), conector (6) y enlace simbólico (7). • Nombre del archivo. Este campo es de longitud variable y su tamaño máximo queda limitado a 256 bytes, luego la longitud máxima del nombre de un archivo es de 255 caracteres. Después del nombre del archivo se incluye un carácter nulo \ 0 para marcar el final del nombre. Además se incluyen tantos caracteres nulos como sea necesario hasta alcanzar una frontera de 4 bytes. 1 11 • • 1 Las dos primeras entradas de la lista de un directorio siempre están asignadas a las entradas que hacen referencia al propio directorio y al directorio padre, respectivamente. 11 , 1 • 1 y 266 e Ampliación de sistemas operativos Ejemplo 6 . 9 En la Figura 6. 1 6 se muestra un ejemplo de la implementación de un directorio en un sistema EXT2. El directorio consta de 4 entradas : la primera asignada a la entrada la segunda a la entrada la tercera asignada al archivo ordinario prueba y la cuarta asignada al directorio f o t o s . Las dos primeras entradas tienen un tamaño de 1 2 bytes, mientras que las dos últimas tienen un tamaño de 1 6 bytes. Se observa como el nombre de una entrada siempre termina con un carácter nulo \ O seguido de otros tantos caracteres nulos como sea necesario hasta completar una frontera de 4 bytes. 1 • 1, 1 • 1 • 1, 1 o Long1tud entrad a (2 b ytes - Longitud nombre ( 1 byte) - Tipo de archivo ( 1 byte) Nombre + carafteres de relleno N° nodo-i ( 4 bytes) • 35 12 1 2 \0 \0 \0 36 12 2 2 \0 \0 51 16 6 1 p r u e b 15 16 5 2 f o t o S Figura 6.16 - a \0 1 \0 \0 \0 \0 Implementación de un directorio en EXT2 • Localización de los bloques de datos de un archivo en EXT2 Un nodo-i de un sistema EXT2, como suele ser habitual en los SOBUNIX, se asocia a un determinado archivo y contiene, entre otros, los siguientes datos sobre el mismo: máscara de modo, UID, GID, tamaño en bytes, fecha y hora del último acceso, fecha y hora de la última modificación de un campo del nodo-i (excluidos los de fecha y hora), fecha y hora de la última modificación del archivo, contador de enlaces duros, contador de bloques y localización de los bloques de datos del archivo. Para localizar los bloques de datos del archivo su nodo-i mantiene las direcciones de los 1 2 primeros bloques de datos del archivo, así como las direcciones de un bloque de indirección simple, un bloque de indirección doble y un bloque de indirección triple. Asignación de espacio en EXT2 En un sistema de archivos EXT2 el núcleo consulta los mapas de bits de los grupos de bloques para decidir que espacio asignar. Con el objetivo de aprovechar mej or el espacio de la partición e intentar reducir los tiempos de búsqueda en el disco el núcleo realiza la asignación de espacio siguiendo las siguientes directrices [Tanenbaum, 2009] : S OBUNIX: el sistema operativo Linux 267 • Los bloques de datos de un mismo archivo se intentan alojar, siempre que sea posible, en bloques de datos contiguos en el disco. Además cada vez que se necesita asignar un bloque para un archivo se le preasignan por adelantado 8 bloques adicionales contiguos en previsión de que los vaya a necesitar. Con estas medidas se pretende reducir la fragmentación externa del disco. • Los nodos-i de los directorios se dispersan entre los grupos de bloques existentes. • Un archivo se intenta aloj ar, si existe espacio, en el mismo grupo de bloques que su directorio padre. • Los bloques de datos de un archivo se intentan aloj ar en el mismo grupo donde se encuentre su nodo-i. Otras características Un sistema de archivos EXT2 proporciona soporte para enlaces simbólicos rápidos, los cuales se implementan de la siguiente forma: si el nombre de ruta al que va hacer referencia el enlace simbólico es mayor de 60 bytes entonces se almacena en los bloques de datos del archivo del enlace simbólico, en caso contrario se almacena directamente en el nodo-i del enlace simbólico con lo que se ahorra espacio en disco y el acceso es más rápido. Por otra parte, EXT2 soporta archivos inmutables, que nunca pueden ser modificados (ni siquiera por el superusuario), y archivos de solo-añadir, a los que solo se les puede ir añadiendo datos al final. La consistencia de un sistema de archivos EXT2 suele verificarse si Linux no se ha cerrado correcta­ mente, por ejemplo debido a una falta de suministro eléctrico, o tras un número prefijados de montajes. El programa 1 sbin/ e 2 f se k permite realizar esta verificación. 6.6.3. El sistema de archivos EXT3 El tercer sistema de archivos extendido (Third EXTended filesystem, EXT3) es una versión mejorada de EXT2 que fue introducida en la versión 2.4. 1 5 del núcleo de Linux liberada a finales de 200 1 . Cual­ quier sistema EXT2 puede ser convertido en un sistema EXT3 de forma fácil y sencilla sin necesidad de formatear la partición de disco, ya que EXT3 fue diseñado pensando en ser totalmente compatible con EXT2, de hecho utiliza la misma estructura en disco. La principal diferencia entre EXT3 y EXT2 es que EXT3 implementa la técnica de registro por diario (journaling), que consiste en que el sistema operativo genera un informe con las operaciones que tiene que realizar sobre el sistema de archivos antes de realizarlas. Dicho informe se almacena en un área reservada de la partición de disco denominada diario (joumal). Una vez que el informe ha sido escrito en el diario se comienzan a realizar las operaciones planificadas. Cuando dichas operaciones han sido completadas el informe se borra. Si se produce algún fallo antes de que se puedan completar todas las operaciones planificadas, cuando se reinicia el sistema operativo éste lee el diario para comprobar si existe un informe. En caso afirmativo se repiten una a una todas las operaciones para que el sistema de archivos quede de nuevo en un estado consistente. 268 Ampliación de sistemas operativos El tiempo para recuperar un sistema EXT3 , a diferencia de un sistema EXT2, ya no depende del tamaño del sistema de archivos ni del número de archivos que contiene sino del tamaño del diario. Otra característica importante de un sistema EXT3 , no disponible en EXT2, es que EXT3 puede trabaj ar con fragmentos de archivos, es decir, datos de un archivo que no completan un bloque. Los fragmentos de diferentes archivos se pueden almacenar en un mismo bloque, reduciéndose así la frag­ mentación interna. 6.6.4. El sistema de archivos EXT4 El cuarto sistema de archivos extendido (Fourth EXTended filesystem, EXT4) es una versión me­ jorada de EXT3 cuya implementación estable fue introducida en la versión 2.6.28 del núcleo de Linux liberada a finales de 2008. Entre las principales características de un sistema EXT4 destacan las siguien­ tes [Mathur et al. , 2007] : • Es plenamente compatible con EXT3 . Cualquier sistema EXT3 puede ser montado como sistema EXT4 sin necesidad de formatear la partición de disco. • Incorpora el uso de extends para mejorar el rendimiento y reducir la fragmentación al trabaj ar con archivos de gran tamaño. Un extend es un conjunto de bloques físicos contiguos. El tamaño máximo que puede tener un extend depende del tamaño de bloque, por ejemplo con un tamaño de bloque de 4 KiB un extend puede tener un tamaño máximo de 128 MiB . • Implementa comprobaciones adicionales en la técnica de registro por diario para mejorar su fiabi­ lidad. • Soporta la asignación de múltiples bloques para un mismo archivo en un sola operación lo que reduce la fragmentación. • Implementa la técnica de reserva retrasada de espacio (Allocate-on-flush o delayed allocation) que consiste en retrasar la asignación de espacio en el disco para realizar una operación de escritura hasta justo el momento en que la información va a ser escrita. Nótese la diferencia con otros sistemas de archivos donde la reserva de espacio se realiza previamente a realizar la operación de escritura. Esta técnica se implementa de la siguiente forma: cuando se deben asignar bloques del disco para realizar una operación de escritura el espacio necesario se resta del contador de espacio libre del superbloque, pero no se asigna en el mapa de bits. En su lugar los datos que hay que escribir son almacenados en buffers en la memoria principal hasta que el núcleo decide escribir los buffers modificados en el disco. Usando esta técnica se consigue mejorar el rendimiento del sistema y disminuir la fragmentación. • La comprobación del estado de un sistema EXT4 con el programa ext 2 f s c k se realiza mucho más rápidamente ya que en EXT4 los grupos de bloques y las secciones de la tabla de nodos-i que no están asignadas son marcadas como tales lo que permite a ext 2 f s c k saltárselas. En general el soporte por parte del núcleo de un sistema EXT4 requiere de un menor uso del proce­ sador. Además su implementación y gestión mejora la velocidad de lectura y escritura en el disco. SOBUNIX: el sistema operativo Linux 6. 7. 269 Resumen El sistema operativo Linux es un SOBUNIX de código libre escrito en su primera versión por Linus Benedict Torvalds. El acceso libre y gratuito al código fuente de Linux ha propiciado que miles de personas en todo el mundo colaboren con Linus desinteresadamente en el desarrollo y mejora de Linux. En Linux un proceso monohilo, un proceso ligero o un hilo del núcleo es considerado simplemente un flujo de ejecución independiente o tarea que debe ser planificada y ejecutada. La creación de procesos en Linux, al igual que en otros SOBUNIX, se puede realizar mediante la llamada al sistema f ork. Además Linux dispone de la llamada al sistema e l one para crear un nuevo proceso ligero asociado al proceso invocador de la llamada. Esta llamada es bastante potente y flexible ya que permite especificar los recursos asociados al proceso creador que se desean que sean accedidos por el proceso ligero que es creado. Esta llamada también permite crear un nuevo proceso de la misma forma que la llamada f o rk. Linux es un sistema operativo de núcleo expropiable que utiliza como unidad de planificación a las tareas. Cada tarea tiene asignada una prioridad, que desde el punto de vista del núcleo es un número entero positivo comprendido dentro del rango de valores [0, 1 39], siendo O la prioridad más alta posible y 1 39 la más baja. El núcleo siempre planifica a la tarea en el estado ejecutándose de mayor prioridad. Dicha tarea será expropiada si entra en el estado ejecutándose una tarea de mayor prioridad. A las tareas con prioridad comprendida en el rango [ 1 00, 1 39] se les considera tareas convencionales o tareas de tiempo compartido. A las tareas con prioridad comprendida en el rango [0, 99] se les considera tareas de tiempo real. En Linux el tamaño del espacio de direcciones virtuales de un proceso es tres veces mayor que el tamaño del espacio de direcciones del núcleo. Linux divide la memoria principal en nodos de memoria. El tiempo de acceso a una posición de memoria de un determinado nodo es siempre el mismo. Obviamente en una organización UMA existirá un único nodo de memoria, mientras que en una organización NUMA existirá un nodo de memoria por cada memoria local de un procesador. Por otra parte, Linux considera las particularidades de direccionamiento (tamaño de registros, líneas de direccionamiento para DMA, etc) de cada arquitectura hardware, descomponiendo cada nodo de memoria en zonas, que son regiones contiguas de memoria principal. Linux soporta tablas de páginas paginadas de hasta un máximo de cuatro niveles de paginación. El número de niveles de paginación que utiliza depende de la arquitectura del computador donde se ejecuta. Por ejemplo en la arquitectura Intel x86 se pueden utilizar hasta dos niveles de paginación. En Linux el asignador a nivel de página del subsistema de gestión del núcleo utiliza el algoritmo buddy para asignar uno o varios marcos de página contiguos. Por otra parte, en Linux el asignador de la memoria del núcleo implementa un asignador slab. Para seleccionar los marcos de página cuyo contenido puede ser reemplazado Linux utiliza el algo­ ritmo para reclamar marcos de página o algoritmo PFRA que es una variante del algoritmo del reloj y una aproximación del algoritmo LRU. Los sistemas de archivos nativos de Linux más conocidos son: EXT2, EXT3 o EXT4. Además Linux puede trabaj ar con otros tipos de sistemas de archivos . Para poder soportar diferentes tipos de sistemas de archivos Linux utiliza la capa nodo virtual/ sistema de archivos virtual. En Linux a esta capa se le denomina capa VFS y se implementa mediante las siguientes estructuras de datos : objeto superbloque 270 Ampliación de sistemas operativos (implementa un sistema de archivos virtual), objeto nodo-i (implementa un nodo virtual), objeto entrada de directorio y objeto archivo abierto. 6.8. Lecturas recomendadas Si se desea obtener una explicación adicional y ampliar los contenidos tratados en este capítulo se pueden consultar, por ejemplo: [Bovet y Cesati, 2005], [Mauerer, 2008] , [Love, 20 1 0] y el capítulo 1 0 de [Tanenbaum, 2009] . 6.9. Autoevaluación 6. 1 . ¿Qué se entiende por tarea en Linux? ¿ Y por grupo de hilos? (Respuesta en sección 6.3) 6.2. Enumerar y explicar brevemente la información contenida en una estructura task_s t ruc t de Linux. (Respuesta en sección 6 . 3 . 1 ) 6.3 . Dibujar e l diagrama de transición d e estados de las tareas e n Linux adecuadamente rotulado. Ex­ plicar brevemente el diagrama dibuj ado. (Respuesta en sección 6.3 . 1 ) 6.4. Dibuj ar un diagrama, adecuadamente rotulado, donde s e representa la relación existente entre las estructuras del núcleo de Linux asignadas a una tarea. Explicar brevemente el diagrama dibuj ado. (Respuesta en sección 6.3 . 1 ) 6. 5 . Explicar el uso y l a sintaxis de l a llamada al sistema c l on e . (Respuesta en sección 6.3 .2) 6. 6. ¿Qué función tienen los hilos del núcleo en Linux? (Respuesta en sección 6.3.3) 6.7. Dibujar un diagrama, adecuadamente rotulado, de la escala de prioridades de ejecución considera­ da por el núcleo de Linux. (Respuesta en sección 6.4) 6. 8. ¿Qué inconveniente presentaba el planificador implementado en las primeras versiones de Linux? (Respuesta en sección 6.4. 1 ) 6. 9. ¿Por qué s e caracteriza e l planificador 0( 1 ) de Linux? ¿En que versión de Linux s e introdujo? (Respuesta en sección 6.4. 1 ) 6. 10. ¿Para que s e utiliza l a estructura runqueue? ¿Qué información contiene? (Respuesta en sección 6.4. 1 ) 6. 1 1 . Explicar las políticas de planificación qué se distinguen en el planificador 0( 1 ) de Linux. (Respuesta en sección 6.4. 1 ) 6. 12. Explicar l a asignación de l a prioridad de una tarea en Linux. (Respuesta en sección 6.4. 1 ) SOBUNIX: el sistema operativo Linux 271 6. 13. Explicar las principales características del planificador utilizado a partir de la versión 2.6.23 del núcleo de Linux. (Respuesta en sección 6.4.2) 6. 14. Explicar el funcionamiento del algoritmo CFS . (Respuesta en sección 6.4.2) 6. 15. ¿Para qué se utiliza la estructura rq? ¿Qué información contiene? (Respuesta en sección 6.4.2) 6. 16. Explicar la gestión del espacio de direcciones virtuales en Linux (Respuesta en sección 6.5 . 1 ) 6. 17. Dibuj ar un diagrama, adecuadamente rotulado, de las estructuras de datos que utiliza Linux para gestionar el espacio de direcciones virtuales de un proceso. (Respuesta en sección 6 .5 . 1 ) 6. 18. Explicar l a distinción entre nodos y zonas que realiza Linux al gestionar l a memoria principal (Respuesta en sección 6.5 .2) 6. 19. Enumerar y explicar brevemente las estructuras de datos que utiliza Linux para gestionar la me­ moria principal. (Respuesta en sección 6.5 .2) 6.20. Enumerar los tipos de tablas que el núcleo de Linux utiliza para implementar los niveles de pagi­ nación. Dibujar los campos en que se descompone una dirección virtual en Linux. (Respuesta en sección 6.5 .2) 6. 21 . Enumerar los asignadores de memoria implementados en Linux. (Respuesta en sección 6.5 .2) 6. 22. Explicar el funcionamiento del algoritmo buddy (Respuesta en sección 6.5 .2) 6.23. ¿Qué proceso se utiliza en Linux para realizar el reemplazamiento de páginas? ¿Cuándo se ejecuta? (Respuesta en sección 6.5 .2) 6.24. Explicar el funcionamiento del algoritmo PFRA. (Respuesta en sección 6.5 .2) 6.25 . Dibujar el diagrama de estados de un marco de página al aplicar el algoritmo PFRA. (Respuesta en sección 6.5 .2) 6. 26. Explicar la implementación de la capa nodo virtual/sistema de archivos virtual en Linux. (Respuesta en sección 6.6. 1 ) 6. 27. Explicar la estructura de un sistema de archivos EXT2 (Respuesta en sección 6.6 .2) 6.28. Explicar la implementación de directorios en EXT2 (Respuesta en sección 6.6.2) 6. 29. ¿Qué directrices se siguen en EXT2 para asignar espacio en el disco? (Respuesta en sección 6.6.2) 6. 30. Enumerar las principales características de un sistema de archivos EXT3 . (Respuesta en sección 6.6.3) 6. 31 . Enumerar las principales características de un sistema de archivos EXT4. (Respuesta en sección 6.6.4) 272 Ampliación de sistemas operativos 6.10. Ejercicios 6 . 1 Considérese el programa prog 6 1 (ver Figura 6.2) del Ejemplo 6. 1 . Contestar a los siguientes apartados : a ) ¿Es posible haciendo uso del comando ki l l enviar una señal a u n determinado hilo del proceso asociado al programa prog 6 1 ? b) Consultar el manual de ayuda, o l a bibliografía que considere necesaria, para determinar las llamadas al sistema, las funciones de librerías o los comandos que se pueden utilizar en Linux para enviar una señal a un determinado hilo de un proceso. 6.2 Considérese el programa prog 6 2 (ver Figura 6.4) del Ejemplo 6.3. Contestar a los siguientes apartados : a) ¿Qué sucede s i la llamada a l sistema c l one e s invocada únicamente con l a constante simbólica CLONE_THREAD? b) ¿Y si se invocara con las constantes CLONE_THREAD 1 CLONE_VM? e) ¿Y si se invocara con las constantes CLONE_THREAD 1 CLONE_S IGHAND? d) ¿Qué conclusión se deduce del resultado de los apartados anteriores? 6 .3 Ejecutar en un sistema Linux el comando ps ax y explicar brevemente la información que muestra en pantalla. 6.4 Ejecutar en un sistema Linux el comando t op y explicar brevemente la información que muestra en pantalla. 6. 5 En una partición de 32 GiB se ha creado un sistema de archivos EXT2 que utiliza un tamaño de bloque de 4 KiB . Determinar: a) Tamaño máximo de un grupo de bloques. b) Número de grupos de bloques en que se dividirá la partición. e) Número de nodos-i que se pueden almacenar en un bloque. d) Tamaño máximo que en teoría podría tener un archivo si la partición pudiera ser mayor de 32 GiB . Capítulo 7 El sistema operativo MS -DOS Objetivos docentes Los objetivos docentes de este capítulo son los siguientes : • Conocer los orígenes del sistema operativo MS-DOS . • S aber cuáles son las características principales de MS-DOS . • Conocer la estructura de MS-DOS . • Saber cómo se invocan e implementan las llamadas al sistema en MS-DOS . • Conocer la interfaz con el usuario disponible en MS-DOS : el intérprete c omrnand . c orn. • Conocer los comandos básicos disponibles en c omrnand . c orn. • Saber cómo se implementan y controlan los procesos en MS-DOS . • Saber cómo se gestiona la memoria en MS-DOS . • Conocer la principales características de la gestión de archivos en MS-DOS . • Conocer la estructura y la gestión del sistema de archivos PAT. • Conocer la gestión de la E/ S en MS-DOS . 273 274 Ampliación de sistemas operativos 7 .l. Introducción MS-DOS (MicroSoft Disk Operating System, sistema operativo de disco de Microsoft) fue uno de los sistemas operativos más utilizados en los computadores personales durante la década de los ochenta. En la actualidad prácticamente ya no se utiliza, salvo en algunos sistemas empotrados. Pese a ello todavía merece la pena estudiarlo ya que su diseño e implementación son relativamente simples. Además las primeras versiones de Windows, que será objeto de estudio en el próximo capítulo, se ejecutaban sobre este sistema operativo. Este capítulo está dedicado al estudio del sistema operativo MS-DOS . En primer lugar se realizan una serie de consideraciones generales acerca de MS-DOS : su cronología histórica, sus características principales, su estructura, las llamadas al sistema, su secuencia de arranque y la interfaz con el usuario. En segundo lugar se describe la implementación y control de procesos. En tercer lugar se estudia la gestión de memoria. En cuarto lugar se estudia la gestión de archivos y el sistema de archivos PAT. Finalmente se estudia la gestión de la E/ S . Consideraciones generales sobre MS-DOS 7 .2. 721 . . . Cronología histórica A principios de los años 70 se desarrolló la tecnología de integración a gran escala (Large S cale Inte­ gration, LSI) que permitía almacenar cientos de miles de componentes electrónicos en un chip cuadrado de silicio de aproximadamente un centímetro cuadrado. Usando esta tecnología se logró implementar el primer microprocesador, es decir, un chip que integraba todos los componentes de un procesador. Con la aparición del microprocesador nacieron los primeros computadores personales (Personal Computers, PCs) que inicialmente recibieron el nombre de microcomputadores. Desde el punto de vista de su arquitectura y tamaño los primeros microcomputadores eran pareci­ dos a los minicomputadores. Sin embargo, eran mucho más baratos, lo que hizo accesible su compra a cualquier persona. Uno de los primeros sistemas operativos para microcomputadores fue CP/M que se diseñó en 1 974 para ejecutarse con el microprocesador Intel 8080 de 8 bits. Este sistema operativo fue el dominante durante cerca de cinco años. A principios de los 80, IBM estaba buscando un sistema operativo para su línea de computadores personales con procesadores Intel x86 de 1 6 bits. Digital Research le propuso utilizar su nuevo sistema CP /M-86. Por su parte, Microsoft ofreció a IBM utilizar MS-DOS l. O. Este sistema operativo había sido desarrollado por Tim Paterson con el nombre de 86-DOS para Seattle Computer Products, pero Microsoft, que se dio cuenta de las posibilidades del mismo, se lo compró a Seattle Computer. IBM finalmente se decantó en 1 9 8 1 por utilizar MS-DOS en sus computadores personales, baj o el nombre de PC-DOS . Conviene señalar que aunque en sus primeras versiones PC-DOS y MS-DOS eran idénticos, posteriormente comenzaron a existir diferencias entre ellos. La incorporación de MS-DOS en los computadores personales de IBM hizo que este sistema ope­ rativo fuera utilizado por una multitud de usuarios y desarrolladores de software. Otros fabricantes de El sistema operativo MS-DOS 275 computadores personales pronto se dieron cuenta que los sistemas operativos que incluían en sus compu­ tadores no podían competir, en cuanto a nivel de aceptación de los usuarios, con MS-DOS . Por ello decidieron desarrollar sus propios sistemas operativos compatibles con MS-DOS . Así por ejemplo, Di­ gital Research en 1 988 desarrolló DR-DOS 3 . 4 1 que era compatible con MS-DOS 3 .30. Cuando Novell compró Digital DR-DOS pasó a denominarse Novell DOS . Posteriormente Novell vendió DR-DOS a Caldera, que durante un corto periodo de tiempo lo distribuyó, incluido su código fuente, con licencia freeware con el nombre de OpenDOS . Otra variante de MS-DOS de código abierto es FreeDOS que puede ser ejecutado en SOBUNIX. Existe toda una familia de sistemas DOS : MS-DOS , PC-DOS, DR-DOS, OpenDO S , FreeDOS , etc. Todos ellos presentan obviamente una serie de características comunes, pero también tienen sus propias características particulares que les diferencian. En relación con MS-DOS , miembro fundador de la familia DOS , a lo largo de la década de los ochenta y principio de los noventa fueron apareciendo nuevas versiones compatibles con las anteriores que fueron mej orando el sistema y añadiéndole características adicionales. La versión 6.22 lanzada en 1 994 fue la última que distribuía a MS-DOS separado del sistema operativo Windows. La última versión de MS-DOS fue la 8.0 y se incluía con Windows Me lanzado en el año 2000. En la actualidad MS-DOS y los restantes miembros de la familia DOS prácticamente ya no se utili­ zan, salvo en algunos sistemas empotrados. Por otra parte, conviene señalar que las versiones modernas del sistema operativo Windows disponen del intérprete de comandos cmd . exe 1 que es una mejora del intérprete de comandos c ommand . c om de MS-DOS . 7.2.2. Características principales de MS-DOS Las características principales del sistema operativo MS-DOS son: • Es un sistema monousuario, es decir, solo puede atender a un único usuario simultáneamente. • Es un sistema monotarea, es decir, solo se puede ejecutar un proceso y hasta que no termina su ejecución no se puede ejecutar otro. Contrariamente a la creencia general MS-DOS no es un sistema monoprogramado ya que en la memoria principal pueden estar cargados varios procesos. • El núcleo está escrito en lenguaje ensamblador. • El núcleo posee una estructura de tipo monolítica o simple. • No implementa un planificador de procesos. Los procesos son ejecutados en orden de creación y hasta que no termina un proceso no se puede ejecutar otro. • Administra la memoria principal mediante la técnica de particionamiento dinámico. • Considera a los archivos como una secuencia de bits y delegan en las aplicaciones la interpretación de los mismos. 1 A este intérprete se puede acceder, por ejemplo, a través del accesorio Símbolo del sistema de Windows. 276 Ampliación de sistemas operativos • Soporta tanto el acceso secuencial como el acceso aleatorio a los archivos . • E n los nombres d e archivos n o distingue entre minúsculas y mayúsculas. • Cada sistema de archivos presente en memoria secundaria es tratado como una unidad o volumen lógico independiente, cada una de los cuales posee su propia estructura de directorios. MS-DOS asigna una letra (C, D, E, . . . ) a cada unidad. • Dentro de cada volumen soporta una estructura de directorios de árbol de directorios. El directorio raíz de un volumen se denota por el carácter ' \ ', que además se usa para separar los componentes del nombre de ruta de un archivo. Para acceder al sistema de archivos de otra unidad se debe especificar al comienzo de la ruta la letra de la unidad seguido de dos puntos, por ejemplo: e : \ . • En sus primeras versiones únicamente soportaba sistemas de archivos FAT. Aunque en sus últimas versiones también pasó a soportar sistemas de archivos de discos ópticos (CD-ROM) como el ISO 9660. • La interfaz con el usuario es del tipo línea de comandos . 7.2.3. Estructura del sistema operativo MS-DOS El sistema operativo MS-DOS se descompone en las siguientes capas lógicas (ver Figura 7 . 1 ) : • Módulo B IOS . E s l a capa del sistema operativo encargada de comunicarse con el hardware. Contie­ ne los drivers residentes de los dispositivos de E/S (disco de arranque, pantalla, teclado, puerto de impresión, reloj , etc) que son suministrados por defecto por el fabricante del computador. A estos drivers se les denomina drivers residentes porque se encuentran almacenados permanentemente en una memoria ROM. Durante el proceso de arranque del sistema estos drivers son copiados en la memoria RAM formando parte del archivo i o . sys . Q Usuario Intérprete de comandos (COMMAND.COM) Núcleo de DOS MS-DOS Módulo BIOS Hardware Figura 7.1 - Estructura del sistema operativo MS-DOS El sistema operativo MS-DOS • Núcleo de MS-DOS . Es una capa independiente del hardware propiedad de Microsoft Corporation que se encarga de implementar los servicios o funciones del sistema: control de procesos, gestión de memoria, gestión de archivos y gestión del reloj (fecha y hora). El código del núcleo de MS­ DOS se encuentra en el archivo msdo s . sys y es cargado en la memoria principal durante el arranque del sistema. • Intérprete de comandos. Implementa una interfaz de línea de comandos que permite al usuario invocar los servicios del núcleo y ejecutar programas mediante la introducción de órdenes. El intérprete de comandos incluido en MS-DOS por defecto es c ommand . c om. Aunque un usuario puede elegir otro intérprete compatible con MS-DOS de su elección, simplemente debe cambiar el valor de la variable SHELL del archivo de configuración c o n f i g . sys . 724 . 277 . . Llamadas al sistema En MS-DOS las llamadas al sistema son denominadas funciones del sistema y son invocadas direc­ tamente en lenguaj e ensamblador. Un programa para invocar a una función del sistema primero debe cargar el número que identifica a la función del sistema (ver Tabla 7 . 1 ) y los parámetros de la misma en diferentes registros. A continuación debe invocar la interrupción software int 2 lh que transfiere el control al sistema operativo, el cual se encarga de invocar al manipulador de la interrupción. Función del sistema 3 1H 3 9H 3 AH 3 BH 3 CH 3 DH 3 EH 3 FH 4 0H 4 1H 42H 43H 43H 4 7H 4 BH 4CH 4 DH Tabla 7 . 1 - Utilidad Terminar un programa y permanecer residente en memoria Crear directorio Quitar directorio Establecer directorio actual Crear archivo Abrir archivo Cerrar archivo Leer archivo Escribir archivo Eliminar archivo Mover puntero d e archivo Obtener atributos de archivo Establecer atributos de archivo Obtener directorio actual Cargar y ejecutar programa (EXEC) Terminar un programa Obtener el código de retomo Algunas ejemplos de funciones del sistema de MS-DOS 278 Ampliación de sistemas operativos El manipulador de la interrupción realiza, entre otras, las siguientes acciones: salva el contenido de los registros hardware en la pila de usuario del programa, cambia a una pila del sistema, usa el identificador de la función para localizarla en la tabla de funciones del sistema e invoca a la función del sistema apropiada. Cuando termina de ejecutarse la función del sistema se devuelve el control al manipulador de la interrupción que cambia a la pila del usuario, restaura el contenido hardware del programa que se estaba ejecutando y le transfiere el control para que continúe su ejecución. e Ejemplo 7 . 1 En la Figura 7.2 se muestra a modo de ejemplo un fragmento de un programa en ensamblador [Duncan, 1988b] que permite ilustrar la invocación de llamadas al sistema en MS-DOS . En este ejemplo, se invoca a la función del sistema 3 CH para crear y abrir el archivo prueba . txt dentro del directorio traba j o . S i l a función del sistema s e ejecuta con éxito devuelve el manipulador (handle) del archivo, que e s el equivalente al descriptor de archivo en los SOBUNIX. fname db ' C : \ t raba j o \ prueba . tx t ' , O fhand l e dw ? man ipu l ador de l a r c h ivo número de la func i ón mov ah , 3 ch xor CX , CX a t r i bu t o no rma l mov dx , s eg fname d i r e c c i ón de l nomb r e d e l archivo mov ds , dx mov dx , o f f s e t f name i n t 2 lh tran s f e r enc i a del c o n t r o l a l núc l e o de MS - DO S jc s a l tar en c a s o de e r r o r en l a creac i ón error mov fhandl e , ax Figura 7.2 - a lmac enar e l man i p u l ador del arch ivo Ejemplo de invocación de una llamada al sistema en MS-DOS • 7.2.5. Arranque de MS-DOS El programa de arranque de MS-DOS contenido en el primer sector de la partición del disco de arranque de un sistema FAT en primer lugar comprueba si en dicha partición se encuentran los archivos i o . sys y msdo s . sys . En caso negativo, muestra un mensaj e en pantalla para que el usuario cambie el disco de arranque. En caso afirmativo, copia en la memoria principal los dos archivos 2 y transfiere el 2Dependiendo de la implementación el programa de arranque puede que solo copie en memoria el archivo i o . sys y éste cuando comienza a ejecutarse se encarga de copiar en memoria a msdos . sys . El sistema operativo MS-DOS 279 control a i o . sys . El archivo i o . sys que se carga en la memoria principal queda formado por dos módulos. El primer módulo es una copia de la BIOS . El segundo módulo es sys i n i t que es enlazado junto i o . sys y que es invocado por el código de inicialización de la BIOS . sys i n i t es un programa propiedad de Microsoft que se encarga de determinar la cantidad de memoria principal contigua disponible, transferirse a si mismo a la parte alta de la memoria, e invocar a la rutina de inicialización del núcleo de MS-DOS msdos . sys . El núcleo de MS-DOS al inicializarse configura sus estructuras de datos y los manipuladores de las interrupciones. A continuación invoca a cada uno de los drivers residentes existentes en la BIOS para que determinen el estado del computador e inicialicen a los dispositivos de E/S . Finalmente devuelve el control a sys ini t que invoca a diferentes funciones del sistema para abrir el archivo c o n f i g . sys . Este archivo de texto, que puede ser configurado por el usuario, contiene diferentes parámetros y órdenes de configuración del sistema, como por ejemplo la instalación de drivers instalables (ver sección 7.7.2). Si sys ini t detecta la existencia del archivo c o n f i g . sys lo carga en memoria para procesarlo línea a línea. Además asigna memoria para la caché de buffers de bloques de disco y para los bloques de control de archivos. Asimismo carga en memoria los drivers instalables. Finalmente invoca a una función del sistema para cargar en memoria y comenzar a ejecutar el intérprete de comandos, por defecto c ommand . c om. Una vez finalizada su tarea el módulo sys i n i t se elimina de la memoria. 726 . . . Interfaz con el usuario La interfaz con el usuario que soporta MS-DOS es una interfaz de línea de comandos implementada a través de un intérprete de comandos muy similar a los usados en los SOBUNIX. Por defecto dicho intérprete es el programa c ommand . c om, aunque se puede utilizar cualquier otro intérprete compatible con MS-DOS , como por ejemplo el intérprete 4DOS de JP Software. B asta con cambiar el nombre del intérprete en la variable SHELL del archivo con f i g . sys . Estructura de los comandos Una orden o comando de un intérprete de MS-DOS es una cadena de caracteres que, de forma general, tiene la siguiente estructura: nombre_c omando arg_l arg_2 . . . arg_N / op_l / op_2 . . . / op_M nombre_ c omando es el nombre del programa ejecutable que se desea ejecutar, arg_i i = 1 , 2, . . . , N son los argumentos que acepta el programa y 1 op_j j = 1 , 2, . . . , M son opciones, también denomina­ dos modificadores, que permiten configurar el comportamiento del programa. Conviene señalar que los intérpretes de MS-DOS al interpretar una orden no diferencian entre mayúsculas y minúsculas. Si se desea obtener información sobre el significado, los argumentos y las opciones de un determi­ nado comando, se puede usar la orden: help nombre_c omando 280 Ampliación de sistemas operativos Comando cd o chdi r c opy de l o eras e dir f ind f o rma t md o mkdir ren o rename type Tabla 7.2 - Utilidad Cambiar el directorio de trabajo Copiar uno o más archivos en otra ubicación B orrar uno o más archivos Mostrar el contenido (archivos y subdirectorios) de un directorio B uscar una cadena de texto en uno o más archivos Formatear partición de disco Crear un directorio Cambiar el nombre de uno o más archivo Mostrar el contenido de uno o más archivos de texto Algunos comandos básicos disponibles en el intérprete de comandos c ommand . c om El listado de comandos soportados por c ommand . c om es extenso. En la Tabla 7.2 se recopilan, a modo de ejemplo, solamente algunos comandos básicos. El intérprete c ommand . c om al igual que los intérpretes de comandos de los SOBUNIX soporta el uso de comodines, el redireccionamiento de la E/S y el uso de tuberías. e Ejemplo 7 . 2 Supóngase que el directorio de trabajo actual de un usuario en MS-DOS es e : \ t rabaj o y que en dicho directorio se encuentra alojado únicamente el archivo dat o s . txt. Si se escribe la orden dir en la pantalla se mostraría información sobre el contenido del directorio de trabajo: E l vo l umen de l a un i dad e no t i ene e t i queta . E l número de s e r i e de l vo lumen e s : 9 0 e e - E l BD D i r e c t o r i o de e : \ trabaj o 2 0 / 04/2 012 2 0 / 04/2 012 2 0 / 04/2 012 < D I R> 11 : 47 < D I R> 11 : 47 11 : 37 4 0 0 da t o s . txt 4 0 0 byt e s 1 archivo s 2 dirs 5 7 6 . 7 1 6 . 8 0 0 bytes l i bre s Para crear u n subdirectorio d e nombre borrador dentro del directorio d e trabajo actual s e puede usar, por ejemplo, la orden md borrador Nótese que se ha utilizado el nombre de ruta relativa del subdirectorio, aunque por supuesto también se podría haber utilizado el nombre de ruta absoluta. En dicho caso la orden hubiera sido la siguiente: El sistema operativo MS-DOS 281 md c : \ trabaj o \ bo rrador También se podría haber omitido la letra que designa la unidad: md \ t rabaj o \ borrador Se puede comprobar que dicho directorio ha sido creado usando la orden dir. Si se desea que el directorio creado pase a ser el directorio de trabajo se puede usar la orden cd borrador Para copiar el archivo da t o s . txt dentro de este directorio se debe usar el comando c opy, cuya sintaxis se puede conocer usando la orden he lp c opy De acuerdo con la información mostrada se deben proporcionar como argumentos de entrada del coman­ do c opy las nombres de ruta absolutas o relativas del elemento que se desea copiar y del directorio donde se desea realizar la copia. Para este ejemplo, la orden a utilizar sería: c opy \ t rabaj o \ dat o s . txt \ t rabaj o \ borrador En este caso se han utilizado nombres de rutas absolutas. Obviamente para este ejemplo esta orden puede escribirse mucho más abreviadamente teniendo en cuenta que el directorio origen es el directorio padre del directorio destino y que el directorio destino es el directorio de trabajo actual, es decir, usando las entradas y ' . ': 11 • • 11 c opy . . \ da t o s . txt . • Tipos de comandos El intérprete c ommand . c om soporta tres tipos de comandos : • Comandos internos. S o n aquellos cuyo código d e ejecución s e incluye dentro del código del intér­ prete. Ejemplos de comandos internos son: c opy, d i r y de l . • Comandos externos. También denominados programas transitorios. Son aquellos cuyo código de ejecución se encuentra en un archivo ejecutable en disco que debe ser buscado por el intérprete y cargado en la memoria principal en una región denominada área de programas transitorios. Ejemplos de comandos externos son: chkds k y backup . • Archivo de procesamiento por lotes (archivos batch) . Son archivos de texto con extensión . ba t que contienen un listado de comandos (internos o/y externos) u otros archivos batch. También pue­ den contener sentencias propias de los lenguaj es de programación como i f o g o t o . El intérprete va leyendo y ejecutando secuencialmente cada línea del archivo. Señalar la gran analogía existente entre los archivos batch y los shell scripts de los intérpretes de los SOBUNIX. 282 Ampliación de sistemas operativos Funcionamiento El intérprete de comandos c orrunand . c om es cargado en la memoria principal durante el arranque del sistema. El código del intérprete c orrunand . c om se estructura en dos partes: • Parte residente. Se carga en las direcciones bajas de memoria principal a continuación del núcleo de MS-DOS y sus estructuras de datos y buffers. Contiene las rutinas que se encargan, entre otras, de las siguientes tareas : atender diferentes combinaciones de teclas 3 , atender los errores críticos, gestionar la terminación de los programas que se invocan desde el intérprete, y cargar la parte tran­ sitoria del intérprete. También en esta parte se almacena el código de los comandos internos. Esta parte se denomina parte residente porque siempre permanece cargada en la memoria principal. • Parte transitoria. Se carga en las direcciones altas de la memoria principal. Contiene las rutinas que se encargan de mostrar el apuntador en pantalla, leer las ordenes desde el teclado o desde un archivo batch, y ejecutarlas. Esta parte se denomina parte transitoria porque puede ser eliminada de la memoria si el programa que se va ejecutar desde el intérprete necesita usar más memoria de la inicialmente asignada. Cuando un programa termina la parte residente del intérprete comprueba si la parte transitoria está cargada en memoria, en caso negativo procede a cargarla. Cuando c orrunand . com comienza a ejecutarse en primer lugar comprueba si existe en el directorio de arranque el archivo aut o exec . bat . Este archivo contiene un conjunto de órdenes configurables por el usuario para establecer las variables de entorno del intérprete de comandos y lanzar otros programas, como por ejemplo, un antivirus. Si el archivo aut o exec . bat existe entonces c orrunand . c om comienza a ejecutarlo. Una vez fina­ lizada su ejecución c orrunand . c om muestra en pantalla el apuntador y se queda a la espera de que el usuario escriba una orden. El apuntador, por defecto es el nombre de ruta absoluta del directorio de tra­ baj o actual seguido del carácter ' > ' . Así por ejemplo si el directorio de trabajo es el directorio raíz ' \ ' de la unidad " e : " entonces el apuntador sería "e : \ > " . Cuando u n usuario escribe una orden, c orrunand . c om comprueba en primer lugar s i s e trata de una orden interna. En caso afirmativo procede a ejecutarla puesto que su código ya se encuentra cargado en memoria dentro del código del propio intérprete. En caso negativo, comprueba si se trata de una orden externa o de un archivo batch. Para ello busca la existencia de un archivo homónimo a la orden con extensión . c om, . exe o . ba t . Primero busca el archivo en el directorio de trabajo actual y si no lo encuentra lo busca en los directorios especificados en la variable de entorno PATH. Si tampoco lo encuentra en estas ubicaciones entonces muestra en la pantalla un mensaje de error. Si lo encuentra invoca a la función del sistema 4BH (función EXEC) para que cargue el archivo en memoria y comenzar su ejecución. Cuando el programa termine de ejecutarse el control regresa al intérprete de comandos que vuelve a mostrar el apuntador en la pantalla. 3 Como por ejemplo [ c ontro l ] + [ e ] y [ c o n t ro l ] + [ break ] . El sistema operativo MS-DOS 73 . . 283 Implementación y control de procesos en MS-DOS MS-DOS es un sistema operativo monotarea, hasta que no termina de ejecutarse un proceso no puede comenzar a ejecutarse otro. El proceso que se ejecuta tiene un control total de los recursos de la máquina. Su ejecución solo puede ser interrumpida por el sistema operativo para realizar el tratamiento de las interrupciones. Esta característica simplifica enormemente el control de los procesos y elimina la necesidad de implementar un planificador de procesos. En MS-DOS el control de procesos es muy simple, el proceso que se está ejecutando (proceso padre) puede crear otro proceso (proceso hijo) invocando a la función del sistema 4BH también conocida como función EXEC. Esta función carga en memoria al proceso hijo y le transfiere el control. Mientras el pro­ ceso hijo se ejecuta el proceso padre permanece bloqueado en la memoria principal. Cuando el proceso hijo va a terminar su ejecución invoca a una función del sistema para transferir el control de nuevo a su proceso padre. Cuando se crea un proceso, el sistema operativo crea las siguientes estructuras de datos asociadas al proceso: • Bloque de entorno. Contiene las variables de entorno del proceso. El bloque de entorno de un pro­ ceso es inicialmente una copia del bloque de entorno de su proceso padre. Las variables de entorno se pueden cambiar usando el comando s e t , bien directamente a través del teclado o mediante un archivo batch. • Prefijo del segmento del programa (Program Segment Prefix, PSP). Contiene la información que necesita conocer el sistema operativo para controlar la ejecución del proceso, como por ejemplo: puntero al bloque de entorno, puntero al PSP del proceso padre, punteros a diferentes funciones del sistema, y la tabla de manipuladores de archivos abiertos que son heredados del proceso padre. En el PSP también se almacenan los argumentos del programa. 7.3.1. Creación y ejecución de un proceso La función del sistema 4 BH o función EXEC es el mecanismo existente en MS-DOS para crear y co­ menzar a ejecutar un proceso. EXEC en primer lugar intenta localizar el archivo ejecutable y comprueba el tipo de programa que se desea ejecutar inspeccionando la cabecera del archivo ejecutable. MS-DOS soporta dos formatos de archivos ejecutables : • Programas c om. Se caracterizan porque su tamaño está limitado a 64 KiB . Las regiones (código, datos y pila) de un programa c om comparten un mismo segmento de memoria principal . . . • Programas exe. Su tamaño está limitado a la memoria principal disponible. Las regiones de un programa exe pueden ocupar múltiples segmentos de memoria principal. . . En segundo lugar EXEC asigna al nuevo proceso un bloque de entorno. A continuación asigna al proceso un bloque de memoria en el área de programas transitorios de memoria principal (ver sección 7 .4). En este bloque carga la región de código y de datos del proceso, y crea su pila. Además crea e 284 Ampliación de sistemas operativos inicializa el PSP del proceso. Finalmente EXEC transfiere el control al nuevo proceso para que comience a ejecutarse. Durante el tratamiento de la interrupción software int 2 1 H asociada a la invocación de la función del sistema EXEC, el contexto a nivel de registros del proceso padre es almacenado en su pila. Además la dirección de la pila es almacenada en su PSP. Cuando el proceso hijo termina, se busca la dirección del PSP del proceso padre en el PSP del proceso hijo. Una vez encontrada se accede al PSP del padre para localizar la dirección de su pila, restaurar su contexto hardware y continuar con la ejecución del proceso padre. La función del sistema EXEC dispone de un parámetro que permite especificar su comportamiento: cargar y ejecutar, solo cargar o solo ejecutar. 732 . . . Terminación de un proceso En MS-DOS existen dos funciones del sistema que producen la terminación de un proceso: la función 4CH y la función 3 1H. La función 4CH libera la memoria principal asignada al proceso que invoca a la función, cierra los archivos abiertos, escribe en el disco los buffers de la caché que han sido modificados por el proceso, almacena el código de finalización o retomo en un registro, y transfiere el control al proceso padre. El código de finalización o retorno de un proceso de MS-DOS es similar al estatus de salida de los procesos en los SOBUNIX. El proceso padre puede recuperar el código de finalización de su proceso hijo invocando a la función del sistema 4 DH. Por su parte, la función del sistema 3 1 H se limita a transferir el control al proceso padre. A un proceso que termina mediante la invocación de esta función se le denomina proceso TSR (Termínate and Stay Resident, terminar y permanecer residente) ya que todos sus recursos, salvo quizás el bloque asignado en el área de programas transitorios, siguen estando asignados al proceso aunque éste haya terminado. De forma general los procesos TSR se utilizan en MS-DOS para ejecutar drivers o manej adores de interrupciones escritos por el usuario. Los procesos TSR se activan mediante interrupciones hardware o software. Cuando terminan de realizar su tarea invocan a la función 3 1 H para terminar y permanecer residentes a la espera de volver a ser invocados por otra interrupción. 7.4. Gestión de memoria en MS-DOS Cuando se desarrolló el sistema operativo MS-DOS los computadores personales disponían general­ mente de 64 KiB de memoria principal. Por otra parte, los procesadores 8088 y 8086 de Intel usados en los primeros PCs de IBM en los que se iba a ejecutar MS-DOS podían direccionar hasta 1 MiB , lo cual era un gran avance para la época. Este es el motivo por el que las primeras versiones de MS-DOS traba­ j aban considerando la existencia de 1 MiB de memoria, de los cuales solo 640 MiB podían ser utilizados por el sistema operativo y las programas. Posteriormente la aparición de computadores personales con mayor capacidad de memoria y de apli­ caciones software que requerían de más memoria, hizo que MS-DOS incluyera diferentes mecanismos El sistema operativo MS-DOS 285 para poder superar el límite de los 640 MiB , entre ellos: el uso de la memoria alta (high memory), el soporte de las especificaciones de memoria expandida (Expanded Memory Specifications, EMS) y el soporte de las especificaciones de memoria extendida (Extended Memory Specifications, XMS) . Los lectores interesados en conocer las características de estos mecanismos pueden consultar la referencias que se indican en la sección 7 . 9 . Una vez arrancado e l sistema operativo MS-DOS la memoria principal queda distribuida general­ mente de la forma que se muestra en la Figura 7 . 3 . En la parte más baj a de la memoria se almacena la tabla de vectores de interrupción. A continuación se almacenan la tabla de las funciones de la BIOS y la tabla de las funciones del sistema MS-DOS . En la siguiente zona de memoria se almacena el núcleo de MS-DOS . Luego existe un área reservada para la memoria caché de buffers de disco. Por encima de la caché se alojan los drivers instalables y la parte residente del intérprete c ommand . c oro. Justo a con­ tinuación comienza el área de programas transitorios reservada para la carga de programas invocados 1 MiB BIOS Libre Tarj eta de video 640 KiB Command.com (transitorio) Á rea de programas transitorios Command. com (residente) Drivers instalables Caché de buffers Núcleo de DOS Tablas de funciones DOS y BIOS o Figura 7.3 - Tabla de vectores de interrupción Distribución de la memoria principal que realiza MS-DOS 286 Ampliación de sistemas operativos por el usuario. A continuación se aloj a la parte transitoria del intérprete c ommand . c om hasta alcanzar el límite de 640 KiB . Esta zona puede ser utilizada por el programa en ejecución si requiere de más memoria. El área de memoria comprendida entre 640 KiB y 1 MiB es utilizada por la tarj eta de vídeo y para almacenar una copia de la B IOS . Cuando MS-DOS tiene que asignar memoria principal a un programa utiliza el espacio del área de programas transitorios. Este área de memoria es gestionada por el sistema operativo mediante la técnica de particionamiento dinámico. La memoria se asigna en bloques o particiones4 . El tamaño mínimo que puede tener un bloque es 1 6 bytes, unidad denominada párrafo en MS-DOS . El tamaño máximo de una partición queda limitado por el tamaño del área de programas transitorios. Para implementar la técnica del particionamiento dinámico el núcleo enlaza todos los bloques de memoria existentes formando una lista (ver Figura 7 .4). Para ello reserva los primeros cinco bytes de un bloque, a modo de cabecera del bloque, para almacenar la siguiente información: puntero al siguiente bloque de la lista, tamaño del bloque en párrafos, estado del bloque (libre u ocupado) y puntero al PSP del programa al que está asignado el bloque (si éste no está libre). Cabecera Cabecera Bloque 2 1 1 l1 Cabecera Ocupado Libre Cabecera B loque 3 Bloque 4 Ocupado Libre 11 Figura 7.4 - Lista enlazada de bloques de memoria principal mantenida en MS-DOS MS-DOS se encarga de realizar la gestión de la memoria únicamente cuando se le solicita a través de alguna de las siguientes funciones del sistema: • Función 4 8 H. Asigna un bloque de memoria. Cuando MS-DOS tiene que asignar un bloque de memoria de tamaño S busca en la lista de bloques un bloque de tamaño igual o mayor a S . Por defecto utiliza el algoritmo del primer ajuste (first fit) para realizar la búsqueda. Este algoritmo comienza a buscar desde el principio de la lista y asigna el primer bloque que encuentra con un tamaño igual o mayor a S . Este algoritmo es sencillo de implementar además mínimiza el tiempo de búsqueda. MS-DOS también soporta el algoritmo del mejor ajuste (best fist), que busca en toda la lista un bloque cuyo tamaño sea igual o exceda lo menos posible del tamaño S requerido, y el algoritmo del último ajuste (last fist), busca en la lista el bloque ubicado en la direcciones más altas de tamaño igual o mayor al tamaño S requerido. El algoritmo a utilizar se especifica como un parámetro de la función. 4En MS-DOS un bloque o partición también recibe el nombre de arena. 287 El sistema operativo MS-DOS • Función 4 9 H. Libera un bloque de memoria. Cuando MS-DOS tiene que liberar un bloque intenta fusionarlo con otros bloques libres contiguos para formar un bloque libre de mayor tamaño. • Función 4AH. Redimensionar un bloque de memoria. Por defecto, MS-DOS a un programa c om le asigna el bloque de memoria libre de mayor tamaño que encuentre, aunque no lo necesite. En el caso de un programa exe busca en la cabecera del archivo ejecutable el espacio mínimo y máximo de memoria que el programa necesita. MS-DOS en primer lugar intenta asignarle un bloque de tamaño igual o mayor a la máxima memoria que va a utilizar el programa. Si no existe ninguno disponible le intenta asignar un bloque de tamaño igual o mayor a la mínima memoria. Si tampoco existe ninguno disponible el programa no puede ejecutarse. Señalar que cuando un programa está bien diseñado debe devolver al sistema la memoria principal que no vaya a utilizar, para ello debe invocar a esta función del sistema. . . Estas funciones pueden ser invocadas tanto por un programa de usuario como por otras funciones del sistema. Por ejemplo la rutina que implementa la función EXEC invoca a la función 4 8 H para asignar un bloque de memoria al proceso que se va a cargar en memoria. 7.5. Gestión de archivos en MS-DOS 7.5.1. Archivos En MS-DOS la información contenida en un archivo se estructura como una secuencia lineal de bytes. MS-DOS no interpreta dicha información, simplemente se limita a operar (leer o escribir) con ella a nivel de byte. Es el proceso en ejecución el encargado de interpretar la información contenida en el archivo sobre el que ha solicitado operar al sistema operativo. En MS-DOS el acceso al contenido de un archivo se puede realizar de forma secuencial o de forma directa. MS-DOS distingue dos tipos de archivos : archivos regulares (archivos binarios y archivos ASCII) y directorios. El nombre de un archivo es una cadena de caracteres ASCII. La longitud máxima del nombre de un archivo es de ocho caracteres más una extensión de tres caracteres. Por ejemplo, prueba . txt sería un nombre de archivo válido en MS-DOS . Señalar que estos límites están impuestos por el sistema de archivos FAT (ver sección 7.6), que es el sistema de archivos principal utilizado por MS-DOS . La implementación de la gestión de archivos utilizada en la primera versión de MS-DOS estaba ins­ pirada en la del sistema operativo CP /M, que se basaba en el uso de una estructura de datos denominada bloque de control del archivo (File Control Block, FCB). Un FCB es una estructura que se crea en el espacio de direcciones del proceso y que se asocia a un archivo abierto. Cuando un proceso desea abrir o crear una archivo, primero debe inicializar un FCB con la unidad, el nombre y la extensión del archivo. A continuación debe invocar a una función del sistema para abrir o crear el archivo, pasándole como ar­ gumento la dirección del FCB . Si el archivo es abierto o creado con éxito, entonces MS-DOS almacena en el FCB una copia de la información existente en la entrada del directorio asociada al archivo. En el FCB también se almacena el puntero de lectura/ escritura. Cada vez que un proceso desea leer o escribir un archivo debe invocar a la función del sistema apropiada pasándole como argumento la dirección del FCB . 288 Ampliación de sistemas operativos A partir de su versión 2.0 MS-DOS implementó un modelo de gestión de archivos similar al utilizado en los SOBUNIX: cuando un proceso desea abrir o crear un archivo debe invocar a la función del sistema 3 DH pasándole como argumento el nombre de ruta del archivo. Si el archivo se abre o se crea con éxito MS-DOS devuelve al proceso un manejador de archivo (file handle) de 16 bits, similar al descriptor de archivo de los SOBUNIX. El proceso cuando invoca a una función del sistema para leer o escribir un archivo debe pasarle como argumento su manej ador de archivo. MS-DOS dentro del PSP de un proceso (ver Figura 7.5) mantiene una tabla de manejadores de archivos (job handle table) 5 , similar a la tabla de descriptores de archivos de los SOBUNIX. Cada entrada de tabla contiene la dirección de la tabla de archivo del sistema (System File Table, SFT), similar al objeto de archivo abierto de los S OBUNIX. Una SFT es una estructura de datos que se crea en el espacio del núcleo y que contiene toda la información que necesita conocer el sistema operativo para trabajar con un archivo abierto, como por ejemplo: la unidad lógica donde se ubica, la dirección de su primer clusters de datos. el tamaño del archivo, sus atributos, el modo de acceso, el puntero de lectura/escritura, etc. MS-DOS enlaza todas las SFTs existentes en una estructura denominada tabla de archivos. El modelo de gestión de archivos implementado en MS-DOS 2.0 para mantener la compatibilidad con la versión anterior también soportaba el uso de FCBs. Para unificar el tratamiento de los archivos, MS-DOS también asigna una SFT a un archivo abierto con un FCB . Tabla de archivos PSP de un proceso A � Manejador de archivo 'l"'"� '' ''' ! : '' : ''' :' '' ' 1------------------J ! Tabla de manej adores de archivos de un proceso A SFT 0 + SFT � SFT • Figura 7.5 - Estructura de datos utilizadas en MS-DOS para gestionar los archivos 5 La traducción real al español de este término inglés sería tabla para evitar confusiones. tabla de manejadores de archivos de manejadores de tareas pero he preferido traducirla por El sistema operativo MS-DOS 7.5.2. 289 Directorios En su primera versión MS-DOS implementaba una estructura de directorios de un único nivel, es decir, todos los archivos estaban contenidos en un único directorio. A partir de su versión 2.0 comenzó a implementar una estructura de árbol de directorios, la cual posibilita la existencia de múltiples directorios y subdirectorios. El directorio raíz se denota por el carácter \ que además se usa para separar los componentes del nombre de ruta de un archivo. Los bloques de datos de un directorio contienen una lista con los archivos y subdirectorios alojados en el directorio. En dicha lista existen también dos entradas especiales : la entrada que hace referencia que hace referencia al directorio padre. al propio directorio y la entrada En cada entrada de la lista contenida en un directorio se almacena la siguiente información: el nombre y la extensión del archivo, los atributos del archivo, el tamaño del archivo en bytes, la fecha y hora de la última modificación, y la dirección del primer bloque de datos. El formato de una entrada de la lista de un directorio viene impuesto por el sistema de archivos FAT. Los atributos del archivo son una máscara de 8 bits que permiten especificar el tratamiento que debe dar MS-DOS al archivo al que está asociada la entrada. Cada atributo tiene asignado un bit que puede ser activado o desactivado mediante el uso del comando a t tr ib. Entre los atributos soportados se encuentran los siguientes : 1 1, 1 11 • • • 1 11 • Solo lectura. Si está activado (0000 000 1 ) significa que el archivo o subdirectorio al que está asociada la entrada no puede ser modificado. • Oculto. Si está activado (0000 00 1 0) significa que la entrada no debe mostrarse en los listados del directorio. • Sistema. Si está activado (0000 0 1 00) significa que la entrada está asociada a un archivo del siste­ ma, como por ejemplo i o . sys . • Etiqueta del volumen. Si está activado (0000 1 000) significa que la entrada no está asociada a ningún archivo sino que contiene el nombre o etiqueta del volumen o unidad lógica. • Subdirectorio. Si está activado (000 1 0000) significa que la entrada está asociada a un subdirecto­ rio. • Archivo. Si está activado (00 1 0 0000) significa que la entrada está asociada a un archivo ordinario. 7 .5.3. Sistemas de archivos Cada sistema de archivos presente en memoria secundaria es tratado por MS-DOS como una unidad o volumen lógico independiente, cada una de los cuales posee su propia estructura de directorios. MS­ DOS asigna una letra (C, D, E, . . . ) a cada unidad. El nombre de ruta de un archivo comienza por tanto con el nombre del volumen lógico en el que está almacenado. En sus primeras versiones MS-DOS soportaba únicamente sistemas de archivos FAT. Aunque poste­ riormente implementó la interfaz de sistemas de archivos instalables (lnstallable File System, IFS) que 290 Ampliación de sistemas operativos le permitía soportar algunos otros sistemas de archivos, como por ejemplo, el sistema de archivos ISO 9660 de los CD-ROM. La interfaz IFS traduce las operaciones independientes del sistema de archivos que invoca un proceso a las funciones dependientes del sistema de archivos que las implementan. Las funciones dependientes del sistema de archivos se implementan en un archivo ejecutable que debe ser cargado cuando se arranca el computador. Por ejemplo, ms c dex . exe en el caso de los sistema de archivos ISO 9660 de los CD­ ROM. 7.6. El sistema de archivos FAT El sistema de archivos FAT, también conocido como sistema FAT- 12, fue creado por Bill Gates y Marc McDonald en 1 977. El sistema FAT- 1 2 utiliza como unidad de asignación de espacio al cluster, el cual está formado por varios sectores de disco contiguos. El tamaño de un cluster se fija en el momento de la creación del sistema de archivos. Asimismo se utilizan 12 bits para direccionar un cluster, de ahí el nombre de FAT- 12. En un sistema FAT- 1 2 el tamaño máximo de un volumen está limitado a 32 MiB . Posteriormente a finales de los ochenta se desarrolló el sistema FAT- 1 6, el cual utilizaba 1 6 bits para direccionar un cluster. Además soportaba un tamaño máximo de volumen de 2 GiB . El sistema FAT- 1 6 fue introducido como sistema d e archivos principal en l a versión 4 . 0 d e MS-DOS aparecida e n 1988. La última versión aparecida de un sistema FAT, es el sistema FAT-32, el cual utiliza 32 bits para direccionar a un cluster (realmente solo se utilizan 28 bits). Además soporta un tamaño máximo de volumen de 4 GiB . El sistema FAT-32 fue introducido como sistema de archivos principal en la versión OSR2 de Windows 95 aparecida en 1 996. En las siguientes secciones se describe la estructura y gestión del sistema de archivos FAT- 12, que fue el sistema de archivos principal de las primeras versiones de MS-DOS . 7.6.1. Estructura de un sistema FAT Un sistema de archivos FAT presenta la siguiente estructura en la partición de disco donde se crea (ver Figura 7.6): • A rea de arranque. Se sitúa al principio de la partición. Contiene información sobre la partición de disco sobre la que se ha creado el sistema de archivos, como por ejemplo: los bytes que ocupa un sector, los sectores que hay en una pista, el número de sectores y los sectores que tiene un cluster. El área de arranque también contiene información sobre la estructura del sistema de archivos: número de tablas de asignación de archivos existentes (original y copias), los sectores que ocupa una tabla de asignación de archivos y el número máximo de entradas del directorio raíz. Además si se trata de la partición activa el área de arranque contiene el código necesario para iniciar el arranque del sistema operativo. • Tabla de asignación de archivos. Cada entrada de esta tabla tiene un tamaño de 12 bits y contiene información sobre el estado de un cluster del disco: El sistema operativo MS-DOS - ooo. 291 Indica que el cluster está libre. - FF O - FF 6 . Indica que el cluster está reservado. Este es el caso de los clusters que se utilizan para almacenar la lista de archivos y subdirectorios del directorio raíz. - FF 7 . Indica que el cluster está defectuoso. - F F 8 - FF F . Indican que se trata del último cluster de un archivo. - Resto de valores. Indican que el cluster está asignado. En este caso el valor almacenado en la entrada es la dirección física del siguiente cluster de datos del archivo. Recuérdese que la entrada del directorio asociada a un archivo contiene la dirección del primer cluster del archivo. Señalar que las dos primeras entradas de la tabla están reservadas para codificar el tipo de disposi­ tivo donde se ha creado el sistema de archivos : disco duro o disquete. • Copias de la tabla de asignación de archivos. Puesto que la tabla de asignación de archivos es una estructura crítica para el funcionamiento del sistema por motivos de seguridad se mantienen varias copias de la misma en la partición del disco. • Directorio raíz. Contiene una lista de los subdirectorios y archivos existentes en dicho directorio. Su tamaño máximo es fijo y queda establecido en el momento de la creación del sistema de archivos en función del tamaño del disco. • Área de datos. Contiene los clusters de datos libres y los clusters de datos asignados a archivos y a directorios. Partición de disco Á rea de datos Figura 7.6 - Estructura 7.6.2. de un sistema de archivos FAT Implementación de directorios en un sistema FAT En un sistema de archivos FAT la lista de archivos y subdirectorios de un directorio se implementa con entradas de tamaño fijo igual a 32 bytes. En cada entrada se almacena la siguiente información (ver Figura 7.7): nombre, extensión, atributos, hora y fecha de la última modificación, dirección del primer cluster y tamaño. En la lista contenida en un directorio, excepto si se trata del directorio raíz, existen dos entradas especiales: la entrada que contiene la dirección del cluster del directorio y la entrada " . . " que contiene la dirección del primer cluster del directorio padre. Si el directorio padre es el directorio raíz entonces contiene la dirección O. 1 • 1 292 Ampliación de sistemas operativos 32 bytes Nombre Atributos 3 bytes 8 bytes 1 byte Figura 7.7 - Estructura 7.6.3. Tamaño 1 O bytes 2 bytes 2 bytes 2 bytes 4 bytes de una entrada de un directorio en un sistema de archivos FAT Localización de los clusters de datos de un archivo en un sistema FAT En un sistema FAT la entrada asociada a un archivo en un directorio contiene, entre otros datos, la dirección del primer cluster de datos del archivo. Para obtener la dirección del segundo cluster de datos del archivo hay que acceder a la entrada de la tabla de asignación de archivos asociada al primer cluster, la cual contiene la dirección del segundo cluster. Los restantes clusters del archivo se localizan de forma semej ante ya que la entrada de la tabla de asignación de archivos asociada a un determinado cluster i de datos de un archivo contiene la dirección de siguiente cluster i + l . En el caso de que el cluster i sea el último del archivo entonces su entrada de la tabla de asignación de archivos contiene el valor - 1 (FFF en hexadecimal). e Ejemplo 7 . 3 En la parte izquierda de la Figura 7 . 8 se ha representado la tabla de asignación de archivos de un cierto sistema PAT. Recuérdese que la tabla tiene una entrada por cada cluster existente en la partición de disco. Supóngase la dirección física del primer cluster (i = O) de un cierto archivo es Bp = 1 0 1 , esta información se encuentra en la entrada del directorio que contiene al archivo. La entrada 1 0 1 de la tabla de asignación de archivos contiene la dirección física Bp = 9 1 del segundo cluster (i = 1) del archivo. La entrada 9 1 de l a tabla de asignación de archivos contiene l a dirección física B p = 97 del tercer cluster ( i = 2 ) del archivo. La entrada 97 de la tabla de asignación de archivos contiene la dirección física Bp = 93 del cuarto cluster (i = 3) del archivo. Finalmente la entrada 93 de la tabla de asignación de archivos contiene el valor - 1 que indica que este cluster es el último del archivo. • 7.7. Gestión de la E/S en MS-DOS 7.7. 1. Subsistema de E/S El subsistema de E/S de MS-DOS es muy simple debido a que se trata de un sistema operativo monotarea. Cuando el proceso que se está ejecutando realiza una petición de E/ S ésta es inmediatamente procesada. Hasta que no se atiende una petición el proceso no puede realizar otra. MS-DOS no implementa ningún tipo de planificación de la E/ S ya que por su diseño no tenía nece­ sidad de ello. Si bien a partir de su versión 3 .0 empezó a implementar spooling en el uso de la impresora, El sistema operativo MS-DOS Tabla de asignación de archivos o Clusters o 1 1 1 1 1 1 1 1 1 1 1 1 1 1 B F- 90 1 1 1 1 2 3 ) = 91 92 93 94 95 96 97 98 99 1 00 1 0 1 1 02 90 91 97 92 93 -1 94 95 96 97 93 98 99 1 00 101 91 1 1 02 1 1 1 1 Nombre archivo 1 _L Dirección 1 cr cluster 1 Entrada asociada al archivo en el directorio al que pertenece .. 1 2 bits Figura 7.8 - Localización del cluster de un archivo en un sistema FAT 293 294 Ampliación de sistemas operativos el cual permitía mantener una cola de archivos a ser imprimidos. MS-DOS también implementa la técnica de bu.ffering con el objetivo de mejorar el rendimiento de la E/S, para ello mantiene en la memoria principal una caché de bu.ffers de bloques de disco. La caché suele tener un tamaño de entre 30 y 40 buffers, con un tamaño de buffer de 532 bytes. Cada buffer contiene una cabecera con información de control del buffer y los datos propiamente dichos, los cuales pueden ser de tres tipos : datos de la tabla de asignación de archivos, datos de un directorio o datos de un archivo. Todos los buffers existentes son organizados en una lista doblemente enlazada que es gestionada mediante un algoritmo LRU. Cuando un buffer es utilizado es colocado al principio de la lista. De esta forma al final de la lista están los buffers menos utilizados. Cuando hay que asignar un buffer siempre se escoge al último de la lista. Si dicho buffer contiene un bloque de datos cuyo contenido ha sido modificado entonces es escrito en el disco antes de reemplazar su contenido. Si el buffer contiene un bloque de datos de la tabla de asignación de archivos que ha sido modificado entonces se actualizan todas las copias de la tabla de asignación de archivos existentes en el disco. 7.7.2. Drivers de dispositivos E/S En MS-DOS se distinguen dos tipos de drivers de dispositivos de E/S : drivers residentes y drivers instalables. Los drivers residentes se organizan en una capa de software denominada BIOS cuya parte más primitiva es almacenada por el fabricante del computador en una memoria ROM. Durante el proceso de arranque del sistema la BIOS es cargada en la memoria RAM j unto con el archivo i o . s y s , el cual contiene rutinas de E/S que extienden la funcionalidad de la BIOS . Los drivers residentes permiten al sistema operativo comunicarse con el hardware básico de la computadora (disco de arranque, pantalla, teclado, puerto de impresión, reloj , etc). Para poder utilizar otros dispositivos de E/ S distintos a los soportados en la B IOS , sus drivers deben ser instalados durante el arranque del sistema. A estos drivers se les denomina drivers instalables, y son cargados en la memo­ ria principal en un área distinta de la B IOS . Señalar que un driver instalable también puede sustituir en la memoria principal a un driver residente. De esta forma es posible utilizar versiones más actualizadas de un determinado driver residente. 7.7.3. Manejadores de interrupciones MS-DOS implementa 256 manej adores de interrupción uno por cada una de las interrupciones dis­ ponibles en los procesadores de la familia Intel 80x86. Las 256 interrupciones disponibles se pueden agrupar en tres categorías : • Interrupciones hardware externas. Son generadas por los controladores de E/S de los dispositivos de E/S • Interrupciones hardware internas. También denominadas excepciones. Son generadas durante la ejecución de un programa, debido a alguna condición de error, como por ejemplo, una división por cero. El sistema operativo MS-DOS • 295 Interrupciones software. Son generadas por el sistema operativo y por los programas, para acceder a las funciones del sistema y de la BIOS . Esta arquitectura implementa interrupciones vectorizadas. La tabla de vectores de interrupción se almacena en las direcciones más baj as de la memoria principal. Cada entrada de esta tabla está asociada a una determinada interrupción y contiene la dirección de comienzo del manej ador de la interrupción asociada a dicha interrupción. S eñalar que en MS-DOS algunos manej adores de interrupciones pueden implementarse mediante procesos TSR. 78 . . Resumen MS-DOS es un sistema operativo monousuario y monotarea escrito en lenguaj e ensamblador. Se descompone en las siguientes capas lógicas : módulo BIOS , núcleo de MS-DOS e intérprete de comandos. El módulo BIOS es la capa del sistema operativo encargada de comunicarse con el hardware. Contiene los drivers residentes de los dispositivos de E/S (disco de arranque, pantalla, teclado, puerto de impresión, reloj , etc) que son suministrados por defecto por el fabricante del computador. El núcleo de MS-DOS es una capa independiente del hardware que se encarga de implementar los servicios ofunciones del sistema. El intérprete de comandos implementa una interfaz de línea de comandos que permite al usuario invocar los servicios del núcleo y ejecutar programas mediante la introducción de órdenes . Por defecto dicho intérprete es el programa c ommand . c om. MS-DOS es un sistema operativo monotarea, hasta que no termina de ejecutarse un proceso no puede comenzar a ejecutarse otro. El proceso que se ejecuta tiene un control total de los recursos de la máquina. Su ejecución solo puede ser interrumpida por el sistema operativo para realizar el tratamiento de las interrupciones. Esta característica simplifica enormemente el control de los procesos y elimina la necesidad de implementar un planificador de procesos. En MS-DOS el control de procesos es muy simple, el proceso que se está ejecutando (proceso padre) puede crear otro proceso (proceso hijo) invocando a la función del sistema 4 BH también conocida como función EXEC. Esta función carga en memoria al proceso hijo y le transfiere el control. Mientras el pro­ ceso hijo se ejecuta el proceso padre permanece bloqueado en la memoria principal. Cuando el proceso hijo va a terminar su ejecución invoca a una función del sistema para transferir el control de nuevo a su proceso padre. Las primeras versiones de MS-DOS trabaj an considerando la existencia de 1 MiB de memoria prin­ cipal, de los cuales solo 640 MiB podían ser utilizados por el sistema operativo y las programas. Cuando MS-DOS tiene que asignar memoria principal a un programa utiliza el espacio del área de programas transitorios. Este área de memoria es gestionada por el sistema operativo mediante la técnica de parti­ cionamiento dinámico. A partir de su versión 2.0 MS-DOS implementó un modelo de gestión de archivos similar al utilizado en los SOBUNIX: cuando un proceso desea abrir o crear un archivo debe invocar a la función del sistema 3 DH pasándole como argumento el nombre de ruta del archivo. Si el archivo se abre o se crea con éxito MS-DOS devuelve al proceso un manejador de archivo de 1 6 bits, similar al descriptor de archivo de 296 Ampliación de sistemas operativos los SOBUNIX. El proceso, cuando invoca a una función del sistema para leer o escribir un archivo, debe pasarle como argumento su manej ador de archivo. Cada sistema de archivos presente en memoria secundaria es tratado por MS-DOS como una unidad o volumen lógico independiente, cada una de los cuales posee su propia estructura de directorios. MS­ DOS asigna una letra (C, D, E, . . . ) a cada unidad. El nombre de ruta de un archivo comienza por tanto con el nombre del volumen lógico en el que está almacenado. En sus primeras versiones MS-DOS soportaba únicamente sistemas de archivos PAT. Aunque poste­ riormente implementó la inteifaz IFS que le permitía soportar algunos otros sistemas de archivos, como por ejemplo, el sistema de archivos ISO 9660 de los CD-ROM. El sistema FAT utiliza como unidad de asignación de espacio al cluster, el cual está formado por varios sectores de disco contiguos. Un sistema de archivos FAT presenta la siguiente estructura en la partición de disco donde se crea: área de arranque, tabla de asignación de archivos, copias de la tabla de asignación de archivos, directorio raíz y área de datos. El subsistema de E/S de MS-DOS es muy simple debido a que se trata de un sistema operativo monotarea. Cuando el proceso que se está ejecutando realiza una petición de E/ S ésta es inmediatamente procesada. Hasta que no se atiende una petición el proceso no puede realizar otra. 7.9. Lecturas recomendadas Si se desea obtener una explicación adicional y ampliar los contenidos tratados en este capítulo se pueden consultar, por ejemplo: [Duncan, 1 988b] , [Podanoffsky, 1 994] y el capítulo 14 de [McHoes y Flynn, 20 1 0] . 7.10. Autoevaluación 7 . 1 . Enumerar las características principales del sistema operativo MS-DOS . (Respuesta en sección 7 .2.2) 7 . 2 . Explicar la estructura del sistema operativo MS-DOS. (Respuesta en sección 7.2.3) 7 . 3 . Explicar cómo se implementan las llamadas al sistema en MS-DOS . (Respuesta en sección 7.2.4) 7 . 4 . Describir el proceso de arranque del sistema operativo MS-DOS . (Respuesta en sección 7.2.5) 7 . 5 . ¿Qué tipos de comandos soporta el intérprete c ommand . c om de MS-DOS ? (Respuesta en sección 7.2.6) 7 . 6 . Enumerar y describir las partes en que se estructura el intérprete c ommand . c om. (Respuesta en sección 7.2.6) 7 . 7 . Explicar el funcionamiento del intérprete c ommand . c om. (Respuesta en sección 7.2.6) 7 . 8 . Explicar la implementación y el control de procesos en MS-DOS . (Respuesta en sección 7 . 3 ) El sistema operativo MS-DOS 297 7 . 9 . Describir la creación y ejecución de procesos en MS-DOS . (Respuesta en sección 7 .3 . 1 ) 7 . 10 . Describir l a terminación d e procesos en MS-DOS . (Respuesta en sección 7.3 .2) 7 . 1 1 . Dibujar un diagrama adecuadamente rotulado de la distribución de la memoria principal en MS­ DOS . Suponer que el tamaño de memoria es de 1 MiB . (Respuesta en sección 7.4) 7 . 12 . ¿Qué técnica utiliza MS-DOS para gestionar la memoria principal? Explicar su implementación. (Respuesta en sección 7 .4) 7 . 13 . Explicar la implementación de la gestión de archivos utilizada en la primera versión de MS-DOS. (Respuesta en sección 7 . 5 . 1 ) 7 . 14 . Explicar la implementación de la gestión de archivos utilizada a partir de la versión 2.0 de MS­ DOS . (Respuesta en sección 7 .5 . 1 ) 7 . 15 . Describir los posibles atributos de un archivo en MS-DOS . ¿Qué comando permite configurar los atributos? (Respuesta en sección 7.5 .2) 7 . 16 . ¿Cuál es la utilidad de la interfaz IFS de MS-DOS ? (Respuesta en sección 7 . 5 . 3 ) 7 . 17 . Describir la estructura de un sistema de archivos FAT. (Respuesta e n sección 7.6. 1 ) 7 . 18 . Describir l a implementación de directorios en u n sistema de archivos FAT. (Respuesta en sección 7 .6.2) 7 . 19 . Explicar cómo se localizan los clusters de datos de un archivo en un sistema FAT. (Respuesta en sección 7 . 6 . 3 ) 7 . 20 . Explicar cómo se implementa el buffering en MS-DOS . (Respuesta e n sección 7 . 7 . 1 ) 7 . 21 . Describir los tipos de drivers que s e distinguen en MS-DOS . (Respuesta en sección 7.7 .2) 7.11. Ejercicios 7 . 1 . Explique razonadamente cuál sería el resultado de la ejecución de las siguientes órdenes en el intérprete de comandos c ommand . c om de MS-DOS o cmd . exe de Windows : a ) d i r > l i s t ado . txt b) dir e) de l f ind / I " l i s tado " * . txt 7 . 2 . Supóngase que en el directorio de trabajo actual existe el archivo tabl a . txt . ¿Qué orden se debe escribir en el intérprete de comandos c ommand . c om de MS-DOS o cmd . exe de Windows para conseguir que el archivo sea un archivo oculto de solo lectura? 298 Ampliación de sistemas operativos 7 . 3 En MS-DOS la cabecera de un bloque de memoria principal ocupa 5 bytes y tiene el siguiente formato: • Byte O. Contiene el valor 9 O h del código ASCII si es el último bloque de la cadena de bloques o el valor 7 7h en caso contrario. • B ytes 1 -2. De estos 16 bits el bit más significativo determina si el bloque está libre ( 1 ) u ocupado (0). Los 1 5 bits restantes especifican el puntero al PSP del programa al que pertenece el bloque. • B ytes 3-4. Contienen el número de parágrafos contenidos en el bloque. Determinar toda la información posible que proporciona la siguiente cabecera de un bloque de memoria: 7 7 0 0 0 0 0 0 0 3 h. 7 . 4 En un disco duro que utiliza un tamaño de sector de 5 1 2 bytes se desea crear un sistema de archivos FAT- 1 2 con un tamaño de cluster de 1 6 sectores: a) ¿Cuál sería el tamaño máximo del volumen? b) ¿Qué tamaño tendría la tabla de asignación de archivos ? 7 . 5 Considérese u n sistema d e archivos FAT- 1 2 con u n tamaño d e cluster d e 2 KiB . Se pide: a) Determinar el tamaño máximo de un archivo. b) Determinar el número de entradas de directorio que caben en un cluster e) Determinar la precisión de la hora de creación o modificación de un archivo almacenada en una entrada de un directorio. Capítulo 8 El s istema operativo Windows Objetivos docentes Los objetivos docentes de este capítulo son los siguientes : • Conocer el origen y la evolución del sistema operativo Windows. • Conocer las características principales y la estructura de Windows. • Conocer de forma general el funcionamiento y la implementación de la interfaz gráfica de usuario proporcionada por Windows. • S aber cómo se implementan y atienden las llamadas al sistema en Windows. • Conocer las principales llamadas al sistema disponibles en Windows. • S aber cómo se implementa la seguridad y la protección de los datos de los usuarios en Windows. • Conocer la implementación, el control y la planificación de procesos multihilos en Windows. • Conocer los principales mecanismos de sincronización y de comunicación entre procesos disponi­ bles en Windows. • Saber cómo se realiza la administración de memoria en Windows. • Conocer cómo se realiza la gestión de archivos en Windows. • Conocer las características principales y la estructura de un sistema de archivos NTFS . • Saber cómo se realiza la gestión de la E/ S en Windows. 299 300 8. 1. Ampliación de sistemas operativos Introducción Desde principios de los noventa hasta la actualidad Windows de Microsoft es el sistema operativo preferido por la mayoría de los usuarios de computadores personales en todo el mundo. Sus detracto­ res cargan contra su carácter comercial, su código cerrado y sus problemas de estabilidad y seguridad. Sin embargo, es indudable que su sencilla interfaz gráfica y el precio razonable de las máquinas que traen instalado Windows han facilitado el acercamiento al mundo de los computadores personales de los usuarios sin conocimientos previos de informática o programación. Este capítulo está dedicado al estudio del sistema operativo Windows 1 • En primer lugar se realizan una serie de consideraciones generales sobre Windows. En concreto se comenta su cronología histórica, sus características principales, su estructura, la implementación de la API Win32, su interfaz con el usuario, la implementación de las llamadas al sistema, el registro de Windows, su secuencia de arranque y la seguridad en Windows. En segundo lugar se describe la implementación, el control y la planificación de procesos multihilos en Windows. En tercer lugar se estudian los mecanismos de sincronización del núcleo y los mecanismos de comunicación entre procesos. En cuarto lugar se estudia la gestión de memoria. En quinto lugar se describe la gestión de archivos y el sistema de archivos NTFS , que es el sistema de archivos principal utilizado en Windows. En sexto lugar se estudia la gestión de la E/S . Señalar que obviamente, por motivos de espacio, este capítulo no es más que un pequeño resumen de las características de Windows. Los lectores que deseen profundizar en su estudio pueden consultar las referencias bibliográficas reseñadas en la sección 8. 1 O. También conviene tener presente que algunos puntos del funcionamiento de este sistema operativo no se pueden explicar con el detalle deseado porque Microsoft no los ha hecho públicos. Finalmente comentar que algunos lectores echarán en falta en este capitulo la inclusión de programas que ilustren el funcionamiento de las llamadas al sistema disponibles en Windows, tal y como se hizo al describir los S OBUNIX en los capítulos anteriores. Esta decisión se fundamenta en el gran número de parámetros de las funciones de la API Win32 a partir de las cuales se invocan a las llamadas al sistema en Windows, lo que lleva a que los programas sean mucho más extensos y relativamente más difíciles de comprender que los utilizados en los SOBUNIX. 8.2. Consideraciones generales sobre Windows 8.2.1. Cronología histórica A mediados de los ochenta el sistema operativo más popular que disponía de una interfaz gráfica de usuario basada en ventanas era el sistema Mac OS utilizado en los computadores personales Macintosh de Apple. Influenciado por este sistema operativo Microsoft decidió dotar a su sistema operativo MS-DOS de una GUI similar a la que bautizó con el nombre de Windows. La primera versión de Windows apareció en el mercado en 1 985 y la segunda versión en 1 987. Estas dos primeras versiones no tuvieron mucho 1 En concreto las explicaciones de este capítulo toman como base a Windows Vista, que es el nombre comercial de Windows NT 6.0. El sistema operativo Windows 301 éxito. No fue hasta su versión 3 . 0 aparecida en 1 990 cuando Windows comenzó a hacerse realmente popular. La versión 3 . 1 de Windows aparecida en 1 992 fue la última versión de Windows que puede con­ siderarse como únicamente una GUI. Fue en 1 995 con la aparición de Windows 95, cuando Windows adquirió la categoría de sistema operativo como tal, implementando gestión de procesos, multiprogra­ mación y gestión de la memoria virtual. Aunque si bien usaba MS-DOS para arrancar. Uno de los principales problemas de Windows 95 era su inadecuado aislamiento entre los procesos de usuario y el núcleo, lo que producía serios problemas de estabilidad. Las siguientes versiones de Windows: Windows 98 aparecida en 1 998 y Windows Millennium (Windows Me) aparecida en 2000, tampoco resolvieron el problema de la estabilidad. Además también usaban MS-DOS para arrancar. A finales de los ochenta, en paralelo al desarrollo de Windows, Microsoft comenzó a desarrollar otro sistema operativo nuevo basándose en los siguientes principios de diseño [Russinovich et al. , 2009] : • Extensibilidad. El código del sistema operativo debe poder ser modificado y extendido de forma sencilla para poder adaptarse a las nuevas necesidades del mercado. • Portabilidad. El sistema operativo debe poder ser ejecutado en diferentes arquitecturas hardware. • Fiabilidad y robustez. Una aplicación de usuario no debe poder acceder al espacio de direcciones del sistema operativo ni al de otras aplicaciones. • Compatibilidad. El sistema operativo debe de ser capaz de soportar las aplicaciones que se desarro­ llen para otros sistemas operativos como MS-DOS , OS/2 o aquellas que sigan las especificaciones POS IX. La primera versión de este nuevo sistema operativo apareció en 1 993 y se denominó Windows NT 2 3 . 1 . Señalar que Microsoft usó para la primera versión de NT el mismo número de versión que el de la versión de Windows existente por aquella fecha. Inicialmente Windows NT no tuvo mucho éxito pero a partir de su versión 4.0 aparecida en 1 996 que incluía una GUI similar a la de Windows 95 comenzó a ganar cuota de mercado como sistema operativo de servidores y estaciones de trabajo. El éxito tanto técnico como comercial de la versión 5 . 0 de Windows NT aparecida en el mercado en el 2000 bajo el nombre de Windows 2000 hizo que Microsoft dej ara de utilizar MS-DOS como base de su sistema operativo Windows . A partir de entonces todos los sistemas operativos de Microsoft están basados en alguna versión de Windows NT. Con respecto a las versiones cliente de Windows, es decir, aquellas destinadas a computadores perso­ nales, en 200 1 Microsoft sacó al mercado Windows NT 5 . 1 con el nombre de Windows XP. Este sistema es uno de los sistemas operativos de Microsoft que más tiempo ha perdurado. No sería hasta 2007 cuan­ do Microsoft sacó una nueva versión: Windows Vista, que era el nombre comercial de Windows NT 6.0 (Build 6000). Este sistema operativo recibió bastantes críticas ya que presentaba bastantes problemas. 2NT es el acrónimo del término inglés New Technology (nueva tecnología). 302 Ampliación de sistemas operativos En cuanto a las versiones servidor de Windows, es decir, aquellas destinadas a servidores y estaciones de trabajo, en 2003 Microsoft sacó al mercado Windows NT 5 . 2 con el nombre de Windows Server 2003 . Cinco años más tarde, en 2008, lanzó Windows Server 2008 que era el nombre comercial de Windows NT 6.0 (Build 600 1 ) . E n e l momento d e escribir estas líneas las últimas versiones aparecidas de Windows son Windows 7 y Windows Server 2008 R2, aparecidas en 2009, que son los nombres comerciales de la versión cliente y de la versión servidor, respectivamente, de Windows NT 6. 1 . 8.2.2. Características principales de Windows Las características principales del sistema operativo Windows son: • Es un sistema operativo multiprogramado y multitarea, con soporte para multiprocesamiento. • El núcleo está escrito en su mayor parte en lenguaj e C. • El núcleo posee una estructura de tipo monolítica o simple. • La planificación se realiza a nivel de hilos del núcleo usando un algoritmo de planificación basada en múltiples colas de planificación y realimentación. • Dispone de mecanismos de sincronización y comunicación entre procesos o hilos: semáforos, eventos, tuberías, conectores, objetos compartidos, mailslots, etc. • No suelen utilizar ninguna estrategia de tratamiento de interbloqueos. Dichas estrategias si se quieren implementar deben hacerse a nivel de los programas de aplicación. • Administra la memoria principal e implementa la memoria virtual usando la técnica de demanda de página. • Considera a los archivos como una secuencia de bits y delegan en las aplicaciones la interpretación de los mismos. • Soporta tanto el acceso secuencial como el acceso aleatorio a los archivos. • En los nombres de archivos no distingue entre minúsculas y mayúsculas. • Cada sistema de archivos presente en memoria secundaria es tratado como una unidad o volumen lógico independiente, cada una de los cuales posee su propia estructura de directorios. Windows asigna una letra (C, D, E, . . . ) a cada unidad. • Dentro de cada volumen soporta una estructura de directorios de gráfica acíclica. El directorio raíz de un volumen se denota por el carácter \ , que además se usa para separar los componentes del nombre de ruta de un archivo. Para acceder al sistema de archivos de otra unidad se debe especificar al comienzo de la ruta la letra de la unidad seguido de dos puntos, por ejemplo: e : \ . ' • ' La interfaz con el usuario es del tipo interfaz de usuario gráfica (GUI). El sistema operativo Windows 8.2.3. 303 Estructura del sistema operativo Windows El sistema operativo Windows consta de diversos componentes software (ver Figura 8. 1 ), unos se ejecutan en modo usuario y otros en modo núcleo. Los componentes de Windows que se ejecutan en modo usuario son los siguientes : • Procesos del sistema. Son procesos creados por el sistema operativo que se encargan de realizar tareas que dependen del usuario interactivo que haya iniciado una sesión de trabajo en el sistema. Ejemplos de procesos del sistema son el gestor de sesión de usuario (sms s . exe) y el proceso de inicio de sesión (wi n l o gon . exe). • Procesos de servicios. También denominados servicios. Son procesos creados por el sistema ope­ rativo que se encargan de realizar diferentes tareas administrativas en modo usuario independien­ temente del usuario interactivo que haya iniciado una sesión de trabajo en el sistema. Los procesos de servicios de Windows son similares a los procesos demonio de los SOBUNIX. Todos los proce­ sos de servicios existentes son controlados por el gestor de control de servicios ( s ervi c e s . exe) que se encarga de iniciar, parar e interactuar con los procesos de servicio. Un ejemplo de proceso de servicio de Windows es el spooler de impresión. • Aplicaciones. Son procesos asociados a archivo ejecutables de algunos de los siguientes tipos: Windows 32 o 64 bits, Windows 3 . 1 1 6 bits, MS-DOS 16 bit o POSIX 32 bits. Algunas aplicacio­ nes son iniciadas por el propio sistema operativo cuando un usuario inicia una sesión en el sistema. Este es el caso por ejemplo del explorador de Windows (exp l orer . exe) que implementa el escri­ torio de Windows. Otras aplicaciones, como navegadores web, gestores de correos y procesadores Procesos del sistema 1 Procesos de servicios Aplicaciones 1 1 Subsistemas de entorno 1 Modo usuario Librerías de enlace dinámico (DLLs) Interfaz de llamadas al sistema (ntdl l . dl l ) -----------------------------------�------------------------------ Núcleo de --Windows N T � Drivers dispositivos 1 Ej ecutivo Kernel 1 Implementación de la GUI Capa de abstracción del hardware (HAL) Hardware Figura 8.1 1 - Estructura del sistema operativo Windows Modo núcleo 304 Ampliación de sistemas operativos de texto, son invocadas por el propio usuario interactuando con los iconos o los menús de la GUI de Windows. • Librerías de enlace dinámico. Una librería de enlace dinámico o librería DLL (Dynamic Link Library) es un conjunto de rutinas enlazadas juntas en un archivo binario que puede ser cargado en tiempo de ejecución dentro del espacio de direcciones de los procesos que invocan a dichas rutinas. Las librerías DLL permiten ahorrar espacio en memoria ya que solo es necesario cargar una única copia del código de la librería en la memoria principal. Todos los procesos que tienen enlazada la misma librería DLL en su espacio de direcciones comparten el mismo código de la librería. Luego las librerías DLL son el mecanismo utilizado en Windows para implementar librerías compartidas. Una de las librerías DLL más importante es ntdl l . dl l ya que es usada por las restantes librerías DLL. La librería ntdl l . dl l contiene todas las funciones de envoltura de las llamadas al sistema de Windows (ver sección 8.2.6). También contiene otras funciones de apoyo, como por ejemplo: funciones para comunicarse con el subsistema de entorno de Windows, funciones para gestionar el montículo, etc. Las librerías DLL no solo se ejecutan en modo usuario, algunas DLLs también pueden ser utiliza­ das en modo núcleo por el propio sistema operativo. • Subsistemas de entorno. En sus orígenes Windows NT soportaba tres interfaces de programación de aplicaciones o APis : la API de Windows (API Win323 ), la API POSIX, y la API OS/2. A partir de Windows XP la API OS/2 dejó de soportarse. Por otra parte, la API POSIX ha sido extendida y mejorada desde Windows Vista. El sistema operativo para implementar la funcionalidad de cada API utiliza un proceso de usuario denominado subsistema de entorno y un conjunto de librerías DLL. El subsistema de entorno Windows siempre se ejecuta ya que se utiliza, como se verá en la sección 8.2.5, para implementar la GUI de Windows. Por su parte el subsistema de entorno POSIX, conocido desde Windows Vista como subsistema para aplicaciones basadas en UNIX (Subsystem for Unix-based Applications, SUA), solo se ejecuta si se ejecuta una aplicación que utiliza la API POSIX. Por otra parte los componentes de Windows que se ejecutan en modo núcleo son los siguientes: • Capa de abstracción del hardware (Hardware Abstraction Layer, HAL). Esta capa oculta los de­ talles del hardware al resto de componentes del sistema operativo lo que facilita la portabilidad del sistema operativo a diferentes plataformas. La capa HAL se implementa en la forma de una li­ brería DLL (ha l . dl l ) . Cada plataforma tiene asociado su librería DLL correspondiente. Cuando un componente del sistema operativo necesita realizar una operación sobre el hardware invoca a una función de la capa HAL la cual se encarga de traducirla a la función adecuada dependiente del hardware. 3 Aunque denominada como API Win32 esta interfaz es válida tanto para las versiones de Windows de 32 bits como para las versiones de 64 bits. El sistema operativo Windows 305 • Drivers de dispositivos. En Windows los drivers de dispositivos se implementan como módulos que pueden ser cargados dinámicamente y que se ejecutan en modo núcleo en el contexto de un hilo asociado a un proceso de usuario que ha iniciado una operación de E/S, en el contexto de un hilo del sistema, o como resultado de una interrupción. Generalmente el código de un driver se en­ cuentra en un archivo con extensión . sys . A diferencia de otros sistemas operativos la mayoría de los drivers en Windows no interaccionan directamente con el hardware sino que utilizan funciones de la capa HAL. • Implementación de la GUI. La GUI de Windows (ver sección 8.2.5) es un componente tan utilizado que para mejorar el rendimiento del sistema se ejecuta en modo núcleo. • Núcleo de Windows NT. Se implementa en el archivo ejecutable n t o skrnl . exe y se descompone en dos capas : - Ejecutivo (executive). Es la capa superior del núcleo de Windows NT. Atiende las llamadas al sistema e implementa los servicios4 del sistema operativo: administrador de memoria, admi­ nistrador de la caché5 , administrador de E/ S , administrador de PnP (Plug-and-Play, conectar y usar), monitor de seguridad, administrador de procesos, administrador de objetos, admi­ nistrador de configuración. El ejecutivo también se encarga de implementar las llamadas a procedimientos locales avanzados (Advanced Local Procedure Calls, ALPCs) (ver sección 8.4.3). - Núcleo (kernel). Es la capa inferior del núcleo de Windows NT. Se encarga del encamina­ miento de las interrupciones, excepciones y trampas, También se ocupa de la planificación y sincronización de hilos. Además contiene las rutinas para la manipulación de objetos. 8.2.4. Implementación de la API Win32 La API Wln32 es la interfaz de programación de aplicaciones más utilizada por las aplicaciones de Windows. Para implementar su funcionalidad Windows utiliza el subsistema de entorno Windows {c s r s s . exe) y centenares de librerías DLL, ubicadas en su mayoría en e : \wi ndows \ sys t em3 2 . Entre las librerías DLL más importantes que implementan la funcionalidad de la API Win 32 se encuentran las siguientes : • kerne l 3 2 . dl l . Contiene funciones que permiten acceder a la mayoría de llamadas al sistema básicas excepto las que implementan la GUI. • gdi 3 2 . dl l . Contiene funciones que permiten acceder a llamadas al sistema asociadas con la GUI para el manejo de elementos tales como fuentes, textos, colores, mapas de bits, etc. 4 Aquí la palabra servicio se refiere a las tareas que realiza el núcleo. No debe confundirse con Jos servicios de Windows que son procesos que se ejecutan en modo usuario. 5Se trata de la caché software, denominada caché del sistema, que implementa Windows para almacenar las regiones de los archivos usadas más recientemente. 306 Ampliación de sistemas operativos • u s e r 3 2 . dl l . También contiene funciones que permiten acceder a llamadas al sistema asociadas con la GUI para el manejo de elementos como ventanas, iconos, menús, cursor, etc. • advap i 3 2 . dl l . Contiene funciones que permiten acceder a llamadas al sistema asociadas a tareas de administración, registro y seguridad, entre otras. Cuando se invoca a una función de una librería DLL pueden producirse las siguientes acciones : • La función realiza una cierta tarea y retorna sin necesidad de invocar ninguna llamada al sistema. • La función invoca a una función de la librería n tdl l . dl l para invocar a una llamada al sistema. La librería ntdl l . dl l contiene las funciones de envoltura de las llamadas al sistema de Windows. • La función envía un mensaj e al subsistema de entorno al que pertenece, el cual realiza ciertas tareas. En algunos casos el subsistema es capaz de realizar todo el trabajo en modo usuario sin ne­ cesidad de realizar una llamada al sistema. En otros debe realizar una llamada al sistema invocando a una función de la librería n tdl l . dl l . Finalmente comentar que las funciones de la API Win32 se caracterizan por tener nombres largos que suelen resumir bastante bien la tarea que implementan. Por ejemplo, la función CreateProc e s s permite crear u n proceso. También las funciones de la API Win32 se caracterizan por tener bastantes argumentos de entrada. Ello las dota de una mayor flexibilidad pero también complica su uso a los programadores. 8.2.5. Interfaz con el usuario de Windows Windows desde su concepción se caracteriza por proporcionar al usuario una interfaz gráfica. Si bien también es posible trabajar con una interfaz de línea de comandos usando el intérprete de comandos cmd . exe, una versión mej orada del c ommand . c om de MS-DOS . En sus orígenes la GUI de Windows se ejecutaba en modo usuario como parte del subsistema de Windows, pero ello afectaba bastante al rendimiento del sistema debido a los constantes cambios de hilos y de procesos que había que realizar. A partir de Windows NT 4.0 la mayor parte de la GUI se implementa como un conjunto de rutinas ejecutables en modo núcleo dentro del driver win3 2 k . sys . Estas rutinas pueden ser invocadas mediante funciones de las librerías DLL que implementan la API Win32, como por ejemplo: u s e r 3 2 . dl l y gdi 3 2 . dl l . El driver win3 2 k . sys implementa los siguientes componentes de la GUI de Windows : • Sistema de ventanas. Se encarga d e controlar como s e muestran las ventanas e n l a pantalla. Además gestiona la entrada de datos a través del teclado y el ratón, u otros dispositivos de interfaz humana, y envía mensajes a las aplicaciones que esperan dichas entradas. También gestiona la salida en pantalla de acuerdo con los mandatos de las aplicaciones. • Inteifaz de dispositivos gráficos (Graphics Device Interface, GDI). Es una librería de funciones para creación y manipulación de gráficos (líneas, textos, figuras, etc) en dispositivos gráficos. El sistema operativo Windows • 307 Funciones de envoltura a DirectX. DirectX es una API que proporciona funciones para aplicacio­ nes multimedia (juegos, reproducción de video, etc). Esta API se implementa a través del driver dxgkrn l . sys Para interactuar con los dispositivos gráficos las rutinas del driver win3 2 k . sys utilizan a su vez diferentes drivers, algunos de los cuales para mej orar el rendimiento interaccionan directamente con el hardware sin necesidad de invocar funciones de la capa HAL. 8.2.6. Llamadas al sistema en Windows En Windows a las llamadas al sistema se les denomina servicios del sistema, para evitar confusiones con los procesos de servicio a los que también se les denomina servicios, en todo el capítulo se utiliza el término llamada al sistema en lugar de servicio del sistema. Las funciones de envoltura de las llamadas al sistema de Windows se encuentran en la librería n tdl l . dl l . Microsoft ha publicado muy poca información sobre las funciones de esta librería, por lo que es difícil trabaj ar con ellas directamente. El acceso a las funciones de la librería ntdl l . dl l , es decir, la invocación de llamadas al sistema, se realiza de forma indirecta a través de las funciones de las librerías DLL que implementan las APis soportadas por Windows, las cuales están bien documentadas 6 . En la Tabla 8. 1 se muestran algunos ejemplos de funciones de la librería kerne l 3 2 . dl l de la API Win32 y se especifican las funciones de la librería ntdl l . dl l que son invocadas durante su ejecución. Función de kerne l 3 2 . dl l Función d e ntd l l . dl l CreateF i l e ReadF i l e Wr i t eF i l e C l o s eHandl e Creat e Pro c e s s Creat eThread NTCreateF i l e NTReadF i l e NTWr i te Fi l e NTC l o s e NTCreateUs erPro c e s s NTCreat eThread Tabla 8.1 - Tarea que realiza Crear o abrir u n archivo y a existente Leer archivo Escribir archivo Cerrar e l descriptor de u n objeto abierto Crear u n proceso Crear un hilo Algunos ejemplos de funciones de la API Win32 y de sus funciones asociadas en la librería n t dl l . dl l El programador de aplicaciones únicamente debe conocer cuál es la función F l de l a API que necesita invocar para realizar una cierta tarea. Si la realización de dicha tarea requiere invocar a una llamada al sistema entonces la función Fl invocará a la función F2 apropiada de la librería ntdl l . dl l . La función Fl antes de invocar a la función F2 quizás tenga que realizar algún procesamiento sobre los argumentos que va a pasar a F2. Por ejemplo, si hay algún argumento de tipo carácter o cadena de caracteres codificados en ANSI la función F2 deberá pasarlos a codificación Unicode, que es el estándar de codificación utilizado por las llamadas al sistema de Windows. La función de envoltura F2 de la librería n tdl l . dl l es la que se encarga de pasarle al sistema operativo toda la información que necesita conocer para tratar la llamada al sistema: el identificador de 6www.msdn.microsoft.com 308 Ampliación de sistemas operativos la llamada, que es un número entero positivo que la identifica de forma univoca, y los argumentos de la llamada. Cuando ya tiene lista esta información invoca una instrucción hardware especial denominada trampa que transfiere el control al sistema operativo. Cuando tiene lugar una trampa asociada a una llamada al sistema se produce una conmutación de modo usuario a modo núcleo y se guarda el contexto hardware del hilo en ejecución. A continuación se invoca a una rutina general de tratamiento de llamadas al sistema denominada despachador de llamadas al sistema que guarda los argumentos de la llamada al sistema en la pila del núcleo asociada al hilo en ejecución y obtiene de un registro de la máquina el identificador de la llamada al sistema. El despachador utiliza el identificador de la llamada para buscar en la tabla de despacho de llamadas al sistema la dirección de inicio de la rutina de tratamiento especifica de la llamada al sistema que ha sido invocada. Esta tabla contiene una entrada por cada llamada al sistema soportada. Cada entrada contiene la dirección de inicio de la rutina de tratamiento asociada a dicha llamada. Una vez localizada. el manej ador invoca a la rutina específica de tratamiento de la llamada. Dicha rutina se implementa en el ejecutivo del núcleo (nt o s krnl . exe ) si se trata de una llamada no asociada con la GUI, o en el driver que implementa la GUI en modo núcleo (win3 2 k . sys ) si se trata de una llamada asociada con la GUI. Cuando finaliza la rutina de tratamiento de la rutina se devuelve el control al despachador que restaura el contexto hardware del hilo y le transfiere el control para que continúe su ejecución en modo usuario en el punto de retomo de la llamada al sistema. 8.2. 7. El registro de Windows El registro de Windows (registry) es una base de datos que recopila toda la información fundamental relativa al hardware, al software y a los usuarios necesaria para arrancar y configurar el sistema de acuerdo con el usuario que ha iniciado una sesión. La mayor parte del registro se almacena en el sistema de archivos de la partición de arranque de Windows en el directorio e : \ windows \ sys t ern3 2 \ c o n f i g en diferentes archivos denominados subárboles (hives). El registro de Windows se implementa mediante dos tipos de elementos: las llaves y los valores. Una llave es similar a un directorio. Mientras que un valor es similar a un archivo. Cada llave tiene asignado un nombre y puede contener valores y otras llaves. Señalar que a una llave contenida dentro de otra llave también se le denomina subllave. Luego una subllave es similar a un subdirectorio. Por su parte cada valor tiene asignado un nombre, un tipo de datos y los datos propiamente dichos. El nombre de un valor es una cadena de caracteres codificados en código Unicode. El tipo de un valor especifica el tipo de datos que contiene el valor. Existen definidos hasta catorce tipos distintos de valo­ res, entre ellos: REG_s z cadena de caracteres codificados en Unicode, REG_B INARY número binario de longitud arbitraria, REG_L INK enlace simbólico en Unicode y REG_DWORD entero de 32 bits. En el nivel raíz del registro existen creadas, por defecto, seis claves raíz: • HKEY_USERS . Contiene los perfiles de todos los usuarios. Existe una clave por cada perfil de usuario definido Windows. El perfil contiene las preferencias del usuario relativas al escritorio, teclado, sonidos, software, etc. El sistema operativo Windows • 309 HKEY_CURRENT_USER. Enlace simbólico a la parte del registro donde se encuentran las preferen­ cias del usuario actual. • HKEY_CLAS SES_ROOT. Enlace simbólico a la parte del registro que mantiene la información sobre las aplicaciones que hay que invocar en función de las extensiones de los archivos . Por ejemplo, cuando se hace doble clic sobre un archivo con extensión . pdf el programa que gestiona la entrada del ratón lee esta clave del registro para determinar que aplicación debe iniciar, normalmente Adobe Reader. • HKEY_LOCAL_MACHINE. Contiene información sobre el hardware y el sistema operativo. Esta información se distribuye en cinco subclaves : - HARDWARE . Describe el hardware de la máquina especificando que controlador debe utilizarse para cada dispositivo. Señalar que esta información no se almacena en el disco sino que es recopilada cuando se arranca el sistema por un componente del ejecutivo, el administrador de PnP. SAM (Security Account Manager, administrador de cuentas de seguridad) . Contiene informa­ ción sobre las cuentas de los usuarios, como por ejemplo, sus nombres de usuario y contra­ señas. SECURITY. Contienen información sobre la implementación de la política de seguridad: nú­ mero de veces que un usuario ha introducido una contraseña no válida, la longitud mínima que debe tener una contraseña para considerarse como válida, etc. SOFTWARE. Contiene información almacenada por las aplicaciones. Por ejemplo, si se tiene instalado el navegador Firefox entonces existirá creada una subclave de nombre Mo z i l l a donde la aplicación puede guardar toda la información que considere necesaria, por ejemplo, su número de versión o información para desinstalar la aplicación. SYSTEM. Contiene información necesaria para arrancar el sistema, como por ejemplo, un listado con los drivers que hay instalar y un listado con los procesos de servicios que hay que iniciar. • HKEY_CURRENT_CONF I G . Enlace simbólico a la parte del registro donde se encuentra la configu­ ración actual del hardware. • HKEY_PERFORMANCE_DATA. Permite acceder al estado del hardware y a los contadores que man­ tiene el sistema operativo. Esta información resulta de gran utilidad para monitorizar el rendimien­ to del sistema. El registro de Windows puede ser accedido y manipulado por el sistema operativo, por las aplicacio­ nes y por los usuarios. Estos últimos pueden acceder al registro haciendo uso de alguno de los programas distribuidos por Microsoft como por ejemplo: el editor del registro (regedi t . exe) 7 y el monitor de 7Conviene señalar que este programa no muestra la clave HKEY_PERFORMANCE_DATA. 310 Ampliación de sistemas operativos procesos (pro cmon . exe ) . Es importante señalar que la manipulación del registro debe ser realizada por usuarios y administradores expertos. Una manipulación inapropiada del registro puede provocar que el sistema no sea capaz de arrancar y haya que instalar de nuevo todo el sistema operativo. Por otra parte, existen varias funciones en la API Win32 que permiten a las aplicaciones acceder y manipular el registro. Por ejemplo, la función RegQueryVa l u e Ex busca los datos de un valor den­ tro de una clave, la función RegCrea t eKeyEx crea una nueva clave dentro del registro, y la función RegDe l e t eKey borra una clave del registro. 8.2.8. Arranque de Windows El arranque de Windows se implementa mediante el programa boo tmgr . exe ubicado en el direc­ torio raíz de la partición de arranque. Este programa en primer lugar comprueba el estado en que se en­ cuentra el sistema operativo. Si estaba en el estado suspendido o el estado hibernado entonces invoca al programa winresume . exe para reanudar la ejecución del sistema. Si el sistema estaba en el estado apa­ gado entonces inicia la carga del sistema operativo mediante la invocación del programa win l o ad . exe. winl oad . exe se encarga de cargar en la memoria principal los componentes del sistema operativo que se ejecutan en modo núcleo: el ejecutivo y el núcleo ubicados en ntoskrnl . exe , la capa HAL ubicada en la librería hal . dl l , el driver win3 2 k . sys que implementa la GUI, y todos los drivers ne­ cesarios de acuerdo con la información especificada en la clave SYSTEM del registro de Windows. A continuación transfiere el control a una rutina de bajo nivel del núcleo que se encarga de inicializar todos los componentes del sistema operativo que se ejecutan en modo núcleo. Además crea el primer proce­ so en modo usuario: el gestor de sesión de usuario ( sms s . exe ) . Este proceso se encarga de iniciar el subsistema de entorno de Windows ( c s r s s . exe ) , cargar diferentes librerías DLL, realizar las operacio­ nes de configuración establecidas en los subárboles del registro, e iniciar al proceso de inicio de sesión (winl ogon . exe ) . El proceso winl ogon . exe se encarga de crear al administrador de validación ( l s a s s . exe ) y el gestor de servicios ( s ervi c e s . exe ) . Además se encarga de realizar todas las tareas necesarias para iniciar la sesión del usuario. El proceso l s a s s . exe se encarga de la autentificación del usuario. Si el nombre de usuario y la contraseña introducidas coinciden con algunos de los perfiles definidos en el registro entonces se inicia el proceso exp l orer . exe que implementa el escritorio de Windows . Por su parte el proceso s e rvi c e s . exe se encarga de crear y controlar a los procesos de servicios de Windows. Este proceso para saber cuáles son los servicios que debe iniciar consulta el registro. 8.2.9. Objetos del núcleo Un objeto del núcleo es una estructura de datos mantenida en el espacio de direcciones del núcleo de Windows NT asociada a una determinada abstracción y gestionada por el administrador de objetos del ejecutivo. La abstracción a la que está asociado un objeto define el tipo del objeto. Por ejemplo, el núcleo asocia a un archivo abierto un objeto archivo, a un proceso un objeto proceso, a un hilo un obj eto hilo, a un semáforo un objeto semáforo, a una clave del registro un objeto clave, a un driver instalado un objeto driver, etc. El sistema operativo Windows 311 Conviene señalar que Windows está escrito en su mayor parte en el lenguaj e de programación C, por lo que estos objetos no deben confundirse con los obj etos utilizados en lenguaj es de programación orientados a objetos como C + + . Los objetos implementados en Windows mediante C soportan ocultación de datos y abstracción, pero no polimorfismo ni herencia. Cada objeto tiene asignado un nombre codificado en Unicode, el ejecutivo mantiene un espacio de nombres en memoria con una organización similar a una estructura de directorios de gráfica acíclica. Windows utiliza el nombre de un objeto para localizar el objeto. Un objeto implementado en Windows consta de dos partes : la cabecera y los datos específicos del objeto. La cabecera contiene la información necesaria para gestionar el obj eto, como por ejemplo: su nombre y directorio en el espacio de nombres, su descriptor de seguridad (ver sección 8.2. 1 0) y un puntero a la estructura que implementa el tipo de obj eto. Esta información es común a todos los objetos e independiente de su tipo. Cada tipo de objeto definido se implementa mediante una estructura que contiene información de­ pendiente del tipo de objeto, como por ejemplo: derechos de acceso, recursos de memoria que puede consumir y punteros a las funciones que se pueden invocar para operar (abrir, cerrar, borrar, etc) sobre este tipo de objeto. El administrador de objetos es el componente del ejecutivo encargado de gestionar (crear, configurar, borrar, etc) todos los objetos del núcleo. Además proporciona una interfaz a los restantes componentes del ejecutivo para crear y operar con los objetos. Diferentes llamadas al sistema requieren de la creación de nuevos obj etos del núcleo o del acceso a objetos ya existentes. Cuando un proceso invoca a una llamada al sistema que requiere de la apertura o de la creación de un objeto del núcleo, el sistema devuelve al proceso como parámetro de retorno de la llamada un descriptor del objeto (handle). Dicho descriptor debe ser utilizado como argumento de entrada en las posteriores llamadas al sistema que realice dicho proceso que involucren a dicho objeto. El sistema mantiene una tabla de descriptores (handle table) local a cada proceso. De esta forma un mismo descriptor puede hacer referencia en dos procesos distintos a objetos distintos. 8.2.10. Seguridad en Windows Mecanismos de seguridad Windows NT dispone de los siguientes mecanismos de seguridad [Tanenbaum, 2009] : • Inicio de sesión seguro. En Windows NT un usuario para que le aparezca la pantalla de inicio de sesión en la que introducir su nombre usuario y su contraseña debe pulsar la combinación de teclas [ c ontro l ] + [ a l t ] + [ supr l . El manej ador de interrupciones del teclado al detectar esta combinación de teclas inicia al autentico programa de inicio de sesión. Los procesos de usuario no disponen de ningún mecanismo para poder cambiar el programa al que redirecciona esta combina­ ción de teclas. Con ello se pretende evitar que un software malicioso pueda simular la ventana de inicio de sesión para capturar la contraseña. Señalar que en algunos casos Windows NT desactiva este mecanismo, de hecho en las versiones cliente de Windows está desactivado por defecto. 312 Ampliación de sistemas operativos • Control de acceso discrecional. Los propietarios de un objeto pueden especificar que otros usuarios pueden utilizar dicho obj eto y que operaciones pueden realizar. • Control de acceso privilegiado. El administrador del sistema es el único que dispone de los permi­ sos necesarios para modificar los permisos establecidos por un usuario. • Protección del espacio de direcciones de un proceso. Cada proceso dispone de un espacio de direcciones virtuales independiente que no puede ser accedido por otros procesos. • Borrado explicito de los marcos de página asignados a un proceso. Los marcos de memoria prin­ cipal asignados a las páginas de la región de pila o al montículo de un proceso se deben rellenar inicialmente con páginas inicializadas con ceros para evitar que el proceso pueda leer la informa­ ción de las páginas contenidas en los marcos que le acaban de ser asignados. • Auditoría de seguridad. El administrador del sistema dispone de mecanismos para poder crear un registro de eventos asociados con la seguridad del sistema. Estructuras de datos utilizadas para implementar la seguridad La implementación de la seguridad en Windows se basa principalmente en el uso de las siguientes estructuras de datos: • Ficha de acceso (access token). Cuando un usuario se autentifica Windows asigna al primer pro­ ceso creado para el usuario, el proceso exp l orer . exe que implementa el escritorio, una ficha de acceso que es heredada por todos los procesos que cree dicho proceso inicial debido a la interac­ ción del usuario con el escritorio. Una ficha de acceso es una estructura que contiene, entre otros, los siguientes datos: Identificador de seguridad del usuario al que pertenece el proceso. En Windows cada usuario tiene asociado un identificador de seguridad (Security IDentifier, SID), que lo identifica de forma univoca. El SID es un número binario largo parte del cual se genera aleatoriamente. El SID de un proceso puede ser obtenido usando la función LookupAc count S i d. - Identificador de seguridad del grupo de usuarios al que pertenece el proceso. Similar al SID de usuario pero aplicado a nivel de grupo. - Grupos. Especifica los grupos a los que pertenece el usuario. Este campo es necesario para implementar la API POSIX. - Lista de control de acceso discrecional. Es la lista de control de acceso asignada por defecto a los objetos creados por el proceso. Privilegios. Permiten proporcionar a un proceso permisos para realizar ciertas tareas reser­ vadas para el administrador, como por ejemplo apagar el computador o el acceso a ciertos archivos. El sistema operativo Windows • 313 Descriptor de seguridad. En Windows cuando se crea un objeto se le asigna un descriptor de seguridad que es una estructura de datos que especifica las operaciones que pueden realizar los diferentes usuarios sobre el objeto. Contiene, entre otros, los siguientes datos: SID del usuario propietario del objeto, SID del grupo propietario del objeto y un puntero a la lista de acceso discrecional (Discretionary Access Control List, DACL) asociada al objeto. La DACL del objeto contiene una o varias entradas. Cada entrada contiene un SID (de usuario o de grupo), el estado asociado a dicha entrada: permitir o denegar, y el conjunto de operaciones seleccionadas sobre el objeto. El descriptor de seguridad de un objeto puede ser pasado como argumento de las llamadas al sistema que producen la creación de dicho objeto. Por ejemplo la función CreateF i l e de la API Win32 que produce la creación de un objeto archivo admite como argumento de entrada información que permite configurar el descriptor de seguridad del archivo. Si no se suministra esta información entonces el sistema utiliza para configurarlo la información contenida en la ficha de acceso del proceso que invoca la llamada. También es posible usar diferentes funciones de la API Win32 para inicializar y configurar el descriptor de seguridad de un objeto, como por ejemplo la función Ini t i a l i z e S ecuri tyDe s c r ip t o r . Un hilo de un proceso para poder operar sobre un objeto ya creado debe primero abrirlo utilizan­ do alguna función de tipo Open. Por ejemplo para abrir un objeto archivo se debe invocar la función OpenF i l e de la API Win32 pasándole como argumento el nombre del objeto y el conjunto de permisos que necesita (lectura, escritura, etc). El monitor de referencia de seguridad del ejecutivo en primer lugar consulta la ficha de acceso del proceso para determinar su SID. A continuación busca en las entradas de la DACL del descriptor de seguridad del objeto una entrada que contenga dicho SID. Cuando la en­ cuentra comprueba si el proceso tiene concedidos todos los permisos que necesita. En caso afirmativo la función devuelve al hilo un descriptor del objeto para que lo utilice en lugar del nombre del objeto en las futuras funciones que operen sobre el objeto abierto. 8.3. Implementación, control y planificación de procesos multihilos en Windows 8.3.1. Implementación de los procesos multihilos en Windows Windows implementa un modelo de proceso multihilo con un hilo del núcleo por cada hilo de usuario. Un proceso es considerado como la unidad de gestión de recursos mientras que un hilo del núcleo es utilizado como la unidad de asignación del procesador. Todos los hilos de un proceso comparten los recursos asociados a dicho proceso (espacio de memoria, tabla de descriptores, ficha de acceso, etc). Un hilo se ejecuta en modo usuario hasta que invoca una llamada al sistema, entonces pasa a ser ejecutado en modo núcleo. También existen hilos creados por el propio sistema operativo para realizar diferentes tareas administrativas que únicamente se ejecutan en modo núcleo. Windows utiliza varias estructuras de datos para implementar el modelo de proceso multihilo. Algu­ nas de estas estructuras son mantenidas en el espacio de direcciones virtuales del proceso, mientras que 314 Ampliación de sistemas operativos Pila en modo usuario Pila en modo usuario ¡------ - - - - - - - - - - - - -------------- ----------- ... ... ... ... ., !1 • PEB ------------ - ------------------ - B loque EPROCESS ;-- r-- - ·-- �� L .. 4 W32PROCESS � Tabla de de s c r ip tores ---- L .... TEB - -- - Bloque KPROCESS - -- --------------- -- B loque KTHREAD - ------- Bloque - KTHREAD Bloque ETHREAD B loque ETHREAD r- - TEB ! Espacio de direcciones del proceso Espacio de direcciones del núcleo --- ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _, .. 4 W32THREAD Pila en modo núcleo • -1 W32THREAD Pila en modo núcleo Principales estructuras de datos mantenidas por Windows para un implementar un proceso multihilo, en este caso de dos hilos Figura 8.2 - otras son mantenidas en el espacio de direcciones virtuales del núcleo. En las siguientes subsecciones se describen las estructuras de datos asociadas a un proceso y las estructuras de datos asociadas a un hilo. Estructuras de datos asociadas a un proceso Windows para implementar un proceso mantiene, entre otras, las siguientes estructuras de datos (ver Figura 8.2) : • Bloque de entorno del proceso (Process Environment Block, PEB). Estructura mantenida en el espacio de direcciones del proceso que contiene, entre otros, los siguientes datos: lista de módulos cargados (archivos . exe y . dl l ) , variables de entorno, directorio de trabaj o actual, e información para gestionar el montículo del proceso. • Tabla de descriptores. Estructura de datos mantenida en el espacio de direcciones del núcleo que contiene los descriptores de los obj etos abiertos (archivos, semáforos, etc) por el proceso. El sistema operativo Windows 315 • Bloque W32PROCESS . Estructura mantenida en el espacio de direcciones del núcleo que contiene la información sobre el proceso a la que necesita acceder win3 2 k . sys . Este bloque es creado la primera vez que un hilo del proceso invoca una llamada al sistema asociada con la GUI de Windows. • Bloque KPROCES S , también denominado bloque de control del proceso (Process Control Block, PCB). Estructura mantenida en el espacio de direcciones del núcleo que contiene los datos que Windows necesita conocer para planificar los hilos del proceso, como por ejemplo: puntero al directorio de páginas del proceso (ver sección 8 . 5 . 3), tiempo de ejecución en modo usuario y en modo núcleo, procesador al que está asociado, prioridad base del proceso, cuanto asignado a un hilo por defecto y punteros para mantener una lista de hilos del núcleo (bloques KTHREAD) asociados al proceso. • Bloque EPROCESS . Estructura mantenida en el espacio de direcciones del núcleo que contiene, entre otros, los siguientes datos relativos a un proceso: bloque KPROCESS , identificador del pro­ ceso, identificador del proceso padre, estatus de salida, fecha y hora de creación y terminación, cuotas de uso de los recursos, información para la gestión de memoria del proceso, puntero a la ficha de acceso, puntero a la tabla de descriptores, puntero al bloque PEB , nombre de ruta del archivo ejecutable que contiene la imagen del proceso, clase de prioridad del proceso, puntero al bloque W32PROCESS y puntero al objeto tarea. Señalar que una tarea Uob) en Windows es un conjunto de procesos. Estructuras de datos asociadas a un hilo Windows para implementar un hilo mantiene, entre otras, las siguientes estructuras de datos (ver Figura 8 .2): • Pila en modo usuario y pila en modo núcleo. Windows asigna a cada hilo de un proceso una pila en modo usuario y una pila en modo núcleo. Un hilo utiliza su pila en modo usuario hasta que invoca una llamada al sistema y comienza a ejecutarse en modo núcleo entonces utiliza su pila en modo núcleo. Cuando un hilo realiza una llamada al sistema o cuando se bloquea a la espera de un evento su contexto hardware se almacena en una estructura de datos CONTEXT que se almacena en la pila del núcleo. El contexto hardware almacenado en la pila es restaurado para poder continuar con la ejecución del hilo justo en el punto en el que fue interrumpida. • Bloque de entorno del hilo (Thread Environment B lock, TEB). Estructura mantenida en el espacio de direcciones del proceso que contiene, entre otros, los siguientes datos: identificador del hilo, información sobre la pila en modo usuario, y puntero al bloque PEB . • B loque KTHREAD. Estructura mantenida en el espacio de direcciones del núcleo que contiene los datos que Windows necesita conocer para planificar y sincronizar la ejecución del hilo. También mantiene un puntero a una estructura con información sobre la pila del núcleo del hilo y un puntero al TEB del hilo. 316 Ampliación de sistemas operativos • B loque W32THREAD. Estructura mantenida en el espacio de direcciones del núcleo que contiene la información sobre el hilo a la que necesita acceder win3 2 k sys . Este bloque es creado la pri­ mera vez que un hilo del proceso invoca una llamada al sistema asociada con la GUI de Windows. . • 8.3.2. Bloque ETHREAD. Estructura mantenida en el espacio de direcciones del núcleo que contiene, entre otros, los siguientes datos relativos a un hilo de un proceso: bloque KTHREAD, puntero al bloque W32THREAD, hora y fecha de creación y terminación del hilo, identificador del proceso al que pertenece el hilo, puntero al bloque EPROCESS de dicho proceso, y dirección de comienzo de la rutina que implementa el hilo. Control de procesos multihilos en Windows Estados de un hilo Un hilo en Windows durante su tiempo de vida puede encontrarse en algunos de los siguientes esta­ dos 8 (ver Figura 8.3): • Inicializado (initialized). Es el estado en que se encuentra un hilo mientras está siendo creado. • Preparado (ready). El hilo está a la espera de ser planificado para ejecución. • Ejecutándose (running). Solo puede existir un hilo en este estado por cada procesador disponible. Un hilo entra en este estado cuando se está ejecutando en un procesador. • Suspendido (standby). Solo puede existir un hilo en este estado por cada procesador disponible. Un hilo en el estado preparado pasa a este estado cuando es seleccionado por el planificador para ser ejecutado en el procesador al que está vinculado a continuación del hilo que lo está utilizando actualmente. Puede suceder que un hilo en el estado suspendido regrese al estado preparado sin llegar a usar el procesador si el planificador selecciona a otro hilo más prioritario para que entre en el estado suspendido. • Terminado (terminated). El hilo ha terminado su ejecución. • Esperando (waiting). Un hilo pasa a este estado cuando tiene que esperar por un determinado suceso, por ejemplo que se complete una operación de E/S . • Transición (transition). Un hilo entra en este estado si al salir del estado esperando las páginas de su pila del núcleo no están cargadas en la memoria principal. Cuando estas páginas sean cargadas el hilo pasará al estado preparado. 8En realidad existen más estados de los aquí enumerados pero se ha preferido omitirlos para facilitar la comprensión del diagrama de transición de estados. El sistema operativo Windows Se selecciona otro hilo más prioritario // /Seleccionado Pasa a ej ecutarse Expropiado Creación 317 Termina Se produce el evento pero su pila del núcleo no está cargada en memoria Figura 8.3 - Diagrama de transición de estados de un hilo en Windows Creación de procesos e hilos Existen varias funciones en la API Win32 que permiten crear un proceso, aunque sin duda la más conocida y utilizada es la función Cre a te Pro c e s s de la librería kerne l 3 2 . dl l . Esta función admite hasta diez argumentos de entrada, entre ellos: el nombre del programa a ejecutar en el hilo o hilos del nuevo proceso, un puntero a las variables de entorno, las prioridades de planificación, el directorio de trabajo del nuevo proceso, y los indicadores para especificar los descriptores de obj etos que heredará el nuevo proceso. Una vez invocada una función CreatePro c e s s se realizan, entre otras, las siguientes acciones [Rus­ sinovich et al., 2009] : l. Validar los argumentos de entrada y convertirlos al formato admitido por los argumentos de la llamada al sistema NtCreateUserPro c e s s . A continuación invocar a esta llamada. 2. En modo núcleo la rutina de tratamiento del núcleo de esta llamada al sistema procesa los argu­ mentos, abre el archivo ejecutable que se desea ejecutar y crea un objeto sección (ver sección 8.5 .2) para más tarde mapear el archivo dentro del espacio de direcciones virtuales del nuevo proceso. 3 . Crear un objeto proceso en el ejecutivo de Windows . La creación de este objeto requiere asignar y configurar un bloque EPROCESS , crear la ficha de acceso inicial, crear la tabla de descripto- 318 Ampliación de sistemas operativos res, crear el espacio de direcciones inicial del proceso (ver sección 8.5 .2), inicializar el bloque KPROCESS, configurar el PEB y concluir la configuración del espacio de direcciones virtuales del proceso, la cual entre otras acciones requiere mapear el archivo ejecutable dentro del espacio del nuevo proceso. 4. Crear un objeto hilo inicial en el ejecutivo de Windows. La creación de este objeto requiere crear e inicializar un bloque ETHREAD, crear la pila en modo usuario y en modo núcleo, configurar el contexto hardware inicial, asignar y configurar el TEB del hilo, y configurar el bloque KTHREAD. El nuevo hilo es puesto en el estado inicializado. 5 . Realizar una postinicialización especifica del subsistema de entorno de Windows. En este punto la llamada al sistema NtCreateUserPro c e s s ha retornado a modo usuario y existe un nuevo pro­ ceso creado que consta de un único hilo. Sin embargo todavía es necesario realizar algunas tareas adicionales relacionadas con el subsistema de entorno de Windows ( c s r s s . exe ) para comple­ tar la inicialización del proceso [Tanenbaum, 2009] : kerne l 3 2 . dl l en primer lugar envía un mensaje a c s r s s . exe para comunicarle que un nuevo proceso ha sido creado y le adjunta los des­ criptores del objeto proceso y del obj eto hilo creados para que pueda duplicarlos. En segundo lugar c s r s s . exe registra al proceso y su hilo en las tablas que mantiene con todos los procesos e hilos Win32 existentes. En tercer lugar asigna e inicializa una estructura W3 2 PROCE S S y W3 2 THREAD. Finalmente muestra en la pantalla un cursor con la forma de corona circular en movimiento para indicarle al usuario que se está iniciando un programa pero que puede usar el cursor mientras tanto. Si el proceso no realiza una llamada al sistema asociada con la GUI en los próximos segundos el cursor vuelve a su forma habitual (una flecha). Si el proceso realiza una llamada al sistema asocia­ da con la GUI transcurrido un cierto tiempo se muestra una ventana y el cursor vuelve a su forma original. 6. Comenzar la ejecución del hilo inicial. En este punto c s r s s . exe ha devuelto el control a la fun­ ción CreatePro c e s s que ahora invoca a la llamada al sistema NtResumeThread para pasar al estado preparado al hilo inicial del nuevo proceso y que éste pueda ser planificado para ejecución. Además devuelve al proceso padre los identificadores y descriptores del proceso e hilo recién crea­ dos. Si la creación se ha realizado con éxito entonces C r e a t e Proc e s s devuelve como valor de retorno un valor distinto de cero, en caso de error devuelve cero. Por otra parte un hilo de un proceso puede crear otro hilo dentro del mismo proceso invocando a la función Creat eThread de la librería kerne l 3 2 . dl l . Las acciones que realiza esta función guardan cierta analogía con las acciones anteriormente descritas para la función CreatePro c e s s pero aplica­ das a nivel de hilo. Si se desea crear un hilo dentro de un proceso distinto se puede usar la función Creat eRemo t eThread. Otras operaciones sobre procesos e hilos Aparte de las funciones C r e a t e Pro c e s s y Creat eThread existen otras muchas funciones dispo­ nibles en la API Win32 para operar a nivel de procesos o de hilos, como por ejemplo: El sistema operativo Windows 319 • OpenPro c e s s . Abre un objeto proceso. • OpenThread. Abre un objeto hilo. • Exi tPro c e s s . Termina la ejecución de un proceso y todos sus hilos. • Exi tThread. Termina la ejecución de un hilo • Ge tCurrent Pro c e s s id. Devuelve el identificador del proceso actual. • GetCurrentThreadid. Devuelve el identificador del hilo actual. • GetExi tCodePro c e s s . Devuelve el estatus de salida de un proceso. • GetExi tCodeThr ead. Devuelve el estatus de salida de un hilo. • S l eep. Pone al hilo en el estado esperando durante un determinado periodo de tiempo expresado en milisegundos que se debe introducir como argumento de entrada de la función. • Swi t chToThread. Cede el uso del procesador a otro hilo en el estado preparado. 8.3.3. Planificación de procesos multihilos en Windows Windows es un sistema operativo de núcleo expropiable que utiliza como unidad de planificación a los hilos del núcleo. Windows implementa un algoritmo de planificación de tipo expropiativo basado en múltiples colas de prioridad y realimentación. Se consideran 32 niveles de prioridad comprendidos dentro del rango [0, 3 1 ] , siendo O la prioridad más baj a posible y 3 1 la más alta. Estos 32 niveles son divididos en dos grupos (ver Figura 8 .4) : • Niveles de tiempo real. Comprende los 1 6 niveles más prioritarios, es decir, los niveles de prioridad en el rango [ 1 6, 3 1 ] . Windows asigna estos niveles de prioridad a los hilos del núcleo asociados a tareas críticas del sistema, como por ejemplo: la gestión de la memoria, la gestión de la caché, etc. También un usuario con los privilegios adecuados puede hacer que un hilo de un proceso de usuario se ejecute con un nivel de prioridad de tiempo real, aunque no es recomendable para no interferir con las tareas del sistema. Un hilo con un nivel de prioridad de tiempo real nunca ve modificada su prioridad durante su tiempo de vida, es decir, su prioridad se mantiene fija. Señalar que realmente Windows no implementa las características típicas de una planificación de tiempo real. El nombre de niveles de tiempo real se utiliza únicamente para indicar que los hilos con estos niveles de prioridad se planifican preferentemente a los restantes niveles. • Niveles dinámicos. Comprende los niveles de prioridad en el rango [ 1 , 1 5 ] . En estos niveles se eje­ cutan la mayoría de los procesos de los usuarios. Un hilo con un nivel de prioridad dinámico puede ver modificada su prioridad durante su tiempo de vida. Windows bajo determinadas circunstancias puede incrementar durante un corto periodo de tiempo la prioridad de un hilo. 320 Ampliación de sistemas operativos Prioridad más alta 31 � Niveles de tiempo real 16 15 1 1 1 1 1 1 1 1 1 1 1 - Niveles dinámicos 1 Prioridad más baj a Figura 8.4 - o Nivel reservado Niveles de prioridad definidos en Windows El nivel de prioridad O está reservado por Windows para la ejecución del hilo de inicialización de pá­ ginas a cero (Zero page thread) (ver sección 8.5 .4). Existe una instancia de este hilo por cada procesador existente en el computador. Prioridad de un proceso y de un hilo Cada proceso tiene asignado una prioridad denominada prioridad base del proceso Pbp que es fun­ ción de la clase de planificación a la que pertenezca el proceso. Se distinguen seis posibles clases de planificación: tiempo real (real-time), alta (high), por encima de normal (above normal), normal, debajo de normal (below normal) y ociosa (idle). La Pbp por defecto asociada a cada una de estas clases es : 24, 1 3 , 1 0, 8, 6 y 4, respectivamente. Algunos procesos del sistema tienen una Pbp ligeramente superior al nivel por defecto. Por ejemplo el gestor de sesión tiene asociada una Pbp ligeramente superior al valor por defecto (8) de la clase de planificación normal. La clase de planificación, y en consecuencia la prioridad base de un proceso, es heredada por defecto de su proceso padre. Para cambiar la clase de planificación de un proceso se puede usar por ejemplo la función S e t P r i o r i tyC l as s . Por s u parte, cada hilo tiene asociada dos prioridades: • Prioridad base del hilo Pbh · Esta prioridad es función de la clase de planificación a la que pertenece el proceso al que está asociado el hilo y de la prioridad relativa del hilo Pr respecto a Pbp · La prioridad relativa de un hilo puede tomar alguno de los siguientes valores : tiempo crítico (time El sistema operativo Windows 321 critica!) P r = 1 5 , la más alta (highest) P r = 2, por encima de normal (above normal) P r = 1 , normal P r = O , debajo de normal (below normal) P r = - 1 , la más baja (lowest) Pr = - 2 y ociosa (idle) P r = - 1 5. Cuando la prioridad relativa de un hilo se configura al valor tiempo crítico ( P r = 1 5) entonces Pbh se fij a al nivel de prioridad más alto dentro del grupo de niveles de prioridad en los que se encuentre la Pbp de la clase de planificación a la que pertenece el proceso al que está asociado el hilo. Luego si P r = 1 5 y el proceso al que está asociado el hilo pertenece a la clase de tiempo real entonces Pbh = 3 1 . En el caso de que el proceso pertenezca a cualquiera de las restantes clases de planificación entonces Pbh = 1 5 . Por el contrario s i l a prioridad relativa de un hilo s e configura al valor ocioso (P r = - 1 5) entonces la Pbh se fij a al nivel de prioridad más bajo dentro del grupo de niveles de prioridad en los que se encuentre la Pbp de la clase de planificación a la que pertenece el proceso al que está asociado el hilo. Luego si P r = - 1 5 y el proceso al que está asociado el hilo pertenece a la clase de tiempo real entonces Pbh = 1 6. En el caso de que el proceso pertenezca a cualquiera de las restantes clases de planificación entonces Pbh = l . En el caso de que la prioridad relativa P r se configure con los valores: la más alta (P r = 2), por encima de normal ( P r = 1 ) , normal (P r = 0), debajo de normal (P r = - 1 ) o la más baja ( P r = - 2 ), entonces la prioridad base del hilo se calcula mediante la siguiente expresión: Pbh = Pbp + P r (8. 1 ) La funciones GetThreadPr i o r i ty y S e tThreadP r i o r i ty permiten obtener y configurar, res­ pectivamente, la prioridad relativa de un hilo. • e Prioridad actual del hilo P a · Es la prioridad que utiliza el sistema operativo para decidir que hilo se debe planificar en un procesador. Inicialmente Pa es igual a Pbh pero si el proceso al que perte­ nece el hilo pertenece a una clase distinta de la tiempo real el sistema operativo puede aumentar temporalmente la Pa de un hilo dentro del rango [Pbh + 1 , 1 5] bajo determinadas circunstancias. Ejemplo 8.1 Supóngase un proceso que consta de un hilo. El proceso tiene asociada la clase de planificación normal, luego la prioridad base del proceso es por defecto Pbp = 8 . Supóngase además que la prioridad relativa del hilo está configurada al valor P r = 2 (la más alta). En dicho caso la prioridad base del hilo será igual a Pbh = Pbp + P r = 8 + 2 = 1 0 . Por su parte la prioridad actual del hilo tomará inicialmente el valor P a = Pbh = 1 0. Posteriormente el sistema operativo puede elevar, bajo determinadas circunstancias, temporalmente Pa a algún nivel de prioridad dentro del rango [ 1 1 , 1 5 ] . • Implementación y gestión de las colas de prioridad Los hilos en el estado preparado de la misma prioridad se mantienen en una cola PIFO. Windows mantiene un conjunto de 32 colas de hilos en el estado preparado por cada procesador existente en el 322 Ampliación de sistemas operativos sistema. Además por cada conjunto mantiene una máscara de 32 bits denominada resumen de prepara­ dos (ready summary). A las estructuras de datos que mantiene Windows para implementar las colas de planificación se les denomina base de datos del distribuidor (dispatcher database). Cada bit j de un resumen de preparados está asociado a una cola j, si el bit j está activado significa que la colaj no esta vacía. Windows examina el resumen de preparados para determinar la cola de mayor prioridad no vacía, ya que siempre planifica para ejecutarse en un procesador al primer hilo de la cola de prioridad más alta no vacía del conjunto de colas asociada a dicho procesador. S i existen varios hilos en la cola de mayor prioridad entonces se aplica una algoritmo de tumo rotatorio, es decir, cada hilo se ejecuta durante un cuanto a cuyo término pasa al final de la cola y comienza a ejecutarse el siguiente hilo de la cola. Si solo existe un hilo en la cola de mayor prioridad, dicho hilo va recibiendo los cuantos que vaya necesitando hasta completar su ejecución. Si durante la ejecución de un hilo H l entra en el estado preparado un hilo de mayor prioridad H2 entonces se le expropia el procesador al hilo H l sin esperar a que termine su cuanto. H l es colocado al principio de la cola y cuando sea planificado se ejecutará durante el tiempo de cuanto que le restaba por ejecutar. Sentido de análisis Colas de hilos en el estado preparado asociadas a la CPU 1 Prioridad más alta '-T-......_.-' Resumen de preparados Cabecera cola 31 Cabecera cola 30 Cabecera cola Cabecera cola 1 Prioridad más baja Figura 8.5 - Windows Cabecera cola o Posible estado de las colas de hilos preparados asociadas a un procesador en un sistema El sistema operativo Windows e 323 Ejemplo 8.2 Supóngase que el estado de las colas de hilos preparados asociadas al procesador CPU l de un sistema Windows es el que se muestra en la Figura 8 . 5 . Se observa que no existe ningún hilo en la cola 3 1 ni en la cola i. Por el contrario hay dos hilos en la cola 30, tres hilos en la cola 1 y un hilo en la cola O. Para determinar cuál será el próximo hilo que va a ser planificado en CPU l Windows examina de iz­ quierda a derecha el resumen de preparados de las colas de hilos preparados de CPU l en busca de un 1 , es decir, busca la cola asociada al nivel de prioridad más alto que no esté vacía. Así, en primer lugar examina el bit O, éste vale O ya que la cola 3 1 está vacía. A continuación examina el bit l . Como la cola 30 contiene dos hilos el bit 1 tiene el valor l . Luego se selecciona para ser planificado el hilo situado más cerca de la cabecera de la cola 30. • Duración del cuanto La duración de un cuanto depende del tipo de versión de Windows: cliente o servidor. En el caso de una versión cliente el cuanto asignado a un hilo tiene una duración por defecto de 2 tics de reloj , mientras que el caso de una versión servidor tiene una duración de 12 tics de reloj . La duración de un tic de reloj depende de cada plataforma. En el caso de la arquitectura x86 un tic de reloj tiene una duración de 1 0 ms, luego l a duración d e u n cuanto e s de 20 m s e n e l caso de las versiones cliente de Windows y 1 20 ms en el caso de las versiones servidor. Estos valores pueden ser incrementados por el administrador en un factor de 2, 4 o 6, si así lo desea. Windows también aumenta el cuanto del hilo asociada a la ventana que se pone en primer plano en el escritorio de acuerdo con una cantidad especificada en el registro de Windows. Con esta medida se pretende mejorar la interactividad con el usuario. Estructura e invocación del planificador El planificador de Windows, a diferencia de otros sistemas operativos, no es implementado con una única rutina que se ejecuta dentro de un hilo especial del núcleo, sino mediante un conjunto de rutinas denominadas de forma colectiva como distribuidor o despachador del núcleo (kernel's dispatcher). Estas rutinas son invocadas durante la ejecución de un hilo en modo núcleo cuando se produce determinados eventos, como por ejemplo: • Un hilo ha pasado al estado preparado, bien porque acaba de ser creado o porque se ha producido el evento por el que estaba esperando, por ejemplo, que se completara una operación de E/ S . • U n hilo sale del estado ejecutándose porque h a terminado, h a consumido s u cuanto, cede e l uso del procesador a otro hilo o se bloquea en espera de que se produzca un evento (entra en el estado esperando). • La prioridad de un hilo es cambiada por una llamada al sistema o por el propio distribuidor del núcleo. 324 Ampliación de sistemas operativos • Se cambia mediante una llamada al sistema, como por ejemplo S e tTheadAf f i n i tyMa s k, el procesador en que debe ejecutarse un hilo. Por defecto un hilo puede ejecutarse en cualquiera de los procesadores existentes en la máquina, pero Windows permite asociar la ejecución de un hilo a un determinado procesador, es lo que se conoce como afinidad del procesador. Aumento temporal de la prioridad actual de un hilo B ajo determinadas circunstancias Windows puede aumentar temporalmente la prioridad actual Pa de un hilo perteneciente a una clase de planificación distinta de la de tiempo real que acaba de salir del estado esperando o que lleva mucho tiempo en el estado preparado y todavía no ha accedido al procesador. El objetivo de este aumento de la prioridad es compensar al hilo por el tiempo que ha perdido esperando. Al aumentar su prioridad tendrá más probabilidades de ser elegido para ser planificado. Inicialmente la prioridad actual de un hilo es igual a su prioridad base, es decir, Pa = Phh · B aj o determinadas circunstancias la prioridad actual s e aumenta a u n valor Phh + x comprendido dentro del rango [Pbh + 1 , 1 5 ] . Este aumento de la prioridad actual es temporal, ya que cada vez que el hilo consume un cuanto su prioridad actual se decrementa en una unidad, es decir, Phh + x - l. La prioridad actual dej a de decrementarse cuando alcanza e l valor d e l a prioridad base del hilo, e s decir, Pa = Phh · E l valor del incremento x d e l a prioridad base de un hilo e s fijado por el sistema operativo e n función de las circunstancias por las que el hilo pasó al estado esperando o si lleva demasiado tiempo en el estado esperando. Entre las circunstancias que producen un aumento de la prioridad de un hilo se encuentran las siguientes: • Cuando se completa una operación de E/S, se despiertan a los hilos que estaban esperando por dicha operación, el incremento x de prioridad actual que reciben dependen del dispositivo de E/S en el que se ha realizado la operación. Si se trata de un disco duro, un CD-ROM, un puerto paralelo o una tarj eta de vídeo, entonces x = l. Si se trata de una operación de red entonces x = 2. Si se trata del teclado o del ratón entonces x = 6. Finalmente si se trata de una tarj eta de sonido entonces X = 8. • Cuando se despierta a un hilo que estaba esperando por un mecanismo de sincronización (ver sección 8 .4.2) como por ejemplo un semáforo o un evento, entonces x = 2 si pertenece al proceso que se encuentra en primer plano (foreground). En caso contrario x = l. • Cuando se despierta a un hilo asociado a una ventana de la GUI su prioridad se incrementa en x = 2 con el fin de mej orar la interactividad con el usuario. • Si un hilo lleva en el estado preparado durante más de 4 segundos entonces configura la prioridad actual del hilo a su máximo valor posible, es decir, Pa = 1 5 y puede ejecutarse durante cuatro cuantos consecutivos al término de los cuales su prioridad actual se configura a su valor base Pa = Phh · Con esta medida se pretende evitar el abandono de hilos. El sistema operativo Windows e 325 Ejemplo 8.3 Supóngase un hilo cuya prioridad base es Pbh = Pbp + Pr = 8 + 2 = 10. Inicialmente su prioridad actual es Pa = 1 0. Si el hilo pasa al estado esperando en espera de que se complete una operación de E/S en la red entonces cuando se complete la operación su prioridad actual se incrementará en x = 2 unidades, es decir, Pa = Pbh + 2 = 10 + 2 = 12. Cuando complete un cuanto su prioridad se reducirá a Pa = 1 1 y cuando complete un segundo cuanto su prioridad se reducirá a Pa = 1 0. Es decir, al cabo de dos cuantos recupera su valor de prioridad actual inicial. • 8.4. Sincronización y comunicación entre procesos multihilos en Windows 8.4.1. Mecanismos de sincronización usados por el sistema operativo Windows es un sistema operativo de código reentrante que soporta multiprogramación y multiproce­ samiento simétrico. Para garantizar la exclusión mutua en el uso de las secciones críticas del código del sistema operativo se utilizan diferentes mecanismos de sincronización, siendo los spinlocks (ver sección 3 . 3) uno de los más utilizados. Las rutinas básicas para la manipulación de un spinlock son: • KeAc qu i r e SpinLock ( ) . Para adquirir (cerrar) el spinlock. • KeRe l ea s e SpinLo c k ( ) . Para liberar (abrir) el spinlock. Estas rutinas son implementadas en el kernel de Windows y pueden ser invocadas por los componen­ tes de Windows que se ejecutan en modo núcleo. 8.4.2. Mecanismos de sincronización entre procesos multihilos disponibles en Windows Objetos del distribuidor Otros mecanismos de sincronización disponibles en Windows son los semáforos, los semáforos bina­ rios o mutexes, y los eventos. Estos mecanismos pueden ser utilizados tanto por los propios componentes de Windows como por los hilos de los procesos de los usuarios. Se implementan mediante objetos del núcleo que disponen de capacidades de sincronización: objeto semáforo, objeto mutex, objeto evento, etc. A estos objetos se les denomina de forma general como objetos del distribuidor ya que su uso puede alterar o afectar a la planificación del hilo que se sincroniza con el obj eto. Un objeto del distribuidor puede encontrarse en dos posibles estados: estado no señalado y estado señalado. El estado inicial de un objeto se configura cuando es creado. Un obj eto del distribuidor se encuentra en el estado no señalado hasta que se produce un determinado suceso que hace que pase al estado señalado. Cuando el objeto pasa al estado señalado el sistema operativo realiza unas determinadas acciones, que generalmente consisten en despertar a uno o varios hilos que estaban esperando por el objeto. Una vez realizadas dichas acciones, el objeto puede retomar o no al estado no señalado. Tanto el 326 Ampliación de sistemas operativos suceso que produce el cambio de estado del objeto como las acciones que realiza el sistema operativo dependen de cada tipo de obj eto. Por ejemplo, en un objeto semáforo binario o mutex cuando un hilo libera el semáforo el objeto pasa al estado señalado. Entonces el núcleo desbloquea a uno de los hilos que se encontraban en el estado esperando dentro de la cola del semáforo para que continúe su ejecución y devuelve al objeto al estado no señalado. Un hilo puede sincronizarse con un objeto del distribuidor, para ello debe invocar a alguna de las funciones de la API Win329 pasándole como argumento el descriptor del objeto por el que desea esperar. Por ejemplo se podría utilizar la función wa i tForS ingl eObj e c t . También se podría utilizar la función Wa i tF o rMu l t ip l eübj e c t s si el hilo desea esperar por varios obj etos. Cuando se atiende la llamada, el hilo pasa al estado esperando y se añade a la cola de hilos esperando por un determinado objeto. Cuando se produzca el suceso asociado al objeto por el que espera el hilo entonces éste será despertado. También es posible pasar como argumento de entrada de la función un indicador para especificar que la espera debe ser cancelada si se sobrepasa una determinada cantidad de tiempo. Semáforos generales y binarios Un objeto semáforo general o semáforo con contador se crea con la función Crea t e s emaphor e . Además esta función también permite inicializarlo y fijar s u valor máximo. L a operaciones s i gna l_s em (operación V) y wa i t_s em (operación P) se implementan con las funciones Re l ea s e S emaphore y Wa i tForS ingl eObj e c t , respectivamente. En el caso de un objeto semáforo binario o mutex, su creación e inicialización se realiza con la fun­ ción CreateMut ex. Mientras que las operaciones básicas s i gnal_s em y wai t_s em se implementan con las funciones Re l ea s eMut ex y Wa i tForS ingl eObj e c t , respectivamente. Eventos Un objeto evento se crea con la función Creat eEvent . Esta función también permite especificar su estado inicial: señalado o no señalado. Si la función se ejecuta con éxito devuelve el descriptor del obj eto. Un hilo puede esperar por un objeto evento determinado invocando a Wa i tForS ingl eObj e c t pa­ sándole como argumento el descriptor del objeto. Otro hilo para avisar que el evento se ha producido debe invocar a S e tEvent . Las acciones que realiza esta función dependen del tipo de evento. Si se trata de un evento de notificación entonces se desbloquean a todos los hilos que estaban esperando por el evento y el evento permanece en el estado señalado hasta que un hilo invoca a una función Re s e tEvent sobre el evento. En el caso de un evento de sincronización se despierta a un hilo de los que estaban esperando por el evento y el evento es configurado al estado señalado. 9En el caso de un componente de Windows en modo núcleo éste invocaría directamente a la llamada al sistema correspon­ diente sin necesidad de usar la función de la API Win32. El sistema operativo Windows 327 Si se invoca a la función S e t Event y no existe ningún hilo esperando en el evento entonces sim­ plemente se cambia el estado del evento a señalado. De esta forma si un hilo invoca a una función Wa i tForS ingl eObj e c t no tendrá que esperar por el evento porque ya se habrá producido. La función Pu l s eEvent es similar a S e t Event con la diferencia de que si no existe ningún proceso esperando entonces el evento se pierde ya que el evento no se pone en el estado señalado. 8.4.3. Mecanismos de comunicación entre procesos multihilos disponibles en Windows Windows implementa diversos mecanismos de comunicación entre procesos multihilos, entre ellos : tuberías, conectores, mailslots, llamadas a procedimientos remotos y objetos compartidos. En las si­ guientes subsecciones se describen estos mecanismos. Tuberías El funcionamiento de las tuberías implementadas en Windows es similar al estudiado en los SOBU­ NIX. La principal novedad de la implementación de las tuberías en Windows es que existen dos modos de funcionamiento: • Modo byte. Por la tubería se transmite un fluj o no estructurado de datos (bytes) con un tamaño máximo fijo. Este modo coincide con la implementación de las tuberías en los SOBUNIX. • Modo mensaje. Por la tubería se transmite un fluj o de bytes estructurado en mensaj es de un cierto tamaño. Por ejemplo un flujo de bytes de 256 bytes puede estructurarse en dos mensaj es de 1 28 bytes cada uno. Las tuberías sin nombre se crean con la función C r e a t e P i p e . Para leer o escribir en ellas se deben usar las funciones ReadF i l e y wr i t eF i l e , respectivamente. En el caso de las tuberías con nombre se crean mediante la función CreateNamedP ipe. Para conectarse a una tubería con nombre el proceso cliente debe utilizar las funciones C r e a t eF i l e o Cal lNamedP ipe. Cuando un hilo deja de usar una tubería debe usar la función C l o s eHand l e para liberar los recursos que utiliza. Conectores Los conectores (sockets) se crean usando la función s o c k e t . Si un hilo desea conectarse a un conec­ tor ya creado debe usar la función C onne c t . Una vez conectado si desea enviar información debe usar la función S end, mientras que para recibir información debe usar la función Recv. Cuando se termina de usar un conector debe cerrarlo utilizando la función C l o s e S o c k e t . 328 Ampliación de sistemas operativos Mailslots Los mailslots son un mecanismo de comunicación unidireccional entre procesos multihilos importa­ do del sistema operativo OS/2. Se puede utilizar tanto localmente dentro de una misma máquina como para comunicaciones en red, aunque no operan en redes de área ancha como Internet. Los mailslots permiten enviar un mismo mensaj e a múltiples receptores. Sus principales inconvenientes es que son unidireccionales y que no generan de forma explicita un mensaje de confirmación de recepción del en­ vío. Un hilo puede crear un mailslot usando la función CreateMa i l s l o t la cual devuelve un descriptor del objeto mailslot creado. Una vez creado, otros hilos si conocen el descriptor del mailslot pueden escribir mensajes en el mailslot usando la función Wr i t e F i l e . El propietario del mailslot puede leer los mensajes usando la función ReadF i l e . Cuando un hilo dej a de usar un mailslot debe usar la función C l o s eHand l e para liberar los recursos que utiliza. Llamadas a procedimientos remotos Las llamadas a procedimientos remotos (Remote Procedure Calls, RPCs) son un mecanismo de comunicación entre procesos que permite a un proceso A invocar a un procedimiento implementado en otro proceso B de la misma máquina local o de una máquina remota. El proceso B ejecuta dicho procedimiento y devuelve el resultado al proceso A. Este mecanismo se suele implementar mediante paso de mensaj es de acuerdo a una estructura del tipo cliente-servidor. La capa de transporte de mensajes puede implementarse mediante conectores TCP/IP, tuberías con nombre o llamadas a procedimientos remotos avanzados (Advanced Local Procedure Calls, ALPCs). Las ALPCs son un mecanismo de comunicación entre procesos usado internamente por Windows que permite el paso de mensaj es a alta velocidad. Las ALPCS se implementan en el ejecutivo de Windows. Objetos compartidos Algunos objetos de Windows pueden ser compartidos por varios procesos. Este es el caso de un objeto sección que permite mapear un archivo dentro del espacio de direcciones virtuales de cada uno de los procesos que comparten el obj eto. Los cambios que realice un proceso sobre el archivo asociado al objeto sección dentro de su espacio de direcciones serán visibles para todos los procesos que comparten el objeto. 8.5. Gestión de memoria en Windows 8.5.1. Administrador de memoria Windows utiliza la técnica de demanda de página para administrar la memoria principal. Todas las tareas relativas a la administración de memoria son realizadas por el administrador de memoria que es uno de los servicios que ocupa una mayor parte del código del ejecutivo de Windows, lo que da idea de la complejidad del mismo. El sistema operativo Windows 329 El administrador de memoria se implementa como un conjunto de rutinas que pueden ser invocadas dentro del contexto de los hilos de los procesos. Además también utiliza para su implementación los siguientes hilos del sistema: • Gestor de configuración del equilibrio. Se encarga de invocar periódicamente o si se cumplen determinadas circunstancias a otros hilos asociados al administrador de memoria. • Gestor de los conjuntos de trabajo. Se encarga de implementar las políticas generales de gestión de memoria: reemplazamiento de páginas, gestión de los conjuntos de trabajo, envejecimiento de páginas, etc. Es invocado por el gestor de configuración del equilibrio una vez por segundo o si la memoria libre disminuye por debajo de un determinado límite. • lntercambiador de pilas de procesos o hilos. Se encarga de realizar el intercambio de procesos e hilos dentro y fuera de la memoria principal. Este hilo es despertado por el gestor de configuración del equilibrio o por el planificador cuando hay que realizar una operación de intercambio. • Escritor de páginas modificadas. Se encarga de escribir páginas modificadas en los archivos de pá­ ginas (ver sección 8 . 5 .5). Este hilo es despertado cuando el tamaño de la lista de hilos modificados excede un determinado límite. • Escritor de páginas mapeadas. Se encarga de escribir páginas modificadas en los archivos que mapean dichas páginas. Este hilo es despertado cuando el tamaño de la lista de hilos modificados excede un determinado límite o si las páginas de un archivo han estado en esta lista durante más de cinco minutos. • Hilo de puesta de páginas a cero. Se encarga de inicializar con ceros las páginas de la lista de marcos libres y pasarlos a la lista de marcos inicializados a cero (ver sección 8.5 .4). • Hilo de segmento dereferenciado. Se encarga de la reducción de la caché del sistema, así como del aumento o la disminución de tamaño de los archivos de páginas. El administrador de memoria de Windows utiliza diferentes algoritmos y reglas heurísticas para realizar sus tareas. Señalar que estos algoritmos y reglas no han sido hechos públicos por Microsoft. 8.5.2. Gestión de la memoria virtual En Windows cada proceso tiene su propio espacio de direcciones virtuales. Un proceso ejecutándose en modo usuario únicamente puede acceder a su espacio de direcciones de usuario. Las modificaciones que un proceso realice en su espacio no son visibles al resto de procesos salvo que las realice en una región de memoria compartida. Por otra parte, cuando el proceso se ejecuta en modo núcleo, como por ejemplo cuando atiende una llamada al sistema, puede acceder a todo el espacio de direcciones virtuales, tanto al del usuario como al del sistema operativo. Conviene recordar que cuando un proceso se ejecuta en modo núcleo en realidad se está ejecutando el sistema operativo pero en el nombre del proceso. 330 Ampliación de sistemas operativos OxFFFFFFFF Espacio de direcciones virtuales del sistema operativo O x8 0 0 0 0 0 0 0 Ox7 FFEFFFF Reservada (64 KiB) Espacio de direcciones virtuales del usuario O xO O O l O O O O OxO O O O O O O O 2 GiB 2 GiB Reservada (64 KiB) - Estructura más habitual del espacio de direcciones virtuales utilizado por Windows en arquitecturas Intel x86 de 32 bits Figura 8.6 En Windows, al igual que en otros sistemas operativos, el tamaño y la distribución del espacio de direcciones virtuales dependen de cada arquitectura. En la arquitectura Intel x86 de 32 bits el espacio de direcciones virtuales tiene un tamaño máximo de 232 = 4 GiB . Windows divide este espacio en cuatro zonas (ver Figura 8.6): • Espacio reservado para la captura de punteros nulos. Esta zona no es accesible por los procesos. Tiene un tamaño de 64 KiB . • Espacio de direcciones del usuario. Contiene las regiones de un proceso: código, datos, montículo, pilas de los hilos, regiones de memoria compartida y archivos mapeados en memoria. Tiene un tamaño próximo a los 2 GiB . • Espacio reservado para la captura de punteros erróneos. Esta zona no es accesible por los proce­ sos. Tiene un tamaño de 64 KiB . • Espacio de direcciones del sistema operativo. Contiene el código y los datos de los distintos com­ ponentes del sistema operativo: HAL, ejecutivo, núcleo, drivers, etc. También contiene las tablas El sistema operativo Windows 331 de páginas de los procesos, la caché del sistema y la reserva de memoria paginada y no paginada. Tiene un tamaño de 2 GiB . En esta arquitectura Windows también puede trabaj ar con un espacio de direcciones de usuario de casi 3 GiB con lo que el espacio de direcciones del sistema operativo queda reducido a 1 GiB . Obviamente, en las arquitecturas de 64 bits el espacio de direcciones de usuario puede ser mucho más grande. Por ejemplo en la arquitectura AMD x64 los procesos de 32 bits pueden tener un espacio de direcciones de usuario de casi 4 GiB . Mientras que los procesos de 64 bits pueden tener un espacio de usuario de 8 TiB . En esta arquitectura el espacio de direcciones del sistema operativo también tiene un tamaño de 8 TiB . En la técnica de demanda de página el espacio de direcciones virtuales se divide en páginas cuyo tamaño depende de la arquitectura y del sistema operativo. Windows en las arquitecturas Intel x86 y AMD x64 suele trabaj ar con un tamaño de página de 4 KiB , aunque en determinadas circunstancias también trabaj a con páginas grandes de 4 MiB para mejorar el rendimiento del sistema. En Windows una página i del espacio de direcciones virtuales puede encontrarse en tres posibles estados : • Libre. L a página n o está siendo utilizada actualmente. Cualquier referencia a una dirección virtual asociada a una página libre produce un fallo de página por referencia a una dirección ilegal. A este tipo de fallos de página en Windows se les denomina violación de acceso. • Asignada (committed). La página está asociada a una determinada información (código, datos, etc). • Reservada. Son páginas libres que se reservan para contener una determinada información. Por ejemplo, cuando se crea la pila de usuario de un nuevo hilo se reservan varias páginas virtuales libres contiguas en el espacio de direcciones del proceso para su implementación. De las páginas reservadas para la pila inicialmente solo una de ellas pasa al estado asignada. Estructuras de datos para implementar el espacio de direcciones virtuales de un proceso El administrador de memoria de Windows cuando se crea un proceso asigna a cada región existen­ te en el espacio de direcciones virtuales de un proceso un descriptor de direcciones virtuales (Virtual Address Descriptor, VAD) que es una estructura que contiene, entre otros, los siguientes datos: • Rango de direcciones virtuales reservado para la región. • Objeto sección. Es un obj eto del núcleo que establece la correspondencia con el almacenamiento de respaldo de las páginas de la región en memoria secundaria (ver sección 8 . 5 .5). Este objeto permite mapear un archivo dentro de una región del espacio de direcciones virtuales. • Tipo de región. La región puede ser privada para uso exclusivo para el proceso o compartida por varios procesos . • Protección : solo lectura, lectura y escritura, y copiar a l escribir. 332 Ampliación de sistemas operativos Para encontrar más rápidamente el VAD que contiene una determinada dirección virtual, todos los VADs del espacio de direcciones de un mismo proceso son organizados en un árbol AVL. Funciones de la API Wln32 para la gestión del espacio de direcciones virtual de un proceso Existen diferentes funciones en la API Win32 que permiten operar sobre el espacio de direcciones virtuales de un proceso, entre las más utilizadas se encuentran las siguientes: • Vi rtualAl l o c . Permite reservar o asignar una cierta región en el espacio de direcciones virtuales de un proceso. • V i r tua l Fre e . Permite liberar, desasignar, o liberar y desasignar una cierta región en el espacio de direcciones virtuales de un proceso. • Vi r tua l Pro t e c t . Permite cambiar los permisos de una región. • Vi r tua l Query. Devuelve información sobre el estado de una cierta región. • CreateF i l eMapp ing. Crea una objeto de archivo mapeado, que es otra forma de denominar a un objeto sección. • Op enF i l eMapp ing. Abre un archivo de objeto de archivo mapeado previamente creado. • MapVi ewO f F i l e . Mapea parcial o totalmente un archivo en el espacio de direcciones de un pro­ ceso. • 8.5.3. unrnapVi ewO f F i l e . Elimina un archivo mapeado del espacio de direcciones de un proceso. Traducción de direcciones Windows soporta tablas de páginas paginadas de diferentes niveles. El número de niveles de pagi­ nación utilizados depende de la arquitectura hardware. Por ejemplo, en el caso de la arquitectura Intel x86 de 32 bits Windows utiliza por defecto dos niveles de paginación. Aunque si se trabaj a en dicha arquitectura con el modo de extensión de dirección física (Physical Address Extension, PAE) entonces utiliza tres niveles. También en la arquitectura Intel IA-64 utiliza tres niveles de paginación. En el caso de la arquitectura AMD x64 utiliza cuatro niveles de paginación. e Ejemplo 8.4 En la arquitectura Intel x86 las direcciones virtuales tienen un tamaño de 32 bits. Windows considera que las direcciones virtuales se descomponen en tres campos (ver Figura 8.7): • Índice del directorio de páginas (IDP). Tiene un tamaño de 10 bits. Contiene el índice de la entrada de la tabla de páginas de primer nivel, también denominado directorio de páginas, donde hay que buscar el número del marco de memoria que contiene a la página que ubica a la tabla de páginas de segundo nivel que es una tabla de páginas ordinaria. El sistema operativo Windows 333 N° de página i � -------------..., ------------- Indice directorio de páginas (IDP) Indice tabla de páginas (ITP) Desplazamiento 1 0 bits 1 0 bits 1 2 bits Figura 8.7 - Formato de una dirección virtual en la arquitectura x86 • Índice de la tabla de páginas (ITP). Tiene un tamaño de 10 bits (o 9 bits si el modo PAE esta activado) Contiene el índice de la entrada de la tabla de páginas donde hay que buscar el número del marco de memoria que contiene a la página que ha sido referenciada. • Desplazamiento. Tiene un tamaño de 12 bits. Especifica el desplazamiento desde el comienzo de la página. Cada proceso tiene un único directorio de páginas con tantas entradas como tablas de página se requieran para describir el espacio de direcciones virtuales del proceso. La dirección física de comienzo Dirpo del directorio de páginas de un proceso se almacena en el bloque KPROCESS asociado al proceso. Cuando el proceso es planificado para ejecución el registro CR3 se carga con la Dirro del directorio de páginas del proceso. Señalar que como el campo IDP tiene un tamaño de 10 bits entonces el número de entradas máximo que puede tener el directorio de páginas de un proceso es 2 10 = 1 024 entradas. Windows utiliza 4 bytes para representar a una entrada de un directorio de páginas. Luego el tamaño máximo del directorio de páginas de un proceso es de 4 KiB . Cada entrada de un directorio de páginas contiene la localización y el estado de una de las tablas de páginas del proceso. Las tablas de páginas se van creando según se van necesitando por ello el número de entradas del directorio de páginas de un proceso no suele ser muy alto. Asimismo puesto que el campo ITP tiene un tamaño de 1 0 bits entonces el número de entradas máximo que puede tener una tabla de páginas es también de 1 024 entradas. Tanto Windows como el hardware uti­ lizan 5 bytes para representar una entrada de una tabla de páginas, luego el tamaño máximo de una tabla de páginas es de 32 KiB . Señalar que en una entrada de una tabla de páginas el tamaño del campo número de marco de página es de 20 bits, luego como máximo la memoria principal se puede descomponer en 220 marcos. Cuando el procesador referencia a una dirección virtual la traducción de dicha dirección a una dirección física se realiza siguiendo los siguientes pasos (ver Figura 8 . 8) : l . Se suma IDP a l a dirección Dirpo contenida e n e l registro CR3 para construir la dirección física Dirp 1 de la entrada del directorio de páginas donde hay que buscar el número k del marco de memoria que contiene a la página que ubica a la tabla de páginas. 334 Ampliación de sistemas operativos 2. Se accede a Dirp¡ para leer dicho número de marco k y se le suma a ITP para construir la dirección física Dirp2 de la entrada de la tabla de páginas donde hay que buscar el número j del marco de memoria que contiene a la página i referenciada en la dirección virtual. 3. Si la entrada está marcada como válida se accede a la memoria principal para leer el número de marco j y poder construir la dirección física Dirp3 a la que equivale la dirección virtual referenciada por el procesador. En caso contrario se produciría un fallo de página. • La traducción de una dirección virtual en una dirección física solo se realiza si referencia a una pá­ gina válida y el tipo de acceso que se pretende realizar está permitido por la protección de la página. En caso contrario se produce un fallo de página que es atendido por el manipulador de fallos de página de Windows (MrnAc c e s s Fau l t ( ) ) que es una rutina que se ejecuta en el contexto del hilo cuya ejecuDirección virtual � N° de página i 1 IDP 1 + ITP 1 Desplazamiento J 1 Marco j 1 • ...... DirFo J 1 Desplazamiento _./ Dirección Física Registro CR3 DirFo Directorio de páginas � Marco k ll'f ¡ } DP �(+ � D1rF2 Marco k Tabla de páginas Marco} I ITP r-- Marco} � DirF3 Página i t Desp. ' Memoria Principal Figura 8.8 - Traducción Memoria Principal Memoria Principal de una direcciones virtual válida a direcciones físicas en la arquitectura x86 El sistema operativo Windows 335 ción produjo el fallo de página. A los fallos de página que no requieren leer la página en disco se les denominan fallos de página suaves. En caso contrario se les denominan fallos de página duros. Con objeto de reducir el número de fallos de página duros que produce la ejecución de un proceso, Windows almacena la secuencia de páginas referenciadas por dicho proceso utilizando una tecnología denominada SuperFetch. Cuando dicho proceso vuelve a ser iniciado el administrador de memoria realiza una prepaginación de las páginas del proceso de acuerdo con la secuencia de páginas referenciadas guardada para dicho proceso. Dichas páginas son cargadas en la lista de marcos de página en standby de memoria principal (ver sección 8.5 .4) de tal forma que las referencias a estas páginas producen fallos de página suaves. 8.5.4. Gestión de la memoria física Conjunto de trabajo Para gestionar la memoria física Windows utiliza el concepto de conjunto de trabajo de un proceso el cual queda definido por las páginas pertenecientes al proceso cargadas en memoria, las cuales pueden ser referenciadas sin producir un fallo de página. El tamaño y la composición del conj unto de trabajo de un proceso varían durante el tiempo de vida del proceso. Windows fija un límite inferior y un límite superior para el tamaño del conjunto de trabajo de un proceso. Cuando se inicia la ejecución de un proceso el límite inferior se configura a un valor de entre 20 y 50 páginas. Mientras que el límite superior se configura a un valor comprendido entre 45 y 345 páginas. Estos valores iniciales dependen de la memoria física total existente, además pueden ser configurados por el administrador del sistema. Cuando existe suficientemente memoria libre Windows permite que un proceso pueda superar su límite máximo. Sin embargo cuando la memoria principal libre comienza a escasear Windows comienza a reducir el tamaño de los conjuntos de trabajo de los procesos que han superado su límite máximo. Si con esta medida la memoria libre sigue siendo escasa entonces comienza a reducir el tamaño de los conjuntos de trabaj o de todos procesos. La gestión de los conjuntos de trabajo de los procesos es realizada por el gestor de los conjuntos de trabajo que es invocado por el gestor de configuración del equilibrio una vez por segundo o si la memoria libre disminuye por debajo de un determinado límite. Base de datos de los números de marcos de página La base de datos de los números de marco de página es una tabla que el administrador de memoria de Windows mantiene para describir el estado de cada marco de página de la memoria principal. Un marco de página puede encontrarse en alguno de los siguientes estados [Russinovich et al. , 2009] : • Activo o válido. El marco contiene una página que pertenece al conjunto de trabaj o de uno o varios procesos o al conjunto de trabajo del sistema operativo. • Transición. Se trata de un estado temporal para un marco que contiene una página que no pertenece al conjunto de trabaj o de un proceso ni a ninguna de las listas de páginas. Un marco se encuentra 336 Ampliación de sistemas operativos en este estado cuando está asignado a una página sobre la que se está realizando una operación de E/S. • Standby. El marco contiene una página que ha sido eliminada del conjunto de trabajo de un proceso o ha sido prepaginada. Dicha página no ha sido modificada desde su última escritura en el disco. • Modificado. El marco contiene una página que ha sido eliminada del conjunto de trabajo de un proceso y cuyo contenido ha sido modificado y debe ser escrita en el almacenamiento de respaldo. • Libre. El marco está libre y contiene una página con datos. • Inicializado a O. El marco está libre y contiene una página llena de ceros. • Rom. El marco contiene una página que representa a la memoria ROM. • Malo (bad). El marco contiene una página que ha generado un error de paridad, u otro error hard­ ware, y no puede ser utilizada. También se utiliza internamente para representar a las páginas del sistema operativo que se encuentran en transición entre un estado y otro. La base de datos de los números de marco de página tiene una entrada por cada marco de página en que se descompone la memoria principal. La tabla está indexada por el número de marco de página j. Cada entrada se implementa como una estructura de datos de tamaño fijo cuyo formato puede variar en función de si la página contenida en el marco es compartida o privada. En cada entrada se almacenan, entre otros, los siguientes datos: estado del marco, contador del número de entradas de tablas de página que referencian al marco, y un puntero a la entrada de la tablas de páginas ordinarias (si es una página privada) o prototipo (si es una página compartida) que apunta al marco. Una tabla de página prototipo es una variante de una tabla de páginas ordinarias que Windows utiliza para almacenar la información necesaria para traducir las referencias a una determinada región de memoria compartida. Las entradas de las tablas de páginas que hacen referencia a una determinada página de una región de memoria compartida poseen un puntero a la entrada adecuada de la tabla de páginas prototipo asociada a la región de memoria compartida correspondiente. e Ejemplo 8.5 En la Figura 8.9 se muestra a modo de ejemplo un posible estado de las entradas k = 35 a k = 42 de la base de datos de los números de marcos de página. La entrada k = 35 de la base de datos está asociada al marco j = 35 de memoria principal. De acuerdo con la figura este marco se encuentra actualmente en el estado activo lo que significa que contiene una página que actualmente pertenece al conjunto de trabajo de un proceso. En concreto se trata de la página i = O del proceso A. La entrada k = 37 de la base de datos está asociada al marco j = 37 de memoria principal, el cual se encuentra actualmente en el estado modificado. Luego contiene una página que ha sido eliminada del conjunto de trabajo de un proceso y su contenido ha sido modificado y debe ser escrita en el almacena­ miento de respaldo. En concreto se trata de la página i = 2 del proceso A. Finalmente, la entrada k = 42 de la base de datos está asociada al marco j = 42 de memoria principal que se encuentra actualmente en el estado activo. Se observa que dicho marco contiene una página compartida El sistema operativo Windows 337 Base de datos de los números de marcos de página i 1 ! 1 k=3 5 Activo Tabla de páginas Proceso A ------------------------------------------------------- 1111 k=3 6 k=37 k=3 8 j=6 1 v=O i=3 p=O v= l i=4 j=60 v=O Modificado ------------------------------------------------------- 1111 .--- k=3 9 J 1 '' '1 k=40 k=4 1 k=42 37 m 1 i=O i= l i=2 j-35 v- l Activo ----------------· j-42 v- 1 �p p= l ! 11 i' 1 ! 1 Tabla de páginas prototipo de una región compartida Figura 8.9 - o Tabla de páginas Proceso B i=O p=O v= l 1 1 Estado de la base de datos de los números de marcos de página del Ejemplo 8 . 5 y a que l a entrada p = O d e la tabla d e páginas prototipo asociada a una región d e memoria compartida entre los procesos A y B apunta precisamente a la entrada k = 42 de la base de datos. • El administrador de memoria organiza las entradas de la base de datos de los números de marco de página en cuatro listas: lista de marcos inicializados a O, lista de marcos libres, lista de marcos modificados, y lista de marcos en standby. Cada entrada perteneciente a una lista contiene el número de la siguiente entrada que forma parte de dicha lista. Cuando un proceso termina su ejecución todas sus páginas privadas son colocadas en la lista de marcos libres. Por otra parte, cuando una página dej a de pertenecer al conjunto de trabaj o de un proceso, bien porque ha sido reemplazada o bien porque se ha reducido el tamaño del conj unto por necesidades de memoria, si la página ha sido modificada entonces ingresa en la lista de marcos modificados. Mientras que si no ha sido modificada ingresa en la lista de marcos en standby. Cuando el administrador de memoria necesita asignar un marco inicializado a cero, por ejemplo, para la región de pila o el montículo de un proceso, entonces selecciona un marco de la lista de marcos inicializados a cero. Si esta lista está vacía entonces selecciona un marco de la lista de marcos libres y antes de asignarlo lo inicializa a cero. Si la lista de marcos libres también está vacía entonces selecciona 338 Ampliación de sistemas operativos a un marco de la lista de marcos en standby y antes de asignarlo lo inicializa a cero. Existe un hilo denominado hilo de puesta de página a cero que se encarga de inicializar a cero las páginas que se encuentran en la lista de marcos libres y pasarlas a la lista de marcos inicializados a cero. Existen varias instancias de este hilo, una por cada procesador disponible. Este hilo se ejecuta con la prioridad más baj a posible (prioridad 0), en consecuencia solo se ejecuta cuando no existe ningún otro hilo en el estado preparado. Por otra parte, cuando el administrador de memoria necesita asignar un marco de memoria que no requiere estar inicializado a cero entonces primero lo intenta obtener de la lista de marcos libres. Si dicha lista está vacía entonces lo intenta obtener de la lista de marcos inicializados a cero. Finalmente si esta lista también está vacía entonces acude a la lista de marcos en standby. En la Figura 8 . 1 0 se resumen las posibles transiciones entre las listas de marcos de páginas . Fallo de página inicializada a cero Lista de marcos inicializados a cero , .... Fallo de página duro Conjuntos de trabajos de los procesos Fallo de página suave Fallo de página suave Lista de marcos libres Lista de marcos en standby � Lista de marcos modificados Paginas seleccionadas para ser remplazadas Figura 8.10 - 8.5.5. Transiciones entre las listas de marcos de páginas Respaldo de páginas en memoria secundaria Las regiones del espacio de direcciones virtuales de un proceso se pueden clasificar en dos tipos: regiones generadas mediante el mapeado de un determinado archivo (código y datos inicializados) y regiones anónimas no generadas mediante ningún mapeado (pila y montículo). El sistema operativo Windows 339 El almacenamiento de respaldo en memoria secundaria de las páginas pertenecientes a regiones ge­ neradas mediante el mapeado de un archivo es el propio archivo en disco. Para las páginas pertenecientes a regiones anónimas o copias privadas de páginas marcadas como copiar al escribir, Windows utiliza como almacenamiento de respaldo los archivos de páginas (page files) que son archivos cuyos bloques de datos almacenan páginas de los procesos o del sistema operativo. Windows soporta hasta 1 6 archivos de páginas. El tamaño máximo de un archivo de páginas varía en función de la arquitectura. Por ejemplo, en la arquitectura Intel x86 sin uso del modo PAE el tamaño máximo de un archivo de páginas es de 4,095 MiB . Mientras que si se usa el modo PAE el tamaño máximo es 1 6 TiB . Cuando se arranca el sistema el proceso gestor de sesión accede al registro para leer la lista de los archivos de página que hay que abrir. Cuando una página de una región anónima es asignada Windows reserva para ella espacio en un archivo de páginas. Sin embargo la localización exacta de la página dentro del archivo no se asigna por adelantado sino una vez que la página es escrita en el archivo. Los marcos de página que contienen páginas modificadas que han sido seleccionadas para ser inter­ cambiadas fuera de memoria principal se encuentran dentro de la lista de marcos de página modificados. Estas páginas modificadas deben ser escritas en su almacenamiento de respaldo correspondiente. Existen dos hilos del sistema encargados de realizar esta tarea: • Escritor de páginas modificadas. Se encarga de escribir páginas modificadas en los archivos de páginas. Este hilo es despertado cuando el tamaño de la lista de hilos modificados excede un determinado límite. • Escritor de páginas mapeadas. Se encarga de escribir páginas modificadas en los archivos que mapean dichas páginas. Este hilo es despertado cuando el tamaño de la lista de hilos modificados excede un determinado límite o si las páginas de un archivo han estado en esta lista durante más de cinco minutos. Una vez que una página modificada ha sido escrita en su almacenamiento de respaldo el marco que la contiene pasa de la lista de marcos modificados a la lista de marcos en standby. 8.6. Gestión de archivos en Windows 8.6.1. Funciones de la API Win32 asociadas a la gestión de archivos y directorios Existen varias funciones en la API Win32 asociadas a la gestión de archivos y directorios, entre las funciones más conocidas se encuentran las siguientes : • CreateF i l e . Permite crear un archivo nuevo o abrir uno ya creado. Si se ejecuta correctamente se crea un objeto archivo cuyo descriptor (handle) es devuelto al proceso que ha invocado a esta función. • C l o s eHand l e . Cierra el archivo asociado al descriptor pasado como argumento de entrada. 340 Ampliación de sistemas operativos • De l e t eF i l e . Borra un archivo cuyo nombre de ruta debe ser pasado como parámetro de entrada. Su funcionamiento es similar a la función unl ink de los SOBUNIX. • ReadF i l e . Lee una determinada cantidad de bytes de un archivo y los almacena en una determina posición de memoria. • Wr i t eF i l e . Lee una determinada cantidad de bytes de una determinada posición de memoria y los escribe en un archivo. • S e tF i l e P o inter. Configura a una determina posición el puntero de lectura/escritura de un ar­ chivo. • Ge t F i l eAttribu t e s . Devuelve los atributos de un archivo: si es un archivo o un directorio, si está encriptado, si está comprimido, si está oculto, si es un enlace simbólico, etc. • CreateDi r e c to ry. Crea un directorio nuevo cuyo nombre de ruta debe ser introducido como argumento de entrada. • Remove D i r e c t o ry. Elimina un directorio vacío cuyo nombre de ruta debe ser introducido como argumento de entrada. • 8.6.2. S e tCurrentDi r e c t o ry. Cambia el directorio de trabaj o actual. Sistemas de archivos Cada sistema de archivos presente en memoria secundaria es tratado por Windows como una unidad o volumen lógico independiente, cada una de los cuales posee su propia estructura de directorios. Windows asigna una letra (C, D, E, . . . ) a cada unidad. El nombre de ruta de un archivo comienza por tanto con el nombre del volumen lógico en el que está almacenado. Las versiones modernas de Windows cuando se instalan en una partición de disco crean un sistema de archivos principal de tipo NTFS (ver sección 8 .7). Aparte de NTFS Windows también soporta sistemas de archivos CDFS , UFS , FAT 1 2, FAT 1 6, FAT32 y extFAT. Para interactuar con un determinado tipo de sistema de archivos, el gestor de E/S (ver sección 8 . 8) invoca al driver del sistema de archivos (File System Driver, FSD) correspondiente. Por ejemplo, para los sistemas de archivos NTFS utiliza el FSD nt f s . sys , para el sistema CDFS utiliza el FSD c d f s . sys , etc. El sector de arranque de cada partición con un sistema de archivos soportado por Windows contiene la información necesaria para que el FSD asociado a dicho tipo de sistema de archivos pueda identificar y localizar los metadatos y datos almacenados en el volumen. S eñalar que el FSD no accede al disco directamente sino que invoca al gestor del volumen que a su vez invoca al driver del disco correspondiente Por otra parte, cuando un proceso intenta acceder (leer o escribir) a alguna parte del archivo que no está cargada en memoria entonces el gestor de memoria invoca al gestor de E/S que en función de donde está localizado el archivo invoca al FSD adecuado para que realice la operación solicitada. El sistema operativo Windows 8. 7. 341 El sistema de archivos NTFS El sistema de archivos NTFS (New Technology File System, sistema de archivos de tecnología nue­ va) fue desarrollado a mediados de los noventa para ser el sistema de archivos principal de Windows NT. Se trata de un sistema de archivos bastante versátil diseñado para cumplir con las necesidades de almacenamiento y recuperación de datos de altas prestaciones necesarias en las estaciones de trabajo y servidores de las empresas. 8.7.1. Características principales Un sistema de archivos NTFS presenta, entre otras, las siguientes características : • La unidad básica de asignación de espacio es el cluster. Un cluster es un conjunto contiguo de sectores de disco. El tamaño en bytes de un cluster es una potencia de dos comprendida dentro del rango [5 1 2 bytes, 64 KiB] . El tamaño se define en el momento de la creación del sistema de archivos en función del tamaño de la partición. Por ejemplo si la partición tiene un tamaño mayor de 32 GiB el tamaño de un cluster se fij a a 64 KiB . En este caso si un sector tiene un tamaño de 5 1 2 bytes entonces un cluster constará de 1 28 sectores contiguos. Cada cluster tiene asignada una dirección de 64 bits, con lo que teóricamente el tamaño máximo de una partición NTFS es de 2 64 clusters. Actualmente Windows limita el tamaño de una dirección de un cluster a 32 bits lo que para un tamaño de cluster de 64 KiB permite que el tamaño máximo de una partición sea ligeramente inferior a 256 TiB . En Windows el flujo de datos de un archivo se descompone en M clusters virtuales contiguos, mientras que una partición de disco se descomponen en P clusters lógicos contiguos. En ambos casos los clusters se numeran comenzando en O. Si se utiliza Bv para denotar a una número de cluster virtual de un archivo y BL para denotar a un número de cluster lógico de una partición, entonces Bv puede tomar valores enteros comprendidos entre O y M l. Mientras que BL puede tomar valores enteros comprendidos entre O y P - l . - • Toda l a información contenida e n u n sistema de archivos NTFS s e estructura e n archivos. Los da­ tos almacenados en un volumen NTFS , incluidos los metadatos del propio sistema de archivos, se estructuran en archivos. Esta estructuración simplifica la localización, mantenimiento y protección de los datos. • Un archivo (o un directorio) consiste de múltiples flujos de bytes. A diferencia de otros sistemas de archivos donde un archivo es implementado como una secuencia lineal de bytes, en NTFS un archivo (o un directorio) consiste de múltiples flujos de bytes de tamaño variable. Cada flujo de bytes representa a un atributo del archivo. Entre los atributos de un archivo soportados en un sistema NTFS se encuentran los siguientes: - Información estándar ( $ STANDARD_INFORMAT I ON) . Este atributo especifica si el archivo es de solo lectura o de lectura-escritura, si es un archivo o un directorio, la fecha y hora de 342 Ampliación de sistemas operativos creación del archivo, la fecha y hora de la última modificación del archivo, y el contador de enlaces duros. - Nombre del archivo ( $ F I L E_NAME). - Descriptor de seguridad ( $ SECURITY_DESCRI PTOR) . Este atributo se mantiene por compatibilidad con las versiones anteriores de NTFS . La versión actual de NFTS almacena los descriptores de seguridad de todos los archivos en el archivo de metadatos $ S e cur e. - Datos ( $ DATA). Contiene los datos del archivo (o directorio). - Identificador del objeto ( $ OBJECT_I D). Es un identificador de 64 bytes, siendo los 1 6 bytes menos significativos únicos de cada volumen. Existen funciones de la API Win32 que permiten abrir un archivo por su identificador de objeto en lugar de usar su nombre. - Raíz índice. ( $ INDEX_ROOT). Se utiliza para implementar directorios (ver sección 8.7.3). - Asignación índice. ( $ INDEX_ALLOCATI ON) . Se utiliza para implementar directorios. - Mapa de bits. ( $ B I TMAP). Se utiliza para implementar directorios. Cada atributo tiene la siguiente estructura: código numérico del tipo atributo y valor del atributo. También es posible poner un nombre a un atributo, el cual se almacena después del valor del atri­ buto. El uso del nombre de un atributo resulta de utilidad cuando un archivo tiene varios atributos de un mismo tipo. Por ejemplo un archivo puede tener varios atributos datos o varios atributos nombre. • Un archivo puede constar de múltiples flujos de datos. En NTFS un archivo consta por defecto de un único atributo datos que no tiene nombre. Adicionalmente es posible definir otros atributos datos asignándole a cada uno un nombre. Luego en NTFS un archivo puede contener múltiples flujos de datos de diferentes tamaños. Cada fluj o de datos adicional puede ser accedido de la siguiente forma: nombre de l archivo : nombre de l f lu j o Por ejemplo, supóngase un archivo prueba . txt que consta de dos flujos de datos: uno el flujo de datos por defecto sin nombre y otro definido adicionalmente de nombre par t e 2 . Para trabaj ar con el flujo por defecto simplemente se referencia al nombre del archivo, mientras que para trabaj ar con el flujo con nombre habría que referenciarlo de la siguiente forma: prueba . txt : part e2 . • Los nombres de los archivos se codifican en código Unicode. Ello permite utilizar en los nombres de los archivos caracteres diferentes del alfabeto latino. Un sistema NTFS es capaz de distinguir entre caracteres en mayúsculas o en minúsculas en los nombres de archivos, luego Prueba . txt y prueba . txt son nombres de archivos diferentes en NTFS . Sin embargo Windows no hace uso de esta propiedad salvo cuando ejecuta funciones asociadas a la API POSIX. En la API Win32 no se distingue entre mayúsculas y minúsculas a la hora de interpretar los nombres de los archivos, aunque si respeta el que se usen mayúsculas en los nombres, es decir, que no las cambia cuando almacena el nombre. El sistema operativo Windows 343 • Un nombre de archivo puede tener una extensión máxima de 255 caracteres. • Implementa una estructura de directorios de gráfica acíclica con soporte de enlaces duros y de enlaces simbólicos. Los enlaces duros solo se pueden realizar a archivos dentro de un mismo vo­ lumen. Además no es posible realizar enlaces duros a directorios. Estas restricciones no se aplican en el caso de los enlaces simbólicos. Por motivos de seguridad únicamente el administrador del sistema tiene los privilegios necesarios para crear enlaces simbólicos. Por su parte un enlace duro puede ser creado por cualquier usuario siempre que tenga los derechos de acceso oportunos sobre el archivo al que apunta con el enlace duro. Para crear un enlace duro se puede usar el comando rnkl ink con la opción / H . También se puede utilizar la función C r e a t eHardL ink de la API Win32. La creación de un enlace simbólico se puede realizar usando el comando rnkl i nk o la función Creat eSyrnbo l i c L i nk de la API Win32. Los enlaces simbólicos son implementados en NTFS mediante un mecanismo de redirecciona­ miento denominado puntos de reanálisis. Un punto de reanálisis (reparse points) es un archivo o un directorio que tiene un bloque de datos denominado datos de reanálisis asociado con él. Un dato de reanálisis es un dato definido por el usuario sobre el estado o localización del archivo o el directorio. Cuando NTFS encuentra un punto de reanálisis durante el análisis de un nombre de ruta, se produce un error y se devuelve el dato de reanálisis al gestor de obj etos que reinicia la operación de análisis teniendo en cuenta el dato de reanálisis encontrado. Los puntos de reanálisis también son utilizados en NTFS para el montaj e del sistema de archivos. • Recuperación. Un sistema de archivos NTFS puede ser recuperado y devuelto a un estado con­ sistente si se producen fallos en el sistema o en el disco. Conviene aclarar que lo que se recupera es el estado de las estructuras de datos del sistema de archivos no los contenidos de los archivos. Para poder recuperar el sistema de archivos se realiza un almacenamiento redundante de los datos críticos del sistema. Además las operaciones que pueden cambiar el estado del sistema se imple­ mentan mediante transacciones atómicas. Una transacción atómica se realiza completamente o no se realiza. Cada transacción se registra en el archivo de registro (log file) antes de realizarse sobre el volumen. De esta forma si se produce un fallo se consulta el archivo de registro para saber que transacciones estaban pendientes para rehacerlas o deshacerlas. De esta forma siempre se garantiza que el sistema de archivos se encuentra en un estado consistente. • Registro por diario Uournaling). NTFS soporta diferentes mecanismos para registrar los cambios que se producen en los archivos y directorios almacenados en un volumen NTFS . Esta información resulta de gran utilidad para determinadas aplicaciones, como por ejemplo los gestores de copias de seguridad. Uno de estos mecanismos es el diario de cambios (change j ournal) que es un archivo especial (interno del sistema NTFS) que puede ser leído por las aplicaciones. • Soporte de cuotas. NTFS dispone de mecanismos para fiscalizar y limitar el espacio en disco disponible para cada usuario. 344 Ampliación de sistemas operativos • Compresión. NTFS permite crear archivos en modo comprimido, para ello se debe introducir el argumento adecuado en la función CreateF i l e . El proceso de compresión es realizado por el driver del sistema NTFS cuando el archivo es escrito en el disco. Cuando un proceso solicita leer un archivo almacenado en modo comprimido el driver descomprime el archivo. Tanto la compresión como la descompresión son actividades totalmente invisibles al proceso que no se percata de su realización. • Encriptación. NTFS también permite crear archivos encriptados . El procedimiento de encriptación y desencriptación es realizado por el driver de encriptación del sistema de archivos o driver EFS (Encryption File System). Solo los usuarios autorizados pueden ver el contenido de un archivo encriptado. Generalmente la encriptación se aplica marcando un directorio con este atributo de tal forma que todos los archivos que se aloj an en el directorio (y los nuevos que se creen en él) son encriptados de manera automática. Tanto la encriptación como la desencriptación son actividades totalmente invisibles al proceso que no se percata de su realización. 8. 7 2 . . Estructura en el disco de un sistema NTFS Un sistema de archivos NTFS presenta la siguiente estructura en la partición de disco donde se crea [Stallings, 2005] (ver Figura 8 . 1 1 ) : Partición de disco Tabla de archivos maestra Figura 8.11 Á rea de datos de los archivos - Estructura en el disco de un sistema de archivos NTFS • Á rea de arranque. Se sitúa al principio de la partición y puede ocupar hasta 1 6 sectores consecuti­ vos. Contiene información sobre la estructura del volumen y las estructuras de datos del sistema de archivos, como por ejemplo la dirección física de comienzo de la tabla de archivos maestra. Si se trata de la partición activa entonces el área de arranque también contiene el programa de arranque o cargador de Windows. • Tabla de archivos maestra (Master File Table, MFT). Es la estructura de datos más importante de un sistema NTFS . Contiene información sobre los atributos de todos los archivos y directorios existentes en el volumen. • A rchivos del sistema. Es un área de aproximadamente 1 MiB de tamaño donde se alojan archivos cuyos clusters de datos contienen metadatos del sistema de archivos. • Á rea de datos de los archivos. Se utiliza para almacenar los valores de los atributos de un archivo o directorio que por su tamaño no pueden ser almacenados en la MFT. El sistema operativo Windows 345 Tabla de archivos maestra Cada fila de la MFT se implementa mediante una estructura de datos denominada registro de archivo (file record) que tiene un tamaño fijo de 1 KiB . Esta estructura contiene los atributos de un archivo o un directorio. Por cada atributo se almacena una cabecera y el valor del atributo. Luego un registro de archivo puede verse como una sucesión de pares (cabecera de atributo, valor de atributo). Si el valor de un atributo ocupa mucho espacio entonces no puede ser contenido en un registro de archivo de la MFT y se le debe asociar uno o más grupos de clusters en el área de datos de la partición. A cada grupo de clusters en el área de datos asociado al valor de un atributo se le denomina serie (run) o extensión. La cabecera del atributo contiene la información necesaria para localizar las series del valor del atributo en la partición. A un atributo cuyo valor se almacena completamente en la MFT se le denomina atributo residente, en caso contrario se le denomina atributo no residente. Algunos atributos siempre son, por definición, residentes. Este es el caso, por ejemplo, de la información estándar y del nombre del archivo. Otros atributos serán residentes o no en función de su tamaño. Este es el caso, por ejemplo, del atributo datos del archivo. La cabecera de un atributo indica si el atributo es residente o no residente. Además para el caso de un atributo residente la cabecera contiene también la longitud del atributo y el desplazamiento a partir del comienzo de la cabecera donde comienza el valor del atributo. En el caso de atributos no residentes la cabecera contiene la siguiente información por cada serie: número de cluster virtual del archivo Bv de inicio de la serie, número de cluster lógico de la pattición BL de inicio de la serie y número de clusters que ocupa la serie. Cada archivo o directorio existente en el volumen, incluida la propia MFT, tiene asignado al menos un registro de archivo en la MFT. A dicho registro se le denomina registro de archivo base. Si el archivo contiene tantos atributos (o sus cabeceras ocupan mucho espacio) que no pueden almacenarse en un único registro de archivo de la MFT entonces se le asigna un segundo registro de archivo para poder mantener toda la información del archivo. Si con un segundo registro tampoco es suficiente se le asignará un tercer registro, y así sucesivamente hasta poder acomodar toda la información sobre los atributos de un archivo (o directorio). Para localizar en que registro de archivo se encuentra cada atributo en el registro de archivo base se añade un atributo denominado lista de atributos que contiene el nombre y código de cada uno de los atributos del archivo y la referencia de archivo del registro de archivo de la MFT donde se encuentra localizado el atributo. La referencia del archivo es un identificador de 64 bits que se construye en función de la entrada k (k = O, 1 , 2, 3 , . . . , 248 1 ) que ocupa el archivo en la MFT. Este identificador consta de dos campos: el número de secuencia y el número de archivo. El número de secuencia ocupa los 16 bits más significativos y es un contador que se incrementa cada vez que dicha entrada es reutilizada. Por su parte el número de archivo ocupa los restantes 48 bits y codifica el índice k de la entrada. La referencia de un archivo puede cambiar durante el tiempo de vida de un archivo, es decir, un archivo bajo determinadas circunstancias puede ser movido de una entrada k a otra entrada h de la MFT. La referencia de un archivo guarda cierta analogía con el número de nodo-i de los SOBUNIX. - 346 Ampliación de sistemas operativos 1 KiB Atributo Atributo información estándar nombre de archivo Cabecera Figura 8.12 - Atributo datos Valor . Estado del registro de archivo de la MFf asociado al archivo prueba txt del Ejemplo 8.6 1 KiB Atributo Atributo información estándar nombre de archivo 1111 Fila 65 de la MFT ---- 1 225 1 1 226 1 2 227 1 3 228 Primera serie de datos Figura 8.13 - Estado del 8.6 IJo llll Alumnos.dat o 1 --- - 1 Atributo datos 1 5 4 316 1 IJo - - Localización de las series 1- - - - - - - - - - - · :... Bv BL N o 225 4 4 316 2 Área de datos de la partición 317 Segunda serie de datos . registro de archivo de la MFf asociado al archivo Alumno s da t del Ejemplo El sistema operativo Windows e 347 Ejemplo 8.6 En la Figura 8 . 1 2 se muestra el estado de un registro de archivo (fila 5 1 ) de la MFf asociado a un archivo de nombre prueba . txt cuyos atributos están contenidos en su totalidad en el registro. Por el contrario en la Figura 8 . 1 3 se muestra el estado de un registro de archivo (fila 65) de la MFf asociado a un archivo de nombre Al urnn o s . da t cuyo atributos datos tiene un tamaño que ha requerido almacenarlo en dos series de 4 y 2 clusters contiguos respectivamente del área de datos de la partición. La primera serie está asociada a los 4 primeros clusters virtuales de datos del archivo (Bv = O, 1 , 2, 3) y ocupa los clusters lógicos BL = 225 a BL = 228 de la partición. Por su parte, la segunda serie está asociada a los dos últimos clusters virtuales de datos del archivo (Bv = 4, 5) que ocupan los clusters BL = 3 1 6 y BL = 3 1 7 de la partición. • Archivos del sistema El nombre de los archivos del sistema empieza con el carácter $ . Algunos ejemplos de archivos del sistema son: • Espejo de la MFf ( $MFTM i r r ). Este archivo contiene por motivos de seguridad una copia de las 33 primeras filas de la MFf. • Archivo registro ( $ L o g F i l e ). Contiene un listado de las transacciones atómicas a realizar sobre el sistema de archivos . Se mantiene para poder recuperar el estado del volumen en caso de fallo. • Tabla de definición de atributos ($AttrDe f). Esta tabla define los atributos de los archivos sopor­ tados en el volumen. Además establece si los atributos se pueden indexar para realizar búsquedas o si se pueden recuperar durante una operación de recuperación del sistema. • Mapa de bits de los clusters del volumen ( $ B i tMap ) . El estado de un bit del mapa determina el estado (libre o asignado) de un determinado cluster del volumen. El área de arranque también se implementa como un archivo del sistema (archivo $Boot), pero a diferencia del resto de archivos del sistema se ubica siempre al principio de la partición. También la propia MFf se implementa como un archivo del sistema (archivo $MFT) que se ubica a continuación del área de arranque. Los 33 primeros registros de archivos de la MFf (filas O a 32) están reservadas para los archivos del sistema. El archivo de la MFf ( $ MFT) tiene asignada la fila O, el archivo espej o de la MFf ( $ MFTMirr ) tiene asignada la fila 1 , al archivo registro ( $LogF i l e ) la fila 2, el archivo de la tabla �e definición de atributos ($At t rDe f ) la fila 4, el directorio raíz ( \ ) la fila 5, el archivo del mapa de bits de los clusters del volumen ( $ B i tMap ) la fila 6 y el archivo del sector de arranque ( $ B o o t ) la fila 7. 348 Ampliación de sistemas operativos 8. 7 .3. Estructura de un directorio en NTFS Cada directorio (al igual que cada archivo) definido en una partición de NTFS tiene asignado al menos un registro de archivo en la MFf. El registro de archivo asociado a un directorio contiene (ver Figura 8 . 14 ), aparte del atributo información estándar y nombre del archivo (directorio en este caso), un atributo denominado raíz índice cuyo valor es una lista de las entradas del directorio. Cada entrada del directorio consta de una parte de longitud fija y otra de longitud variable. La parte de longitud fija almacena, entre otros datos, la referencia del archivo (o subdirectorio) en la MFf y la longitud del nombre del archivo (o subdirectorio). La parte de la entrada del directorio de longitud variable se utiliza para almacenar el nombre del archivo o subdirectorio. Si un directorio contiene muchas entradas entonces parte de las entradas se almacenan en el atributo índice raíz y el resto de entradas se almacenan en series en el área de datos. A las series que contienen entradas de directorios se les denomina bu.ffers índices. En este caso se añade en el registro de archivo base del directorio un atributo denominado asignación índice que contiene la información necesaria para localizar los buffers índices en la partición. También se añade un atributo denominado Mapa de bits que permite establecer que entradas en los buffers índices están libres y cuales ocupadas. En el caso de directorios con muchas entradas en vez de mantener una lista se implementa un árbol­ B 10 con objeto de acelerar la gestión del directorio: realización de búsquedas e inserción de nuevas entradas. e Ejemplo 8.7 En la Figura 8 . 1 4 se muestra el estado de un registro de archivo (fila 80) de la MFf asociado a un di­ rectorio de nombre F o t o s . Se observa que este directorio, de acuerdo con el valor de su atributo raíz índice, consta de tres entradas. En cada entrada se almacenará información sobre un archivo o subdirec­ torio. Puesto que el número de entradas es pequeño se pueden almacenar en su totalidad en el registro de archivo base y no ha sido necesario utilizar buffers índices. l KiB �1 Atributo Atributo información estándar Nombre archivo Fil• 8 de la MFT !f Cabecera Figura 8.14 - Estructura de � \ 11 Valor Fotos Atributo Raíz índice 1 1 t 1 1 1_-_-_-_-_-j Entrada del directorio un registro de archivo en la MFf asociado a un directorio con pocas entradas • 10Un árbol-B es una estructura de datos del tipo árbol binario de búsqueda [Cormen et al . , 2009]. El sistema operativo Windows 8. 7 4 . . 349 Localización de archivos en un sistema NTFS Cuando un proceso crea un nuevo archivo o abre un archivo ya existente debe introducir como ar­ gumento de entrada de la función Create F i l e de la API Win 32 el nombre de ruta del archivo. Dicha ruta debe ser analizada componente a componente. Si la ruta es correcta cada componente contiene la referencia de archivo del siguiente componente. e Ejemplo 8.8 Supóngase que para abrir el archivo C : \ Fo t o s \ p l aya . j pg un proceso ha invocado a la función CreateF i l e . El análisis de la ruta comienza en el directorio raíz ( \ ) del volumen C que recuérdese tiene asignado el registro de archivo 5 de la MFT. S e buscaría en el atributo índice raíz del directorio raíz una entrada cuyo nombre coincida con F o t o s . S i existe dicha entrada, entonces se lee l a referencia d e archivo del directorio \ Fo t o s l a cual apunta al registro de archivo asociado a dicho directorio. Supóngase que se trata del registro de archivo 80. A continuación se accede al atributo raíz índice de dicho registro de archivo en busca de una entrada que contenga el nombre p l aya . j pg . De nuevo, si existe dicha entrada, se lee la referencia de archivo del archivo e : \ Fo t o s \ p l aya . j pg la cual apunta finalmente al registro de archivo asociado al archivo que se desea abrir. Supóngase, por ejemplo, que se trata del registro de archivo 95 . • Si un archivo se crea o se abre correctamente se le asigna un objeto archivo cuyo descriptor es devuelto al proceso que invocó a la función CreateF i l e de la API Win 32. El objeto archivo contiene un puntero a una estructura de datos que depende del tipo de sistema de archivos (FAT, NTFS , etc). En el caso de un sistema de archivos NTFS esta estructura se denomina bloque de control del flujo (Stream Control Block, SCB). Existe un S CB por cada atributo de un archivo. Cada SCB contiene la información necesaria poder localizar a un determinado atributo. El obj eto archivo apunta al SCB del atributo del archivo que se desea leer o escribir. Todos los SCB de un determinado archivo apuntan a una estructura denominada bloque de control del archivo (File Control Block, FCB ) que, entre otros datos, contiene la referencia del archivo al que está asociado la cual permite localizar el registro base del archivo en la MFT. e Ejemplo 8.9 En la Figura 8. 1 5 se muestran las estructuras de datos necesarias para poder implementar una operación solicitada por un proceso sobre el fluj o de datos sin nombre (atributo datos) de un archivo perteneciente a un sistema de archivos NTFS . • 350 Ampliación de sistemas operativos Tabla de descriptores (handles) asociada a un proceso Bloque de control de fluj o (SCB) Objeto archivo ---+ Atributo Datos Tabla de archivos Maestra (MFT) � Bloque de control del archivo (FCB) Figura 8.15 - Estructuras de datos del Ejemplo 8.9 Subsistema de E/S en el ejecutivo Administrador de E/S Administrador de PnP : ._ Drivers Administrador de potencia Tratamiento de interrupciones en el núcleo i HAL ; Hardware Figura 8.16 - Componentes E/S. de Windows que se ejecutan en modo núcleo encargados de la gestión de la El sistema operativo Windows 8.8. 351 Gestión de E/S en Windows La gestión de la E/S en Windows involucra a diferentes componentes del sistema operativo que se ejecutan en modo núcleo (ver Figura 8 . 1 6): el subsistema de E/S, los drivers de dispositivos y las rutinas de tratamiento de las interrupciones. En las siguientes secciones se describe como se implementan estos componentes en Windows. 8.8.1. Subsistema de E/S El subsistema de E/ S de Windows se implementa mediante los siguientes componentes del ejecutivo: • Administrador de E/S . Es el componente central del subsistema de E/S. Se encarga de atender las peticiones de E/S realizadas por las aplicaciones de los usuarios y el resto de componentes del ejecutivo. Además define y soporta la interfaz de trabajo con los drivers. Más en detalle el administrador de E/S se encarga de la realización, entre otras, de las siguientes tareas : - Asignar a cada petición de E/ S una estructura de datos denominada paquete de petición de E/S (1/0 Request Packet, IRP) que contiene toda la información que necesita conocer el subsistema para procesar la petición. - Invocar al driver adecuado. El administrador de E/S cuando invoca un driver para realizar una determinada operación le pasa su IRP correspondiente. Asimismo cuando el driver termina de realizar la operación solicitada modifica adecuadamente el IRP y se lo envía al administrador de E/ S para que proceda a completar la petición o se la pase a otro driver. - Asignar a cada driver que se carga en el sistema un objeto driver e invocar a la rutina de inicialización del driver que rellena los atributos del objeto con los puntos de entrada del driver. Un punto de entrada es la dirección de comienzo de una rutina del driver. - Proporcionar rutinas comunes que pueden ser invocadas por los drivers. Por ejemplo una rutina para interactuar con el controlador de DMA, una rutina para invocar a otros drivers, una rutina para solicitar buffers de E/ S, etc. • Administrador de PnP. Se encarga de detectar la conexión o desconexión de dispositivos de E/S en el computador. Cuando el administrador de PnP detecta que se ha conectado un dispositivo entonces se encarga de cargar el driver apropiado. • Administrador de potencia. Se encarga de gestionar el consumo de energía de los componentes hardware del computador. Estos componentes del ejecutivo de Windows para realizar sus tareas trabajan estrechamente con los drivers de los dispositivos. En el registro de Windows se almacena una descripción de los dispositivos de E/S existentes en el computador así como la información necesaria para la inicialización y configuración de los drivers de dichos dispositivos. 352 Ampliación de sistemas operativos 8.8.2. Drivers de dispositivos en Windows De forma general los drivers de dispositivos usados en Windows se pueden descomponer en dos grandes categorías : los drivers que se ejecutan en modo usuario y los drivers que se ejecutan en modo núcleo. Ejemplos de drivers de Windows que se ejecutan en modo usuario son por ejemplo los drivers de dispositivos virtuales que se utilizan para emular aplicaciones de 1 6 bits de MS-DOS y los drivers de las impresoras del subsistema Windows. Los drivers que se ejecutan en modo núcleo se pueden clasificar en las siguientes categorías [Russi­ novich et al. , 2009] : • Drivers de sistemas de archivos. Un driver de un sistema de archivos atiende las peticiones de E/ S sobre un determinado tipo de sistema de archivos. Por ejemplo el driver nt f s . sys atiende las peticiones de E/S sobre los sistemas NTFS . • Drivers PnP. Son drivers que trabaj an en colaboración con el administrador de PnP y el adminis­ trador de potencia. • Drivers no PnP. Son drivers no asociados a ningún dispositivo hardware que extienden la funcio­ nalidad del sistema operativo. Ejemplos de drivers no PnP son los drivers que se utilizan para el soporte de los diferentes protocolos de red. Otra posible clasificación de los drivers proviene del modelo de drivers de Windows (Windows Driver Model, WDM) establecido por Microsoft, que proporciona a los programadores las especificaciones que debe cumplir un driver para funcionar correctamente en Windows. De acuerdo con este modelo se distinguen tres tipos de drivers [Russinovich et al. , 2009] : • Drivers de bus. Gestionan buses físicos o lógicos. Se encargan de detectar e informar al adminis­ trador de PnP de los dispositivos que se encuentran conectados a los buses. También se encargan de controlar y configurar el consumo de energía de los buses. Microsoft proporcionan drivers de bus para los tipos de buses más comunes : PCI, PnPISA, SCSI, USB and FireWire. Aunque si lo desean los programadores pueden escribir sus propios drivers de bus. • Drivers de función. Un driver de función es el driver principal de un dispositivo ya que es el que más información contiene sobre el funcionamiento de un determinado dispositivo y el que accede propiamente al mismo. Este tipo de drivers suelen ser escritos por el fabricante del dispositivo. • Drivers de filtro. Son drivers opcionales que se utilizan para añadir alguna funcionalidad o modi­ ficar el comportamiento de un dispositivo o de otro driver. Se observa que en el WDM cada tipo de driver (bus, función o filtro) se encarga de realizar una tarea muy específica. Con este reparto de tareas se pretende simplificar la escritura de los drivers a los programadores. Puesto que Microsoft proporciona drivers de buses y como los drivers de filtros son opcionales, los programadores únicamente tienen que concentrarse en escribir bien el driver de función de un determinado dispositivo. El sistema operativo Windows 353 En Windows es común que una petición de E/ S requiera de la ejecución de varios drivers. El subsis­ tema de E/S se encarga de invocar al primer driver necesario para realizar una determinada tarea. Dicho driver puede que requiera invocar a otro driver, para ello debe utilizar alguna función proporcionada por el administrador de E/S . e Ejemplo 8.10 Supóngase que un hilo de un proceso de usuario ha invocado a la función Wr i t e F i l e de la API Win32 para escribir en un determinado archivo. Los argumentos de entrada de esta función son, entre otros, los siguientes : el manej ador del archivo, el número de bytes a escribir, el buffer donde se encuentran aloj ados los datos a escribir, y el desplazamiento medido en bytes desde el comienzo del archivo donde se escribirán los bytes. Dicha función invoca a la llamada al sistema NtWr i t eF i l e que al ser atendida por el ejecutivo produce que se invoque al administrador de E/ S . É ste a su vez invoca al driver del sistema de archivos al que pertenece al archivo, por ejemplo si es un sistema NTFS al driver n t f s . sys . Este driver traduce el desplazamiento en bytes relativo al comienzo del archivo en un desplazamiento en bytes relativo al comienzo del volumen. Entonces pasa esta información mediante el administrador de E/S al driver de disco. Este driver traduce esta posición lógica en una posición física y se comunica con el disco para escribir los datos en el disco. • Cuando se carga un driver el administrador de E/S le asigna un objeto driver. Además invoca a la rutina de inicialización del driver que rellena los atributos del objeto con los puntos de entrada del driver. También crea un objeto dispositivo por cada dispositivo que controla dicho driver. Un objeto dispositivo Objeto driver ,... Función Read Función Write Función Ioctl � __. _. ...... Código del driver cargado en memoria princip al Objeto dispositivo 1 Figura 8.17 - __. Obj eto dispositivo 1 Relación entre un objeto driver y sus objetos dispositivos asociados 354 Ampliación de sistemas operativos es un objeto que representa y contiene información sobre un determinado dispositivo físico o lógico en el sistema. Todos los objetos dispositivos asociados a un determinado driver son organizados en una lista (ver Figura 8 . 17). El objeto driver contiene un puntero al comienza de su lista de objetos dispositivos. Además cada obj eto dispositivo contiene un puntero al objeto driver que lo controla. Cuando el administrador de E/S recibe una petición de E/S sobre un determinado dispositivo con­ sulta el objeto dispositivo asociado a dicho dispositivo para obtener la dirección del objeto driver que controla al dispositivo. Entonces accede al objeto driver para obtener el punto de entrada de la función del driver que implementa la operación que se requiere realizar sobre el dispositivo y le pasa como ar­ gumento el IRP que contiene la información sobre la operación a realizar. Cuando el driver termina de realizar la operación solicitada modifica adecuadamente el IRP y se lo envía al administrador de E/S para que proceda a completar la petición o se la pase a otro driver. 8.8.3. Gestión de interrupciones en Windows Niveles de petición de interrupción En Windows la gestión de las interrupciones se implementa en el núcleo. Éste prioriza la atención de las interrupciones asignando a cada tipo de interrupción una prioridad denominada nivel de petición de interrupción (lnterrupt Request Level, IRQL) que es un número entero positivo comprendido en el rango [0, N] . El valor de N depende de cada arquitectura. Por ejemplo en la arquitectura Intel x86 N=3 1 , mientras que en las arquitecturas AMD x64 e Intel IA64 N= 1 5 . Cuanto mayor e s e l valor del IRQL de una interrupción mayor e s s u prioridad, luego e l nivel de prioridad de interrupción más alto corresponde a IRQL=N. Asimismo el nivel de prioridad de interrupción más baj o corresponde a IRQL=O. A este nivel se le denomina nivel pasivo y no está asociado a ningún tipo de interrupción sino que es el nivel de prioridad asignado a la ejecución normal de los hilos tanto en modo núcleo como en modo usuario. e Ejemplo 8.1 1 E n la arquitectura Intel x86 Windows distingue 3 2 niveles de petición de interrupción. E l nivel más prioritario es IRQL=3 1 que está asignado a la ejecución de la rutina del núcleo KeBugChec kEx ( ) , la cual es invocada cuando se detecta una inconsistencia irrecuperable que podría corromper el sistema. El siguiente nivel, IRQL=30, se asigna a interrupciones asociadas a un fallo de potencia. El nivel IRQL=29 se asigna a interrupciones usadas para implementar la comunicación entre procesadores. El reloj del sistema es atendido con un nivel IRQL=28. El nivel IRQL=27 se reserva para la implementa­ ción del perfil del núcleo, un mecanismo de medida del comportamiento del sistema. Los dispositivos hardware tienen asignado un nivel IRQL comprendido dentro del rango [3, 26] . Los niveles IRQL= l e IRQL=2 están reservados para las interrupciones software. Mientras que el IRQL=O, es el nivel pasivo. • El sistema operativo Windows 355 Supóngase que se está ejecutando un hilo H l en modo usuario con IRQL=O y llega una interrupción 1 1 con un nivel IRQL=h. Entonces se produce una conmutación de modo usuario a modo núcleo y se almacena el contexto hardware del hilo en un marco de su pila del núcleo asociada para que pueda continuarse con su ejecución una vez finalizado el tratamiento de la interrupción y si no existe un hilo más prioritario. A continuación el flujo de control se transfiere a un manipulador general de interrupciones que identifica la interrupción que se ha producido, eleva el nivel de prioridad del procesador al valor IRQL=h e invoca a la rutina especifica de tratamiento de la interrupción 1 1 . Nótese que al elevar el nivel de prioridad de un procesador al valor IRQL=h todas las interrupciones con IRQL::; h quedan bloqueadas (enmascaradas) en dicho procesador. Un interrupción con IRQL=k con k < h no será atendida hasta que el nivel de prioridad del procesador sea configurado por debajo de k. Por otra parte, si mientras se está ejecutando la rutina de tratamiento de 1 1 con IRQL=h llega una interrupción 12 con IRQL=p, con p > h, entonces se eleva el nivel de prioridad del procesador al valor p, se guarda el contexto hardware de la rutina de tratamiento de 1 1 , y comienza a ejecutarse la rutina de tratamiento de 12. Cuando esta rutina se complete entonces el nivel de prioridad del procesador se reducirá de nuevo al valor h, y se continuará con la ejecución de la rutina de tratamiento de 1 1 . Interrupciones software generadas por el sistema operativo Aunque el hardware es la principal fuente de interrupciones, el sistema operativo también puede generar interrupciones. En este caso se trata de interrupciones software las cuales tienen asignado los niveles de petición de interrupción IRQL= 1 e IRQL=2. Entre las principales causas que producen que Windows genere una interrupción software se encuentran las siguientes : • Llamadas a procedimientos asíncronos (Asynchronous Procedure Call, APC). Una APC es un mecanismo que permite ejecutar un determinado código de una aplicación o del propio sistema operativo en el contexto de un determinado hilo, es decir, dentro de un determinado espacio de direcciones. Se distinguen dos tipos de APCs: - APCs en modo núcleo. Se caracterizan porque para ser ejecutadas no requieren el permiso del hilo en cuyo contexto desean ser ejecutados. Son utilizadas por los componentes del ejecutivo de Windows para realizar aquellas tareas que requieren ser completadas en el contexto de un determinado hilo, como por ejemplo suspender o terminar un determinado hilo. - APCs en modo usuario. Se caracterizan porque para ser ejecutadas requieren el permiso del hilo en cuyo contexto desean ser ejecutados . Diferentes funciones de la API Win 32 utilizan APCs en modo usuario. Por ejemplo la función ReadF i l eEx permite al hilo que la invoca especificar la rutina que debe ser ejecutada cuando la operación de lectura solicitada se complete. Una APC se describe mediante un objeto del núcleo denominado objeto APC. Las APCs que se encuentran a la espera de ser ejecutados residen en una cola APC gestionada por el núcleo. Existe una cola APC por cada hilo. Cada vez que el núcleo inserta un objeto APC en la cola APC de 356 Ampliación de sistemas operativos un hilo genera una interrupción software asociada al APC con un IRQL= l . Cuando el hilo es planificado para ser ejecutado en primer lugar se ejecutan las interrupciones software asociados a las APCs que tenga en su cola. • Llamadas a procedimientos aplazados (Deferred Procedure Call, DPC). Una DPC es una función que implementa una tarea del sistema cuya ejecución puede ser aplazada hasta completar la tarea actual. Las DPC proporcionan al sistema operativo la capacidad de generar una interrupción y ejecutar una determinada función del sistema en modo núcleo. El núcleo utiliza DPCs para repla­ nificar el procesador después de que el cuanto de un hilo ha expirado. Los drivers utilizan DPCs para completar peticiones de E/ S . Una DPC s e describe mediante u n obj eto del núcleo denominado objeto DPC. Las DPCs que se encuentran a la espera de ser ejecutados residen en una cola DPC gestionada por el núcleo. Existe una cola DPC por cada procesador. El núcleo cada vez que inserta un objeto DPC en la cola DPC de un procesador genera una interrupción software con IRQL=2. Cuando el IRQL de un procesador cae por debajo del valor IRQL=2 entonces se comienzan a ejecutar las DPCs existentes en la cola DPC de dicho procesador. 8.9. Resumen Windows de Microsoft es un sistema multiprogramado y multitarea, con soporte para multiprocesa­ miento. Consta de diversos componentes software, algunos de los cuales (procesos de servicios, procesos del sistema, aplicaciones, librerías DLL y subsistemas de entorno) se ejecutan en modo usuario, mientras que otros (capa HAL, drivers de dispositivos, GUI, núcleo y ejecutivo) se ejecutan en modo núcleo. En Windows a las llamadas al sistema se les denomina servicios del sistema. Las funciones de envol­ tura de las llamadas al sistema de Windows se encuentran en la librería ntdl l . dl l . Microsoft ha pu­ blicado muy poca información sobre las funciones de esta librería, por lo que es difícil trabaj ar con ellas directamente. El acceso a las funciones de la librería ntdl l . dl l , es decir, la invocación de llamadas al sistema, se realiza de forma indirecta a través de las funciones de las librerías DLL que implementan las APis soportadas por Windows, las cuales si están bien documentadas. Windows implementa un modelo de proceso multihilo con un hilo del núcleo por cada hilo de usua­ rio. Existen varias funciones en la API Win32 que permiten crear un proceso, aunque sin duda la más conocida y utilizada es la función C r e a t e Pro c e s s de la librería kerne l 3 2 . dl l . Por otra parte un hilo de un proceso puede crear otro hilo dentro del mismo proceso invocando a la función Crea t eThread. Windows es un sistema operativo de núcleo expropiable que utiliza como unidad de planificación a los hilos del núcleo. Windows implementa un algoritmo de planificación de tipo expropiativo basado en múltiples colas de prioridad y realimentación. Se consideran 32 niveles de prioridad comprendidos dentro del rango [0, 3 1 ] , siendo O la prioridad más baj a posible y 3 1 la más alta. Estos 32 niveles son divididos en dos grupos : niveles de tiempo real y niveles dinámicos. Los niveles de tiempo real comprenden los 16 niveles más prioritarios, es decir, los niveles de prioridad en el rango [ 1 6, 3 1 ] . Windows asigna estos niveles de prioridad a los hilos del núcleo asociados a tareas críticas del sistema, como por ejemplo: El sistema operativo Windows 357 la gestión de la memoria, la gestión de la caché, etc. Los niveles dinámicos comprenden los niveles de prioridad en el rango [ 1 , 1 5 ] . En estos niveles se ejecutan la mayoría de los procesos de lo usuarios. Windows para garantizar la exclusión mutua en el uso de las secciones críticas del código del sistema operativo utiliza principalmente spinlocks. Otros mecanismos de sincronización disponibles en Windows son los semáforos, los semáforos binarios o mutexes, y los eventos. Estos mecanismos pueden ser utili­ zados tanto por los propios componentes de Windows como por los hilos de los procesos de los usuarios. Windows también implementa diversos mecanismos de comunicación entre procesos multihilos: tube­ rías, conectores, mailslots, llamadas a procedimientos remotos, obj etos compartidos, etc. Windows utiliza la técnica de demanda de página para administrar la memoria principal. Todas las tareas relativas a la administración de memoria son realizadas por el administrador de memoria que es uno de los servicios que ocupa una mayor parte del código del ejecutivo de Windows, lo que da idea de la complejidad del mismo. Para gestionar la memoria física Windows utiliza el conjunto de trabajo de un proceso el cual queda definido por las páginas pertenecientes al proceso cargadas en memoria que pueden ser referenciadas sin producir un fallo de página. El tamaño y la composición del conjunto de trabajo de un proceso varían durante el tiempo de vida del proceso. Cada sistema de archivos presente en memoria secundaria es tratado por Windows como una unidad o volumen lógico independiente, cada una de los cuales posee su propia estructura de directorios. Windows asigna una letra (C, D, E, . . . ) a cada unidad. El nombre de ruta de un archivo comienza por tanto con el nombre del volumen lógico en el que está almacenado. Existen varias funciones en la API Win32 asociadas a la gestión de archivos, entre las funciones más conocidas se encuentran las siguientes : CreateF i l e , De l e t eF i l e , C l o s eHandl e , ReadF i l e y Wr i t eF i l e . Las versiones modernas d e Windows cuando s e instalan e n una partición d e disco crean u n sistema de archivos principal de tipo NTFS . Aparte de NTFS Windows también soporta sistemas de archivos CDFS , UFS , FAT 1 2, FAT 1 6, FAT32 y extFAT. Para interactuar con un determinado tipo de sistema de archivos, el gestor de E/ S invoca al driver del sistema de archivos correspondiente. El sistema de archivos NTFS fue desarrollado a mediados de los noventa para ser el sistema de archivos principal de Windows NT. Este sistema de archivos presenta, entre otras, las siguientes caracte­ rísticas: la unidad básica de asignación de espacio es el cluster, toda información contenida en un sistema de archivos se estructura en archivos, un archivo (o un directorio) consiste de múltiples flujos de bytes, un archivo puede constar de múltiples flujos de datos, un nombre de archivo puede tener una extensión máxima de 255 caracteres, los nombres se codifican en código Unicode, implementa una estructura de directorios de gráfica acíclica con soporte de enlaces duros y enlaces simbólicos, posibilidad de recupe­ ración del sistema, registro por diario, soporte de cuotas, compresión y encriptación. Un sistema de archivos NTFS presenta la siguiente estructura en la partición de disco donde se crea: área de arranque, tabla de archivos maestra, archivos del sistema, área de datos de los archivos. La tabla de archivos maestra es la estructura de datos más importante de un sistema NTFS . Contiene información sobre los atributos de todos los archivos y directorios existentes en el volumen. Por su parte los archivos del sistema son archivos cuyos clusters de datos contienen metadatos del sistema de archivos. 358 Ampliación de sistemas operativos El administrador de E/S es el componente central del subsistema de E/ S de Windows. Se encarga de atender las peticiones de E/S realizadas por las aplicaciones de los usuarios y el resto de componentes del ejecutivo. Además define y soporta la interfaz de trabajo con los drivers. 8.10. Lecturas recomendadas Si se desea obtener una explicación adicional de los contenidos tratados en este capítulo se pueden consultar, por ejemplo: el capítulo 1 1 de [Tanenbaum, 2009] y el libro [Russinovich et al. , 2009] . Asimismo para obtener una descripción detallada del uso de las funciones de la API Win 32 apare­ cidas en este capítulo se puede consultar los libros [Richter y Nasarre, 2007] y [Hart, 20 1 0] . También se puede consultar la librería MSDN (www . msdn . mi c ro s o f t . c om) . 8.11. Autoevaluación 8.1. Enumerar las características principales del sistema operativo Windows. (Respuesta en sección 8 .2.2) 8.2. Dibujar un diagrama adecuadamente rotulado de la estructura del sistema operativo Windows. Comentar brevemente cada uno de sus componentes. (Respuesta en sección 8 .2.3) 8.3. ¿Qué es la API Win32? Comentar algunas de las librerías DLL que implementan la funcionalidad de la API. (Respuesta en sección 8.2.4) 8.4. Explicar las acciones que pueden producirse cuando se invoca a una función de una librería DLL. (Respuesta en sección 8 .2.4) 8.5. Explicar la implementación de la interfaz con el usuario de Windows. (Respuesta en sección 8 .2.5) 8.6. Explicar cómo se invocan y el tratamiento de las llamadas al sistema en Windows. (Respuesta en sección 8.2.6) 8.7. ¿Qué es el registro de Windows ? ¿Cómo se implementa? ¿Quién puede acceder al registro? (Respuesta en sección 8.2.7) 8.8. ¿Qué son los objetos del núcleo? ¿Cómo se implementan? ¿Quién se encarga de gestionarlos? (Respuesta en sección 8.2.9) 8.9. Explicar qué es el identificador de un objeto y para qué se utiliza. (Respuesta en sección 8.2.9) 8.10. Enumerar y comentar brevemente los mecanismos de seguridad disponibles en Windows NT. (Respuesta en sección 8.2. 1 0) 8.11. Enumerar y comentar brevemente las estructuras de datos utilizadas en Windows para implementar la seguridad. (Respuesta en sección 8.2. 1 0) El sistema operativo Windows 359 8.12. Describir la implementación de los procesos multihilos en Windows. (Respuesta en sección 8 .3 . 1 ) 8.13. Dibujar un diagrama, adecuadamente rotulado, con las principales estructuras d e datos mantenidas por Windows para implementar un proceso multihilo que conste de dos hilos. Explicar brevemente la utilidad de cada una de las estructuras que aparezcan en el diagrama. (Respuesta en sección 8 .3 . 1 ) 8.14. Dibujar el diagrama de transición de estados d e u n hilo en Windows. (Respuesta e n sección 8 . 3 .2) 8.15. Explicar la creación de procesos e hilos en Windows. (Respuesta en sección 8 . 3 .2) 8.16. Dibujar un diagrama, adecuadamente rotulado, con los niveles de prioridad definidos en Windows. (Respuesta en sección 8 . 3 . 3 ) 8.17. Describir las prioridades asociadas a u n proceso o a u n hilo e n Windows. (Respuesta en sección 8 . 3 . 3 ) 8.18. Describir l a implementación y la gestión d e las colas d e prioridad e n Windows . (Respuesta e n sección 8 . 3 . 3 ) 8.19. Explicar l a duración d e u n cuanto e n Windows . (Respuesta en sección 8 . 3 . 3 ) 8.20. Describir la estructura y la invocación del planificador de Windows. (Respuesta en sección 8 . 3 . 3 ) 8.21. Explicar el objetivo y las circunstancias bajo las cuales Windows aumenta temporalmente la prio­ ridad actual de un hilo. (Respuesta en sección 8 . 3 .3) 8.22. Describir los mecanismos de sincronización utilizados por Windows. (Respuesta en sección 8 .4. 1 ) 8.23. Describir los mecanismos d e sincronización entre procesos multihilos disponibles e n Windows. (Respuesta en sección 8 .4.2) 8.24. Describir los mecanismos de comunicación entre procesos multihilos disponibles en Windows. (Respuesta en sección 8.4.3) 8.25. Explicar la implementación del administrador de memoria de Windows. (Respuesta en sección 8 . 5 .2) 8.26. Explicar la gestión de la memoria virtual en Windows. (Respuesta en sección 8 . 5 .2) 8.27. Describir la traducción de direcciones en Windows. (Respuesta en sección 8 . 5 . 3 ) 8.28. Describir el uso que hace Windows del concepto de conjunto de trabajo de un proceso. (Respuesta en sección 8 . 5 .4) 8.29. ¿Qué es la base de datos de los números de marcos de página? ¿En que posibles estados puede encontrarse un marco de página? (Respuesta en sección 8 . 5 .4) 8.30. Dibujar un diagrama adecuadamente rotulado con las transiciones entre las listas de marcos de página definidas en Windows. Explicar el diagrama dibujado. (Respuesta en sección 8 . 5 .4) 360 Ampliación de sistemas operativos 8.31. ¿Qué son los archivos de páginas? (Respuesta en sección 8.5 .5) 8.32. Enumerar diez funciones de la API Win32 disponibles para la gestión de archivos y directorios en Windows (Respuesta en sección 8.6. 1 ) 8.33. Explicar el tratamiento que realiza Windows de los diferentes sistemas de archivos existentes en la memoria secundaria. (Respuesta en sección 8.6.2) 8.34. Enumerar y comentar las características principales de un sistema de archivos NTFS . (Respuesta en sección 8.7. 1 ) 8.35. Explicar la estructura en disco que presenta u n sistema de archivos NTFS . (Respuesta en sección 8.7.2) 8.36. Describir la implementación de la tabla de archivos maestra. (Respuesta en sección 8.7 .2) 8.37. Enumerar algunos ejemplos de archivos del sistema. (Respuesta en sección 8.7 .2) 8.38. Explicar la estructura de un directorio en un sistema de archivos NTFS . (Respuesta en sección 8.7.3) 8.39. Explicar la localización de archivos en un sistema de archivos NTFS . (Respuesta en sección 8.7.4) 8.40. Describir la implementación del subsistema de E/S en Windows . (Respuesta en sección 8 . 8 . 1 ) 8.41. Explicar qué es un driver de sistema de archivos, un driver PnP y un driver no PnP. (Respuesta en sección 8 . 8 .2) 8.42. Explicar los tipos de drivers que se distinguen de acuerdo con el modelo WDM. (Respuesta en sección 8.8 .2) 8.43. Dibujar un diagrama adecuadamente rotulado que ilustre la relación entre un objeto driver y sus obj etos dispositivos asociados. Explicar la interacción del administrador de E/S con estos objetos. (Respuesta en sección 8 . 8 .2) 8.44. Explicar cómo se gestionan las interrupciones en Windows. (Respuesta en sección 8 . 8 . 3 ) 8.45. Explicar qué son y cómo se gestionan las llamadas a procedimientos remotos ¿Qué tipos se distin­ guen? (Respuesta en sección 8 . 8 . 3 ) 8.46. Explicar qué son y cómo se gestionan las llamadas a procedimientos aplazados. (Respuesta en sección 8 . 8 . 3 ) El sistema operativo Windows 8.12. 361 Ejercicios 8.1. En un sistema Windows una cierta aplicación está implementada como un proceso monohilo. Supuesto que dicho proceso tiene asociada la clase de planificación alta y que la prioridad relativa del hilo está configurada al valor debajo de normal determinar: a) La prioridad base del proceso. b) La prioridad base del hilo. e) La prioridad actual del hilo. 8.2. Supóngase que en un cierto sistema Windows existen dos volúmenes C y D de tipo NTFS, y que el volumen de trabajo actual es el volumen D. Explicar el significado de las siguientes órdenes del intérprete cmd . exe : a) mkl i nk / h en l a c e l . txt \ t rabaj o \ l i s t ado . txt b) mkl i nk /d f o t o s C : \ imagene s \ f o t o s \ camara 8.3. Diseñar en el intérprete cmd . ex e de Windows un ejemplo que ilustre que un archivo en NTFS puede constar de múltiples flujos de datos. 8.4. En un volumen NTFS un cierto archivo consta de 10 clusters virtuales (Bv = O, 1, . . . , 9) los cuales se encuentran almacenados en los siguientes clusters lógicos BL: 6 1 , 62, 63, 90, 9 1 , 92, 93, 85, 86 y 1 1 2. Determinar la información relativa a la localización de las series del atributo de datos del archivo que se almacena en la entrada asociada al archivo en la MFT del volumen. 8.5. El proceso de compresión de un archivo en un sistema NTFS es el siguiente: primero se aplica el algoritmo de compresión sobre los 16 primeros clusters virtuales del archivo, si el resultado de la compresión se puede almacenar en menos de 16 cluster lógicos, entonces los clusters virtuales comprimidos se escriben en el disco, a ser posible en una única serie. Si el resultado de la compre­ sión ocupa 16 clusters entonces los clusters se almacenan sin comprimir en el disco en una única serie, si es posible. A continuación se repite el análisis con los siguientes 16 clusters virtuales del archivo, y así sucesivamente hasta analizar todos los clusters virtuales. Supóngase un cierto archivo cuyos datos ocupan 48 clusters virtuales. Los primeros 16 clusters virtuales han podido ser comprimidos en una serie de 8 cluster lógicos contiguos, la dirección del primer cluster lógico de la serie es BL = 40. Los siguientes 1 6 clusters virtuales no han podido ser comprimidos y se han almacenado en una serie de 16 cluster lógicos contiguos, la dirección del primer cluster lógico de la serie es BL = 50. Finalmente los últimos 16 clusters virtuales han podido ser comprimidos en serie de ocho clusters lógicos contiguos, la dirección del primer cluster lógico de la serie es BL = 95. Determinar la información relativa a la localización de las series del atributo de datos del archivo que se almacena en la entrada asociada al archivo en la MFT del volumen NTFS . 8.6 Realizar una búsqueda en Internet sobre las herramientas software disponibles para analizar el funcionamiento y el rendimiento del sistema operativo Windows. Apéndice A Soluci ones completas de los ej erci cios A.l. Soluciones ejercicios capítulo 1 Solución Ejercicio 1.1 a) Para visualizar la ruta absoluta del directorio de trabaj o actual se debe escribir la orden pwd b) Para visualizar un listado del contenido del directorio de trabajo actual que permita diferenciar entre archivos y directorios se puede escribir la orden ls - ls -1 F También se podría usar la orden e) Para visualizar un listado que únicamente contenga los nombres de los archivos que acaban con la 1 1 1 1 letra o o con la letra t se debe escribir la orden l s * [ ot ] d) Para mover al subdirectorio f o t o s los archivos da t o s y f o t o smu l se debe escribir la orden mv da t o s f o t o smu l f o t o s e ) Para hacer que e l directorio f o t o s pase a ser e l directorio d e trabajo actual se debe escribir la orden cd f o t o s 363 364 Ampliación de sistemas operativos Solución Ejercicio 1.2 a) Esta orden ordena ( s o r t ) por orden alfabético las líneas del archivo fA y escribe el resultado en el archivo fB. b) Esta orden almacena en el archivo asd el resultado de la orden ls 1 que genera un listado largo de los archivos del directorio de trabajo incluyendo tamaño, permisos, propietario, permisos, etc. Si el archivo asd no existe se crea. Si el archivo ya existe se eliminará su contenido y se remplazará por la salida de la orden l s - l . - , e) Esta orden almacena en el archivo wer t 2 3 el resultado de l a orden l s - i , que genera un listado largo de los archivos del directorio de trabajo mostrando en la primera columna el número de nodo-i de cada archivo. Si el archivo wer t 2 3 no existe se crea. Si el archivo ya existe, la salida de la orden l s - i se añade a partir del final del archivo. d) Esta orden muestra en la pantalla el contenido del archivo prueba . txt ubicado en el directorio de trabaj o inicial. Señalar que el nombre de ruta del archivo especificado en esta orden hace uso del comodín tilde ' � ' el cual sustituye al nombre de ruta absoluta ( / home o /us ers) del directorio que contiene los directorios de trabajo de todos los usuarios. Si se utiliza en la forma 1 entonces hace referencia al nombre de ruta absoluta del directorio de trabajo inicial del usuario. 11 � 11 Por lo tanto supuesto, por ejemplo, que el directorio de trabaj o inicial del usuario que invoca esta orden es / home / j o s erna entonces la orden propuesta expandiendo el comodín tilde sería equivalente a mo re / home / j o s ema / prueba . txt e) Esta orden copia los archivos par t epr imera y par t e s egunda en el directorio traba j o . Señalar que la orden hace uso del comodín llaves { c adl , c ad2 , . . . , cadN } el cual expande una palabra por cada una de las cadenas de caracteres cadj j = 1 , . ,N incluidas dentro de las llaves. 11 11 . . Solución Ejercicio 1.3 a) Se puede usar por ejemplo el comando c a t / e t c / she l l s b) S e puede usar por ejemplo el comando where i s bash También se podría usar el comando loca t e bash o el comando whi ch bash. e) Se puede visualizar el valor de la variable de entorno SHELL: Soluciones completas de los ejercicios 365 echo $ SHELL Si estamos usando el intérprete bash se mostrará en pantalla un mensaj e similar a / b i n / bash d) Se puede visualizar el valor de la variable del intérprete BASH_VERSION: echo $ BASH_VERS I ON e) En bash existen definidas diferentes variables especiales, entre las más utilizadas se encuentran las siguientes: • $ ? . Contiene el estatus de salida del último comando ejecutado por bash. El estatus de sa­ lida de un comando es un valor entero positivo pequeño que éste devuelve cuando finaliza. Como regla general se suele considerar que si el estatus de salida es igual a O entonces el comando se ha ejecutado con éxito, mientras que si es igual a 1 entonces se ha producido al­ gún error. También se pueden devolver otros valores distintos, de acuerdo con algún criterio prestablecido por el programador. El comando exi t permite especificar el valor del estatus de salida. • $ $. Contiene el PID del proceso que implementa el intérprete. • $ ! . Contiene el PID del proceso asociado al último comando que fue ejecutado en segundo plano por el intérprete. • $ o . Contiene la cadena de caracteres asociada al nombre del shell script que se está ejecutan­ do actualmente en el intérprete. • $ # . Contiene una cadena de caracteres con el número de argumentos de entrada del shell script o la función. • $ * . Contiene los argumentos de entrada del shell script o función. Se suele utilizar en los bucles f or. • $ i . Con i = 1, 2, 3 , . . . , 9 contiene la cadena de caracteres asociada al argumento i-ésimo de entrada del shell script que se está ejecutando actualmente. f) La sustitución de comandos permite usar la salida de un comando como si fuese el valor de una variable. En bash esto se consigue de la siguiente forma: $ ( c omando ) Por ejemplo la orden D I R = $ ( pwd ) almacena en la variable D I R la salida del comando pwd el cual muestra el nombre de ruta del directorio de trabaj o actual. Otra forma de implementar la sustitución de comandos es entrecomillando el comando con acentos graves : 366 Ampliación de sistemas operativos ' c omando ' Luego la orden DIR= ' pwd ' es equivalente a DIR= $ ( pwd ) . g) En bash una cadena de caracteres entrecomillada con comillas dobles, " c adena " , es analizada por el intérprete en busca de comandos que deban ser sustituidos o caracteres que deban ser expandi­ dos. Por ejemplo, supuesto que en el directorio de trabaj o actual existe el archivo s c r ip t l . sh que contiene 78 palabras entonces la orden echo " N º pa l abras = $ ( wc -w s c r ip t l . sh ) " produce la siguiente salida en la pantalla: Nº palabras = 7 8 s c r i p t l . sh Por el contrario, una cadena de caracteres entrecomillada con comillas simples, 'c adena', no es analizada por el intérprete. Por ejemplo, la orden echo ' N º pa l abras = $ ( wc -w s c r i p t l . sh ) ' genera la siguiente salida en la pantalla: Nº pa l abras = $ ( wc -w s c r i p t l . sh ) h) En bash l a introducción de datos desde el teclado se consigue usando la orden read. Por ejemplo, la orden: read -p " Introduce tu nombr e : " nombre muestra en la pantalla el mensaj e In tradu c e tu nombre y se queda a la espera de que el usuario introduzca por el teclado una cadena de caracteres y pulse la tecla de retomo de carro. Cuando esto suceda la cadena introducida se almacenará en la variable nombre . i) Los operadores lógicos disponibles en b a s h son: • El && E2 . Realiza la operación AND lógica entre El y E2. • El • 1 1 E2 . Realiza la operación O R lógica entre E l y E2. ! E l . Realiza la operación NOT lógica sobre E l . Los operadores de comparación de cadenas disponibles en bash son: • A = B. Comprueba si las cadenas A y B son iguales. Soluciones completas de los ejercicios • A ! = B. Comprueba si las cadenas A y B son distintas. • A • A > B. Comprueba s i l a cadena A es mayor lexicográficamente que l a cadena B . • - n A . Comprueba s i l a cadena A e s n o nula y tiene una longitud mayor d e cero. • - z A. Comprueba si la cadena A tiene una longitud igual a cero. < 367 B . Comprueba si la cadena A es menor lexicográficamente que la cadena B . Finalmente los operadores de comparación de números enteros disponibles en bash son: e X -lt y. Comprueba si x es menor que y. e X -le y. Comprueba s i x es menor o igual a y. e X - eq y. Comprueba si x es igual a y. e X -ne y. Comprueba si x es distinto de y. e X -ge y. Comprueba si x es mayor o igual a y. e X -gt y. Comprueba si x es mayor que y. j) Algunos de los operadores de comparación de atributos de archivos disponibles en bash son: • -a archivo. Comprueba si el archivo existe. • - e archivo . Comprueba si el archivo existe. • - • - s archivo. Comprueba si el archivo existe y no está vacío. f archivo. Comprueba si el archivo existe y es un archivo regular. k) El operador [ expre s i ón J evalúa la expresión incluida en su interior. Esta es la forma más habitual de realizar la evaluación de condiciones en las sentencias condicionales. También se puede utilizar el comando t e s t . 1 ) S e puede usar el comando l e t operac i ón o e l doble paréntesis ( ( operac i ón ) ) . Las siguien­ tes líneas ilustran la realización de operaciones aritméticas con enteros: X=2 ; Y = 3 ; l e t Z l =X+Y Z 2 = $ ( ( X+Y ) ) echo $ Z l $ Z 2 También se podría usar e l comando expr. m) En bash una sentencia condicional de tipo i f tiene la siguiente sintaxis : 368 Ampliación de sistemas operativos i f c ondi c i ón1 then s ent enc i as 1 e l i f c ondi c i on2 then s entenc i a s 2 e l i f c ondi c i on3 then s ent enc i a s 3 else s entenc i a s 4 fi L a sentencia condicional anterior s e puede escribir también d e la siguiente forma: i f c ondi c i ón1 ; then s ent enc i a s 1 e l i f c ondi c i on2 ; then s ent enc i a s 2 e l i f c ondi c i on3 ; then s entenc i a s 3 e l se s ent enc i a s 4 fi Las siguientes líneas muestran un ejemplo de sentencia condicional d e tipo i f en bash: X = 2 ; Y= 5 ; i f [ $ X - l e $Y l ; then echo X = $ X e s menor o i gual a Y= $Y el se e cho X=$X e s mayor a Y= $ Y fi n ) E n bash u n bucle whi l e tiene la siguiente sintaxis : whi l e [ c ondi c i ón do s ent enc i a s done Un bucle unt i l tiene una sintaxis similar, simplemente hay que cambiar whi l e por un ti l. Las siguientes líneas muestran un ejemplo de bucle whi l e : Soluciones completas de los ejercicios 369 COLOR= " ve rde " whi l e [ $ COLOR ! = " ro j o " do echo " Incorrec t o " read -p " Introdu c e un c o l or : " COLOR done echo " C orrec t o " o) En bash un bucle f o r se implementa de forma diferente a otros lenguajes de programación, como por ejemplo C. Su sintaxis es la siguiente: f o r var i ab l e [ in l i s t a ] do s ent enc i a s que u s an $var i ab l e done Las siguientes líneas muestran un ejemplo de bucle f o r : for i in $ ( l s ) ; do echo " i t em : $ i " done El siguiente ejemplo ilustra una implementación del bucle for más similar a la usada en C: for i in $ ( seq 1 1 0 ) ; do echo " i = $ i " done p) En bash la sentencia c a s e presenta la siguiente sintaxis : c a s e c adena i n patron1 ) s en t enc i as 1 ; ; pat ron2 ) s ent enc i a s 2 ; ; esac Las siguientes líneas muestran un ejemplo de uso de c a s e : 370 Ampliación de sistemas operativos Z=2 case $ Z 1 ) echo echo 2 ) echo echo 3 ) echo echo * ) echo echo e s ae in Uno A¡ ¡ Dos B Tres e¡ ¡ Cua l qu i e r o t r o va l o r Otros ¡ ¡ ' ' q) El siguiente ejemplo ilustra cómo se declara y se invoca en bash una función con parámetros de entrada que devuelve un valor de salida #Dec l arac i ón de una func i ón asd que rec ibe dos argument o s de entrada : func t i on asd { echo " pr imer parame t r o = $ 1 , segundo parame t r o = $ 2 " re turn 3 # Invo c a c i ón de l a fun c i ón c on l o s paráme t r o s Ho l a a s d Ho l a Mundo echo " $ ? " #Vi sual i z ac i ón de l c ódigo de s a l i da y Mundo Señalar que para devolver un cierto valor desde una función se debe usar el comando r e turn. El valor devuelto hace las veces de estatus de salida. Solución Ejercicio 1.4 El shell script de la Figura l. 7 en primer lugar muestra en el dispositivo de salida estándar, por defecto la pantalla, el mensaje Introduc e una cadena : En segundo lugar invoca al comando read para leer una cadena de caracteres desde el dispositivo de entrada estándar, por defecto el teclado. La cadena leída se almacena en la variable CADENA. En tercer lugar define una variable LONG I TUD a la que le asigna la salida del comando echo $ CADENA 1 wc -e Este comando, mediante el uso de una tubería ( 1 ) , pasa l a cadena contenida en l a variable CADENA como entrada del comando wc - e , el cual cuenta el número de caracteres que contiene la cadena. A continuación mientras el valor de LONGITUD sea mayor que O se ejecuta un bucle whi l e en el que se realizan las siguientes acciones en cada iteración del bucle: en primer lugar se define una variable x como la unión de la cadena de caracteres x y la unión de la cadena de caracteres que resulta de eliminar Soluciones completas de los ejercicios 371 (comando cut) LONGITUD caracteres de la variable CADENA. En segundo lugar se decrementa en una unidad el valor de LONG I TUD. Cuando se sale del bucle whi l e se imprime en la pantalla el valor de la variable x la cual contiene la cadena CADENA escrita al revés. En definitiva este shell script solicita al usuario una cadena de caracteres y la muestra en pantalla escrita al revés. Solución Ejercicio 1.5 El shell script de la Figura 1 . 8 en primer lugar comprueba si el número de argumentos de entrada ( $ #) del shell script es mayor que cero. En caso negativo muestra en el dispositivo de salida unos mensajes para indicar que se ha producido un error al especificar los argumentos e invoca al comando exi t para terminar la ejecución del script y devolver como estatus de salida el valor l. Por el contrario, si se cumple la condición entonces ejecuta un bucle f o r tantas veces como argu­ mentos de entrada ( $ * ) se le hayan pasado al shell script. En cada iteración del bucle se realizan las siguientes acciones : en primer lugar se ejecuta el comando mv para mover el archivo cuyo nombre ha sido pasado como argumento del script al directorio . / pap e l era el cual debe estar creado antes de ejecutar el shell script. Nótese que si no es así el efecto del comando mv es el del cambiar el nombre del archivo pasado como argumento por el nombre de pape l era. Señalar que los posibles mensajes de error que pueda generar este comando son redireccionados (2 >) al dispositivo nulo, el cual actúa como un sumidero. En segundo lugar se comprueba el estatus de salida ( $ ? ) del comando mv. En función de su valor se muestra un determinado mensaj e en el dispositivo de salida estándar. Cuando se termina de ejecutar el bucle f o r se ejecuta el comando exi t para terminar la ejecución del script y devolver como estatus de salida el valor O. En definitiva este shell script mueve los archivos pasados como argumentos de entrada a un directorio previamente creado en el directorio de trabajo denominado pap e l era. Es decir, implementa una especie de papelera de reciclaje. # ! / b i n / bash # CONT= O wh i l e [ $ C ONT - l e 6 0 ] do date CONT = $ ( ( $ CONT + 1 ) ) s l e ep 1 done Figura A.l - Solución del Ejercicio 1 .6 372 Ampliación de sistemas operativos Solución Ejercicio 1.6 En la Figura A. l se muestra el código de un shell script para el intérprete bash que muestra la fecha y la hora del sistema cada segundo durante un minuto. Solución Ejercicio 1.7 En la Figura A.2 se muestra el código de un shell script para el intérprete bash que muestra el día de la semana que fue ayer. # ! / b i n / ba s h # HOY= $ ( da t e + %w } AYER= $ ( ( $ HOY - 1 } } # da t e + %w devuelve e l d í a de l a s emana en f o rma t o nurne r l c o , # c o n va l o r e s c omprend i d o s e n t r e O # En e s t e c a s o , ( domingo } y 6 ( s ábado } . ayer t ornará va l o r e s entre - 1 y 5 . # HOY a lmac ena el nume r o de d i a para hoy . # AYER a l ma c e n a e l va l o r de HOY echo meno s l. " Ayer fue " c a s e $AYER i n -1} echo 0} echo " S abado " ; ; " Domingo 11 ; ; 11 Lune s .. ; ; 1} echo 2} echo " Ma r t e s u 3} echo " Mi e rc o l e s " ; ; 4} echo 5} *} echo " Jueves u ; ; 11 V i e rne S 11 j j echo " Inde f i n i do " ; ; ; ; esac Figura A.2 - Solución del Ejercicio 1 .7 Solución Ejercicio 1.8 La función de librería sys t em cuya declaración es # inc lude < s td l i b . h> int sys t em ( c ons t char * C adena ) ; resulta útil para ejecutar desde un programa una orden del intérprete de comandos. Esta función invoca un intérprete de comandos para que ejecute la orden c adena y regresa después de que la orden se haya terminado de ejecutar. Si la invocación al intérprete falla el valor devuelto por sys t em es 1 27. Si se puede invocar al intérprete de comandos pero se produce algún error en la ejecución de la orden el valor Soluciones completas de los ejercicios 373 devuelto es - 1 . Si el comando se ejecuta correctamente, la función devuelve el estatus de salida de la orden. En la Figura A.3 se muestra un pequeño programa escrito en C que ilustra el uso de esta función. # i nc lude < s t dl ib . h> ma i n ( ) { i n t s a l i da ; s a l i da = sys t em ( " \ n echo $ PATH \ n " ) ; p r i n t f ( " \ n S a l i da= %d \ n " , s a l i da ) ; Figura A.3 - Solución del Ejercicio 1 . 8 Solución Ejercicio 1.9 a) La máscara de modo 6644 en octal equivale a la máscara de modo binaria 11 O 11 O 1 O O 1 O O que indica que los bits S_I SUID, S_I SGID están activados, que el propietario del archivo tiene permiso de lectura y escritura, y que el grupo propietario de archivo y el resto de usuarios tienen permiso de lectura sobre el archivo. b) La máscara de modo simbólica asociada a este archivo es : - rwS r - S r - - e) La máscara de modo simbólica - rwx r - - - - T es equivalente a la máscara de modo binaria 001 111 100 000 que en octal es 1 7 4 o . Luego un posible comando a utilizar es: chmod 1 7 4 0 r e s u l tados Solución Ejercicio 1.10 El enunciado del problema afirma que s 0 7 pertenece al usuario Wo l f (UID=50 1 , GID=202). Asi­ mismo su máscara de modo simbólica es - rwxrw- rwx, cuyo significado es el siguiente: • El propietario del archivo (Wo l f ) puede leer, escribir y ejecutar el archivo. Además este archivo tiene su bit S_I SUI D desactivado. • Los usuarios del grupo propietario del archivo, por defecto GID=202, pueden leer y escribir el archivo, pero no pueden ejecutarlo. Además este archivo tiene su bit S_I SGID desactivado. • El resto de usuarios pueden leer, escribir o ejecutar el archivo. Además este archivo tiene su bit s I SVTX desactivado. 374 Ampliación de sistemas operativos Una vez explicado el significado de la máscara de modo simbólica es posible contestar a lo que pide en el enunciado: a) De acuerdo con la máscara de modo simbólica el propietario del archivo (Wo l f) tiene permiso de ejecución sobre su archivo s 0 7 . Al proceso A que se crea en el sistema asociado a la ejecución de s 0 7 se le asigna UID=50 1 y EUID=50 1 . Al ejecutar el proceso A en primer lugar se invoca a la llamada al sistema getuid para asignar el valor del UID del proceso a la variable u [ o ] , en este caso u [ O ] = 5 0 1 . Luego se invoca a la llamada al sistema g e t e u i d para asignar el valor del EUID del proceso a la variable u [ 1 J , en este caso u [ 1 J = 5 o l . A continuación se invoca a l a llamada al sistema s e tu i d para asignar al UID y al EUID del proceso el valor de u [ 1 ] . Como el EUID del proceso A no es el del superusuario y el valor de u [ 1 ] coincide con el valor del UID del proceso A entonces solamente el EUID del proceso A se configura con el valor de u [ 1 ] . En resumen el resultado de esta llamada al sistema es configurar el EUID del proceso A al valor EUID=50 1 , valor que por otra parte ya tenía sin necesidad de haber ejecutado esta llamada al sistema. Cómo s e tu i d se ha ejecutado con éxito entonces se asigna el valor O a la variable u [ 2 J • Luego se comprueba si el valor de u [ 2 J es igual O. Cómo la comprobación es positiva se invoca a la función anfitriona al pasándole como argumento la dirección de memoria de la función huésped a2 . La primera acción que se realiza al ejecutar la función a l es invocar a la función huésped a2 pasándole como argumento el valor inicial de la variable u, que es 30,4. Se comienza a ejecutar la función a2 . La primera acción asociada a a2 es ejecutar un bucle 4 veces dentro del cual se multiplica el valor de la variable a por 0.5. El resultado de la multiplicación se asigna a la variable a A continuación se muestran los valores que toma la variable a en cada iteración del bucle: • (h = - 1 ) a = 0,5 * • (h = O) a = 0,5 * 1 5,2 = 7,6 • (h = 1 ) a = 0,5 * 7,6 = 3,8 • (h = 2) a = 0,5 * 3 , 8 = 1 ,9 30,4 = 1 5 ,2 Finalizado el bucle se ejecuta la instrucción re turn ( a ) con lo que la función a2 finaliza devol­ viendo 1 ,9 como valor de salida que es asignado a la variable u de la función a l . A continuación se ejecuta la instrucción r e turn ( u ) de la función al con lo que finaliza devolviendo el valor 1 ,9 como valor de salida que es asignado a la variable z del código principal. Por último se imprime en pantalla el mensaje [501 , 501 , y el proceso A finaliza su ejecución. o, 1 . 90 ] Soluciones completas de los ejercicios 375 b) El usuario Fox no pertenece al grupo de Wo l f ya que sus GID son distintos. De acuerdo con la máscara de modo simbólica tiene permiso de ejecución sobre s o 7 . Al proceso A que se crea en el sistema asociado a la ejecución de s 0 7 se le asigna UID=502 y EUID=502. El funcionamiento del proceso A es similar al descrito en el caso anterior, con la diferencia de que en este caso el mensaj e que aparece en pantalla es [ 502 , 502 , A.2. o, 1 . 90] Soluciones ejercicios capítulo 2 Solución Ejercicio 2.1 Al escribir la orden j 0 9 4 se comienza a ejecutar el programa j 0 9 . Supóngase que a la ejecución de dicho programa se le asocia el proceso A, cuyo PID= 1 1 20 según el enunciado. En primer lugar se comprueba si el número de argumentos con que ha sido invocado j o 9 es distinto de dos. Conviene recordar que el nombre del programa cuenta como argumento. Puesto que j o 9 4 tiene dos argumentos la condición del i f no se cumple y se pasan a ejecutar las sentencias del e l s e . En segundo lugar s e invoca a la función d e librería a t o i que convierte e l segundo argumento ( ' 4 ' ) de la invocación de j o 9 a número entero y lo almacena en la variable a. En tercer lugar se ejecuta un bucle whi l e cuya condición para que se ejecute una iteración es que la llamada al sistema f o rk devuelva el valor O y que la variable b no tome el valor a. Recuérdese que f o rk crea un proceso hijo y devuelve O para el proceso hijo y el PID del hijo al proceso padre. La evaluación de esta condición da como resultado la creación de un hijo B con PID= 1 1 2 1 , que al cumplir que f o rk le ha devuelto O y que b es distinta de 4 ejecuta el contenido del bucle. El proceso padre A no ejecuta el bucle ya que fork no le ha devuelto un O sino 1 1 2 1 , por lo que ejecuta una llamada al sistema wa i t y se queda a la espera de que su proceso hijo B termine. El proceso B muestra en pantalla el mensaj e Mens a j e 1 [ 1 1 2 1 ] e incrementa en una unidad el valor de la variable b que ahora toma el valor l. A continuación evalúa la condición del bucle que da como resultado la creación de un hijo C con PID= 1 1 22, que al cumplir que fork le ha devuelto O y que b es distinto de 4 ejecuta el contenido del bucle. El proceso padre B no ejecuta el bucle ya que f ork no le ha devuelto un O sino 1 1 22, por lo que ejecuta una llamada al sistema wa i t y se queda a la espera de que su proceso hijo C termine. El proceso C muestra en pantalla el mensaj e Mens a j e 1 [ 1 1 2 2 ] e incrementa en una unidad el valor de la variable b que ahora toma el valor 2. A continuación evalúa la condición del bucle que da como resultado la creación de un hijo D con PID= 1 1 23, que al cumplir que f o rk le ha devuelto O y que b es distinto de 4 ejecuta el contenido del bucle. El proceso padre C no 376 Ampliación de sistemas operativos ejecuta el bucle ya que f o rk no le ha devuelto un O sino 1 1 23, por lo que ejecuta una llamada al sistema wa i t y se queda a la espera de que su proceso hijo D termine. El proceso D muestra en pantalla el mensaj e Mens a j e 1 [ 1 1 2 3 ] e incrementa en una unidad el valor de la variable b que ahora toma el valor 3 . A continuación evalúa la condición del bucle que da como resultado la creación de un hijo E con PID= 1 1 24, que al cumplir que f ork le ha devuelto O y que b es distinto de 4 ejecuta el contenido del bucle. El proceso padre D no ejecuta el bucle ya que f ork no le ha devuelto un O sino 1 1 24, por lo que ejecuta una llamada al sistema wa i t y se queda a la espera de que su proceso hijo E termine. El proceso E muestra en pantalla el mensaje Mens a j e 1 [ 1 1 2 4 ] e incrementa en una unidad el valor de la variable b que ahora toma el valor 4. A continuación evalúa la condición del bucle que da como resultado la creación de un hijo F con PID= 1 1 25 , sin embargo como b es igual 4 no ejecuta el contenido del bucle sino que ejecuta la llamada al sistema wa i t , pero como F no tiene hijos no se suspende sino que imprime en pantalla el mensaj e Mens a j e 2 [ 1 1 2 5 ] = 1 1 2 4 y finaliza. Por su parte el proceso padre E tampoco ejecuta el bucle porque para él fork no ha devuelto un O sino 1 1 25, y además b=4 por lo que ejecuta una llamada al sistema wa i t y se queda a la espera de que su proceso hijo F termine. Como éste ya ha finalizado imprime en pantalla el mensaje Mensaj e 2 [ 1 1 2 4 ] = 1 1 2 3 y finaliza. Puesto que su hijo E ya ha terminado se despierta al proceso D que imprime en pantalla el mensaje Mensaj e 2 [ 1 1 2 3 ] = 1 1 2 2 y finaliza. Como su hijo D ya ha terminado se despierta al proceso C que imprime en pantalla el mensaj e Mensaj e 2 [ 1 1 2 2 ] = 1 1 2 1 y finaliza. A continuación puesto que su hijo C ya ha terminado se despierta al proceso B que imprime en pantalla el mensaje Mens a j e 2 [ 1 1 2 1 ] = 1 1 2 0 y finaliza. Finalmente como su hijo B ya ha terminado se despierta al proceso A que imprime en pantalla el mensaj e Mensaj e 2 [ 1 1 2 0 ] = 1 0 0 0 y finaliza. Soluciones completas de los ejercicios 377 Solución Ejercicio 2.2 1) Al escribir la orden j O 7 se comienza a ejecutar el programa j O 7 al que se le asocia, de acuerdo con el enunciado, un proceso A cuyo PID es 789. Al ejecutar el proceso A en primer lugar se ejecuta la llamada al sistema s i gnal que asigna el manej ador func a las señales del tipo S I GSUR l . En segundo lugar, se asigna a la variable u el resto que resulta de dividir u entre 2, es decir, se le asigna el valor 15 %2 = l. En tercer lugar se crea, mediante la invocación de la llamada al sistema f o rk, un proceso hijo B del proceso padre A. El PID de este proceso es, de acuerdo al enunciado, igual a 790. Luego en este punto del programa se tienen dos procesos el padre A y el hijo B . Es el planificador del núcleo el que decidirá qué proceso A o B se ejecuta en primer lugar. Cuando se planifica para ejecución el proceso A, la llamada al sistema fork le devuelve el PID del proceso hijo creado, es decir, 790. En consecuencia no se cumple la condición que evalúa el i f por lo que se ejecuta la llamada al sistema exi t que finaliza el proceso A y devuelve a su proceso padre (el interprete de comandos) un estatus de salida igual a l. Cuando se planifica para ejecución el proceso B , la llamada al sistema fork le devuelve el valor O. En consecuencia se cumple la condición que evalúa el i f por lo que se ejecuta la llamada al sistema execv que invoca al programa ex2 . La primera acción del programa ex2 es invocar a la llamada al sistema pau s e que hace que el proceso B se quede a la espera de recibir una señal que no ignore o que no tenga bloqueada. 2) Para responder adecuadamente a este apartado se debe recordar que durante la ejecución de la llamada al sistema execv se carga en memoria el nuevo ejecutable (ex2 ) reemplazando las re­ giones de código, datos y pila de usuario del proceso asociado al ejecutable actual (j 0 7 ) . Además el núcleo borra en el área-u asociada al proceso A las direcciones de los manej adores de señales y establece las acciones por defecto para cada tipo de señal . En el caso de la señal S I GUSRl su acción asignada por defecto es terminar el proceso. Al ejecutar la orden ki l l - S I GUSRl 7 9 0 se envía una señal S I GUSRl al proceso con PID igual a 790, es decir, al proceso B . Este proceso no tiene establecido un manej ador para este tipo de señales luego se ejecuta la acción por defecto, en consecuencia el proceso B finaliza. Solución Ejercicio 2.3 1) Al escribir la orden s O S & se comienza a ejecutar el programa s O S en segundo plano. Supóngase que a la ejecución de dicho programa se le asocia el proceso A, por el enunciado se sabe que su PID es 1 035. Al ejecutar el proceso A en primer lugar se invoca a la llamada al sistema s i gna l para especificar que cuando el proceso reciba la señal S I GUSRl la acción que debe realizar el núcleo es la acción por defecto asociada a dicha señal, es decir, terminar el proceso. En segundo lugar se invoca a la llamada al sistema f o r k para crear un proceso hij o B , cuyo PID sería1 036. Como f ork devuelve 378 Ampliación de sistemas operativos el PID del proceso hijo al padre, entonces no se cumple la condición i f y se invoca a la llamada al sistema wa i t que suspende la ejecución del proceso A hasta que finalice su proceso hijo B . Asimismo cuando el proceso hijo B sea planificado invocará a l a llamada al sistema s i gna l para especificar que cuando reciba una señal S I GUSR1 se debe ignorar. A continuación invoca a la llamada al sistema pau s e que hace que el proceso B quede a la espera de una señal que no ignore o que no tenga bloqueada. 2) Al escribir la orden ki l l - S I GUSR1 1 0 3 6 se envía una señal S I GUSR1 al proceso con PID 1 036, es decir, al proceso hijo B. Dicha señal es ignorada por el proceso. 3) Finalmente al escribir la orden ki l l - S IGUSR2 1 0 3 6 se envía ahora una señal S IGUSR2 al proceso B . Como no se ha especificado ninguna acción para esta señal se ejecuta la acción por defecto, que es terminar el proceso B . Así internamente el núcleo invoca a la rutina exi t ( ) siendo el estatus de salida para el proceso padre A el número asociado a dicha señal, cuyo valor depende de cada SOBUNIX y que lo vamos a denotar por NS . Puesto que el proceso B ha finalizado, obviamente ya no se ejecuta la sentencia exi t ( 5 ) . El núcleo devuelve al proceso A el PID 1 036 del proceso hij o y el estatus de salida NS. Así el proceso A imprime en pantalla el mensaje 1 0 3 6 NS y finaliza. Solución Ejercicio 2.4 Al escribir la orden m O 9 se comienza a ejecutar el programa m O 9 . Supóngase que a la ejecución de dicho programa se le asocia el proceso A, cuyo PID=2045 según el enunciado. El proceso A hace una llamada al sistema fork para crear un proceso hijo (proceso B) al cual se le asigna, de acuerdo con el enunciado, PID= 2046. La llamada al sistema fork devuelve O al proceso hijo y 2046 al proceso padre. Estos valores se almacenan en la variable a del padre y del hijo, respectivamente. Depende del planificador qué proceso se ejecuta después del f o rk, el padre A o el hijo B . Supóngase que se ejecuta primero el proceso hij o B . Entonces entra en un bucle whi l e infinito, en cada pasada del bucle se realizan las siguientes acciones : en primer lugar se realiza una operación binaria XOR ( a = a " 1 O), es decir, a = a EB 1 0 = a· 1 0 + a · 10, entre la variable a, que contiene el valor O en la primera pasada del bucle, y el número 10, almacenando el resultado en a. En segundo lugar se invoca a la llamada al sistema ra i s e que envía al proceso B la señal S I GCHLD, la acción por defecto asociada a esta señal es ignorarla, por lo que el proceso B no se ve perturbado por dicha señal. Esta llamada al sistema devuelve un O si se ejecuta con éxito y - 1 en caso contrario. Se va a suponer que se ejecuta con éxito. En tercer lugar se imprime en pantalla el mensaje Mensaj e 1 [ 2 0 4 6 ] : 0 : 1 0 Soluciones completas de los ejercicios 379 si se trata de una iteración impar del bucle, o el mensaje Mensaje 1 [2046]: 0: O si se trata de iteración par. En cuarto lugar invoca a la llamada al sistema s 1 eep que suspende la ejecución del proceso B durante 4 segundos. Cuando se planifica el proceso padre A invoca a la llamada al sistema sleep que suspende su ejecu­ ción durante 16 segundos. Cuando reanuda su ejecución invoca a la llamada al sistema kill para enviar la señal SIGUSR1 al proceso hijo B. Esta señal tiene establecida como acción por defecto la terminación del proceso que la recibe. A continuación el proceso A invoca a la llamada al sistema sleep que suspende su ejecución du­ rante 3 segundos. Finalmente invoca a la llamada al sistema kill para enviar la señal SIGUSR2 a su proceso padre, que es el intérprete de comandos (PID=2000). Esta señal tiene establecida como acción por defecto la terminación del proceso que la recibe. Por lo que el intérprete de comandos desde donde se invocó al programa m O 9 termina y se cierra su ventana. Debe tenerse en cuenta que durante los 16 segundos que duerme el proceso padre A al proceso hijo B le da tiempo a mostrar por pantalla los siguientes cuatro mensajes antes de que se cierre la ventana del intérprete de comandos: Mensaje Mensaje Mensaje Mensaje 1 1 1 1 [2046]: [2046]: [2046]: [2046]: 0: o: o: o: 10 o 10 o Solución Ejercicio 2.5 1) Al escribir la orden s09R 1 & se comienza a ejecutar el programa s09R en segundo plano. Su­ póngase que a la ejecución de dicho programa se le asocia el proceso A, por el enunciado se sabe que su PID es 245. Al ejecutar el proceso A en primer lugar invoca a la llamada al sistema signal para especificar que cuando el proceso reciba la señal SIGUSR1 la acción que debe realizar el núcleo es ejecutar la función fl. En segundo lugar invoca otra vez a la llamada signal para especificar que cuando el proceso reciba la señal SIGUSR2 la acción que debe realizar el núcleo es ejecutar la la acción por defecto asociada a dicha señal, es decir, terminar el proceso. En tercer lugar invoca a la función atoi para pasar de carácter ASCII a número entero el primer argumento de entrada (b [1]) del programa. Luego en la variable d se almacena el valor l. En cuarto lugar comprueba si el número de argumento de entradas a es igual a 2 y si además d es igual a l. Ambas condiciones en este caso se cumplen, ya que conviene recordar que el nombre del programa cuenta como argumento de entrada. Por lo tanto se invoca a la llamada al sistema sigsetrnask para bloquear la recepción de las señales SIGUSRl. Señalar que sigrnask es una macro que permite configurar el valor de un determinado bit de la máscara de señales bloqueadas. 380 Ampliación de sistemas operativos Si la llamada se ejecuta con éxito en la variable e se almacena la máscara de señales bloqueadas que se tenía especificada antes de ejecutar esta llamada al sistema. A continuación se invoca a la llamada al sistema sigblock para añadir nuevas señales a la másca­ ra de señales bloqueadas. En este caso se bloquea la recepción de las señales SIGUSR2. Finalmente se invoca a la llamada al sistema pause y el proceso A entra en el estado dormido a la espera de recibir una señal que no ignore y que no tenga bloqueada. 2) Al escribir la orden kill -SIGUSR1 245 se envía una señal SIGUSR1 al proceso con PID 245, es decir, al proceso A. De acuerdo con el apartado anterior la recepción de señales SIGUSR1 está bloqueada por el proceso luego el núcleo no le despierta. 3) Igual que el apartado anterior pero aplicado a las señales SIGUSR2. Solución Ejercicio 2.6 La respuesta a este ejercicio depende de la salida que muestre por la pantalla el comando ps -j. Por ejemplo se va a suponer que la salida es: PID 3 9 68 63 82 63 3 9 PGID 3968 6382 63 3 9 SID 3968 3968 3968 TTY pts /2 pts /2 pts /2 TIME 0:01 0:00 12:46 CMD bash ps prog27 a) SID=3968. El proceso líder de la sesión es aquél cuyo PID coincide con el valor del SID, en este caso el proceso con PID=3968 asociado al intérprete de comandos bash. b) Se observa que existen tres grupos de procesos, cuyos PGID son: 3968. 6382 y 6339. En este caso cada grupo consta de un único proceso. A.3. Soluciones ejercicios capítulo 3 Solución Ejercicio 3.1 En un sistema BSD4.3 el rango de prioridades [0, 49] está reservado para la ejecución de un proceso en modo núcleo. Además el sistema BSD4.3 es de núcleo no expropiable, ello significa que si un proceso está ejecutándose en modo núcleo éste continuará usando el procesador hasta que termine su ejecución, se lo ceda voluntariamente a otro proceso o entre en el estado dormido a la espera de que se produzca algún evento. En consecuencia el cuanto de ejecución de un proceso ejecutándose en modo núcleo es infinito. Por otra parte, un algoritmo de planificación de turno rotatorio con un cuanto de ejecución infinito es equivalente a un algoritmo PIFO. Luego éste sería el algoritmo de planificación que se estaría aplicando de forma efectiva sobre las colas asociadas al rango de prioridades [0, 49]. Soluciones completas de los ejercicios 381 Solución Ejercicio 3.2 En el intervalo [0, 100) ms se ejecuta el proceso A. Durante este intervalo la rutina de tratamiento de la interrupción recalcula la prioridad del proceso A en t = 40 ms y en t = 80 ms. En t = 40 ms el campo p_cpu del proceso A toma el valor 4, luego sustituyendo valores en (3. 1) y operando se obtiene que el valor de la prioridad en modo usuario p_usrpri del proceso A en t = 40 ms es: p_usrpri = PUSER+ ¡u + 2·p_nice = 50+ � + 2·20 = 9 1 p_ De forma análoga en t = 8 0 ms el valor de la prioridad en modo usuario del proceso A es: p_usrpri = 50+ � + 2·20 = 92 En t = 100 ms el campo p_cpu del proceso A toma el valor 10 y finaliza el cuanto asignado al proceso A. Se invoca entonces a la rutina roundrobin ( ) para planificar al siguiente proceso de la cola, en este caso el proceso B. En el intervalo [ 100, 200) ms se ejecuta el proceso B. Durante este intervalo la rutina de tratamiento de la interrupción recalcula la prioridad del proceso A en t = 140 ms y en t = 180 ms. En t = 140 ms el campo p_cpu del proceso B toma el valor 4, luego el valor de la prioridad en modo usuario p_usrpri del proceso B en t = 140 ms es: p_usrpri = 50+ � + 2·20 = 91 De forma análoga !!n t = 180 ms el valor de la prioridad en modo usuario del proceso B es: p_usrpri = 50+ � + 2·20 = 92 En t = 200 ms el campo p_cpu del proceso A toma el valor 10 y finaliza el cuanto asignado al proceso B. Se invoca entonces a la rutina roundrobin ( ) para planificar al siguiente proceso de la cola, en este caso el proceso C. En el intervalo [200, 300) ms se ejecuta el proceso B. Durante este intervalo la rutina de tratamiento de la interrupción recalcula la prioridad del proceso A en t = 240 ms y en t = 280 ms. En t = 240 ms el campo p_cpu del proceso e toma el valor 4, luego el valor de la prioridad en modo usuario p_usrpri del proceso e en t = 140 ms es: p_usrpri = 50+ � + 2·20 = 91 De forma análoga en t = 280 ms el valor de la prioridad en modo usuario del proceso e es: p_usrpri = 50+ � + 2·20 = 92 382 Ampliación de sistemas operativos La ejecución de los procesos A, B y C se va alternando en la forma descrita hasta llegar a t = 1000 ms. El proceso A se ejecuta en los intervalos [300, 400), [600, 700) y [900, 1000) ms. Al terminar estos tres cuantos de ejecución el valor del campo p_cpu del proceso A es igual a 40. El proceso B se ejecuta en los intervalos [400, 500) y [700, 800) ms. Al terminar estos dos cuantos de ejecución el valor del campo p_cpu del proceso B es igual a 30. Por su parte el proceso C se ejecuta en los intervalos [500, 600) y [800, 900) ms. Al terminar estos dos cuantos de ejecución el valor del campo p_cpu del proceso B es igual a 30. En t = 1000 ms se invoca a la rutina schedcpu ( ) que en primer lugar multiplica el valor del campo p_cpu de todos los procesos por el factor de decadencia, que de acuerdo con el enunciado es igual a 0,5. Se tiene por tanto que p_cpu= 0,5·40 = 20 para el proceso A y p_cpu= 0,5·30 = 15 para los procesos B y C. En segundo lugar la rutina schedcpu ( ) recalcula la prioridad en modo usuario de cada proceso. Para el proceso A se obtiene O p_usrpri = 50+ + 2·20 = 95 � Mientras que para los procesos B y C se obtiene p_usrpri = 50+ � + 2·20 = 93 Por último la rutina schedcpu ( ) mueve a los procesos de la cola i = 22 asociada al rango de prioridades [88, 9 1] en la que habían sido colocados al ser creados y los coloca en la cola i = 23 que está asociada al rango de prioridades [92, 95]. Señalar que si no llega al sistema otro proceso más prioritario, el proceso B será el próximo proceso en ser planificado. Solución Ejercicio 3.3 Puesto que ts_cpupri = 1 la fila de la tabla de despacho que hay que consultar, cuando se produzca alguno de los eventos que generan un cambio de prioridad, es la fila segunda asociada al nivel de prioridad (PRIORITY LEVEL) 1. a) Si el proceso consume su cuanto asignado entonces el valor de ts_cpupri se actualiza con el valor de ts_tqexp de la segunda fila. Luego ts_cpupri =O. Como ts_upri = 14 entonces el nuevo valor de la prioridad en modo usuario de acuerdo con (3.3) y (3.4) será ts_umdpri = 14. Por lo tanto la prioridad en modo usuario del proceso se reduce de 15 a 14. b) Si el proceso ha estado esperando para poder consumir su cuanto más de 5 segundos entonces el valor de ts_cpupri se actualiza con el valor de ts_lwait de la segunda fila. Luego ts_cpupri = 1 1. Como ts_upri = 14 entonces el nuevo valor de la prioridad en modo usuario de acuerdo con (3.3) y (3.4) será ts_umdpri = 25. Luego la prioridad en modo usuario del proceso aumenta. e) Si el hilo es despertado, cuando regresa a modo usuario el valor de ts_cpupri se actualiza con el valor de ts_slpret de la segunda fila. Luego ts_cpupri = 1 1. Como ts_upri = 14 entonces 383 Soluciones completas de los ejercicios el nuevo valor de la prioridad en modo usuario de acuerdo con (3.3) y (3.4) será ts_umdpri En conclusión la prioridad en modo usuario del proceso aumenta. = 25. Solución Ejercicio 3.4 Al escribir la orden j1 o se comienza a ejecutar el programa j1 o. Supóngase que a la ejecución de dicho programa se le asocia el proceso A. En primer lugar se le asigna el valorO a los elementos sem_flg del array de estructuras z de tipo sembuf. Conviene recordar que las estructuras de tipo sembuf se utilizan para especificar las operaciones que se desean realizar sobre un conjunto de semáforos. Los elementos sem_flg permiten pasar indicadores a la llamada al sistema semop. En segundo lugar se invoca a la llamada al sistema ftok para crear una llave numérica de 32 bits que permite controlar el acceso a una instancia de un mecanismo IPC. Supuesto que se ejecuta con éxito en y, que es una variable del tipo predefinido key_t, se almacenará la llave creada. En caso de error en y se almacenará el valor (key_t) -l. En tercer lugar se comprueba, si se ha producido algún error en la ejecución de la llamada al sistema ftok. En dicho caso se ejecuta la función printf que muestra por pantalla el mensaje mensaje wert y se invoca a la llamada al sistema exit para terminar la ejecución del proceso A, devolviendo como estatus de salida para su proceso padre el valor 3. Si ftok se ha ejecutado con éxito entonces se invoca a la llamada al sistema semget para obtener o crear un conjunto de dos semáforos. Si la llamada se ejecuta con éxito entonces en x se almacenará el identificador entero del conjunto de 2 semáforos asociados a la llave y. Si no existe un conjunto de semáforos asociado a dicha llave se crea (IPC_CREAT) uno nuevo con permisos de lectura y escritura para el propietario (0600). Después se invoca a la llamada al sistema semctl para inicializar con el valorO todos los semáforos (SETALL) del conjunto de semáforos con identificador x. A continuación se invoca a la llamada al sistema fork para crear un proceso hijo (proceso B). Esta llamada devuelveO al proceso hijo y el PID del hijo al proceso padre. El proceso que se ejecute primero (padre o hijo) dependerá del planificador. Supongamos que se ejecuta primero el proceso hijo B, entonces tras asignar el valorO al elemento sem_num y el valor 1 al elemento sem_op de la estructura z [ O l invoca a la llamada semop para realizar una operación signal_sem sobre el semáforoO del conjunto x. Como el semáforoO de x había sido inicializado aO tras realizar esta operación toma el valor l. A continuación el proceso B tras asignar el valor 1 al elemento sem_num y el valor -1 al elemento sem_op de la estructura z [1] invoca a la llamada semop para realizar una operación wait_sem sobre el semáforo 1 del conjunto x. Como el semáforo 1 de x había sido inicializado aO, el proceso B se bloquea en espera de que algún otro proceso realice una operación signal_sem sobre este semáforo. Por su parte el proceso padre A, invoca de nuevo a la llamada al sistema fork para crear otro proceso hijo (proceso C). Esta llamada devuelve O al proceso hijo y el PID del hijo al proceso padre. El proceso que se ejecute primero (padre o hijo) dependerá del planificador. Supongamos que se ejecuta primero el proceso padre A, éste como no tiene más instrucciones para ejecutar finaliza. Se ejecuta entonces el 384 Ampliación de sistemas operativos hijo C que invoca a la llamada al sistema sleep y suspende su ejecución durante dos segundos. Cuando despierta tras asignar el valorO al elemento sem_num y el valor -1 al elemento sem_op de la estructura z [ O J invoca a la llamada semop para realizar una operación wait_sem sobre el semáforoO del conjunto x. Como el semáforoO de x había sido puesto a 1 por el proceso B, esta operación pone aO el semáforo O. A continuación el proceso C ejecuta la función printf que muestra por pantalla el mensaje mensaje fgh Posteriormente el proceso C tras asignar el valor 1 al elemento sem_num y el valor 1 al elemento sem_op de la estructura z [1] invoca a la llamada semop para realizar una operación sobre el semáforo 1 del conjunto x. Como resultado de esta operación pone el semáforo 1 aO y despierta al proceso B que estaba bloqueado en el semáforo. Qué proceso se ejecuta (B o C) depende del planificador. Supuesto que se ejecuta el C, éste simplemente finaliza. Entonces se planifica el proceso B que ejecuta la función printf, muestra por pantalla el mensaje mensaje asd y finaliza. Solución Ejercicio 3.5 Al escribir la orden sl O sistemas_operativos_2 se comienza a ejecutar el programa sl O. Su­ póngase que a la ejecución de dicho programa se le asocia el proceso A. En primer lugar se comprueba que el número de parámetros (b) introducidos por la línea de comandos al invocar a s1 O es igual a 2. La comprobación es positiva los dos parámetros introducidos son s1 O y sistemas_operativos_2. En segundo lugar se invoca a la llamada al sistema pipe para crear una tubería sin nombre y se comprueba si se ha producido algún error, es decir, devuelve el valor -l. Si pipe (pdf) se ha ejecutado con éxito entonces se crea una tubería sin nombre y en pdf se habrán almacenado dos descriptores de archivos. Para leer de la tubería hay que usar el descriptor almacenado en pdf [ O J , mientras que para escribir hay que usar el descriptor almacenado en pdf [1] . Si se produjese algún error durante la ejecución de pipe entonces se imprimiría en pantalla el mensaje El: [mensaje] donde [mensaje J hace referencia al mensaje asociado al identificador de error contenido en la variable errno. A continuación se invocaría a la llamada al sistema exit para terminar la ejecución del proceso A. Si pipe se ha ejecutado con éxito entonces se invoca a la llamada al sistema fork para crear un proceso hijo (proceso B). Esta llamada devuelve el valorO al proceso hijo y el PID de hijo al proceso padre. Dicho valor, en cualquier caso, se almacena en la variable a. Será el planificador el que decida que proceso se ejecuta a continuación el padre o el hijo. Supongamos que se ejecuta primero el proceso padre hasta finalizar y luego el proceso hijo. Primero cierra el archivo con descriptor pdf [ O J (asociado a las operaciones de lectura en la tubería) ya que Soluciones completas de los ejercicios 385 no lo va usar. En segundo lugar invoca a la llamada al sistema write para escribir en el archivo con descriptor pdf [1] (asociado a las operaciones de escritura en la tubería) los diez primeros bytes del segundo parámetro introducido por la línea de órdenes, es decir, escribe en la tubería sistemas_o. En tercer lugar cierra el archivo con descriptor pdf [1] , ya que no va escribir más en la tubería. En cuarto lugar escribe en pantalla el texto Mensaje Y finalmente invoca a la llamada al sistema exit para finalizar. A continuación se ejecuta el proceso hijo. En primer lugar cierra el archivo con descriptor pdf [1] ya que no va escribir en la tubería. En segundo lugar ejecuta un bucle while, en cada pasada del bucle se lee un byte de la tubería y se escribe en la pantalla. El bucle finaliza cuando ya no quedan más bytes por leer en la tubería. En la pantalla se habrá mostrado el siguiente texto sistemas o En tercer lugar escribe en la pantalla un salto de línea. En cuarto lugar, cierra el archivo con descriptor pdf [1] ya que no va a leer más en la tubería. En quinto lugar muestra en pantalla el texto Mensaje Y finalmente invoca a la llamada al sistema exit para finalizar. Señalar que si se hubiese planificado el proceso hijo antes que el padre éste se hubiera quedado bloqueado al intentar leer en una tubería vacía. Solo cuando el proceso padre escriba en la tubería el proceso hijo será desbloqueado. En dicho momento será el planificador el que decida si se continúa con la ejecución del padre o con la del hijo. De acuerdo con la explicación anterior y dependiendo del orden de planificación de los procesos en la pantalla se pueden llegar a visualizar dos posibles trazas de ejecución: • Traza 1: Mensaje sistema_o Mensaje • Traza 2: sistema_o Mensaje Mensaje 386 Ampliación de sistemas operativos Solución Ejercicio 3.6 Al escribir la orden j08a & se comienza a ejecutar el programa j08a en segundo plano. Supóngase que a la ejecución de dicho programa se le asocia el proceso A. Al ejecutar el proceso A en primer lugar se invoca a la función de librería malloc para reservar memoria para almacenar 9 caracteres. Si la función se ejecuta con éxito entonces en b se almacena un puntero a la zona de memoria reservada. En caso contrario devuelve NULL. En segundo lugar, se ejecuta nueve veces un bucle for. En cada ejecución del bucle se asigna el carácter nulo 1\ o a los elementosO a 8 de la cadena de caracteres apuntada por el puntero b. 1 En tercer lugar se invocaría a la llamada al sistema mknod para crear en el directorio actual una tubería con nombre o archivo PIFO de nombre asd con permisos de lectura y escritura para todos los usuarios. En cuarto lugar, se invocaría a la llamada al sistema open para abrir el archivo PIFO asd con permi­ sos de lectura y escritura para todos los usuarios. Si la llamada al sistema se ejecuta con éxito se almacena el descriptor del archivo en la variable enterar. En quinto lugar, se invocaría a la llamada al sistema read para leer 7 bytes del archivo PIFO asd y copiarlos en el array de caracteres b. Puesto que la tubería está vacía (ya que no se disponen de datos que hagan suponer lo contrario) el proceso A se bloqueará hasta que otro proceso escriba en la tubería. Al escribir la orden j08b se comienza a ejecutar el programa j08b en primer plano. Supóngase que a la ejecución de dicho programa se le asocia el proceso B. En primer lugar el proceso B invoca a la llamada al sistema open para abrir el archivo asd con permisos de lectura y escritura para todos los usuarios. Si la llamada al sistema se ejecuta con éxito se almacena el descriptor del archivo en la variable entera r. Se va a suponer en lo que resta que se ha podido abrir el archivo asd. En segundo lugar invoca a la llamada al sistema wri te para escribir los 8 bytes de la cadena de caracteres b en el archivo asd. Finalmente, el proceso B invoca a la llamada al sistema close para cerrar el archivo y el proceso B finaliza. Por otra parte, puesto que la tubería ya contiene datos el núcleo despierta al proceso A, cuando éste vuelva a ser planificado para ejecución se podrá completar la llamada al sistema read, es decir, leer 7 bytes del archivo PIFO asd y copiarlos en el array de caracteres b. A continuación, se ejecutará la función de librería printf que mostrará en la pantalla Texto 1 Después se invocará a la llamada al sistema e lose para cerrar el archivo asd, Finalmente se invocará a la llamada al sistema unlink para eliminar el archivo PIFO asd y el proceso A finalizará. Conviene señalar que dependiendo del orden de planificación de los procesos, podría suceder que al escribir el proceso B en la tubería se despertase al proceso A y esté fuese planificado para ser ejecutado hasta su finalización. Una vez finalizado el proceso A se continuaría con la ejecución del proceso B. Soluciones completas de los ejercicios A.4. 387 Soluciones ejercicios capítulo 4 Solución Ejercicio 4.1 La modificación pedida consiste simplemente en cambiar MAP_SHARED por MAP_ PRIVATE en la llamada al sistema mmap. Se puede comprobar que con este cambio el mapeado del archivo textol.txt pasa a ser de tipo privado y en consecuencia las modificaciones que realice el proceso sobre el archivo no pueden ser visualizadas por lo procesos que mapeen este archivo en su espacio de direcciones. Además dichas modificaciones no son trasladadas al archivo en el disco duro. En consecuencia cuando finaliza la ejecución del proceso asociado al programa prog42 el contenido del archivo sigue siendo el mismo. Solución Ejercicio 4.2 La solución de este ejercicio depende del SOBUNIX y de la arquitectura del computador que se utilicen. En este caso se ha utilizado OpenSolaris sobre una máquina virtual Oracle V M VirtualBox. a) Supóngase que al ejecutar el comando prog42 & se ha creado un proceso A con PID=6659. Supóngase además que cuando aparece en pantalla el mensaje PID=6659 duerme 20 s y se ejecuta el comando pmap 6659 aparece en la pantalla la salida que se muestra en la Figura A.4. Este comando muestra información sobre el espacio de direcciones virtuales del proceso A. La pri­ mera fila contiene el PID del proceso y el nombre de ruta relativa del programa que está ejecutando 6659: prog42 08046000 8K rwx-- 08050000 4K r-x-- /export/home/josema/prog43 08060000 8K rwx-- /export/home/josema/prog43 D1E30000 24K rwx-- D1E40000 1280K r-x-- D1F80000 28K rwx-- /usr/lib/libc/libc_hwcap1.so.1 D1F87000 8K rwx-- /usr/lib/libc/libc_hwcap1.so.1 D1F90000 4K rwx-- D1FAOOOO 4K rw--- [ anon ] D1FBOOOO 4K rw--- [ anon ] D1FBFOOO 180K r-x-- /lib/ld.so.1 D1FFCOOO 8K rwx-- /lib/ld.so.1 4K rwx-- /lib/ld.so.1 D1FFEOOO total [ stack l [ anon ] /usr/lib/libc/libc_hwcap1.so.1 [ anon l 1564K Figura A.4- Salida del comando pmap 6659 del apartado a) del Ejercicio 4.2 388 Ampliación de sistemas operativos el proceso. La última fila contiene el tamaño total en KiB del espacio de direcciones virtuales del proceso, en este caso 1564 KiB. Cada una de las restantes filas contiene la siguiente información sobre una determinada región del espacio de direcciones virtuales del proceso: dirección virtual de inicio de la región, tamaño de la región en KiB, permisos de acceso y nombre de ruta absoluto del archivo que se mapea en dicha región. Por otra parte, se observa que en el espacio de direcciones virtuales del proceso A existen las siguientes regiones: pila, código, datos, y librerías compartidas. Además existen definidas varias regiones de memoria anónima [ anon ] , estas regiones serán utilizadas, por ejemplo, para alber­ gar las copias de las páginas de las regiones que hayan sido configuradas con un mapeado de tipo de privado. b) Supóngase además que cuando aparece en pantalla el mensaje PID=6659 vuelve a dormir otros 20 s y se ejecuta el comando pmap 6659 aparece en la pantalla la salida que se muestra en la Figura A.5. Se observa que el tamaño del espacio del proceso A ha crecido en 8 KiB pasándose a ser ahora de 1572 KiB. Estos 8 KiB adicionales provienen de la región de montículo ( [ heap l) que se ha creado para albergar la reserva de memoria realizada con la función malloc. 6659: prog42 08046000 8K rwx-- 08050000 4K r-x-- /export/home/josema/prog43 08060000 8K rwx-- /export/home/josema/prog43 08062000 8K rwx-- D1E30000 24K rwx-- [ stack l [ heap l D1E40000 1280K r-x-- [ anon l /usr/lib/libc/libc_hwcap1.so.1 D1F80000 28K rwx-- /usr/lib/libc/libc_hwcap1.so.1 D1F87000 8K rwx-- /usr/lib/libc/libc_hwcap1.so.1 D1F90000 4K rwx-- D1FA0000 4K rw--- [ anon l [ anon l [ a non l !lib/ld. so.1 D1FB0000 4K rw--- D1FBFOOO 180K r-x-- D1FFC000 8K rwx-- !lib/ld.so.1 D1FFEOOO 4K rwx-- /lib/ld. so.1 total 1572K Figura A.S- Salida del comando pmap 6659 del apartado b) del Ejercicio 4.2 Solución Ejercicio 4.3 El comando vmstat 5 2 finaliza su ejecución transcurridos 5·2 = 10 s y durante este tiempo mues­ tra cada 5 segundos información sobre el estado de la memoria y sobre ciertos eventos del sistema (carga Soluciones completas de los ejercicios 389 de CPU, paginación, número de cambios de contexto, interrupciones de dispositivo, llamadas del sis­ tema, etc). Señalar que la información mostrada depende de la implementación que se realiza de este comando en cada SOBUNIX. Por ejemplo, supóngase que este comando se ejecuta en un sistema OpenSolaris y que la salida que se muestra en la pantalla es la siguiente: kthr page memory r b w swap fau1ts disk cpu free re o o o 391292 39676 2 7 o o 1 2792 6 1 -2 o o 241 283 220 12 4 85 1 o 9 80 o o o 1656 o 3 o o 254 352 241 98 2 o 334500 36728 mf pi po fr de sr sO s1 o in sy es us sy id o Señalar que existen dos líneas de datos numéricos, la primera línea se muestra cuando se lanza la orden y la segunda línea cuando transcurren 5 s. Si se ejecuta la orden man vmstat se puede conocer el significado de la información que se mues­ tra en cada columna de la salida. kthr contiene información sobre el número de hilos del núcleo en diferentes estados, memory contiene información sobre el uso de memoria, page contiene información sobre fallos de página y paginación, disk informa sobre el número de operaciones en disco por segundo, faul ts informa sobre la tasa de trampas e interrupciones y cpu informa sobre el porcentaje promedio de uso de los procesadores. A.5. Soluciones ejercicios capítulo 5 Solución Ejercicio 5.1 a) Esta llamada al sistema abre el archivo datos.txt con permisos de lectura y escritura (O_RDWR). Además sitúa el puntero de lectura/escritura al final del archivo (o_APPEND). b) Esta llamada al sistema abre el archivo prueba con permiso de solo lectura (o_RDONLY). Si el archivo no estaba ya creado lo crea (o_CREAT) con la máscara de modo octal 0600. Solución Ejercicio 5.2 La llamada al sistema o el comando unlink eliminan un enlace duro, es decir, una entrada de un directorio. En este caso como el archivo únicamente tenía un enlace duro la aplicación de esta llamada al sistema produce la eliminación del archivo. Solución Ejercicio 5.3 En la Figura A.6 se muestra el código C del programa ejstat que hace uso de la llamada al sistema stat para mostrar algunos de los atributos del archivo cuyo nombre de ruta debe pasarse como argu­ mento de entrada del programa. Por ejemplo si en un intérprete de comandos de un SOBUNIX se escribe la orden 390 Ampliación de sistemas operativos ejstat texto1.txt en la pantalla aparecerían algunos de los atributos del archivo con nombre de ruta relativa texto1.txt, supuesto que dicho archivo existe en el directorio de trabajo actual. #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> main(int argc, char *argv[ ] ) { struct stat buffer; int r; if(argc==2) { r = stat(argv[l ] , &buffer); if(r==-1) printf("\nError al ejecutar la llamada stat\n"); else printf("\n**Información sobre el archivo printf("Número nodo-i = %s**\n", argv[l ] ); %:1. \n",buffer.st_ino); printf("Número de enlaces duros = printf("UID del propietario = %:1.\n", buffer.st_nlink); %:1.\n", buffer.st_uid); printf("GID del grupo propietario = printf("Tamaño en bytes = %:1.\n", buffer.st_gid); %:1.\n", buffer.st_size); printf("Fecha y hora del último acceso = %s\n",ctime(&buffer.st_atime)); Figura A.6- Código C del programa ejstat Solución Ejercicio 5.4 Al escribir la orden j1o 25 wert.txt se comienza a ejecutar el programa j1o. Supóngase que a la ejecución de dicho programa se le asocia el proceso A. En primer lugar se comprueba que el número de parámetros (np) introducidos por la línea de comandos al invocar a j10 es igual a 3. La comprobación es positiva los tres parámetros introducidos son j10, 25 y wert.txt. En segundo lugar se invoca a la función atoi para convertir a número entero el segundo paráme­ tro (25) introducido por la línea de comandos. Dicho número se almacena en la posición de memoria asociada a la variable entera x. A continuación se invoca a la función malloc para reservar N bytes de memoria de forma dinámica para x& (x-1) elementos de tipo carácter. x& (x-1) es el resultado de realizar la operación AND a nivel Soluciones completas de los ejercicios 39 1 de bit entre x y x-1. Puesto que x= 25 = 1100 12 y x-1= 24 = 110002 , el resultado de la operación AND es 1 1000 = 24. Posteriormente se ejecuta un bucle for mientras la variable h sea menor o igual (x& (x-1) ) -2, es decir, menor o igual a 22. En cada ejecución del bucle se asigna el carácter nulo '\o' al elemento h de la cadena de caracteres apuntada por el puntero L. Luego se invoca a la llamada al sistema open para abrir un archivo ya existente para leer y escribir. El nombre del fichero es el tercer parámetro (pe [2 l ) de la orden introducida en la línea de comandos, es decir, wert.txt. Supuesto que la llamada se ejecuta con éxito en la variable a se almacena el descriptor del fichero. A continuación se llama a la función try, que primero invoca a la llamada al sistema read para leer los 15 primeros bytes (byte O a byte 14) del fichero wert.txt y almacenarlos en un espacio de memoria cuya primera dirección está apuntada por el puntero L. A continuación muestra por pantalla los 15 caracteres leídos. De acuerdo con el contenido del fichero wert.txt mostrado en la Figura 5. 12 y puesto que cada carácter ocupa 1 byte en la pantalla se muestra el siguiente texto: "El sistema oper" Cuando la función try finaliza se invoca la llamada al sistema lseek para desplazar 25 bytes a partir de un determinado origen el puntero de lectura/escritura asociado al fichero con descriptor a. El origen para realizar el desplazamiento queda establecido por el valor del resto de la división entera de 25 entre 2. Como el resto de dicha división es 1 lseek avanza 25 bytes desde la posición actual del puntero de lectura/escritura. Como dicho puntero después de la última lectura se quedó apuntando al byte 15 entonces si lseek se ejecuta con éxito queda apuntando al byte 40. A continuación se vuelve a llamar a la función try, que primero invoca a la llamada al sistema read para leer los 15 bytes del fichero wert.txt a partir de la posición actual del puntero de lectura/escritura (byte 40) y almacenarlos en un espacio de memoria cuya primera dirección está apuntada por el puntero L. A continuación muestra por pantalla los 15 caracteres leídos. De acuerdo con el contenido del fichero wert.txt y puesto que cada carácter ocupa 1 byte en la pantalla se muestra el siguiente texto: "multiusuario y " Finalmente, se invoca la llamada al sistema exit para finalizar la ejecución del proceso A devol­ viendo como estatus de salida para su proceso padre el valor 3. Solución Ejercicio 5.5 El comando df proporciona información sobre la utilización del espacio en disco en los diferentes sistemas de archivos montados en el sistema. Cuando se invoca sin opciones la primera columna de la salida que se muestra en pantalla muestra el nombre de la partición tal como aparece en el directorio 1 dev, la segunda columna la capacidad total de la partición expresada en número de bloques de 1 KiB, la tercera columna el número de bloques utilizados, la cuarta el número de bloques libres, la quinta columna el porcentaje de bloques utilizados con respecto al número total de bloques y la sexta columna el nombre de ruta del punto de montaje del sistema de archivos. 392 Ampliación de sistemas operativos Señalar que el valor que se muestra en la tercera columna, es decir, el número de bloques utilizados no incluye los bloques reservados por el núcleo para optimizar el funcionamiento de las rutinas que operan sobre el disco. Típicamente el número de bloques reservados suele ser el 10% del total, aunque este valor puede ser modificado. A continuación se muestra, a modo de ejemplo, una posible salida del comando df: Filesystem 1k-blocks Used Available Use% Mount /dev/hda2 2949060 2102856 696400 /dev/hda1 23302 2593 19506 75% 1 12% /boot Aprovechando este ejemplo se puede comprobar que el número de bloques utilizados que se muestra en la columna Used no incluye los bloques reservados por el núcleo. Por ejemplo para la partición 1 dev /hda2 se comprueba que 2102856+ 696400 = 2799256 que es menor que 2949060. El número de bloques reservados por el núcleo en esta partición es igual a 2949060 - 2799256 = 149804, es decir, un 5,079% del número total de bloques. Solución Ejercicio 5.6 El nodo-i de un archivo en un sistema UFS contiene, entre otros datos, la localización de los bloques de datos del archivo. En concreto contiene 15 direcciones de bloques de disco de 32 bits cada. Las 12 primeras direcciones son las de los 12 primeros bloques de datos del archivo, también denominados bloques directos. Las tres direcciones restantes localizan a un bloque de indirección simple, un bloque de indirección doble y un bloque de indirección triple, respectivamente. En el caso de usar un tamaño de bloque de 8 KiB, en un bloque pueden almacenarse 8 KiB 32 bits/dirección 3 21 bytes . , 1 . 4 bytes d1recc10n = . 11 . 2 direcciOnes Con lo que el tamaño máximo de un archivo en teoría podría ser de A.6. Soluciones ejercicios capítulo 6 Solución Ejercicio 6.1 a) No es posible, el comando kill solo se puede usar para enviar señales a procesos no a hilos. El envío de una determinada señal con kill afecta a todos los hilos del proceso no a un hilo concreto. b) Para enviar señales a un hilo en Linux se puede usar la llamada al sistema tgkill o la función pthread_kill de la librería Pthreads. Soluciones completas de los ejercicios 393 Solución Ejercicio 6.2 a) Al contrario de lo que podría pensarse la invocación de la llamada al sistema clone con este bit únicamente activado hace que la llamada no se ejecute con éxito y devuelva el valor -l. b) Igual que el apartado anterior. e) Igual que el apartado anterior. d) La conclusión que se deduce es que si se desea usar la llamada al sistema clone para crear un nuevo proceso ligero es necesario invocarla con los tres bits CLONE_THREAD f CLONE_VM f CLONE_SIGHAND activados. Solución Ejercicio 6.3 Este comando en un sistema Linux muestra la siguiente información por cada proceso existente en el sistema: identificador del proceso ( PID) , terminal de control (TTY) , información sobre su estado (STAT ) , tiempo de uso del procesador (TIME) y nombre de ruta del programa (COMMAND ) cuya ejecución ha generado el proceso. Señalar que en el caso de los hilos del núcleo en (COMMAND ) se muestra entre corchetes el nombre de la rutina del núcleo que ejecuta. A modo de ejemplo ilustrativo a continuación se reproducen las tres primeras líneas que muestra este comando al ser ejecutado en un determinado sistema Linux: PID TTY STAT TIME COMMAND 1 ? Ss 0:00 /sbin/init 2 ? S 0:00 [kthreadd) 3 ? S 0:05 [ksoftirqd/O J Solución Ejercicio 6.4 El comando top muestra información que se actualiza cada pocos segundos sobre el consumo de recursos (memoria, CPU, etc) que realizan los procesos existentes en el sistema. La salida por pantalla del comando top puede dividirse en dos partes (ver Figura A.7): la cabecera y el listado de procesos. La cabecera consta de cinco líneas. En la primera línea se muestra, entre otras datos, el tiempo que lleva arrancado el sistema, el número de usuarios conectados y la carga promedio. En la segunda línea se muestra el número de tareas existentes en el sistema en los diferentes estados. En la tercera línea se muestra información sobre el consumo de CPU. En la cuarta línea se muestra información sobre el consumo de memoria. Finalmente en la quinta línea se muestra información sobre el consumo de espacio en el área de intercambio. El listado de procesos muestra varios datos sobre los procesos que se están ejecutando en el sistema, entre ellos: su PID, el nombre del usuario al que está asociado, su prioridad, su consumo de CPU, 394 Ampliación de sistemas operativos su consumo de memoria, su tiempo de ejecución y el nombre de ruta del programa cuyo código está ejecutando el proceso. Señalar que el listado muestra ordenados a los procesos según su consumo de CPU lo que puede resultar muy útil al administrador del sistema para detectar a aquellos procesos que realizan un consumo excesivo de CPU. top - 16:42:06 up 6 days, Tasks: 168 total, Cpu(s): 98.7%us, 7:11, 2 users, load average: 11.06, 10.97, 9.16 4 running, 163 sleeping, 1.3%sy, 0.0%ni, O.O%id, O stopped, O.O%wa, 1 zombie O.O%hi, Mero: 508000k total, 469740k used, 38260k free, Swap: 522236k total, 44048k used, 478188k free, O.O%si, 131824k cached PID USER PR NI VIRT RES SHR S !!CPU !IMEM 19330 josema 20 O 1700 248 196 R 17.6 0.0 4:10.24 ejemplo6_2 TIME+ COMMAND 19340 josema 20 O 1700 248 196 R 17.6 0.0 4:03.86 ejemplo6_2 19183 josema 20 O 10132 296 232 R 9.0 0.1 4:14.83 ejemplo6_1 19226 josema 20 O 10132 276 216 S 9.0 0.1 3:24.67 ejemplo6_1 19313 josema 20 O 10132 276 216 S 9.0 0.1 2:07.49 ejemplo6_1 19126 josema 20 O 10132 280 216 S 8.6 0.1 11:10.58 ejemplo6_1 19146 josema 20 O 10132 276 216 S 8.6 0.1 5:25.16 ejemplo6_1 19195 josema 20 O 10132 276 216 S 8.6 0.1 3:47.49 ejemplo6_1 19251 josema 20 O 10132 280 216 S 8.6 0.1 2:43.87 ejemplo6_1 20 O 76488 46m 7236 S 1.7 9.4 6:05.50 Xorg 1061 root O.O%st 19184k buffers 1502 josema 20 O 95040 14m 10m S 1.0 3.0 1:37.97 gnome-terminal 1407 josema 20 O 73308 26m 6784 S 0.3 5.4 11:59.43 ubuntuone-syncd 19363 root 20 O O O S 0.3 0.0 0:00.32 kworker/0:1 19428 josema 20 O 2632 1168 864 R 0.3 0.2 0:00.03 top 20 O 3040 1648 1192 S 0.0 0.3 0:00.69 init 1 root O Figura A.7- Posible salida del comando top en un sistema Linux Solución Ejercicio 6.5 a) La estructura de un sistema de archivos EXT2 establece que el mapa de bits de bloques de da­ tos existente en un grupo de bloques ocupa un único bloque. El tamaño de un bloque de 4 KiB expresado en bits es igual a 32768 bits. Cada bit describe el estado de un bloque del grupo de bloques, luego en un grupo de bloques pueden existir como máximo 32768 bloques de datos, o equivalentemente 32 Kibloques. Multiplicando por el tamaño de un bloque se obtiene finalmente que el tamaño máximo de un grupo de bloques es igual a 4 (KiB/bloque)·32 Kibloques = 128 MiB b) El número de grupos de bloques en que se divide una partición se puede obtener dividiendo el Soluciones completas de los ejercicios 395 tamaño de la partición por el tamaño de un grupo de bloques determinado en el apartado anterior: 32 GiB _ 128 (MiB/grupo) - 256 grupos e) El número de nodos-i que se pueden almacenar en un bloque se determina dividiendo el tamaño de un bloque por el tamaño de un nodo-i, el cual de acuerdo a la sección 6.6.2 es igual a 128 bytes: 4 (KiB/bloque) 128 (B/nodo) _ - 32 (nodos/bloque) d) El nodo-i de un archivo en un sistema EXT2 contiene, entre otros datos, la localización de los bloques de datos del archivo. En concreto contiene 15 direcciones de bloques de disco de 32 bits cada. Las 12 primeras direcciones son las de los 12 primeros bloques de datos del archivo, también denominados bloques directos. Las tres direcciones restantes localizan a un bloque de indirección simple, un bloque de indirección doble y un bloque de indirección triple, respectivamente. En el caso de usar un tamaño de bloque de 4 KiB, en un bloque pueden almacenarse 4 KiB 32 bits/dirección 212 bytes --=---- = --- 4 bytes/dirección 210 direcciones Con lo que el tamaño máximo de un archivo en teoría podría ser de A.7. Soluciones ejercicios capítulo 7 Solución Ejercicio 7.1 a) Esta orden almacena en el archivo listado. txt la salida del comando dir, el cual muestra información sobre el contenido (archivos y subdirectorios) del directorio de trabajo actual. Señalar que si el archivo no estaba creado entonces con esta orden se crea. Por otra parte si el archivo ya estaba creado la información se almacena desde el comienzo del archivo por lo que su contenido previo se borra. b) Esta orden utiliza una tubería' 1' para pasar la salida del comando dir como entrada del comando find 1 I listado El comando dir muestra información sobre el contenido (archivos y sub­ directorios) del directorio de trabajo actual. Por su parte el comando find 1 I 1 istado busca en la entrada pasada aquellas líneas que contengan la cadena "listado" sin discriminar entre mayúsculas y minúsculas (opción /I). Si encuentra alguna línea que contenga la palabra buscada la muestra en la pantalla. 11 11• 11 11 e) Esta orden borra en el directorio de trabajo actual todos los archivos con la extensión . txt. 396 Ampliación de sistemas operativos Solución Ejercicio 7.2 Se debe escribir la orden attrib +R +H tabla.txt Solución Ejercicio 7.3 El byteO de la cabecera del bloque tiene el valor 7 7 lo que significa que no es el último bloque de la cadena de bloques de memoria. Los bytes 1 y 2 contienen el valor O O o O lo que significa que es un bloque ocupado y que su puntero al bloque PSP esO. Finalmente los bytes 3 y 4 contienen el valor 0003 lo que significa que el bloque contiene tres parágrafos. Solución Ejercicio 7.4 a) En un sistema FAT- 12 se utilizan 12 bits para direccionar un cluster, luego el número total de clusters que puede tener un volumen es 212 . Multiplicando el número de clusters por el tamaño de un cluster y por el tamaño de un sector se obtiene el tamaño del volumen: 25 212 (clusters)·24 (sectores/cluster)-29 (bytes/sector) = 2 bytes = 32 MiB b) En un sistema FAT- 12 la tabla de asignación de archivos tiene una entrada por cada cluster exis­ tente en el volumen, luego de acuerdo con el apartado anterior tendrá 212 entradas. Por otra parte cada entrada debe ser capaz de almacenar la dirección de un cluster luego debe tener un tamaño mínimo de 12 bits. El tamaño de la tabla de asignación de archivos de un sistema FAT- 12 se obtiene multiplicando el número de entradas de la tabla por el tamaño de una entrada: 2 12 (entradas)· 12 (bits/entrada) = 48 Kib = 6 KiB Solución Ejercicio 7.5 a) En un sistema de archivos FAT- 12 se utilizan 12 bits para especificar la dirección de un cluster. Luego el espacio posible de direcciones consta de 212 = 4096 direcciones. Sin embargo, no todas las direcciones se encuentran disponibles para especificar los siguientes clusters donde se alojan los datos de un archivo. De acuerdo con la sección 7.6. 1 siete direcciones (FFO- FF6) están reservadas para clusters utilizados por estructuras del sistema de archivos, una dirección (FF7) está reservada para especificar que un cluster está defectuoso, una dirección (000) está reservada para especificar que un cluster está libre y 8 direcciones (FF8-FFF) están reservadas para especificar el final de un archivo o directorio. Luego hay un total de 17 direcciones reservadas, lo que dejaría un total de 4096 - 17 = 4079 direcciones disponibles para especificar los siguientes clusters donde se alojan los datos de un archivo. Soluciones completas de los ejercicios 397 Por lo tanto, un archivo como máximo puede ocupar 4080 clusters, el cluster del primer bloque, que se direcciona en la entrada del directorio asociada al archivo, y 4079 en la FAT. Como cada bloque ocupa 2 KiB, el tamaño máximo que puede tener un archivo es: 4080·2 KiB = 8 160 KiB = 7,97 MiB b) En un sistema FAT- 12 una entrada de un directorio ocupa 32 bytes. El número de entradas de directorio que caben en un cluster de 2 KiB se calcula de la siguiente forma: 2 (KiB/cluster) 211 (bytes/cluster) = s = 64 (entradas/cluster) 32 (bytes/entrada) 2 (bytes/entrada) e) El número de segundos que tiene un día se calcula de la siguiente forma: 24 (horas/dia)·60 (minutos/hora)·60 (segundos/minuto) = 86400 segundos En la sección 7.6.2 se indica que el campo hora de una entrada de un directorio tiene un tamaño de 2 bytes = 16 bits, con los cuales se pueden codificar 216 = 65536 segundos. La precisión se puede determinar de la siguiente forma: ceil A.S. ( �����) = ceil(l,32) = 2 segundos Soluciones ejercicios capítulo 8 Solución Ejercicio 8.1 a) La prioridad base de un proceso Pbp es función de la clase de planificación a la que pertenece el proceso. Por defecto la prioridad base de un proceso que pertenece a la clase de planificación alta es Pbp = 13. b) La prioridad base de un hilo Pbh es función de la clase de planificación a la que pertenece el proceso al que está asociado el hilo y de la prioridad relativa del hilo Pr respecto a Pbp· Cuando la prioridad relativa Pr se configura al valor debajo de normal (Pr = - 1) entonces la prioridad base del hilo se calcula mediante la siguiente expresión: Pbh = Pbp+ Pr Sustituyendo valores en la expresión anterior y operando se obtiene finalmente que Pbh = 13 - 1 = 12 e) Por defecto la prioridad actual del hilo Pa es igual a la prioridad base del hilo Pbh· Luego de acuerdo con el apartado anterior Pa=12. 398 Ampliación de sistemas operativos Solución Ejercicio 8.2 a) Esta orden crea en el directorio de trabajo actual un enlace duro de nombre enlacel. txt al archivo \trabajo\listado.txt. b) Esta orden crea en el directorio de trabajo actual un enlace simbólico de nombre fotos al direc­ torio e: \imagenes\fotos\camara. Solución Ejercicio 8.3 La siguiente secuencia de órdenes en un intérprete cmd.exe de Windows permite ilustrar que un archivo en NTFS puede constar de múltiples flujos de datos: echo Este es el primer flujo de datos del archivo > prueba.txt echo Este es el segundo flujo de datos > prueba.txt:flujo2.txt notepad prueba.txt notepad prueba.txt:flujo2.txt Solución Ejercicio 8.4 La información relativa a la localización de las series del atributo de datos del archivo que se al­ macena en la entrada asociada al archivo en la MFT del volumen es la que se muestra en la siguiente tabla: Cluster Virtual Bv Cluster Lógico BL Número de clusters N o 3 7 9 61 90 85 1 12 3 4 2 1 Solución Ejercicio 8.5 La información relativa a la localización de las series del atributo de datos del archivo que se al­ macena en la entrada asociada al archivo en la MFT del volumen es la que se muestra en la siguiente tabla: Cluster Virtual Bv Cluster Lógico BL Número de clusters N o 8 16 32 40 40 o 50 95 o 8 8 16 8 8 Soluciones completas de los ejercicios 399 Señalar que las entradas con BL = O indican que los clusters virtuales correspondientes no tienen asignado espacio físico puesto que han desaparecido al ser comprimidos. Windows necesita mantener esta información para poder realizar la descompresión del archivo. Solución Ejercicio 8.6 Entre las herramientas software más conocidas para analizar el funcionamiento y el rendimiento del sistema operativo Windows se encuentran las siguientes: • • • Herramientas que se distribuyen conjuntamente con Windows. Como el administrador de tareas, el monitor de recursos y el control de servicios. Herramientas de depuración para Windows (debugging tools for Windows) de Microsoft. Como el depurador del núcleo o la lista de tareas. Herramientas de Windows Sysinternals. Como el explorador de procesos y el monitor de procesos. Bibliografía [Bach, 1986] J. M. Bach, The Design of the UNIX Operating System, Prentice-Hall, 1986. [Bonwick, 1994] J. Bonwick. The Slab Allocator: An Object-Caching Kernel Memory Allocator, Proceedings of the Summer 1994 USEN/X Technicaleonference, Jun. 1994, pp. 87-94. [Bovet y Cesati, O'Reilly, 2005. [Cooper, 2005] D. P. Bovet y M. Cesati, Understanding the Linux Kernel (3rd Edition), 2002] J. Cooper, Special Edition Using MS-DOS 6. 22 (3rd Edition), Que, 2002. [Cormen et al., 2009] T. H. Cormen, C. E. Leiserson, R. L. Rivest y C. Stein, Introduction to Algorithms (9th Edition), MIT Press, 2009. [Díaz et al., 201 1] J. M. Díaz, D. Chaos, R. Muñoz y J. Aranda, Fundamentos Básicos de los Sistemas Operativos, Sanz y Torres, 20 11. [Dijkstra, 1965] E. Dijkstra, "Co-operating Sequential Processes", Programming Languages, Aca­ demic Press, 1965. [Duncan, 1988. 1988] R. Duncan, MS-DOS Encyclopedia: Versions 1. 0 Through 3. 2, Microsoft Press, [Duncan, 1988b] R. Duncan, Advanced MS-DOS Programming: The Microsoft Guide for As­ sembly Language ande Programmers, Microsoft Press, 1988. [Gottfried, [Hart, 2005] B. S. Gottfried, Programación ene (2° Edición), McGraw-Hill, 2005. 2010] J. M. Hart, Windows System Programming (4th Edition) (Addison-Wesley Microsoft Technology Series), Addison-Wesley, 2010. [Kemighan y Ritchie, Prentice Hall, 199 1. 1991] B. W. Kernighan y D. Ritchie, El Lenguaje de Programación e, [Love, 2010] R. Love. Linux Kernel Development (3rd Edition), Addison-Wesley Educational Pu­ blishers Inc, 20 10. 401 402 Ampliación de sistemas operativos [Love et al., 2005] P. Love, J. Merlino, C. Zimmerman, J. C. Reed y P. Weinstein. Beginning Unix (Programmer to Programmer), Wiley, 2005. [Márquez, 2004] F. Márquez, UNIX. Programación Avanzada (3° Edición), Ra-Ma, 2004. [Mathur et al., 2007] A. Mathur, M. Cao, S. Bhattacharya, A. Dilger, A. Tomas y L. Vivier, " The New ext4 Filesystem: Current Status And Future Plans", Proceedings of the Linux Symposium, vol.2, pp.2 1-33, junio 2007, Ottawa, Ontario (Canada). [Mauerer, 2008. 2008] W. Mauerer, Professional Linux Kernel Architecture, John Wiley & Sons Ltd, [McDougall y Mauro, Hall, 2006. [McHoes y Flynn, 2006] R. McDougall y J. Mauro, Solaris Internals, 2nd Edition, Prentice 2010] A. McHoes e l. M. Flynn, Understanding Operating Systems (6th Edi­ tion), Course Technology, 20 10. [Podanoffsky, 1994] M. Podanoffsky, Dissecting DOS: A Code-Level Look at the DOS Operating System, Addison-Wesley, 1994. [Richter y Nasarre, 2007] J. M. Richter y C. Nasarre, Windows via C/C++ (Pro - Developer), Microsoft Press, 2007. [Ritchie y Thompson, 1974] D. M. Ritchie y K. Thompson, "The UNIX Timesharing System", Communications of the Association for Computing Machinery, vol. 17, pp. 365-375, julio 1974. [Russinovich et al., 2009] M. Russinovich, D. A. Solomon y A. Ionescu, Windows@ Internals: Including Windows Server 2008 and Windows Vista, Fifth Edition (Pro Developer), Microsoft Press, 2009. [Silberschatz et. al, 2002] A. Silberschatz, P. B. Galvin y G. Gagne, Sistemas Operativos (6a Edición), Limusa Wiley, 2002. [Stallings, 2005] W. Stallings, Sistemas Operativos: Aspectos Internos y Principios de Diseño (5a Edición), Pearson Educación, 2005. [Tanenbaum, 2009] A. S. Tanenbaum, Sistemas Operativos Modernos (3° Edición), Pearson­ Prentice Hall, 2009. [Vahalia, 1995] U. Vahalia, UNIX Internals: The New Frontiers, Prentice Hall, 1995. , Indice alfabético A Á rea Administrador de memoria, 328 de objetos, 3 1 1 Afinidad del procesador, 324 de intercambio, 174 de programas transitorios, 28 1, 286 de usuario, 5 1 Arena, 286 Array de descriptores de página, 253 de referencias a estructuras anon, 157 Asignación índice, 348 Asignador a nivel de página, 172 de la memoria del núcleo, 172, 178 slab, 179, 258 Atributo no residente, 345 residente, 345 Atributos del archivo, 189, 289 AIX, 4, 5 Alarm, 68 Alarmas, 68 Algoritmo buddy, 254, 257 CFS, 248 del último ajuste, 286 del mejor ajuste, 286 del primer ajuste, 286 del reloj con dos manecillas, 169 LRU, 169, 18 1 PFRA, 259 Alias, 13 Almquist shell, 8 Apuntador, 14 Á rbol de directorios, 289 rojo-negro, 249 Archivo binario, 188 de registro, 343 mapeado en memoria, 149 ordinario, 188 regular, 188 de páginas, 339 de solo-añadir, 267 inmutables, 267 B Base de datos del distribuidor, 322 Bind, 224 BIOS, 276 Bit pegajoso, 28 Bloque de arranque, 264 de control del archivo, 287, 349 de control del flujo, 349 de control del proceso, 3 15 EPROCESS, 315 ETHREAD, 3 16 KPROCESS, 315 KTHREAD, 3 15 W32PROCESS, 315 403 404 Ampliación de sistemas operativos W32THREAD, 3 16 Bloques buddies, 258 Bloques directos, 2 10, 392, 395 Bonus, 247 Bourne shell, 8 Bourne-Again shell, 8 Brk, 145 BSD, 3 BSD 4.3, 96 Buffering, 294 Buffers índices, 348 Clone, 239 Clase, 193 CloseHandle, 339 Cluster, 290, 341 Código de finalización o retorno, 284 reentrante, 1 10 Colas de prioridad, 95 de mensajes, 1 19, 124 de tareas activas, 243 hash, 2 12 e Com, 283 e shell, 8 Comando Caché externo, 14 de búsqueda de nombres en directorios, 262 interno, 14 de buffers de bloques de disco, 2 19, 294 Command.com, 279 de nombres en directorios, 208 Comodines, 11 de páginas, 220 Conectores, 135, 222 del sistema, 305 Conjunto de trabajo de un proceso, 335 Callouts, 96 Connect, 224 Cambios de contexto, 94 Contexto hardware salvado, 5 1 Canal Copiar a l escribir, 62, 148, 156, 160 de dormir, 1 16 Copy_process(), 240 de espera, 1 16 CP/M, 274 Capa Creat, 192 anónima, 157 Create, 198 H AT, 164 CreateDirectory, 340 nodo virtual/sistema de archivo virtual, 24, 202 CreateFile, 339 swapfs, 174 Credenciales, 50 V FS, 262 Cuotas, 68, 10 1 Cerrojo, 1 1 1 D Cerrojo exclusivo, 1 13 Datagrama, 223 lector 1 escritor, 1 13 Dato de reanálisis, 343 mutex, 1 13 DeleteFile, 340 Chdir, 196 Demanda de página, 328 Chown, 28 Demonio de páginas, 170 Clases de planificación, 95, 247 Descriptor de archivo, 24, 190 Claves raíz, 308 Cliente X, 19 de direcciones virtuales, 33 1 Índice alfabético de grupo, 264 de nodo, 254 de página, 253 de zona, 254 del objeto, 3 1 1 del proceso, 234 Despachador de llamadas al sistema, 308 Despertar, 1 16 Df, 201 Diario, 267 Diario de cambios, 343 Directorio de páginas, 332 de páginas global, 256 Dispadmin, 103 Dispositivo cero, 2 16 nulo, 2 16 Distribuciones de Linux, 233 Distribuidor o despachador del núcleo, 323 DLL, 304 Do_fork(), 240 Dormir, 116 Driver del dispositivo, 2 16, 220 del segmento, 153 del sistema de archivos, 340 instalable, 279, 294 residente, 276, 294 Dup, 19 1 Dup2, 19 1 E EGID, 32 Enlaces duros, 197, 343 simbólicos, 198 simbólicos rápidos, 267 Entorno, 60 Entorno de escritorio, 2 1 del proceso, 16 Entradas de tamaño variable, 265 Env, 16 Ermo, 23 Escáner de paginas, 170 Espacio de nombres, 3 1 1 Estado de un proceso, 50 no señalado, 325 señalado, 325 Estatus de salida, 63, 365 EUID, 32 Eventos, 325, 326 Excepciones, 68, 294 Exe, 283 Exec, 51, 54, 60, 159 Exit, 63, 159 Exit(), 64 Export, 16 EXT, 263 EXT2, 263 EXT3, 267 EXT4, 268 Extend, 268 Extensión de dirección física, 332 F Factor de decadencia, 96 Fallo de página, 144, 163, 165, 334 de página duro, 335 de página suaves, 335 de protección, 162 Fcntl, 194 FFS, 202 PIFOs, 135 Fork, 56, 159, 160, 239 Fragmentos de archivos, 268 FreeBSD, 4 Fsck, 20 1 Ftok, 1 19, 126 405 406 Ampliación de sistemas operativos Función de envoltura o redireccionamiento, 22 Funciones del sistema, 277 G Gestor de archivos, 22 de control de servicios, 303 de los conjuntos de trabajo, 335 de ventanas, 22 GetFileAttributes, 340 Getpgid, 79 Getpgrp, 79 Getpid, 58 Getppid, 58 Getsid, 80 GID, 25 GNU, 4 Grupo de hilos, 234 de procesos, 79 propietario, 28 H Hilo de puesta de página a cero, 338 Hilos, 46 Hilos de control, 46 de usuario, 47 del núcleo, 47, 240 HP-UX, 4, 5 1 Identificador de grupo, 25 de grupo de hilos, 236 de grupo de tareas, 235 de grupo efectivo, 32 de la clase de planificación, 100 de sesión, 80 de tarea, 235 de usuario, 25 de usuario efectivo, 32 del grupo de procesos, 79 del hilo, 235 del proceso, 50 numérico del proceso ligero, 82 Identificadores efectivos, 32 reales, 26 Init, 64 Instrucción comprobar y configurar, 1 1 1 Intérprete de comandos, 7 , 279 Interfaz de línea de comandos, 279 IFS, 289 Inversión de prioridad, 99 Ipcrm, 128, 133 Ipcs, 128, 133 Issig(), 73 J Joumal, 267 K Kill, 68, 71 Kom shell, 8 L Ladrón de páginas, 170 Latencia de despacho, 103 Libe, 22 Librería e estándar, 22 de enlace dinámico, 304 de hilos, 47, 76 Líder de la sesión, 80 del grupo, 79 Link, 198 Linux, 4, 5, 232 Lista de atributos, 345 de marcos de página activos, 259 de marcos de página inactivos, 259 de marcos de página libres, 167 Índice alfabético de marcos en standby, 337 de marcos inicializados aO, 337 de marcos libres, 337 de marcos modificados, 337 de nodos-im libres, 212 de procesos, 236 de tareas, 236 Listas LRU, 259 Listen, 224 Llamadas a procedimientos locales avanzados, 305 a procedimientos remotos, 328 a procedimientos remotos avanzados, 328 al sistema, 22 Llave, 308 Llave IPC, 119 Lockf, 194 Ls, 189 Lseek, 24, 148, 193 LSI, 274 M Mac OS X, 4, 5 Mailslots, 328 Malloc, 145 Manejador de archivo, 288 de fallos de página, 165 de la interrupción, 216, 222 de la señal, 69 de llamadas al sistema, 22 Mapa de intercambio, 174 Marcos bloqueados, 167 Máscara de modo, 27 simbólica, 29 Mecanismos IPC, 119 Memoria anónima, 156 compartida, 119, 129 local, 252 MINIX, 4, 5 Mkdir, 197, 198 Mkfifo, 135 Mkfs, 200 Mknod, 135 Mmap, 149 MMU, 161 Mnttab, 200 Modelo de drivers de Windows, 352 Modificadores, 279 Monitor de procesos, 3 10 de referencia de seguridad, 3 13 Monotarea, 283 Montículo, 146 Mount, 200 MS-DOS, 274 Msgctl, 126 Msgget, 125 Msgrcv, 125 Msgsnd, 125 Mtab, 200 MULTICS, 2 Munmap, 150 Mutex, 113 Mutexes, 325 N NetBSD, 4 Netware, 4 Nice, 95, 97, 242, 246 Nivel de petición de interrupción, 354 de prioridad de la interrupción, 221 pasivo, 354 Nodo índice, 204 sombra, 219 virtual, 24, 191, 203 Nodo-im, 212 Nodos de memoria, 252 407 408 Ampliación de sistemas operativos NTFS, 341 Número de archivo, 345 Número de dispositivo, 2 18 de dispositivo mayor, 2 17 de dispositivo menor, 217 de secuencia, 345 o Objeto anónimo, 153 de archivo abierto, 24, 190 de archivo mapeado, 332 de datos, 152 del núcleo, 3 1O dispositivo, 353 driver, 35 1, 353 sección, 328 Objetos del distribuidor, 325 Open, 24, 34, 148, 190, 193 OpenBSD, 4 OpenServer, 4 OpenSolaris, 4 p Página anónima, 155 Paginación por demanda, 144 Paquete de petición de E/S, 35 1 Parámetros de planificación, 243 Párrafo, 286 Particionamiento dinámico, 286 Passwd, 32 Pause, 69 PEB, 3 14 PGID, 79 PID, 50 Pila de usuario, 5 1, 146 del núcleo, 51, 53, 234, 238 Pipe, 134 Planificador central, 247, 248 de memoria, 17 1 0(1), 242 Pmap, 8 1, 147 POSIX, 5 Primer plano, 14 Priocntl, 95, 105 Prioridad actual del hilo, 321 base del hilo, 320 base del proceso, 320 de usuario, 105 dinámica, 245, 247 en modo usuario, 105 estática, 245 relativa del hilo, 320 Procesadores virtuales, 47 Proceso, 46 Proceso hijo, 56 inicial, 64 intercambiador, 171 monohilo, 47 multihilo, 47 padre, 56 TSR, 284 del sistema, 240 ligero, 47 Procfs, 50 Programa de acceso, 33 de login, 33 transitorio, 28 1 Protocolo X, 20 Prstat, 83 Ps, 8 1-83 Pseudodispositivos, 216 Pseudoterminales, 79 Psig(), 73 Índice alfabético Pthreads, 47, 76 Ptrace, 234 Punto de montaje, 200 de reanálisis, 343 R Raíz índice, 348 Read, 23, 148, 193 ReadFile, 340 Readv, 224 Recv, 224 Recvfrom, 224 Recvmsg, 224 Redireccionamiento de la E/S, 12 Reemplazamiento de páginas, 168 Referencia del archivo, 345 Región de texto, 145 Regiones de memoria compartida, 146 Registro de archivo, 345 de archivo base, 345 de estado del procesador, 222 por diario, 2 10, 267 RemoveDirectory, 340 Rename, 209 Reserva retrasada de espacio, 268 Resumen de preparados, 322 Rmdir, 197 Root, 25 Runqueue, 243 S S5FS, 202 S_ISGID, 28 S_ISUID, 28 S_ISV TX, 28 Símbolo del sistema, 275 Sbrk, 145 Sched_setparam, 242 Sched_setscheduler, 242 Sched_yield, 245 SCO Xenix, 3 Señales, 67, 1 19, 133 Secciones, 9 Segmento, 152 Segundo plano, 14 Semáforo binario, 1 13, 325 con contador, 114 general, 114 Semáforos, 119, 120, 325 Semctl, 12 1 Semget, 120 Semop, 120 Send, 224 Sendmsg, 224 Sendsig(), 73 Sendto, 224 Serie, 345 Servicios, 303 Servicios del sistema, 307 Servidor X, 19 Sesión, 80 Set, 15 SetCurrentDirectory, 340 SetFilePointer, 340 Setpgid, 80 Setpgrp, 80 Setpriority, 242, 246 Setsid, 80 Shell, 7 Shell script, 17 Shmat, 129 Shmctl, 130 Shmdt, 130 Shmget, 129 SID, 80 Sigaction, 69 Sigblock, 69 Sigmask, 69 409 410 Ampliación de sistemas operativos Signal, 69 T Sigpause, 69 Tabla de archivo del sistema, 288 de archivos, 288 de colas de mensajes, 125 de descriptores, 3 1 1 d e descriptores de archivos, 24, 190 de descriptores de grupo, 264 de despacho, 103 de despacho de llamadas al sistema, 308 de llamadas al sistema, 22 de manejadores de archivos, 288 de montaje, 200 de nodos-i, 262 de página, 16 1 de página prototipo, 336 de procesos, 49 de conmutación, 22 1 Tarea, 233 Tareas convencionales, 241 de tiempo compartido, 241 de tiempo real, 242 Task_struct, 234 TEB, 3 15 TENEX C shell, 8 Terminal serie, 79 Terminales emulados, 79 TGID, 236 Thread_info, 234 Tipo del mensaje, 124 del objeto, 3 10 Top, 393 Trabajo, 14 Trampa, 22, 308 Transacciones atómicas, 343 Tuberías, 13, 1 19, 134 Tuberías con nombre, 135 Sigsend, 68 Sigsetmask, 69 Sigsuspend, 69 Sigvec, 69 Sistema buddy, 179 de archivos de procesos, 8 1 de archivos extendido, 263 de archivos virtual, 202, 206 de paginación, 172 de ventanas, 19 X Window, 19 Socket, 222 Solaris, 4, 5, 99 Source, 17 Spinlocks, 1 1 1, 325 Spooling, 292 Stat, 193, 194 STREAMS, 132, 135 Strsignal, 73 SUA, 304 Subárboles, 308 Subintérprete, 18 Subllave, 308 Subsistema de administración de memoria, 144 de E/S, 2 16, 2 19, 35 1 de entorno, 304 SunOS, 4 Superbloque, 209 SuperFetch, 335 Superusuario, 25 SUS, 5 Swapfs, 174 Symlink, 198 Syscall(), 22 System V, 3 Índice alfabético sin nombre, 134 Turno rotatorio, 245 Turnstile, 117 Type, 14 u UID, 25 Umount, 200 Unalias, 13 Unidad de gestión de memoria, 144 UnixWare, 4 Unlink, 199 Unset, 16 V Valor amable, 97, 246 Variable de entorno, 16, 60 del intérprete de comandos, 15 V fork, 62, 239 V FS, 202 Violación de acceso, 33 1 V M, 152 w Wait, 65 Wait3, 65 Wait4, 65, 234, 235 Waitid, 65 Waitpid, 65, 234, 235 Widgets, 2 1 Widgets toolkits, 2 1 Windows, 300 Windows NT, 30 1 W rite, 23, 148, 193 W riteFile, 340 W ritev, 224 X X Toolkit Intrinsics, 2 1 Xenix, 3 Xlib, 20 z Z shell, 8 ZFOD, 156 Zona roja, 177 Zonas, 253 411