Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas

Sistemas Operacionais, Resumos de Sistemas Operacionais

Ao longo de mais de 40 anos, sistemas operacionais têm sido desenvolvidos com o propósito de tornar a utilização do computador mais eficiente e mais conveniente. Para isso, um enorme número de conceitos, abstrações, mecanismos e algoritmos foram criados e aprimorados. Este artigo é um tutorial a respeito das técnicas fundamentais empregadas nos sistemas operacionais contemporâneos. Também são discutidos aqui os sistemas operacionais distribuídos e de tempo real.

Tipologia: Resumos

2025

Compartilhado em 05/05/2025

henrique-pereira-martins
henrique-pereira-martins 🇧🇷

1 documento

1 / 33

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
Sistemas Operacionais
Rômulo Silva de Oliveira 1
Alexandre da Silva Carissimi 2
Simão Sirineo Toscani 3
Resumo: Ao longo de mais de 40 anos, sistemas operacionais têm sido
desenvolvidos com o propósito de tornar a utilização do computador mais
eficiente e mais conveniente. Para isso, um enorme número de conceitos,
abstrações, mecanismos e algoritmos foram criados e aprimorados. Este artigo
é um tutorial a respeito das técnicas fundamentais empregadas nos sistemas
operacionais contemporâneos. Também são discutidos aqui os sistemas
operacionais distribuídos e de tempo real.
Abstract: For more than 40 years, operating systems have been developed
with the goal of making the utilization of computers more efficient and more
comfortable. A huge number of concepts, abstractions, mechanisms and
algorithms were created and improved. This paper is a tutorial on the
fundamental techniques applied in the construction of contemporary operating
systems. We also discuss distributed operating systems and real-time operating
systems.
1 Dep. de Automação e Sistemas, UFSC, Caixa Postal 476, 88040-900, Florianópolis-SC
romulo@das.ufsc.br
2 Instituto de Informática, UFRGS, Caixa Postal 15064, 91501-970, Porto Alegre-RS
asc@inf.ufrgs.br
3 Instituto de Informática, UFRGS, Caixa Postal 15064, 91501-970, Porto Alegre-RS
simao@inf.ufrgs.br
Revista de Informática Teórica e Aplicada - RITA - Volume VIII, Número 3, dezembro de 2001
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21

Pré-visualização parcial do texto

Baixe Sistemas Operacionais e outras Resumos em PDF para Sistemas Operacionais, somente na Docsity!

Sistemas Operacionais

Rômulo Silva de Oliveira 1

Alexandre da Silva Carissimi 2

Simão Sirineo Toscani 3

Resumo : Ao longo de mais de 40 anos, sistemas operacionais têm sido desenvolvidos com o propósito de tornar a utilização do computador mais eficiente e mais conveniente. Para isso, um enorme número de conceitos, abstrações, mecanismos e algoritmos foram criados e aprimorados. Este artigo é um tutorial a respeito das técnicas fundamentais empregadas nos sistemas operacionais contemporâneos. Também são discutidos aqui os sistemas operacionais distribuídos e de tempo real.

Abstract : For more than 40 years, operating systems have been developed with the goal of making the utilization of computers more efficient and more comfortable. A huge number of concepts, abstractions, mechanisms and algorithms were created and improved. This paper is a tutorial on the fundamental techniques applied in the construction of contemporary operating systems. We also discuss distributed operating systems and real-time operating systems.

1 Dep. de Automação e Sistemas, UFSC, Caixa Postal 476, 88040-900, Florianópolis-SC romulo@das.ufsc.br 2 Instituto de Informática, UFRGS, Caixa Postal 15064, 91501-970, Porto Alegre-RS asc@inf.ufrgs.br 3 Instituto de Informática, UFRGS, Caixa Postal 15064, 91501-970, Porto Alegre-RS simao@inf.ufrgs.br

Sistemas Operacionais

1 Introdução

O sistema operacional procura tornar a utilização do computador mais eficiente e mais conveniente. A utilização mais eficiente busca um maior retorno no investimento feito no hardware, significa mais trabalho obtido do mesmo hardware. Uma utilização mais conveniente vai diminuir o tempo necessário para a construção e utilização dos programas. Um enorme número de conceitos, abstrações, mecanismos e algoritmos foram criados e aprimorados ao longo dos últimos 40 anos. Este artigo é um tutorial a respeito das técnicas fundamentais empregadas nos sistemas operacionais contemporâneos. O artigo baseia-se, em grande parte, no texto do livro “Sistemas Operacionais”, dos mesmos autores [1].

Para atingir os objetivos propostos, o sistema operacional oferece diversos tipos de serviços. Todo sistema operacional oferece meios para que um programa seja carregado na memória principal e executado. Talvez o serviço mais importante oferecido seja o que permite a utilização de arquivos e diretórios. Também o acesso aos periféricos é feito através do sistema operacional. À medida que diversos usuários compartilham o computador, passa a ser interessante saber quanto de quais recursos cada usuário necessita. Diversas informações sobre o estado do sistema são mantidas. Nessa categoria, temos a hora e a data correntes, a lista de usuários utilizando o computador no momento, a versão do sistema operacional em uso. Cabe também ao sistema operacional garantir que cada usuário possa trabalhar sem sofrer interferência danosa dos demais.

