Llamadas al sistema

 Llamadas al sistema.- 

Un Sistema Operativo multitarea en el que van a convivir un gran número de procesos. Es posible, bien por un fallo de programación o bien por un intento malicioso, que alguno de esos procesos haga cosas que atenten contra la estabilidad de todo el sistema. Por ello, con vistas a proteger esa estabilidad, el núcleo o kernel del sistema funciona en un entorno totalmente diferente al resto de programas. Se definen entonces dos modos de ejecución totalmente separados: el modo kernel y el modo usuario. Cada uno de estos modos de ejecución dispone de memoria y procedimientos diferentes, por lo que un programa de usuario no podrá ser capaz de dañar al núcleo.

Aquí se plantea una duda: si el núcleo del sistema es el único capaz de manipular los recursos físicos del sistema (hardware), y éste se ejecuta en un modo de ejecución totalmente disjunto al del resto de los programas, ¿cómo es posible que un pequeño programa hecho por mí sea capaz de leer y escribir en disco? Bien, la duda es lógica, porque todavía no hemos hablado de las “llamadas o peticiones al sistema” (“syscalls”).

Las syscalls o llamadas al sistema son:

  1. El mecanismo por el cual los procesos y aplicaciones de usuario acceden a los servicios del núcleo.
  2. Son la interfaz que proporciona el núcleo para realizar desde el modo usuario las cosas que son propias del modo kernel (como acceder a disco o utilizar una tarjeta de sonido).

Figura 1.1.1. Mecanismo de petición de servicios al kernel.

Explicación:

  • El proceso de usuario necesita acceder al disco para leer, para ello utiliza la syscall read() utilizando la interfaz de llamadas al sistema.
  • El núcleo atiende la petición accediendo al hardware y devolviendo el resultado al proceso que inició la petición.

Este procedimiento me recuerda al comedor de un restaurante, en él todos los clientes piden al camarero lo que desean, pero nunca entran en la cocina. El camarero, después de pasar por la cocina, traerá el plato que cada cliente haya pedido. Ningún comensal podría estropear la cocina, puesto que no tiene acceso a ella.

1.2.1. Estructuras de datos

Si queremos implementar la ejecución de varias tareas al mismo tiempo, los cambios de contexto entre tareas y todo lo concerniente a la multiprogramación, es necesario disponer de un modelo de procesos y las estructuras de datos relacionadas para ello.

Un modelo de procesos típico consta de los siguientes elementos:

  • PCB (Process Control Block): un bloque o estructura de datos que contiene la información necesaria de cada proceso. Permite almacenar el contexto de cada uno de los procesos con el objeto de ser reanudado posteriormente. Suele ser un conjunto de  identificadores de proceso, tablas de manejo de memoria, estado de los registros del procesador, apuntadores de pila, etc.
  • Tabla de Procesos: la tabla que contiene todos los PCBs o bloques de control de proceso. Se actualiza a medida que se van creando y eliminando procesos o se producen transiciones entre los estados de los mismos.
  • Estados y Transiciones de los Procesos: los procesos se ordenan en función de su información de Planificación, es decir, en función de su estado. Así pues, habrá procesos bloqueados en espera de un recurso, listos para la ejecución, en ejecución, terminando, etc.
  • Vector de Interrupciones: contiene un conjunto de apuntadores a rutinas que se encargarán de atender cada una de las interrupciones que puedan producirse en el sistema.

En Linux esto está implementado a través de una estructura de datos denominada task_struct. Es el PCB de Linux, en ella se almacena toda la información relacionada con un proceso: identificadores de proceso, tablas de manejo de memoria, estado de los registros del procesador, apuntadores de pila, etc.

La Tabla de Procesos no es más que un array de task_struct,

extern struct task_struct *pidhash[PIDHASH_SZ];

PIDHASH_SZ determina el número de tareas capaces de ser gestionadas por esa tabla (definida en “/usr/src/linux/include/linux/sched.h”). Por defecto PIDHASH_SZ vale 512, es decir, es posible gestionar 512 tareas concurrentemente desde un único proceso inicial o “init”. Podremos tener tantos procesos “init” o iniciales como CPUs tenga nuestro sistema:

extern struct task_struct *init_tasks[NR_CPUS];

NR_CPUS determina el número de procesadores disponibles en el sistema (definida en “/usr/src/linux/include/linux/sched.h”). Por defecto NR_CPUS vale.1, pero si se habilita el soporte para multiprocesador, SMP, este número puede crecer hasta 32.

1.2.2. Estados de los procesos en linux

