<bluehttp://www.moses.uklinux.net/patches/lki.

sgml> Esta gu´ ıa es ahora parte del Proyecto de Documentaci´ on de Linux y tambi´ en puede ser descargada en varios formatos desde: <bluehttp://www.linuxdoc.org/guides.html> o puede ser le´ ıda en linea (la u ´ltima versi´ on en Ingl´ es) en: <bluehttp://www.moses.uklinux.net/patches/lki.html> Esta documentaci´ on es software libre; puedes redistribuirla y/o modificarla bajo los t´ erminos de la GNU General Public License tal como ha sido publicada por la Free Software Foundation en la versi´ on 2 de la Licencia, o (a tu elecci´ on) por cualquier versi´ on posterior. El autor esta trabajando como ingeniero decano del n´ ucleo Linux en VERITAS Software Ltd y escribi´ o este libro con el prop´ osito de dar soporte al peque˜ no entrenamiento de cursos/charlas que di´ o sobre este tema, internamente en VERITAS. Gracias a: Juan J. Quintela (quintela@fi.udc.es), Francis Galiegue (fg@mandrakesoft.com), Hakjun Mun (juniorm@orgio.net), Matt Kraai (kraai@alumni.carnegiemellon.edu), Nicholas Dronen (ndronen@frii.com), Samuel S Chessman (chessman@tux.org), Nadeem Hasan (nhasan@nadmm.com) por varias correcciones y sugerencias. El cap´ ıtulo del Cach´ e de P´ aginas de Linux fue escrito por Christoph Hellwig (hch@caldera.de). El cap´ ıtulo sobre los mecanismos IPC fue escrito por Russell Weight (weightr@us.ibm.com) y Mingming Cao (mcao@us.ibm.com)

Dentro del n´ ucleo Linux 2.4
Tigran Aivazian tigran@veritas.com 23 Agosto 2001 (4 Elul 5761)

Contents
1 Arrancando 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 Construyendo la Imagen del N´ ucleo Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrancando: Vista General . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrancando: BIOS POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrancando: sector de arranque y configuraci´ on . . . . . . . . . . . . . . . . . . . . . . . . . . Usando LILO como cargador de arranque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Inicializaci´ on de Alto Nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arranque SMP en x86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Liberando datos y c´ odigo de inicializaci´ on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Procesando la linea de comandos del n´ ucleo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 4 4 4 7 8 10 10 11 13 13 16 19 21 23 25 25

2 Procesos y Manejo de Interrupciones 2.1 2.2 2.3 2.4 2.5 2.6 2.7 Estructura de Tareas y Tabla de Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Creaci´ on y terminaci´ on de tareas e hilos del n´ ucleo . . . . . . . . . . . . . . . . . . . . . . . . Planificador Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementaci´ on de la lista enlazada (de) Linux . . . . . . . . . . . . . . . . . . . . . . . . . . Colas de espera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Cron´ ometros del n´ ucleo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bottom Halves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1. Arrancando

2

3.4 3.5 3.6 3.7 3.8

Administraci´ on de estructuras de ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Administraci´ on de Puntos de Montaje y Superbloque . . . . . . . . . . . . . . . . . . . . . . . Ejemplo de un Sistema de Ficheros Virtual: pipefs . . . . . . . . . . . . . . . . . . . . . . . . Ejemplo de Sistema de Ficheros de Disco: BFS . . . . . . . . . . . . . . . . . . . . . . . . . . Dominios de Ejecuci´ on y Formatos Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42 45 49 51 53 54 56 57 57 59 60 65 65 68 70 72 72 74 75 77 77 78 79

4 Memoria Intermedia de P´ aginas Linux 5 Mecanismos IPC 5.1 Sem´ aforos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.1 5.1.2 5.1.3 5.2 Interfaces de la Llamada al sistema de los Sem´ aforos . . . . . . . . . . . . . . . . . . . Estructuras Espec´ ıficas de Soporte de Sem´ aforos . . . . . . . . . . . . . . . . . . . . . Funciones de Soporte de Sem´ aforos . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Colas de Mensajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1 5.2.2 5.2.3 Interfaces de las llamadas al sistema de Colas . . . . . . . . . . . . . . . . . . . . . . . Estructuras Espec´ ıficas de Mensajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funciones de Soporte de Mensajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5.3

Memoria Compartida 5.3.1 5.3.2 5.3.3

Interfaces de las llamadas al sistema de la Memoria Compartida . . . . . . . . . . . . Estructuras de Soporte de Memoria Compartida . . . . . . . . . . . . . . . . . . . . . Funciones de Soporte De Memoria Compartida . . . . . . . . . . . . . . . . . . . . . .

5.4

Primitivas IPC de Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4.1 5.4.2 Primitivas IPC de Linux Gen´ ericas usadas con Sem´ aforos, Mensajes y Memoria Compartida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Estructuras Gen´ ericas IPC usadas con Sem´ aforos, Mensajes, y Memoria Compartida .

6 Sobre la traducci´ on

1
1.1

Arrancando
Construyendo la Imagen del N´ ucleo Linux

Esta secci´ on explica los pasos dados durante la compilaci´ on del n´ ucleo Linux y la salida producida en cada etapa. El proceso de construcci´ on depende de la arquitectura, por lo tanto me gustar´ ıa enfatizar que s´ olo consideraremos la construcci´ on de un n´ ucleo Linux/x86. Cuando el usuario escribe ’make zImage’ o ’make bzImage’ la imagen inicializable del n´ ucleo resultante es almacenado como arch/i386/boot/zImage o arch/i386/boot/bzImage respectivamente. Aqu´ ı est´ a como es construida la imagen: 1. Los archivos fuente en C y ensamblador son compilados en formato de objetos reasignables ELF (.o) y algunos de ellos son agrupados l´ ogicamente en archivos (.a) usando ar(1).

1. Arrancando

3

2. Usando ld(1), los anteriores .o y .a son enlazados en vmlinux, el cual es un fichero ejecutable ELF 32-bits LSB 80386 est´ aticamente enlazado al que no se le han eliminado los s´ ımbolos de depuraci´ on. 3. System.map es producido por nm vmlinux, donde los s´ ımbolos irrelevantes o que no interesan son desechados. 4. Se entra en el directorio arch/i386/boot. 5. El c´ odigo de ensamblador del sector de arranque bootsect.S es preprocesado con o sin D BIG KERNEL , dependiendo de cuando el objetivo es bzImage o zImage, en bbootsect.s o bootsect.s respectivamente. 6. bbootsect.s es ensamblado y entonces convertido en la forma ’raw binary’ llamada bbootsect (o bootsect.s ensamblado y convertido a raw en bootsect para zImage). 7. El c´ odigo de configuraci´ on setup.S (setup.S incluye video.S) es preprocesado en bsetup.s para bzImage o setup.s para zImage. De la misma forma que el c´ odigo del sector de arranque, la diferencia radica en que -D BIG KERNEL est´ a presente para bzImage. El resultado es entonces convertido en la forma ’raw binary’ llamada bsetup. 8. Se entra en el directorio arch/i386/boot/compressed y se convierte /usr/src/linux/vmlinux a $tmppiggy (nombre temporal) en el formato binario raw, borrando las secciones ELF .note y .comment. 9. gzip -9 < $tmppiggy > $tmppiggy.gz 10. Se enlaza $tmppiggy.gz en ELF reasignable (ld -r) piggy.o. 11. Compila las rutinas de compresi´ on head.S y misc.c (todav´ ıa arch/i386/boot/compressed) en los objetos ELF head.o y misc.o. en el directorio

12. Se enlazan todas ellas: head.o, misc.o y piggy.o en bvmlinux (o vmlinux para zImage, ¡no confundas esto con /usr/src/linux/vmlinux!). Destacar la diferencia entre -Ttext 0x1000 usado para vmlinux y -Ttext 0x100000 para bvmlinux, esto es, el cargador de compresi´ on para bzImage es cargado m´ as arriba. 13. Se convierte bvmlinux a ’raw binary’ bvmlinux.out borrando las secciones ELF .note y .comment. 14. Se vuelve atr´ as al directorio arch/i386/boot y, usando el programa tools/build, se concatenan todos ellos: bbootsect, bsetup y compressed/bvmlinux.out en bzImage (borra la ’b’ extra anterior para zImage). Esto escribe variables importantes como setup sects y root dev al final del sector de arranque. El tama˜ no del sector de arranque es siempre de 512 bytes. El tama˜ no de la configuraci´ on debe ser mayor que 4 sectores, pero est´ a limitado superiormente sobre los 12k - la regla es: 0x4000 bytes >= 512 + sectores configuraci´ on * 512 + espacio para la pila mientras est´ a funcionando el sector de arranque/configuraci´ on Veremos m´ as tarde de d´ onde viene esta limitaci´ on. El l´ ımite superior en el tama˜ no de bzImage producido en este paso est´ a sobre los 2.5M para arrancarcon LILO y 0xFFFF p´ arrafos ((0xFFFF0 = 1048560 bytes) para arrancar im´ agenes directamentee, por ejemplo desde un diskette o CD-ROM (con el modo de emulaci´ on El-Torito). Destacar que mientras que tools/build valida el tama˜ no del sector de arranque, la imagen del n´ ucleo y el l´ ımite inferior del tama˜ no de la configuraci´ on, no chequea el l´ ımite *superior* de dicho tama˜ no de configuraci´ on. Entonces, es f´ acil construir un n´ ucleo defectuoso justamente sum´ andole alg´ un gran ”.espacio” al final de setup.S.

1. Inicializaci´ on de alto nivel en C. La fuente de alimentaci´ on inicia el generador de reloj y aserta la se˜ nal #POWERGOOD en el bus. 1. La linea CPU #RESET es asertada (CPU est´ a ahora en modo real 8086). 3. El n´ ucleo es descomprimido en modo protegido.lejos del camino */ la configuraci´ on empieza aqu´ ı */ el sistema es cargado en 0x10000 (65536) */ tama~ no del sistema: # de palabras de 16 bits */ . Debido al dise˜ no antiguo y a la compatibilidad hacia atr´ as. Todos los chequeos POST son realizados con las interrupciones deshabilitadas. sector 1 en la direcci´ on f´ ısica 0x7C00 (0x07C0:0000). La inicializaci´ on de bajo nivel es realizada por el c´ odigo ensamblador. %cs=0xFFFF0000. • Sector de arranque de LILO (u otros cargadores de arranque) o • sin sector de arranque (loadlin. 6. 2. el firmware del PC arranca el sistema operativo a la vieja usanza. La BIOS selecciona el dispositivo de arranque. 5. Esto carga la pista 0. La BIOS carga el sector de arranque del dispositivo de arranque. etc) Consideraremos aqu´ ı el sector de arranque de Linux en detalle. 2. 5. Este proceso puede ser separado en las siguientes seis etapas l´ ogicas: 1. con %dl conteniendo el dispositivo de arranque ’n´ umero de controladora’. 6. 1. La TVI (Tabla de Vectores de Interrupci´ on) es inicializada en la direcci´ on 0. por lo tanto centraremos nuestra atenci´ on en la arquitectura IBM PC/IA32. 3.S). La funci´ on de la BIOS de cargador de la rutina de arranque es llamada a trav´ es de la int0x19.%eip = 0x0000FFF0 (c´ odigo ROM BIOS POST). Arrancando 4 1. %ds=%es=%fs=%gs=%ss=0.3 Arrancando: BIOS POST 1.4 Arrancando: sector de arranque y configuraci´ on El sector de arranque usado para arrancar el n´ ucleo Linux puede ser uno de los siguientes: • Sector de arranque de Linux (arch/i386/boot/bootsect. Las primeras lineas inicializan las macros convenientes para ser usadas por los valores de segmento: 29 30 31 32 33 34 SETUPSECS BOOTSEG INITSEG SETUPSEG SYSSEG SYSSIZE = = = = = = 4 0x07C0 DEF_INITSEG DEF_SETUPSEG DEF_SYSSEG DEF_SYSSIZE /* /* /* /* /* /* tama~ no por defecto de los sectores de configuraci´ on */ direcci´ on original del sector de arranque */ movemos el arranque aqu´ ı . 4.2 Arrancando: Vista General Los detalles del proceso de arranque son espec´ ıficos de cada arquitectura. 4. las rutinas de descompresi´ on y la imagen del n´ ucleo comprimida. El sector de arranque carga la configuraci´ on.

esto es. DEF SETUPSEG. establece el n´ umero de palabras de 16 bits en %cx (256 palabras = 512 bytes = 1 sector) 4. %si %di. Esto y las tres instruciones siguientes (lineas 64-76) preparan la pila en $INITSEG:0x4000-0xC. %ds $INITSEG. Arrancando 5 (los n´ umeros a la izquierda son los n´ umeros de linea del archivo bootsect. en el segmento 0x9000. %es $256. va all´ ı y copia 512 bytes (rep movsw) El motivo por el que este c´ odigo no usa rep movsd es intencionado (hint . movw $0x4000-12. Aqu´ ı es de d´ onde viene el l´ ımite del tama˜ no de la configuraci´ on que mencionamos antes (ver Construyendo la Imagen del N´ ucleo Linux). Las lineas 77-103 parchean la tabla de par´ ametros del disco para el primer disco para permitir lecturas multi-sector: .cambiado 0xff00 a 0x4000 para usar el depurador despu´ es de 0x6400 (bde). %di # # # # # 0x4000 es un valor arbitrario >= longitud de sector de arranque + longitud de la configuraci´ on + espacio para la pila. %cx %si. Las lineas 54-63. Tambi´ en mi BIOS puede ser configurada para poner las tablas wini de controladoras en la memoria alta en vez de en la tabla de vectores. %ss %di. %ax %ax. %sp # pone la pila en INITSEG:0x4000-12. La l´ ınea 64 salta a la etiqueta go: en la nueva copia hecha del sector de arranque. La vieja pila quiz´ as tenga que ser insertada en la tabla de controladores.. 12 es el tama~ no parm del disco. ax y es ya contienen INITSEG go: movw movw movw %ax. mueven el c´ odigo del sector de arranque desde la direcci´ on 0x7C00 a 0x90000. Esto es realizado de la siguiente manera: 1. No tendr´ ıamos que preocuparnos por esto si chequeamos el l´ ımite superior de la memoria. $go bde . %ax %ax. %ss = $INITSEG (0x9000) y %sp = 0x3FF4 (0x4000-0xC). a menos que realmente sepas lo que est´ as haciendo. establece %es:%di a $INITSEG:0 (0x9000:0 = 0x90000) 3. %di $INITSEG.S: 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 # # # # # movw movw movw movw movw subw subw cld rep movsw ljmp $BOOTSEG.S) Los valores de DEF INITSEG. consideremos el c´ odigo actual de bootsect.h: /* No toques esto. */ #define DEF_INITSEG 0x9000 #define DEF_SYSSEG 0x1000 #define DEF_SETUPSEG 0x9020 #define DEF_SYSSIZE 0x7F00 Ahora. %ds %ax. establece %ds:%si a $BOOTSEG:0 (0x7C0:0 = 0x7C00) 2.1. limpia la bandera DF (direcci´ on) en EFLAGS a direcciones auto-incrementales (cld) 5. DEF SYSSEG y DEF SYSSIZE son tomados desde include/asm/boot. esto es.code16).

%cl $0x0200. %al $0x13 ok_load_setup %ax print_nl %sp. %bx %ds %fs:(%bx). %dx $0x02.INITSEG. Como que las lecturas simples de sectores son lentas y fuera de la cuesti´ on tenemos que tener cuidado con esto creando nuevas tablas de par´ ametros (para el primer disco) en la RAM. movw movw pushw ldsw movb pushw rep movsw popw popw movb movw movw %cx.esto quiz´ as signifique 7 sectores en algunos casos.88. %si $6. %ah %dl. %fs:2(%bx) # parchea el contador de sectores El controlador de diskettes es reinicializado usando el servicio de la BIOS int 0x13 funci´ on 0 (reinicializa FDC) y los sectores de configuraci´ on son cargados inmediatamente despu´ es del sector de arranque. Arrancando 6 77 78 79 80 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 # # # # # # # # # # # # # Las tablas por defecto de par´ ametros del disco de muchas BIOS no reconocer´ an lecturas multi-sector m´ as all´ a del n´ umero m´ aximo especificado en las tablas de par´ ametros del diskette por defecto . en INITSEG servicio 2. funci´ on 2 (leer sector(es)). Lo peque~ no si.el m´ aximo que encontraremos en un ED 2. y gs queda sin usar. 0x4(%di) %di. %fs:(%bx) %es. "leer sector(es)" (asume todos en la cabeza 0. %bx $0x02.continua # vuelca el c´ odigo de error . Estableceremos la cuenta m´ axima de sectores a 36 . %cl %di # establece fs a 0 # fs:bx es la direcci´ on de la tabla de par´ ametros # # # # ds:si es el c´ odigo copia 12 bytes di = 0x4000-12. %bp print_hex %ax load_setup # reinicializa FDC # # # # # # # controladora 0. pista 0) los lee ok . %fs $0x78. Esto sucede durante las lineas 107-124: 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 load_setup: xorb xorb int xorw movb movw movb movb int jnc pushw call movw call popw jmp ok_load_setup: %ah. en la direcci´ on f´ ısica 0x90200 ($INITSEG:0x200). %ah setup_sects. Lo grande no hace da~ no. otra vez usando el servicio de la BIOS int 0x13. cabeza 0 sector 2.1. fs = 0. esto es. Los segmentos son como sigue: ds = es = ss = cs . no necesita cld -> hecho en la linea 66 %di %ds $36. %dl $0x13 %dx. pista 0 direcci´ on = 512.

Esta rutina de ayuda es referida por bootsect kludge en bootsect. esto es. pero que obviamente no es suficiente). Esto asegura que el c´ odigo en bootsect. Examinemos este truco en el c´ odigo del sector de arranque que nos permite cargar un n´ ucleo grande. Arrancando 7 Si la carga falla por alguna raz´ on (floppy defectuoso o que alguien quit´ o el diskette durante la operaci´ on).misc. a menos que los reintentos tengan ´ exito. que descomprime el n´ ucleo en la direcci´ on 0x100000 y salta a ella. el c´ odigo en la configuraci´ on tiene que tener cuidado de varias combinaciones de tipo/versi´ on del cargador vs zImage/bzImage y esto es altamente complejo. Habilidad para escoger entre varios n´ ucleos Linux o incluso m´ ultiples Sistemas Operativos. Alguna gente (notablemente Peter Anvin) argumentan que el soporte para zImage deber´ ıa de ser quitado. Esto es hecho por setup. el cual es el motivo por el que existe c´ odigo en la configuraci´ on para cargar el resto de si mismo si se necesita. 3.5M vs 1M. Viejas versiones de LILO (v17 y anteriores) no pod´ ıan cargar n´ ucleos bzImage. Si la carga de los sectores setup sects del c´ odigo de configuraci´ on es realizada con ´ exito.S no se va fuera de memoria cuando est´ a copiando datos desde disco. Habilidad para pasar par´ ametros a la linea de comandos del n´ ucleo (existe un parche llamado BCP que a˜ nade esta habilidad al esqueleto desnudo de sector de arranque + configuraci´ on). por supuesto).S es simplemente porque no existe m´ as espacio libre en bootsect.hay aproximadamente 4 bytes dispersos y al menos 1 byte disperso en bootsect. saltamos a $SETUPSEG:0(arch/i386/boot/setup. Esta rutina usa el servicio de la BIOS int 0x15 (ax=0x8700) para moverlo a la memoria alta y restablecer %es al punto de siempre 0x10000. 2.S. Entonces procedemos a cargar la imagen comprimida del n´ ucleo en la direcci´ on f´ ısica 0x10000.S (lo cual no es estrictamente verdad . Una vez que los datos no se necesitan mas (ej. por lo que el sector de arranque puede usar la instrucci´ on lcall para saltar a ´ el (salto entre segmentos). Destacar que los viejos cargadores de arranque (viejas versiones de LILO) s´ olo pod´ ıan cargar los 4 primeros sectores de la configuraci´ on. La u ´nica forma de salir de ´ el es reiniciando la m´ aquina.c}. Esto inicializa la pila y llama a decompress kernel(). pero el n´ ucleo es cargado en fragmentos de 64k cada vez usando una rutina de ayuda especial que llama a la BIOS para mover datos desde la memoria baja a la memoria alta.S. tambi´ en conocido como ”bzImage”.hasta los 2.5 Usando LILO como cargador de arranque Existen varias ventajas en usar un cargador de arranque especializado (LILO) sobre un esqueleto desnudo de un sector de arranque: 1.S). el cual prepara las cosas para el modo protegido y salta a 0x1000. Esto es realizado para preservar las ´ areas de datos del firmware en la memoria baja (0-64K). Las versiones m´ as nuevas (como las de hace un par de a˜ nos y posteriores) usan la misma t´ ecnica que el sector de arranque + configuraci´ on de mover datos desde la memoria baja a la memoria alta mediante los servicios de la BIOS. volcamos el c´ odigo de error y se intenta en un bucle infinito. Tambi´ en. arch/386/boot/compressed/{head. El motivo por lo cual esto es realizado en setup. Despu´ es de que es cargado el n´ ucleo. El motivo . no se realizan m´ as llamadas a la BIOS) es sobreescrito moviendo la imagen entera (comprimida) del n´ ucleo desde 0x10000 a 0x1000 (direcciones f´ ısicas.S. que es el comienzo del n´ ucleo comprimido.S contiene el valor del segmento de configuraci´ on y el desplazamiento del c´ odigo bootsect helper en ´ el. Habilidad para cargar n´ ucleos bzImage m´ as grandes . pero usualmente no lo tienen (si algo est´ a mal s´ olo se pondr´ a peor). saltamos a la etiqueta ok load setup:.S y es definida como bootsect helper en setup. Los sectores de configuraci´ on son cargados usualmente en la direcci´ on 0x90200. La etiqueta bootsect kludge en setup. 1.S.1.

inicializa los perfiles de memoria intermedia.c:initialize secondary() el cual recarga esp/eip y no retorna. etc . que es el comienzo del n´ ucleo descomprimido. Realiza un cierre global del n´ ucleo (es necesario para que s´ olo una CPU realice la inicializaci´ on).S.. Inicializa las irqs. 9.. cpuid.6 Inicializaci´ on de Alto Nivel Por ”Inicializaci´ on de Alto Nivel” consideramos cualquier cosa que no est´ a directamente relacionada con la fase de arranque. 1.. Inicializa las tablas de p´ aginas. Inicializa la consola. si es posible. 3. Inicializa las traps.y si ready=1 todas las otras llaman arch/i386/kernel/smpboot. Inicializa los valores de segmento (%ds = %es = %fs = %gs = 2. Esto es tomado desde la variable linux banner definida en init/version. Analiza las opciones del arranque de la linea de comandos. esto es arch/i386/kernel/head. 2. 5.). Habilita el paginamiento estableciendo el bit PG en %cr0. . Realiza configuraciones espec´ ıficas de la arquitectura (an´ alisis de la capa de memoria. mientras que la carga de n´ ucleos zImage se realiza correctamente. 8. 3.1. Chequea el tipo de CPU usando EFLAGS y.c y es la misma cadena mostrada por cat /proc/version. a KERNEL DS = 0x18). Copia los primeros 2k de los par´ ametros de arranque (linea de comandos del n´ ucleo). 11. copia de la linea de comandos de arranque otra vez. el compilador usado para construirlo. a la memoria intermedia con forma de anillo del n´ ucleo para los mensajes. La funci´ on init/main. La primera CPU llama a start kernel(). La u ´ltima cosa que hace LILO es saltar a setup. 5. Si el soporte para m´ odulos ha sido compilado en el n´ ucleo. Inicializa el subsistema softirq. capaz de detectar 386 y superiores. 10.S y entonces las cosas prosiguen de la forma normal. 6. Arrancando 8 principal (de acuerdo con Alan Cox) para que permanezca es que aparentemente existen algunas BIOS defectuosas que hacen imposible arrancar n´ ucleos bzImage. Si la linea de comandos ”profile=” ha sido suministrada. Limpia a cero BSS (en SMP. Los siguientes pasos son realizados: 1. Inicializa los datos requeridos por el planificador (scheduler). inicializa la facilidad para la carga din´ amica de m´ odulos. 6. 4. 12. s´ olo la primera CPU realiza esto).c:start kernel() est´ a escrita en C y realiza lo siguiente: 1. 4. etc. Muestra el ”anuncio” del n´ ucleo Linux conteniendo la versi´ on. 7. incluso aquellas partes del c´ odigo que est´ an escritas en ensamblador. Inicializa el tiempo manteniendo los datos. 7.

Habilita las interrupciones.initcall. /bin/init. inicializa el subsistema. Por consiguiente. o intenta ejecutar /sbin/init. cambia el orden en el cual sus archivos objetos son listados. Rogier Wolff propuso introducir una infraestructura jer´ arquica de ”prioridades” donde los m´ odulos pueden dejar que el enlazador conozca en que orden (relativo) deber´ ıan de ser enlazados. fork init(). Si. totalram pages y high memory y muestra la linea ”Memory: . imag´ ınate dos subsistemas A y B. ocurre una situaci´ on de p´ anico con la ”sugerencia” de usar el par´ ametro ”init=”. 14. tal como han sido listados secuencialmente en el mismo Makefile. el cual es s´ olo chequeado si el n´ ucleo ha sido compilado para menos de un 686 y corregido adecuadamente. el orden en el cual estas funciones de inicializaci´ on son llamadas puede cambiar. aseg´ urate de que el orden de enlace es correcto. 24. con B dependiendo de alguna inicializaci´ on realizada por A. ¿qu´ e pasa si A y B est´ an est´ aticamente enlazadas en el n´ ucleo? El orden en el cual son llamadas depende del desplazamiento relativo del punto de entrada en la secci´ on ELF . dependiendo de las posici´ on de los directorios en los ´ arboles y de las estructuras en los Makefiles. finaliza la inicializaci´ on del asignador slab. Si el soporte de quota ha sido compilado en el n´ ucleo. trabajar´ an siempre. entonces B es tambi´ en necesariamente un m´ odulo para que no existan problemas. inicializa la mayor´ ıa del asignador slab.”. Pero. crea e inicializa una antememoria slab especial para ´ el. N´ otese que para System V shm. kmem cache sizes init().. Se va a un bucle vac´ ıo. cuando es posible. crea uid cache. Llama a mem init(). 16. 15. pero todav´ ıa no existen parches disponibles que implementen esto de una forma suficientemente elegante para ser aceptada en el n´ ucleo. . Una cosa importante que hay que hacer notar aqu´ ı es que el hilo del n´ ucleo init() llama a do basic setup(). Inicializa las estructuras de datos usadas por procfs. 23. Establece una bandera para indicar que un planificador deber´ ıa de ser llamado en la ”siguiente oportunidad” y crea un hilo del n´ ucleo init() que ejecuta execute command si este ha sido suministrado a trav´ es del par´ ametro de inicio ”init=”..1. que calcula max mapnr. Si no trabajan. 22. VM. en el ejemplo anterior. etc. A veces esto es importante. Si el soporte para System V IPC ha sido compilado en el n´ ucleo. kmem cache init(). 25. /bin/sh en este orden.init de la imagen del n´ ucleo. esto incluye el montaje de una instancia (dentro del n´ ucleo) del sistema de archivos shmfs. Si A es compilada est´ aticamente y B es un m´ odulo entonces el punto de entrada de B est´ a garantizado para ser llamado despu´ es de que A prepare todo el entorno necesario. activa las correcciones para los fallos de procesadores/bus/etc. que va a trav´ es de la lista de funciones registradas por medio de las macros initcall o module init() y las invoca. Arrancando 9 13. 21. el cual cuando vuelve llama a do initcalls(). Estas funciones no dependen de otras o sus dependencias han sido manualmente arregladas por el orden de enlazado en los Makefiles. A y B trabajan bien cuando han sido compilados est´ aticamente una vez. este es un hilo vac´ ıo con pid=0. Si A es un m´ odulo. inicializa max threx threads bas´ andose en la cantidad de memoria disponible y configura RLIMIT NPROC para que init task sea max threads/2. 18. Calcula el valor BogoMips para esta CPU. /etc/init. 20. si todos estos fallan. 19. Comparando varias arquitecturas vemos que ”ia64 no tiene fallos” e ”ia32 tiene unos pocos”. Crea varias antememorias slab necesitadas para VFS. Realiza ”chequeos de fallos” espec´ ıficos de la arquitectura y. Un buen ejemplo es el ”fallo f00f”. Esto significa que. 17. la antememoria intermedia.

por supuesto. El c´ odigo del AP escribe un n´ umero m´ agico en su propio c´ odigo. El motivo referente a deshechar el c´ odigo/datos de inicializaci´ on es que Linux suministra dos macros para ser usadas: • • init . Esto es u ´til para la recuperaci´ on desde un /sbin/init accidentalmente sobreescrito o para depurar a mano los guiones de inicializaci´ on (rc) y /etc/inittab.S. Este es el motivo por el que init tss[NR CPUS] es una array. etc. y entonces sobre smp init() y especialmente src/i386/kernel/smpboot. se salta el c´ odigo que limpia BSS y entonces entra en initialize secondary(). que es la entrada principal a arch/i386/kernel/head. no puede usar tal escusa porque bajo Linux ”si en principio algo es posible.init"))) . el n´ ucleo Linux s´ olo puede ser compilado como un binario ELF. Por lo tanto. la mayor´ ıa del c´ odigo y estructuras de datos no se necesitar´ an otra vez. etc. La funci´ on smp boot cpus() entra en un buche para cada apicid (identificador de cada APIC). Destacar que init task puede ser compartido. FreeBSD.para el c´ odigo de inicializaci´ on initdata .8 Liberando datos y c´ odigo de inicializaci´ on Cuando el sistema operativo se inicializa a si mismo.S. gastando entonces valiosa memoria f´ ısica del n´ ucleo. entonces ya est´ a implementado o alguien est´ a trabajando en ello”.. Lo que hace do boot cpu() es crear (esto es: fork by hand) una tarea vac´ ıa para la cpu de destino y escribe en localizaciones bien conocidas definidas por la especificaci´ on Intel MP (0x467/0x469) el EIP del c´ odigo del trampol´ ın encontrado en trampoline. Arrancando 10 Otra cosa de alg´ un valor es la habilidad de Linux de ejecutar un ”programa init alternativo” por medio del pase de la linea de comandos ”init=”. Linux. hasta NR CPUS.S y descubriendo que no es un BP. hasta que llega a start kernel().c:smp boot cpus(). el BP (Procesador de arranque) va a trav´ es de la secuencia normal del sector de arranque. como he dicho anteriormente. El motivo que ellos no lo realizan (ver el libro de McKusick 4. ejecut´ andolos de uno en uno.data. el cual justamente entra en la tarea vac´ ıa para esta CPU recalcar que init tasks[cpu] ya hab´ ıa sido inicializada por el BP ejecutando do boot cpu(cpu). y ahora adivinamos el motivo (o uno de los motivos) para ello. el AP empieza ejecutando head. La CPU de arranque crea una copia del c´ odigo trampol´ ın para cada CPU en la memoria baja. 1.init"))) __attribute__ ((__section__ (".text.) no pueden deshacerse de esta informaci´ on innecesaria. configuraci´ on.1. pero cada hilo vac´ ıo debe de tener su propio TSS. y llama a do boot cpu() en ´ el.S.h: #ifndef MODULE #define __init #define __initdata __attribute__ ((__section__ (".. Ahora.7 Arranque SMP en x86 En SMP. el cual es verificado por el BP para asegurarse que el AP est´ a ejecutando el c´ odigo trampol´ ın. Entonces genera STARTUP IPI a la cpu de destino la cual hace que este AP (Procesador de Aplicaci´ on) ejecute el c´ odigo en trampoline. El requerimiento de que el c´ odigo trampol´ ın tenga que estar en la memoria baja es forzado por la especificaci´ on Intel MP. El c´ odigo trampol´ ın simplemente establece el registro %bx a uno.para datos Estas eval´ uan al atributo especificador gcc (tambi´ en conocido como ”gcc magic”) tal como ha sido definido en include/linux/init. 1.4BSD) es que ”el c´ odigo relevante est´ a propagado a trav´ es de varios subsistemas y por lo tanto no es factible liberarlo”. entra en modo protegido y salta a startup 32. La mayor´ ıa de los sistemas operativos (BSD.

LILO (o BCP) acepta la linea de comandos usando los servicios de teclado de la BIOS y los almacena en una localizaci´ on bien conocida en la memoria f´ ısica. tambi´ en como una firma diciendo que all´ ı existe una linea de comando v´ alida. Volvemos a la l´ ınea de comandos en parse options() (llamada por start kernel()) el cual procesa algunos par´ ametros ”dentro del n´ ucleo” (actualmente ”init=” y entorno/argumentos para init) y pasa cada palabra a checksetup(). Jeff Garzik coment´ o: ”los hackers que hacen esto son buenos :)” ¿Por qu´ e? Porque esto es claramente espec´ ıfico del orden de enlazado. cuando se est´ a dise˜ nando un subsistema (no necesariamente un m´ odulo).S copia los primeros 2k de ella fuera de la p´ agina cero. En un sistema t´ ıpico (mi estaci´ on de trabajo).text. arch/i386/kernel/setup. En caso contrario (si es un m´ odulo) las macros no eval´ uan nada. N´ otese que usando el valor de retorno de 0 desde la funci´ on registrada a trav´ es de setup(). pero ellas est´ an m´ as directamente conectadas al soporte de m´ odulos por lo que ser´ an explicadas en una secci´ on posterior. 3. 4. llamadas exit y exitdata.init y llama a cada funci´ on. Arrancando 11 #else #define __init #define __initdata #endif Lo que esto significa es que si el c´ odigo es compilado est´ aticamente en el n´ ucleo (MODULO no est´ a definido). el enlazado del n´ ucleo en un orden tendr´ a a la llamada de la funci´ onA antes de la funci´ onB y otro los tendr´ a en orden inverso.setup. el cual es declarado en el mapa del enlazado en arch/i386/vmlinux. el subsistema en cuesti´ on.c). las cuales trabajan de una manera similar. Si realmente necesitas pasarle lineas de comando m´ as grandes de los 79 bytes. Las funciones registradas a trav´ es de module init() son colocadas en . con el resultado dependiendo del orden. pueda ser modularizado si se necesita. Esto es un fallo no trivial en LILO (cuando el soporte para EBDA grandes est´ a activado) y Werner prometi´ o arreglarlo pr´ oximamente. entonces puedes usar BCP o codificar tu linea de comandos en la funci´ on arch/i386/kernel/setup. bdflush (ver fs/buffer.initcall.c:parse mem cmdline() (llamada por setup arch(). Un ejemplo de esto es pipefs. Incluso si un subsistema nunca fuese convertido a m´ odulo. checksetup() va a trav´ es del c´ odigo en la secci´ on ELF .c:init()) llama a la funci´ on espec´ ıfica de la arquitectura free initmem() la cual libera todas las p´ aginas entre las direcciones init begin e init end. . arch/i386/kernel/head. ver fs/pipe.9 Procesando la linea de comandos del n´ ucleo D´ ejanos recalcar qu´ e es lo que le pasa a la linea de comandos cuando se le pasa al n´ ucleo durante el arranque: 1. esto resulta en la liberaci´ on de unos 260K de memoria.c:parse mem cmdline(). es posible pasarle el mismo ”variable=value” a m´ as de una funci´ on con el ”value” inv´ alido a una y v´ alido a otra.lds. 5. ej. 2. Hay dos macros m´ as. suministrada aunque no haga nada cuando la funci´ on es precisamente llamada. entonces es colocado en la secci´ on especial ELF . Esta misma rutina procesa la opci´ on ”mem=” si est´ a presente y realiza los ajustes apropiados en los par´ ametros de la VM. es suministrar puntos de entrada init/exit desde las etapas tempranas del dise˜ no para que en el futuro.init el cual es tambi´ en liberado en el caso est´ atico.init. Lo que pasa durante el arranque es que el hilo del n´ ucleo ”init” (funci´ on init/main. La actual tendencia en Linux. esto es. N´ otese que la actual versi´ on de LILO (21) corta la linea de comandos a los 79 bytes. y esta llamada por start kernel()) copia 256 bytes de la p´ agina cero a saved command line la cual es mostrada por /proc/cmdline. pas´ andole la palabra si corresponde.1. 1. a´ un es bonito y arreglado usar la macro module init() contra su funci´ on de inicializaci´ on.c.

#ifndef MODULE #define __setup(str.1. } __setup("BusLogic=".c): static int __init BusLogic_Setup(char *str) { int ints[3]. int (*setup_func)(char *). por lo tanto el c´ odigo que quiere procesar la linea de comandos del arranque. Destacar que setup() no hace nada por los m´ odulos. return 0. return BusLogic_ParseDriverOptions(str). } if (str == NULL || *str == ’\0’) return 0. NULL).func) /* nada */ endif Por lo tanto. ints). t´ ıpicamente la usar´ as en tu c´ odigo de esta forma (tomado del c´ odigo del controlador real. fn) \ static char __setup_str_##fn[] __initdata = str. BusLogic_Setup). if (ints[0] != 0) { BusLogic_Error("BusLogic: Obsolete Command Line Entry " "Format Ignored\n". Esto tambi´ en significa que es posible escribir c´ odigo que procese los par´ ametros cuando es compilado como un m´ odulo pero no cuando es est´ atico o viceversa. ARRAY_SIZE(ints). ¿c´ omo debemos de escribir el c´ odigo que procesa la linea de comandos del arranque? Nosotros usamos la macro setup() definida en include/linux/init. extern struct kernel_param __setup_start. BusLogic HBA drivers/scsi/BusLogic. Arrancando 12 Por lo tanto. debe de ser llamado pas´ andole la funci´ on manualmente en la rutina de inicializaci´ on del m´ odulo. }. que puede ser un m´ odulo o estar est´ aticamente enlazado. \ static struct kernel_param __setup_##fn __initsetup = \ { __setup_str_##fn. .h: /* * Usado por la configuraci´ on de par´ ametros de la l´ ınea de comandos * del n´ ucleo */ struct kernel_param { const char *str. fn } #else #define __setup(str. __setup_end. (void)get_options(str.

2 y anteriores). en la arquitectura IA32.1)) Las tareas son ordenadas por su valor pid y la posterior funci´ on de ordenaci´ on se supone que distribuye los elementos uniformemente en sus dominios de (0 a PID MAX-1). b´ asicamente significa num physpages/4. puedes crear 32k de hilos. y 2. o simplemente usando la interfaz procfs para el ajuste del n´ ucleo: # cat /proc/sys/kernel/threads-max 32764 # echo 100000 > /proc/sys/kernel/threads-max # cat /proc/sys/kernel/threads-max 100000 # gdb -q vmlinux /proc/kcore Core was generated by ‘BOOT_IMAGE=240ac18 ro root=306 video=matrox:vesa:0x118’. en una m´ aquina de 512M. #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ . #0 0x0 in ?? () (gdb) p max_threads $1 = 100000 El conjunto de procesos en el sistema Linux est´ a representado como una colecci´ on de estructuras struct task struct. Como ejemplo. **htable = &pidhash[pid_hashfn(pid)].c:fork init()): /* * El n´ umero m´ aximo por defecto de hilos es establecido * a un valor seguro: las estructuras de hilos pueden ocupar al * menos la mitad de la memoria. lo cual.h: /* PID hashing.h: static inline struct task_struct *find_task_by_pid(int pid) { struct task_struct *p. La tabla hash es usada para encontrar r´ apidamente una tarea por su pid usando find task pid() dentro de include/linux/sched. como una lista circular doblemente enlazada usando los punteros p->next task y p->prev task. La tabla hash es llamada pidhash[] y est´ a definida en include/linux/sched. y es igual a (ver kernel/fork. Esto es una mejora considerable sobre el l´ ımite de 4k-epsilon para los n´ ucleos viejos (2. Es m´ as. como una tabla hash. ordenados por el pid. .1 Procesos y Manejo de Interrupciones Estructura de Tareas y Tabla de Procesos Cada proceso bajo Linux es din´ amicamente asignado a una estructura struct task struct. */ max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 2. las cuales est´ an enlazadas de dos formas: 1. El n´ umero m´ aximo de procesos que pueden ser creados bajo Linux est´ a solamente limitado por la cantidad de memoria f´ ısica presente. (>no deber´ ıa de ser din´ amico?) */ #define PIDHASH_SZ (4096 >> 2) extern struct task_struct *pidhash[PIDHASH_SZ].2. Procesos y Manejo de Interrupciones 13 2 2. esto puede ser cambiado en tiempo de ejecuci´ on usando el KERN MAX THREADS sysctl(2).