Os programas solicitam serviços ao sistema operacional através das chamadas de sistema. Elas são semelhantes às chamadas de sub-rotinas. Entretanto, enquanto as chamadas de sub-rotinas são transferências para procedimentos normais do programa, as chamadas de sistema transferem a execução para o sistema operacional. Através de parâmetros, o programa informa exatamente o que necessita. O retorno da chamada de sistema, assim como o retorno de uma sub-rotina, faz com que a execução do programa seja retomada a partir da instrução que segue a chamada. Para o programador assembly (linguagem de montagem), as chamadas de sistema são bastante visíveis. Por exemplo, o conhecido "INT 21H" no MS- DOS. Em uma linguagem de alto nível, elas ficam escondidas dentro da biblioteca utilizada pelo compilador. O programador chama sub-rotinas de uma biblioteca, e estas chamam o sistema. Por exemplo, qualquer função da biblioteca que acesse o terminal (como printf()na linguagem C) exige uma chamada de sistema.

A parte do sistema operacional responsável por implementar as chamadas de sistema é normalmente chamada de núcleo ou kernel. Os principais componentes do kernel de qualquer sistema operacional são a gerência de processador , a gerência de memória , o sistema de arquivos e a gerência de entrada e saída. Cada um desses componentes será descrito nas próximas seções.

Os programas de sistema , algumas vezes chamados de utilitários , são programas normais executados fora do kernel do sistema operacional. Eles utilizam as mesmas chamadas de sistema disponíveis aos demais programas. Esses programas implementam

Sistemas Operacionais

do editor de textos. Todos os processos utilizam uma mesma cópia do código do editor de textos, porém cada processo trabalha sobre uma área de variáveis privativa.

2.1 Propriedades dos Processos

Processos são criados e destruídos. O momento e a forma pela qual eles são criados e destruídos depende do sistema operacional em consideração. Alguns sistemas trabalham com um número fixo de processos. A forma mais flexível de operação é permitir que processos possam ser criados livremente, através de chamadas de sistema. Além da chamada de sistema "cria processo", serão necessárias chamadas para "autodestruição do processo" e também para "eliminação de outro processo".

A maioria dos processos de um sistema executam programas dos usuários. Entretanto, alguns podem realizar tarefas do sistema. São processos do sistema ( daemons ), não dos usuários. Por exemplo, para evitar conflitos na utilização da impressora, muitos sistemas trabalham com uma técnica chamada spooling. Para imprimir um arquivo, o processo de usuário deve colocá-lo em um diretório especial. Um processo do sistema copia os arquivos desse diretório para a impressora. Dessa forma, um processo de usuário nunca precisa esperar a impressora ficar livre, uma vez que ele não envia os dados para a impressora, mas sim para o disco. O processo que envia os dados para a impressora não está associado a nenhum usuário. É um processo do próprio sistema operacional.

Alguns sistemas suportam o conceito de grupo de processos. Por exemplo, todos os processos associados a um mesmo terminal podem formar um grupo. Em muitos sistemas os processos são criados por outros processos, através de uma chamada de sistema. Nesse caso, é possível definir uma hierarquia de processos. O processo que faz a chamada de sistema é chamado de processo pai. O processo criado é chamado de processo filho. Um mesmo processo pai pode estar associado a vários processos filhos. Os processos filhos podem criar outros processos. Essa situação é facilmente representada através de uma árvore.

A descrição do funcionamento da multiprogramação mostrou diversos momentos pelos quais passa o processo. A partir dessa descrição, pode-se estabelecer os estados possíveis para um processo. Após ser criado, o processo entra em um ciclo de processador. Entretanto, o processador poderá estar ocupado com outro processo, e ele deverá esperar. Diversos processos podem estar nesse mesmo estado. Em máquinas multiprocessadoras existem diversos processadores. Nesse caso, diversos processos executam ao mesmo tempo. Porém, essa não é a situação mais comum. Vamos supor que existe um único processador no computador. Nesse caso, é necessário manter uma fila com os processos aptos a ganhar o processador. Essa fila é chamada " fila de aptos " ( ready queue ).

A Figura 1 mostra o diagrama de estados de um processo. No estado executando , um processo pode fazer chamadas de sistema. Até a chamada de sistema ser atendida, o processo não pode continuar sua execução. Ele fica bloqueado e só volta a disputar o processador após a conclusão da chamada. Enquanto espera pelo término da chamada de sistema, o processo está no estado bloqueado ( blocked ).

Sistemas Operacionais

A mudança de estado de qualquer processo é iniciada por um evento. Esse evento aciona o sistema operacional, que então altera o estado de um ou mais processos. A transição do estado executando para bloqueado é feita através de uma chamada de sistema. Uma chamada de sistema é necessariamente feita pelo processo no estado executando. Ele fica no estado bloqueado até o atendimento. Com isso, o processador fica livre. O sistema operacional então seleciona um processo da fila de aptos para receber o processador. O processo selecionado passa do estado de apto para o estado executando. O módulo do sistema operacional que faz essa seleção é chamado escalonador ( scheduler ).

