SlideShare una empresa de Scribd logo
1 de 29
Descargar para leer sin conexión
Gestión de memoria Minix 3

    • 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)




Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 1
         Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




                                                                                                                   1
Gestión de memoria Minix 3

                                                    Visión general

                                        •No utiliza paginación
          Modo usuario                  •Posible activación del uso de Intercambio
                                        •Comunicación por paso de mensajes
           Gestor de                    •Implementa la estrategia de asignación de memoria:
           Procesos                       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…

         Modo supervisor

             Núcleo                      •Implementa el mecanismo de asignación de memoria:
            de Minix                      Copia de mapas de memoria



              Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 2
                       Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                 2
Gestión de memoria Minix 3
          4.1 Organización de la memoria (1)
                                                                                                         Fichero ejecutable en disco




                                                                           Direcciones Altas →
                                                                                                             Símbolos (opcional)
                                                                                                             Datos (inicializados)
                                         Pila
                                                                                                                  Código (text)
            Prog. B                      gap               Segmento
                                                                                                                    Cabecera
            I+D Separados           Datos+bss
                                       Codigo              Segmento


                                                                                                 • Bit de I+D juntos o separados
                                         Pila                                                    • Tamaños de memoria:
              Prog. A                    gap                                                       Total, Código, Datos, y BSS
              I+D juntos                                   Segmento                              (El tamaño de memoria total en I+D juntos
                                    Datos+bss                                                    comprende la cantidad de memoria total
                                                                                                 necesaria para el proceso. En I+D separados,
                                       Código
                                                                                                 éste, no incluye el tamaño del código)
                                        Minix




                Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -                       Capítulo IV. Pag. nº 3
                         Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                                       3
Gestión de memoria Minix 3
                  4.1 Organización de la memoria (2)
                                              Asignación de memoria.
                                              · Con  las llamadas:
                       Gestor de                  fork: la misma cantidad que al padre
                       Procesos                   execve: la cantidad indicada en la cabecera del fichero ejecutable
                                              Liberación de memoria.
                                              · Cuando un proceso muere, bien mediante exit ó por la llegada
                                                de una señal cuyo tratamiento sea matar al proceso receptor.
                                              · Con execve se libera si la llamada tiene éxito.
        Dir. Altas →




                                         Hijo de A               Prog. C                  Mapas de memoria: (I+D juntos)
                       Prog. B            Prog. B                                         (a) Originalmente,
                                                                 Prog. B
                                                                                          (b) Después de fork,
                                                                                          (c) Después de execv del hijo.
                       Prog. A            Prog. A                Prog. A
                       Minix               Minix                  Minix

                        (a)                 (b)                     (c)

                         Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 4
                                  Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.
Gestión de memoria Minix 3
          4.2 Tratamiento de mensajes (1)
          Tipo mensaje                Parámetros de entrada                                                  Valor devuelto
          fork                        ninguno                                                                Pid hijo y 0 al hijo
          exit                        exit status                                                            Si éxito, ninguna
          wait                        ninguno                                                                Status
          waitpid                     Identificador de proceso y flags                                       Status
          brk                         Nuevo tamaño                                                           Nuevo tamaño
          exec                        Puntero pila inicial                                                   Si éxito, ninguna
          kill                        Identificador proceso y señal                                          Status
          alarm                       Nº segundos a esperar                                                  Tiempo que queda
          pause                       ninguno                                                                Si éxito, ninguna
          sigaction                   Nº de señal, accion, accion ant.                                       Status
          sigsuspend                  Mascara de señal                                                       Si éxito, ninguna
          sigpending                  ninguno                                                                Status
          sigprocmask                 Cómo, set y set anterior                                               Status
          sigreturn                   contexto                                                               Status
          getuid                      ninguno                                                                Uid y uid efectivo
          getgid                      ninguno                                                                Gid y gid efectivo
          getpid                      ninguno                                                                Pid y pid del padre

                   Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -                Capítulo IV. Pag. nº 5
                            Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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”.




                                                                                                                                                   5
Gestión de memoria Minix 3
        4.2 Tratamiento de mensajes (2)
        Tipo mensaje              Parámetros de entrada                                                Valor devuelto
        setuid                    Nuevo uid                                                            Status
        setgid                    Nuevo gid                                                            Status
        setsid                    Nuevo sid                                                            Grupo de proceso
        getpgrp                   Nuevo gid                                                            Grupo de proceso
        time                      Puntero al lugar donde poner el tiempo actual                        Status
        stime                     Puntero al tiempo actual                                             Status
        times                     Puntero a buffer para tiempos del proceso e hijo                     Tiempo desde el arranque
        ptrace                    petición, pid, dirección y datos                                     Status
        reboot                    Cómo (halt, reboot o panic)                                          Si éxito, ninguna
        svrctl                    Petición, datos, (depend. Func.)                                     Status
        getsysinfo                Petición, datos, (depend. Func.)                                     Status
        getprocnr                 ninguno                                                              Nº de proceso
        memalloc                  Tamaño, puntero a dirección                                          Status
        memfree                   Tamaño, dirección                                                    Status
        getpriority               Pid, tipo y valor                                                    Prioridad
        setpriority               Pid, tipo y valor                                                    Prioridad
        gettimeofday              ninguno                                                              Tiempo

                 Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -           Capítulo IV. Pag. nº 6
                          Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                            6
Gestión de memoria Minix 3
         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 de
          procesos 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…*/



               Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 7
                        Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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’.




                                                                                                                                  7
Gestión de memoria Minix 3
         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];




                Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 8
                         Escuela Universitaria de Informática - Universidad Politécnica de Madrid.



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 un
entero sin signo, de 16 bits en un procesador de baja capacidad y de 32 procesadores
superiores.




                                                                                                                                   8
