Você está na página 1de 11

Implementação UNIX

K. Thompson

Laboratórios Bell
Murray Hill, Nova Jersey 07974

ABSTRATO

Este artigo descreve em termos de alto nível a implementação do residente


UNIX† núcleo. Esta discussão está dividida em três partes. A primeira parte descreve como
oUNIX o sistema visualiza processos, usuários e programas. A segunda parte descreve o
sistema de E/ S. A última parte descreve oUNIXsistema de arquivo.

1. INTRODUÇÃO
OUNIXkernel consiste em cerca de 10.000 linhas de código C e cerca de 1.000 linhas de código
assembly. O código assembly pode ser dividido em 200 linhas incluídas por uma questão de eficiência (elas
poderiam ter sido escritas em C) e 800 linhas para executar funções de hardware que não são possíveis em C.
Esse código representa de 5 a 10 por cento do que foi agrupado na ampla expressão "o UNIX
sistema operacional.'' O kernel é o únicoUNIXcódigo que não pode ser substituído por um usuário ao seu gosto.
Por esse motivo, o kernel deve tomar o mínimo possível de decisões reais. Isso não significa permitir ao usuário
um milhão de opções para fazer a mesma coisa. Em vez disso, significa permitir apenas uma maneira de fazer
uma coisa, mas que seja o mínimo divisor comum de todas as opções que podem ter sido fornecidas.
O que é ou não implementado no kernel representa uma grande responsabilidade e um grande poder. É uma
plataforma de palanque sobre "como as coisas devem ser feitas". Mesmo assim, se "o caminho" for muito radical,
ninguém o seguirá. Cada decisão importante foi pesada cuidadosamente. Em todo o processo, a simplicidade foi
substituída pela eficiência. Algoritmos complexos são usados apenas se sua complexidade puder ser localizada.

2. CONTROLE DE PROCESSO
NoUNIXsistema, um usuário executa programas em um ambiente chamado processo de usuário. Quando uma
função do sistema é necessária, o processo do usuário chama o sistema como uma sub-rotina. Em algum ponto desta
chamada, há uma mudança distinta de ambientes. Depois disso, o processo é dito ser um processo do sistema. Na
definição normal de processos, os processos do usuário e do sistema são fases diferentes do mesmo processo (eles
nunca são executados simultaneamente). Para proteção, cada processo do sistema possui sua própria pilha.
O processo do usuário pode ser executado a partir de um segmento de texto somente leitura, que é
compartilhado por todos os processos que executam o mesmo código. Não háfuncionalbeneficiar de segmentos de
texto compartilhado. Umeficiência O benefício vem do fato de que não há necessidade de trocar segmentos somente
leitura porque a cópia original na memória secundária ainda é atual.

trocados enquanto espera pela entrada do terminal.


da mesma cópia de um segmento somente leitura, apenas uma cópia precisa residir na memória primária. Isso é
um efeito secundário, porque a execução simultânea de um programa não é comum. É irônico que esse
efeito, que reduz o uso da memória primária, só surja quando há superabundância de memória primária,
ou seja, quando há memória suficiente
Este é um grande benefício para programas interativos que tendem a ser Além
para manter carregados os processos em
disso, se dois processos estiverem sendo executados simultaneamente
espera.

__________________
†UNIX é uma marca comercial da Bell Laboratories.
-2-

Todos os segmentos de texto somente leitura atuais no sistema são mantidos a partir dotabela de texto. Uma entrada de
tabela de texto contém a localização do segmento de texto na memória secundária. Se o segmento for carregado, essa tabela
também contém a localização da memória principal e a contagem do número de processos que compartilham essa entrada.
Quando essa contagem é reduzida a zero, a entrada é liberada junto com qualquer memória primária e secundária que contenha
o segmento. Quando um processo executa pela primeira vez um segmento de texto compartilhado, uma entrada na tabela de
texto é alocada e o segmento é carregado na memória secundária. Se um segundo processo executa um segmento de texto que
já está alocado, a contagem de referência de entrada é simplesmente incrementada.
Um processo de usuário tem alguns dados de leitura/gravação estritamente privados contidos em seu segmento de dados. Na
medida do possível, o sistema não usa o segmento de dados do usuário para manter os dados do sistema. Em particular, não há buffers de
E/ S no espaço de endereço do usuário.

O segmento de dados do usuário tem dois limites crescentes. Um, aumentado automaticamente pelo sistema
como resultado de falhas de memória, é usado para uma pilha. O segundo limite só é aumentado (ou reduzido) por
solicitações explícitas. O conteúdo da memória primária recém-alocada é inicializado em zero.
Também associado e trocado com um processo está um pequeno segmento de dados do sistema de tamanho fixo. Este
segmento contém todos os dados sobre o processo que o sistema precisa apenas quando o processo está ativo. Exemplos do
tipo de dados contidos no segmento de dados do sistema são: registradores do processador central salvos, descritores de
arquivos abertos, informações contábeis, área de dados rascunhos e a pilha para a fase do sistema do processo. O segmento de
dados do sistema não é endereçável do processo do usuário e, portanto, está protegido.

