sábado, 26 de novembro de 2011

SO - Semáforos

... e ninguém desrespeita!

Os sistemas operacionais tipo Unix têm uma característica interessante; neles, tudo é tratado como um arquivo. Por exemplo: existem arquivos comuns, diretórios, links para arquivos e até mesmo um dispositivo é um arquivo. A saída (terminal/monitor), o teclado, uma unidade de disco ou uma porta de rede são todos tratados como arquivos pelo SO.

O Unix é tipicamente um SO multiusuário e, não raro, o sistema deve permitir o acesso a um arquivo por um aplicativo e, frequentemente, acontece de mais de um processo precisar acessar um arquivo.

Nos Unices, semáforo (semaphore) é a designação de uma variável que funciona como um contador que controla o acesso aos recursos (arquivos) pelos processos dos usuários. Ou seja, se um processo está acessando um arquivo o semáforo é iniciado e, se outro tenta acessar o mesmo arquivo ele verifica o estado do semáforo e o encontra iniciado, tendo que esperar "o sinal abrir".  Quando o primeiro processo termina o acesso ao arquivo o semáforo é reiniciado e o outro processo pode acessar o arquivo pois "o sinal está aberto". Para o usuário, todo o processo é transparente e controlado pelo núcleo (kernel) do SO.

No caso de mais de um processo — que é o que ocorre mais comumente — o semáforo serve para coordenar a fila de processos que vai usar determinado recurso. Cada semáforo tem uma identidade e deve ser armazenado no kernel para poder ser acessível a todos os processos.

Um semáforo pode ser criado pela função em C "int semget(key_t, int, int)" que recebe três argumentos e retorna um int que é o identificador do semáforo.



   #include <sys/types.h> 

   #include <sys/ipc.h> 
   #include <sys/sem.h> 

   int semget(key_t key, int nsems, int flag);  


Com base nos valores de "key" e "flag" a função retorna o identificador do semáforo criado ou -1 no caso de ocorrer um erro e não se possa criar o semáforo.

O comando 'ipcs -s' no Unix reporta os semáforos existentes. Para cada semáforo criado é criada uma estrutura no kernel do SO com o nome de 'semid_ds' prototipada em <sys/ipc.h> e em <sys/sem.h>.

As funções abaixo são de semáforos (veja "man <num> <func>" para mais detalhes).


ipcrm (1)            - remove uma fila de mensagens, conjunto de semáforos ou memória compartilhada
sem (4)              - POSIX semaphores (BSD)
sem_close (3)        - fecha um determinado semáforo

sem_destroy (3)      - destrói um semáforo qualquer
sem_getvalue (3)     - obtém o valor de um semáforo
sem_init (3)         - inicia um semáforo qualquer
sem_open (3)         - inicia e abre um determinado semáforo
sem_overview (7)     - POSIX semaphores (Linux)
sem_post (3)         - destrava um semáforo (incrementa)
sem_timedwait (3)    - trava um semáforo (BSD/Linux)
sem_trywait (3)      - trava um semáforo (decrementa)
sem_unlink (3)       - remove um determinado semáforo
sem_wait (3)         - trava um semáforo (decrementa)
semctl (2)           - controle de operações de semáforo
semget (2)           - obtém um identificador para semáforo
semop (2)            - Operações de semáforos
semtimedop (2)       - Operações de semáforos


Referências:


Dunstan, N. e Fris, "Process Scheduling and UNIX Semaphores", in: I.Software—Practice and Experience, Vol. 25(10), 1141–1153 (October, 1995).

Callari F. "Synopsis on Concurrency", http://www.cim.mcgill.ca/~franco/OpSys-304-427/lecture-notes/node29.html (acesso em 25/11/2011)

Apêndice (exemplo):

Os códigos abaixo (de Unix Systems Programming - Examples) demonstram a utilização de semáforos.

semabinit.c:
/* semabinit.c - initialize a semaphore for use by programs sema and semb */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>

/* The semaphore key is an arbitrary long integer which serves as an
   external identifier by which the semaphore is known to any program
   that wishes to use it. */

#define KEY (1492)

