sistemas_operacionais

87
SISTEMAS OPERACIONAIS JANEIRO 2013

Upload: fabio-alves

Post on 10-Aug-2015

106 views

Category:

Documents


8 download

TRANSCRIPT

Page 1: Sistemas_Operacionais

SISTEMAS OPERACIONAIS

JANEIRO 2013

Page 2: Sistemas_Operacionais

Sistemas Operacionais ii

Sumário

1 Introdução ................................................................................................................... 5

1.1 O Hardware ................................................................................................................................ 5 1.2 O Software ................................................................................................................................. 5 1.3 Máquina de Níveis ..................................................................................................................... 5 1.4 O Sistema Operacional ............................................................................................................... 6

1.4.1 O Sistema Operacional como uma Máquina Estendida ...................................................... 6

1.4.2 O Sistema Operacional como um Gerenciador de Recursos .............................................. 6 1.5 Histórico dos Sistemas Operacionais ......................................................................................... 7

1.5.1 1ª Geração (1945-1955) - válvulas e painéis de conectores ................................................ 7 1.5.2 2ª Geração (1955-1965) - transistores e sistemas de lote .................................................... 7

1.5.3 3ª Geração (1965-1980) - circuitos integrados ................................................................... 8 1.5.4 4ª Geração (1980-hoje) - integração em larga escala .......................................................... 8

1.6 Conceitos de Hardware .............................................................................................................. 8

1.7 Conceitos de Software ............................................................................................................. 10 1.8 Conceitos de Sistema Operacional ........................................................................................... 10

1.8.1 Processos ........................................................................................................................... 10 1.8.2 Arquivos ............................................................................................................................ 11

1.8.3 Interpretador de Comandos (Shell) ................................................................................... 11 1.8.4 Chamadas de Sistema ........................................................................................................ 12

1.9 Tipos de Sistemas Operacionais .............................................................................................. 14 1.9.1 Multiprocessamento .......................................................................................................... 15

1.10 Sistemas Multiprogramáveis .................................................................................................. 16

1.11 Estrutura do Sistema Operacional .......................................................................................... 17 1.11.1 Modos de Operação ........................................................................................................ 17

1.11.2 Ativação e Desativação do Sistema ................................................................................ 17 1.11.3 Sistemas Monolíticos ...................................................................................................... 18

1.11.4 Sistemas em Camadas ..................................................................................................... 18 1.11.5 Máquinas Virtuais ........................................................................................................... 19 1.11.6 Sistemas Microkernel ...................................................................................................... 20

1.12 Exercícios ............................................................................................................................... 20

2 Processos .................................................................................................................. 22

2.1 Mudança de Contexto .............................................................................................................. 23

2.2 Subprocesso e Thread .............................................................................................................. 24 2.3 Tipos de Processos ................................................................................................................... 25 2.4 Processos Cooperativos............................................................................................................ 25 2.5 Exercícios ................................................................................................................................. 26

3 Comunicação Entre Processos.................................................................................. 27

3.1 Condição de Corrida ................................................................................................................ 27

3.2 Região Crítica .......................................................................................................................... 28 3.3 Sincronização Condicional ...................................................................................................... 28 3.4 Exclusão Mútua por Hardware ................................................................................................ 29

3.4.1 Desabilitação de Interrupções ........................................................................................... 29 3.4.2 Instrução TSL (Test and Set Lock) ................................................................................... 29

3.5 Exclusão Mútua por Software com Espera Ativa .................................................................... 30

Page 3: Sistemas_Operacionais

Sistemas Operacionais iii

3.5.1 Alternância Estrita............................................................................................................. 30 3.5.2 Solução de Peterson .......................................................................................................... 31

3.6 Exclusão Mútua por Software sem Espera Ativa ..................................................................... 31 3.6.1 Semáforos.......................................................................................................................... 31 3.6.2 Monitores .......................................................................................................................... 33

3.6.3 Troca de Mensagens.......................................................................................................... 35 3.7 Deadlock .................................................................................................................................. 37

3.7.1 Prevenção de Deadlock ..................................................................................................... 37 3.7.2 Detecção do Deadlock ...................................................................................................... 38 3.7.3 Correção do Deadlock ....................................................................................................... 38

3.8 Problemas Clássicos de Comunicação Interprocessos ............................................................. 38 3.8.1 Problema dos Filósofos Comendo .................................................................................... 38 3.8.2 Problema dos Leitores e dos Escritores ............................................................................ 40 3.8.3 Problema do Barbeiro Adormecido .................................................................................. 40

3.9 Exercícios ................................................................................................................................. 41

4 Gerência do Processador .......................................................................................... 43

4.1 Algoritmos de Escalonamento ................................................................................................. 44 4.1.1 Escalonamento First In First Out (FIFO) .......................................................................... 44 4.1.2 Escalonamento Shortest Job First (SJF)............................................................................ 44

4.1.3 Escalonamento Cooperativo ............................................................................................. 44 4.1.4 Escalonamento Circular (Round Robin) ........................................................................... 44

4.1.5 Escalonamento por Prioridade .......................................................................................... 45 4.1.6 Escalonamento por Múltiplas Filas ................................................................................... 46 4.1.7 Escalonamento por Múltiplas Filas com Realimentação .................................................. 46

4.1.8 Escalonamento em Sistemas de Tempo Real .................................................................... 47 4.2 Escalonamento com Múltiplas UCPs ....................................................................................... 47

4.3 Exercícios ................................................................................................................................. 47

5 Gerência de Memória ............................................................................................... 49

5.1 Atribuição de Endereços .......................................................................................................... 49 5.2 Carregamento Dinâmico .......................................................................................................... 49

5.2.1 Ligação Dinâmica ............................................................................................................. 50 5.3 Alocação Contígua Simples ..................................................................................................... 50 5.4 Alocação Particionada .............................................................................................................. 50

5.4.1 Alocação Particionada Estática ......................................................................................... 50 5.4.2 Alocação Particionada Dinâmica ...................................................................................... 51

5.4.3 Estratégias para Escolha da Partição ................................................................................. 51 5.5 Swapping .................................................................................................................................. 53 5.6 Memória Virtual ....................................................................................................................... 53

5.6.1 Espaço de Endereçamento Virtual .................................................................................... 54 5.6.2 Paginação .......................................................................................................................... 54

5.6.3 Segmentação ..................................................................................................................... 60

5.6.4 Segmentação com Paginação ............................................................................................ 61

5.6.5 Segmentação com Paginação: O Pentium Intel ................................................................ 62 5.6.6 Proteção ............................................................................................................................. 63 5.6.7 Compartilhamento de Memória ........................................................................................ 63 5.6.8 Trashing ............................................................................................................................ 63

5.7 Exercícios ................................................................................................................................. 64

Page 4: Sistemas_Operacionais

Sistemas Operacionais iv

6 Sistema de Arquivos ................................................................................................. 67

6.1 Arquivos ................................................................................................................................... 67

6.1.1 Organização de Arquivos .................................................................................................. 67 6.1.2 Métodos de Acesso ........................................................................................................... 68 6.1.3 Tipos de Arquivos ............................................................................................................. 68 6.1.4 Operações de Entrada/Saída .............................................................................................. 69 6.1.5 Atributos............................................................................................................................ 69

6.2 Diretórios ................................................................................................................................. 69 6.3 Alocação de Espaço em Disco ................................................................................................. 70

6.3.1 Alocação Contígua ............................................................................................................ 71 6.3.2 Alocação por Lista Encadeada .......................................................................................... 71 6.3.3 Alocação por Lista Encadeada Utilizando Índice ............................................................. 72

6.3.4 Nós-I (Alocação Indexada) ............................................................................................... 72 6.4 Exemplos de Implementação de Diretórios ............................................................................. 73

6.4.1 Diretórios no CP/M ........................................................................................................... 73

6.4.2 Diretórios no MS-DOS ..................................................................................................... 73 6.4.3 Diretórios no UNIX .......................................................................................................... 74

6.5 Proteção de Acesso .................................................................................................................. 74 6.5.1 Senha de Acesso................................................................................................................ 74

6.5.2 Grupos de Usuários ........................................................................................................... 74 6.5.3 Lista de Controle de Acesso .............................................................................................. 75

6.6 Implementação de Cache ......................................................................................................... 75 6.7 Exercícios ................................................................................................................................. 75

7 Gerência de Dispositivos .......................................................................................... 77

7.1 O Hardware de Entrada/Saída .................................................................................................. 77 7.1.1 Espera em Ciclo ................................................................................................................ 78

7.1.2 Interrupções ....................................................................................................................... 79 7.1.3 Acesso Direto à Memória ................................................................................................. 79

7.2 Operações de Entrada/Saída ..................................................................................................... 79 7.3 Subsistema de Entrada/Saída ................................................................................................... 80

7.4 Drivers de Dispositivo ............................................................................................................. 80 7.5 Controladores ........................................................................................................................... 81 7.6 Dispositivos de Entrada/Saída ................................................................................................. 82

7.6.1 Discos de RAM ................................................................................................................. 82 7.6.2 Disco Rígido ..................................................................................................................... 82

7.7 Exercícios ................................................................................................................................. 85

8 Bibliografia ............................................................................................................... 87

Page 5: Sistemas_Operacionais

Sistemas Operacionais 5

1 Introdução

1.1 O Hardware

Um sistema computacional moderno consiste em:

1 ou mais processadores;

memória RAM;

disco;

interface de rede;

impressora;

placa de vídeo;

etc.

É extremamente difícil escrever um programa para controlar todos os dispositivos. É preciso

isolar o programador do hardware.

Solução: Colocar uma camada de software por sobre o hardware básico para gerenciar o

sistema e oferecer ao usuário uma máquina virtual.

1.2 O Software

O software permite ao computador recuperar, armazenar e processar informações.

De forma geral pode ser dividido em duas espécies: programas de sistema e programas

aplicativos. Os programas de sistema gerenciam a operação do computador, enquanto os

programas aplicativos executam o trabalho que o usuário deseja.

O sistema operacional é o principal programa de sistema. Ele fornece a base para que

programas aplicativos possam ser escritos.

1.3 Máquina de Níveis

O hardware não tem muita utilidade isoladamente. São os programas (softwares) que realizam

as operações que os usuários desejam. O hardware é o responsável pela execução das instruções do

software para que seja realizada alguma tarefa.

Tanto o hardware quanto o software são logicamente equivalentes, interagindo de forma única

para o usuário.

Nos primeiros computadores, a programação era realizada em painéis, através de fios, o que

era uma grande dificuldade para os programadores da época. A solução foi o surgimento do sistema

operacional, que tornou a interação entre usuário e computador mais simples, confiável e eficiente.

A partir desse acontecimento a parte física do computador tornou-se transparente para o usuário.

Partindo desse princípio, podemos considerar o computador como uma máquina de níveis,

onde inicialmente existem dois níveis: o nível do hardware e o nível do sistema operacional. Assim

o usuário pode enxergar a máquina como sendo apenas o sistema operacional (como se o hardware

não existisse).

Um computador possui tantos níveis quantos forem necessários para adequar o usuário às

suas diversas aplicações. Quando o usuário está trabalhando em um desses níveis, não necessita

saber da existência das outras camadas.

Page 6: Sistemas_Operacionais

Sistemas Operacionais 6

Os programas de sistema que executam acima do Sistema Operacional executam no modo

usuário, enquanto o sistema operacional executa no modo kernel (ou modo supervisor), sendo

protegido do usuário pelo hardware.

A linguagem de máquina não é um hardware propriamente dito, mas considera-se que ela faz

parte do hardware.

O microprograma só existe em máquinas CISC. Ele tem a função de interpretar a linguagem

de máquina.

1.4 O Sistema Operacional

O sistema operacional é um programa que atua como um intermediário entre o usuário e o

hardware. Seu propósito é fornecer um ambiente no qual os usuários possam executar seus

programas e tem como principal objetivo tornar conveniente o uso de um sistema computacional

usando o hardware de maneira eficiente.

O sistema operacional realiza basicamente duas funções sem muita correlação, funcionando

como uma máquina estendida e como um gerenciador de recursos.

1.4.1 O Sistema Operacional como uma Máquina Estendida

A arquitetura da maior parte dos computadores no nível de linguagem de máquina é difícil de

programar (principalmente E/S). Para escrever em um disco, por exemplo, é preciso escolher a

trilha, a cabeça e o setor, formatar, inicializar a controladora e o dispositivo, etc., tudo isto

escrevendo em registradores.

O sistema operacional é o programa que esconde do programador a complexidade do

hardware, fornecendo-lhe uma interface bastante simples.

O sistema operacional também oculta detalhes relacionados a interrupções, temporizadores,

gerenciamento de memória e outros recursos de baixo nível.

Deste ponto de vista, a função do sistema operacional é apresentar ao usuário uma máquina

estendida (ou máquina virtual), que apesar de equivalente ao verdadeiro hardware é mais fácil de

programar. Ele facilita o acesso aos recursos do sistema.

1.4.2 O Sistema Operacional como um Gerenciador de Recursos

Computadores consistem em processadores, memórias, temporizadores, discos, etc. Sob um

ponto de vista alternativo ao anterior, a função do sistema operacional é oferecer um esquema de

alocação ordenada e controlada dos dispositivos que compõem o computador, entre os programas

que competem por ele.

Exemplo: Acesso à impressora.

Quando um computador tem múltiplos usuários a necessidade de gerenciar e proteger a

memória e outros recursos é ainda maior, para evitar que um usuário interfira no trabalho de outro.

Sistema Bancário

Reserva de Passagem

Navegador WEB

Compila-dores Editores Shell

Sistema Operacional

Linguagem de Máquina

Microprograma

Dispositivos Físicos

Programas aplicativos

Programas de sistema

Hardware

Page 7: Sistemas_Operacionais

Sistemas Operacionais 7

Este ponto de vista sustenta que a tarefa principal do sistema operacional é monitorar quem

está utilizando qual recurso, atender requisições de recursos, medir a utilização dos recursos e medir

as requisições conflitantes de diferentes processos.

1.5 Histórico dos Sistemas Operacionais

A história dos sistemas operacionais está intimamente relacionada à história da arquitetura das

máquinas nas quais eles rodam.

Os primeiros sistemas operacionais foram desenvolvidos para facilitar o uso do hardware. A

medida que os sistemas operacionais foram desenvolvidos tornou-se evidente que mudanças no

projeto de hardware poderia simplificá-los. Desta forma, os sistemas operacionais não só foram

influenciados pela arquitetura dos computadores, como também influenciaram no desenvolvimentos

destas arquiteturas.

1.5.1 1ª Geração (1945-1955) - válvulas e painéis de conectores

Um único grupo de pessoas projetava, construía, programava e operava cada máquina. Toda a

programação era feita em linguagem de máquina ligando fios aos painéis de conectores (não existia

sequer a linguagem Assembly).

Não existia o conceito de sistema operacional.

O ENIAC (Electronic Numerical Integrator and Computer) foi o primeiro computador

eletrônico digital de propósito geral. Ele possuía 18.000 válvulas, 10.000 capacitores, 70.000

resistores e pesava 30 toneladas. Quando em operação consumia 140 quilowatts e era capaz de

realizar 5.000 adições por segundo.

O UNIVAC I (Universal Automatic Computer) foi a primeira máquina comercial a ser

construída, criada especialmente para o censo americano de 1950.

1.5.2 2ª Geração (1955-1965) - transistores e sistemas de lote

Com a invenção do transistor os computadores se tornaram menores, mais confiáveis, mais

rápidos e passaram a consumir menos energia. As memórias de núcleo magnético foram

substituídas por memórias mais rápidas e com maior capacidade de armazenamento.

Os computadores passaram a ser fabricados e vendidos com a expectativa de uma

durabilidade maior.

Nesta geração surgiram as primeiras linguagens de programação. Para executar um “job” o

programador escrevia seus programas em cartões perfurados (Fortran, Assembly ou Cobol). Os

usuários não interagiam diretamente com o sistema o operador pegava o conjunto de cartões do

programador e os carregava na máquina junto com o conjunto de cartões do compilador. O

resultado era gerado em uma impressora.

O sistema operacional destas máquinas tinha a função de carregar os cartões em memória e

transferir o controle da máquina para o programa recém carregado. Os sistemas operacionais

passaram a ter seu próprio conjunto de rotinas para operações de entrada e saída (E/S), facilitando o

trabalho dos programadores.

Para agilizar o processamento, serviço com necessidades semelhantes eram agrupados

formando um mesmo lote (batch), e executados como um grupo.

Neste ambiente a UCP ficava quase sempre ociosa devido à velocidade dos mecanismos de

E/S.

As máquinas desta geração executavam cerca de 200.000 instruções por segundo.

Page 8: Sistemas_Operacionais

Sistemas Operacionais 8

1.5.3 3ª Geração (1965-1980) - circuitos integrados

No início da década de 60 a maioria dos fabricantes de computadores tinham duas linhas de

produtos, uma linha científica e uma linha comercial.

A IBM desenvolveu o Sistema 360 (System 360), uma família de máquinas compatíveis a

nível de software, onde elas diferiam em preço e performance. Foi preciso desenvolver um sistema

operacional que executasse em todos os modelos, sendo bom tanto em ambientes científicos quanto

em ambientes comerciais.

O OS/360 era um sistema operacional muito grande escrito em Assembly que continha muitos

problemas, necessitando de um fluxo contínuo de versões. Com ele foram introduzidos os conceitos

de multiprogramação, spool (para entrada e saída de jobs) e tempo compartilhado (com terminais

on-line).

Em 1969 surge o sistema operacional UNIX, construído com base no MULTICS.

As máquinas desta geração executavam cerca de 5 milhões de instruções por segundo.

1.5.4 4ª Geração (1980-hoje) - integração em larga escala

O desenvolvimento de circuitos LSI e VLSI permitiu o desenvolvimento de computadores

pessoais. Surgiram também as estações de trabalho (computadores pessoais mais poderosos,

geralmente ligados em rede).

Surgiram os sistemas operacionais DOS (Disk Operation System) e VMS (Virtual Memory

System).

Surgiram os conceitos de multitarefa e multiprocessamento, assim como máquinas com mais

de um processador, impulsionados por aplicações que exigiam uma grande quantidade de cálculos.

Com a difusão das WANs foram desenvolvidos vários protocolos de rede para a interconexão

de computadores. Os softwares de rede passaram a estar intimamente relacionados ao sistema

operacional e surgem os sistemas operacionais de rede.

As máquinas mais modernas desta geração executam bilhões de instruções de ponto flutuante

por segundo.

1.6 Conceitos de Hardware

A unidade central de processamento (UCP), ou simplesmente processador, controla as

funções realizadas por cada unidade funcional do restante do sistema. Ela executa as instruções dos

programas através de duas unidades básicas: unidade de controle (UC) e unidade lógica e

aritmética (ULA). Sua velocidade é medida pelo número de instruções que ela executa por segundo

(MIPS e FLOPS).

A unidade lógica e aritmética tem por função realizar operações sobre os dados que lhe são

entregues, enquanto cabe à unidade de controle enviar sinais de controle a todas as partes do

sistema para que essas partes possam funcionar de forma coordenada a fim de executar as

instruções que são entregues à UCP.

Memória

Entrada

SaídaUnidade de

Controle

Unidade Lógica e Aritmética

UCP

Page 9: Sistemas_Operacionais

Sistemas Operacionais 9

O relógio (clock) gera pulsos periódicos para sincronizar o sistema. O sinal do relógio é usado

pela unidade de controle para a execução das instruções. Em conjunto com a UCP determina a

velocidade do sistema.

Registradores são dispositivos de alta velocidade localizados na UCP para armazenamento

temporário de dados. O número de registradores varia de acordo com a arquitetura da UCP. Existem

registradores de uso específico e registradores de uso geral. Alguns exemplos de registradores de

uso específico são: contador de instruções - CI (program counter - PC), apontador da pilha - AP

(stack pointer – SP) e registrador de estado - RE (program status word - PSW).

Na memória principal são armazenados instruções e dados durante a execução de um

programa. É composta por unidades chamadas células, que contém bits. A célula é acessada através

de seu endereço. Uma célula é endereçada através do registrador de endereço de memória - REM

(memory address register - MAR) e do registrador de dados da memória - RDM (Memory Buffer

Register - MBR), ambos localizados na UCP.

As memórias podem ser classificadas, de acordo com a sua volatilidade, em: R/W (RAM),

ROM, PROM, EPROM e EEPROM. Podem ainda ser estáticas ou dinâmicas.

A memória cache é uma memória volátil de alta velocidade que tem seu uso limitado devido

ao alto custo. Ela possui algoritmo próprio de operação, podendo ser separada em cache de

instruções e cache de dados.

A memória secundária é um meio não volátil de armazenamento de programas e dados. Tem

acesso lento quando comparado à memória principal. Disco magnético, CD-ROM e unidade de fita

são exemplos de memória secundária.

Os dispositivos de entrada e saída (E/S) permitem a comunicação entre o computador e o

mundo externo. Existem duas categorias de dispositivos de E/S: dispositivos utilizados como

memória secundária e dispositivos utilizados para a comunicação homem-máquina.

O barramento é um conjunto de condutores onde trafegam dados, endereços e sinais de

controle, interligando os vários componentes do sistema. Exemplos de barramento para

microcomputadores incluem: ISA, EISA, PCI, PCI-Express, AGP, USB, SCSI e SATA.

Processadores RISC (Reduced Instruction Set Computer) se caracterizam por possuírem

poucas instruções, que são executadas diretamente pelo hardware. Costumam possuir um grande

número de registradores. Exemplos de processadores RISC são Sparc (Sun), RS-6000 (IBM), Alpha

(DEC) e o MIPS.

Unidade de Controle

Unidade Lógica e Aritmética

Registradores

Acumulador RDM

REM

CI

RI

Decodificador de instruções

UC

Clock

Memória Principal

Barramentode dados

Barramentode endereços

ULA

Bar

ram

en

toin

tern

o

Page 10: Sistemas_Operacionais

Sistemas Operacionais 10

Processadores CISC (Complex Instruction Set Computer) possuem instruções complexas que

são interpretadas pelo microprograma da UCP e costumam possuir um pequeno número de

registradores. Exemplos de processadores CISC são a linha x86 e Pentium da Intel e a linha 68xxx

da Motorola.

1.7 Conceitos de Software

O hardware do computador tem pouca utilização prática se não houver um conjunto de

softwares que permitam ao usuário utilizar a máquina.

O tradutor faz o mapeamento de uma linguagem em outra. Quando traduz da linguagem

Assembly para um código objeto é conhecido como montador (assembler). Quando traduz de uma

linguagem de alto nível para um código objeto é conhecido como compilador.

O interpretador é um software que no momento da execução do programa traduz cada

instrução e a executa. A maior desvantagem dos interpretadores é o tempo gasto com a tradução no

momento da execução. Sua vantagem é permitir tipos de dados dinâmicos.

O linker é o responsável por gerar, a partir de um ou mais módulos objeto, um único

programa executável. Ele deve procurar em bibliotecas as funções referenciadas nos objetos.

O loader é o programa responsável por carregar um programa em memória para execução.

O depurador (debugger) é um utilitário que permite ao usuário controlar a execução de um

programa a fim de detectar erros.

A linguagem de máquina é a linguagem que a UCP entende e pode ser processada

diretamente por seu hardware.

O microprograma é um programa interno às UCPs CISC que interpreta a linguagem de

máquina destas UCPs. Processadores microprogramáveis permitem a criação de novas instruções

através da modificação de seu microprograma.

1.8 Conceitos de Sistema Operacional

A interface entre o sistema operacional e os programas de usuário é definida pelo conjunto de

instruções estendidas que o sistema operacional proporciona. Estas instruções estendidas são

conhecidas por chamadas de sistema (ou system calls).

As chamadas de sistema variam de um sistema operacional para outro.

1.8.1 Processos

Um processo é basicamente um programa em execução mais seu espaço de endereçamento

(incluindo valores de variáveis) e conjunto de registradores.

Associado a cada processo está seu espaço de endereçamento (ou imagem de núcleo). O

espaço de endereçamento é uma lista de endereços da memória onde o processo pode ler e escrever,

contendo o programa executável, os dados e a pilha.

Também associado com cada processo está um conjunto de registradores (contador de

instruções, ponteiro da pilha, etc.).

Periodicamente o sistema operacional pode parar de executar um processo e começar a

executar outro. Mais tarde o processo deve ser reiniciado no mesmo estado em que estava quando

parou. As informações sobre o processo são armazenadas em uma tabela conhecida como tabela de

processos.

As principais chamadas do sistema de gerenciamento de processos são as que lidam com a

criação e com o encerramento de processos. Se um processo pode criar um ou mais processos filho,

que podem criar novos processos filho, obtém-se uma árvore de processos.

Page 11: Sistemas_Operacionais

Sistemas Operacionais 11

Processos que cooperam para executar determinada tarefa costumam se comunicar. Essa

comunicação é chamada de comunicação interprocessos.

Ocasionalmente é necessário transmitir informações para um processo que não as está

aguardando. Neste caso envia-se um sinal ao processo.

A cada usuário do sistema corresponde um uid (identificação de usuário). Cada processo tem

a uid do usuário que o iniciou.

1.8.2 Arquivos

Uma das funções do sistema operacional é esconder as peculiaridades dos discos e outros

dispositivos de E/S. São necessárias chamadas de sistema para criar, remover, ler e escrever

arquivos.

Para fornecer um local para manter os arquivos existe o conceito de diretório. São necessárias

chamadas de sistema para criar e remover diretórios, assim como inserir e remover arquivos de

diretórios. As entradas de diretórios podem ser arquivos ou diretórios.

Cada arquivo dentro da hierarquia de diretórios pode ser especificado por seu nome de

caminho a partir do diretório raiz (por seu caminho absoluto) ou por seu caminho a partir do

diretório de trabalho atual (caminho relativo). Processos podem mudar seu diretório de trabalho

emitindo uma chamada de sistema.

Arquivos e diretórios costumam ser protegidos. As permissões são verificadas no momento

em que é solicitada a abertura de um arquivo. Se o acesso for permitido é devolvido um número

inteiro chamado descritor de arquivo.

Alguns sistemas operacionais permitem acesso a um sistema de arquivos somente se ele

estiver montado em um sistema de arquivos raiz ou primário.

Existe um recurso denominado pipe que é um tipo de pseudo-arquivo que pode ser utilizado

para conectar dois processos.

1.8.3 Interpretador de Comandos (Shell)

Um dos componentes mais importantes de um sistema operacional é o interpretador de

comandos (shell), que constitui a interface entre o usuário e o sistema operacional. Alguns sistemas

operacionais incluem o interpretador de comandos em seu núcleo, enquanto outros tratam o

interpretador de comandos como um programa especial.

Uma forma usual de requerer que uma tarefa seja realizada pelo sistema operacional é por

meio do uso de comandos de controle. Nestes sistemas, um programa que lê e interpreta comandos

de controle é automaticamente executado. Tal programa é frequentemente conhecido como a shell

(casca) do sistema. Sua tarefa é bastante simples: obter o próximo comando e providenciar para que

ele seja executado.

Nesse aspecto, os sistemas operacionais costumam ser bastante diferentes uns dos outros. Um

