Presentación

Anuncio
Sistemas Operativos I
Práctica 2
Continuación de la implementación de msh:
Uso de la memoria compartida y de semáforos
Diana Rosario Pérez Marín
Marzo 2007
EPS-UAM
1
Conceptos teóricos
• Para comunicar a varios procesos en ejecución, el sistema operativo Linux
nos ofrece varias alternativas: usar el valor devuelto por exit, ficheros, pipes o
memoria compartida. Nosotros nos vamos a centrar en esta última forma de
comunicación, que consiste en crear una zona de memoria común a la que
todos los procesos se enganchan y de esta forma, lo que un proceso escribe
en ella, lo pueden leer todos los demás.
• Por ejemplo :
Proceso 1
Escribe :
valor = 9
Memoria compartida
Valor = 9
Proceso 2
Lee :
valor = 9
2
Varias instancias de msh compartiendo
información común
• En esta práctica continuamos con el desarrollo de nuestro intérprete
de comandos. De forma que todos los requisitos exigidos en la
práctica anterior se mantienen en esta práctica.
• Con la novedad de que ahora ya no nos vamos a centrar
únicamente en una ejecución de msh, sino que vamos a ejecutar
varios mshs de forma concurrente. Todos ellos serán procesos padre
que vayan lanzando hijos para que vayan ejecutando los comandos
que se les pida de forma interactiva o por fichero.
• Habrá un único fichero .msprofile al que todos tendrán acceso y la
información de los procesos y las variables la guardarán en un
espacio de memoria común (la memoria compartida) de forma que
todas las instancias de msh, tengan el mismo valor para todas las
variables de entorno. Se definen nuevas variables:
– MAX_NUM_MSHS: número máximo de mshs que se pueden ejecutar en paralelo.
– ID_MEMORIA: clave que se usará para identificar la memoria.
3
Modificaciones a realizar en los comandos
propios de msh (I)
• exit: Sigue siendo el comando que se encarga de cerrar un
intérprete de comandos (aunque pueden quedar otros
ejecutándose). Debe liberar la memoria prestada, esperar a todos los
hijos, desengancharse de la memoria compartida y si es ya la última
instancia de msh que queda, liberar la memoria compartida.
• msps: Si la memoria compartida de procesos tiene permisos de
lectura, lista todos los procesos que estén en ejecución en todas las
mshs activas. Mostrando una línea por proceso que sigue el formato:
pid ppid status t_ejec comando idMsh
• eVar, rVar y Entorno: Su sintaxis no varía respecto a la práctica
anterior. Tan solo hay que tener cuidado de que ahora sólo se puede
ejecutar si se tiene permiso de lectura de la memoria compartida de
variables.
4
Modificaciones a realizar en los comandos
propios de msh (II)
• aVar: Si la msh desde donde se ejecuta tiene permisos de escritura
en memoria compartida de variables entonces funciona igual que
en la práctica anterior, en caso contrario debe dar error.
• bVar: La msh desde donde se ejecuta debe tener permisos de
lectura y escritura. En caso contrario, debe dar error.
• eId: A cada uno de los msh que tenemos en ejecución le vamos a
asignar un identificador que puede ser su número de pid o bien el
valor de un contador que empieza en uno y se va incrementando
cada vez que se crea un nuevo msh. El comando eId nos debe
mostrar por pantalla el identificador del msh desde donde se ejecuta
• eNum: Muestra por pantalla el número de mshs que se están
ejecutando concurrentemente.
5
Modificaciones a realizar en los comandos
propios de msh (III)
• dPermVar: Concede permisos para acceder a la memoria
compartida de variables (dPermVar r, concede el permiso de
lectura; dPermVar w: concede el permiso de escritura; y, dPermVar:
concede ambos permisos).
• dPermProc: Igual que dPermVar pero pero para la memoria de
procesos.
• ePermVar: Muestra qué permisos tiene la memoria compartida de
variables (ePermVar r, muestra si tiene permiso de lectura; ePermVar
w: muestra si tiene el permiso de escritura; y, ePermVar: ambos).
• ePermProc: Igual que ePermVar pero para la memoria de procesos
• bPermVar: deniega permisos para acceder a la memoria
compartida de variables (bPermVar r, deniega el permiso de lectura;
bPermVar w: deniega el permiso de escritura; y, bPermVar: ambos).
• bPermProc: Igual que bPermVar pero para la memoria compartida
de procesos.
6
Pseudocódigo de msh (I)
• Comprobar argumentos para saber si entra en modo fichero o en
modo interactivo o bien si algún error en los argumentos salir.
• Leer el fichero de configuración .msprofile y entonces:
– Si es el primer msh que se ejecuta, crear la memoria compartida
reservando espacio para una estructura con información sobre :
•
•
•
•
•
El número real de procesos que se han creado.
El número máximo de procesos que se pueden guardar.
El número real de variables de entorno que se están gestionando.
El número máximo de variables que se pueden guardar.
Un array con los identificadores de las instancias de msh de tamaño el máximo
número de mshs que se puedan ejecutar concurremente.
• Un array de igual tamaño que el anterior que indique el modo de cada msh:
FICHERO (F) o INTERACTIVO (I).
• Un array de tamaño el número de mshs que guarde para cada msh un
contador del número de hijos, que se incremente en cada fork y se
decremente en cada wait/waitpid. De forma que cuando su valor llegue a
cero esto indique que ya no queda ningún proceso en estado zombi.
7
Pseudocódigo de msh (II)
•
•
•
•
•
Un array de estructuras variables de tamaño el número máximo de variables.
Un array de estructuras procesos de tamaño el número máximo de procesos.
Un array de permisos de lectura de tamaño el máximo número de mshs.
Un array de permisos de escritura de tamaño el máximo número de mshs.
Un contador del número real de mshs en ejecución (opcional).
– Engancharse a la memoria compartida. Tanto si es la primera instancia
de msh como las siguientes, todos los procesos que quieran tener acceso
a la memoria compartida, necesitan obtener un puntero a ella.
– Si es la primera instancia de msh, inicializar los campos de la estructura
de la memoria con valores por defecto e ir rellenando los campos de las
variables con la información leída del fichero .msprofile. Si no es la
primera, actualizar únicamente los campos adecuados (como colocar su
valor en los arrays e incrementar los contadores pertinentes). Tener en
cuenta que no tener permiso de escritura no debe impedir la escritura de
las variables leídas de .msprofile.
– Ejecutar los posibles comandos de inicio que hubiera en el fichero
.msprofile.
8
Pseudocódigo de msh ( III )
– Mientras que siga habiendo comandos a ejecutar:
• Analizar la línea de comandos. La sintaxis es la misma que en la práctica
anterior. No es necesario guardar los comandos en memoria compartida, pero
sí los procesos.
• Identificar el comando. Si el comando es alguno de los comentados en las
diapositivas anteriores entonces es el propio padre quien lo ejecuta, en caso
contrario crear un hijo para que lo ejecute llamando a execvp (¡el hijo debe
desvincularse de la memoria compartida antes de llamar a execvp!).
• Mientras el hijo está ejecutando el comando, el padre guarda la información
sobre el proceso en memoria compartida.
• Si es el comando no debe ser ejecutado en background, entonces hacer
espera bloqueante con waitpid hasta que termine el proceso.
– Esperar a que todos los hijos terminen. Incluídos los que estén en
background y comprobar que el contador de los hijos llega a 0 para que
no se queden procesos zombi.
– Liberar todos los recursos prestados.
9
Vista global
• Ver en el enunciado ejemplo de .msprofile y ficheroScript.
consola1
./msh
Creando mc..OK
Lect. .msprofile..OK
archivo1
msh% eNum
Hay 1 msh. en ejec.
msh% exit
consola2
Consola3
./msh
Enganche a mc..OK
Lect. .msprofile..OK
archivo1
msh% dPermVar w
Permiso concedido.
msh% exit
./msh ficheroScript
Enganche a mc..OK
Lect. .msprofile..OK
archivo1
msh% exit
10
Qué es un semáforo ( I )
• Además, en esta práctica nos vamos asegurar que no haya ningún
problema de concurrencia en la ejecución de las mshs.
• Para ello vamos a usar semáforos.
• Un semáforo no es más que un número entero positivo y es así como hay que
imaginarlo, pero siguiendo la metáfora de un semáforo que estará en rojo
cuando el valor del entero sea 0 y estará en verde cuando tenga un valor
positivo.
• Cuando un proceso toma el semáforo, lo primero que se hace es comprobar
el valor del semáforo. Si es 0, entonces se le deja dormido, y sólo cuando
otro proceso lo libera e incrementa su valor, se le deja pasar. Lo primero que
hace es decrementar el valor para que cuando llegue a 0 se le cierre el
paso a todos los demás procesos que vengan por detrás. Por lo tanto para
proteger una sección crítica con semáforos se hará :
Down(semaforo)
Sección crítica
Up(semaforo)
11
Qué es un semáforo ( II )
• De esta forma se asegura :
– La exclusión mutua, esto es, que sólo va a haber un proceso en la
sección crítica.
– Un proceso que no está en su sección crítica no puede bloquear
a otros procesos.
– El proceso que está en la sección crítica no bloqueará para
siempre al resto, ya que cuando termine de escribir en la
memoria compartida entonces liberará el semáforo y el primer
proceso de la cola de los que estaban dormidos a la espera de
que el semáforo despertara será el que haya ganado el acceso a
la sección crítica. Así cuando termine de manipular la memoria
compartida liberará el semáforo para que pase al siguiente. Hasta
que finalmente pasen todos por esta zona protegida del código.
12
Tipos de semáforo
• Hay dos tipos de semáforo :
– Binarios : Sólo pueden tomar 2 valores, 1 ó 0. De forma que
cuando están a 1 permiten acceder a la zona crítica y cuando
están a 0 ya no permiten acceder a ella. Son muy útiles como
semáforos mutex para controlar escritura de variables en memoria
compartida que sólo permite que 1 proceso esté en la sección
crítica.
– N-arios : Se inicializan al número de procesos que se permite que
estén en la sección crítica, de forma que cada uno de ellos lo va
decrementando al hacer el down y luego cuando llega a 0 ya no
permite que pasen ninguno más, hasta que se va liberando. Son
muy útiles para operaciones de consulta de la memoria
compartida en las que se permite que varios procesos lean de
ella siempre que no haya ningún otro proceso escribiendo lo que
se controlaría con un semáforo binario.
13
Operaciones con semáforos
• Down, p, wait o toma_semaforo : Consiste en la petición del semáforo por
parte de un proceso que quiere entrar en la sección crítica. Lo que se hace
es comprobar si el valor del semáforo es 0 para en ese caso quedarse
dormido porque el semáforo está en rojo y habrá otro proceso en la sección
crítica. Mientras que si está en un valor positivo entonces el semáforo está en
verde y de forma atómica (esto es el planificador no puede dar ahora la
ejecución a otro proceso) decrementa el valor del semáforo.
• Up, v, signal o libera_semaforo : Consiste en la liberación del semáforo por
parte del proceso que ya ha terminado de trabajar en la sección crítica y
por lo tanto de forma atómica también incrementar el valor del semáforo.
Por lo que si fuera binario estaría despertando a otro proceso que estuviera
esperando en down (al pasar el valor de 0 a 1), si fuera N-ario simplemente
incrementa una unidad y si estaba en 0 también significará que lo pone en
verde y permitiendo el acceso a la sección crítica al otro proceso.
14
Llamadas al sistema operativo para
trabajar con memoria compartida
• Veamos ahora de qué funciones disponemos :
–
–
–
–
shmget : Crear la zona de memoria compartida
shmat : Engancharse a la zona de memoria compartida
shmdt : Desengancharse de la zona de memoria compartida
shmctl : Operaciones de control e información sobre la memoria
compartida
• Todas ellas por razones de portabilidad a otros sistemas operativos se
deben encapsular en un fichero aparte ( por ej. memoria.c ) con su
correspondiente archivo de cabecera ( por ej. memoria.h ) que no
deben contener información referente a esta práctica.
• En las siguientes transparencias estudiaremos sus prototipos y las
librerías que hay que incluir para poderlas usar.
15
shmget
• Hay que incluir las librerías :
– #include <sys/ipc.h>
– #include <sys/shm.h>
• Sirve para crear una zona de memoria compartida, y su prototipo es :
– int shmget ( key_t key, int size, int shmflg ) ;
– El valor de retorno es el identificador de la zona de memoria compartida si todo va
bien, -1 si error y errno coge el número de error encontrado. Importante distinguir el
valor EEXIST de errno puesto que no se debe considerar error como tal, ya que lo
que indica es que la memoria compartida ya existía y no se está creando nueva.
– Los argumentos son :
• key_t key : Clave para crear la memoria compartida ( MEMID ), la misma para
todos los procesos que quieren acceder a esta zona común.
• int size : Tamaño a reservar ( sizeof de la estructura ).
• int shmflg : Opciones de configuración. Se hace un OR entre ellas. Ejemplos :
– IPC_CREAT sirve para crear un nuevo segmento
– IPC_EXCL para asegurarse que no existía anteriormente
– Permisos : SHM_R para leer y SHM_W para escribir (para todas las mshs*) .
* los permisos los gestionamos nosotros con los arrays para la lectura y la escritura
16
shmat
• Hay que incluir las librerías :
– #include <sys/ipc.h>
– #include <sys/shm.h>
– #include <sys/types.h>
• Sirve para engancharse a una zona de memoria compartida. Prototipo :
– char * shmat ( int shmid, char * shmaddr, int shmflg ) ;
– El valor de retorno es el puntero a la zona de memoria compartida si todo va bien,
NULL si error y errno coge el número de error encontrado.
– Los argumentos son :
• int shmid : Identificador devuelto por shmget.
• char * shmaddr : Como realloc se le podría indicar un puntero a una zona de
memoria ya existente. Por defecto, este argumento se pasa a NULL.
• int shmflg : Opciones de configuración. Se hace un OR entre ellas. Ejemplos :
– SHM_R permiso para leer.
condicionado a shmget
– SHM_W permiso para escribir.
17
shmdt
• Hay que incluir las librerías :
– #include <sys/ipc.h>
– #include <sys/shm.h>
– #include <sys/types.h>
• Sirve para desengancharse de una zona de memoria compartida.
Prototipo :
– int shmdt ( char * shmaddr ) ;
– El valor de retorno es 0 si todo va bien, -1 si error y errno coge el número
de error encontrado.
– El argumento es char * shmaddr : Puntero a la zona de memoria
compartida de la que nos queremos desenganchar. Se suele
pasar con un casting a ( char * ) para compatibilizar argumentos.
18
shmctl
• Hay que incluir las librerías :
– #include <sys/ipc.h>
– #include <sys/shm.h>
• Sirve entre otras funciones de control sobre la memoria compartida,
para eliminarla definitivamente ( siempre y cuando todos los
procesos se hayan desenganchado de ella). Su prototipo es :
– int shmctl ( int shmid, int comando, struct shmid_ds * buf ) ;
– El valor de retorno es 0 si todo va bien, -1 si error y errno coge el número
de error.
– Los argumentos son :
• int shmid : Identificador devuelto por shmget.
• int comando : IPC_RMID para eliminarla.
• struct shmid_ds * buf : Estructura con información de control. Por
defecto se pasa a NULL cuando no se quiere trabajar con ella.
19
Ejemplo básico de creación de memoria
compartida ( I )
#include
#include
#include
#include
#include
<stdio.h>
<sys/ipc.h>
<sys/shm.h>
<sys/types.h>
<errno.h>
int main ()
{
int shm ; /* identificador de la zona de memoriacompartida */
int * dir ; /* direccion de enlace de la memoria con los datos */
int * numero ; /* numero que guardamos en la memoria compartida */
printf(“Estoy creando la memoria compartida\n") ;
20
Ejemplo ( II )
/* creo la zona de memoria compartida */
shm = shmget(3000, sizeof(int), IPC_CREAT|IPC_EXCL|SHM_R|SHM_W) ;
if ( shm == -1 && errno == EEXIST)
shm = shmget(3000, sizeof(int), SHM_R|SHM_W) ;
printf ( “Cree la zona de memoria %d\n", shm) ;
/* lo enlazo a un proceso */
dir = (int *) shmat ( shm, NULL, 0) ;
if ( dir == (int *)-1)
{
perror("error de enlace\n") ;
exit(-1) ;
}
/* utilizo la informacion */
numero = (int *) dir ;
*numero = 0 ;
printf("%d", *numero) ;
21
Ejemplo ( III )
/* libero la memoria */
if ( shmdt(dir) == -1)
{
perror("error de liberacion\n") ;
exit(-1) ;
}
else
{
if (shmctl(shm, IPC_RMID, 0) == -1)
{
perror("error de borrado fisico\n") ;
exit(-1) ;
}
}
exit(0) ;
}
22
Funciones de semáforos
• Veamos ahora cómo se implementan los semáforos en Linux. Para
usarlos lo primero que hay que hacer es incluir las librerías
<sys/types.h>, <sys/ipc.h> y <sys/sem.h>, las funciones que vamos a
usar son semget, semop y semctl. Que como en la práctica anterior
no usaremos directamente para que el código sea portable a otros
sistemas operativos sino que las encapsularemos en otras funciones
en un archivo semaforo.c y su correspondiente archivo de cabecera
semaforo.h. En semaforo.h además de incluir las librerías y los
prototipos de las funciones que encapsularan las llamadas al sistema
operativo, tenemos que escribir:
#if defined ( __GNU_LIBRARY__) && !defined ( _SEM_SEMUN_UNDEFINED )
#else
union semun {
int valor ;
/* valor para SETVAL */
struct semid_ds * buf ;
/* buffer para IPC_STAT, IPC_SET */
unsigned short int * array ; /* array para GETALL, SETALL */
struct seminfo * __buf ;
/* buffer para IPC_INFO */
};
#endif
23
semget ( I )
• Sirve para crear el array de semáforos. Siempre se crea un array de
semáforos aunque sólo queramos usar 1, se creará un array de un
solo elemento y para referirte a él será el semáforo número 0.
• Igual que con shmget, se debe distinguir entre el primer msh que es el
que debe crear el array de semáforos usando las opciones
IPC_CREAT|IPC_EXCL|SHM_R|SHM_W, y el resto de mshs que sólo la
llaman para obtener el identificador del array de semáforos con los
permisos de lectura y escritura ( SHM_R|SHM_W ).
• Su prototipo es : int semget ( key_t key, int nsems, int flags )
• Donde el int de retorno es el identificador del array de semáforos
creados si no ha habido ningún error, -1 si lo ha habido con errno
indicando qué es lo que ha ocurrido ( distinguir EEXIST ).
24
semget ( II )
• Los argumentos que recibe son :
– key_t key : La clave que todos los procesos deben pasar a la
función. Debe estar definido en el .h por ejemplo como
CLAVE_SEMAFORO.
– int nsems : El número de semáforos que se quieren crear
– int flags : Opciones de creación, OR entre IPC_CREAT, IPC_EXCL,
SHM_R y SHM_W
• Cuidado que la creación de un semáforo es independiente de su
inicialización y usar un semáforo sin inicializar puede tener resultados
catastróficos, por lo que hay que asegurarse de inicializar los
semáforos inmediatamente después de crearlos.
• Veamos ahora un ejemplo de código que usa semget :
25
semget ( III )
#define CLAVE_SEMAFORO 2000
int semid ;
semid = semget( CLAVE_SEMAFORO, 1, IPC_CREAT|IPC_EXCL|SHM_R|SHM_W);
if ( semid == -1 )
{
if ( errno == EEXIST )
semid = semget( CLAVE_SEMAFORO, 1, SHM_R|SHM_W ) ;
else {
perror(“semget”);
exit(errno); }
}
else printf(“Soy el primero que crea el semáforo con identificador %d\n”, semid);
26
semop ( I )
• Implementa las operaciones de down y up. Su prototipo es :
int semop ( int semid, struct sembuf * sops, unsigned nsops )
• Donde el int del retorno es para control de errores.
• Los argumentos son :
– int semid : Es el identificador devuelto por semget.
– unsigned nsops : El número de operaciones a realizar sobre el
semáforo
– struct sembuf * sops : Es una estructura con 3 campos :
• sem_num : El número de semáforo con el que queremos trabajar.
• sem_op : La operación a realizar, que se indica con un número
– Positivo : Si se quiere liberar al semáforo ( up ), ya que lo que hace es
incrementar su valor.
– Negativo : Si se quiere tomar el semáforo ( down ) ya que lo que hace es
decrementar su valor.
– 0 : Si lo que se quiere el bloquear el semáforo hasta que tenga un valor 0.
27
semop ( II )
• sem_flag : Flags para configurar la ejecución de la operación
– SEM_UNDO : Para que cuando el proceso haga exit se nivelen los semáforos y no
haya problemas de que deje bloqueados a otros procesos.
– IPC_NOWAIT : Para que no se quede bloqueado y vuelva del down aunque el
semáforo sea 0.
• Ejemplo de pseudocódigo de lo que hace semop internamente :
coge_semaforo ( int sem ) {
if ( sem es 0 )
duerme hasta que
sem != 0 ;
else sem-- ;
}
libera_semaforo ( int sem ) {
if ( hay procesos durmiendo )
despierta al primero
sem++ ;
}
28
semop ( III ). Ejemplo de código de down
struct sembuf pbuf ;
pbuf.sem_num = 0 ;
pbuf.sem_op = -1 ;
pbuf.sem_flg = SEM_UNDO ;
if ( semop(semid, &pbuf, 1 ) == -1 )
{
perror(“semop:”);
return ERROR ;
}
29
semop ( IV ). Ejemplo de código de up
struct sembuf vbuf ;
vbuf.sem_num = 0 ;
vbuf.sem_op = 1 ;
vbuf.sem_flg = SEM_UNDO ;
if ( semop(semid, &vbuf, 1 ) == -1 )
{
perror(“semop:”);
return ERROR ;
}
30
semctl ( I )
• Sirve para inicializar, obtener información o eliminar el array de
semáforos. Su prototipo es : int semctl ( int semid, int semnum, int
comando, union semun arg )
• Donde el valor de retorno es para control de errores o el retorno de
determinados comandos como GETVAL o GETNCNT.
• Los argumentos son :
– int semid : Valor devuelto por semget.
– int comando : Puede ser GETNCNT ( para saber cuántos procesos están
esperando en el semáforo ), GETALL ( para obtener los valores de todos
los semáforos del array ), GETVAL ( para uno ), SETALL ( para inicializar
todos los semáforos del array ), SETVAL ( para uno ), IPC_RMID ( para
borrar el array ).
– int semnum : El número del semáforo sobre el que queremos trabajar. Se
ignora si se usa GETALL/SETALL que es para todos.
31
semctl ( II )
– union semun arg : Estructura con los campos
• val : El valor para SETVAL
• struct semid_ds * buf : Información para IPC_STAT e IPC_SET
• unsigned short * array : Array de valores para GETALL y SETALL.
• Ejemplos de código donde se usa semctl :
– Para inicializar el array :
union semun carg ;
carg.array = array ; // en array los valores a los que queremos inicializar los sem
if ( semctl( semid, 0, SETALL, carg ) == -1 ) ERROR else OK
– Para saber si hay procesos esperando en un semáforo :
numero = semctl ( semid, 1, GETNCNT, carg )
– Para eliminar el array de semáforos :
semctl ( semid, 0, IPC_RMID, NULL )
32
ipcs / icrpm
• De gran utilidad para esta práctica son estos dos comandos :
– ipcs : Se teclea ipcs y nos muestra los segmentos de memoria
compartida y semáforos que están reservados.
– ipcrm : Nos permite eliminar manualmente cualquier segmento de
memoria compartida ( shm ), cola de mensajes ( msg ) o array de
semáforos ( sem ) que por un fallo del programa no se haya eliminado
automáticamente. Para memoria compartida se ejecuta : ipcrm shm
idMemoria y para semáforos: ipcrm sem idSemaforo
• Un ejemplo de uso sería :
– ipcs ( y así averiguamos el identificador de la memoria compartida )
– ipcrm shm id ( y sí ya se han terminado todos los procesos enganchados
a la memoria compartida desaparece )
33
Pasos orientativos para resolver la práctica
• 1°. Familiarizarse con las funciones de creación, modificación y
liberación de la memoria compartida y de semáforos. Codificar
memoria.c, memoria.h, semaforo.c y semaforo.h. Modificar el
makefile para tener en cuenta estos nuevos ficheros.
• 2º. Copiar los ficheros de la práctica anterior en un nuevo directorio
para asegurarnos que seguimos teniendo la versión anterior.
• 3º. Modificar main.c para que se tenga en cuenta que ahora se
trabaja con memoria compartida y con semáforos.
• 4º. Modificar func.c para tener en cuenta los nuevos comandos y la
gestión de la memoria compartida.
• ¡Importante! Asegurarnos de eliminar posibles memorias compartidas
que se hayan quedado sin borrar antes de la siguiente ejecución.
34
Control de errores y mejoras
• Control de errores:
– Controlar los retornos de todas las funciones de manejo de la memoria
compartida y los semáforos.
– En general, robustecer el código frente a cualquier posible error que ocurra
y asegurar que se dejará la máquina cómo estaba antes de que se
ejecutase alguna instancia de msh.
• Mejoras:
– Cualquiera de las mejoras de la práctica anterior, son aplicables aquí. En
general, se deja abierta la posibilidad de mejorar el código para que se
parezca lo máximo posible a un intérprete de comandos Linux.
– IPC_NOWAIT para que en el caso de que el código se vaya a quedar
bloqueado en un punto nos muestra antes un mensaje informándonos.
– Usar memoria dinámica compartida.
– Usar algoritmos como lectores/escritores o mejores para no hacer todos los
acceso bloqueantes, sólo cuando 1 escribe, que los demás no lean, pero
permitir que hay lecturas concurrentes.
35
Problema lector/escritor. Teoría ( I )
• Vamos a ver el problema del lector/escritor en el que tenemos
procesos con una base de datos común y unos quieren leer de ella y
otros escribir. Al igual que en la práctica no se permite que mientras
que están leyendo otro escriba, aunque si leer puesto que esto no
supone ninguna modificación de la memoria compartida, y
tampoco se permite que varios estén escribiendo.
• Para resolverlo se usan 2 semáforos binarios :
– mutex : Que controla la escritura de la variable que indica el número de lectores (
numLectores ). De forma que si hay algún lector ya no puede haber escritor y
cuando ya no haya lectores ya puede entrar el escritor.
– db : Que controla el acceso a la base de datos por parte tanto de los lectores
como los escritores. Si está a 1 entonces es que no hay nadie escribiendo y
pueden entrar tantos lectores como quieran a consultar la base de datos. Si es 0
para a los lectores porque el escritor está modificando la base de datos.
36
Problema lector/escritor. Pseudocódigo ( II
)
semaforo mutex = 1
semaforo db = 1
int numLectores = 0
Escritor
while ( 1 )
{
down(db) ;
escribir
up(db) ;
}
Lector
while ( 1 ){
down( mutex ) ;
numLectores++ ;
if ( numLectores == 1 )
down(db) ;
up(mutex);
leer
down(mutex);
numLectores-- ;
if ( numLectores == 0 )
up(db);
up(mutex); }
37
Descargar