Gestión de memoria Minix 3
          4.3 Estructuras de datos y algoritmos del Gestor de Procesos (3)

               Tabla de procesos                                                                  Lista de huecos
                                                              Gestor de
                                                              Procesos

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


                        210 KB (0x34800)                Virtual Física Long.                           Virtual Física Long.
              Pila                           Código 0x00           0xC8       0x00           Código 0x00         0xC8       0x03
                        208 KB (0x34000)       Datos 0x00          0xC8       0x07            Datos 0x00         0xCB       0x04
                        207 KB (0x33C00)         Pila 0x08         0xD0       0x02               Pila 0x05       0xD0       0x02
             Datos                                                    I+D juntos                              I+D separados
                        203 KB (0x32C00)
            Código                                Respecto a dir. fisica de                         Respecto a la dir. fisica
                        200 KB (0x32000)          datos: D0 – C8 = 08                               de datos: D0 - CB = 05


                 Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -             Capítulo IV. Pag. nº 9
                          Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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).




                                                                                                                                              9
Gestión de memoria Minix 3
        4.3 Estructuras de datos y algoritmos del Gestor de Procesos (4)

                 Gestor de
                                                        Estructuras ‘hole’ en lista ordenada
                 Procesos                               por dirección base ascendente.                  Prog. C

                                               hole_head
                                                                                                        Prog. B

            Lista de huecos del GP
                                                                                                        Prog. A

               Dirección base                                                  Puntero al siguiente      Minix
                                            Tamaño del hueco
               (en clics) ‘h_base’          (en clics) ‘h_len’                 ‘h_next’

                                              #define NR_PROCS 100
                                              #define NR_HOLES (2*NR_PROCS+4) /* max elementos en tabla ‘hole’*/
                                              #define NIL_HOLE (struct hole *) 0
                                              PRIVATE struct hole {
          Declaracion en ‘C’ de
                                                   struct hole *h_next;          /* puntero al siguiente */
          la estructura de datos                   phys_clicks h_base;           /* comienzo del hueco */
          para la lista de huecos                  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 */

              Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 10
                       Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                  10
Gestión de memoria Minix 3
        4.3 Estructuras de datos y algoritmos del Gestor de Procesos (5)

                                                                                                        200
                   40 clics                                                     hole_head                20


                      C
                   30 clics                                                     free_slots


                   10 clics                                                                             300
                                                                                                         10
                      B
                   20 clics
                                                                                                        340
                                                                                                         40
                      A                                                                                 Null
                   60 clics


                   20 clics
                                               200
                    MINIX



              Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -          Capítulo IV. Pag. nº 11
                       Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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
Gestión de memoria Minix 3
         4.3 Estructuras de datos y algoritmos del Gestor de Procesos (5)
                                                                        Memoria           clics
                                                                         254 KB                                        Null
                                                                                         0x0302
                                             (1)                                                          D. Base Tamaño
                                                                         Prog. B
                                    Situación Inicial                                    0x02D0
                                                                                                           0302   00FE
                                    de la memoria y
                                     lista de huecos                     500 KB
                                                                                         0x00DC            00DC 01F4
                  (2)                                                    Prog. A         0x00B4
            Situación tras                                                Minix                            hole_head
                                                                                         0x0000
          ubicar el programa
             ‘C’ de 100 KB’

                                                                                                                      (3)
              Memoria          clics                                            Memoria           clics         Situación tras
              254 KB                                          Null                                            La muerte de los
                              0x0302
                                               D. Base Tamaño
                                                                                                              Programas ‘B’ y ‘C’
              Prog. B
                              0x02D0
                                                0302     00FE                   804 KB                                        Null
              400 KB
                              0x0140                                                                            D. Base Tamaño
              Prog. C         0x00DC            0140     0190                                                     00DC 0324
                                                                                                0x00DC
              Prog. A         0x00B4                                            Prog. A
                                                                                                0x00B4
               Minix                            hole_head                         Minix                           hole_head
                              0x0000                                                            0x0000


               Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -            Capítulo IV. Pag. nº 12
                        Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                            12
Gestión de memoria Minix 3
      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 . . . . . */

             Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 13
                      Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.



                                                                                                                                 13
Gestión de memoria Minix 3
       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*/


               Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 14
                        Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                   14
Gestión de memoria Minix 3
           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’ propio
              del 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*/
       }




                  Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 15
                           Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                      15
Gestión de memoria Minix 3
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 únicamente
   para 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 */
}




          Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 16
                   Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




                                                                                                                              16
Gestión de memoria Minix 3
         4.4 Las llamadas al sistema, fork, exit y wait: (fork)
                                                                        Proceso
        Proceso de                                                       hijo
         Usuario
                            ▪ Comprueba tabla
                              de procesos llena
                            ▪ Reserva memoria                                                               ▪ Avisa a la TS
                                                           ▪ Informa a la ▪ Informa al
                              para datos y pila                                                               para que copie
                                                             TS del nuevo   GF del nuevo
                              del hijo                                                                        el nuevo mapa
                                                             proceso        proceso
                            ▪ Solicita copiar                                                                 de memoria
                              datos a la TS
        Gestor de
        Procesos
                                             ▪ Busca entrada libre




                                                                                                               SYS_NEWMAP
                                               en la TP para el hijo
                           SYS_VIRCOPY




                                                                                          Tell_fs(FORK,.)
                                               y copia la entrada del




                                                                           SYS_FORK
                                               padre en ella.                                                               ▪ Copia el nuevo
                                             ▪ Cumplimenta el nuevo                                                           mapa desde la
                                               mapa de memoria del                                                           TP del GP a la TP
                                               hijo en su entrada.                                                           del kernel
                                             ▪ asigna un nuevo ‘pid’
       Tarea del                                para el hijo.
        Sistema
                                                              ▪ Crea proceso hijo                             ▪ Crea proceso hijo
                          ▪ Copia datos y pila
       Gestor de                                                en su propia Tabla                              en su propia Tabla
                            del padre a la imagen
       Ficheros             de memoria del hijo
         Significado de las flechas: send_rec (…)
                            flechas                                                                            send (…)

                 Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -                         Capítulo IV. Pag. nº 17
                          Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                                           17
