2/3
Apresentação do Curso
4/3
- visões do sistema operacional:
- interpretadores de comandos
- chamadas do sistema
Processos
- o modelo de processos
- estado: contador de programa, registradores, pilha, etc
- multiprogramação e troca de contexto
- estados e transições
Criação de novos processos
- a primitiva fork do Unix (e minix)
- hierarquias de processos
referência: Tanenbaum, 2.1, 2.1.1,
2.1.2
9/3
Processos
Criação de novos processos (cont)
- primitiva fork do Unix (e minix)
- primitiva exec
- primitiva wait
Sinais
referência: Tanenbaum, 4.7.7
obs: A discussão das primitivas fork, exec
e wait não consta no livro texto.
A documentaçAo dessas primitivas pode ser obtida através das páginas man
de qualquer Unix, Linux ou minix.
11/3
Processos
Threads ou Processos Leves
- motivação:
- concorrência com menos custo para o sistema (troca de contexto, etc)
- necessidade de compartilhamento de dados globais
- threads como parte do kernel e como bibliotecas de usuário
Escalonamento de Processos
- introdução: objetivos do escalonamento
- (1) escalonamento por rodízio
- (2) escalonamento por prioridades
- prioridades estáticas
- prioridades dinâmicas
- prioridades recalculadas conforme comportamento
do processo
- processos I/O-bound e CPU-bound
referência:
Tanenbaum, 2.1.3, 2.4, 2.4.1, 2.4.2.
16/3
Processos
Escalonamento de Processos (cont)
- (3) escalonamento em sistemas de tempo real
- (4) escalonamento em dois níveis
Comunicação e Sincronização entre Processos
Comunicação por memória compartilhada
- como ocorre
- problemas de inconsistência:
- sequência de execução de instruções de processos distintos
é imprevisível
- solução com espera ocupada
referência:
Tanenbaum, 2.4.7, 2.4.8, 2.2, 2.2.1, 2.2.3.
18/3
Processos - Comunicação e Sincronização entre Processos
Comunicação por memória compartilhada (cont)
- revisão das soluções com espera ocupada e alternância estrita
- necessidades: atomicidade e bloqueio
- proposta Dijkstra, 1965: SEMÁFOROS
Semáforos
- definição: operações P() e V()
- exemplo de uso: produtor-consumidor
- semáforo binário para exclusão mútua
- semáforo contador para evitar a espera ocupada por itens
referência:
Tanenbaum, 2.2.3, 2.2.4 (só a definição do problema produtor-consumidor),
2.2.5.
23/3
Processos - Comunicação e Sincronização entre Processos
Comunicação por memória compartilhada (cont)
Semáforos (cont)
- o problema dos filósofos
- deadlock e starvation
- exercícios
25/3
Processos - Comunicação e Sincronização entre Processos
Comunicação por Troca de Mensagens
- primitivas send e receive
- envio síncrono (bloqueante) e assíncrono (não bloqueante)
- bufferização de mensagens
- recebimento síncrono e assíncrono
- casos em que o recebimento assíncrono faz sentido
- identificação do destinatário
- pid
- caixas de correio
Comunicação no Unix/Minix: pipes
- canal de leitura e escrita
- sequências de bytes sem fronteiras explícitas
- escrita é atômica e assíncrona a menos que pipe esteja cheio
(capacidade é pelo menos 4096 bytes)
- leitura é síncrona: só retorna quando há algo a ser lido
- pode ler menos bytes do que o esperado!
referência:
Tanenbaum, 2.2.7;
Stevens,
3.3, 3.5.
30/3
Processos - Comunicação e Sincronização entre Processos
Comunicação no Unix/Minix: pipes (cont.)
- exemplo de código utilizando pipes
- estruturas comuns no uso de pipes
Problema dos Leitores e Escritores
Suponha que diversos processos tentam continuamente realizar
acessos a uma base de dados, algumas vezes para realizar
operações de leitura e outras para realizar operações de escrita.
A escrita requer acesso exclusivo à base de dados.
Diversas leituras podem ocorrer simultaneamente.
Como modelar este comportamento com memória compartilhada?
- solução apresentada no livro: possibilidade de starvation
de escritores
referência:
Tanenbaum, 2.3.2.
6/04
8/04
Semáforos
Revisitando alguns problemas
- Problema dos Filósofos
- uso de um semáforo para cada garfo
- possibilidade de deadlock se cada filósofo
pega, por exemplo, primeiro o garfo a esquerda e
depois o da direita
- solução: um deles deve pegar os garfos na
ordem contrária
void filosofo(i) {
while(TRUE) {
pensa();
requisita_garfos(i);
come();
libera_garfos(i);
}
}
void requisita_garfos(int i) { void libera_garfos(int i) {
P(menor_garfo(i)); V(maior_garfo(i));
P(maior_garfo(i)); V(menor_garfo(i));
} }
Outra forma de entender a solução é ordenar os
recursos utilizados e exigir que cada processo
demande os recursos em ordem crescente!
- Problema do Buffer Limitado
O mesmo problema do produtor-consumidor visto antes,
mas agora o buffer tem um número máximo P de posições.
Caso o buffer esteja cheio o produtor não pode depositar
um item.
Como fazer para o produtor não ficar em espera ocupada
neste caso?
Construção de uma barreira de sincronização
Em muitas aplicações que envolvem diversos processos executando
o mesmo código (chamadas single program multiple data), há alguns
pontos onde cada processo só deve prosseguir quando todos os demais
tiverem atingido o o mesmo ponto de execução.
Como programar uma primitiva barreira() que bloqueie
um processo até que todos os demais chamem a mesma
primitiva?
- com dois processos, processo 0 e processo 1:
sem barr[2]; barr[0].valor=0; barr[1].valor=0;
void barreira(int i) {
/* i é o número do processo! */
V(barr[1-i]); /* avisa que chegou */
P(barr[i]); /* espera o outro chegar */
}
- com n processos:
- uso de um processo mestre para coordenar a barreira
sem barr[n]; barr[0].valor=0; ... barr[n-1].valor=0;
sem mestre; mestre.valor=0;
no mestre:
while(1) {
for (i=0;i<n;i++) P(mestre);
for (i=0;i<n;i++) V(barr[i]);
}
em cada trabalhador:
void barreira(int i) {
/* i é o número do processo! */
V(mestre);
P(barr[i]);
}
referência:
Tanenbaum, 2.2.5 (buffer limitado), 2.3.1 (outra solução para
o problema dos fliósofos: ESTUDAR!).
13/04
Monitores
- anos 70: suporte das linguagens de programação à construção de Tipos Abstratos de Dados
- encapsulação de dados e rotinas
- Propostas independentes de Brinch-Hansen e Hoare: monitores
- rotinas (pontos de entrada) do monitor têm garantia de execução em exclusão mútua.
- para sincronização: variáveis de condição que podem ser operadas
com wait() e signal()
- wait(cond) bloqueia o processo corrente incondicionalmente,
colocando-o na fila associada a cond
- signal(cond) acorda um processo na fila associada a cond, se houver algum.
- A execução de um wait libera o monitor para a entrada
de outros processos.
- escalonamento: Supõe-se que nenhum novo processo vai conseguir entrar
no monitor enquanto houver algum processo pronto dentro fo monitor.
Disciplinas de Sinalização
A execução de um signal é uma indicação para outro processo de que uma
condição que ele estava esperando se tornou verdadeira.
No entanto, pode ser que até que ele entre em execução realmente a condição
tenha sido novamente alterada...
Diferentes disciplinas de sinalização lidam de formas distintas
com esse problema:
- Signal and Exit (SX): O processo que sinaliza é obrigado a sair do
monitor imediatamente, ie, o signal tem que ser sempre a última
coisa a ser executada.
- Essa foi a disciplina proposta por Brinch-Hansen
- Signal and Urgent Wait (SU): O processo que sinaliza fica
bloqueado até que o processo sinalizado saia do monitor ou
execute outro wait.
- Essa foi a disciplina proposta por Hoare
- Signal and Continue (SC ou "signal como dica"): O processo
que sinaliza continua executando sem restrições dentro do
monitor. O processo sinalizado meramente passa para a fila
de prontos; ao voltar a execução, tipicamente ele deve
novamente testar se a condição sinalizada ainda é verdade.
- Essa é a disciplina utilizada em Java
- linguagens que usam essa disciplina normalmente oferecem
uma operação SignalAll() sobre uma variável de sincronização,
que acorda todos os processos bloqueados na fila dessa variável!
Só faz sentido discutir se determinada solução por monitor funciona
ou não se soubermos qual a disciplina de sinalização utilizada!
Exemplo de Utilização de Monitores: Leitores e Escritores
A idéia é programar um monitor ControlaBase com operações Pede_leitura,
Pede_escrita, Libera_leitura e Libera_escrita,
que devem ser chamadas por processos leitores e escritores antes
e depois do acesso a base, respectivamente.
Observe que pareceria mais elegante definir um monitor
Base com operaçãoes Leitura e Escrita.
Por que isso é complicado???
Uma primeira tentativa de solução é pegar a solução do livro
(Figura 2.19) e transpor diretamente os trechos protegidos
por P(e) e V(e) (no livro, down(&mutex) e up(&mutex)) para
rotinas do monitor:
monitor ControlaBase {
cond db;
int leitores=0, escritores=0;
void pede_leitura() {
leitores++;
if (leitores==1) wait(db);
}
...
}
Essa solução NÃO vai funcionar!!!!!!!
Variáveis de condição, ao contrário de semáforos, não têm valor.
Assim, se logo de início um leitor executar Pede_leitura,
ele vai ficar bloqueado (incondicionalmente!) ao executar wait!!!
referência:
Tanenbaum, 2.2.6.
15/04
Monitores (cont.)
Soluções para o problema dos leitores e escritores
- Solução com a disciplina SC (signal&continue)
monitor ControlaBase {
cond okLeitura, okEscrita;
int leitores=0, escritores=0;
void pede_leitura() {
while (escritores) wait(okLeitura);
leitores++;
}
void pede_escrita() {
while (leitores || escritores) wait(okEscrita);
escritores++;
}
void libera_leitura() {
leitores--;
if (!leitores)
signal(okEscrita);
}
void libera_escrita() {
escritores--;
signal(okEscrita);
signalAll(okLeitura);
}
- Essa solução possibilita starvation de escritores!
- exercício: A solução funciona se for usada uma única variável de condição
para todos os wait e signal?
- Solução com disciplina SX:
Só pode haver uma sinalização em cada rotina, pois a sinalização
necessariamente é a última ação de um processo dentro do monitor!
monitor ControlaBase {
cond okLeitura, okEscrita;
int leitores=0, escritores=0, esp_leit=0, esp_escrita=0;
void pede_leitura() {
if (escritores) { /* teste possibilita starvation de escritores! */
esp_leit++;
wait(okLeitura);
esp_leit--;
}
leitores++;
if (esp_leit) signal(okLeitura); /* acorda eventuais colegas de fila */
}
void pede_escrita() {
if (leitores || escritores) {
esp_escrita++;
wait(okEscrita);
esp_escrita--;
}
escritores++;
}
void libera_leitura() {
leitores--;
if (!leitores)
signal(okEscrita);
}
void libera_escrita() {
escritores--;
if (esp_escrita) signal(okEscrita); /* aqui prioridade para escritores */
else signal(okLeitura); /* vai acordar só 1 leitor! */
}
}
- Essa solução ainda possibilita starvation de escritores,
mas pode ser alterada para evitar esse problema!
20/04
Deadlocks
- Deadlock: Um conjunto de processos onde cada processo está
bloqueado esperando por um evento que só pode ser gerado por outro
processo do mesmo conjunto.
- A espera pode ser motivada por uma requisição de recurso qualquer;
além de bloqueios por semáforos, um processo pode ficar bloqueado
a espera de uma mensagem ou a espera de um recurso qualquer, como
um lock de arquivo, uma página de memória, etc.
Condições para ocorrência de deadlock
Grafo de Alocação de Recursos
Formas de Tratamento
Detecção
Prevenção Estrutural
A idéia é negar uma das quatro condições necessárias
para ocorrência do deadlock!
- negação da exclusão mútua
- negação do hold and wait
- alocação de recursos em bloco
- negação da espera circular
- ordenação de recursos e alocação em ordem crescente
- negação da não preempção
- apenas em contextos específicos: checkpoints e estados consistentes
Prevenção Dinâmica
- Com uma unidade de cada recurso, o sistema operacional
pode manter o grafo de alocação de recursos e, antes
de cada alocação, verificar se ela causaria um ciclo no grafo.
- Essa técnica não funciona com mais de uma unidade de
cada recurso...
- Regiões de execução inseguras e o algoritmo do banqueiro
Algoritmo da Avestruz
referência: Tanenbaum, 3.3.
22/04
Transações Atômicas
propriedades:
- atomicidade
- serialização
- permanência
Atomicidade
- por cópias (de arquivos ou páginas)
- por log
Serialização
Nada se pode afirmar sobre a ordem em que duas transações
concorrentes serão executadas, mas o efeito das transações deve
ser coerente com a término completo de cada transação antes
do início da próxima.
Formas de garantir a serialização
- mais comum: uso de locks
- garantia de execução eventual
- possibilidade de deadlocks
27/04
aula de dúvidas
29/04
Prova 1
4/05
correção da prova
6/05
Gerência de Memória
endereços físicos e endereços lógicos ou virtuais
amarração de endereços lógicos (do programa executável)
com físicos (da memória principal)
- em tempo de compilação
- usado em arquiteturas antigas, onde o programa
era sempre carregado na mesma posição da memória
- só faz sentido com monoprogramação
- em tempo de carga
- executável contém mapa de relocação
- incompatível com técnicas como swapping
- em tempo de execução (necessidade de hardware especial)
- exemplo: registrador base cujo valor é sempre somado a endereço lógico
Swapping
Controle de Espaço Livre
- blocos de tamanho variável: listas encadeadas
- blocos de tamanho fixo: bitmaps
- fragmentação externa
Segmentação
- espaço lógico do programa é dividido em unidades
que têm sentido para o programa: segmentos
- cada segmento pode ser alocado na memória física de forma independente
Paginação
- páginas físicas (frames) e páginas lógicas (pages)
- mapeamento de endereços: tabela de páginas
- fragmentação interna
referência: Tanenbaum, 4.1, 4.2, 4.3.1.
11/05
Gerência de Memória (cont.)
Paginação
- tabela de páginas como conjunto de registradores na MMU
- tabela de páginas na MP e registrador com endereço da tabela na MMU
- necessidade de atualizar as informações na MMU na troca de contexto
- cache da tabela de páginas: TLB
- influência de acessos à tabela de páginas no tempo de acesso à memória
- paginação em vários níveis
- segmentação com paginação
Memória virtual
- permite que o programa de usuário utilize um
espaço de endereçamento virtual maior que a memória
física total.
- parecido com swapping mas...
- muito mais eficiente
- Com swapping, o espaço de endereçamento de cada
processo tem que ser menor que a MP; o que pode ser maior que a
MP é a soma dos espaços de endereçamento de todos os processos.
- Apenas algumas das páginas virtuais têm um correspondente físico
em cada instante; as demais páginas são marcadas
como inválidas na tabela de páginas.
- Quando um programa faz um acesso a uma página que não
está carregada na MP, a MMU gera um trap para
o sistema operacional, que entra em ação para
carregar a página requisitada.
referência: Tanenbaum, 4.3.2, 4.3.3, 4.3.4.
13/05
nao houve aula
18/05
Gerência de Memória (cont.)
Memória Virtual (cont.)
- revisão do tratamento de um page fault
- necessidade de encontrar uma página física livre
- alocação de páginas global e local
Algoritmos de Substituição de Páginas
- o algoritmo usado deve minimizar a taxa de page faults...
- algoritmo ótimo: escolha da página que levará mais tempo para ser
referenciada
- impossível de implementar mas bom como referência para outros algoritmos
- algoritmo FIFO
- algoritmo LRU
- algoritmo NRU
- diferentes algoritmos exigem diferentes tipos de suporte de hardware
- algumas contabilidades podem ser feitas por software...: uso
de bits de proteção na tabela de páginas (ler-somente e bit de validade) para
realizar controle de utilização de páginas por software
referência: Tanenbaum, 4.4.1,
4.4.2, 4.4.4, 4.4.5, 4.4.6, 4.4.7.
20/05
Memória Virtual (cont.)
Thrashing
- se o nível de multiprocessamento é muito alto,
cada processo pode ficar com poucas páginas, e o sistema
pode entrar em um estado onde a maior parte do tempo
é gasto com tratamentos de page faults: thrashing
- técnicas para evitar thrashing utilizam o conceito
de conjunto de trabalho.
- O conjunto de trabalho de um processo é o conjunto
de páginas que ele continuamente referencia durante certo
período de sua execução.
- O sistema operacional deve tentar garantir que esse
conjunto permaneça carregado em memória física.
discussão sobre tamanho da página
- páginas grandes: fragmentação interna (memória desperdiçada
dentro de páginas alocadas a processos)
- páginas pequenas: muitas pgae faults
Segmentação com Paginação
referência: Tanenbaum, 4.5.1, 4.5.3, 4.6.
25/05
Memória Virtual (cont.): exemplo - System V
27/05
Sistemas de Arquivos
Visão do Usuário (programador)
arquivos
- nomes
- tipos
- estrutura interna
- acesso sequencia e aleatório
- atributos
- operações típicas
diretórios
- hierarquias: árvores e grafos genréricos
- compartilhamento de arquivos entre diretórios: links
Implementação
implementação de diretórios
descricao de localização de arquivos
- arrays
- listas encadeadas
- listas encadeadas com números de blocos: DOS
- arrays com indireção: Unix
referência: Tanenbaum, 5.1, 5.2.1,
5.3.1, 5.3.2.
1/06
nao houve aula - entrega do trabalho
3/06 - feriado
8/06
Implementação de sistemas de arquivos (cont.)
Gerência de espaço livre
- listas encadeadas
- bit maps
Consistência
- procedimentos de verificação de consistência (uso
de informações redundantes)
- exemplo Unix: verificação de blocos livres e ocupados
Desempenho
- uso de pools de buffers (cache) para manter cópias
de blocos de disco em memória principal
- técnicas de substituição: inadequação de NRU puro
- write-through (DOS)
- sync (Unix)
referência: Tanenbaum, 5.3.3, 5.3.4, 5.3.5.
10/06
Segurança em sistemas de arquivos
- parte do sistema onde característica multi-usuário tem mais impacto...
- necessidade de autenticar usuário e proteger recursos contra uso indevido
- autenticação: perigos do uso de senhas
- esquemas de proteção: listas de direitos de acesso por recurso
e direitos de acesso por usuário (capabilities).
- exemplo: o esquema de proteção do Unix
- bits de proteção ler, escrever e executar para categorias
usuário, grupo e outros
- bit setuid
15/06
- discussão sobre buffers internos aos programas
(por exemplo, com uso de bibliotecas de I/O bufferizado)
e buffers do sistema operacional
- custo de chamadas ao sistema operacional!
Descritores de arquivos e u-area
- tabelas de descritores de arquivos
- tabelas de arquivos
- cópias de i-nodes em memória
- efeitos de fork (duplicação da tabela de descritores)
e exec (nenhum)
- a chamada dup
- uso de dup para redireção de entrada e saída
Montagem de Sistemas de Arquivos
- para que serve
- efeitos de um mount:
- marca na cópia em memória do i-node do ponto de montagem
- cópia para memória do i-node correspondente ao
diretório raiz do sistema de aruivos montado
- nova entrada na tabela de montagens, apontando
para os dois i-nodes acima
referência: Tanenbaum, 5.6.6, 5.6.7.
Last update: Tue Jun 15 17:19:29 EST 1999
by Noemi