Anda di halaman 1dari 59

Introdução aos Sistemas Operacionais para

Sistemas de Informação

Universidade Salgado de Oliveira


Análise de Sistemas
Sistemas Operacionais

Wagner Arbex
arbex AT jf.universo.edu.br
arbex AT arbex.pro.br

Fevereiro/2010
Sumário

Lista de Figuras

Lista de Tabelas

Lista de Algoritmos p. 8

Apresentação p. 9

1 Introdução aos sistemas de computação p. 11

1.1 Definição de sistemas de computação . . . . . . . . . . . . . . . . . . . p. 11

1.2 Introdução à arquitetura de computadores e classificação de processadores p. 12

1.3 Evolução e classificação de sistemas de computação em relação ao funci-


onamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 16

1.3.1 Sistemas monoprogramados . . . . . . . . . . . . . . . . . . . . p. 17

1.3.2 Sistemas multiprogramados . . . . . . . . . . . . . . . . . . . . p. 17

1.3.3 Sistemas de tempo compartilhado . . . . . . . . . . . . . . . . . p. 17

1.3.4 Sistemas multiexecutados . . . . . . . . . . . . . . . . . . . . . p. 18

1.3.5 Sistemas multiprocessados . . . . . . . . . . . . . . . . . . . . . p. 18

2 Conceitos básicos de sistemas operacionais p. 19

2.1 Introdução e definição . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 19

2.2 Arquitetura de sistemas operacionais . . . . . . . . . . . . . . . . . . . p. 20

2.2.1 Arquitetura monolítica . . . . . . . . . . . . . . . . . . . . . . . p. 20

2.2.2 Arquitetura em camadas . . . . . . . . . . . . . . . . . . . . . . p. 21


2.2.3 Arquitetura de micronúcleo . . . . . . . . . . . . . . . . . . . . p. 21

2.3 Chamadas ao sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 22

2.4 Interpretador de comandos . . . . . . . . . . . . . . . . . . . . . . . . . p. 24

3 Processos p. 26

3.1 Definição e estrutura . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 26

3.2 Estados e transições . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 28

3.3 Problemas de comunicação entre processos . . . . . . . . . . . . . . . . p. 30

3.3.1 O problema do produtor-consumidor (PPC) . . . . . . . . . . . p. 30

3.3.2 O problema dos filósofos chineses (PFC) . . . . . . . . . . . . . p. 33

3.4 Escalonamento de processos . . . . . . . . . . . . . . . . . . . . . . . . p. 35

3.4.1 Escalonamento first-in-first-out (FIFO) . . . . . . . . . . . . . . p. 35

3.4.2 Escalonamento shortest job first (SJF) . . . . . . . . . . . . . . p. 36

3.4.3 Escalonamento round-robin (RR) . . . . . . . . . . . . . . . . . p. 36

3.4.4 Escalonamento com prioridade (EP) . . . . . . . . . . . . . . . p. 38

4 Gerência de memória p. 40

4.1 Introdução e conceitos básicos . . . . . . . . . . . . . . . . . . . . . . . p. 40

4.2 Gerência de memória pelo modelo de monoprogramação . . . . . . . . . p. 40

4.3 Gerência de memória pelo modelo de multiprogramação . . . . . . . . . p. 41

4.3.1 Multiprogramação com partições fixas . . . . . . . . . . . . . . p. 42

4.3.2 Multiprogramação com partições variáveis . . . . . . . . . . . . p. 42

4.3.3 Estratégia de ocupação em memória em sistemas de multiprogra-


mação com partições variáveis . . . . . . . . . . . . . . . . . . . p. 44

4.4 Memória virtual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 45

4.4.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 45

4.4.2 Conceitos básicos . . . . . . . . . . . . . . . . . . . . . . . . . . p. 45

4.4.3 Funcionamento geral . . . . . . . . . . . . . . . . . . . . . . . . p. 46


4.4.4 Paginação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 47

4.4.5 Segmentação . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 48

5 Sistemas de arquivos p. 51

5.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 51

5.2 Estruturas de arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 52

5.2.1 Seqüência de bytes não estruturada . . . . . . . . . . . . . . . . p. 52

5.2.2 Seqüência de registros de comprimento fixo . . . . . . . . . . . . p. 52

5.2.3 Árvore de registros . . . . . . . . . . . . . . . . . . . . . . . . . p. 53

5.3 Método de alocação de espaço para arquivos . . . . . . . . . . . . . . . p. 53

5.3.1 Alocação contígua . . . . . . . . . . . . . . . . . . . . . . . . . . p. 53

5.3.2 Alocação por lista encadeada . . . . . . . . . . . . . . . . . . . p. 54

5.3.3 Alocação encadeada por tabela . . . . . . . . . . . . . . . . . . p. 55

5.3.4 Alocação indexada . . . . . . . . . . . . . . . . . . . . . . . . . p. 56

5.4 Gerenciamento de espaço livre . . . . . . . . . . . . . . . . . . . . . . . p. 57

Referências p. 59
Lista de Figuras

1 Componentes básicos de um computador de arquitetura von Neumann. p. 12

2 Arquitetura SISD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 13

3 Arquitetura MISD. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 14

4 Arquitetura SIMD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 14

5 Arquitetura MIMD. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 15

6 Diferenciação entre multiprocessadores e multicomputadores pelo tipo de


memória. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 15

7 Diferenciação entre multiprocessadores e multicomputadores pelo con-


ceito de localidade (Fonte: Giozza et al. (1986)). . . . . . . . . . . . . . p. 16

8 Representação da organização de um sistema operacional com arquite-


tura de microkernel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 22

9 Exemplo de uma árvore de processos. O processo A é a raiz da árvore e


criou dois processos-filho B e C, o processo B é raiz de uma sub-árvore e
criou três processos-filho D, E e F e os processos C, D, E e F são folhas
da árvore. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 28

10 Grafo de processo ou grafo de execução de programa. . . . . . . . . . . p. 30

11 Classes de prioridade para aplicação do algoritmo EP. . . . . . . . . . . p. 39

12 Três modelos simples de organização de memória (TANENBAUM, 2008). p. 41

13 Partições fixas: (a) Múltiplas filas de entrada, separadas por partições.


(b) Fila de entrada única (TANENBAUM, 2008). . . . . . . . . . . . . . p. 43

14 Substituição dos processos em memória, por meio de swap (TANENBAUM,


2008). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 43

15 No espaço de endereçamento único pode ocorrer “colisão" entres as ta-


belas (TANENBAUM, 2008). . . . . . . . . . . . . . . . . . . . . . . . . . p. 48
16 Múltiplos espaços de endereçamento. Cada tabela pode aumentar ou
diminuir, independente das demais (TANENBAUM, 2008). . . . . . . . . p. 49

17 Estrutura de arquivos: (a) Seqüência de byte não estruturada. (b)


Seqüência de registros de comprimento fixo. (c) Árvore de registros (TA-
NENBAUM, 2008). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 53

18 Exemplo de utilização de alocação contígua, mostrando o espaço alocado


para 8 arquivos (A, B, C, F, H, I, P e J). . . . . . . . . . . . . . . . . . p. 54

19 Exemplo de utilização de alocação contígua, mostrando um disco ocu-


pado por 7 arquivos (A, B, C, D, E, F e G) e em seguida, dois arquivos
são removidos (D e E) (TANENBAUM, 2008). . . . . . . . . . . . . . . . p. 54

20 Alocação encadeada (Fonte: Silberschatz, Galvin e Gagne (2009)). . . . p. 55

21 Alocação encadeada por tabela . . . . . . . . . . . . . . . . . . . . . . p. 56

22 Alocação indexada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 56

23 Implementação da alocação indexada pelo unix file system - UFS, com


índices direto, simples, duplo e triplo. . . . . . . . . . . . . . . . . . . . p. 57

24 Mapa de bits para gerência de espaço livre. . . . . . . . . . . . . . . . . p. 58


Lista de Tabelas

1 Classificação de Flynn. . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 13

2 Exemplo de uma tabela de processo. . . . . . . . . . . . . . . . . . . . p. 27


8

Lista de Algoritmos

1 Um interpretador de comandos simplificado. . . . . . . . . . . . . . . . . p. 25

2 Algoritmo para solução do Problema do Produtor-Consumidor, utilizando


as primitivas sleep e wakeup. . . . . . . . . . . . . . . . . . . . . . . . . p. 32

3 Algoritmo para solução do Problema dos Filósofos Chineses. . . . . . . . p. 33

4 Um exemplo do SJF para escalonamento a longo prazo. . . . . . . . . . p. 37


9

Apresentação

sse texto é indicado para estudos iniciais em sistemas operacionais, fornecendo uma
E visão abrangente, mas, não aprofundada, dos fundamentos dos sistemas operacionais,
definindo os conceitos fundamentais e básicos, seus aspectos e mecanismos, diferenciando
os modelos conceitual a de implementação dos sistemas operacionais para permitir a
compreensão de suas funcionalidades e tarefas. Destina-se a alunos dos primeiros períodos
dos cursos de Análise de Sistemas e Sistemas de Informação e, de forma alguma, substitui
textos clássicos, tais como Tanenbaum (2003) e Oliveira, Caríssimi e Toscani (2004), que,
inclusive, são largamente utilizados na elaboração do conteúdo1 aqui exposto.

Os objetivos desse texto são:

• Apresentar problemas conceituais e tecnológicos e as respectivas soluções presentes


na construção de sistemas operacionais modernos, no que visa a esconder as peculi-
aridades do hardware, para apresentar aos usuários um sistema de computação mais
fácil de ser utilizado, mais amigável e mais seguro;

• Apresentar problemas conceituais e tecnológicos e as respectivas soluções, presen-


tes na construção de sistemas operacionais modernos, no que visa a administração
dos recursos de hardware, de modo a satisfazer as solicitações de forma eficiente,
garantindo o compartilhamento e resolvendo possíveis conflitos;

• Comparar os principais problemas conceituais e tecnológicos presentes na construção


dos sistemas operacionais e suas soluções adotadas na implementação de sistemas
operacionais modernos.

Em contrapartida, não é objetivo desse texto aprofundar-se em qualquer tópico refe-


rente ao projeto ou à implementação de sistemas operacionais, assim como não se propõe
a ensinar a operar qualquer sistema operacional.
1
O conteúdo desse texto foi preparado a partir das publicações citadas ao longo do mesmo, assim como
dos trabalhos de Deitel, Deitel e Choffnes (2005), Silberschatz, Galvin e Gagne (2008) e Tanenbaum e
Woodhull (2008). Todos os textos utilizados encontram-se listados nas referências bibliográficas.
10

A motivação para a elaboração do presente texto está no fato de que, o correto en-
tendimento dos mecanismos presentes nos sistemas operacionais permite, ao profissional
de informática, uma melhor compreensão do escopo do seu trabalho, bem como, do seu
ambiente de trabalho, resultando no desenvolvimento de soluções com maior qualidade e
eficiência. Assim, esse texto busca fornecer ao futuro profissional de sistemas um conheci-
mento fundamental para atuar nas áreas de suporte, seleção e recomendação de sistemas
operacionais, além disso, visão básica do desenvolvimento e implementação de um sis-
tema operacional e, principalmente, fornecer subsídios necessários para a compreensão do
funcionamento dos sistemas operacionais.

Ao ler este texto, lembre-se:

esse texto é somente uma referência e, obrigatoriamente, deve ser


acompanhado de estudo complementar .

e... bom proveito.


11

1 Introdução aos sistemas de


computação

1.1 Definição de sistemas de computação

Um sistema de computação é um conjunto de recursos computacionais,


sendo uma parte implementado em hardware e outra parte em software, de-
pendendo do projeto, para interagirem cooperativamente.

Em torno de um sistema de computação, existem usuários com problemas distintos