Gestión de memoria Minix 3
          4.4 Las llamadas al sistema, fork, exit y wait: (exit)                                                                   Desaparece,
                                                                                            Proceso                               No hay respuesta
         Proceso de                                                                          padre
          Usuario
                                                          ▪Contabiliza                      SI : --- “cleanup”---
                                                           tiempos de                     ▪Libera entrada en la TP
                                                                                                                                     Se desbloquea
                                                           cpu al padre                   ▪decrementa contador
                                                          ▪Notifica a                        procesos.                               ▪ Recorre TP:
                                                           la TS del fin                  ▪Despierta al padre                        Para cada entrada:
                                                           de ejecución                     devolviendole el pid                     Si es hijo
                                                                                                                                      hijo->parent = Init
         ¿ ALARMA                   ▪Solicita                        ▪Informa al ▪Informa a
                                                                                  la TS del fin                                       Si zombie & Init waiting
         PENDIENTE ?                 tiempos de                       GF del fin
                                                                                  del proceso ¿ PADRE                                  “cleanup” del hijo
                                     cpu a la TS                      del proceso
                                                                                                              EN WAIT ?
         Gestor de
         Procesos
                                              SYS_TIMES




                                                                           Tell_fs (EXIT)               ▪Libera memoria del
                               SYS_SETALARM




                                                                                             SYS_EXIT
                                                              SYS_NICE




              SI:Desactivar                                                                             proceso. Segmento             NO : ------------
                                                                                                        de Datos + Pila y             ▪Estado=zombie.
                                                                                                        segmento de Código            ▪Envía SIGCHLD
         Tarea del                                                                                      si no está más en uso          “sig_proc()”
          Sistema                                                                                       ▪Guarda en entrada
                                                                                                         de TP “exit_status”
         Gestor de                ▪Desactiva                ▪Detiene el
         Ficheros                  alarma                    proceso

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

                  Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -                           Capítulo IV. Pag. nº 18
                           Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                                                 18
Gestión de memoria Minix 3
          4.4 Las llamadas al sistema, fork, exit y wait: (wait)


          Proceso de
           Usuario
                                        Esta respuesta se
                                        envía en cleanup.

            --do_waitpid--                                                                 ----SI -----
                                                ----SI-----
            ▪Recorre la TP                                              ----SI -----       return(0)
                                                 ▪cleanup;
             y para cada                                                 ¿ Opción
                                  ---SI---       ▪return
             entrada:                                                   “WNOHANG”      ?
                                  ¿ Está   (SUSPEND)
            ¿ Es un proceso
                                  zombie ?
                 hijo ?
          Gestor de
          Procesos

                                                                                           estado=WAITING     Return
                                      ¿ Está                             ¿ Tiene            return(SUSPEND)   (ECHILD)
                                                       ----SI -----      algún hijo ?
                                      stopped ?        return(pid)




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

                  Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -     Capítulo IV. Pag. nº 19
                           Escuela Universitaria de Informática - Universidad Politécnica de Madrid.



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.


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.




                                                                                                                                        19
Gestión de memoria Minix 3
        4.5 La llamada al sistema execve (1/5)
                                                                                      Al comprobar el tamaño requerido
                                                                                      nunca se tiene en cuenta que la
                                 “C” hace execve() a                                  liberación de la propia imagen
                                  un programa “D” de                                  sumada a algún hueco contiguo
          40 clics                tamaño 70 clics.                                    podría satisfacer el requisito de
                                 ---------------------------
                                                                                      memoria. Si se puede compartir
                                 ¡ NO HAY MEMORIA !
             C                                                                        código sólo se pide memoria para
          30 clics                                                                    el segmento de datos+gap+pila. No
                                                                                      se considera nunca la posibilidad
                                                                                      de hacer dos peticiones de memo-
          10 clics                                                                    ria, aún siendo I+D separados.
                                    “B” hace execve()
                                    a un programa “E”                                        I+D Juntos
             B
                                      con una pila de
          20 clics                                                                    Tamaños segmentos de “E”
                                        1400 bytes
                                                                                      ------------------------
                                     --------------------
                                  ¡ NO HAY MEMORIA !                                          Bytes     Clics
             A
                                                                                      Código 20.510        --
          60 clics
                                                                                      Datos       220
                                                                                      BSS     10.000       31
          20 clics                                                                    Pila     1.400        2
                            d                                                         Total   32.740       32
          MINIX



              Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -    Capítulo IV. Pag. nº 20
                       Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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
Gestión de memoria Minix 3
         4.5 La llamada al sistema execve (2/5)
        envp [ ]                                               execve(“/bin/ls”, argv, envp)             0   t        s     A   8188
                                 0    t          s   a   52
           0                                                                                             /    r        s     u   8184
                                  /    r          s   u   48
                                                                      0    t       s    A   8188        /    =        E     M   8180
                                  /    =         E    M   44
                                                                       /    r       s    u   8184        O    H        0    c   8176
       HOME = /usr/ast           O     H         0   c   40
                                                                       /   =        E    M   8180        .    g        0    c   8172
                                  .    g         0   c   36
                                                                      O    H        0   c   8176        -    f        0    l   8168
                                  -    f         0   l   32
                                                                       .   g        0   c   8172        -    0       s     l   8164
               PILA               -    0         s   l   28
                                                                                                                   0             8160
                                                                       -    f       0   l   8168
                                            0             24
                                                                       -   0       s    l   8164              8178              8156
                                            42            20
                                                                                0            8160                  0             8152
                                            0             16
                                                                                                               8174              8148
        argv [ ]
                                                                            8178             8156
                                            38            12
                                                                                0            8152              8170              8144
           0                                34             8
                                                                            8174             8148              8167              8140
                    g.c                     31             4
                                                                            8170             8144              8164              8136
                    f.c                     28             0
                                                                                                               8156              8132
                                                                            8167             8140                                       envp
                     -l                                                                                        8136              8128   argv
                                                                            8164             8136
                                      DATOS
                     ls                                                                       crtso                4             8124   argc
                                      CODIGO                                                                  return             8120
                                            (a)                                 (b)                                    (c)
               Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -             Capítulo IV. Pag. nº 21
                        Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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