exemplo de interface amigável pode ser encontrado no Macintosh e no Windows, com janelas,

menus e mouse. Outros usuários preferem interpretadores de comandos com interfaces mais

poderosas, complexas e mais difíceis de aprender, onde são digitados comandos que são mostrados

em uma tela ou terminal. As interfaces dos interpretadores de comandos do UNIX e do MS-DOS

são desta forma.

O interpretador de comandos, faz uso pesado de muitos recursos do sistema operacional,

sendo a interface primária entre o usuário e o sistema operacional.

Exemplos de comandos:

date

date > arq

Page 12: Sistemas_Operacionais

Sistemas Operacionais 12

sort <arq1 >arq2

cat arq1 arq2 arq3 | sort > /dev/lp

cat arq1 arq2 arq3 | sort > /dev/lp &

1.8.4 Chamadas de Sistema

A interação entre os processos dos usuários e o sistema operacional é feita por meio de

chamadas de sistema (system calls). Em geral as chamadas de sistema estão disponíveis em

linguagem Assembly, mas alguns sistemas permitem que as chamadas de sistema sejam feitas

diretamente em uma linguagem de alto nível.

Serão vistos alguns exemplos de chamadas de sistemas do padrão POSIX divididos em seis

categorias:

gerenciamento de processos;

sinais;

gerenciamento de arquivos;

gerenciamento de diretório;

proteção;

gerenciamento de tempo.

Chamadas de sistema para gerenciamento de processos:

pid = fork()→ Cria um processo filho.

pid = waitpid(pid, &statloc, opts) → Bloqueia até que o processo filho termine.

s = wait(&status) → Bloqueia até que um processo filho termine. Obsoleta.

s = execve(name, arg, envp) → Substitui a imagem de memória por um processo.

exit(status) → Termina o processo.

size = brk(addr) → Especifica o segmento de dados.

pid = getpid()→ Retorna o id do processo.

pid = getpgrp()→ Retorna o id do grupo do processo.

l = ptrace(req, pid, addr, data) → Permite controlar a execução de outro processo.

Exemplo de um shell simples:

while (1) {

ler_comando(comando, parametros);

if (fork() != 0)

waitpid(-1, &status, 0); /* código pai */

else

execve(comando, parametros, 0); /* executa comando */

}

Chamadas de sistema para sinalização:

s = sigaction(sig, &act, &oldact) → Especifica a ação a ser executada para um sinal.

s = sigreturn(&context) → Retorna de uma rotina de tratamento de sinal.

s = sigprocmask(how, &set, &old) → Altera a lista de sinais bloqueados.

s = sigpending(set) → Retorna o conjunto de sinais pendentes por loqueio.

s = sigsuspend(mask) → Suspende o processo até que chegue um dos sinais selecionados.

s = kill(pid, sig) → Envia um sinal a um processo.

residual = alarm(seconds) → Programa um sinal SIGALARM.

s = pause() → Suspende o processo até a chegada de um sinal.

Exemplo:

Page 13: Sistemas_Operacionais

Sistemas Operacionais 13

void handler(int sig)

{

printf("Terminada a execucao\n");

exit(0);

}

main()

{

signal(SIGINT, handler);

while (1);

}

Chamadas de sistema para gerenciamento de arquivos:

fd = create(name, mode) → Cria um novo arquivo. É uma chamada obsoleta.

fd = mknod(name, mode, addr) → Cria arquivos especiais.

fd = open(file, how, ...) → Abre um arquivo, criando se necessário.

s = close(fd) → Fecha o arquivo.

n = read(fd, buffer, nbytes) → Lê dados de um arquivo.

n = write(fd, buffer, nbytes) → Grava dados em um arquivo.

pos = lseek(fd, offset, whence) → Move o ponteiro do arquivo.

s = stat(name, &buf) → Retorna informações sobre um arquivo.

s = fstat(fd, &buf) → Retorna informações sobre um arquivo aberto.

fd = dup(fd) → Cria cópia do descritor de arquivos.

s = pipe(&fd[0]) → Cria um par de descritor de arquivos ligados por um pipe.

s = ioctl(fd, request, argp) → Operações em arquivos especiais.

f = access(name, amode) → Verifica se o processo tem permissão para acessar o arquivo.

s = rename(old, new) → Muda o nome do arquivo.

s = fcntl(fd, cmd, ...) → Executa operações especiais em arquivos.

Exemplo de configuração de pipeline entre 2 processos:

pipeline (char *process1, char *process2)

{

int fd[2];

pipe(&fd[0]);

if (fork() != 0) { /* processo pai */

close(fd[0]);

close(STD_OUTPUT);

dup(fd[1]);

close(fd[1]);

execve(process1, NULL, 0);

}

else { /* processo filho */

close(fd[1]);

close(STD_INPUT);

dup(fd[0]);

close(fd[0]);

execve(process2, NULL, 0);

}

Chamadas de sistema para gerenciamento de diretórios:

s = mkdir(name, mode) → Cria diretório.

s = rmdir(name) → Remove diretório vazio.

s = link(name1, name2) → Cria uma nova entrada para um arquivo em um diretório.

s = unlink(name) → Remove uma entrada de diretório.

s = mount(special, name, flag) → Monta um sistema de arquivos.

s = umount(special) → Desmonta um sistema de arquivos.

s = sync()→ Força a escrita de blocos em cache no disco.

Page 14: Sistemas_Operacionais

Sistemas Operacionais 14

s = chdir(dirname) → Muda o diretório de trabalho.

s = chroot(dirname) → Muda o diretório raiz.

Chamadas de sistema para proteção:

s = chmod(name, mode) → Muda as permissões de um arquivo.

uid = getuid()→ Retorna o id do usuário do processo.

gid = getgid()→ Retorna o id do grupo do usuário do processo.

s = setuid(uid) → Muda o id do usuário do processo.

s = setgid(gid) → Muda o id do grupo do processo.

s = chown(name, owner, group) → Muda o dono do arquivo.

oldmask = umask(complmode) → Muda a máscara de criação de arquivos.

Chamadas de sistema para gerenciamento de tempo:

seconds = time(&seconds) → Obtém a hora.

s = stime(tp) → Ajusta o relógio.

s = utime(file, timep) → Altera a hora de último acesso do arquivo.

s = times(buffer) → Obtém quanto tempo de UCP o processo utilizou.

1.9 Tipos de Sistemas Operacionais

Os sistemas operacionais podem ser classificados em sistema monoprogramável/monotarefa,

sistema multiprogramável/multitarefa e sistema com múltiplos processadores.

O sistema monoprogramável/monotarefa é voltado para a execução de um único programa.

Para executar outro programa, o programa corrente em execução deve terminar antes. Processador,

memória, periféricos e outros recursos são dedicados exclusivamente ao programa em execução.

Nesses sistemas enquanto um programa aguarda por um evento o processador permanece ocioso.

Caso a memória não seja totalmente utilizada pelo programa ela é subutilizada. Não existe

preocupação com a proteção do sistema.

No sistema multiprogramável/multitarefa vários programas podem estar em execução

“simultaneamente”. O sistema operacional se preocupa em gerenciar, de forma ordenada e

protegida, o acesso concorrente aos seus recursos. Dependendo do número de usuários que podem

usar o sistema, ele pode ser classificado como monousuário ou multiusuário. Um sistema

monousuário é aquele em que somente um usuário pode estar utilizando o sistema, mesmo que

executando mais de um programa em um determinado instante. Já um sistema multiusuário permite

que mais de um usuário utilize o sistema simultaneamente. As vantagens dos sistemas

multiprogramáveis são o aumento da produtividade dos usuários e a redução de custos a partir do

compartilhamento dos diversos recursos do sistema.

Os sistemas multiprogramáveis/multitarefa podem ser classificados pela forma como suas

aplicações são gerenciadas em:

Sistemas batch → Programas ficam armazenados em discos ou fitas, esperando para

serem executados sequencialmente.

Sistemas batch multiprogramados → Programas ficam armazenados na memória. Quando

um programa que está sendo executado e precisa esperar por uma tarefa, em vez da UCP

ficar ociosa ela passa a executar outro programa, e assim por diante.

Sistemas de tempo compartilhado (time-sharing) → São uma extensão da

multiprogramação. Permitem a interação de usuários com o sistema através de terminais.

Cada usuário recebe uma fatia do tempo do processador.

Page 15: Sistemas_Operacionais

Sistemas Operacionais 15

Sistemas de tempo real → Semelhantes aos sistemas de tempo compartilhado, atendendo

ao tempo de resposta exigido para as aplicações. O escalonamento não se dá por fatia de

tempo, mas sim por prioridade.

Os sistemas com múltiplos processadores são estudados no tópico a seguir

(multiprocessamento).

1.9.1 Multiprocessamento

Um sistema multiprocessado é um sistema que possui mais de uma UCP, localizadas

próximas uma das outras, se comunicando e compartilhando um barramento, por onde são

transmitidos os dados entre os componentes do sistema computacional.

A principal vantagem do multiprocessamento é o aumento da quantidade de trabalho

realizado por unidade de tempo (throughput) do sistema como um todo.

Um sistema multiprocessado pode ser desenvolvido para apresentar maior confiabilidade,

sendo construído de forma que a parada de um processador não provoque a parada do sistema como

um todo.

Os sistemas com múltiplos processadores caracterizam-se por possuir duas ou mais UCPs

interligadas, trabalhando em conjunto. Em função da forma como as UCPs se comunicam e do grau

de compartilhamento de memória e outros recursos, os sistemas com múltiplos processadores

podem ser classificados em fortemente acoplados ou fracamente acoplados.

Nos sistemas fortemente acoplados existem dois ou mais processadores compartilhando uma

única memória e controlados por um único sistema operacional. São geralmente utilizados no

processamento de aplicações que fazem uso intensivo da UCP. Sua grande vantagem é o aumento

do throughput. Subdividem-se ainda em sistemas simétricos e sistemas assimétricos.

Nos sistemas fortemente acoplados assimétricos existe um processador principal,

responsável pelo controle dos demais processadores e pela execução do sistema operacional. O

processador principal também é responsável por realizar todas as operações de entrada e saída.

Nos sistemas fortemente acoplados simétricos todos os processadores realizam a mesma

função, exceto pela inicialização do sistema (boot), que é realizada por um único processador. O

sistema operacional deve ser construído de forma a evitar conflitos entre os processadores.

Nos sistemas fracamente acoplados existem dois ou mais sistemas de computação

conectados através de linhas de comunicação. Cada sistema possui seu próprio sistema operacional

e funciona de forma independente. Sua utilização é caracterizada pelo processamento distribuído

entre os vários processadores. Subdividem-se ainda em sistemas operacionais de rede e sistemas

operacionais distribuídos.

No sistema operacional de rede cada nó possui seu próprio sistema operacional, podendo

inclusive ser diferente dos demais. Caso haja perda da conexão entre os nós, os sistemas podem

operar normalmente, podendo apenas ter alguns recursos indisponíveis. Permite, entre outras, cópia

remota de arquivos, emulação de terminal, impressão remota, gerência remota e correio eletrônico.

Os usuários estão cientes da existência de múltiplos computadores e podem se conectar a máquinas

remotas.

No sistema operacional distribuído cada nó da rede possui seu próprio sistema operacional,

mas com um relacionamento mais forte entre os nós. Geralmente os sistemas operacionais dos nós é

o mesmo. Aparece para os usuários como um sistema tradicional. O usuário não sabe onde seus

programas estão sendo executados e onde seus arquivos são armazenados. Permite que um

aplicativo execute em mais de um processador. Os sistemas distribuídos podem ser considerados

como uma evolução dos sistemas fortemente acoplados, onde uma aplicação pode ser executada por

qualquer processador. Sua grande vantagem é a possibilidade de redundância do sistema.

Page 16: Sistemas_Operacionais

Sistemas Operacionais 16

Mesmo que um sistema não tenha programas que tenham sido escritos para tirar vantagem da

máquina ter mais de um processador, é vantajoso ter mais de um processador na máquina, uma vez

que isto permitirá que mais de um processo seja executado simultaneamente, aumentando o

throughput do sistema.

1.10 Sistemas Multiprogramáveis

A possibilidade de periféricos e dispositivos funcionarem simultaneamente, juntamente com a

UCP, permitiu a execução de tarefas concorrentes, que é o princípio para projeto de sistemas

multiprogramáveis.

Nos sistemas multiprogramáveis vários processos podem estar residentes em memória

concorrendo pela utilização da UCP. Quando um processo solicita uma operação de E/S, outros

processos poderão estar disponíveis para utilizar a UCP.

A utilização concorrente da UCP deve ser implementada de maneira que, quando um processo

perde seu uso e depois retorna para continuar o processamento, seu estado deve ser idêntico ao do

momento em que foi interrompido.

Durante a execução de um processo, alguns eventos podem ocorrer, obrigando a intervenção

do sistema operacional. Este tipo de intervenção é chamado interrupção ou exceção. O que

diferencia uma interrupção de uma exceção é o tipo de evento que gera esta condição.

Uma interrupção é gerada pelo sistema operacional ou por algum dispositivo e independe do

programa que está sendo executado.

No momento em que a unidade de controle detecta a ocorrência de algum tipo de interrupção,

o programa em execução é interrompido, e o controle é desviado para uma rotina responsável pelo

tratamento da interrupção. Muitas vezes, após a execução dessa rotina, o controle deve voltar ao

programa que estava sendo processado. Para isso acontecer é necessário que, no momento da

interrupção, um conjunto de informações sobre a execução do programa seja preservado. Essas

informações consistem no conteúdo de alguns registradores que deverão ser restaurados

posteriormente para a continuação do programa.

No momento que uma interrupção acontece, a UCP deve saber para qual rotina de tratamento

deverá ser desviado o fluxo de execução. Essa informação está em uma estrutura do sistema

chamada vetor de interrupção.

A interrupção é o mecanismo que tornou possível a implementação da concorrência nos

computadores, sendo o fundamento dos sistemas multiprogramáveis.

Uma exceção é resultado direto da execução de uma instrução do próprio programa. O

mecanismo de tratamento de exceções é semelhante ao de interrupções e podem ser escritas pelo

próprio programador. Dessa forma, é possível evitar que um programa seja encerrado no caso de

ocorrer, por exemplo, uma divisão por zero.

Obtém o endereço da rotina de tratamento

Salva os registradores

Identifica a origemda interrupção

Restaura os registradores

Programa

Rotina de tratamento

Interrupção

Page 17: Sistemas_Operacionais

Sistemas Operacionais 17

1.11 Estrutura do Sistema Operacional

O sistema operacional é formado por um conjunto de rotinas que oferecem serviços às

aplicações dos usuários do sistema, bem como a outras rotinas do próprio sistema. Esse conjunto de

rotinas é chamado de kernel ou núcleo do sistema.

As principais funções do kernel são:

tratamento de interrupções;

criação e eliminação de processos;

sincronização e comunicação entre processos;

escalonamento e controle dos processos;

gerência de memória;

gerência do sistema de arquivos;

operações de E/S;

contabilização e segurança do sistema.

Para proteger o kernel, os usuários solicitam serviços através de chamadas de sistemas.

1.11.1 Modos de Operação

Para que um sistema operacional possa trabalhar de forma segura é preciso existir um

esquema de proteção que proteja o sistema operacional e todos os outros programas e dados de

qualquer programa incorreto. Assim é necessário que o hardware ofereça pelo menos dois modos de

operação para diferenciar os modos de execução. Estes modos são conhecidos como modo usuário

e modo kernel. O modo kernel também é conhecido como modo supervisor ou modo privilegiado.

Existem instruções que só devem ser executadas pelo sistema operacional para impedir

problemas de segurança do sistema. Instruções que tem o poder de comprometer o sistema são

conhecidas como instruções privilegiadas, enquanto instruções que não oferecem perigo ao sistema

são conhecidas como instruções não privilegiadas.

Quando o sistema está no modo usuário as aplicações podem executar somente instruções

não privilegiadas. Quando o sistema está no modo kernel as aplicações podem executar qualquer

instrução do processador.

Durante a inicialização do sistema (boot) o sistema está no modo kernel. Quando o sistema

operacional é carregado ele inicia a execução de processos do usuário no modo usuário. Sempre que

ocorre uma interrupção o modo de operação muda para o modo kernel e, antes de transferir o

controle da máquina para um processo do usuário, ele coloca a UCP novamente no modo usuário.

Sempre que uma aplicação necessita de um serviço privilegiado ela deve fazer uma chamada

de sistema. A chamada de sistema muda o modo de operação para o modo kernel, executando o

serviço e voltando ao modo usuário.

Caso uma aplicação tente executar uma instrução privilegiada sem estar no modo kernel uma

exceção será gerada.

1.11.2 Ativação e Desativação do Sistema

Toda vez que um computador é ligado, é necessário que o sistema operacional seja carregado

da memória secundária para a memória principal. Esse processo é denominado ativação do sistema

(boot).

AplicaçãoChamada

de SistemaNúcleo Hardware

Page 18: Sistemas_Operacionais

Sistemas Operacionais 18

Além da carga do sistema operacional, a ativação do sistema também consiste na execução de

arquivos de inicialização. Nestes arquivos são especificados procedimentos de inicialização e

configuração de hardware e software específicos para cada ambiente.

Na maioria dos sistemas também existe o processo de desativação (shutdown). Este

procedimento permite que as aplicações e componentes do sistema sejam desativados de forma

ordenada, garantindo a integridade do sistema.

1.11.3 Sistemas Monolíticos

Não existe uma estrutura real. O sistema operacional é escrito como uma coleção de

procedimentos, cada um podendo chamar qualquer um dos outros. Os procedimentos são

compilados e “linkados” em um único programa executável. Cada procedimento é visível aos

demais.

É possível ter um mínimo de estrutura requisitando-se chamadas de sistema ao colocar os

parâmetros em locais bem definidos e chamar interrupções. A máquina é comutada para o modo

kernel e o controle é transferido ao sistema operacional. Quando a chamada termina o controle é

devolvido ao programa de usuário.

1.11.4 Sistemas em Camadas

São definidas camadas sobrepostas onde cada camada oferece um conjunto de funções que

podem ser utilizadas pelas camadas superiores.

A vantagem da estruturação em camadas é isolar as funções do sistema operacional, criando

uma hierarquia e protegendo as camadas mais internas.

A depuração de erros também é simplificada. Como cada nível usa apenas operações

fornecidas por níveis inferiores, o sistema operacional pode ser depurado da camada mais interna

para a mais externa sem grandes dificuldades.

Um problema com a implementação em camadas é a queda de desempenho provocada no

sistema, uma vez que qualquer operação deve passar por todas as camadas inferiores até chegar ao

hardware.

Programa de usuário 2

Programa de usuário 1

1

2

3

4

Programas deusuário executados

no modo usuário

Sistema operacional executado nomodo kernel

1. Interrupção gerada para o kernel2. S.O. determina o serviço necessário3. S.O. chama o procedimento de serviço4. Controle retorna para o programa de usuário

Kernel

Executivo

Supervisor

Usuário

VMS

Operador

Programas de usuário

Entrada/Saída

Comunicação

Gerência de memória

Multiprogramação

MULTICS

0

1

2

3

4

5

Page 19: Sistemas_Operacionais

Sistemas Operacionais 19

Abaixo segue uma breve descrição das camadas do MULTICS.

A camada 0 lida com a alocação do processador alternando entre processos quando ocorrem

interrupções ou quando temporizadores expiram. Acima da camada 0 os processos não precisam se

preocupar se existe mais de um processo sendo executado.

A camada 1 faz o gerenciamento de memória, alocando espaço para os processos. Acima dela

os processos não precisam se preocupar se estão em memória ou em disco.

A camada 2 manipula a comunicação de cada processo com a console do operador. Acima

dela cada processo tem seu próprio console de operador.

A camada 3 cuida de gerenciar os dispositivos de E/S. Acima dela cada processo pode lidar

com dispositivos de E/S abstratos amigáveis.

Na camada 4 residem os programas de usuários, que não precisam se preocupar com

gerenciamento de processos, memória, console e E/S.

O processo do operador do sistema se localiza na camada 5.

1.11.5 Máquinas Virtuais

No hardware básico é executado o monitor de máquina virtual, oferecendo várias máquinas

virtuais à camada superior. Tais máquinas virtuais são cópias exatas do hardware básico e cada uma

pode executar qualquer sistema operacional.

Quando um processo executa uma chamada de sistema:

A chamada é interceptada pelo sistema operacional da máquina virtual;

O sistema operacional emite as instruções normais de E/S de hardware;

As instruções são interceptadas pelo monitor de máquina virtual que as executa

simulando o hardware real.

Colocando a multiprogramação como responsabilidade do monitor de máquina virtual pode-

se oferecer uma máquina estendida mais simples, flexível e fácil de manter.

O modelo de máquina virtual não só oferece a possibilidade de executar diferentes sistemas

operacionais em um mesmo hardware como também garante o isolamento total entre as máquinas,

fornecendo um excelente nível de segurança entre elas.

Em uma outra abordagem a máquina virtual pode ser criada com um subconjunto dos recursos

da máquina real. Em uma camada mais baixa é executado um programa chamado exokernel, que

atribui recursos às máquinas virtuais e garante que nenhuma máquina virtual utilize recursos de

outra. Cada máquina virtual pode ter seu próprio sistema operacional. Tem a vantagem de

economizar uma camada de mapeamento necessária ao monitor de máquina virtual, já que o

monitor de máquina virtual precisa manter uma tabela dos recursos em uso por cada máquina

virtual.

processos

kernel

hardware

processos

kernel

processos

kernel

processos

kernel

Monitor de máquina virtual

hardware

Máquina sem monitorde máquina virtual

Máquina com monitorde máquina virtual

Page 20: Sistemas_Operacionais

Sistemas Operacionais 20

1.11.6 Sistemas Microkernel

Uma tendência nos sistemas operacionais modernos é mover o máximo do código para as

camadas superiores, implementando parte das funções do sistema operacional em processos de

usuário. Isto faz com que o núcleo seja menor e mais simples.

Para requisitar um serviço um processo de usuário (processo cliente) envia uma requisição a

um processo servidor. O kernel gerencia a comunicação entre os processos.

Tal divisão torna as partes menores e mais fáceis de gerenciar. Para trocar alguma

característica do sistema operacional basta modificar o servidor específico.

Se algum servidor falhar o sistema operacional não ficará completamente comprometido.

O modelo se adapta facilmente a sistemas com múltiplos processadores e sistemas

distribuídos. Quando o cliente se comunica com um servidor ele não precisa saber em que

processador ou máquina o servidor se encontra.

Nem todos os processos servidores podem executar em modo usuário, já que alguns precisam

utilizar instruções privilegiadas (drivers, por exemplo). O kernel deve incorporar estas funções ou

devem existir alguns processos servidores críticos executando em modo kernel. Outra solução é

implementar mecanismos no kernel deixando a política para os servidores no espaço do usuário.

1.12 Exercícios

1) Qual a diferença entre programas aplicativos e programas de sistema? O sistema operacional é

um programa de sistema ou um programa aplicativo?

2) Qual o objetivo em se instalar um sistema operacional em um computador?

3) Quais as dificuldades que um programador teria no desenvolvimento de uma aplicação para um

ambiente sem sistema operacional?

4) Explique a seguinte afirmação: “O sistema operacional pode ser visto como uma máquina

estendida ou como um gerenciador de recursos.”

5) Explique o conceito de máquina estendida. Qual sua vantagem?

6) Qual a função da unidade central de processamento (UCP) de um computador?

7) Quais são os componentes de uma UCP e quais são suas funções?

8) Qual a diferença entre registrador, memória cache, memória principal e memória secundária?

9) O que são memórias voláteis e não voláteis?

10) Quais as diferenças entre processadores CISC e processadores RISC?

11) O que se entende por “ativação do sistema (boot)” e “desativação do sistema (shutdown)”.

12) Qual a diferença entre tradutor e interpretador?

13) Qual a diferença entre um compilador e um linker?

14) O que é um microprograma?

15) Por que o código objeto gerado por um compilador ainda não pode ser executado?

16) Por que a execução de programas interpretados é mais lenta do que a de programas compilados?

17) Qual a função do linker?

Processocliente

Processocliente

Processoservidor

Servidorterminal ...... Servidor

arquivosServidormemória

kernel

Modo usuário

Modo kernel

Page 21: Sistemas_Operacionais

Sistemas Operacionais 21

18) Qual a função do loader?

19) Como um depurador pode facilitar o trabalho de um programador?

20) O que são as chamadas de sistema de um sistema operacional?

21) O que é um interpretador de comandos (shell)?

22) O que diferencia um sistema operacional monoprogramável/monotarefa de um sistema

operacional multiprogramável/multitarefa? Que tipo de importância cada um deles costuma dar

à proteção do sistema?

23) Por que diz-se que um sistema operacional monoprogramável subutiliza o sistema?

24) Por que a proteção do sistema é tão importante para um sistema operacional multitarefa?

25) O que vem a ser um sistema com múltiplos processadores? Qual a vantagem?

26) É vantajoso ter mais de uma UCP em uma máquina somente se esta máquina executar

programas aplicativos desenvolvidos para explorar o paralelismo da máquina? Explique.

27) O que vem a ser um sistema multiprogramável?

28) O que caracteriza um sistema batch?

29) Como funcionam os sistemas de tempo compartilhado? Qual a vantagem na sua utilização?

30) Qual a diferença entre sistemas de tempo compartilhado e de tempo real?

31) Quando se deve utilizar um sistema operacional de tempo real?

32) Qual a diferença entre sistema multiprocessados simétricos e assimétricos?

33) Qual a importância do mecanismo de interrupção para um sistema operacional multitarefa?

34) Qual a diferença entre uma interrupção e uma exceção?

35) Explique o funcionamento do mecanismo de interrupções.

36) O que é o núcleo (kernel) do sistema operacional? Cite 2 funções importantes.

37) Qual a diferença entre um processo estar executando no modo usuário ou no modo kernel?

38) O que são instruções privilegiadas e não privilegiadas?

39) O que é uma chamada de sistema? Qual sua importância para a segurança do sistema?

40) Como um programador pode fazer para executar uma chamada de sistema em seu programa?

41) O que é um sistema operacional com estrutura monolítica?

42) Cite uma vantagem de se projetar um sistema operacional em camadas.

43) Qual a função do monitor de máquina virtual?

44) O que se pode falar sobre a interferência do funcionamento de uma máquina virtual em outra

dentro de um mesmo computador?

45) O que caracteriza um sistema operacional com estrutura microkernel?

46) Como funciona o modelo cliente-servidor na arquitetura microkernel? Cite vantagens na sua

utilização?

Page 22: Sistemas_Operacionais

Sistemas Operacionais 22

2 Processos

Os primeiros sistemas permitiam a execução de apenas um programa de cada vez. Esse

programa tinha o controle completo do sistema e acesso a todos os seus recursos. Os sistema atuais

permitem que diversos programas sejam carregados na memória e executados simultaneamente.

Essa evolução tornou necessário um controle maior na divisão de tarefas dos vários programas,

resultando na noção de processo.

Em um sistema multiprogramável a UCP alterna entre processos, dedicando um pouco de seu

