ENUNCIADO

La práctica consiste en generar cuatro procesos que pretenden acceder a una zona de exclusión mutua en la que sólo pueden estar dos de ellos a la vez. El acceso a la zona de exclusión mutua se regulará por un semáforo. Cada proceso calculará un número igual a la parte entera de la última cifra de su PID dividida por dos. A ese número le sumará uno y le llamará somnolencia. El proceso dormirá tanto como indique su somnolencia, esperará hasta entrar en la sección crítica, dormirá dentro tanto como indique su somnolencia y, vuelta a empezar. Se puede usar sleep. Cuando se pulse CTRL+C, todos los procesos acabarán y el semáforo será borrado del Sistema Operativo.

Salida por pantalla: para comprobar el correcto comportamiento del programa, la salida por pantalla será como sigue. Al principio, el primer proceso limpiará la pantalla con la función:
system("clear"); 
Cuando un proceso entre en la sección crítica escribirá la última cifra de su PID en la línea de la pantalla cuyo número es igual a ella. Cuando vaya a salir de la sección crítica borrará (escribirá un espacio encima) en dicha línea. Así se podrá observar a simple vista que no hay más de dos procesos a la vez en la sección crítica. Para poner el cursor en la línea 7, columna 3, por ejemplo, se puede usar la función:
system("tput cup 7 3"); 
Observación: si usáis printf para escribir en la pantalla debéis saber que esta función tiene un búfer intermedio que hace que lo que se escriba no aparezca inmediatamente en pantalla. El búfer se vacía en tres circunstancias:
  1. Se recibe un retorno del carro (\n).
  2. Se llena y no queda más remedio que volcarlo.
  3. Se ejecuta la función fflush(stdout);.

Tenedlo en consideración. Podéis encontrar en /home/so/SESION6/critica el ejecutable de esta práctica.

COMENTARIOS

La práctica con 0.25 es la de IorI:
/* ::::::::Esto tiene que ir antes de los ficheros de cabecera::::::::: */
#ifndef _HPUX_SOURCE
  #define _HPUX_SOURCE
#endif

/* ::::::::Bibliotecas::::::::: */
/* ::::::::Ficheros de cabeceras, más bien::::::::: */
#include <stdio.h>    /* printf()... */
#include <signal.h>   /* sigaction()... */
#include <unistd.h>   /* system(), fork(),... */
#include <sys/sem.h>  /* semctl(), semop(),... */
#include <sys/wait.h> /* wait() */

/* ::::::::Macros, contantes::::::::: */
/* ::::::::¡Buena idea!::::::::: */
#define SEM_WAIT(x,n)\
  x##.sem_num=n;\
  x##.sem_op=-1;\
  x##.sem_flg=0;

#define SEM_SIGNAL(x,n)\
  x##.sem_num=n;\
  x##.sem_op=1;\
  x##.sem_flg=0;


/* ::::::::Variables Globales::::::::: */
/* ::::::::¡Yu, yu!::::::::: */
int semid;
pid_t pid[3];

/* ::::::::Prototipos::::::::: */
void newsigint(int signal);
void MEZ_algoritm(int semid); /* Mutual Exclusion Zone Algoritm :P */
                              /* Mutual Exclusion Zone Algorithm ;) */

/* ::::::::MAIN::::::::: */  
/* Gracias por la información. */
int main(int argc, char *argv[])
{
  int info;
  struct sigaction new, old;
  sigset_t mask;
  
  if((semid=semget(IPC_PRIVATE, 1, IPC_CREAT | 0600)) == -1)
  {
    perror("Error en creación de semáforo");
    exit(1);
  }
  semctl(semid,0,SETVAL,2); /* ¡¡¡Comprobación errores!!! */

  system("clear");

  switch(pid[0]=fork())
  {
    case -1 :
      perror("Error en creación del segundo proceso");
      exit(2);

    case 0  : /* Segundo proceso */
      MEZ_algoritm(semid);

    default : /* Primer proceso */
      switch(pid[1]=fork())
      {
        case -1 :
          perror("Error en creación del tercer proceso");
          exit(3);

        case 0  : /* Tercer proceso */
          MEZ_algoritm(semid);

        default : /* Primer proceso */
          switch(pid[2]=fork())
          {
            case -1 :
              perror("Error en creación del cuarto proceso");
              exit(4);

            case 0  : /* Cuarto proceso */
              MEZ_algoritm(semid);

            default : /* Primer proceso */
              new.sa_handler=newsigint;
              sigemptyset(&mask);
              new.sa_mask=mask;
              new.sa_flags=SA_RESTART | SA_RESETHAND;
              /* ¿Para qué sirven aquí estos flags? 
                 Todavía SA_RESTART hace que los semops no fallen cuando se recibe una señal,
                 pero el otro... */

              if(sigaction(SIGINT, &new, &old)==-1)
              {
                perror("Error en registro de SIGINT");
                exit(-5);
              }

              MEZ_algoritm(semid);
          }
      }
   }
}


/* ::::::::Funciones propias::::::::: */

void newsigint(int signal)
{
  int i, data;

  for(i=0;i<3;i++)
  {
    kill(pid[i],SIGINT);
    wait(&data);
  }

  if(semctl(semid,0,IPC_RMID)==-1)
  {
    perror("Error en borrado de semáforo");
    exit(5);
  }

  system("clear"); /* Bueno para borrar huellas... */
  exit(0);
}

void MEZ_algoritm(int semid)
{
  /* Poner código antes de haber acabado con las variables es propio de C++ */
  int somnolencia=((getpid() % 10) >> 1) + 1;
  /* Forma de dividir por dos -----^^^^ */
  struct sembuf semact;
  char command[20];

  while(666)
  {
    sleep(somnolencia);

    SEM_WAIT(semact,0);
    if(semop(semid,&semact,1)==-1)
    {
      perror("Operación WAIT en semáforo");
      exit(6);
    }

    sprintf(command,"tput cup %d 1",getpid() % 10);
    system(command);
    printf("%d\b",getpid() % 10);
    fflush(stdout);
    sleep(somnolencia);
    printf(" \b");
    fflush(stdout);

    SEM_SIGNAL(semact,0);
    if(semop(semid,&semact,1)==-1)
    {
      perror("Operación SIGNAL en semáforo");
      exit(7);
    }
  }
}
   


MENCIONES ESPECIALES

LPEs

Los semáforos son difíciles de manejar por dos circunstancias: primero, hay que diseñar dónde colocarlos y qué valores iniciales y operaciones vamos a realizar sobre ellos. En segundo lugar, hay que saber programar eso que se ha diseñado. Me alegro mucho que esta sección esté en blanco.
© 2000 Guillermo González Talaván.