para serem resolvidos. Por exemplo, um usuário precisa editar texto, enquanto outro
precisa fazer a contabilidade da empresa. Deste modo, o problema de cada usuário deve
ser atendido por um programa, ou aplicativo, específico que, nesse caso, seriam, respecti-
vamente, um editor de textos e um programa de contabilidade. Além disso, o sistema de
computação deve possuir o hardware necessário, ou seja, os dispositivos físicos ou recursos
físicos, para a execução desses programas. De um modo geral, programas possuem muito
em comum, por exemplo, tanto editor de texto como o sistema de contabilidade precisam
acessar o disco, e a forma de acesso aos periféricos é a mesma para todos os programas.

Para um bom aproveitamento dos recursos computacionais, os sistemas de computação


são projetados para que vários usuário possam compartilhar os recursos simultaneamente.
Nesse caso, os aplicativos podem apresentar necessidades conflitantes, pois muitas vezes
disputam os mesmos recursos. Por exemplo, o editor de texto e o sistema de contabilidade
podem solicitar, ao mesmo tempo, a única impressora do sistema de computação.

Assim, qualquer pessoa que utilize um sistema de computação deve saber que existe
um software chamado sistema operacional, que de alguma forma controla os recursos do
sistema de computação, qualquer que seja o seu tipo ou porte. Sistemas operacionais
são necessários nos mais simples microcomputadores domésticos ou nos mais sofisticados
supercomputadores de institutos de pesquisa ou de grandes corporações.
12

1.2 Introdução à arquitetura de computadores e classi-


ficação de processadores

A arquitetura de um computador é um modelo da organização e funcionamento do


hardware de um sistema de computação. A descrição da arquitetura destaca as funções
dos componentes básicos de um computador, a interconexão desses componentes e o modo
como interagem.

Como exemplo, serão tratados sistemas que fazem parte do cotidiano, basicamente,
microcomputadores e estações de trabalho, e a Figura 1 mostra a organização típica desses
dois tipos de sistemas (arquitetura von Neumann1 ).

Figura 1: Componentes básicos de um computador de arquitetura von Neumann.

A Figura 1 mostra os componentes básicos de um computador: o processador, a


memória principal e as interfaces de entrada e saída. O processador é o componente
ativo, controlador de todo o sistema. É o processador que realiza todas as operações
sobre os dados, de acordo com o indicado pelas instruções no código do programa.

A memória principal armazena tanto as instruções que são executadas pelo proces-
sador como os dados que serão manipulados. As interfaces de entrada e de saída são as
portas de comunicação para o mundo externo, às quais estão conectados os dispositivos
periféricos, tais como, vídeo, teclado, discos e impressora.

Esses componentes estão interconectados por meio de um barramento, através do


qual o processador realiza o acesso às instruções e aos dados armazenados na memória
principal. É também por meio desse mesmo barramento que o processador recebe ou envia
dados de/ou para as interfaces de entrada/saída. Instruções e dados estão armazenados
1
John von Neumann, matemático húngaro, naturalizado americano, que definiu o modelo do compu-
tador seqüencial digital em que o programa a ser executado é armazenado na memória, e o processamento
é feito de forma algorítmica, ou seja, passo a passo, caracterizando um comportamento determinístico.
13

em locações de memória, na memória principal. Para realizar o acesso a uma informação,


o processador envia para a memória, por meio do barramento, o endereço da locação de
memória que contém a informação onde será feito o acesso. A informação é trocada entre
o processador e a memória também através do barramento. O acesso às interfaces de
entrada/saída é semelhante, sendo cada interface identificada por um endereço único.

Um processador busca instruções e operandos na memória principal, executa as ins-


truções e coloca os resultados na memória. As instruções são transferidas seqüencialmente
da memória para o processador, formando o fluxo de instruções. Da mesma forma, os ope-
randos formam uma outra seqüência, fluxo de dados, entre a memória (M) e o processador
(P).

Foram feitas diferentes propostas para a classificação de arquiteturas de computadores


e de processadores, mas talvez a mais aceita seja a proposta por Flynn (1972) apud
Rose e Navaux (2003), mostrada na Tabela 1, que baseia-se no fato de um computador
executar uma seqüência de instruções, ou fluxo de instruções (instruction stream), sobre
uma seqüência de dados, ou fluxo de dados (data stream).

Single Data Multiple Data


(SD) (MD)
Single Instruction SISD SIMD
(SI) von Neumann Máquinas array
Multiple Instruction MISD MIMD
(MI) Multiprocessadores e Multicomputadores

Tabela 1: Classificação de Flynn.

Na classe SISD (Figura 2), um único fluxo de instruções atua sobre um único fluxo
de dados. Nessa categoria se encaixam os computadores com um único processador que
possuem um único elemento processador, que enquadra as máquinas von Neumann tra-
dicionais com apenas um processador, como microcomputadores pessoais e estação de
trabalho. As instruções são executadas seqüencialmente, mas o procedimento de execu-
ção pode ser subdividido em diferentes etapas de execução em série, como em um pipeline.

Figura 2: Arquitetura SISD

Na classe MISD (Figura 3), múltiplos fluxos de instruções atuariam sobre um único
fluxo de dados, onde diferentes unidades de processamento executariam diferentes instru-
ções sobre um mesmo fluxo de dados. Na prática, diferentes instruções deveriam operar
14

a mesma posição de memória ao mesmo tempo. Tal situação, até os dias atuais, não faz
o menor sentido, assim não existem sistemas que representem essa classe.

Figura 3: Arquitetura MISD.

As máquinas paralelas podem ser SIMD ou MIMD, representadas, respectivamente,


nas Figuras 4 e 5. No caso da arquitetura SIMD, uma única instrução é executada ao
mesmo tempo sobre múltiplos dados. Todos os processadores, controlados por uma única
unidade de controle (C), executam suas instruções em paralelo de forma síncrona sobre
diferentes fluxos de dados. Pode-se dizer que o mesmo programa está sendo executado
sobre diferentes dados, o que faz com que o princípio de execução SIMD seja muito
próximo ao paradigma de execução seqüencial. Nessa classe encontram-se as máquinas
array, enquadrando alguns supercomputadores.

Figura 4: Arquitetura SIMD

A classe MIMD, em essência, pode representar um grupo de computadores indepen-


dentes, cada um com seu contador de programa, com seu próprio programa e com seus
próprios dados, operando de forma assíncrona. Nessa classe, entre outras, enquadram-
se servidores com múltiplos processadores, tais como dual ou quad e, ainda, redes com
estações que implementam sistemas distribuídos.

Essa classe pode ser subdividida em duas subclasses de acordo com o tipo de me-
mória, se compartilhada ou não (Figura 6). Máquinas com memória compartilhada são
conhecidas como multiprocessadores, enquanto que a demais são denominadas multicom-
15

Figura 5: Arquitetura MIMD.

putadores. Em um multiprocessador, existe um único espaço de endereçamento, o qual


é compartilhado por todos os processadores, como em um Pentium Dual, por exemplo,
em que dois processadores se comunicam por meio da memória comum. Em oposição,
sistemas multicomputadores possuem memória privada por processador, ou seja, memória
distribuída.
 _ _ _ _ _ _ _ _/ COMUTADOS


 _ _ _ _ _ _ _ _ _/ MULTICOMPUTADORES _ _ _/ BASEADOS EM BARRAMENTOS



MIMD: SISTEMAS PARALELOS E DISTRIBUÍDOS



_ _ _ _ _ _ _ _ _/ MULTIPROCESSADORES
 _ _ _/ BASEADOS EM BARRAMENTOS



_ _ _ _ _ _ _ _/ COMUTADOS

Figura 6: Diferenciação entre multiprocessadores e multicomputadores pelo tipo de me-


mória.

A diferenciação entre multiprocessadores e multicomputadores também pode ser vista


por meio do “conceito de localidade", representado na Figura 7, que influencia a forma
de comunicação entre os processadores, quando são chamados, respectivamente, sistemas
fortemente e fracamente acoplados2 . Se o multicomputador for um sistema distribuído,
então o sistema de computação será “fracamente acoplado", ou ainda “medianamente
acoplado", o que indica que a comunicação dos processadores é feita somente por troca
de mensagens entre os processadores, por meio de uma rede de comunicação de dados.
Já o multiprocessador é um sistema de computação “fortemente acoplado", ou seja, a
comunicação entre os processadores é feita por meio de memória compartilhada.
2
Do inglês strongly coupled e weakly coupled.
16

Figura 7: Diferenciação entre multiprocessadores e multicomputadores pelo conceito de


localidade (Fonte: Giozza et al. (1986)).

1.3 Evolução e classificação de sistemas de computação


em relação ao funcionamento

Os diversos tipos de sistemas de computação diferem entre si, principalmente, no to-


cante a administração dos processos3 e capacidade de atendimento aos processos. Por
exemplo, alguns sistemas de computação tratam um só processo de usuário por vez, ou
seja, mantém apenas um processo de usuário na memória, enquanto outros sistemas de
computação já suportam múltiplos processos simultaneamente em memória. Entretanto,
o simples fato de existir mais de um processo em memória, não garante que todos, ou
alguns, serão atendidos ao mesmo tempo, ou seja, paralelamente. A estrutura de funcio-
namento de um sistema de computação depende dos recursos de hardware do sistema de
computação bem como, da capacidade do sistema operacional em atender às solicitações
dos processos, em disponibilizar os recursos aos processos e ao escalonamento de uso do(s)
processador(es).

A classificação de sistemas de computação apresentada a seguir, considera a estrutura


de funcionamento dos mesmos, porém, é importante ressaltar que os estudos devem ser
complementados em outros textos, pois existem diferenças de classificação entre auto-
res. Portanto, a classificação apresentada a seguir parte de conceitos básicos, sem seguir
especificamente um único autor.
3
O conceito de processo será visto mais adiante. Em termos gerais, um processo é um programa em
execução.
17

1.3.1 Sistemas monoprogramados

São os mais simples e foram largamente utilizados. Permitiam que somente um pro-
grama por vez fosse processado, ou seja, somente um programa tinha posse de todos os
recursos do sistema de computação, indiferente da sua necessidade de uso, conseqüente-
mente, a utilização dos recursos não era otimizada. Além disso, o programa em execução
deve administrar todo o sistema de computação. Se este tipo de controle for utilizado em
equipamentos caros, a relação custo x benefício tende a ser desvantajosa.

1.3.2 Sistemas multiprogramados

Com o objetivo de melhorar a relação custo x benefício, e visando a otimização do


uso dos recursos, surge, no início dos anos 70, o conceito e a primeira implementação de
multiprogramação. A idéia foi otimizar a utilização de recursos, destinando determina-
das tarefas a componentes específicos do sistema de computação. Por exemplo, quando
executa-se uma rotina de I/O, pode-se atribuir o procedimento de I/O aos hardware e
software dedicados à função. Assim, o processador ficaria disponível, podendo ser utili-
zado por outro processo. Para tanto, deve ser mantido em memória, simultaneamente,
uma quantidade n de processos capaz de ocupar o processador durante todo o tempo.

Uma variação um pouco mais sofisticada dos sistemas multiprogramados, são os sis-
temas que fazem multitarefa cooperativa4 , que seguem o mesmo conceito com algumas
melhorias.

1.3.3 Sistemas de tempo compartilhado

Os sistemas de tempo compartilhado (time sharing ou time shared ), da mesma forma


que os sistemas multiprogramados, possibilitavam que, em um instante de tempo vários
processos fossem alocados na memória, entretanto, a execução se daria em intervalos
cíclicos de tempo, determinando uma “fatia" de tempo (time slice) para cada processo.

O tempo compartilhado é largamente utilizado nos equipamentos de grande porte