Gestión de memoria Minix 3
         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 segmentos
              read_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


                Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 22
                         Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                    22
Gestión de memoria Minix 3
        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 TS
                     Rellena 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 memoria
             Parchea 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
             memoria
             Si código compartido se lo salta: lseek (fd,..)                  Posible envío LSEEK al GF
             sino, se leo del disco: rw_seg (R, fd, T,..)                     Posible envío READ(s) al GF
             Leo segmento datos: rw_seg (R, fd, D,..)                         Envío READ(s) al GF
             Cierra el fichero: close (fd)                                    Envía CLOSE al GF



               Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 23
                        Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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 se
comparte 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”]).




                                                                                                                                   23
Gestión de memoria Minix 3
        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 GF
             Si 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 GF
             Establece el campo “mp_name” al nombre del fichero ejecutado (para depuracion, ps, salida ,
             etc.)
        - Informa al kernel de que el proceso es ejecutable
             sys_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);




               Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -   Capítulo IV. Pag. nº 24
                        Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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.




                                                                                                                                   24
Gestión de memoria Minix 3
         4.6 La llamada al Sistema brk

       Proceso de
        Usuario
                            Posible respuesta                                  ▪Restaura los antiguos
                            con error o no de-                                  valores de los segmentos
                            pendiendo de la                                    ▪Responde error
                            comprobación
                            anterior                                  ▪¿ Caben los segmentos
                                                                       de datos y pila en el                                     Responde
                                                                       espacio de usuario ?                                        OK
        Gestor de
        Procesos
                  ▪Comprueba que el nuevo                          ------- adjust -------
                   valor es distinto del             SYS_GETINFO   ▪Comprueba si el tamaño de




                                                                                                           SYS_NEWMAP
                   anterior y si lo es, que                         la pila quedaría negativo.                          ----SI ---
                   no es inferior al comienzo                      ▪Calcula el tamaño del gap.                          Informa a la TS
                   del segmento de datos.                          ▪Añade un margen de segu-                            del nuevo mapa
                  ▪Pide a la TS el valor del                        ridad para crecimiento pila.                        de memoria
                   puntero de pila                                 ▪Actualiza tamaño segmento
       Tarea del                                                    de datos y tamaño y origen
                                                                    del segmento de pila.
        Sistema
                                                  ▪Devuelve entre otras
                                                   cosas el puntero de pila

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

                 Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada -                   Capítulo IV. Pag. nº 25
                          Escuela Universitaria de Informática - Universidad Politécnica de Madrid.




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
2 gestion de memoria en minix 3
2 gestion de memoria en minix 3
2 gestion de memoria en minix 3
2 gestion de memoria en minix 3

Más contenido relacionado

La actualidad más candente

Taxonomia de las herramientas case
Taxonomia de las herramientas caseTaxonomia de las herramientas case
Taxonomia de las herramientas caseisidro luna beltran
 
Ranuras De Expansion
Ranuras De ExpansionRanuras De Expansion
Ranuras De Expansionstteffanya
 
Excel Trabajo Luis Completo Y Terminado Con Powerpoint
Excel Trabajo Luis Completo Y Terminado Con PowerpointExcel Trabajo Luis Completo Y Terminado Con Powerpoint
Excel Trabajo Luis Completo Y Terminado Con PowerpointLuis Rozo Sanchez
 
Arquitectura del sistema operativo windows
Arquitectura del sistema operativo windowsArquitectura del sistema operativo windows
Arquitectura del sistema operativo windowsflaviosimbana07
 
FUNDAMENTOS DE SISTEMAS OPERATIVOS
FUNDAMENTOS DE SISTEMAS OPERATIVOSFUNDAMENTOS DE SISTEMAS OPERATIVOS
FUNDAMENTOS DE SISTEMAS OPERATIVOSBenjaminAnilema
 
Sistema De Gestión De Base De Datos
Sistema De Gestión De Base De DatosSistema De Gestión De Base De Datos
Sistema De Gestión De Base De DatosGuillermo Chirinos
 
Teformas Ejercicios Prácticos Excel
Teformas Ejercicios Prácticos ExcelTeformas Ejercicios Prácticos Excel
Teformas Ejercicios Prácticos ExcelTeFormas
 
Capa de enlace de datos
Capa de enlace de datosCapa de enlace de datos
Capa de enlace de datosComdat4
 
CUADRO COMPARATIVO ENTRE MODELO OSI Y TCP/IP
CUADRO COMPARATIVO ENTRE MODELO OSI Y TCP/IPCUADRO COMPARATIVO ENTRE MODELO OSI Y TCP/IP
CUADRO COMPARATIVO ENTRE MODELO OSI Y TCP/IPdisenarUniminuto
 
2 ejercicio de access grado 11-1
2 ejercicio de access grado 11-12 ejercicio de access grado 11-1
2 ejercicio de access grado 11-1shernanva
 
Power Point de introducción a Excel de Microsoft
Power Point de introducción a Excel de MicrosoftPower Point de introducción a Excel de Microsoft
Power Point de introducción a Excel de MicrosoftFrancesc Oliva Lloveras
 
Funcionamiento Del Switch Y Del Router
Funcionamiento Del Switch Y Del RouterFuncionamiento Del Switch Y Del Router
Funcionamiento Del Switch Y Del Routerutch
 