Por último, há uma tabela de processos com uma entrada por processo. Esta entrada contém todos os dados
necessários ao sistema quando o processo énãoativo. Exemplos são o nome do processo, a localização dos outros
segmentos e informações de agendamento. A entrada da tabela de processos é alocada quando o processo é criado e
liberada quando o processo termina. Esta entrada de processo é sempre diretamente endereçável pelo kernel.

A Figura 1 mostra as relações entre os vários dados de controle do processo. De certo modo, a tabela de
processos é a definição de todos os processos, pois todos os dados associados a um processo podem ser acessados
a partir da entrada na tabela de processos.

TEXTO
PROCESSO MESA
MESA ENTRADA
ENTRADA

TABELA DE TEXTO
RESIDENTE
TABELA DE PROCESSOS

SISTEMA
PERMUTÁVEL
DADOS
SEGMENTO DO UTILIZADOR

TEXTO
DO UTILIZADOR SEGMENTO
DADOS
SEGMENTO

DO UTILIZADOR

ENDEREÇO
ESPAÇO

Fig. 1—Estrutura de dados de controle de processo.

2.1. Criação de processos e execução de programas

Os processos são criados pela primitiva do sistemagarfo. O processo recém-criado (filho) é uma cópia do
processo original (pai). Não há compartilhamento detectável de memória primária entre os dois processos. (Claro, se o
processo pai estiver executando a partir de um segmento de texto somente leitura, o filho compartilhará o segmento de
texto.) Cópias de todos os segmentos de dados graváveis são feitas para o processo filho. Arquivos que foram abertos
antes dogarfo são verdadeiramente compartilhados após ogarfo. Os processos são informados quanto à sua parte no
relacionamento para permitir que eles selecionem seu próprio destino (geralmente não idêntico). O pai podeespere
-3-

pela rescisão de qualquer um de seus filhos.

Um processo podeexecutivoum arquivo. Isso consiste em trocar os segmentos de texto e dados atuais do processo
por novos segmentos de texto e dados especificados no arquivo. Os segmentos antigos são perdidos. fazendo umexecutivo
faznão processos de mudança; o processo que fezexecutivopersiste, mas após oexecutivoele está executando um programa
diferente. Arquivos que foram abertos antes doexecutivopermanecer aberto após oexecutivo.
Se um programa, digamos a primeira passagem de um compilador, deseja se sobrepor a outro programa,
digamos a segunda passagem, então ele simplesmenteexecutivoé o segundo programa. Isso é análogo a um ''goto''. Se
um programa deseja recuperar o controle apósexecutivoum segundo programa, devegarfoum processo filho, faça com
que o filho executivoo segundo programa, e ter o paiesperepara a criança. Isso é análogo a uma "chamada". Quebrar a
chamada em uma ligação seguida por uma transferência é semelhante à ligação de sub-rotina no SL-5. 1

2.2. Trocando
Os principais dados associados a um processo (o segmento de dados do usuário, o segmento de dados
do sistema e o segmento de texto) são trocados de e para a memória secundária, conforme necessário. O
segmento de dados do usuário e o segmento de dados do sistema são mantidos na memória primária contígua
para reduzir a latência de troca. (Quando dispositivos de baixa latência, como bolhas, CCDs ou dispositivos de
dispersão/ reunião, são usados, esta decisão terá que ser reconsiderada.) A alocação de memória primária e
secundária é executada pelo mesmo algoritmo simples de primeiro ajuste. Quando um processo cresce, um
novo pedaço de memória primária é alocado. O conteúdo da memória antiga é copiado para a nova memória. A
memória antiga é liberada e as tabelas são atualizadas. Se não houver memória primária suficiente, a memória
secundária será alocada. O processo é trocado para a memória secundária,
Um processo separado no kernel, o processo de troca, simplesmente troca os outros processos para dentro e
para fora da memória primária. Ele examina a tabela de processos procurando por um processo que foi trocado e está
pronto para ser executado. Ele aloca memória primária para esse processo e lê seus segmentos na memória primária,
onde esse processo compete pelo processador central com outros processos carregados. Se nenhuma memória
principal estiver disponível, o processo de troca disponibiliza a memória examinando a tabela de processos em busca
de processos que podem ser trocados. Ele seleciona um processo para trocar, grava-o na memória secundária, libera a
memória primária e depois volta para procurar um processo para trocar.
Assim, existem dois algoritmos específicos para o processo de troca. Qual dos possivelmente muitos processos
que são trocados deve ser trocado? Isso é decidido pelo tempo de residência do armazenamento secundário. Aquele
com o tempo limite mais longo é trocado primeiro. Há uma pequena penalidade para processos maiores. Qual dos
possivelmente muitos processos carregados deve ser trocado? Processos que estão esperando por eventos lentos (isto
é, não rodando no momento ou esperando por E/S de disco) são escolhidos primeiro, por idade na memória primária,
novamente com penalidades de tamanho. Os outros processos são examinados pelo mesmo algoritmo de idade, mas
não são retirados a menos que tenham pelo menos alguma idade. Isso adiciona histerese à troca e evita a debulha total.

