29
1 Gestión de memoria Minix 3 Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Escuela Universitaria de Informática - Universidad Politécnica de Madrid. Capítulo IV. Pag. nº 1 •4 Gestión de memoria y procesos de usuario 4.1 Organización de la memoria (Tanenbaum 4.7.1) 4.2 Tratamiento de mensajes (Tanenbaum 4.7.2) 4.3 Estructuras de datos y algoritmos del Gestor de Procesos. (Tanenbaum 4.7.3) 4.4 Las llamadas al sistema fork, exit, y wait (Tanenbaum 4.7.4) 4.5 La llamada al sistema execve (Tanenbaum 4.7.5) 4.6 La llamada al sistema brk (Tanenbaum 4.7.6) 4.7 Tratamiento de señales (Tanenbaum 4.7.7) 4.8 Otras llamadas al sistema (Tanenbaum 4.7.8)

2-Gestion de Memoria en Minix 3

  • Upload
    lino-gh

  • View
    282

  • Download
    2

Embed Size (px)

Citation preview

Page 1: 2-Gestion de Memoria en Minix 3

1

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 1

• 4 Gestión de memoria y procesos de usuario4.1 Organización de la memoria (Tanenbaum 4.7.1)

4.2 Tratamiento de mensajes (Tanenbaum 4.7.2)

4.3 Estructuras de datos y algoritmos del Gestor de Procesos.

(Tanenbaum 4.7.3)

4.4 Las llamadas al sistema fork, exit, y wait (Tanenbaum 4.7.4)

4.5 La llamada al sistema execve (Tanenbaum 4.7.5)

4.6 La llamada al sistema brk (Tanenbaum 4.7.6)

4.7 Tratamiento de señales (Tanenbaum 4.7.7)

4.8 Otras llamadas al sistema (Tanenbaum 4.7.8)

Page 2: 2-Gestion de Memoria en Minix 3

2

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 2

Gestor de Gestor de ProcesosProcesos

Modo usuario•No utiliza paginación•Posible activación del uso de Intercambio•Comunicación por paso de mensajes•Implementa la estrategia de asignación de memoria:

Qué procesos residen en memoria y dónde•Dos funciones básicas:

•Gestión de laTabla de procesos y de la lista de huecos• Implementación de las llamadas al sistema:

fork, wait, execve, exit, brk, getpid, signal, kill, alarm, pause, getuid, getgid, setuid, setgid…

•Implementa el mecanismo de asignación de memoria: Copia de mapas de memoria

Modo supervisor

NNúúcleo cleo de Minixde Minix

Visión general

La gestión de memoria en Minix 3 es simple. En primer lugar, no se utiliza paginación en absoluto y en segundo lugar, aunque el código fuente completo incluye la posibilidad del uso de intercambio de memoria (“swapping”), por simplicidad, no se estudiará esta opción, ya que en la actualidad, la cantidad de memoria disponible en los sistemas, hace que en la práctica, ésta sea raramente necesaria. El Gestor de Procesos (“Process Manager”) es el proceso de Minix que se encarga de gestionar la memoria así como de manejar las llamadas al sistema relacionadas con la gestión de procesos. Algunas como “fork, exec y brk” muy relacionadas con la gestión de memoria, otras relacionadas con señales, y otras con características de los procesos, como el usuario y grupo propietario, tiempos de uso, etc. En cuanto a las estructuras de datos gestionadas, destacan la Tabla de Procesos, con sus campos específicos y la Lista de Huecos, la cual mantiene los huecos de memoria libres, ordenados ascendentemente por dirección de memoria. Sin el uso de Intercambio, la posición en memoria de un proceso no cambia durante toda su ejecución y tampoco aumenta o disminuye su asignación de espacio. Esta estrategia, aunque discutible, obedece principalmente a tres factores: 1) El deseo de mantener el sistema sencillo, 2) La arquitectura original del IMB PC (Intel 8088) y 3) Conseguir una fácil portabilidad a otros sistemas con distinto hardware.Hay que señalar un aspecto que diferencia a Minix de muchos otros sistemas operativos. El GP no es parte del núcleo (kernel), si no que es un proceso que corre en el espacio de usuario y se comunica con el núcleo mediante el mecanismo estándar del paso de mensajes. Esto permite separar la estrategia del mecanismo de asignación de memoria. La decisión de qué proceso y dónde se ubicará en memoria (estrategia) es responsabilidad del GP. El establecimiento de los mapas de memoria (mecanismo) lo lleva a cabo la Tarea del Sistema, la cual forma parte del núcleo.

Page 3: 2-Gestion de Memoria en Minix 3

3

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 3

Pilagap

Datos+bssCodigo

Pilagap

Datos+bssCódigoMinix

Dire

ccio

nes

Alta

s→

Prog. B I+D Separados

Prog. A I+D juntos

Segmento

Segmento

Segmento

Símbolos (opcional)Datos (inicializados)

Código (text)

Cabecera

Fichero ejecutable en disco

4.1 Organización de la memoria (1)

• Bit de I+D juntos o separados• Tamaños de memoria:

Total, Código, Datos, y BSS(El tamaño de memoria total en I+D juntos comprende la cantidad de memoria total necesaria para el proceso. En I+D separados, éste, no incluye el tamaño del código)

En Minix es posible compilar los programas para que éstos usen espacios de códigos y datos (I+D) juntos o separados. Con espacios I+D juntos todas las partes de un proceso, código (text), datos y pila, comparten un mismo bloque de memoria (que para el procesador INTEL será un segmento). Este bloque será asignado o liberado como un todo. Con espacios I+D separados, las partes de código, datos y pila de un proceso se agrupan en dos bloques (o segmentos para INTEL). En el primer bloque se ubica únicamente el código. En el otro bloque se ubican los datos y la pila (con un espacio dinámico entre ellos que denominaremos ‘gap’). Estos dos bloques o segmentos pueden ser asignados y/o liberados de forma independiente. Por ejemplo, en la llamada al Sistema “fork”, se ha de asignar la misma cantidad de memoria que usa el padre para el hijo. Si se utilizan espacios I+D juntos, el proceso es simple, se asigna un único bloque de igual tamaño que tiene el padre para el hijo. Si se utilizan espacios I+D separados, el bloque de código se puede compartir entre el padre y el hijo y por tanto no se tiene que asignar. Sólo se requiere asignar un bloque nuevo de igual tamaño que el del padre para el hijo, conteniendo las áreas de datos y pila (mas gap). Minix conoce el número de procesos que están usando el mismo segmento de código en un momento dado. Al finalizar un proceso siempre se libera su bloque de datos y pila (segmento de INTEL). El bloque (segmento de INTEL) de código sólo se liberaría, si éste bloque pasara a la situación de no ser usado por ninguno otro proceso.Los ficheros ejecutables en disco, aparte del código y datos inicializados, incorporan opcionalmente una tabla de símbolos (para depuración), y también, de forma obligatoria, una cabecera con información sobre las necesidades de memoria para las distintas partes del proceso, así como de la necesidad total de memoria, además de si el uso de espacios I+D es juntos o separados. Si los espacios son juntos, el tamaño de la memoria total se refiere a la cantidad de memoria total que requiere el proceso (incluye datos+bss+gap+pila). En espacios I+D juntos, la cantidad de memoria total incluye además el código. Por ejemplo, si un fichero con espacios I+D juntos tiene 4KB de código, 2KB de datos+bss, 1KB de pila y 40KB memoria total, el gap sería de 33KB. Si fueran separados el gap sería de 37 KB.El área BSS son datos del proceso que no requieren inicialización y por ello no se guardan en el archivo ejecutable.

Page 4: 2-Gestion de Memoria en Minix 3

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 4

Asignación de memoria.· Con las llamadas:

fork: la misma cantidad que al padreexecve: la cantidad indicada en la cabecera del fichero ejecutable

Liberación de memoria.· Cuando un proceso muere, bien mediante exit ó por la llegadade una señal cuyo tratamiento sea matar al proceso receptor.

· Con execve se libera si la llamada tiene éxito.

4.1 Organización de la memoria (2)

Gestor de Procesos

Prog. B

Prog. A

Minix

Dir.

Alta

s → Hijo de A

Prog. B

Prog. A

Minix

Prog. CProg. B

Prog. A

Minix

(a) (b) (c)

Mapas de memoria: (I+D juntos)(a) Originalmente, (b) Después de fork,(c) Después de execv del hijo.

En las operaciones normales de MINIX 3 se asigna memoria a un proceso sólo en dos ocasiones: Con la llamada “fork”, en la que asigna la cantidad de memoria que precisa el hijo y con la llamada “execve”, en la que se devuelve la memoria usada por la vieja imagen a la lista de huecos libres y se asigna memoria para la nueva imagen.La liberación de memoria ocurre en dos ocasiones: a) Cuando un proceso muere, bien sea por la llamada “exit” o la recepción de una señal y b) Durante la llamada “execve” si ésta tiene éxito.En la llamada “fork”, dependiendo de si los espacios I+D son juntos o separados, se asigna memoria para el proceso hijo de una forma u otra. Con espacios juntos, la memoria nueva asignada constituirá un solo bloque (segmento de Intel), del mismo tamaño que el del padre, el cual se corresponde con la memoria total utilizada por el padre, es decir, la suma de los tamaños del código, datos, gap y pila. Con espacios separados, se asigna un nuevo bloque para el hijo de igual tamaño que el de padre, pero este bloque (segmento) contiene únicamente las áreas de datos, gap y pila. El bloque de código (segmento) en cambio, no se asigna, ya que puede ser y es, compartido con el padre.En la llamada “execve” se asigna al proceso la cantidad de memoria indicada en la cabecera del fichero ejecutable, teniendo en cuenta si los espacios son juntos o separados, tal y como ya se ha contado. Hay que tener en cuenta en esta llamada, que si los espacios son separados se complica un poco la gestión, ya que al liberar la vieja imagen, tendrá que tenerse en cuenta si el bloque de código está siendo compartido por otro proceso, en cuyo caso no se liberaría éste último. Este problema no ocurre con espacios juntos, ya que al no haber bloques de código compartidos, siempre que se libera la imagen de memoria vieja, se libera de forma completa, ya que ésta constituye un único segmento (o bloque).Un problema similar ocurre con espacios separados cuando un proceso muere. También se ha de tener en cuenta si el bloque (segmento) de código está siendo usado por otro proceso o no, para liberarlo o no, en consecuencia.