Discos duros hdd ssd
Discos duros hdd ssdDiscos duros hdd ssd
Discos duros hdd ssdltorres47
 

La actualidad más candente (20)

Taxonomia de las herramientas case
Taxonomia de las herramientas caseTaxonomia de las herramientas case
Taxonomia de las herramientas case
 
Sistemas Operativos Mono Proceso
Sistemas Operativos Mono ProcesoSistemas Operativos Mono Proceso
Sistemas Operativos Mono Proceso
 
Ranuras De Expansion
Ranuras De ExpansionRanuras De Expansion
Ranuras De Expansion
 
Memoria caché (Explicación)
Memoria caché  (Explicación)  Memoria caché  (Explicación)
Memoria caché (Explicación)
 
Excel Trabajo Luis Completo Y Terminado Con Powerpoint
Excel Trabajo Luis Completo Y Terminado Con PowerpointExcel Trabajo Luis Completo Y Terminado Con Powerpoint
Excel Trabajo Luis Completo Y Terminado Con Powerpoint
 
Arquitectura del sistema operativo windows
Arquitectura del sistema operativo windowsArquitectura del sistema operativo windows
Arquitectura del sistema operativo windows
 
Capa fisica
Capa fisicaCapa fisica
Capa fisica
 
Intel vs amd tablas comparativas
Intel vs amd   tablas comparativasIntel vs amd   tablas comparativas
Intel vs amd tablas comparativas
 
FUNDAMENTOS DE SISTEMAS OPERATIVOS
FUNDAMENTOS DE SISTEMAS OPERATIVOSFUNDAMENTOS DE SISTEMAS OPERATIVOS
FUNDAMENTOS DE SISTEMAS OPERATIVOS
 
Sistemas distribuidos
Sistemas distribuidosSistemas distribuidos
Sistemas distribuidos
 
Sistema De Gestión De Base De Datos
Sistema De Gestión De Base De DatosSistema De Gestión De Base De Datos
Sistema De Gestión De Base De Datos
 
Teformas Ejercicios Prácticos Excel
Teformas Ejercicios Prácticos ExcelTeformas Ejercicios Prácticos Excel
Teformas Ejercicios Prácticos Excel
 
Disco Duro
Disco DuroDisco Duro
Disco Duro
 
Capa de enlace de datos
Capa de enlace de datosCapa de enlace de datos
Capa de enlace de datos
 
Sistema de archivos HPFS
Sistema de archivos HPFSSistema de archivos HPFS
Sistema de archivos HPFS
 
CUADRO COMPARATIVO ENTRE MODELO OSI Y TCP/IP
CUADRO COMPARATIVO ENTRE MODELO OSI Y TCP/IPCUADRO COMPARATIVO ENTRE MODELO OSI Y TCP/IP
CUADRO COMPARATIVO ENTRE MODELO OSI Y TCP/IP
 
2 ejercicio de access grado 11-1
2 ejercicio de access grado 11-12 ejercicio de access grado 11-1
2 ejercicio de access grado 11-1
 
Power Point de introducción a Excel de Microsoft
Power Point de introducción a Excel de MicrosoftPower Point de introducción a Excel de Microsoft
Power Point de introducción a Excel de Microsoft
 
Funcionamiento Del Switch Y Del Router
Funcionamiento Del Switch Y Del RouterFuncionamiento Del Switch Y Del Router
Funcionamiento Del Switch Y Del Router
 
Discos duros hdd ssd
Discos duros hdd ssdDiscos duros hdd ssd
Discos duros hdd ssd
 

Similar a 2 gestion de memoria en minix 3

administracion de memoria y archivos
administracion de memoria y archivosadministracion de memoria y archivos
administracion de memoria y archivosSamir Barrios
 
administración de memoria y archivos
administración de memoria y archivosadministración de memoria y archivos
administración de memoria y archivosSamir Barrios
 
Segunda unidas open suse
Segunda unidas open suseSegunda unidas open suse
Segunda unidas open suseMiguel Magaña
 
Sistemas Operativos - Memoria
Sistemas Operativos - MemoriaSistemas Operativos - Memoria
Sistemas Operativos - Memoriavdelgado3
 
CPU, MEMORIA, REGISTROS, UNIDAD ARITMETICO-LOGICA
CPU, MEMORIA, REGISTROS, UNIDAD ARITMETICO-LOGICACPU, MEMORIA, REGISTROS, UNIDAD ARITMETICO-LOGICA
CPU, MEMORIA, REGISTROS, UNIDAD ARITMETICO-LOGICAobhyguan
 
medios de almacenamiento
medios de almacenamientomedios de almacenamiento
medios de almacenamientoClaudio Lobos
 
U2 computadores idea general (hardware y software)
U2   computadores idea general (hardware y software)U2   computadores idea general (hardware y software)
U2 computadores idea general (hardware y software)Julio Ravazzani
 
Castaño y navarro
Castaño y navarroCastaño y navarro
Castaño y navarrokathenavarro
 
Castaño y navarro
Castaño y navarroCastaño y navarro
Castaño y navarrokathenavarro
 
Evaluacion1 para estudiar
Evaluacion1  para estudiarEvaluacion1  para estudiar
Evaluacion1 para estudiarAlex Silva
 
1 hardware software_so
1 hardware software_so1 hardware software_so
1 hardware software_somozkon
 
Conceptos basicos de informatica
Conceptos basicos de informaticaConceptos basicos de informatica
Conceptos basicos de informaticamelvinsosa1234
 

Similar a 2 gestion de memoria en minix 3 (20)

administracion de memoria y archivos
administracion de memoria y archivosadministracion de memoria y archivos
administracion de memoria y archivos
 