Esses algoritmos de troca são os mais suspeitos no sistema. Com memória primária limitada, esses
algoritmos causam troca total. Isso não é ruim em si, porque a troca não afeta a execução dos processos
residentes. No entanto, se o dispositivo de troca também deve ser usado para armazenamento de arquivos, o
tráfego de troca afeta severamente o tráfego do sistema de arquivos. São exatamente esses pequenos
sistemas que tendem a dobrar o uso de recursos de disco limitados.

2.3. Sincronização e agendamento


A sincronização do processo é realizada fazendo com que os processos esperem por eventos. Os eventos são
representados por inteiros arbitrários. Por convenção, os eventos são escolhidos para serem endereços de tabelas associadas a
esses eventos. Por exemplo, um processo que está aguardando o término de qualquer um de seus filhos aguardará um evento
que seja o endereço de sua própria entrada na tabela de processos. Quando um processo termina, ele sinaliza o evento
representado pela entrada da tabela de processos de seu pai. Sinalizar um evento no qual nenhum processo está esperando não
tem efeito. Da mesma forma, sinalizar um evento no qual muitos processos estão esperando despertará todos eles. Isso difere
consideravelmente das operações de sincronização P e V de Dijkstra, 2em que nenhuma memória está associada a eventos.
Portanto, não há necessidade de alocação de eventos antes de seu uso. Os eventos existem simplesmente por serem usados.
-4-

Do lado negativo, como não há memória associada aos eventos, nenhuma noção de ''quanto'' pode ser
sinalizada por meio do mecanismo de eventos. Por exemplo, processos que precisam de memória podem esperar.
em um evento associado à alocação de memória. Quando qualquer quantidade de memória fica disponível, o
evento seria sinalizado. Todos os processos concorrentes então acordariam para lutar pela nova memória. (Na
realidade, o processo de troca é o único processo que espera que a memória principal fique disponível.)

Se ocorrer um evento entre o momento em que um processo decide esperar por esse evento e o momento em que o
processo entra no estado de espera, o processo aguardará um evento que já ocorreu (e pode nunca mais ocorrer). Essa
condição de corrida ocorre porque não há memória associada ao evento para indicar que o evento ocorreu; a única ação de um
evento é mudar um conjunto de processos do estado de espera para o estado de execução. Esse problema é amplamente
aliviado pelo fato de que a alternância de processos só pode ocorrer no kernel por chamadas explícitas ao mecanismo de espera
de evento. Se o evento em questão for sinalizado por outro processo, não há problema. Mas se o evento for sinalizado por uma
interrupção de hardware, deve-se tomar cuidado especial. Essas corridas de sincronização representam o maior problema
quandoUNIXé adaptado para configurações de múltiplos processadores. 3

O código de espera de evento no kernel é como uma ligação de co-rotina. A qualquer momento, todos, exceto um dos
processos, chamaram event-wait. O processo restante é aquele em execução no momento. Quando ele chama event-wait, um
processo cujo evento foi sinalizado é selecionado e esse processo retorna de sua chamada para event-wait.

Qual dos processos executáveis deve ser executado a seguir? Associado a cada processo está uma prioridade. A
prioridade de um processo do sistema é atribuída pelo código que emite a espera em um evento. Isso é aproximadamente
equivalente à resposta que se esperaria em tal evento. Os eventos de disco têm alta prioridade, os eventos de teletipo são baixos
e os eventos de hora do dia são muito baixos. (A partir da observação, a diferença nas prioridades do processo do sistema tem
pouco ou nenhum impacto no desempenho.) Todas as prioridades do processo do usuário são inferiores à prioridade mais baixa
do sistema. As prioridades do processo do usuário são atribuídas por um algoritmo com base na proporção recente da
quantidade de tempo de computação em relação ao tempo real consumido pelo processo.
Um processo que tem usado muito
o tempo de computação na última unidade de tempo real é atribuído a uma prioridade de usuário baixa. Porque os processos interativos são
caracterizado por baixas taxas de computação para tempo real, a resposta interativa é mantida sem
nenhum arranjo especial.
O algoritmo de escalonamento simplesmente escolhe o processo com a prioridade mais alta, escolhendo assim todos os processos do

