Semáforos

 

São variáveis que permitem sincronização entre processos, de modo a evitar race conditions (quando dois ou mais processos necessitam de escrever/ler dados partilhados simultaneamente) ,cujas operações são atómicas (indivisíveis) e executadas pelo kernel. São utilizados para a gestão de recursos (ex: se temos uma impressora que só admite 5 impressões simultâneas, temos de assegurar que em cada instante existem no máximo 5 processos a imprimir - ficando os outros à espera para imprimir). Uma situação especial da competição por um recurso é a exclusão mútua (apenas um processo pode efectuar uma determinada operação). Um exemplo desta situação é actualização de uma base de dados partilhada por vários processos.

 

Modo de Armazenamento

struct semid_ds

{

struct ipc_perm sem_perm; /* estrutura de permissões */

short pad1[7]; /* reservado */

unshort sem_nsems; /* nº de semáforos no array */

time_t sem_otime; /* instante da última operação semop */

time_t sem_ctime; /* instante da última alteração de atributos do conjunto de semáforos através de semctl + IPC_SET */

}

 

- o valor do semáforo;

- pid do último processo a manipular o semáforo (através de semop/semctl);

- o número de processos que estão à espera que o valor do semáforo seja 0 .

- o número de processos que estão à espera que seja feito um UP (libertar recursos) no semáforo;

 

System Calls

 

 

O nome pode ter os seguintes valores:

- permite criar um conjunto de semáforos privado ao processo invocador;

- o identificador resultante nunca vai ser retornado a outros processos que invoquem semget, seja qual for o seu parâmetro chave;

- existe a garantia de que o sistema cria um conjunto novo;

- o identificador do conjunto pode ser partilhado com outros processos, ao serem feitos sucessivos fork(), em que os valores das variáveis do processo pai são herdados pelas correspondentes variáveis dos processos filho.

 

- permite criar ou aceder a um conjunto de semáforos, de acordo com o conteúdo das flags flags;

- o conjunto pode ser partilhado com qualquer outro processo, desde que o parâmetro chave seja igual.

 

Valores que flags pode ter:

 

- Cria um conjunto de semáforos nome (se este não existir);

- Permite verificar se um dado conjunto existe ou não.

 

- Acede ao conjunto de semáforos nome (se existir);

- Se já existir o conjunto nome e o seu número de semáforos é inferior que nsems, então retorna erro.

 

- Acede ao conjunto de semáforos nome (se existir); se não existir cria-o;

- Se já existir o conjunto nome e o seu número de semáforos é inferior que nsems, então retorna erro.

 

 

Nota: Na operação de abertura de um conjunto de semáforos, devem ser respeitadas as permissões de acesso, isto é, as flags de permissão presentes no argumento flags (escritas na forma 0xxx), devem conter aquelas presentes no conjunto de semáforos (aquando da sua criação).

Se a system call originar um erro, o valor retornado é -1, sendo associado à variável global errno o erro correspondente.

 

 

 

Argumento sops:

struct sembuf

{ short sem_num; /* número do semáforo */

int sem_op; /* operação */

int flags;

}

 

e significa executar a operação sem_op sobre o semáforo sem_num de acordo com flags.

 

 

< 0

 

o      executa uma operação DOWN(P) - aloca p recursos.

o      Se valor(sem_num) ³ ô sem_opô , a operação tem sucesso e é decrementado ao valor do semáforo ô sem_opô ;

o      Se o resultado é 0, o kernel acorda todos os processos bloqueados à espera que o semáforo sem_num fosse igual a 0.

 

> 0

o      executa a operação UP(V) - liberta v recursos.

o      O valor de sem_op é adicionado ao valor de sem_num;

o      O kernel acorda todos os processos bloqueados devido a esse semáforo.

 

= 0

o      espera que o semáforo sem_num seja igual a 0.

 

 

 

Flag IPC_NOWAIT:

- caso de sem_op < 0 e valor(sem_num) <ô sem_opô

 

 

Activa: semop falha e retorna erro.

Não activa: semop suspende o processo invocador até que:

- valor(sem_num) ³ ô sem_opô ;

- semid seja removido do sistema, o que origina erro como valor de retorno da system call semop;

- o processo invocador recebe um sinal capturado

 

- caso de sem_op = 0 e valor(sem_num) ¹ 0

 

Activa: semop falha e retorna erro.

Não activa: semop suspende o processo invocador até que:

- valor(sem_num) = 0, o que implica que semop retorna com sucesso;

- valor(sem_num) = ô sem_opô , o que implica que o kernel execute de novo a operação.

- semid seja removido do sistema, o que origina um erro como valor de retorno.

- O processo invocador receba um sinal capturado.

 

Flag SEM_UNDO