tempo a cada um, dando a ilusão de paralelismo. Este esquema costuma ser chamado de

pseudoparalelismo.

Neste modelo, todo software executado no computador é organizado em processos

sequenciais, ou, simplesmente, processos. Este modelo foi desenvolvido para tornar o paralelismo

mais fácil de tratar, uma vez que monitorar atividades paralelas é muito difícil.

Um processo é um programa em execução, incluindo os valores atuais dos registradores e

variáveis, assim como seu espaço de endereçamento. Um programa por si só não é um processo,

mas uma entidade passiva. Um processo é uma entidade ativa, com um contador de instruções que

especifica a próxima instrução a ser executada e um conjunto de registradores a ele associado.

Embora dois processos possam estar associados a um mesmo programa, são duas sequências

de execução distintas.

Conceitualmente, cada processo tem sua própria UCP. Com a UCP alternando entre os

processos, a velocidade com que um processo executa não será uniforme.

É preciso dispor de uma forma de criar e destruir processos quando for necessário. No UNIX

os processos são criados pela chamada de sistema “fork” que cria uma cópia exata do processo em

execução. Em outros sistemas existem chamadas para criar um processo, carregar sua memória, e

começar a executar. O Windows pode criar processos das duas formas.

Eventualmente um processo que está em execução necessita parar momentaneamente sua

execução por estar aguardando uma informação ainda não disponível ou porque já executou por

muito tempo e precisa liberar a UCP para outro processo.

Um processo pode mudar de estado, podendo estar nos estados novo, executando, pronto,

bloqueado ou terminado.

Para implementar o modelo de processos o sistema operacional mantém uma tabela de

processos, com uma entrada por processo. Esta entrada é chamada de Bloco de Controle de

Processo – BCP (Process Control Block – PCB), e contém todas as informações do processo.

Algumas entradas do BCP são:

AB

CD

Pro

cess

o

Tempo

4 processos concorrendo

Executando

BloqueadoNovo Terminado

Pronto

15

2

3

4 6

1. O processo é admitido no sistema2. O escalonador põe o processo selecionado em execução3. O processo é interrompido4. O processo bloqueia para E/S ou evento5. Fim de E/S ou ocorrência de evento esperado6. O processo termina sua execução

Page 23: Sistemas_Operacionais

Sistemas Operacionais 23

estado do processo

prioridade do processo

número do processo

registradores da UCP

informações relativas ao gerenciamento de memória

informações de contabilidade

informações sobre operações de E/S

etc.

Essa visão dá origem ao seguinte modelo:

O nível mais baixo do sistema operacional é o escalonador (também conhecido como

agendador). Ele cuida do gerenciamento de interrupções e dos detalhes de como iniciar e parar

processos. Também costuma ser muito pequeno.

Um processo passa pelas várias filas de seleção durante sua execução. Cabe ao escalonador

selecionar processos destas filas e decidir qual será o próximo a ser executado.

O escalonador é chamado com muita frequência. Um processo pode ser executado por apenas

alguns milissegundos e ter que esperar por ter feito uma requisição de E/S. O escalonador costuma

ser chamado pelo menos uma vez a cada 100 ms para realizar a troca de processos. Devido ao

pequeno intervalo de tempo entre as chamadas ao escalonador sua execução deve ser bastante

rápida para que não se gaste muito tempo de UCP com trabalho de gerência.

2.1 Mudança de Contexto

Para transferir o controle da UCP de um processo para outro é necessário guardar o estado do

processo em execução e carregar o estado do processo a entrar em execução. Esta tarefa é

conhecida como mudança de contexto (ou troca de contexto).

O tempo gasto na mudança de contexto varia e depende de fatores como velocidade da

memória, quantidade de registradores e existência de instruções especiais. Este tempo costuma

variar de 1 a 1000 s.

O contexto de um processo pode ser dividido em 3 elementos básicos: contexto de hardware,

contexto de software e espaço de endereçamento.

O contexto de hardware constitui-se basicamente do conteúdo dos registradores. No

momento em que o processo perde a UCP, o sistema salva suas informações. Ele é fundamental

para a implementação dos sistemas multiprogramáveis.

O contexto de software especifica características do processo que vão influir na execução de

um programa. Ele define basicamente 3 grupos de informações sobre um processo: identificação,

quotas e privilégios. A identificação identifica o processo para o sistema de forma única, através de

seu pid, uid e gid. Quotas são os limites de cada recurso que o sistema operacional pode alocar,

como número de arquivos abertos, quantidade de memória, número subprocessos que podem ser

criados, etc. Privilégio é o que o processo pode ou não fazer em relação ao sistema e outros

processos.

O espaço de endereçamento é a área de memória do processo onde o programa será

executado e a área de memória onde os dados do processo serão armazenados. Cada processo

possui seu próprio espaço de endereçamento, que deve ser protegido dos demais.

processos sequenciais0 1 2 3 ... n-2 n-1

Escalonador gerencia interrupções e agendamento

Page 24: Sistemas_Operacionais

Sistemas Operacionais 24

2.2 Subprocesso e Thread

Quando um processo (processo pai) cria um outro processo, o processo criado é conhecido

como subprocesso ou processo filho. O subprocesso, por sua vez, pode criar outros subprocessos.

Caso um processo deixe de existir, os subprocessos subordinados são eliminados.

A utilização de subprocessos permite dividir uma aplicação em partes que podem trabalhar de

forma concorrente.

O uso de subprocessos demanda consumo de diversos recursos do sistema. Sempre que um

novo processo é criado o sistema deve alocar recursos (contexto de hardware, contexto de software

e espaço de endereçamento) para ele, além de consumir tempo de UCP. Ainda, cada processo

possui seu próprio BCP.

Na tentativa de diminuir o tempo gasto na criação/eliminação de processos, bem como

economizar recursos do sistema, foi introduzido o conceito de thread. Em um ambiente com

múltiplos threads não é necessário haver vários processos para se implementar aplicações

concorrentes.

Threads compartilham o processador da mesma maneira que um processo. Cada thread possui

seu próprio conjunto de registradores (contexto de hardware), porém compartilha o mesmo espaço

de endereçamento com os demais threads do processo. No momento em que um thread perde a

utilização do processador, o sistema salva suas informações. Threads passam pelos mesmos estados

que um processo.

A grande diferença entre subprocessos e threads é em relação ao espaço de endereçamento.

Subprocessos possuem, cada um, espaços independentes e protegidos. Threads, por outro lado,

compartilham o mesmo espaço de endereçamento do processo, sem nenhuma proteção, permitindo

que um thread possa alterar dados de outro thread. Threads são desenvolvidos para trabalharem de

forma cooperativa, voltados para desempenhar uma tarefa em conjunto. Threads também são

conhecidas como processos leves.

A mudança de contexto entre threads em um mesmo processo exige uma alteração de um

conjunto de registradores, mas não é necessário nenhum outro trabalho, como gerenciamento de

memória, por exemplo, tornando-a mais leve que a mudança de contexto entre processos.

Quando múltiplos threads estão presentes no mesmo processo, alguns campos da tabela de

processos não são por processo, mas por thread.

Em alguns sistemas os threads são gerenciados no espaço do usuário, sem o conhecimento do

sistema operacional. É o caso do pacote P-threads (POSIX). A comutação de threads é muito mais

rápida quando é feita no espaço do usuário por não precisar fazer uma chamada ao kernel. Porém,

quando os threads são executados no espaço do usuário e um thread bloqueia, todo o processo é

bloqueado pelo kernel. Threads no nível do usuário também faz com que o tempo dedicado a

threads de diferentes processos não seja distribuído de uma forma justa.

1 processo com 3 threads3 processos, com 1 thread cada

thread processocontador de programa

Page 25: Sistemas_Operacionais

Sistemas Operacionais 25

2.3 Tipos de Processos

Existem basicamente dois tipos de processos com relação ao tipo de processamento que

executam: CPU-bound e I/O-bound.

Os processos do tipo CPU-bound passam a maior parte do tempo no estado executando,

realizando poucas operações de E/S. Costumam ser encontrados em aplicações científicas.

Os processos do tipo I/O-bound passam a maior parte do tempo no estado bloqueado por

realizar um elevado número de operações de E/S. Costumam ser encontrados em aplicações

comerciais. Processos interativos também são exemplos deste tipo de processo.

2.4 Processos Cooperativos

Os processos concorrentes em um sistema operacional podem ser independentes ou

cooperativos. Um processo é um processo independente se ele não afeta e não é afetado por

nenhum outro processo do sistema. Por outro lado, qualquer processo que afetada ou é afetado por

outro processo é dito um processo cooperativo. Algumas razões para a cooperação entre processos

são:

Compartilhamento de informações → Acesso concorrente a recursos.

Aumento da velocidade de processamento → Aplicações que podem ser divididas em

subtarefas a serem executadas paralelamente.

Modularidade → Dividir as funções do sistema em processos separados.

Conveniência → Permitir a troca de dados entre processos dos usuários.

Para que possa ser implementada a cooperação entre processos é preciso existir mecanismos

que permitam aos processos comunicarem-se uns com os outros e sincronizarem suas ações.

Para ilustrar o conceito de processos cooperativos pode-se usar o exemplo do

produtor/consumidor. Um processo produtor produz informações que são consumidas por um

processo consumidor.

Para permitir a execução concorrente dos processos produtor e consumidor é preciso haver

uma área de armazenamento de itens a qual os dois processos tenham acesso. Ainda, os dois

processos devem estar sincronizados para que o consumidor não tente consumir quando não houver

item produzido.

Outro problema é que a área de armazenamento costuma ser limitada. Assim o produtor só

poderá produzir enquanto houver espaço para armazenamento de itens.

O controle de acesso a área de armazenamento pode ser feito por meio do sistema operacional

usando comunicação interprocessos ou explicitamente pelo programador com o uso de memória

compartilhada.

Utilizando memória compartilhada teríamos as seguintes definições compartilhadas pelos

processos:

const

n = ...;

type

item = ...;

var

buffer: array [0..n-1] of item;

counter, in, out: integer;

A área de armazenamento compartilhada é implementada por meio de uma fila circular. A

variável in aponta para a próxima posição livre na área de armazenamento, enquanto out aponta

para a primeira posição ocupada na área de armazenamento.

O código do processo produtor fica:

Page 26: Sistemas_Operacionais

Sistemas Operacionais 26

repeat

nextp := produz_item;

while (counter = n) do (*faz nada*);

buffer[in] := nextp;

in := (in+1) mod n;

counter := counter + 1;

until false;

O código do processo consumidor fica:

repeat

while (counter = 0) do (*faz nada*);

nextc := buffer[out];

out := (out+1) mod n;

counter := counter - 1;

consome_item(nextc);

until false;

2.5 Exercícios

1) O que é um processo?

2) Pode-se afirmar que um programa executado em uma mesma máquina com sistema

operacional multitarefa levará sempre o mesmo tempo para terminar sua execução? Explique.

3) Quais os estados em que um processo pode estar? Que transições podem ocorrer entre estes

estados?

4) Para cada possível mudança de estado de um processo, cite um exemplo de evento que pode

levar a tal mudança.

5) Qual a função do bloco de controle de processos (PCB)?

6) Qual a função do escalonador de um sistema operacional?

7) O que significa mudança de contexto?

8) Que importância tem a tabela de processos para a mudança de contexto?

9) O que são threads?

10) Qual a diferença entre threads e subprocessos?

11) Como o uso de threads pode melhorar o desempenho de aplicações paralelas em ambientes

com múltiplos processadores?

12) Cite uma vantagem de se utilizar threads no lugar de processos cooperativos.

13) O que diferencia um processo CPU-bound de um processo I/O-bound?

14) Dê exemplos de aplicações CPU-bound e I/O-bound.

15) O que são processos cooperativos?

16) Cite duas razões para a cooperação entre processos.

Page 27: Sistemas_Operacionais

Sistemas Operacionais 27

3 Comunicação Entre Processos

O surgimento dos sistemas multiprogramáveis tornou possível criar aplicações nas quais

diferentes partes de seu código pudessem executar de forma concorrente. Tais aplicações são

conhecidas como aplicações concorrentes.

É comum que processos de aplicações concorrentes compartilhem recursos do sistema. Não

importa quais recursos são compartilhados, os problemas decorrentes serão os mesmos. O

compartilhamento de recursos entre processos pode gerar situações indesejáveis capazes de

comprometer o sistema.

Se dois processos concorrentes trocam informações através de um buffer, um processo só

poderá gravar dados caso o buffer não esteja cheio, enquanto um processo só poderá ler dados caso

o buffer não esteja vazio. Em qualquer caso, os processos deverão aguardar até que o buffer esteja

pronto para as operações de E/S.

As considerações anteriores levam a 3 questões:

Como um processo passa informações a outro?

Como certificar que 2 processos não se interfiram?

Como realizar o sequenciamento quando um processo depende do outro?

É bastante comum que aplicações concorrentes necessitem trocar informações. Tal

comunicação pode se dar por intermédio de variáveis compartilhadas (memória compartilhada) ou

por troca de mensagens. Mas, independente do mecanismo de comunicação utilizado, é preciso que

os processos tenham condições de se manterem sincronizados.

Os mecanismos que garantem a comunicação entre processos concorrentes e o acesso a

recursos compartilhados são chamados mecanismos de sincronização. Os mesmos mecanismos se

aplicam a threads.

3.1 Condição de Corrida

Em alguns sistemas, processos que estão trabalhando em conjunto muitas vezes utilizam uma

memória comum, onde cada processo pode ler ou escrever. Este armazenamento compartilhado

pode ser feito na memória principal ou em um arquivo em disco.

Seja, por exemplo, um programa que atualize o saldo de um cliente após o lançamento de um

débito ou um crédito em um registro. O trecho do programa que faz a atualização poderia ser:

Read(Arquivo, Registro);

Readln(Valor_cred_deb);

Registro.Saldo := Registro.Saldo + Valor_cred_deb;

Write(Arquivo, Registro);

Suponha que o processo seja executado de forma concorrente por dois caixas e que o primeiro

processo leia o valor do registro. Caso o segundo processo venha a ler também o valor do mesmo

registro antes que o primeiro tenha a oportunidade de gravar sua alteração, haverá uma

inconsistência ao final das operações. Uma delas não será efetivada.

Problemas como esse são conhecidos como condição de corrida, que é quando dois ou mais

processos estão acessando dados compartilhados e o resultado final do processamento depende de

quem executa quando.

Page 28: Sistemas_Operacionais

Sistemas Operacionais 28

3.2 Região Crítica

Como evitar as condições de corrida? A forma mais simples é impedir que dois ou mais

processos acessem um mesmo recurso no mesmo instante, impedindo que eles acessem o recurso

compartilhado simultaneamente. Quando um processo estiver acessando o recurso os demais

deverão esperar. A esta exclusividade de acesso dá-se o nome de exclusão mútua, uma questão

importante para o desenvolvimento de um sistema operacional.

A parte do programa que acessa a memória compartilhada é denominada seção crítica ou

região crítica (RC). Quando um processo está executando dentro de sua região crítica, nenhum

outro processo pode entrar na região crítica.

Begin

......

Entra_Regiao_Critica;

Regiao_Critica;

Sai_Regiao_Critica;

......

End.

Não é suficiente evitar que o processo seja interrompido dentro da região crítica. São quatro

as condições para uma boa solução:

1. Não pode haver mais de um processo simultaneamente dentro de suas regiões crítica.

2. Nenhuma suposição pode ser feita sobre a velocidade ou o número de UCPs.

3. Nenhum processo que execute fora de sua região crítica pode bloquear outro processo.

4. Nenhum processo deve ter que esperar eternamente para entrar em sua região crítica

(starvation).

Como pode ser observado, para garantir a implementação da exclusão mútua os processos

envolvidos devem fazer acesso aos recursos compartilhados de forma sincronizada.

3.3 Sincronização Condicional

Outra situação na qual é necessária a sincronização entre processos concorrentes é quando um

recurso compartilhado não se encontra pronto para ser utilizado. Nesse caso, o processo que deseja

acessar o recurso deverá ser colocado no estado bloqueado até o recurso ficar pronto para o

processamento.

Um exemplo clássico é a comunicação entre dois processos através de operações de gravação

e leitura em um buffer. Os processos envolvidos devem estar sincronizados de forma que um

processo não tente gravar dados em um buffer cheio ou ler de um buffer vazio.

Abaixo é mostrado, como exemplo de sincronização condicional, o programa

produtor/consumidor.

PROGRAM Produtor_Consumidor;

CONST TamBuf = (* Tamanho qualquer *); (* Tamanho do Buffer *)

TYPE Tipo_Dado = (* Tipo qualquer *); (* Tipo do dado *)

VAR Buffer : ARRAY [1..TamBuf] OF Tipo_Dado;

Dado : Tipo_Dado;

Cont : 0..TamBuf; (* Contador que controla o Buffer *)

PROCEDURE Produtor;

BEGIN

REPEAT

Produz_Dado (Dado);

WHILE (Cont = TamBuf) DO (* Nao faz nada *);

Grava_Buffer (Dado, Cont);

UNTIL False;

END;

Page 29: Sistemas_Operacionais

Sistemas Operacionais 29

PROCEDURE Consumidor;

BEGIN

REPEAT

WHILE (Cont = 0) Do (* Nao faz nada *);

Le_Buffer (Dado, Cont);

Consome_Dado (Dado);

UNTIL False;

END;

BEGIN

Cont := 0;

PARBEGIN

Produtor;

Consumidor;

PAREND;

END.

Nessa solução, a tarefa de colocar e retirar os dados do buffer é realizada pelos procedimentos

Grava_Buffer e Le_Buffer. Essas duas rotinas deverão ser executadas de forma mutuamente

exclusiva.

3.4 Exclusão Mútua por Hardware

As soluções por hardware não podem ser utilizadas por qualquer programa, pois normalmente

requerem o uso de instruções privilegiadas, mas são importantes de serem estudadas porque criam

mecanismos que permitem a implementação das soluções por software.

3.4.1 Desabilitação de Interrupções

Consiste em permitir ao processo do usuário desabilitar interrupções antes de entrar na região

crítica de forma a evitar que o processo seja interrompido dentro da região crítica. Como a mudança

de contexto só pode ser realizada através de interrupções, o processo que as desabilitou terá acesso

exclusivo garantido. O processo deve habilitar novamente as interrupções ao sair da região crítica.

Não é bom que processos de usuários possam desativar interrupções. Além disso, sistemas

com mais de uma UCP só terão uma delas desativada. É um mecanismo útil apenas quando

utilizado pelo kernel.

3.4.2 Instrução TSL (Test and Set Lock)

Consiste em uma instrução especial da UCP que permite ler uma variável, armazenar seu

conteúdo em uma outra e atribuir um novo valor a essa variável. Tal instrução tem como

característica ser sempre executada sem ser interrompida. Assim, não existe a possibilidade de dois

processos estarem manipulando uma variável compartilhada ao mesmo tempo, possibilitando a

implementação da exclusão mútua.

Test_and_Set (X,Y);

A instrução possui o formato acima e quando executada o valor lógico de Y é copiado para X,

sendo atribuído à Y o valor lógico verdadeiro.

PROGRAM Programa_Test_and_Set;

VAR Bloqueio : BOOLEAN;

PROCEDURE Processo_A;

VAR Pode_A : BOOLEAN;

BEGIN

REPEAT

Pode_A := True;

WHILE (Pode_A) Do

Test_and_Set (Pode_A, Bloqueio);

Regiao_Critica_A;

Bloqueio := False;

UNTIL False;

END;

Page 30: Sistemas_Operacionais

Sistemas Operacionais 30

PROCEDURE Processo_B;

VAR Pode_B : BOOLEAN;

BEGIN

REPEAT

Pode_B := True;

WHILE (Pode_B) Do

Test_and_Set (Pode_B, Bloqueio);

Regiao_Critica_B;

Bloqueio := False;

UNTIL False;

END;

BEGIN

Bloqueio := False;

PARBEGIN

Processo_A;

Processo_B;

PAREND;

END.

A solução apresentada acima, apesar de funcionar apresenta o problema de ser uma solução

com espera ativa (espera ocupada), ou seja, apesar do processo não poder continuar sua execução,

ele continua consumindo tempo de UCP desnecessariamente.

3.5 Exclusão Mútua por Software com Espera Ativa

As primeiras soluções por software que surgiram para implementar a exclusão mútua, apesar

de mais simples, apresentam o problema da espera ativa. A seguir são mostradas duas destas

soluções.

3.5.1 Alternância Estrita

Dois processos concorrem de forma alternada por uma região crítica e testam continuamente

uma variável até que seu valor seja o desejado. Assim, eles alternam a ordem de entrada na região

crítica.

Program Alternancia;

var vez: char;

procedure Processo_A;

begin

repeat

while (vez = 'B') do (* nao faz nada *);

Regiao_Critica_A;

vez := 'B';

Processamento_Longo;

until false;

end;

procedure Processo_B;

begin

repeat

while (vez = 'A') do (* nao faz nada *);

Regiao_Critica_B;

vez := 'A';

until false;

end;

begin

vez := 'A';

parbegin

Processo_A;

Processo_B;

parend;

end.

Page 31: Sistemas_Operacionais

Sistemas Operacionais 31

A solução apresentada tem o problema de desperdiçar tempo de UCP (espera ativa), além de

violar as 2a e 3

a condições para uma boa solução.

3.5.2 Solução de Peterson

Peterson apresentou uma solução totalmente por software em 1981.

Program Peterson;

var

CA, CB: Boolean;

Vez: Char;

Procedure Processo_A;

Begin

Repeat

CA := True;

Vez := 'B';

While (CB and Vez='B') do (* faz nada *);

Regiao_Critica_A;

CA := False;

Processamento_A;

Until False;

End;

Procedure Processo_B;

Begin

Repeat

CB := True;

Vez := 'A';

While (CA and Vez='A') do (* faz nada *);

Regiao_Critica_B;

CA := False;

Processamento_B;

Until False;

End;

Begin

CA := False;

CB := False;

ParBegin

Processo_A;

Processo_B;

ParEnd;

End.

3.6 Exclusão Mútua por Software sem Espera Ativa

A espera ativa gasta tempo de UCP e apresenta o problema da inversão de prioridade, que

ocorre quando um processo de maior prioridade deseja entrar em sua região crítica mas não pode

porque outro processo de menor prioridade já está na região crítica. O processo de maior prioridade

não pode entrar na região crítica e o de menor prioridade recebe pouco tempo de UCP para sair da

região crítica.

Para resolver este problema é melhor utilizar mecanismos de sincronização para bloquear um

processo que deseja entrar na região crítica e não pode, evitando que a UCP seja utilizada de forma

desapropriada.

3.6.1 Semáforos

Um semáforo é uma variável inteira que conta sinais enviados a ela. Associadas aos

semáforos existem duas operações especiais: up e down. A operação down decrementa o valor do

semáforo se ele for maior que 0, senão o processo é bloqueado. A operação up incrementa o valor

do semáforo caso não hajam processos que tenham sido bloqueados pela operação down, senão um

processo é desbloqueado.

Page 32: Sistemas_Operacionais

Sistemas Operacionais 32

No caso da exclusão mútua, as instruções down e up funcionam como protocolos para que um

processo possa entrar e sair de sua região crítica. O semáforo fica associado a um recurso

compartilhado, indicando quando o recurso está sendo acessado por um dos processos concorrentes.

Se seu valor for maior que 0, nenhum processo está utilizando o recurso, caso contrário, o processo

fica impedido de acessar o recurso.

Sempre que deseja entrar na sua região crítica um processo executa uma instrução down. Se o

semáforo for maior que 0 ele é decrementado de 1 e o processo que solicitou a operação pode

executar sua região crítica. Se uma instrução down é executada em um semáforo cujo valor seja 0, o

processo que solicitou a operação ficará no estado bloqueado em uma fila associada ao semáforo.

Quando o processo que está acessando o recurso sai de sua região crítica, ele executa uma

instrução up, incrementando o semáforo de 1 e liberando o acesso ao recurso. Se um ou mais

processos estiverem esperando, o sistema escolhe um processo na fila de espera e muda seu estado

para pronto.

As operações up e down são realizadas pelo sistema operacional, que deve garantir que elas

sejam executadas atomicamente.

Exemplo de utilização de semáforos para acesso à região crítica:

Program Semaforo;

Var s: semaforo;

Procedure Processo_A;

Begin

Repeat

Down(s);

Regiao_Critica_A;

Up(s);

Until False;

End;

Procedure Processo_B;

Begin

Repeat

Down(s);

Regiao_Critica_B;

Up(s);

Until False;

End;

Begin

s := 1;

ParBegin

Processo_A;

Processo_B;

ParEnd;

End.

Exemplo de sincronização condicional com semáforos (produtor/consumidor):

PROGRAM Produtor_Consumidor;

CONST TamBuf = (* Tamanho qualquer *); (* Tamanho do buffer *)

TYPE Tipo_Dado = (* Tipo qualquer *);

VAR Vazio : Semaforo; (* Quantas entradas do buffer estao vazias *)

Cheio : Semaforo; (* Quantas entradas do buffer estao ocupadas *)

Mutex : Semaforo; (* Semaforo usado para garantir a exclusao mutua *)

Buffer : ARRAY [1..TamBuf] OF Tipo_Dado;

Dado1, Dado2 : Tipo_Dado;

PROCEDURE Produtor;

BEGIN

REPEAT

Produz_Dado (Dado1);

down (Vazio);

down (Mutex);

Page 33: Sistemas_Operacionais

Sistemas Operacionais 33

Grava_Dado (Dado1, Buffer);

up (Mutex);

up (Cheio);

UNTIL False;

END;

PROCEDURE Consumidor;

BEGIN

REPEAT

down (Cheio);

down (Mutex);

Le_Dado (Dado2, Buffer);

up (Mutex);

up (Vazio);

Consome_Dado (Dado2);

UNTIL False;

END;

BEGIN

Vazio := TamBuf;

Cheio := 0;

Mutex := 1;

PARBEGIN

Produtor;

Consumidor;

PAREND;

END.

Deve-se ter cuidado com o uso de semáforos. No exemplo do produtor/consumidor, se o

produtor fizer down em mutex antes de em vazio e o buffer estiver cheio, o consumidor faz down

em mutex para acessar o buffer e os dois processos bloqueiam. Semáforo devem ser usados com

cuidado.

Pode-se ocultar o mecanismo de interrupções utilizando um semáforo para cada dispositivo de

E/S.

3.6.2 Monitores

O uso de semáforos exige do programador muito cuidado, pois qualquer engano pode levar a

problemas de sincronização imprevisíveis e difíceis de reproduzir. Monitores são mecanismos de

sincronização de alto nível que tentam tornar mais fáceis o desenvolvimento e a correção de

programas concorrentes.

Um monitor é uma coleção de variáveis, procedimentos e estruturas de dados que são

agrupados em um pacote. Em um dado instante somente um processo pode estar ativo em um

monitor. Toda vez que algum processo chama um procedimento do monitor, o monitor verifica se já

existe outro processo executando qualquer procedimento do monitor. Caso exista, o processo ficará

aguardando a sua vez até que tenha permissão para executar.