sistema primeiro e depois os processos do usuário. A proporção de computação em tempo real é atualizada a cada segundo. Assim, todas as outras

coisas sendo iguais, os processos de usuário em loop serão programados round-robin com um quantum de 1 segundo. A ativação de um processo de

alta prioridade interromperá um processo em execução de baixa prioridade. O algoritmo de escalonamento tem um caráter de feedback negativo muito

desejável. Se um processo usar sua alta prioridade para monopolizar o computador, sua prioridade cairá. Ao mesmo tempo, se um processo de baixa

prioridade for ignorado por muito tempo, sua prioridade aumentará.

3. SISTEMA DE E/S
O sistema de I/O é dividido em dois sistemas completamente separados: o sistema de I/O de blocos e o sistema de I/O de
caracteres. Em retrospecto, os nomes deveriam ter sido ''E/S estruturada'' e ''E/S não estruturada'', respectivamente; enquanto o
termo ''E/S de bloco'' tem algum significado, ''E/S de caractere'' é um nome completamente impróprio.

Os dispositivos são caracterizados por um número de dispositivo principal, um número de dispositivo secundário e
uma classe (bloco ou caractere). Para cada classe, há uma matriz de pontos de entrada nos drivers de dispositivo. O número
do dispositivo principal é usado para indexar a matriz ao chamar o código para um driver de dispositivo específico. O número
do dispositivo secundário é passado para o driver do dispositivo como um argumento. O número menor não tem outro
significado senão aquele que lhe é atribuído pelo condutor. Normalmente, o driver usa o número menor para acessar um dos
vários dispositivos físicos idênticos.
O uso da matriz de pontos de entrada (tabela de configuração) como a única conexão entre o código do sistema e
os drivers de dispositivo é muito importante. As primeiras versões do sistema tinham uma conexão muito menos formal
com os drivers, de modo que era extremamente difícil criar sistemas configurados de maneira diferente. Agora é
possível criar novos device drivers em média em poucas horas. A tabela de configuração em
-5-

na maioria dos casos é criado automaticamente por um programa que lê a lista de peças do sistema.

3.1. Bloquear o sistema de E/S

O dispositivo de E/S do bloco de modelo consiste em blocos de memória secundária de 512 bytes cada, endereçados
aleatoriamente. Os blocos são endereçados uniformemente 0, 1, . . . até o tamanho do dispositivo. O driver do dispositivo de
bloco tem a função de emular este modelo em um dispositivo físico.
Os dispositivos de E/S de bloco são acessados por meio de uma camada de software de buffer. O sistema
mantém uma lista de buffers (normalmente entre 10 e 70), cada um atribuído a um nome de dispositivo e um endereço
de dispositivo. Este buffer pool constitui um cache de dados para os dispositivos de bloco. Em uma solicitação de
leitura, o cache é procurado pelo bloco desejado. Se o bloco for encontrado, os dados são disponibilizados ao
solicitante sem nenhuma E/S física. Se o bloco não estiver no cache, o bloco usado menos recentemente no cache é
renomeado, o driver de dispositivo correto é chamado para preencher o buffer renomeado e, em seguida, os dados são
disponibilizados. As solicitações de gravação são tratadas de maneira análoga. O buffer correto é encontrado e rotulado
novamente, se necessário. A gravação é executada simplesmente marcando o buffer como ''sujo''. A E/S física é então
adiada até que o buffer seja renomeado.
Os benefícios na redução de E/S física desse esquema são substanciais, especialmente considerando a
implementação do sistema de arquivos. Existem, no entanto, algumas desvantagens. A natureza assíncrona do
algoritmo torna quase impossível o relatório de erros e o tratamento significativo de erros do usuário. A
abordagem cavalheiresca para o tratamento de erros de E/S noUNIXsistema é parcialmente devido à natureza
assíncrona do sistema de E/S de bloco. Um segundo problema está nas gravações atrasadas. Se o sistema parar
inesperadamente, é quase certo que há muitas E/S logicamente completas, mas fisicamente incompletas, nos
buffers. Existe uma primitiva de sistema para liberar toda atividade de E/S pendente dos buffers. O uso periódico
dessa primitiva ajuda, mas não resolve o problema. Finalmente, a associatividade nos buffers pode alterar a
sequência de I/O física daquela da sequência de I/O lógica. Isso significa que há momentos em que as estruturas
de dados no disco são inconsistentes, mesmo que o software tenha o cuidado de executar a E/S na ordem
correta. Em dispositivos não aleatórios, principalmente fita magnética, as inversões de gravações podem ser
desastrosas.

3.2. Sistema de E/S de personagem