e permitiu a implementação de sistemas multiusuários, onde vários usuários, de forma
simultânea e on-line, concorrem pelo tempo de execução. Como exemplo de sistemas de
tempo compartilhados podemos citar os sistemas de multitarefa preemptiva5 .
4
Procure saber mais sobre multitarefa cooperativa.
5
Procure saber mais sobre multitarefa preemptiva.
18

1.3.4 Sistemas multiexecutados

Outra evolução natural dos sistemas monoprogramados são os sistemas multiexecu-


tados, pois a própria evolução do hardware permitiu a execução de várias tarefas concor-
rentemente, por meio de tempo compartilhado, sendo que essas tarefas são comandadas
por um único usuário (monousuário). Ou seja, os sistemas multiexecutados são similares
aos de tempo compartilhado, porém não permitem multiusuários.

1.3.5 Sistemas multiprocessados

Os sistemas multiprocessados permitem que durante um determinado instante de


tempo, vários processos estejam alocados na memória, sendo executados simultaneamente
(processamento paralelo), não de forma concorrente, como em outros sistemas multitarefas
(por exemplo, sistema exclusivamente multiprogramado ou exclusivamente por tempo
compartilhado). Os sistemas de computação que suportam multiprocessamento possuem
mais do que um elemento processador.

A execução dos programas nos diversos processadores, assim como, nos sistemas de
tempo compartilhado, multiprogramados e multiexecutados, não impede a utilização dos
periféricos em outras tarefas independente dos processadores, de forma a otimizar, ainda
mais, a utilização do sistema de computação. Sistemas multiprocessados são ambientes
com sistema operacional complexos e sofisticados, normalmente, destinados a sistemas de
computação de alto desempenho.
19

2 Conceitos básicos de sistemas


operacionais

2.1 Introdução e definição

Um sistema de computação é composto por um ou mais processadores, uma certa


quantidade de memória, terminais, discos magnéticos, interfaces de rede e dispositivos de
I/O etc., ou seja, é um sistema extremamente complexo, e desenvolver software que geren-
cie e integre esses componentes, fazendo-os trabalhar corretamente e de forma otimizada,
não é tarefa fácil.

O sistema operacional é uma camada de software colocada entre os recursos de hard-


ware e os programas que executam tarefas para os usuários, sendo que uma de suas prin-
cipais responsabilidades é permitir e promover o acesso aos periféricos, sempre que um
programa solicitar uma operação de I/O. Assim, por meio dessa intervenção do sistema
operacional, o programador não precisa conhecer detalhes do hardware, pois informações
de como, por exemplo, “enviar um caracter para a impressora", são internas ao sistema
operacional. Além disso, como todos os acessos aos periféricos são feitos através do sis-
tema operacional, fica mais fácil controlar a disponibilização dos recursos, buscando uma
distribuição justa, eficiente e conveniente, não só dos recursos de I/O, mas de todo o
sistema de computação.

Uma utilização mais eficiente do sistema de computação é obtida a partir da distribui-


ção de seus recursos entre os programas, por exemplo, a distribuição do espaço em disco,
da memória principal, do tempo do processador, do acesso a disco etc. Já a utilização
mais conveniente, é obtida a partir da disponibilização dos recursos do sistema de compu-
tação, sem que os usuários conheçam os detalhes dos recursos. Por exemplo, supondo que
um usuário especializado, um programador, que, ao desenvolver um programa, precisa
colocar um caracter na tela. Para tanto, em geral, é necessária toda uma seqüência de
acessos à interface do vídeo, diversos registradores devem ser lidos ou escritos e, além
20

disso, podem existir diferentes tipos de interfaces que exigirão diferentes seqüências de
acesso. Porém, por meio do sistema operacional, o programador apenas informa, no pro-
grama, qual caracter deve ser colocado na tela e todo o trabalho de acesso ao periférico é
feito pelo sistema operacional.

Ao esconder detalhes dos periféricos, muitas vezes são criados recursos de mais alto
níveis, ou seja, as abstrações, como, por exemplo, os arquivos. Os programas utilizam
o espaço em disco através do conceito de arquivo. Arquivos não existem no hardware,
mas são um recurso criado a partir do que o hardware oferece. Para o programador é
muito mais confortável trabalhar com arquivos do que receber uma área de espaço em
disco que ele próprio teria que organizar. Outro exemplo de abstração, pode ser uma
instrução de I/O, tal como, read ou write em um IBM PC, que deve ser acompanhada
de 13 parâmetros, especificando o endereço do bloco a ser lido, o número de setores por
trilha, o modo de gravação no meio físico, o espaçamento entre setores etc., sem contar
com as tarefas de ordem física/mecânica, ou seja, verificar se o motor do drive já está
acionado etc.

A grande maioria dos programadores não se envolve com tais detalhes, pois lida com
uma abstração de alto nível e, portanto mais simples. No exemplo em questão, a abstração
feita nos discos é visualizá-los como uma coleção de arquivos identificados por nomes, onde
os eventos, a manipulação dos arquivos, não consideram maiores detalhes e restringem-se
a simplesmente abrir, ler/escrever e fechar.

2.2 Arquitetura de sistemas operacionais

A arquitetura de um sistema operacional é a estrutura básica sobre a qual é projetado o


sistema operacional, de como as abstrações são realmente implementadas, como o sistema
operacional deve ser solicitado e atender aos aplicativos, como interagem as partes do
sistema operacional entre si e como o sistema operacional responde às solicitações dos
aplicativos.

2.2.1 Arquitetura monolítica

É a arquitetura mais antiga e mais comum. Cada componente do sistema operaci-


onal é contido no núcleo (kernel ) e pode comunicar-se com qualquer outro componente
diretamente. Essa intercomunicação direta permite rapidez na resposta de sistemas opera-
cionais monolíticos, entretanto, como núcleos monolíticos agrupam os componentes todos
21

juntos, torna-se difícil identificar a origem de um determinado problema ou erro. Além


disso, todo o código do sistema operacional é executado com acesso irrestrito ao sistema, o
que pode facilitar a ocorrência de danos provocados intencionalmente, ou não, por outros
aplicativos.

2.2.2 Arquitetura em camadas

À medida que os sistemas operacionais tornaram-se mais complexos e maiores, pro-


jetos puramente monolíticos tornaram-se inviáveis e, então a arquitetura em camada,
ou modular, tornou-se uma boa opção, agrupando “camadas" de componentes, ou seja,
conjunto de procedimentos, que realizam tarefas similares. Cada camada comunica-se
somente com as suas camadas imediatamente inferior e superior. Uma camada inferior
sempre presta um serviço à sua camada superior, sendo que a camada superior não sabe
como o serviço é feito, apenas o solicita. A implementação de uma camada pode ser
modificada sem exigir modificação em outra camada, pois possuem componentes auto-
contidos1 . Em uma abordagem em camadas, a solicitação de um serviço pode precisar
passar por muitas camadas antes de ser atendida, assim o desempenho se degrada em
comparação ao de núcleos monolíticos.

2.2.3 Arquitetura de micronúcleo

A arquitetura de micronúcleo (microkernel ), representada na Figura 8, também, é


uma forma de arquitetura modular ou em camadas. Na tentativa de reduzir os procedi-
mentos mais fundamentais, somente um pequeno número de serviços, tais como, parte do
gerenciamento de memória, a sincronização entre processos e a comunicação entre proces-
sos, terá acesso direto ao hardware. Por sua vez, o serviço de comunicação entre processo,
que está dentro do micronúcleo, é o responsável por habilitar os serviços de, por exemplo,
redes, sistemas de arquivos e gerenciamento de dispositivos, que, normalmente, podem
ser implementados no núcleo do sistema operacional, não no micronúcleo, ou até como
procedimentos (aplicativos) externos ao núcleo. Alguns sistemas operacionais permitem
que aplicações acessem diretamente os serviços oferecidos pelo micronúcleo. Por ser si-
milar a arquitetura modular, a arquitetura em micronúcleo possui, em geral, as mesmas
vantagens e desvantagens da arquitetura em camadas.
1
Cada componente oculta o modo de como realiza sua tarefa e apresenta uma interface padrão para
que outros componentes possam requisitar seus serviços.
22

Figura 8: Representação da organização de um sistema operacional com arquitetura de


microkernel.

2.3 Chamadas ao sistema

As chamadas ao sistema2 (system calls) são interrupções (traps) originadas por soft-
ware, que fornecem a interface entre processos e o sistema operacional. Os programas
solicitam serviços ao sistema operacional por meio de chamadas ao sistema que provocam
interrupções da execução de determinada tarefa para que uma outra atividade seja execu-
tada. Uma atividade protegida por uma interrupção é específica do sistema operacional
e, portanto, tem privilégio sobre a anterior, que, posteriormente, será restaurada.

As chamadas aos sistema são semelhantes às chamadas de sub-rotinas, entretanto,


enquanto que as chamadas de sub-rotinas são transferências para procedimentos do mesmo
programa que fez a chamada, as chamadas ao sistema transferem a execução para o sistema
operacional, e, por meio de parâmetros, o programa solicitante, informa exatamente o que
necessita. Desse modo, todos os serviços que o sistema operacional disponibiliza para os
aplicativos são atendidos pelas chamadas ao sistema. Por exemplo, se um programa lista
o conteúdo de um arquivo texto na tela, com certeza fará uma chamada ao sistema para
verificar se o arquivo existe e um dos parâmetros dessa chamada será nome do arquivo a
ser listado. Além disso, tanto o procedimento de leitura do arquivo, quanto o de escrita
no vídeo, também correspondem a chamadas ao sistema.

A implementação das chamadas ao sistema, ou seja, a implementação dos procedimen-


tos que correspondem a essas, é feita no núcleo do sistema operacional, pois cada chamada
ao sistema corresponde a um procedimento de uma biblioteca de procedimentos do sis-
tema operacional, que têm sua execução de forma privilegiada. Em geral, os aplicativos
somente conseguem acessar esses procedimentos através das chamadas ao sistema.

Ao ser executado, o procedimento coloca os parâmetros das chamadas ao sistema


nos locais específicos – por exemplo, nos registradores – e é executado a partir de uma
interrupção, denominada trap, que faz a execução protegida do procedimento, isto é,
2
Alguns autores dizem "chamadas de sistema".
23

em modo privilegiado 3 . A interrupção, no caso de chamadas ao sistema, é gerada por


um pedido específico de um aplicativo para que um serviço do sistema operacional seja
executado.

O procedimento de atendimento a uma solicitação de serviço ocorre da seguinte forma:

1. O aplicativo solicita o serviço ao sistema operacional, que será atendido por meio
da execução de uma ou mais chamadas ao sistema;

2. Cada chamada ao sistema corresponde a execução de um procedimento no núcleo


do sistema operacional, que inicialmente coloca os parâmetros da chamada - por
exemplo, um nome de arquivo - nos devidos lugares;

3. Em seguida é emitida uma trap, que provocará a intervenção direta do sistema


operacional, por meio da execução do procedimento específico, correspondente à
chamada ao sistema. A solicitação será atendida se os parâmetros forem válidos e
existirem recursos para tal;

4. Após a execução da solicitação, o sistema operacional coloca em um registrador um


código de estado (status), indicando se operação foi bem sucedida ou se falhou;

5. O sistema operacional devolve o controle ao processo que provocou a chamada ao


sistema, que terá acesso ao código de estado gerado.

As chamadas ao sistema variam entre sistemas operacionais, pois os sistemas opera-


cionais mais específicos para uma determinada atividade podem possuir chamadas espe-
cíficas para essa atividade. Entretanto, algumas chamadas ao sistema são comuns entre
os sistemas operacionais, por exemplo:

• Chamadas ao sistema para gerenciamento de processos: É um grupo de chamadas


extremamente importante sendo responsável por criar e finalizar processos;