Page 5: 2-Gestion de Memoria en Minix 3

5

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 5

4.2 Tratamiento de mensajes (1)Tipo mensaje Parámetros de entrada Valor devuelto

fork ninguno Pid hijo y 0 al hijoexit exit status Si éxito, ningunawait ninguno Statuswaitpid Identificador de proceso y flags Statusbrk Nuevo tamaño Nuevo tamañoexec Puntero pila inicial Si éxito, ningunakill Identificador proceso y señal Statusalarm Nº segundos a esperar Tiempo que quedapause ninguno Si éxito, ningunasigaction Nº de señal, accion, accion ant. Statussigsuspend Mascara de señal Si éxito, ningunasigpending ninguno Statussigprocmask Cómo, set y set anterior Statussigreturn contexto Statusgetuid ninguno Uid y uid efectivogetgid ninguno Gid y gid efectivogetpid ninguno Pid y pid del padre

El Gestor de Procesos, al igual que el resto de componentes de Minix 3, está dirigido por mensajes. Después de la inicialización, éste entra en el bucle principal, el cual consiste básicamente en: Esperar un mensaje, realizar la petición contenida en el mismo y enviar un mensaje de respuesta. Los mensajes recibidos pertenecen a dos categorías, mensajes de procesos de usuario y mensajes de notificación del Sistema. Siendo estos últimos usados para la comunicación de alta prioridad entre el “kernel” y los gestores (servidores de Minix).La mayoría de los mensajes recibidos por el GP resultan de llamadas al Sistema generadas por los procesos de usuario. Estos mensajes son los que se muestran en la transparencia. También sucede, como cabria esperarse, que la mayoría de las llamadas al Sistema que trata el Gestor de Procesos están relacionadas con la memoria, procesos o señales, no obstante, algunas que no lo están, como por ejemplo “time, stime, ptrace, etc”, simplemente se han incluido aquíporque el Gestor de Ficheros ya era bastante grande y complejo. El mensaje “reboot” tiene efectos en todo el S.O., pero su principal trabajo es enviar señales de terminación a todos los procesos de forma controlada, por eso lo trata el Gestor de Procesos. Lo mismo sucede con el mensaje “svrctl” que activa y desactiva el intercambio. Los mensajes “getsysinfo, getprocnr, memalloc, memfree, y getsetpriority” no están pensados para procesos de usuario ordinarios, y no forman parte de POSIX.En un sistema monolítico, las operaciones que realizan el tratamiento de estos mensajes están compiladas en el kernel como llamadas a función, pero en Minix 3, partes que normalmente son consideradas del Sistema Operativo, corren en espacio de usuario. Algunas de ellas hacen poco más que implementar un interfaz a una llamada al “kernel”, término que podemos usar para peticiones de servicio del kernel vía “Tarea del Sistema”.

Page 6: 2-Gestion de Memoria en Minix 3

6

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 6

4.2 Tratamiento de mensajes (2)Tipo mensaje Parámetros de entrada Valor devuelto

setuid Nuevo uid Statussetgid Nuevo gid Statussetsid Nuevo sid Grupo de procesogetpgrp Nuevo gid Grupo de procesotime Puntero al lugar donde poner el tiempo actual Statusstime Puntero al tiempo actual Statustimes Puntero a buffer para tiempos del proceso e hijo Tiempo desde el arranqueptrace petición, pid, dirección y datos Statusreboot Cómo (halt, reboot o panic) Si éxito, ningunasvrctl Petición, datos, (depend. Func.) Statusgetsysinfo Petición, datos, (depend. Func.) Statusgetprocnr ninguno Nº de procesomemalloc Tamaño, puntero a dirección Statusmemfree Tamaño, dirección Statusgetpriority Pid, tipo y valor Prioridadsetpriority Pid, tipo y valor Prioridadgettimeofday ninguno Tiempo

Existe una estructura de datos fundamental para el procesado de los mensajes: La tabla “call_vec”, la cual contiene los punteros a los procedimientos que tratan los diferentes mensajes.Cuando llega un mensaje al Gestor de Procesos (en el bucle principal), se extrae el tipo de mensaje y se pone en la variable global “call_nr”. Este valor -el tipo-, se usa como índice de la tabla “call_vec” para encontrar el puntero al procedimiento que tratará el mensaje recién llegado, para a continuación, hacer una llamada al mismo y ejecutar la llamada al Sistema. El valor devuelto por este procedimiento se incluye en un mensaje que se le envía al proceso llamante como respuesta, informándole así de la conclusión de la llamada y del éxito o fracaso (código de error) de la misma.

Page 7: 2-Gestion de Memoria en Minix 3

7

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 7

4.3 Estructuras de datos y algoritmos del Gestor de Procesos (1)

/* -------------------------------- Tabla de procesos ---------------------------------------Esta tabla tiene una entrada por proceso. Contiene toda la información de gestión deprocesos para cada proceso. Entre otras cosas, define los segmentos de código, datos y pila.Valores uids y gids, y varios flags.

*/EXTERN struct mproc {

struct mem_map mp_seg[NR_LOCAL_SEGS]; /* mapa del código, datos y pila */char mp_exitstatus; /* guarda status cuando el proceso acaba */char mp_sigstatus; /* guarda nº señal de procesos matados */pid_t mp_pid; /* identificador de proceso */pid_t mp_procgrp; /* pid de grupo de procesos (usado para señales)*/pid_t mp_wpid; /* pid del proceso que se está esperando */int mp_parent; /* indice del proceso padre *//* Tiempos de usuario y sistema de los hijos. Contabilizado en ‘exit’ del hijo. */clock_t mp_child_utime; /* tiempo de usuario acumulado de los hijos */clock_t mp_child_stime; /* tiempo de sistema acumulado de los hijos *//* uids y gids reales y efectivos. */uid_t mp_realuid; /* uid real del proceso */uid_t mp_effuid; /* uid efectivo del proceso */gid_t mp_realgid; /* gid real del proceso */gid_t mp_effgid; /* gid efectivo del proceso *//* Identificacion de fichero para compartir. */ino_t mp_ino; /* nº de inodo de fichero */dev_t mp_dev; /* nº de dispositivo del sistema de ficheros */time_t mp_ctime; /* cambio de tiempo en inodo */. . ./* Continua. Sig. Transp…*/

Existen dos estructuras básicas que usa el Gestor de Procesos: La tabla de procesos (mantenida en la variable “mproc”) y la tabla de huecos. Algunos de los campos de la tabla de procesos los necesita el “kernel”, otros el Gestor de Procesos y otros el Gestor de Ficheros. En Minix 3, cada una de estas tres partes del S.O. tiene su propia tabla de procesos, en la que únicamente se tienen los campos que necesitan. Salvo algunas excepciones, las entradas en la tabla se corresponden exactamente. Así la posición ‘k’ de la tabla del G.P. se refiere al mismo proceso que la posición ‘k’ de la tabla del G.F.. Cuando se crea o destruye un proceso, las tres partes actualizan sus tablas para reflejar la nueva situación consistentemente. Las excepciones son procesos que no son conocidos fuera del “kernel”, como las Tareas de Reloj y de Sistema, o bien los “falsos” procesos IDLE y KERNEL (ocupan entrada pero no tienen código). En la tabla del “kernel”, estas excepciones tienen asignados números negativos de entrada. Estas entradas no existen ni en el G.P. ni en el G.F.. Así, lo dicho anteriormente acerca de la posición ‘k’, es estrictamente cierto para valores mayores o iguales a cero. Otros procesos de Minix como el Gestor de Procesos y el Gestor de Ficheros tienen asignadas las entradas 0 y 1 respectivamente en todas las tablas.Un campo a destacar de la tabla de procesos del G.P. es el vector “mp_seg”, el cual tiene tres elementos para expresar los segmentos de código, datos y pila (no confundir el término segmento de Minix, que hemos denominado anteriormente ‘bloque’, con el mismo término segmento de INTEL). Cada elemento a su vez, es una estructura que contiene las direcciones virtual y física, así como la longitud de cada segmento, todo ello expresado en ‘clics’. El tamaño de un ‘clic’ es dependiente de la implementación, siendo en Minix-3 de 1024 bytes. Todos los segmentos ocupan un número entero de clics y empiezan siempre en una dirección múltiplo del tamaño del ‘clic’.

Page 8: 2-Gestion de Memoria en Minix 3

8

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 8

4.3 Estructuras de datos y algoritmos del Gestor de Procesos (2)

/* Continuación anterior transparencia */. . ./* Información de manejo de señales. */sigset_t mp_ignore; /* 1 significa ignorar la señal, 0 no */sigset_t mp_catch; /* 1 significa capturar la señal, 0 no */sigset_t mp_sig2mess; /* 1 significa transformar en mensaje de notificación */sigset_t mp_sigmask; /* señales a bloquear */sigset_t mp_sigmask2; /* copia segura de ‘mp_sigmask’ */sigset_t mp_sigpending; /* señales pendientes de manejar */struct sigaction mp_sigact[_NSIG + 1]; /* como en ‘sigaction(2)’ */vir_bytes mp_sigreturn; /* dirección de la librería C funcion ‘__sigreturn’ */struct timer mp_timer; /* temporizador ‘watchdog’ para ‘alarm(2)’ *//* compatibilidad con anteriores versiones para señales. */sighandler_t mp_func; /* todas las señales vectorizadas a una única func. de usuario */unsigned mp_flags; /* flag bits */vir_bytes mp_procargs; /* puntero a argumentos en pila iniciales del proceso */struct mproc *mp_swapq; /* cola de procesos esperando intercambio */message mp_reply; /* mensaje de respuesta para enviar *//* Prioridad de planificación. */signed int mp_nice; /* ‘nice’ es PRIO_MIN..PRIO_MAX, estándard 0. */char mp_name[PROC_NAME_LEN]; /* nombre de proceso */

} mproc[NR_PROCS];