O sistema de I/O de caracteres consiste em todos os dispositivos que não se enquadram no modelo de I/O de
bloco. Isso inclui os dispositivos de caracteres "clássicos", como linhas de comunicação, fita de papel e impressoras
de linha. Também inclui fitas magnéticas e discos quando não são usados de maneira estereotipada, por exemplo,
registros físicos de 80 bytes em fita e cópias em disco de faixa por vez. Resumindo, a interface de E/S de caractere
significa ''tudo menos o bloco''. As solicitações de E/S do usuário são enviadas ao driver de dispositivo essencialmente
inalteradas. A implementação dessas solicitações, é claro, depende do driver do dispositivo. Existem diretrizes e
convenções para ajudar na implementação de certos tipos de drivers de dispositivo.

3.2.1. Drivers de disco


Os drivers de disco são implementados com uma fila de registros de transações. Cada registro
contém um sinalizador de leitura/gravação, um endereço de memória primária, um endereço de memória
secundária e uma contagem de bytes de transferência. A troca é realizada passando esse registro para o
driver do dispositivo de troca. A interface de E/S do bloco é implementada passando esses registros com
solicitações para preencher e esvaziar os buffers do sistema. A interface de E/S de caracteres para os
drivers de disco cria um registro de transação que aponta diretamente para a área do usuário. A rotina
que cria esse registro também garante que o usuário não seja trocado durante essa transação de E/S.
Assim, implementando o driver de disco geral, é possível usar o disco como um dispositivo de bloco, um
dispositivo de caractere e um dispositivo de troca.

3.2.2. listas de personagens

Dispositivos reais orientados a caracteres podem ser implementados usando o código comum para lidar com caracteres. listas.
Uma lista de caracteres é uma fila de caracteres. Uma rotina coloca um personagem em uma fila. Outro recebe um

personagem de uma fila. Também é possível perguntar quantos personagens estão atualmente em uma fila.
O armazenamento de todas as filas no sistema vem de um único pool comum. Colocando um personagem em uma fila
-6-

irá alocar espaço do pool comum e vincular o personagem à estrutura de dados que define a fila.
Obter um personagem de uma fila retorna o espaço correspondente ao pool.
Um dispositivo típico de saída de caracteres (furador de fita de papel, por exemplo) é implementado
passando caracteres do usuário para uma fila de caracteres até que um número máximo de caracteres
esteja na fila. A E/S é estimulada a iniciar assim que houver algo na fila e, uma vez iniciada, é sustentada
por interrupções de conclusão de hardware. Cada vez que há uma interrupção de conclusão, o driver
obtém o próximo caractere da fila e o envia para o hardware. O número de caracteres na fila é verificado e,
conforme a contagem cai em algum nível intermediário, um evento (o endereço da fila) é sinalizado. O
processo que está passando caracteres do usuário para a fila pode estar aguardando o evento, e
recarrega a fila ao máximo quando o evento ocorrer.
Um dispositivo de entrada de caracteres típico (por exemplo, um leitor de fita de papel) é tratado de maneira
muito semelhante.

Outra classe de dispositivos de caracteres são os terminais. Um terminal é representado por três filas de
caracteres. Existem duas filas de entrada (raw e canônica) e uma fila de saída. Personagens indo para a
saída de um terminal são tratadas por código comum exatamente como descrito acima. A principal diferença é
que também há código para interpretar o fluxo de saída como caracteres ASCII e para executar algumas traduções, por exemplo, escapes para terminais
deficientes.
Outro aspecto comum dos terminais é o código para inserir
real-
atraso de tempo após certos caracteres de controle.

A entrada nos terminais é um pouco diferente. Os personagens são coletados do terminal e colocados em
um
fila de entrada bruta. Alguma conversão de código
dependente de dispositivo e interpretação de escape são tratadas aqui. Quando uma linha é concluída na fila bruta, um
evento é sinalizado. O código que captura esse sinal copia uma linha da fila bruta para uma fila canônica, executando o
apagamento de caracteres e a edição de eliminação de linha. As solicitações de leitura do usuário nos terminais podem
ser direcionadas para as filas brutas ou canônicas.

3.2.3. Outros dispositivos de personagem

Finalmente, existem dispositivos que não se encaixam em nenhuma categoria geral. Esses dispositivos são configurados como
drivers de E/S de caracteres. Um exemplo é um driver que lê e grava na memória primária não mapeada como um dispositivo de E/S.
Alguns dispositivos são muito rápidos para serem tratados como um caractere de cada vez, mas não se encaixam no molde de E/S do
disco. Exemplos são linhas de comunicação rápidas e impressoras de linha rápida. Esses dispositivos têm seus próprios buffers ou
"pegam emprestados" os buffers de E/S do bloco por um tempo e depois os devolvem.

4. O SISTEMA DE ARQUIVOS