Variáveis Globais

Procedimento

Procedimento

Procedimento

Procedimento

Código de Inicialização

Page 34: Sistemas_Operacionais

Sistemas Operacionais 34

As variáveis globais do monitor são visíveis apenas a ele e a seus procedimentos. O bloco de

comandos do monitor é responsável por inicializar essas variáveis, sendo executado apenas urna vez

na ativação do programa onde está declarado o monitor.

Cabe ao compilador implementar as exclusões mútuas em entradas de monitor. A cláusula

synchronized da linguagem Java é um exemplo de implementação de monitores. O programador

transforma regiões críticas em procedimentos de monitor colocando todas as regiões críticas em

forma de procedimentos no monitor. Desta forma o desenvolvimento de programas concorrentes

fica mais fácil.

Para a implementação da sincronização condicional é necessário utilizar variáveis de condição

e duas instruções que operam sobre elas: wait e signal. Por exemplo, quando o produtor não pode

continuar faz wait na variável “full” e fica bloqueado. O consumidor pode então entrar na região

crítica e acordar o produtor com signal. Para não haver mais de um processo na região crítica,

signal tem que ser a última instrução executada na região crítica.

PROGRAM Produtor_Consumidor;

CONST TamBuf = (* Tamanho qualquer *); (* Tamanho do buffer *)

TYPE Tipo_Dado = (* Tipo qualquer *);

VAR Buffer : ARRAY [1..TamBuf] OF Tipo_Dado;

MONITOR Condicional;

VAR Vazio, Cheio : Condition; (* Variaveis de condicao *);

Cont : INTEGER;

PROCEDURE Produz(Dado : Tipo_Dado);

BEGIN

IF (Cont = TamBuf) THEN WAIT (Cheio);

Grava_Dado (Dado, Buffer);

Cont := Cont + 1;

IF (Cont = 1 THEN SIGNAL (Vazio);

END;

PROCEDURE Consome(var Dado : Tipo_Dado);

BEGIN

IF (Cont = 0) THEN WAIT (Vazio);

Le_Dado (Dado, Buffer);

Cont := Cont - 1;

IF (Cont = TamBuf - 1) THEN SIGNAL (Cheio);

END;

BEGIN

Cont := 0;

END; (* Termina monitor *)

PROCEDURE Produtor;

VAR

Dado : Tipo_Dado;

BEGIN

REPEAT

Produz_Dado (Dado);

Condicional.Produz (Dado);

UNTIL False;

END;

PROCEDURE Consumidor;

VAR

Dado : Tipo_Dado;

BEGIN

REPEAT

Condicional.Consome (Dado);

Consome_Dado (Dado);

UNTIL False;

END;

BEGIN

PARBEGIN

Produtor;

Consumidor;

Page 35: Sistemas_Operacionais

Sistemas Operacionais 35

PAREND;

END.

3.6.3 Troca de Mensagens

A troca de mensagens é um mecanismo de comunicação e sincronização entre processos sem

a necessidade de utilização de memória compartilhada. Ela é implementada pelo sistema

operacional através das rotinas send e receive. A rotina send é responsável por enviar uma

mensagem para um processo receptor enquanto a rotina receive é responsável por receber uma

mensagem de um processo.

send (Receptor, Mensagem);

receive (Transmissor, Mensagem);

Os procedimentos send e receive, mesmo não tendo suas execuções mutuamente exclusivas,

permitem tanto a comunicação entre processos quanto a sincronização entre eles.

Um problema com a troca de mensagens é que uma mensagem de um processo P para outro

processo Q pode ser perdida em algum lugar. Existem três métodos básicos para tratamento dessa

falha:

O sistema operacional é responsável por detectar a ocorrência dessa falha e por enviar

novamente a mensagem.

O processo remetente é responsável por detectar a ocorrência dessa falha e por transmitir

novamente a mensagem.

O sistema operacional é responsável por detectar a ocorrência dessa falha e notifica o

processo remetente de que a mensagem foi perdida.

A detecção de mensagens perdidas não é sempre necessária. Alguns protocolos de rede

especificam que o envio de mensagens não é confiável, enquanto outros garantem o envio.

O método mais comum de detecção de mensagem perdida é baseado em expiração de tempo.

Quando uma mensagem é enviada, uma mensagem de confirmação de recebimento da mensagem é

enviada de volta. O sistema operacional ou um processo pode então especificar um intervalo de

tempo durante o qual ele espera pela chegada da confirmação de recebimento da mensagem. Se a

confirmação não chegar nesse período supõem-se que a mensagem se perdeu e ela é reenviada. No

entanto é possível que ela não tenha se perdido, mas simplesmente demorado mais do que o

esperado. Nesse caso, pode-se ter várias cópias de uma mesma mensagem. Deve existir um

mecanismo para fazer a distinção entre esses vários tipos de mensagens. Esse problema pode ser

resolvido numerando-se as mensagens.

Para que dois processos se comuniquem, eles devem ter uma maneira de se referir um ao

outro.

No mecanismo de comunicação direta, também conhecida como endereçamento direto, cada

processo que queira se comunicar com outro deve usar explicitamente o nome do processo receptor

ou remetente da mensagem. Nesse esquema, as primitivas send e receive são definidas:

send (P, mensagem) → Envia uma mensagem ao processo P.

receive (Q, mensagem) → Recebe uma mensagem do processo Q.

A comunicação direta só permite a comunicação entre dois processos. Seu maior problema é a

necessidade da especificação da identificação dos processos envolvidos na troca de mensagens.

O exemplo produtor/consumidor para este caso é:

Produtor Consumidor REPEAT

Produz_Dado (Dado);

SEND (Consumidor, Dado);

UNTIL False;

REPEAT

RECEIVE (Produtor, Dado);

Consome_Dado (Dado);

UNTIL False;

Page 36: Sistemas_Operacionais

Sistemas Operacionais 36

Esse exemplo exibe uma simetria no endereçamento, tanto o processo remetente quanto o

destinatário precisam usar o nome do outro processo para se comunicar. Uma variante desse

esquema usa uma assimetria no endereçamento, apenas o remetente necessita usar o nome do

receptor. Assim, as primitivas send e receive são definidas:

send (P, mensagem) → Envia uma mensagem ao processo P.

receive (id, mensagem) → Recebe uma mensagem de qualquer processo e a identificação

do processo com o qual a comunicação ocorreu é armazenado na variável id.

Na comunicação indireta, também conhecida como endereçamento indireto, as mensagens

são enviadas e recebidas por intermédio de uma área compartilhada conhecida como caixa postal

(mailbox). Uma caixa postal pode ser vista abstratamente como um lugar no qual processos podem

colocar mensagens e do qual mensagens podem ser retiradas. Nesse esquema um processo pode se

comunicar com outro por intermédio de diversas caixas postais diferentes. Dois processos só podem

se comunicar se compartilharem o uso de alguma caixa postal. As primitivas send e receive são

definidas:

send(A, mensagem) → Envia uma mensagem para a caixa postal A.

receive(A, mensagem) → Recebe uma mensagem da caixa postal A.

A troca de mensagens pode ou não bloquear os processos envolvidos. Basicamente, existem

duas formas de troca de mensagens entre processos: comunicação síncrona e comunicação

assíncrona.

Na comunicação síncrona, quando um processo envia uma mensagem fica esperando até que

o processo receptor leia a mensagem, ou quando um processo tenta receber uma mensagem

permanece esperando até que o processo transmissor envie alguma mensagem. Esse tipo de

comunicação dispensa a necessidade de buffers. Esse mecanismo também é conhecido como

rendezvous.

Na comunicação assíncrona nem o receptor permanece aguardando o envio de uma

mensagem nem o transmissor o seu recebimento. Além da necessidade de buffers para armazenar as

mensagens, deve haver outros mecanismos de sincronização que permitam ao processo identificar

se uma mensagem já foi enviada ou recebida. A grande vantagem da comunicação assíncrona é o

maior paralelismo na execução dos processos.

O programa a seguir ilustra a utilização de troca de mensagens para o produtor/consumidor.

PROGRAM Produtor_Consumidor;

PROCEDURE Produtor;

VAR Msg : Tipo_Msg;

BEGIN

REPEAT

Produz_Mensagem (Msg);

SEND (Msg);

UNTIL False;

END;

PROCEDURE Consumidor;

VAR Msg : Tipo_Msg;

BEGIN

REPEAT

RECEIVE (Msg);

Consome_Mensagem (Msg);

UNTIL Falte;

END;

BEGIN

PARBEGIN

Produtor;

Consumidor;

PAREND;

Page 37: Sistemas_Operacionais

Sistemas Operacionais 37

END.

3.7 Deadlock

Um processo está em deadlock quando está esperando por um evento que nunca ocorrerá.

Essa situação costuma ser consequência do compartilhamento de recursos do sistema entre vários

processos, sendo que cada processo deve ter acesso ao recurso de forma exclusiva.

Sendo A e B dois processos que necessitem dos recursos 1 e 2, suponha que A adquira o

recurso 1 e que B adquira o recurso 2. Se no futuro A precisar do recurso 2 e B precisar do recurso

1, os dois processos entrarão em espera circular, levando ao deadlock.

Para que ocorram situações de deadlock em um sistema, são necessárias pelo menos quatro

condições:

1. Cada recurso só pode estar alocado a um único processo em um determinado instante

(exclusão mútua).

2. Um processo, além dos recursos já alocados, pode estar esperando por outros recursos.

3. Um recurso não pode ser liberado de um processo só porque outros processos desejam o

mesmo recurso (não preempção).

4. Um processo pode ter de esperar por um recurso alocado a outro processo e vice-versa

(espera circular).

3.7.1 Prevenção de Deadlock

Para prevenir a ocorrência de deadlocks basta garantir que uma das quatro condições

necessárias para sua ocorrência nunca se satisfaça.

A ausência da primeira condição (exclusão mútua) certamente acaba com o problema do

deadlock. Entretanto, a falta da exclusão mútua gera inconsistências sérias no nível dos processos e

do sistema.

Na segunda condição, se for possível evitar que os processos que já possuam recursos

garantidos requisitem novos recursos, estará resolvido o problema do deadlock. Uma maneira de

implementar esse mecanismo é, sempre que um processo necessitar de recursos para executar, ele

deve requisitá-los antes de começar sua execução. Se todos os recursos necessários ao processo

estiverem disponíveis o processo poderá alocá-los e iniciar sua execução. Caso contrário, nenhum

recurso será alocado e o processo ficará no estado bloqueado. Esse mecanismo pode produzir um

grande desperdício na utilização dos recursos do sistema. Outro problema decorrente desse

mecanismo é a dificuldade de se determinar o número de recursos que um processo deverá alocar

antes da sua execução. No entanto, o mais grave nesse método é a possibilidade de um processo

sofrer starvation, ou seja, todos os recursos necessários à sua execução nunca estarem disponíveis

ao mesmo tempo.

A terceira condição pode ser evitada quando permitimos que um recurso seja retirado de um

processo no caso de outro processo necessitar do mesmo recurso. A liberação de recursos já

garantidos por um processo pode ocasionar sérios problemas, podendo até fazer o processo perder

todo o processamento até então realizado. Outro problema desse mecanismo é a possibilidade de

um processo sofrer starvation, quando o processo garante alguns recursos necessários à sua

execução e o sistema os libera em seguida.

A última maneira de evitar um deadlock é excluir a possibilidade da espera circular (quarta

condição). Uma forma de implementar esse mecanismo é forçar o processo a ter apenas um recurso

de cada vez. Se necessitar de outro recurso, deve liberar o primeiro. Outra forma de implementar tal

mecanismo é numerar os recursos e forçar que os processos possam alocar os recursos somente em

determinada ordem.

Page 38: Sistemas_Operacionais

Sistemas Operacionais 38

3.7.2 Detecção do Deadlock

Em sistemas que não possuam mecanismos que previnam a ocorrência de deadlocks, é

necessário um esquema de detecção e correção do problema.

Para detectar deadlocks os sistemas operacionais devem manter estruturas de dados capazes

de identificar cada recurso do sistema, o processo que o está alocando e os processos que estão à

espera da liberação do recurso. Toda vez que um recurso é alocado ou liberado por um processo, a

estrutura deve ser atualizada.

Dependendo do tipo de sistema, o ciclo de busca por um deadlock pode variar. Em sistemas

de tempo compartilhado, o tempo de busca pode ser maior, sem comprometer o desempenho e a

confiabilidade do sistema. Sistemas de tempo real, por sua vez, devem constantemente certificar-se

da ocorrência de deadlocks, porém essa maior segurança gera mais overhead no sistema.

3.7.3 Correção do Deadlock

Uma solução bastante utilizada pela maioria dos sistemas operacionais é simplesmente

eliminar um ou mais processos envolvidos no deadlock, eliminando a espera circular.

A eliminação dos processos envolvidos no deadlock pode não ser simples, dependendo do

tipo do recurso envolvido. Se um processo estiver atualizando um arquivo ou imprimindo uma

listagem, o sistema deve garantir que esses recursos sejam liberados sem problemas. Os processos

eliminados não têm como ser recuperados, porém outros processos que estavam em deadlock

poderão prosseguir.

Uma solução menos drástica envolve a liberação de apenas alguns recursos alocados aos

processos para outros processos, até que o ciclo de espera termine. Para que essa solução seja

realmente eficiente, é necessário que o sistema possa suspender um processo, liberar seus recursos

e, após a solução do problema, retornar à execução do processo sem perder o processamento já

realizado. Esse mecanismo é conhecido como rollback e, além do overhead gerado, é muito difícil

de ser implementado por ser bastante dependente da aplicação que está sendo processada.

3.8 Problemas Clássicos de Comunicação Interprocessos

3.8.1 Problema dos Filósofos Comendo

É um problema que visa demonstrar a sincronização entre processos. Consiste em filósofos

sentados em torno de uma mesa redonda, cada um com um prato de macarrão a sua frente. O

macarrão é escorregadio e só pode ser comido se forem usados dois garfos. Os filósofos só pensam

ou comem, e para comer precisam dos dois garfos. Eles só pegam os garfos na hora de comer.

Solução errada:

procedure filosofo(i: integer);

begin

while (true)

begin

pensa;

pega_garfo(ESQ(i));

pega_garfo(DIR(i));

come;

libera_garfo(ESQ(i));

libera_garfo(DIR(i));

end;

end;

Se todos pegarem o garfo esquerdo, todos os processos ficarão bloqueados.

Se colocar as 5 declarações após “pensa” protegidas por um semáforo binário funciona, mas

somente um filósofo come por vez.

Page 39: Sistemas_Operacionais

Sistemas Operacionais 39

Uma solução correta é:

Program Filosofos;

const N=5; PENSANDO=0; FOME=1; COMENDO=2;

var

estado: array [1..N] of integer;

mutex: semaphore;

S: array [1..N] of semaphore;

i: integer;

function ESQ(i:integer): integer;

begin

ESQ := ((i-1) mod N) + 1;

end;

function DIR(i:integer): integer;

begin

DIR := ((i+1) mod N) + 1;

end;

procedure teste(i: integer)

begin

if ((estado[i]=FOME) and (estado[ESQ(i)]<>COMENDO)

and (estado[DIR(i)]<>COMENDO)) then

begin

estado[i] := COMENDO;

up(S[i]);

end;

end;

procedure pega_garfos(i: integer);

begin

down(mutex);

estado[i] := FOME;

teste(i);

up(mutex);

down(s[i]);

end;

procedure larga_garfos(i: integer);

begin

down(mutex);

estado[i] := PENSANDO;

teste(ESQ(i));

teste(DIR(i));

up(mutex);

end;

procedure filosofo(i: integer);

begin

while (true)

begin

pensa;

pega_garfos(i);

come;

larga_garfos(i);

end;

end;

begin

mutex := 1;

for i:=1 to N do S[i] := 0;

for i:=1 to N do estado[i] := PENSANDO;

parbegin

for i:=1 to N do filosofo(i);

end;

end.

Page 40: Sistemas_Operacionais

Sistemas Operacionais 40

3.8.2 Problema dos Leitores e dos Escritores

Visa resolver o problema de atualizar um banco de dados no qual vários processos estão

realizando consultas paralelamente. Procura-se uma solução na qual as consultas possam ser

realizadas em paralelo, ao passo que uma atualização só poderá ocorrer se o processo conseguir

acesso exclusivo ao banco de dados.

Program LeitoresEscritores;

var

mutex, db: semaphore;

rc: integer;

procedure escritor;

begin

while (true)

begin

cria_dados;

down(db);

escreve_banco_dados;

up(db);

end;

end;

procedure leitor;

begin

while (true)

begin

down(mutex);

rc := rc + 1;

if (rc=1) then down(db);

up(mutex);

le_banco_dados;

down(mutex);

rc := rc - 1;

if (rc=0) then up(db);

up(mutex);

usa_dados;

end;

end;

begin

mutex := 1;

db := 1;

rc := 0;

(* Iniciar processos leitores e escritores *)

end.

Problema: Escritor só acessa se nenhum leitor quiser acessar antes.

3.8.3 Problema do Barbeiro Adormecido

Simula uma barbearia onde vários barbeiros estão a disposição dos clientes que entram. Se

não houver cliente, o barbeiro dorme. O primeiro cliente que chega acorda um barbeiro para ter seu

cabelo cortado. O cliente que chega e encontra todos os barbeiros ocupados senta-se em uma das

cadeiras para aguardar até que um barbeiro fique livre. Se não tiver cadeira para sentar o cliente vai

embora.

Program BarbeiroAdormecido;

const CADEIRAS=5;

var

clientes, barbeiros, mutex: semaphore;

esperando: integer;

procedure barbeiro;

begin

while(true)

begin

down(clientes);

Page 41: Sistemas_Operacionais

Sistemas Operacionais 41

down(mutex);

esperando := esperando - 1;

up(barbeiros);

up(mutex);

corta_cabelo;

end;

end;

procedure cliente;

begin

down(mutex);

if (esperando<CADEIRAS) then

begin

esperando := esperando + 1;

up(clientes);

up(mutex);

down(barbeiros);

cabelo_cortado;

end

else

up(mutex);

end;

begin

clientes := 0;

barbeiros := 0;

mutex := 0;

esperando := 0;

(* iniciar processos clientes e barbeiros *)

end.

3.9 Exercícios

1) O que é uma aplicação concorrente?

2) O que é um mecanismo de sincronização?

3) O que é condição de corrida? Por que ela é problemática?

4) O que é região crítica? Qual é a sua finalidade?

5) Por que o mecanismo de exclusão mútua evita a condição de corrida?

6) O que é starvation?

7) Para que serve a sincronização condicional?

8) Como a desabilitação de interrupções pode evitar a condição de corrida?

9) Por que não adianta desabilitar interrupções em sistemas com mais de uma UCP?

10) Por que não é conveniente que o sistema operacional permita que um programa de usuário

possa desabilitar interrupções?

11) O que é espera ativa? Por que ela deve ser evitada?

12) Explique de forma sucinta como é o funcionamento dos semáforos.

13) O mecanismo de semáforo pode ser utilizado para resolver o problema de starvation?

Explique.

14) Como o mecanismo de semáforo pode ser utilizado para resolver o problema da condição

de corrida?

15) Como o mecanismo de semáforo pode ser utilizado para realizar a sincronização

condicional?

16) A utilização errada de semáforos pode levar ao travamento de uma aplicação? Explique.

Page 42: Sistemas_Operacionais

Sistemas Operacionais 42

17) Como é o funcionamento dos monitores como solução para a condição de corrida? Por que

signal deve ser a última instrução executada na região crítica?

18) Por que monitores são de mais fácil utilização pelos programadores do que os semáforos?

19) Para a cooperação entre processos pode ser utilizada memória compartilhada ou troca de

mensagens. Qual a diferença?

20) Explique de forma sucinta como funciona o mecanismo de troca de mensagens.

21) Na troca de mensagens, qual a diferença entre comunicação direta e comunicação indireta?

22) Na troca de mensagens, qual a diferença entre comunicação síncrona e comunicação

assíncrona?

23) No mecanismo de troca de mensagens, o que ocorre quando um processo tenta enviar uma

mensagem ao outro e o canal de comunicação não tem mais espaço para armazenar esta mensagem?

24) O que é deadlock?

25) Qual a diferença entre prevenção de deadlock e detecção do deadlock?

26) Cite uma forma de corrigir o deadlock.

Page 43: Sistemas_Operacionais

Sistemas Operacionais 43

4 Gerência do Processador

A multiprogramação tem como objetivo permitir que, a todo instante, haja algum processo

sendo executado para maximizar a utilização da UCP.

O conceito que possibilitou a implementação de sistemas multiprogramáveis foi a

possibilidade da UCP ser compartilhada entre diversos processos. Portanto, deve existir um critério

para determinar qual a ordem na escolha dos processos para execução dentre os vários que

concorrem pela UCP.

O procedimento de seleção é conhecido como escalonamento (scheduling). A parte do

sistema operacional responsável pelo escalonamento é o escalonador (scheduler), as vezes

chamado de agendador. Sempre que a UCP se torna ociosa o escalonador seleciona um processo,

dentre aqueles que estão na memória prontos para serem executados, e aloca a UCP para que ele

possa ser executado.

Os principais objetivos do escalonamento são:

Manter a UCP ocupada a maior parte do tempo;

Balancear a utilização da UCP entre os processos;

Maximizar o throughput do sistema;

Oferecer tempos de resposta razoáveis para os usuários interativos.

Esses objetivos devem ser atendidos de forma que nenhum processo fique esperando

indefinidamente pela utilização do processador (starvation).

A principal função de um algoritmo de escalonamento é decidir qual dos processos prontos

deve ser alocado à UCP. Os principais critérios de escalonamento são:

Imparcialidade → Oferecer uma fatia justa da UCP a cada processo.

Utilização da UCP → É desejável que a UCP passe a maior parte de seu tempo ocupada.

Throughput (produtividade) → Representa o número de processos executados durante um

intervalo de tempo.

Tempo de processamento (turnaround) → Tempo que um processo leva desde a sua

admissão no sistema até o seu término.

Tempo de resposta → Em sistemas interativos, é o tempo desde o momento da submissão

de um pedido até a primeira resposta produzida.

Tempo de espera → Tempo que um processo fica esperando na fila de processos prontos.

Algumas destas metas são contraditórias. Sempre que se favorece alguma meta outra é

prejudicada. Outra complicação é que o comportamento dos processos é imprevisível.

O algoritmo de escalonamento não é o único responsável pelo tempo de execução de um

processo. Ele afeta somente o tempo de espera na fila de processos prontos.

Os algoritmos de escalonamento podem ser classificados como preemptivos ou

não-preemptivos. Quando o sistema pode interromper um processo durante sua execução para

colocá-lo no estado pronto e colocar outro processo no estado executando, tem-se um sistema

preemptivo. Senão tem-se um sistema não-preemptivo.

O escalonamento preemptivo permite que o sistema dê atenção imediata a processos mais

prioritários, além de proporcionar melhor tempo de resposta em sistemas de tempo compartilhado.

Outro benefício decorrente é o compartilhamento do processador de uma maneira mais uniforme.

Page 44: Sistemas_Operacionais

Sistemas Operacionais 44

A troca de um processo por outro na UCP (mudança de contexto) causada pela preempção,

gera um overhead ao sistema. Para isto não se tornar crítico, o sistema deve estabelecer

corretamente os critérios de preempção.

Sistemas que usam escalonamento preemptivo têm o problema da condição de corrida, o que

não ocorre com sistemas que usam escalonamento não-preemptivo. No escalonamento não-

preemptivo, quando um processo ganha o direito de utilizar a UCP, nenhum outro processo pode

lhe tirar esse recurso.

4.1 Algoritmos de Escalonamento

4.1.1 Escalonamento First In First Out (FIFO)

Nesse escalonamento, o processo que chegar primeiro é o primeiro a ser selecionado para

execução. É necessária apenas uma fila, onde os processos que passam para o estado pronto entram

no seu final. Quando um processo ganha a UCP, ele a utiliza sem ser interrompido, caracterizando-o

como um algoritmo não-preemptivo.

O problema do escalonamento FIFO é a impossibilidade de se prever quando um processo

terá sua execução iniciada. Outro problema é a possibilidade de processos CPU-bound de menor

importância prejudicarem processos I/O-bound mais prioritários.

Este algoritmo foi inicialmente implementado em sistemas batch.

4.1.2 Escalonamento Shortest Job First (SJF)

Esse algoritmo associa a cada processo seu tempo de execução. Quando a UCP está livre, o

processo no estado pronto que precisar de menos tempo para terminar é selecionado para execução.

O escalonamento SJF beneficia processos que necessitam de pouco processamento e reduz o

tempo médio de espera em relação ao FIFO. O problema é determinar quanto tempo de UCP cada

processo necessita para terminar seu processamento. Em ambientes de produção é possível estimar

o tempo de execução, mas em ambientes de desenvolvimento é muito difícil.

É um algoritmo de escalonamento não-preemptivo e, assim como o FIFO, também foi

utilizado nos primeiros sistemas operacionais com processamento batch.

4.1.3 Escalonamento Cooperativo

Tanto o SJF quanto o FIFO não são algoritmos de escalonamento aplicáveis a sistemas de

tempo compartilhado, onde um tempo de resposta razoável deve ser garantido a usuários

interativos.

No escalonamento cooperativo, quando um processo já está em execução a um determinado

tempo, ele voluntariamente libera a UCP retornando para a fila de processos prontos.

Sua principal característica está no fato da liberação da UCP ser uma tarefa realizada

exclusivamente pelo processo em execução, que a libera para um outro processo. Não existe

nenhuma intervenção do sistema operacional na execução do processo. Isto pode ocasionar sérios

problemas na medida em que um programa pode não liberar a UCP ou um programa mal escrito

pode entrar em loop, monopolizando a UCP.

É um algoritmo de escalonamento não-preemptivo.

4.1.4 Escalonamento Circular (Round Robin)

Esse algoritmo é bem semelhante ao FIFO. Entretanto, quando um processo passa para o

estado executando, existe um tempo limite (conhecido como time-slice ou quantum) para utilização

da UCP de forma contínua. Quando esse tempo expira o processo volta ao estado pronto, dando a

vez para outro processo.

Page 45: Sistemas_Operacionais

Sistemas Operacionais 45

A fila de processos no estado pronto é tratada como uma fila circular. O escalonamento é

realizado alocando a UCP para cada processo da fila no intervalo de tempo determinado pelo

quantum.

Se o quantum for muito pequeno gasta-se muito tempo de UCP com trabalho administrativo.

Se o quantum for muito grande a interatividade fica prejudicada, já que um processo que sai de

execução pode demorar muito a voltar. Em geral, o quantum varia de 10 a 100 ms.

Através do escalonamento circular, nenhum processo poderá monopolizar a UCP,

caracterizando-o como um algoritmo de escalonamento preemptivo.

Um problema com o escalonamento circular é que ele não oferece qualquer tratamento

diferenciado a processos I/O-bound. Assim processos CPU-bound terão a tendência de monopolizar

a utilização da UCP enquanto processos I/O-bound permanecem a espera.

4.1.5 Escalonamento por Prioridade

