Sincronização com Posix Threads Sistemas Embutidos 2004.

Post on 17-Apr-2015

129 views 9 download

Transcript of Sincronização com Posix Threads Sistemas Embutidos 2004.

Sincronização com Posix Threads

Sistemas Embutidos

2004

Motivação

Como compartilhar recursos?

Processo 0 Processo 1

Inicio

lê (in)

out = in

imprime (out)

Fim

Inicio

lê (in)

out = in

imprime (out)

Fim

Revisão de Conceitos

Condição de corridaConjunto de eventos que levam a resultados não determinísticos

Região CríticaParte do código que implementa o acesso a um recurso compartilhado

Exclusão mútuaMecanismo que impede o uso simultâneo de um recurso compartilhado

Um problemaconst n=50;var t: integer; procedure total;

var count:integer;begin

for count = 1 to n do t= t + 1

end; 

begint=0;parbegin

total;total;

parend;write ( t );

end.

A: Ra t RaRa+1 t=0 Ra=1B: t 49 Rb49 t=49 Rb=49A: tRa t=1B: Rbt Rb=1A: t50 t=50B: RbRb+1 t2 t=2

 

Semáforos

Solução proposta por Dijkstra em 1965

Principio básico: um processo é suspenso enquanto não obtém permissão para executar uma RC e é “ acordado” através de um sinal

Para enviar um sinal via semáforo s o processo executa uma primitiva signal(s) e para receber um sinal via semáforo s o processo executa um primitiva wait(s)

Threads

Modelo de Processo (únicoThread)

Bloco de

controle de

processo

Espaço de

endereçamento

do usuário

Pilha

Usuário

Pilha

Kernel

Modelo MultiThread

Bloco de

controle de

processo

Espaço de

endereçamento

do usuário

Pilha

Usuário

Pilha

Kernel

Bloco do

controle

Thread

Pilha

Usuário

Pilha

Kernel

Bloco do

controle

Thread

Pilha

Usuário

Pilha

Kernel

Bloco do

controle

Thread

Thread Thread Thread

Threads de Usuário

Escalonamento e sincronização não passam pelo kernelEscalonamento é determinado pelo aplicativoPodem ser usadas em qualquer S.O.

Chamadas ao sistema bloqueiam o processoNão pode fazer uso de mais de um processador

Threads de Kernel

Threads do mesmo processo podem ser executados simultaneamente em processadores diferentes

O bloqueio de um thread não bloqueia as demais threads de um processo

Escalonamento e sincronização são muito custosos

LightWeightProcess

Criação de threads é feita em modo usuário

A maior parte do escalonamento e sincronização acontece em modo usuário

Os threads em modo usuário são mapeadas num número possivelmente menor de threads do kernel

Comunicação entre Threads

• compartilhamento de recursos

• espaço de endereçamento de memória compartilhado

• técnicas de sincronização semelhante as utilizadas em processos

Threads no Linux

• Implementação no kernel através da função clone()

• Utilização da biblioteca Pthreads para garantir a portabilidade

• Em um programa em C, incluir o cabeçalho pthread.h e acrescentar –lpthread na linkedição.

Pthreads

pthread_create(*t, *a, rotina, arg);

t é um ponteiro para uma variável do tipo pthread_t que a é um ponteiro para os atributos. Os atributos são

armazenados em uma variável do tipo pthread_attr_t. Um valor NULL indica o uso de valores default. Para detalhes veja pthread_attr_init(3)

rotina é o nome (ponteiro) para a função que será executada.

arg é um void * que é passado como argumento para rotina.

Pthreads

• A nova thread é disparada imediatamente e termina no retorno da função ou pela chamada da função pthread_exit(*ret)

• O argumento ret aponta uma variável que armazenará o valor de retorno.

• Já que pthread_exit nunca retorna, é de certa forma o equivalente à função exit.

Pthreads

pthread_join(pthread_t id, void **return);pthread_detach(id );

id é identificação da threadreturn é o valor de retorno (pode ser NULL)

Pthreads - Sincronização

Mutex

int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex

Variáveis de condição

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond);

Ex. Calculo de Pi

#include <stdio.h>#include <stdlib.h>#include <pthread.h>volatile double pi = 0.0; pthread_mutex_t pi_lock; volatile double intervals;

void *process(void *arg){ double width, localsum; int i; int iproc = (*((char *) arg) - ’0’); width = 1.0 / intervals; localsum = 0; for(i = iproc; i < intervals; i += 2) { register double x = (i + 0.5) * width; localsum += 4.0 / (1.0 + x * x); } localsum *= width;// Lock & Unlock before accessing PIpthread_mutex_lock(&pi_lock); pi += localsum;pthread_mutex_unlock(&pi_lock);return(NULL);}

int main(int argc, char **argv){pthread_t thread0, thread1;void * retval;intervals = atoi(argv[1]); //intervalospthread_mutex_init(&pi_lock, NULL);if (pthread_create(&thread0, NULL, process, "0") ||pthread_create(&thread1, NULL, process, "1")){fprintf(stderr, "%s: cannot make thread\n", argv[0]);exit(1);}if (pthread_join(thread0, &retval) ||pthread_join(thread1, &retval)){fprintf(stderr, "%s: thread join failed\n", argv[0]);exit(1);}printf("Estimation of pi is %f\n", pi);exit(0);}

Sincronização de Threads

Comunicação Serial

Características

• Um “servidor” pode receber/enviar mensagens através de uma porta serial RS485.

• A comunicação com um “cliente” só pode acontecer quando a linha estiver livre

• O acesso ao meio serial deve ser controlado por mutex

Problema

RespServ

GERENTEContagem

ConvL

armazenamento

Ocorrências de queda ou retorno de fase

Poll

Mensagens não transmitidas ao Servidor

Fast Select Caso servidor não esteja disponível

EnviaMsg

Servidor

Servidor

Programa principal

int main(int argc, char **argv){pthread_t tp;pthread_mutex_t lock;

config_serial();pthread_mutex_init(&lock,NULL); pthread_mutex_lock(&lock); //obtém lockpthread_mutex_init(&serial,NULL);

if (pthread_create(&tp, NULL,(void *)Poll, NULL) ) { perror("Criacao da thread Poll");

exit(1); }while(1);}

Tratamento de relógio

//Inicializa Timervoid init_timer(){ struct sigaction timer; timer.sa_handler = handler_timer; timer.sa_flags = SA_RESTART; sigemptyset(&timer.sa_mask); sigaction(SIGALRM,&timer,

NULL);}

void handler_timer (){int p;p =0;trata_relogio(p);

}

void trata_relogio(int p){struct itimerval valor,ovalor; if (p == 1){ // liga timer valor.it_interval.tv_sec = 30; pthread_mutex_lock(&lock);

}else{ // desliga timervalor.it_interval.tv_sec = 0;valor.it_value.tv_sec=0;pthread_mutex_unlock(&lock); } valor.it_interval.tv_usec = 0; valor.it_value = valor.it_interval; setitimer (ITIMER_REAL, &valor,0);}

Rotina Poll

void Poll() {estado = INICIO_POLL; while (1) {

switch (estado){ case INICIO_POLL:

pthread_mutex_lock(&lock); // fica preso pthread_mutex_lock(&serial); // obtém meio serial

• • •

case FIM_POLL:pthread_mutex_unlock(&serial); // libera meio serialtrata_relogio(1);

}

O que aconteceria ...

• se durante um ciclo, a thread poll atualizasse uma tabela compartilhada na memória?

• se após o ciclo, esta tabela fosse atualizada no disco?

• Se durante o ciclo, uma outra thread retirasse um elemento da tabela?

CUIDADO COM A ORDEM DOS SEMÀFOROS:SUJEITO A DEADLOCK

Exercício

Altere o programa anterior para que os dados obtidos no ciclo de Poll sejam armazenados em um arquivo. Periodicamente este arquivo é lido por um outro thread e enviado via internet para um servidor remoto (simplesmente utilize uma função Envia( ) admitindo que está funcionando corretamente)