NoUNIXsistema, um arquivo é uma matriz (unidimensional) de bytes. Nenhuma outra estrutura de arquivos está implícita no
sistema. Os arquivos são anexados em qualquer lugar (e possivelmente se multiplicam) em uma hierarquia de diretórios. Os diretórios são
simplesmente arquivos que os usuários não podem escrever. Para uma discussão mais aprofundada sobre a visão externa de arquivos e
diretórios, consulte a Ref. 4.

OUNIXsistema de arquivos é uma estrutura de dados de disco acessada completamente por meio do sistema de E/S de blocos.
Conforme declarado anteriormente, a visão canônica de um ''disco'' é uma O primeiro bloco (endereço 0) não é usado
matriz endereçável aleatoriamente de blocos de 512 bytes. A por O segundo bloco (endereço 1) contém o so-
sistema de arquivos divide o disco em quatro regiões de auto-identificação.
chamado ''super-bloco.''
o sistema de arquivos. Ele é deixado de lado para procedimentos de inicialização.

Esse bloco, entre outras coisas, contém o tamanho do disco e os limites. Em seguida, vem a i-das outras regiões. estrutura,list,uma lista de
definições de arquivo. Cada definição de arquivo é um arquivo de 64 bytes

chamada de i-node. O deslocamento de um i-node específico dentro da i-list é chamado de i-number. O


combinação de nome do dispositivo (números principais e secundários) e i-number serve para nomear exclusivamente um
arquivo específico. Após o i-list, e até o final do disco, vêm os blocos de armazenamento gratuitos que estão disponíveis para o
conteúdo dos arquivos.
O espaço livre em um disco é mantido por uma lista encadeada de blocos de disco disponíveis. Cada bloco
nesta cadeia contém um endereço de disco do próximo bloco na cadeia. O espaço restante contém o endereço de até
50 blocos de disco também livres. Assim, com uma operação de I/O, o sistema obtém 50 blocos livres e um ponteiro
onde encontrar mais. Os algoritmos de alocação de disco são muito diretos. Como toda alocação é em blocos de
tamanho fixo e há uma contabilidade rigorosa de espaço, não há necessidade de compactação ou coleta de lixo. No
entanto, à medida que o espaço em disco se torna disperso, a latência aumenta gradualmente.
-7-

Algumas instalações optam por compactar ocasionalmente o espaço em disco para reduzir a latência.

Um i-node contém 13 endereços de disco. Os primeiros 10 desses endereços apontam diretamente para os
primeiros 10 blocos de um arquivo. Se um arquivo for maior que 10 blocos (5.120 bytes), o décimo primeiro endereço
aponta para um bloco que contém os endereços dos próximos 128 blocos do arquivo. Se o arquivo ainda for maior que
isso (70.656 bytes), o décimo segundo bloco aponta para até 128 blocos, cada um apontando para 128 blocos do
arquivo. Arquivos ainda maiores (8.459.264 bytes) usam o décimo terceiro endereço para um endereço ''triplo indireto''.
O algoritmo termina aqui com o tamanho máximo do arquivo de 1.082.201.087 bytes.
Uma hierarquia lógica de diretórios é adicionada a essa estrutura física plana simplesmente adicionando um novo tipo
de arquivo, o diretório. Um diretório é acessado exatamente como um arquivo comum. Ele contém entradas de 16 bytes que
consistem em um nome de 14 bytes e um i-number. A raiz da hierarquia está em um número i conhecido (isto é,2). A estrutura
do sistema de arquivos permite um gráfico arbitrário e direcionado de diretórios com arquivos regulares vinculados em locais
arbitrários neste gráfico. Na verdade, muito cedo UNIXsistemas utilizados tal estrutura. A administração dessa estrutura tornou-
se tão caótica que os sistemas posteriores ficaram restritos a uma árvore de diretórios. Mesmo agora, com arquivos regulares
vinculados se multiplicando em locais arbitrários na árvore, a contabilização do espaço se tornou um problema. Pode ser
necessário restringir toda a estrutura a uma árvore e permitir uma nova forma de ligação que seja subserviente à estrutura da
árvore.
O sistema de arquivos permite fácil criação, fácil remoção, fácil acesso aleatório e muito fácil alocação de
espaço. Com a maioria dos endereços físicos confinados a uma pequena seção contígua do disco, também é fácil
descarregar, restaurar e verificar a consistência do sistema de arquivos. Arquivos grandes sofrem de endereçamento
indireto, mas o cache impede a maior parte da E/S física implícita sem adicionar muita execução. As propriedades de
sobrecarga de espaço desse esquema são muito boas. Por exemplo, em um determinado sistema de arquivos, existem
25.000 arquivos contendo 130M bytes de conteúdo de arquivo de dados. A sobrecarga (i-node, blocos indiretos e quebra
do último bloco) é de cerca de 11,5 milhões de bytes. A estrutura de diretórios para suportar esses arquivos tem cerca
de 1.500 diretórios contendo 0,6 M bytes de conteúdo de diretório e cerca de 0,5 M bytes de sobrecarga no acesso aos
diretórios. Adicionado de qualquer maneira, isso resulta em menos de 10% de sobrecarga para os dados reais
armazenados. A maioria dos sistemas tem essa sobrecarga apenas em espaços em branco à direita acolchoados.