Outro tipo de evento corresponde às interrupções do hardware. Elas, em geral, informam o término de uma operação de E/S. Isso significa que um processo bloqueado será liberado. O processo liberado passa do estado de bloqueado para o estado de apto. Ele volta a disputar o processador com os demais da fila de aptos.

Alguns outros caminhos também são possíveis no grafo de estados. A destruição do processo pode ser em função de uma chamada de sistema ou por solicitação do próprio processo. Entretanto, alguns sistemas podem resolver abortar o processo, caso um erro crítico tenha acontecido durante uma operação de E/S. Nesse caso, passa a existir um caminho do estado bloqueado para a destruição. Algumas chamadas de sistema são muito rápidas. Por exemplo, leitura da hora atual. Não existe acesso a periférico, mas apenas consulta às variáveis do próprio sistema operacional. Nesse caso, o processo não precisa voltar para a fila de aptos. Ele simplesmente retorna para a execução após a conclusão da chamada. Muitos sistemas procuram evitar que um único processo monopolize a ocupação do processador. Se um processo está há muito tempo no processador, ele volta para o fim da fila de aptos. Um novo processo da fila de aptos ganha o processador. Esse mecanismo cria um caminho entre o estado executando e o estado apto.

C r i a ç ã o

A p t o E x e c u t a n d o D e s t r u i ç ã o

B l o q u e a d o

R e t o r n o I m e d ia t o

L im ite d e T e m p o

E r r o C r í t i c o

C h a m a d a S is t e m a I n t e r r u p ç ã o d e H a r d w a r e

S e le ç ã o

Figura 1. Diagrama de estados de um processo O sistema operacional é responsável por implementar uma proteção apropriada para o sistema. Para isso é necessário o auxílio da arquitetura do processador (hardware). A forma usual é definir dois modos de operação ou níveis de proteção para o processador. Pode-se chamá-los de modo usuário e modo supervisor. Quando o processador está em modo supervisor, não existem restrições, e qualquer instrução pode ser executada. Em modo

Sistemas Operacionais

junto com os demais. Em outras palavras, o processo foi criado. A base da multiprogramação é o compartilhamento do processador entre os processos. Em um sistema multiprogramado, é necessário interromper processos para continuá-los mais tarde. Essa tarefa é chamada de chaveamento de processo , ou chaveamento de contexto de execução. O local usado para salvar o contexto de execução de um processo é o seu próprio bloco descritor.

Um processo é uma abstração que reúne uma série de atributos como espaço de endereçamento, descritores de arquivos abertos, permissões de acesso, quotas, etc. Um processo possui ainda áreas de código, dados e pilha de execução. Também é associado ao processo um fluxo de execução. Por sua vez, uma thread nada mais é que um fluxo de execução. Na maior parte das vezes, cada processo é formado por um conjunto de recursos mais uma única thread.

A idéia de multithreading é associar vários fluxos de execução (várias threads ) a um único processo. Em determinadas aplicações, é conveniente disparar várias threads dentro do mesmo processo (programação concorrente). É importante notar que as threads existem no interior de um processo, compartilhando entre elas os recursos do processo, como o espaço de endereçamento (código e dados). Devido a essa característica, a gerência de threads (criação, destruição, troca de contexto) é "mais leve" quando comparada com processos. Threads são muitas vezes chamadas de processos leves.

2.3 Algoritmos de Escalonamento

No escalonamento do processador é decidido qual processo será executado a seguir. Na escolha de um algoritmo de escalonamento, utiliza-se como critério básico o objetivo de aumentar a produtividade do sistema e, ao mesmo tempo, diminuir o tempo de resposta percebido pelos usuários. Esses dois objetivos podem tornar-se conflitantes em determinadas situações. Variância elevada significa que a maioria dos processos recebe um serviço (tempo de processador) satisfatório, enquanto alguns são bastante prejudicados. Provavelmente, será melhor sacrificar o tempo médio de resposta para homogeneizar a qualidade do serviço que os processos recebem.

Quando os processos de um sistema possuem diferentes prioridades, essa prioridade pode ser utilizada para decidir qual processo é executado a seguir. Considere o cenário no qual números menores indicam processos mais importantes. Um processo com prioridade 5 (vamos chamá-lo P5) está executando. Nesse momento, termina o acesso a disco de um processo com prioridade 2 (P2), e ele está pronto para iniciar um ciclo de processador. Nessa situação, o sistema pode comportar-se de duas formas distintas. O processo que chega na fila do processador respeita o ciclo de processador em andamento, ou seja, o P2 será inserido normalmente na fila. O processo em execução somente libera o processador no final do ciclo de processador. Temos nesse caso a " prioridade não-preemptiva ". O processo P2, por ser mais importante que o processo em execução, recebe o processador imediatamente. O processo P5 é inserido na fila conforme a sua prioridade. Essa solução é conhecida como " prioridade preemptiva ".

Sistemas Operacionais

