sincronização com posix threads sistemas embutidos 2004
TRANSCRIPT
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)