administración de memoria y archivos
administración de memoria y archivosadministración de memoria y archivos
administración de memoria y archivos
 
La computadora
La computadoraLa computadora
La computadora
 
Segunda unidas open suse
Segunda unidas open suseSegunda unidas open suse
Segunda unidas open suse
 
Sistemas Operativos - Memoria
Sistemas Operativos - MemoriaSistemas Operativos - Memoria
Sistemas Operativos - Memoria
 
CPU, MEMORIA, REGISTROS, UNIDAD ARITMETICO-LOGICA
CPU, MEMORIA, REGISTROS, UNIDAD ARITMETICO-LOGICACPU, MEMORIA, REGISTROS, UNIDAD ARITMETICO-LOGICA
CPU, MEMORIA, REGISTROS, UNIDAD ARITMETICO-LOGICA
 
Memoria
MemoriaMemoria
Memoria
 
Sistemas de Computo
Sistemas de ComputoSistemas de Computo
Sistemas de Computo
 
medios de almacenamiento
medios de almacenamientomedios de almacenamiento
medios de almacenamiento
 
U2 computadores idea general (hardware y software)
U2   computadores idea general (hardware y software)U2   computadores idea general (hardware y software)
U2 computadores idea general (hardware y software)
 
Memoria en los SO
Memoria en los SOMemoria en los SO
Memoria en los SO
 
Hardware software y-clasifiacion
Hardware software y-clasifiacionHardware software y-clasifiacion
Hardware software y-clasifiacion
 
Hardware software y-clasifiacion
Hardware software y-clasifiacionHardware software y-clasifiacion
Hardware software y-clasifiacion
 
Castaño y navarro
Castaño y navarroCastaño y navarro
Castaño y navarro
 
Castaño y navarro
Castaño y navarroCastaño y navarro
Castaño y navarro
 
Evaluacion1 para estudiar
Evaluacion1  para estudiarEvaluacion1  para estudiar
Evaluacion1 para estudiar
 
1 hardware software_so
1 hardware software_so1 hardware software_so
1 hardware software_so
 
Evaluacion1
Evaluacion1Evaluacion1
Evaluacion1
 
Estructurra
EstructurraEstructurra
Estructurra
 
Conceptos basicos de informatica
Conceptos basicos de informaticaConceptos basicos de informatica
Conceptos basicos de informatica
 