No método fatia de tempo , também conhecido como round-robin , cada processo recebe uma fatia de tempo do processador ( quantum ). Se o processo realizar uma chamada de sistema e ficar bloqueado antes do término da sua fatia, simplesmente o próximo processo da fila recebe uma fatia integral e inicia sua execução. Se terminar a fatia de tempo do processo em execução, ele perde o processador e volta para o fim da fila. Nesse algoritmo não é possível postergação indefinida (ficar esperando para sempre), pois processos sempre entram no fim da fila. Um problema é definir o tamanho da fatia de tempo. É preciso levar em conta que o chaveamento entre processos não é instantâneo. Se a fatia de tempo for muito pequena, esse custo cresce em termos relativos. Por outro lado, uma fatia de tempo muito grande destrói a aparência de paralelismo na execução dos processos.

Em um mesmo sistema, normalmente convivem diversos tipos de processos. Por exemplo, em um Centro de Processamento de Dados, os processos da produção (folha de pagamentos, etc) podem ser disparados em background (execução sem interação com o usuário), enquanto os processos do desenvolvimento interagem com os programadores (execução em foreground ). É possível construir um sistema no qual existem múltiplas filas , uma fila de processador para cada tipo de processo. Com múltiplas filas, o tipo do processo define a fila na qual ele é inserido, e o processo sempre volta para a mesma fila. Quando o processo pode mudar de fila durante a sua execução, temos múltiplas filas com realimentação.

3 Gerência da Memória

Na multiprogramação, diversos processos são executados simultaneamente, através da divisão do tempo do processador. Para que o chaveamento entre eles seja rápido, esses processos devem estar na memória, prontos para executar. É função da gerência de memória do sistema operacional prover os mecanismos necessários para que os diversos processos compartilhem a memória de forma segura e eficiente. A técnica particular que determinado sistema operacional emprega depende, entre outras coisas, de o que a arquitetura do computador em questão suporta. Em [2], [3], [4] e [5], podem ser encontradas as descrições de algumas soluções empregadas em sistemas operacionais específicos. Uma excelente descrição de arquiteturas contemporâneas pode ser encontrada em [6] e [7].

A memória lógica de um processo é aquela que o processo enxerga, ou seja, aquela que o processo é capaz de acessar. Os endereços manipulados pelo processo são endereços lógicos. Em outras palavras, as instruções de máquina de um processo especificam endereços lógicos. Por exemplo, um processo executando um programa escrito na linguagem C manipula variáveis tipo pointer. Essas variáveis contêm endereços lógicos. Em geral, cada processo possui a sua memória lógica, que é independente da memória lógica dos outros processos. A memória física é aquela implementada pelos circuitos integrados de memória, pela eletrônica do computador. O endereço físico é aquele que vai para a memória física, ou seja, é usado para endereçar os circuitos integrados de memória.

Sistemas Operacionais

página física correspondente. Para que o programa execute corretamente, é necessário transformar o endereço lógico especificado em cada instrução no endereço físico correspondente. Isso é feito com o auxílio da tabela de páginas.

O endereço lógico gerado é inicialmente dividido em duas partes: um número de página lógica e um deslocamento dentro da página. O número da página lógica é usado como índice no acesso à tabela de páginas. Cada entrada da tabela de páginas possui o mapeamento de página lógica para página física. Já o deslocamento do byte dentro da página física será o mesmo deslocamento desse byte dentro da página lógica, pois cada página lógica é carregada exatamente em uma página física. Basta juntar o número de página física obtido na tabela de páginas com o deslocamento já presente no endereço lógico para obter-se o endereço físico do byte em questão. A Figura 2 mostra como o endereço lógico do byte Y2 é transformado no endereço físico correspondente.

Memória Lógica Memória Física 000 00 000 01 000 10 000 11 001 00 001 01 001 10 001 11 010 00 010 01 010 10 010 11

000 00 000 01 000 10 000 11 001 00 001 01 001 10 001 11 010 00 010 01 010 10 010 11

100 00 100 01 100 10 100 11

011 00 011 01 011 10 011 11

101 00 101 01 101 10 101 11

X X X X Y Y Y Y Z Z Z Z

X X X X

Y Y Y Y

Z Z Z Z

Tabela de Páginas Pág.Lógica Pág.Física 000 001 010

010 101 000

End.Lógico de Y

001

End.Físico de Y

01 101 01

Figura 2. Mecanismo básico de paginação Como a unidade de alocação é a página, um processo sempre ocupa um número inteiro de páginas físicas, introduzindo assim uma fragmentação interna. A gerência de memória deve manter controle das áreas ainda livres na memória. Um aspecto importante da paginação é a forma como a tabela de páginas é implementada. Observe que ela deve ser

Sistemas Operacionais

consultada a cada acesso à memória. Uma solução é manter a tabela de páginas na própria memória. O problema desse mecanismo é que agora cada acesso que um processo faz à memória lógica transforma-se em dois acessos à memória física. Uma forma de reduzir o tempo de acesso à memória no esquema anterior é adicionar uma memória cache especial que vai manter as entradas da tabela de páginas mais recentemente utilizadas. Essa memória cache interna à MMU é chamada normalmente de Translation Lookaside Buffer ( TLB ).