O escalonamento circular consegue melhorar a distribuição do tempo de UCP em relação aos

escalonamentos não-preemptivos, porém ainda não consegue implementar um compartilhamento

equitativo entre os diferentes tipos de processos.

Para solucionar esse problema, os processos I/O-bound devem levar alguma vantagem no

escalonamento, a fim de compensar o tempo excessivo gasto no estado bloqueado. Como alguns

processos devem ser tratados de maneira diferente dos outros, é preciso associar a cada um deles

uma prioridade de execução. Assim, processos de maior prioridade são escalonados

preferencialmente.

A preempção por prioridade é implementada mediante um relógio que interrompe o

processador periodicamente para que a rotina de escalonamento reavalie prioridades e,

possivelmente, escalone outro processo, caracterizando-o como um algoritmo de escalonamento

preemptivo.

Todos os sistemas de tempo compartilhado implementam algum esquema de prioridade. A

prioridade é uma característica do contexto de software de um processo, podendo ser estática ou

dinâmica.

Tem-se a prioridade estática quando a prioridade não é modificada durante a existência do

processo. Apesar da simplicidade de implementação, a prioridade estática pode ocasionar tempos de

resposta elevados.

Na prioridade dinâmica, a prioridade do processo pode ser ajustada de acordo com o tipo de

processamento realizado pelo processo e/ou a carga do sistema. Quando o processo sai do estado

bloqueado, recebe um acréscimo à sua prioridade. Dessa forma, os processos I/O-bound terão mais

chance de ser escalonados e compensar o tempo que passam no estado bloqueado.

Para evitar que processos com maior prioridade executem indefinidamente, a prioridade é

diminuída com o passar do tempo.

Uma outra forma de se obter prioridade dinâmica é fazer com que o quantum do processo seja

inversamente proporcional à fração do último quantum utilizado.

Embora os sistemas de prioridade dinâmica sejam mais complexos e gerem um overhead

maior, o tempo de resposta oferecido compensa.

A B C D preempção

Processo atual

Próximo processo

B C D A

Processo atual

Próximo processo

Page 46: Sistemas_Operacionais

Sistemas Operacionais 46

4.1.6 Escalonamento por Múltiplas Filas

Como os processos de um sistema possuem diferentes características de processamento, é

difícil que um único mecanismo de escalonamento seja adequado a todos. Uma boa política seria

classificar os processos em função do tipo de processamento realizado e aplicar a cada grupo

mecanismos de escalonamentos distintos.

O escalonamento por múltiplas filas implementa diversas filas de processo no estado pronto,

onde cada processo é associado exclusivamente a uma delas. Cada fila possui um mecanismo

próprio de escalonamento, em função das características do processo. Nesse esquema, os processos

devem ser classificados, previamente, em função do tipo de processamento, para poderem ser

encaminhados a uma determinada fila.

Cada fila possui uma prioridade associada, que estabelece quais filas são prioritárias em

relação às outras. O sistema só pode escalonar processos de uma fila se todas as outras de prioridade

maior estiverem vazias.

Para exemplificar esse escalonamento, considere que os processos, em função de suas

características, sejam divididos em três grupos: sistema, interativo e batch. Os processos do sistema

devem ser colocados em uma fila de prioridade superior à dos outros processos, implementando um

algoritmo de escalonamento baseado em prioridades. Os processos de usuários interativos devem

estar em uma fila de prioridade intermediária, implementando, por exemplo, o escalonamento

circular. O mesmo mecanismo de escalonamento pode ser utilizado na fila de processos batch, com

a diferença de que esta fila deverá possuir uma prioridade mais baixa.

4.1.7 Escalonamento por Múltiplas Filas com Realimentação

No escalonamento por múltiplas filas os processos são previamente associados a uma

determinada fila. No caso de um processo que altere o seu comportamento no correr do tempo, esse

esquema é falho, pois o processo não poderá ser redirecionado para uma outra fila mais adequada.

O escalonamento por múltiplas filas com realimentação também implementa diversas filas,

onde cada qual tem associada uma prioridade de execução, porém os processos não permanecem

obrigatoriamente em uma mesma fila até o término do processamento.

Esse esquema permite que os processos sejam redirecionados entre as filas do sistema,

fazendo com que o sistema operacional implemente um mecanismo de ajuste dinâmico (mecanismo

adaptativo) que tem como objetivo ajustar os processos em função de seu comportamento.

Um processo, ao ser criado, entra no final da fila de mais alta prioridade. Quando um processo

em execução deixa a UCP, seja por preempção por prioridade ou por solicitação a um recurso do

sistema, ele é reescalonado dentro da mesma fila. Caso o processo esgote seu quantum, ele é

redirecionado para uma fila de menor prioridade.

O quantum em cada fila varia em função da sua prioridade. Quanto maior a prioridade da fila,

menor é o seu quantum. Assim, o quantum dos processos não é estático, variando em função da fila

na qual ele se encontra.

Essa política de escalonamento atende as necessidades dos diversos tipos de processos. No

caso de processos I/O-bound, ela oferece um bom tempo de resposta, já que esses processos têm

prioridades altas por permanecerem a maior parte do tempo nas filas de mais alta ordem. No caso de

processos CPU-bound, a tendência é de que, ao entrar na fila de mais alta prioridade, o processo

ganhe a UCP, gaste seu quantum de tempo e seja direcionado para uma fila de menor prioridade.

O maior problema deste algoritmo é que por sua complexidade ele pode gerar um grande

overhead ao sistema.

Page 47: Sistemas_Operacionais

Sistemas Operacionais 47

4.1.8 Escalonamento em Sistemas de Tempo Real

Sistemas de tempo real devem ser utilizados quando existem aplicações que necessitem dar

respostas imediatas a alguns eventos do sistema.

O escalonador deve levar em conta a a importância relativa da tarefa para o sistema e tratá-la

com a prioridade adequada. Assim, o algoritmo de escalonamento por prioridade é o mais adequado

para sistemas deste tipo. Não deve haver o conceito de fatia de tempo e a prioridade de cada

processo deve ser estática.

4.2 Escalonamento com Múltiplas UCPs

O mecanismo de escalonamento para sistemas com múltiplas UCPs é bem mais complexo que

com uma única UCP. A abordagem é diferenciada para sistemas fracamente acoplados ou

fortemente acoplados.

Em sistemas fracamente acoplados, cada UCP faz seu próprio escalonamento local. O sistema

possui, além da UCP, sua memória principal, sistema operacional, algoritmo de escalonamento e

sua própria fila de processos prontos para execução.

Se várias UCPs idênticas estiverem disponíveis em um sistema fortemente acoplado, seria

possível usar uma fila de processos prontos separada para cada UCP. Entretanto uma UCP poderia

ficar ociosa, enquanto outra estivesse muito ocupada. Assim, implementa-se uma única fila de

processos prontos para todas as UCPs. Todos os processos estão presentes nesta única fila e são

escalonados no primeiro processador disponível. Como a memória é única para todas as UCPs e

todos os programas, não faz diferença em qual UCP a execução ocorrerá. Este caso em que não

importa em qual das UCPs se dará a execução do processo é conhecido como multiprocessamento

simétrico.

Nesta solução é importante que seja implementada a exclusão mútua. No caso de mais de uma

UCP tornar-se disponível em um mesmo instante, não pode haver a possibilidade de um mesmo

processo ser escalonado por duas UCPs diferentes.

Uma outra solução é designar uma UCP que executa o algoritmo de escalonamento, sendo ela

a responsável por determinar a tarefa de cada UCP do sistema.

4.3 Exercícios

1) Qual a função do escalonador de um sistema operacional?

2) No que se refere à gerência do processador, o que diferencia um sistema operacional

preemptivo de um sistema operacional não-preemptivo?

3) Por que um algoritmo de escalonamento preemptivo pode levar ao problema da condição de

corrida?

4) Por que no algoritmo de escalonamento FIFO processos CPU-bound de menor importância

podem prejudicar processos I/O-bound prioritários?

5) Como o algoritmo de escalonamento SJF reduz o tempo médio de espera em relação ao

FIFO?

6) Como funciona o escalonamento cooperativo? Ele é preemptivo? Explique.

7) Como é possível que um processo consiga utilizar a UCP sem dar a chance de outro

processo entrar em execução quando se usa escalonamento cooperativo?

8) O que é quantum para um algoritmo de escalonamento preemptivo?

9) O que diferencia o escalonamento circular (Round Robin) do escalonamento por prioridade?

Page 48: Sistemas_Operacionais

Sistemas Operacionais 48

10) Para o escalonamento circular, o que é quantum?

11) Por que um valor alto para quantum prejudica a interatividade?

12) Como o sistema operacional faz para garantir que o escalonamento circular seja

preemptivo?

13) Por que processos I/O-bound podem ser prejudicados no escalonamento circular? Como o

escalonamento por prioridade resolve este problema?

14) Explique sucintamente como funciona um algoritmo de escalonamento por prioridade.

15) Qual a diferença entre prioridade estática e prioridade dinâmica para um algoritmo de

escalonamento por prioridade?

16) Como o escalonador determina o próximo processo a entrar em execução em um sistema

de tempo real?

17) Como o escalonador de tempo real trata o quantum?

18) Em um sistema com múltiplas UCPs, é possível que um processo que saiu de execução em

uma UCP, quando volte ao estado pronto, entre em execução em outra UCP? Explique.

19) O que é multiprocessamento simétrico?

Page 49: Sistemas_Operacionais

Sistemas Operacionais 49

5 Gerência de Memória

Na memória principal residem todos os programas e dados que serão executados ou

referenciados pela UCP. Toda vez que for ser executado um programa residente na memória

secundária deve-se carregá-lo na memória principal.

Enquanto nos sistemas monoprogramáveis a gerência da memória não é muito complexa, nos

sistemas multiprogramáveis ela se torna crítica, uma vez que o sistema operacional deve gerenciar o

acesso dos vários processos à memória de forma concorrente.

O gerenciador de memória é a parte do sistema operacional que gerencia a hierarquia de

memória. Seu trabalho consiste em:

Controlar que parte da memória está em uso.

Alocar memória para os processos.

Gerenciar a troca entre a memória principal e o disco.

5.1 Atribuição de Endereços

Um programa é armazenado em disco como um arquivo binário executável. Para ser

executado ele deve ser colocado em memória como parte de um processo.

Os sistemas operacionais atuais permitem que os processos dos usuários sejam colocados em

qualquer parte da memória. Assim, embora o espaço de endereçamento do computador comece no

endereço zero, este espaço não precisa ser o endereço inicial de um processo. Os endereços usados

nos programas podem ser representados de maneiras diferentes. No programa fonte os endereços

costumam ser simbólicos, representados por nomes de variáveis. O compilador normalmente

associa este endereço simbólico a um endereço relativo, como uma posição a partir do início do

programa. O loader por sua vez transforma endereços relativos em endereços absolutos.

A atribuição de endereços a instruções ou dados pode ser feita:

Em tempo de compilação → A posição de memória em que o programa será armazenado

é conhecida durante a compilação. Se precisar mudar o programa deve ser recompilado.

Em tempo de carga → A atribuição final de endereços absolutos é feita durante a carga

do programa em memória.

Em tempo de execução → Quando um programa durante sua execução, pode ser

transferido de um local para outro na memória. A atribuição de endereços deve ser feita

somente durante a execução.

5.2 Carregamento Dinâmico

No carregamento dinâmico o programa é dividido em um programa principal e uma série de

rotinas que ficam armazenadas em disco. Para a execução o programa principal é carregado na

memória. Quando uma rotina é chamada, ela é carregada na memória.

A vantagem do carregamento dinâmico é que uma rotina não usada nunca é carregada na

memória.

O carregamento dinâmico não requer obrigatoriamente suporte especial do sistema

operacional, mas o sistema operacional pode fornecer chamadas de sistema para facilitar sua

implementação.

Page 50: Sistemas_Operacionais

Sistemas Operacionais 50

5.2.1 Ligação Dinâmica

O conceito de ligação dinâmica é similar ao de carregamento dinâmico, só que na ligação

dinâmica é a ligação (linkedição) que é realizada em tempo de execução.

Este recurso é comumente utilizado para bibliotecas do sistema. Sua ausência obriga que

todos os programas do sistema tenham uma cópia das rotinas do sistema que utiliza, gerando um

grande desperdício tanto de espaço em disco quanto de memória. Com este mecanismo é incluído

no código executável do programa um código para ligação dinâmica, que indica onde e como

armazenar uma rotina de biblioteca.

Este recurso pode ser estendido para casos em que ocorrem mudanças no código da

biblioteca. Uma biblioteca pode ser substituída por uma nova versão e todos os programas que a

usam passarão automaticamente a usar a nova versão.

Diferentemente do carregamento dinâmico, a ligação dinâmica costuma requerer algum

suporte do sistema operacional.

5.3 Alocação Contígua Simples

Nesse esquema a memória principal é dividida em duas partes: uma para o sistema

operacional e outra para o programa do usuário. Assim o programador deve criar suas aplicações,

preocupado apenas em não ultrapassar o espaço de memória disponível.

O usuário tem controle sobre toda a memória principal, podendo acessar qualquer posição de

memória. Para proteger o sistema operacional, alguns sistemas implementam proteção através de

registradores, que delimitam as áreas do sistema operacional e do usuário. Assim, sempre que um

programa de usuário faz referência a um endereço na memória, o sistema verifica se o endereço está

nos seus limites.

A principio, os programas dos usuários estavam limitados ao tamanho da memória principal

disponível. A solução encontrada foi dividir o programa em partes, de forma que pudessem executar

independentemente uma da outra, utilizando uma mesma área de memória. Essa técnica é chamada

de overlay (sobreposição).

A definição das áreas de overlay é função do programador. O tamanho de uma área de overlay

será estabelecido a partir do tamanho do maior módulo. A técnica de overlay tem a vantagem de

permitir ao programador expandir os limites da memória principal, mas pode trazer implicações

quanto ao desempenho das aplicações pela excessiva transferência entre as memórias principal e

secundária.

5.4 Alocação Particionada

Nos sistemas monoprogramáveis o processador permanece ocioso e a memória é subutilizada,

enquanto o programa aguarda por algum evento.

Para a multiprogramação ser eficiente é necessário que vários programas tenham a

possibilidade de estarem na memória principal ao mesmo tempo.

5.4.1 Alocação Particionada Estática

Nos primeiros sistemas multiprogramáveis, a memória foi dividida em pedaços de tamanho

fixo, chamados partições. O tamanho das partições era estabelecido na fase de inicialização do

sistema. Sempre que fosse necessária a alteração do tamanho de uma partição o sistema deveria ser

reinicializado com uma nova configuração.

Os programas só podiam executar em uma das partições, mesmo se outras estivessem

disponíveis, por causa dos compiladores e montadores que geravam apenas código absoluto. A esse

tipo de alocação chamou-se alocação particionada estática absoluta.

Page 51: Sistemas_Operacionais

Sistemas Operacionais 51

Com a evolução dos compiladores, linkers e loaders, a geração de código relocável foi

possível e os programas puderam ser carregados em qualquer partição. Assim foi criado um novo

tipo de organização, denominado alocação particionada estática relocável. Neste esquema o

endereço base é determinado durante o carregamento do programa.

Para manter o controle sobre quais partições estavam alocadas ou não, os sistemas possuíam

uma tabela delimitando cada partição, seu tamanho e se estava em uso ou não.

Nesse esquema de alocação de memória, a proteção baseia-se em dois registradores, que

indicam os limites inferior e superior da partição onde o programa está sendo executado.

Tanto nos sistemas de alocação absoluta quanto nos de alocação relocável, os programas

normalmente não preenchiam totalmente as partições onde eram carregados. Além disso, se um

programa for maior que qualquer partição livre, ele ficará aguardando uma que o acomode, mesmo

que existam partições adjacentes que somadas totalizem o tamanho do programa. Esse tipo de

problema é conhecido como fragmentação interna.

5.4.2 Alocação Particionada Dinâmica

A alocação particionada estática leva ao problema da fragmentação, diminuindo a capacidade

de compartilhamento de memória.

Na alocação particionada dinâmica não existe o conceito de partições de tamanho fixo. Nesse

esquema, cada processo cria uma partição com o tamanho que necessita.

A fragmentação começará a ocorrer quando os programas forem terminando e deixando

espaços na memória, não permitindo o ingresso de novos programas. Este tipo de fragmentação é

denominada fragmentação externa.

Depois de detectada a fragmentação, existem duas soluções para o problema. Na primeira, os

espaços adjacentes são reunidos, produzindo um único espaço de tamanho maior. A segunda

solução envolve a relocação de todas as partições ocupadas, eliminando todos os espaços entre elas.

Esse mecanismo de compactação, também conhecido como alocação dinâmica com

relocação, reduz o problema da fragmentação, mas a complexidade do seu algoritmo e o consumo

de recursos do sistema podem torná-lo inviável.

5.4.3 Estratégias para Escolha da Partição

Existem basicamente cinco estratégias para determinar em qual partição livre um programa

será carregado: primeiro ajuste, próximo ajuste, melhor ajuste, pior ajuste e ajuste rápido.

Memória Principal

Partição onde o programa está sendo

executado

Endereço inicial

Endereço final

Sistema Operacional

Sistema Operacional

Processo A

Processo B

Processo C

Processo D

2 MB

3 MB

4 MB

2 MB

1 MB

Sistema Operacional

12 MB

A B C D

3 MB2 MB 4 MB 2 MB

Page 52: Sistemas_Operacionais

Sistemas Operacionais 52

A melhor estratégia a ser adotada depende do tamanho dos programas processados no

ambiente. Independentemente do algoritmo utilizado, o sistema deve possuir uma relação de áreas

livres com o endereço e o tamanho de cada uma.

Na representação com lista encadeada é mantida uma lista encadeada com os segmentos

alocados e com os segmentos livres. A manutenção é mais rápida se a lista for duplamente

encadeada.

Mantendo a lista classificada por endereço, quando um processo termina, a área que ele

ocupava pode se juntar a uma ou duas lacunas adjacentes formando uma só lacuna.

5.4.3.1 Algoritmo do Primeiro Ajuste (First-fit)

Esse algoritmo escolhe a primeira partição livre de tamanho suficiente para carregar o

processo. Ele costuma ser muito rápido por pesquisar o mínimo possível.

Nesse algoritmo a lista de áreas livres costuma estar ordenada por endereço.

5.4.3.2 Algoritmo do Próximo Ajuste

É semelhante ao algoritmo do primeiro ajuste, mas inicia sua procura a partir do ponto em que

parou na procura anterior.

5.4.3.3 Algoritmo do Melhor Ajuste (Best-fit)

Esse algoritmo escolhe a partição em que o processo deixa o menor espaço sem utilização.

Nesse algoritmo, se a lista de áreas livres não estiver ordenada por tamanho, o tempo de busca por

uma área desocupada pode comprometer seu desempenho.

Uma desvantagem desse método é que é alocada a partição que deixa a menor área livre,

existindo uma tendência de que a memória fique com pequenas áreas não contíguas, aumentando o

problema da fragmentação.

5.4.3.4 Algoritmo do Pior Ajuste (Worst-fit)

Esse algoritmo escolhe a partição em que o processo deixa o maior espaço sem utilização. Ao

utilizar as partições maiores, são deixados espaços livres maiores que permitem a um maior número

de programas utilizar a memória, diminuindo o problema da fragmentação. Simulações mostraram

que este algoritmo não traz bons resultados.

5.4.3.5 Algoritmo do Ajuste Rápido

Este algoritmo mantém listas separadas para alguns tamanhos mais comuns. Ele também gera

lacunas pequenas, mas é extremamente rápido na alocação de memória.

Simulações mostraram que dentre os algoritmos acima o que obteve melhores resultados foi o

algoritmo do primeiro ajuste, sendo o mais rápido e o que consome menos recursos do sistema.

A lista de lacunas pode ser mantida ordenada de formas diferentes a fim de beneficiar o

algoritmo que estiver em uso.

Um outro aspecto a ser levado em conta é que processos podem crescer. Portanto é bom

deixar lacunas entre os processos para que seja permitido o seu crescimento.

...A B C D E

PA 0 5 L 5 3 PB 8 6 PC 14 4

L 18 2 PD 20 6 PE 26 3 L 29 3

Lista encadeada

Page 53: Sistemas_Operacionais

Sistemas Operacionais 53

5.5 Swapping

Mesmo com o aumento da eficiência da gerência de memória, muitas vezes um programa não

podia ser executado por falta de uma partição livre disponível. A técnica de swapping veio tentar

resolver o problema da insuficiência de memória.

Nos esquemas anteriores, um processo permanecia na memória principal até o final da sua

execução. Eles funcionam bem para sistemas em lote, mas para sistemas de tempo compartilhado

uma nova estratégia deve ser adotada, uma vez que pode não haver memória suficiente para

armazenar todos os processos.

O swapping é uma técnica aplicada à gerência de memória para programas que esperam por

memória livre para serem processados. O sistema escolhe um processo que é levado da memória

principal para o disco (swap out), retornando posteriormente para a memória principal (swap in).

Um dos problemas gerados pelo swapping é a relocação dos processos. No caso de um

processo que sai e volta muitas vezes para a memória, é necessário que a relocação seja realizada

pelo loader a cada carregamento. Essa situação torna o mecanismo ineficiente em função do tempo

gasto para o carregamento.

A melhor solução é uma implementação em hardware para permitir que a relocação seja

realizada durante a execução do programa. Esse tipo de mecanismo é denominado relocação

dinâmica.

A relocação dinâmica é realizada através de um registrador especial denominado registrador

de relocação. No momento em que o processo é carregado na memória, o registrador recebe o

endereço inicial da região de memória que o processo irá ocupar. Toda vez que ocorrer uma

referência a algum endereço, o endereço contido na instrução será somado ao conteúdo do

registrador, gerando o endereço físico. Assim um programa pode ser carregado em qualquer região

da memória.

Quando o swap cria muitas lacunas pode-se ganhar espaço movendo os processos para

realizar a compactação de memória. A compactação costuma ser evitada por tomar muito tempo da

UCP.

Se o processo precisar alocar memória e existir uma lacuna entre ele e o processo adjacente, é

alocado espaço desta lacuna. Se tal lacuna não existir o processo deve ser movido para uma lacuna

com espaço suficiente ou então deve ser realizada uma compactação de memória. Se não existe

espaço na memória nem em disco o processo deve esperar ou ser eliminado.

O conceito de swapping permitiu um maior compartilhamento da memória assim como um

maior throughput. A técnica se mostrou eficiente para sistemas com poucos usuários em ambientes

com aplicações pequenas. Seu maior problema é o elevado custo das operações de entrada/saída.

O CTSS do MIT e o OS/360 da IBM são exemplos de sistemas operacionais que

implementam tal técnica.

5.6 Memória Virtual

Memória virtual é uma técnica onde as memórias principal e secundária são combinadas,

dando ao usuário a ilusão de existir uma memória maior que a memória principal.

Até então quando o programa era muito grande para caber na memória principal empregava-

se a técnica de overlay, ficando a cargo do programador a divisão do programa em módulos. Com a

técnica de memória virtual cabe ao sistema operacional dividir o programa em partes e manter em

memória somente as partes do programa necessárias à execução do mesmo. As outras partes são

mantidas na memória secundária.

Page 54: Sistemas_Operacionais

Sistemas Operacionais 54

Isto é muito útil porque verifica-se que, em muitos casos, nem todo o código do programa é

executado. Por exemplo:

Código para tratamento de condições de erro.

Tamanho da área de memória alocada por listas e tabelas que não são sempre totalmente

utilizadas.

Opções e recursos de programas que raramente são utilizados.

Mesmo quando todo o código de um programa é usado, ele não costuma ser usado

inteiramente ao mesmo tempo.

Ainda, a possibilidade de executar um programa armazenado apenas parcialmente na

memória traz benefícios como:

A área de memória disponível ao programa não fica limitada ao tamanho da memória

física disponível.

Um número maior de programas pode ser executado simultaneamente.

Uma menor quantidade de operações de E/S é necessário para carregar programas na

memória.

O conceito de memória virtual está baseado em desvincular o endereçamento feito pelo

processo dos endereços físicos da memória principal. Assim, os processos deixam de estar limitados

ao tamanho da memória física disponível.

A memória virtual também minimiza o problema da fragmentação da memória.

5.6.1 Espaço de Endereçamento Virtual

Quando se utiliza memória virtual um processo não faz referência a endereços físicos

(endereços reais), apenas a endereços virtuais. No momento da execução de uma instrução, o

endereço virtual é traduzido para um endereço físico. O mecanismo de tradução de um endereço

virtual para um endereço físico é denominado mapeamento.

O conjunto de endereços virtuais que os processos podem endereçar é chamado espaço de

endereçamento virtual, enquanto o conjunto de endereços reais é chamado espaço de

endereçamento real.

O espaço de endereçamento virtual não tem nenhuma relação direta com os endereços no

espaço real. Como os processos podem ser muito maiores que a memória física, apenas parte deles

pode estar residente na memória em um determinado instante. O sistema operacional utiliza a

memória secundária como extensão da memória principal.

Quando um usuário desenvolve uma aplicação cabe ao compilador e ao linker gerar o código

executável em função dos endereços virtuais, e ao sistema operacional cuidar dos detalhes durante a

execução.

5.6.2 Paginação

A paginação é uma técnica de gerência de memória onde o espaço de endereçamento virtual e

o espaço de endereçamento real são divididos em blocos do mesmo tamanho, chamados páginas.

Memória virtual Memória principal

Memória secundária

Page 55: Sistemas_Operacionais

Sistemas Operacionais 55

As páginas no espaço virtual são denominadas páginas virtuais, enquanto as páginas no espaço real

são chamadas de páginas reais ou molduras de página (frames). Em geral as páginas variam de

512 bytes a 64 Kbytes.

Todo o mapeamento é realizado por intermédio de tabelas de páginas. Cada página virtual

possui uma entrada na tabela contendo informações de mapeamento que permitem ao sistema

localizar a página real correspondente.

Quando um processo entra no estado executando as páginas virtuais são transferidas da

memória secundária para a memória principal e colocadas em molduras de página. Sempre que o

processo fizer referência a um endereço virtual o mecanismo de mapeamento localizará na tabela de

páginas o endereço físico da moldura de página.

Nesse sistema, o endereço virtual é formado pelo número da página virtual e um

deslocamento dentro da página. O número da página virtual identifica unicamente uma página

virtual na tabela de páginas, enquanto o deslocamento funciona como índice dentro da página. O

endereço físico é calculado somando-se o endereço da moldura de página (localizada na tabela de

páginas) com o deslocamento (contido no endereço virtual).

Além da informação sobre a localização da página virtual a tabela de páginas possui outras

informações como o bit presente/ausente, que indica se uma página está ou não na memória física.

Sempre que o processo faz referência a um endereço virtual, o sistema verifica, através do bit

presente/ausente, se a página que contém o endereço referenciado está ou não na memória principal.

Caso não esteja, o sistema tem que transferir a página da memória secundária para a memória

principal. Toda vez que isso ocorre diz-se que ocorreu um falha de página (page fault).

Quando ocorre uma falha de página uma interrupção é gerada para passar o controle da UCP

