Escolar Documentos
Profissional Documentos
Cultura Documentos
CDU 004.4
4.6 SEGMENTAÇÃO
A memória virtual discutida até aqui é unidimensional, pois os endereços virtuais vão de 0 até
algum endereço máximo, um endereço após o outro. Para muitos problemas, ter dois ou mais
espaços de endereçamento virtuais separados pode ser muito melhor do que ter apenas um.
Por exemplo, um compilador tem muitas tabelas que são construídas à medida que a compi-
lação prossegue, possivelmente incluindo:
1. O salvamento do texto do código-fonte para a listagem impressa (em sistemas de
lote).
2. A tabela de símbolos, contendo os nomes e atributos das variáveis.
3. A tabela contendo todas as constantes inteiras e em ponto flutuante usadas.
4. A árvore de análise, contendo a análise sintática do programa.
5. A pilha usada para chamadas de função dentro do compilador.
Cada uma das quatro primeiras tabelas cresce continuamente, à medida que a compila-
ção prossegue. A última aumenta e diminui de maneiras imprevisíveis durante a compilação.
Em uma memória unidimensional, para essas cinco tabelas, teriam de ser alocados trechos
adjacentes do espaço de endereçamento virtual, como se vê na Figura 4-21.
Considere o que acontecerá se um programa tiver um número excepcionalmente grande
de variáveis, mas uma quantidade normal do restante. O trecho do espaço de endereçamento
alocado para a tabela de símbolos poderá ser totalmente preenchido, mas ainda poderá haver
muito espaço disponível nas outras tabelas. Naturalmente, o compilador poderia simplesmen-
te emitir uma mensagem dizendo que a compilação não pode continuar devido à existência de
variáveis demais, mas fazer isso não parece muito justo, quando resta espaço sem utilização
nas outras tabelas.
Outra possibilidade é brincar de Robin Hood, roubando espaço das tabelas com excesso
de espaço e dando-o para as tabelas com pouco espaço. Essa troca pode ser feita, mas é aná-
logo a gerenciar os próprios overlays – na melhor das hipóteses, um incômodo, e, na pior, um
trabalho enorme e sem recompensa.
384 SISTEMAS OPERACIONAIS
Pilha de chamadas
Espaço de Livre
endereçamento
alocado para a Espaço correntemente
Árvore de análise
árvore de análise usado pela árvore de análise
Tabela de
constantes
Texto do
código-fonte
A tabela de símbolos
Tabela de símbolos colidiu com a tabela
do texto do código-fonte
O que é realmente necessário é uma maneira de fazer com que o programador não tenha
que gerenciar o aumento e a redução das tabelas, da mesma maneira que a memória virtual
elimina a preocupação de organizar o programa em overlays.
Uma solução simples e extremamente geral é fornecer à máquina vários espaços de
endereçamento completamente independentes, chamados de segmentos. Cada segmento con-
siste em uma seqüência linear de endereços, de 0 até algum máximo. O comprimento de cada
segmento pode ser qualquer um, de 0 até o máximo permitido. Diferentes segmentos podem
ter (e normalmente têm) comprimentos diferentes. Além disso, o comprimento dos segmen-
tos pode mudar durante a execução. O comprimento de um segmento de pilha pode aumentar
quando algo for colocado na pilha e diminuir quando algo for retirado dela.
Como cada segmento constitui um espaço de endereçamento separado, diferentes seg-
mentos podem aumentar ou diminuir independentemente, sem afetar uns aos outros. Se uma
pilha em determinado segmento precisa de mais espaço de endereçamento para crescer, ela
pode tê-lo, pois não há mais nada em seu espaço de endereçamento para colidir. É claro que
um segmento pode ser preenchido, mas os segmentos normalmente são muito grandes, de
modo que essa ocorrência é rara. Para especificar um endereço nessa memória segmentada,
ou bidimensional, o programa precisa fornecer um endereço de duas partes, um número de
segmento e um endereço dentro do segmento. A Figura 4-22 ilustra uma memória segmen-
tada sendo usada para as tabelas de compilador discutidas anteriormente. Cinco segmentos
independentes são mostrados aqui.
Salientamos que, em sua forma mais pura, um segmento é uma entidade lógica, da qual
o programador está ciente e a usa como tal. Um segmento pode conter uma ou mais funções,
um array, uma pilha ou um conjunto de variáveis escalares, mas normalmente ele não contém
uma mistura de tipos diferentes.
Uma memória segmentada tem outras vantagens, além de simplificar a manipulação de
estruturas de dados que crescem ou diminuem. Se cada função ocupa um segmento separado,
com o endereço 0 como seu endereço inicial, a ligação de funções compiladas separadamente
é bastante simplificada. Depois que todas as funções que constituem um programa tiverem
CAPÍTULO 4 • GERENCIAMENTO DE MEMÓRIA 385
20K
16K 16K
Constantes
0K 0K 0K 0K 0K
Segmento Segmento Segmento Segmento Segmento
0 1 2 3 4
Figura 4-22 Uma memória segmentada permite que cada tabela aumente ou diminua inde-
pendentemente das outras tabelas.
sido compiladas e ligadas, uma chamada para a função no segmento n usará o endereço de
duas partes (n, 0) para endereçar a palavra 0 (o ponto de entrada).
Se a função no segmento n for subseqüentemente modificada e recompilada, nenhuma
outra função precisará ser alterada (pois nenhum endereço inicial foi modificado), mesmo que
a nova versão seja maior do que a antiga. Com uma memória unidimensional, as funções são
concisamente empacotadas uma ao lado da outra, sem nenhum espaço de endereçamento entre
elas. Conseqüentemente, alterar o tamanho de uma função pode afetar o endereço inicial de ou-
tras funções não relacionadas. Isso, por sua vez, exige modificar todas as funções que chamam
qualquer uma das funções deslocadas por essa alteração para incorporar seus novos endereços
iniciais. Se um programa contém centenas de funções, esse processo pode ser dispendioso.
A segmentação também facilita o compartilhamento de funções ou dados entre vários
processos. Um exemplo comum é o de biblioteca compartilhada. As estações de trabalho mo-
dernas que executam avançados sistemas de janelas, freqüentemente têm bibliotecas gráficas
extremamente grandes compiladas em praticamente todo programa. Em um sistema segmenta-
do, a biblioteca gráfica pode ser colocada em um segmento e compartilhada por vários proces-
sos, eliminando a necessidade de tê-la no espaço de endereçamento de cada processo. Embora
também seja possível ter bibliotecas compartilhadas nos sistemas de paginação puros, isso é
muito mais complicado. Na verdade, esses sistemas fazem isso simulando a segmentação.
Como cada segmento forma uma entidade lógica da qual o programador está ciente,
como uma função, um array ou uma pilha, diferentes segmentos podem ter diferentes tipos
de proteção. Um segmento de função pode ser especificado como apenas de execução, proi-
bindo tentativas de leitura ou de armazenamento de dados nele. Um array em ponto flutuante
pode ser especificado como de leitura/escrita, mas não execução, e as tentativas de “executá-
lo” serão detectadas. Tal proteção é útil na identificação de erros de programação.
Você deve tentar entender por que a proteção faz sentido em uma memória segmentada,
mas não em uma memória paginada unidimensional. Em uma memória segmentada, o usuá-
rio está ciente do que há em cada segmento. Normalmente, um segmento não conteria uma
função e uma pilha, por exemplo, mas uma ou a outra. Como cada segmento contém apenas
um tipo de objeto, o segmento pode ter a proteção apropriada para esse tipo em particular. A
paginação e a segmentação são comparadas na Figura 4-23.
386 SISTEMAS OPERACIONAIS
Por que essa técnica foi inventada? Para se obter Para permitir que
um espaço de programas e dados sejam
endereçamento linear divididos em espaços de
sem ter de comprar endereçamento logicamente
mais memória física independentes e para ajudar
no compartilhamento e na
proteção
De certo modo, o conteúdo de uma página é acidental. O programador ignora até mes-
mo o fato de que a paginação está ocorrendo. Embora fosse possível colocar alguns bits em
cada entrada da tabela de páginas para especificar o acesso permitido, o programador para
utilizar esse recurso teria de monitorar onde estariam todos os limites de página em seu espa-
ço de endereçamento. Entretanto, a paginação foi inventada para eliminar precisamente esse
tipo de gerenciamento mais complexo. Como o usuário de uma memória segmentada tem a
ilusão de que todos os segmentos estão o tempo todo na memória principal – isto é, ele pode
endereçá-los como se estivessem lá –, ele pode proteger cada segmento separadamente, sem
precisar se preocupar com a administração de overlays.
(3K) (3K)
Segmento 4 Segmento 4
(7K) (7K) Segmento 5 Segmento 5 (10K)
(4K) (4K)
(4K)
Segmento 3 Segmento 3 Segmento 3
Segmento 5
(8K) (8K) (8K) Segmento 6 (4K)
(4K)
Segmento 6
Segmento 2 Segmento 2 Segmento 2 Segmento 2 (4K)
(5K) (5K) (5K) (5K)
Segmento 2
(3K) (3K) (3K) (5K)
Segmento 1
(8K) Segmento 7 Segmento 7 Segmento 7 Segmento 7
(5K) (5K) (5K) (5K)
Segmento 0 Segmento 0 Segmento 0 Segmento 0 Segmento 0
(4K) (4K) (4K) (4K) (4K)
(a) (b) (c) (d) (e)
Bits 13 1 2
Índice