El vector “mp_seg” permite traducir las referencias de direcciones virtuales a direcciones físicas. Dada una dirección virtual y el proceso al que pertenece, es sencillo verificar si se trata de una dirección que cae dentro del mapa del proceso, y en ese caso traducirla a dirección física de memoria. Más adelante se muestra una transparencia con un ejemplo de uso de este vector.La tabla “mproc” mantiene también otra información necesaria para la gestión de lo procesos. Esta incluye identificadores de proceso, los uid’s y gid’s (identificadores de usuario y grupo, reales y efectivos), información sobre las señales, y el estado de finalización si el proceso quedara en estado “zombie” (cuando muere y su padre no estáesperando por él). Hay campos para un temporizador para “sigalarm” y campos para contabilizar los tiempos de usuario y sistema empleados por los procesos hijos. (en anteriores versiones de Minix esto último era responsabilidad del kernel).La mayoría de los campos están descritos adecuadamente en su comentario. Algunos campos tratan sobre el manejo de señales, “mp_ignore, mp_catch, mp_sig2mess, mp_sigmask, mp_sigmask2, y mp_sigpending” son mapas de bits (bitmaps), en los que cada bit representa una de las señales. Actualmente hay 22 señales definidas, aunque algunas no están soportadas, según permite el estándar POSIX. La señal 1 se corresponde con el bit menos significativo. En cualquier caso, POSIX requiere funciones estándar para añadir o quitar miembros del conjunto de señales representado en estos “bitmaps”, tal que su manipulación sea transparente en sus detalles al programador. El array “mp_sigact” es importante para el manejo de señales, tiene un elemento para cada tipo de señal del tipo “estructura sigaction”.El campo “mp_flags” se usa para guardar un conjunto variado de bits. El campo es unentero sin signo, de 16 bits en un procesador de baja capacidad y de 32 procesadores superiores.

Page 9: 2-Gestion de Memoria en Minix 3

9

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 9

4.3 Estructuras de datos y algoritmos del Gestor de Procesos (3)

Proceso “i”campo campo

‘‘mp_segmp_seg’’

Código

Datos

Pila210 KB (0x34800)

208 KB (0x34000)

207 KB (0x33C00)

203 KB (0x32C00)

200 KB (0x32000)

I+D juntos I+D separados

Dirección virtual, física y tamaño de cada segmento interno, medido todo ello en “clics”.(1 clic = 1024 bytes) (datos en hexadecimal)

Virtual Física Long.Código 0x00 0xC8 0x00Datos 0x00 0xC8 0x07

Pila 0x08 0xD0 0x02

Virtual Física Long.Código 0x00 0xC8 0x03Datos 0x00 0xCB 0x04

Pila 0x05 0xD0 0x02

Respecto a dir. fisica de datos: D0 – C8 = 08

Respecto a la dir. fisica de datos: D0 - CB = 05

Gestor de Procesos

Tabla de procesos Lista de huecos

El método usado para registrar la ubicación en memoria de los procesos se puede ver en la transparencia. En este ejemplo se muestra un proceso de 3 KB de código, 4 KB de datos, 1 KB de hueco (gap) y una pila de 2 KB. Las direcciones virtuales de los segmentos de código y datos son siempre 0. El tamaño del segmento de código en el caso de I+D juntos es siempre 0 (ya que el código está en el segmento de datos, es como decir que no hay segmento de código). El tamaño del segmento de datos, en este mismo caso, tiene la suma de los tamaños de código y datos (3+4=7). La dirección virtual de la pila se calcula siempre como la diferencia entre las direcciones físicas del comienzo de la pila y el comienzo del segmento de datos (que en el caso de I+D juntos coincide también con el comienzo del segmento de código). Cuando un proceso hace referencia a la dirección virtual 0, tanto en el espacio de código como en el de datos, se usará la dirección física de comienzo correspondiente al segmento referido, es decir la dirección 0x32000 (= 200 KB, = 0xC8 clic), si es en el espacio de código, ó 0x32C00 (= 203 KB, = 0xCB clic), si es en el espacio de datos. Dada una dirección virtual y el espacio al que pertenece, es fácil determinar si dicha dirección virtual es legal o no (que pertenezca al segmento), y si es legal, determinar cual es su dirección física.Nótese que la dirección virtual en la que comienza la pila depende de la cantidad total de memoria reservada para el proceso. Si se usara el comando “chmem” para modificar la cabecera del fichero para dotar al proceso de más espacio dinámico (más gap), la siguiente vez que se ejecutara, la pila comenzaría en una dirección virtual más alta. Si la pila creciera 1 clic, la tripleta de pila [0x8, 0xD0, 0x2] pasaría a ser [0x7, 0xCF, 0x3], esto reduciría a la nada el gap si no se modificase la cantidad total de memoria del proceso (10 KB).

Page 10: 2-Gestion de Memoria en Minix 3

10

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 10

4.3 Estructuras de datos y algoritmos del Gestor de Procesos (4)

Estructuras ‘hole’ en lista ordenadapor dirección base ascendente.

Dirección base(en clics) ‘h_base’

Tamaño del hueco(en clics) ‘h_len’

Puntero al siguiente‘h_next’

Gestor de Procesos

Lista de huecos del GP

hole_head

#define NR_PROCS 100#define NR_HOLES (2*NR_PROCS+4) /* max elementos en tabla ‘hole’*/#define NIL_HOLE (struct hole *) 0PRIVATE struct hole {

struct hole *h_next; /* puntero al siguiente */phys_clicks h_base; /* comienzo del hueco */phys_clicks h_len; /* longitud del hueco */} hole [NR_HOLES];

PRIVATE struct hole *hole_head; /* puntero al primer hueco */PRIVATE struct hole *free_slots; /* primera posición sin usar */

Declaracion en ‘C’ de la estructura de datos para la lista de huecos

Prog. C

Prog. B

Prog. A

Minix

La otra estructura de datos principal del Gestor de Procesos es la tabla de huecos, la cual mantiene una lista de todos los huecos de memoria en orden ascendente de dirección de memoria.Los huecos entre los datos y pila de un proceso no son considerados huecos, ya que ya

han sido asignados a los procesos, y por ello consecuentemente, no figuran en la lista de huecos.Tanto la dirección base del hueco como el tamaño del mismo vienen expresados en ‘clics’en vez de en bytes. La razón de ello es porque es mucho más eficiente. Por ejemplo, si se usan enteros de 16 bits para almacenar direcciones de memoria, con unidades de 1024 bytes por clic, sería posible hacer referencia hasta 64 MB en vez de sólamente 64 KB.

Page 11: 2-Gestion de Memoria en Minix 3

Gestión de memoria Minix 3

4.3 Estructuras de datos y algoritmos del Gestor de Procesos (5)

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 11

free_slots

hole_head

30010

34040

Null

200 2040 clics

C30 clics

B20 clics

A60 clics

20 clics

10 clics

200

MINIX

La variable “hole_head” apunta al primer elemento del vector ‘hole’ (de 204 elementos), el cual contiene el primer hueco. La variable “free_slots” apunta al primer elemento “hole” libre (slot). “hole_head” establece el comienzo de la lista de huecos y ‘”free_slots” el de la lista de elementos “hole” (slots) que no están asociados a ningún hueco de memoria, es decir, libres.

11

Page 12: 2-Gestion de Memoria en Minix 3

12

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 12

4.3 Estructuras de datos y algoritmos del Gestor de Procesos (5)

254 KB

Prog. B

500 KB

Prog. AMinix

0x0302

0x02D0

0x00DC

0x00B4

0x0000

Memoria clics

D. Base Tamaño

00DC 01F4

hole_head

0302 00FE

Null

254 KB

Prog. B

400 KB

Prog. CProg. AMinix

0x0302

0x02D0

0x0140

0x00DC

0x00B4

0x0000

Memoria clics

D. Base Tamaño

0140 0190

hole_head

0302 00FE

Null

804 KB

Prog. AMinix

0x00DC

0x00B4

0x0000

Memoria clics

D. Base Tamaño

hole_head

00DC 0324

Null

(1)Situación Inicial de la memoria y lista de huecos

(2)Situación tras

ubicar el programa‘C’ de 100 KB’

(3)(3)Situación tras

La muerte de losProgramas ‘B’ y ‘C’

Las principales operaciones en la lista de huecos son: a) Reservar una cantidad de memoria y b) Devolver una cantidad previamente asignada. Para reservar memoria, se busca en la lista -que se halla ordenada ascendentemente por direcciones de memoria-, desde el principio en adelante, un hueco que satisfaga la necesidades de memoria. Una vez seleccionado el hueco, si el tamaño no fuese exacto, se reduciría el tamaño del mismo en la cantidad solicitada y el hueco permanecería en la lista con el nuevo tamaño residual. Si el tamaño fuese exacto, el hueco desaparecería de la lista. Este esquema es rápido y simple pero sufre tanto de fragmentación interna (hasta 1023 bytes de posible desperdicio) como de fragmentación externa.Cuando un proceso finaliza, si se han usado espacios I+D juntos, su memoria se libera y se devuelve a la lista de huecos como un todo. Si los espacios son separados, el segmento de datos se devuelve a la lista de huecos, pero la liberación del segmento de código dependerá de que no se encuentre otro proceso que lo esté compartiendo. Si se encontrara, éste no se liberaría. Los segmentos de código y datos no tienen porqué ser contiguos, y sería posible por tanto devolver dos regiones de memoria. Para cada región devuelta se comprueba si en su vecindad hay algún hueco, y si así fuera, se fusionarían dicho hueco o huecos con la región devuelta, quedando en la lista un único hueco de tamaño suma de la región o regiones vecinas y la propia región devuelta. De este modo nunca hay huecos adyacentes en la lista.En la transparencia se muestra un ejemplo de reserva y liberación de espacio de memoria. El tamaño del clic, como ya se ha dicho, es de 1024 bytes. Minix ocupa 180 KB, Los programas ‘A’, ‘B’ y ‘C’ ocupan 40 KB, 50 KB y 100 KB respectivamente, aunque esta información se puede obtener fácilmente desde la figura.

