Sistemas Operativos Universidad Tecnológica Nacional - FRBA Técnicas Digitales III Autor: Alejandro Furfaro 1 Introducción 1 1 Sistema Operativo ¿Que es? Es un programa de control que se ocupa de: 2 2 2 2 1 Administrar los recursos de la computradora. Administrar la ejecución de los diferentes programas en muchos casos pueden ser de diferentes usuarios. Facilitar la tarea del programador permitiendole acceso a los recursos de manera independiente del hardware. Proveer servicios a los programas de aplicación a través de un conjunto de llamadas standard. Estas acciones se resuelven a través de una implementación que puede representarse en capas: Aplicaciones Programador de Aplicaciones Utilidades / Servicios Sistema Operativo Programador de Sistemas Hardware Autor: Alejandro Furfaro 2 Clasificación de los Sistemas Operativos 1 Sistemas Real Time: 2Se utilizan para sistemas de control industriales, centrales de conmutación, instrumentación científica. 2Por lo general tienen una muy pobre capacidad de interfaz con el usuario, no tienen utilitarios. 2Su fortaleza consiste en administrar los recursos de la computadora, de modo de ejecutar una operación en particular en la misma cantidad de tiempo cada vez que ocurre el evento que la dispara. Concepto evendriven. 2En el tipo de aplicaciones que resuelven estos sistemas operativo, si una parte se mueve mas rápido solo porque los recursos están disponibles puede generar resultados tan catastróficos como si no se mueve porque el recurso está ocupado. 2Ejemplos de implementaciones QNX i RT-Linux i Autor: Alejandro Furfaro 3 Clasificación de los Sistemas Operativos 1 Monotarea - Monousuario Están preparados para ejecutar solo una tarea a la vez. no puede ejecutar mas de una en forma concurrente. 2 Interfaz para un solo usuario, (una sola sesión de trabajo). 2 Transfiere el control de la máquina a la aplicación que va a ejecutarse, y solo interviene a demanda de ésta mediante alguna llamada a los servicios de su kernel, o cuando la aplicación finaliza y devuelve el control. 2 El viejo MS-DOS (sucesor del mas viejo aún CPM/86), es el mas difundido de este tipo de sistemas. 2 Un ejemplo muchísimo mas actual, útil, y eficiente de este tipo de sistemas es el Palm OS que corre en las computadoras de mano Palm Pilot. Aquí no hay programas residentes y se tiene un sistema operativo con una interfaz de usuario muy cómoda que permite ejecutar aplicaciones de a una por vez. 2 Autor: Alejandro Furfaro 4 Clasificación de los Sistemas Operativos 1 Multitarea 2 2 2 2 Monousuario Hoy en día es habitual utilizar estos sistemas operativos en las PC de escritorio. Interfaz para un solo usuario, pero pueden mantener en memoria múltiples aplicaciones en forma estable y dentro de un entorno de protección (algunos con mas suerte que otros...) Es habitual descargar correo de Internet o bajar un archivo extenso durante minutos mientras se trabaja en la redacción de un documento, o en la escritura de un programa de aplicación, y hasta se chequea el estado de una unidad de disco , y se realiza un backup de información, todo a la vez. Ejemplos habituales de este tipo de sistemas. i i i i i i Windows XP, NT Workstation, 2000 Workstation, OS/2, Machintosh, Linux o cualquier UNIX instalado como Workstation Autor: Alejandro Furfaro 5 Clasificación de los Sistemas Operativos 1 Multiusuario 2 2 2 2 2 Esta es la forma mas avanzada de los sistemas operativos, y curiosamente la que primó en los sistemas pioneros como UNIX. La falta de capacidad del hardware de por entonces (1969) hizo que se implementasen versiones mas simplificadas para usuarios individuales. Aquí la interfaz de usuario soporta múltiples sesiones. Esto por extensión implica que tiene capacidades multitarea, ya que con solo ejecutarse un proceso por usuario se tiene mas de una tarea en ejecución en la memoria del sistema. Estos sistemas son los mas poderosos y normalmente los mas eficientes: MVS, para los mainframes, UNIX (o cualquiera de sus versiones free como LINUX, o free BSD, por ejemplo) son los mejores exponentes de este tipo de sistemas. Microsoft tiene versiones denominadas Server de Windows XP 2000, y una evolución de 2000, denominada 2003 que soporta al procesador Itanium. Autor: Alejandro Furfaro 6 Funciones de un Sistema Operativo 1 Gestión del procesador 2 Gestión del tiempo de procesamiento para cada tarea (scheduling de procesos). 1 Gestión de la Memoria. 2 Asignación de memoria RAM para las aplicaciones aplicando criterios basados en la MMU del procesador. 2 Gestión de la Memoria Virtual 2 Gestión de la memoria cache 1 Gestión de los dispositivos de E/S. 2 Acceso al hardware de manera transparente para las aplicaciones. 2 Manejo de la concurrencia de acceso en sistemas multiusuario o multitarea especialmente Autor: Alejandro Furfaro 7 Funciones de un Sistema Operativo 1 Gestión del storage 2 (File Systems en los medios masivos de almacenamiento: discos rígidos, CD-ROMs, DVDs). 1 Interfaz para las Aplicaciones. 2 Colección de llamadas para ejecutarse desde los programas de aplicación para acceder a servicios brindados pro código del sistema operativo. Se las conoce como System Calls. 2 En los multitarea se manejan mediante este subsistema, los diferentes niveles de privilegio que posea el Sistema Operativo (y que dependen del procesador utilizado en el sistema) 1 Interfaz para los usuarios. 2 Manejo de interfaces sencillas para usuarios no expertos GUI i Texto i Combinación de ambas i Autor: Alejandro Furfaro 8 Modelo derivado de las funciones Pr oc es os Aplicaciones de usuario Interfaz de aplicaciones (API o System Call) G es tió n de Capa de interfaz para acceso a los servicios del S.O. Por parte de las aplicaciones Hardware M em or ia Bu de ffer Fi E/ s le S M Sy an st ag em er Ge Scheduler st ió n de Se rv ic io s De de Dr vi E/ iv ce S er s Programas de Aplicación y Utilitarios Ejecutan en el menor nivel de privilegio KERNEL Hardware (lo que golpeamos a causa de los estándares de calidad de algunos S.O.’s ....) Autor: Alejandro Furfaro 9 Linux: Introducción 1 1 1 Sistema Operativo Unix-like Basado en POSIX (Portable OS based on UNIX) Kernel monolítico (programa único +módulos) 2 1 1 Diseñado bajo el concepto Lightweight Processes (LWP) Nonpreemptive 2 2 1 1 1 1 1 Opuesto a los Sistemas MicroKernel que reinaron en los 70’s. No puede intercalar arbitrariamente flujos de ejecución mientras está en modo privilegiado. Solaris 2.x y Mac 3.0 son full preemptive Soporta SMP (Symetric MultiProcessing) Soporta varios File Systems (IBM AIX, SGI Irix, FAT32, etc) Puede ser muy pequeño. Se puede ajustar a un floppy 3”1/2 Es libre. Podemos intslarlo y modificar su código sin otra limitación que nuestro hardware. Versiones. Se representan con tres números separados por puntos. Ej: 2.4.18, o 2.5.22. El primero es la Versión. El segundo indica si es un kernel estable (par) o en desarrollo (impar). El tercero es el release. Autor: Alejandro Furfaro 10 El Kernel 1 1 Es el principal programa del SO Actualmente soportado por las siguientes arquitecturas: 2 2 2 2 2 2 2 2 1 Aprovecha las capaciades del hardware 2 2 1 ARM (ARM based Computers) Alpha (Compaq) Intel (ia32 e ia64-Itanium) Familia 68K (Motorola) MIPS (Silicon Graphics) Power PC Sparc y Ultra Sparc (32 y 64 bits Sun Microsystems) S390 (IBM) Maneja el acceso a los recursos hardware específicos. A través de Device Drivers. Provee servicios de acceso al hardware a los programas de usuario. Es reentrante 2 Múltiples procesos acceden al kernel de manera simultánea. Autor: Alejandro Furfaro 11 El Kernel 1 Maneja niveles de Protección 2 Ejecución en modo Kernel. 2 Ejecución en modo User. 2 Maneja Stacks separados. Proceso 3 SystemCall Call System Handler Handler Autor: Alejandro Furfaro Scheduler Scheduler Proceso 4 Requerimiento a un driver / Interrupción desde un dispositivo página de memoria no presente) Fallo de Página Modo Kernel (Accede a una Interrupción de timer Modo Usuario Servicio del Sistema Proceso 1 Proceso 2 Exception Exception Handler Handler Device Device Driver Driver 12 Manejo de Memoria Modelo: Dirección Lógica Segmento:Desplazamiento Unidad Unidad de de Segmentación Segmentación Dirección Lineal Unidad Unidad de de Paginación Paginación Dirección Física Autor: Alejandro Furfaro 13 Manejo de Memoria: Tabla GDT Segmentación: Basada en Modelo FLAT 1 Descriptores en GDT 2 2 2 Advanced Power Management 2 1 Los cuatro segmentos tienen: 2 2 2 2 2 2 2 Autor: Alejandro Furfaro Kernel Code Segment (KCS) Kernel Data Segment (KDS) User Code Segment (UCS) User Data Segment (UDS) Base = 0x00000000 Limit = 0xfffff G (granularity flag) = 1, para expresar el tamaño del segmento en paginas de 1024 bytes. S (system flag) = 1, para segmentos de código normales. Type = 0xA, para segmentos de código que se puedan leer y ejecutar, o 0x2 para segmentos de datos de lectura / escritura. DPL (Descriptor Privilege Level): 0 para Modo Kernel (KCS y KDS), o 3 para Modo Usuario (UCS y UDS) D/B (32-bit address flag) = 1. Direcciones con 14 offset de 32 bits Manejo de Memoria : Tabla GDT 1 Descriptores de Sistema 2 Task State Segment Descriptor (TSSD): Uno por cada procesador. Limite =0xEB (236 bytes). i S = 1, DPL = 00, Tipo = 9 u 11 (TSS de 32 bits Available / Busy ) i Dirección Base init_tss i CPU#0_TSSD CPU#1_TSSD CPU#n_TSSD i 2 Se aplian las TSS de cada CPU en un array llamado init_tss LDT Descriptor: uno genérico compartido por todos los procesos. El kernel de Linux no usa LDT. Contiene un descriptor Null. i Dirección base: Se almacena en la default_ldt. i Límite 7 i Los procesos pueden crear su propia LDT mediante una función especial del kernel: modify_ldt( ). i Autor: Alejandro Furfaro VOLVER 15 Manejo de Memoria 1 Paginación: 2 Paginas de tamaño fijo (4 u 8 KB) 2 Definiciones: PAGE = Es el rango de direcciones lineales mapeados dentro de esa página, junto con los datos contendidos por dichas direcciones (En la jerga, “data chunk”) i Page Frame = Es el área de memoria que contiene una página, por eso también se la puede encontrar bajo el nombre de phisical page, o page container i 2 Paging Unit: i 2 Extended Paging: i 2 A partir del pentium se tiene la posibilidad de definir páginas de 4Mbytes. Three-level paging i 1 Convierte direcciones lineales en físicas Procesadores de 64 bit. Ubicación de las páginas del Kernel Autor: Alejandro Furfaro 16 Paginación Paginación Extendida Paginación Paginación en tres niveles Autor: Alejandro Furfaro 17 Modelo Kernel/Proceso 1 Proceso: Instancia de un programa en ejecución, o un contexto de ejecución. 2 2 2 1 1 Tienen un espacio de direccionamiento determinado (direcciones de memoria que tienen permitidas para acceder) Normalmente trabajan en modo User. Cuando requieren servicios del kernel, switchean a modo kernel. Una vez resuelto el requerimiento regresan a modo User El kernel hace que cada proceso “vea” una CPU dedicada. Es un administrador de procesos Existe un grupo de programas privilegiado llamados “kernel threads”. 2 2 2 2 Corren en modo Kernel en el espacio de direccionamiento del kernel. No interactúan con el usuario. No tienen terminal asociada. Se crean durante el startup y mueren en el shutdown Autor: Alejandro Furfaro 18 Procesos 1 El kernel debe saber para cada proceso: 2 Su prioridad 2 Su estado 2 Su espacio de direcciones asignado 2 Sus archivos abiertos y en acceso 1 Descriptores de Proceso, estructura task_struct, VER 2 El kernel define un array de estas estructuras denominado task_array. 1 Parentezco Autor: Alejandro Furfaro entre procesos. 19 Descriptor de Procesos 1 state flags nedd_resched counter nice next_task prev_task tty_struct Lista de tty’s asociadas al proceso run_list p_optr p_pptr ………. 1 fs_struct Directorio actual tty 1 files_struct Punteros a file descriptors thread fs files mMm sigmask_lock sig ……. Autor: Alejandro Furfaro mm_struct Punteros a descriptores de áreas de memoria signal_struct Tiene por objeto darle al Sistema Operativo la visión clara de que está haciendo el proceso. Definido en la declaración de task_struct VER Dada la complejidad de esta estructura algunos campos son referencias a otras estructuras como la tabla de file descriptors del proceso Señales recibidas 20 Identificando un proceso 1 Cada proceso tiene un Descriptor que lo define unívocamente. 1 Los punteros a los descriptores de proceso sirven para identificarlos. Son Números de 32 bits 1 Los UNIX identifican a los procesos con un Process ID (PID). Los PID van desde 0 a 32767. 1 El Process ID corresponde al campo pid en el descriptor de proceso (task_struct) task_struct Autor: Alejandro Furfaro 21 Estados de un proceso 1 Corresponden al campo state de task_struct. Valores posibles (flags) 2 TASK_RUNNING 2 TASK_INTERRUPTIBLE 2 TASK_UNINTERRUPTIBLE 2 TASK_STOPPED 2 TASK_ZOMBIE */ Definidos en usr/src/linux/sched.h */ #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_ZOMBIE 4 #define TASK_STOPPED 8 1 Para setear el estado de un proceso el kernel usa las macros set_task_state o set_current_state 22 Autor: Alejandro Furfaro Descriptor de Proceso: Alocando memoria 1 En union task_union { struct task_struct task; unsigned long stack[2048]; }; Autor: Alejandro Furfaro Kernel Mode, los procesos usan un Stack del Segmento de Datos del Kernel. 1 El uso de este stack no es intensivo. 1 Los stacks expanden hacia la base de memoria (el registro esp se decrementa) 1 Conociendo el registro esp se deduce el comienzo del Descriptor de Proceso 23 La Macro current 1 1 Sirve para que el kernel pueda ubicar el inicio del Descriptor de Proceso. El código es: movl $0xffffe000, %ecx andl %esp, %ecx movl %ecx, p 1 Tiene dos complicaciones. 2 2 Cuando hay poca memoria RAM Cuando se trabaja con sistemas SMP Se usa caché de 16 task_union 1 free_task_struct (), () libera los 8K de memoria usados por el proceso en curso y lo guarda en el caché a menos que esté full 1 alloc_task_struct(), () aloja los 8 K en memoria y si hay dos páginas seguidas en el caché los cachea 1 Autor: Alejandro Furfaro 24 Lista de Procesos 1 Task array: Lista doblemente enlazada de punteros a estructuras de tipo task_struct 1 La cantidad de elementos se guarda en la variable N_TASKS. El campo pid es el índice de este arreglo. 1 Los campos prev_task y next task de los descriptores de procesos (task_struct), task_struct vinculan los elementos. 1 El primer elemento es el puntero a task_struct del proceso 0 llamado swapper. Autor: Alejandro Furfaro 25 Implementación de listas doblemente enlazadas en Linux 1 Linux utiliza numerosas listas doblemente enlazadas. 1 Buscando un estilo de programación homogéneo se define la siguiente estructura genérica: struct list_head { struct list_head *next, *prev; }; 1 La misma se encuentra en el archivo /usr/src/linux[version]/includes/linux/list.h List_head List_head List_head next prev next prev next prev Autor: Alejandro Furfaro 26 Runqueue 1 Por una cuestión de eficiencia,el kernel mantiene una lista separada de los procesos que están en estado TASK_RUNNING. 1 La variable nr_running mantiene la cantidad de procesos en este estado. 1 La lista está encabezada por init_task (descriptor de proceso del proceso 0 o swapper ) 1 El campo run_list de task_struct es una estructura list_head para implementar esta lista. 1 Funciones: 2 add_to_runqueue(): () Inserta un proceso a la lista 2 del_from_runqueue(): () remueve un proceso de la lista 2 move_first_runqueue(): () Mueve un proceso al principio de la lista 2 move_last_runqueue(): () Mueve un proceso al final de la 27 Autor: Alejandro Furfaro lista Parentesco entre procesos 1 En el descriptor de procesos se dispone de los siguientes campos para reflejar parentesco 2p_opptr (original parent) 2p_pptr (parent) 2p_cptr (child) 2p_ysptr (younger sibling) 2p_osptr (older sibling) Autor: Alejandro Furfaro 28 Creación de procesos 1 Cada vez que un proceso crea a otro, le transfiere sus recursos. Pero no se duplican 1 Copy on write. El kernel detecta el acceso a modificar un recurso por parte del child y en ese momento le crea una copia propia. 1 Lightweight processes. Comparten las estructuras del kernel, y el espacio de direcciones en modo User. Autor: Alejandro Furfaro 29 Creación de procesos: fork () 1 Se emplean diferentes system calls para crear procesos. La mas utilizada es fork (). () Proceso principal Descriptor Proceso principal If (!fork()) Internal de fork (kernel) Retorna PID del proceso creado FALSE Proceso hijo Descriptor Proceso child Retorna 0 TRUE Proceso en memoria física Una Una misma misma copia copia física física del del código código yy los los datos datos en en memoria memoria apuntada apuntada por por dos dos descriptores descriptores de de proceso proceso diferentes. diferentes. Una Una copia = Dos procesos. (Lightweight Process) copia = Dos procesos. (Lightweight Process) Autor: Alejandro Furfaro 30 Creación de procesos: vfork () 1 La system call vfork () crea un child que hace suyo el espacio completo de direcciones del proceso padre, obligándolo a esperar su finalización si requiere acceso a un objeto compartido. 1 El espacio de direcciones del proceso se compone de: 2 El código ejecutable del programa 2 El área de datos inicializados del programa. 2 El área de datos no inicilizados el programa 2 El stack inicial del programa (el stack de Modo User) 2 El código ejecutable y los datos de las librerías compartidas necesarias. 2 El heap (la memoria que el programa puede requerir dinámicamente) Autor: Alejandro Furfaro 31 clone () 1 1 System Call propia e LINUX. __clone (fn, arg, flags, child_stack) 2 2 2 fn es la función que ejecutará el proceso child, que finaliza cuando dicha función ejecute return (). arg: Puntero a la lista de argumentos de fn. flags: cuatro bytes. El menos significativo es el número de la señal que el child va a enviar al padre cuando termine (default SIGCHILD). Los otros codifican flags: CLONE_VM: comparte descriptores de memoria y las tablas de páginas i CLONE_FS: comparte la tabla que identifica el file system i CLONE_FILES: comparte la tabla de descriptores de archivos abiertos i CLONE_SIGHAND: comparte la tabla de handlers de señal i CLONE_PID: comparte el PID (solo si el parent tiene PID 0 y en entorno uniprocesador i CLONE_PTRACE: si el parent es traceado por ptrace(), el hijo también i CLONE_VFORK: i 2 child_stack: indica el stack de Modo USER que se le asignará al esp del child. Si es 0, corresponde al stack de modo USER del parent Autor: Alejandro Furfaro 32 Flags del proceso 1 Corresponden al campo flags en el descriptor de proceso. #define PF_ALIGNWARN 0x00000001 #define PF_STARTING #define PF_EXITING #define PF_FORKNOEXEC #define PF_SUPERPRIV #define PF_DUMPCORE #define PF_SIGNALED #define PF_MEMALLOC #define PF_MEMDIE #define PF_FREE_PAGES #define PF_NOIO #define PF_FSTRANS #define PF_USEDFPU /* Ptrace flags */ #define PT_PTRACED #define PT_TRACESYS #define PT_DTRACE #define PT_TRACESYSGOOD #define PT_PTRACE_CAP Autor: Alejandro Furfaro 0x00000002 0x00000004 0x00000040 0x00000100 0x00000200 0x00000400 0x00000800 0x00001000 0x00002000 0x00004000 0x00008000 0x00100000 0x00000001 0x00000002 0x00000004 0x00000008 0x00000010 /* Print alignment warning msgs */ /* Not implemented yet, only for 486*/ /* being created */ /* getting shut down */ /* forked but didn't exec */ /* used super-user privileges */ /* dumped core */ /* killed by a signal */ /* Allocating memory */ /* Killed for out-of-memory */ /* per process page freeing */ /* avoid generating further I/O */ /* inside a filesystem transaction */ /* task used FPU this quantum (SMP) */ /* delayed trace (used on m68k, i386) */ /* ptracer can follow suid-exec */ 33 Límite de direcciones de un proceso 1 El campo addr_limit de task_struct es una estuctura del tipo typedef struct { unsigned long seg; } mm_segment_t; 1 contiene: 2 El valor PAGE_OFFSET para procesos normales 2 0xFFFFFFFF para kernel threads. 1 El kernel puede cambiar dinámicamente el valor de addr_limit.seg mediante las macros get_fs y set_fs. set_fs Esto le permite al kernel invocar directamente system calls a rutinas de servicio y pasarles las direcciones en el segmento de datos del kernel (KDS_desc, CPL = 00) Autor: Alejandro Furfaro 34 Dominios de ejecución 1 Linux puede ejecutar programas nativos de otros S.O. 2 2 1 En el caso de sistemas No POSIX, emplea emuladores que traducen “on the fly” las llamadas de la aplicación al kernel de su S.O. Nativo, a llamadas POSIX para el kernel de Linux (Ej. Wine o DOSemu) Si el sistema es POSIX, en general no hay problemas ya que todos trabajan con el mismo API. Para especificar su S.O. Nativo, un proceso necesita definir Personality Operating system su dominio de ejecución PER_LINUX Standard execution domain 2 2 Setea el campo personality de la estructura exec_domain de su descriptor de proceso (task_struct) system call personality (). Autor: Alejandro Furfaro PER_SVR4 PER_SVR3 PER_SCOSVR3 PER_OSR5 PER_WYSEV386 PER_ISCR4 PER_BSD PER_SUNOS PER_XENIX PER_IRIX32 PER_IRIXN32 PER_IRIX64 PER_RISCOS PER_SOLARIS PER_UW7 System V Release 4 System V Release 3 SCO Unix Version 3.2 SCO OpenServer Release 5 Unix System V/386 Release 3.2.1 Interactive Unix BSD Unix SunOS Xenix SGI Irix-5 32 bit SGI Irix-6 32 bit SGI Irix-6 64 bit RISC OS Sun's Solaris Caldera's UnixWare 7 35 Scheduling de procesos 1 Linux divide el tiempo de la CPU en períodos (o épocas) 1 En cada época de la CPU, Linux (como UNIX) asigna un quantum de tiempo a cada proceso -> time slicing 1 La duración del quantum es critica en la performance 1 Tres clases de procesos 2 Interactivos 2 Batch 2 Real Time 1 Preempción. Se maneja con el campo need_resched del descriptor de proceso Autor: Alejandro Furfaro 36 Políticas de Scheduling / Time Sharing 1 1 Linux programa al timer tick de modo de generar una interrupción (tick) cada 10mseg. aproximadamente. Un proceso tiene asignado un tiempo de ejecución llamado quantum, que es múltiplo entero de un tick. 2 2 El valor de ticks asignado a un proceso se almacena en el campo counter del descriptor de proceso (task_struct). Cuando expira el quantum, se suspende la ejecución del proceso. La rutina que llama el kernel desde el handler del timer tick para esta función es update_ process_times( ) cuyo código es if (current->pid) { --current->counter; if (current->counter <= 0) { current->counter = 0; current->need_resched = 1; } } 2 2 Si counter < 0 => need_resched se pone a 1. Antes de volver a Modo User (para lo cual ejecutará la función ret_from_sys_call( ))se chequea si need_resched = 1 se invoca la ) función schedule ( ) para pasar al siguiente proceso en la lista. Autor: Alejandro Furfaro 37 Políticas de Scheduling / Time Sharing 1 Otros campos de task_struct relacionados con laspolíticas de scheduling son: 2 policy. policy Define la clase de scheduling: i SCHED_FIFO. Proceso Real Time First-In First-Out. Cuando le asigna CPU, el scheduler lo deja en su posición actual en la run_queue a menos que exista otro proceso real time de mayor prioridad en estado TASK_RUNNING. i SCHED_RR.Proceso Real Time Round Robin. Cuando le asigna CPU el scheduler lo envía al fondo de la run_queue. Es mas justo que el anterior para procesos de igual prioridad. i SCHED_OTHER. Proceso común de tiempo compartido. 2 rt_priority. rt_priority Es la prioridad de un proceso real time (1 a 99). 2 nice. nice Determina la longitud de un quantum al inicio de un nuevo período de la CPU (o época). Vale de –20 a +19 (de mayor a menor proridad. EN generalk un proceso se lanza con un valor 0, en este campo. Autor: Alejandro Furfaro 38 Políticas de Scheduling / Time Sharing 1 Otros campos de task_struct relacionados con laspolíticas de scheduling son: 2 cpus_allowed.Bit cpus_allowed mask que especifica las CPUs presentes en el sistema sobre las que el proceso está habilitado para ejecutar. Para arquitecturas IA-32 el máximo número de CPUs es 32, de modo que la máscara cabe en un int. 2 cpus_runnable. cpus_runnable Bit mask que especifica cual de las CPUs del sistema es la que está ejecutando el proceso. Si el proceso no está siendo ejecutado por ninguna CPU del sistema todos los bits de esta máscara están en 1.De otro modo están en 0 todos los bits excepto el correspondiente a la CPU que está ejecutando el proceso. Mediante una AND entre este campo y cpus_allowed el kernel puede determinar si un proceso puede ser schedulado sobre una CPU determinada. 2 processor. processor Indica que CPU está ejecutamndo el proceso o en su defecto cual fue la última en ejecutarlo. 39 Autor: Alejandro Furfaro Process Preemption 1 Los procesos en Linux son interrumpibles (preempted). 1 Cuando un proceso entra en estado TASK_RUNNING, el kernel chequea su prioridad y la compara con el corriente. Si su prioridad es mayor el proceso actual es interrumpido (preempted). 1 Cuando a causa de una interrupción se debe despertar a un proceso (ponerlo TASK_RUNNING) y su prioridad es mayor que la del proceso en curso, el kernel setea el bit current>need_resched y por lo tanto antes de salir de la interrupción se llamará a la función schedule ( ) para pasar al nuevo proceso. 40 Autor: Alejandro Furfaro Temporización y scheduler System Calls 1 Temporizacion 2 time () 2 ctime () 2 ftime () 2 gettimeofday () 1 Manejo de scheduler 2 nice () 2 renice () 2 getpriority () 2 setpriority () Autor: Alejandro Furfaro 41 Trace de procesos Cuando se invocan las system call clone( ), ) fork( ), ) o vfork( ), el kernel llama a la función do_fork( ). ) 1 do_fork () ejecuta numerosas acciones para crear un proceso. Algunas dependen de los flags que se le pasen como argumentos a las funciones primitivas: 1 Si no se seteó el flag CLONE_PTRACE, 1 2 2 1 do_fork () pone a ‘0’ el campo ptrace en el descriptor de proceso (task_struct) del proceso child. Este campo almacena algunos pocos flags utilizados cuando un proceso está siendo “traceado” por otro. El child no deberá “tracearse” aún cuando lo esté siendo el proceso actual. La función ret_from_fork( ) chequea el valor del campo ptrace del descriptor de proceso actual (offset 24 de task_struct). If Si el campo no es nulo , “se tracea” la system call fork( ), ) vfork( ), ) o clone( ), ) invocandose a la función syscall_trace( ) para notificar al proceso de debugging. Autor: Alejandro Furfaro 42 Global Kernel Locks 1 Han ido decrementando su uso a medida que evolucionó el kernel desde las versiones 2.0 a 2.4 1 Actualmente se usan para sibncronizar accesos al File system y para evitar condiciones de carreras entre procesos. 1 Consisten en un spin lock llamado kernel_flag 1 Los utilizan handlers de interrupción y excepción y procesos botton half. 1 Cuando un proceso requiere un kernel lock invoca la system call lock_kernel (), () y para liberarlo se invoca unlock_kernel (), () que incrementan y decrementan el campo lock_depth del descriptor de proceso Autor: Alejandro Furfaro 43 Conmutación de Procesos 1 Es responsbilidad del scheduler (sched.c) e incluye: 2 Contexto de Hardware i Directorio de Páginas para cambiar al nuevo espacio de direcciones i Stacks de Modo User y Modo Kernel, mas los Registros propios de los procesadores IA-32 2 Registros adicionales que al igual que en el procesador de Intel hay que manejarlo en forma manual, por falta de soporte en el hardware i Registros de FPU i Registros 2 de control y Debug Utiliza una macro llamada switch_to() en lugar de utilizar un jump far al descriptor del TSS h void switch_to (struct task_struct *prev, struct task_struct *next, struct task_struct *last) Autor: Alejandro Furfaro 44 Conmutación de Procesos 1 Linux mantiene una única TSS por cada CPU. VER 1 A pesar del no uso de jmp far para conmutar no puede prescindir de al menos un TSS por cada CPU: 2 Al aumentar el nivel de privilegio de una tarea, el procesador busca en el TSS actual (cuyo selector contiene el registro TR), los valores de SS y ESP correspondientes al mayor nivel de privilegio. 2 Para acceder a E/S desde una tarea cuyo CPL no sea el adecuado debe consultarse el IO BitMap del TSS de la tarea. 1 Al usar un único TSS por CPU, Linux debe actualizarle determinados campos en cada process switch. Autor: Alejandro Furfaro 45 Conmutación de Procesos 1 El contexto de hardware de cada tarea se almacena en una estructura del tipo thread en task_struct. task_struct VER 1 Esta estructura puede ser tomada por cualquier CPU presente en el sistema ya que contiene los datos que de guardarse en la TSS no podrían ser tomados por otra CPU. 1 Observaciones: 2 Solo guarda los registros de propósito general estrictamente necesarios. El resto va al stack 2 Se almacena el estado de la FPU, y debug registers que no son contemplados en el TSS 2 Se mantiene el bitmap de E/S del proceso en esta estructura Autor: Alejandro Furfaro 46 Conmutación de Procesos: macro switch_to() 1 Se invoca desde schedule () mediante la linea switch_to (prev, next, prev) 1 Se ejecuta el siguiente código movl movl movl prev,%eax next,%edx %eax,%ebx ;eax = prev ;edx = next ;ebx = eax = prev push %esi ; apila en pila de Modo Kernel del proceso prev push %edi ; los registros esi,edi, y ebp push %ebp ;la estructura thread ocupa el offset 608 dentro de task_struct movl %esp, 616(%eax) ; salva esp en prev->thread.esp movl 616(%edx), %esp ; carga esp con next->thread.esp movl $1f, 612(%eax) ; guarda ret addr en prev->thread.eip pushl 612(%edx) ; guarda ret addr en next->thread.eip jmp __switch_to Autor: Alejandro Furfaro 47 Conmutación de Procesos: rutina __ switch_to 1 Ejecuta los siguientes pasos: 2 unlazy_fpu (prev) ;Si cambiaron, salva los registros de la FP, MMX, y XMM. 2 Init_tss [smp_precessor_id ( ) ].esp0 = next ->thread.esp0 i Smp_processor_id ( ) devuelve el índice a la CPU que está ejecutando elñ proceso 2 Salva en la estructura thread los registros FS y GS (El kernel no los usa, pero las aplicaciones pueden usarlos. movl %fs, 620 (%esi) ;esi apunta a prev -> thread movl %gs,624 (%esi) 2 Carga los nuevos valores de fs y gs. movl 12 (%ebx),%fs ;ebx apunta a next -> thread movl 16 (%ebx),%gs Autor: Alejandro Furfaro 48 Conmutación de Procesos: rutina __ switch_to 2 Salva los valores de los debug registers, si el proceso al que se va a activar los había utilizado. if (next->thread.debugreg[7]){ loaddebug(&next->thread, 0); loaddebug(&next->thread, 1); loaddebug(&next->thread, 2); loaddebug(&next->thread, 3); /* no 4 and 5 */ loaddebug(&next->thread, 6); loaddebug(&next->thread, 7); } 2 Salva en el TSS de la CPU que corresponda los valores de IO BitMap, si prev o next tienen permisos customizados de E/S. if (next->thread.ioperm) { memcpy(init_tss[smp_processor_id( )].io_bitmap, next-> thread.io_bitmap,128)); init_tss[smp_processor_id( )].bitmap = 104; } else if (prev->thread.ioperm) init_tss[smp_processor_id( )].bitmap = 0x8000; Autor: Alejandro Furfaro 49 Conmutación de Procesos: rutina __ switch_to 2__switch_to fue llamada mediante un jmp. Pero antes se guardó en la pila la dirección de retorno. popl %ebp popl %edi popl %esi 2Finalmente copia el contenido de %ebx en la variable prev mov %ebx,prev Autor: Alejandro Furfaro 50 Kernel threads 1 Son hilos de ejecución del kernel que corren permanentemente en modo kernel para cumplir funciones específicas 2 2 2 2 1 1 1 swapping out de una página no utilizada atender conexiones de red flush de los caches de disco etc. Solo un kernel thread puede crear un kernel thread Ejecutan solo una función específica del sistema (los procesos Modo USER pueden ejecutar varias syscall) swapper o Process 0: es el primer kernel thread, creado en el arranque por la función start_kernel(). Autor: Alejandro Furfaro 51 Interrupciones y Excepciones 1 Interrupciones 2Sincronicas: Excepciones (se producen una vez completada la instrucción en curso). 2Asincrónicas: Interrupciones de Hardware. 1 Vectores en Linux: 20-31 excepciones y NMI 232-47 IRQ's 2El resto para Int's soft, Linux usa la 128 (0x80) (system calls, cunado un proceso user la ejecuta, se pasa a modo kernel). 1 Interrupt Handling ≠ Process Switching Autor: Alejandro Furfaro 52 IDT 1 Asocia interrupciones y excepciones con sus respectivos handlers. 1 Es reinicializada luego del POST, de acuerdo a lo establecido en la dispositiva anterior 1 Linux NO usa rutinas del BIOS!. Son de modo Real. Las reemplazan funcionalmente los Device Drivers 1 Se inicializa antes que el kernel habilite las interrupcoines!!! 1 Puede contener tres tipos de descriptores: 2 Task Gate (Linux NO los usa!!!) 2 Interrupt Gate (para atender interrupciones) 2 Trap Gate (para atender excepciones) 1 Linux diferencia de intel los nombres... Autor: Alejandro Furfaro 53 IDT 1 Terminologia Linux: 1 Interrupt Gate: 2 Solo para modo Kernel 2 Son las interrupt gates de intel con DPL=0. 1 System Gate: es la trap gate de Intel. 23 (int3) 2 4 (into) 2 5 (bound) 2 128 (system call) (equiv a int 21 en DOS) 2 Se ejecutan en modo user. 1 Trap Gate: es una trap gate Intel pero solo accesible en modo kernel. Autor: Alejandro Furfaro 54 Handlers de Excepciones del procesador # Exception 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Divide error Debug NMI Breakpoint Overflow Bounds check Invalid opcode Device not available Double fault Coprocessor segment overrun Invalid TSS Segment not present Stack exception General protection Page Fault Intel reserved Floating-point error Alignment check Machine check SIMD floating point Autor: Alejandro Furfaro Exception handler divide_error( ) debug( ) nmi( ) int3( ) overflow( ) bounds( ) invalid_op( ) device_not_available( ) double_fault( ) coprocessor_segment_overrun( ) invalid_tss( ) segment_not_present( ) stack_segment( ) general_protection( ) page_fault( ) None coprocessor_error( ) alignment_check( ) machine_check( ) simd_coprocessor_error( ) Signal SIGFPE SIGTRAP None SIGTRAP SIGSEGV SIGSEGV SIGILL SIGSEGV SIGSEGV SIGFPE SIGSEGV SIGBUS SIGBUS SIGSEGV SIGSEGV None SIGFPE SIGBUS None SIGFPE 55 Manejo de las excepciones 1 Linux asume las excepciones como condiciones de error, y las trata en consecuencia 1 Cuando el procesador genera una excepción, el kernel toma el control y envía al proceso que causante de la excepción una señal (Ver tabla de la diapositiva anterior). 2 Ante una división por 0, el kernel envía SIGFPE al proceso. 2 El proceso maneja la señal o deja al handler default del kernel. En el caso de SIGFPE, el default es abort. 1 Los handlers de Excepción en general proceden a: 2 Salvar la mayoría de los registros en el stack de Modo Kernel (esta parte se codifica en assembly). 2 Manejar la excepción mediante una función de alto nivel escrita en C. 2 Salir mediante la función ret_from_exception( ). Autor: Alejandro Furfaro 56 Excepciones de manejo de recursos 1 Cuando una excepción obedece a mecanismos del procesador para el manejo de recursos, el kernel debe determinar si responde a un error de la aplicación o si en cambio es una excepción habitual en el acceso a un recurso. 1 Analizamos el comportamiento (global) de la Acceso de acuerdo excepción Page Fault. Excepción 14 La Excepción ocurrió en Modo Kernel Bug del Kernel !!! Mata al proceso Excepción en Modo User por violación de los permisos del área de memoria. con los permisos del área de memoria. Acceso Legal. Aloja el Page Frame Acceso Ilegal. Envía SIGSEGV Autor: Alejandro Furfaro 57 Zoom en la Excepción 14 Autor: Alejandro Furfaro 58 Manejo de Interrupciones IRQ INT 0 32 Timer 1 33 Keyboard 2 IRQ_Sharing 2 34 PIC cascading 3 35 Second serial port 2 IRQ 4 36 First serial port 6 38 Floppy disk 8 40 System clock 10 42 Network interface 11 43 USB port, sound card 12 44 PS/2 mouse 13 45 Mathematical coprocessor 14 46 EIDE disk controller's first chain 15 47 EIDE disk controller's second chain 1 Flexibilidad en el Handler de Interrupción Dynamic Allocation Device Device Device Device Device Device #2 #n #1 #n #2 #1 PIC PIC IRQn_Interupt IRQn_Interupt (( )) do_IRQ do_IRQ (n) (n) Hardware Autor: Alejandro Furfaro Software ISR1 ISR1 (( )) Hardware Device El ElS.O. S.O.Debe Debeser sercapaz capazde de compartir una misma IRQ compartir una misma IRQ entre entrediferentes diferentesISR’s ISR’s (Interrupt Service (Interrupt ServiceHandlers) Handlers) ISR2 ISR2 (( )) 59 Manejo de Interrupciones 1El proceso interrumpido está TASK_RUNNING. 1Tiene 1La asignado un quantum para su ejecución. Interupción no puede superar ese quantum 2Acciones críticas. Se resuelven en el handler con las interrupciones deshabilitadas 2Acciones No críticas. Se resuelven en el handler con las interrupciones habilitadas 2Acciones No críticas diferibles. Se ejecutan en algún momento oportuno fuera del handler de interrupción Autor: Alejandro Furfaro 60 Manejo de las Interrupciones en Linux Rango Uso 0-19 (0x0-0x13) Nonmaskable interrupts and exceptions 20-31 (0x14-0x1f) Reservado por Intel 32-127 (0x20-0x7f) External interrupts (IRQs) 128 (0x80) Programmed exception for system calls 129-238 (0x81-0xee) External interrupts (IRQs) 239 (0xef) Local APIC timer interrupt 240-250 (0xf0-0xfa) Reserved by Linux for future use 251-255 (0xfb-0xff) Interprocessor interrupts Vectores Vectores de de Interrupción Interrupción en en Linux Linux Autor: Alejandro Furfaro 61 softirqs, tasklets, y bottom halves 1 Son métodos del kernel (en particular kernel threads) para resolver tareas no críticas diferibles. 2 Tasklets ejecutan sobre softirqs 2 Botton halves ejecutan por medio de tasklets Función Diferible Allocación DInámica Concurrencia No Pueden ejecutar en forma concurrente en diferentes CPU’s aún si son del mismo tipo. Tasklet Si Pueden ejecutar en forma concurrente en diferentes CPU’s Tasklets de diferente tipo, no así tasklets del mismo tipo. Bottom half No Bottom halves no pueden ejecutar concurrentemente en diferentes CPU’s. Softirq Autor: Alejandro Furfaro 62 softirq’s 1 1 1 Corren como kernel threads estáticos. Cada thread corresponde a una softirq y su tipo es el definido en la tabla siguiente. El kernel lleva la cuenta de las softirq’s pendientes Chequea por softirq’s pendientes en varios puntos: 2 2 2 2 2 Cuando se rehabilitan las softirq’s mediante la macro local_bh_enable Cuando se finaliza un handler de interrupción Cuando termina el handler de interrupción del timer o del local timer en sistemas SMP Cuando se despierta un kernel thread que maneja una softirq Cuando se recibe un paquete en la placa de red. Softirq HI_SOFTIRQ 0 NET_TX_SOFTIRQ 1 NET_RX_SOFTIRQ 2 TASKLET_SOFTIRQ 3 Autor: Alejandro Furfaro Description Index (priority) Mayor Prioridad 1 Maneja tasklets y bottom halves de alta prioridad Transmite paquetes a las placas de red Recibe paquetes desde placas de red Maneja tasklets 63 tasklets 1 1 1 1 Se utilizan para implementar funciones diferibles en Device Drivers. Se cargan en forma dinámica (móduos) Se construyen sobre softirq’s de tipo HI_SOFTIRQ y TASKLET_SOFTIRQ. (Prioridad única diferencia) Se manejan mediante una estructura del tipo tasklet_struct Campo Descripción Next Puntero al próximo descriptor en la lista State Estado de la tasklet Count Contador de Locks Func Puntero a la función de la tasklet Data unsigned long que puede ser utilizado por la función tasklet Autor: Alejandro Furfaro 64 Botton Halves 1 Esencialmente Bottom half Peripheral device es una TIMER_BH Timer tasklet de alta prioridad que TQUEUE_BH Cola de tarea periódica no puede ejecutarse de DIGI_BH DigiBoard PC/Xe SERIAL_BH Serial port manera concurrente con RISCOM8_BH RISCom/8 otro bottom half, aún si es SPECIALIX_BH Specialix IO8+ de tipo diferente ni sobre AURORA_BH Aurora multiport card (SPARC) ESP_BH HaSi ESP serial card una CPU dkiferente. SCSI_BH SCSI interface 1 El spin lock global_bh_lock IMMEDIATE_BH Cola de tarea Immediata Cyclom-Y serial se utiliza para asegurar que CYCLADES_BH Cyclades multiport CM206_BH CD-ROM Philips/LMS cm206 disk a lo sumo se ejecute un MACSERIAL_BH Power Macintosh's serial port solo bottom half. ISICOM_BH MultiTech's ISI cards Linux Linux Botton Botton Halves Halves Autor: Alejandro Furfaro 65 Temporizaciones 1 El kernel lleva a cabo dos tipos de temporizaciones: 2 Mantiene la fecha y hora que retorna a los programas de aplicación como respuesta a las System Calls time (), ftime (), y gettimeofday () 2 Mantiene Timers, es decir, mecanismos que le notifique a él o a las aplicaciones que ha transcurrido un lapso de tiempo predeterminado Autor: Alejandro Furfaro 66 Temporizaciones 1 Hay 2 El tres relojes de hardware en una PC: RTC (Motorola 146818). i Usa IRQ8, para interrumpir desde 2 hasta 8192Hz. i Se accede por /sbin/clock i Actúa sobre el dispositivo /dev/rtc i Se accede en la address 70h y 71h de E/S 2 Time Stamp Counter i Registro homónimo (TSC) de 64 bits interno al procesador a partir del Pentium. Cuenta a la velocidad del clock 2 Programable Interval Timer (8254) i Usa IRQ0 i Es programado en el startup para interrumpir a 100 hz Autor: Alejandro Furfaro 67 Timer Interrupt Handler 1 Actualiza el tiempo transcurrido desde el startup 1 Actualiza Hora y Fecha 1 Determina cuanto hace que corre el proceso actual y eventualmente decide si lo “schedula”. 1 Actualiza los time stamps de uso de los recursos del sistema 1 Chequea los estados de los timers de software en uso por parte de las aplicaciones Autor: Alejandro Furfaro 68 Timer Interrupt Handler 1 Tiene 2 Una dos partes: en tiempo real i Actualiza tres variables 4 Jiffies -> ticks transcurridos desde el startup (el kernel la inicializa a 0). Roll Over 497 días.... 4 Lost_ticks -> cantidad de ticks transcurridos desde la última actualización de la estructura xtime 4 Lost_ticks_system -> cantidad de ticks transcurridos en Modo Kernel desde la última actualización de la estructura xtime 2 La otra diferida (botton half) i Actualiza fecha y hora i Actualiza estadísticas de uso del sistema i Actualiza el campo Counter de la copia del descriptor del proceso actual que guarda en la macro current. Si expiro el tiempo asignado schedula otro proceso Autor: Alejandro Furfaro 69 System Calls 1 1 1 API: Formato de una llamada para obtener un servicio del kernel System Call: requerimiento explícito cursado al kernel para resolver un servicio. La inversa no es necesariamente cierta. Algunas API resuelven directamente en Modo Usuario si pasar a Modo kernel para resolver el pedido (math.lib por ejemplo). Modo Usuario … func () … func () ¨{ … int 80h … } Accede a una rutina Aplicación Wrapper en la Invoca System Call librería standard libc Autor: Alejandro Furfaro Modo Kernel system call: … sys_func () … ret_from_sys_call: … iret sys_func () { … … … } handler de System Call del kernel rutina de servicio de la System Call 70 IPC (Inter Process Communication) 1 Como hacer que dos o más procesos intercambien información 1 Sincronización de Objetos. 1 Primero debemos contar con más de un proceso (fork y exec). 1 Warning: fork () -> poder, poder asociado gralmente a destruccion!!! 1 Cuidado con esto! Autor: Alejandro Furfaro 71 IPC, Señales 1 Los procesos pueden señalizarse entre sí o puede hacerlo el kernel. 1 Algunas señales no pueden ignorarse, otras pueden interceptarse y cambiar el handler. 1 Conjunto de SIG signals (kill –l) 1 Comando kill – permite enviar señales desde el prompt Autor: Alejandro Furfaro 72 IPC, Señales # 1 2 3 4 5 6 6 7 8 9 10 11 12 13 14 15 16 Nombre SIGHUP SIGINT SIGQUIT SIGILL SIGTRAP SIGABRT SIGIOT SIGBUS SIGFPE SIGKILL SIGUSR1 SIGSEGV SIGUSR2 SIGPIPE SIGALRM SIGTERM SIGSTKFLT Acción Default Terminate Terminate Dump Dump Dump Dump Dump Dump Dump Terminate Terminate Dump Terminate Terminate Terminate Terminate Terminate Autor: Alejandro Furfaro Descripción POSIX Se desconectó la terminal asociada al procesoSi Interrupción desde el teclado Si Quit desde el teclado (CTRL+C) Si Si Instrucción ilegal Breakpoint para debugging No Abnormal termination Si Equivalente a SIGABRT No Bus error No Floating-point exception Si Fuerza la terminación del proceso Si Dispponible para el proceso Si Referencia inválida a memoria Si Si Dispponible para el proceso Escritura en un pipe sin procesos lectores Si Real-timer clock Si Si Terminación de un Proceso Coprocessor stack error No 73 IPC, Señales # 17 18 19 20 21 22 23 24 25 26 27 28 29 29 30 31 31 Nombre SIGCHLD SIGCONT SIGSTOP SIGTSTP SIGTTIN SIGTTOU SIGURG SIGXCPU SIGXFSZ SIGVTALRM SIGPROF SIGWINCH SIGIO SIGPOLL SIGPWR SIGSYS SIGUNUSED Acción Default Ignore Continue Stop Stop Stop Stop Ignore Dump Dump Terminate Terminate Ignore Terminate Terminate Terminate Dump Dump Autor: Alejandro Furfaro Descripción Proceso Child STOPPED o terminó Reasume la ejecución, si estaba STOPPED Detiene al proceso (lo pone STOPPED) Idem SIGSTOP enviada por la tty (CTRL+Z) Proceso Background requiere entrada Proceso Background requiere salida Condición Urgent en un socket Límite de tiempo de CPU excedido Tamaño límite de Archivo excedido Virtual timer clock Profile timer clock Window resizing Ahora es posible una Operación de I/O Equivalente a SIGIO Power supply failure system call errónea Equivalente a SIGSYS POSIX Si Si Si Si Si Si No No No No No No No No No No No 74 Campos de task_struct relacionados con las señales Type Name Description spinlock_t sigmask_lock Protección por Spin Lock para pendientes y bloqueadas struct signal_struct * sig Puntero al descriptor de señal del proceso sigset_t blocked Máscara de las señales bloqueadas struct sigpending pending unsigned long sas_ss_sp size_t sas_ss_size int (*) (void *) notifier void * notifier_data Puntero al dato que debe usar la función de notificación (cambio prevo de la tabla) sigset_t * notifier_mask Máscara de Bits de señales bloqueadas por un device driver mediante la función de notificación Autor: Alejandro Furfaro Estructura de Datos para almacenar las señales pendientes. Dirección del stack del handler alternativo de la señal. Tamaño del stack del handler alternativo de la señal. Puntero a la función usada por un device driver para bloquear algunas señales del proceso 75 Campos de task_struct relacionados con las señales struct signal_struct { atomic_t struct k_sigaction spinlock_t }; struct sigpending { count; action[64]; siglock; struct sigqueue * head, * tail; sigset_t signal; } struct sigqueue { struct sigqueue * next; siginfo_t info; } Autor: Alejandro Furfaro 76 Control de las señales struct task_struct struct sigpending Head Tail Signal pending sig struct signal_struct count Descriptor de Proceso action struct sigqueue Head Next struct sigqueue Head Next struct sigqueue Head Next Info Info Info struct sigaction sa_handler sa_flags sa_mask siglock Descriptor de Señal Autor: Alejandro Furfaro 77 IPC, mecanismos 1 Algunas operaciones con IPC: 2 Pipes 2 Named Pipes (FIFOS) 2 File Locking 2 Sys V IPC's i Message queues i Semaphores i Shared Memory 2 Mem. Mapped Files 2 Sockets Autor: Alejandro Furfaro 78 “We're back to the times when men where men and wrote their own device drivers...” Device Drivers 1 Basicamente Linus Torvalds es código que se ejecuta en modo Kernel. 1 Es la mediación entre los dispositivos hard y los procesos del sistema o de usuario. 1 Linux puede incluirlo: 2 En el kernel monolítico (rapido y compacto) 2 Como modulos run time linkeables (flexible pero lento la primera vez que se usa). 1 Se tiende cada vez mas a estructura modular. 1 El driver se ocupa de resolver el mecanismo de acceso al hardware. No se concentra en la política de manejo de la información, aspecto que queda para el software de usuario Autor: Alejandro Furfaro 79 Device Drivers: inserción en el kernel Autor: Alejandro Furfaro 80 Device Drivers: Clasificación 1 Char devices 2 2 1 Se acceden como un stream de bytes, tal como si fuesen nodos del File System. Ej: TTY's (/dev/console). Serial ports (/dev/ttyS0) A diferencia de los archivos comunes, no nos podemos desplazar hacia atrás y hacia adelante. Accede a los datos en forma secuencial. Block devices 2 2 2 Se acceden en el directorio /dev igual que los char devices. La dferencia pasa por como el kernel maneja internamente los datos. Por lo regular es de a bloques (512 o 1024 bytes) Son dispositivos que pueden hostear un File System. Ej: Discos, Cintas. Autor: Alejandro Furfaro 81 Device Drivers: Clasificación 1 Network devices 2 2 1 Controlan las interfaces para transaccionar paquetes de datos en red contra un equipo remoto, sin conocer en detalle el mapa de transacciones que conforman esos paquetes. No siempre son de hardware (loopback por ejemplo) Al no ser un dispositivo orientado a stream, no es fácilmente mapeable en el /dev Miscelaneos 2 Algunos autores clasifican en una categoría especial , y diferentes de las tres básicas a los drivers de los controladores de buses, ya que son bastante particulares. PCI i USB i SCSI i Autor: Alejandro Furfaro 82 Device Drivers: Módulos 1 Escribir un device driver, es escribir código de kernel En modo kernel se dispone de un tipo especial de programa denominado Módulo de Kernel (kernel module) 1 Una aplicación convencional realiza una tarea única del principio hasta el fin. 1 Un módulo se registra a si mismo a fin de prestar servicios a futuro. Su función principal es efímera, pero queda “instalado” en el sistema 1 ¿impacientes? ..... Bueno ahí va.... #define MODULE #include <linux/module.h> int init_module(void) { printk("<1>¡Hola mundo! \n"); return 0; } void cleanup_module(void) { printk("<1>¡Adios mundo cruel!\n"); } Alejandro Furfaro Autor: 83 Device Drivers: Módulos 1 1 1 ¿y la función main?????? No usa. ¿entonces?...... Veamos como se compila y ejecuta (atentos con el prompt....) root# gcc -c hello.c root# insmod ./hello.o ¡Hola mundo! root# rmmod hello ¡Adios mundo cruel! root# 1 1 1 Insmod y rmmod, se utilizan para testear nuestro módulo. Insmod lo instala. Una vez ejecutado quedará registrado hasta ejecutar rmmod insmod hace que se ejecute la función init_module () (Con Uds.... La “función main” del módulo) rmmod hace ejecutar la función cleanup_module (). Autor: Alejandro Furfaro 84 Device Drivers: Módulos Autor: Alejandro Furfaro 85 Device Drivers: Esquema de llamadas al sistema Memoria User Mode System Cal API open() write() close() Autor: Alejandro Furfaro Kernel Mode File_ops Dev_open() Dev_write() Dev_close() Device outb() Ports 86 Char Devices: File Operations (1) • struct module *owner Es el primer campo de file_operations No es en sí mismo una operación Es un puntero al módulo “dueño” de la estructura. Se usa para evitar que el módulo sea cargado mientras sus operaciones están en uso. – A menudo se lo inicializa sencillamente con la macro THIS_MODULE, definida en <linux/module.h>. – – – – • loff_t (*llseek) (struct file *, loff_t, int); – El método llseek se usa para cambiar la posición actual de lectura/ escritura en un archivo – La nueva posición se retorna como un valor positivo – loff_t es un “long offset” y tiene al menos un ancho de 64 bits aún en plataformas de 32-bit. – Si se produce algún error en su ejecución retorna un valor negativo – Si este puntero se inicializa en NULL en file_operations, seek () modificará el contador de posición en la estructura file (de formas potencialmente impredecibles). 87 Autor: Alejandro Furfaro Char Devices: File Operations (2) • ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); – Lee datos desde un archivo o device. – Un puntero NULL en esta posición hace que la system call read () sobre este device devuelva -EINVAL (“Invalid argument”). – Un valor de retorno no negativo representa el número de bytes leídos • ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t); − Inicia una lectura asincrónica (puede no completarse antes de retornar). − Si es NULL, todas las operaciones serán ejecutadas en forma sincrónica por read (). • ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); – Envía datos a un archivo o device. – Si este puntero es NULL, la system call write () retorna -EINVAL al programa que la invoca – Un valor de retorno, no negativo, es el número de bytes escritos. 88 Autor: Alejandro Furfaro Char Devices: File Operations (3) • ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *); – Inicia una operación de escritura asincrónica sobre el device. • int (*readdir) (struct file *, void *, filldir_t); – Se usa para leer directorios. Solo lo usan los file systems. Debe ser NULL para cualquier device. • unsigned int (*poll) (struct file *, struct poll_table_struct *); – El método poll es el back end de tres system calls: poll (), epoll (), y select (). – Se usa para saber si un read () o un write () a uno o mas descriptores de archivo va a bloquear. – El método poll () debe retornar una máscara de bits que indica si son factibles lecturas o escrituras no bloqueantes. – El kernel con esta información pone un proceso en estado sleeping hasta que sea posible la operación de E/S. – Si un driver deja NULL este método, se asume que puede ser leído o escrito sin bloqueo. 89 Autor: Alejandro Furfaro Char Devices: File Operations (4) • int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); – La system call ioctl () envía comandos device específicos. – El kernel generalmente procesa ioctl () por medio del método definido en file_operations. – Si no hay un method ioctl (), la system call retorna error para cualquier requerimiento no predefinido (-ENOTTY, “No such ioctl for device”). • int (*mmap) (struct file *, struct vm_area_struct *); – mmap requiere el mapeo de un device de memoria al espacio de direcciones del proceso. – Si este método es NULL, la system call mmap () retorna -ENODEV. • int (*open) (struct inode *, struct file *); – Como SIEMPRE es la primer operación realizada sobre el archivo o device, no es necesario declararlo – Si es NULL, el device siempre se abre, pero no se notifica al driver. Autor: Alejandro Furfaro 90 Char Devices: File Operations (5) • int (*flush) (struct file *); – La operación flush () se invoca cuando un proceso cierra su copia del file descriptor de un device – Ejecuta (y espera por) cualquier operación excepcional sobre el device. – No confundir con la operación fsync () requerida por un programa. – flush () se usa en muy pocos drivers: el driver SCSI de cinta lo use, por ejemplo, para asegurar que todos los datos escritos estén en la cinta antes de cerrar el dispositivo – Si es NULL, el kernel simplemente ignora el requerimiento. • int (*release) (struct inode *, struct file *); – Se invoca cuando se desea liberar la estructura. – Igual que open () puede ser NULL. – release () no se invoca cada vez que un proceso llama a close (). Si una estructura file se comparte (como resultado de fork () o dup() ), release () se invoca cuando todas las copias ejecutan 91 close ().Furfaro Autor: Alejandro Char Devices: File Operations (6) • int (*fsync) (struct file *, struct dentry *, int); – Es el back end de la system call fsync (), que es llamada por un programa para flushear cualquier dato pendiente. Si es NULL, retorna -EINVAL. • int (*aio_fsync)(struct kiocb *, int); – Es la versión asincrónica del método fsync. • int (*fasync) (int, struct file *, int); – Se usa para notificar al device que cambió su flag FASYNC. – Puede ser NULL si el driver no soporta notificación asincrónica. • int (*lock) (struct file *, int, struct file_lock *); – Se usa para implementar file locking. – Es indispensable en archivos, pero rara vez se usa en drivers. Autor: Alejandro Furfaro 92 Char Devices: File Operations (7) • ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); • ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); – Implementan operaciones de lectura escritura fragmentada, que ocasionalmente necesitan involucrar múltiples áreas de memoria – Estas system calls fuerzan operaciones extra de copia sobre los datos. – Si estos punteros se dejan NULL, se llaman en su lugar los métodos read () y write () (quizá mas de una vez). • ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *); – Implementa el lado read de la system call sendfile (), que mueve los datos desde un file descriptor hacia otro con mínima copia – Se usa por ejemplo en un web server que necesita enviar los contenidos de un archivo fuera hacia la red. – Los device drivers Autor: Alejandro Furfaro normalmente la dejan en NULL. 93 Char Devices: File Operations (8) • ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); – sendpage es la otra mitad de sendfile; – El kernel la llama para enviar datos al archivo correspondiente, una página a la vez. – Los device drivers normalmente no implementan sendpage. • unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long); – El objetivo de este método es encontrar una ubicación adecuada en el espacio de direcciones del proceso para mapearla sobre un segmento de memoria del device. – Normalmente es el código de manejo de la memoria quien realiza esta tarea – Este método permite a los drivers forzar los requerimientos de alineamiento que pueda tener cualquier device. La mayoría de los drivers dejan este método NULL. Autor: Alejandro Furfaro 94 Char Devices: File Operations (9) • int (*check_flags)(int) – Permite al módulo chequear los flags que se le pasan en una llamada fcntl (F_SETFL...). • int (*dir_notify)(struct file *, unsigned long); – Se invoca cuando una aplicación usa fcntl () para pedir modificaciones en un directorio. – Sólo es útil en file systems – Los drivers no necesitan implementar dir_notify. Autor: Alejandro Furfaro 95 Char Devices: File Operations (10) Es la estructura principal para mapear el sistema de system calls del sistema operativo sobre el hardware • Declaradas en <linux/fs.h> struct file_operations struct file_operations midriver_fops = { midriver_fops = NULL, //lseek { midriver_read, .owner = THIS_MODULE, midriver_write, .read = scull_read, NULL, //readdir .write = scull_write, NULL, //poll .ioctl = scull_ioctl, midriver_ioctl, .open = scull_open, NULL, //mmap .release = scull_release, midriver_open, }; NULL, //flush midriver_release, NULL, //fsync NULL, //fasync NULL, //check_media_change NULL, //revalidate NULL, //lock 96 }; Autor: Alejandro Furfaro • Otras estructuras del sistema a considerar: struc file 1 Definida en <linux/fs.h> 1 Contiene la información lógica de un archivo abierto con open (). 1 Campos de interés para un char device 2 mode_t f_mode; //Modo en (FMODE_READ, FMODE_WRITE) que se abrió el archivo 2 loff_t f_pos; //Puntero de 64 bits offset dentro del archivo 2 unsigned int f_flags; //O_RDONLY, O_NONBLOCK, O_SYNC. 2 struct file_operations *f_op; 2 void *private_data; i open () la carga con NULL antes de llamar al método open propio del driver. i Se puede utilizar para guardar datos propios del driver 2 struct dentry *f_dentry; Autor: Alejandro Furfaroentry i Directory 97 Otras estructuras del sistema a considerar: struct inode 1 Definida en <linux/fs.h> 1 Contiene la información de un nodo del file system (no de un archivo abierto) 2 Campos i dev_t de interés para un char device i_rdev; //contiene el número de device (32 bits: 12 major number 20 minor number) i struct cdev *i_cdev; //es una estructura del LDM que representa a un char device. Si el inodo no contiene un char device este campo es NULL. 2 Para obtener el major y el minor number a partir de inode i unsigned Autor: Alejandro Furfaro i int iminor (struct inode *inode); d i i j ( i d *i d ) 98 Device Drivers: Módulos 1 El kernel de LINUX es concurente, por lo tanto un driver debe estar escrito con la idea que en un mismo instante ocurren varias cosas. Debe ser reentrante. 1 Desde el kernel no tenemos los recursos que usamos en las aplicaciones: 2 No se accede a las system call standard 2 No están disponibles los IPCs!!!! 1 Ejemplo. Para averiguar el proceso que invocó alguna de las funciones el driver, vamos a task_struct..... printk("The process is \"%s\" (pid %i)\n", current->comm, current->pid); 1 Es decir: Bienvenidos a la cocina del restaurante. ¿se entiende? Autor: Alejandro Furfaro 99 Device Drivers: Char devices 1 1 Deben existir como filesystem node en /dev Se crean con un comando especial: “mknod <nombre> <type> <Mn> <mn>” 1 Numero mayor y menor. crw-rw-rwcrw------crw------crw-rw-rwcrw-rw-rwcrw------crw------crw-rw-rw- 1 1 1 1 1 1 1 1 1 1 root root rubini root root root root root root root tty dialout dialout sys sys root 1, 10, 4, 4, 4, 7, 7, 1, 3 1 1 64 65 1 129 5 Feb Feb Aug Jun Aug Feb Feb Feb 23 23 16 30 16 23 23 23 1999 1999 22:22 11:19 00:00 1999 1999 1999 null psaux tty1 ttyS0 ttyS1 vcs1 vcsa1 zero El kernel usa el Major number para despachar la ejecución del driver correcto en el momento en que se ejecuta la función open () desde el proceso que lo desea acceder. El Minor number es usado por el driver. El kernel solo lo pasa al driver para que este lo utilice si lo necesita. Autor: Alejandro Furfaro 100 Device Drivers: Hands On! (1) struct file_operations ser_fops = { read: ser_read, write: ser_write, open: ser_open, release: ser_release }; int init_module() { if (register_chrdev(SER_MAYOR, "ser", &ser_fops)){ printk("<1> SER: init_module ha fallado instalando SER driver...\n"); return -EIO; } else { printk("<1> SER: modulo instalado!\n"); return 0; } } Autor: Alejandro Furfaro 101 Device Drivers: Hands On! (2) void cleanup_module (){ if (ser_busy) printk("<1> SER: driver ocupado, no se pudo remover.... \n"); else { if (unregister_chrdev(SER_MAYOR,"ser") != 0) printk("<1> SER: cleanup_module ha fallado, no se puede desregistrar SER deiver...\n"); else printk("<1> SER: modulo desinstalado!\n"); } } Autor: Alejandro Furfaro 102 Device Drivers: Hands On! (3) Método Open (1) static int ser_open(struct inode * inode, struct file * file) { int codigo; unsigned char bitpos = 1; // verifico minor igual a cero unsigned int minor = MINOR(inode->i_rdev); #ifdef SERDEB printk("<1> SER: abriendo...\n"); #endif if (minor != 0) return -ENODEV; // verifico semaforo en verde. Si está siendo usado if (ser_busy==SER_BUSY) return -EBUSY;//sale por error ser_busy=SER_BUSY; // lo marco ocupado codigo = request_irq (SER_IRQ, ser_irq, SA_INTERRUPT,"ser", NULL); //pido irq... if (codigo){ printk ("<1> SER: no se puede utilizar la interrupción %d\n",SER_IRQ); return codigo; Autor: Alejandro Furfaro 103 Device Drivers: Hands On! (4) Método Open (2) } else { // habilito mascara de interrupciones en el PIC master bitpos = ~(bitpos << SER_IRQ); outb (inb(0x21)&(bitpos),0x21); outb (0x20, 0x20); #ifdef SERDEB printk("<1> SER: IRQ registrada.\n"); #endif } //aqui setear la velocidad del puerto también se debería //guardar la velocidad anterior de la misma forma que la //mascara original outb(0x08, SER_MCR);//out2 del MCR outb(0x01, SER_IER);//habilita interrupciones del 8250 IER #ifdef SERDEB printk ("<1> SER: puerto abierto.\n"); #endif return 0; } 104 Autor: Alejandro Furfaro Device Drivers: Hands On! (5) Método Release static int ser_release (struct inode *inode, struct file *file) { unsigned char bitpos = 1; #ifdef SERDEB printk ("<1> SER: release.\n"); #endif ser_busy=SER_FREE; free_irq(SER_IRQ,NULL); //error check outb(0x00, SER_IER); //deshabilita interrupciones en el IER bitpos = (bitpos << SER_IRQ); outb (inb(0x21)|(bitpos),0x21); //libera mascara de PIC outb (0x20, 0x20); #ifdef SERDEB printk ("<1> SER: serial cerrado.\n"); #endif return 0; } Autor: Alejandro Furfaro 105 Device Drivers: Hands On! (6) Petición de IRQ al kernel 1int request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id); 2 Retorna 0 o un código negativo de error. (-EBUSY si otro driver está usando la IRQ pedida por ejemplo). 2 Argumentos i i i unsigned int irq: Número de la IRQ requerida. void (*handler) (int, void *, struct pt_regs *): Puntero a la función que se desea instalar como handler. unsigned long flags: Máscara de bits relacionada al manejo de interrupciones. 4 4 i i 1void SA_INTERRUPT, Indica que es un ‘‘fast’’ interrupt handler: Se ejecutará aún con las interupciones deshabilitadas SA_SHIRQ. Indica que la interrupción puede compartirse entre varios devices. const char *dev_name: Nombre del device en /dev. Lo usa en /proc/interrupts para mostrar el dueño de la IRQ. void *dev_id: Puntero usado para compartir IRQ’s. Cuando no se comparte IRQ se lo deja en NULL free_irq (unsigned int irq, void *dev_id); Autor: Alejandro Furfaro 106 Device Drivers: Hands On! (7) Método Read (1) int ser_busy = SER_FREE; // pongo una cola de espera #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) DECLARE_WAIT_QUEUE_HEAD(ser_wait); //cabeza de cola de espera #else struct wait_queue *ser_wait_q; ser_wait_q=NULL; #endif int readready = 0; static void ser_irq(int irq,void *dev_id,struct pt_regs *regs){ char testvalue; //despierta al que esta esperando la int SER_IRQ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) wake_up_interruptible(&ser_wait); #else wake_up_interruptible(&ser_wait_q); #endif testvalue=inb(SER_IIR); // ack de ints en 8250 leyendo IIR } Autor: Alejandro Furfaro 107 Device Drivers: Hands On! (8) Método Read (2) static ssize_t ser_read (struct file *file, char *buf, size_t count, loff_t *nose){ char testvalue; if (count != 1) return -EINVAL; // debe leer de a uno #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) interruptible_sleep_on(&ser_wait); #else interruptible_sleep_on(ser_wait_q); // espero interrupción #endif testvalue=inb(SER_IN); //lee lo que llego __copy_to_user(buf,&testvalue,count); //envío dato al user return count; } Autor: Alejandro Furfaro 108 Device Drivers: Hands On! (9) Colas de Espera 1 La lista runqueue agrupa a los procesos que están en estado de ejecución (TASK_RUNNING) 1 Los procesos que estén en TASK_ZOMBIE, o TASK_STOPPED no requieren ser agrupados ya que su padre los recupera a partir de su PID. 1 Los procesos que estén en estado TASK_INTERRUPTIBLE o TASK_UNINTERRUPTIBLE pueden ser agrupados según varios criterios, de modo que para ser recuperados se requiere mas información que la de su Descriptor de proceso. Autor: Alejandro Furfaro 109 Device Drivers: Hands On! (10) Colas de espera - ¿qué esperan? 1 El kernel utiliza a las colas de espera para: 2 Manejo de interrupciones (espera un carácter o un bloque de caracteres por un port) 2 Sincronización de procesos (espera a que se libere un recurso) 2 Temporización (espera que transcurrar una demora determinada) 1 Las colas de espera agrupan a los procesos en diferentes listas para esperar por eventos. Solo el kernel espera el evento. De este modo con la ayuda de las colas de espera el kernel “duerme” al proceso, hasta que llegue el evento. 1 El kernel detecta los eventos y despierta al proceso de la lista que lo esté esperando. Autor: Alejandro Furfaro 110 Device Drivers: Hands On! (11) Colas de espera: estructura 1 Para esperar un evento, un proceso crea una cola de espera. 1 El kernel 2.4, incorpora mejoras en el manejo de listas doblemente enlazadas, cuyos elemento apuntan a descriptores de procesos. 1 Cada lista comienza con un elemento de encabezado struct __wait_queue_head { spinlock_t lock;//previene locks en accesos concurrentes struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t; 1 Luego cada elemento es una estructura del tipo: struct __wait_queue { unsigned int flags; //1 para procesos exclusivos. 0 de otro modo struct task_struct * task; struct list_head task_list; }; typedef struct __wait_queue wait_queue_t; Autor: Alejandro Furfaro 111 Device Drivers: Hands On! (12) Colas de espera: estructura (2) 1 Procesos exclusivos: El kernel los despierta 1 Cuando se quiere definir una cola de mensajes se utiliza el siguiente código: # include <linux/wait.h> wait_queue_head_t my_queue; init_waitqueue_head (&my_queue); /*Otra forma de declarar una cola de espera es en forma estática (no como una variable automática de un procedimiento o como resultado de alocar dinámicamente una estructura ), se hace del siguiente modo: DECLARE_WAIT_QUEUE_HEAD (my_queue); */ Autor: Alejandro Furfaro 112 Device Drivers: Hands On! (13) Colas de espera: estructura (3) wait_queue_head_t Cola Colade deespera esperavacía vacía:: Ningún Ningúnproceso procesoduerme duermeen enella ella struct task_struct * task spinlock_t lock struct list_head task_list struct list_head task_list wait_queue_head_t struct task_struct * task spinlock_t lock Proceso Procesoactual actual(current) (current) durmiendo en una durmiendo en unacola colade de espera espera struct list_head task_list struct list_head task_list Diversos DiversosProcesos Procesosdurmiendo durmiendo en una misma cola de en una misma cola deespera espera wait_queue_head_t spinlock_t lock struct task_struct * task struct task_struct * task struct list_head task_list struct list_head task_list struct list_head task_list Autor: Alejandro Furfaro 113 Device Drivers: Hands On! (14) Colas de espera: funcionamiento (1) 1 El kernel utiliza un set de funciones para administrar las colas de mensajes: 2 init_waitqueue_head (q): (q) inicializa una cola de espera vacía. 2 add_wait_queue (q, entry): inserta un proceso no exclusivo con dirección entry a la cola q. 2 add_wait_queue_exclusive (q, entry): inserta un proceso exclusivo en la última posición de una cola 2 remove_wait_queue (q, entry): remueve un elemento con dirección entry a la cola q. 2 waitqueue_active( ): Chequea si una cola de espera está vacía Autor: Alejandro Furfaro 114 Device Drivers: Hands On! (15) Colas de espera: funcionamiento (2) 1 Cuando un proceso necesita esperar un evento tiene varias funciones a su disposición: 2 2 2 sleep_on(): () Pone al proceso en estado TASK_UNINTERRUPTIBLE, lo inserta en la cola de espera, e invoca al scheduler para que pase al siguiente proceso. Cuando el proceso insertado en la cola de espera se despierta, llama al scheduler para retomar la ejecución de ese proceso void sleep_on (wait_queue_head_t *q) { unsigned long flags; struct wait_queue_t wait; wait.flags = 0; wait.task = current; add_wait_queue (q, &wait); current->state = TASK_UNINTERRUPTIBLE; //Proceso sleeping. add_wait_queue(q, &wait); schedule( ); remove_wait_queue(q, &wait); } Interruptible_sleep_on(): Es idéntica a sleep_on () con la única diferencia que el proceso se pone en estado TASK_INTERRUPTIBLE sleep_on_timeout() e interruptible_sleep_on_timeout(): Iguales a las anteriores, le permiten a la aplicación definir y enviar como parámetro un tiempo máximo para la espera del evento. 115 Autor: Alejandro Furfaro ANEXOS Estructuras y piezas de código Autor: Alejandro Furfaro 116 task_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] struct task_struct { /* * offsets of these are hardcoded elsewhere - touch with care */ volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ unsigned long flags; /* per process flags, defined below */ int sigpending; typedef struct { mm_segment_t addr_limit; /* thread address space: unsigned long seg; 0-0xBFFFFFFF for user-thead } mm_segment_t; 0-0xFFFFFFFF for kernel-thread */ /*Process Excecution Domain */ struct exec_domain *exec_domain; volatile long need_resched; unsigned long ptrace; int lock_depth; /* Lock depth */ /* * offset 32 begins here on 32-bit platforms. We keep * all fields in a single cacheline that are needed for * the goodness() loop in schedule(). */ Autor: Alejandro Furfaro 117 task_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] long counter; long nice; unsigned long policy; struct mm_struct *mm; int processor; /* * cpus_runnable is ~0 if the process is not running on any * CPU. It's (1 << cpu) if it's running on a CPU. This mask * is updated under the runqueue lock. * * To determine whether a process might run on a CPU, this * mask is AND-ed with cpus_allowed. */ unsigned long cpus_runnable, cpus_allowed; /* * (only the 'next' pointer fits into the cacheline, but * that's just fine.) */ struct list_head run_list; unsigned long sleep_time; Autor: Alejandro Furfaro 118 task_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] struct task_struct *next_task, *prev_task; struct mm_struct *active_mm; struct list_head local_pages; unsigned int allocation_order, nr_local_pages; /* task state */ struct linux_binfmt *binfmt; int exit_code, exit_signal; int pdeath_signal; /* The signal sent when the parent dies */ /* ??? */ unsigned long personality; int did_exec:1; unsigned task_dumpable:1; pid_t pid; pid_t pgrp; pid_t tty_old_pgrp; pid_t session; pid_t tgid; /* boolean value for session group leader */ int leader; Autor: Alejandro Furfaro 119 task_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] /* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->p_pptr->pid) */ struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr; struct list_head thread_group; /* PID hash table linkage. */ struct task_struct *pidhash_next; struct task_struct **pidhash_pprev; wait_queue_head_t wait_chldexit; /* for wait4() */ /* for vfork() */ struct completion *vfork_done; unsigned long rt_priority; unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; struct timer_list real_timer; struct tms times; unsigned long start_time; long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; Autor: Alejandro Furfaro 120 task_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap; int swappable:1; /* process credentials */ uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; int ngroups; gid_t groups[NGROUPS]; kernel_cap_t cap_effective, cap_inheritable, cap_permitted; int keep_capabilities:1; struct user_struct *user; /* limits */ struct rlimit rlim[RLIM_NLIMITS]; unsigned short used_math; char comm[16]; /* file system info */ int link_count, total_link_count; /* NULL if no tty */ struct tty_struct *tty; unsigned int locks; /* How many file locks are being held */ /* ipc stuff */ struct sem_undo *semundo; struct sem_queue *semsleeping; Autor: Alejandro Furfaro 121 task_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] /* CPU-specific state of this task */ struct thread_struct thread; /* filesystem information */ struct fs_struct *fs; /* open file information */ struct files_struct *files; /* namespace */ struct namespace *namespace; /* signal handlers */ spinlock_t sigmask_lock; /* Protects signal and blocked */ struct signal_struct *sig; sigset_t blocked; struct sigpending pending; unsigned long sas_ss_sp; size_t sas_ss_size; int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; Autor: Alejandro Furfaro 122 task_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] /* Thread group tracking */ u32 parent_exec_id; u32 self_exec_id; /* Protection of (de-)allocation: mm, files, fs, tty */ spinlock_t alloc_lock; /* journalling filesystem info */ void *journal_info; }; Autor: Alejandro Furfaro 123 mm_segment_t (/usr/src/linux-[version]/includes/asm-i386/processor.h) -[version] typedef struct { unsigned long seg; } mm_segment_t; Autor: Alejandro Furfaro 124 exec_domain (/usr/src/linux-[version]/includes/linux/personality.h) -[version] struct exec_domain { const char handler_t unsigned char unsigned char unsigned long unsigned long struct map_segment struct map_segment struct map_segment struct map_segment struct module *module; struct exec_domain }; Autor: Alejandro Furfaro *name; handler; pers_low; pers_high; *signal_map; *signal_invmap; *err_map; *socktype_map; *sockopt_map; *af_map; *next; /* name of the execdomain */ /* handler for syscalls */ /* lowest personality */ /* highest personality */ /* signal mapping */ /* reverse signal mapping */ /* error mapping */ /* socket type mapping */ /* socket option mapping */ /* address family mapping */ /* module context of the ed. */ /* linked list (internal) */ 125 mm_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] struct mm_struct { struct vm_area_struct * mmap; /* list of VMAs */ rb_root_t mm_rb; struct vm_area_struct * mmap_cache; /* last find_vma result */ pgd_t * pgd; atomic_t mm_users; /* How many users with user space? */ atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ int map_count; /* number of VMAs */ struct rw_semaphore mmap_sem; spinlock_t page_table_lock; /* Protects task page tables and mm->rss */ struct list_head mmlist; /* List of all active mm's. These are globally strung together off *init_mm.mmlist, and are protected by mmlist_lock */ unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; unsigned long rss, total_vm, locked_vm; unsigned long def_flags; unsigned long cpu_vm_mask; unsigned long swap_address; unsigned dumpable:1; mm_context_t context; /* Architecture-specific MM context */ };Autor: Alejandro Furfaro 126 list_head (/usr/src/linux-[version]/includes/linux/list.h) -[version] struct list_head { struct list_head *next, *prev; }; Autor: Alejandro Furfaro 127 linux_binfmt (/usr/src/linux-[version]/includes/linux/binfmts.h) -[version] /* * This structure defines the functions that are used to load the binary formats that * linux accepts. */ struct linux_binfmt { struct linux_binfmt * next; struct module *module; int (*load_binary)(struct linux_binprm *, struct pt_regs * regs); int (*load_shlib)(struct file *); int (*core_dump)(long signr, struct pt_regs * regs, struct file * file); unsigned long min_coredump; /* minimal dump size */ }; Autor: Alejandro Furfaro 128 completion (/usr/src/linux-[version]/includes/linux/completion.h) -[version] struct completion { unsigned int done; wait_queue_head_t wait; }; Autor: Alejandro Furfaro 129 timer_list (/usr/src/linux-[version]/includes/linux/timer.h) -[version] /* * In Linux 2.4, static timers have been removed from the kernel. * Timers may be dynamically created and destroyed, and should be initialized * by a call to init_timer() upon creation. * * The "data" field enables use of a common timeout function for several * timeouts. You can use this field to distinguish between the different * invocations. */ struct timer_list { struct list_head list; unsigned long expires; unsigned long data; void (*function)(unsigned long); }; Autor: Alejandro Furfaro 130 tms (/usr/src/linux-[version]/includes/linux/times.h) -[version] struct tms { clock_t tms_utime; clock_t tms_stime; clock_t tms_cutime; clock_t tms_cstime; }; Autor: Alejandro Furfaro 131 user_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] /* * Some day this will be a full-fledged user tracking system.. */ struct user_struct { atomic_t __count; /* reference count */ atomic_t processes; /* How many processes does this user have? */ atomic_t files; /* How many open files does this user have? */ /* Hash table maintenance information */ struct user_struct *next, **pprev; uid_t uid; }; Autor: Alejandro Furfaro 132 rlimit (/usr/src/linux-[version]/includes/linux/resources.h) -[version] struct rlimit { unsigned long rlim_cur; unsigned long rlim_max; }; Autor: Alejandro Furfaro 133 tty_struct (/usr/src/linux-[version]/includes/linux/tty.h) -[version] struct tty_struct { int magic; struct tty_driver driver; struct tty_ldisc ldisc; struct termios *termios, *termios_locked; int pgrp, session; kdev_t device; unsigned long flags; int count; struct winsize winsize; unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; unsigned char low_latency:1, warned:1; unsigned char ctrl_status; struct tty_struct *link; struct fasync_struct *fasync; struct tty_flip_buffer flip; int max_flip_cnt; int alt_speed; /* For magic substitution of 38400 bps */ wait_queue_head_t write_wait; wait_queue_head_t read_wait; struct tq_struct tq_hangup; void *disc_data; Autor: Alejandro Furfaro 134 tty_struct (/usr/src/linux-[version]/includes/linux/tty.h) -[version] void *driver_data; struct list_head tty_files; #define N_TTY_BUF_SIZE 4096 /* The following is data for the N_TTY line discipline. For historical reasons, this is included in the tty structure./ unsigned int column; unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char closing:1; unsigned short minimum_to_wake; unsigned long overrun_time; int num_overrun; unsigned long process_char_map[256/(8*sizeof(unsigned long))]; char *read_buf; int read_head, read_tail, read_cnt; unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))]; int canon_data; unsigned long canon_head; unsigned int canon_column; struct semaphore atomic_read struct semaphore atomic_write; spinlock_t read_lock; struct tq_struct SAK_tq; /* If the tty has a pending do_SAK, queue it here - akpm */ }; Autor: Alejandro Furfaro 135 thread_struct (/usr/src/linux-[version]/includes/asm-i386/processor.h) -[version] struct thread_struct { unsigned long esp0; unsigned long eip; unsigned long esp; unsigned long fs; unsigned long gs; /* Hardware debugging registers */ unsigned long debugreg[8]; /* %%db0-7 debug registers */ /* fault info */ unsigned long cr2, trap_no, error_code; /* floating point info */ union i387_union i387; /* virtual 86 mode info */ struct vm86_struct * vm86_info; unsigned long screen_bitmap; unsigned long v86flags, v86mask, saved_esp0; /* IO permissions */ int ioperm; unsigned long io_bitmap[IO_BITMAP_SIZE+1]; }; Autor: Alejandro Furfaro 136 fs_struct (/usr/src/linux-[version]/includes/linux/processor.h) -[version] struct fs_struct { atomic_t count; rwlock_t lock; int umask; struct dentry * root, * pwd, * altroot; struct vfsmount * rootmnt, * pwdmnt, * altrootmnt; }; Autor: Alejandro Furfaro 137 files_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] /* * Open file table structure */ struct files_struct { atomic_t count; rwlock_t file_lock; /* Protects all the below members. Nests inside tsk->alloc_lock */ int max_fds; int max_fdset; int next_fd; struct file ** fd; /* current fd array */ fd_set *close_on_exec; fd_set *open_fds; fd_set close_on_exec_init; fd_set open_fds_init; struct file * fd_array[NR_OPEN_DEFAULT]; }; Autor: Alejandro Furfaro 138 namespace (/usr/src/linux-[version]/includes/linux/namespace.h) -[version] struct namespace { atomic_t struct vfsmount * struct list_head struct rw_semaphore }; Autor: Alejandro Furfaro count; root; list; sem; 139 signal_struct (/usr/src/linux-[version]/includes/linux/sched.h) -[version] struct signal_struct { atomic_t struct k_sigaction spinlock_t }; Autor: Alejandro Furfaro count; action[_NSIG]; siglock; 140 sigpending (/usr/src/linux-[version]/includes/linux/signal.h) -[version] struct sigpending { struct sigqueue *head, **tail; sigset_t signal; }; Autor: Alejandro Furfaro 141