En Linux el estado de cada proceso se almacena dentro de un campo de la estructura task_struct. Dicho campo, “state”, irá variando en función del estado de ejecución en el que se encuentre el proceso, pudiendo tomar los siguientes valores:

  • TASK_RUNNING (0): Indica que el proceso en cuestión se está ejecutando o listo para ejecutarse. En este segundo caso, el proceso dispone de todos los recursos necesarios excepto el procesador.
    TASK_INTERRUPTIBLE (1): el proceso está suspendido a la espera de alguna señal para pasar a listo para ejecutarse. Generalmente se debe a que el proceso está esperando a que otro proceso del sistema le preste algún servicio solicitado.
    TASK_UNINTERRUPTIBLE (2): el proceso está bloqueado esperando a que se le conceda algún recurso hardware que ha solicitado (cuando una señal no es capaz de “despertarlo”).
    TASK_ZOMBIE (4): el proceso ha finalizado pero aún no se ha eliminado todo rastro del mismo del sistema. Esto es habitualmente causado porque el proceso padre todavía lo espera con una wait().
    TASK_STOPPED (8): el proceso ha sido detenido por una señal o bien mediante el uso de ptrace() para ser trazado.

En función del estado de la tarea o proceso, estará en una u otra cola de procesos:

  1. Cola de Ejecución o runqueue: procesos en estado TASK_RUNNING.
  2. Colas de Espera o wait queues: procesos en estado TASK_INTERRUPTIBLE ó TASK_ININTERRUPTIBLE.
  3. Los procesos en estado TASK_ZOMBIE ó TASK_STOPPED no necesitan colas para ser gestionados.

1.2.3. Identificativos de proceso

Todos los procesos del sistema tienen un identificativo único que se conoce como Identificativo de Proceso o PID. El PID de cada proceso es como su CURP

El comando “ps”, que nos informa del estado de los procesos:

pepeton@localhost:~$ ps xa
 PID TTY      STAT   TIME COMMAND
    1 ?        Ss     0:00 /sbin/init
    2 ?        S      0:00 [kthreadd]
    3 ?        S      0:00 [migration/0]
    4 ?        S      0:00 [ksoftirqd/0]
    5 ?        S      0:00 [watchdog/0]
    6 ?        S      0:00 [migration/1]
.
 5022 pts/0    S+     0:00 gnome-pty-helper
 5023 pts/1    Ss+    0:00 /bin/bash
 5148 pts/2    Ss     0:00 bash
 5165 pts/2    S      0:00 /bin/bash
 5182 pts/2    R+     0:00 ps xa

En la primera columna vemos cómo cada uno de los procesos, incluido el propio “ps xa” tienen un identificativo único o PID. Además de esto, es posible saber quién ha sido el proceso padre u originario de cada proceso, consultando su PPID, es decir, el Parent Process ID.

Además de esto, es posible saber quién ha sido el proceso padre u originario de cada proceso, consultando su PPID, es decir, el Parent Process ID. De esta manera es bastante sencillo hacernos una idea de cuál ha sido el árbol de creación de los procesos, que podemos obtener con el comando “pstree”:

pepeton@localhost:~$ pstree
init─┬─NetworkManager
     ├─acpid
     ├─apache2───6*[apache2]
     ├─atd
     ├─avahi-daemon───avahi-daemon
     ├─gnome-settings-
     ├─gnome-terminal─┬─bash───bash───pstree
     │                ├─gnome-pty-helpe
     │                └─{gnome-terminal}
     ├─firefox-bin─┬─plugin-containe───4*[{plugin-contain}]
     │             └─25*[{firefox-bin}]

El comando “pstree” es el proceso hijo de un intérprete de comandos (bash) que a su vez es hijo de una sesión de SSH (Secure Shell). Otro dato de interés al ejecutar este comando se da en el hecho de que el proceso init es el proceso padre de todos los demás procesos. Esto ocurre siempre: primero se crea el proceso init, y todos los procesos siguientes se crean a partir de él.

Además de estos dos identificativos existen lo que se conocen como “credenciales del proceso”, que informan acerca del usuario y grupo que lo ha lanzado. Esto se utiliza para decidir si un determinado proceso puede acceder a un recurso del sistema, es decir, si sus credenciales son suficientes para los permisos del recurso. Existen varios identificativos utilizados como credenciales, todos ellos almacenados en la estructura task_struct:

/* process credentials */
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;

Su significado es el siguiente :

Identificativos reales

uid

Identificativo de usuario real asociado al proceso

gid

Identificativo de grupo real

asociado al proceso

Identificativos efectivos

euid

Identificativo de usuario efectivo asociado al proceso

egid

Identificativo de grupo efectivo asociado al proceso

Identificativos guardados

suid

Identificativo de usuario guardado asociado al proceso

sgid

Identificativo de grupo guardado asociado al proceso

Identificativos de acceso a ficheros

fsuid

Identificativo de usuario asociado al proceso para los controles de acceso a ficheros

fsgid

Identificativo de grupo asociado al proceso para los controles de acceso a ficheros

Tabla 1.2.1 Credenciales de un proceso y sus significados.

Comentarios

Entradas populares de este blog

ejercicios 2da practicas de programación batch

Ejercicios... Practica uno Programación Batch

Pregunta 4 -Mencione las ventajas y desventajas sobre el uso de memoria virtual.