Page 13: 2-Gestion de Memoria en Minix 3

13

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 13

4.4 Las llamadas al sistema (Bucle principal GP, 1/4)

PUBLIC int main() { /* Rutina “main” del Gestor de Procesos */int result, s, proc_nr; struct mproc *rmp; sigset_t sigset;

pm_init(); /* Inicializa tablas del Gestor de Procesos *//* Este el el bucle principal : obtiene trabajo y lo hace, indefinidamente */while (TRUE) {

get_work(); /* Espera mensaje para el Gestor de Procesos *//* Comprueba notificaciones del Sistema primero. Casos especiales */if (call_nr == SYN_ALARM) {

pm_expire_timers(m_in.NOTIFY_TIMESTAMP); result = SUSPEND; /* no responder */

} else if (call_nr == SYS_SIG) { /* señales pendientes */sigset = m_in.NOTIFY_ARG; if (sigismember(&sigset, SIGKSIG)) (void) ksig_pending();result = SUSPEND; /* no responder */

} /* Sino, si el nº de llamada al Sistema es válido, la lleva a cabo */else if ((unsigned) call_nr >= NCALLS) {

result = ENOSYS; /* Nº llamada erróneo */} else {

result = (*call_vec[call_nr])(); /* LLama a la función “do_...” */}/* Envía respuesta de finalización al usuario */if (result != SUSPEND) setreply(who_p, result);

/* Continua . . . . . */

El Gestor de Procesos se compila y monta de forma independiente del “kernel” y del Gestor de Ficheros, por tanto tiene su propia función “main”que comienza después de que el kernel haya finalizado su inicialización. Después de efectuar su propia inicialización, mediante “pm_init()”, se entra en un bucle, en el cual se llama a “get_work()”, para esperar algún mensaje entrante. Entonces se llama a una de las funciones do_xxx(), por medio de la tabla de punteros a función “call_vec” para llevar a cabo la petición. Finalmente se envía una respuesta, si se requiere.Este escenario está algo simplificado. Mensajes de notificación pueden ser enviados a cualquier proceso. Estos están identificados por valores especiales en el campo “call_nr”. En el código puede verse la comprobación de estos mensajes y la acción especial que se toma en su caso. También se comprueba que “call_nr” tenga un valor correcto, ya que aunque un valor incorrecto es improbable, la comprobación no es costosa y se evita un error grave.Finalmente, aunque el comentario de la línea donde aparece “setreply”dice que se envía la respuesta de finalización, esto es algo más complicado. La función “setreply()” construye una respuesta en la entrada de la Tabla de Procesos (TP) del proceso actual. Más adelante (siguiente transparencia) hay un bucle donde se recorre la TP y se comprueba si hay respuestas (mensajes) pendientes de enviar y se envían, omitiéndose aquellas que no pueden enviarse en ese momento.

Page 14: 2-Gestion de Memoria en Minix 3

14

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 14

4.4 Las llamadas al sistema (Bucle principal GP. 2/4)

/* Continuación …… */swap_in(); /* Quizá un proceso pueda entrar en memoria (intercambio) */

/* Da salida a todos los mensajes pendientes de envío, incluyendo la respuesta a la llamada recien* hecha arriba. Los procesos no deben estar fuera de la memoria (“not swapped out”)*/

for (proc_nr=0, rmp=mproc; proc_nr < NR_PROCS; proc_nr++, rmp++) {/* Mientras tanto, el proceso puede haber muerto por una señal (e.g. si una señal letal* pendiente hubiera sido desbloqueada) sin que el GP lo supiera. Si la entrada en la TP * está libre o como “zombie”, no se responde.*/

if ((rmp->mp_flags & (REPLY | ONSWAP | IN_USE | ZOMBIE)) ==(REPLY | IN_USE)) {

if ((s=send(rmp->mp_endpoint, &rmp->mp_reply)) != OK) {printf("PM can't reply to %d (%s)\n",

rmp->mp_endpoint, rmp->mp_name);panic(__FILE__, "PM can't reply", NO_NUM);

}rmp->mp_flags &= ~REPLY;

} /* end-if*/} /*end-for*/

} /*end-while*/return(OK);

} /*end-main*/

Otro punto a hacer notar es la llamada a “swap_in”. Esta llamada está vacía si la configuración de Minix no incluye “intercambio de memoria”. Pero si Minix se configura para incluir todo el fuente con intercambio activado, es aquí donde se realiza la comprobación de si un proceso puede ser intercambiado a memoria.

Page 15: 2-Gestion de Memoria en Minix 3

15

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 15

Las llamadas al sistema (Bucle principal GP. 3/4)

PRIVATE void get_work() /* Espera el siguiente mensaje y extrae información útil de él */{if (receive(ANY, &m_in) != OK) panic(__FILE__,"PM receive error", NO_NUM);who_e = m_in.m_source; /* quién envió el mensaje */if(pm_isokendpt(who_e, &who_p) != OK)

panic(__FILE__, "PM got message from invalid endpoint", who_e);call_nr = m_in.m_type; /* Nº de llamada al Sistema */

/* ‘Slot’ (entrada en la tabla de procesos) del proceso llamador (caller). Se usa el ‘slot’ propiodel Gestor de Procesos si es el kernel el que hace la llamada. Esto puede suceder en el caso de alarmas sincronas (CLOCK), o señales del kernel pendientes tipo evento (SYSTEM) */

mp = &mproc[who_p < 0 ? PM_PROC_NR : who_p]; /* mp es el puntero al ‘slot’ */if(who_p >= 0 && mp->mp_endpoint != who_e) {

panic(__FILE__, "PM endpoint number out of sync with source",mp->mp_endpoint);

} /*end-if*/}

Los procedimientos “get_work” y “setreply” manejan de hecho, la recepción y envío de mensajes respectivamente. El primero efectúa un pequeño truco para hacer parecer que un mensaje del kernel lo era en realidad del propio Gestor de Procesos, ya que el kernel no tiene una entrada (slot) propia en la tabla de procesos. La otra función no envía en realidad la respuesta, simplemente la prepara, para que pueda ser enviada más tarde, tal y como ya se ha indicado.

Page 16: 2-Gestion de Memoria en Minix 3

16

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 16

Las llamadas al sistema (Bucle principal GP. 4/4)

PUBLIC void setreply(proc_nr, result)int proc_nr; /* Proceso a responder */int result; /* Resultado de la llamada (normalmente OK, o nº error) */{/* Cumplimenta un mensaje de respuesta para ser enviado más tarde a un proceso de usuario.

Las llamadas al Sistema pueden ocasionalmente rellenar otros campos, esto es únicamentepara retornar el valor al bucle “main” y también establecer el flag “must send reply”.

*/register struct mproc *rmp = &mproc[proc_nr];

if(proc_nr < 0 || proc_nr >= NR_PROCS)panic(__FILE__,"setreply arg out of range", proc_nr);

rmp->mp_reply.reply_res = result;rmp->mp_flags |= REPLY; /* Respuesta pendiente */

if (rmp->mp_flags & ONSWAP)swap_inqueue(rmp); /* Se debe intercambiar el proceso, traer de vuelta a memoria */

}

Page 17: 2-Gestion de Memoria en Minix 3

17

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 17

4.4 Las llamadas al sistema, fork, exit y wait: (fork)

Gestor de Procesos

Proceso de Usuario

Tarea del Sistema

Gestor de Ficheros

Significado de las flechasSignificado de las flechas: send_rec (…) send (…)

▪ Comprueba tablade procesos llena

▪ Reserva memoriapara datos y piladel hijo

▪ Solicita copiardatos a la TS

SYS_

VIRC

OPY

SYS_

VIRC

OPY

▪ Busca entrada libreen la TP para el hijoy copia la entrada del padre en ella.

▪ Cumplimenta el nuevomapa de memoria delhijo en su entrada.

▪ asigna un nuevo ‘pid’para el hijo.

SYS_

FORK

SYS_

FORK

Tell_

fs(F

ORK

,.)

Tell_

fs(F

ORK

,.)

SYS_

NEW

MAP

SYS_

NEW

MAP

Proceso hijo

▪ Copia datos y piladel padre a la imagende memoria del hijo

▪ Informa a laTS del nuevoproceso

▪ Crea proceso hijoen su propia Tabla

▪ Informa alGF del nuevoproceso

▪ Crea proceso hijoen su propia Tabla

▪ Avisa a la TSpara que copieel nuevo mapade memoria

▪ Copia el nuevomapa desde la

TP del GP a la TPdel kernel