Na prática, as tabelas de páginas possuem um tamanho variável, ajustado à necessidade de cada processo. Ocorre que, se as tabelas puderem ter qualquer tamanho, então teremos fragmentação externa novamente (a maior razão para usar paginação foi a eliminação da fragmentação externa). Para evitar isso, são usadas tabelas de páginas com dois níveis. As tabelas de páginas crescem de pedaço em pedaço, e uma tabela auxiliar chamada diretório mantém o endereço de cada pedaço. Para evitar a fragmentação externa, cada pedaço da tabela de páginas deve ter um número inteiro de páginas físicas, mantendo assim toda a alocação de memória física em termos de páginas, não importando a sua finalidade. Entradas desnecessárias em cada pedaço são marcadas como inválidas. É importante destacar que a implementação de paginação descrita aqui não é a única possível. Por exemplo, um esquema chamado Tabela de Páginas Invertida ( Inverted Page Table ) é usado em alguns processadores, como o PowerPC [6].

3.2 Segmentação

O conceito de página, fundamental para a paginação, é uma criação do sistema operacional para facilitar a gerência da memória. Programadores e compiladores não enxergam a memória lógica dividida em páginas, mas sim em segmentos. Uma divisão típica descreve um programa em termos de quatro segmentos: código, dados alocados estaticamente, dados alocados dinamicamente e pilha de execução. É possível orientar a gerência de memória para suportar diretamente o conceito de segmento. Uma posição da memória lógica passa a ser endereçada por um número de segmento e um deslocamento em relação ao início do seu segmento. Em tempo de carga, cada segmento é copiado para a memória física, e uma tabela de segmentos é construída. Essa tabela informa, para cada segmento, qual o endereço da memória física onde ele foi colocado e qual o seu tamanho.

Uma diferença importante é que a segmentação não apresenta fragmentação interna, visto que a quantidade exata de memória necessária é alocada para cada segmento. Entretanto, como áreas contíguas de diferentes tamanhos devem ser alocadas, temos a ocorrência de fragmentação externa. Uma solução possível é paginar cada segmento. Na segmentação paginada o espaço lógico é formado por segmentos, e cada segmento é dividido em páginas lógicas. O grande atrativo da segmentação está na facilidade para compartilhar memória. Cada segmento representa uma parte específica do programa, podendo ou não ser compartilhado. Segmentos tendem a ser homogêneos nesse sentido. Isto é, todo o segmento pode ser compartilhado, ou nenhuma parte do segmento pode ser compartilhada.

Sistemas Operacionais

  • Uma operação de leitura do disco deve ser solicitada, indicando o endereço da página lógica no disco e o endereço da página física alocada.

A gerência do processador pode então selecionar outro processo para executar. Quando a operação de leitura do disco for concluída, a gerência de memória deve concluir o atendimento à falta de página realizando as seguintes ações:

  • A tabela de páginas do processo é corrigida para indicar que a página lógica causadora da interrupção é agora válida e está na página física que fora alocada antes;
  • O descritor do processo é retirado da "fila dos processos esperando página lógica" e colocado na fila do processador.

Observe que o processo deverá repetir a instrução que causou a falta de página (esta instrução não foi executada devido a falta de página). Repetir uma instrução após a carga da página faltante requer uma arquitetura de computador adequada, especialmente projetada para suportar memória virtual. Nem todos os processadores suportam esse mecanismo.

A parte do sistema operacional responsável por carregar páginas do disco para a memória principal é muitas vezes chamada de Pager. É importante fazer distinção entre o Pager e o Swapper. O Pager carrega uma página específica de um processo e está associado com o mecanismo de memória virtual. O Swapper carrega sempre um programa inteiro do disco para a memória e vice-versa, estando associado com o mecanismo chamado swapping.

À medida que processos vão sendo carregados para a memória, é possível que todas as páginas físicas acabem ocupadas. Nesse caso, para atender à falta de página, será necessário antes liberar uma página física ocupada. Isso significa escolher uma página lógica que está na memória, copiar seu conteúdo de volta para o disco e marcar a respectiva página como inválida na tabela de páginas do seu processo. A página escolhida para ser copiada de volta ao disco é chamada de página vítima.

Vários bits auxiliares são adicionados à tabela de páginas, com o objetivo de facilitar a implementação do mecanismo de substituição de páginas. Embora a existência de tais bits não seja absolutamente necessária, eles tornam o mecanismo mais simples e eficiente. O bit de sujeira ( dirty bit ) indica se uma página foi alterada durante a execução do processo. O bit de referência ( reference bit ) indica se uma página foi acessada pelo processo. O bit tranca ( lock bit ) serve para o sistema operacional "trancar" uma página lógica na memória física.

Um algoritmo baseado em bit de referência é a segunda chance. Nesse caso, a gerência de memória considera que todas as páginas lógicas presentes na memória formam uma lista circular. Um apontador percorre a lista circular formada por todas as páginas e indica qual a próxima página a ser usada como vítima. O algoritmo verifica o bit de referência da página indicada pelo apontador. Caso esse bit esteja desligado, essa página é efetivamente escolhida como vítima, e o apontador avança uma posição na lista circular. Caso o bit de referência da página apontada esteja ligado, o bit de referência é desligado, e ela recebe uma segunda chance. O apontador avança uma posição na lista circular, e o procedimento é repetido para a próxima página.