4.1. Implementação do sistema de arquivos

Como o i-node define um arquivo, a implementação do sistema de arquivos gira em torno do acesso ao i-
node. O sistema mantém uma tabela de todos os i-nodes ativos. À medida que um novo arquivo é acessado, o
sistema localiza o i-node correspondente, aloca uma entrada na tabela de i-node e lê o i-node na memória
primária. Como no cache do buffer, a entrada da tabela é considerada a versão atual do i-node. As modificações
no i-node são feitas na entrada da tabela. Quando o último acesso ao i-node desaparece, a entrada da tabela é
copiada de volta para a i-list do armazenamento secundário e a entrada da tabela é liberada.
Todas as operações de E/S em arquivos são realizadas com o auxílio da entrada da tabela de i-node
correspondente. O acesso a um arquivo é uma implementação direta dos algoritmos mencionados
anteriormente. O usuário não está ciente dos i-nodes e i-numbers. As referências ao sistema de arquivos são
feitas em termos de nomes de caminhos da árvore de diretórios. A conversão de um nome de caminho em uma
entrada de tabela de i-node também é direta. Começando em algum i-node conhecido (a raiz ou o diretório atual
de algum processo), o próximo componente do nome do caminho é pesquisado pela leitura do diretório. Isso
fornece um número i e um dispositivo implícito (o do diretório). Assim, a próxima entrada da tabela i-node pode
ser acessada. Se esse foi o último componente do nome do caminho, esse i-node é o resultado. Caso contrário,
este i-node é o diretório necessário para procurar o próximo componente do nome do caminho,
O processo do usuário acessa o sistema de arquivos com certas primitivas. Os mais comuns são
abrir,criar, ler,escrever,procurar, efechar. As estruturas de dados mantidas são mostradas na Fig. 2.
-8-

POR USUÁRIO ABERTO


TABELA DE ARQUIVOS

TROCADO
POR/USUÁRIO

ABRIR ARQUIVO
I-NODE ATIVO
MESA MESA

RESIDENTE
POR/SISTEMA

I-NODE SECUNDÁRIO
ARMAZENAR
ARQUIVO POR/SISTEMA DE ARQUIVOS

ARQUIVO
{ ALGORITMOSMAPEAMENTO }

Fig. 2—Estrutura de dados do sistema de arquivos.

No segmento de dados do sistema associado a um usuário, há espaço para alguns (geralmente entre 10 e 50) arquivos abertos.
Essa tabela de arquivo aberto consiste em ponteiros que podem ser usados para acessar entradas de tabela de i-node
correspondentes. Associado a cada um desses arquivos abertos está um ponteiro de E/S atual. Este é um deslocamento de byte
da próxima operação de leitura/gravação no arquivo. O sistema trata cada solicitação de leitura/gravação como aleatória com
uma busca implícita no ponteiro de E/S. O usuário geralmente pensa no arquivo como sequencial com o ponteiro de E/S
contando automaticamente o número de bytes que foram lidos/gravados no arquivo. O usuário pode, é claro, executar E/S
aleatória definindo o ponteiro de E/S antes das leituras/gravações.
Com o compartilhamento de arquivos, é necessário permitir que processos relacionados compartilhem um ponteiro de
E/S comum e ainda tenham ponteiros de E/S separados para processos independentes que acessam o mesmo arquivo. Com
essas duas condições, o ponteiro de E/S não pode residir na tabela i-node nem na lista de arquivos abertos para o processo.
Uma nova tabela (a tabela de arquivo aberto) foi inventada com o único propósito de conter o ponteiro de E/S. Processos que
compartilham o mesmo arquivo aberto (resultado degarfos) compartilham uma entrada de tabela de arquivo aberto comum. Uma
abertura separada do mesmo arquivo compartilhará apenas a entrada da tabela i-node, mas terá entradas distintas da tabela de
arquivos abertos.
As principais primitivas do sistema de arquivos são implementadas da seguinte maneira.abrirconverte um nome de caminho do
sistema de arquivos em uma entrada de tabela de i-node. Um ponteiro para a entrada da tabela i-node é colocado em uma entrada de
tabela de arquivo aberto recém-criada. Um ponteiro para a entrada da tabela de arquivos é colocado no segmento de dados do sistema
para o processo. criarfiprimeiro cria uma nova entrada de i-node, grava o i-number em um diretório e, em seguida, constrói a mesma
estrutura de umabrir.lereescreverbasta acessar a entrada i-node conforme descrito acima.procurar simplesmente manipula o ponteiro de
E/S. Nenhuma busca física é feita.fecharapenas libera as estruturas construídas porabrir e criar. As contagens de referência são mantidas
nas entradas da tabela de arquivo aberto e nas entradas da tabela i-node para liberar essas estruturas após a última referência
desaparecer.desvincularsimplesmente diminui a contagem do número de diretórios apontando para o i-node fornecido. Quando a última
referência a uma entrada da tabela i-node desaparece, se o i-node não tiver diretórios apontando para ele, o arquivo será removido e o i-
node será liberado. Essa remoção atrasada de arquivos evita problemas decorrentes da remoção de arquivos ativos. Um arquivo pode ser
removido enquanto ainda está aberto. O arquivo sem nome resultante desaparece quando o arquivo é fechado. Este é um método de
obtenção de arquivos temporários.