Cuando se crea un proceso la Tabla de Procesos (TP) debe ser actualizada, lo que incluye también a las TP’s del Kernel y Gestor de Ficheros (GF). El Gestor de Procesos (GP) coordina esta actividad. La creación de procesos se efectúa mediante “fork”, tal y como se muestra, a grandes rasgos, en la transparencia. Es difícil e inconveniente detener una llamada “fork” en curso, por lo que se han de hacer una serie de comprobaciones para asegurar que la llamada no falla. En primer lugar (1) se comprueba que haya sitio en la TP. El GP lleva una contador de procesos creados, así es fácil saber si se puede crear uno nuevo. Si hay sitio en la TP (2) se reserva memoria para el nuevo proceso, (si I+D separados no se reserva espacio para el código). Si hubo éxito en la reserva la llamada “fork” no fallará, entonces (3) se rellena el espacio memoria reservado (copiando la imagen de memoria del padre). (4) Se ubica una nueva entrada en la TP y se rellena copiándose de la entrada del padre, actualizando convenientemente los campos “mp_parent, mp_flags, mp_child_utime, mp_child_stime, (5) mp_seg (nuevo mapa de memoria), mp_exitstatus y mp_sigstatus” (algunos bits de “mp_flags” se heredan). (6) Se escoge un “pid”, este paso no es trivial; el “pid” empieza en 1 incrementándose hasta un valor máximo de 30.000, cuando se alcanza este valor, se vuelve a empezar a partir del 2, comprobando que no esté en uso, si lo estuviera se incrementaría en 1 y se volvería a comprobar, y así sucesivamente hasta encontrar uno libre. (7) Se informa a las otras partes de Minix (kernel y GF) del nuevo proceso creado, para que éstas a su vez puedan actualizar sus TP’s. Por último, (8) se prepara la respuesta, primero al proceso hijo con pid=0, y luego al padre, con el “pid” asignado al hijo. El orden en el que se realizan las respuestas dependerá de la posición que ocupen los procesos padre e hijo en la tabla de procesos. Se envía primero el que ocupa una posición mas baja.

Page 18: 2-Gestion de Memoria en Minix 3

18

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 18

4.4 Las llamadas al sistema, fork, exit y wait: (exit)

Gestor de Procesos

Proceso de Usuario

Tarea del Sistema

Gestor de Ficheros

Significado de las flechasSignificado de las flechas: send_rec (…) send (…)

¿ ALARMAPENDIENTE ?

SYS_

SETALA

RMSY

S_SE

TALA

RM

▪Solicitatiempos decpu a la TS

Proceso padre

▪Desactiva alarma

▪Contabilizatiempos decpu al padre

▪Notifica ala TS del finde ejecución

▪Detiene elproceso

▪Informa alGF del findel proceso

▪Informa ala TS del findel proceso

▪Libera memoria delproceso. Segmentode Datos + Pila ysegmento de Códigosi no está más en uso▪Guarda en entrada

de TP “exit_status”

SYS_

TIM

ESSY

S_TIM

ES

SYS_

NIC

ESY

S_NIC

E

Tell_

fs (EX

IT)

Tell_

fs (EX

IT)

SYS_

EXIT

SYS_

EXIT

SI : --- “cleanup”---▪Libera entrada en la TP▪decrementa contador

procesos.▪Despierta al padre

devolviendole el pid

▪ Recorre TP:Para cada entrada:Si es hijo

hijo->parent = InitSi zombie & Init waiting“cleanup” del hijo

Se desbloquea

¿ PADREEN WAIT ?

NO : ------------▪Estado=zombie.▪Envía SIGCHLD

“sig_proc()”

SI:Desactivar

Desaparece,No hay respuesta

Un proceso termina totalmente cuando suceden dos cosas: (1) El proceso ha finalizado (por “exit” o recepción de una señal), y (2) su padre estaba esperando en un “wait”. Un proceso que ha finalizado, pero cuyo padre no ha efectuado “wait” por él, entra en un estado conocido como “zombie”. Su memoria se libera, no se planifica y su posible alarma se desactiva, sin embargo, todavía ocupa su entrada en la TP. Este estado no suele durar y cuando su padre, eventualmente, ejecuta “wait”, se produce la liberación de la entrada de la TP y se informa al GF y kernel para que hagan lo propio.Si un proceso muriese estando su padre ya muerto, habría un problema, porque un proceso “zombie” quedaría así para siempre. Para evitar esto, Minix hace que cuando un proceso muere, todos sus hijos, pasen a ser hijos de “Init”. Al arrancar el Sistema, “Init” lee el fichero “/etc/ttytab”para obtener la lista de todos los terminales, y hace “fork”, con cambio de imagen a “login”, por cada terminal. A continuación se bloquea a la espera de la terminación de cualquier proceso hijo y de este modo “limpiar” cualquier “zombie “huérfano.La llamada “exit” ejecuta el procedimiento “do_pm_exit”, el cual llama a “pm_exit” que es quien hace todo el trabajo. Esto es porque a “pm_exit” también se le llama cuando un proceso termina por una señal, aunque con parámetros de llamada distintos.El procedimiento “pm_exit” hace lo siguiente: (1) Si el proceso tiene una alarma pendiente la detiene. (2) Contabiliza al padre los tiempos consumidos por el proceso que acaba. (3) Notifica a la TS de que el proceso deja de ejecutarse. (4) Notifica al GF, y (5) a la TS después, del fin del proceso (exit) para que actualicen sus TP,s liberando las entradas del proceso respectivas. (6) Se libera la memoria del proceso. Para ello, primero se determina si el segmento de código está siendo usado por algún otro proceso. Si no es así se libera, y a continuación se libera el segmento de datos y pila. (7) Si el proceso padre le está esperando (en un “wait”), se llama al procedimiento “cleanup”, el cual libera la entrada del proceso en la TP del GP, decrementa el contador de procesos y despierta al padre. En caso contrario, deja al proceso en estado “zombie” y envía la señal SIGCHILD al padre. Después de “cleanup”, tanto si el proceso queda “zombie” como si no, se recorre la TP buscando cualquier proceso hijo. Si se encuentra alguno le hace hijo de ”Init”. Si “Init”está “WAITING” y algún hijo está “zombie” se llama al procedimiento “cleanup” para dicho hijo.

Page 19: 2-Gestion de Memoria en Minix 3

19

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 19

4.4 Las llamadas al sistema, fork, exit y wait: (wait)

Gestor de Procesos

Proceso de Usuario

Significado de las flechasSignificado de las flechas: send_rec (…) send (…)

--do_waitpid--▪Recorre la TP

y para cadaentrada:

¿ Es un procesohijo ?

estado=WAITINGreturn(SUSPEND)

----SI -----return(pid)

---SI---¿ Estázombie ?

----SI-----▪cleanup;▪return

(SUSPEND)

¿ Estástopped ?

¿ Tienealgún hijo ?

----SI -----¿ Opción

“WNOHANG” ?

----SI -----return(0)

Return(ECHILD)

Esta respuesta seenvía en cleanup.

Cuando el proceso padre hace “wait” o “waitpid”, se ejecuta la función “do_waitpid”, la cual rastrea la TP en busca de algún hijo. Si hay alguno, comprueba si está en estado “zombie”, en cuyo caso, se llama a la función “cleanup” y la función “do_waitpid” devuelve “SUSPEND”, como código de retorno (lo que significa que no se manda mensaje de respuesta al padre, sin embargo no quedarábloqueado porque la respuesta se envía en la función “cleanup”). Si se encuentra algún hijo en estado “stopped” (siendo trazado), se informa de ello en el mensaje de respuesta y se retorna (hay mensaje respuesta al padre). Si no se hubiera encontrado ningún hijo en la TP, se devuelve error. Si hubiera hijos pero ninguno en estado “zombie” o “stopped”, dependiendo de si se ha llamado a “wait” con la opción de espera (caso normal) o no (WNOHANG), se deja al proceso bloqueado “WAITING” (se retorna SUSPEND y por tanto no se le responde con mensaje), o se retorna 0 (se responde con mensaje y el proceso puede continuar).Cuando un proceso acaba y su padre lo estaba esperando, cualquiera que sea el orden de estos eventos, se llama al procedimiento “cleanup” para efectuar los últimos pasos. Queda poco por hacer. El padre es despertado de su “wait” y se le entrega el “pid” del hijo que ha finalizado, asícomo su código de salida (exit code). El GP ya ha liberado la memoria del proceso hijo y el kernel ya ha suspendido la planificación y liberado su entrada en la TP, así como también el GF. En este punto el proceso hijo se ha ido para siempre.

A la Izquierda, situación antes de que salga el proceso 12.

A la derecha un poco antes de salir. La función clean_up limpiaría el proceso zombie 53, y éste desapa-recería como hijo de init. El proceso 52 seguiría adoptado por init.

Page 20: 2-Gestion de Memoria en Minix 3

Gestión de memoria Minix 3

4.5 La llamada al sistema execve (1/5)

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 20

40 clics

C30 clics

B20 clics

A60 clics

20 clics

10 clics

dMINIX

“C” hace execve() aun programa “D” detamaño 70 clics.

---------------------------¡ NO HAY MEMORIA !

“B” hace execve()a un programa “E”

con una pila de 1400 bytes

--------------------¡ NO HAY MEMORIA !

I+D JuntosTamaños segmentos de “E”------------------------

Bytes ClicsCódigo 20.510 --Datos 220 BSS 10.000 31 Pila 1.400 2Total 32.740 32

Al comprobar el tamaño requeridonunca se tiene en cuenta que laliberación de la propia imagen sumada a algún hueco contiguopodría satisfacer el requisito dememoria. Si se puede compartircódigo sólo se pide memoria parael segmento de datos+gap+pila. Nose considera nunca la posibilidad de hacer dos peticiones de memo-ria, aún siendo I+D separados.

Durante la llamada “execve”, cuando Minix busca un hueco de tamaño suficiente para satisfacer una petición de memoria, no se tiene en cuenta si la memoria liberada por la imagen saliente sumada a algún posible hueco adyacente puede satisfacer dicha petición. Cuando el programa “C” hace “execve” sobre el programa “D”, Minix busca un hueco de tamaño suficiente para albergar a “D” al completo. Si “D” tiene espacios I+D separados, la necesidad podría repartirse en principio entre dos huecos, sin embargo Minix no solicita estos dos huecos, por lo que esto es algo mejorable. Sin embargo Minix, si contempla la posibilidad de que “D”, con I+D separados, pueda compartir el código con algún programa ya en memoria, para buscar si es así, un único hueco para el segmento de datos+gap+pila. Cuando el programa “B” hace “execve” con una pila de 1.400 bytes, e intenta cambiar la imagen de memoria por la del programa “E” que tiene en su cabecera los tamaños siguientes: Text=20.510, Datos=220, BSS=10.000 y Total=32.740 con I+D juntos. La llamada falla por memoria insuficiente. La explicación es la siguiente: El segmento de datos incluye el código y datos no inicializados (BSS), su tamaño es 20.510 + 220 + 10.000 = 30.730 que expresados en clics son 31. El tamaño total que son 32.740 bytes nos debería dejar un espacio para pila y gap de 32.740 – 30.730 = 2.010 bytes, que en principio sería suficiente para la pila de 1.400 bytes, sin embargo, en el mapa de memoria los segmentos se requieren en clics. El tamaño del segmento de pila son 2 clics, y el tamaño “Total” en clics, según el campo del fichero es de 32 clics, que es inferior a la suma del de datos y pila (31+2). El programa “E” tendría que ser ejecutado con un tamaño de pila máximo de 1 clic si se desea tener éxito en el “execve”. Este problema surge debido a la perdida de memoria que sufren los segmentos por fragmentación interna.