ao sistema operacional. O sistema operacional então seleciona uma moldura de página (frame) para

liberar e, se necessário, a grava em disco. Por fim o sistema operacional carrega a página necessária

na moldura de página que acabou de ser liberada. A medida que um processo vai tendo suas páginas

carregadas ele tem suas molduras de página espalhadas pela memória, uma vez que elas entram

onde há espaço livre.

Cada processo de usuário vê a memória como um espaço único e contíguo, contendo apenas

esse processo. Mas na verdade o processo fica espalhado por toda a memória, que contém também

outros processos. Para que haja uma conciliação entre a memória vista pelo programa do usuário e a

xx1x24x03x

Memória virtual Memória principal01

234

01

234

5

67

89 página virtual

moldura de página

nº página virtual deslocamento

endereço do frame

Tabela de páginas

+

endereço físico

Memória principal

Page 56: Sistemas_Operacionais

Sistemas Operacionais 56

memória física é necessário que seja feita uma tradução de endereços virtuais em endereços físicos,

preferencialmente sem o conhecimento do programa do usuário.

Nos sistemas atuais, a tarefa de tradução do endereço é realizada pelo hardware, juntamente

com o sistema operacional, de forma a não comprometer seu desempenho. Como a maioria das

aplicações tende a fazer referência a um número reduzido de páginas, somente uma pequena fração

da tabela de páginas é realmente necessária. Assim, foi introduzido um hardware especial para

mapear endereços virtuais em endereços físicos sem a necessidade de acesso à tabela de páginas,

chamado memória associativa ou translation lookaside buffer (TLB). Este hardware fica dentro da

unidade de gerenciamento de memória (memory management unit – MMU)

As páginas dos processos são transferidas da memória secundária para a principal apenas

quando são referenciadas. Este mecanismo é chamado paginação por demanda e é conveniente, na

medida em que leva para a memória principal apenas as páginas realmente necessárias à execução

do processo. Assim, é possível que partes do programa nunca sejam carregadas para a memória.

5.6.2.1 Tabela de páginas

A tabela de páginas tem o propósito de mapear páginas virtuais em molduras de página para

formar um endereço físico de memória, ou seja, realizar a tradução de um endereço virtual em um

endereço físico.

Dois aspectos devem ser observados quanto a tabela de páginas. O primeiro deles é que uma

tabela pode ser muito grande, consumindo parte significativa da memória. Se um sistema utilizar,

por exemplo, páginas de 4 Kbytes e 32 bits para endereçamento, sua tabela de páginas terá mais que

1 milhão de entradas. Se, por outro lado, forem utilizadas páginas muito grandes, haverá um

aumento na fragmentação interna.

O segundo aspecto é que o mapeamento deve ser feito da forma mais rápido possível, pois é

feito a cada referência à memória, ou seja, uma ou mais vezes por instrução executada.

Uma forma de diminuir o tamanho das tabelas de páginas é através do uso de tabelas de

páginas multinível, que procura evitar manter toda a tabela de páginas na memória. Neste esquema

existe uma tabela de páginas de primeiro nível que é usada para fornecer o endereço de uma

moldura de página que contém uma tabela de páginas de segundo nível, e assim por diante. Por

exemplo, suponha um sistema onde são dedicados para cada processo 12 Mbytes, sendo 4 Mbytes

para o código executável, 4 Mbytes para dados e 4 Mbytes para a pilha. Assim, tem-se para a tabela

de páginas de primeiro nível:

Entrada 0 → Tabela de páginas do código executável.

Entrada 1 → Tabela de páginas de dados.

Entrada 2 → Tabela de páginas da pilha.

Entradas 3 a 1023 → Inválidas.

Para o exemplo são necessárias apenas 4 tabelas em memória (a tabela de primeiro nível mais

3 tabelas de segundo nível). Desta forma armazena-se cerca de 4 mil entradas em memória, no lugar

das cerca de 1 milhão de entradas que seriam necessárias se não fossem utilizadas tabelas de

páginas multinível.

A figura abaixo mostra um exemplo de como é o funcionamento de uma tabela de páginas de

dois níveis e a tradução de um endereço virtual em um endereço físico:

TP1 TP2 Deslocamento

10 10 12

1024 entradas para tabela de 2º nível

32 bits de endereçamento virtual

Page 57: Sistemas_Operacionais

Sistemas Operacionais 57

O formato de uma entrada na tabela de páginas varia com o sistema, mas em geral costuma

ser:

O bit presente/ausente indica se a página está na memória principal ou não. A proteção

informa que tipos de acesso são permitidos. O bit modificada informa se a página foi ou não

modificada desde a sua carga do disco. O bit referenciada é ligado sempre que a página é

referenciada. O bit desabilitar cache é usado apenas em sistemas que usam E/S mapeada em

memória, para evitar que o resultado de uma E/S seja mascarado.

A cada busca de instrução ou dados são necessários acessos extras à memória para consultar

as tabelas de páginas. É fato que a maioria dos processos tende a fazer um grande número de

referências a um pequeno número de páginas. Assim surgiu a ideia de equipar os computadores com

dispositivos de hardware para mapear os endereços sem passar pelas tabelas de páginas. Estes

dispositivos são conhecidos como Translation Lookaside Buffers (TLB) ou memória associativa,

e se encontram dentro da MMU (unidade de gerenciamento de memória).

O TLB é composto de entradas que contém informações sobre a página mapeada. Quando um

endereço virtual é passado à MMU, o hardware verifica se o número de página está no TLB. Se

estiver e não violar bits de proteção, a moldura de página será obtida diretamente do TLB, caso

contrário será gerada uma falha de TLB.

Quando o número de página não está no TLB, ele expulsa uma das entradas e a substitui pela

entrada da tabela de páginas que acabou de ser pesquisada.

Para algumas UCPs o gerenciamento e o tratamento de falhas do TLB são feitos inteiramente

pelo hardware da MMU.

Para outras UCPs a MMU simplesmente gera uma falha de TLB e passa o problema ao

sistema operacional. Neste caso, o sistema operacional deverá localizar a página, remover uma

entrada do TLB, carregar a página e reiniciar a instrução que falhou. Se o TLB for grande (64

entradas costuma ser suficiente) o gerenciamento por software será eficiente.

O ganho principal do gerenciamento do TLB por software é que a MMU fica mais simples,

sobrando mais espaço para cache.

página 0página 1

página 100

página 500

página 708

página 900

página 929

...1

500

...100

708

...929

900

..........

Tabela de páginas de

primeiro nível

Tabela de páginas de

segundo nível Memória

n1 n2 p

n1

n2

Tabela de páginas de

primeiro nível Tabela de páginas de

segundo nível

Endereço físico

Memória

p

... nº da moldura de página

presente/ausenteproteçãomodificadareferenciadadesabilitar cache

Page 58: Sistemas_Operacionais

Sistemas Operacionais 58

5.6.2.2 Páginas Compartilhadas

Outra vantagem do mecanismo de paginação é a possibilidade de compartilhamento de

código, evitando que processos oriundos de um mesmo programa necessitem manter em memória

cópias de um mesmo código executável. Isto é possível de ser realizado quando o processo utiliza

código reentrante, ou seja, um código executável que não modifica a si próprio.

Para que se consiga o compartilhamento de páginas basta mapear as páginas virtuais dos

processos em uma mesma moldura de página. Deve-se ter o cuidado de contabilizar quantos

processos estão utilizando a mesma moldura de página para que ela não seja liberada de forma

indevida.

5.6.2.3 Working Set (Conjunto Funcional)

O mecanismo de memória virtual, apesar de suas vantagens, introduz um grande problema:

sempre que um processo faz referência a uma de suas páginas e ocorre uma falha de página é

exigido do sistema operacional pelo menos uma operação de E/S, que, quando possível, deve ser

evitada.

O sistema deve se preocupar em manter na memória principal um certo número de páginas

que reduza ao máximo a taxa de paginação dos processos, ao mesmo tempo que não prejudique os

demais processos que desejam ter acesso à memória.

O conceito de working set (conjunto funcional) surgiu a partir da análise da taxa de

paginação dos processos. Quando um processo começa a ser executado, percebe-se uma elevada

taxa de falhas de páginas, que se estabiliza com o decorrer de sua execução. Esse fato está ligado

diretamente a localidade (referência localizada) que é a tendência que existe em um processo de

fazer referências a posições de memória próximas. Isso significa que um processo tenderá a

concentrar suas referências em um mesmo conjunto de páginas durante determinado período de

tempo.

Se a memória for pequena para armazenar o working set, o processo poderá sofrer com a

elevada taxa de paginação (trashing), comprometendo seu desempenho.

5.6.2.4 Alocação Global x Alocação Local

A substituição de página global permite selecionar uma moldura de página para um processo

do conjunto de todas as molduras de página da memória, mesmo que esteja alocada a outro

processo. Na substituição local uma moldura de página alocada a um processo deve permanecer no

conjunto de molduras de página reservadas para uso desse processo.

Com a estratégia de substituição local a quantidade de molduras de página alocadas a um

processo não varia. Com a alocação global um processo pode sempre alocar molduras de página de

outros processos aumentando sua quantidade de molduras de páginas, desde que outros processos

não selecionem suas molduras de página.

Um problema com o algoritmo de substituição global é que um processo não pode controlar

sua própria taxa de ocorrência de páginas ausentes. O seu conjunto de páginas armazenadas na

memória dependerá também do comportamento dos outros processos, uma vez que seu

escalonamento não acontecerá, obrigatoriamente, a uma taxa constante. Por outro lado, a

substituição local pode retardar a execução de um processo, não deixando disponível a ele molduras

de página utilizadas com menos frequência.

Em geral a substituição global resulta em uma maior quantidade de processos executados por

unidade de tempo, sendo o método mais utilizado.

Page 59: Sistemas_Operacionais

Sistemas Operacionais 59

5.6.2.5 Realocação de Páginas

O maior problema na gerência de memória virtual é decidir quais páginas remover. Quando o

limite do working set de um processo é alcançado e este necessita de novas molduras de página, o

sistema operacional deve escolher quais páginas devem ser liberadas.

Qualquer estratégia de realocação de páginas deve considerar se uma página foi ou não

modificada antes de liberá-la. Sempre que o sistema for liberar uma página modificada, ele antes

deverá gravá-la na memória secundária (page out), preservando seu conteúdo. O sistema mantém

um arquivo de paginação onde as páginas modificadas são armazenadas. Sempre que uma destas

páginas for novamente referenciada, ela será trazida de volta para o working set do processo (page

in).

O sistema consegue implementar esse mecanismo através do bit modificada, que existe em

cada entrada da tabela de páginas. Sempre que uma pagina é alterada, o valor do bit modificada é

marcado como 1.

A melhor estratégia de realocação de páginas seria aquela que escolhesse uma página que não

fosse referenciada num futuro próximo, porém o sistema operacional não tem como prever se uma

página será ou não utilizada novamente. A seguir serão analisadas as principais estratégias adotadas

pelos sistemas operacionais para a realocação de páginas.

Aleatória

A escolha aleatória não utiliza critério algum de seleção. Todas as páginas têm a mesma

chance de ser selecionada. Apesar de ser uma estratégia que consome poucos recursos do sistema,

raramente é utilizada por conta de sua baixa eficiência.

Not Recently Used (NRU)

Neste esquema são utilizados o bit referenciada (bit R - indica se a página foi referenciada ou

não) e o bit modificada (bit M - indica se a página foi modificada desde que foi carregada em

memória). Estes bits são marcados pelo hardware e desmarcados pelo sistema operacional.

Periodicamente o bit R é desmarcado pelo sistema operacional para determinar as páginas que

foram acessadas recentemente. Quando ocorre uma falha de página, as páginas são divididas em

quatro classes:

Classe 0 → Não referenciada, não modificada.

Classe 1 → Não referenciada, modificada.

Classe 2 → Referenciada, não modificada.

Classe 3 → Referenciada, modificada.

O algoritmo NRU remove uma página da classe não vazia com numeração mais baixa.

First In First Out (FIFO)

Nesse esquema, a página que foi utilizada primeiro será a primeira a ser escolhida para ser

liberada. Sua implementação é bastante simples, sendo necessária apenas uma fila.

Segunda Chance

É uma modificação do algoritmo FIFO para não jogar fora uma página muito utilizada. Neste

algoritmo o bit referenciada da página mais antiga é verificado. Se for 0 ela é substituída, senão o

bit será marcado como 0, a página irá para o final da fila e a próxima página será inspecionada.

Least Frequently Used (LFU)

Neste esquema a página menos referenciada (menos frequentemente utilizada) será a página

escolhida para ser substituída. Para isso é mantido um contador do número de referências feitas às

páginas, e a página que tiver o contador com o menor valor será a página escolhida. O algoritmo

mantém na memória as páginas que são bastante utilizadas. É uma boa estratégia, mas as páginas

Page 60: Sistemas_Operacionais

Sistemas Operacionais 60

que entram mais recentemente no working set serão aquelas que estarão com os contadores com

menor valor. Outro problema é que é preciso manter uma lista ordenada por uso que deve ser

alterada a cada acesso à memória, gerando um grande overhead que dificulta sua utilização.

Least Recently Used (LRU)

As páginas que foram intensamente utilizadas nas últimas instruções provavelmente serão

intensamente utilizadas nas instruções seguintes. Assim, essa estratégia seleciona a página que está

a mais tempo sem ser referenciada (a página menos utilizada recentemente) para ser substituída. O

problema é que é preciso manter uma lista ordenada por uso que deve ser alterada a cada acesso à

memória, gerando um grande overhead que dificulta sua utilização.

Simulação de LRU em Software

Embora seja possível implementar o LRU, as máquinas não costumam ter o hardware

necessário. Uma solução é utilizar o algoritmo não frequentemente utilizado (not frequently used

– NFU), que usa um contador de software associado a cada página. Esse contador é iniciado com 0

e a cada interrupção do relógio o sistema operacional varre todas as páginas. O bit referenciada de

cada página é somado ao contador e zerado. Quando ocorre uma falha, a página com o contador

mais baixo é escolhida para ser substituída.

Se, ao invés do bit referenciada ser somado ao contador, o contador tiver seus bits deslocados

para a direita e o bit referenciada for somado à esquerda, obtêm-se o algoritmo da idade. Nesse

algoritmo, quando uma falha ocorre a página que tem o menor valor de contador é removida.

Os algoritmos NFU e da idade diferenciam-se do LRU por não oferecerem uma noção exata

do tempo. Se duas páginas tiverem igual histórico do bit referenciada não há como saber qual foi a

menos utilizada.

5.6.3 Segmentação

A memória estudada até o momento segue um modelo unidimensional, com endereços de 0

até um máximo.

A segmentação é uma técnica de gerência de memória onde os programas são divididos

logicamente em sub-rotinas e estruturas de dados e colocados em blocos de informações na

memória. Os blocos podem ter tamanhos diferentes e são chamados segmentos, cada um com seu

próprio espaço de endereçamento. Ainda, por constituírem espaços de endereçamento diferentes,

segmentos distintos podem crescer ou diminuir independentemente de forma dinâmica, sem afetar

os demais.

Enquanto a paginação divide o processo em partes de tamanho fixo, sem qualquer ligação

com a estrutura do programa, a segmentação permite uma relação entre a lógica do processo e sua

divisão na memória.

Os segmentos são mapeados através de tabelas de mapeamento de segmentos e os endereços

são compostos pelo número do segmento e um deslocamento (endereço dentro do segmento). O

número do segmento identifica unicamente uma entrada na tabela de segmentos, onde estão as

informações sobre o segmento na memória real. O endereço absoluto é calculado a partir do

endereço inicial do segmento mais o deslocamento.

Além do endereço do segmento na memória física, cada entrada na tabela de segmentos

possui informações sobre o tamanho do segmento, se ele está ou não na memória e sua proteção.

Na segmentação, apenas os segmentos referenciados são transferidos da memória secundária

para a memória real. Assim, para serem mais eficientes, os programas devem estar bem

modularizados.

Page 61: Sistemas_Operacionais

Sistemas Operacionais 61

A figura abaixo exemplifica o uso de uma memória segmentada por um processo com cinco

segmentos.

A memória segmentada também facilita o trabalho do linkeditor, pois procedimentos em

segmentos separados sempre começam a executar no endereço 0. Assim, só é preciso compilar e

linkeditar as partes do programa do segmento alterado.

Outro aspecto facilitado pela segmentação é o do compartilhamento de código e dados entre

processos. Uma biblioteca compartilhada, por exemplo, pode ser colocada em um segmento e

compartilhada por múltiplos processos.

Ainda, segmentos diferentes podem ter proteções diferentes. Por exemplo:

Segmento de procedimento → somente execução.

Segmento de dados → leitura e escrita.

O problema da fragmentação também ocorre nesse modelo, quando as áreas livres são tão

pequenas que não acomodam nenhum segmento que necessite ser carregado. Nesse caso a

compactação pode ser utilizada para resolver a fragmentação.

5.6.4 Segmentação com Paginação

Permite a divisão lógica dos programas em segmentos que, por sua vez, são divididos em

páginas, para que não precisem ser totalmente mantidos em memória.

Assim, um endereço é formado pelo número do segmento, um número de página dentro

desse segmento e um deslocamento dentro dessa página. Através do número do segmento obtém-se

uma entrada na tabela de segmentos que contém informações da tabela de páginas do segmento.

Com o número da página obtém-se uma entrada na tabela de páginas com informações da página

nº do segmento deslocamento

endereço do segmento

Tabela de segmentos

+

endereço físico

Memória principal

Sub-rotina 1

Segmento 0

Sub-rotina 2

Segmento 1

Pilha

Segmento 3

Dados

Segmento 4

Programa principal

Segmento 2

Segmento 0

Segmento 3

Segmento 2

Segmento 4

Segmento 1

1000400400

11001000

Limite

14006300430032004700

Base

01234

1400

2400

3200

4300

4700

5700

6300

6700

Page 62: Sistemas_Operacionais

Sistemas Operacionais 62

na memória física. O endereço físico é obtido somando-se a posição inicial da moldura de página

(frame) e o deslocamento.

5.6.5 Segmentação com Paginação: O Pentium Intel

O Pentium tem 16K segmentos independentes, cada um podendo conter até um bilhão de

palavras de 32 bits.

A memória virtual é constituída por duas tabelas, a LDT (Tabela Local de Descritores) e a

GDT (Tabela Global de Descritores). Cada processo tem sua própria LDT, mas a GDT é única,

compartilhada por todos os processos. A LDT descreve os segmentos próprios de cada processo,

enquanto que a GDT descreve os segmentos do sistema, incluindo o próprio sistema operacional.

Para acessar um determinado segmento deve-se carregar um seletor para este segmento em

um dos seis registradores de segmento (CS = segmento de código, DS = segmento de dados, etc.).

Cada seletor é um número de 16 bits:

1 bit para informar se o segmento é local ou global

13 bits para identificar o número da entrada

2 bits para proteção

Quando o seletor é carregado no registrador de segmento, o descritor correspondente é

buscado na LDT ou na GDT e armazenado em um dos registradores do microprograma para que

possa ser acessado rapidamente. Um descritor consiste em oito bytes, contendo informações como o

endereço base, o tamanho e outras informações do segmento.

Para converter um par (seletor, deslocamento) em um endereço físico:

O microprograma descobre qual registrador de segmento está sendo utilizado e carrega o

descritor em seus registradores. Se o segmento não existe ou não está carregado na

memória é gerada uma interrupção.

O microprograma verifica se o deslocamento está dentro do segmento. Se não estiver

gera uma interrupção.

O deslocamento é somado à base do segmento para formar o endereço linear. Se a

paginação estiver desativada o endereço linear é interpretado como um endereço físico.

Se a paginação estiver ativada, o endereço linear será interpretado como um endereço

virtual e será mapeado para o endereço físico usando uma tabela de páginas de 2 níveis.

nº do segmento nº página virtual

endereço do segmento

Tabela de segmentos

+

endereço físico

Memória principal

deslocamento

endereço do frame

Tabela de páginas

+

13 1 2

Índice Nível de privilégio0=GDT, 1=LDT

SELETOR

Endereço de Base

Limite

Outros campos

DESCRITORDESLOCAMENTO

+

Endereço linear de 32 bits

Page 63: Sistemas_Operacionais

Sistemas Operacionais 63

Cada processo possui um diretório de páginas que consiste em 1024 entradas de 32 bits. Cada

entrada deste diretório aponta para uma tabela de páginas que também contém 1.024 entradas de 32

bits, que apontam para molduras de páginas.

O campo dir é usado como índice para o diretório de páginas, a fim de localizar um ponteiro

para a tabela de páginas correspondente. O campo página é usado como índice na tabela de páginas

para encontrar o endereço físico da moldura onde está a página. O campo deslocamento é

adicionado ao endereço da moldura para obtenção do endereço físico da palavra.

Para evitar repetidas referências à memória o Pentium tem uma pequena memória associativa

(TLB) que mapeia diretamente as páginas mais usadas recentemente no endereço físico de suas

molduras. Somente quando a combinação corrente não estiver no TLB é que o mecanismo do

sistema operacional será acionado e o TLB será atualizado com o resultado obtido.

5.6.6 Proteção

Em qualquer sistema multiprogramável deve existir um mecanismo que proteja o espaço de

memória de cada processo, assim como a área do sistema operacional.

No esquema de memória virtual a tradução dos endereços é realizada pelo sistema

operacional. Assim, é impossível que um processo tenha acesso a áreas de memória de outros

processos. A proteção é necessária para impedir que um processo, ao acessar uma página ou

segmento do sistema, a modifique. Mesmo as páginas e segmentos do processo podem estar

definidas com uma proteção que impeça, por exemplo, a gravação.

Em sistemas que implementam paginação e/ou segmentação, a proteção deve ser realizada em

nível de cada página/segmento na memória. Esse mecanismo é implementado utilizando-se as

tabelas de mapeamento, onde alguns bits especificam os acessos permitidos a cada uma das

páginas/segmentos.

5.6.7 Compartilhamento de Memória

Em sistemas que implementam memória virtual é bastante simples o compartilhamento de

código e dados entre processos. Para isso basta que as entradas das tabelas de páginas/segmentos

apontem para as mesmas páginas/segmentos na memória principal. Assim é possível reduzir o

número de programas na memória e aumentar o número de usuários compartilhando o mesmo

recurso.

A vantagem da segmentação em relação à paginação no aspecto de compartilhamento baseia-

se na forma em que os programas são divididos. Como as tabelas de segmentos mapeiam estruturas

lógicas, o compartilhamento de segmentos é mais simples do que o de páginas.

5.6.8 Trashing

Quando a quantidade de molduras de página alocadas a um processo decresce até um valor

inferior ao conjunto funcional, sua execução deve ser suspensa, liberando o espaço ocupado por

suas molduras de página. Caso este processo não seja suspenso e o número de páginas em uso seja

DIR PÁGINA

Diretório de páginas

DESLOCAMENTO10 10 12

DIR

Tabela de páginas

PÁGINA

Moldura de página

DESLOCAMENTO

Entrada de diretório aponta para a tabela de páginas

Entrada na tabela de páginas aponta para a moldura da página

Page 64: Sistemas_Operacionais

Sistemas Operacionais 64

maior que o número de molduras de página disponíveis, ocorrerá um número excessivo de falhas de

página.

Quando o processo faz paginação excessiva, consumindo mais tempo em substituição de

páginas do que executando seu código, diz-se que está havendo trashing.

Trashing pode ser definido como sendo a excessiva transferência de páginas/segmentos entre

a memória principal e a memória secundária.

O trashing na paginação ocorre em dois níveis: no nível do próprio processo e no nível do

sistema. No nível do processo a excessiva paginação ocorre devido ao elevado número de falha de

páginas, gerado pelo processo em execução. Existem dois motivos que levam um processo a sofrer

esse tipo de trashing: o mal dimensionamento do tamanho do conjunto funcional e a não obediência

ao conceito de localidade.

No nível do sistema o trashing ocorre quando existem mais processos competindo por

memória do que espaço disponível. O sistema deve tentar administrar a memória de forma que

todos os processos sejam atendidos. O primeiro passo é a redução do tamanho dos working sets dos

processos, o que pode levar ao trashing no nível do processo. Caso a redução do working set não

seja suficiente, o sistema começa o trabalho de swapping. Se esse mecanismo for levado ao extremo

o sistema passará mais tempo fazendo swapping que executando os processos.

O trashing na segmentação também ocorre em nível de processo e em nível de sistema. No

nível do processo, a transferência de segmentos é excessiva devido à modularização extrema do

programa, não seguindo o conceito de localidade. No nível do sistema o trashing é bastante

semelhante ao da paginação com a ocorrência de swapping.

De qualquer forma, se existem mais processos para serem executados que memória real

disponível, a única solução é expandir a memória principal.

5.7 Exercícios

1) Por que a gerência de memória para sistemas multiprogramáveis é mais crítica do que para

sistemas monoprogramáveis?

2) O que vem a ser atribuição de endereços em tempo de execução?

3) Como funciona o carregamento dinâmico? Qual a sua vantagem?

4) Como funciona a ligação dinâmica?

5) Como o esquema de ligação dinâmica pode ajudar na atualização de softwares?

6) Por que o esquema de alocação contígua simples não é apropriado para a multiprogramação?

7) Como a técnica de overlay (sobreposição) permite expandir os limites da memória

principal? Qual o impacto no desempenho?

8) Por que para a multiprogramação ser eficiente é necessário que mais de um programa possa

estar carregado na memória principal simultaneamente?

9) Qual a diferença entre a alocação particionada estática e a alocação particionada dinâmica?

10) Como é feita a proteção da memória nos esquemas de alocação particionada? Por que ela é

necessária?

11) Para a gerência de memória, o que é a fragmentação? Por que ela é prejudicial?

12) Qual a diferença entre fragmentação interna e fragmentação externa?

13) O que é a técnica de swapping aplicada à gerência de memória? Qual a sua vantagem sobre

a alocação particionada?

Page 65: Sistemas_Operacionais

Sistemas Operacionais 65

14) O swapping apresenta o problema da relocação. O que vem a ser relocação dinâmica? Por

que ela é necessária?

15) O que é a técnica de memória virtual sob o ponto de vista da gerência de memória?

16) Quais as vantagens de se utilizar memória virtual no lugar de swapping?

17) Qual o relacionamento entre espaço de endereçamento virtual e espaço de endereçamento

real?

18) Como a técnica de memória virtual permite que um programa ultrapasse os limites da

memória principal?

19) O que vem a ser paginação para a gerência de memória?

20) Qual a importância da tabela de páginas para a técnica de paginação aplicada à memória

virtual?

21) O que é uma falha de página quando se está utilizando memória virtual? O que deve ser

feito quando ela ocorre?

22) Como a paginação ajuda a diminuir o problema de fragmentação de memória?

23) Qual o propósito das tabelas de páginas?

24) Para que serve o bit presente/ausente nas tabelas de páginas?

25) Quando uma página carregada em memória precisar ser substituída por outra deve-se

observar o bit modificada. Por quê?

26) Considere um sistema que utilize memória virtual com paginação. Supondo que o