2 gestion de memoria en minix 3

  • 1. Gestión de memoria Minix 3 • 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) Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 1 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 1
  • 2. Gestión de memoria Minix 3 Visión general •No utiliza paginación Modo usuario •Posible activación del uso de Intercambio •Comunicación por paso de mensajes Gestor de •Implementa la estrategia de asignación de memoria: Procesos 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… Modo supervisor Núcleo •Implementa el mecanismo de asignación de memoria: de Minix Copia de mapas de memoria Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 2 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 2
  • 3. Gestión de memoria Minix 3 4.1 Organización de la memoria (1) Fichero ejecutable en disco Direcciones Altas → Símbolos (opcional) Datos (inicializados) Pila Código (text) Prog. B gap Segmento Cabecera I+D Separados Datos+bss Codigo Segmento • Bit de I+D juntos o separados Pila • Tamaños de memoria: Prog. A gap Total, Código, Datos, y BSS I+D juntos Segmento (El tamaño de memoria total en I+D juntos Datos+bss comprende la cantidad de memoria total necesaria para el proceso. En I+D separados, Código éste, no incluye el tamaño del código) Minix Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 3 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 3
  • 4. Gestión de memoria Minix 3 4.1 Organización de la memoria (2) Asignación de memoria. · Con las llamadas: Gestor de fork: la misma cantidad que al padre Procesos execve: la cantidad indicada en la cabecera del fichero ejecutable Liberación de memoria. · Cuando un proceso muere, bien mediante exit ó por la llegada de una señal cuyo tratamiento sea matar al proceso receptor. · Con execve se libera si la llamada tiene éxito. Dir. Altas → Hijo de A Prog. C Mapas de memoria: (I+D juntos) Prog. B Prog. B (a) Originalmente, Prog. B (b) Después de fork, (c) Después de execv del hijo. Prog. A Prog. A Prog. A Minix Minix Minix (a) (b) (c) Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 4 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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.
  • 5. Gestión de memoria Minix 3 4.2 Tratamiento de mensajes (1) Tipo mensaje Parámetros de entrada Valor devuelto fork ninguno Pid hijo y 0 al hijo exit exit status Si éxito, ninguna wait ninguno Status waitpid Identificador de proceso y flags Status brk Nuevo tamaño Nuevo tamaño exec Puntero pila inicial Si éxito, ninguna kill Identificador proceso y señal Status alarm Nº segundos a esperar Tiempo que queda pause ninguno Si éxito, ninguna sigaction Nº de señal, accion, accion ant. Status sigsuspend Mascara de señal Si éxito, ninguna sigpending ninguno Status sigprocmask Cómo, set y set anterior Status sigreturn contexto Status getuid ninguno Uid y uid efectivo getgid ninguno Gid y gid efectivo getpid ninguno Pid y pid del padre Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 5 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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”. 5
  • 6. Gestión de memoria Minix 3 4.2 Tratamiento de mensajes (2) Tipo mensaje Parámetros de entrada Valor devuelto setuid Nuevo uid Status setgid Nuevo gid Status setsid Nuevo sid Grupo de proceso getpgrp Nuevo gid Grupo de proceso time Puntero al lugar donde poner el tiempo actual Status stime Puntero al tiempo actual Status times Puntero a buffer para tiempos del proceso e hijo Tiempo desde el arranque ptrace petición, pid, dirección y datos Status reboot Cómo (halt, reboot o panic) Si éxito, ninguna svrctl Petición, datos, (depend. Func.) Status getsysinfo Petición, datos, (depend. Func.) Status getprocnr ninguno Nº de proceso memalloc Tamaño, puntero a dirección Status memfree Tamaño, dirección Status getpriority Pid, tipo y valor Prioridad setpriority Pid, tipo y valor Prioridad gettimeofday ninguno Tiempo Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 6 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 6
  • 7. Gestión de memoria Minix 3 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 de procesos 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…*/ Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 7 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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’. 7
  • 8. Gestión de memoria Minix 3 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]; Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 8 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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 un entero sin signo, de 16 bits en un procesador de baja capacidad y de 32 procesadores superiores. 8
  • 9. Gestión de memoria Minix 3 4.3 Estructuras de datos y algoritmos del Gestor de Procesos (3) Tabla de procesos Lista de huecos Gestor de Procesos Dirección virtual, física y tamaño de cada segmento Proceso “i” interno, medido todo ello campo en “clics”. ‘mp_seg’ (1 clic = 1024 bytes) (datos en hexadecimal) 210 KB (0x34800) Virtual Física Long. Virtual Física Long. Pila Código 0x00 0xC8 0x00 Código 0x00 0xC8 0x03 208 KB (0x34000) Datos 0x00 0xC8 0x07 Datos 0x00 0xCB 0x04 207 KB (0x33C00) Pila 0x08 0xD0 0x02 Pila 0x05 0xD0 0x02 Datos I+D juntos I+D separados 203 KB (0x32C00) Código Respecto a dir. fisica de Respecto a la dir. fisica 200 KB (0x32000) datos: D0 – C8 = 08 de datos: D0 - CB = 05 Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 9 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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). 9
  • 10. Gestión de memoria Minix 3 4.3 Estructuras de datos y algoritmos del Gestor de Procesos (4) Gestor de Estructuras ‘hole’ en lista ordenada Procesos por dirección base ascendente. Prog. C hole_head Prog. B Lista de huecos del GP Prog. A Dirección base Puntero al siguiente Minix Tamaño del hueco (en clics) ‘h_base’ (en clics) ‘h_len’ ‘h_next’ #define NR_PROCS 100 #define NR_HOLES (2*NR_PROCS+4) /* max elementos en tabla ‘hole’*/ #define NIL_HOLE (struct hole *) 0 PRIVATE struct hole { Declaracion en ‘C’ de struct hole *h_next; /* puntero al siguiente */ la estructura de datos phys_clicks h_base; /* comienzo del hueco */ para la lista de huecos 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 */ Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 10 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 10
  • 11. Gestión de memoria Minix 3 4.3 Estructuras de datos y algoritmos del Gestor de Procesos (5) 200 40 clics hole_head 20 C 30 clics free_slots 10 clics 300 10 B 20 clics 340 40 A Null 60 clics 20 clics 200 MINIX Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 11 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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
  • 12. Gestión de memoria Minix 3 4.3 Estructuras de datos y algoritmos del Gestor de Procesos (5) Memoria clics 254 KB Null 0x0302 (1) D. Base Tamaño Prog. B Situación Inicial 0x02D0 0302 00FE de la memoria y lista de huecos 500 KB 0x00DC 00DC 01F4 (2) Prog. A 0x00B4 Situación tras Minix hole_head 0x0000 ubicar el programa ‘C’ de 100 KB’ (3) Memoria clics Memoria clics Situación tras 254 KB Null La muerte de los 0x0302 D. Base Tamaño Programas ‘B’ y ‘C’ Prog. B 0x02D0 0302 00FE 804 KB Null 400 KB 0x0140 D. Base Tamaño Prog. C 0x00DC 0140 0190 00DC 0324 0x00DC Prog. A 0x00B4 Prog. A 0x00B4 Minix hole_head Minix hole_head 0x0000 0x0000 Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 12 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 12
  • 13. Gestión de memoria Minix 3 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 . . . . . */ Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 13 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 13
  • 14. Gestión de memoria Minix 3 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*/ Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 14 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 14
  • 15. Gestión de memoria Minix 3 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’ propio del 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*/ } Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 15 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 15
  • 16. Gestión de memoria Minix 3 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 únicamente para 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 */ } Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 16 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 16
  • 17. Gestión de memoria Minix 3 4.4 Las llamadas al sistema, fork, exit y wait: (fork) Proceso Proceso de hijo Usuario ▪ Comprueba tabla de procesos llena ▪ Reserva memoria ▪ Avisa a la TS ▪ Informa a la ▪ Informa al para datos y pila para que copie TS del nuevo GF del nuevo del hijo el nuevo mapa proceso proceso ▪ Solicita copiar de memoria datos a la TS Gestor de Procesos ▪ Busca entrada libre SYS_NEWMAP en la TP para el hijo SYS_VIRCOPY Tell_fs(FORK,.) y copia la entrada del SYS_FORK padre en ella. ▪ Copia el nuevo ▪ Cumplimenta el nuevo mapa desde la mapa de memoria del TP del GP a la TP hijo en su entrada. del kernel ▪ asigna un nuevo ‘pid’ Tarea del para el hijo. Sistema ▪ Crea proceso hijo ▪ Crea proceso hijo ▪ Copia datos y pila Gestor de en su propia Tabla en su propia Tabla del padre a la imagen Ficheros de memoria del hijo Significado de las flechas: send_rec (…) flechas send (…) Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 17 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 17
  • 18. Gestión de memoria Minix 3 4.4 Las llamadas al sistema, fork, exit y wait: (exit) Desaparece, Proceso No hay respuesta Proceso de padre Usuario ▪Contabiliza SI : --- “cleanup”--- tiempos de ▪Libera entrada en la TP Se desbloquea cpu al padre ▪decrementa contador ▪Notifica a procesos. ▪ Recorre TP: la TS del fin ▪Despierta al padre Para cada entrada: de ejecución devolviendole el pid Si es hijo hijo->parent = Init ¿ ALARMA ▪Solicita ▪Informa al ▪Informa a la TS del fin Si zombie & Init waiting PENDIENTE ? tiempos de GF del fin del proceso ¿ PADRE “cleanup” del hijo cpu a la TS del proceso EN WAIT ? Gestor de Procesos SYS_TIMES Tell_fs (EXIT) ▪Libera memoria del SYS_SETALARM SYS_EXIT SYS_NICE SI:Desactivar proceso. Segmento NO : ------------ de Datos + Pila y ▪Estado=zombie. segmento de Código ▪Envía SIGCHLD Tarea del si no está más en uso “sig_proc()” Sistema ▪Guarda en entrada de TP “exit_status” Gestor de ▪Desactiva ▪Detiene el Ficheros alarma proceso Significado de las flechas: send_rec (…) flechas send (…) Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 18 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 18
  • 19. Gestión de memoria Minix 3 4.4 Las llamadas al sistema, fork, exit y wait: (wait) Proceso de Usuario Esta respuesta se envía en cleanup. --do_waitpid-- ----SI ----- ----SI----- ▪Recorre la TP ----SI ----- return(0) ▪cleanup; y para cada ¿ Opción ---SI--- ▪return entrada: “WNOHANG” ? ¿ Está (SUSPEND) ¿ Es un proceso zombie ? hijo ? Gestor de Procesos estado=WAITING Return ¿ Está ¿ Tiene return(SUSPEND) (ECHILD) ----SI ----- algún hijo ? stopped ? return(pid) Significado de las flechas: send_rec (…) flechas send (…) Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 19 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 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. 19
  • 20. Gestión de memoria Minix 3 4.5 La llamada al sistema execve (1/5) Al comprobar el tamaño requerido nunca se tiene en cuenta que la “C” hace execve() a liberación de la propia imagen un programa “D” de sumada a algún hueco contiguo 40 clics tamaño 70 clics. podría satisfacer el requisito de --------------------------- memoria. Si se puede compartir ¡ NO HAY MEMORIA ! C código sólo se pide memoria para 30 clics el segmento de datos+gap+pila. No se considera nunca la posibilidad de hacer dos peticiones de memo- 10 clics ria, aún siendo I+D separados. “B” hace execve() a un programa “E” I+D Juntos B con una pila de 20 clics Tamaños segmentos de “E” 1400 bytes ------------------------ -------------------- ¡ NO HAY MEMORIA ! Bytes Clics A Código 20.510 -- 60 clics Datos 220 BSS 10.000 31 20 clics Pila 1.400 2 d Total 32.740 32 MINIX Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 20 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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
  • 21. Gestión de memoria Minix 3 4.5 La llamada al sistema execve (2/5) envp [ ] execve(“/bin/ls”, argv, envp) 0 t s A 8188 0 t s a 52 0 / r s u 8184 / r s u 48 0 t s A 8188 / = E M 8180 / = E M 44 / r s u 8184 O H 0 c 8176 HOME = /usr/ast O H 0 c 40 / = E M 8180 . g 0 c 8172 . g 0 c 36 O H 0 c 8176 - f 0 l 8168 - f 0 l 32 . g 0 c 8172 - 0 s l 8164 PILA - 0 s l 28 0 8160 - f 0 l 8168 0 24 - 0 s l 8164 8178 8156 42 20 0 8160 0 8152 0 16 8174 8148 argv [ ] 8178 8156 38 12 0 8152 8170 8144 0 34 8 8174 8148 8167 8140 g.c 31 4 8170 8144 8164 8136 f.c 28 0 8156 8132 8167 8140 envp -l 8136 8128 argv 8164 8136 DATOS ls crtso 4 8124 argc CODIGO return 8120 (a) (b) (c) Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 21 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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
  • 22. Gestión de memoria Minix 3 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 segmentos read_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 Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 22 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 22
  • 23. Gestión de memoria Minix 3 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 TS Rellena 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 memoria Parchea 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 memoria Si código compartido se lo salta: lseek (fd,..) Posible envío LSEEK al GF sino, se leo del disco: rw_seg (R, fd, T,..) Posible envío READ(s) al GF Leo segmento datos: rw_seg (R, fd, D,..) Envío READ(s) al GF Cierra el fichero: close (fd) Envía CLOSE al GF Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 23 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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 se comparte 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”]). 23
  • 24. Gestión de memoria Minix 3 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 GF Si 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 GF Establece el campo “mp_name” al nombre del fichero ejecutado (para depuracion, ps, salida , etc.) - Informa al kernel de que el proceso es ejecutable sys_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); Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 24 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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. 24
  • 25. Gestión de memoria Minix 3 4.6 La llamada al Sistema brk Proceso de Usuario Posible respuesta ▪Restaura los antiguos con error o no de- valores de los segmentos pendiendo de la ▪Responde error comprobación anterior ▪¿ Caben los segmentos de datos y pila en el Responde espacio de usuario ? OK Gestor de Procesos ▪Comprueba que el nuevo ------- adjust ------- valor es distinto del SYS_GETINFO ▪Comprueba si el tamaño de SYS_NEWMAP anterior y si lo es, que la pila quedaría negativo. ----SI --- no es inferior al comienzo ▪Calcula el tamaño del gap. Informa a la TS del segmento de datos. ▪Añade un margen de segu- del nuevo mapa ▪Pide a la TS el valor del ridad para crecimiento pila. de memoria puntero de pila ▪Actualiza tamaño segmento Tarea del de datos y tamaño y origen del segmento de pila. Sistema ▪Devuelve entre otras cosas el puntero de pila Significado de las flechas: send_rec (…) flechas send (…) Aníbal Ramírez García - Sistemas Operativos II - Departamento de Informática Aplicada - Capítulo IV. Pag. nº 25 Escuela Universitaria de Informática - Universidad Politécnica de Madrid. 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