• Chamadas ao sistema para tratamento de sinais: Seus serviços são utilizados para
sincronizar processos, suspender a execução de processos etc.;

• Chamadas de gerenciamento de arquivos: Possibilita a criação de arquivos, a leitura,


a gravação, a alteração de nomes de arquivos etc.;
3
A grande maioria dos sistemas operacionais podem trabalhar em dois modos: o modo privilegiado,
também chamado de modo supervisor, modo sistema ou modo mestre, e o modo não-privilegiado, também
chamado de modo usuário, modo programa ou modo escravo citeTOC03.
24

• Chamadas de gerenciamento de diretório e de sistema de arquivos: Possibilita a


criação de um novo diretório, a remoção de um diretório existente, criação de uma
nova entrada no diretório (ou seja, a inclusão de um novo arquivo no diretório),
“montar e desmontar"4 um sistema de arquivos etc.;

• Chamadas ao sistema de proteção: Permite que se descubra e que sejam alterados


os donos de processos e arquivos;

• Chamadas de serviços de gerenciamento de tempo: Trata as datas e as horas em um


sistema de computação, assim como permite a contabilidade do tempo de utilização
por parte de um usuário ou processo;

2.4 Interpretador de comandos

Um dos programas que mais executam chamadas ao sistema é o interpretador de


comandos e, como exemplo, será apresentado o shell, que é um interpretador de comandos
do Unix.

Apesar do interpretador de comandos, em geral, não fazer parte do núcleo do sistema


operacional, possui a importante tarefa de prover a interface primária entre o usuário e
o sistema operacional, sem a qual, muitas vezes, não seria possível para o usuário acessar
o sistema de computação. Pode-se visualizar o interpretador de comandos como uma
cápsula circundando o sistema operacional, ou seja, criando uma camada, uma interface,
entre o usuário e o sistema operacional, permitindo que o usuário execute comandos
para acessar todos os recursos que estiverem disponíves no sistema de computação. Os
usuários comunicam-se com o interpretador de comandos através de entrada de comandos,
provocando eventos por meio do mouse ou por linha de comando, que são interpretados
e geram as solicitações de serviços.

Um interpretador de comandos simples é basicamente um programa composto de um


laço que aguarda uma entrada (um comando), cria um novo processo, com uma chamada
ao sistema (no Unix ou no Linux seria a chamada fork ), atribui a esse novo processo o
programa especificado na entrada e executa o processo (no Unix ou no Linux, tanto a
atribuição do programa quanto a execução do processo são feitos pela chamada exec).
Em seguida, aguarda a finalização do processo e, então, espera por uma nova entrada.
Veja o Algoritmo 1
4
Montar um disco significa designar uma identificação (nome, rótulo ou volume) para um disco ou
parte desse. Desmontar, significa retirar essa designinação
25

enquanto (TRUE) faça


ler comando (comando, parâmetros);
fork ();
se (criação de novo processo == ok) então
exec (comando, parâmetro);
senão
“tratamento da falha";
fim
fim
Algoritmo 1: Um interpretador de comandos simplificado.
26

3 Processos

Uma das dificuldades no estudo dos sistemas operacionais é a denominação das ati-
vidades da CPU. Por exemplo, um sistema batch 1 executa jobs (trabalhos, tarefas), en-
quanto que um sistema de tempo compartilhado executa programas de usuários ou tarefas.
Além disso, o sistema operacional precisa atender aos(s) programa(s) em execução, ge-
renciar recursos e atender suas próprias atividades e necessidades. Em vários aspectos,
um job, programa ou uma atividade interna do sistema operacional são semelhantes e,
portanto, chamamos a todos de processo.

3.1 Definição e estrutura

Um processo é basicamente um programa em execução, ou seja, deve-se ter a idéia


de que um processo constitui-se de um certo tipo de atividade, possuindo um programa,
uma entrada, uma saída e um estado. A diferença básica é que um programa é uma
entidade passiva, ao contrário de um processo, que é uma entidade ativa. Uma entidade
ativa possui várias informações e recursos associados que se alteram ao longo do tempo,
por exemplo, o registrador PC (program counter ), referenciando a próxima instrução a ser
executada. Embora dois processos possam estar associados a um mesmo programa, são
duas seqüências separadas de execução, ou seja, vários usuários podem estar executando
um mesmo programa ou, até mesmo, um único usuário pode executar várias cópias de
um mesmo programa, mas cada nova execução gera um novo processo.

Um programa em execução é composto por um código executável, um conjunto de


dados referente ao código, da pilha de execução, do valor do contador do programa (re-
gistrador PC), do valor do apontador de pilha (registrador SP), dos valores dos demais
registradores do hardware, entre outras informações, sendo todas armazenadas em uma
tabela chamada tabela de processo, ou PCB (process control block - bloco de controle do
1
Procure saber o que é um sistema batch.
27

processo), ou, ainda, bloco de controle de tarefa. Um exemplo de uma tabela de processo
pode ser visto na Tabela 2.
estado do processo (status)
identificação do processo (PID)
valor do contador do programa (PC)
valores dos outros resgistradores
limites de memória
informaçãoes sobre arquivos abertos
..
.

Tabela 2: Exemplo de uma tabela de processo.

Imagine um sistema de tempo compartilhado, onde o sistema de computação inter-


rompe a execução de um determinado processo e inicia a execução de outro, visto que
terminou o tempo reservado para o primeiro. Assim, com o primeiro processo tempo-
rariamente suspenso e havendo a necessidade de restaurá-lo posteriormente, a partir do
ponto em que foi interrompido, todas as informações relativas ao processo precisam ser
guardadas em algum lugar, isto é, na tabela de processo. Por exemplo, se o processo es-
tiver com um arquivo aberto, haverá a necessidade de se guardar a posição exata em que
se encontrava a pesquisa desse arquivo, de modo que uma leitura posterior à reativação
do processo leia o dado correto.

Como visto na Subseção 2.3, as chamadas ao sistema mais importantes relacionadas


com os processos são as de criação e de finalização de processos e, assim, o funcionamento
desses será apresentado a partir da análise do Algoritmo 1, que pode representar um shell
simplificado, fazendo as chamadas necessárias para a criação de processos.

Um processo shell lê os comandos de um terminal de usuário, solicitando a execução


de um programa. O shell, então, faz uma chamada ao sistema para criar um novo processo
(“processo-filho"), exatamente igual ao próprio shell (“processo-pai"). Associa o programa
solicitado ao processo-filho, que, a partir de agora, deixa de ser igual ao processo-pai, pois
possui suas próprias características e informações. Posteriormente, para a finalização do
processo que foi criado (processo-filho), o próprio processo-filho gera uma chamada ao
sistema para terminar a si próprio, e esse procedimento de finalização retorna o resultado
da sua execução, ou seja, o resultado da finalização do processo-filho, ao processo-pai.
Um processo pode criar um ou mais processos-filho e esses, por sua vez, podem criar
seu próprios processos-filho, gerando uma árvore de processos (Figura 9), que nada mais
é do que uma simples representação gráfica da estrutura de acionamento, hierarquia e
dependência entre processos.
28

A>
>>
>>
>>

B> C
>>
>>
>>
 
D E F
Figura 9: Exemplo de uma árvore de processos. O processo A é a raiz da árvore e criou
dois processos-filho B e C, o processo B é raiz de uma sub-árvore e criou três processos-filho
D, E e F e os processos C, D, E e F são folhas da árvore.

Em sistemas multiusuários e multitarefas, cada usuário ou processo pode ter uma


ou mais árvores de processos, assim, é importante conhecer qual usuário é dono de qual
processo, neste caso, a cada usuário é atribuída uma identificação, a UID (user identifi-
cation), e cada processo está associado a UID do seu dono, que também é guardada na
tabela de processo. Considerando que um sistema de computação pode ter seus usuários
organizados em grupos - equipes de projeto, departamentos etc. - também existe uma
identificação para cada grupo, a GID (group identification). Tanto a UID, quanto a GID,
tem papel importante na segurança e proteção das informações e do sistema, por exemplo,
por meio do gerenciamento correto da GID e da UID o sistema operacional pode proteger
documentos, permitindo que possam ser lidos somente pelos membros de seu grupo ou
pode permitir ou restringir acesso a determinados recursos sistema de computação para
alguns usuários.

3.2 Estados e transições

Apesar de cada processo ser uma entidade independente, com seu próprio fluxo de
controle e com seu próprio estado interno, eles muitas vezes têm necessidade de interagir
com outros processos, como, por exemplo, um primeiro processo que concatena dois arqui-
vos que, por sua vez, será a entrada de um processo de sort 2 . Dependendo da velocidade
entre os processos, isto é, um pode andar mais rápido do que o outro, que leva em conta
a complexidade do processo em si e o tempo alocado no processador para cada um deles,
poderá ocorrer uma situação em que o sort estará pronto para rodar, mas a concatenação
ainda não gerou os dados necessários. Então, o sort será bloqueado até que sua entrada,
ou seja, a saída do primeiro processo, esteja disponível.

Um bloqueio ocorre quando um processo não possui condições lógicas de prosseguir,


mas também podem ocorrer situações em que o sistema operacional decida entregar o
2
Programa para ordenação de dados.
29

processador para outro processo. Neste último caso, a decisão de suspender a execução do
processo é técnica, normalmente baseada no compartilhamento de tempo do processador.

A Figura 10 apresenta o grafo de processo, ou grafo de execução de programa, em


versão simplificada, mostrando que um processo pode passar por cinco estados:

• Entrada (E): Criação do processo;

• Pronto (P): Em condições de rodar, mas impedido temporariamente para dar vez a
um outro processo, ou seja, está apto a rodar, mas não há processador disponível;

• Rodando (R): Usando o processador no instante, ou seja, está apto a rodar e existe
processador disponível;

• Bloqueado (B): Impedido de rodar até que ocorra um determinado evento externo
ao processo, ou seja, não está apto a rodar;

• Saída (S): Finalização (destruição) do processo.

Ao passar de um estado para outro o processo sofre uma transição, sendo possível
quatro diferentes transições entre os três principais estados que serão estudados:

1. Rodando para pronto: ocorre quando o escalonador de processos verifica que o


processo esgotou seu tempo no processador, ou seja, é uma interrupção técnica, por
tempo;

2. Pronto para rodando: ocorre quando o processo é selecionado para fazer uso do
processador. O escalonador de processos libera o processador para o processo, ou
seja, pela organização da fila de prontos, chegou a hora do processo utilizar sua fatia
de tempo no processador;

3. Rodando para bloqueado: ocorre quando faltam condições lógicas para um pro-
cesso continuar a ocupar o processador. Normalmente causado por sincronização de
processos ou requisição de I/O;

4. Bloqueado para pronto: Ocorre quando um evento externo provê o recurso aguar-
dado pelo processo. A ocorrência do evento se manifesta por meio de uma interrup-
ção.
30

/. *+
S-,
()
~?
f im ~

7654
0123
~
~
RX
~
c ~~
b
~
01 54
76B23 76P23
/01 54
~
~ ~ a 
d _@
@ admissão
@
76
01E54
23
@

Figura 10: Grafo de processo ou grafo de execução de programa.

3.3 Problemas de comunicação entre processos

Os processos precisam “conversar" com muita freqüência, por exemplo, o caso já


visto de um processo que tem como entrada a saída de um outro processo, e os sistemas
operacionais utilizam o compartilhamento de memória, sockets, portas etc., para promover
a comunicação entre processos. Os dois exemplos a seguir ilustram a natureza desses
problemas, que podem ser de cooperação entre processos ou problemas de competição
entre processos3 .

3.3.1 O problema do produtor-consumidor (PPC)

O PPC, também chamado problema do buffer limitado (bounded buffer ), consiste de