Sistemas Operacionais

O tratamento de uma falta de página é várias ordens de grandeza mais lento que um acesso normal à memória. Quando um processo possui um número muito pequeno de páginas físicas para executar, a sua taxa de falta de páginas aumenta. À medida que o número de páginas físicas diminui, a taxa de falta de páginas aumenta de tal forma que o processo pára de realizar qualquer trabalho útil. Nesse momento está ocorrendo thrashing.

4 Sistema de Arquivos

O sistema de arquivos é a parte do sistema operacional mais visível para os usuários. Durante o tempo todo, usuários manipulam arquivos contendo textos, planilhas, desenhos, figuras, jogos, etc. Esse fato exige que o sistema de arquivos apresente uma interface coerente e simples, fácil de usar. Ao mesmo tempo, arquivos são normalmente implementados a partir de discos magnéticos. Como um acesso a disco demora cerca de 100000 vezes mais tempo do que um acesso à memória principal, são necessárias estruturas de dados e algoritmos que otimizem os acessos a disco gerados pela manipulação de arquivos. É importante observar que sistemas de arquivos implementam um recurso em software que não existe no hardware. O hardware oferece simplesmente espaço em disco, na forma de setores que podem ser acessados individualmente, em uma ordem aleatória. O conceito de arquivo, muito mais útil que o simples espaço em disco, é uma abstração criada pelo sistema operacional. Nesse caso, temos o sistema operacional criando um recurso lógico a partir dos recursos físicos existentes no sistema computacional.

Existe ampla literatura que descreve os sistemas de arquivos dos sistemas operacionais mais populares. O sistema de arquivos do UNIX System V release 2 é descrito em [2]. Em [3] também é descrita uma solução específica, utilizada no sistema operacional Xinu. A implementação utilizada no Minix é descrita em [4], inclusive com o código fonte. Em [5] é feita uma excelente descrição de diversos sistemas de arquivos utilizados no mundo UNIX. Entre eles, System V release 4, Fast File System de Berkeley, Network File System da Sun, Remote File Sharing da AT&T, Andrew File System da Carnegie-Mellon University e muitos outros.

Arquivos são recipientes que contêm dados. Cada arquivo contém dados que um usuário, por alguma razão, resolveu colocar juntos no mesmo arquivo. Diretórios são conjuntos de referências a arquivos. Os diretórios permitem-nos separar os arquivos em grupos, facilitando sua localização e manuseio. Existem situações nas quais é importante visualizar um único disco físico como se fossem vários. Por exemplo, o mesmo disco físico pode ser particionado em dois discos lógicos ou partições. É possível, por exemplo, manter todos os arquivos do sistema operacional (interpretador de comandos, compiladores, etc.) em uma partição e todos os arquivos de usuários na outra partição. No momento de fazer uma cópia de segurança ( back-up ) em fita ou CD, o particionamento do disco facilita a cópia dos arquivos do sistema e dos usuários para fitas diferentes.

Cada arquivo é identificado por um nome, o qual permite que o usuário faça referências a ele. Além do nome, cada arquivo possui uma série de outros atributos que são

Sistemas Operacionais

Enquanto um arquivo está sendo acessado, o seu descritor de arquivo é constantemente necessário. Entre outras coisas, o descritor é acessado a cada operação de escrita ou leitura para determinar a localização no disco dos dados a serem escritos ou lidos. Para tornar mais rápido o acesso aos arquivos, o sistema de arquivos mantém na memória uma tabela contendo todos os descritores dos arquivos em uso.

A maioria dos sistemas operacionais exige que os próprios programas informem quando um arquivo entra em uso e quando ele não é mais necessário. Para tanto, existem as chamadas de sistema open e close. Um programa deve abrir o arquivo antes de poder acessar seu conteúdo. Isso é feito passando o nome do arquivo como parâmetro através da chamada de sistema open. Também é usual passar como parâmetro o tipo de acesso que será feito, isto é, apenas leitura ("READONLY" ou "RO") ou leitura e escrita ("READWRITE" ou "RW").

O sistema de arquivos utiliza uma Tabela dos Descritores de Arquivos Abertos (TDAA) para manter em memória os descritores dos arquivos abertos. A TDAA mantém informações relativas aos arquivos abertos por todos os processos no sistema. Cada entrada armazena uma cópia do descritor do arquivo mantido em disco, assim como algumas informações adicionais, necessárias apenas enquanto o arquivo está aberto. Por exemplo, número de processos utilizando o arquivo no momento.