void main()
{
   int id; /* Number by which the semaphore is known within a program */

   /* The next thing is an argument to the semctl() function. Semctl() 
      does various things to the semaphore depending on which arguments
      are passed. We will use it to make sure that the value of the 
      semaphore is initially 0. */

 union semun {
  int val;
  struct semid_ds *buf;
  ushort * array;
 } argument;

   argument.val = 0;

   /* Create the semaphore with external key KEY if it doesn't already 
      exists. Give permissions to the world. */

   id = semget(KEY, 1, 0666 | IPC_CREAT);

   /* Always check system returns. */

   if(id < 0)
   {
      fprintf(stderr, "Unable to obtain semaphore.\n");
      exit(0);
   }

   /* What we actually get is an array of semaphores. The second 
      argument to semget() was the array dimension - in our case
      1. */

   /* Set the value of the number 0 semaphore in semaphore array
      # id to the value 0. */

   if( semctl(id, 0, SETVAL, argument) < 0)
   {
      fprintf( stderr, "Cannot set semaphore value.\n");
   }
   else
   {
      fprintf(stderr, "Semaphore %d initialized.\n", KEY);
   }
} 

sema.c:
/* Semaphore example program a (sema.c) */
/* We have two programs, sema and semb. Semb may be initiated at any 
  time, but will be forced to wait until sema is executed. Sema and
  semb do not have to be executed by the same user! */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define KEY (1492)
/* This is the external name by which the semaphore is known to any
   program that wishes to access it. */

void main()
{
   int id;  /* Internal identifier of the semaphore. */
   struct sembuf operations[1];
   /* An "array" of one operation to perform on the semaphore. */

   int retval; /* Return value from semop() */

   /* Get the index for the semaphore with external name KEY. */
   id = semget(KEY, 1, 0666);
   if(id < 0)
   /* Semaphore does not exist. */
   {
      fprintf(stderr, "Program sema cannot find semaphore, exiting.\n");
      exit(0);
   }

   /* Do a semaphore V-operation. */
   printf("Program sema about to do a V-operation. \n");

   /* Set up the sembuf structure. */
   /* Which semaphore in the semaphore array : */
    operations[0].sem_num = 0;
    /* Which operation? Add 1 to semaphore value : */
    operations[0].sem_op = 1;
    /* Set the flag so we will wait : */   
    operations[0].sem_flg = 0;

    /* So do the operation! */
    retval = semop(id, operations, 1);

    if(retval == 0)
    {
       printf("Successful V-operation by program sema.\n");
    }
    else
    {
       printf("sema: V-operation did not succeed.\n");
 perror("REASON");
    }
}

/* Think carefully about what the V-operation does. If sema is executed 
   twice, then semb can execute twice. */ 

semb.c:
/* Semaphore example program b (semb.c) */
/* We have two programs, sema and semb. Semb may be initiated at any 
  time, but will be forced to wait until sema is executed. Sema and
  semb do not have to be executed by the same user! */

/* HOW TO TEST:
   Execute semb &
   The & is important - otherwise you would have have to move to
   a different terminal to execute sema.

   Then execute sema.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define KEY (1492)
/* This is the external name by which the semaphore is known to any
   program that wishes to access it. */

void main()
{
   int id;  /* Internal identifier of the semaphore. */
   struct sembuf operations[1];
   /* An "array" of one operation to perform on the semaphore. */

   int retval; /* Return value from semop() */

   /* Get the index for the semaphore with external name KEY. */
   id = semget(KEY, 1, 0666);
   if(id < 0)
   /* Semaphore does not exist. */
   {
      fprintf(stderr, "Program semb cannot find semaphore, exiting.\n");
      exit(0);
   }

   /* Do a semaphore P-operation. */
   printf("Program semb about to do a P-operation. \n");
   printf("Process id is %d\n", getpid());

   /* Set up the sembuf structure. */
   /* Which semaphore in the semaphore array : */
    operations[0].sem_num = 0;
    /* Which operation? Subtract 1 from semaphore value : */
    operations[0].sem_op = -1;
    /* Set the flag so we will wait : */   
    operations[0].sem_flg = 0;

    /* So do the operation! */
    retval = semop(id, operations, 1);

    if(retval == 0)
    {
       printf("Successful P-operation by program semb.\n");
       printf("Process id is %d\n", getpid());
    }
    else
    {
       printf("semb: P-operation did not succeed.\n");
    }
}

/* Think carefully about what the V-operation does. If sema is executed 
   twice, then semb can execute twice. */




Nenhum comentário:

Postar um comentário