dois processos que compartilham um buffer de tamanho fixo. Um processo (Pp) produz
e guarda uma informação no buffer, o outro processo (Pc) retira a informação do buffer e
a utiliza da forma desejada. Assim sendo, podem ocorrer duas situações conflitantes:

1. Pp deseja guardar uma informação no buffer, mas ele está cheio;

2. Pc deseja retirar uma informação do buffer, mas ele está vazio.

Esse problema é simples, porém ocorre em um grande número de serviços e atividades


dos sistemas operacionais como, por exemplo, spool de impressão, protocolos de comuni-
cação primitivos (onde o produtor é o emissor que envia uma mensagem e o consumidor
é o receptor que recebe a mensagem).
3
Procure identificar situações em que os processos se comunicam e qual a natureza do problema de
comunicação entre os mesmos.
31

A solução do PPC pode ser implementada de várias formas. A que será apresentada
(Algoritmo 2), trabalha com as primitivas4 de bloqueio (sleep) e de desbloqueio (wakeup),
portanto, devemos saber que a primitiva sleep sempre bloqueia o processo que a chamou,
enquanto que a chamada da primitiva wakeup recebe um parâmetro que indica qual o
processo que deve ser desbloqueado.

Essa primeira solução, entretanto, não é infalível. Considerando Pp e Pc rodando em


tempo compartilhado, ou seja, cada um tem sua fatia de tempo do processador, poderá
ocorrer a seguinte situação:

• Pc está sendo executado e o buffer está vazio, então o valor de contador será 0;

• Tempo de Pc termina com o contador atualizado em 0, porém não houve tempo


para executar a instrução decorrente da verificação do teste do buffer vazio. Assim
sendo, ele foi enviado para a fila de prontos esperando sua próxima fatia de tempo;

• Pp retorna sua condição de rodando, coloca uma informação no buffer, atualiza o


contador para 1 e manda desbloquear Pc, porém, Pc não está bloqueado, ele está
na fila de prontos e, então, a primitiva wakeup não produz efeito.

Com essa situação, dando prosseguimento à execução do algoritmo, percebe-se que:

• Quando Pc retomar a condição de rodando, iniciará exatamente do ponto onde


parou, ou seja, executará a primitiva sleep e será bloqueado;

• Pp retoma sua execução e coloca outra informação no buffer atualiza o contador


em 2 e não executará a primitiva wakeup para desbloquear Pc, que desta vez estará
bloqueado;

• Em seguida, Pp produzirá e guardará informações no buffer até que o contador seja


igual a N e, então, Pp será bloqueado.

Dessa forma, os dois processo estarão bloqueados e cada um ficará aguardando pelo
outro indefinidamente5 .
4
Os procedimentos do kernel que respondem às chamadas ao sistemas também são chamados de
“primitivas".
5
O problema exposto pode ser resolvido alterando o algoritmo Produtor-Consumidor ou o funciona-
mento das primitivas. Pense em como alterar o algoritmo proposto para solucionar esse problema.
32

# define N 100 /* Determina o tamanho do buffer */


int contador = 0; /* Qtd de itens existentes no buffer */

void produtor (void)


início
int inf;
enquanto (TRUE) faça
produzInf (&inf);
se (contador == N) então
/* A qtd de itens no buffer chegou ao máximo? */
sleep ();
fim
guardaInf (inf);
contador++; /* Atualiza o número de ítens armazenados no buffer
*/
se (contador == 1) então
/* Existe um único item guardado no buffer? */
wakeup (consumidor);
fim
fim
fim

void consumidor (void)


início
int inf;
enquanto (TRUE) faça
se (contador == 0) então
/* O buffer está vazio? */
sleep ();
fim
removeInf (&inf);
contador−−; /* Atualiza o número de ítems armazenados no buffer
*/
se (contador == (N-1)) então
/* Existe um único lugar vazio no buffer? */
wakeup (produtor);
fim
utilizaInf (inf);
fim
fim
Algoritmo 2: Algoritmo para solução do Problema do Produtor-Consumidor, utili-
zando as primitivas sleep e wakeup.
33

3.3.2 O problema dos filósofos chineses (PFC)

O “Problema dos Cinco Filósofos Chineses" representa a situação em que processos


competem por acesso exclusivo a um número limitado de recursos, tais como alguns
dispositivos de I/O. Esse problema é ilustrado como cinco filósofos sentados à mesa,
sendo que cada um recebe uma porção de arroz e um único hashi (chopstick, talher
chinês, “palito"etc.) e quando um filósofo quiser se alimentar, ele deve pegar dois palitos
que estejam disponíveis e ao seu alcance. Assim sendo, devido à falta de talher, existirá
outro filósofo que não poderá se alimentar ao mesmo tempo que o primeiro.

A vida de um filósofo consiste de duas ações alternadas, pensar ou comer, ou seja, o


filósofo executará uma das ações por vez, e a solução consiste em elaborar um algoritmo
para que cada filósofo (processo) utilize os talheres (recursos) da forma mais otimizada
possível.

Na solução inicial proposta, Algoritmo 3, o procedimento obterTalher sempre aguarda


pela disponibilidade do palito, caso não esteja disponível, e por estar sendo considerado
que a execução se dá em um sistema de computação multitarefa de qualquer tipo (tempo
compartilhado, multiexecutado etc.), existem cinco instâncias do mesmo algoritmo (cinco
processos), uma para cada filósofo, que estão sendo executadas simultaneamente.

void Filosofo (void)


início
enquanto (TRUE) faça
pensar ();
obterTalher (esq);
obterTalher (dir);
comer ();
devolverTalher (esq);
devolverTalher (dir);
fim
fim
Algoritmo 3: Algoritmo para solução do Problema dos Filósofos Chineses.

Infelizmente, a solução proposta não está correta. Suponha que todos os cincos filó-
sofos resolvam comer ao mesmo tempo e que cada um consiga pegar o talher da esquerda.
A partir daí, nenhum deles terá acesso ao palito da direita, configurando uma situação
de deadlock, ou impasse, que ocorre, em um conjunto de processos, se cada
processo do conjunto estiver esperando por um evento que somente outro
processo pertencente ao conjunto poderá fazer.
34

No exemplo, o primeiro filósofo pega o palito da esquerda e fica aguardando que o


segundo filósofo libere o palito, porém este outro está aguardando por este mesmo evento
em relação ao terceiro filósofo, assim sucessivamente, até o quinto filósofo, que aguarda a
liberação do talher pelo primeiro filósofo.

Aparentemente, essa situação pode ser solucionada se, após pegar o palito da esquerda,
o filósofo verificar se o palito da direita encontra-se disponível. Se não estiver, o filósofo
deve devolver o palito da esquerda e aguardar um tempo determinado para repetir o
processo6 . Entretanto, essa solução também não está correta.

Como no caso anterior pode acontecer de que os filósofos obtenham ao mesmo tempo
o palito da esquerda, então, vão observar que o palito da direita estará ocupado e todos
devolverão o palito da esquerda ao mesmo tempo. Em seguida, esperam o tempo determi-
nado e pegam os palitos da esquerda novamente. Mais uma vez, verificarão que os palitos
da direita estarão ocupados, devolverão os palitos da esquerda e, assim sucessivamente e
indefinidamente. Uma situação como essa, em que os programas rodam indefini-
damente e não fazem nenhum progresso em seu processamento, é denominada
starvation.

Para solucionar esse novo problema, assim como o do impasse, basta fazer com que
o intervalo de tempo seja aleatório, para o início de uma nova tentativa e, assim, será
praticamente impossível que uma outra tentativa de pegar os talheres seja feita ao mesmo
tempo por todos os filósofos.

Como informação, a solução definitiva para esse problema foi dada por Edsger Dijks-
tra, em 1965, utilizando semáforos binários de exclusão mútua. A grosso modo, seria
colocar no procedimento uma variável (semáforo) que assuma somente dois valores (biná-
ria), por exemplo, FALSO ou VERDADEIRO. Quando um filósofo resolver comer, antes
da ação de “pegar o talher", ele deve verificar se a variável está com o valor VERDA-
DEIRO, então ele altera o valor para FALSO e segue o processamento. Quando devolver
o talher da direita, retorna a variável para VERDADEIRO. Assim, exclui-se a possibili-
dade de que outro filósofo tente comer ao mesmo tempo que o primeiro. Da mesma forma
que será excluída a possibilidade de que este tente comer ao mesmo tempo que um outro
(exclusão mútua).
6
Faça a alteração proposta para o Algoritmo 3.
35

3.4 Escalonamento de processos

Nos problemas de comunicação entre processos, são observadas situações em que dois
ou mais processos podem estar “prontos" e, portanto aguardando o momento de rodar.
Essa decisão cabe ao sistema operacional, ou melhor, ao escalonador de processos do sis-
tema operacional, que utiliza um algoritmo de escalonamento para decidir qual processo
vai ocupar o processador. Os antigos sistemas batch possuíam um algoritmo de escalo-
namento que consistia simplesmente em fazer com que o próximo processo da fila fosse
colocado para rodar ao final do processo corrente de forma não-preemptiva7 . Porém, com
o advento dos sistemas de tempo compartilhados, multiusuários etc., esses algoritmos
tornaram-se mais complexos, permitindo alteração da fila de prontos ou a suspensão de
processos em função de novos processos que estivessem entrando na fila, ou seja, uma
forma preemptiva8 de organizar a fila de prontos.

Um bom algoritmo de escalonamento deve possuir as seguintes características:

1. Garantir que todos os processos do sistema terão chances iguais de uso do proces-
sador (exceto nos casos de um processo ser priorizado em relação a outro);

2. Manter o processador ocupado todo o tempo;

3. Minimizar o tempo de resposta para os usuários interativos;

4. Minimizar o tempo que os usuários batch devem esperar pela saída (turnaroud );

5. Maximizar o número de jobs processados por unidade de tempo (throughput).

3.4.1 Escalonamento first-in-first-out (FIFO)

É o algoritmo de implementação mais simples. O processo que primeiro requisita o


processador é atendido primeiro, nesse caso, a fila de prontos é implementada seguindo
o modelo first-come-first-served (FCFS), onde o primeiro a entrar na fila é o primeiro a
ser atendido. Entretanto, apesar de simples de entender e de implementar, a eficiência
do escalonamento pode ficar bastante comprometida porque é muito dependente dos pro-
cessos que formam a fila de prontos. Este é um algoritmo naturalmente não-preemptivo
já que o critério de ordem de execução entre os processos está baseado “em quem chegar
primeiro".
7
Em algoritmos de escalonamento não-preemptivos, se um processo assume o estado “rodando" não
sofre transição até a sua conclusão.
8
Procure saber o conceito de preempção.
36

3.4.2 Escalonamento shortest job first (SJF)

O SJF associa ao processo uma determinada medida (métrica, peso etc.) que pode
ser originada por um único valor, por exemplo, tempo previsto de ocupação da CPU, ou
ser obtida a partir de um conjunto de valores, tais como, tempo previsto de ocupação
da CPU, previsão da quantidade de memória a ser utilizada, previsão dos dispositivos de
I/O a serem utilizados. Assim, essa medida associada ao processo passa a ser um peso,
às vezes, uma prioridade, para a organização da fila.

O SJF pode ser implementado nas formas de escalonador de longo, médio ou curto
prazo9 . Como escalonador de longo prazo, o algoritmo determina uma nova ordem para os
processos que se encontram na fila de prontos e, a partir dessa nova ordem, os processos são
executados sem nenhuma alteração, como se fosse o FIFO. No contexto dessa disciplina,
será visto o caso mais simples, ou seja, a implementação para escalonadores de longo prazo,
tomando como medida somente o tempo previsto de CPU para o processo e organizando
a fila de prontos na ordem crescente dos valores previstos. Normalmente a previsão do
tempo de uso da CPU é informada pelo usuário que submeteu o processo e, assim, é
possível que a previsão seja superestimada ou subestimada10 .

Como exemplo, suponha que as informações dos processos na fila de prontos sejam ar-
mazenadas em uma estrutura de dados como descrita a seguir, assim, para implementação
do escalonador SJF, com escalonador de longo prazo, pode ser utilizado o Algoritmo 4.

processo.id processo.peso

Em seus estudos, altere o Algoritmo 4, fazendo que com os processos sejam incluídos
em lista_processo em ordem crescente de peso. Como sugestão, implemente lista_processo
em uma lista encadeada e não em um vetor.)