mapeamento das páginas virtuais em molduras de página seja realizado pela tabela de páginas

abaixo, para qual endereço físico será mapeado o endereço virtual que tem número de página 2C9 e

deslocamento 2CB (2C9:2CB)?

nº página

virtual

Endereço

moldura

.......

...

2C7

2C8

2C9

2CA

2CB

2CC

...

...

25000

3C000

8F000

BA000

45000

D8000

...

.......

.......

.......

.......

.......

.......

.......

.......

27) Por que o mapeamento de memória deve ser realizado em blocos e não sobre células

individuais?

28) Qual a função do TLB (memória associativa) para a gerência de memória?

29) Quando se usa a paginação é possível realizar o compartilhamento de páginas. O que vem

a ser isto? Explique o benefício que se obtém.

30) O que é working set (conjunto funcional)?

31) Qual a relação existente entre working set (conjunto funcional) e trashing?

32) Explique o funcionamento do algoritmo da idade, usado para escolher uma página que

deve ser retirada da memória.

33) O que é a técnica de segmentação aplicada à gerência de memória?

Page 66: Sistemas_Operacionais

Sistemas Operacionais 66

34) Quando se usa segmentação o endereço é dado por um par segmento:deslocamento. Como

é o mapeamento de um endereço deste tipo em um endereço real?

35) Como a segmentação pode facilitar o compartilhamento de código entre processos

oriundos de um mesmo programa?

36) O que é trashing? Como este problema pode ser solucionado?

Page 67: Sistemas_Operacionais

Sistemas Operacionais 67

6 Sistema de Arquivos

O armazenamento e a recuperação de informações são atividades essenciais para qualquer

tipo de aplicação. As principais exigências para armazenamento de informações são:

Deve ser possível armazenar uma grande quantidade de informações.

A informação deve sobreviver à finalização do processo que a utiliza.

Múltiplos processos devem ser capazes de acessar as informações concorrentemente.

É mediante a implementação de arquivos em discos ou outras mídias que o sistema

operacional estrutura e organiza estas informações. A parte responsável por essa gerência é

denominada sistema de arquivos. A manipulação de arquivos deve ocorrer de maneira uniforme,

independentemente dos diferentes dispositivos de armazenamento.

O sistema de arquivos é constituído de duas partes distintas: um conjunto de arquivos, que

armazenam dados, e uma estrutura de diretórios, que organiza e fornece informações sobre os

arquivos do sistema.

6.1 Arquivos

Computadores podem armazenar dados em diferentes meios de armazenamento. Para que um

sistema possa ser usado de forma conveniente, o sistema operacional deve oferecer uma visão

lógica e uniforme do meio de armazenamento.

Um arquivo é constituído de informações, podendo representar programas ou dados. Um

programa contém instruções compreendidas pela UCP (arquivo executável), enquanto um arquivo

de dados pode ser estruturado livremente (podem ser numéricos, alfabéticos, alfanuméricos ou

binários).

Arquivos podem ser armazenados pelo sistema operacional em diferentes dispositivos físicos

(fita magnética, disco magnético, CD-ROM, etc.). O tipo de dispositivo deve ser isolado pelo

sistema operacional de forma que exista uma independência entre os arquivos a serem manipulados

e o meio de armazenamento.

Um arquivo é identificado por meio de um nome, e as regras para os nomes de arquivos

variam de sistema para sistema. Em alguns sistemas operacionais o nome do arquivo é composto

por duas partes separadas com um ponto. A parte após o ponto é denominada extensão do arquivo e

tem como finalidade identificar seu conteúdo. As principais diferenças entre as regras para os

nomes de arquivo são:

Quantidade máxima de caracteres.

Diferenciação entre caracteres maiúsculos e minúsculos.

Uso de caracteres especiais.

Nomes com extensão tendo significado ou não.

Quando um processo cria um arquivo ele deve dar um nome a este arquivo para que quando o

processo termine o arquivo continue existindo e possa ser acessado por outros processos pelo

mesmo nome. Ao receber um nome o arquivo se torna independente do processo, do usuário e até

mesmo do sistema que o criou.

6.1.1 Organização de Arquivos

No momento da criação de um arquivo é possível definir que organização será adotada. Esta

organização pode ser uma estrutura suportada pelo sistema operacional ou definida pela própria

aplicação.

Page 68: Sistemas_Operacionais

Sistemas Operacionais 68

A forma mais simples de organização de arquivos é através de uma sequência não

estruturada de bytes. Nesse tipo de organização o sistema de arquivos não impõe nenhuma

estrutura lógica para os dados (a aplicação deve definir toda a organização). A grande vantagem é a

flexibilidade para criar diferentes estruturas de dados, mas todo o controle de acesso ao arquivo é de

inteira responsabilidade da aplicação.

Uma forma estruturada para o armazenamento de arquivos é a sequência de registros de

tamanho fixo. Nela o arquivo é composto por uma série de registros com estrutura interna

característica, e as operações de leitura/gravação trabalham com registros inteiros.

Outra estrutura é a árvore de registros, uma organização que é composta por registros que não

tem necessariamente o mesmo tamanho, e cada um contém um campo com uma chave em uma

posição fixa. A árvore é ordenada pelo campo chave de forma a permitir uma busca rápida.

6.1.2 Métodos de Acesso

Os primeiros sistemas operacionais acessavam os registros de um arquivo na ordem em que

eram gravados, sendo a gravação de novos registros possível apenas no final. A esse tipo de acesso

dá-se o nome de acesso sequencial. Não é possível pular ou ler registros fora de ordem, mas é

possível retroceder registros.

Posteriormente surgiu um método de acesso mais eficientes, o acesso aleatório (acesso

direto), que permite a leitura/gravação de um registro diretamente na sua posição relativa ao início

do arquivo. No acesso aleatório não existe restrição à ordem em que os registros são lidos ou

gravados. O acesso aleatório somente é possível quando o arquivo é definido com registros de

tamanho fixo.

Em sistemas operacionais antigos o tipo de acesso ao arquivo é determinado no momento em

que o arquivo é criado. Nos sistemas operacionais modernos todos os arquivos são de acesso

aleatório, excetuando-se os casos em que o dispositivo no qual o arquivo está armazenado não

permita o acesso aleatório.

Existem ainda outros métodos de acesso menos comuns, como o acesso indexado. O acesso

indexado envolve a construção de um índice para o arquivo. Esse índice contém ponteiros para os

blocos e, para acessar uma posição de arquivo, primeiro pesquisa-se o índice e depois utiliza-se o

ponteiro para a posição desejada.

6.1.3 Tipos de Arquivos

Os sistemas operacionais costumam suportar vários tipos de arquivos. Os tipos mais comuns

são:

Arquivos regulares → Arquivos que contém informações genéricas como, por exemplo,

dados dos usuários.

Diretórios → Arquivos de sistema usados para manter a estrutura do sistema de arquivos.

Arquivos especiais de caractere → Relacionam-se com operações de E/S e costumam

modelar dispositivos seriais.

Arquivos especiais de bloco → Usados para modelar dispositivos de bloco, em especial

discos.

1 byte 1 registro A E L

F G KB C D M N O

H I J

Sequência de bytes Sequência de registros

Árvore

Page 69: Sistemas_Operacionais

Sistemas Operacionais 69

Em geral arquivos regulares são classificados como arquivo texto ou arquivo binário. Um

arquivo texto (ou arquivo ASCII) é constituído por linhas de texto que podem ter tamanhos

diferentes e terminam por caracteres especiais para indicar o fim da linha. São arquivos que quando

exibidos na tela ou impressos podem ser compreendidos pelas pessoas. Podem, ainda, ser editados

com um editor de textos comum.

Arquivos binários são arquivos que não são arquivos texto. Sua listagem gera um conjunto de

caracteres incompreensíveis. Eles podem ser arquivos de usuários (com ou sem estrutura interna) ou

arquivos executáveis (com estrutura conhecida pelo sistema operacional e códigos que são

executados pela UCP).

6.1.4 Operações de Entrada/Saída

O sistema de arquivos oferece um conjunto de chamadas de sistema que permite às aplicações

realizarem operações de E/S. As chamadas de sistema de E/S têm como função oferecer uma

interface simples e uniforme entre a aplicação e os diversos tipos de dispositivos.

Na parte introdutória desta apostila foram mostradas as chamadas de sistema POSIX para

gerenciamento de arquivos, que está presente na maioria dos sistemas operacionais.

6.1.5 Atributos

Além do nome e dos dados, arquivos costumam possuir informações extras para controle

denominadas atributos. Alguns atributos variam dependendo do sistema de arquivos, mas alguns

como tamanho do arquivo, proteção, identificação do criador e data de criação, estão presentes em

quase todos os sistemas. Os principais atributos são:

Atributos relacionados com a proteção → proteção (quem e de que maneira pode

acessar), senha, criador, proprietário.

Atributos que controlam ou ativam alguma atividade específica → somente leitura,

oculto, sistema, arquivo, ASCII/binário, acesso aleatório, temporário, bloqueio.

Atributos para arquivos cujos registros podem ser pesquisados → comprimento do

registro, posição da chave, comprimento da chave.

Atributos relacionados com data/hora → tempo de criação, tempo do último acesso,

tempo da última alteração.

Atributos relacionados com o tamanho → tamanho atual, tamanho máximo.

6.2 Diretórios

A estrutura de diretórios é o modo como o sistema organiza logicamente os diversos arquivos

contidos em um disco. O diretório é um arquivo que contém uma estrutura de dados com entradas

associadas aos arquivos onde são armazenadas informações como localização física, nome,

organização e demais atributos.

Quando é solicitada a abertura de um arquivo o sistema operacional pesquisa o diretório até

encontrar uma entrada com o nome do arquivo. Quando a entrada é encontrada o sistema

operacional copia os atributos e os endereços de disco e os coloca em uma estrutura na memória, a

tabela de arquivos, que contém todos os arquivos abertos. Quando o arquivo é fechado, sua entrada

na tabela de arquivos é liberada.

A implementação mais simples de uma estrutura de diretórios é chamada de nível único, (um

nível), onde existe um único diretório contendo todos os arquivos do disco. Este modelo é bastante

limitado, já que não permite que usuários criem arquivos com o mesmo nome, o que ocasionaria um

conflito no acesso aos arquivos. Este tipo de implementação não é mais utilizado atualmente.

Em uma outra estrutura, conhecida como diretório de dois níveis, existe um diretório para os

arquivos do sistema e um diretório para cada usuário. Com esta implementação, cada usuário pode

Page 70: Sistemas_Operacionais

Sistemas Operacionais 70

criar arquivos sem a preocupação de conhecer os demais arquivos do disco. Para que o sistema

possa localizar arquivos nesta estrutura existe um nível de diretório adicional denominado master

file directory, indexado pelo nome do usuário. Nele, cada entrada aponta para o diretório de cada

usuário. Para referenciar um arquivo neste tipo de estrutura é necessário especificar o diretório onde

ele se encontra e o seu nome, especificando assim seu caminho (path).

A organização dos arquivos em um único diretório não permite uma organização adequada. A

extensão para um modelo de múltiplos níveis permite que os arquivos sejam melhor organizados.

Este modelo, chamado estrutura de diretórios em árvore ou sistema de diretórios hierárquico, é

atualmente adotado pela maioria dos sistemas operacionais. Na estrutura em árvore é possível criar

quantos diretórios se deseje, podendo um diretório conter arquivos ou outros diretórios

(subdiretórios). Cada arquivo, nesta estrutura, possui um caminho (path) único que descreve todos

os diretórios desde a raiz até o diretório no qual o arquivo está, mais o nome do arquivo.

Quando o sistema de arquivos é organizado como uma árvore de diretórios, os nomes de

caminhos podem ser absolutos ou relativos. Um nome de caminho absoluto consiste no caminho

desde o diretório raiz (diretório inicial do sistema de arquivos) até o arquivo.

O nome de caminho relativo é utilizado em conjunto com o conceito de diretório de trabalho

(diretório atual) ,que é o diretório usado atualmente pelo processo e serve como base caso o nome

do caminho não inicie com o diretório raiz. Quando é utilizado um caminho relativo, o caminho até

o arquivo é buscado a partir do diretório de trabalho.

Cada processo possui seu próprio diretório de trabalho. Assim, se algum processo alterar seu

diretório de trabalho, os outros processos do usuário não serão afetados.

A maioria dos sistemas que suporta estrutura de diretórios em árvore tem duas entradas

especiais para cada diretório:

“.”→ Diretório atual.

“..”→ Diretório pai. Pode ser usada para subir na árvore de diretórios.

6.3 Alocação de Espaço em Disco

Os discos são o meio de armazenamento secundário mais comum no qual os arquivos são

armazenados. As transferências de dados entre a memória e o disco são realizadas em unidades

chamadas blocos, cada qual constituída por um ou mais setores. Dependendo do disco um setor

pode variar de 32 a 4096 bytes, sendo mais comum setores de 512 bytes.

A criação de arquivos em disco exige que o sistema operacional tenha o controle de quais

blocos no disco estão livres. Este controle é realizado através de uma estrutura de dados que

armazena informações que possibilitam ao sistema de arquivos gerenciar o disco.

A forma mais simples de implementar uma estrutura de espaços livres é através de uma tabela

denominada mapa de bits. A cada entrada da tabela é associada um bloco do disco representado por

um bit, que pode assumir valor igual a 0 (bloco livre) ou 1 (bloco alocado).

Uma segunda maneira de realizar este controle é por meio de uma lista encadeada de todos os

blocos livres do disco. Cada bloco possui uma área reservada para armazenamento do endereço do

próximo bloco, e a partir do primeiro bloco livre pode-se ter acesso aos demais de forma encadeada.

Suas principais restrições são o espaço utilizado no bloco com informação de controle e o fato do

algoritmo de busca de espaço livre sempre ter que realizar uma pesquisa sequencial na lista.

Outra solução leva em conta que blocos contíguos são geralmente alocados ou liberados

simultaneamente, enxergando o disco como um conjunto de segmentos de blocos livres. Assim é

possível manter uma tabela de blocos livres com o endereço do primeiro bloco de cada segmento e

o número de blocos livres contíguos que se seguem.

Page 71: Sistemas_Operacionais

Sistemas Operacionais 71

6.3.1 Alocação Contígua

A alocação contígua consiste em armazenar um arquivo em blocos contíguos de dados no

disco. Neste tipo de alocação, o sistema localiza um arquivo através do endereço do primeiro bloco

e da sua quantidade de blocos.

O acesso a arquivos dispostos contiguamente no disco é bastante simples tanto para o acesso

sequencial quanto para o acesso aleatório. Seu principal problema é a alocação de espaço livre para

novos arquivos, já que para um arquivo ser criado com n blocos é necessário que exista uma cadeia

de n blocos dispostos sequencialmente no disco.

Pode-se enxergar o disco como um grande vetor onde os elementos podem ser considerados

segmentos com tamanhos diferentes de blocos contíguos, dispostos alternadamente entre segmentos

ocupados e segmentos livres. No momento em que o sistema operacional deseja alocar espaço para

armazenar um novo arquivo, pode existir mais de um segmento livre disponível com o tamanho

exigido. Neste caso, é necessário que alguma estratégia de alocação seja adotada para selecionar

qual o segmento na lista de blocos livres deve ser escolhido. As estratégias adotadas são as mesmas

estudadas para escolha de partição em memória, existindo também o problema da fragmentação.

Quando o disco estiver muito fragmentado para que se possa alocar espaço para a criação do

arquivo será necessário realizar a desfragmentação do disco, que consiste em mover os arquivos

para abrir espaço suficiente para o novo arquivo. A desfragmentação é um processo demorado que

deve ser realizado periodicamente.

Nem sempre é possível determinar no momento da criação de um arquivo qual o seu tamanho

máximo, podendo posteriormente existir a necessidade de extensão. A pré-alocação de espaço é

uma solução que, apesar de resolver o problema, pode fazer com que parte do espaço alocado

permaneça ocioso.

O sistema operacional IBM VM/CMS usava este tipo de alocação.

6.3.2 Alocação por Lista Encadeada

Na alocação por lista encadeada um arquivo é organizado como um conjunto de blocos

ligados logicamente, independentemente da sua localização física. Cada bloco deve possuir um

ponteiro para o bloco seguinte e assim por diante. A entrada de diretório precisa armazenar somente

o endereço do primeiro bloco.

A fragmentação do disco não ocorre na alocação encadeada já que os blocos alocados para

um determinado arquivo não precisam estar em posições contíguas. O que ocorre neste método é a

fragmentação de arquivos, que é a quebra do arquivo em diversos blocos espalhados pelo disco. A

fragmentação dos arquivos resulta no aumento do tempo de acesso, pois o processo de

leitura/gravação provoca um grande número de deslocamentos da cabeça de leitura/gravação. Para

otimizar o tempo das operações de E/S é importante que o disco seja desfragmentado

periodicamente.

0 1 2 3 4

5 6 7 8 9

10 11 12 13 14

15 16 17 18 19

20 21 22 23 24

25 26 27 28 29

1 1 1 0 00 0 0 1 11 1 0 0 10 0 1 1 10 0 0 0 00 0 1 1 1

03121520

Bloco

03121520

Contador

Mapa de bits Lista encadeada Tabela de blocos livres

Page 72: Sistemas_Operacionais

Sistemas Operacionais 72

A alocação por lista encadeada só permite o acesso sequencial aos blocos de um arquivo. Isto

constitui uma das principais desvantagens dessa técnica. Além disso é desperdiçado espaço nos

blocos com o armazenamento de ponteiros.

Outro problema da alocação por lista encadeada é referente à confiabilidade. Se o valor de um

ponteiro é corrompido se perde o encadeamento do arquivo.

6.3.3 Alocação por Lista Encadeada Utilizando Índice

É um esquema de alocação muito parecido com a alocação por lista encadeada, mas no lugar

de fazer o encadeamento utilizando um ponteiro no bloco, o encadeamento é mantido em uma

tabela.

Embora a cadeia ainda precise ser seguida para o acesso aleatório, ela é seguida por

intermédio da tabela, e não consultando bloco a bloco, e pode, ainda, ser mantida em memória,

tornando o acesso mais rápido.

A principal desvantagem é que a tabela pode ser muito grande para discos grandes. Este

problema pode ser minimizado agrupando os blocos em clusters para formar unidades maiores, mas

isto provoca um maior desperdício por causa da parte não utilizada do último cluster, que tenderá a

ser maior.

6.3.4 Nós-I (Alocação Indexada)

O método consiste em associar a cada arquivo uma tabela denominada nó-i (nó-índice ou

inode) que lista os atributos e os endereços em disco dos blocos do arquivo.

0 1 2 3 4

5 6 7 8 9

10 11 12 13 14

15 16 17 18 19

20 21 22 23 24

25 26 27 28 29

10 06 03 18 21 28

Alocação por lista encadeada

0 1 2 3 4

5 6 7 8 9

10 11 12 13 14

15 16 17 18 19

20 21 22 23 24

25 26 27 28 29

.. .. .. 18 ..

.. 03 .. .. ..

06 .. .. .. ..

.. .. .. 21 ..

.. 28 .. .. ..

.. .. .. -1 ..

Alocação por lista encadeada utilizando índice

0 1 2 3 4

5 6 7 8 9

10 11 12 13 14

15 16 17 18 19

20 21 22 23 24

25 26 27 28 29

100603182128

12

nó-i

Page 73: Sistemas_Operacionais

Sistemas Operacionais 73

Os primeiros endereços de disco são armazenados no próprio nó-i. Isto significa que para

arquivos pequenos toda a informação está contida no próprio nó-i e pode ser transferida para a

memória quando o arquivo é aberto. Para arquivos maiores, um dos endereços no nó-i é o endereço

de um bloco do disco chamado bloco indireto simples, que contém endereços de blocos adicionais

para o arquivo. No nó-i existe ainda o endereço de um bloco indireto duplo e de um bloco indireto

triplo. O bloco indireto duplo contém o endereço do bloco que possui uma lista de blocos indiretos

simples relativos ao arquivo, enquanto o bloco indireto triplo contém o endereço do bloco que

possui uma lista de blocos indiretos duplos.

6.4 Exemplos de Implementação de Diretórios

Quando é solicitada a abertura de um arquivo, o sistema operacional usa o nome do caminho

fornecido para localizar a entrada de diretório.

Uma questão importante é onde armazenar os atributos. Alguns sistemas armazenam

diretamente na entrada de diretório, enquanto outros armazenam no nó-i.

6.4.1 Diretórios no CP/M

O CP/M possui apenas um diretório, assim a pesquisa por um arquivo é realizada somente

neste diretório.

A entrada possui os números de blocos de disco. Se um arquivo usa mais blocos de disco que

os que se ajustam em uma entrada, são alocadas entradas adicionais.

O CP/M não informa o tamanho do arquivo em bytes. Pode-se apenas saber quantos blocos

estão alocados ao arquivo.

O campo código do usuário identifica o proprietário do arquivo. O nome de um arquivo é

composto pelo campo nome do arquivo (até 8 caracteres) concatenado com o campo tipo (até 3

caracteres, separados por um ponto. O campo grau é usado para indicar a ordem em que as entradas

do arquivo devem ser acessadas quando o arquivo for maior que 16 blocos.

6.4.2 Diretórios no MS-DOS

É um sistema de arquivos baseado em árvores de diretórios onde cada entrada de diretório

possui 32 bytes.

Atributos Bloco indireto simples

Nó-i

Bloco indireto duplo

Bloco indireto

triplo

Nome do arquivo Tipo (ext)

Código do usuárioGrau

Endereços de blocos de discoContagem de blocos

1 8 3 1 2 1 16

Page 74: Sistemas_Operacionais

Sistemas Operacionais 74

O campo número do primeiro bloco é usado como índice para uma tabela que representa a

lista encadeada de blocos.

Diretórios podem conter outros diretórios, conduzindo a um sistema de arquivos hierárquico.

6.4.3 Diretórios no UNIX

Cada entrada possui apenas um nome de arquivo e o número de seu nó-i. As outras

informações sobre o arquivo estão contidas no nó-i.

Quando é solicitada a abertura de um arquivo com caminho absoluto, o sistema operacional

localiza o diretório raiz (em uma posição fixa no disco) e busca diretório a diretório até chegar ao

diretório em que se encontra o arquivo. Então o nó-i do arquivo é carregado e mantido em memória

até que o arquivo seja fechado.

Nomes com caminho relativo são pesquisados da mesma forma, exceto que iniciam a partir do

diretório de trabalho.

A entrada “.” tem o número do próprio nó-i, enquanto a entrada “..” tem o número do nó-i do

diretório pai.

6.5 Proteção de Acesso

Como os meios de armazenamento são compartilhados entre diversos usuários é importante

que mecanismos de proteção sejam implementados para garantir a proteção individual de arquivos e

diretórios.

O tipo de acesso a arquivos é implementado mediante a concessão ou não dos diferentes

acessos que podem ser realizados, como leitura, gravação, execução e eliminação. O controle de

acesso às operações realizadas com diretórios possui diferenças em relação às operações realizadas

com arquivos.

Existem diferentes mecanismos e níveis de proteção, cada qual com suas vantagens e

desvantagens.

6.5.1 Senha de Acesso

O controle de acesso por senha se resume ao usuário ter o conhecimento da senha e,

consequentemente, ter a liberação do acesso ao recurso.

Como cada recurso possui apenas uma senha, o acesso é liberado ou não na sua totalidade.

Assim, não é possível determinar quais tipos de operação podem ou não ser concedidas. Outra

desvantagem é a dificuldade de compartilhamento de arquivos, pois além do dono do arquivo todos

os demais usuários teriam que conhecer a senha de acesso.

6.5.2 Grupos de Usuários

Esse tipo de proteção tem como princípio a associação de cada usuário do sistema a um ou

mais grupos. Os grupos de usuários são organizados logicamente com o objetivo de compartilhar

recursos, sendo que os usuários que desejam compartilhar recursos entre si devem pertencer a um

mesmo grupo.

Esse mecanismo implementa três níveis de proteção: dono, grupo e todos. Na criação do

recurso o usuário especifica se o recurso deve ser acessado somente pelo seu criador, pelos usuários

do grupo ao qual ele pertence ou por todos os usuários do sistema. É necessário especificar o tipo de

acesso (leitura, escrita, execução e eliminação) para cada nível de proteção.

Nome do arquivo

Extensão Número do primeiro blocoAtributos

8 3 1 10

Reservado

2Tama-nho

2 2 4

HoraData

Page 75: Sistemas_Operacionais

Sistemas Operacionais 75

6.5.3 Lista de Controle de Acesso

A Lista de Controle de Acesso (Access Control List - ACL) consiste em uma lista associada

a cada recurso, onde são especificados os usuários e os tipos de acesso permitidos. Quando um

usuário tenta acessar um recurso, o sistema operacional verifica se a lista de controle de aceso

autoriza a operação desejada.

Essa estrutura pode ser bastante extensa. Isto pode levar a um overhead se comparado com o

mecanismo de proteção por grupos de usuários por conta da pesquisa sequencial que o sistema

deverá realizar sempre que um acesso for solicitado.

Em alguns sistemas é possível encontrar tanto o mecanismo de proteção por grupos de

usuários quanto o por lista de controle de acesso, oferecendo uma maior flexibilidade ao mecanismo

de proteção de arquivos e diretórios.

6.6 Implementação de Cache

O acesso a disco é lento se comparado ao acesso à memória principal. Este é o principal fator

para as operações de E/S com discos serem um problema para o desempenho do sistema. Com o

objetivo de minimizar este problema, a maioria dos sistemas de arquivos implementa uma técnica

denominada cache. Neste esquema o sistema operacional reserva uma área na memória principal

para que se tornem disponíveis caches utilizados em operações de acesso ao disco.

Quando uma operação é realizada o sistema verifica se a informação desejada se encontra na

cache. Caso esteja disponível não é necessário acesso ao disco. Caso o bloco requisitado não se

encontre no cache, a operação de E/S é realizada e a cache é atualizada. Como existe uma limitação

no tamanho da cache, cada sistema adota uma política para substituição de blocos como, por

exemplo, a LRU.

Apesar de melhorar o desempenho do sistema, aspectos de segurança devem ser levados em

consideração. No caso de blocos de dados permanecerem por muito tempo na cache, a ocorrência

de problemas de energia pode ocasionar a perda de tarefas já realizadas e consideradas salvas em

disco. Existem duas maneiras de tratar este problema. No primeiro caso o sistema operacional

possui uma rotina que executa periodicamente atualizando em disco todos os blocos modificados da

cache. Uma segunda alternativa é realizar imediatamente uma atualização no disco sempre que um

bloco da cache for modificado (cache write-through). Quando a memória cache não atualiza o

disco imediatamente temos a chamada cache write-back. A técnica write-back implica em menor

quantidade de operações de E/S, porém o risco de perda de dados é maior. Isto não acontece nas

caches write-through em função do seu próprio funcionamento, mas o aumento considerável nas

operações de E/S tornam este método menos eficiente.

6.7 Exercícios

1) Qual o objetivo de um sistema de arquivos sob o ponto de vista do sistema operacional?

2) O que é um arquivo?

3) Por que o sistema operacional deve isolar o usuário com relação ao tipo de dispositivo em