El motivo por el que esto es interesante es porque los escritores deben de deshabilitar las interrupciones en la CPU local. etc. y esta es llamada desde kill fasync() en el contexto de interrupciones. Destacar que for each task() est´ a usando init task para marcar el principio (y el final) de la lista . Ellos se corresponden d´ ebilmente con los miembros de las estructuras de UNIX ’struct proc’ y ’struct user’ combinadas entre ellas.h: #define for_each_task(p) \ for (p = &init_task . 0 ejecutable. El u ´nico motivo para este feo dise˜ no es que la memoria era un recurso muy escaso.h y es actualmente de un tama˜ no de 1680 bytes. ordenadas por el mismo valor) son enlazadas por p->pidhash next/pidhash pprev el cual es usado por hash pid() y unhash pid() para insertar y quitar un proceso dado en la tabla hash. Esto es realizado bajo la protecci´ on del spinlock read/write (lectura/escritura) llamado tasklist lock tomado para ESCRITURA. Los modificadores de los procesos de la tabla hash y/o los enlaces de la tabla de procesos.). s´ olo Linux por el momento. la cual deber´ a de ser mantenida en memoria residente durante todo el tiempo (llamada ’proc structure’ la cual incluye el estado del proceso. p && p->pid != pid.).2. p = p->pidhash_next) . d´ ejanos examinar los miembros de task struct. (p = p->next_task) != &init_task . El motivo para esto no es trivial: la funci´ on send sigio() anda por la lista de tareas y entonces coge tasklist lock para ESCRITURA. deben de coger la tasklist lock para ESCRITURA. La estructura task struct est´ a declarada en include/linux/sched. El campo de estado es declarado como: volatile long state. Ahora que entendemos c´ omo las estructuras task struct son enlazadas entre ellas. ) Los usuarios de for each task() deber´ ıan de coger la tasklist lock para LECTURA. } Las tareas en cada lista ordenada (esto es. como FreeBSD ( que parece que avanzan en esta direcci´ on. return p. exit() y ptrace(). informaci´ on de planificaci´ on.esto es seguro porque la tarea vac´ ıa (pid 0) nunca existe. Este es el motivo por el que los escritores deben de deshabilitar las interrupciones mientras los lectores no lo necesitan. informaci´ on sobre la cuota de disco etc. y otra parte. hacia Linux) no necesitan tal separaci´ on y entonces mantienen el estado de procesos en una estructura de datos del n´ ucleo residente en memoria durante todo el tiempo. la cual es solamente necesitada cuando el proceso est´ a funcionando (llamada ’u area’ la cual incluye la tabla de descriptores de archivos. Procesos y Manejo de Interrupciones 14 for(p = *htable. Los sistemas operativos modernos (bueno. notablemente fork(). Esto es realizado por la macro for each task() desde include/linux/sched. Las otras versiones de UNIX separan la informaci´ on del estado de las tareas en una parte. #define #define #define #define #define #define /* -1 no ejecutable. pero otros. >0 parado */ 0 1 2 4 8 32 TASK_RUNNING TASK_INTERRUPTIBLE TASK_UNINTERRUPTIBLE TASK_ZOMBIE TASK_STOPPED TASK_EXCLUSIVE . La lista circular doblemente enlazada que usa p->next task/prev task es mantenida para que uno pueda ir f´ acilmente a trav´ es de todas las tareas del sistema.

El motivo por lo que quiz´ as no est´ e a´ un en la cola de ejecuci´ on es porque marcar una tarea como TASK RUNNING y colocarla en la cola de ejecuci´ on no es at´ omico. p->priority. Los campos p->mm y p->active mm apuntan respectivamente a la direcci´ on del espacio del proceso descrita por la estructura mm struct y al espacio activo de direcciones. Necesitar´ as mantener el spinlock read/write runqueue lock en lectura para mirar en la cola de ejecuci´ on. TASK STOPPED: tarea que fue parada. hilos . Sin embargo. TASK EXCLUSIVE: este no es un estado separado pero puede ser uno de TASK INTERRUPTIBLE o TASK UNINTERRUPTIBLE.2. /* banderas para cada proceso. los controladores pueden marcarse a ellos mismos (o en realidad. puede ser despertada s´ ola en vez de causar el problema de ”movimiento general” despertando a todos los que est´ an esperando. De una forma parecida. TASK ZOMBIE: tareas que han terminado pero que no tienen su estado reflejado (para wait()-ed) por el padre (natural o por adopci´ on). en tal caso permanecen en la cola de ejecuci´ on).3. 4. solo para 486 */ #define PF_STARTING 0x00000002 /* Durante la creaci´ on */ #define PF_EXITING 0x00000004 /* Durante la destrucci´ on */ #define PF_FORKNOEXEC 0x00000040 /* Dividido pero no ejecutado */ #define PF_SUPERPRIV 0x00000100 /* Usados privilegios de super-usuario */ #define PF_DUMPCORE 0x00000200 /* N´ ucleo volcado */ #define PF_SIGNALED 0x00000400 /* Asesinado por una se~ nal */ #define PF_MEMALLOC 0x00000800 /* Asignando memoria */ #define PF_VFORK 0x00001000 /* Despertar al padre en mm_release */ #define PF_USEDFPU 0x00100000 /* La tarea us´ o de FPU este quantum (SMP) */ Los campos p->has cpu. Esto significa que cuando esta tarea est´ a durmiendo o un una cola de espera con otras tareas. 6. 3. en el contexto del proceso en el que est´ an) como TASK INTERRUPTIBLE (o TASK UNINTERRUPTIBLE) y entonces llaman a schedule(). los cuales no son mutuamente exclusivos: unsigned long flags. la conversi´ on no es verdad por los motivos explicados anteriormente. 2. por se˜ nales de control de trabajos o por ptrace(2). Procesos y Manejo de Interrupciones 15 ¿Por qu´ e TASK EXCLUSIVE est´ a definido como 32 y no c´ omo 16? Porque 16 fue usado por TASK SWAPPING y me olvid´ e de cambiar TASK EXCLUSIVE cuando quit´ e todas las referencias a TASK SWAPPING (en alg´ un sitio en 2. p->policy y p->rt priority son referentes al planificador y ser´ an mirados m´ as tarde. TASK RUNNING: significa que la tarea est´ a ”supuestamente” en la cola de ejecuci´ on. excepto que no puede ser despertado. TASK UNINTERRUPTIBLE: lo mismo que TASK INTERRUPTIBLE. Las banderas de las tareas contienen informaci´ on sobre los estados de los procesos. si el proceso no tiene una verdadera (ej. La declaraci´ on volatile en p->statesignifica que puede ser modificada asincr´ onicamente (desde el manejador de interrupciones). 1. 5.x). ver´ as que cada tarea en la cola de ejecuci´ on est´ a en el estado TASK RUNNING. p->processor. Si lo haces. definidas abajo */ /* * Banderas para cada proceso */ #define PF_ALIGNWARN 0x00000001 /* Imprime mensajes de peligro de alineaci´ on */ /* No implementada todav´ ıa. p->counter. TASK INTERRUPTIBLE: significa que la tarea est´ a durmiendo pero que puede ser despertada por una se˜ nal o por la terminaci´ on de un cron´ ometro. el cual entonces los quita de la cola de ejecuci´ on (a memos que exista una se˜ nal pendiente.

2. empezando por ”instancia de un programa en ejecuci´ on” y finalizando con ”lo que es producido por las llamadas del sistema clone(2) o fork(2)”. si nosotros estamos planificando el hilo del n´ ucleo (el cual no tiene p->mm) entonces su next->active mm ser´ a establecido al prev->active mm de la tarea que fue descargada. 2. Los hilos del n´ ucleo usualmente no tienen espacio de direcciones de usuario. Ellos son asignados a n´ umeros pid en el rango bajo. . Esto ayuda a minimizar las descargas TLB en los intercambios del espacio de direcciones cuando la tarea es descargada. el cual desenrolla la llamada al sistema fork(2) a mano (en algunas arquitecturas).c:do fork(). es entonces creado ”manualmente” para cada CPU por medio de la funci´ on espec´ ıfica de la arquitectura fork by hand() en arch/i386/kernel/smpboot. El hilo vac´ ıo es creado en tiempo de compilaci´ on para la primera CPU. usar la bandera CLONE PID en clone(2). porque ellos explicitamente hacen exit mm(). la forma en que ciertas llamadas al sistema se comportan para emular la ”personalidad” de tipos externos de UNIX. Los hilos del n´ ucleo son creados usando la funci´ on kernel thread() la cual invoca a la llamada al sistema clone(2) en modo n´ ucleo. Todas las tareas vac´ ıas tienen pid = 0 y ninguna otra tarea puede compartir el pid. Funcionando en el anillo del procesador 0 (en x86) implica que los hilos del n´ ucleo disfrutan de todos los privilegios de E/S y no pueden ser pre-desocupados por el planificador. • hilos del n´ ucleo. Bajo Linux. El campo p->sig contiene los manejadores de se˜ nales y puede ser compartido entre tareas clonadas por medio de CLONE SIGHAND. las cuales internamente invocan a kernel/fork. dentry del directorio de trabajo actual y punto de montaje. hay tres clases de procesos: • el/los hilo(s) vac´ ıo(s). Esta estructura tambi´ en incluye un contador de referencia porque puede ser compartido entre tareas clonadas cuando la bandera CLONE FS es pasada a la llamada al sistema clone(2). dentry de un directorio ra´ ız alternativo y punto de montaje. Los campos p->exec domain y p->personality se refieren a la personalidad de la tarea. esto es. Procesos y Manejo de Interrupciones 16 de n´ ucleo). esto es. • tareas de usuario. esto es p->mm = NULL. Las tareas vac´ ıas comparten una estructura init task pero tienen una estructura privada TSS. ej.c. Los hilos del n´ ucleo siempre pueden acceder al espacio de direcciones del n´ ucleo directamente. dentry del directorio ra´ ız y punto de montaje. en la matriz de cada CPU init tss. a trav´ es de la funci´ on daemonize(). suministrando CLONE FILES el cual es especificado con clone(2). Las tareas de usuario son creadas por medio de las llamadas al sistema clone(2) o fork(2). lo cual significa bajo Linux tres partes de informaci´ on: 1. El campo p->fs contiene informaci´ on sobre el sistema de archivos. 3. El espacio de direcciones puede ser compartido entre hilos si la bandera CLONE VM es pasada a las llamadas al sistema clone(2) o vfork(2). Por lo tanto.2 Creaci´ on y terminaci´ on de tareas e hilos del n´ ucleo Diferentes libros sobre sistemas operativos definen un ”proceso” de diferentes formas. El campo p->files contiene la tabla de descriptores de ficheros. Esto tambi´ en puede ser compartido entre tareas. la cual ser´ a la misma que prev->mm si prev->mm != NULL. 2.

por ejemplo como resultado de exec() (ejecutar) otro programa o exit(2). incrementa la cuenta de procesos con el uid dado p->user->count. current->vfork sem es inicializado (es m´ as tarde limpiado en el hijo).librarse de el. Una nueva estructura de tarea es asignada usando la macro dependiente de la arquitectura alloc task struct(). Esto es usado por sys vfork() (la llamada al sistema vfork(2) corresponde a clone flags = CLONE VFORK|CLONE VM|SIGCHLD) para hacer que el padre duerma mientras el hijo hace mm release(). si no. Linux es suficientemente flexible para hacer de ellos una cuesti´ on mejor que un hecho). falla con -EAGAIN. la actual funci´ on subyacente do fork() que hace el trabajo es portable y est´ a localizada en kernel/fork. falla con -EAGAIN. ¿Quiz´ as deber´ ıa de ser reemplazada por un establecimiento de memoria? M´ as tarde. incrementa el contador de referencia del m´ odulo correspondiente. ya que este es el valor al que errno deber´ ıa de ser establecida si fork(2) falla al asignar una nueva estructura de tarea. Si CLONE PID es establecido en clone flags entonces devuelve un error (-EPERM). Las p->flags del hijo son establecidas de acuerdo a los valores de clone flags. a menos que el llamante sea el hilo vac´ ıo (s´ olo durante el arranque). Para fork(2). EL hijo es puesto en el estado ’durmiendo no interrumpible’. Si el n´ umero de tareas a lo largo del sistema excede el valor de max threads (recordar que es ajustable). El hijo es marcado como ’no tiene que ser ejecutado’ (p->did exec = 0) 12. Si el binario que se ejecuta pertenece a un dominio modularizado de ejecuci´ on. El hijo es marcado como ’no intercambiable’ (p->swappable = 0) 13. esto ser´ a p->flags = PF FORKNOEXEC. Como fork(2) es dependiente de la arquitectura debido a las diferentes formas de pasar la pila y registros de usuario. . Los siguientes pasos son realizados: 1. Un gran cierre del n´ ucleo es tomado durante el resto del c´ odigo ya que en otro caso ser´ ıa no reentrante. es irrelevante que clone flags sea establecido a SIFCHLD .si lo es. 2. 10.esto es s´ olo relevante cuando do fork() es invocado desde sys clone() el cual pasa clone flags desde el valor pedido desde el espacio de usuario. Todos los valores de la actual estructura de tareas del proceso son copiadas en la nueva. 7. Si el binario que se ejecuta pertenece a un formato binario modularizado. 4. devolvemos -ENOMEM. Si la primera asignaci´ on falla. 5. La variable local retval es establecida a -ENOMEM. los campos que no deber´ ıan de ser heredados por el hijo son establecidos a los valores correctos.c. incrementa el contador de referencia del correspondiente m´ odulo. Linus confirma que no se necesita) 14. Por lo tanto. esto es p->state = TASK UNINTERRUPTIBLE (POR HACER: ¿por qu´ e es realizado esto? Creo que no se necesita . 11. En x86 es justo un gfp a la prioridad GFP KERNEL. un hilo de un usuario normal no puede pasar CLONE PID a clone(2) y esperar que tenga ´ exito. 6. 8. 3.2. Procesos y Manejo de Interrupciones 17 D´ ejenos entender qu´ e pasa cuando un proceso de usuario realiza una llamada al sistema fork(2). Este es el primer motivo por el que la llamada fork(2) quiz´ as duerma. para fork(2) limpias. usando un asignamiento de estructura *p = *current. Si el padre no tiene recursos de usuario (un concepto de UID. entonces verifica si el l´ ımite blando de usuario RLIMIT NPROC ha sido excedido . 9.

3. sparc64). (POR HACER: wake up process(p) establece p->state = TASK RUNNING y a˜ nade el proceso a la cola de ejecuci´ on. La pdeath signal es usada cuando un proceso ’olvida’ el padre original (durante la muerte) y puede ser establecido/tomado por medio del comando PR GET/SET PDEATHSIG de la llamada al sistema prctl(2) (Tu quiz´ as argumentes que la forma en la que el valor de pdeath signal es devuelto a trav´ es de un argumento de un puntero del espacio de usuario en prctl(2) es un poco tonto . • Llama schedule() al final. El resto del c´ odigo en do fork() inicializa el resto de la estructura de la tarea del hijo. • Usa un cierre global del n´ ucleo (cierra pero no abre).c:get pid() (POR HACER: el spinlock lastpid lock puede ser redundante ya que get pid() siempre es llamado bajo un gran cierre del n´ ucleo desde do fork(). entonces probablemente no necesita establecer p->state a TASK RUNNING tempranamente en do fork()). mips64) (POR HACER: quitar el argumento ’flags’ de sparc. Hay varias formas para la terminaci´ on de tareas: 1. Las funciones implementando llamadas al sistema bajo Linux son prefijadas con sys . El pid del hijo p->pid es establecido usando el algoritmo r´ apido en kernel/fork. mips. • Establece el estado de tareas a TASK ZOMBIE. tambi´ en quita los argumentos bandera de get pid(). Procesos y Manejo de Interrupciones 18 15. cierra los archivos abiertos. haciendo la llamada al sistema exit(2).c.mirar despu´ es). • Notifica al padre con una current->exit signal. realiza lo que el hardware requiera para pasar la FPU al due˜ no (si el due˜ no es el actual) a ”none” (ninguno). • Libera los recursos asignador por fork. La funci´ on do exit() es encontrada en kernel/exit. la estructura de tarea del hijo es ordenada en la tabla hash pidhash y el hijo es despertado. despu´ es de que Andries Brouwer actualizara la p´ agina man era muy tarde para arreglarlo . Los puntos que destacar sobre do exit() son. • En arquitecturas que usan FPU lentas (ia64. pero ellas son usualmente concernientes s´ olo al chequeo de argumentos o a formas espec´ ıficas de la arquitectura de pasar alguna informaci´ on y el trabajo actual es realizado por las funciones do . el cual es usualmente igual a SIGCHLD. enviando un se˜ nal con la disposici´ on por defecto de morir. parche enviado a Alan el 20/06/2000 . etc. 4. es con sys exit() el cual llama a do exit() para hacer el trabajo. llamando bdflush(2) con func == 1 (esto es espec´ ıfico de Linux.) Entonces las tareas son creadas.2. • Notifica cualquier hijo con current->pdeath signal.mea culpa. para compatibilizaci´ on de viejas distribuciones que todav´ ıa tienen la linea ’update’ en /etc/inittab . si no 0. La parte interesante es establecer p->exit signal a clone flags & CSIGNAL. 16. el cual nunca regresa. siendo forzado a morir bajo ciertas excepciones.hoy en d´ ıa el trabajo de update es hecho por el hilo del n´ ucleo kupdate). la cual para fork(2) significa justamente SIGCHLD y establece p->pdeath signal a 0. Muy al final. . 2. Por lo tanto. Aunque otras partes del n´ ucleo a veces invocan a sys exit() mientras que deber´ ıan realmente de llamar a do exit().

• p->counter: n´ umero de ticks de reloj que quedan en esta porci´ on de tiempo del planificador.1b) y SCHED RR (proceso en tiempo real round-robin POSIX).4BSD/SVR4 setpriority(2).c. Se quita el cierre global del n´ ucleo. excepto que cuando su porci´ on de tiempo acaba vuelve al final de la cola de ejecutables.2. 2. decrementada por un cron´ ometro. La cola de tareas suministra al n´ ucleo un mecanismo para planificar la ejecuci´ on de las funciones m´ as tarde. • p->rt priority: prioridad en tiempo real. SCHED FIFO (proceso FIFO en tiempo real POSIX. Se inicializan las variables locales prev y this cpu a las tareas y CPUs actuales.4. Se chequea si schedule() fue llamada desde el controlador de interrupciones (debido a un fallo) y provoca un p´ anico si ha sido as´ ı. D´ ejanos mirar la funci´ on en detalle: 1. Tambi´ en. 3. Esto tambi´ en es llamado a veces ’prioridad din´ amica’ de un proceso porque puede cambiarse a si mismo. Si hay algo que hacer en la cola de tareas tq scheduler. Lo miraremos en detalle en otra parte. 4. Cuando este campo se convierte a un valor menor o igual a cero. por ejemplo llamando a la llamada al sistema sched yield(2). o c) es predesocupado por otro proceso de tiempo real con un valor m´ as alto de p->rt priority. • p->priority: la prioridad est´ atica del proceso.1b sched setparam(2) o 4. respectivamente. es reinicializado a 0 y p->need resched es establecido. incluso un hilo del n´ ucleo (current->mm == NULL) debe de tener un p->active mm v´ alido durante todo el tiempo. • p->policy: la pol´ ıtica de planificaci´ on. b) expl´ ıcitamente deje la CPU. Si current->active mm == NULL entonces algo est´ a mal. 5. destacar que el planificador (como la mayor´ ıa del n´ ucleo) fue totalmente reescrito para el 2. Un proceso FIFO en tiempo real funcionar´ a hasta que: a) se bloquee en una E/S. espec´ ıfica a la clase de planificaci´ on que pertenece la tarea. Procesos y Manejo de Interrupciones 19 2. El planificador est´ a implementado en el ’archivo principal del n´ ucleo’ kernel/sched. Los valores v´ alidos son SCHED OTHER (proceso UNIX tradicional). entonces la discusi´ on de m´ as abajo no se aplica a los n´ ucleos 2. s´ olo cambiada a trav´ es de bien conocidas llamadas al sistema como nice(2).2 o anteriores. Los campos de una estructura de tareas relevante a planificar incluyen: • p->need resched: este campo es establecido si schedule() deber´ ıa de ser llamado en la ’siguiente oportunidad’. POSIX. La funci´ on es compleja porque implementa tres algoritmos de planificaci´ on en uno y tambi´ en porque disimula los espec´ ıficos de SMP. Las aparentemente ’inservibles’ etiquetas (gotos) en schedule() est´ an all´ ı con el prop´ osito de generar el mejor c´ odigo optimizado (para i386). Las tareas pueden cambiar su clase de planificaci´ on usando la llamada al sistema sched setscheduler(2). SCHED RR es el mismo que SCHED FIFO. entonces se procesa ahora. El archivo de cabeceras correspondiente include/linux/sched. Uno puede tambi´ en SCHED YIELD a alguno de esos valores para significar que el proceso decidi´ o dejar la CPU. EL algoritmo de planificaci´ on es simple.3 Planificador Linux El trabajo de un planificador es decidir el acceso a la actual CPU entre m´ ultiples procesos.h est´ a incluido virtualmente (expl´ ıcita o implicitamente) en todos los archivos de c´ odigo fuente del n´ ucleo. . El actual proceso. olv´ ıdate de la gran complejidad aparente de la funci´ on schedule().

