1. SEÑALES: INTERRUPCIONES SOFTWARE
Generalidades.
Durante una sesión cualquiera, el número de procesos depende del trabajo
que los usuarios realicen. Se sabe que los procesos tienen su propio
contexto, pero esto no quiere decir que estén incomunicados entre sí.
Existe un conjunto de métodos mantenidos por el kernel que permiten
entablar diálogos entre ellos. Estos métodos se llaman mecanismos IPC
(Interprocess Comunication). Dentro del conjunto de IPC’s se tienen a los
semáforos, la memoria compartida, colas de mensajes, etc. Estas no son
las únicas formas de intercomunicación de que dispone el sistema
operativo Los procesos también pueden enviarse interrupciones software,
señales. El conjunto de señales lo maneja el gestor de señales. El número
y tipo de señales viene impuesto por el sistema operativo y cada una de
ellas será empleada en un caso concreto siendo su número la única
información que realmente se transmite entre los procesos cuyo significado
dependerá de la interpretación del programador.
2. SEÑALES Y EXEPCIONES
Cuando un S.O desea notificar a un proceso la ocurrencia de un determinado evento o error, recurre a 2 tipos de
mecanismos: SEÑALES y EXEPCIONES, la primera se utiliza en POSIX y la segunda en WINDOWS NT .
SEÑALES
Las señales tienen frente al proceso el mismo comportamiento que las interrupciones tienen frente al procesador, por
lo que se puede decir que una señal es una interrupción del proceso.
El proceso que recibe una señal se comporta de la siguiente forma:
• El proceso detiene su ejecución en la instrucción de máquina que está ejecutando
• Bifurca a ejecutar una rutina de tratamiento de la señal, cuyo código ha de formar parte del propio proceso
• Una vez ejecutada la rutina de tratamiento, sigue la ejecución del proceso en la instrucción en el que fue
interrumpido.
El origen de una señal puede ser un proceso o el sistema operativo
3. SEÑAL PROCESO-PROCESO
Un proceso puede enviar una señal a otro proceso que tenga el mismo identificador de usuario (uid) pero no a los
que lo tengan distinto . Un proceso también puede mandar una señal a un grupo de procesos, que han de tener su
mismo uid
PROGRAMA 03-02
Programa que imprime la información de identificación de un proceso.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
void main(void)
{
printf("Identificador de usuario: %dn", getuid());
printf("Identificador de usuario efectivo: %dn", geteuid());
printf("Identificador de grupo: %dn", getgid());
printf("Identificador de grupo efetivo: %dn", getegid());
}
4. SEÑAL SISTEMA OPERATIVO-PROCESO
El sistema operativo también toma la decisión de enviar señales a los procesos cuando ocurren
determinados condiciones; Por ej. Las excepciones de ejecución programa( el desbordamiento en las
operaciones aritméticas , la división por cero, el intento de ejecutar una instrucción con código de
operación incorrecto o de direccionar una porción de memoria prohibida), las convierte el S.O en
señales al proceso que ha causado la excepción.
Debido a que existen gran variedad de señales que para indicarle al proceso muchas cosas pj, podemos
categorizar a las señales como:
Excepciones de hardware
Comunicación
E/S asíncrona
EFECTO DE LA SEÑAL Y ARMADA DE LA MISMA
El efecto de la señal es ejecutar una rutina de tratamiento, para que esto sea asi, el proceso debe tener
armada ese tipo de señal, es decir ha de estar preparado para recibir ese tipo de señal.
Armar una señal significa indicar al S.O el nombre de la rutina del proceso que ha de tratar ese tipo de
señal.
5. COMPORTAMIENTO
Cuando un proceso recibe una señal, puede tratarla de tres formas diferentes:
1.- Ignorar la señal, con lo cual no tiene efecto.
2.- Invocar a la rutina de tratamiento correspondiente al número de señal. Esta rutina no la codifica el
programador, sino que la aporta el kernel y normalmente tiene como fin el terminar el proceso que recibe la
señal. En algunos casos, antes de eliminar al proceso, el kernel se encarga de generar en el directorio de
trabajo actual del proceso un fichero llamado core que contiene un volcado de memoria del contexto del
proceso. Analizando dicho fichero se podrá saber en qué punto terminó el proceso y por qué motivo se le
envió la señal.
3.- Invocar a una rutina que se encarga de tratar la señal y que ha sido creada por el programador. Esta rutina
establecerá un mecanismo de comunicación entre procesos o modificará el curso normal del programa. En
estos casos, el proceso no va a terminar a menos que la rutina de tratamiento indique lo contrario.
Señal La primera señal que recibe no provoca que
Señal Señal el proceso cambie el curso de su
ejecución, esto es debido a que la acción que
está activa es que el proceso ignore la señal.
El proceso prosigue su ejecución y recibe una
Inicio Fin segunda señal que le fuerza a entrar en una
rutina de tratamiento. Esta rutina, después de
tratar la señal, puede optar por tres acciones:
Fin restaurar la ejecución del proceso al punto
Tratamiento donde se produjo la interrupción, finalizar el
por defecto proceso o restaurar alguno de los estados
Tratamiento pasados del proceso y continuar la ejecución
Volcado de desde ese punto.
memoria Fin El proceso puede también recibir una señal
Fin que le fuerce a entrar en la rutina de
tratamiento por defecto.
6. SERVIDORES Y DEMONIOS
Los servidores y los demonios son dos tipos de procesos
muy frecuentes cuyas características son:
Un servidor es un proceso que está pendiente de recibir
ordenes de trabajo que provienen de otros procesos
llamados clientes , una vez recibida la orden, la ejecutan
y la responden al peticionario con el resultado.
El proceso servidor tiene la siguiente estructura de
bucle infinito:
1. Lectura de orden. El proceso está bloqueado
esperando a que llegue una orden.
2. Recibida la orden, el servidor lo ejecuta.
3. Finaliza la ejecución, el servidor responde con el
resultado al proceso cliente y vuelve al punto No 1
El proceso servidor tiene abierto un puerto del que lee las
peticiones, en la solicitud el cliente envía el identificador
del puerto en el que el servidor debe contestar.
Un servidor será secuencial cuando no admite una solicitud
de trabajo hasta que no termine la solicitud de trabajo
actual.
Se puede necesitar que el servidor sea paralelo es decir que
admita varias peticiones y los atienda simultáneamente, y
para conseguir esto se puede actuar de la siguiente forma:
1. Lectura del Orden (proceso bloqueado esperando que llegue una orden)
2. Asignación de un nuevo puerto para el nuevo cliente
3. Generación de un proceso hijo que realiza el trabajo solicitado por el cliente
4. Vuelta al punto 1
7. PROCESO CLIENTE SERVIDOR EN MAQUINAS DISTINTAS
Un proceso demonio es un proceso que tiene las siguientes características:
• Se arranca al iniciar el sistema, puesto que siempre debe estar activo
• No muere, en caso de que un demonio muera por algún imprevisto, es muy recomendable que exista un
mecanismo que detecte la muerte y lo re arranque
• Los procesos servidores suelen tener el carácter de demonios
8. SERVICIOS POSIX PARA LA GESTION DE PROCESOS
IDENTIFICACION DE PROCESOS
POSIX identifica cada proceso por medio de un entero único denominado identificador de proceso de tipo pid_t,
y estos servicios son los siguientes:
Pid_t getpid(void)
Identificador del proceso padre pid_t getppid(void)
Programa que imprime el identificador del proceso y el identificador de su proceso padre.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
void main(void)
{
pid_t id_proceso;
pid_t id_padre;
id_proceso = getpid();
id_padre = getppid();
printf("Identificador de proceso: %dn", id_proceso);
printf("Identificador del proceso padre: %dn", id_padre);
}
9. SERVICIOS POSIX PARA LA GESTION DE PROCESOS
Cada proceso además lleva asociado un usuario que se denomina propietario, Cada usuario en el sistema tienen un identificador único
denominado identificador de usuario de tipo uid_t. El proceso también tiene un identificador de usuario efectivo, que determina los
privilegios que un proceso tienen cuando se encuentra ejecutando.
El sistema también incluye grupos de usuarios, cada usuario debe ser miembro de al menos un grupo . Al igual que los usuarios cada
proceso lleva asociado el identificador de grupo al que pertenece y el identificador de grupo efectivo, y los servicios que nos permite tener
esta información son:
Obtener el identificador de usuario real
Uid_t getuid(void);
Obtener el identificador de usuario efectivo
uid_t geteuid(void);
Obtener el identificador del grupo real
Gid_t getgid(void);
Obtener el identificador del grupo efectivo
Gid_t getegid(void);
Programa que imprime la información de identificación de un proceso.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
void main(void)
{
printf("Identificador de usuario: %dn", getuid());
printf("Identificador de usuario efectivo: %dn", geteuid());
printf("Identificador de grupo: %dn", getgid());
printf("Identificador de grupo efetivo: %dn", getegid());
}
10. SERVICIOS POSIX PARA LA GESTION DE PROCESOS
ENTORNO DE UN PROCESO
El entorno de un proceso viene definido por una lista de variables que se pasan al mismo en el momento de comenzar su ejecución, estas
variables se llaman variables de entorno y son accesibls a un proceso a través de la variable externa environ, declarada de la siguiente forma:
1 extern char ** environ;
Esta variable apunta a una lista de variables de entorno. Esta lista no es mas que un vector de punteros a cadenas de caracteres de la forma
nombre=valor, donde nombre hace referencia al nombre de una variable de entorno y el valor al contenido de la misma.
Programa que imprime el entorno del proceso.
#include <stdio.h>
#include <stdlib.h> Cada aplicación interpreta la lista de variables de entorno de forma
específica POSIX establece el significado de determinadas variables de
extern char **environ; entorno. Las mas comunes son:
• HOME direct. Trabajo inicial del usuario
void main(int argc, char **argv) • LOGNAME: nombre del usuario asociado a un proceso
{ • PATH: prefijo de directorios para encontrar ejecutables
int i; • TERM: tipo de terminal
• TZ: información de la zona horaria
printf("Lista de variables de entorno de %sn",argv[0]);
Programa que imprime el valor de la variable HOME.
for (i=0 ; environ[i] != NULL ; i++)
printf("environ[%d] = %sn", i, environ[i]); #include <stdio.h>
} #include <stdlib.h>
OBTENER EL VALOR DE UNA VARIABLE DE void main(void)
ENTORNO {
El servicio getenv permite buscar una variable de entorno char *home = NULL;
su sintaxis es: char *getenv(const char *name);
Esta función devuelve un puntero al valor asociado a la variable de home = getenv("HOME");
entorno de nombre name. if (home == NULL)
Si la variable de entorno no se encuentra definida, la función printf("$HOME no se encuentra definidan");
devuelve NULL else
printf("El valor de $HOME es %sn", home);
}
11. CREACIÓN DE UN PROCESO
En una interfaz POSIX la forma de crear un proceso es invocando el servicio fork. El S.O trata este servicio realizando
una clonación del proceso que lo solicite, constituyendo la relación con el nuevo proceso padre-hijo.
Su prototipo es: pid_t fork();
La creación del proceso se realiza copiando la imagen de memoria y el BCP, definiendo que el proceso hijo es una
copia del proceso padre en el instante en que este solicita el servicio fork(). Significa que los datos y la pila del
proceso hijo son los que tiene el proceso padre en ese instante de ejecución. Es mas dado que al entrar el S.O a tratar
el servicio, lo primero que hace es salvar los registros del BCP padre, al copiarse el BCP se copian los valores salvados
de los registros , por lo que el hijo tiene los mismos valores que el padre.
12. TALLERES PARA GRUPOS DE TRABAJO
1. SINCRONIZACION DE PROCESOS
1. PROBLEMA DE LA SECCION CRITICA: SEMAFOROS GRUPO No 3
2. PROBLEMAS CLASICOS DE SINCRONIZACION
1. BUFFER LIMITADO GRUPO No 2
2. LECTORES Y ESCRITORES
3. FILOSOFOS COMENSALES
3. REGIONES CRITICAS
1. MONITORES GRUPO No 1
4. BLOQUEOS MUTUOS: MODELO DEL SISTEMA
1. ESTRATEGIAS COMBINADAS PARA LOS BLOQUEOS MUTUOS GRUPO No 4
13. CREACIÓN DE UN PROCESO
Todo esto significa que el contador de programa de los 2 procesos tienen el mismo valor, por lo que van a ejecutar la
misma instrucción de máquina, No conceptualizar que el proceso hijo empieza la ejecución del código en su punto de
inicio, repetimos que el hijo empieza a ejecutar al igual que el padre en la sentencia que está después del fork()
El proceso hijo no es totalmente idéntico al padre, algunos valores de su BCP han de ser distintos y estos son:
1. El Ph tiene su propio identificador de proceso
2. El Ph tiene una nueva descripción de memoria, aunque tenga los mismos segmentos con el mismo contenido, no
tienen porque estar en la misma zona de memoria (especialmente en sistemas con memoria virtual)
3. El tiempo de ejecución del proceso hijo es igual a 0
4. El valor que retorna el S.O como resultado del fork es distinto en el hijo y el padre (hijo recibo 0 y el padre recibe
el identificador del proceso hijo)
5. Todas las alarmas pendientes se desactivan en el proceso hijo.
14. CREACIÓN DE UN PROCESO
Programa que crea un proceso.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
void main(void)
{
pid_t pid;
pid = fork();
switch(pid)
{
case -1: /* error del fork() */
perror("fork");
break;
case 0: /* proceso hijo */
printf("Proceso %d; padre = %d n", getpid(), getppid());
break;
default: /* padre */
printf("Proceso %d; padre = %d n", getpid(), getppid());
}
}
15. CREACIÓN DE UN PROCESO
Programa que crea la cadena de procesos
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
1
void main(void)
{ 2
pid_t pid;
int i;
int n = 10;
for (i = 0; i < n; i++) 3
{
pid = fork();
if (pid != 0)
break;
}
printf("El padre del proceso %d es %dn", getpid(), getppid()); N
}
Encada ejecución del bucle se crea un proceso. El proceso padre obtiene el identificador del proceso hijo, que será
distinto de 0 y saldrá del bucle utilizando break. El proceso hijo continuará la ejecución, repitiéndose este proceso
hasta que llegue al final del bucle.
16. CREACIÓN DE UN PROCESO
PROGRAMA 03-07
Programa que crea la estructura de procesos. 1
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void main(void) 2 3 N
{
pid_t pid;
int i;
int n = 10;
for (i = 0; i < n; i++)
{
pid = fork();
if (pid == 0)
break;
}
printf("El padre del proceso %d es %dn", getpid(), getppid());
}
En este programa, es el proceso hijo el que finaliza la ejecución del bucle ejecutando la sentencia break, siendo
el padre encargado de crear todos los procesos
17. SERVICIOS POSIX EXEC
Este servicio tiene como objetivo cambiar el programa que está ejecutando un proceso. Y se puede decir que tiene 2
fases:
En la primera se vacía el proceso de casi todo su contenido,
En la segunda se carga en nuevo programa
En la fase de vaciado se conservan
algunas informaciones como:
• Entorno del proceso Que el S.O
incluye en la nueva pila del
proceso.
• Algunas informaciones del BCP
como Identificador del
proceso, del proceso padre, del
usuario y descriptores de archivos
abiertos
En la fase de carga hay que realizar
las siguientes operaciones:
• Asignar el proceso un nuevo
espacio de memoria.
• Cargar el texto y datos iniciales en
los segmentos correspondientes
• Crear la pila inicial del proceso con
el entorno y los parámetros que
Recuerde que el servicio fork crea un nuevo proceso que ejecuta el mismo se pasan
programa que el proceso padre y el servicio exec no crea un nuevo proceso sino • Rellenar el BCP con los valores
iniciales de los registros y la
que permite que un proceso pase a ejecutar un programa distinto descripción de los nuevos
segmentos de memoria
18. SERVICIOS POSIX EXEC
La familia de funciones exec remplaza la imagen del
proceso actual por una nueva imagen. Esta nueva
imagen se construye a partir de un archivo ejecutable. Si
la llamada se ejecuta con éxito, ésta no devolverá
ningún valor puesto que la imagen del proceso habrá
sido remplazada, caso contrario devuelve –1.
La función main() del nuevo programa tendrá la forma:
int main(int argc, char **argv)
// Ejemplo: Programa que ejecuta el comando ls –l
#include <sys/types.h>
#include <sys/wait.h> // Ejemplo: Programa que ejecuta el comando ls –l mediante execvp
#include <stdio.h> #include <sys/types.h>
#include <unistd.h> #include <stdio.h>
int main() #include <unistd.h>
{ int main(int argc, char **argv)
pid_t pid; {
int status; pid_t pid;
pid = fork(); char *argumentos[3];
switch(pid) argumentos[0] = "ls";
{ argumentos[1] = "-l";
case -1: /* error del fork() */ argumentos[2] = NULL;
exit(-1); pid = fork();
case 0: /* proceso hijo */ switch(pid)
execlp("ls","ls","-l",NULL); {
perror("exec"); case -1: /* error del fork() */
break; exit(-1);
default: /* padre */ case 0: /* proceso hijo */
printf("Proceso padren"); execvp(argumentos[0], argumentos);
while(pid != wait(&status)); perror("exec");
} break;
} default: /* padre */
printf("Proceso padren");
}
}