Quando o processo executando faz uma chamada de sistema open, o sistema de arquivos realiza as seguintes tarefas:

  • Localiza no disco o descritor do arquivo cujo nome foi fornecido. Isso é feito através de uma pesquisa nos diretórios da partição. Essa operação é muitas vezes chamada de lookup. Não se trata de uma chamada de sistema, mas sim de uma operação interna do sistema de arquivos.
  • Verifica se o arquivo solicitado já se encontra aberto. Isso é feito através de uma pesquisa na TDAA. A maioria dos sistemas operacionais mantém uma estrutura tipo tabela de dispersão ( hash table ) para localizar a entrada na TDAA correspondente a um determinado arquivo.
  • Caso o arquivo ainda não esteja aberto, aloca uma entrada livre na tabela TDAA e copia o descritor do arquivo que está no disco para a entrada alocada na TDAA.
  • Uma vez que o descritor do arquivo foi copiado para a memória, verifica se o processo em questão tem o direito de abrir o arquivo conforme solicitado. Isso é feito através de uma consulta aos direitos de acesso que estão armazenados no próprio descritor do arquivo.
  • A partir desse momento, o arquivo está aberto e pode ser acessado. Quando um processo realiza a chamada de sistema close, o número de processos utilizando o arquivo em questão é decrementado na sua respectiva entrada da TDAA. Quando esse número chega a zero, o descritor do arquivo é atualizado em disco e a entrada da TDAA liberada.

Sistemas Operacionais

Existem informações diretamente associadas com o processo que acessa o arquivo e não podem ser mantidas na TDAA pois, como vários processos acessam o mesmo arquivo, elas possuirão valores diferentes. A solução típica é criar, para cada processo, uma Tabela de Arquivos Abertos por Processo (TAAP). Cada processo possui a sua TAAP. Cada entrada ocupada na TAAP corresponde a um arquivo aberto pelo processo correspondente. No mínimo, a TAAP contém em cada entrada as seguintes informações: posição corrente no arquivo, tipo de acesso autorizado (apenas leitura ou leitura e escrita), apontador para a entrada correspondente na TDAA.

As operações de leitura e de escrita são realizadas através das chamadas de sistema read e write. O processo especifica o handle do arquivo a ser acessado, o qual aponta o respectivo descritor na memória. Existem duas funções importantes que o sistema de arquivos deve realizar na implementação das chamadas read e write: a montagem e desmontagem de blocos lógicos e a localização dos blocos lógicos no disco.

A maneira como o mapeamento entre blocos lógicos e blocos físicos é realizada depende de como é mantida a informação "onde o arquivo está no disco". A alocação indexada é capaz de resolver o problema do crescimento dos arquivos ao mesmo tempo que permite o acesso relativo. Na alocação indexada, cada arquivo possui uma tabela de índices. Cada entrada da tabela de índices contém o endereço de um dos blocos físicos que formam o arquivo. Um acesso relativo pode ser facilmente realizado através de uma consulta à tabela de índices. Para que esse acesso seja rápido, a tabela de índices é normalmente mantida na memória principal enquanto o arquivo estiver aberto. Uma forma conveniente é manter a tabela de índices dentro do próprio descritor do arquivo. Quando o arquivo é aberto, seu descritor de arquivo é copiado para a memória principal. Nesse caso, a sua tabela de índices também estaria sendo copiada.

A solução típica para compatibilizar uma maioria de arquivos pequenos com um tamanho máximo de arquivo satisfatório é empregar níveis de indireção na indexação. Por exemplo, suponha que o descritor de arquivos contém uma tabela de índices com 13 entradas. As primeiras 10 entradas (numeradas de 0 a 9) apontam para blocos de dados do arquivo, permitindo o acesso aos primeiros 40 Kbytes de cada arquivo (supondo blocos de 4 Kbytes). Eles são chamados de apontadores diretos.

A entrada 10 da tabela não aponta para um bloco de dados do arquivo, mas sim para um bloco que contém apontadores para blocos de dados. Supondo que os números de blocos físicos ocupem 4 bytes, um bloco de 4 Kbytes é capaz de armazenar 1024 apontadores. Como cada apontador representa um bloco físico, temos que um único apontador indireto na tabela de índices permite o acesso a 4 Mbytes de dados do arquivo. A entrada 11 da tabela contém um apontador duplamente indireto , o que permite o acesso a até 4 Gbytes de dados. Para suportar arquivos realmente grandes, é usada a entrada 12, a qual contém um apontador triplamente indireto , o que permite indexar um total de 4 Tbytes (terabytes).

Sistemas Operacionais

forma que os usuários também possam criar livremente os seus próprios subdiretórios. O resultado é um sistema de diretórios organizado na forma de árvore. Em um sistema de diretórios organizado na forma de árvore, qualquer arquivo ou subdiretório pode ser identificado de forma não ambígua através do caminho ( pathname ) para atingi-lo a partir da raiz da árvore. Utilizando a notação do sistema operacional UNIX, "/" representa a raiz da árvore. A subárvore pertencente ao usuário João inicia no subdiretório "/usr/joao". O usuário João possui um total de três arquivos: "/usr/joao/teste", "/usr/joao/so/trab1" e "/usr/joao/so/trab2". Facilmente, a árvore de diretórios cresce até uma altura tal que passa a ser desconfortável fornecer sempre o caminho completo até cada arquivo ou diretório. Ao mesmo tempo, a cada momento, um usuário tipicamente manipula apenas arquivos de um dado diretório. O conceito de diretório corrente facilita a identificação dos arquivos.