20

Page 21: 2-Gestion de Memoria en Minix 3

Gestión de memoria Minix 3

0

4.5 La llamada al sistema execve (2/5)

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 21

envp [ ] execve(“/bin/ls”, argv, envp)

0

g.cf.c-lls

argv [ ]

HOME = /usr/ast

\0 t s a 52

/ r s u 48

/ = E M 44

O H \0 c 40

. g \0 c 36

- f \0 l 32

- \0 s l 28

0 24

42 20

0 16

38 12

34 8

31 4

28 0

DATOS

CODIGO

\0 t s A 8188

/ r s u 8184

/ = E M 8180

O H \0 c 8176

. g \0 c 8172

- f \0 l 8168

- \0 s l 8164

0 8160

8178 8156

0 8152

8174 8148

8170 8144

8167 8140

8164 8136

PILA

\0 t s A 8188

/ r s u 8184

/ = E M 8180

O H \0 c 8176

. g \0 c 8172

- f \0 l 8168

- \0 s l 8164

0 8160

8178 8156

0 8152

8174 8148

8170 8144

8167 8140

8164 8136

8156 8132

8136 8128

4 8124

return 8120

(a) (b) (c)

crtso

envpargvargc

En la figura de arriba se muestran las operaciones básicas que se llevan a cabo con la pila durante la llamada “execve”, tomando como ejemplo la ejecución del comando “ls –l f.c g.c” en la “Shell”. Ésta interpreta dicho comando y efectúa la llamada: “execve (“/bin/ls”, argv, envp)”, la cual construye la pila inicial en el espacio de usuario tal y como se muestra en la figura de arriba (a), con direcciones relativas a la dirección virtual 0.Esta pila se copia tal cual a una variable local dentro del espacio del GP. Éste parchea la pila modificando las direcciones de los punteros acorde a las direcciones que deberían tener, dentro de una pila ubicada en la nueva imagen. Suponiendo por ejemplo, que el tamaño de la memoria total del nuevo programa fuera de 8.192 bytes (la última dirección del programa sería la 8.191), entonces el GP, sabiendo que dicha pila se situará al final de la memoria disponible para el programa, modificará las direcciones de todos los punteros dentro de la pila de acuerdo a su nueva ubicación y mandará a la TS que copie la pila, desde su copia local, a la nueva imagen en espacio de usuario. Con la llamada “execve” ya finalizada, la pila de la nueva imagen quedaría tal y como se muestra en la figura (b), con el puntero de pila en el valor 8.136. Todavía quedaría algo por resolver. El programa principal a ejecutar tendrá probablemente una función como esta: “main (argc, argv, envp)”. Para el compilador de “C” “main” es otra función más. El código compilado supone que los tres parámetros, y la dirección de retorno están en la pila, y no es así como lo ha dejado “execve”. Por esto los programas no empiezan directamente en “main”. Hay una pequeña rutina llamada “C run-time, start-off” (crtso) que se monta (link) en la dirección de comienzo 0 y toma inicialmente el control. Su trabajo es apilar estos tres parámetros y llamar a “main” mediante “call”. El resultado de la pila se muestra en la figura (c ). Si desde “main” no se invocase “exit”para finalizar el programa, la dirección de retorno de la pila devolvería el control a la rutina crtso,la cual efectuaría la llamada a “exit”

21

Page 22: 2-Gestion de Memoria en Minix 3

22

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 22

4.5 La llamada al sistema execve (3/5)- Si exceso en el tamaño de la pila o longitud ruta excesiva. ¡ Error execve !- Obtiene el nombre del fichero. Lo copia de espacio de usuario a espacio del GP:

sys_datacopy (). Si error ¡ Error execve! Envía SYS_VIRCOPY a la TS.- Copia la pila del proceso de espacio de usuario a espacio del GP:

sys_datacopy (). Si error ¡ Error execve! Envía SYS_VIRCOPY a la TS.- Bucle de hasta dos iteraciones. Si es un “script” se ejecuta 2 veces. Si no sólo 1.▪ Cambia al directorio de usuario: tell_fs (CHDIR,…); Envia CHDIR al GF.▪ Comprueba si el fichero es ejecutable:

fd=allowed (nombrefichero_ejecutable, …):access (). Envia ACCESS al GF.tell_fs (SETUID, superuser,..); Envía SETUID al GF.fd=open (). Envía OPEN al GF.tell_fs (SETUID, euid,..); Envia SETUID al GF.fstat (). Envía STAT al GF.Si fichero no ejecutable: close(fd); ¡ Error execve ! Si error envía CLOSE al GF.

▪ Lee la cabecera del fichero y obtiene los tamaños de los segmentosread_header (fd, &txt_bytes, &data_bytes, &bss_bytes, &tot_bytes, ….):

read (fd, &header,..); Envía READ al GF.Comprueba tamaño mínimo de cabecera, número mágico, si es un script

