Post on 16-Aug-2020
Sistemas Operacionais
Programação ConcorrenteIntrodução
Edson Morenoedson.moreno@pucrs.br
http://www.inf.pucrs.br/~emoreno
Introdução Programa Seqüencial Representado por apenas um processo Existe apenas um fluxo de execução
Programas concorrentes Representado por dois ou mais processos que coexistem em um dado momento Se há cooperação, exige interação entre processos para sincronização Descrição reflete em múltiplos fluxos possíveis de execução
Paralelismo Garantido em nível de hardware Paralelismo real Ocorre em plataformas que garantem mais de um elemento de
processamento Paralelismo aparente Ocorrem em máquinas monoprocessadas, o que é emulado pelo sistema
operacional
Programação Concorrente Um programa descrito de forma concorrente possui
Um conjunto de processos de execução seqüencial
Processos executados de forma concorrente
Concorrência:
Tradicionalmente significa: Competir por algo (recurso)
Alternativamente significa: Cooperar por algo (fim comum)
Programação Concorrente Objetivos da exploração de paralelismo
Reduzir o tempo total de processamento
Execução em múltiplos processadores
Aumentar confiabilidade e disponibilidade
Emprego de processadores distribuídos
Obter especialização de serviços
Sistemas operacionais
Implementar aplicações distribuídas
Correio eletrônico
Desvantagens Programação Complexa
Paradigma de codificação visando paralelismo deve ser levado em conta
Erros de programação
Adiciona-se erros intrínsecos ao modelo
Velocidade distinto para os processos de um programa concorrente
Aspectos não determinísticos
Difícil depuração
Fluxo de Execução
Execução seqüencial Controle de fluxo de execução Seqüencial Condicional Iterativo
Requisição de execução explícita:chamada de métodos implícita: ativação de exceções
Ordem de execução Controlado pela aplicação
Execução concorrente
Processos são unidades autônomas
Exemplo: threads
Processos podem ser independentes
E.g. execução de mesmo método por dois objetos
Processos podem necessitar comunicação
Exigirá mecanismo de compartilhamento de dado
Ordem de execução
Programa não controla
Fluxo de Execução
Vários fluxos de execuçãoFluxo único de execução
tarefa 1
tarefa 2
tarefa 3
tarefa 1 tarefa 2 tarefa 3
cada fluxo possui uma pilha de execução
Criação de Processos
Processos podem executar a criação de mais processos
Cria o conceito de árvore de processos
Criação realizada a partir de chamadas de sistemas
Processos executam em “paralelo”
A não ser que o processo pai queira (explicitamente esperar pelo filho)
Comandos de criação de processos UNIX:
Fork()
Windows
CreateProcess()
Criação de Processos
Inter-process Communication – IPC
Comunicação entre processos
Pipes, message queues, sockets
Controle de processos Alguns comandos Wait: Força que o processo pai aguarde o fim da execução do processo filho Exit: Força a finalização do processo que o chamou Sleep: Força um processo a ser suspenso por um tempo determinado
Exemplo:#include <stdio.h>
#include <unistd.h>
int main (){
int pid, status;
if(fork()) {
printf(“Processo pai: aguardando pelo processo filho”);pid = wait(&status);printf(“Pai:n – O PID do meu filho eh (%d) e seu status eh (%d)", pid, status);
}
else{
printf(“Processo Filho -- dormindo");
sleep(1);
printf(“Processo filho -- finalizando");exit(0);
}
printf(“Fim de execucao (%d)“, getpid());
}
Controle de processos Alguns comandos
Kill: Força a finalização de todos processos associados ao processo que chamou
Ver exemplos de código
Exemplo:
#include <stdio.h>#include <sys/types.h>#include <signal.h>int main (){int id;id = fork();if (id != 0){printf (“Eu sou o pai\n”);sleep(5);kill(id,SIGKILL);
}else{printf (“Eu sou o filho\n”);while(1);
}}
Single Thread
MS-DOS suporta um único
processo de usuário e uma
única thread.
Alguns UNIX, suportam
múltiplos processos de
usuário mas com apenas uma
thread por processo
Multithreading
JRE é um processo único com
múltiplas threads
Múltiplos processos E threads são
encontrados em Windows, Solaris,
e muitos sistemas modernos
UNIX
Processos e Threads Pense nos processos como se englobassem duas características:
Alguns SOs suportam múltiplas threads em um único processo.
Processos leves também são escalonados
A unidade que contém o domínio de recurso é conhecida como processo/tarefa
Domínio de recurso Execução/Escalonamento
Um processo inclui um espaço de
endereçamento virtual para guardar a imagem
do processo: uma coleção da pilha com os dados
do programa e atributos
Execução de um processo segue um caminho de
execução intercalado com outros processos
Threads vs processos Independente do processo, cada thread tem:
Um estado de execução (running, ready, etc.)
Contexto de thread salvo quando não executando
Uma pilha de execução
Espaço para variáveis locais estáticas
Acesso à memória e recursos do processo ao qual pertence (todas as
threads do processo compartilham isso)
Threads vs processos
Threads vs processos
Relação entre um processo e suas threads
Ações aplicadas a um processo pai afetem sua threads
Thread somente é swapped out se o processo pesado for
O OS deve gerenciar isso no nível de processo
Ex.:
Suspender um processo suspende todas as suas threads
Terminar um processo termina todas as suas threads
Processos leves (Threads) Threads
Mais leves do que processos
Tem menos informações para salvar e restaurar para troca de contexto
Podem comunicar-se através de variáveis globais ao processo
Principal diferença entre processos e threads
Espaço de endereçamento
Processos
Espaço de endereçamento único por processo
Variáveis distintas para cada processo
Threads
Trabalham no mesmo espaço de endereçamento de um processo pai
Compartilham as mesmas variáveis de um processo
Processos leves (Threads)
Vantagens quando comparada a processos
Menor tempo de criação, término e chaveamento entre threads
Contexto de software é o mesmo do processo original
Diferença está no grupo de informações referentes ao hardware
Comunicam-se sem interferência do kernel
Exploram o mesmo espaço de endereçamento na memória
Implementação de threads
User Level Thread (ULT)
Kernel level Thread (KLT) ou:
kernel-supported threads
User-Level Threads
Núcleo do sistema trata somente processos pesados
Gerenciamento das threads realizada pela aplicação do usuário
Programador responsável por
Definir o modo de escalonamento
Tratar os estados da thread
Requer uma biblioteca de apoio
Ex. Biblioteca Pthreads (Linux)
Código deve dar suporte
Criação / eliminação
Passagem de mensagem entre threads
Escalonar threads
Salvar / restaurar contexto
User-Level Threads
Vantagens
Chaveamento entre threads não envolve kernel
Escalonamento pode ser dependente da aplicação
Pode rodar em qualquer SO
Desvantagens
Maioria das chamadas de sistema são bloqueantes
Ao bloquear o processo pesado, todas threads são bloqueadas
O núcleo somente associa processos pesados a processadores
Duas threads de um mesmo processo não podem rodar em processadores
diferentes
Kernel-Level Threads
Gerência de processos feita no nível de núcleo
Não emprega bibliotecas de threads
Núcleo mantém informações de contexto para processos pesados e
threads
Escalonamento e chaveamento
Realizado no nível de threads
Kernel-Level Threads Vantagens
O kernel pode simultaneamente escalonar múltiplas threads do mesmo
processo em múltiplos processadores
Se uma thread em um processo é bloqueada, o kernel pode escalonar outra
thread do mesmo processo.
Desvantagens
A transferência de controle de uma thread para outra do mesmo processo
requer uma troca de modo para o kernel
ULT são mais rápidas por serem tratadas pelo programador
Programação com Threads POSIX Threads (PTHREADS) Bibliotecas de threads POSIX são um padrão API para threads baseado em C/C++
Permite gerar fluxo de processos concorrentes
Mais eficaz em sistemas multi-processadores ou multi-core Fluxo do processo pode ser programado para executar em outro processador
Permite obter ganho de velocidade através do processamento paralelo ou distribuído.
Ganhos também são encontrados em sistemas com um único processador
Exploração da latência de I/O entre outras que podem parar a execução do processo
Uma thread pode executar enquanto a outra está bloqueada.
Threads mais leves que processos pesados Exigem menos sobrecarga do que "bifurcação (fork)" ou geração de um processo novo,
O sistema não inicializa um espaço de memória virtual novo para o processo
Todos as threads dentro de um processo compartilham o mesmo espaço de endereço.
Programação com Threads Codificação
PTHREAD_CREATE
Cria uma thread associada a um processo
Dispara sua execução
PTHREAD_JOIN
Faz com que o processo criador aguarde uma
sinalização de fim de execução da thread
Threads
Criadas a partir de funções
Variáveis locais acessíveis por uma única
thread
Variáveis globais acessíveis por todas as
threads
#include <stdio.h>
#include <pthread.h>
void *Thread0() {
int i;
for(i=0;i<100;i++)
printf(“Thread0 = %d\n”, i);
}
void *Thread1() {
int i;
for(i=100;i<200;i++)
printf(“Thread1 = %d\n”, i);
}
main(){
pthread_t t0, t1;
pthread_create( &t0, NULL, Thread0, NULL);
pthread_create( &t1, NULL, Thread1, NULL);
pthread_join( t0, NULL);
pthread_join( t1, NULL);
printf(“Main…\n”);
}
Programação com Threads Criação de uma Thread
int = pthread_create(pthread_t *thid, const pthread_attr_t
*atrib, void *(*funcao), void *args);
pthread_t *thid: Identificador thread
const pthread_attr_t *atrib: Escalonamento (null = default),
void *(*funcao): Função que a implementa,
void *args: Ponteiro para os parâmetros a serem passados para a thread
O processo de criação pode
Falhar Thread não é criada
Sinais e seus significados:
EAGAIN: O sistema sem recursos necessários para criar a nova thread
EFAULT: O nome da função ou attr não é um ponteiro válido
EINVAL: attr (atrib) não é um atributo inicializado
Ocorrer com sucesso Retorna zero (0)
Programação com Threads
Vinculação entre o processo e suas threads
pthread_join (pthread _thid, void **args);
Bloqueia até que a thread _thid termine
Thread pode retornar valores em args
0, sucesso
< 0, caso de falha
Valor de retorno, calculado pela thread
Caso não desejar valor de retorno, passar NULL