1.1 Objetivos Específicos
a) Explicar en forma descriptiva el funcionamiento de los procesos en un sistema operativo
b) Describir los tipo de procesos sus características y comportamiento en un sistema operativo
c) Describir las técnicas de comunicación entre procesos.
d) Mostrar ejemplos y casos prácticos de comunicación entre procesos.
1.2 Contenido del PAPER
Introducción
En el presente articulo se explica como trabajan, se crea y eliminan los procesos en GNU/linux utilizando GCC o G++. Además se explican como es la comunicación entre procesos y que instrucciones se utilizan para la gestión de procesos. Se hace una énfasis de los mecanismos de comunicación IPC entre procesos.
Este material esta dedicado a estudiantes de Informático de la Universidad Nacional Jorge Basadre Grohmman - Perú que quieren aprender el Sistema Operativo GNU/Linux pero desde un punto de vista PRACTICO y no teórico ni menos abstracto. (Recuerdo: SOLO PARA PRACTICOS).
I. PROCESOS.
Definición formal
Programa o comando en ejecución.
Características:
Un proceso consta de código y datos.
Los procesos existen en una jerarquía de árbol (varios Hijos, un sólo padre).
El sistema asigna un identificador de proceso (PID) único al iniciar el proceso.
El planificador de tareas asigna un tiempo compartido para el proceso según su prioridad (sólo root puede cambiar prioridades).
Ejecución de los Procesos
Ejecución en 1er plano:
proceso iniciado por el usuario.
Ejecución en 2do plano:
proceso no interactivo que no necesita ser iniciado por el usuario.
Demonio:
proceso en 2do plano siempre disponible, que da servicio a varias tareas (debe ser propiedad de un SU). Ejem. pstree (Arbol de Procesos en GNU/Linux)
Tipos de Procesos
Proceso zombi:
Proceso parado que queda en la tabla de procesos hasta que termine su padre. Este hecho se produce cuando el proceso padre no recoge el código de salida del proceso hijo.
Proceso huérfano:
Proceso en ejecución cuyo padre ha finalizado. El nuevo identificador de proceso padre (PPID) coincide con el identificador del proceso init (1).
Ejemplo de Procesos en Linux
Si ejecutamos la instrucción Top o Gtop en la consola de Linux nos mostrará el siguiente el siguiente detalle:
El número de identificador de Proceso (PID),
El usuario que lo está ejecutando (USER),
La prioridad del proceso (PRI),
El valor nice (NI),
El tamaño del proceso (SIZE),
El tamaño total del proceso junto con los datos que maneja (RSS),
El tamaño usado por el proceso en la memoria (SHARE),
El estado del proceso(STAT),
El porcentaje de CPU ( %CPU) y de memoria (%MEM)
El tiempo de ejecución (TIME) y el nombre del proceso (COMMAND).
II. FORMAS DE COMUNICACIÓN ENTRE PROCESOS
Existen 4 formas de comunicación entre procesos en Linux:
1. A través de variables de entorno:
Solo es posible de padres a hijos.
2. Mediante una señal:
Solo indica que algo ha ocurrido y solo lleva como información de un número de señal.
3. Mediante entrada salida:
Es la forma más corriente a nivel de shell. Ejem: el operador pipe '|' que conecta dos procesos.
4. Mediante técnicas IPC u otras:
Semáforos, Memoria compartida, Colas de mensajes.
III. MECANISMO DE COMUNICACIÓN ENTRE PROCESOS
3.1 A través de variables de entorno:
Solo es posible de padres a hijos.
Ejecución de Comandos (Algoritmo)
Descripción:
Llamada a un intérprete para ejecutar un comando.
El proceso espera a que finalice la ejecución de la subrutina y devuelve la salida del programa ejecutado.
Formato:
#include
const char *cadena;
Parámetro:
cadena - Comando a ejecutar.
Ejecución de Comandos (Algoritmo)
Devuelve:
Estado de salida del programa ejecutado. -1 o 127 en caso de error.
Algoritmo:
a) Se crea un proceso hijo (fork) y se lanza (exec) /usr/bin/bsh, que interpreta el comando a ejecutar.
b) Si la llamada se hace con camino seguro, la orden exec ejecuta el intérprete /usr/bin/tsh.
c) Se ignoran las señales SIGINT y SIGQUIT y se bloquea la señal SIGCHLD.
d) La salida de system no afecta a la salida de los procesos hijos del proceso ejecutor.
Ejecución de Comandos (programa)
Compile ($make system) y Ejecute el programa con: ($./system)
/* system.c - Listar los procesos del usuario usando system. */
#include
#include
int main ()
{
int salida; /* Salida del comando */
char comando[100]; /* Comando a ejecutar */
printf ("Ejemplo de system.
");
sprintf (comando, "/bin/ps -fu %s", getenv ("USER"));
salida = system (comando);
printf ("Salida del comando: %d
", salida);
exit (salida);
}
Ejecución de Comandos (¿Problema?)
Suponiendo que no existe el comando NOVAO y sustituyendo la ejecución de system por la siguiente línea,
salida = system ("NOVATO");
¿Qué salida mostrará ?
3.1.1 Creación de Procesos EN Gnu/linux
Descripción:
Crea un nuevo proceso (hijo), copia casi exacta del proceso generador (padre).
Formato:
#include
pid_t fork ();
Devuelve:
0 al proceso hijo y PID del hijo al proceso padre (-1, si error).
Creación de Procesos (Algoritmo)
Atributos que hereda el proceso hijo.
Entorno.
Bit FD_CLOEXEC para cada descriptor de fichero.
Señales capturadas.
SUID y SGID.
Estado de privilegios y prioridades.
Librerías compartidas y segmentos de memoria compartida.
PGID y TTYGID.
Directorio actual y directorio raíz.
Máscara y límites de medida para ficheros.
Eventos y estado de auditoría.
Estado de depuración.
Creación de Procesos (Algoritmo)
Atributos diferenciadores entre padre e hijo:
PID único.
PPID distintos (el PPID del hijo coincide con el PID del padre).
El proceso hijo tiene su propia copia de los descriptores de fichero del padre, pero comparte con éste un puntero a fichero para cada descriptor del proceso padre.
Bloqueos de proceso, texto y datos no se heredan.
Las subrutinas times se ponen a 0.
Las alarmas pendientes toman su valor inicial.
Se eliminan las señales pendientes para el proceso hijo.
Importante: Compile ($make fork) y Ejecute el programa con: ($./fork)
Creación de Procesos (Programa)
/* fork.c - Ejecución conjunta de procesos padre e hijo Bifurcación)*/
#include
#include
main ()
{
printf ("Ejemplo de fork.
");
printf ("Inicio del proceso padre. PID=%d
", getpid ());
if (fork() == 0)
{ /* Proceso hijo */
printf ("Inicio proceso hijo. PID=%d, PPID=%d
",
getpid (), getppid ());
sleep (1);
}
else
{ /* Proceso padre */
printf ("Continuación del padre. PID=%d
", getpid ());
sleep (1);
}
printf ("Fin del proceso %d
", getpid ());
exit (0);
}
Creación de Procesos (¿Problema?)
Suponiendo que tenemos que matar uno de los procesos:
¿Qué operaciones tengo que realizar ? Realice un programa en C para eliminar
el proceso Hijo y padre
3.2 Mediante una señal:
Solo indica que algo ha ocurrido y solo lleva como información de un número de señal.
Señales: Definiciones Formales
Señal:
Evento que debe ser procesado y que puede interrumpir el flujo normal de un programa.
Capturar una señal:
Una señal puede asociarse con una función que procesa el evento que ha ocurrido.
Ignorar una señal:
El evento no interrumpe el flujo del programa. Las señales SIGINT y SIGSTOP no pueden ser ignoradas (ver tabla de señales).
Señales: Definiciones Formales
Acción por defecto:
Proceso suministrado por el sistema para capturar la señal (ver tabla de señales).
Alarma:
Señal que es activada por los temporizadores del sistema.
Error:
Fallo o acción equivocada que puede provocar la terminación del proceso.
Error crítico:
Error que provoca la salida inmediata del programa.
Capturar Señales (Algoritmo)
Descripción:
Asocia una acción determinada con una señal.
Formato:
#include
void (*signal (señal, acción)) ()
int señal;
void (*accón) ();
Capturar Señales (Algoritmo)
Parámetros:
señal: Número de señal, excepto SIGKILL.
Acción: Puntero a la rutina asociada con la señal o uno de los valores:
- SIG_DFL: acción por defecto para dicha señal.
- SIG_IGN: ignorar la señal,
Devuelve:
Valor de la acción anteriormente asociada; -1, en caso de error.
Importante: Compile ($signal.c) y Ejecute el programa con: ($./signal)
Capturar señales. (Programa)
/* signal.c - Contar el número de CTRL-C en 15 segundos */
#include
#include
int numcortes=0; /* Contador de CTRL-C */
int enbucle=1; /* Controlador de salida del bucle de espera */
void alarma (); /* Captura la señal de alarma SIGALRM */
void cortar (); /* Captura la señal de interrupción SIGINT */
int main ()
{
signal (SIGINT, cortar);
signal (SIGALRM, alarma);
printf ("Ejemplo de signal.
");
printf ("Pulsa varias veces CTRL-C durante 15 segundos.
");
alarm (15);
while (enbucle);
signal (SIGINT, SIG_IGN);
printf ("Has intentado cortar %d veces.
", numcortes);
printf ("Hasta luego Lucas.
");
exit (0);
}
{
signal (SIGALRM, SIG_IGN);
enbucle=0; /* Salir del bucle */
printf ("¡Alarma!
");
}
void cortar ()
{
signal (SIGINT, SIG_IGN);
printf ("Has pulsado CTRL-C
");
numcortes++;
signal (SIGINT, cortar);
}
Alarmas y temporizadores.
Descripción:
Genera alarmas de reloj (señal SIGALRM) para el proceso actual.
Formato
#include
unsigned int segundos;
unsigned int ualarm (valor, intervalo)
unsigned int valor, intervalo;
Parámetros:
segundos: Número de segundos para enviar al proceso la señal SIGALRM.
valor: Número de señales generadas.
intervalo: Intervalo (en ms.) entre las señales.
Devuelve:
alarm devuelve el número de segundos que restan para generar la señal.
ualarm devuelve el número de microsegundos que restan hasta la próxima señal.
Importante: Compile ($signal.c) y Ejecute el programa con: ($./signal)
Alarmas y temporizadores.
/* alarm.c - Esperar una alarma */
#include
#include
int main ()
{
printf ("Una alarma en 3 segundos.
");
alarm (3);
printf ("Esperando...
");
while (1);
printf ("Esta línea no se ejecutará nunca.
");
exit (0);
}
Alarmas y Temporizadores (¿Problema?)
¿Cuándo se podrá habilitar la Línea de Código del programa ?
3.3. Mediante entrada salida:
Pipe y Tuberias
Definición Formal de Tuberias
Tubería:
Mecanismo de intercomunicación entre procesos que permite que 2 o más procesos envíen información a cualquier otro.
Una pipe relaciona la salida estándar de un comando con la entrada estándar de otro comando.
Ejem: Pipe
Importante: Compile ($pipe.c) y Ejecute el programa con: ($./pipe ls
wc )
Tubería entre dos Comandos usando Pipe
/* pipe.c - Tubería entre 2 comandos usando pipe. */
#include
#include
#define LEER 0
#define ESCRIBIR 1
int main (int contargs, char *args[])
{
int descr[2]; /* Descriptores de E y S de la turbería */
{
printf ("Formato: %s comando_ent comando_sal.
", args[0]);
exit (1);
}
/*Tuberia entre dos Comandos usando Pipe */
pipe (descr);
if (fork () == 0)
{
close (descr[LEER]);
dup2 (descr[ESCRIBIR], 1);
close (descr[ESCRIBIR]);
execlp (args[1], args[1], NULL);
perror (args[0]);
} else
{
close (descr[ESCRIBIR]);
dup2 (descr[LEER], 0);
close (descr[LEER]);
execlp (args[2], args[2], NULL);
perror (args[0]);
} }
$ ls -al /unjbg
$ ls -al /etc | less
$ man ls | col -b | lpr
$ ls -la > nuevo.txt | less
$ ./pipe ls more
3.4 Mediante técnicas IPC u otras:
3.4.1 Mecanismos IPC.
a) Características comunes.
El resto del conjunto de mecanismos IPC (semáforos, memoria compartida y cola de mensajes) poseen una serie de características comunes a todos ellos, que se pueden resumir de forma básica en los siguientes puntos:
1. Una estructura con información acerca de qué se está haciendo con dicho mecanismo.
2. Una estructura que define los permisos de los usuarios y grupos de usuarios que pueden acceder al mecanismo IPC.
3. Una clave de acceso o llave.
4. Un conjunto de funciones que permitirán realizar un control sobre el mecanismo en cuestión. Este conjunto de funciones se puede dividir en tres grupos:
• La familia get, para crear o buscar un mecanismo.
• La familia ctl, para realizar operaciones de control y suprimir mecanismos.
• Un conjunto de funciones particulares a cada mecanismo (msgsnd, shmat, etc.).
Operaciones disponibles
Colas de mensajes Semáforos Memoria compartida
fichero include
llamada para crear o abrir msgget() semget() shmget()
llamada para operaciones de control msgctl() semctl() shmctl()
llamadas para operaciones de IPC msgsnd(); msgrcv() semop() shmat(); shmdt()
b) Control de las facilidades IPC desde la línea de órdenes.
El sistema operativo UNIX ofrece los siguientes programas para controlar y observar desde la línea de comandos el desarrollo de los IPC que gestiona el sistema.
Comando IPCS
El programa estándar ipcs facilita información sobre los mecanismos utilizados por nuestro sistema, informando de a quién están asignados, permisos, información estadística, etc.
Ejem:
$ipcs
Si no se especifica ninguna opción, el programa muestra un resumen de la información administrativa que se almacena para los semáforos, memoria compartida y colas de mensajes.
Las informaciones que suministra son:
T: tipo de mecanismo (q para cola de mensajes, m para segmento de memoria y s para semáforo);
ID: identificación del mecanismo;
KEY: clave del mecanismo;
MODE: derechos de acceso al mecanismo;
OWNER: propietario del mecanismo;
GROUP: grupo propietario del mecanismo,
Sus principales opciones son:
-q muestra información de las colas de mensajes.
-m muestra información de los segmentos de memoria compartida.
-s muestra información de los semáforos.
-b muestra una información completa sobre los mecanismos IPC.
Comando IPCRM
El programa ipcrm se utiliza para liberar un mecanismo de intercomunicación, con solo conocer su identificador. Se utiliza de la siguiente forma:
$ ipcrm [opciones] id
Algunas de las opciones más importantes son:
-q borra la cola de mensajes cuyo identificador coincide con id,
-m borra la zona de memoria compartida cuyo identificador coincide con id,
-s borra el semáforo cuyo identificador coincide con id.
Semáforos, Memoria compartida, Colas de mensajes.
Ejemplo:
$ipcrm
Comunicacion entre procesos en PHP
Para crear un segmento de memoria compartida usamos:
$shm_id = shmop_open($key, $mode, $perm, $size);
Donde:
$key es el numero que idnetifica el segmento de memoria compartida, todos los procesos que quieran acceder a este un mismo segmento deben conocer este numero como clave para acceder al segmento.
$mode es el modo de creacion, "c" es usado para crear un segmento mientras que "a" se usa para acceder a un segmento ya creado
$perm define los permisos del segmento de acuerdo al mecanismo de permisos de Unix
$size define el tamaño del segmento
La funcion devuelve un id que debermos usar para leer/escribir en el segmento. No es similar a un identificador de archivo ya que las funciones que se usan para manipular memoria compartida son otras.
Ejemplo:
- Crear el archivo ipc_php.php
$ cat > ipc_php.php
- Colocar el siguiente contenido al archvio ipc_php.php
$shm_id = shmop_open(0xff3, "c", 0666, 190);
echo $shm;
?>
- Ejecutar el Script PHP
$php ipc_php.php
- Comprobar el IPC de Memoria Compartida
$ipcs -m
3.4.2 Semáforos
Un semáforo es un mecanismo de comunicación con el cual no se mueven datos, puesto que solo se puede consultar y modificar su valor al tener un carácter puramente informativo.
Dijkstra define un semáforo como una variable entera positiva o nula sobre la que sólo se pueden realizar dos operaciones: wait (también denominada P) y signal (también denominada V). La operación wait decrementa el valor del semáforo siempre que éste tenga un valor mayor que 0; por lo tanto esta operación se utiliza para adquirir el semáforo o para bloquearlo en el caso de que valga 0. La operación signal incrementa el valor del semáforo y por tanto se utiliza para liberarlo o inicializarlo.
Ambas operaciones deben ser atómicas para que funcionen correctamente; es decir que una operación wait no puede ser interrumpida por otra operación wait o signal sobre el mismo semáforo, y lo mismo ocurre para la operación signal. Este hecho garantiza que cuando varios procesos compitan por la adquisición de un semáforo, sólo uno de ellos va a poder realizar la operación.
Además, se ha de indicar que este mecanismo memoriza las peticiones de operaciones wait no satisfechas y despierta por tanto a los procesos en espera.
El mecanismo IPC de semáforos implementado en UNIX es una generalización más compleja del concepto descrito por Dijkstra, ya que va a permitir manejar un conjunto de semáforos mediante el uso de un identificador asociado y realizar operaciones wait y signal que actualizan de forma atómica todos los semáforos asociados bajo un mismo identificador. Esta complejidad en la utilización de los semáforos, se justifica mediante la imposibilidad de resolver una cierta categoría de problemas con los semáforos manipulados individualmente, por medio únicamente de las operaciones wait y signal.
3.4.3 Memoria Compartida
Memoria compartida es una región de memoria que puede ser accedida por múltiples procesos. Por ejemplo, si declaramos un vector de 1000 bytes en un programa, sólo puede acceder a él ese programa. Si declaramos un segmento de memoria compartida de 1000 bytes, muchos procesos pueden realizar operaciones de lectura y escritura sobre esa memoria compartida. La ventaja principal de la memoria compartida es que un programa la ve exactamente de la misma forma que si fuera memoria normal. Además, las operaciones de lectura y escritura son muy rápidas. Su utilización es relativamente simple. De la misma forma que las colas de mensajes, cada segmento de memoria compartida tiene asociado una clave. Esta identifica de forma unívoca el segmento de memoria compartida, y cualquier proceso que desee acceder a él necesita conocer la clave.
• La llamada shmget() se usa para obtener un id para una clave asociada. Este id es similar al id de cola de mensajes y se usa como parámetro en otras llamadas al sistema relacionadas con memoria compartida. La llamada shmget() también se usa para crear segmentos de memoria compartida.
• shmctl() se usa para realizar operaciones de control sobre la memoria compartida, entre ellas, la de eliminar segmentos de memoria compartida del sistema.
• shmat() devuelve un puntero que referencia al segmento de memoria compartida. Este puntero se emplea para acceder al segmento de memoria compartida para realizar tanto operaciones de lectura como escritural.
• shmdt() se emplea para desconectar del segmento de memoria compartida.
El siguiente algoritmo muestra como usar memoria compartida:
• Usa shmget() para obtener el id de la memoria compartida a partir de la clave, creando el segmento de memoria compartida si es necesario.
• Usa shmat() para obtener el puntero necesario para acceder a la memoria compartida.
• Accede al segmento de memoria compartidas mediante el puntero, y realiza las operaciones pertinentes.
• Usa shmdt() para desconectar del segmento.
• Usa shmctl() para eliminar el segmento de memoria compartida del sistema.
En general, la memoria compartida se emplea cuando se necesita transferir gran cantidad de datos en un corto período de tiempo.
Ejemplo:
El programa ejemplo para recursos compartidos analiza el tiempo que lleva en transferir un mensaje mediante una cola de mensajes. Es un programa simple, pero introduce varias técnicas que pueden usarse en una variedad de aplicaciones diferentes. El algoritmo básico del programa es :
• El padre crea una cola de mensajes
• El padre inicia dos procesos hijos
• El primer hijo :
o Recibe un mensaje de la cola
o Llama a gettimeofday() para obtener el instante de tiempo actual
o Utilizando el valor de tiempo almacenado en el mensaje, calcula la diferencia y la almacena en un vector
o Repite los pasos anteriores un número de veces que se indique
o Muestra los resultados
• El segundo hijo:
o Llama a gettimeofday() para obtener el instante de tiempo actual
o Coloca el valor del tiempo en un mensaje
o Coloca el mensaje en la cola de mensajes
o Espera para permitir que el otro hijo pueda extraer y procesar el mensaje
o Repite los pasos anteriores un número de veces que se indique
• El padre espera a que terminen los hijos y elimina la cola de mensajes
3.4.4 Una cola de mensajes
Una cola es una construcción FIFO (first in, first out). En otras palabras, el primer mensaje que se introduce en la cola es el primer mensaje que se extrae de la misma. Esto se dice que es una comunicación síncrona dado que los mensajes se leen en el mismo orden en que se enviaron, en contraposición a asíncrono, donde el orden de recepción puede ser diferente del orden de envio. Hay cuatro llamadas al sistema asociadas con colas de mensajes :
• msgget() sirve para crear una cola de mensajes y/o obtener un identificador (id) de cola de mensajes a partir de una clave. La clave es un número único que sirve para identificar la cola de mensajes. Cada proceso que desee comunicarse con la cola de mensajes debe conocer su clave. El id es un número asignado por el sistema y obtenido mediante la llamada msgget() y la clave. El id es un parámetro para los otros comandos que actúan sobre la cola de mensajes.
• msgctl() sirve para realizar operaciones de control sobre la cola, incluyendo su eliminación.
• msgsnd() sirve para colocar un mensaje en la cola.
• msgrcv() sirve para extraer un mensaje de la cola.
El siguiente esquema muestra cómo trabajar con colas de mensajes:
• Usa msgget() para obtener el identificador de la cola de mensajes a partir de su clave, creando la cola si es necesario.
• Usa msgsnd() y msgrcv() para transferir información hacia o desde la cola de mensajes identificada por el id obtenido previamente.
• Usa msgctl() para eliminar la cola del sistema, o para realizar otras acciones de control sobre la misma.
Las colas de mensajes son relativamente fáciles de usar. El sistema operativo gestiona los detalles internos de la comunicación. Cuando se envía un mensaje a la cola, se alerta a cualquier proceso que esté esperando obtener un mensaje de la misma. El bloqueo de las colas de mensajes es innecesario dado que el sistema operativo verifica la integridad de la cola y no permitirá que dos procesos accedan a la cola de una forma destructiva. Las colas de mensajes tienen dos inconvenientes importantes. Son mecanismos lentos para transferir gran cantidad de datos, y hay una fuerte limitación sobre el tamaño de los paquetes de datos que pueden transferirse. Por tanto, las colas de mensajes se usan principalmente para pequeñas transferencias de datos, con un ancho de banda limitado. Las colas de mensajes son un mecanismo excelente para pasar información de control de unos procesos a otros.
Autor: MsC.Daniel Alejandro Yucra Sotomayor (Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.)
Bibliografía
Antonio Ovando Sistemas Operativos / Diseño e Implementación - Apuntes
URLs:
1. Administración de procesos en Sistemas Operativos Monografias.com
URL: http://www.monografias.com/trabajos14/ administ-procesos/administ-procesos.shtml
2. Clásicos de Comunicación entre Procesos
URL: http://wwwdi.ujaen.es/~lina/TemasSO/CONCURRENCIA
3. Comunicación entre Procesos IPC
URL: http://labsopa.dis.ulpgc.es/prog_c/IPC.HTM