Existe a possibilidade de uma secção crítica ficar bloqueada eternamente - quando o processo que a reservou não a libertar (ex: quando um processo recebe um sinal no meio da secção crítica, termina o processo, sem desbloquear a secção crítica).

Esta flag, tem como objectivo tornar fiável a utilização de semáforos, fazendo com que o kernel mantenha memorizadas as operações de sincronização executadas pelos processos. É utilizada uma estrutura dinâmica que memoriza todas as operações executadas pelo processo sobre cada semáforo. Quando o processo termina, o kernel percorre toda a estrutura e reactualiza os valores dos semáforos.

 

 

 

 

union semun

{

int val;

struct semid_ds *buf;

unsigned short *array;

}

cmd pode ter os seguintes valores:

 

 

Observações:

 

 

 

 

Exemplo

 

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

 

/* Este exemplo cria 20 processos. Em cada instante existem no maximo 5 processos - so' cria outro processo quando um filho terminar e libertar o recurso...*/

 

 

 

main()

{

int s,i,j;

pid_t pid;

union semun seu;

struct sembuf up,down;

/* estrutura para libertar um recurso */

up.sem_num=0; /* numero do semaforo - array de semaforos…*/

up.sem_op=1; /* numero de recursos que liberta */

up.sem_flg=0;

/* estrutura usada para ocupar um recurso */

down.sem_num=0; /* numero do semaforo */

down.sem_op=-1; /* numero de recursos que ocupa */

down.sem_flg=0;

/* cria o semaforo */

if ((s=semget((key_t)0,1,0777|IPC_CREAT)) == -1)

{

printf("Erro ao abrir o semaforo\n");

exit(-1);

}

/* inicializa o semaforo - neste caso existem 5 unidades disponiveis */

seu.val=5;

semctl(s,0,SETVAL,seu);

/* cria,cao dos 20 processos - apenas podem existir 5 processos filho

em cada instante */

for(i=0;i<20;i++)

{ /* ocupa um recurso - so' continua quando existirem recursos...*/

semop(s,&down,1);

if ((pid=fork())==0)

{

/* filho */

break;

}

}

 

if (pid>0)

{

for (i=0;i<20;i++)

wait(&j);

            semctl(s,0,IPC_RMID,NULL);

 

}

else

 

{

/* filho */

printf("O filho %d foi criado\n",i);

sleep(i);

semop(s,&up,1); /* liberta o recurso utilizado */

printf("Fim do filho %d\n",i);

exit(0);

}

 

}

 

 

 

Exercícios

 

1- Faça as seguintes funções:

 

int cria_semaforo(int chave, int n_recursos) - esta função cria um semáforo com n_recursos.

 

int ocupa_recursos(int identificador, int n_recursos) - esta função ocupa n_recursos de um semáforo

 

int liberta_recursos(int identificador, int n_recursos) - esta função liberta n_recursos de um semáforo

 

int remove_semaforo(int identificador) - esta função remove do sistema um semáforo.

 

Nota: todas as funções partem do pressuposto que para cada conjunto de semáforos apenas é utilizado um semáforo.

 

 

 

2 - Faça um programa que:

 

 

 

3 - Faça um programa que:

Nota: utilize semáforos para resolver o problema. Também pode fazer o programa de remoção de linhas, ou seja, o programa que liberta recursos.

 

 

 

4 - O mesmo que o exercício anterior, mas agora cada processo pode escrever 3 linhas no ficheiro de texto.

 

 

 

5 - Faça os seguintes programas:

 

Leitor

 

Escritor

Execute vários leitores e escritores para testar a funcionalidade do programa desenvolvido.

 

 

 

 

6 - Considere uma ponte onde em cada instante apenas é possível a passagem de automóveis num sentido (AB ou BA). Na ponte podem estar 15 automóveis, sendo que cada automóvel antes de entrar na ponte tem de cumprir os seguintes requisitos:

 

- Só pode entrar na ponte se ainda não estiverem lá 15 automóveis e seja esse o sentido actual dos carros que estão a percorrer a ponte. Se já estiverem na ponte 15 automóveis e for esse o sentido actual da ponte, então deve esperar que um dos automóveis saia da ponte, assegurando que vai para a ponte na sua vez.

 

Para testar o impacto no tráfego, você foi encarregado em desenvolver os programas que permitam a simulação desta situação.

 

Nota 1: O comportamento dos automóveis é efectuado através dos programas SENTIDO_AB e SENTIDO_BA. Considere que esses programas vão ser executados pelo programa gera_teste que é responsável por lançar novos automóveis na simulação.

 

Nota 2: Cada "automóvel" deve apresentar no monitor o tempo que demorou a atravessar a ponte.

 

Nota 4: implmente também o programa gera_teste que é responsável por lançar novos automóveis na simulação.