Sistemas Operativos Práctica 4 - Página personal de Andrés

Anuncio
Sistemas Operativos
Práctica 4
Ing. Andrés Bustamante
[email protected]
Ingenierı́a de Sistemas
Facultad de Ingenierı́a
Universidad de la Amazonia
2009
1.
Objetivo
El objetivo de la práctica es que el estudiante practique los conceptos aprendidos sobre la comunicación entre procesos (IPC) y los semáforos aplicados en el sistema operativo Linux.
2.
Metodologı́a
Mediante el uso del lenguaje de programación C, el estudiante aprenderá a establecer y manejar
una comunicación entre varios procesos, y creará una pequeña aplicación que permita crear hilos y la
implementación de semáforos para resolver el problema del productor/consumidor. Para tal fin se le
instruirá en las funciones básicas para dichos manejos y como es costumbre, se apoya en la utilización
de las páginas del manual de Linux (man). Es de tener en cuenta que se requieren los conocimientos
adquiridos sobre hilos y procesos de la practica 3.
3.
Marco teórico
3.1.
Programación con hilos y semáforos
Los hilos comparten el acceso al espacio de direcciones de un proceso, por lo tanto, se necesitan
primitivas de sincronización para controlar el acceso a los datos comunes. La biblioteca de hilos de
Linux proporciona varias primitivas de sincronización, entre las que se encuentran los semáforos
generales.
Un semáforo en POSIX es un tipo variable de sem_t con operaciones atómicas tales como inicialización, wait y signal. Existen semáforos nombrados y no nombrados. Un semáforo no nombrado
puede ser usado por solo un proceso o por sus procesos hijos. Un semáforo nombrado puede ser utilizado por cualquier proceso. Nosotros utilizaremos semáforos no nombrados para realizar las prácticas.
Las funciones sobre semáforos no nombrados son:
int
int
int
int
sem_init(sem_t *sem, int pshared, unsigned int value)
sem_wait(sem_t *sem)
sem_post(sem_t *sem)
sem_destroy(sem_t *sem)
1
sem_init crea e inicializa la variable del semáforo (sem); value es el valor inicial del semáforo
(no puede ser negativo); pshared indica si el semáforo es nombrado (valor distinto de 0) o no
nombrado (valor 0).
sem_wait bloquea al hilo si el valor del semáforo es cero, si no, decrementa el valor del semáforo.
sem_post incrementa el valor del semáforo. Puede llegar a desbloquear un hilo que ejecutó sem_wait.
sem_destroy sirve para eliminar definitivamente un semáforo previamente inicializado. Solo se
utiliza en el caso en el que ya no sea necesario dicho semáforo para el funcionamiento del programa.
Todas estas funciones devuelven un valor de 0 si ha tenido éxito y -1 si se ha producido cualquier
error.
Ejemplo:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sm;
sem_t p;
int s = 0;
int suma = 0;
p1()
{
int n= 0;
int i= 0;
while (i< 10)
{
sem_wait(&sm);
n++;
i++;
if ( (n%2) != 0)
{
s= n;
sem_post(&p);
}
else sem_post(&sm);
}
}
p2()
{
int i=0;
while(i < 5)
{
sem_wait(&p);
i= i++;
2
suma= suma + s;
sem_post(&sm);
}
}
main()
{
int error1, error2;
pthread_t tp1;
pthread_t tp2;
sem_init (&sm, 0, 1);
sem_init(&p, 0, 0);
error1= pthread_create (&tp1, NULL, (void *)p1, NULL);
if (error1) printf("\n", error1);
error2= pthread_create (&tp2, NULL, (void *)p2, NULL);
if (error2) printf("\n", error2);
pthread_join (tp1,NULL);
pthread_join(tp2, NULL);
printf("%s%d", "La suma es ->", suma);
}
Para que un programa con múltiples hilos funcione bien se ha de asegurar que dos o más activaciones concurrentes de una misma rutina, compartida por dichos hilos, han de estar correctamente
sincronizadas. Por lo tanto, el acceso concurrente a datos compartidos deja a estos en una situación
consistente. Si varios hilos pueden llamar simultáneamente a funciones que pertenecen a un mismo
módulo, asegurándose la seguridad y corrección en todo momento, se dice que tales funciones son
seguras (también se las denomina reentrantes). Por ejemplo, funciones como sin() que acceden a
datos globales sólo en lectura, son trivialmente reentrantes.
Cualquier código que vaya a ser ejecutado de una manera ası́ncrona por hilos ha de ser reentrante
y ha de tener las siguientes caracterı́sticas:
No se debe cambiar ningún elemento situado en memoria global
No se debe cambiar el estado de ningún archivo o dispositivo
Debe hacerse referencia a un elemento situado en memoria global sólo en circunstancias especiales
(p.e. asegurando la exclusión mutua en el acceso a datos globales)
El código que se ejecuta con mayor frecuencia, de una manera ası́ncrona y compartida, son las
bibliotecas del sistema. Por lo tanto, toda biblioteca que se enlace con un programa ha de ser mtsegura o tener una interfaz mt-seguro. Las funciones que se pueden encontrar en bibliotecas, se pueden
clasificar en cuatro grupos:
Funciones que tienen una interfaz mt-segura ya que siempre han sido mtseguras o que han sido
modificadas para que lo sean.
Funciones que no son mt-seguras porque su tamaño se hubiera incrementado demasiado.
Funciones que tienen un interfaz no mt-seguro.
Funciones que son equivalentes a las del tercer grupo, pero que han sido modificadas para hacerlas
mt-seguras. Las funciones de este grupo se las identifica por el sufijo _r.
3
Tanto las funciones mt-seguras, como las funciones que tienen interfaces mt-seguros pueden ser
utilizadas transparentemente por el programador (ésto es, no tiene que prestarles mayor atención).
Para la mayorı́a de las funciones con interfaces no mt-seguros han sido desarrolladas funciones mtseguras con sufijo _r.
Si las paginas man no dicen nada acerca de si una función es mt-segura, entonces lo es. Todas las
funciones no mt-seguras son identificadas explı́citamente en las páginas del manual. Las funciones para
las que no haya garantı́a de que sean reentrantes, se les puede utilizar en programas con múltiples
hilos, si las llamadas a dichas funciones se hacen sólo desde el hilo principal (main()).
También se puede utilizar con seguridad las funciones no-reentrantes, siempre que se sincronice el
acceso de los hilos a dichas funciones.
3.2.
Compilación
Para compilar y enlazar con éxito un programa que contenga múltiples hilos y semáforos, necesita
incluir los archivos de cabecera siguientes:
pthread.h
semaphore.h
Para compilar un programa en C que utiliza la biblioteca de hilos, ponemos igual que en la práctica
3:
$ cc -lpthread nombre_archivo.c -o archivo_salida
3.3.
Recomendaciones y buenas prácticas
Por último y como resumen, he aquı́ una serie de normas que es conveniente observar cuando se
programa con hilos:
Se ha de saber qué bibliotecas o módulos utilizamos en una aplicación y si éstos son reentrantes.
Un programa con hilos no debe utilizar código secuencial sin hilos, de una forma arbitraria.
Un código con hilos debe utilizar código no-reentrante sólo en el hilo principal (aquel que contiene
a main()).
Las bibliotecas suministradas son reentrantes, salvo que se diga lo contrario.
El hilo inicial debe asegurar que no hay acceso concurrente a la biblioteca stdio cuando se
ejecuta código que no asegure la propiedad de reentrancia.
No intente realizar operaciones globales (o acciones con efectos laterales globales) entre varios
hilos. Por ejemplo, los hilos no deben cooperar en el acceso a archivos.
Cuando se espere un comportamiento del programa en el que han de cooperar varios hilos,
utilice las funciones adecuadas. Por ejemplo, si la terminación de main() debe significar sólo la
terminación de dicho hilo, entonces el final del código de main() debe ser pthread_exit().
4.
Práctica
Implemente el problema del Productor/Consumidor con buffer finito utilizando semáforos. Tanto
el productor como el consumidor serán hilos. Recuerde:
Productor: produce un dato y lo pone en la siguiente posición libre en el buffer. Si el buffer
está lleno, se bloqueará hasta que haya espacio libre.
4
Consumidor: toma un dato del buffer y lo imprime. Si el buffer está vacı́o se bloqueará hasta
que haya al menos un dato.
A continuación se da una estructura de guı́a para desarrollar la solución:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define veces 30
#define tamano 10
/* inserte aqui las declaraciones de semaforos y variables o estructuras de datos
compartidas, necesarias para resolver el problema */
productor()
{
int i;
/* otras declaraciones locales */
for (i=0; i<veces; i++)
{
sleep(1); /* retardo */
/* codigo del productor */
}
}
consumidor ()
{
int i;
/* otras declaraciones locales */
for (i=0; i<veces; i++)
{
/* codigo del proceso consumidor */
sleep(i%5); /* retardo */
}
}
main ()
{
/* declaracion de los hilos productor y consumidor */
/* inicializacion de los semaforos */
setbuf(stdout,NULL); /*evita que las E/S se hagan con buffering*/
/* creacion de hilos y deteccion de errores */
printf("FIN\n");
}
5.
Entregables
1. Se debe entregar informe únicamente en formato PDF con la descripción del proceso realizado
de manera detallada. Se recomienda realizar capturas de pantalla describiendo los resultados
5
obtenidos. También se deben incluir conclusiones del aprendizaje de la práctica y bibliografı́a
consultada (si es el caso).
2. También se debe entregar el archivo con el código fuente del programa elaborado en lenguaje
C. Se recomienda realizar comentarios en el código para documentar el programa y colocar el
nombre de los integrantes del grupo al principio del archivo de código fuente (como comentario).
Los entregables se deben enviar vı́a correo electrónico a la dirección electrónica del profesor.
6
Descargar