que um arquivo foi armazenado?

4) As regras para formação do nome de um arquivo podem ser diferentes de um sistema de

arquivos para outro? Explique.

5) O que caracteriza um sistema de arquivos que armazena arquivos como uma sequência de

registros de tamanho fixo?

6) Qual a diferença entre acesso sequencial e acesso aleatório a um arquivo?

7) O que diferencia um diretório de um arquivo regular?

Page 76: Sistemas_Operacionais

Sistemas Operacionais 76

8) Qual a diferença entre arquivos especias de caractere e arquivos especiais de bloco?

9) O que diferencia um arquivo texto (ASCII) de um arquivo binário?

10) O que são atributos de arquivos?

11) O que é a tabela de arquivos?

12) O que é um sistema de diretórios hierárquico?

13) Qual a diferença entre o caminho absoluto e o caminho relativo de um arquivo?

14) Explique como funciona o esquema de alocação em disco utilizando:

lista encadeada

lista encadeada utilizando índice

nó-i

15) O que é a fragmentação para o esquema de alocação contínua de espaço em disco para

arquivos?

16) Por que se torna necessário realizar a desfragmentação de disco no esquema de alocação

contínua de espaço em disco para arquivos?

17) O que é a fragmentação para o esquema de alocação de arquivos por lista encadeada?

18) Qual a principal vantagem do esquema de alocação por lista encadeada utilizando índice

sobre o esquema de alocação por lista encadeada quando se deseja acrescentar informações a um

arquivo?

19) Por que se torna necessária a utilização de clusters (agrupamento de setores) quando se

utiliza a alocação por lista encadeada utilizando índice?

20) Para que são utilizados os blocos indiretos no esquema de alocação indexada (nós-i)?

21) Onde são armazenados os atributos de arquivos nas implementações de diretórios do:

MS-DOS

UNIX

22) Por que um sistema de arquivos precisa fornecer um esquema de proteção de acesso a

arquivos e diretórios?

23) O que é uma lista de controle de acesso?

24) Em que um sistema de cache auxilia no acesso a arquivos?

25) Qual a diferença entre uma cache write-through e uma cache write-back?

26) Que tipo de problema pode ocorrer que possa leva a um sistema de arquivos se tornar

inconsistente por causa do uso de cache? Como pode ser resolvido?

Page 77: Sistemas_Operacionais

Sistemas Operacionais 77

7 Gerência de Dispositivos

Os dispositivos que podem ser conectados a um computador podem variar de diversas

maneiras. Alguns exemplos das diferenças existem entre os dispositivos são:

transferência por bloco ou caractere

dados armazenados sequencialmente ou aleatoriamente

transferência síncrona ou assíncrona

acesso restrito ou compartilhado

somente leitura ou leitura e escrita

velocidade de acesso

Devido a esta grande variação, o sistema operacional precisa fornecer uma vasta gama de

funcionalidade às aplicações.

A gerência de dispositivos de entrada/saída (dispositivos de E/S) é uma das funções mais

complexas exercidas por um sistema operacional. Sua implementação é estruturada através de

camadas de software e de hardware, onde as camadas de mais baixo nível escondem das camadas

superiores as características do hardware, oferecendo uma interface simples e confiável para o

usuário e suas aplicações.

A gerência de dispositivos deve esconder das camadas superiores os diversos detalhes de cada

periférico e suas diferenças, como velocidade de operação, unidade de transferência, representação

dos dados, tipos de operações, etc. As camadas são divididas em dois grupos. O primeiro grupo

(independente do dispositivo) visualiza os diversos tipos de dispositivos do sistema de um modo

único, enquanto o segundo grupo (dependente do dispositivo) é específico para cada dispositivo.

As metas do software de entrada/saída são:

Independência do dispositivo → Processos devem poder ler/escrever de qualquer

dispositivo sem a necessidade de ser alterado.

Atribuição uniforme de nomes → O nome de um arquivo ou dispositivo deve ser simples

(uma string ou um número).

Tratamento de erros → Deve ser feito na camada mais baixa possível. Se for possível

corrigir, deve ser feito de forma que camadas superiores não sintam.

Transferências síncronas assíncronas → O sistema operacional deve fazer com que

todas as operações de E/S bloqueiem para o processo do usuário.

Dispositivos compartilhados dedicados → O sistema operacional deve tratar todos os

problemas inerentes ao acesso concorrente ao dispositivo.

7.1 O Hardware de Entrada/Saída

A comunicação de um dispositivo com o sistema computacional é feita por intermédio de

envio de sinais. Esta comunicação se dá a partir de um ponto de comunicação denominado porta. O

meio utilizado por um ou mais dispositivos para conexão com o restante do sistema computacional

Operações de E/S

Subsistema de E/S

Drivers de dispositivo

Controladores

Dispositivos

Independentedo dispositivo

Dependentedo dispositivo

Software

Hardware

Page 78: Sistemas_Operacionais

Sistemas Operacionais 78

é denominado barramento. Para cada barramento é definido um protocolo que especifica um

conjunto de mensagens que podem ser enviadas através dele.

A figura abaixo mostra uma estrutura típica do barramento de um microcomputador. Ela

possui um barramento que conecta o subsistema processadormemória aos dispositivos rápidos e

um barramento de expansão que faz a comunicação com dispositivos mais lentos.

Um controlador é um conjunto de componentes eletrônicos para operação de uma porta,

barramento ou dispositivo. Para que a UCP possa enviar comandos e dados a um controlador, o

controlador faz uso de registradores para armazenar dados e sinais de controle. A comunicação da

UCP com os controladores se dá por operações de leitura e escrita nestes registradores.

Uma porta de E/S consiste tipicamente em 4 registradores:

Status → Contém bits que podem ser lidos pela UCP e fornecem informações sobre o

estado do dispositivo.

Controle → Pode ser modificado pela UCP para alterar o modo de funcionamento do

dispositivo.

Entrada de dados → A UCP obtém valores do dispositivo lendo-os a partir deste

registrador.

Saída de dados → A UCP envia valores ao dispositivo escrevendo-os neste registrador.

7.1.1 Espera em Ciclo

Supondo que sejam utilizados 2 bits para controlar a troca de dados entra a UCP e o

controlador, o controlador indica seu estado por meio de um bit ocupado no registrador de status (1

indica que o controlador está realizando alguma operação e 0 indica que o controlador está pronto

para executar o próximo comando). A UCP usa o bit processar no registrador de controle,

marcando-o para especificar que um comando deve ser processado. Assim, a comunicação entre a

UCP e o controlador segue:

1. A UCP lê repetidamente o bit ocupado até que o bit esteja desligado.

2. A UCP armazena um valor no registrador de saída de dados e liga o bit escrever no

registrador de controle.

3. A UCP liga o bit processar.

4. O controlador vê o bit processar ligado e liga o bit ocupado.

5. O controlador vê o bit escrever ligado e lê o valor armazenado no registrador de saída

de dados, gravando este valor no dispositivo.

6. O controlador desliga os bits processar e ocupado para indicar que a operação de E/S

terminou.

Controlador de terminal gráfico

Monitor

Controlador de Memória

UCP

Memória cache

MemóriaControlador

SCSI

Controlador de disco IDE

Interface do barramento de

expansão

Porta paralela

Porta serial

Teclado

Barramento

Barramento de expansão

Discos SCSI

Discos IDE

Page 79: Sistemas_Operacionais

Sistemas Operacionais 79

Se o tempo de espera para o dispositivo ficar pronto for muito grande, a UCP ficará ociosa

por muito tempo enquanto poderia estar realizando outra tarefa.

7.1.2 Interrupções

O problema da espera em ciclo é o desperdício de UCP que o método gera. Para evitar este

desperdício o controlador pode fazer uso do mecanismo de interrupções para avisar a UCP sempre

que ele estiver pronto.

Ainda, o uso de interrupções possibilita à UCP responder a eventos assíncronos.

7.1.3 Acesso Direto à Memória

Para dispositivos de E/S cuja operação geralmente envolve grandes quantidades de dados, é

um desperdício usar a UCP para fazer a transferência, é melhor utilizar um processador

especializado chamado controlador de DMA (Direct Memory Access - Acesso Direto à Memória).

Para iniciar uma transferência a UCP informa ao controlador de DMA um endereço no

dispositivo de E/S, um endereço de memória, a quantidade de bytes a transferir e o sentido da

transferência. O controlador de DMA prossegue com a operação de E/S, usando diretamente o

barramento, ficando a UCP livre para executar outras operações.

Quando o controlador de DMA termina a transferência ele gera uma interrupção para avisar à

UCP.

7.2 Operações de Entrada/Saída

Sempre que um processo realiza uma operação de E/S o sistema deve tornar a tarefa o mais

simples possível para o usuário e suas aplicações. Para isso, o sistema operacional deve ser capaz de

se comunicar com qualquer dispositivo que possa ser conectado ao hardware do computador.

A maneira mais simples de um processo ter acesso a um dispositivo é através de bibliotecas

de comandos de entrada/saída, oferecidas pela maioria das linguagens de programação. Os

comandos de E/S de linguagens de alto nível independem do sistema operacional onde se está

trabalhando.

Para obter a independência de dispositivos as operações de E/S devem ser realizadas por

intermédio de chamadas de sistema (system calls). As chamadas de sistema responsáveis por essa

comunicação são rotinas presentes na camada de mais alto nível implementada pelo sistema

operacional, permitindo ao usuário realizar operações de E/S sem se preocupar com detalhes do

dispositivo que está sendo acessado. O relacionamento entre os comandos de E/S oferecidos pelas

linguagens de programação de alto nível e as chamadas de sistema é criado durante a linkedição do

programa.

Nem todo software de entrada/saída no nível do usuário pertence à biblioteca de

procedimentos. Outra categoria importante é a dos sistemas de spooling. O spool (Simultaneous

Peripheral Operation On-Line) é uma forma de se tratar com dispositivos dedicados que fazem

parte de um sistema multiprogramado. Para realizar o spool são criados processos especiais

denominados daemons e diretórios denominados diretórios de spooling.

Para evitar que um processo ganhe o uso de um dispositivo compartilhado e não o libere mais,

ou que mais de um processo ganhe acesso simultâneo a um dispositivo que deva trabalhar de forma

dedicada, o daemon é o único processo que poderá ganhar o uso deste dispositivo. Sempre que um

processo desejar escrever neste dispositivo, ele deverá gerar um arquivo no diretório de spooling e

AplicaçãoComandos

de E/SChamadas de sistema

Dispositivos

Page 80: Sistemas_Operacionais

Sistemas Operacionais 80

solicitar ao daemon que faça a transferência do arquivo para o dispositivo. Exemplos de daemons

são daemon de impressão e daemon de rede.

7.3 Subsistema de Entrada/Saída

O subsistema de entrada/saída é responsável por realizar as funções que são comuns a todos

os dispositivos oferecendo uma interface uniforme, ficando os aspectos específicos de cada

periférico a cargo dos drivers de dispositivo.

Uma de suas funções é mapear o nome do dispositivo com o seu respectivo driver. As

camadas superiores conhecem apenas o nome do dispositivo e utilizam esse nome para terem acesso

ao periférico.

Cada dispositivo trabalha com unidades de informação de tamanhos diferentes. O subsistema

de E/S é responsável por criar uma unidade lógica de informação independente do dispositivo e

repassá-la aos níveis superiores.

O tratamento de erros nas operações de E/S costuma ser realizado nas camadas mais próximas

ao hardware, mas certos erros podem ser tratados e reportados de forma uniforme

independentemente do dispositivo pelo subsistema de E/S.

Alguns dispositivos podem ser compartilhados simultaneamente entre diversos usuários,

enquanto outros devem ter acesso exclusivo. O sistema operacional deve controlar os

compartilhamentos de forma organizada. O subsistema de E/S é responsável também por

implementar o mecanismo de proteção de acesso aos dispositivos.

A bufferização é outra tarefa do subsistema de E/S. Ela permite reduzir o número de

operações de E/S utilizando uma área de memória intermediária (buffer). Quando é solicitada a

leitura de um dado do disco o sistema traz para o buffer, além do dado, um bloco de dados. Na

próxima vez que uma nova leitura de um dado que pertença ao bloco for solicitada, ele já estará no

buffer, evitando uma nova operação de E/S.

Muitas das funções do subsistema de E/S estão envolvidas com o sistema de arquivos. A

implementação de caches é um exemplo desse relacionamento.

As operações de E/S podem ser classificadas de acordo com seu sincronismo. Um processo

que realiza uma operação síncrona fica bloqueado até o término da operação, enquanto um processo

que realiza uma operação assíncrona não precisa aguardar pelo término da operação e pode

continuar executando normalmente.

7.4 Drivers de Dispositivo

O driver de dispositivo (device driver), ou somente driver, tem como função a comunicação

com dispositivos de E/S em nível de hardware. Enquanto o subsistema de E/S trata de funções que

afetam a todos os dispositivos, os drivers tratam apenas dos seus aspectos particulares.

Cada driver manipula somente um tipo de dispositivo ou grupo de dispositivos semelhantes.

Os drivers têm como função receber comandos genéricos sobre acessos aos dispositivos e traduzi-

los para comandos específicos, que poderão ser executados pelos controladores.

O driver interage diretamente com as funções do controlador. Ele reconhece quantos

registradores o controlador possui, como são utilizados e quais são seus comandos. Sua função

principal é receber comandos abstratos e traduzi-los para comandos que o controlador possa

executar.

Page 81: Sistemas_Operacionais

Sistemas Operacionais 81

Após a solicitação ao controlador o processo pode bloquear ou não. Um processo que foi

bloqueado será acordado por uma interrupção. O processo não será bloqueado por uma operação de

E/S somente se o dispositivo responde muito rápido a uma solicitação (um terminal, por exemplo).

Depois de responder a uma solicitação o driver fica bloqueado aguardando uma nova

solicitação, a não ser que haja uma outra solicitação enfileirada, caso em que ela passa ser atendida.

Os drivers fazem parte do núcleo do sistema operacional. Devido ao alto grau de dependência

entre os drivers e as chamadas de sistema de E/S, os fabricantes desenvolvem, para um mesmo

dispositivo, diferentes drivers (um para cada sistema operacional). Sempre que um novo dispositivo

é adicionado, o driver do dispositivo deve ser incorporado ao sistema.

7.5 Controladores

Os controladores são componentes eletrônicos (hardware) responsáveis por manipular

diretamente os dispositivos de E/S. Os drivers se comunicam com os dispositivos através dos

controladores.

O controlador costuma possuir memória e registradores próprios para executar instruções

enviadas pelo driver. Quando se tem uma arquitetura com E/S mapeada em memória o endereço dos

registradores fazem parte do espaço de endereçamento da memória (por exemplo, processadores

680x0 da Motorola). Outras arquiteturas possuem espaço de endereçamento especial de E/S, com

cada controladora recebendo uma parte deste espaço (por exemplo, processadores i386 da Intel).

Os controladores fazem uso de interrupções para informar à UCP quando estão prontos.

Máquinas Pentium, por exemplo, possuem 15 linhas de IRQ (Interrupt Request – Requisição de

Interrupção), que podem ser configuradas de forma automática (Plug and Play) ou de forma

manual. As IRQs configuradas manualmente podem ser configuradas por hardware (switches ou

jumpers) ou por software. Se a controladora for hard-wired não é possível mudar sua IRQ.

O controlador de interrupções mapeia o software de serviço da interrupção através do vetor

de interrupções.

Na maioria dos dispositivos orientados a bloco é implementada a técnica de DMA (Direct

Memory Access Acesso Direto à Memória) para transferência de dados entre o controlador e a

memória principal.

Alguns controladores implementam técnicas de cache semelhantes às implementadas pelos

sistemas de arquivos, visando melhorar o desempenho. Normalmente o controlador avisa o sistema

operacional do término de uma operação de gravação quando os dados no buffer do controlador são

gravados no disco (write-through). O controlador também pode ser configurado para avisar do

término da gravação quando os dados ainda se encontram no buffer do controlador (write-back),

oferecendo ganho de desempenho.

Um padrão muito popular para a conexão de dispositivos a um computador é o SCSI (Small

Computer Systems Interface). O SCSI define padrões de hardware e software que permitem

conectar ao computador qualquer tipo de dispositivo. Para tanto deve-se configurar o sistema

operacional com um driver SCSI e o hardware com um controlador SCSI, ao qual os periféricos são

conectados.

Chamada de sistema

Driver de disco

Controlador de disco

Ler bloco n

Ler bloco no

disco xcilindro ycabeça wsetor z

Page 82: Sistemas_Operacionais

Sistemas Operacionais 82

7.6 Dispositivos de Entrada/Saída

Os dispositivos de E/S são responsáveis pela comunicação entre o computador e o mundo

externo. A transferência de dados pode ser feita através de blocos de informação ou palavra a

palavra, ambas por meio dos controladores dos dispositivos sob a supervisão da UCP.

Em função da forma com que os dados são armazenados, os dispositivos de E/S podem ser

classificados em duas categorias: dispositivos estruturados e dispositivos não estruturados.

Os dispositivos estruturados (dispositivos de bloco) caracterizam-se por armazenar

informações em blocos de tamanho fixo, possuindo cada qual um endereço. Os blocos podem ser

lidos ou gravados de forma independente. Os dispositivos estruturados classificam-se ainda em

dispositivos de acesso direto e dispositivos de acesso sequencial. Em um dispositivo de acesso

direto cada bloco pode ser acessado diretamente através de seu endereço, enquanto em um

dispositivo de acesso sequencial o dispositivo deve percorrer sequencialmente o meio à procura do

bloco.

Os dispositivos não estruturados (dispositivos de caractere) são aqueles que enviam/recebem

uma sequência de caracteres sem estar estruturada no formato de um bloco. Não é possível o acesso

ao dado após a transmissão (os dados não possuem endereço).

7.6.1 Discos de RAM

O disco de RAM é um driver utilizado para permitir que uma parte da memória seja utilizada

como um disco comum. Ele tem a vantagem de ter acesso instantâneo, sendo adequado para

programas e dados que são acessados frequentemente.

O disco de RAM é dividido em blocos, cada qual do mesmo tamanho que um bloco de disco.

Quando o driver recebe uma solicitação ele calcula onde os dados devem ser lidos/escritos na

memória e faz a transferência.

7.6.2 Disco Rígido

Um disco rígido é constituído por vários discos sobrepostos, unidos por um eixo vertical

girando a uma velocidade constante. Cada disco compõe-se de trilhas concêntricas, que são

divididas em setores. As trilhas dos diferentes discos que ocupam a mesma posição vertical formam

um cilindro. Para cada superfície de um disco existe uma cabeça de leitura/gravação. O conjunto de

cabeças é preso a um braço que se movimenta entre os vários cilindros no sentido radial.

Discos modernos têm mais setores nas trilhas exteriores que nas trilhas interiores. O

processamento destes detalhes é feito pelo hardware da própria unidade de disco.

O tempo necessário para ler/gravar um bloco de dados é função de três fatores: tempo de

busca, latência e transferência. O tempo de busca (seek) é o tempo gasto para mover o braço até o

cilindro onde o bloco se encontra. O tempo de latência é o tempo de espera até que o setor desejado

setor

trilha

eixo

Cabeça de leitura/gravação

braço

Page 83: Sistemas_Operacionais

Sistemas Operacionais 83

se posicione sob a cabeça de leitura/gravação. O tempo de transferência corresponde ao tempo

necessário para ler/gravar o bloco.

O tempo total das operações de E/S pode ser extremamente demorado se comparado ao tempo

da UCP. Para a maioria dos discos magnéticos o tempo de busca é o fator de maior impacto no

acesso aos dados. Se o driver de disco processa as solicitações na ordem em que elas chegam

(FCFS – First Come First Served) não está havendo nenhuma otimização no sistema. O driver

pode manter uma tabela de solicitações indexada pelo número do cilindro para melhorar o

algoritmo.

Um algoritmo conhecido como busca mais curta primeiro (SSF – Shortest Seek First) trata a

solicitação mais próxima a seguir, reduzindo o movimento total do braço em comparação com o

FCFS. O SSF tem o problema da tendência do braço permanecer no meio do disco a maior parte do

tempo, havendo a possibilidade de solicitações longe do meio obterem serviço deficiente.

O algoritmo do elevador resolve este problema movendo o braço sempre na mesma direção

até que não haja mais solicitação nesta direção.

A utilização de cache associada com a leitura de mais dados que o solicitado também ajuda a

melhorar o desempenho.

A verificação por redundância cíclica (CRC) é gravada junto com o setor para verificação de

erros.

7.6.2.1 Arrays de Discos

A redundância pela utilização de múltiplos discos rígidos (arrays de discos) é uma forma

muito comum de se aumentar o desempenho e a segurança no armazenamento de dados. O tipo

mais comum de implementação de array de disco é o RAID (Redundant Array of Inexpensive

Drives).

O RAID pode ser implementado tanto em software quanto em hardware. A implementação

em software é mais barata, mas torna o sistema mais lento. Quando implementado em hardware o

RAID não só é mais rápido, como também pode permitir a troca de um dispositivo defeituoso sem a

necessidade de desligar o equipamento (o chamado hot swap).

O RAID pode ser implementado em 7 níveis diferentes, cada um com um objetivo específico.

O RAID nível 0 não é redundante, consequentemente não deveria se chamar “RAID”. No

nível 0, os dados são divididos pelos discos, resultando em uma maior vazão. Considerando que

nenhuma informação redundante é armazenada o desempenho é muito bom, mas uma falha de

qualquer dos discos resulta na perda dos dados. Este nível é costumeiramente chamado de striping.

O RAID nível 1 provê redundância escrevendo todos os dados em dois ou mais discos. O

desempenho do nível 1 tende a ser maior na leitura e menor na escrita quando comparado a um

sistema com um único disco, mas se qualquer dos discos falhar, nenhum dado é perdido. Este é um

bom sistema redundante, uma vez que são requeridos somente dois discos. Porém, como um disco é

Cabeça

BuscaLatência

Transferência

Page 84: Sistemas_Operacionais

Sistemas Operacionais 84

usado para armazenar uma cópia dos dados, o custo por megabyte é alto. Este nível é

costumeiramente chamado de mirroring (espelhamento).

O RAID nível 2, que usa o código de Hamming para correção erros, é voltado para uso em

discos que não têm detecção de erro embutida.

O RAID nível 3 divide os dados a nível de byte por vários discos, com paridade armazenada

em um disco. A divisão a nível de byte requer suporte por hardware para uso eficiente.

O RAID nível 4 divide os dados a nível de bloco por vários discos, com paridade armazenada

em um disco. A informação de paridade permite a recuperação de falha provocada por falha em

qualquer dos discos. O desempenho do nível 4 é muito bom para leitura (semelhante ao nível 0).

Para escrita, entretanto, é necessário que a paridade seja atualizada a todo instante. Isto reduz a

velocidade para pequenas escritas randômicas; entretanto, para grandes escritas ou escritas

sequenciais, ele é bastante rápido. Como somente um disco do array armazena dados redundantes, o

custo por megabyte do nível 4 é baixo.

O RAID nível 5 é semelhante ao nível 4, mas distribui a paridade entre os discos. Isto pode

acelerar pequenas escritas em sistemas multiprocessados, já que o disco de paridade não se torna

um gargalo. Como os dados da paridade precisam ser saltados em cada disco durante a leitura, o

desempenho para leitura tende a ser menor que no nível 4. O custo por megabyte é igual ao do nível

4.

O RAID nível 6 funciona como o RAID nível 5, porém apresenta o dobro de bits para

paridade. Com isto o sistema garante a integridade dos dados mesmo com a perda de dois discos.

É possível também a composição de diferentes níveis de RAID:

RAID 0+1 → É uma combinação dos níveis 0 (striping) e 1 (mirroring), onde os dados

são divididos entre os discos para melhorar o rendimento, mas também utilizam outros

discos para duplicar as informações. Assim, é possível utilizar o bom rendimento do nível

0 com a redundância do nível 1.

RAID 1+0 → Exige ao menos 4 discos rígidos. Cada par será espelhado, garantindo

redundância, e os pares serão distribuídos, melhorando desempenho. Até metade dos

discos pode falhar simultaneamente, sem colocar o conjunto a perder, desde que não

falhem os dois discos de um espelho qualquer.

RAID 50 → É um arranjo híbrido que usa as técnicas de RAID com paridade em

conjunção com a segmentação de dados. Um arranjo RAID-50 é essencialmente um

arranjo com as informações segmentadas através de dois ou mais arranjos.

RAID 100 → É basicamente composto do RAID 10+0.

Page 85: Sistemas_Operacionais

Sistemas Operacionais 85

7.7 Exercícios

1) Por que a gerência de dispositivos é implementada mediante a utilização de camadas?

2) Por que a gerência de dispositivos esconde dos processos dos usuários os detalhes de cada

periférico?

3) Para um hardware de entrada/saída defina:

porta

barramento

controlador

4) Por que a espera em ciclo não é um bom método para comunicação da UCP com um

controlador?

5) Como é a transferência de dados por DMA? Qual a sua importância para o sistema?

6) Por que a transferência de dados por DMA é preferível em relação à transferência de dados

individuais com utilização de interrupções?

7) Como um programa faz para ter acesso às operações de E/S?

8) Como se dá em um sistema de multiprogramação o acesso de processos a dispositivos

dedicados, como impressoras, que devem ser acessados com exclusividade pelos processos?

9) Para que serve a bufferização no subsistema de E/S?

10) Qual a diferença entre uma operação de E/S síncrona e uma operação de E/S assíncrona?

11) O que é um driver de dispositivo (device driver)?

12) Qual a vantagem da implementação de cache em controladores de dispositivos de E/S?

13) Qual a diferença entre dispositivos de bloco e dispositivos de caractere?

Page 86: Sistemas_Operacionais

Sistemas Operacionais 86

14) Por que o processo de desfragmentação o disco torna o acesso aos arquivos mais rápido?

15) Explique como a utilização de um RAID pode tornar o sistema mais rápido e seguro.

Page 87: Sistemas_Operacionais

Sistemas Operacionais 87

8 Bibliografia

Machado, F. B. & Maia, L. P., “Arquitetura de Sistemas Operacionais”, 4a ed, LTC Editora,

Rio de Janeiro, 2012.

Silberschatz, A. & Galvin, P. B., “Sistemas Operacionais – Conceitos”, 5a ed, Prentice Hall,

São Paulo, 2000.

Tanenbaum, A. S. & Woodhull, A. S., “Sistemas Operacionais – Projeto e Implementação”,

2a ed, Ed. Bookman, Porto Alegre, 2000.

Tanenbaum, A. S., “Sistemas Operacionais Modernos”, LTC Editora, Rio de Janeiro, 1995.

Tanenbaum, A. S., “Structured Computer Organization”, 3rd

ed, Prentice Hall International,

1990.

Soares, L. F. G. et alii, “Redes de Computadores – Das LANs, MANs e WANS às Redes

ATM”, 2a ed, Ed. Campus, Rio de Janeiro, 1995.

Tanenbaum, A. S., “Redes de Computadores”, 3a ed, Ed. Campus, Rio de Janeiro, 1997.