Pues, hoy voy a ponernos un reto a ver si lograís resolverlo. Bueno, en realidad os voy a poner el enunciado y solución (que yo dí) a una de las prácticas que nos pusieron en la asignatura de Laboratorio de Sistemas Operativos (LabSSOO para los amigos) de 2º de ITIS de la Universidad de Salamanca.
Os animo a tratar de sacar vuestra propia solución y así probaís vuestros conocimientos de programación en C, en este caso para sistemas UNIX.
Pretendo poner 1º una parte general del enunciado y, para que el post no sea muy largo, el resto del enunciado y la solución en la parte de "leer más". Es una nueva opción que he implementado en la plantilla del blog y no se qué tal funcionará, asi que lo siento si no funciona xD. Como siempre, admito comentarios de cualquier tipo, ya sean de la solución, otras soluciones o de la forma de publicar estos "retos". Como por ejemplo, publicar la solución un poco oculta o separada del enunciado para evitar tentativas de mirarla. Y ya no me enrollo más.
Os animo a tratar de sacar vuestra propia solución y así probaís vuestros conocimientos de programación en C, en este caso para sistemas UNIX.
Pretendo poner 1º una parte general del enunciado y, para que el post no sea muy largo, el resto del enunciado y la solución en la parte de "leer más". Es una nueva opción que he implementado en la plantilla del blog y no se qué tal funcionará, asi que lo siento si no funciona xD. Como siempre, admito comentarios de cualquier tipo, ya sean de la solución, otras soluciones o de la forma de publicar estos "retos". Como por ejemplo, publicar la solución un poco oculta o separada del enunciado para evitar tentativas de mirarla. Y ya no me enrollo más.
Tú la llevas
El programa que hay que presentar constará de un único fichero fuente de nombre
La ejecución del programa creará una serie de procesos que accederán a una zona de exclusión mutua. La regulación del acceso a dicha zona se hará mediante el paso de un testigo.
Los procesos se dispondrán en círculo. Mientras ningún proceso quiera entrar en la sección crítica, el testigo circula por el círculo de procesos. Cuando un proceso quiera entrar, ha de esperar a estar en posesión del testigo. Entrará entonces en la sección crítica. Cuando salga, procederá con la circulación del testigo.
La invocación de la práctica se hará con dos argumentos, el último de ellos opcional:
Si el segundo argumento es la palabra debug, la práctica se ejecuta en modo de depuración. Las características de este modo se explican más abajo. Si los parámetros introducidos no respetan las reglas anteriores, el programa lo detectará, informará al usuario y acabará.
lallevas.c
. La correcta compilación de dicho programa, producirá un fichero ejecutable, cuyo nombre será obligatoriamente lallevas
. Respetad las mayúsculas/minúsculas de los nombres, si las hubiere. La ejecución del programa creará una serie de procesos que accederán a una zona de exclusión mutua. La regulación del acceso a dicha zona se hará mediante el paso de un testigo.
Los procesos se dispondrán en círculo. Mientras ningún proceso quiera entrar en la sección crítica, el testigo circula por el círculo de procesos. Cuando un proceso quiera entrar, ha de esperar a estar en posesión del testigo. Entrará entonces en la sección crítica. Cuando salga, procederá con la circulación del testigo.
La invocación de la práctica se hará con dos argumentos, el último de ellos opcional:
lallevas n_procs [debug]El primero es un número entero comprendido entre 3 y 33. Es el número de procesos que participan en la práctica. De ellos, uno es el padre y el resto son hijos suyos. El modo en que se forma el corro para la circulación del testigo se deja a la libertad de los autores.
Si el segundo argumento es la palabra debug, la práctica se ejecuta en modo de depuración. Las características de este modo se explican más abajo. Si los parámetros introducidos no respetan las reglas anteriores, el programa lo detectará, informará al usuario y acabará.
Funcionamiento de cada proceso
Durante el período de funcionamiento de la práctica, los procesos estarán en el siguiente bucle infinito:- Si estamos en el modo de depuración, duerme 1 segundo. Si no, no hace nada.
- Entra en la sección crítica.
- Si estamos en el modo de depuración, imprime en la pantalla: E(pid). Si no, imprime solamente una E. En ningún caso imprimirá un salto de línea.
- Si estamos en el modo de depuración, duerme 2 segundo. Si no, no hace nada.
- Si estamos en el modo de depuración, imprime en la pantalla: S(pid). Si no, imprime solamente una S. En ningún caso imprimirá un salto de línea.
- Sale de la sección crítica.
El paso del testigo se va a simular mediante el envío de señales. Cuando un proceso en posesión del testigo, desea pasárselo a otro, le enviará la señalSIGUSR1
. Un proceso que no tenga el testigo pasa a poseerlo cuando reciba la señal. Si debe reenviarlo, esperará al menos una dé cima de segundo si estamos en el modo de depuración. De no estar en dicho modo, el reenvío será inmediato. Para efectuar esa pausa, se usará la funciónnanosleep
(mirad la página de manual).
Para hacer que el proceso duerma en el resto de casos, se usará la señalSIGALRM
, nunca la funciónsleep
. Dormir hará que el proceso, en modo de depuración, apenas consuma CPU. Lo podéis comprobar con la ordentop
.
Para que el buffer intermedio usado porprintf
no interfiera con la salida de los procesos, es importante usarwrite
para la salida por pantalla en su lugar.
Finalización ordenada
La práctica acabará cuando el usuario pulse CTRL-C. Los procesos deben morir y el padre, una vez hayan muerto todos imprimirá un salto de línea y la frase: "Programa acabado correctamente".
Restricciones
- Se deberán usar llamadas al sistema siempre que sea posible, a no ser que se especifique lo contrario.
- No está permitido usar la función de biblioteca
system
, salvo indicación explícita en el enunciado de la práctica. - No se puede suponer que los PIDs de los procesos de una ristra van a aparecer consecutivos. Puestos en plan exquisito, ni siquiera podemos suponer que estarán ordenados de menor a mayor (puede ocurrir que se agoten los PIDs y se retome la cuenta partiendo de cero).
- No está permitido el uso de ficheros, tuberías u otro mecanismo externo para transmitir información entre los procesos, salvo que se indique en el enunciado.
- Supondremos un límite máximo de procesos igual a 33. Este límite os puede servir para no tener que usar memoria dinámica si no lo deseáis.
- Solución
#include
#include
#include
#include
#define EXERR -1
int pidPadre, numProc = 0, pidSiguiente;
int entrar = 0, modoD = 0, sonoAlarma = 0;
sigset_t blockAlarm;
void morir(int);
void testigo(int);
void alarma(int);
void error(int);
int main(int argc, char * argv[]){
int i = 0, n;
int pid, pidAnterior;
char mensaje[50];
struct sigaction manejadora;
sigset_t bloqueo;
if (sigfillset(&bloqueo) != 0) return EXERR;
if (sigemptyset(&blockAlarm) != 0) return EXERR;
if (sigprocmask(SIG_SETMASK, &bloqueo, NULL) != 0) return EXERR;
manejadora.sa_handler = morir;
if (sigemptyset(&manejadora.sa_mask) != 0) return EXERR;
if (sigaddset(&manejadora.sa_mask, SIGUSR1) != 0) return EXERR;
manejadora.sa_flags = 0;
if (sigaction(SIGINT, &manejadora, NULL) != 0) return EXERR;
manejadora.sa_handler = testigo;
if (sigemptyset(&manejadora.sa_mask) != 0) return EXERR;
if (sigaddset(&manejadora.sa_mask, SIGALRM) != 0) return EXERR;
manejadora.sa_flags = 0;
if (sigaction(SIGUSR1, &manejadora, NULL) != 0) return EXERR;
manejadora.sa_handler = alarma;
if (sigemptyset(&manejadora.sa_mask) != 0) return EXERR;
if (sigaddset(&manejadora.sa_mask, SIGUSR1) != 0) return EXERR;
manejadora.sa_flags = 0;
if (sigaction(SIGALRM, &manejadora, NULL) != 0) return EXERR;
manejadora.sa_handler = error;
manejadora.sa_mask = bloqueo;
if (sigdelset(&manejadora.sa_mask, SIGINT) != 0) return EXERR;
manejadora.sa_flags = 0;
if (sigaction(SIGTERM, &manejadora, NULL) != 0) return EXERR;
pidPadre = getpid();
pidAnterior = pidPadre;
if(argc == 1){
if(sprintf(mensaje, "Faltan argumentos\n") < 0)return EXERR;
if (write(0, mensaje, strlen(mensaje)) == -1)return EXERR;
return EXERR;
}
if(argc == 3){
if(strcmp(argv[2], "debug")== 0){
modoD = 1;
}
else{
if(sprintf(mensaje, "Segundo argumento invalido\n") < 0)return EXERR;
if(write(0, mensaje, strlen(mensaje)) == -1)return EXERR;
return EXERR;
}
}
n = atoi(argv[1]);
if(n <= 0){
if(sprintf(mensaje, "N?? procesos inv??lido, debe ser mayor que 0\n") < 0)return
EXERR;
if(write(0, mensaje, strlen(mensaje)) == -1)return EXERR;
return EXERR;
}
for(i = 0; i < n; i++){
pid = fork();
if(pid == -1){
raise(SIGTERM);
}
else if(pid == 0){
pidSiguiente = pidAnterior;
break;
}
else{
pidAnterior = pid;
numProc++;
}
}
if(getpid() == pidPadre){
pidSiguiente = pidAnterior;
if (kill(pidSiguiente, SIGUSR1) != 0){
kill(0, SIGINT);
for(i = 0; i < numProc; i++){
wait(&n);
}
sprintf(mensaje, "\nEjecucion concluida erroneamente\n");
write(0, mensaje, strlen(mensaje));
_Exit(-1);
}
}
if (sigdelset(&bloqueo, SIGUSR1) != 0) kill(pidPadre, SIGTERM);
if (sigdelset(&bloqueo, SIGTERM) != 0) kill(pidPadre, SIGTERM);
if (sigdelset(&bloqueo, SIGINT) != 0) kill(pidPadre, SIGTERM);
if (sigprocmask(SIG_SETMASK, &bloqueo, NULL) != 0) kill(pidPadre, SIGTERM);
while(1){
entrar = 0;
if(modoD){
sonoAlarma = 0;
alarm(1);
while(!sonoAlarma){
sigsuspend(&blockAlarm);
}
}
entrar = 1;
sigsuspend(&bloqueo);
}
return 0;
}
void morir(int sennal){
int i;
int codigo;
char mensaje[85];
if(getpid() != pidPadre){
_Exit(0);
}
else{
for(i = 0; i < numProc; i++){
wait(&codigo);
}
sprintf(mensaje, "\nEjecucion concluida correctamente\n");
write(0, mensaje, strlen(mensaje));
_Exit(0);
}
}
void testigo(int sennal){
char mensaje[40];
struct timespec duerme;
if(entrar == 1){
entrar = 0;
if(modoD){
sonoAlarma = 0;
if(sprintf(mensaje, "E(%d)", getpid()) < 0) kill(SIGTERM, pidPadre);
if(write(0, mensaje, strlen(mensaje)) == -1) kill(SIGTERM, pidPadre);
alarm(2);
while(!sonoAlarma){
sigsuspend(&blockAlarm);
}
}
else{
if(sprintf(mensaje, "E") < 0) kill(SIGTERM, pidPadre);
if(write(0, mensaje, strlen(mensaje)) == -1) kill(SIGTERM, pidPadre);
}
if(modoD) {
if(sprintf(mensaje, "S(%d)", getpid()) < 0) kill(SIGTERM, pidPadre);
}
else {
if(sprintf(mensaje, "S") < 0) kill(SIGTERM, pidPadre);
}
if(write(0, mensaje, strlen(mensaje)) == -1) kill(SIGTERM, pidPadre);
}
if(modoD){
duerme.tv_sec = 0;
duerme.tv_nsec = 100000;
while(nanosleep(&duerme, &duerme));
}
if (kill(pidSiguiente, SIGUSR1) != 0) kill(pidPadre, SIGTERM);
return;
}
void alarma(int sennal){
sonoAlarma = 1;
return;
};
void error(int sennal){
kill(0, SIGINT);
raise(SIGINT);
}
No hay comentarios:
Publicar un comentario