Existe um tipo de arquivo FIFO sem nome chamadocano.Implementação decanos consiste em


implícito procurars antes de cadalerouescreverpara implementar o primeiro a entrar, o primeiro a sair. Há
também verificações e sincronização para evitar que o escritor supere grosseiramente o leitor e para evitar
que o leitor ultrapasse o escritor.
-9-

4.2. Sistemas de arquivos montados

O sistema de arquivos de umUNIXo sistema começa com algum dispositivo de bloco designado formatado
conforme descrito acima para conter uma hierarquia. A raiz desta estrutura é a raiz do UNIXsistema de arquivo. Um
segundo dispositivo de bloco formatado pode ser montado em qualquer folha da hierarquia atual. Isso estende
logicamente a hierarquia atual. A implementação da montagem é trivial. Uma tabela de montagem é mantida contendo
pares de nós de folha designados e dispositivos de bloco. Ao converter um nome de caminho em um i-node, é feita uma
verificação para ver se o novo i-node é uma folha designada. Se for, o i-node da raiz do dispositivo de bloco o substitui.

A alocação de espaço para um arquivo é obtida do pool livre no dispositivo no qual o arquivo reside. Assim, um sistema de

arquivos que consiste em muitos dispositivos montados não possui um pool comum de espaço de armazenamento secundário livre.

Essa separação de espaço em diferentes dispositivos é necessária para facilitar a desmontagem de um dispositivo.

4.3. Outras funções do sistema

Há algumas outras coisas que o sistema faz para o usuário – um pouco de contabilidade, um pouco de
rastreamento/depuração e um pouco de proteção de acesso. A maioria dessas coisas não está muito bem
desenvolvida porque nosso uso do sistema na pesquisa em ciência da computação não precisa delas. Existem
alguns recursos que são perdidos em alguns aplicativos, por exemplo, melhor comunicação entre processos.
OUNIXO kernel é mais um multiplexador de E/S do que um sistema operacional completo. Isto é como deveria ser.
Devido a essa perspectiva, muitos recursos são encontrados na maioria dos outros sistemas operacionais que estão faltando
noUNIXnúcleo. Por exemplo, oUNIXkernel não suporta métodos de acesso a arquivos, disposição de arquivos, formatos de
arquivo, tamanho máximo de arquivo, spooling, linguagem de comando, registros lógicos, registros físicos, atribuição de
nomes de arquivos lógicos, nomes de arquivos lógicos, mais de um conjunto de caracteres, um console do
operador, um operador, login ou logout. Muitas dessas coisas são sintomas e não características. Muitos desses
as coisas são implementadas no software do usuário usando o kernel como uma ferramenta. Um bom exemplo disso é o com-A
linguagem mando.5Cada usuário pode ter sua própria linguagem de comando. manutenção desse código é tão fácil
como manter o código do usuário. A idéia de implementar o código do ''sistema'' com primitivas gerais do usuário
vem diretamente do MULTICS.6

Referências

1. RE Griswold e DR Hanson, ''An Overview of SL5,''SIGPLAN Avisos12(4), pp.40-50 (abril de 1977).

2. EW Dijkstra, ''Cooperating Sequential Processes'', pp. 43-112 emLinguagens de programação,


ed. F. Genuys, Academic Press, Nova York (1968).
3. JA Hawley e WB Meyer, ''MUNIX, Uma versão de multiprocessamento de UNIX,'' MS Thesis,
Naval Postgraduate School, Monterey, Cal. (1975).
4. DM Ritchie e K. Thompson, ''The UNIXSistema de compartilhamento de tempo,''Bell Sys. Tecnologia j.57(6),
pp.1905-1929 (1978).
5. SR Bourne, ''UNIXSistema de compartilhamento de tempo: The U NIXConcha,''Bell Sys. Tecnologia j.57(6), pp.1971-
1990 (1978).
6. EI Organick,ElesULTICSSistema,MIT Press, Cambridge, Mass. (1972).

Você também pode gostar