(“#!” dos primeros caracteres), etc. Si algo inválido se retorna error. Lee y establece espacios I+D juntos o separados.Prepara tamaños de los segmentos y comprueba si son coherentes.

- Fin Bucle

La llamada “execve” está implementada por la función “do_exec()”. Después de efectuar algunas comprobaciones, el GP manda a la TS que copie el nombre del fichero ejecutable, desde el espacio de usuario al suyo propio. En el mensaje con la orden se incluye además la dirección y longitud del nombre. A continuación vuelve a ordenar a la TS que copie la pila construida por la librería de usuario a una variable local propia. En el mensaje con la orden se incluye la dirección de la pila y su tamaño. Seguidamente se entra en un bucle. Si el fichero es un binario el bucle se ejecuta una única vez , si es un script dos. Primero veremos el caso de que fichero sea un binario: 1) El GP envía un mensaje al GF para que cambie el directorio actual al directorio de trabajo del usuario. 2) Se llama a la función “allowed()” para comprobar si se permite la ejecución. Si es así, se abre el fichero y se devuelve el descriptor, sino se devuelve error y la llamada falla. 3) Se lee la cabecera del fichero para obtener los tamaños de los segmentos. Si el fichero es un binario el código devuelto provoca la salida del bucle. Lo que sucede si el fichero es un script es lo siguiente: Cuando se lee la cabecera del fichero se comprueban los dos primeros caracteres (#!) que indican que el fichero es un script, en la misma línea también se especifica el programa que interpretará el script junto con posibles opciones, por ejemplo: #! /usr/local/bin/perl wT. En este caso el fichero que se tiene que cargar en memoria es el binario intérprete del script en lugar del script, además se parchea la pila en la función “patch_stack()” añadiendo a ésta los parámetros indicados en la línea del script (respetando los que se hubieran especificado en la llamada “execve”). No se permite que el interprete de un “script” sea a su vez otro “script”, limitando el número de iteraciones del bucle a 2.Si hubiera algún error en la cabecera, se devolvería éste y la llamada “execve” fallaría.

Page 23: 2-Gestion de Memoria en Minix 3

23

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 23

4.5 La llamada al sistema execve (4/5)

- Si hubo algún error en cabecera: close (fd) ¡ Error execve ! Si error envía CLOSE al GF.- Comprueba si se puede compartir el segmento de código.- Toma memoria para la nueva imagen, libera la antigua, actualiza el mapa e informa al kernel.

new_mem():si error “alloc_mem()” ¡ Error execve !si segmento código saliente no está siendo compartido “free_mem(seg_codigo,..)”“free_mem(seg_datos,..)”sys_newmap () Envía SYS_NEWMAP a la TSRellena a 0’s BSS, gap y pila: sys_memset (…) Envia SYS_MEMSET a la TS

- Parchea la pila y la copia desde el GP a la nueva imagen de memoriaParchea la pila : patch_ptr (..)Copia la pila: sys_datacopy (..) Envía SYS_VIRCOPY a la TS

- Lee de disco el segmento de datos y posiblemente el de código a la nueva imagen de memoriaSi código compartido se lo salta: lseek (fd,..) Posible envío LSEEK al GFsino, se leo del disco: rw_seg (R, fd, T,..) Posible envío READ(s) al GFLeo segmento datos: rw_seg (R, fd, D,..) Envío READ(s) al GFCierra el fichero: close (fd) Envía CLOSE al GF

El siguiente paso es comprobar si se puede compartir el código. Para ello el GP se recorre la Tabla de Procesos buscando un proceso cuyos campos “mp_ino, mp_dev y mp_ctime” indiquen que el fichero ejecutable es el mismo que el que se está intentando cargar en memoria, además de tener espacios I+D separados. Si lo encuentra, guarda en una variable (“sh_pm”) esta situación (la cual refleja I+D separados y código compartido).La función “new_mem()” comprueba si hay suficiente memoria para la nueva imagen. Si secomparte código (e I+D separados, “sh_pm”) se busca un hueco suficiente para el segmento de datos y pila, sino el hueco ha de ser suficientemente grande para incluir además el código. Si la imagen entrante tuviera I+D separados pero no pudiera compartir código con ningún otro proceso, por ser dicho código el primero en entrar en memoria, sería factible y en principio deseable, el solicitar dos huecos de memoria independientes para los dos segmentos del proceso. Esto supondría una mejora sobre la implementación de Minix ya que actualmente no lo hace así. Si hubo suficiente memoria (éxito en alloc_mem) se libera la memoria de la imagen saliente (no se liberaría el segmento de código si este estuviera siendo compartido por algún otro proceso) y se asigna memoria para la imagen entrante. Se actualiza el mapa de memoria (campo “mp_seg”) y se informa al kernel con “sys_newmap()”. Finalmente “new_mem()” encarga a la TS la inicialización a 0’s las áreas bss, gap y pila mediante la llamada al kernel “sys_memset()”.A continuación se parchean las direcciones de los punteros en la pila tal y como ya se ha visto mediante el procedimiento “patch_ptr()” y se manda a la TS que copie la pila parcheada, desde el espacio del GP al espacio de usuario de la nueva imagen, con la función “sys_datacopy()”.Finalmente, se leen y se llevan los segmentos de código y datos de disco a memoria (el segmento de código sólo se lee y lleva si el código no es compartido [variable “sh_pm”]).

Page 24: 2-Gestion de Memoria en Minix 3

24

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 24

4.5 La llamada al sistema execve (5/5)

- Comprueba y maneja los bits “setuid” y “setgid”Si el bit SET_UID del fichero está activo:

usr_efectivo proceso = uid del fichero: tell_fs (SETUID,..); Envía SETUID al GFSi el bit SET_GID del fichero está activo:

grupo_efectivo proceso = gid del fichero: tell_fs (SETGID,..); Envía SETGID al GF- Actualiza adecuadamente los campos de la entrada de la TP del proceso

Resetea las señales capturadas. Tratamiento a SIG_DFL. Desenmascara todas las señales.Establece flag de I+D acorde con el del fichero.Informa al GF de execve: tell_fs (EXEC,..); Envía EXEC al GFEstablece el campo “mp_name” al nombre del fichero ejecutado (para depuracion, ps, salida , etc.)

- Informa al kernel de que el proceso es ejecutablesys_exec (who, new_sp, name, pc); Envía SYS_EXEC a la TS

- Causa la señal SIGTRAP si el proceso estuviese siendo trazado.if (TRACED) check_sig (rmp->mp_pid, SIGTRAP);

- No se responde: return (SUSPEND);

La función “rw_seg()” en vez de leer y copiar a espacio de usuario el fichero bloque a bloque, utiliza un truco para permitir al GF cargar de una sola vez el segmento al espacio de usuario. La llamada es decodificada por el GF de una forma ligeramente especial. Hace parecer que la lectura es de todo el segmento por parte del proceso de usuario mismo. Sólo unas pocas líneas del comienzo de la rutina de lectura del GF son precisas para tratar este aspecto “extraño”. Con esto se consigue una apreciable incremento en la velocidad de carga del(de los) segmento(s) .Por último señalar, que si la llamada ha tenido éxito no se puede responder al proceso que la invocó. La forma en que el proceso reanuda la ejecución es atípica, no es como respuesta a la primitiva “send_rec()” que se empleó en la llamada, sino que la TS al recibir el mensaje SYS_EXEC se encarga de retirar al proceso de la cola de procesos que esperan recibir un mensaje y ponerlo en la cola de planificación, con el contador de programa inicializado a la primera instrucción del programa.

Page 25: 2-Gestion de Memoria en Minix 3

Gestión de memoria Minix 3

4.6 La llamada al Sistema brk

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 25

Gestor de Procesos

Proceso de Usuario

Tarea del Sistema

Significado de las flechasSignificado de las flechas: send_rec (…) send (…)

▪Comprueba que el nuevovalor es distinto del anterior y si lo es, que no es inferior al comienzodel segmento de datos.

▪Pide a la TS el valor delpuntero de pila SY

S_GE

TINFO

SYS_

GETI

NFO

------- adjust -------▪Comprueba si el tamaño de

la pila quedaría negativo.▪Calcula el tamaño del gap.▪Añade un margen de segu-

ridad para crecimiento pila.▪Actualiza tamaño segmento

de datos y tamaño y origendel segmento de pila.

▪Devuelve entre otrascosas el puntero de pila

▪¿ Caben los segmentosde datos y pila en el espacio de usuario ?

----SI ---Informa a la TSdel nuevo mapade memoria

SYS_

NEW

MAP

SYS_

NEW

MAP

▪Restaura los antiguosvalores de los segmentos

▪Responde error

RespondeOK

Posible respuesta con error o no de-pendiendo de la comprobación anterior

Los procedimientos de librería “brk y sbrk” se utilizan para ajustar el límite superior del segmento de datos. El primero tiene como parámetro la dirección del límite superior del segmento de datos y la segunda un incremento o decremento del tamaño actual del segmento de datos. Esta última en realidad lo único que hace es calcular el nuevo tamaño del segmento de datos y llamar a “brk”. El modelo básico de memoria de Minix es bastante simple, se asigna un espacio contiguo de memoria para los segmentos de datos y pila al crearse el proceso. Este espacio (segmento intel) no puede reubicarse, crecer, ni disminuir. Lo único posible es que el segmento de datos aumente su límite superior a costa del gap desde abajo, o que el segmento de pila crezca hacia abajo a costa del gap por arriba. Con estas limitaciones la implementación es sencilla. Consiste en comprobar que los cambios encajan en el espacio de direcciones, ajustar las tablas e informar al “kernel”. La parte principal de esta llamada la hace el procedimiento “adjust”, el cual comprueba que los segmentos de datos y pila no colisionen. Si lo hicieran la llamada fallaría. Al hacer esta comprobación se añade al segmento de datos unos bytes de margen de seguridad (SAFETY_BYTES), tal que la decisión de que la pila ha crecido mucho pueda hacerse con todavía suficiente espacio en la pila (margen de seguridad). La base del segmento de datos es constante, por tanto sólo se ajusta su tamaño. La pila crece hacia abajo, si “adjust” descubre que el puntero de pila se ha situado más abajo del origen del segmento de pila, entonces se cambia tanto el origen como el tamaño.

25

Page 26: 2-Gestion de Memoria en Minix 3

Gestión de memoria Minix 3

4.7 Tratamiento de señales

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 26

Las tres fases del tratamiento con señales:

Preparación: El código del programa se prepara para una posible señal.Respuesta: La señal se recibe y se toma un acción.Restauración: Se restaura la operación normal del proceso.

La estructura “sigaction”:

struct sigaction { __sighandler_t sa_handler; /* SIG_DFL, SIG_IGN, SIG_MESS, o puntero a función */ sigset_t sa_mask; /* señales a bloquear durante el manejador */int sa_flags; /* flags especiales */

}

Las señales constituyen una forma de enviar información a un proceso que no la está esperando. Existe un conjunto definido de señales. Cada señal tiene una acción por defecto, bien matar al proceso que la recibe o bien ser ignorada, sin embargo, los procesos puede usar llamadas al Sistema para alterar estas acciones. Un proceso puede hacer que una señal sea ignorada o bien hacer que se ejecute un tratamiento cuando se reciba, exceptuando en ambos casos la señal especial “kill”. Así, para el programador hay dos momentos distintos en el que el S.O. trata con las señales. Una fase de preparación, en la que un proceso modifica la respuesta a una futura señal, y una fase de respuesta, en la que la señal es generada y se actúa en consecuencia. La acción puede ser la ejecución de un manejador de señal personalizado. Existe una tercera fase. Cuando el manejador acaba, una llamada al Sistema especial restaura la operación normal del proceso “señalado”. El programador no necesita saber de esta fase. El escribe el tratamiento de señal como una función más. El S.O. se ocupa de los detalles de invocar y terminar el manejador y gestionar la pila.Hay varias llamadas al Sistema que en la fase de preparación puede modificar la acción por defecto de una señal. La más general es “sigaction()” que puede especificar que se ignore, capture (especificando una función manejadora) o restaure la acción por defecto de una señal. Otra llamada, “sigprocmask()”, puede bloquear una señal, haciendo que se encole y que actúe sólo cuando el proceso la desbloquee, en un momento posterior. La fase de preparación se hace desde el GP. Cada proceso tiene varios “bitmaps”, uno de ellos define las señales a ser ignoradas, otro las capturadas, etc. También dispone de una tabla de estructuras “sigaction”, de tantos elementos como posibles señales. Con este campo se puede saber para cada señal la función de tratamiento (“sa_handler”), las señales bloqueadas durante la ejecución del tratamiento (“sa_mask”). El valor de “sa_handler” además del de la dirección de una función puede ser SIG_DFL (tratamiento por defecto) y SIG_IGN (ignorar). Los procesos del Sistema usan el nuevo tipo de manejador “SIG_MESS”, que le dice al GP que redirija una señal mediante un mensaje de notificación SYS_SIG. No se requiere la fase tres de restauración para las señales tipo SYG_MESS.

26

Page 27: 2-Gestion de Memoria en Minix 3

27

Gestión de memoria Minix 3

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 27

4.7 Tratamiento de señalesSeñal Descripción Generada por

SIGHUP “Hangup” Llamada al Sistema killSIGINT Interrupción TTYSIGQUIT Abandonar (Quit) TTYSIGILL Instrución ilegal Kernel (*)SIGTRAP “Trace trap” Kernel (M)SIGABRT Terminación anormal TTYSIGFPE Excepción punto flotante Kernel (*)SIGKILL “kill” (no puede ser capturada ni ignorada) Llamada al Sistema killSIGSEGV Violación de segmento Kernel (*)SIGPIPE Escritura en un pipe sin nadie para leerlo Gestor de FicherosSIGALRM Alarma vencida Gestor de ProcesosSIGTERM Terminación por software, kill() Llamada al Sistema killSIGCHILD Proceso hijo finalizado o detenido Gestor de ProcesosSIGKMESS Mensaje del Kernel KernelSIGKSIG Señal pendiente del Kernel KernelSIGKSTOP Kernel apagando (shutting down) Kernel

(*) Dependientes del Hardware. (M) De Minix, no de Posix.

Cuando se genera una señal intervienen varias partes de Minix. La respuesta empieza en el GP, el cual sabe qué procesos deben recibir la señal gracias a las estructuras ya mencionadas. Si la señal estuviera capturada, el GP se la envía al proceso destinatario. Esto requiere salvar el estado del proceso (para que pueda continuar su ejecución donde se quedó). Esta información la salva en la pila del proceso “señalado”, comprobando previamente si hay espacio en ella, y encargándoselo a la TS. La TS, además, manipula el contador de programa para que apunte al comienzo del manejador de la señal. Cuando el manejador acaba se efectúa una llamada al sistema “sigreturn”. Mediante esta llamada, el GP y el “kernel” participan en la restauración del contexto del proceso “señalado” para que pueda retornar a su ejecución. Si la señal no estuviera capturada se ejecuta la acción por defecto. El GP puede tener que repetir más de una vez estas acciones, ya que una señal puede tener que ser distribuida a un grupo de procesos.Minix define todas las señales obligatorias de Pósix estándar, aunque no todas las opcionales.Tradicionalmente las señales pueden generarse de dos modos: por la llamada al Sistema “kill”, y por el “kernel”. Las señales “sigint y sigquit” pueden originarse por la pulsación de teclas especiales del teclado. “sigalrm” es manejada por el GP. “sigpipe” por el GF. El programa “kill” puede enviar cualquier señal a cualquier proceso. Algunas señales dependen del hardware, por ejemplo el 8086 no soporta la detección de “instrucción ilegal”.Con independencia del origen de la señal, el GP procesa todas las señales del mismo modo. Para cada proceso a ser “señalado” se efectúa una serie de comprobaciones. Un proceso puede enviar una señal a otro si es el superusuario, o si su usuario real o efectivo es igual al usuario real o efectivo del destinatario. Algunas condiciones impiden el envío. a) El destinatario no puede ser “zombie”, b) la señal no debe estar ignorada ni bloqueada (no es lo mismo estar bloqueada que ignorada, las señales bloqueadas se envían si/cuando finaliza la situación de bloqueo), y por último c) si el espacio de pila es insuficiente, el proceso “señalado” es eliminado (“killed”).Si se cumplen todas las condiciones, la señal se envía. Si la señal no está capturada no se envía información al proceso. En ese caso el GP mata al proceso (o ignora la señal si esa fuera su acción por defecto). Algunas señales no están soportadas en Minix, pero si definidas aunque ignoradas, tal y como permite el estándar Posix. Estas son: SIGUSR1, SIGUSR2, SIGCONT, SIGSTOP, SIGSTP, SIGTTIN y SIGTTOU.