3.4.3 Escalonamento round-robin (RR)

O RR, também conhecido como algoritmo de revezamento, é um dos algoritmos mais


antigos e simples11 , portanto muito utilizado. Foi criado para sistemas de tempo com-
partilhado e baseia-se na própria concepção do compartilhamento de tempo. Sob certos
9
Como escalonador de médio ou curto prazo, o algoritmo associa a cada processo a medida aproximada
do seu próximo ciclo de CPU a ser executado e o processador recebe o processo que possui o menor próximo
ciclo de CPU estimado. Essa estimativa é obtida a partir de formulações matemáticas.
10
Procure saber o que acontece nos casos em que a previsão é superestimada ou subestimada.
11
Tente escrever o algoritmo Round-Robin.
37

enquanto (TRUE) faça


i ← 0;
enquanto (existir processo na fila de prontos) faça
i++;
ler processo;
/* Guarda as informações referentes ao processo lido na lista
de */
/* processos */
lista_processo[i].id ← processo.id;
lista_processo[i].metrica ← processo.peso;
fim
enquanto (existir elemento em lista_processo) faça
/* Guarda a posição do vetor lista_processo em que se encontra
o */
/* processo de menor métrica */
menor ← minimo(lista_processo);
/* Executa o processo de menor métrica */
executar (lista_processo[menor].id);
/* Exclui da lista de processos o processo que foi executado
*/
excluir (lista_processo[menor]);
fim
fim
Algoritmo 4: Um exemplo do SJF para escalonamento a longo prazo.
38

aspectos, assemelha-se ao FIFO, mas, a preempção foi acrescentada ao fim de intervalos


de tempo iguais e determinados, chamados de quantum, para alternar o uso do processa-
dor entre os processos. Um processo ocupa o processador durante seu quantum, ou até,
se for o caso, ser bloqueado, prevalecendo o que acontecer primeiro, e então o processador
será ocupado pelo próximo processo pronto. Nenhum processo utiliza o processador por
mais de um quantum seguido, a menos que seja o único processo do sistema de compu-
tação. A única dificuldade na sua implementação é determinar o tamanho quantum, pois
influencia o percentual de tempo em que o sistema de computação fará apenas as rotinas
administrativas. Quando o escalonador promove a troca de processo, deve ser executada
a troca de contexto12 , que é uma rotina administrativa para atualização de registradores,
tabelas, listas, mapas de memória etc.

Suponha que o quantum foi determinado em 20 ms e são gastos 5 ms para a troca de


contexto. Desse modo, 20% do tempo do processador é atribuído para administração do
sistema, pois:

1 rodada = quantum + troca de contexto ⇒


⇒ 20 ms + 5 ms = 25 ms

1 rodada = 25 ms ⇒ 5ms = 20%

Se o tamanho do quantum for ampliado para 500 ms, por exemplo, o tempo atribuído
para as tarefas administrativas será menor do que 1%, porém, para os usuários interativos
pode-se gerar uma espera muito longa para que tenham a chance de utilizar o processador.
O tamanho do quantum ideal13 depende do tipo de processos e de usuários que estão
utilizando o sistema em cada momento, portanto, em alguns sistemas, seu valor pode ser
alterado na configuração do sistema.

3.4.4 Escalonamento com prioridade (EP)

Alguns algoritmos, como o RR, assumem que todos os processos são igualmente im-
portantes, porém, normalmente associam-se “pesos" aos processos para determinar a hie-
rarquia de execução destes. A idéia é simples: a cada processo associa-se uma prioridade,
12
Pense sobre a função e o funcionamento da troca de contexto nos diversos tipos de sistemas e veja
como essa rotina se relaciona com a tabela de processo.
13
Analise qual a relação entre o tamanho do quantum e as características de um bom algoritmo de
escalonamento, relacionadas na Seção 3.4.
39

e o processo “pronto" de maior prioridade rodará primeiro. Nesse exemplo de algoritmo


de escalonamento com prioridade, os processos são organizados em classes de prioridade
(Figura 11) e dentro de cada classe utiliza-se um RR um pouco modificado, para evitar
que um processo, ou uma classe de processos, monopolize o sistema. Nesse caso, ado-
taremos a política de decrementar o valor da prioridade do processo que saiu do estado
“rodando" a cada quantum decorrido.

PID A B C D E F G H I J K L
Prioridade 4 3 4 3 4 3 2 1 3 1 2 1

Prioridade 4 → A → C → E
Prioridade 3 → B → D → F → I
Prioridade 2 → G → K
Prioridade 1 → H → J → L
Figura 11: Classes de prioridade para aplicação do algoritmo EP.
40

4 Gerência de memória

4.1 Introdução e conceitos básicos

A memória, um dos recursos de maior importância e de maior custo em um sistema


de computação, precisa ser gerenciada com especial atenção, pois apesar da quantidade
de memória disponível nos equipamentos ter crescido muito, os programas cresceram em
igual velocidade, sempre ocupando a quantidade de memória disponível.

O gerente de memória é a parte do sistema operacional que administra as partes


da memória em uso e as que não estão, alocando memória livre quando o processos
precisarem, liberando memória ocupada quando os processos forem finalizados e, ainda, é
responsável pelo swapping 1 entre a memória e o disco, quando a memória não for grande
o suficiente para guardar todos os processos.

Como sabemos, em ambientes multitarefa, diversos processos são executados simul-


taneamente, por meio da divisão do tempo do processador. Para que a troca entre eles
seja rápida, esses processos devem estar em memória e prontos para assumirem o proces-
sador, sendo função do gerente de memória prover os mecanismos necessários para que os
diversos processos compartilhem a memória de forma segura e eficiente.

Existem diversas técnicas de gerência de memória e decidir qual será técnica imple-
mentada em um sistema operacional depende muito do que a arquitetura do computador
hospedeiro suportar, pois as técnicas de gerência são intimamente ligadas ao hardware.

4.2 Gerência de memória pelo modelo de monoprogra-


mação

A monoprogramação é o esquema mais simples de alocação de memória, pois existe


somente um processo na memória em cada instante, e este pode ocupar toda a memória
1
Procure saber mais sobre swap.
41

disponível. Atualmente, nem mesmo os computadores pessoais utilizam esse mo-


delo, pois mesmo os mais simples microcomputadores dividem a memória entre o sistema
operacional e o processo, podendo:

(a) O sistema operacional ocupar o início da RAM;

(b) O sistema operacional ocupar a ROM (na parte mais alta do sistema de endereça-
mento de memória);

(c) O sistema operacional ocupar o início da RAM, e a ROM será ocupada pelos drivers
de dispositivos, que formam o BIOS (Basic Input/Output System). Esse é o caso
do IBM PC.

Na Figura 12 está ilustrado esses três modelos simples de organização de memória,


sem swapping e sem paginação2 . Como nos três modelos existe mais de um programa
em memória, isto é, o sistema operacional e um programa de usuário, esses modelos não
representam monoprogramação.

Figura 12: Três modelos simples de organização de memória (TANENBAUM, 2008).

4.3 Gerência de memória pelo modelo de multiprogra-


mação

Esse modelo permite o acesso interativo por parte de vários usuários simultaneamente,
existindo mais de um processo na memória ao mesmo tempo. Os processos ocupam
partições diferentes e essas partições podem ser fixas ou variáveis.
2
Os conceitos de swapping e paginação serão apresentados posteriormente.
42

4.3.1 Multiprogramação com partições fixas

Partimos do princípio que processos diferentes podem ocupar diferentes quantidades


de memória e, assim, dividimos os processos em classes determinadas por partições fixas
de memória, por exemplo:

• Partição 0 - sistema operacional (100 KB);

• Partição 1 - processos que ocupam até 100 KB;

• Partição 2 - de 100 KB a 200 KB;

• Partição 3 - de 200 KB a 400 KB;

• etc.

Assim sendo, podemos utilizar duas3 estratégias:

1. Múltiplas filas de entrada, separadas por partições:


Ou seja, preparar várias filas de processos, em função da quantidade de memória
prevista para os processos, sendo que esses processos só podem ser executados na
sua partição mesmo que partições maiores estejam desocupadas;

2. Fila de entrada única:


Nesta estratégia, preferencialmente, o processo deve ser executado na sua partição,
porém se esta estiver ocupada e se existir a possibilidade de ser executado em uma
partição maior, sem prejuízo de nenhuma prioridade, o processo poderá ser execu-
tado em outra partição. Assim, o gerente de memória procura na fila qual o processo
que melhor se “encaixa” nas partições que estiverem disponíveis.

4.3.2 Multiprogramação com partições variáveis

No caso de sistemas batch, a organização da memória em partições fixas é simples


e eficiente, porém nos sistemas com tempo compartilhado, a situação é diferente, pois
normalmente existem mais usuários do que memória suficiente para armazenar todos os
processos, de forma que os excedentes devem ser mantidos em disco e, então utiliza-se
swap para levar e trazer processos da memória para o disco, e vice-versa.
3
Procure saber as vantagens e desvantagens do sistema com múltiplas filas e do sistema com fila única.
43

Figura 13: Partições fixas: (a) Múltiplas filas de entrada, separadas por partições. (b)
Fila de entrada única (TANENBAUM, 2008).

A princípio, um sistema que utiliza swapping pode ser baseado na divisão da memória
em partições fixas. Sempre que um processo for bloqueado, ele pode ser gravado em disco
e um novo processo será transferido para a área de memória liberada. Porém, nesse caso,
o aproveitamento da memória fica comprometido, pois o novo processo pode ocupar uma
área maior do que a necessária. Além disso, a área de dados de um processo pode aumentar
ou diminuir ao longo de sua execução, assim sendo, uma partição fixa que contenha um
processo em um determinado momento, talvez não o comporte no momento seguinte e,
então, esse processo deve esperar uma partição maior ou ser interrompido.

Quando partições variáveis são utilizadas, obtêm-se maior flexibilidade, por meio do
swapping na substituição dos processos em memória, que variam dinamicamente em ta-
manho e quantidade, diferentemente das partições fixas.

Figura 14: Substituição dos processos em memória, por meio de swap (TANENBAUM,
2008).

Os fatores citados e, ainda, o número e o tamanho dos processos variando dinamica-


mente, o gerenciamento do swapping em disco etc., tornam a gerência de memória com
44

partições variáveis um processo sofisticado e muito complexo - se comparado às partições


fixas - mesmo assim é economicamente viável (custo x benefício) e muito eficiente para
sistemas multitarefa.

4.3.3 Estratégia de ocupação em memória em sistemas de multi-


programação com partições variáveis

Em sistemas de multiprogramação por partições variáveis, freqüentemente é possível


escolher qual a “lacuna” em que um processo deve ser acomodado. A determinação de
lugares onde dados e programas serão colocados é feita por uma estratégia de ocupação
que, normalmente, são: first-fit (o primeiro que couber), best-fit (o que melhor couber);
worst-fit (o que pior couber).

1. First-fit:
O sistema coloca um job na memória principal na primeira lacuna que seja grande
o suficiente para contê-lo. Sendo este o método mais rápido para esta decisão;