Uma questão importante é como especificar, no nome do arquivo, qual a partição de disco na qual ele se encontra. Uma possibilidade é preceder o caminho absoluto com uma identificação da partição em questão. Por exemplo, "C:\fontes\prog.c" indica, no MSDOS, o programa cujo caminho absoluto é "\fontes\prog.c" a partir da raiz da partição identificada por "C:". Uma forma alternativa é usada no UNIX. Uma partição é escolhida como principal. O diretório raiz do sistema de diretórios corresponde ao diretório raiz dessa partição principal ou partição de root. Todas as demais partições são montadas em subdiretórios dessa partição principal. Através de um comando específico, o administrador do sistema faz com que um dado subdiretório permita o acesso ao diretório raiz da árvore sendo montada.

A forma mais simples de implementar diretórios e subdiretórios é considerá-los como arquivos especiais, cujo conteúdo é manipulado pelo próprio sistema operacional. Dessa forma, todo o mecanismo de alocação, liberação e localização de blocos físicos no disco, disponível para arquivos, também é usado para os diretórios.

Quando diretórios são implementados como conjuntos de descritores de arquivos , o conteúdo de um diretório corresponde aos descritores dos arquivos. Nesse caso, o nome do arquivo ou subdiretório faz parte do seu descritor. Nesse esquema, após a leitura do diretório, o sistema de arquivos já possui na memória todas as informações necessárias para procurar pelo nome do arquivo buscado e, caso encontre, acessar o seu conteúdo. Por outro lado, os nomes estão fortemente vinculados aos descritores de arquivos. Isso impede, por exemplo, que um mesmo arquivo possua mais de um nome. Além disso, diretórios tendem a ser maiores e estão mais sujeitos a inconsistências, pois informações importantes estão espalhadas por todo o disco.

Outra possibilidade é separar um conjunto de blocos da partição para armazenar exclusivamente os descritores de arquivos e de subdiretórios. Esse conjunto de blocos forma um vetor de descritores , no qual cada descritor pode ser perfeitamente identificado pelo número da partição e pela posição nesse vetor. Essa estrutura de dados forma o que é normalmente conhecido como um flat file system (na terminologia do UNIX). A estrutura em árvore é criada a partir de alguns arquivos do sistema flat (que na verdade atuam como subdiretórios), cada um organizado internamente como uma tabela contendo nomes e respectivos endereços no vetor de descritores. Dessa forma, para percorrer um caminho na

Sistemas Operacionais

árvore dos diretórios, é necessário abrir o subdiretório, procurar o nome desejado, pegar o endereço flat associado e ler o respectivo descritor de arquivo do vetor de descritores. Esse procedimento deve ser repetido para cada nome presente no caminho.

Cada partição é autocontida no sentido de que todas as informações necessárias para o acesso aos seus arquivos estão contidas na própria partição. As informações incluem: diretórios e subdiretórios contidos na partição; descritores dos arquivos na partição; blocos de dados dos arquivos contidos na partição; lista ou mapa de blocos livres da partição.

5 Entrada e Saída

O objetivo primeiro de um computador é solucionar problemas. Para tanto, é necessário que algum tipo de mecanismo exista para que possamos informar esse problema ao computador e recuperar sua solução. Esse mecanismo constitui o que denominamos genericamente de dispositivos de entrada e saída. Atualmente, é possível encontrar uma grande variedade de dispositivos, desde dispositivos desenvolvidos para permitir a comunicação do homem com o computador (teclado, mouse, monitor de vídeo, etc) até dispositivos que possibilitam a comunicação entre computadores (modems, placas de redes, etc), ou ainda aqueles destinados à conexão de outros equipamentos ao computador (unidades de fita, disquetes, disco rígido, CD-ROM, etc). Apesar dessa diversidade, esses dispositivos de entrada e saída possuem alguns aspectos de hardware em comun. De acordo com o sentido do fluxo de dados entre o computador e o dispositivo, esses podem ser divididos em periféricos de entrada, periféricos de saída, ou ainda periféricos de entrada e saída. Um periférico pode ser visto como qualquer dispositivo conectado a um computador de forma a possibilitar sua interação com o mundo externo. Os periféricos são conectados ao computador através de um componente de hardware denominado interface. Considerando-se a diversidade, a complexidade, e as diferentes formas de operações em função do tipo de periférico, as interfaces empregam no seu projeto um outro componente de hardware: o controlador. A função básica de um controlador é traduzir operações genéricas do tipo “ler dados”, “escrever dados”, “reinicializar”, “ler status” ou “escrever comando” para uma seqüência de acionamentos eletrônicos, elétricos e mecânicos capazes de realizar a operação solicitada. Para isso, o controlador deve saber como o periférico funciona, resultando que cada tipo de periférico necessita de um controlador diferente. A gerência de E/S está intimamente relacionada com aspectos de hardware de um computador.

O mecanismo de interrupções permite que um controlador de periférico chame a atenção do processador. Fisicamente, o barramento de controle é usado pelos controladores de periféricos para o envio de sinais elétricos associados com a geração de uma interrupção. Uma interrupção sempre sinaliza a ocorrência de algum evento. Quando ela acontece, desvia a execução da posição atual de programa para uma rotina específica. Essa rotina, responsável por atender a interrupção, é chamada de tratador de interrupção. O tratador realiza as ações necessárias em função da ocorrência da interrupção. Ele é, simplesmente, uma rotina que somente é executada quando ocorre uma interrupção.