si el actual valor de la virtud es 0 entonces la lista entera de los procesos (¡no s´ olo los de la lista de ejecutables!) es examinada y sus prioridades din´ amicas son recalculadas usando el simple algoritmo: recalculate: { struct task_struct *p. Por esto. Si hay alg´ un trabajo que hacer a trav´ es del mecanismo de softirq. Se inicializa el puntero local struct schedule data *sched data para que apunte a cada CPU (alineado de la linea de antememoria para prevenir que la linea de antememoria salte) planificando el ´ area de datos. Ahora el concepto de ”puede ser planificado en esta CPU” debe de ser clarificado: en UP. es movido al estado TASK RUNNING. 10. Estado de tareas de la m´ aquina: si la tarea est´ a en el estado TASK RUNNING entonces se deja s´ olo. dar ventaja a un proceso en la misma CPU). esto es. si est´ a en el estado TASK INTERRUPTIBLE y hay una se˜ nal pendiente. for_each_task(p) p->counter = (p->counter >> 1) + p->priority. siendo mayor que 1000 se garantiza que no puede ganar otro proceso SCHED OTHER. con la esperanza de que haya otro mejor que ´ el. cuando abrimos runqueue lock. la cual trata los procesos en tiempo real haciendo sus virtudes muy altas (1000 + p->rt priority). Si la tarea prev (actual) est´ a en el estado TASK RUNNING entonces la actual virtud es establecida a su virtud y es marcado como mejor candidato para ser planificado que la tarea vac´ ıa.2. la virtud de este candidato es establecida a un valor muy bajo (-1000). Destacar que usamos spin lock irq() porque en schedule() garantizamos que las interrupciones est´ an habilitadas. La constante espec´ ıfica de la arquitectura PROC CHANGE PENALTY intenta implementar la ”afinidad de cpu” (esto es. read_lock(&tasklist_lock). todos los procesos en la cola de ejecuci´ on son elegibles para ser planificados. ¿pero porqu´ e inicializa tambi´ en init idle() en UP (monoprocesadores)? 8. 12. Ahora la cola de ejecuci´ on es examinada y una virtud de cada proceso que puede ser planificado en esta CPU es comparado con el actual valor. 7. los procesos interactivos son favorecidos m´ as que el l´ ımite de impulsos de la CPU. 13. se hace ahora. read_unlock(&tasklist_lock). por lo tanto s´ olo compiten con los otros procesos en tiempo real que quiz´ as tengan un mayor p->rt priority.por este camino. 11. En todos los otros casos es borrado de la cola de ejecuci´ on. 9. Procesos y Manejo de Interrupciones 20 6. el proceso con la virtud m´ as alta gana. el valor inicial de la virtud es establecido a p->counter . en SMP. podemos rehabilitarlas en vez de salvar/restaurar las eflags (variante spin lock irqsave/restore). Para procesos que no son en tiempo real. s´ olo los procesos que no estean corriendo en otra CPU son elegibles para ser planificados en esta CPU. Es tomado el spinlock runqueue lock. La funci´ on virtud devuelve 0 si la porci´ on de tiempo del proceso (p->counter) se acab´ o. next (mejor candidato para ser planificado) es establecido a la tarea vac´ ıa de esta CPU. hilos del n´ ucleo. spin_unlock_irq(&runqueue_lock). } . spin_lock_irq(&runqueue_lock). esto es. Tambi´ en da una ligera ventaja a los procesos con mm apuntando al actual active mm o a procesos sin espacio de direcciones (de usuario). el cual contiene el valor TSC de last schedule y el puntero a la u ´ltima estructura planificada (POR HACER: sched data es usada s´ olo en SMP. En todo caso. La virtud es calculada por una funci´ on llamada goodness(). el proceso tiene menos posibilidades para alcanzar la CPU si ya la tuvo por alg´ un tiempo.

&(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr). Desde este punto. La runqueue lock puede ahora ser abierta. La estructura de datos fundamental aqu´ ı es struct list head: struct list_head { struct list_head *next. saltar todos los niveles hardware (registros. schedule() corriendo en otra CPU podr´ ıa estar recalculando las prioridades din´ amicas. Procesos y Manejo de Interrupciones 21 Destacar que tiramos el runqueue lock antes de recalcular. *prev. por lo tanto debemos de inicializar next->has cpu a 1 y next->processor a this cpu.h” porque el archivo m´ as relevante es include/linux/list. head) \ for (pos = (head)->next.2. debemos de informarnos con la implementaci´ on est´ andar de la lista doblemente enlazada Linux. . 2. Muy bien. Las colas de espera (igual que todo lo dem´ as en Linux) hacen un uso fuerte de ellas y entonces son llamadas en la jerga ”implementaci´ on list. 15. La macro switch to() es espec´ ıfica de la arquitectura. mientras que nosotros en esta CPU seremos obligados a recalcular. En i386. durante el cual el schedule() puede ser llamado por otra CPU y seleccionar un proceso con la suficiente virtud para esta CPU. las cuales deber´ ıan de ser usadas aqu´ ıpor ejemplo. c) recargar los registros de segmento.) y el grupo relacionado con la VM (Memoria Virtual) (cambiar la p´ agina del directorio. Si estamos volviendo a la misma tarea (next == prev) entonces podemos simplemente readquirir un cierre global del n´ ucleo y volver. pila. es concerniente con: a) manejo de la FPU (Unidad de Punto Flotante).h. esto puede llevar un gran tiempo. \ } while (0) #define list_entry(ptr. El motivo para esto es que vamos a trav´ es del conjunto entero de procesos. es cierto que next apunta a la tarea a ser planificada. type. pos = pos->next) Las tres primeras macros son para inicializar un lista vac´ ıa apuntando los punteros next y prev a ellos mismos. LIST HEAD INIT() puede ser usada para la inicializaci´ on de elementos de la estructura en la declaraci´ on. member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) #define list_for_each(pos. 14.4 Implementaci´ on de la lista enlazada (de) Linux Antes de ir a examinar las implementaci´ on de las colas de espera. Esto es obvio debido a las restricciones sint´ acticas de C. pos != (head). (ptr)->prev = (ptr). la segunda puede ser usada para la inicializaci´ on de las declaraciones de variables est´ aticas y la tercera puede ser usada dentro de la funci´ on. etc. admitamos que esto es algo inconsistente porque mientras que nosotros (en esta CPU) estamos seleccionando un proceso con la mejor virtud. #define LIST_HEAD_INIT(name) { &(name). b) manejo de la LDT. }. esto es.) 16. d) manejo de TSS y e) recarga de los registros de depuraci´ on. recalcular active mm etc.