2. Best-fit:
O sistema coloca o job na memória principal na lacuna mais “justa”, ou seja, na
lacuna que couber mais exatamente e deixe a menor quantidade de espaço sem uso.
A determinação do melhor espaço requer sobrecarga de processamento para a sua
determinação, pois o sistema precisa ter conhecimento de todas as lacunas;

3. Worst-fit:
O sistema coloca o job na memória principal na lacuna mais “folgada”, ou seja, na
maior lacuna encontrada e que deixe a maior quantidade de espaço sem uso. Assim,
o espaço que sobrar tende ser suficientemente grande para acomodar outro job. Para
a utilização dessa estratégia, o sistema também precisa conhecer todas as lacunas
para decidir qual é a maior.

Ainda existe uma variação da estratégia first-fit, conhecida como next-fit (próximo que
couber). Nesse caso, o sistema coloca um job na memória principal na primeira lacuna
que seja grande o suficiente para contê-lo, entretanto a pesquisa para encontrar a lacuna
não começa a partir da primeira posição vaga de memória, mas da posição onde a pesquisa
anterior terminou.
45

4.4 Memória virtual

4.4.1 Introdução

Há muitos anos os sistemas de computação esbarraram no problema dos programas


que são muito grandes, ou seja, programas que não podem caber na memória disponível
do sistema e a solução adotada era dividir o programa em “pedaços”, chamados de overlay,
de forma que cada pedaço coubesse separadamente na memória disponível.

O primeiro “pedaço" (overlay 0) era o primeiro a ser executado e chamava outras


partes do overlay quando fosse necessário. Alguns sistemas de overlay eram altamente
complexos, permitindo múltiplos overlays simultaneamente em memória. Um programa
montado sobre a técnica de overlay era mantido em disco e suas partes eram levadas e
trazidas para a memória dinamicamente e de acordo com a necessidade.

Embora o trabalho efetivo de troca de overlays fosse feito pelo sistema operacional, o
trabalho de dividir o programa em partes era do programador, exigindo tempo, técnica e
paciência. O programador precisava ter em mente a ordem em que os overlays deveriam
ser trocados para evitar que rotinas com grande interação ficassem em diferentes overlays.

Em 1961, foi criada uma maneira de atribuir todo o trabalho ao sistema operacional,
denominada memória virtual. O princípio básico é que o tamanho combinado do programa
(o tamanho da área de dados e da pilha de execução) possa exceder o tamanho da memória
física disponível, pois o sistema operacional mantém as partes do programa em uso na
memória principal e o restante em disco (swapping). Ou seja, conceitualmente, seguia a
idéia de overlay, entretanto, sob total controle do sistema operacional.

4.4.2 Conceitos básicos

1. Memória lógica:
É a memória que um processo enxerga, ou seja, aquela que o processo é capaz de
acessar. Deste modo, todos os endereços manipulados pelos processos são endere-
ços lógicos, assim como todas as instruções de máquina de um processo especificam
endereços lógicos. Em geral, cada processo possui sua memória lógica, que é inde-
pendente da memória dos outros processos;

2. Memória física:
É implementada pelos circuitos integrados de memória e é formada pelos endereços
físicos, que são utilizados para endereçar os circuitos integrados de memória;
46

3. Espaço de endereçamento lógico:


É formado pelo conjunto de endereços lógicos que um processo pode gerar/manipular
e são independentes por processo, ou seja, cada processo tem o seu próprio espaço
de endereçamento lógico;

4. Espaço de endereçamento físico:


É formado por todos os endereços aceitos pelos circuitos integrados de memória;

5. Unidade de gerência de memória (memory management unit - MMU ):


É o componente de hardware responsável por prover os mecanismos que serão usados
pelo sistema operacional para gerenciar a memória. Entre outras coisas, a MMU
vai mapear os endereços lógicos, manipulados pelos processos, nos correspondentes
endereços físicos que serão enviados para a memória. Na verdade, é muito comum a
MMU estar embutida dentro do processador, ao invés de ser um circuito integrado
separado.

4.4.3 Funcionamento geral

A memória virtual é uma técnica de organização e acesso à memória para sistemas


multiprogramados baseada no conceito de memória lógica (memória virtual), que é a
memória percebida pelo usuário, e de memória física, que é a memória efetivamente dis-
ponível no sistema e, na prática, facilita a tarefa de programação, visto que o programador
não precisa se preocupar com a memória física disponível e nem com a localização dos
endereços na memória física.

Em qualquer sistema existe um conjunto de instruções de acesso à memória para


ler ou guardar valores nos endereços de memória, tal como MOV REG 1000, ou seja,
nesse exemplo, o valor de REG está sendo guardado no endereço 1000, ou vice-versa,
dependendo do conjunto de instruções do processador.

Os endereços manipulados por essas instruções são os endereços lógicos ou virtuais e


formam o espaço de endereçamento lógico ou virtual. Em sistemas sem memória virtual o
endereço lógico é utilizado diretamente para acesso à memória física, Entretanto, quando
se utiliza a memória virtual, o endereço lógico é levado para a MMU para ser convertido
em um endereço físico.
47

4.4.4 Paginação

A paginação é uma organização de memória, utilizada para a implementação de me-


mória virtual, que faz com que a memória física seja vista como molduras ou quadros de
página (frames) e a memória lógica como páginas (pages). As páginas e suas molduras
possuem o mesmo tamanho fixo e, ainda, a área de swap ou, se for o caso, o disco4 de
swap, é dividido em blocos que possuem o mesmo tamanho da página.

Vários sistemas geram endereços de memória que são divididos em duas partes: o
número da página (page number ) e o deslocamento na página (page offset). A página é
utilizada como um índice na tabela de páginas, que contém o endereço base dos quadros
(Q), e combinado com deslocamento (D) fornece o endereço na memória física.

No exemplo5 , cada página possui 4 “palavras” 6 e supondo o acesso ao endereço lógico


___________, que se encontra na página ___________, com deslocamento
___________, então o endereço físico (EF) será:

EF = (quadro x tamanho da página em palavras) + D

EF = ( ___________ x ___________ ) + ___________ = ___________

Cada processo possui sua própria tabela de páginas que é referenciada pela sua tabela
de processo. O problema dessa organização é o tempo gasto para os dois acessos a cada
pesquisa em memória, um para a tabela de páginas e outro para memória física e, então,
procura-se contornar o atraso com o uso de uma memória cache específica para a conversão
dos endereços.
4
Um disco fabricado e utilizado com o propósito exclusivo de ser a área de swap é chamado de backing
storage.
5
Ver exemplo feito em sala.
6
“Palavra de memória" é uma unidade de medida e corresponde a um espaço de endereçameto na
memória, ou seja, corresponde ao tamanho do espaço alocado para cada endereço de memória.
48

4.4.5 Segmentação

Duas das diferenças mais importantes na implementação da paginação e da segmenta-


ção são: os múltiplos espaços de endereçamento virtual da segmentação, que na paginação
é apenas um, e a forma de organização do espaço de endereçamento virtual, que na pagi-
nação é alocado em páginas de tamanho fixo e, na segmentação, é organizado de acordo
com a necessidade de cada programa, não havendo uma unidade ou tamanho fixo de
alocação.

A memória virtual em um sistema de paginação organiza o espaço de endereçamento


virtual em um único bloco de endereços contíguos, onde os endereços iniciam de 0 até um
endereço máximo. Entretanto, em alguns casos, ter mais de um espaço de endereçamento
lógico pode ser mais vantajoso.

P. ex., um compilador precisa armazenar dados em forma de tabelas sem “misturar”


as áreas de armazenamento do programa fonte, da tabela de símbolos (que contém os
nomes e atributos das variáveis), da tabela de constantes, da árvore de análise sintática e
da pilha utilizada para chamadas de procedimentos do compilador. As quatro primeiras
áreas crescem continuamente, já a última aumenta e diminui de modo imprevisível.

Figura 15: No espaço de endereçamento único pode ocorrer “colisão" entres as tabelas
(TANENBAUM, 2008).

Suponha que a tabela de símbolos esgotou seu espaço, então, seria necessário adotar
uma das seguintes soluções:

• O compilador deveria ter sua execução interrompida, avisando ao usuário que o


número de variáveis excede ao permitido. Obviamente, seria uma péssima solução;
49

• Implementar no compilador uma gerência de memória, tipo overlay, e ocupar os


espaços vazios das outras tabelas, o que seria uma solução no mínimo muito traba-
lhosa;

• “Criar” no sistema vários espaços de endereçamento lógicos e completamente inde-


pendentes, denominados segmentos, de forma que cada segmento, por ser indepen-
dente dos demais, tivesse a flexibilidade necessária para aumentar ou diminuir de
tamanho, dependendo do tipo de dados ou instruções que fossem armazenados em
cada segmento.

Cada segmento consiste de uma seqüência contínua de endereços, iniciando em 0


até um valor máximo, sendo que o máximo de cada segmento pode ser diferente dos
demais. Além disso, o tamanho de cada segmento pode variar durante execução de um
programa, aumentando sempre que um novo dado é acrescentado e diminuindo quando
ocorrer o inverso. Cada segmento também pode vir a ficar completamente ocupado, mas
normalmente segmentos são muito grandes, o que minimiza essa situação.

Figura 16: Múltiplos espaços de endereçamento. Cada tabela pode aumentar ou diminuir,
independente das demais (TANENBAUM, 2008).

A segmentação também facilita o compartilhamento de procedimentos ou dados entre


os processos, tais como as bibliotecas compartilhadas. P. ex., estações de trabalho com
sistemas gráficos de janelas, geralmente, têm bibliotecas gráficas muito grandes e compi-
ladas em quase todos os programas, assim, com a segmentação, a biblioteca gráfica pode
ser colocada em um segmento e compartilhada por vários processos.

Outra vantagem da segmentação é facilitar o controle e a segurança do conteúdo de


cada segmento. Pois cada um deles possui um único tipo de dados, não misturando
50

procedimentos com dados de programas e, assim, o usuário (programador) pode conhecer


o tipo de dado que está guardado em cada segmento e tirar proveito dessa propriedade.
51

5 Sistemas de arquivos

5.1 Introdução

O sistema de arquivos é a parte mais visível de um sistema de computação, pois a


maioria dos programas lê e escreve pelo menos um arquivo e os usuários estão sempre
preocupados com a existência - ou com o “desaparecimento" - de seus arquivos, com seu
conteúdo e com as suas propriedades.

Além disso, para grande parte dos usuários, especializados ou não, a conveniência e
a utilidade do sistema de computação é determinada pela interface, pela estrutura e pela
confiabilidade do sistema de arquivos.

Dentro do contexto proposto, um arquivo será definido como uma unidade


lógica de armazenamento de dados destinada a abstrair as propriedades físicas
do meio de armazenamento. Assim sendo, um arquivo é um tipo abstrato de dados
- ou um objeto, instância de uma classe, segundo o paradigma de orientação a objetos -
e, portanto, para uma definição mais precisa, é necessário considerar suas propriedades
(nome, tamanho, a data de criação, características de proteção do arquivo etc.) e as
operações - eventos - que podem ser executadas sobre o mesmo (criação, leitura, gravação
etc.).

Usuários e aplicativos “percebem" os arquivos pela visibilidade criada por essas pro-
priedades e características, ou seja, o arquivo é visto por meio da abstração criada pelo
sistema de arquivos implementado pelo sistema operacional.
52

5.2 Estruturas de arquivos

5.2.1 Seqüência de bytes não estruturada

Nessa estrutura, também conhecida como “fluxo de bytes não estruturado", o sistema
operacional não sabe e nem se importa com o que está no arquivo e tudo o que pode ser
percebido são simplesmente bytes e qualquer significado que se queira atribuir a esses bytes
deve ser tratado pelo programa aplicativo. Assim, com o sistema operacional considerando
os arquivos nada mais do que uma seqüência de bytes obtém-se o máximo de flexibilidade,
visto que os aplicativos podem armazenar dados de qualquer espécie (caractere, áudio,
vídeo etc.) e, posteriormente, tratá-los de acordo com suas devidas características e
necessidades.

