LEIC/LERC 2011/12

Sistemas Operativos

Aula 3

Tarefas e Mecanismos de Sincronização

 

Objectivo

 


Introdução

1. Criação de tarefas

a) Copie o ficheiro aula3-eg1.tgz para a sua área de trabalho e descomprima o seu conteúdo:

  tar -zxvf aula3-eg1.tgz

b) Estude, compile e execute a aplicação. Compreenda o resultado (slide).

c) Porque razão é indeterminado o conteúdo da variável global Value?

d) Monte uma experiência que prove a afirmação da alínea c). (sugestão: utilize a chamada sistema sleep e a chamada time para obter variabilidade).

2. Identificação e resolução de problemas de exclusão mútua

a) Copie o ficheiro aula3-eg2.tgz para a sua área de trabalho e descomprima o seu conteúdo:

  tar -zxvf aula3-eg2.tgz

b) Estude, compile e execute a aplicação. Compreenda o resultado (slide).

c) Que valores são possíveis para a variával Value no final da execução? Qual é o problema? Monte uma experiência que prove esta afirmação.

d) Corrija o problema do exemplo anterior utilizando trincos (slide). Verifique que, com esta alteração, a experiência montada na alínea anterior produz um resultado correcto.

e) A solução funcionaria se a variável Mutex fosse local à função thr_func?

Material de Apoio

 


Exercícios

Tarefas Contadoras

Implemente uma aplicação que incrementa concorrentemente uma variável Counter de 0 até 20 utilizando tarefas. Pode utilizar como esqueleto base a estrutura aula3-eg3.tgz. Sugestão: crie versões individuais da solução de cada uma das alíneas.

1. Tarefas concorrentes

a) Utilize duas tarefas, implementadas por duas funções - thr_inc_even e thr_inc_odd. A primeira incrementa o valor se Counter for par, a outra se Counter for ímpar. Sincronize devidamente o acesso à variável partilhada com trincos. As tarefas devem imprimir uma mensagem cada vez que incrementarem a variável partilhada. Por exemplo, o resultado deve ser o seguinte:

        ...
        [E] Counter=11
        [O] Counter=12
        [E] Counter=13
        [O] Counter=14
        ...

b) Generalize o exercício anterior de forma que a variável seja incrementada por N tarefas (N é definido por uma constante). A cada tarefa é atribuída um identificador id (entre 0 e N-1). Cada tarefa só incrementa Counter quando id=Counter%N. Ao invés de E ou O, deve ser impresso a identificação da tarefa que incrementa a variável. Sugestão: utilize apenas uma função thr_incrementer que receba como argumento o identificador da função (exemplo).

i) Faça testes com diferentes valores de N.

ii) Faça experiências inserindo instruções de sleep na secção crítica para confirmar que a sincronização funciona devidamente.

iii) Porque razão se definiu a secção dessa forma? Porque não mais pequena ou maior?

c) Modifique a solução anterior utilizando um semáforo para substituir o trinco (exemplo).

i) Qual o significado do valor 1 na inicialização do semáforo?

ii) Se o valor de inicialização do semáforo fosse diferente de 1, teria um comportamento semelhante ao trinco?

2. Tarefas cooperantes

Utilize agora duas tarefas em cascata - thr_inc_low e thr_inc_high, respectivamente. A primeira incrementa Counter entre 0 e 10, a segunda incrementa Counter entre 11 e 20. Ambas são lançadas simultaneamente, mas a tarefa thr_inc_high deve ficar bloqueada até que a tarefa thr_inc_low termine. Utilize um semáforo para sincronizar as tarefas.

a) Com que valor deve inicializar o semáforo?

b) Qual a função que o semáforo desempenha em termos de sincronização?

c) Neste caso tem algum problema de exclusão mútua que necessita resolver? Porquê?

 


Exercício Suplementar

Produtores-Consumidores

Considere um buffer circular com capacidade para armazenar N itens de informação. O buffer é usado para transmitir informação das tarefas produtoras para as tarefas consumidoras. Em cada acesso ao buffer, cada produtor deposita apenas um item no buffer e cada consumidor retira apenas um item. Quando um produtor tenta introduzir informação no buffer estando este repleto, deve passar ao estado bloqueado; o mesmo deve suceder a um consumidor que tenta extrair um item quando o buffer está vazio. Os processos assim bloqueados devem ser acordados apenas quando o conteúdo do buffer assim o justificar.

Utilize o esqueleto aula3-eg6.tgz como base para a implementação do exercício. Implemente as funções thr_producer e thr_consumer de forma a obedecer a especificação. Utilize semáforos e trincos para sincronização.


Exercício de Avaliação 3

Tarefas Cooperantes e Concorrentes

Combine os exercícios 1 e 2 num único com várias tarefas concorrentes/cooperantes. Cada uma das tarefas deverá incrementar uma variável à vez. Cada uma das tarefas deverá receber o valor mínimo e máximo da variável que os coloca a trabalhar. As tarefas com intervalos de funcionamento sobrepostos serão concorrentes e as com intervalos de funcionamento não sobrepostos serão cooperantes. Apesar da diferença sugere-se que todas as tarefas tenham o mesmo código.

O número de tarefas a lançar bem como os intervalos de funcionamento deverão ser especificados por variáveis como por exemplo.

#define N 3

int limites[N][2] = {{0, 10},{0, 10},{11,20}}

O indentificador de cada uma das tarefas deve ser o indice para o vector de limites descrito acima, sendo que o primeiro elemento de cada um dos tuplos é valor mínimo (inclusivê) em que a respectiva tarefa trabalha e o segundo elemento é o valor máximo (inclusive) em que a tarefa trabalha.

Todas as tarefas são lançadas simultaneamente.

Assume-se que os intervalos do vector de limites são bem formados e que estão por ordem crescente do limite inferior, mas podem existir sobreposições de intervalos.

Sugestões

a) Deverá ser criado um vector de semáforos onde cada tarefa espera que o seu limite inferior seja antingido

b) Depois de uma tarefa se desbloquear no semáforo anterior deverá entrar num ciclo até que o valor do contador exceda o seu limite superior.

c) Caso tenha optado por não proteger o acesso ao contador partilhado no teste da condição anterior deverá repetir o teste dentro do ciclo antes de incrementar o contador

d) Depois do incremento deverão ser acordadas as tarefas em que limite inferior foi atingido.