Page 28: 2-Gestion de Memoria en Minix 3

Gestión de memoria Minix 3

4.7 Tratamiento de señales

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 28

Dir. Retorno

Vars. localesdel proceso

Registros CPU

(originales)

Antes(a)

Regs. CPU modificados

CP=manejador

Manejador eje-cutandose (b)

Regs. CPUModificados

CP=manejador

Sigreturn eje-cutandose (c)

RegistrosCPU

originales

Vuelta a normal(d)

Dir. Retorno

Vars. localesdel proceso

Regs CPUoriginales

Dir Ret 2EstructuraSigframeDir. Ret 1

Vars. LocalesManejador

Dir. Retorno

Vars. localesdel proceso

Regs. CPUOriginales

Dir. Ret 2Vars. Locales

Sigreturn

Dir. Retorno

Vars. localesdel proceso

Mar

co d

e pila

Tabla ProcesosPila

Capturar una señal significa ejecutar una función de tratamiento personalizada para dicha señal (manejador de la señal). La dirección de este manejador está guardada en la estructura “sigaction” de la Tabla de Procesos (TP). Modificando el marco de pila (“stackframe” guardado en la TP) de un proceso “señalado” se consigue que la próxima vez que se le permita ejecutar, se ejecute el manejador. Modificando la pila del proceso, se consigue que cuando el manejador acabe, se ejecute la llamada al Sistema “sigreturn” . Esta llamada no la programa el usuario; se ejecuta después de que el “kernel” haya puesto su dirección en la pila, de tal modo que sea ésta la dirección de retorno que se saca de la pila cuando termina el manejador. “Sigreturn” restaura el “stackframe” original del proceso “señalado”, para que éste pueda volver a ejecutarse desde el punto donde fue interrumpido por la señal. En (a) se muestra una vista simplificada de la pila y parte de su descriptor de proceso, justo después de haber sido expulsado por una interrupción. En el momento de la expulsión los registros de la CPU se copian a la estructura “stackframe” de la TP del kernel del proceso expulsado. Esta sería la situación en el momento en que la señal es generada. Las señales son generadas por un proceso diferente al destinatario, por ello este último no puede estar ejecutándose en ese momento. En la preparación del manejo de la señal, la TS copia el “stackframe” de su TP en la pila del receptor como una estructura “sigcontext”, preser-vandola, después se apila una estructura “sigframe”, con información que usará “sigreturn” cuando acabe el manejador. También apila la dirección Ret1 de la función que ejecutará a la propia “sigreturn” y otra dirección de retorno, Ret2, que es donde debe volver el programa interrumpido, aunque estaúltima no se usa en la ejecución normal. El contador de programa (CP) del “stackframe” de la TP se modifica para hacer que sea el manejador el que se ejecute cuando el proceso “señalado”vuelva a ejecución. En (b) se muestra la situación después de la preparación y con el manejador ejecutándose. (C) muestra la situación con “sigreturn” ejecutándose. El resto de la estructura “sigframe” ahora son sus variables locales. “Sigreturn” acaba como cualquier otra llamada al Sistema, permitiendo que el planificador seleccione el siguiente proceso. Eventualmente el proceso “señalado” será planificado y retomará el control a partir de esta dirección (Ret2). La misión de “sigreturn” es dejar las cosas como estaban. El “stackframe” de la TP es restaurado a su estado original, usando la copia de la pila. (d) muestra la situación final, con el proceso esperando volver a su ejecución en el punto o estado en el que estaba cuando se interrumpió.

28

Page 29: 2-Gestion de Memoria en Minix 3

Gestión de memoria Minix 3

4.7 Tratamiento de señales. sigaction (int sig, const struct sigaction *act, struc sigaction *oact)

Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -Escuela Universitaria de Informática - Universidad Politécnica de Madrid.

Capítulo IV. Pag. nº 29

Proceso de Usuario

Gestor de Procesos

send_rec: SIGACTION

Tarea del Sistema

send_rec: SYS_VIRCOPY Una o dos veces

int do_sigaction() {Si (sig = SIGKILL) return (OK)Si (sig <1 o sig > NSIG) return (EINVAL)Si (oact != NULL)

Pide a la TS que copie la estructura “sigaction” de laTP a “oact” ….. sys_datacopy (…).

Si (act = NULL) return (OK)Pide a la TS que copie de “act” a la estructura “sigaction”de la TP .….. sys_datacopy (…).Si (act->sa_handler = SIG_IGN)

sigaddset (mp_ignore)sigdelset (mp_sigpending, mp_catch, mp_sig2mess)

sino si (act->sa_handler = SIG_DFL)sigdelset (mp_ignore, mp_catch, mp_sig2mess)

sino si act->sa_handler= SIG_MESS) si (! (mp_flags & PRIV_PROC)) return (EPERM)sigaddset (mp_catch..) sigdelset (mp_ignore, mp_sig2mess)

sino sigaddset (mp_catch)sigdelset (mP-ignore, mp_sig2mess)

}

La llamada “sigaction” soporta ambas funciones: sigaction() y signal(), las cuales le permiten a un proceso alterar su respuesta a las señales. “Sigaction()” es la requerida por POSIX y en general se la prefiere para la mayoría de los propósitos, aunque “signal()” es “C” estándar y se necesita para portabilidad con sistemas NO-POSIX. El código comienza con una validación: Que el número de señal sea válido y que no se intente alterar la respuesta a la señal “sigkill”. A continuación se invoca a la tarea de sistema para que copie los atributos a la estructura “oact”, si el puntero a ésta no fuera NULL, además si el puntero a “act” no fuera NULL, se coparian los atributos de esta estructura al espacio del GP. Más adelante se modifican los mapas de bits mp_catch, mp_ignore y mp_sigpending, de acuerdo a la que la nueva acción sea ignorar, capturar o restaurar el tratamiento por defecto de la señal. El campo “sa_handler” de la estructura “sigaction” contiene el puntero a la función que se ejecutará si señal es capturada, o bien alguno de los valores especiales SIG_IGN o SIG_DFL, definidos en POSIX. También es posible el valor SIG_MESS que es especifico de Minix. Se utiliza para procesos del sistema. Estos procesos están normalmente bloqueados a la espera de un mensaje, de este modo, el método normal por el cual el GP le pide al kernel que ponga un marco de señal en la pila del receptor, será demorado hasta que un mensaje despierte al receptor. El código SIG_MESS le dice al GP que emita un mensaje de notificación, el cual es prioritario sobre un mensaje normal. Este mensaje de notificación contiene como argumento un conjunto de señales pendientes, permitiendose con ello que se pasen multiples señales en un solo mensaje.Finalmente, los otros campos de la TP del GP relacionados con señales son rellenados. Para cada posible señal hay un “bitmap”, sa_mask, en el que se definen que señales deben bloquearse mientras el manejador de esa señal se esté ejecutando. También hay para cada señal un puntero, sa_handler. Éste puede contener la dirección de una función de tratamiento, o valores especiales para indicar si la señal será ignorada, tendrá tratamiento por defecto oo se será usada para generar un mensaje. La dirección de la rutina de librería que invoca sigreturn cuando termina el manejador es almacenada en mp_sigreturn. Esta durección es uno de los campos del mensaje recivido por el GP.

29