. next = p. donde andamos a trav´ es de la cola de ejecuci´ on buscando al proceso con la virtud m´ as alta: static LIST_HEAD(runqueue_head).. list_for_each(tmp.. } } Aqu´ ı. &runqueue_head). struct file { ..next.. run_list). p->run_list. p->run list es declarada como struct list head run list dentro de la estructura task struct y sirve como ancla de la lista. for (p = sb->s_files. this_cpu.. struct file. nr_running++.2. haz algo a ’file’ } Un buen ejemplo del uso de la macro list for each() est´ a en el planificador. prev->active_mm).c:fs may remount ro()): struct super_block { . . f_list). struct task_struct *p. Quitando y a˜ nadiendo (al principio o al final de la lista) un elemento de la lista es hecho por las macros list del()/list add()/list add tail(). struct list_head f_list. } *file. } . if (can_schedule(p)) { int weight = goodness(p. struct list_head s_files. } *sb = &some_super_block.. list_del(&p->run_list). } static inline void add_to_runqueue(struct task_struct * p) { list_add(&p->run_list. Procesos y Manejo de Interrupciones 22 La macro list entry() da acceso individual a los elementos de la lista. Los ejemplos siguientes est´ an a˜ nadiendo y quitando una tarea de la cola de ejecuci´ on: static inline void del_from_runqueue(struct task_struct * p) { nr_running--. struct list_head *tmp.. por ejemplo (desde fs/file table.next = NULL. struct task_struct.. struct list_head *p. &runqueue_head) { p = list_entry(tmp. if (weight > c) c = weight. p != &sb->s_files. p = p->next) { struct file *file = list_entry(p.

por medio de la cola de espera kswapd wait. Un ejemplo del uso de una cola de espera aut´ onoma es la interacci´ on entre la solicitud de datos de un proceso de usuario a trav´ es de la llamada al sistema read(2) y el n´ ucleo funcionando en el contexto de interrupci´ on para suministrar los datos. Ahora. la llamada al sistema read(2) puede ser implementada como: ssize_t rtc_read(struct file file. y es despertado cuando el asignador de p´ aginas necesita liberar algunas p´ aginas. current).5 Colas de espera Cuando un proceso solicita el n´ ucleo para hacer algo que es actualmente imposible pero que quiz´ as sea posible m´ as tarde. el proceso es puesto a dormir y es despertado cuando la solicitud tiene m´ as probabilidades de ser satisfecha. el demonio kswapd duerme en esta cola.c. unsigned long data. void *dev_id. wake_up_interruptible(&rtc_wait). Un manejador de interrupciones quiz´ as se parezca a (drivers/char/rtc interrupt() simplificado): static DECLARE_WAIT_QUEUE_HEAD(rtc_wait).c: alloc pages()) y el demonio del n´ ucleo kswapd (en mm/vmscan. tambi´ en puedes usar una cola bien conocida y entonces simplificar sleep on/sleep on timeout/interruptible sleep on/interruptible sleep on timeout. char *buf. ssize_t retval. &runqueue_head). . list_add_tail(&p->run_list. list_add(&p->run_list. struct pt_regs *regs) { spin_lock(&rtc_lock). } 2. La implementaci´ on de Linux nos permite despertar usando la bandera TASK EXCLUSIVE. rtc_irq_data = CMOS_READ(RTC_INTR_FLAGS). el manejador de interrupciones obtiene los datos leyendo desde alg´ un puerto de E/S espec´ ıfico del dispositivo (la macro CMOS READ() devuelve un par de outb/inb) y entonces despierta a quien est´ e durmiendo en la cola de espera rtc wait. } Por lo tanto. declarada en mm/vmscan. &runqueue_head).c:kswap()).2. Uno de los mecanismos del n´ ucleo usados para esto es llamado ’cola de espera’. loff_t *ppos) { DECLARE_WAITQUEUE(wait. o puedes definir tu propia cola de espera y usar add/remove wait queue para a˜ nadir y quitarte desde ella y wake up/wake up interruptible para despertar cuando se necesite. Procesos y Manejo de Interrupciones 23 static inline void move_last_runqueue(struct task_struct * p) { list_del(&p->run_list). } static inline void move_first_runqueue(struct task_struct * p) { list_del(&p->run_list). Un ejemplo del primer uso de las colas de espera es la interacci´ on entre el asignador de p´ aginas (en mm/page alloc. size_t count. Con las colas de espera. void rtc_interrupt(int irq. spin_unlock(&rtc_lock).

usando una cola de espera. if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN. Marcamos el actual contexto como TASK INTERRUPTIBLE lo que significa que no ser´ a replanificado despu´ es de la pr´ oxima vez que duerma. rtc_irq_data = 0.2. &wait). esto es. Procesos y Manejo de Interrupciones 24 add_wait_queue(&rtc_wait. if (data != 0) break. es bastante m´ as f´ acil implementar la llamada al sistema poll(2): . Tambi´ en chequeamos si hay alguna se˜ nal pendiente y si por lo tanto informamos a las ”capas superiores” para reinicializar la llamada al sistema si es necesario. } schedule(). goto out. Por ”si es necesario” yo entiendo los detalles de la disposici´ on de la se˜ nal tal como est´ an especificadas en la llamada al sistema sigaction(2). do { spin_lock_irq(&rtc_lock). if (!retval) retval = sizeof(unsigned long). retval = put_user(data. Si todav´ ıa no hay datos. Si no nos marcamos como TASK INTERRUPTIBLE entonces el planificador nos podr´ a planificar tan pronto como los datos estean disponibles. Entonces ”salimos”. data = rtc_irq_data. current->state = TASK_INTERRUPTIBLE. 4. Es tambi´ en valioso apuntar que. Declaramos un elemento de la cola de espera apuntando al contexto del proceso actual. goto out. out: current->state = TASK_RUNNING. y si es as´ ı entonces fallamos con EAGAIN (el cual es el mismo que EWOULDBLOCK) 6. 7. si los hay empezamos. hasta que sea despertado por el manejador de interrupciones. (unsigned long *)buf). nos dormimos. 2. nos quitamos de la cola de espera y regresamos. copiamos los datos a la memoria intermedia del usuario. &wait). causando as´ ı procesamiento no necesario. } Lo que pasa en rtc read() es esto: 1. } if (signal_pending(current)) { retval = -ERESTARTSYS. } while(1). nos marcamos como TASK RUNNING. Chequeamos si no hay datos disponibles. chequeamos cuando el usuario especific´ o una E/S no bloqueante. return retval. remove_wait_queue(&rtc_wait. spin_unlock_irq(&rtc_lock). 5. A˜ nadimos este elemento a la cola de espera rtc wait 3.

El campo expires es el valor de jiffies cuando el manejador function deber´ ıa de ser invocado con data pasado como par´ ametro. La estructura de datos principal es struct timer list declarada en include/linux/timer. cuando las interrupciones est´ an habilitadas (ej. poll_table *wait) { unsigned long l. las cuales ser´ an el t´ ıtulo de la siguiente secci´ on. para realizar alg´ un post-procesamiento sobre los datos. En Linux 2. llamado ’colas de tareas’. wait). Los cron´ ometros del n´ ucleo son usados para expedir la ejecuci´ on de una funci´ on particular (llamada ’manejador de cron´ ometros’) en un tiempo especificado en el futuro. DEBE de ser inicializado por medio de la funci´ on init timer(). ) y el trabajo que puede ser postpuesto para m´ as tarde.x. poll_wait(file.0. }. Procesos y Manejo de Interrupciones 25 static unsigned int rtc_poll(struct file *file.6 Cron´ ometros del n´ ucleo Ahora d´ ejanos poner nuestra atenci´ on en los cron´ ometros del n´ ucleo. Antes de que el cron´ ometro sea usado. todo lo que necesitamos hacer es apuntarla a la cola de espera la cual es despertada por nuestro manejador de interrupciones espec´ ıfico del dispositivo. actualizar las estad´ ısticas. l = rtc_irq_data. etc. return 0.7 Bottom Halves A veces es razonable partir la cantidad de trabajo para ser realizada dentro de un manejador de interrupciones en un trabajo inmediato (ej. void (*function)(unsigned long). if (l != 0) return POLLIN | POLLRDNORM. spin_lock_irq(&rtc_lock). agradecer la interrupci´ on.h: struct timer_list { struct list_head list. &rtc_wait. unsigned long data. los campos function y expires deben de ser establecidos. El campo running es usado en SMP para probar si el manejador de cron´ ometros est´ a actualmente funcionando en otra CPU Las funciones add timer() y del timer() a˜ naden y quitan un cron´ ometro determinado de la lista. unsigned long expires. un nuevo mecanismo fue a˜ nadido. spin_unlock_irq(&rtc_lock). protegida por el spinlock timerlist lock. volatile int running. Los bottom halves son el mecanismo m´ as viejo para posponer la ejecuci´ on de una tarea del n´ ucleo y est´ an disponibles desde Linux 1. Y entonces es a˜ nadido. Cuando un cron´ ometro se termina. . este es borrado autom´ aticamente. etc). 2. } Todo el trabajo es realizado por la funci´ on independiente del dispositivo poll wait() la cual hace las manipulaciones necesarias en la lista de espera. 2.2. despertar a los procesos esperando por estos datos. El campo list es para enlazar con la lista interna.

por lo tanto la pregunta ”¿c´ uando es el manejador bottom half ejecutado?” es realmente ”¿cu´ ando son los tasklets ejecutados?”. Desde que el manejador de cron´ ometro funciona en el contexto de interrupci´ on. El slot debe de estar numerado en include/linux/interrupt.8 Colas de Tareas Las colas de tareas pueden ser entendidas como una extensi´ on din´ amica de los viejos bottom halves. En vez de crear nuestra propia cola de tareas (y tener que consumirla manualmente) puedes usar una de las colas de tareas predefinidas en Linux las cuales son consumidas en puntos bien conocidos: 1. el bottom half es marcado (esto es planificado) para ejecuci´ on . Y la respuesta es. obviamente). Las funciones requeridas para manipular los bottom halves son las siguientes (todas exportadas a m´ odulos). el caso schedule() es realmente aburrido . S´ olo puede haber 32 bottom halves registrados en total. S´ olo hay un n´ umero fijo de ellos (32). en opuesto a un bucle ocupado en global bh lock. esto es. Procesos y Manejo de Interrupciones 26 Los bottom halves son serializados por el spinlock global bh lock. en dos sitios: a) en cada schedule() y b) en cada camino de retorno de interrupciones/llamadas al sistema en entry. 2.por lo tanto el procesamiento puede continuar. con las colas de tareas. desinstala el bottom half instalado en el slot nr. . por lo tanto no pueden bloquear. los viejos bottom halves discutidos en la secci´ on anterior tienen estas limitaciones: 1.2. 2. Los bottom halves son tasklets globalmente cerrados. una rutina de inicializaci´ on del subsistema (init module() para los m´ odulos) instala el bottom half requerido usando esta funci´ on.parece a˜ nadir todav´ ıa otra interrupci´ on muy muy lenta. T´ ıpicamente. un manejador de interrupciones marcar´ a este bottom half (¡de aqu´ ı el nombre!) para ejecuci´ on en un ”tiempo seguro”. (POR HACER: ¿no ser´ ıa bonito tener una lista /proc/bottom halves con todos los bottom halves en el sistema? Esto significa que global bh lock deber´ ıan hacer lecturas/escrituras. En realidad. s´ olo puede haber un bottom half funcionando en cualquier CPU a la vez.S (POR HACER: entonces. • void mark bh(int nr): marca el bottom half en el slot nr para ejecuci´ on. T´ ıpicamente. por lo tanto. No hay chequeos de errores realizados aqu´ ı. si no est´ a disponible global bh lock.h en la forma XXXX BH. M´ as espec´ ıficamente. Los Bottom halves son consumidos con un spinlock mantenido. Por lo tanto. De cualquier modo. en el c´ odigo fuente son a veces referidas como los ”nuevos” bottom halves. funciona en cada interrupci´ on del cron´ ometro y cuando se libera un dispositivo tty (cerrando o liberando un dispositivo de terminal medio abierto). Uno crea una nueva cola de tareas usando la macro DECLARE TASK QUEUE() y encola la tarea en ´ el usando la funci´ on queue task(). como ejemplo remove bh(32) rompe el sistema. la tarea tq timer tambi´ en funciona en el contexto de interrupci´ on y de este modo tampoco puede bloquearse. ¿por qu´ e no desembarazarse de la etiqueta handle softirq de schedule() en su conjunto?). 3. ej. • void remove bh(int nr): hace lo opuesto de init bh(). • void init bh(int nr. que puede ser reusado por alg´ un otro subsistema. esto es. void (*routine)(void)): instala un manejador de bottom half apuntado por el argumento routine en el slot nr. cuando se intenta ejecutar el manejador. un n´ umero arbitrario de funciones pueden ser encadenadas y procesadas una despu´ es de otra en un tiempo posterior. TIMER BH o TQUEUE BH. tq timer: la cola de tareas de cron´ ometros. La cola de tareas entonces puede ser procesada usando run task queue(). una rutina de limpieza del subsistema (cleanup module() para los m´ odulos) usa esta funci´ on para liberar el slot. T´ ıpicamente. Cada bottom half s´ olo puede estar asociado con una funci´ on de manejador.

Por lo tanto. pero no el u ´nico ejemplo) se aclara si uno recuerda que el controlador puede planificar tareas en la cola. 4. Cuando una aplicaci´ on del espacio de usuario realiza una llamada del sistema. Solaris/x86). Esta cola de tareas es exportada a los m´ odulos pero no deber´ ıa de ser usada excepto para los prop´ ositos especiales para los que fue dise˜ nada. consumida por el planificador (y tambi´ en cuando se cierran dispositivos tty. usar los datos del contexto de los procesos (pero porque ellos quieren). UnixWare 7 etc. dpl 3) apunta a la direcci´ on de la entrada system call desde arch/i386/kernel/entry. Como el planificador es ejecutado en el contexto de los procesos siendo re-planificados.S.lo cual usualmente significa hasta que la aplicaci´ on la cierre. pero la funci´ on manejadora es llamada lcall7 func. esto es bloquear. El motivo por el que la cola de tareas tq timer/tq scheduler no es consumida s´ olo en los sitios usuales sino en otras partes (cerrando un dispositivo tty. tq immediate: esto es realmente un bottom half IMMEDIATE BH.10 Softirqs Todav´ ıa no. porque permiti´ endoles funcionar en un tiempo posterior quiz´ as no tenga sentido . Esto causa un reajuste en el modo n´ ucleo y el procesador salta al punto de entrada system call en entry. la funci´ on arch/i386/kernel/traps. etc . por lo tanto el vector 0x80 (del tipo 15. los argumentos son pasados a trav´ es de los registros y la aplicaci´ on ejecuta la instrucci´ on ’int 0x80’. &tq immediate) y entonces mark bh(IMMEDIATE BH) ser consumido en el contexto de interrupci´ on. y estas tareas solo tienen sentido mientras una instancia particular del dispositivo sea todav´ ıa v´ alida .) usan el mecanismo lcall7. las estructuras de datos relevantes quiz´ as no hayan sido liberadas/reusadas por una instancia diferente. 3. tq scheduler: la cola de tareas del planificador. excepto bajo ciertas circunstancias explicadas a continuaci´ on. el controlador quiz´ as necesite llamar a run task queue() para encender las tareas que el (y alguno m´ as) ha puesto en la cola. A menos que un controlador use su propia cola de tareas.esto es. las tareas tq scheduler pueden hacer todo lo que quieran. por lo tanto los controladores pueden queue task(task. como tq timer). Este es el motivo por el que ves run task queue() en tq timer y tq scheduler en otros lugares m´ as que el cron´ ometro de interrupciones y schedule() respectivamente. Cuando el sistema arranca.2. estar´ an en una revisi´ on futura 2. 2.S.9 Tasklets Todav´ ıa no. El nombre ’lcall7’ es hist´ oricamente enga˜ noso porque tambi´ en cubre lcall27 (ej.c:trap init() es llamada. tq disk: usado por un acceso de dispositivo de bloqueo de bajo nivel (y RAID) para empezar la actual petici´ on. estar´ an en una revisi´ on futura 2. • interrupci´ on software int 0x80. no necesita llamar a run tasks queues() para procesar la cola. Los programas nativos de Linux utilizan int 0x80 mientras que los binarios de los distintos tipos de UNIX (Solaris. la cual inicializa el IDT. Lo que esto hace es: .11 ¿C´ omo son las llamadas al sistema implementadas en la arquitectura i386? Existen dos mecanismos bajo Linux para implementar las llamadas al sistema: • las llamadas puerta lcall7/lcall27 . Procesos y Manejo de Interrupciones 27 2.

/* uno de cada vez. %edx. por lo tanto todas las referencias de datos (y segmentos extras) son hechas en el espacio de direcciones del n´ ucleo. %edi (y %ebp usado temporalmente. chequeando si un schedule() es necesitado (tsk->need resched !=0). realiza un procesamiento especial. Un ejemplo de esto puede ser encontrado en arch/i386/kernel/microcode. Ellas son pasadas en %ebx. Esto es para soportar programas como strace (an´ alogo a SVR4 truss(1)) o depuradores. por favor */ if (test_and_set_bit(MICROCODE_IS_OPEN. Estos manejadores de las llamadas al sistema de C encontrar´ an sus argumentos en la pila donde SAVE ALL las almacen´ o. Procesos y Manejo de Interrupciones 28 1. por ejemplo para suministrar acceso exclusivo para abrir un dispositivo. 4.h). relacionado con el manejo de tasklets (incluyendo bottom halves). lcall27. ej. %esi. sys exit. sys open. Son ampliamente usados para un simple cierre. Los bitmaps son muy convenientes para mantener un concepto de unidades ”asignadas” o ”libres” para alguna colecci´ on grande donde cada unidad es identificada por alg´ un n´ umero. El n´ umero de llamadas al sistema es pasado a trav´ es de %eax. Si la tarea est´ a siendo ptraced (tsk->ptrace & PF TRACESYS). los cuales bajo Linux son (usualmente) prefijados con sys . %ecx. Llamada sys call table+4*(syscall number desde %eax). 5. 2. Esta tabla es inicializada en el mismo archivo (arch/i386/kernel/entry. struct file *file) { if (!capable(CAP_SYS_RAWIO)) return -EPERM. 2. /* * Forzamos a un s´ olo usuario a la vez aqu´ ı con open/close. 6. No hay necesidad de inicializar microcode status a 0 ya que BSS es limpiado a cero expl´ ıcitamente bajo Linux. Entra en el ’camino de retorno de la llamada al sistema’.S) para apuntar a los manejadores individuales de las llamadas al sistema.12 Operaciones At´ omicas Hay dos tipos de operaciones at´ omicas: bitmaps (mapas de bits) y atomic t. Establece %ds y %es a KERNEL DS. Esto es. */ static int microcode_open(struct inode *inode. Linux soporta hasta 6 argumentos para las llamadas al sistema. 3. Si el valor de %eax es mayor que NR syscalls (actualmente 256) fallar´ a con el error ENOSYS. Esta es una etiqueta separada porque es usada no s´ olo por int 0x80 sino tambi´ en por lcall7. Guarda los registros. &microcode_status)) return -EBUSY.c: /* * Bits en microcode_status. etc. .2. ver syscall6() en asm-i386/unistd. chequeando si hay se˜ nales pendientes y por lo tanto manej´ andolas. por ejemplo tres inodos o tres bloques. (31 bits de espacio para una futura expansi´ on) */ #define MICROCODE_IS_OPEN 0 /* establece si el dispositivo est´ a en uso */ static unsigned long microcode_status.

devuelve 0 en otro caso. devuelve 0 en otro caso. • int test and change bit(int nr. A veces las manipulaciones de bits no son convenientes. • void atomic sub(int i. pero en cambio necesitamos realizar operaciones aritm´ eticas . • void clear bit(int nr. devuelve 1 si el nuevo valor es 0. devuelve 1 si el nuevo valor es 0. volatile void *addr): at´ omicamente cambia el bit nr y devuelve el viejo valor del bit. • int atomic dec and test(volatile atomic t *v): decrementa el valor. • void atomic add(int i. volatile atomic t *v): resta el entero i del valor de la variable at´ omica apuntada por v. Estas operaciones usan la macro LOCK PREFIX. para los inodos). volatile void *addr): cambia el bit nr (si est´ a establecido limpia. i): establece el valor de la variable atomic t v al entero i. volatile void *addr): limpia el bit nr en el bitmap apuntado por addr. volatile atomic t *v): resta el entero i del valor de la variable at´ omica apuntada por v. devuelve 1 si el nuevo valor es 0. Devuelve 0 si el resultado es mayor o igual a 0. incremento decremento. • int test and set bit(int nr. • int atomic inc and test(volatile atomic t *v): incrementa el valor. return 0. • int test and clear bit(int nr. • void atomic dec(volatile atomic t *v): decrementa el valor en 1. si est´ a limpio establece) en en el bitmap apuntado por addr. • void atomic inc(volatile atomic t *v): incrementa el valor en 1. volatile atomic t *v): suma el valor de i a v y devuelve 1 si el resultado es negativo. Esta operaci´ on es usada para implementar sem´ aforos. . Procesos y Manejo de Interrupciones 29 MOD_INC_USE_COUNT. devuelve 0 en otro caso. • int atomic sub and test(int i. • int atomic add negative(int i. Los casos t´ ıpicos son cuentas de referencia (ej. volatile void *addr): establece el bit nr en el bitmap apuntado por addr. Esta facilidad es suministrada por el tipo de datos atomic t y las siguientes operaciones: • atomic read(&v): lee el valor de la variable atomic t v. Esto garantiza la atomicidad del acceso en el entorno SMP. volatile void *addr): at´ omicamente limpia el bit nr y devuelve el viejo valor del bit.suma. resta. volatile atomic t *v): suma un entero i al valor de la variable at´ omica apuntado por v.2. la cual en n´ ucleos SMP eval´ ua la instrucci´ on prefijo de cierre del bus y no hace nada en UP. • atomic set(&v. volatile void *addr): at´ omicamente establece el bit nr y devuelve el viejo valor del bit. • void change bit(int nr. } Las operaciones en los bitmaps son: • void set bit(int nr.

42 el 15 de Noviembre de 1995 (el parche original fue hecho para el 1. En todos los dem´ as casos donde el patr´ on de acceso no concuerda con ninguno de estos dos escenarios. El soporte SMP fue a˜ nadido a Linux 1.13 Spinlocks. Los Spinlocks vienen en tres tipos: plano. irq() y bh(). Por el camino. save_flags(flags). con una penalizaci´ on para los escritores. La lista es guardada por el spinlock read-write file systems lock porque uno necesita acceso exclusivo s´ olo cuando se est´ a registrando/desregistrando un sistema de archivos. los desarrolladores se encararon con el cl´ asico problema de acceder a datos compartidos entre los diferentes tipos de contexto (procesos de usuario vs interrupciones) y diferentes instancias del mismo contexto para m´ ultiples cpus. Mientras que esto est´ a bien en UP. Hay tres tipos de spinlocks: vanilla (b´ asico). no suministra protecci´ on contra todas las carreras entre los contextos funcionando en diferentes CPUs. 1. y mientras cli() suministra protecci´ on contra las carreras con el contexto de interrupciones en cada CPU individualmente.los lectores usualmente ocupan el cierre por un instante de tiempo muy peque˜ no. pero cualquier proceso puede leer el archivo /proc/filesystems o usar la llamada al sistema sysfs(2) para forzar un escaneo de s´ olo lectura de la lista file systems.c). No puedes bloquear mientras mantienes alg´ un tipo de spinlock. ¿ellos realmente deber´ ıan de tener hambre mientras el escritor usa el cierre para periodos potencialmente m´ as largos? Los spinlocks Big-reader son una forma de spinlocks read-write altamente optimizados para accesos de lectura muy ligeros. esto es. Esto lo hace sensible a usar spinlocks read-write. Es aqu´ ı donde los spinlocks son u ´tiles. cli().el argumento para lo contrario es . /* c´ odigo cr´ ıtico */ restore_flags(flags). si Linux pudiera distribuir correctamente la soluci´ on del hambre potencial del escritor por los m´ ultiples lectores. por lo tanto.2. la cual simplemente deshabilita (en el cierre) y re-habilita (en el desbloqueo) las interrupciones en la actual CPU.37 en Octubre del mismo a˜ no).actualmente s´ olo existen dos. obviamente no lo est´ a us´ andolo en SMP porque la misma secuencia de c´ odigo quiz´ as sea ejecutada simult´ aneamente en otra cpu. Con los spinlocks read-write.3. Hay un n´ umero limitado de spinlocks bigreader . spin lock()/spin unlock() plano: si conoces que las interrupciones est´ an siempre deshabilitadas o si no compites con el contexto de interrupciones (ej. desde un manejador de interrupciones). spin lock irq()/spin unlock irq(): si sabes que las interrupciones est´ an siempre habilitadas entonces puedes usar esta versi´ on. ser´ ıa bonito si nuevos lectores no isaran un cierre mientras hay un escritor intentando usar un cierre. uno puede tener m´ ultiples lectores a la vez pero s´ olo un escritor y no puede haber lectores mientras hay un escritor. Por ejemplo. Esto quiere significar que los lectores deben de ser bloqueados mientras exista un escritor intentando usar el cierre. en el siglo XX). Procesos y Manejo de Interrupciones 30 2. No toca el estado de interrupci´ on en la actual CPU. Un ejemplo de esto es el acceso a las lista de sistemas de archivos registrados (ver fs/super. read-write y spinlocks big-reader.3. entonces la forma de protegerlo usando las instrucciones cli/sti en UP es: unsigned long flags. de los cuales uno es usado s´ olo en sparc64 (irq global) y el otro es usado para redes. Si la regi´ on cr´ ıtica del c´ odigo puede ser ejecutada por el contexto de los procesos y el contexto de las interrupciones. Spinlocks Read-Write y Spinlocks Big-Reader Desde los primeros d´ ıas del soporte Linux (al principio de los 90. entonces puedes utilizar este. se deber´ ıa utilizar los spinlocks b´ asicos. 2. Los spinlocks read-write deber´ ıan de ser usados cuando existe una tendencia natural de ’muchos lectores y pocos escritores’. Este no es actualmente el caso y no es obvio cuando deber´ ıa de ser arreglado . rtc read() usa spin lock irq(&rtc lock) .

representado aqu´ ı como un m´ etodo t´ ıpico de un controlador . /* secci´ on cr´ ıtica */ spin_unlock(&lock). } my_irq_handler() { spin_lock(&lock). mientras se est´ a accediendo a estructuras de datos compartidas. no hay punteros us´ andolo si nuestro manejador de interrupciones no ejecuta ning´ un c´ odigo cr´ ıtico. Los sem´ aforos read-write difieren de los sem´ aforos b´ asicos de la misma forma que los spinlocks read-write difieren de los spinlocks b´ asicos: uno puede tener m´ ultiples lectores a la vez pero s´ olo un escritor y no puede . uno debe realizar operaciones que puedan bloquear. pero s´ olo si las interrupciones no importan nada. El uso m´ as com´ un de un spinlock es para acceder a estructuras de datos compartidas entre el contexto de proceso de usuario y el manejador de interrupciones: spinlock_t my_lock = SPIN_LOCK_UNLOCKED. /* secci´ on cr´ ıtica */ spin_unlock_irq(&my_lock). Hay dos tipos de sem´ aforos: b´ asicos y sem´ aforos read/write.14 Sem´ aforos y sem´ aforos read/write A veces.2. deben de usar spin lock irq() porque conocen que las interrupciones est´ an siempre habilitadas mientras se ejecuta un m´ etodo de dispositivo ioctl(). habiendo sido interrumpido. representado aqu´ ı por my irq handler() (otra vez los argumentos son omitidos para una mayor claridad) pueden usar la forma spin lock() plana porque las interrupciones est´ an deshabilitadas dentro del manejador de interrupciones. } Hay un par de cosas que destacar sobre este ejemplo: 1.ioctl() (argumentos y valores de retorno omitidos para una mayor claridad). por ejemplo copiar datos al espacio de usuario. es usada cuando el estado de las interrupciones no es conocido. el esperar´ a ocupado por el bloqueo para siempre: el que tenga el bloqueo. N´ otese que rtc read() usa spin lock irq() y no el m´ as gen´ erico spin lock irqsave() porque en la entrada a cualquier llamada al sistema las interrupciones est´ an siempre habilitadas. El motivo por el que no puedes usar el spin lock() plano si compites contra el manejador de interrupciones es porque si lo usas y despu´ es un interrupci´ on viene en la misma CPU. El contexto de interrupciones. La directiva del cierre disponible para tales escenarios bajo Linux es llamada sem´ aforo. no continuar´ a hasta el que manejador de interrupciones vuelva. El contexto del proceso. 2. 3. pueden ser usados para exclusi´ on mutua (valor inicial a 1) o para suministrar un tipo m´ as sofisticado de acceso. esto es. 2. Dependiendo del valor inicial del sem´ aforo. Procesos y Manejo de Interrupciones 31 (las interrupciones est´ an siempre habilitadas dentro de read()) mientras que rtc interrupt() usa spin lock(&rtc lock) (las interrupciones est´ an siempre deshabilitadas dentro del manejador de interrupciones). my_ioctl() { spin_lock_irq(&my_lock). spin lock irqsave()/spin unlock irqrestore(): la forma m´ as fuerte.

return errno. } up_write(&uts_sem). errno.justamente usan las operaciones down/up interruptible() en vez del down()/up() plano y chequean el valor devuelto desde down interruptible(): no ser´ a cero si la operaci´ on fue interrumpida. up_read(&uts_sem). name.nodename. Las funciones quiz´ as bloqueen mientras est´ an copiando datos desde/al espacio de usuario en copy from user()/copy to user(). errno = 0.nodename[len] = 0. errno = 0. down_write(&uts_sem). if (copy_to_user(name. Tambi´ en. return errno.nodename). el llamante no conoce a priori cuando la funci´ on bloquea o no. 2.esto es. Un ejemplo simple del uso de sem´ aforos est´ a en la implementaci´ on de las llamadas al sistema gethostname(2)/sethostname(2) en kernel/sys. el escritor bloquea a todos los lectores. Entonces no pueden usar ninguna forma de spinlock aqu´ ı. if (i > len) i = len. y los nuevos lectores se bloquean mientras un escritor est´ a esperando. i)) errno = -EFAULT. .nodename. if (len < 0) return -EINVAL. Procesos y Manejo de Interrupciones 32 haber lectores mientras hay escritores . los sem´ aforos b´ asicos pueden ser interrumpidos . if (len < 0 || len > __NEW_UTS_LEN) return -EINVAL. El tipo de sem´ aforo escogido es read-write en oposici´ on al b´ asico porque quiz´ as existan un mont´ on de peticiones concurrentes gethostname(2) las cuales no tienen que ser mutuamente exclusivas. down_read(&uts_sem). if (!copy_from_user(system_utsname. esto es.c. El uso de sem´ aforos para exclusi´ on mutua es ideal para situaciones donde una secci´ on cr´ ıtica de c´ odigo quiz´ as sea llamada por funciones de referencia desconocidas registradas por otros subsistemas/m´ odulos. } asmlinkage long sys_gethostname(char *name. } Los puntos a destacar en este ejemplo son: 1. errno = -EFAULT. int len) { int i. len)) { system_utsname. asmlinkage long sys_sethostname(char *name. int len) { int errno. system_utsname. i = 1 + strlen(system_utsname. if (!capable(CAP_SYS_ADMIN)) return -EPERM.2.

tienes nada HECHO. Linux esta y siempre estar´ a basado en un dise˜ no monol´ ıtico. 2. Formatos de Ficheros Binarios (ej. Dominios de Ejecuci´ on (ej. Hay unas pocas cosas que no pueden ser implementadas como m´ odulos bajo Linux (probablemente porque no tienen sentido el ser modularizadas): 1. en /proc y en devfs (ej. UnixWare7. antememoria de p´ aginas y otras antememoria. 3. Algoritmos de planificaci´ on. en m´ aquinas con poca memoria o para n´ ucleos de instalaci´ on. no existe el concepto de sem´ aforos read-write interrumpible. lo cual significa que todos los subsistemas funcionan en el mismo modo privilegiado y comparten el mismo espacio de direcciones. los cuales de otra forma pueden contener controladores de dispositivos ISA autoprobables que son mutuamente exclusivos). por ejemplo. separ´ andolo en m´ odulos del n´ ucleo din´ amicamente cargados bajo demanda es deseable en algunas circunstancias (ej. /dev/cpu/microcode vs 4. Las siguientes funcionalidades pueden ser implementadas como m´ odulos cargables bajo Linux: 1. 2. Antememoria intermedia. etc). . la comunicaci´ on entre ellos es realizada por medio de las llamadas usuales de funciones de C. De cualquier modo.. etc). Eso es obvio porque no hay situaciones en el mundo real que requieran estos tipos ex´ oticos de directivas. 6. pero actualmente no masturbaci´ on de la ciencia de la computaci´ on.. System V IPC. Pol´ ıticas de VM (Memoria Virtual). aunque la separaci´ on de la funcionalidad del n´ ucleo en ”procesos” separados realizada en los micro-n´ ucleos es definitivamente una mala idea. 7. El soporte para la auto-carga de m´ odulos a trav´ es del mecanismo request module() es una opci´ on separada de compilaci´ on (CONFIG KMOD). la verdad permanece (cita de Linus Torvalds): . Entonces. 2. La decisi´ on de cuando incluir soporte para la carga de m´ odulos es hecha en tiempo de compilaci´ on y es determinada por la opci´ on CONFIG MODULES. existen posibles escenarios que uno puede pensar en los cuales no est´ an todav´ ıa implementados. incluyendo los controladores de dispositivos misc. 3. ELF. Ficheros virtuales (regulares) /dev/misc/microcode). 5. Controladores de dispositivos de bloque y de car´ acter. Disciplinas de linea de Terminal. el paso de mensajes como la operaci´ on fundamental del SO es s´ olo un ejercicio de Quiz´ as suene bien. Solaris.2. aout. Sistemas de ficheros.15 Soporte del N´ ucleo para la Carga de M´ odulos Linux es un sistema operativo monol´ ıtico y olv´ ıdate de todos los dichos modernos sobre algunas ”ventajas” ofrecidas por los sistemas operativos basados en el dise˜ no micro-n´ ucleo. Linux. Procesos y Manejo de Interrupciones 33 Aunque la implementaci´ on de Linux de los sem´ aforos y los sem´ aforos de read-write es muy sofisticada.

esto es realizado por fs/block dev. para la mayor´ ıa de los n´ umeros mayores bien conocidos (y otros tipos de m´ odulos) los comandos modprobe/insmod conocen qu´ e m´ odulo real cargar sin necesitar una declaraci´ on expl´ ıcita de un alias en /etc/modules. tambi´ en es posible tener el m´ odulo insertado autom´ aticamente por el n´ ucleo cuando una funcionalidad particular es requerida. Procesos y Manejo de Interrupciones 34 Linux suministra varias llamadas al sistema para asistir en la carga de m´ odulos: 1. Si name == NULL. autor.c:do mount() la cual entonces pasa a fs/super. La interfaz de comandos disponible a los usuarios consiste en: • insmod: inserta un m´ odulo simple. Este nuevo m´ odulo es enlazado en la cabecera de la lista por module list. long query module(const char *name. no existe tal m´ odulo llamado block-major-N (los desarrolladores Linux solo escogen nombres sensibles para sus m´ odulos) pero es mapeado al propio nombre del m´ odulo usando el archivo /etc/modules. De cualquier forma. etc. La funci´ on devuelve 0 si es exitosa. S´ olo un proceso con CAP SYS MODULE puede llamar a esta llamada al sistema. • modprobe: inserta un m´ odulo incluyendo todos los otros m´ odulos de los cuales dependa. ej. int which. 4. Por ejemplo.c:get fs type(): . otros ver´ an como se les retorna EPERM.conf. void *buf. size t bufsize.2. size t size): asigna size bytes usando vmalloc() y mapea una estructura de un m´ odulo al principio de este. struct module *image): carga la imagen del m´ odulo reasignado y motiva que la rutina de inicializaci´ on del m´ odulo sea invocada. size t *ret): devuelve informaci´ on sobre un m´ odulo (o sobre todos los m´ odulos). otros ver´ an como se les retorna EPERM. Un buen ejemplo de la carga de un m´ odulo est´ a dentro de la llamada del sistema mount(2). 2. • modinfo: imprime alguna informaci´ on sobre un m´ odulo. • rmmod: quita un m´ odulo. caddr t create module(const char *name. La request module(name) internamente crea un hilo del n´ ucleo el cual ejecuta el comando del espacio de usuario modprobe -s -k module name. long init module(const char *name. por lo tanto los m´ odulos tambi´ en pueden cargar otros m´ odulos. S´ olo un proceso con CAP SYS MODULE puede llamar a esta llamada al sistema. En vez de esto. La interface del n´ ucleo para esto es la funci´ on llamada request module(name) la cual es exportada a los m´ odulos. el intento es hecho para descargar todos los m´ odulos no utilizados.c:get blkfops() para cargar un m´ odulo block-major-N cuando el intento es hecho para abrir un dispositivo de bloque con n´ umero mayor N. if (check_some_feature() == NULL) return -ENODEV. de cualquier forma no es usualmente v´ alido chequear el c´ odigo de retorno desde request module(). La llamada al sistema mount(2) acepta el tipo de sistema de archivos como una cadena fs/super. Aparte de ser capaz de cargar un m´ odulo manualmente usando insmod o modprobe. par´ ametros que acepta el m´ odulo. el idioma de programaci´ on es: if (check_some_feature() == NULL) request_module(module). Obviamente. descripci´ on. 3.conf. long delete module(const char *name): intenta descargar el m´ odulo. usando la interfaz est´ andar del n´ ucleo exec usermodehelper() (que es tambi´ en exportado a los m´ odulos).

if (fs && !try_inc_mod_count(fs->owner)) fs = NULL. podr´ ıamos tirar file systems lock de cualquier forma. if (fs && !try_inc_mod_count(fs->owner)) fs = NULL. Tiramos el file systems lock porque lo siguiente que vamos a hacer (request module()) es una operaci´ on bloqueante. incluso si request module() fuera garantizada para ser no bloqueante y la carga de m´ odulos fuera ejecutada en el mismo contexto at´ omicamente. En otro caso devolvemos NULL. } return fs. Si tal sistema de archivos es encontrado intentamos coger una nueva referencia a ´ el intentando incrementar la cuenta mantenida del m´ odulo. read_unlock(&file_systems_lock). entonces cogemos el spinlock file systems lock e intentamos situar el nuevamente registrado sistema de archivos en la lista. 5. 2. fs = *(find_filesystem(name)). if (!fs && (request_module(name) == 0)) { read_lock(&file_systems_lock).2. Primero intentamos encontrar el sistema de archivos con el nombre dado entre aquellos ya registrados. .esto es. en este caso espec´ ıfico. Si el sistema de archivos es encontrado y es capaz de obtener una referencia a el. 3. puede ser referido por cualquier s´ ımbolo que sea exportado como p´ ublico por el n´ ucleo usando la macro EXPORT SYMBOL() o por otros m´ odulos actualmente cargados. es marcado como dependiente de ese m´ odulo durante el rec´ alculo de dependencias. 4. en tal caso request module() fallar´ a incluso aunque el nuevo sistema de archivos halla sido registrado. El motivo para esto es que la funci´ on de inicializaci´ on de m´ odulos intentar´ a llamar a register filesystem(). la devolvemos. es tan bueno como si no estuviera all´ ı en absoluto. fs = *(find_filesystem(name)). despu´ es de instalar un nuevo n´ ucleo). Actualmente. N´ otese que esto es ligeramente err´ oneo porque es posible en un principio que un fallo en el comando modprobe pueda causar un volcado del n´ ucleo despu´ es de cargar con ´ exito el m´ odulo pedido. Procesos y Manejo de Interrupciones 35 static struct file_system_type *get_fs_type(const char *name) { struct file_system_type *fs. Esto siempre devuelve 1 para sistemas de archivos enlazados din´ amicamente o para m´ odulos que actualmente no se han borrados. Si try inc mod count() devuelve 0 entonces lo consideraremos un fallo . realizado funcionando el comando depmod -a en el arranque (ej. la cual tomar´ a el mismo spinlock read-write file systems lock para escritura. read_unlock(&file_systems_lock). y entonces no podemos mantener un spinlock sobre el. Esto es hecho bajo la protecci´ on de file systems lock tomado para lectura (ya que no estamos modificando la lista registrada de sistemas de archivos). } Hay que destacar unas pocas cosas en esta funci´ on: 1. read_lock(&file_systems_lock). Si el intento de carga tiene ´ exito. si el m´ odulo est´ a all´ ı pero est´ a siendo borrado. y todav´ ıa no lo encontrar´ a get fs type(). Si el m´ odulo usa s´ ımbolos de otro m´ odulo. Cuando un m´ odulo es cargado en el n´ ucleo.

inode hashtable. Los elementos de la parte p´ ublica (exportada) de la tabla de s´ ımbolos son construidos en la declaraci´ on de C de suma de control 32bit. i nlink>0 y i state & I DIRTY. Si ninguno de los dos usa los nombres originales de los s´ ımbolos el cargador simplemente intenta comprobar la versi´ on del n´ ucleo declarada por el m´ odulo y el exportado por el n´ ucleo y rechaza cargarlo si difieren. Cuando el inodo es marcado como sucio. Una lista por cada superbloque del tipo ”sucia” (sb->s dirty) que contiene los inodos v´ alidos con i count>0. donde cada inodo es ordenado por el valor del puntero del superbloque y el n´ umero de inodo de 32bit. Ejemplos de inodos an´ onimos son los conectores creados por net/socket. Los inodos nuevamente asignados por get empty inode() y get new inode() son a˜ nadidos a la lista inode in use. uno debe comprobar el conjunto de los m´ odulos con la versi´ on de las interfaces del n´ ucleo que usan. 3. Sistemas de Archivos Virtuales (VFS) 36 Usualmente. ellos son tomados y devueltos a esta antememoria SLAB. Esto s´ olo pasa cuando el n´ ucleo y el m´ odulo son compilados con el versionamiento de m´ odulos habilitado.c. Una lista global del tipo ”en uso” (inode in use). la cual contiene los inodos v´ alidos con i count>0 y i nlink>0. llamado por fs/inode. Los inodos sin un superbloque (inode->i sb == NULL) son a˜ nadidos a la lista doblemente enlazada encabezada por anon hash chain en su lugar. Linux contiene un nivel especial de interfaces del n´ ucleo llamado VFS (Interruptor de Sistemas de Ficheros Virtuales). Por lo tanto. digamos. La antememoria de inodos de Linux es implementada en un simple fichero. .c:sock alloc(). Esto es muy similar a la interfaz vnode/vfs encontrada en los derivados de SVR4 (originalmente ven´ ıan de BSD y de las implementaciones originales de Sun). el cual consiste de 977 lineas de c´ odigo. De cualquier forma. es a˜ nadido a la lista sb->s dirty si el est´ a tambi´ en ordenado. 3 3. tipo de lista (en uso. La estructura de la antememoria de inodos Linux es como sigue: 1. fs/inode.3. Una lista global del tipo ”sin usar” (inode unused). Manteniendo una lista sucia por superbloque de inodos nos permite r´ apidamente sincronizar los inodos. la tabla hash desde inode->i hash. la cual contiene los inodos v´ alidos con i count = 0. Una tabla global hash.42. Es interesante notar que no se han realizado muchos cambios en ´ el durante los u ´ltimos 5-7 a˜ nos: uno todav´ ıa puede reconocer alg´ un c´ odigo comparando la u ´ltima version con.una antememoria SLAB llamada inode cachep. ser´ a rechazada para cargar el m´ odulo si estos s´ ımbolos difieren. sin usar o sucia). 4. Cada inodo puede estar en una tabla hash y en uno. hay una funcionalidad limitada llamada ”versionamiento de m´ odulos” o CONFIG MODVERSIONS la cual nos permite eliminar el recompilamiento de m´ odulos cuando cambiamos a un nuevo n´ ucleo. en orden de resolver un s´ ımbolo usado por un m´ odulo durante la carga. 1.1 Sistemas de Archivos Virtuales (VFS) Cach´ e de Inodos e Interacci´ on con Dcache En orden para soportar m´ ultiples sistemas de archivos. Los tipos de listas son sujetadas desde inode->i list. Una antememoria propia de inodos . y en s´ olo uno. Tal como los objetos inodos son asignados como libres.3. Lo que pasa aqu´ ı es que la tabla de s´ ımbolos de n´ ucleo es tratada de forma diferente para el acceso interno y para el acceso de los m´ odulos. lo cual bajo Linux simplemente significa la ”versi´ on del n´ ucleo” ya que no hay versionados especiales del mecanismo de interfaces del n´ ucleo en general. 5. 2.c:get empty inode(). el cargador debe comprobar la representaci´ on total del s´ ımbolo que incluye la suma de control.

Se le pasa un argumento simple .c:sys open y el trabajo real es realizado por la funci´ on fs/open. Esto es por lo que la antememoria de inodos puede configurarse ella misma dependiendo de cuanta memoria est´ a disponible. close(fd). tambi´ en llama al m´ etodo espec´ ıfico del sistema de ficheros f op->open() el cual fue inicializado en inode->i fop cuando el inodo fue le´ ıdo en open namei() (el cual suministra el inodo a trav´ es de dentry->d inode). O_RDONLY).prev)->prev (struct list_head *) 0xdfb5a2e8 set $i = (struct inode *)0xdfb5a2e0 p $i->i_ino 0x3bec7 p $i->i_count {counter = 0x0} Destacar que restamos 8 de la direcci´ on 0xdfb5a2e8 para obtener la direcci´ on de struct inode (0xdfb5a2e0) de acuerdo a la definici´ on de la macro list entry() de include/linux/list. inode). El subsistema de cach´ e de inodos es inicializado cuando la funci´ on inode init() es llamada desde init/main. Podemos examinar una de las listas desde gdb en un n´ ucleo en funcionamiento de esta forma: (gdb) 8 (gdb) $34 = (gdb) $35 = (gdb) $36 = (gdb) $37 = (gdb) (gdb) $38 = (gdb) $39 = printf "%d\n". d´ ejanos seguir un tiempo de vida de un inodo de un fichero regular en el sistema de ficheros ext2. lo que significa que el c´ odigo ser´ a lanzado posteriormente. esto es.h. dentry open(): dado dentry y vfsmount. el cual en el regreso llama a real lookup().el n´ umero de p´ aginas f´ ısicas en el sistema. La misi´ on de este m´ etodo es encontrar la entrada en el directorio padre con el nombre correcto y entonces hace iget(sb. 2. La llamada al sistema open(2) es implementada en la funci´ on fs/open. el cual llama al m´ etodo espec´ ıfico del sistema de ficheros inode operations->lookup().3. Cuando el inodo es leido. open namei(): rellena la estructura nameidata conteniendo las estructuras dentry y vfsmount. la cual est´ a dividida en dos partes: 1. Sistemas de Archivos Virtuales (VFS) 37 Todas estas listas est´ an protegidas por un spinlock simple: inode lock. Las u ´nicas estad´ ısticas de informaci´ on sobre la antememoria de inodos es el n´ umero de inodos sin usar. el c´ omo es abierto y c´ omo es cerrado: fd = open("file". La funci´ on open namei() interact´ ua con la antememoria dentry a trav´ es de path walk(). esta funci´ on asigna una nueva struct file y las enlaza a todas ellas. Para entender c´ omo trabaja la antememoria de inodos.c:start kernel(). n´ otese que en los sistemas de ficheros del estilo UNIX que tienen el concepto de n´ umero de inodos en disco. (unsigned long)(&((struct inode *)0)->i_list) p inode_unused 0xdfa992a8 p (struct list_head)inode_unused {next = 0xdfa992a8.nr unused y accesibles por los programas de usuario a trav´ es de los archivos /proc/sys/fs/inode-nr y /proc/sys/fs/inode-state. el trabajo del m´ etodo lookup es mapear su bit menos significativo al actual . Mientras estamos en ´ el. La funci´ on es marcada como init.c:filp open(). prev = 0xdfcdd5a8} p ((struct list_head)inode_unused).prev (struct list_head *) 0xdfcdd5a8 p (((struct list_head)inode_unused).el cual nos trae la antememoria de inodos. almacenados en inodes stat. el dentry es instanciado por medio de d add(dentry. crea una tabla hash m´ as grande si hay suficiente memoria. ino) para coger el correspondiente inodo .

es quitado de cualquier tipo de lista (inode->i list) en la que est´ e (tiene que estar en la lista inode unused. es llamada inmediatamente sin mantener ning´ un spinlock (por lo tanto puede bloquear). veamos que pasa cuando cerramos este descriptor de ficheros. si era 0 anteriormente al incremento y el inodo no estaba sucio. el inodo est´ a bloqueado (i state = I LOCK).c:sys close(). por lo tanto el spinlock que guarda la tabla hash tiene que ser quitado. Si el par´ ametro pasado a nosotros es NULL. NULL). Intenta encontrar un inodo con el superbloque emparejado y el n´ umero de inodo en la tabla hash bajo la protecci´ on de inode lock.c: fput() la cual llama a fput() en la cual es donde sucede la interacci´ on con dcache (y entonces con la memoria intermedia de inodos . inode). Si hay un m´ etodo espec´ ıfico del sistema de archivos sb->s op->put inode().3. La llamada al sistema close(2) est´ a implementada en la funci´ on fs/open. su cuenta de referencia (i count) es incrementada.¡recuerda que dcache es la memoria intermedia de inodos Maestra!). ino) el cual es realmente iget4(sb. finalmente inodes stat. si esta vez es encontrado. inode = iget(sb. el cual hace: 1. Si a´ un no se ha encontrado en la tabla hash. por lo tanto est´ a garantizado que iget4() devolver´ a un inodo desbloqueado. get new inode() asigna un nuevo inodo desde la antememoria SLAB inode cachep. Desde que hemos quitado el spinlock. Por lo tanto. 2. por supuesto) e insertado en la lista del tipo inode in use. Si el inodo no fue encontrado en la tabla hash entonces es la primera vez que se pide este inodo. ´ el es desbloqueado despu´ es de que el m´ etodo read inode() regrese y todos los que est´ an esperando por el hayan sido despertados. Sistemas de Archivos Virtuales (VFS) 38 formato de la CPU. Esto nos proporciona desde la antememoria de inodos la vuelta al c´ odigo del sistema de archivos . 2. si el n´ umero de inodos en la entrada del directorio sin formato (espec´ ıfico del sistema de ficheros) est´ a en el formato de 32 bits little-endian uno har´ ıa: unsigned long ino = le32_to_cpu(de->inode). Ahora. La parte interesante sucede en fput(). pero esta operaci´ on puede bloquear (asignaci´ on GFP KERNEL). entonces es inicializado a los valores requeridos y el m´ etodo espec´ ıfico del sistema de ficheros sb->s op->read inode() es llamado para propagar el resto del inodo.nr unused es decrementado. cuando abrimos un fichero nosotros llamamos a iget(sb. ino). la cual llama a do close(fd. por lo tanto llamamos a get new inode(). esperaremos hasta que se desbloquee. no hacemos nada y regresamos. por lo tanto d´ ejanos entender fs/inode. 3. ej. ino. 1) el cual rompe (reemplaza con NULL) el descriptor del descriptor de ficheros de la tabla del proceso y llama a la funci´ on filp close(). El fs/dcache. entonces el nuevo inodo que tenemos acaba de ser asignado y es el que va a ser usado. y si es as´ ı llama a fs/file table.recuerda que venimos de la antememoria de inodos cuando el m´ etodo espec´ ıfico del sistema de ficheros lookup() llama a iget(). NULL. entonces debemos de volver a buscar el inodo en la tabla. Mientras el m´ etodo s op->read inode() est´ a leyendo el inodo del disco. . Si el inodo est´ a actualmente bloqueado. d_add(dentry.c:iput(inode): 1. Si el inodo es encontrado. la cual chequea si era la u ´ltima referencia al fichero. la cual realiza la mayor parte del trabajo. 4.c:dput() hace dentry iput() la cual nos brinda la vuelta a la memoria intermedia de inodos a trav´ es de iput(inode). se devuelve (despu´ es de incrementar la referencia por iget) el que se encontr´ o en la tabla hash y se destruye el nuevamente asignado. pas´ andole el puntero al sitio de la tabla hash donde deber´ ıa de ser insertado.

el camino es realizado por Linux trasparentemente. Los motivos hist´ oricos para esto son: 1. EL trabajo realizado por iput() en la u ´ltima referencia del inodo es bastante complejo. Sistemas de Archivos Virtuales (VFS) 39 3. 3.esto est´ a bien porque printk() nunca bloquea. El spinlock inode lock es tomado y i count es decrementado. Si no hay un m´ etodo s op->delete inode() registrado por el sistema de ficheros (ej. Como un ejemplo concreto le´ ı sistemas de ficheros BFS para Linux en modo s´ olo lectura en unas 10 horas. Si i nlink != 0 entonces chequeamos si hay otros inodos en el mismo cubo hash y si no hay ninguno. D´ ejanos considerar los pasos requeridos para implementar un sistema de ficheros bajo Linux.snapshot) entonces lo borramos de la lista de tipos y lo limpiamos/destruimos completamente. La interfaz para los escritores de sistemas de ficheros tiene que ser muy simple para que la gente pueda intentar hacer ingenier´ ıa inversa con los sistemas de ficheros existentes para escribir versiones de s´ olo lectura de ellos. entonces puede ser llamado absolutamente en cualquier contexto (¡incluso desde el manejador de interrupciones!). son borradas por medio de truncate all inode pages(&inode->i data). esta cuenta de referencia del dispositivo es borrada por bdput(inode->i bdev). En el mundo donde la gente a´ un usa sistemas operativos no Linux para proteger sus inversiones en el software legado. 2. ramfs) entonces llamamos a clear inode(inode).la mayor´ ıa no merecen existir pero s´ olo por compatibilidad con los existentes sistemas operativos no Linux.nr unused. el cual llama s op->clear inode() si est´ a registrado y si un inodo corresponde a un dispositivo de bloques. entonces si el inodo no est´ a sucio lo borramos desde su tipo de lista y lo a˜ nadimos a la lista inode unused incrementando inodes stat. Todo lo que se necesita es rellenar una . N´ otese que llamamos a printk() mientras mantenemos el spinlock inode lock .2 Registro/Desregistro de sistemas de Ficheros El n´ ucleo Linux suministra un mecanismo para los nuevos sistemas de ficheros para ser escritos con el m´ ınimo esfuerzo. 3. Si era la u ´ltima referencia activa entonces alg´ un trabajo necesita ser realizado. si hay alguna p´ agina de datos mantenida en la antememoria de p´ aginas para este inodo. Si hay inodos en el mismo cubo hash entonces los borramos de la lista de tipo y lo a˜ nadimos a la lista inode unused. Linux tiene que suministrar interoperabilidad para soportar una gran multitud de sistemas de ficheros diferentes . Si NO era la u ´ltima referencia a este inodo entonces simplemente chequeamos si hay muchas referencias a el y entonces i count puede urdir sobre los 32 bits asignados a el si por lo tanto podemos imprimir un mensaje de peligro y regresar. por lo tanto lo separaremos en una lista de si misma: 1. 2. La interfaz VFS es exportada. Si no hab´ ıa ning´ un inodo (NetApp . Entonces el m´ etodo espec´ ıfico del sistema de archivos s op->delete inode() es llamado. Si i nlink == 0 (ej. 4. Entonces el VFS de Linux hace muy f´ acil implementar sistemas de ficheros de s´ olo lectura: el 95% del trabajo est´ a por finalizar a˜ nadi´ endole un soporte total para escritura. El c´ odigo para implementar un sistema de ficheros puede ser un m´ odulo din´ amicamente cargado o est´ ar est´ aticamente enlazado en el n´ ucleo. el fichero fu´ e desenlazado mientras lo manten´ ıamos abierto) entonces el inodo es quitado de la tabla hash y de su lista de tipos. y entonces todos los sistemas de ficheros Linux pueden ser implementados como m´ odulos.3. el cual t´ ıpicamente borra la copia en disco del inodo. pero llev´ o varias semanas completarlo para tener un soporte total de escritura (e incluso hoy algunos puristas dicen que no est´ a completo porque no tiene soporte de compactaci´ on).

/* For kernel mount.h: struct file_system_type { const char *name. aparece en el fichero /proc/filesystems y es usado como clave para encontrar un sistema de ficheros por su nombre. void *. bfs_read_super). (obviamente) s´ olo puede haber un sistema de ficheros con un nombre dado.h> static struct super_block *bfs_read_super(struct super_block *. } module_init(init_bfs_fs) module_exit(exit_bfs_fs) Las macros module init()/module exit() aseguran que. pipefs. static int __init init_bfs_fs(void) { return register_filesystem(&bfs_fs_type). if it’s FS_SINGLE fs */ struct file_system_type * next.3. este mismo nombre es usado por el tipo de sistema de ficheros en mount(2). las funciones init bfs fs() y exit bfs fs() se convierten en init module() y cleanup module() respectivamente. ej. static DECLARE_FSTYPE_DEV(bfs_fs_type. struct vfsmount *kern_mnt. struct super_block *(*read_super) (struct super_block *. • fs flags: una o mas (ORed) de las banderas: FS REQUIRES DEV para sistemas de ficheros que s´ olo pueden ser montados como dispositivos de bloque. La struct file system type es declarada en include/linux/fs. } static void __exit exit_bfs_fs(void) { unregister_filesystem(&bfs_fs_type). int fs_flags. la operaci´ on de montaje (desde el espacio de usuario o . Sistemas de Archivos Virtuales (VFS) 40 estructura struct file system type y registrarla con el VFS usando la funci´ on register filesystem() como en el siguiente ejemplo de fs/bfs/inode. si no es suministrada. y deber´ ıa de ser u ´nico. Para los m´ odulos.c: #include <linux/module. int). los nombres de los punteros al espacio de direcciones del m´ odulo no son copiados: esto significa que cat /proc/filesystems puede fallar si el m´ odulo fue descargado pero el sistema de ficheros a´ un est´ a registrado. FS NOMOUNT para los sistemas de ficheros que no pueden ser montados desde el espacio de usuario por medio de la llamada al sistema mount(2): ellos pueden de todas formas ser montados internamente usando la interfaz kern mount(). • read super: un puntero a la funci´ on que lee el superbloque durante la operaci´ on de montaje. Esta funci´ on es requerida. si BFS est´ a est´ aticamente enlazado en el n´ ucleo el c´ odigo exit bfs fs() lo hace innecesario. cuando BFS es compilado como un m´ odulo.h> #include <linux/init. int). "bfs". void *. FS SINGLE para sistemas de ficheros que pueden tener s´ olo un superbloque. struct module *owner. }. Los campos anteriores son explicados de esta forma: • name: nombre humano le´ ıble.

Esto es establecido por kern mount() (POR HACER: kern mount() deber´ ıa de rechazar montar sistemas de ficheros si FS SINGLE no est´ a establecido). Lee el superbloque desde el dispositivo especificado a trav´ es del argumento sb->s dev. 5. • kern mnt: s´ olo para sistemas de ficheros FS SINGLE. Y cada dentry apunta a un inodo a trav´ es de dentry->d inode. • owner: puntero al m´ odulo que implementa este sistema de ficheros. etc. Cuando un proceso realiza la llamada al sistema open(2).. Inicializa sb->s op para apuntar a la estructura struct super block operations. tipicamente el read super() har´ a: 1. ”borrar inodo”.3 Administraci´ on de Descriptores de Ficheros Bajo Linux hay varios niveles de rodeos entre el descriptor de ficheros del usuario y la estructura de inodos del n´ ucleo.c). entonces tiene sentido usar breada() para planificar el leer bloque extra de forma as´ ıncrona. asignando el inodo raiz e inicializando cualquier informaci´ on privada del sistema de ficheros asociadas por esta instancia montada del sistema de ficheros. rwlock_t file_lock. el n´ ucleo devuelve un entero peque˜ no no negativo el cual puede ser usado para operaciones de E/S subsecuentes en este fichero.. El trabajo de la funci´ on read super() es la de rellenar los campos del superbloque. Por lo tanto. usando la funci´ on de la antememoria intermedia bread(). Sistemas de Archivos Virtuales (VFS) 41 desde el n´ ucleo) fallar´ a siempre excepto en el caso FS SINGLE donde fallar´ a en get sb single(). 2. No necesitas establecer esto manualmente puesto que la macro THIS MODULE lo hace autom´ aticamente. Si el sistema de ficheros est´ a enlazado est´ aticamente en el n´ ucleo entonces esto es NULL. 3.) 3. Si se anticipa a leer unos pocos m´ as bloques de metadatos inmediatamente subsecuentes. Cada tarea contiene un campo tsk->files el cual es un puntero a struct files struct definida en include/linux/sched. Verifica que el superbloque contiene el n´ umero m´ agico v´ alido y todo ”parece” correcto.3. 4. La lista est´ a protegida por el spinlock read-write file systems lock y las funciones register/unregister filesystem() modificada por el enlace y desenlace de la entrada de la lista.h: /* * Abre la estructura tabla del fichero */ struct files_struct { atomic_t count. intentando desreferenciar un puntero a NULL en fs type->kern mnt->mnt sb con (fs type->kern mnt = NULL). Si el sistema de ficheros no est´ a montado como s´ olo lectura entonces establece sb->s dirt a 1 y marca la antememoria conteniendo el superbloque como sucio (POR HACER: ¿porqu´ e hacemos esto? Yo lo hice en BFS porque MINIX lo hizo . Cada estructura de fichero apunta a dentry a trav´ es de file->f dentry. • next: enlaza a la cabecera de la lista simplemente enlazada file systems (ver fs/super. Asigna el inodo y dentry raiz usando d alloc root(). Esta estructura contiene las funciones espec´ ıficas del sistema de ficheros implementando las operaciones como ”leer inodo”. .

como la liberaci´ on de conjuntos de bloqueos. fd_set *open_fds.1b rt se~ nal para ser enviada en ES */ */ f_list. Esto puede ser visto en kernel/fork. int signum. f_reada. euid. Cuando el descriptor es cerrado un bit fd es limpiado en current->files->open fds y current->files->next fd es establecido igual a fd como una indicaci´ on para encontrar el primer descriptor sin usar la pr´ oxima vez que este proceso quiera abrir un fichero. f_owner. bajo la protecci´ on del spinlock files lock. El tsk->files puede ser compartido entre padre e hijo si el hilo hijo fue creado usando la llamada al sistema clone() con la bandera CLONE FILES establecida en los argumentos de las banderas de clone. Sistemas de Archivos Virtuales (VFS) 42 int max_fds. *f_dentry. La diferencia entre fput() y put filp() es que fput() hace m´ as trabajo usualmente necesitado para ficheros regulares. fd_set open_fds_init. *f_vfsmnt. incrementada por get file() (usualmente llamada por fget()) y decrementada por fput() y por put filp(). la estructura del fichero asignada para ´ el es instalada en el slot current->files->fd[fd] y un bit fd es establecido en el bitmap current->files->open fds. struct file ** fd. quita el fichero desde anon list y lo a˜ nade a la free list. int max_fdset. }. fd_set close_on_exec_init.h: struct fown_struct { int pid. Cuando un fichero es abierto. El file->count es una cuenta de referencia. f_mode. *f_op. Todo esto es realizado bajo la protecci´ on de escritura del spinlock read-write current->files->file lock. struct file { struct list_head struct dentry struct vfsmount struct file_operations atomic_t unsigned int mode_t loff_t unsigned long struct fown_struct unsigned int int /* pid o -pgrp donde SIGIO deber´ ıa de ser enviado /* uid/euid del proceso estableciendo el due~ no */ /* posix. decrementa la cuenta. mientras que put filp() es s´ olo para manipular las estructuras de tablas de ficheros. esto es. f_pos. liberaci´ on de dentry.c:copy files() (llamada por do fork()) el cual s´ olo incrementa el file->count si CLONE FILES est´ a establecido. f_flags. f_ralen. f_uid.4 Administraci´ on de estructuras de ficheros La estructura de ficheros es declarada en include/linux/fs. f_raend. f_count. uid_t uid. 3. int next_fd. f_error. struct file * fd_array[NR_OPEN_DEFAULT]. /* actualmente una matriz de descriptores de ficheros */ fd_set *close_on_exec. en vez de la copia usual de la tabla de descriptores de ficheros en la tradici´ on respetable en el tiempo de los cl´ asicos fork(2) de UNIX. f_rawin.3. . f_gid. }. f_ramax. etc.

entonces dentry open() (llamado por filp open()) enlaza el fichero en esta lista. esto es un valor de 64 bits. f uid. f rawin: para soportar readahead . f raend. f list: este campo enlaza la estructura del fichero con una (y s´ olo una) de las listas: a) sb->s files lista de todos los ficheros abiertos en este sistema de ficheros. fs/nfs/file. Sistemas de Archivos Virtuales (VFS) 43 unsigned long f_version. f ralen. f dentry: la dentry (entrada de directorio) correspondiente a este fichero. O TRUNC . f op: el puntero a file operations. f flags: banderas O XXX desde la llamada al sistema open(2) copiadas all´ ı (con ligeras modificaciones de filp open()) por dentry open() y despu´ es de limpiar O CREAT. La dentry es creada en tiempo de b´ usqueda de nombre y datos (nameidata) por open namei() (o m´ as bien path walk() la cual lo llama a ´ el) pero el campo actual file->f dentry es establecido por dentry open() para contener la dentry de esta forma encontrada.3. Esto es establecido por dentry open().muy complejo para ser discutido por mortales . }.c:kill fasync()). D´ ejanos mirar varios de los campos de struct file: 1. f reada. Bajo i386 es del tipo long long. Todas estas listas son protegidas por el spinlock files lock.c:generic file write(). f gid . 6. cuando la estructura del fichero es creada por get empty filp(). 5. f error: usado por el cliente NFS para devolver errores de escritura. 9.no hay sitio para almacenar estas banderas permanentemente ya que no pueden ser modificadas por las llamadas fcntl(2) F SETFL (o consultadas por F GETFL). f vfsmnt: el puntero a la estructura vfsmount del sistema de ficheros conteniendo el fichero. f ramax. usado por netfilter ipv4. f count: cuenta de referencia manipulada por get file/put filp/fput.) 10. Esto es establecido en . cuando una nueva estructura de ficheros es creada por get empty filp() es colocada en esta lista.c y chequeado en mm/filemap. 2. 8. f owner: due˜ no del archivo de E/S a recibir las modificaciones de E/S as´ ıncronas a trav´ es del mecanismo SIGIO (ver fs/fcntl. 12. El punto de conversi´ on es almacenar los accesos de lectura y escritura en bits separados. Miraremos los m´ etodos file operations en detalle m´ as tarde en esta secci´ on. f mode: una combinaci´ on de banderas del espacio de usuario y modos. el cual contiene varios m´ etodos que pueden ser llamados desde el fichero. 11.establece el identificador del usuario y el identificador del grupo del proceso que abri´ o el fichero. /* necesitado para este controlador tty. si el correspondiente inodo no es an´ onimo. Esto es copiado desde inode->i fop que es colocado aqu´ ı durante la b´ usqueda nameidata. y quiz´ as por otros */ void *private_data. pero es encontrado como una parte de la b´ usqueda de nameidata por open namei() (o m´ as bien path init() la cual lo llama a ´ el). O NOCTTY. por lo tanto uno los chequear´ ıa f´ acilmente como (f mode & FMODE WRITE) y (f mode & FMODE READ). Si el fichero es un conector. conteniendo las estructuras de ficheros sin utilizar. 4. f pos: la actual posici´ on en el fichero para la siguiente lectura o escritura.c:anon list. b) fs/file table. O EXCL. 3. establecido por dentry open(). 7.c:free list. c) fs/file table.

f version . 5. Ahora d´ ejanos mirar en la estructura file operations la cual contiene los m´ etodos que ser´ an llamados en los archivos. int. ssize_t (*read) (struct file *. 2. en cambio los controladores necesitan controlarlo en tiempo de apertura/liberaci´ on. D´ ejanos recalcar que es copiado desde inode->i fop donde es establecido por el m´ etodo s op->read inode().3. int datasync). filldir_t). struct file_lock *). la cual hace lo correcto (POR HACER: fuerza a todos aquellos que establecen a NULL actualmente a usar default llseek . size_t. 3.c:generic read dir() (la cual simplemente devuelve -EISDIR) para directorios aqu´ ı. struct file *.mecanismo de versionado para la invalidaci´ on de antememorias. 14. Los sistemas de ficheros pueden usar mm/filemap. llseek: implementa la llamada al sistema lseek(2). loff_t *). ioctl: implementa el controlador o los ioctls espec´ ıficos del sistema de ficheros. void *. ssize_t (*writev) (struct file *. N´ otese que los ioctls gen´ ericos de los ficheros como FIBMAP. int (*release) (struct inode *. Sistemas de Archivos Virtuales (VFS) 44 13. int (*fasync) (int. int (*ioctl) (struct inode *. los sistemas de ficheros puede felizmente ignorarlos porque sus cuentas de m´ odulos son controladas en el tiempo de montaje/desmontaje. int (*open) (struct inode *. loff_t *). readdir: usado por los sistema de ficheros.c:generic file read() para ficheros regulares y fs/read write. owner: un puntero al m´ odulo que es due˜ no del subsistema en cuesti´ on. struct file *). ssize_t (*write) (struct file *. los cuales pueden ser usados por los sistemas de ficheros (ej.c:default llseek(). write: implementa la llamada al sistema write(2). ssize_t (*readv) (struct file *. struct file *. const char *. Los controladores de dispositivos (en la presencia de devfs) pueden usar este campo para diferenciar entre m´ ultiples instancias. 1. unsigned long). unsigned int. 4. 7. 6. int (*lock) (struct file *.que es el camino por el que salvamos una if() en llseek()). S´ olo los controladores necesitan establecerlo a THIS MODULE. incrementado (usando un event global) cuando cambia f pos. int (*flush) (struct file *). int). struct file *). int (*mmap) (struct file *. loff_t *). struct vm_area_struct *). int). size_t. Se declara en include/linux/fs. int (*fsync) (struct file *. unsigned long. unsigned int (*poll) (struct file *. coda almacena las credenciales aqu´ ı) o por otros controladores de dispositivos. struct poll_table_struct *). poll: implementa las llamadas al sistema poll(2) y select(2). Los sistemas de ficheros pueden usar mm/filemap. en vez del cl´ asico n´ umero menor codificado en file->f dentry->d inode->i rdev. loff_t (*llseek) (struct file *. Usualmente es omitida y es usada fs/read write. Ignorado por los ficheros regulares e implementa las llamadas al sistema readdir(2) y getdents(2) para directorios. int (*readdir) (struct file *. const struct iovec *. char *. private data: datos privados para cada fichero. loff_t *). read: implementa la llamada al sistema read(2). FIONREAD son implementados por niveles m´ as altos y por lo tanto nunca leer´ an el m´ etodo f op->ioctl(). const struct iovec *.c:generic file write() para ficheros regulares e ignorarlo para directorios aqu´ ı.h: struct file_operations { struct module *owner. }. loff_t. FIGETBSZ. . unsigned long. struct dentry *.

writev: implementa la llamada al sistema writev(2). 14. si tiene ´ exito pero el c´ odigo de bloqueo estandart POSIX falla. Aunque definido como un entero de retorno. 16. no necesariamente el u ´ltimo (ver el m´ etodo release() a continuaci´ on). con el u ´ltimo argumento especificando cuando es fsync o fdatasync. ej.3. 9.. entonces nunca ser´ a desbloqueado en un nivel dependiente del sistema de ficheros. Los sistemas de ficheros raramente usan esto. s_blocksize_bits. declarado en include/linux/fs. Sistemas de Archivos Virtuales (VFS) 45 8. El sistema de ficheros Ext2 ignora el u ´ltimo argumento y realiza lo mismo para fsync(2) y fdatasync(2). 12. D´ ejanos mirar primero en struct super block. El u ´nico sistema de ficheros que lo utiliza es en un cliente NFS para pasar a disco todas las p´ aginas sucias. readv: implementa la llamada al sistema readv(2).5 Administraci´ on de Puntos de Montaje y Superbloque Bajo Linux.super block y vfsmount. fsync: mapea directamente a las llamadas al sistema fsync(2)/fdatasync(2). *dq_op. release:llamado por la u ´ltima close(2) de este fichero. s_magic. El u ´nico fallo aqu´ ı es porque es llamado antes por una porci´ on independiente del sistema de ficheros (posix lock file()). *s_root. *s_type. 11. . la informaci´ on sobre los sistemas de ficheros montados es mantenida en dos estructuras separadas .. flush: llamada en cada close(2) de este fichero. s_blocksize. s_flags. el valor de retorno es ignorado por VFS (ver fs/file table. /* Mantiene esto primero */ s_dev. Por lo menos no se realiza trabajo por VFS sobre esto. open: llamado en tiempo de open(2) por dentry open(). 15. El motivo para esto es que Linux permite montar el mismo sistema de ficheros (dispositivo de bloque) bajo m´ ultiples puntos de montaje. lock: parte del mecanismo de bloqueo de la regi´ on del fcntl(2) POSIX de la porci´ on espec´ ıfica del sistema de ficheros.h: struct super_block { struct list_head kdev_t unsigned long unsigned char unsigned char unsigned char struct file_system_type struct super_operations struct dquot_operations unsigned long unsigned long struct dentry wait_queue_head_t s_list. 3. coda intenta almacenar el fichero localmente en tiempo de apertura. fasync: este m´ etodo es llamado cuando cambia file->f flags & FASYNC. 10. mmap: implementa la llamada al sistema mmap(2). s_lock. s_wait. 13. lo cual significa que el mismo super block puede corresponder a m´ ultiples estructuras vfsmount. Los sistemas de ficheros pueden usar aqu´ ı generic file mmap para ficheros regulares e ignorarlo en los directorios. N´ otese que esto puede devolver un error el cual ser´ a retornado al espacio de usuario que realiz´ o la llamada al sistema close(2).c: fput()). *s_op. s_dirt. excepto el mapear el descriptor del fichero a una estructura de fichero (file = fget(fd)) y bajar/subir el sem´ aforo inode->i sem. esto es cuando file->f count llega a 0.

3. un superbloque est´ a actualmente bloqueado por 5. El m´ etodo read super() del sistema de ficheros no necesita ser establecido como VFS fs/super. entre 1 y 255 incluidos. */ struct semaphore s_vfs_rename_sem. todos los sistemas de archivos necesitan sb-private . Tal como construye un camino en el ´ arbol dcache * desde el fondo hasta arriba. */ struct semaphore s_nfsd_free_path_sem. Los sistemas de ficheros * no tienen trabajo alguno mirando en ´ el.. s blocksize bits: tama˜ no del bloque y log2(tama˜ no del bloque). 2. s dirt: establece cuando el superbloque est´ a modificado.. i) donde i es el primer bit no establecido en la matriz unnamed dev in use. Este sem´ aforo asegura que hay s´ olo * siempre un camino libre por sistema de ficheros. 6. N´ otese que * los ficheros no conectados (o otros no directorios) son * permitidos.. s type: puntero a struct file system type del sistema de ficheros correspondiente. struct quota_mount_options s_dquot. Para otros (llamados sistemas de ficheros an´ onimos) esto es un entero MKDEV(UNNAMED MAJOR.c:get unnamed dev()/put unnamed dev(). struct ext2_sb_info ext2_sb. y limpiado cuando es vuelto a ser escrito a disco. Has sido avisado.3. /* inodos sucios */ struct block_device *s_bdev. ...c:read super(). /* * El siguiente campo es *s´ olo* para VFS. struct list_head s_mounts. Esto es para los sistemas de ficheros FS REQUIRES DEV. Las diversos campos en la estructura super block son: 1. s blocksize. void *generic_sbp. union { /* vfsmount(s) de este */ /* Opciones Espec´ ıficas de Diskquota */ struct minix_sb_info minix_sb. } u. Sistemas de Archivos Virtuales (VFS) 46 struct list_head struct list_head s_dirty. . 4. n´ otese que no he dicho ”de todos los sistemas de ficheros montados” porque bajo Linux uno puede tener m´ ultiples instancias de un sistema de ficheros montados correspondientes a un superbloque simple. s lock: indica cuando lock super()/unlock super(). s_files. s dev: para sistemas de ficheros que requieren un bloque para ser montado en ´ el.. pero no los directorios no conectados. Ha sido sugerido muchas veces que los sistemas de ficheros an´ onimos no deber´ ıan de usar el campo s dev. quiz´ as exista durante alg´ un * tiempo un subcamino de dentrys que no est´ an conectados al * ´ arbol principal. Ver fs/super. }. s list: una lista doblemente enlazada de todos los superbloques activos. esto es la i dev del dispositivo de bloques. /* Truco */ /* El siguiente campo es usado por knfsd cuando convierte un * manejador de ficheros (basado en el n´ umero de inodo) en * una dentry.

Las operaciones de superbloque son descritas en la estructura super operations declarada en include/linux/fs. s dirty: una lista de todos los inodos sucios. s wait: cola de espera de los procesos esperando para que el superbloque sea desbloqueado. 12. s flags: banderas de superbloque. Sistemas de Archivos Virtuales (VFS) 47 lo establece para ti si el read super() que es espec´ ıfico del sistema de ficheros tiene ´ exito.c:get new inode(). 8.en otro caso get new inode() fallar´ a. ej. Es el trabajo del m´ etodo read super() del sistema de ficheros inicializar s op correctamente. read inode: lee el inodo desde el sistema de archivos. Recalcar que si un inodo est´ a sucio (inode->i state & I DIRTY) entonces su lista sucia espec´ ıfica del superbloque es enlazada a trav´ es de inode->i list. struct statfs *). void (*clear_inode) (struct inode *). Es trabajo de read super() leer el inodo raiz desde el disco y pas´ arselo a d alloc root() para asignar la dentry e instanciarlo. y se reinicializa a NULL si es que falla. 15. void (*put_inode) (struct inode *). 11. }. y desde iget4() (y por consiguiente iget()). Mientras el inodo est´ a siendo leido est´ a bloqueado (inode->i state = I LOCK). la cual contiene m´ etodos espec´ ıficos del sistema de ficheros para leer/escribir inodos. char *). int *. 9. Si un sistema de ficheros quiere usar iget() entonces read inode() debe de ser implementado .3. int (*remount_fs) (struct super_block *. s files: una lista de todos los ficheros abiertos en este superbloque. s mounts: una lista de todas las estructuras vfsmount.c:fs may remount ro() el cual va a trav´ es de la lista sb->s files y deniega el remontar si hay ficheros abiertos para escritura (file->f mode & FMODE WRITE) o ficheros con desenlaces pendientes (inode->i nlink == 0). s magic: n´ umero m´ agico del sistema de ficheros. dq op: operaciones de cuota de disco.h: struct super_operations { void (*read_inode) (struct inode *). s op: puntero a la estructura super operations. int). Util para decidir cu´ ando los sistemas de archivos pueden ser remontados como de s´ olo lectura. 10. 16. 7. 1. pipefs se monta a si mismo en ”pipe:” como su raiz en vez de ”/”. Es s´ olo llamado desde fs/inode. s root: dentry de la raiz del sistema de ficheros. una por cada instancia montada de este superbloque. void (*write_inode) (struct inode *. s dquot: m´ as miembros de diskquota. void (*delete_inode) (struct inode *). Algunos sistemas de ficheros dicen ”raiz” mejor que ”/” y por lo tanto usamos la funci´ on m´ as gen´ erica d alloc() para unir la dentry a un nombre. int (*statfs) (struct super_block *. 14. etc. 17. ver fs/file table. Usado por el sistema de ficheros de minix para diferenciar entre m´ ultiples tipos del mismo. void (*umount_begin) (struct super_block *). void (*put_super) (struct super_block *). s bdev: para FS REQUIRES DEV. void (*write_super) (struct super_block *). Cuando la . 13. esto apunta a la estructura block device describiendo el dispositivo en el que el sistema de ficheros est´ a montado.

la cuenta del sistema de ficheros es incrementada dos veces . El sistema de ficheros borra la copia en disco del inodo y llama a clear inode() en el inodo VFS para ”terminar con ´ el con el perjuicio extremo”. 6. no un puntero del usuario. write super: llamado cuando el superbloque necesita ser vuelto a escribir en el disco. put inode: llamado cuando la cuenta de referencia es decrementada. 3. Tambi´ en deber´ ıa de limpiar la bandera sb->s dirt. Esto no tiene nada que hacer con la idea del soporte de desmontaje forzado del nivel gen´ erico de VFS Por lo tanto. es el puntero del n´ ucleo. Los sistemas que atacan datos privados a la estructura del inodo (a trav´ es del campo generic ip) deben liberarse aqu´ ı.c:sys mount() que es justo un envoltorio que copia las opciones. Actualmente usado s´ olo por NFS. El controlador del sistema de ficheros es cargado si se necesita y la cuenta de referencia del m´ odulo es incrementada. put super: llamado en las u ´ltimas etapas de la llamada al sistema umount(2) para notificar al sistema de ficheros que cualquier informaci´ on mantenida por el sistema de ficheros sobre esa instancia tiene que ser liberada. todos los que est´ an esperando en inode->i wait son despertados. Similar a read inode() en que necesita localizar el bloque relevante en el disco e interactuar con la antememoria intermedia llamando a mark buffer dirty(bh). d´ ejanos mirar qu´ e pasa cuando montamos un sistema de ficheros en disco (FS REQUIRES DEV).3. 9. 8. inodos. el tipo del sistema de ficheros y el nombre del dispositivo para la funci´ on do mount(). 5. Los sistemas de ficheros que no implementan read inode() son ramfs y pipefs. Sistemas de Archivos Virtuales (VFS) 48 funci´ on regresa. 7. Este m´ etodo es llamado en los inodos sucios (aquellos marcados como sucios por mark inode dirty()) cuando el inodo necesita ser sincronizado individualmente o como parte de la actualizacion entera del sistema de ficheros. Si no est´ a implementada entonces statfs(2) fallar´ a con ENOSYS. 2. 4. la cual realiza el trabajo real: 1.una vez por do mount() llamando a get fs type() y otra vez por get sb dev() llamando a get filesystem() si read super() tuvo ´ exito. N´ otese que el puntero a struct statfs pasado como argumento. La implementaci´ on de la llamada al sistema mount(2) est´ a en fs/super. El trabajo del m´ etodo read inode() del sistema de ficheros es localizar el bloque del disco que contiene el inodo a ser le´ ıdo y usar la funcion de la antememoria intermedia bread() para leerlo e inicializar varios campos de la estructura de inodos. delete inode: llamado cuando inode->i count y inode->i nlink llegan a 0. write inode: escribe el inodo de vuelta al disco. por ejemplo el inode->i op y inode->i fop para que los niveles VFS conozcan qu´ e operaciones pueden ser efectuadas en el inodo o fichero correspondiente. N´ otese que durante la operaci´ on de montaje. Deber´ ıa de encontrar el bloque conteniendo el superbloque (usualmente mantenido en el ´ area sb-private) y mark buffer dirty(bh). T´ ıpicamente esto brelse() el bloque conteniendo el superbloque y kfree() cualesquiera bitmaps asignados para bloques libres. remount fs: llamado cuando el sistema de ficheros est´ a siendo remontado. etc. por lo tanto puede ser lo mejor para asegurarse que nada mantiene al sistema de ficheros ocupado. umount begin: llamado durante el desmontaje forzado para notificarlo al sistema de ficheros de antemano. statfs: implementa las llamadas al sistema fstatfs(2)/statfs(2). Por ejemplo. El primer incremento es para prevenir la . ramfs tiene su propia funci´ on de generaci´ on de inodos ramfs get inode() con todas las operaciones de inodos llam´ andola cuando se necesita. por lo tanto no necesitamos hacer ninguna E/S al espacio de usuario. clear inode: llamado desde el nivel VFS clear inode(). 10.

El campo mnt list nos permite encontrar todas las instancias para todos los superbloques a lo largo del sistema. err = PTR_ERR(pipe_mnt). que en nuestro caso. } module_init(init_pipe_fs) module_exit(exit_pipe_fs) El sistema de ficheros es del tipo FS NOMOUNT|FS SINGLE. do mount() decrementa la cuenta antes de regresar. Si todo va vien. "pipefs". pipefs_read_super. El u ´nico fallo en esta funci´ on es que si kern mount() falla (ej. Sistemas de Archivos Virtuales (VFS) 49 descarga del m´ odulo mientras estamos dentro del m´ etodo read super(). porque kmalloc() fall´ o en add vfsmnt()) entonces el sistema de ficheros es dejado como registrado pero la inicializaci´ on del m´ odulo falla. la cual obtiene la referencia al dispositivo de bloques e interact´ ua con el m´ etodo read super() del sistema de ficheros para rellenar el superbloque. 2. El campo vfsmount de mnt instances nos permite encontrar todas las instancias montadas en el mismo superbloque que este. despu´ es de todo.6 Ejemplo de un Sistema de Ficheros Virtual: pipefs Como un ejemplo simple del sistema de ficheros de Linux que no requiere un dispositivo de bloque para montar. 3. fs type->fs flags & FS REQUIRES DEV es verdad.3. la cuenta s´ olo crece en 1 despu´ es de cada montaje. y el segundo incremento es para indicar que el m´ odulo est´ a en uso por esta instancia montada. El campo mnt sb apunta a este superbloque y mnt root tiene una nueva referencia a la dentry sb->s root. El pre´ ambulo del sistema de ficheros es bastante directo y requiere una peque˜ na explicaci´ on: static DECLARE_FSTYPE(pipe_fs_type. kern_umount(pipe_mnt). Desde. } return err. } static void __exit exit_pipe_fs(void) { unregister_filesystem(&pipe_fs_type). Esto causar´ a que cat . Obviamente. Una nueva estructura vfsmount es asignada y enlazada a la lista sb->s mounts y a la lista global vfsmntlist. la estructura super block es inicializada y tenemos una referencia extra al m´ odulo del sistema de ficheros y una referencia al dispositivo de bloques subyacente.c. if (!err) { pipe_mnt = kern_mount(&pipe_fs_type). por lo tanto. lo cual es exactamente lo que pasa en init pipe fs(). FS_NOMOUNT|FS_SINGLE). 3. static int __init init_pipe_fs(void) { int err = register_filesystem(&pipe_fs_type). El fichero FS SINGLE tambi´ en significa que debe de ser montado a trav´ es de kern mount() despu´ es de que haya sido registrado con ´ exito a trav´ es de register filesystem(). el superbloque es inicializado por una llamada a get sb bdev(). d´ ejanos considerar pipefs desde fs/pipe. lo que significa que no puede ser montado desde el espacio de usuario y s´ olo puede haber uno en el sistema. if (!IS_ERR(pipe_mnt)) err = 0.

x trabajan por pura suerte y parar´ an de trabajar tan pronto como tu retoques ligeramente los campos en el inodo. inode->i sb es establecido al superbloque de pipefs pipe mnt->mnt sb. es invocada.3. Para este inodo. Ahora que el sistema de ficheros est´ a registrado y montado dentro del n´ ucleo podemos usarlo. Si no se encuentra dicho superbloque vac´ ıo entonces uno nuevo es asignado usando kmalloc() con la prioridad GFP USER. esto es s->s dev == 0. Sistemas de Archivos Virtuales (VFS) 50 /proc/filesystems falle (justamente acabo de enviar un parche a Linus mencion´ andole que esto no es un fallo real hoy en d´ ıa porque pipefs no puede ser compilado como m´ odulo. Por lo tanto. El archivo /proc/filesystems deber´ ıa realmente de ser capaz de soportar todas las nuevas banderas FS (y yo he hecho un parche que lo hace) pero no puede ser realizado porque har´ a fallar a todas las aplicaciones de usuario que lo utilicen. . lo cual en C es muy malo y puede trabajar s´ olo con pura suerte. s´ ı. Un m´ etodo espec´ ıfico del sistema de ficheros pipe fs type->read super(). Un nuevo n´ umero de dispositvo sin nombre (an´ onimo) es asignado estableciendo un bit en el bitmap unnamed dev in use. El resultado de register filesystem() es que pipe fs type es enlazado en la lista file systems. "none") la cual asigna una nueva estructura vfsmount y la enlaza en vfsmntlist y sb->s mounts. deber´ ıa de ser escrito desde el punto de vista de que en el futuro pudiera ser modularizado). La escritura en la cara de lectura devuelve un EBADF y lo mismo si se lee en la cara de escritura. pero el trabajo real es realizado por un funci´ on portable fs/pipe. las operaciones del archivo i fop son establecidas a rdwr pipe fops y el n´ umero de lectores y escritores (mantenidos en inode->i pipe) es establecido a 1. A pesar de que los interfaces del n´ ucleo Linux cambian cada minuto (s´ olo para mejor) cuando se refiere a la compatibilidad del espacio de usuario. sb->s root.2. El resultado de kern mount() es que: 1. dos caras del mismo fichero tienes diferentes operaciones file->f op . Linux es un sistema operativo muy conservador que permite que muchas aplicaciones sean usadas durante un largo periodo de tiempo sin ser recompiladas. Bajo Linux. si no hay m´ as bits entonces kern mount() fallar´ a con EMFILE. y establece sb->s op para ser &pipefs ops. Entonces kern mount() llama a add vfsmnt(NULL.la read pipe fops y write pipe fops respectivamente. los n´ ucleos 2. por lo tanto empieza fallando. El punto de entrada en el sistema de ficheros pipefs es la llamada al sistema pipe(2). la cual asigna el inodo y la dentry raiz sb->s root. La interacci´ on con pipefs sucede cuando do pipe() llama a get pipe inode() para asignar un nuevo inodo pipefs. D´ ejanos mirar entonces en do pipe(). por lo tanto uno puede leer /proc/filesystems y encontrar la entrada ”pipefs” all´ ı con la bandera ”nodev” indicando que FS REQUIRES DEV no fue establecida. El motivo por el que el valor de retorno de kern mount() es una estructura vfsmount es porque incluso los sistemas de ficheros FS SINGLE pueden ser montados m´ ultiples veces y por lo tanto sus mnt->mnt sb deber´ ıan apuntar a la misma cosa. El pipe fs type->kern mnt es establecido a esta nueva estructura vfsmount y es devuelta. Una nueva estructura superbloque es asignada por medio de get empty super(). 2. Cada llamada al sistema pipe(2) incrementa una cuenta de referencia en la instancia de montaje pipe mnt. esto es pipefs read super(). uno puede modificar el par´ ametro ajustable /proc/sys/fs/super-max. 5. los pipes no son sim´ etricos (pipes STREAM o bidireccionales) esto es. implementada por una funci´ on dependiente de la arquitectura sys pipe(). 3. El n´ umero m´ aximo de superbloques en el sistema es chequeado en get empty super(). La funci´ on get empty super() camina a trav´ es de las cabeceras de las lista de superbloques por super block y busca una entrada vac´ ıa.c:do pipe(). El motivo por el que hay un campo de inodos separado i pipe en vez de mantenerlo en la uni´ on fs-private es que pipes y FIFOs comparten el mismo c´ odigo y los FIFOs puede existir en otros sistemas de ficheros los cuales usan otros caminos de acceso con la misma uni´ on. que ser´ ıa tonto devolverla desde m´ ultiples llamadas a kern mount(). 4.

set blocksize(s->s dev. 3. Si el inodo raiz fue leido correctamente. lo cual significa que podemos desde este punto llamar a la memoria intermedia de inodos a trav´ es de iget().¡el sistema de ficheros debe de tener un inodo raiz v´ alido en tiempo de montaje! 5. La funci´ on de inicializaci´ on del m´ odulo registra el sistema de ficheros con VFS y la funci´ on de limpieza (s´ olo presente cuando BFS est´ a configurada para ser un m´ odulo) lo desregistra. podemos proceder a montarlo.c: static DECLARE_FSTYPE_DEV(bfs_fs_type.7 Ejemplo de Sistema de Ficheros de Disco: BFS Como un simple ejemplo de un sistema de ficheros Linux en disco. El realiza lo siguiente: 1. debemos inicializar unas pocas cosas. bh = bread(dev. BFS BSIZE): desde que nosotros vamos a interactuar con la capa de dispositivos de bloque a trav´ es de la antememoria intermedia. } module_init(init_bfs_fs) module_exit(exit_bfs_fs) Una declaracion especial de la macro del sistema de ficheros DECLARE FSTYPE DEV() es usada. d´ ejanos considerar BFS.c:bfs read super().si sbh). establecer el tama˜ no del bloque y tambi´ en informar a VFS a trav´ es de los campos s->s blocksize y s->s blocksize bits. 4. Entonces asignamos el bitmap del inodo kmalloc(GFP KERNEL) y limpiamos todos sus bits a 0. lo cual resulta en la invocaci´ on de s op->read inode(). 0. static int __init init_bfs_fs(void) { return register_filesystem(&bfs_fs_type). El pre´ ambulo del m´ odulo de BFS est´ a en fs/bfs/inode. 2. los cuales estableceremos a 1 para indicar que nunca deberemos asignar los inodos 0 y 1. esto es. Esto encuentra el bloque que contiene el inodo especificado (por inode->i ino y inode->i dev) y lo lee.bfs sb. entonces asignamos una dentry con el nombre / (como convirti´ endolo en raiz) y lo instanciamos con este inodo. "bfs". lo cual invocar´ a al m´ etodo fs type->read super() que es implementado en fs/bfs/inode. } static void __exit exit_bfs_fs(void) { unregister_filesystem(&bfs_fs_type).. bfs_read_super). Este bloque es el superbloque del sistema. excepto los dos primeros. . El superbloque es validado contra el n´ umero BFS MAGIC y. Con el sistema de ficheros registrado. si es v´ alido. es almacenado en el campo sb-private s->su sbh (el cual es realmente s->u. Si fallamos al obtener el inodo raiz entonces liberamos el bitmap de inodos y descargaremos la antememoria de superbloque a la antememoria intermedia y devolveremos NULL. la cual establece el fs type->flags a FS REQUIRES DEV para significar que BFS requiere un dispositivo de bloque real para ser montado.3. El inodo 2 es la raiz y el correspondiente bit ser´ a establecido a 1 unas pocas lineas despu´ es de cualquier forma . BFS BSIZE): leemos el bloque 0 del dispositivo a trav´ es de s->s dev. Entonces inicializamos s->s op. Sistemas de Archivos Virtuales (VFS) 51 3.

trabajan en una forma similar. Leyendo inodos.uno no puede enlazar entre m´ ultiples instancias de un sistema de ficheros (o. Ahora vamos a trav´ es de todos los inodos del sistema de ficheros y los leemos en orden a establecer los bits correspondientes en nuestro bitmap interno de inodos y tambi´ en calculamos otros par´ ametros internos como el desplazamiento del u ´ltimo inodo y el comienzo/final del u ´ltimo fichero. lo rechazamos con un error EPERM.mnt != nd. Si old nd. 8. cuando lo encuentra.3. la cual chequea si podemos crear una nueva entrada en el directorio e invoca el m´ etodo dir->i op->link(). D´ ejanos examinar el camino de c´ odigo de la llamada al sistema link(2). Estos nombres son convertidos a datos usando path init()/path walk() interactuando con dcache. esto es. escribe en el par nombre/inodo en el bloque correspondiente y lo marca como sucio (a una prioridad no-superbloque).c:sys link(): 1.c:bfs link(). d´ ejanos examinar qu´ e pasa cuando hacemos una E/S en el sistema de ficheros. chequeamos si estamos intentando enlazar un directorio. actualizamos inode->i ctime y marcamos este inodo como sucio a la vez que instanciamos la nueva dentry con el inodo.mnt entonces ”enlace a trav´ es de dispositvos” EXDEV es devuelto . Ya hemos examinado c´ omo los inodos son leidos cuando iget() es llamado y c´ omo son quitados en iput(). Este es el mismo comportamiento que el est´ andar (ext2). y si es as´ ı. lo hice porque lo hac´ ıa minix read super() pero ni minix ni BFS parecen modificar el superbloque en el read super()). 6. 5. Otras operaciones de inodos relacionadas como unlink()/rename() etc. inode->i op y inode->i fop. en Linux esto se traduce en . VFS obtiene la referencia al m´ odulo del sistema de ficheros a trav´ es de la llamada a get filesystem(fs type) en fs/super.c:read super(). 8. por lo tanto regresamos atr´ as a este superbloque inicializado para el llamante en el nivel VFS.no mantenemos una referencia a ´ el m´ as tiempo del necesario. Despu´ es de que la funci´ on read super() regrese con ´ exito. por lo tanto no se gana mucho examin´ andolas a todas ellas en detalle. Si hemos a˜ nadido con ´ exito la entrada de directorio entonces no hay forma de fallar la operaci´ on y por lo tanto incrementamos inode->i nlink. fs/super. en particular entre sistemas de ficheros). abriendo un fichero propagar´ a inode->i fop en file->f op. Ahora. Todo est´ a bi´ en. . 7. Sistemas de Archivos Virtuales (VFS) 52 6. 7. Dentro de bfs link(). configura entre otras cosas. marcamos la memoria intermedia del superbloque como sucio y establecemos la bandera s->s dirt (POR HACER: ¿Porqu´ e hago esto? Originalmente. Los nombres del espacio de usuario son copiados en el espacio del n´ ucleo por medio de la funci´ on getname() la cual realiza el chequeo de errores.c:get sb bdev() y una referencia al dispositivo de bloques. Si el sistema de ficheros no fue montado como de s´ olo lectura.uno no puede enlazar entre sistemas de ficheros. La implementaci´ on de la llamada al sistema est´ a en fs/namei. El resultado es almacenado en las estructuras old nd y nd. Una nueva dentry es creada correspondiente a nd por lookup create() . 2. 3. Una funci´ on gen´ erica vfs link() es llamada. Intentamos a˜ nadir una nueva entrada de directorio al directorio especificado por la funci´ on de ayuda bfs add entry() la cual va a trav´ es de todas las entradas buscando un slot sin usar (de->ino == 0) y. 4. que nos trae atr´ as a la funci´ on espec´ ıfica del sistema de ficheros fs/bfs/dir. Cada inodo que leemos es devuelto atr´ as a la memoria intermedia de inodos a trav´ es de iput() .

UnixWare. por ejemplo a˜ nadiendo una STICKY TIMEOUT a current->personality hacemos que la llamada al sistema select(2) preserve el valor del u ´ltimo argumento (timeout) en vez de almacenar el tiempo no dormido. podemos cambiar la forma en la que el sistema operativo trata ciertas llamadas al sistema. Registrando un nuevo dominio de ejecuci´ on define un ”manejador lcall7” y un mapa de conversi´ on de n´ umero . tres bytes altos . Algunos programas defectuosos conf´ ıan en sistemas operativos defectuosos (no Linux) y por lo tanto suministra una forma para emular fallos en casos donde el c´ odigo fuente no est´ a disponible y por lo tanto los fallos no pueden ser arreglados. M´ as interesantemente. Retorna 0 si tiene ´ exito. Obviamente. los binarios pueden ser almacenados en formatos diferentes y la respuesta del sistema operativo a los programas a trav´ es de las llamadas al sistema pueden desviarla de la norma (la norma es el comportamiento de Linux) tal como es requerido.c:get exec domain list(). en orden a emular los formatos encontrados en otros tipos de UNIX (COFF. est´ a disponible leyendo el archivo /proc/execdomains. mientras haciendo cat /proc/filesystems llama fs/exec domain. 2. Solaris. El dominio de ejecuci´ on es un rango contiguo de personalidades implementadas por un m´ odulo simple.4.personalidad propia.emulaci´ on de fallos: STICKY TIMEOUTS. el comportamiento de esta llamada al sistema no depende de la personalidad. comparado con el 2. • int unregister exec domain(struct exec domain *): desregistra el dominio de ejecuci´ on desenlaz´ andolo desde la lista exec domains. WHOLE SECONDS. la cual establece la actual personalidad del proceso o devuelve el valor de current->personality si el argumento es establecido a una personalidad imposible. etc). Los dominios de ejecuci´ on. etc. pueden ser implementados como m´ odulos din´ amicamente cargados. distinto de cero en caso de fallo.8 Dominios de Ejecuci´ on y Formatos Binarios Linux soporta la carga de aplicaciones binarias de usuario desde disco. Los dominios de ejecuci´ on son implementados en kernel/exec domain.x. Cada tarea Linux tiene una personalidad almacenada en su task struct (p->personality).2. Cambiando la personalidad. Sistemas de Archivos Virtuales (VFS) 53 3. el cual necesita s´ olo acceso de lectura a la lista. La interfaz del n´ ucleo para el registro de dominios de ejecuci´ on consiste en dos funciones: • int register exec domain(struct exec domain *): registra el dominio de ejecuci´ on enlaz´ andolo en una lista simplemente enlazada exec domains bajo la protecci´ on de escritura del spinlock read-write exec domains lock. El motivo por el que exec domains lock es read-write es que s´ olo las peticiones de registro y desregistro modifican la lista. pero a veces es posible implementar personalidades ”cerradas” en un m´ odulo simple sin muchos condicionantes. etc) y tambi´ en emular el comportamiento de las llamadas al sistema de otros tipos (Solaris. a lo largo del rango de personalidades que soportan. Las personalidades actualmente existentes (en el n´ ucleo oficial o en el parche a˜ nadido) incluyen soporte para FreeBSD. Devuelve 0 si tiene ´ exito. El valor de current->personality es dividido en dos partes: 1.c y fueron completamente reescritos para el n´ ucleo 2. UnixWare. OpenServer y algunos otros sistemas operativos populares. otra vez usando el spinlock exec domains lock en modo de escritura. excepto el PER LINUX. La lista de dominios de ejecuci´ on actualmente soportada por el n´ ucleo.3. Usualmente un dominio de ejecuci´ on simple implementa una personalidad simple. byte bajo . La interfaz de usuario es a trav´ es de la llamada al sistema personality(2). un n´ umero u ´nico. Esto es para lo que son los dominios de ejecuci´ on y los formatos binarios.

en C. c´ odificar y forzar a un programa funcionando a cualquier personalidad.c:trap init().4. Una vez que la personalidad (y entonces current->exec domain) es conocida. Porque la memoria intermedia de p´ aginas Linux est´ a cerradamente emparejada a la notaci´ on del espacio de direcciones. Tal como con exec domains lock. struct pt_regs * regs) { abi_dispatch(regs.una memoria intermedia de p´ aginas f´ ısicas.S:lcall7 obtiene el puntero a exec domain desde current y entonces un desplazamiento del manejador lcall7 con el exec domain (el cual es codificado fuertemente como 4 en c´ odigo ensamblador. y entonces usa una estructura address space (explicada posteriormente) como primer par´ ametro. La personalidad del proceso est´ a determinada por el formato binario cargado por el m´ etodo del correspondiente formato load binary() usando algunas heur´ ısticas. Actualmente.S porque fue preparado en arch/i386/kernel/traps. la memoria intermedia de p´ aginas de Linux est´ a dise˜ nada para ser m´ as gen´ erica. uno primero marca el binario usando la utilidad elfmark(1). el parche ABI extiende este concepto a los dominios de ejecuci´ on para incluir informaci´ on extra (como opciones de conector. en un simple paso. En el mundo UNIX el concepto de memoria intermedia de p´ aginas se convirti´ o popular con la introduci´ on de SVR4 UNIX. 4 Memoria Intermedia de P´ aginas Linux En este cap´ ıtulo describimos la memoria intermedia de p´ aginas de Linux 2. el binfmt lock es tomado para leer en la mayor´ ıa de las ocasiones excepto para el registro/desregistro de los formatos binarios. donde reemplaz´ o a la antememoria intermedia para las operaciones de E/S. El m´ etodo load shlib() es usado s´ olo por la vieja llamada al sistema uselib(2) mientras que el m´ etodo load binary() es llamada por el search binary handler() desde do execve() el cual implementa la llamada al sistema execve(2).so. el cual es detectado en tiempo de carga del ELF y current->personality es establecido a PER SVR4.c y es protegida por un cierre read-write binfmt lock.como sugiere el nombre .1 o /usr/lib/libc. Mientras la memoria intermedia de p´ aginas de SVR4 es s´ olamente usada como memoria intermedia de datos del sistema de ficheros y estos usan la estructura vnode y un desplazamiento dentro del fichero como par´ ametros hash. para determinar los binarios UnixWare7. familia de direcciones y mapas de n´ umeros de errores). es usado y la personalidad es establecida a PER SVR4. esto es.so. Por lo tanto. Registrando un nuevo formato binario intensifica la llamada al sistema execve(2) con nuevas funciones load binary()/load shlib(). D´ ejanos asumir que un proceso realiza una llamada al sistema por medio de la instrucci´ on puerta lcall7. Al igual que la habilidad para core dump(). tipos de conector. se parecer´ ıa a esto: static void UW7_lcall7(int segment.4. la cual establece la cabecera de ELF e flags al valor m´ agico 0x314B4455. Uno podr´ ıa escribir un peque˜ no programa de utilidad que usara las capacidades del ptrace(2) de Linux para. 1). una lista simplemente enlazada de formatos es definida en fs/exec. necesitar´ as como m´ ınimo un conocimiento previo del adress spaces para entender la forma en la que trabaja . Despu´ es de la apropiada conversi´ on de la pila. Por ejemplo.1 para indicar un binario SVR4. Memoria Intermedia de P´ aginas Linux 54 de se˜ nales. Los formatos binarios son implementados de una forma similar. las llamadas al sistema son manejadas como sigue. &uw7_funcs[regs->eax & 0xff]. por lo tanto no puedes desplazar el campo handler a trav´ es de la declaraci´ on en C de struct exec domain) y salta a ´ el. Esto transfiere el control a ENTRY(lcall7) de arch/i386/kernel/entry. Si esta heur´ ıstica falla entonces una m´ as gen´ erica como el tratamiento de los caminos del int´ erprete ELF como /usr/lib/ld. } donde abi dispatch() es un envoltorio sobre la tabla de punteros de funci´ on que implementa las llamadas al sistema de esta personalidad uw7 funcs. entry. La memoria intermedia de p´ aginas es .

struct vm_area_struct *i_mmap. struct page *. .4. pero en la pr´ actica necesitamos tambi´ en mirar en >prepare write y ->commit write. Para una vista b´ asica del principio de address spaces (y de la memoria intermedia de p´ aginas) necesitamos hechar una mirada a ->writepage y ->readpage. por lo tanto hecharemos un severo vistazo a la estructura address space operations. sucias y bloqueadas pertenecientes a este address space.quiz´ as sea NULL. dirty pages. definida en la misma cabecera: struct address_space_operations { int (*writepage)(struct page *). suministra una buena forma para entenderlas. unsigned long nrpages. en el caso del swapper (intercambiador) address space (mm/swap state. dirty pages y locked pages son listas doblemente enlazadas de p´ aginas limpias. locked pages y nrpages es obvio. struct vm_area_struct *i_mmap_shared. Un address space es alg´ un tipo de software MMU que mapea todas las p´ aginas de un objeto (ej. nrpages es el n´ umero total de p´ aginas en este address space. hash = page hash(inode->i mapping. struct list_head dirty_pages. struct page *). Su uso en el camino de las E/S de los datos de los sistemas de ficheros. Probablemente supongas que los m´ etodos address space operations lo hacen en virtud de sus nombres solamente. index). struct page *. unsigned). Como la mayor´ ıa de otros sistemas operativos del estilo UNIX. int (*prepare_write)(struct file *. a ops define los m´ etodos de este objeto y host es un puntero perteneciente a este inodo address space .). int (*bmap)(struct address_space *. struct inode *host. struct address_space_operations *a_ops. pero ser´ an leidos/escritos desde/a la memoria intermedia de p´ aginas cuando sea posible. long).h como: struct address_space { struct list_head clean_pages. unsigned. Entonces testeamos cuando la p´ agina actualmente existe. unsigned. inodo) a otro concurrentemente (tipicamente bloques f´ ısicos de disco). }. struct list_head locked_pages. unsigned). }. La memoria intermedia de p´ aginas tiene que obtener datos desde el sistema de ficheros actual de bajo nivel en el caso de que el usuario quiera leer desde una p´ agina que todav´ ıa no est´ a en memoria. Linux tiene unas operaciones gen´ ericas de ficheros (un subconjunto de las operaciones vnode SYSVish) para los datos E/S a trav´ es de la memoria intermedia de p´ aginas. Esto significa que los datos no interact´ uan directamente con el sistema de ficheros en read/write/mmap. los m´ etodos gen´ ericos primero intentar´ an encontrar una p´ agina que corresponda con la pareja buscada de inodo/´ ındice. int (*readpage)(struct file *. requieren hacer alguna explicaci´ on. El uso de clean pages. spinlock_t i_shared_lock. int (*sync_page)(struct page *). s´ olo necesitamos mirar unos pocos de estos campos: clean pages. La estructura address space est´ a definida en include/linux/fs. por lo lejos del m´ as com´ un camino a trav´ es de la memoria intermedia de p´ aginas.c. no obstante. int (*commit_write)(struct file *. Memoria Intermedia de P´ aginas Linux 55 la memoria intermedia de p´ aginas. Para entender la forma en la que address spaces trabaja. o escribir datos al disco en el caso de que la memoria sea insuficiente. En el camino de lectura. ej.

entonces llamamos al m´ etodo ->prepare write address space. La secci´ on 5. esto no realiza todo el trabajo que tiene una granularidad m´ as peque˜ na que PAGE SIZE. mapping->a ops->prepare write(file. El hilo del n´ ucleo bdflush que est´ a intentando liberar p´ aginas. El caso mmap es muy simple. mapping->a ops->commit write(file. rellenando un puntero page->buffers con buffer heads.4. Tal como probablemente has visto ->prepare write y ->commit write son fundamentalmente diferentes de ->readpage y ->writepage. &cached page). bytes). offset+bytes). Debido a la carencia de un bitmap de validez en la p´ agina de estructuras. y la a˜ nadimos al hash de la memoria intermedia de p´ aginas. Para cada p´ agina que el usuario escribe.3 (shared memory) respectivamente. error = mapping->a ops->readpage(file. index. find page nolock(inode->i mapping. y es usada de forma muy difundida en el actual n´ ucleo. Mecanismos IPC 56 hash = page hash(inode->i mapping. page = grab cache page(mapping. tenemos b´ asicamente que hacer lo siguiente: (para el c´ odigo completo ver mm/filemap. Para escribir en el sistema de archivos existen dos formas: una para mapeos escribibles (mmap) y otra para la familia de llamadas al sistema write(2).1 (semaphores). SetPageDirty(page).c:generic file write()). el subsistema VM marca la p´ agina como sucia. Por lo tanto intentamos encontrar la p´ agina ordenada o asignar una nueva. offset. offset+bytes). page. El segundo camino de escritura es mucho m´ as complicado. asignamos una nueva p´ agina. hash). index. que ser´ a usado en try to free buffers (fs/buffers. *hash). Despu´ es de que la p´ agina haya sido hashed (ordenada) utilizamos la operaci´ on ->readpage address space para en este instante rellenar la p´ agina con datos (el fichero es una instancia abierta del inodo). 5 Mecanismos IPC Este cap´ ıtulo describe los mecanismos IPC sem´ aforo. por lo tanto ser´ a el que primero discutamos. la primero es usar la antememoria intermedia de Linux para retrasar la E/S f´ ısica. page = page cache alloc().4 (last) describe un conjunto de funciones comunes y estructuras de datos que son compartidas por los tres mecanismos. Cuando un usuario modifica los mapas. buf. mapping. La otra forma justamente establece la p´ agina como sucia y conf´ ıa en que >writepage realice todo el trabajo. page). y 5.5. El m´ etodo ->writepage tiene ahora que escribir el contenido de las p´ aginas de vuelta al disco y liberar la p´ agina. memoria compartida y cola de mensajes tal como han sido implementados en el n´ ucleo Linux 2. Cuando no existe. Finalmente podemos copiar los datos al espacio de usuario. como actividad en segundo plano o porque no hay suficiente memoria.2 (message queues). . copiamos la antememoria del usuario a la memoria del n´ ucleo y finalmente llamamos al m´ etodo ->commit write. page. offset. Est´ a organizado en 4 secciones. intentar´ a llamar a ->writepage en las p´ aginas que est´ an explicitamente marcadas como sucias. Hay dos (¿o m´ as?) formas para manejar esto. Las tres primeras secciones cubren las interfaces y las funciones de soporte para 5. 5. porque ellas no s´ olo son llamadas cuando la E/S f´ ısica se quiere actualizar sino que son llamadas cada vez que el usuario modifica el fichero. page = index.c) para pedir E/S una vez que no haya suficientemente memoria. add to page cache(page. index). copy from user(kaddr+offset.

1. Despu´ es de copiar los datos de las operaciones de los sem´ aforos.1.3 (GETNCNT). Si SEM UNDO estaba asertado para alguna de las operaciones del sem´ aforo. y una bandera alter es establecida si alguno de los valores de los sem´ aforos es modificado (esto es. En el caso donde un valor de llave es suministrado por un conjunto de sem´ aforos existentes. Si no se encuentra ninguna estructura deshacer para este conjunto de sem´ aforos entonces 5.3 (semctl nolock()) es llamado para realizar las funciones necesarias.3 (semctl main()) es llamado para realizar las funciones necesarias.3 (SEM STAT). es asignad una antememoria m´ as grande.1.4.3 (GETALL).3 (semctl down()) es llamada para realizar las funciones necesarias. 5.sem) En el caso donde un nuevo conjunto de sem´ aforos deben de ser creados. entonces la lista para deshacer la actual tarea es buscada por una estructura deshacer asociada con este conjunto de sem´ aforos. 5.3 (SETALL). La funci´ on 5.1. Para eliminar esta confusi´ on el t´ ermino ”sem´ aforo del n´ ucleo” ser´ a usado en referencia a los sem´ aforos del n´ ucleo. 5. 5. La ID del nuevo conjunto es retornada al llamante.2 sys semget() (sem ids.1 Sem´ aforos Las funciones descritas en esta secci´ on implementan el nivel de usuario de los mecanismos de los sem´ aforos.3 (GETVAL). Durante todas estas operaciones.1.1 (ipc findkey()) es llamado para buscar el correspondiente descriptor del sem´ aforo en el ´ ındice de la matriz.1.1.3 (try atomic semop()) es llamada con el par´ ametro do undo igual a 0 en orden de ejecutar la secuencia de operaciones.1. sys semop() Despu´ es de validar los par´ ametros de la llamada. . si la ID del conjunto de sem´ aforos de alguna de las estructuras deshacer es encontrada ser´ a -1.3 (IPC STAT). Una bandera decrease es establecida si alguna de las operaciones quitan de un valor del sem´ aforo.3 (GETZCNT). es mantenido el sem´ aforo global del n´ ucleo 5. entonces 5.4.4.3 (IPC RMID) y 5. 5.1. Todas las operaciones de los sem´ aforos especificadas por el usuario son analizadas.1.1.1.3 (GETPID). y 5.3 (freeundos()) es llamada para liberar la estructura deshacer y quitarla de la lista. han sido fallidas.3 (SEM INFO).2 (sem ids.1 Interfaces de la Llamada al sistema de los Sem´ aforos La llamada entera a sys semget() es protegida por el sem´ aforo global del n´ ucleo 5. incrementados o decrementados).1.3 (newary()) es llamada para crear e inicializar un nuevo conjunto de sem´ aforos. N´ otese que esta implementaci´ on ayuda en el uso de los spinlocks y sem´ aforos del n´ ucleo. 5. 5. Durante esta b´ usqueda.1. Si una peque˜ na antememoria temporal es suficiente.1. y 5. El valor de retorno indica si ambas operaciones han tenido ´ exito. Los par´ ametros y los permisos del llamante son verificados antes de devolver la ID del conjunto de sem´ aforos.3 (IPC SET). Para los comandos 5. 5. es mantenida una cuenta para todas las operaciones que tienen la bandera SEM UNDO establecida. la funci´ on 5. Los permisos de acceso para el conjunto de sem´ aforos tambi´ en son validados.1. En otro caso.1. los datos de las operaciones de los sem´ aforos son copiados desde el espacio de usuario a una antememoria temporal. 5.3 (alloc undo()) es llamada para asignar e inicializar una.3 (SETVAL).3 (IPC INFO). Mecanismos IPC 57 5.1. Todos los otros usos de la palabra ”sem´ aforo” ser´ a una referencia a los sem´ aforos del nivel de usuario. 5. El n´ umero de cada sem´ aforos a ser modificado es validado.sem). entonces es usada una antememoria de pila.1. 5. 5.5. el spinlock global de los sem´ aforos es cerrado. sys semctl() Para los comandos 5.1. Para los comandos 5. Durante este proceso.1. y la ID del conjunto de sem´ aforos especificado por el usuario es validado.

y c´ omo deber´ ıa de responder. • Si el elemento status de la estructura 5. Otra llamada a 5.1. Esto ocurre cuando una operaci´ on de un sem´ aforo causar´ ıa un valor inv´ alido del sem´ aforo.1.2 (sem queue). Mecanismos IPC 58 o que no han sido ejecutadas porque necesitaban bloquear. Es este caso. Los siguientes casos son manejados: • Si el conjunto de sem´ aforos ha sido borrado. o un operaci´ on marcada como IPC NOWAIT es incapaz de completarse.1.1.2 (sem queue) est´ a establecido a 1. El elemento semsleeping de la tarea actual est´ a establecido para indicar que la tarea est´ a durmiendo en este elemento 5. Si alguna de las operaciones del sem´ aforo especificada eran operaciones alteradoras (incremento o decremento). y 5. y 5. el nuevo elemento es a˜ nadido al principio de la cola.1. y el elemento sleeper del 5. un nuevo elemento 5. y el elemento 5. En este caso.3 (update queue()) es llamado si alguna de las operaciones eran operaciones alterantes. Para este caso.2 (sem queue) es inicializado conteniendo estas operaciones del sem´ aforo.3 (update queue()) es llamado para recorrer la cola de operaciones pendientes del sem´ aforo para el conjunto del sem´ aforo y despertar cualquier tarea dormida que no necesite bloquear m´ as. current->semsleeping es limpiado. la llamada al sistema falla con EINTR. y schedule() es llamado para poner la tarea actual a dormir. Cuando es despertada. Si alguna de estas operaciones afectaran al estado del sem´ aforo. entonces la llamada al sistema falla con EIDRM.1. determina por qu´ e fue despertada.3 (try atomic semop()) devuelve cero para indicar que todas las operaciones en la secuencia han sido realizadas con ´ exito. La condici´ on de error es retornada al llamante de sys semop(). entonces la tarea ha sido despertada por una interrupci´ on. entonces la tarea es despertada en orden a reintentar las operaciones del sem´ aforo.1. • Si el elemento status de la estructura 5.1. En otro caso.3 (try atomic semop()) devuelve un valor negativo.2 (sem queue) NO est´ a establecida a 1. La tarea actual es marcada como TASK INTERRUPTIBLE.3 (try atomic semop()) devuelve un 1 para indicar que la secuencia de operaciones del sem´ aforo no fue ejecutada porque uno de los sem´ aforos bloquear´ ıa. 5. entonces un nuevo elemento de cola es a˜ nadido al final de la cola. ninguna de las operaciones han sido ejecutadas. Tambi´ en 5.5. Es este caso. entonces la tarea debe de bloquearse otra vez como se describi´ o anteriormente. Si try atomic sweep() devuelve 1. .1. En otro caso.1. o un c´ odigo de error apropiado en caso de fallo.2 (sem queue) es borrado de la cola.2 (sem queue) no ha sido quitado de la cola.2 (sem queue) es borrado de la cola. Antes de regresar. Cada uno de estos casos son m´ as ampliamente descritos a continuaci´ on: Operaciones de sem´ aforos no bloqueantes La funci´ on 5. entonces 5.3 (update queue()) para recorrer la cola de operaciones pendientes del sem´ aforo para el conjunto del sem´ aforo y despierta cualquier tarea dormida que no necesite m´ as bloqueos. Antes de que sys semop() retorne.1.3 (try atomic semop()) es realizada para ejecutar la secuencia de las operaciones del sem´ aforo.1.2 (sem queue) es establecido para identificar esta tarea por el durmiente. Operaciones de Sem´ aforo con fallos Si 5.1. El spinlock global del sem´ aforo es entonces desbloqueado. entonces ha sido encontrada una condici´ on de fallo. Operaciones de Sem´ aforo bloqueantes La funci´ on 5. current->semsleeping es limpiado.3 (update queue()) es llamada para recorrer la cola de las operaciones pendientes del sem´ aforo para el conjunto del sem´ aforo y despertar cualquier tarea dormida que no necesite bloquear m´ as. se devuelve 0 en caso de ´ exito. Antes de que sys semop() regrese.1.1. la tarea vuelve a cerrar el spinlock global del sem´ aforo.1. es hecha una llamada a 5. Esto completa la ejecuci´ on de la llamada al sistema sys semop() para este caso.

entonces las operaciones del sem´ aforo ya han sido ejecutadas por 5. /* n´ umero de sem´ aforos en el array */ }. __kernel_time_t sem_otime. /* permisos. __kernel_time_t sem_ctime. int semmns. /* puntero al primer sem´ aforo en el array */ struct sem_queue *sem_pending.2 (sem queue) ha sido quitado de la cola.1. }. int semaem. /* operaciones pendientes para ser procesadas */ struct sem_queue **sem_pending_last. int semmni.. int semopm. /* peticiones deshechas en este array * / unsigned long sem_nsems. /* ´ ultimo tiempo de la operaci´ on con el sem´ aforo */ time_t sem_ctime. ver ipc. int semmnu. int semvmx. /* pid de la ´ ultima operaci´ on */ }.3 (update queue()). */ struct sem_array { struct kern_ipc_perm sem_perm.1. int semusz. Mecanismos IPC 59 • Si el elemento status de la estructura 5. 5. struct sem /* Una estructura de sem´ aforo para cada sem´ aforo en el sistema. /* ´ ultima operaci´ on pendiente */ struct sem_undo *undo. /* permisos . */ struct sem { int semval. ver ipc. int semmsl. struct semid64 ds struct semid64_ds { struct ipc64_perm sem_perm. La cola status.2 (sem queue) NO est´ a establecido a 1. unsigned long sem_nsems.5.. /* valor actual */ int sempid. struct seminfo struct seminfo { int semmap. int semume.h */ /* ´ ultimo tiempo de operaci´ on con el sem´ aforo */ /* ´ ultimo tiempo de cambio */ /* n´ umero de sem´ aforos en la matriz */ .h */ time_t sem_otime.1. se convertir´ a en el valor de retorno de la llamada al sistema. unsigned long __unused2. la cual ser´ a 0 si se tiene ´ exito o un c´ odigo negativo de error en caso de fallo. unsigned long __unused1. y el elemento 5. /* ´ ultimo tiempo de cambio */ struct sem *sem_base.1.2 Estructuras Espec´ ıficas de Soporte de Sem´ aforos Las siguientes estructuras son usadas espec´ ıficamente para el soporte de sem´ aforos: struct sem array /* Una estructura de datos sem_array para cada conjunto de sem´ aforos en el sistema.

1. /* matriz de operaciones pendientes*/ int nsops. __unused3. semadj.1 (ipc alloc()) para asignar la memoria requerida para el nuevo conjunto de sem´ aforos.4. /* n´ umero de operaciones */ int alter. /* banderas de la operaci´ on */ }. /* matriz de sem´ aforos para las operaciones */ int id. Mecanismos IPC 60 unsigned long unsigned long }. 5. /* siguiente entrada en este proceso */ /* siguiente entrada en este conjunto de sem´ aforos */ /* identificador del conjunto de sem´ aforos */ /* matriz de ajustes. struct sembuf /* las llamadas al sistema cogen una matriz de estas. /* id interna del sem´ aforo */ struct sembuf * sops. struct sem undo /* Cada tarea tiene una * ejecutadas cuando el */ struct sem_undo { struct sem_undo struct sem_undo int short * lista de peticiones de deshacer.3 Funciones de Soporte de Sem´ aforos Las siguientes funciones son usadas espec´ ıficamente para soportar los sem´ aforos: newary() newary() conf´ ıa en la funci´ on 5. */ struct sem_queue { struct sem_queue * next.4. */ struct sembuf { unsigned short sem_num.4. Ellas son proceso sale. *(q->prev) == q */ struct task_struct* sleeper. struct sem queue /* Una cola para cada proceso durmiendo en el sistema. /* id del proceso del proceso pedido */ int status. /* estructura deshacer */ int pid.4.1 (ipc addid()) reserva una entrada de la matriz para el conjunto de descriptores del sem´ aforo e inicializa los datos (5. /* operaciones que alterar´ an el sem´ aforo */ }. /* entrada anterior en la cola. semid. una por sem´ aforo */ }. 5. id_next. /* siguiente entrada en la cola */ struct sem_queue ** prev. El asigna suficiente memoria para el conjunto de descriptores del sem´ aforo y para cada uno de los sem´ aforos en el conjunto. * * proc_next.5. y la direcci´ on del primer elemento del conjunto de descriptores del sem´ aforo es pasada a 5. /* este proceso */ struct sem_undo * undo.2 . /* operaci´ on del sem´ aforo */ short sem_flg. /* status de terminaci´ on de la operaci´ on */ struct sem_array * sma.1 (ipc addid()). __unused4. /* indice del sem´ aforo en la matriz */ short sem_op. La memoria asignada es limpiada.

IPC RMID IPC SET sem´ aforos.1.3 (freeary()) para borrar el conjunto del sem´ aforo. los elementos semusz y semaem de la estructura 5. El valor de retorno de las llamadas al sistema es establecido al conjunto m´ aximo de IDs del conjunto de sem´ aforos.1. La variavle global used sems es actualizada por el n´ umero de sem´ aforos en el nuevo conjunto y la inicializaci´ on de los datos (5. IPC INFO y SEM INFO IPC INFO y SEM INFO causan una antememoria temporal 5. SEM INFO y SEM STAT. el spinlock global del sem´ aforo es mantenido a lo largo de la operaci´ on.4.3 (IPC SET) de la llamada al sistema semctl().1. que es entonces devuelta al llamador de newary(). newary() llama a 5. semctl down() semctcl down() suministra las operaciones 5. Otras inicializaciones realizadas para este conjunto son listadas a continuaci´ on: • El elemento sem base para el conjunto es inicializado a la direcci´ on inmediatamente siguiente siguiendo la porci´ on (5. freeary() freeary() es llamada por 5.1 (ipc rmid()) es llamada (a trav´ es del envoltorio sem rmid()) para borrar la ID del conjunto de sem´ aforos y para recuperar un puntero al conjunto de sem´ aforos.5. mode.4. Todas las operaciones siguiendo la llamada a 5.3 (semctl down()) para realizar las funciones listadas a continuaci´ on.1 (sys semctl()) para realizar las operaciones IPC INFO.1 (ipc buildid()) (a trav´ es de sem buildid()).4. • La funci´ on 5.2 (struct kern ipc perm)) para el nuevo conjunto es completada. Esto corresponde a la localizaci´ on del primer sem´ aforon en el conjunto.3 (IPC RMID) y 5. Esta funci´ on usa el ´ ındice del conjunto de descriptores del sem´ aforo para crear una u ´nica ID. El spinlock global del sem´ aforo es entonces mantenido mientras se copian los valores sem otime. La operaci´ on IPC SET actualiza los elementos uid.2 (seminfo) son actualizados de acuerdo con el comando dado (IPC INFO o SEM INFO). y ctime del conjunto de semctl nolock() semctl nolock() es llamada por 5.4.1. Despu´ es de desbloquear el spinlock global de los sem´ aforos. • Todos los procesos pendientes son despertados y son obligados a fallar con EIDRM.2 (seminfo) para que sea inicializada y cargada con los datos estad´ ısticos sin cambiar del sem´ aforo.2 (semid64 ds). SEM STAT SEM STAT causa la inicializaci´ on de la antememoria temporal 5. • La lista de deshacer para el conjunto de sem´ aforos es invalidada. La operaci´ on IPC RMID llama a 5.1.2 (struct sem array)) de los nuevos segmentos asignados.1. . • La cola sem pending es inicializada como vac´ ıa. y sem nsems en la antememoria. y en ambos casos.1. Es llamada con el spinlock global de los sem´ aforos bloqueado y regresa con el spinlock desbloqueado. gid.1. sem ctime. Mecanismos IPC 61 (struct kern ipc perm)) para el conjunto.1.1 (ipc addid()) son realizadas mientras se mantiene el spinlock global de los sem´ aforos. La ID del conjunto de sem´ aforos y los permisos de acceso son verificadas en ambas operaciones. • EL n´ umero de sem´ aforos usados es reducido con el n´ umero de sem´ aforos en el conjunto borrado. • La memoria asociada con el conjunto de sem´ aforos es liberada. Estos datos son entonces copiados al espacio de usuario.

Los datos son entonces copiados al espacio de usuario despu´ es de tirar con el spinlock. GETNCNT Para GETNCNT en el caso de no error. y entonces en el conjunto del sem´ aforo. entonces una pila de antememoria es usada. el valor de retorno para la llamada al sistema es establecido al valor del sem´ aforo especificado. El spinlock es mantenido mientras se copian los valores del sem´ aforo en la antememoria temporal. Este n´ umero es calculado por la funci´ on 5. . El spinlock es recuperado y mantenido mientras las siguientes operaciones son realizadas en el conjunto del sem´ aforo: • Los valores del sem´ aforo son copiados en el conjunto del sem´ aforo.1. GETZCNT Para GETZCNT en la caso de no error. GETALL La operaci´ on GETALL carga los actuales valores del sem´ aforo en una antememoria temporal del n´ ucleo y entonces los copia fuera del espacio de usuario. el spinlock es temporalmente deshechado en orden de asignar una antememoria m´ as grande. los valores sem otime.1. • La funci´ on 5. y mientras se verifican valores razonables. IPC STAT En la operaci´ on IPC STAT. SETALL La operaci´ on SETALL copia los valores del sem´ aforo desde el espacio de usuario en una antememoria temporal. El spinlock es liberado antes de retornar. tal como se describe en la secci´ on posterior.3 (count semzcnt()).5. Si el conjunto del sem´ aforo es peque˜ no.1. Este n´ umero es calculado por la funci´ on 5. sem ctime. el valor de retorno para la llamada al sistema de establecido al n´ umero de procesos esperando en el sem´ aforo siendo menor que cero. semctl main() cierra el spinlock global del sem´ aforo y valida la ID del conjunto de sem´ aforos y los permisos. • Los ajustes del sem´ aforon de la cola de deshacer para el conjunto del sem´ aforo son limpiados. y sem nsems son copiados en una pila de antememoria.3 (count semncnt()). el valor de retorno para la llamada al sistema es establecido al pid asociado con las u ´ltima operaci´ on del sem´ aforo. en otro caso una antememoria m´ as grande es asignado. GETPID Para GETPID en el caso de no error.1. GETVAL Para GETVALL en el caso de no error. Anteriormente a realizar alguna de las siguientes operaciones. • El valor sem ctime para el conjunto de sem´ aforos es establecido. La peque˜ na pila de antememoria es usada si el conjunto del sem´ aforo es peque˜ no. el valor de retorno para la llamada al sistema es establecido al n´ umero de procesos esperando en el sem´ aforo estando establecido a cero. El spinlock es quitado mientras se copian los valores desde el espacio de usuario a la antememoria temporal. Mecanismos IPC 62 semctl main() semctl main() es llamado por 5. Cualquier tarea pendiente que no sea m´ as bloqueada es despertada. En otro caso.1 (sys semctl()) para realizar muchas de las funciones soportadas.3 (update queue()) es llamada para recorrer la cola de semops (operaciones del sem´ aforo) pendientes y mirar por alguna tarea que pueda ser completada como un resultado de la operaci´ on SETALL.

1. Cualquier ajuste que sea encontrado es reinicializado a cero.1. y el elemento de la cola status es establecido para indicar el ´ exito.1. Si estas operaciones tienen ´ exito. Si la secuencia de operaciones bloquearan. y la cola de status es establecida con un c´ odigo de error apropiado.3 (update queue()) es llamada para recorrer la cola de semops (operaciones del sem´ aforo) pendientes y buscar a cualquier tarea que pueda ser completada como resultado de la operaci´ on 5.1. entonces ellas deber´ ıan de pasar un valor cero como par´ ametro deshacer a 5. La tarea bloqueada es despertada. la tarea que es bloqueada en la secuencia de las operaciones del sem´ aforo es despertada. .1. entonces la cola de elementos es pasada por alto. entonces todas las operaciones son deshechas. Si la secuencia de las operaciones pueden alterar los valores del sem´ aforo. entonces son consideradas completas y son borradas de la cola. por lo tanto el elemento de la cola no es quitado de la cola. Mecanismos IPC 63 SETVAL Despu´ es de validar el nuevo valor del sem´ aforo. try atomic semop() try atomic semop() es llamada por 5. count semzcnt() cuenta el n´ umero de tareas esperando por el valor del sem´ aforo para update queue() update queue() recorre la cola de semops pendientes para un conjunto de un sem´ aforo y llama a 5. Si la secuencia de las operaciones no es alterante.1. Si un valor del sem´ aforo es ajustado m´ as alla de los l´ ımites del sistema. -EAGAIN es devuelto si IPC NOWAIT es establecido.1 (sys semop()) y 5. Una secuencia de operaciones puede fallar si una de las operaciones de los sem´ aforos puede causar un valor inv´ alido del sem´ aforo.5. entonces el proceso es abortado y todas los operaciones son deshechas. Cualquier tarea que no vaya a ser m´ as bloqueada es despertada.3 (try atomic semop()). En otro caso.3 (try atomic semop()) para determinar qu´ e secuencias de las operaciones de los sem´ aforos ser´ an realizadas. En este caso. y -ERANGE es retornado. entonces update queue() retornar´ a sin hacer ning´ un cambio. count semncnt() count semncnt() cuenta el n´ umero de tareas esperando por el valor del sem´ aforo para que sea menor que cero.3 (SETALL). count semzcnt() que sea cero. las siguientes funciones son realizadas: • La cola de deshacer es buscada para cualquier ajuste en este sem´ aforo. Las operaciones del sem´ aforo ser´ an ejecutadas por la tarea despertada.3 (try atomic semop()). • El valor del sem´ aforo sem ctime para el conjunto del sem´ aforo es actualizado. La cola status es establecida a 1 para indicar que la tarea bloqueada ha sido despertada. • La funci´ on 5. o una operaci´ on marcada como IPC NOWAIT es incapaz de completarse.3 (update queue()) para determinar si una secuencia de operaciones del sem´ aforo tendr´ an ´ exito. Las operaciones no han sido realizadas. es devuelto 1 para indicar que la secuencia de las operaciones del sem´ aforo est´ a bloqueada. entonces las tareas durmiendo que no necesiten ser m´ as bloqueadas tienen que ser despertadas.1. Si una operaci´ on bloqueante es encontrada. indicando que cualquier operaci´ on alterante deber´ ıa de ser deshecha antes de retornar. El determina esto intentando realizar cada una de las operaciones. pero puede tener ´ exito. • El valor del sem´ aforo es establecido al valor suministrado. Para los otros elementos de la cola. la bandera q-alter es pasada como el par´ ametro deshacer a 5. El elemento de la cola es tambi´ en quitado de la cola. Si el estado de la cola de elementos indica que las tareas bloqueadas ya han sido despertadas.

• 5.1. alloc undo() alloc undo() espera ser llamada con el spinlock global de los sem´ aforos cerrado.1. En el caso de un error. Un puntero a la siguiente estructura deshacer en la lista de procesos es devuelta.2 (sem undo). sem revalidate() sem revalidate() es llamado cuando el spinlock global del sem´ aforo ha sido temporalmente tirado y necesita ser bloqueado otra vez. . la estructura deshacer es quitada de la lista y liberada. El spinlock global de los sem´ aforos es desbloqueado. entonces todas las operaciones son deshechas.5.3 (alloc undo()).1. y el sem otime. • Los ajustes indicadores en la estructura deshacer son aplicados al conjunto de sem´ aforos. y el par´ ametro do undo no es cero. Cuando el procesamiento de la lista est´ a completo.1. y kmallock() es llamado para asignar suficiente memoria para la estructura 5. entonces todas las operaciones tienen ´ exito y contin´ uan obligadas. el spinlock es recuperado con una llamada a 5.1. • La estructura deshacer es liberada. Si el par´ ametro do undo es cero. y es la responsable de ejecutar todos los ajustes deshacer para la tarea saliente. y las siguientes operaciones son realizadas mientras se mantienen y liberan los spinlocks globales de los sem´ aforos a lo largo del procesamiento de cada elemento de la lista. La lista deshacer para la actual tarea es entonces recorrida.3 (sem revalidate()). Si tiene ´ exito. y la direcci´ on de esta estructura es colocada en la direcci´ on suministrada por el llamante. entonces es borrado desde la lista 5. y si tiene ´ exito retorna con el spinlock global de los sem´ aforos bloqueado. campo del conjunto de sem´ aforos es actualizado. Valida la ID del sem´ aforo y los permisos.1. Es llamado por 5.2 (sem queue) mientras se mantiene el spinlock global de los sem´ aforos. Mecanismos IPC 64 Si todas las operaciones de la secuencia tienen ´ exito. Si es encontrada.3 (update queue()) es llamado para recorrer la cola de las semops pendientes y despertar cualquier tarea durmiento que no necesite ser bloqueada como resultado de la operaci´ on deshacer. • El par´ ametro sem otime del conjunto de sem´ aforos es actualizado. regresa con ´ el desbloqueado. freeundos() freeundos() recorre las lista de procesos por deshacer en busca de la estructura deshacer deseada.3 (semctl main()) y 5. el valor current->semundo es limpiado. Las siguientes operaciones son realizadas para cada uno de los elementos deshacer: • La estructura deshacer y la ID del conjunto del sem´ aforo son validadas. sem exit() sem exit() es llamada por do exit(). • La lista deshacer del correspondiente conjunto de sem´ aforos es buscada para encontrar una referencia a la misma estructura deshacer y para quitarla de esa lista. La nueva estructura semundo es entonces inicializada. Si el actual proceso fue bloqueado en un sem´ aforo. La nueva estructura deshacer es entonces colocada en la cabeza de la lista deshacer para la actual tarea. y 0 es devuelto. y tambi´ en para un array de uno de los valores de ajuste para cada sem´ aforo en el conjunto.

5. Mecanismos IPC

65

5.2
5.2.1

Colas de Mensajes
Interfaces de las llamadas al sistema de Colas

sys msgget() La llamada entera a sys msgget() es protegida por el sem´ aforo global de la cola de mensajes (5.4.2 (msg ids.sem)). En el caso donde una nueva cola de mensajes tiene que ser creada, la funci´ on 5.2.3 (newque()) es llamada para crear e inicializar una nueva cola de mensajes, y la nueva ID de la cola es devuelta al llamante. Si un valor llave es suministrado para una cola de mensajes existente, entonces 5.4.1 (ipc findkey()) es llamada para mirar el ´ ındice correspondiente en la matriz de colas globales de descriptores de mensajes (msg ids.entries). Los par´ ametros y los permisos del llamante son verificados antes de devolver la ID de la cola de mensajes. Las operaci´ ones de b´ usqueda y verificaci´ on son realizadas mientras el spinlock global de la cola de mensajes (msg ids.ary) es mantenido. sys msgctl() Los par´ ametros pasados a sys msgctl() son: una ID de una cola de mensajes (msqid), la operaci´ on (cmd), y un puntero al espacio de la antememoria 5.2.2 (msgid ds) del tipo (buf). Seis operaciones son suministradas en esta funci´ on: IPC INFO, MSG INFO,IPC STAT, MSG STAT, IPC SET y IPC RMID. La ID de la cola de mensajes y los par´ ametros de la operaci´ on son validados; entonces, la operaci´ on (cmd) es realizada como sigue: on de la cola global de mensajes es copiada al espacio de IPC INFO (o MSG INFO) La informaci´ usuario. IPC STAT (o MSG STAT) Una antememoria temporal del tipo 5.2.2 (struct msqid64 ds) es inicializado y el spinlock de la cola de mensajes global es cerrado. Despu´ es de verificar los permisos de acceso del proceso llamante, la informaci´ on de la cola de mensajes asociada con la ID de la cola de mensajes es cargada en una antememoria temporal, el spinlock de la cola de mensajes global es abierto, y los contenidos de la antememoria temporal son copiados fuera del espacio de usuario por 5.2.3 (copy msqid to user()). IPC SET Los datos del usuario son copiados a trav´ es de 5.2.3 (copy msqid to user()). El sem´ aforo de la cola de mensajes global y el spinlock son obtenidos y liberados al final. Despu´ es de que la ID de la cola de mensajes y los permisos de acceso del actual proceso hayan sido validados, la informaci´ on de la cola de mensajes es actualizada con los datos suministrados por el usuario, Despu´ es, 5.2.3 (expunge all()) y 5.2.3 (ss wakeup()) son llamadas para despertar todos los procesos durmiendo en las colas de espera del emisor y del receptor de las colas de mensajes. Esto es el motivo por el que algunos receptores quiz´ as sean ahora excluidos por permisos de acceso estrictos y alguno de los emisores sean capaces ahora de enviar el mensaje debido a un incremento del tama˜ no de la cola. IPC RMID El sem´ aforo de la cola de mensajes global es obtenido y el spinlock global de la cola de mensajes es cerrado. Despu´ es de validar la ID de la cola de mensajes y los permisos de acceso de la actual tarea, 5.2.3 (freeque()) es llamado para liberar los recursos relacionados con la ID de la cola de mensajes. El sem´ aforo de la cola de mensajes global y el spinlock son liberados. sys msgsnd() sys msgsnd() recibe como par´ ametros una ID de una cola de mensajes (msqid), un puntero a la antememoria del tipo 5.2.2 (struct msg msg) (msgp), el tama˜ no del mensaje a ser enviado (msgsz), y una bandera indicando esperar o no esperar (msgflg). Hay dos tareas esperando las colas y un mensaje esperando la cola asociada con la ID de la cola de mensajes. Si hay una nueva tarea en la cola de espera del receptor que est´ a esperando por este mensaje, entonces el mensaje es enviado directamente al receptor. En

5. Mecanismos IPC

66

otro caso, si hay suficiente espacio disponible en la cola de espera de mensajes, el mensaje es guardado en esta cola. Como u ´ltimo recurso, la tarea emisora se encola a si misma en la cola de espera del emisor. Una discusi´ on con m´ as profundidad de las operaciones realizadas por sys msgsnd() es la siguiente: 1. Valida la direcci´ on de la antememoria del usuario y el tipo de mensaje, entonces invoca a 5.2.3 (load msg()) para cargar el contenido del mensaje del usuario en un objeto temporal msg del tipo 5.2.2 (struct msg msg). El tipo de mensaje y los campos del tama˜ no del mensaje de msg tambi´ en son inicializados. 2. Cierra el spinlock global de la cola de mensajes y coge el descriptor asociado con la cola de mensajes con la ID de la cola de mensajes. Si dicha cola de mensajes no existe, retorna EINVAL. 3. Invoca a 5.4.1 (ipc checkid()) (a trav´ es de msg checkid()) para verificar que la ID de la cola de mensajes es v´ alida y llama a 5.4.1 (ipcperms()) para chequear los permisos de acceso de proceso llamante. 4. Chequea el tama˜ no del mensaje y el espacio que sobra en la cola de espera de mensajes para ver si hay suficiente espacio para almacenar el mensaje. Si no, los siguiens subpasos son realizados: (a) Si IPC NOWAIT es especificado en msgflg el spinlock global de la cola de mensajes es abierto, los recursos de memoria para el mensaje son liberados, y EAGAIN es retornado. (b) Invoca a 5.2.3 (ss add()) para encolar la actual tarea en la cola de espera del emisor. Tambi´ en abre al spinlock global de la cola de mensajes e invoca a schedule() para poner la actual tarea a dormir. (c) Cuando es despertado, obtiene el spinlock global otra vez y verifica que la ID de la cola de mensajes es todav´ ıa v´ alida. Si la ID de la cola de mensajes no es valida, ERMID es retornado. (d) Invoca 5.2.3 (ss del()) para quitar la tarea emisora de la cola de espera del emisor. Si hay alguna se˜ nal pendiente para la tarea, sys msgsnd() abre el spinlock global, invoca a 5.2.3 (free msg()) para liberar la antememoria del mensaje, y retorna EINTR. En otro caso, la funci´ on va a 3 (back) para chequear otra vez cuando hay otra vez suficiente sitio en la cola de espera de mensajes. 5. Invoca 5.2.3 (pipelined send()) para intentar enviar el mensaje a la cola receptora directamente. 6. Si no hay receptores esperando por este mensaje, desencola msg en la cola de mensajes esperando (msq->q messages). Actualiza los campos q cbytes y q qnum del descriptor de la cola de mensajes, al igual que las variables globales msg bytes y msg hdrs, las cuales indican el n´ umero de bytes total usados por los mensajes y el n´ umero total de mensajes a lo largo del sistema. 7. Si el mensaje ha sido enviado con ´ exito o encolado, actualiza los campos q lspid y q stime del descriptor de la cola de mensajes y abre el spinlock de colas de mensajes global. sys msgrcv() La funci´ on sys msgrcv() recibe como par´ ametro una ID de una cola de mensajes (msqid), un puntero a una antememoria del tipo 5.2.2 (msg msg) (msgp), el tama˜ no del mensaje deseado (msgsz), el tipo de mensaje (msgtyp), y las banderas (msgflg). Busca las colas de mensajes esperando asociadas con la ID de la cola de mensajes, encuentra el primer mensaje en la cola, la cual comprueba el tipo pedido y lo copia en la antememoria del usuario dado. Si tal mensaje no es encontrado en la cola de mensajes esperando, la tarea pedida es encolada en la cola de receptores esperando hasta que el mensaje deseado est´ a disponible. Una discuci´ on m´ as profunda de las operaciones realizadas por sys msgrcv() es la que sigue: 1. Primero, invoca a 5.2.3 (convert mode()) para derivar el modo de b´ usqueda desde msgtyp. sys msgrcv() entonces cierra el spinlock global de la cola de mensajes y obtiene el descriptor de la cola de mensajes asociado con la ID de la cola de mensajes. Si tal cola de mensajes no existe, retorna EINVAL. 2. Chequea cuando la tarea actual tiene los permisos correctos para acceder a la cola de mensajes.

5. Mecanismos IPC

67

3. Empezando desde el primer mensaje en la cola de mensajes de espera, invoca a 5.2.3 (testmsg()) para chequear cuando el tipo del mensaje se empareja con el tipo requerido. sys msgrcv() contin´ ua buscando hasta que un mensaje emparejado es encontrado o la cola de espera entera est´ a exausta. Si el modo de b´ usqueda es SEARCH LESSEQUAL, entonces es buscado el primer mensaje en la cola con un tipo m´ as bajo o igual a msgtyp. 4. Si un mensaje en encontrado, sys msgrcv() realiza los siguientes subpasos: (a) Si el tama˜ no del mensaje es m´ as grande que el tama˜ no deseado y msgflg indica que no se permiten errores, abre el spinlock de cola de mensajes global y retorna E2BIG. (b) Quita el mensaje de la cola de mensajes esperando y actualiza las estad´ ısticas de la cola de mensajes. (c) Despierta todas las tareas durmiendo en las colas de espera de los emisores. El borrado de un mensaje de la cola en los pasos previos hace posible que uno de los emisores progresen. Va a trav´ es de 10 (last step). 5. Si no hay mensajes emparejados el criterio del receptor es encontrado en la cola de mensajes esperando, entonces msgflg es chequeado. Si IPC NOWAIT est´ a establecido, entonces el spinlock global de la cola de mensajes es abierto y ENOMSG es retornado. En otro caso, el receptor es encolado en la cola de espera del receptor como sigue: (a) Una estructura de datos 5.2.2 (msg receiver) msr es asignada y es a˜ nadida en la cabeza de la cola de espera. (b) El campo r tsk de msr es establecido a la tarea actual. (c) Los campos r msgtype y r mode son inicializados con el tipo y modo del mensaje deseado respectivamente. (d) Si msgflg indica MSG NOERROR, entonces el campo r maxsize de msr es establecido para ser el valor de msgsz. En otro caso es establecido para ser INT MAX. (e) El campo r msg es inicializado para indicar que todav´ ıa no ha sido recibido el mensaje. (f) Despu´ es de que la inicializaci´ on est´ a completa, el estado de la tarea receptora es establecido a TASK INTERRUPTIBLE, el spinlock global de colas de mensajes es abierto, y schedule() es invocado. 6. Despu´ es de que el receptor es despertado, el campo r msg de msr es chequeado. Este campo es usado para almacenar el mensaje entubado o, en el caso de un error, almacenar el estado del error. Si el campo r msg es rellenado con el mensaje deseado, entonces va a 10 (last step). En otro caso, el spinlock global de colas de mensajes es cerrado otra vez. 7. Despu´ es de obtener el spinlock, el campo r msg es re-chequeado para ver si el mensaje fue recibido mientras se estaba esperando por el spinlock. Si el mensaje ha sido recibido, ocurre el 10 (last step). 8. Si el campo r msg todav´ ıa est´ a sin cambiar, entonces la tarea tiene que ser despertada en orden de reintentarlo. En este caso, msr es quitado de la cola. Si hay una se˜ nal pendiente para la tarea, entonces el spinlock de la cola de mensajes global es abierto y EINTR es retornado. En otro caso, la funci´ on necesita ir a 3 (back) y reintentarlo. 9. Si el campo r msg muestra que ha ocurrido un error mientras estaba durmiendo, el spinlock de la cola de mensajes global es abierto y el error es devuelto. 10. Despu´ es de validar que la direcci´ on de la antememoria del usuario msp es v´ alida, el tipo de mensaje es cargado en el campo mtype de msp, y 5.2.3 (store msg()) es invocado para copiar el contenido del mensaje al campo de msp. Finalmente la memoria para el mensaje es liberada por la funci´ on 5.2.3 (free msg()).

5.2. Mecanismos IPC 68 5. /* ´ ultimo pid del mensaje recibido */ pid_t q_lrpid. struct msg queue /* una estructura msq_queue para cada cola presente en el sistema */ struct msg_queue { struct kern_ipc_perm q_perm. struct msg msg /* una estructura msg_msg para cada mensaje */ struct msg_msg { struct list_head m_list. long m_type. }. struct msg msgseg /* segmento de mensaje para cada mensaje */ struct msg_msgseg { struct msg_msgseg* next. /* n´ umero de mensajes en la cola */ unsigned long q_qbytes. struct list_head q_senders. /* ´ utimo tiempo de cambio */ unsigned long q_cbytes. /* u ´ltimo tiempo del mensaje enviado */ time_t q_rtime. /* tama~ no del mensaje de texto */ struct msg_msgseg* next. . struct msg receiver /* una estructura msg_receiver para cada receptor durmiendo */ struct msg_receiver { struct list_head r_list. /* ´ ultimo tiempo del mensaje recibido */ time_t q_ctime.2 Estructuras Espec´ ıficas de Mensajes Las estructuras de datos para las colas de mensajes est´ an definidas en msg. struct list_head q_receivers. /* la siguiente parte del mensaje sigue inmediatamente */ }.c. }. /* el mensaje actual sigue inmediatamente */ }. struct msg sender /* un msg_sender para cada emisor durmiendo */ struct msg_sender { struct list_head list. /* n´ umero actual de bytes en la cola */ unsigned long q_qnum. struct task_struct* tsk. /* ´ ultimo pid del mensaje recibido */ struct list_head q_messages. time_t q_stime. /* m´ aximo n´ umero de bytes en la cola */ pid_t q_lspid. int m_ts.

unsigned long msg_qnum. no usado */ ultimo mensaje en la cola. struct msg *msg_last. __kernel_time_t msg_ctime. }. __kernel_pid_t msg_lrpid. }. long r_msgtype. Mecanismos IPC 69 struct task_struct* r_tsk. unsigned long __unused5. __kernel_time_t msg_rtime. no usado */ ´ ultimo tiempo del mensaje enviado */ ´ ultimo tiempo del mensaje recibido */ ´ ultimo tiempo de cambio */ ´ reusar los campos borrados para 32 bit */ idem */ n´ umero actual de bytes en la cola */ n´ umero de mensajes en la cola */ n´ umero m´ aximo de bytes en la cola */ ultimo pid del mensaje enviado */ ´ ultimo pid del mensaje recibido */ ´ msg setbuf struct msq_setbuf { unsigned long uid_t gid_t mode_t }. unsigned long __unused4. unsigned long msg_lcbytes. unsigned short msg_qbytes. unsigned short msg_cbytes. /* ´ ultimo tiempo del mensaje enviado */ /* ´ ultimo tiempo del mensaje recibido */ /* ´ ultimo tiempo de cambio */ /* /* /* /* /* n´ umero actual de bytes en la cola */ n´ umero de mensajes en la cola */ n´ umero m´ aximo de bytes en la cola */ pid del ´ ultimo mensaje enviado */ pid del ´ ultimo mensaje recibido */ struct msqid ds struct msqid_ds { struct ipc_perm msg_perm. unsigned long __unused2. qbytes. long r_maxsize. struct msg_msg* volatile r_msg. struct msqid64 ds struct msqid64_ds { struct ipc64_perm msg_perm. /* /* /* /* /* /* /* /* /* /* /* /* primer mensaje en la cola. __kernel_time_t msg_ctime. unsigned long __unused1.5. unsigned long __unused3. mode. __kernel_pid_t msg_lspid. }. int r_mode. unsigned short msg_qnum. gid. struct msg *msg_first. unsigned long msg_cbytes. __kernel_ipc_pid_t msg_lrpid. unsigned long msg_lqbytes. __kernel_ipc_pid_t msg_lspid. __kernel_time_t msg_rtime. unsigned long msg_qbytes. uid. . __kernel_time_t msg_stime. __kernel_time_t msg_stime.

Todos los mensajes almacenados en esta cola de mensajes son liberados y la memoria para los descriptores de cola son liberados. Rellena el campo tsk del mensaje de la estructura de datos del emisor con el proceso actual. Esta funci´ on es llamada cuando una cola de mensajes es quitada o un operaci´ on de control de mensajes ha sido realizada.4. y el n´ umero de bytes actualmente usados por la cola (q cbytes) es inicializada a 0.1 (ipc addid()) son realizadas mientras se mantiente el spinlock global de cola de mensajes.2 (kern ipc perm) es inicializada. Primero.2. ss wakeup() ss wakeup() despierta todas las tareas en la cola de mensajes del emisor dado.4. y la cola de emisores esperando (q senders) son inicializadas como vac´ ıas. Libera todos los recursos del n´ ucleo asociados con esta cola de mensajes. ss add() ss add() recibe como par´ ametro un descriptor de cola de mensajes y un mensaje de estructura de datos del emisor. • El n´ umero m´ aximo de bytes permitidos en esta cola de mensajes (q qbytes) es establecida a MSGMNB.2 (struct msg queue)) y entonces llama a 5. ss del() Si el mensaje de la estrutura de datos del emisor dado (mss) a´ un est´ a en la cola de espera del emisor asociado. Para cada receptor durmiendo asociado con msq. la funci´ on freeque() es llamada.5.4. entonces inserta el mensaje de la estructura de datos del emisor a la cabeza de la cola de emisores esperando la cola de mensajes dada. 5.1 (ipc addid()). freeque() Cuando una cola de mensajes va a ser borrada.4. que mapea directamente a 5.4. la cola de receptores esperando (q receivers). entonces todos los emisores en la cola son quitados de ella. y la tarea asociada recibiendo es despertada. Despu´ es de abrir el spinlock.2. • La cola de mensajes esperando (q messages). Esta funci´ on asume que el spinlock global de la cola de mensajes ya est´ a cerrado por la funci´ on llamante. • Los campos q stime y q rtime del descriptor de cola de mensajes son inicializados a 0. Todas las operaciones siguiendo la llamada a 5.2.3 (freeque()).3 Funciones de Soporte de Mensajes newque() newqueue() asigna la memoria para un nuevo descriptor de una cola de mensajes (5.3 (expunge all) para despertar a todos los receptores durmiendo en esta cola de mensajes. entonces ss del() quita mss de la cola. El campo q ctime es establecido a CURRENT TIME. . Mecanismos IPC 70 5. cambia el estado del proceso actual a TASK INTERRUPTIBLE. Entonces llama a 5. newque() llama a msg buildid().1 (ipc buildid()). la cual reserva una entrada de la matriz de colas de mensaje para el nuevo descriptor de cola de mensajes. llama a 5. El descriptor de cola de mensajes es inicializado como sigue: • La estructura 5.2.1 (ipc rmid()) (a trav´ es de msg rmid()) para borrar el descriptor de cola de mensajes del array de descriptores de cola de mensajes global. expunge all() expunge all() recibe como par´ ametros un descriptor de la cola de mensajes (msq) y un valor entero (res) indicando el motivo para despertar a los receptores. Si esta funci´ on es llamada por 5.1 (ipc buildid()) usa el ´ ındice del descriptor de cola de mensajes para crear una u ´nica ID de cola de mensaje que es entonces retornada al llamante de newque(). Posteriormente el spinlock global de la cola de mensajes es liberado. el campo r msg es establecido para indicar el motivo para despertar (res).4.

el cual es almacenado en el campo r msg. el receptor esperando es quitado de la cola de receptores esperando. store msg() La funci´ on store msg() es llamada por 5. entonces SEARCH NOTEQUAL es retornado. 0 es devuelto. Asociado con el primer bloque de datos est´ a una estructura 5. La b´ usqueda entonces contin´ ua hasta que alguno de los receptores v´ alidos es encontrado.2.2. y 1 es retornado.1 (sys msgrcv()) para reensamblar un mensaje recibido en la antememoria del espacio de usuario suministrado por el llamante. Los datos descritos por la estructura 5. y son despertados con un status de error de E2BIG. El mensaje es representado en la memoria del n´ ucleo como una lista enlazada de bloques de datos.2.2. la funci´ on 5. entonces SEARCH ANY es devuelto. Mecanismos IPC 71 load msg() Cuando un proceso env´ ıa un mensaje.3 (testmsg()) es invocada para encontrar el primer receptor que est´ a esperando por el mensaje dado.2 (msg msg) y cualquier estructura 5. Si msgtyp es menor que 0.1 (sys msgsnd()) primero invoca a la funci´ on load msg() para cargar el mensaje desde al espacio de usuario al espacio del n´ ucleo.1 (sys msgrcv()). free msg() La funci´ on free msg() libera la memoria para una estructura de datos de mensaje 5. convert mode() convert mode() es llamada por 5. Recibe como par´ ametros las direcciones del tipo del mensaje especificado (msgtyp) y una bandera (msgflg). El bloque de datos asociado con la estructura msg msg est´ a limitado por el tama˜ no de DATA MSG LEN. entonces bloques de datos adicionales son asignados y son reorganizados en una lista enlazada. En el caso donde no hay un receptor esperando por el mensaje. El bloque de datos y la estructura son asignados en un bloque contiguo de memoria que puede ser tan grande como un p´ agina en memoria. y los segmentos del mensaje. entonces msgtyp es establecido a su valor absoluto y SEARCH LESSEQUAL es retornado.5. pipelined send() pipelined send() permite a un proceso enviar directamente un mensaje a la cola de receptores mejor que depositar el mensaje en la cola asociada de mensajes esperando. Devuelve el modo de b´ usqueda al llamante basado en el valor de msgtyp y msgflg.2. Esta funci´ on retorna la direcci´ on de la nueva estructura 5. • El modo de b´ usqueda es SEARCH NOTEQUAL y el tipo de mensajes no es igual al tipo especificado.2. y la tarea receptora asociada es despertada. En otro caso SEARCH EQUAL es retornado.2 (msg msg) si es que tiene ´ exito. Estos bloques de datos est´ an limitados por un tama˜ no de DATA SEG LEN. Si msgtyp es null (cero). los receptores potenciales quiz´ as encuentren que han solicitado un tama˜ no que es muy peque˜ no para el mensaje dado. • El modo de b´ usqueda es SEARCH LESSEQUAL y el tipo de mensaje es menor o igual que el tipo deseado.2. . Tales receptores son quitados de la cola. Devuelve 1 si una de las siguientes condiciones es verdad: • El modo de b´ usqueda indica buscar cualquier mensaje (SEARCH ANY).2. La estructura msg msgseg y los bloques de datos asociados son asignados en un bloque de memoria contigua que puede ser tan grande como una p´ agina en memoria.2 (msg msgseg) son secuencialmente copiados a la antememoria del espacio de usuario.2. y cada uno incluye una estructura 5. En el proceso de b´ usqueda de un receptor. Si el mensaje total no se ajusta en este primer bloque de datos.2 (msg msg).2 (msg msgseg)). Si lo encuentra. • El modo de b´ usqueda es SEARCH EQUAL y el tipo de mensaje es el mismo que el tipo deseado. La funci´ on 5. El mensaje es almacenado en el campo r msg del receptor. testmsg() La funci´ on testmsg() chequea cuando un mensaje conoce el criterio especificado por el receptor. Si MSG EXCEPT es especificado en msgflg.2 (msg msg) que describe el mensaje completo.2. o la cola est´ a exausta.

0 a n donde n es el n´ umero de IDs de memoria compartida en el sistema). la vieja versi´ on IPC.3 (shm get stat()) es llamada para calcular el n´ umero de p´ aginas de memoria compartidas que est´ an residentes en memoria y el n´ umero de p´ aginas de memoria compartida que han sido intercambiadas (swapped out). y los datos del n´ ucleo son trasladados a esta antememoria temporal. En otro caso una antememoria temporal del tipo struct msqid ds es inicializada.3. copy msqid from user() La funci´ on copy msqid from user() recibe como par´ ametros un mensaje de la antememoria del n´ ucleo del tipo struct msq setbuf. Las operaciones de b´ usqueda y verificaci´ on son realizadas mientras es mantenido el spinlock global de memoria compartida. y mode de la antememoria del n´ ucleo son rellenados con los valores de los campos correspondientes desde la antememoria temporal.2 (msqid64 ds). una antememoria de usuario y una bandera de la versi´ on indicando la nueva versi´ on IPC vs. el correspondiente ´ ındice es buscado en la matriz de descriptores de memoria compartida. SHM INFO El sem´ aforo global de memoria compartida y el spinlock global de memoria compartida son mantenidos mientras se obtienen estad´ ısticas de la informaci´ on del sistema para la memoria compartida.2 (struct shmid64 ds) es inicializada.3 5. la vieja versi´ on IPC. y el spinlock global de memoria compartida es cerrado. SHM STAT. La funci´ on 5. una antememoria temporal del tipo 5. Estas estad´ ısticas son almacenadas en una antememoria temporal 5. En la caso de la nueva versi´ on IPC.3.uid.5. copy from user() es llamada para copiar el contenido de la antememoria del usuario a una antememoria temporal del tipo 5. entonces copy to user() es llamado para copiar desde la antememoria del n´ ucleo a la antememoria del usuario directamente. IPC STAT Para SHM STAT y IPC STATA. una antememoria del n´ ucleo del tipo 5.2. Las cuentas de swap attempts y swap successes son codificadas fuertemente a cero.2 (msqid64 ds). Entonces. una antememoria temporal del tipo struct 5. Posteriormente copy to user() es llamado para copiar el contenido de la antememoria temporal a la antememoria del usuario.3. y los par´ ametros y los permisos del llamante son verificados antes de devolver la ID del segmento de memoria compartida.2 (shminfo64) es cargada con los par´ ametros del sistema de memoria compartida y es copiada fuera del espacio de usuario para el acceso de la aplicaci´ on llamante. los campos qbytes. Para el caso SHM STAT. y una bandera versi´ on indicando la nueva versi´ on IPC vs. Si la bandera de la versi´ on es igual a IPC 64.3. En el caso donde un valor de llave es suministrado para un segmento existente de memoria compartida. gid. En el caso de la vieja versi´ on IPC.2.2 (shm info) y copiadas fuera del espacio de usuario para la aplicaci´ on llamante. Recibe como par´ ametros una antememoria del usuario. 5.1 Memoria Compartida Interfaces de las llamadas al sistema de la Memoria Compartida La llamada entera a sys shmget() es protegida por el sem´ aforo global de memoria compar- sys shmget() tida.3.2 (msqid ds) es usado en su vez. Otras estad´ ısticas incluyen el n´ umero total de p´ aginas de memoria compartida y el n´ umero de segmentos de memoria compartida en uso.2. el par´ ametro ID del segmento de memoria compartida se espera que sea un ´ ındice exacto (esto es. Despu´ es de validar . Mecanismos IPC 72 copy msqid to user() copy msqid to user() copia el contenido de una antememoria del n´ ucleo a la antememoria del usuario. sys shmctl() IPC INFO Una antememoria temporal 5.

una direcci´ on en la cual el segmento de memoria compartida deber´ ıa de ser conectada (shmaddr). Para SHM STAT y IPC STAT.3.3 (shm inc()) ser´ a invocado con la llamada a la funci´ on do mmap() a trav´ es de la estructura shm file operations. N´ otese que este incremento garantiza que la cuenta de enlaces no es cero y previene que el segmento de memoria compartida sea destruido durante el proceso de enlazamiento al segmento. el par´ ametro ID del segmento de memoria compartida se espera que sea una ID que ha sido generada por una llamada a 5. Si shmaddr no es cero. el spinlock global de memoria compartida es cerrado. Los par´ ametros para 5. La ID es validada antes de proceder. ´ NOTESE que 5. do mmap() seleccionar´ a la direcci´ on virtual en la cual mapear el segmento de memoria compartida. los permisos de acceso del llamante son verificados.3. y la bandera SHM RND es especificada. 5. y entonces si no hay conexiones actuales. Si shmaddr no es un m´ ultiplo de SHMLBA y SHM RND no es especificado. Estos cambios son realizados mientras se mantiene el sem´ aforo global de memoria compartida global y el spinlock global de memoria compartida.3 (shmem lock()) es llamada para realizar la funci´ on. En el caso transitorio de SHM STAT.1 (shmget()). Para SHM LOCK y SHM UNLOCK.3. entonces la bandera MAP FIXED tambi´ en es pasada a do mmap(). 5. entonces EINVAL es devuelto. y la bandera IPC PRIVATE es establecida para prevenir que otro proceso sea capaz de referenciar la ID de la memoria compartida. Los permisos de acceso del llamante son validados y el campo shm nattch del segmento de memoria compartida es incrementado. SHM LOCK. y mode del segmento de la memoria compartida son actualizadas con los datos del usuario. Para el caso IPC STAT. una ID de segmento de memoria compartida. En otro caso. IPC RMID Durante el IPC RMID el sem´ aforo global de memoria compartida y el spinlock global de memoria compartida son mantenidos a trav´ es de esta funci´ on. En el caso transitorio de IPC STAT. La bandera MAP SHARED es pasada a do mmap(). IPC SET Despu´ es de validar la ID del segmento de memoria compartida y los permisos de acceso del usuario. Estas operaciones son realizadas mientras se mantiene el spinlock global de memoria compartida. para establecer el tiempo actual. y para incrementar el n´ umero de enlaces a este segmento de memoria compartida. Las estad´ ısticas deseadas son cargadas en la antememoria temporal y entonces copiadas fuera de la aplicaci´ on llamante. La funci´ on do mmap() es llamada para crear un mapeo de memoria virtual de las p´ aginas del segmento de memoria compartida. 5. el valor de retorno ser´ a 0.5. son obtenidos el sem´ aforo global de memoria compartida y el spinlock global de la memoria compartida. El campo shm ctime tambi´ en es actualizado. SHM UNLOCK Despu´ es de validar los permisos de acceso. El siguiente cambio en la . Despu´ es de la llamada a do mmap().3 (shmem lock()) identifican la funci´ on a realizar. Notar que esta es una caracter´ ıstica no documentada. Si una direcci´ on fue suministrada por el llamante. Esto es realizado mientras se mantiene el sem´ aforo mmap sem de la tarea actual. En otro caso. y la ID del segmento de memoria compartida es validado. la bandera SHM DEST es establecida para marcarlo para destrucci´ on. entonces shmaddr es redondeado por abajo a un m´ ultiplo de SHMLBA.3 (shm destroy()) es llamada para destruir el segmento de memoria compartida.4.3. Esta funci´ on es llamada para establecer el PID. sys shmat() sys shmat() toma como par´ ametro. Mecanismos IPC 73 el ´ ındice. y las banderas que ser´ an descritas m´ as adelante. las banderas uid. pero es mantenida para el programa ipcs(8). gid. la ID de la memoria compartida ser´ a el valor de retorno. La cuenta de enlaces es entonces decrementada. La ID de la Memoria Compartida es validada.3.1 (ipc buildid()) es llamado (a trav´ es de shm buildid()) para convertir el ´ ındice en una ID de memoria compartida.

3. Finalmente. Si un c´ odigo de error ha sido retornado por do mmap(). __unused3. shmmni. __unused1. id. struct shmid kernel struct shmid_kernel /* privadas { struct kern_ipc_perm struct file * int unsigned long del n´ ucleo */ shm_perm. struct shm info struct shm_info { int used_ids. do munmap() es llamado para deshacer el mapeo de direcciones virtuales para el segmento de la memoria compartida. N´ otese tambi´ en que do munmap() realiza una llamada atr´ as a 5. despu´ es de decrementar la cuenta de enlaces. /* shm asignada total */ shm_rss. Cuando es encontrada.3 (shm destroy()) es llamado para liberar los recursos del segmento de memoria compartida. { long long long long long long long long long shmmax. entonces 5.3 (shm inc()). Mecanismos IPC 74 cuenta de enlaces es 1 para una llamada a shmat() por culpa de la llamada a 5.3. shmseg.3. shmmin. Si. entonces este c´ odigo de fallo es pasado en el valor de retorno para la llamada al sistema. la cual realiza las funciones manteniendo el libro de memoria compartida. sys shmdt() incondicionalmente devuelve 0. /* shm intercambiada total */ swap_attempts.5. shm_file. la direcci´ on virtual en la cual la memoria compartida es mapeada es devuelta al llamante en la direcci´ on especificada por el usuario. sys shmdt() El sem´ aforo global de la memoria compartida es mantenido mientras se realiza sys shmdt(). la cuenta resultante que se encuentra es cero. shmall. __unused2. y libera los recursos del segmento de memoria compartida si no hay m´ as enlaces. y el segmento se marca para la destrucci´ onn (SHM DEST). unsigned long unsigned long unsigned long unsigned long unsigned long }. swap_successes.3 (shm close()). __unused4. /* shm residente total */ shm_swp. shm_nattch. 5.2 Estructuras de Soporte de Memoria Compartida struct shminfo64 struct shminfo64 unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned }. . La mm struct del actual proceso es buscado para la vm area struct asociada con la direcci´ on de memoria compartida.3. shm_tot.

La ID de este segmento es guardada en el campo id del descriptor de memoria compartida.3. asigna un nuevo descriptor de memoria compartida. /* doble bloque indirecto */ unsigned long swapped. El nuevo descriptor de memoria compartida es inicializado e insertado en la matriz global de IPC de descriptores de segmentos de memoria compartida. Despu´ es de validar que el tama˜ no del segmento de la memoria compartida que va a ser creado est´ a entre SHMMIN y SHMMAX y que el n´ umero total de segmentos de memoria compartida no excede de SHMALL. la ID del segmento es retornada a la aplicaci´ on llamante. shm_lprid. que indica el n´ umero total de segmentos de memoria compartida a lo largo del sistema. Si tiene ´ exito. El puntero del archivo retornado es guardado en el campo shm file del descriptor del segmento de memoria compartida asociado. }. Act´ ua con tres par´ ametros para el nuevo segmento: la llave. la bandera y el tama˜ no. /* para el primer bloque */ swp_entry_t **i_indirect. __unused2. shm_atime. /* permisos de la operaci´ on */ /* tama~ no del segmento (bytes) */ /* ´ ultimo tiempo de enlace */ /* ´ ultimo tiempo de desenlace */ /* ´ ultimo tiempo de cambio */ /* pid del creador */ /* pid del ´ ultimo operador */ /* n´ umero de enlaces actuales */ struct shmem inode info struct shmem_inode_info { spinlock_t lock.4. al igual que en el campo i ino del inodo asociado. __unused1. 5. Mecanismos IPC 75 unsigned long time_t time_t time_t pid_t pid_t }. En adicci´ on. swp_entry_t i_direct[SHMEM_NR_DIRECT]. shm_dtime. __unused4. __unused5. shm_segsz. la direcci´ on de las operaciones de memoria compartida definidas en la estructura shm file operation son almacenadas en el fichero asociado.3. . /* en la memoria */ struct list_head list.3 (shmem file setup()) es invocada posteriormente a crear un archivo no enlazado del tipo tmpfs. La ID del segmento de memoria compartida es creado por shm buildid() (a trav´ es de 5. shm_segsz. La funci´ on 5. shm_nattch. shm_cprid. int locked.5. __unused3. shm_perm. shm_cpid. shm_ctim.1 (ipc buildid())).3 Funciones de Soporte De Memoria Compartida newseg() La funci´ on newseg() es llamada cuando un nuevo segmento de memoria compartida tiene que ser creado. es tambi´ en incrementado para reflejar este cambio. shm_atim. struct shmid64 ds struct shmid64_ds { struct ipc64_perm size_t __kernel_time_t unsigned long __kernel_time_t unsigned long __kernel_time_t unsigned long __kernel_pid_t __kernel_pid_t unsigned long unsigned long unsigned long }. unsigned long max_index. El valor de la variable global shm tot. shm_dtim. shm_ctime. shm_lpid.

e incrementa el n´ umero de enlaces para el segmento de memoria compartida dado.1 (ipc rmid()) es llamado (a trav´ es de shm rmid()) para borrar la ID de Memoria Compartida. pero la cuenta de referencia permanece incrementada.3 (shm destroy()) es llamado para liberar los recursos de la memoria compartida. El estado de bloqueo del segmento de memoria compartida es almacenado en el inodo asociado. Estas operaciones son realizadas mientras se mantiene el spinlock global de memoria compartida. La siguiente lista de puntos ocurren en cada p´ agina en el segmento de memoria compartida: • find lock page() es llamado para cerrar la p´ agina (estableciendo PG locked) y para incrementar la cuenta de referencia de la p´ agina. shm destroy() Durante shm destroy() el n´ umero total de p´ aginas de memoria compartida es ajustada para que cuente el borrado del segmento de memoria compartida. shm close() shm close() actualiza los campos shm lprid y shm dtim y decrementa el n´ umero de segmentos enlazados de memoria compartida. decrementando la cuenta de referencia a cero para cada p´ agina.3. Estas operaciones son todas realizadas mientras se mantienen el sem´ aforo global de memoria compartida y el spinlock global de memoria compartida. y calcula el n´ umero total de p´ aginas de memoria en uso por la memoria compartida y el n´ umero total de p´ aginas de memoria compartida que est´ an intercambiadas. 5. y si es necesario. y asigna un nuevo descriptor de fichero y un nuevo objeto inodo del tipo tmpfs. el spinlock para cada estructura inodo que es accedido es cerrado y abierto en secuencia. 5.3. • Si el estado deseado es abierto. Mientras se est´ a manteniendo el sem´ aforo del inodo asociado.3 (shmem lock) es llamado para abrir las p´ aginas de memoria compartida. crea una nueva dentry bajo la raiz montada de tmpfs. entonces PG locked es limpiado. abierto. entonces 5. Entonces asocia el nuevo objeto dentry con el nuevo objeto inodo llamando a d instantiate() y guarda la direcci´ on del objeto dentry en el descriptor de fichero. efectivamente. Incrementando la cuenta de referencia se asegura que el segmento de memoria compartida permanece bloqueado en memoria durante esta operaci´ on. Entonces PG locked es limpiado. fput() es llamado para decrementar el contador de uso para f count para el objeto fichero deseado. shmem file setup() La funci´ on shmem file setup() configura un archivo sin enlazar que vive en el sistema de ficheros tmpfs con el nombre y tama˜ no dados. establece el tiempo actual. Hay una estructura de fichero y una estructura de inodo para cada segmento de memoria compartida. shmem lock() shmem lock() recibe como par´ ametros un puntero al descriptor del segmento de memoria compartida y una bandera indicando cerrado vs. y una vez para la referencia existente que caus´ o que la p´ agina permanezca bloqueada en memoria. El campo i size del objeto inodo es establecido para ser del tama˜ no del fichero y el campo i nlink es establecido para ser 0 en orden a marcar el inodo no enlazado. el estado de bloqueo del inodo es establecido.5. Si hay suficientes recursos de memoria para este fichero. Tambi´ en. • Si el estado deseado es cerrado. Este estado es comparado con el estado de bloqueo deseado: shmem lock() simplemente retorna si ellos se corresponden. shm inc() shm inc() establece el PID. shmem file setup() almacena la direcci´ on de la estructura . Mecanismos IPC 76 shm get stat() Los ciclos de shm get stat() van a trav´ es de todas las estructuras de memoria compartida. kfree() es llamado para liberar el descriptor de segmento de memoria compartida. Si no hay otros enlaces al segmento de memoria compartida. Como los datos requeridos son obtenidos a trav´ es del inodo.4. para liberar los recursos del objeto fichero. entonces la cuenta de referencia es decrementada dos veces durante la actual referencia.

5. 0 es devuelto. Si tiene ´ exito. Mensajes y Memoria Compartida Los sem´ aforos. y ajusta la m´ axima ID en la matriz de descriptores correspondiente si es necesario. la matriz vieja es copiada en la nueva. Si un elemento sin usar es encontrado. El correspondiente spinlock global es mantenido mientras se actualiza la matriz de descriptores para el tipo IPC dado. La estructura 5. actualiza la cuenta de IDs que est´ an en uso. entonces la ID del IPC se considera v´ alida y 1 es devuelto. ipc addid() primero llama a 5. y el ´ ındice de la matriz para el nuevo descriptor es devuelto. La existencia de una secuencia de n´ umeros hace posible detectar el uso de una ID de IPC sin uso. entonces el l´ ımite m´ aximo actual es devuelto. grow ary() grow ary() maneja la posibilidad de que el n´ umero m´ aximo (ajustable) de IDs para un tipo IPC dado pueda ser din´ amicamente cambiado. .4. e inicializa los campos f mode y f vfsmnt del descriptor de fichero propio. ipc addid() Cuando un nuevo conjunto de sem´ aforos. ipc checkid() ipc checkid() divide la ID del IPC dado por el SEQ MULTIPLIER y compara el cociente con el valor seq guardado en el descriptor correspondiente. 5.4.4 5. o segmento de memoria compartido es a˜ nadido. y la vieja es liberada. entonces vmalloc() es usado para asignar memoria. Fuerza al actual l´ ımite m´ aximo para que no sea mayor que el limite del sistema permanente (IPCMNI) y lo baja si es necesario. un nuevo segmento de memoria compartido o un nuevo conjunto de sem´ aforos).4. una nueva matriz m´ as grande es asignada. Una ID es creada multiplicando el n´ umero de secuencia con SEQ MULTIPLIER y a˜ nadiendo el producto al ´ ındice de la matriz de descriptores. ipc rmid() ipc rmid() borra el descriptor IPC desde la matriz de descriptores global del tipo IPC. La secuencia de n´ umeros usados en crear una ID de un IPC particular es entonces almacenada en el descriptor correspondiente. En otro caso. En otro caso. ipc alloc() Si el asignamiento de memoria es mayor que PAGE SIZE. Si el tama˜ no de la matriz existente es suficientemente grande.1 Primitivas IPC de Linux Primitivas IPC de Linux Gen´ ericas usadas con Sem´ aforos. La matriz de descriptores es buscada para el primer elemento sin usar. Si son iguales. y mecanismos de memoria compartida de Linux son construidos con un conjunto de primitivas comunes. retorna con el spinlock global cerrado para el tipo IPC dado. La ID del IPC se convierte f´ acilmente en el ´ ındice de la correspondiente matriz de descriptores. cola de mensajes. kmalloc() es llamado con GFP KERNEL para asignar la memoria. mensajes. Mecanismos IPC 77 shmem file operations en el campo f op. Esta ID es creada a la vez que el nuevo elemento IPC es a˜ nadido (ej. ipc buildid() ipc buildid() crea una u ´nica ID para ser asociada con cada descriptor con el tipo IPC dado. Cada tipo IPC mantiene una secuencia de n´ umeros la cual es incrementada cada vez que un descriptor es a˜ nadido. Cuando ipc addid() tiene ´ exito. shmem file setup() devuelve el nuevo descriptor de fichero. la cuenta de descriptores que est´ an en uso es incrementada. Un puntero al descriptor asociado IPC con la ID del IPC dado es devuelto. Estas primitivas son descritas en las secciones posteriores. Tambi´ en se asegura de que la matriz de descriptores existente es suficientemente grande.2 (kern ipc perm) para el nuevo recurso descriptor es entonces inicializado. La funci´ on shmem truncate() es llamada para completar la inicializaci´ on del objeto inodo. En otro caso.1 (grow ary()) para asegurarse que el tama˜ no de la correspondiente matriz de descriptores es suficientemente grande para el m´ aximo del sistema.

Si la llave no es encontrada. y busca la llave especificada. ipc lock() ipc lock() coge una ID de IPC como uno de sus par´ ametros. Mecanismos IPC 78 ipc findkey() ipc findkey() busca a trav´ es de la matriz de descriptores del objeto especificado 5. gid_t gid.2 Estructuras Gen´ ericas IPC usadas con Sem´ aforos.5. ipc unlockall() ipc unlockall() abre el spinlock global para el mecanismo IPC dado (esto es: memoria compartida. el ´ ındice del descriptor correspondiente es devuelto. y mensajes). y devuelve un puntero al descriptor correspondiente a la ID IPC especificada. sem´ aforos o colas de mensajes) y una ID de un descriptor. Mensajes. mode_t mode. y Memoria Compartida Todos los sem´ aforos. Devuelve 0 si el permiso est´ a garantizado y -1 en otro caso. /* usados por la estructuras de datos en el n´ ucleo */ struct kern_ipc_perm { key_t key. y otros permisos para el acceso de los recursos IPC. ipcperms() ipcperms() chequa el usuario. gid_t cgid.2 (ipc ids). ipc parse version() ipc parse version() borra la bandera IPC 64 desde el comando si est´ a presente y devuelve IPC 64 o IPC OLD. entonces es devuelto -1.4.4. Esto hace posible acceder a cualquier descriptor desde cualquier funci´ on gen´ erica IPC usando un puntero de este tipo de datos. . mensajes. sem´ aforos. shm get()) la cual arroja el tipo de datos al tipo de datos correcto del descriptor. ipc unlock() ipc unlock() libera el spinlock global para el tipo IPC indicado. el tipo de estructura com´ un 5.2 (kern ipc perm) est´ a embebida como la primera entidad en todos los casos. El modelo esperado es que ipc get() es llamado a trav´ es de la funci´ on envoltorio (ej. La funci´ on ipc get() devuelve este tipo de datos com´ un. y devuelve un puntero al descriptor IPC correspondiente. ipc lockall() ipc lockall() cierra el spinlock global para el mecanismo IPC dado (esto es: memoria compartida. Cierra el spinlock global para el tipo IPC dado. uid_t uid. Una vez encontrada. sem´ aforos. N´ otese que aunque los descriptores para cada tipo IPC son tipos de datos diferentes. grupo. }. y mecanismos de memoria compartida hacen un uso de las siguientes estructuras comunes: struct kern ipc perm Cada descriptor IPC tiene un objeto de datos de este tipo como primer elemento. unsigned long seq.4. uid_t cuid. ipc get() ipc get() coge un puntero al tipo particular IPC (memoria compartida. 5. y mensajes).

int max_id. unsigned short seq. La matriz es a veces referida por la matriz de descriptores. struct ipc id Una matriz de estructuras ipc id existe en cada instancia de la estructura 5. colas de mensajes. mensajes y memoria compartida respectivemente.es>. La matriz es din´ amicamente asignada y quiz´ as sea reemplazada con una matriz m´ as grande por 5. }. y memoria compartida. unsigned short seq_max. spinlock_t ary. struct ipc_id* entries. Sobre la traducci´ on 79 struct ipc ids La estructura ipc ids describe los datos comunes para los sem´ aforos. 6 Sobre la traducci´ on Este libro ha sido traducido por Rub´ en Melc´ on <melkon@terra.2 (kern ipc perm) es usado como tipo de datos de descriptores comunes por las funciones gen´ ericas IPC. Hay tres instancias globales de esta estructura de datos –semid ds. el sem´ aforo sem es usado para proteger el acceso a la estructura.4. desde que el tipo de datos 5.1 (grow ary()) tal como requiere.4. Revisi´ on de la traducci´ on: Beta 0. int in_use. El campo seq es una secuencia de n´ umeros global la cual ser´ a incrementada cuando un nuevo recurso IPC es creado. Revisado el 27 de Octubre de 2002 por Manuel Canales Esparcia <macana@macana-es.03 (2 de Marzo de 2002). msgid ds y shmid ds– para los sem´ aforos. struct ipc_id { struct kern_ipc_perm* p. }.4. En cada instancia.con> Publicado por TLDP-ES .6. y el spinlock ary protege el acceso a esta matriz. El campo entries apunta a una matriz de descriptores de IPC.2 (ipc ids). struct semaphore sem. struct ipc_ids { int size.

Sign up to vote on this title
UsefulNot useful