Introdução aos Sistemas Operacionais Threads O que é uma Thread?

Propaganda
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Introdução aos Sistemas
Operacionais
Threads
Eleri Cardozo
FEEC/Unicamp
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
O que é uma Thread?
Uma thread (linha de controle) é uma unidade de execução e de
controle, alocação e compartilhamento de recursos interna ao
processo.
Processo
Threads
Contexto
(threads)
Contexto
(processo)
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Motivações para Threads
Minimizar a troca de contexto causada por processos que
bloqueiam.
●
●
Facilitar a implementação de programas concorrentes:
- Grau mais fino de concorrência.
- Facilidade de comunicação e sincronização inter-threads.
- Troca de contexto mais rápida que processos (e sem
influênciar o processo de paginação).
- Criação e finalização mais rápida que processos (idem).
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Modelos de Threads
Modelos de threads ditam como as threads se relacionam com o
núcleo do sistema operacional.
Quando o núcleo do sistema operacional não provê suporte a
threads, é possível implementar threads no espaço do usuário por
meio de bibliotecas específicas (por exemplo, pth - GNU Portable
Threads). Este cenário é raro na atualidade.
Quando o núcleo do sistema operacional provê suporte a threads,
é possível utilizar threads por meio de chamadas de sistema
específicas (por exemplo, pthreads - POSIX Threads). Este
cenário é o mais comum na atualidade.
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Modelos de Threads
Problemas com threads no espaço do usuário:
Apenas uma thread executa por vez (não tira proveito de
processadores multicore).
●
Não é possível interromper a execução de uma thread (a
biblioteca de threads não pode definir um manipulador de
interrupção de relógio !)
●
Todas as chamadas bloqueantes devem ser redefinidas
pela biblioteca de threads (pth define apenas um subconjunto).
●
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Exemplo: POSIX Threads
#include <pthread.h>
// definicao de thread
void* thread1(void *p) {
printf("Thread recebeu parametro: %s\n", (char *)p);
doWork(p);
}
// programa principal
main() {
pthread_t pth;
pthread_create(&pth, NULL, thread1, "Alo thread");
pthread_join(pth, NULL); // espera thread terminar
}
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Comunicação Inter-threads
Como threads compartilham a área de dados do processo, a
comunicação inter-threads por memória compartilhada é a mais
natural. Este mecanismo não requer a intermediação do sistema
operacional, exceto para fins de sincronização.
Pilha
área compartilhada
Dados
Thread 1
Texto
Thread 2
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Sincronização Inter-threads
Mutexes: semáforos binários (inicializados em 1).
Variáveis de condição: permitem o compartilhamento de
mutexes com o objetivo de evitar espera ocupada (polling).
Tal como os mecanismos de sincronização inter-processo,
mutexes e variáveis de condição devem ser providos pelo
núcleo do sistema operacional.
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Sincronização Inter-threads
Exemplo do uso de variáveis de condição: problema
produtor-consumidor.
Thread mestre (produtora)
Mutex
Buffer
?
Variável de condição
Threads trabalhadoras (consumidoras)
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Sincronização Inter-threads
Implementação (ineficiente !) das threads consumidoras:
#include <pthread.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
while(1) {
// espera ocupada
pthread_mutex_lock(&m);
if(buffer.size == 0) {
pthread_mutex_unlock(&m);
usleep(10000);
} else break;
}
// do work
pthread_mutex_unlock(&m);
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Sincronização Inter-threads
Implementação (eficiente !) das threads consumidoras:
#include <pthread.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
pthread_mutex_lock(&m);
if(buffer.size == 0) pthread_cond_wait(&c, &m);
// do work
pthread_mutex_unlock(&m);
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Sincronização Inter-threads
Implementação (eficiente !) da thread produtora:
#include <pthread.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
pthread_mutex_lock(&m);
// do work
pthread_cond_signal(&c, &m);
pthread_mutex_unlock(&m);
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Escalonamento de Threads
O escalonamento de threads é idêntico ao de processos, ou
seja, cada thread tem seu contexto salvo/restaurado quando a
thread perde/recupera a CPU.
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Escalonamento de Threads
Escalonamento não preemptivo (threads no nível do usuário):
threads executam até bloquear ou terminar.
Escalonamento preemptivo (threads no nível do núcleo):
threads são escalonadas por time-sharing ou por prioridades.
Tal como processos, Linux suporta escalonamento de threads
por prioridades tipo FIFO ou RR (Round Robin).
Os sistemas operacionais atuais escalonam threads, ou seja,
o processo perde a CPU quando expira o quantum de CPU ou
quando não existe nenhuma se suas threads no estado de
pronto.
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Escalonamento de Threads
#include <pthread.h>
main() {
pthread_t pth;
pthread_attr_t ta; // atributos de escalonamento
struct sched_param sp; // pars. de escalonamento
// obtem a maxima prioridade que uma thread pode ter
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
// define parametros de escalonameento
pthread_attr_init(&sp);
pthread_attr_setinheritsched(&ta, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&ta, SCHED_FIFO);
pthread_attr_setschedparam(&ta, &sp);
// cria thread com prioridade explicita
pthread_create(&pth, &ta, thread1, "Alo thread");
pthread_join(pth, NULL); // espera thread terminar
}
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Threads e a Chamada fork
O que acontece quando um programa que iniciou múltiplas
threads executa a chamada fork?
(a) O processo filho inicia com todas as threads em execução
no processo pai.
(b) O processo filho é criado apenas com a thread principal
(função main).
No Unix ocorre a alternativa (b). Pior ainda, os mutexes e as
variáveis de condição no processo filho assumem um estado
indefinido.
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Threads em Sistemas
de Tempo Real
Em sistemas de tempo real deve-se evitar a criação e
destruição frequente de threads durante a execução
do sistema (o overhead é baixo mas não desprezível).
É preferível a criação de um pool de threads na
inicializaçao e, eventualmente, um ajuste do tamanho
do pool durante a execução do sistema.
As threads do pool são sincronizadas com mutexes e
variáveis de condição.
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Exemplo: Servidor Multithreaded
EA876 - Prof. Eleri Cardozo - FEEC/Unicamp
Atividades Práticas
A biblioteca pthread
Criação de threads
Mutexes
Variáveis de condição
Download