Desse modo, o sistema operacional não ajuda, mas também não atrapalha, o que
pode ser um aspecto muito importante. Atualmente, muitos sistemas operacionais por
exemplo, as diferentes versões do Unix, as diversas distribuições do Linux, o MS-DOS,
toda a família Windows etc., utilizam essa estrutura.

5.2.2 Seqüência de registros de comprimento fixo

O arquivo é tratado por registro, ou seja, os bytes não estão “soltos", como no caso
anterior, pois possuem uma estrutura interna, os registros. Assim, as operações de leitura
recuperam um registro e as operações de gravação anexam ou sobrescrevem um registro.

Os antigos cartões perfurados originaram esta estrutura, fixando as colunas em 80


posições, e, assim, um registro representava a imagem de um cartão. Posteriormente,
foram criadas estruturas de 132 posições, por influência das antigas impressoras matriciais.
O sistema operacional mais recente a usar tal estrutura foi o antigo sistema CP/M, que
utilizava uma estrutura de 128 posições. Por questões óbvias, a idéia de manter uma
estrutura tão rígida foi completamente abandonada.

O simples fato de atribuir um formato rígido de gravação confere a esse tipo de


estrutura, por parte do sistema operacional, uma interpretação do dado a ser gravado,
diferentemente da “seqüência de bytes não estruturada", onde toda a interpretação do
dado fica por conta do aplicativo que manipula o arquivo.
53

5.2.3 Árvore de registros

Essa estrutura assemelha-se às “árvores B", sendo os arquivos compostos por uma
árvore de registros, de tamanho fixo ou variável, cada um dos quais contendo um campo
chave (em uma posição fixa do registro), e os registros são ordenados pelo campo chave.

A operação básica não é mais obter o “próximo registro", mas obter o registro que
possua uma chave específica, solicitando ao sistema operacional que encontre o registro
desejado, sem nos preocuparmos com sua posição no arquivo.

Assim - com relação à localização da informação - a organização do arquivo, os even-


tos de exclusão, inclusão etc., são de responsabilidade do sistema operacional, e não do
aplicativo. Ou seja, seu princípio e seu funcionamento são o oposto da “seqüência de bytes
não estruturada", visto que existe a identificação e interpretação do conteúdo do arquivo
por parte do sistema operacional.

A estrutura de árvore de registros é amplamente utilizada em mainframes.

Figura 17: Estrutura de arquivos: (a) Seqüência de byte não estruturada. (b) Seqüência
de registros de comprimento fixo. (c) Árvore de registros (TANENBAUM, 2008).

5.3 Método de alocação de espaço para arquivos

5.3.1 Alocação contígua

O método de alocação contígua requer que cada arquivo ocupe um conjunto de blocos
com endereços consecutivos no disco e a alocação é definida pelo endereço do primeiro
bloco do arquivo. Assim, se o arquivo tem tamanho n e seu primeiro bloco for o bloco b,
então o arquivo ocupará os blocos b, b + 1, b + 2, ...b + (n − 1).

A tabela de alocação de arquivos de um disco organizado segundo essa estratégia guar-


dará somente endereço do primeiro bloco, embora algumas implementações armazenem
54

Figura 19: Exemplo de utilização de


Figura 18: Exemplo de utilização de alo- alocação contígua, mostrando um disco
cação contígua, mostrando o espaço alo- ocupado por 7 arquivos (A, B, C, D, E,
cado para 8 arquivos (A, B, C, F, H, I, F e G) e em seguida, dois arquivos são
P e J). removidos (D e E) (TANENBAUM, 2008).

também o tamanho do arquivo. As principais desvantagens da alocação contígua são: a


necessidade de se fazer uma previsão do tamanho do arquivo no momento da criação, falta
de flexibilidade para ampliação, provoca grande fragmentação externa (fragmentação de
disco) e somente permite acesso o seqüencial aos blocos que compõem o arquivo.

Como vantagem, a alocação contígua, apresenta a rapidez de acesso na leitura dos


arquivos.

5.3.2 Alocação por lista encadeada

Nesse método, cada arquivo é visto como uma lista encadeada de blocos, sendo que
os blocos podem estar “espalhados" em qualquer lugar do disco. A estrutura da tabela de
alocação é um “ponteiro" para o primeiro bloco do arquivo (em algumas implementações
também existe um ponteiro para o último bloco) e o encadeamento entre os blocos é feito
por cada um dos blocos, que aponta para o seu bloco seguinte.

O método de alocação por lista encadeada soluciona o problema da necessidade de


estimação do tamanho de um arquivo e, ainda, não provoca fragmentação externa. Mas
suas desvantagens são: a baixa confiabilidade, o espaço ocupado pelos ponteiros, a lentidão
na leitura dos arquivos e somente permite acesso seqüencial aos blocos.
55

Figura 20: Alocação encadeada (Fonte: Silberschatz, Galvin e Gagne (2009)).

A baixa confiabilidade é vista da seguinte forma: caso seja perdido um determinado


ponteiro, todo o restante do arquivo, a partir deste ponto, estará perdido, ou melhor, não
terá como ser localizado.

5.3.3 Alocação encadeada por tabela

Esse método também utiliza o encadeamento de blocos, porém de forma indireta, pois
os blocos são encadeados em uma tabela auxiliar e não diretamente pelo bloco antecessor.
A tabela de alocação aponta para a posição do primeiro bloco do arquivo na tabela de
blocos e cada bloco aponta para a posição do próximo bloco.

É um método de alocação encadeada melhor do que a alocação por lista encadeada,


sendo mais rápido principalmente quando se quer localizar um bloco em particular. Tam-
bém soluciona o problema da necessidade de estimação do tamanho de um arquivo, não
provoca fragmentação externa, além disso, é mais confiável e permite acesso direto aos
blocos, mas a tabela de blocos somente pode ser acessada seqüencialmente.

Suas desvantagens são: o espaço ocupado pela tabela de blocos e a lentidão na leitura
dos arquivos.

O método de alocação encadeada por tabela é utilizado nos sistemas de arquivos FAT
(file alocation table), em todas as variações FAT 12, FAT 16 e FAT 32. O sistema de
arquivo nativo do MS-DOS e do Windows 9x é a FAT.
56

Figura 21: Alocação encadeada por tabela

5.3.4 Alocação indexada

No método de alocação indexada cada arquivo possui seu próprio bloco de índice, que
é uma tabela de endereços de blocos do disco, sendo que no diretório armazena-se somente
o endereço do bloco de índice.

Figura 22: Alocação indexada

A indexação possui muitas vantagens em relação aos demais métodos, por exemplo,
permite o acesso direto aos blocos, resolve o problema da estimativa inicial do arquivo, da
fragmentação externa, dos ponteiros “espalhados" pelo disco e do crescimento incremental.
57

Entretanto, o armazenamento dos ponteiros do bloco de índice pode vir a ser maior
do que na alocação encadeada, pois quanto mais arquivos de pequeno porte forem arma-
zenados, maior será a taxa de ocupação.

Nesse ponto deve-se pensar: qual deve ser o tamanho do bloco de índice? Normal-
mente, o bloco de índice ocupa uma unidade de alocação (um bloco) e caso um arquivo
necessite de mais de um bloco de índice é feito um encadeamento entre os blocos de índice.

Figura 23: Implementação da alocação indexada pelo unix file system - UFS, com índices
direto, simples, duplo e triplo.

O Unix File System (UFS), o sistema de arquivos padrão do Unix, utiliza esse conceito,
porém sua implementação amplia a idéia, permitindo indexação indireta, ou seja, índices
que referenciam outros índices, que também podem referenciar outros índices, e assim por
diante. O sistema de arquivos NTFS (New Technology File System), do Windows 2000,
XP e Vista, também segue raciocício semelhante e ambos, o NTFS e o UFS, possibilitam
o armazemento de grandes arquivos.

5.4 Gerenciamento de espaço livre

O sistema de arquivos deve ser capaz de controlar a área de espaço livre no disco, pois,
como os discos possuem quantidade limitada de espaço, é necessário reutilizar o espaço
liberado por arquivos apagados, mantendo a chamada “lista de espaços livres".

A lista de espaços livres referencia todos blocos que estão desocupados no disco e,
então, por exemplo, para a criação de um arquivo, faz-se uma busca na lista, de forma
que seja possível localizar e alocar a quantidade de blocos necessária para o arquivo.
58

Frequentemente a lista de espaços livres é implementada como um vetor de bits, onde


cada bloco é representado por um bit. Caso o bit esteja ativado, então o bloco corres-
pondente ao bit esta ocupado e, caso contrário, estará desocupado. Assim, a seqüên-
cia “0110110..." indica que os blocos 0, 3 e 6 estão livres. A grande vantagem dessa
implementação é a simplicidade, entretanto a desvantagem é o espaço utilizado para o
armazenamento do mapa (vetor) de bits.

Figura 24: Mapa de bits para gerência de espaço livre.

Uma segunda maneira para implementar a lista de espaços livres é encadear os blocos
livres, mantendo o endereço do primeiro bloco livre em uma lista de tamanho reduzido -
pois esta lista pode conter somente um endereço - e, a partir do primeiro bloco livre, cada
bloco livre apontará para o seguinte.

As desvantagens desse método são o tempo gasto na localização dos espaços livres, pois
se perde um tempo substancial percorrendo a lista de blocos encadeados, e provavelmente
não será possível perceber a existência de blocos livres contíguos
59

Referências

DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. Sistemas operacionais. São Paulo:


Pearson Prenticce-Hall, 2005.

FLYNN, M. J. Some computer organization and their effectiveness. IEEE Transaction


on Computers, New York, C-21, n. 9, p. 948–960, Sep. 1972.

GIOZZA, W. F.; MOURA, J. A. B.; SAUVÉ, J. P.; ARAÚJO, J. F. M. de. Redes locais
de computadores: tecnologias e aplicações. São Paulo: McGraw-Hill: EMBRATEL, 1986.

OLIVEIRA, R. S. de; CARÍSSIMI, A. S.; TOSCANI, S. S. Sistemas operacionais. 3.


ed. Porto Alegre: Instituto de Informática da UFRGS: Editora Sagra Luzzatto, 2004.
(Livros Didáticos, v. 11).

ROSE, C. A. F. D.; NAVAUX, P. O. A. Arquiteturas paralelas. Porto Alegre: Instituto


de Informática da UFRGS: Editora Sagra Luzzatto, 2003. (Livros Didáticos, v. 15).

SILBERSCHATZ, A.; GALVIN, P. B.; GAGNE, G. Sistemas operacionais com Java. 7.


ed. Rio de Janeiro: Elsevier, 2008.

SILBERSCHATZ, A.; GALVIN, P. B.; GAGNE, G. Operating system concepts. 8. ed.


New York: John Wiley & Sons, 2009.

TANENBAUM, A. S. Sistemas operacionais modernos. 2. ed. São Paulo: Prentice Hall,


2003.

TANENBAUM, A. S. Modern operating Systems. 3. ed. Upper Saddle River: Prentice


Hall, 2008.

TANENBAUM, A. S.; WOODHULL, A. S. Sistemas operacionais: projeto e


implementação. 3. ed. Porto Alegre: Bookman, 2008.

Esse documento foi editado com o Winefish, utilizando a classe ABNTEX, processado pelo LATEX 2ε e está disponível em
http://www.arbex.pro.br/.
Arquivo de impressão gerado em 8 de fevereiro de 2010.

TEX3.141592 (Web2C 7.5.4)