Revisao-enade-prof_joao_bosco.pdf

  • Uploaded by: Joao Lucas Mota
  • 0
  • 0
  • November 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Revisao-enade-prof_joao_bosco.pdf as PDF for free.

More details

  • Words: 13,614
  • Pages: 37
TOPICOS DE SISTEMAS OPERACIONAIS

1

SISTEMAS OPERACIONAIS 1 INTRODUÇÃO................................................................................................................................3 1.1 O que é um SO?........................................................................................................................3 1.2 SO x Empresa...........................................................................................................................3 1.3 Perspectivas de um SO.............................................................................................................4 1.4 Ecossistema...............................................................................................................................4 1.5 Gerações de Sistemas Operacionais.........................................................................................5 1.6 Tipos de Sistemas de Operacionais...........................................................................................6 1.7 Hardware...................................................................................................................................7 1.8 A carga do Sistema Operacional...............................................................................................7 1.9 Conceitos - Processos...............................................................................................................8 1.10 Conceitos - Memória..............................................................................................................9 1.11 Interpretador de Comandos.....................................................................................................9 1.12 Chamadas de Sistemas..........................................................................................................10 1.13 Organização de Sistemas Operacionais (Tipos de Projeto)..................................................11 2 GERÊNCIA DE PROCESSOS......................................................................................................13 2.1 Conceitos Básicos...................................................................................................................13 2.2 Criação de Processos..............................................................................................................14 2.3 Término de Processos.............................................................................................................15 2.4 Hierarquia de Processos..........................................................................................................15 2.5 Estados de Processos (Ciclo de Vida dos Processos)..............................................................16 2.6 Interrupções............................................................................................................................17 3 GERENCIA DE MEMÓRIA.........................................................................................................19 3.1 Gerenciador de memória.........................................................................................................19 3.2 Acesso à memória...................................................................................................................19 3.3 Problemas de Gerenciamento de Memória.............................................................................19 3.4 Gerente de Memória...............................................................................................................20 3.5 Monoprogramação..................................................................................................................20 3.6 Modelo de Multiprogramação................................................................................................21 3.7 Gerenciamento Básico - Multiprogramação...........................................................................21 3.8 Overlays..................................................................................................................................25 3.9 MEMÓRIA PAGINADA........................................................................................................25 4 Gerencia de E/S..............................................................................................................................32 4.1 Introdução...............................................................................................................................32 4.2 Esquemas de E/S.....................................................................................................................33 4.3 DMA (Direct Memory Access)...............................................................................................34 5 Sistemas de Arquivos.....................................................................................................................36 5.1 Introdução...............................................................................................................................36

1 INTRODUÇÃO 1.1 O que é um SO? Um sistema operacional pode ser encarado como um conjunto de programas que auxiliam o usuário na utilização do computador. Dentre esse conjunto de aplicações pode estar editores de texto, navegadores web, interpretadores de comandos, dentre outros. Pois bem, com essa definição é difícil estabelecer que programas fazem parte do SO. Por exemplo, você pode achar que todos os programas que foram instalados com a mídia do SO fazem parte do sistema operacional. Após o exposto aqui gostaria definir o foco de estudo deste material, que é basicamente o núcleo (kernel) do sistema operacional. Nesse curso estaremos preocupados com os aspectos relacionados ao projeto de kernels de sistemas operacionais. De forma que apenas algumas aplicações auxiliares (externas ao contexto do SO) serão citadas como o caso do interpretador de comandos e do bootloader.

1.2 SO x Empresa

Uma analogia que pode ser feita é do SO com uma grande empresa. Uma empresa está inserida em um ambiente onde ela terá que tratar com diversos agentes externos, aqui para exemplificar, temos: Clientes, Fornecedores e o Governo. Dentro da empresa cada recurso é especializado para interagir de alguma forma com um ou mais de um destes elementos. O Depto. Comercial Faz o relacionamento com o cliente até a venda. O Depto. técnico assume o relacionamento com esse cliente a após a venda. O Depto. de Pessoas Gerencia os recursos humanos da empresa. Já o Depto. Financeiro é responsável pela cobrança e pagamentos. As empresas tendem a se dividir em áreas de especialização, essa estratégia é conhecida como dividir para conquistar, e tem o objetivo de dividir um problema maior em partes menores, tornando mais claro os processos internos e atendendo melhor a demanda do cliente. Observe que essa divisão é sugerida e pode variar de empresa para empresa, o fato é que os administradores aceitam essa divisão como uma boa prática. Um sistema operacional pode ser visto da mesma forma. Os departamentos de uma empresa podem ser comparados as gerências de um SO: Por exemplo o Ger. de Processos é responsável por criar os

processos e alocá-los para a CPU conforme a sua característica ou prioridade. Já o Ger. de Memória será responsável por tratar as requisições de memória do processo como também garantir a disponibilidade de memória para outros processos. A Ger. de E/S é acionada sempre que algum processo precisa comunicar-se com o mundo externo, porém elementos internos do próprio SO, podem querer também se comunicar com o mundo externo. Observe que esta é uma divisão também sugerida, e os projetistas de sistemas operacionais tem aceito isso como uma boa forma de organizar as rotinas do SO.

1.3 Perspectivas de um SO O SO existe basicamente para cumprir duas missões básicas: • Gerenciar a expectativa do usuário: atendendo a suas requisições dentro do esperado pelo usuário; • Gerenciar recursos do Sistema: Controlando e disponibilizando recursos para atender as demandas dos usuários. Maquina Estendida O usuário quer que as suas solicitações sejam atendidas, mas não quer, de maneira alguma, saber como isso vai ser feito. Sendo assim o usuário “preenche um formulário” e o SO se encarrega de atender a sua solicitação. Por exemplo, um processo pode pedir: SO Abra um arquivo. Informando o caminho do arquivo, e o local onde será guardado o descritor para o arquivo. O SO por sua vez deve criar as condições para o que o usuário possa realizar operações sobre o arquivo aberto, desta forma o SO através dos diferentes subsistemas deve preparar-se para eventuais solicitações do usuário para aquele arquivo. Gerente de Recursos Como gerente de recursos o sistema operacional tem a função de garantir a disponibilidade dos recursos, para que as solicitações dos processos possam ser atendidas. Muitos processos podem solicitar uma impressão por exemplo. Porém, dada a sua natureza, a porta de impressão só pode ser usada para atender uma requisição por vez, e sendo assim o sistema operacional deve enfileirar essas requisições e atendê-las da maneira mais otimizada possível.

1.4 Ecossistema

O diagrama acima apresenta os diferentes elementos de um sistema operacional moderno. Nos primeiros sistemas computacionais não havia essas camadas, o usuário interagia direto com o hardware. Desta forma só quem poderia operar o computador eram pessoas extremamente especializadas. Com o passar do tempo diferentes camadas foram introduzidas no sentido de facilitar o uso do computador, reduzindo a sua complexidade e tornando o seu uso possível por pessoas cada vez

menos especializadas. Por exemplo, o SO tem como uma de suas finalidades esconder a complexidade do hardware (HW) para o usuário. Porém o uso da interface bruta (API) do sistema operacional, ainda é bastante complicado. Para esconder a complexidade do sistema operacional, existe o interpretador de comandos, também conhecido como SHELL. O SHELL é uma aplicação como outra qualquer, é através dele que o usuário “preenche o formulário” com a sua solicitação para o SO, ou seja, é através do SHELL que o usuário poderá lançar e terminar aplicações. copiar arquivos, desligar o computador, dentre outras operações. Um computador é uma ferramenta de trabalho programável, o que ele pode fazer é limitado apenas pela imaginação do seu programador. Sendo assim, alguns usuários mais especializados podem querer programá-los, para isso existem duas formas, a primeira é escrever um código em linguagem de máquina (0s e 1s), a segunda seria escrever um código em um nível mais elevado e usa-se os montadores e compiladores para traduzir esse código em linguagem de nível mais elevado em código de máquina. Esse código gerado é o que chamamos de aplicativos (APPS).

1.5 Gerações de Sistemas Operacionais Geração

Hardware

Programação

1a. (45-55)

Válvulas

Painéis Programação

2a. (55-65)

Transistores

Sistemas de Lote

3a. (65-80)

Circuitos integrados

Multiprogramação e Timesharing

3a. (80-hoje)

Alto Grau de Integração - LSI CHIPS

SO especializados Desktop Tempo Real

1a Geração de Computadores (45-55) Neste primeiro momento os computadores eram projetados, construídos e programados pelas mesmas pessoas, não existia o comercio destes equipamentos. A programação era realizada em código absoluto, através de fios, plugs e chaves para controlar as funções básicas da máquina, ainda não existia o conceito de linguagem de programação e os sistemas operacionais ainda não existiam. Os computadores podiam ter até 20.000 válvulas. nessa época os programas processados pelos computadores eram constituídos essencialmente por cálculos numéricos repetitivos, como por exemplo a geração de tabelas de funções trigonométricas. Ainda nesta geração, no começo dos anos 50, se criou os cartões perfurados para evitar a programação em painéis. São exemplos de computadores desta época o COLOSSUS, ENIAC e EDVAC. 2a Geração de Computadores (55-65) A principal característica destes computadores é o uso do transistor (inventados no inicio dos anos 50) ao invés das válvulas. Isso teve um grande impacto no custo e no tamanho dos computadores. Nesta época os computadores eram extremamente caros, e surgiram as primeiras técnicas que tinham por objetivo otimizar o uso dos computadores, pois anteriormente perdia-se muito tempo carregando-se programas e dados para o processamento posterior. Foi nesta época que foi criado o sistema de processamento em lote (batch). Os sistemas em lote, consistem no uso de um computador de pequeno porte (ex.: IBM 1401) para a leitura dos jobs (programas + dados) para a fita magnética. Essa fita, contendo os jobs era levada para o computador responsável pelo processamento (ex.: IBM 7094), onde eram executados em

sequência. O principal uso destes computadores foi o cálculo matemático. Esses computadores eram programados em linguagem de montagem ou FORTRAN, são exemplos de sistemas operacionais desta época, o FMS (Fortran Monitor System) e o IBSYS do 7094. 3a Geração de Computadores (65-80) Essa geração de computadores trouxe 3 grandes avanços: O primeiro deles foi a integração, um conceito que permite agrupar vários transistores em uma única pastilha de silício. Com isso era possível construir circuitos integrados para funções específicas, com centenas de transistores, isso trouxe redução no custo, no tamanho e no consumo de energia destes computadores, além do aumento na velocidade de processamento. O outro avanço foi a multiprogramação. Para entender a vantagem desta técnica é preciso entender como um job faz a leitura e saída dos dados. Em geral, os computadores possui dispositivos de E/S responsáveis por fazer a leitura dos dados e eventuais impressões de resultados. O fato é que, tanto na época como hoje, os dispositivos de E/S são mais lentos que a CPU, sendo assim, sempre que um job acessa dados em uma fita por exemplo, a unidade de processamento fica ociosa, até que o resultado seja transferido para a memória. Se durante essa transferência for possível rodar outro job, esse tempo de CPU não seria desperdiçado. Lembrando que um job para rodar precisa está na memória RAM, então se num dado momento existem vários jobs disponíveis na memória para executar, durante um acesso de E/S por parte de um job a CPU poderá ser alocada para outro job. Essa é a ideia da multiprogramação: Permitir que vários jobs estejam na memória do computador em um dado instante. O ultimo avanço foi o Timesharing. Esse tipo de recurso, provê a chamada interatividade para sistemas computacionais. Em um primeiro momento, a interatividade permitiu o uso de terminais on-line conectados a um sistema computacional. Cada terminal pode ser operado por um usuário e este poderá submeter diversos jobs (processos). O sistema operacional se encarregará de escalonar a CPU entre as diferentes requisições, através do compartilhamento do tempo da CPU 4a Geração de Computadores (80 - dias Atuais) O principal avanço desta geração é a integração em Larga escala (LSI), isso tornou os computadores muito baratos, criando as condições para o surgimento dos chamados computadores pessoais (PC).

1.6 Tipos de Sistemas de Operacionais Os sistemas operacionais mais conhecidos hoje em dia, são em sua maioria de proposito geral, ou seja, pode se encaixar em vários dos tipos abaixo. Por exemplo: o GNU/Linux poderá ser usado em Mainframe, Computadores Pessoais e Servidores, mas também é um sistema que pode ter escalonamento de Tempo Real e ainda ser usado em dispositivos embarcados. Então, tenha em mente que a classificação abaixo é apenas para fins didáticos. •

Para computadores de Grande Porte (Main Frame): Essas máquinas se caracterizam pela especialização do Hardware, o que lhe dá um alto poder de processamento.



Para Multiprocessamento: Sistemas Computacionais com mais de um núcleo de processamento;



Multiusuário e Multitarefa: Esse sistema prove proteção entre recursos dos usuários (privacidade) e a capacidade de executar várias tarefas ao “mesmo tempo”;



Tempo Real: São Sistemas Operacionais que devem entregar uma resposta de solicitação dentro de um prazo acordado (SLA);



Embutido: Esse tipo de Sistema Operacional normalmente roda em hardware com

limitações de processamento e memória;

1.7 Hardware O sistema operacional que você irá desenvolver rodará sobre um hardware. Você deve conhecer os aspectos deste hardware para que obter um melhor despenho do sistema operacional. Os principais aspectos que você deve observar: •

CPU: O Conjunto de instruções disponíveis, registradores, mecanismos de proteção existentes, modos de acesso à memória, modo de endereçamento dos controladores de dispositivos, hardware de gerencia de memória.



Memória: Memórias disponíveis no sistema, se existe armazenamento persistente. Se o modelo de memória tem espaço separado para instrução e dados.



E/S: Existe disponibilidade de interrupções, Controlador de interrupções, Controlador DMA?



Barramentos: Os barramentos são padrões de mercado. A tratativa em relação a alguns barramentos mais importantes (ex. PCI/ISA) devem está incluída diretamente no SO (gerencia de E/S), para outros barramentos pode-se implementar o suporte através de drivers de dispositivo.

1.8 A carga do Sistema Operacional Em Computadores mais complexos, o SO costuma está em uma memória não volátil, o processo de copiar esse sistema, ou partes dele, para a memória e transferir o controle da CPU é o que chamo aqui de carga do SO. maneira como o Controle da CPU é transferido para o Sistema Operacional pode variar de acordo com o hardware em questão. Aqui vamos explicar o processo de boot de um sistema baseado na arquitetura intel x86.

1.8.1 BIOS, CMOS ,POST, Bootloader Quando um computador é ligado, via hardware, o controle da CPU é transferido para BIOS (Binary Input Output System), esse é o primeiro programa que executa em um computador deste tipo. Esse software (firmware) fica localizado em uma memória não volátil conhecida como CMOS e durante o processo de inicialização, o conteúdo da CMOS é transferido para uma localização na memória. A primeira instrução executada pelo computador está no endereço 0xFFFF0 (1048560), essa instrução é justamente um salto para o endereço inicial do código da BIOS. A BIOS faz, dentre outras coisas, o chamado POST (Power On Self Test), que é uma verificação da saúde dos componentes de hardware. Após a verificação, a BIOS vai procurar um o Bootloader em um MBR. Uma das configurações que é feita na BIOS é a sequência de inicialização, ou seja, em quais "discos" e em qual sequência o bootloader deve ser procurado. O Bootloader é o sistema responsável pela carga do sistema operacional e fica armazenado nos primeiros 440 bytes do primeiro setor do disco ou partição. A BIOS vai carregar para a memória o MBR que estiver assinado, , um MBR é considerado assinado, ou seja, o dispositivo é considerado inicializável, se os 2 últimos bytes do primeiro setor do disco em questão, forem respectivamente 0xAA55. O bootloader é copiado para o endereço 0000:7C00 e controle da CPU é transferido para o Bootloader. Em ultima instancia o Bootloader faz a carga do sistema operacional na memória e transfere o controle para a mesmo. Isso encerra a carga do SO. A Tabela a seguir ilustra o esquema de um MBR.

000-439 Código do Bootloader 440 bytes 440-445 Nulo

6 bytes

446-509 Tabela de Partições

64 bytes

510-511 Assinatura 0xAA55

2 bytes

1.9 Conceitos - Processos Processos vs. Programas A definição clássica diz que um processo é um programa em execução. Podemos dizer que um processo é uma instância de um programa. O programa é estático, e normalmente está em alguma memória não volátil (disco), já o processo é dinâmico, e além do código executável, é composto de variáveis, pilha, etc. Ou seja no processo os dados podem variar ao longo do tempo. Um programa pode ser inicializado várias vezes, gerando vários processos totalmente distintos entre si. Espaço de Endereçamento É o conjunto da memória acessível ao processo, guarda o executável, as variáveis e a pilha, e normalmente começa de zero e vai até um valor específico. Nos sistemas operacionais mais modernos esse espaço de endereço é continuo, porém a sua organização física na RAM é fragmentada, para suportar isso existem os conceitos de Memória Real e Virtual. Tabela de Processos É a estrutura onde é guardada as informações sobre todos os processos que estão na memória do computador, nesta estrutura tem informações sobre estados do processo e contexto. Normalmente implementado através de uma tabela ou lista encadeada. Registradores e Contexto de Processo Em um sistema multiprogramado, processos podem se alternar no uso da CPU, um processo quando retorna a CPU ele deve continuar de onde ele estava quando deixou a CPU em sua execução anterior, para garantir isso, é preciso salvar o chamado contexto do processo, que é basicamente é constituído dos registradores de uso geral, status, program counter (PC), a pilha de execução, dentre outros.

1.9.1 Hierarquia de Processos Apos a carga do SO esse inicia o primeiro processo, esse processo é responsável pela carga de outros processos e estes podem chamar outros processos. Quando um processo é criado um dos seus atributos é o identificador do processo que o criou, neste caso chamado de processo pai. Esse tipo de relação gera a chamada hierarquia de processos, onde os processos do sistema podem ser organizados em forma de arvore.

1.9.2 Atributos de Processos Quando um processo é iniciado ele é registrado no sistema operacional, em uma estrutura conhecida como tabela de processos, neste cadastro o processo recebe certos atributos que podem ser usados para que tanto o SO como o administrador possam gerenciá-los. São exemplos de atributos: o PID (Process ID), ID do usuário que o criou, comando/programa que originou o processo, etc.

1.9.3 Compilação x Interpretação Existem duas formas de se fazer a tradução da linguagem de nível N (mais alto) para a de nível N-1 (mais baixo), compilação e interpretação. No processo de compilação, a tradução do código é feita por completo em uma única vez, gerando o código na linguagem N-1, chamado código objeto. Já no processo de interpretação a tradução do código é feita em tempo de execução do programa, para isso o código de linguagem N é executado com o auxílio de um software conhecido como interpretador.

1.10 1.10.1

Conceitos - Memória Endereçamento

A memória pode ser vista como um array de posições para armazenamento de dados e instruções. Esse conceito foi introduzido por Von Neumann. O espaço de memória ocupado por um processo é chamado de espaço de endereçamento, e é só nesta área que o processo poderá atuar. Aqui é importante lembrar que as arquiteturas de 32 Bits só permitem o endereçamento de 4GB por parte do processo. Isso também é um limite para o SO. Para quebrar esse limite na arquitetura IA32 a Intel criou uma extensão para seus processadores chamada de PAE (Physical Address Extension), com essa extensão os limites de 4Gb para o processo continua, mas o SO consegue agora gerenciar até 64GB de memória.

1.10.2

Fragmentação

Uma das finalidades do SO é gerenciar memória, entenda-se por isso prover memória para processos e também gerenciar porções livres da memória. Para o SO gerenciar cada posição de memória seria muito caro em termos de processamento e memória. Por isso os sistemas optam por dividir a memória em blocos, e é esses blocos que serão alocados para os processos. Em algumas situações o processo não conseguirá usar todo espaço do bloco, provocando desperdício de memória, a isso chamamos de fragmentação interna. Em outros casos quando o processo é obrigado a ocupar um espaço continuo na memória, podem surgir áreas de memória entre processos que sejam tão pequenas que não poderão ser ocupadas por nenhum processo, o que também gera desperdício, a esse tipo de situação chamamos de fragmentação externa.

1.10.3

Memória Virtual

Para evitar que os programadores tenham que trabalhar diretamente sobre a memória física, foi criado o conceito de memória virtual. Sendo assim independente da área de memória onde o processo foi alocado, o espaço de endereçamento do processo sempre vai de zero até um valor específico. A tradução do endereço virtual para o real é sempre feita com o apoio de hardware específico.

1.10.4

Proteção

O espaço de endereçamento de um processo deve ser protegido de acessos por parte de outros processos. Em geral o sistema operacional conta com o apoio do hardware para prover esse tipo proteção. Quando um processo tenta acessar uma memória que está fora do seu espaço de endereçamento o hardware gera uma (exceção)interrupção e o processo é terminado imediatamente.

1.11

Interpretador de Comandos

A função básica do shell é tornar o uso do sistema operacional mais fácil para os usuários. Basicamente o usuário digita um comando no prompt do shell, e este traduz aquela solicitação em

uma requisição para o sistema operacional. Existem três paradigmas mais usados para usabilidade dos shells. Alguns interpretadores de comandos se apresentam na forma de menu, e o usuário escolhe a operação dentre as opções existente nestes menus. Outros interpretadores podem ser acionados através de comandos digitados em um prompt de comando, esses interpretadores são conhecidos como interpretadores de linha de comando ou CLI (command line interface). Por fim existem os interpretadores capazes de tratar cliques de mouse, esses são os interpretadores gráficos. Por exemplo, no Windows: experimente matar o processo explorer.exe, você verá que não será possível interagir mais com o Windows. Sendo assim podemos dizer que o explorer.exe é uma espécie de interpretador de comandos gráficos (cliques de mouse). Um dos principais usos do interpretador de comandos é o lançamento de aplicações. Em geral, cada comando corresponde a um arquivo no sistema onde está o código de máquina da aplicação. Porém alguns dos comandos disponíveis para uso não são aplicações e tem a sua programação feita internamente dentro do interpretador, esses comandos são conhecidos como comandos internos do interpretador. Todo sistema operacional possui o comando CD nos interpretadores, mas nenhum deles tem um “CD.EXE”. Em geral os interpretadores permitem a execução em lote de programas através de arquivos, que são conhecidos como batches. Um batch é um arquivo contendo uma sequencia de comandos válidos para o interpretador de comandos. Esses comandos podem ser aplicações ou comandos internos. Alguns interpretadores possuem comandos internos que permitem programar desvios de fluxo (if) e laços (for e while) com base no código de retorno gerado ao final da execução dos comandos. A grande parte dos interpretadores presentes nos sistemas operacionais possui uma linguagem de programação (Linguagem Script), os programas escritos nessas linguagens não precisam ser compilados, e são traduzidos para linguagem de máquina pelo interpretador em tempo de execução. Após a digitação de um comando, em geral uma saída (resultado) é gerada na tela. A medida que o usuário digita mais comandos essa saída vai desaparecendo, para dar lugar as novas saídas geradas, de forma que aquele resultado não estará mais disponível. Para evitar isso, os interpretadores possuem o recurso de redirecionamento, com ele é possível direcionar as saídas (padrão e erro) de um comando para um arquivo colocando os caracteres > ou >> entre o comando e o arquivo de saída. Outra funcionalidade disponível nos interpretadores é acoplar as saídas de um comando na entrada de outro, esse recurso é conhecido como pipe e pode ser utilizando o caractere '|' entre os comandos.

1.12 1.12.1

Chamadas de Sistemas Conceitos

Uma chamada de sistema é a forma como o sistema operacional oferece os seus serviços. Em geral quando se faz um programa, existem certas instruções que o programa em execução, aqui chamado de processo, pode executar, como por exemplo operar com posições de memória pertencentes ao seu espaço de endereçamento. Exemplo: um processo pode perfeitamente somar o conteúdo de duas posições de memória e armazenar numa terceira sem precisar do Sistema Operacional, porém se esse processo precisa armazenar isso em um arquivo, mandar via rede ou simplesmente mostrar na tela, ele vai precisar do Sistema operacional para isso. Como usar uma chamada de sistema? Para usar as chamadas de sistema oferecidas por um sistema operacional é necessário conhecer a API ou SPI do sistema operacional que define quais chamadas estão disponíveis e quais são os parâmetros que devem ser passados. Os desenvolvedores de sistemas operacionais devem disponibilizar a documentação desta API, se ele deseja que as pessoas façam programas para o seu

sistema. Aqui você encontra a documentação da API do Windows e Linux. Para explicar esses conceitos utilizaremos GNU/Linux, porém eles se aplicam a qualquer sistema operacional. Cada uma destas chamadas recebe parâmetros que devem ser colocados em registradores específicos. Para exemplificar imagine o programa que tem as seguintes atribuições: ler o conteúdo de um arquivo e exibi-lo na tela. Em termos de chamadas de sistema, o programa deve ser organizado da seguinte forma: Abrir o arquivo (Open), Ler o seu conteúdo (Read) e Escrever na saída (write), Fechar o arquivo (close) e por fim, terminar o processo (exit). No GNU/Linux essas chamadas de sistemas tem os seguintes números: 1 – exit 3 – read 4 – write 5 – open 6 – Close A chamada Open apresenta os parâmetros Arquivo e Modo, onde Arquivo é o nome do arquivo que será aberto e modo é como o arquivo será aberto. O código a seguir ilustra o uso desta chamada usando a linguagem assembly: section .data

sys_open: equ 5 ; sys_open = 5 f: db "file.txt",0 ; f = "file.txt" f_id: dd 0 ; f_id = 0 o_rdonly: equ 0 section .text global _start

_start:

mov eax, sys_open ; move a chamada para o registrador eax mov ebx, f ; move o nome do arquivo para o registrador ebx mov ecx, o_rdonly ; move o modo de abertura para ecx int 80h ; chama o SO mov dword [f_id], eax ; O retorno (resultado) da chamada é armazenado em f_id (file descriptor)

1.13

Organização de Sistemas Operacionais (Tipos de Projeto)

Monolítico: Esse tipo de projeto caracteriza-se por todo o código do sistema operacional rodar com o processador em modo supervisor. Isso torna, em tese, a execução das atividades mais eficiente, pois todas as operações têm acesso a instruções privilégiadas não necessitando repassar requisições deste tipo a um outro módulo (microkernel). Os críticos deste tipo de projeto citam a segurança e a falta de organização como desvantagem deste tipo de projeto. Um exemplo clássico deste projeto é o Linux. Microkernel: Esse projeto seria o contraponto ao projeto monolítico. Os procedimentos do sistema operacional são agrupados em bibliotecas e apenas uma pequena porção do código será executada em modo supervisor. Isso proporciona uma execução mais segura do SO, porém a eficiência das tarefas básicas do SO podem ser prejudicadas haja vista que procedimentos privilégiados terão que ser repassados para o microkernel. Olhando o relacionamento dos componentes do SO Windows,

pode-se concluir que trata-se de um projeto microkernel (Carece de referência). Cliente Servidor: Pode ser encarado como um SO distribuído, ou seja, diferentes componentes do SO ficariam em diferentes hosts distribuídos geograficamente. (Carece de exemplos). Exokernel: Esse projeto é usado para escrever os hypervisors, usados na Virtualização de Sistemas Operacionais. A IBM criou essa possibilidade na década de 70 e isso voltou muito forte nos últimos anos. A ideia principal deste projeto é ter um kernel realmente mínimo com o objetivo de prover interface para que vários kernels possam compartilhar o mesmo hardware.

2 GERÊNCIA DE PROCESSOS 2.1 Conceitos Básicos Não é fácil localizar na história quando surgiu o conceito de processo. Creio eu, que no momento em que houve separação entre um programa supervisor (ancestral do SO) e o job que executa a tarefa para o qual o computador foi comprado (computador não foi comprado para executar SO!), neste ponto podemos dizer que essa tarefa é uma espécie de ancestral do processo. O conceito moderno de processo está relacionado a um conjunto de instruções numa memória volátil e um estado de processo, além disso um processo detém memória bem definida, CPU (por alguns instantes), arquivos abertos, contexto, dentre outras características. Como já falamos anteriormente um processo é um programa em execução.

2.1.1 Multiprogramação As instruções que executam na CPU devem passar obrigatoriamente pela memória RAM, sendo assim, um processo para ser executado precisa está na RAM. Os primeiros sistemas operacionais eram monoprogramados, isso que dizer que em um dado momento somente um processo ocuparia a memória além do SO. Em um sistema multiprogramado, a memória poderá está ocupada por vários processos, cada um com o seu espaço de endereçamento. A ideia desta abordagem surgiu nos SOs da 3a geração, para resolver o problema de desperdício de tempo de CPU durante uma operação de E/S realizada por uma tarefa, isso ocorre principalmente porquê os dispositivos de E/S são mais lentos que a CPU. Em ambiente multiprogramado a CPU poderá ser chaveada rapidamente entre várias tarefas, desta forma, uma situação onde o programa espera por E/S, a CPU poderá ser entregue a outro processo evitando o desperdício de tempo de CPU.

2.1.2 Pseudo Paralelismo Em sala de aula a pergunta que normalmente faço é: O que é um sistema operacional multitarefa? A resposta mais comum é: "Um sistema que executa várias tarefas ao mesmo tempo". Pois bem, essa é de fato a sensação que os usuários experimentam, porém não bem assim que funciona. Para possibilitar essa "experiência multitarefa", o que ocorre é que a CPU é compartilhada entre os processos que estão na memória aguardando CPU. Funciona da seguinte forma: É escolhido um tempo máximo, conhecido como quantum ou timeslice (fatias de tempo), em que a CPU será entregue a cada processo, por exemplo 20ms , desta forma, em único segundo a CPU, terá sido alocada para vários processos. no exemplo 50 vezes teoricamente. Desta forma, para o usuário parece que os vários processos executam juntos, mas o que houve foi um compartilhamento do tempo de CPU. Essa técnica é conhecida como timesharing e foi implementada a partir dos sistemas da 3a Geração. Para que isso funcione é necessário que exista apoio do hardware, neste caso um gerador de interrupções, conhecido como relógio. A cada intervalo de tempo a CPU terá o fluxo de execução desviado para o Sistema Operacional e neste ponto o SO assumirá o controle do sistema, até entregar novamente a CPU a outro processo.

2.1.3 Mudança de Contexto Em um sistema de tempo compartilhado, quando um processo é interrompido por alguma razão (interrupção ou chamada de sistema), este deverá retornar a CPU no futuro próximo, quando o processo retornar ele deve continuar do ponto onde parou. Para que isso ocorra é importante que algumas informações sejam salvas antes que a CPU seja utilizada por qualquer outro software (SO ou processo). A essa informação é chamada de contexto do processo e é composta basicamente de registradores da CPU, como por exemplo o PC (program counter), Os registradores acumuladores,

o registrador de status, dentre outros (existem outras informações que não são registradores, mas essas serão tratadas em um momento oportuno). Dito isso, podemos dizer que quando um processo deixa a CPU, antes de qualquer coisa o SO precisa realizar o Salvamento do Contexto e antes que o SO passe o controle da CPU para um processo, é preciso que seja restaurado o contexto do processo. A esse evento de saída de um processo da CPU e entrada de outro, chamamos de troca ou mudança de contexto.

2.1.4 Processo X Programa Já abordamos isso anteriormente. Um processo tem características que são dinâmicas e que mudam ao longo do tempo. Dentre essas características podemos citar variáveis, estado, arquivos abertos, contexto. O programa é o código (instruções) que encontra-se eu uma memória não volátil diferentemente do processo que estará obrigatoriamente na RAM, quando em execução. OBS.: Sabemos que um processo pode ocupar uma área em disco conhecida como área de troca. Porém é importante lembrar que essa área estende a memória RAM de certa forma, porém um processo para executar (ocupar a CPU) terá que ser movido desta área para a RAM.

2.1.5 Comportamento do Processo (I/O x CPU). Conhecer o perfil de execução de um processo é importante no momento de escolher (escalonar) um processo que vai usar a CPU, o motivo de tal afirmação será mostrado na aula de escalonamento de processos. Existem 2 perfis de execução de processo. O perfil Orientado a CPU (CPU bound) usa extensivamente a CPU. Esses processos quando ocupam a CPU costumam usar toda a sua fatia de tempo. Por exemplo em cálculos matemáticos complexos (ex.: fatorial de 80.000). Já no perfil Orientado a E/S (I/O bound) os processos quando ocupam a CPU usam uma pequena fração da sua fatia de tempo e logo acionam o SO para alguma tarefa.

2.2 Criação de Processos Em linhas gerais o Sistema Operacional é o conjunto de aplicações que tornam possível o uso do computador. Para o nosso estudo estamos preocupados especificamente com o Kernel do SO. O kernel será responsável por prover recursos para que as demais aplicações possam executar suas atividades. Aqui consideramos processos todas aquelas aplicações que NÃO rodam em modo supervisor do processador. Inicialização do SO Por mais rápido que seja o computador, existe sempre um tempo entre o momento que se pressiona o botão ligar e o instante em que o sistema estará pronto para uso. Boa parte deste tempo é usado pelo SO para preparar o sistema. Esse preparo consiste basicamente na inicialização de processos que são primordiais para o funcionamento do sistema. O termo usado para esses processos é Daemon (Serviço). Através de um processo Um processo poderá ser iniciado dentro de um outro processo, usando uma chamada de sistema. No Windows a chamada de sistema CreateProcess() poderá ser usada para criar um processo. Nos sistemas POSIX (Linux, AIX, SUnOS, Opensolaris, HPUX, MacOs), A chamada de sistema usada para criar um processo é a fork(). Essa chamada não recebe nenhum parâmetro e o que ela faz é criar um processo filho que é idêntico ao processo que fez a chamada. Para substituir esse processo filho por um novo processo é executada a chamada de sistema exec(). Essa técnica é o conhecida como fork-exec e á maneira como esses sistemas disparam novos processos. Requisição de Usuário

Um usuário pode a qualquer momento solicitar a criação de um processo. Em um sistema operacional gráfico ele fará isso através de um clique de mouse. Já em uma interface de linha de comando (CLI) isso será feito digitando um comando.

2.3 Término de Processos Podemos dizer que um processo foi finalizado quando este não estiver mais presente na tabela de processos. Isso implica que toda a memória alocada e todos os recursos alocados anteriormente para aquele processo agora estarão livres. Esta seção fala das diversas formas de um processo terminar. Saída Programada Normal e Por Erro Alguns aplicativos executam por um certo tempo até executar a tarefa e depois são terminados. Um exemplo de aplicação é o comando DIR do Windows. Esse termino é sempre codificado pelo programador da aplicação. Outra forma de um processo sair é no caso de algum erro detectado pelo programador durante a execução do processo. Por exemplo num sistema onde o usuário entra algum valor inesperado e o programador devia o fluxo para uma instrução de saída. Nestes casos instrução passada pelo programador para o termino da aplicação se traduzirá sempre em uma chamada de sistema que deve ser usada para avisar o SO para tomar as providências para finalizar o processo em questão. No Windows essa chamada de sistema é o ExitProcess(). Nos Sistemas POSIX a chamada de sistema em questão é a exit(). Erro Fatal O erro fatal ocorre quando o processo executa alguma operação ilegal (por exemplo divisão por zero). Quando ocorre esse tipo de situação é gerada uma interrupção a nível de hardware e o processo atual é interrompido e o controle da CPU volta ao sistema operacional. Nestes casos o comportamento padrão do SO é excluir o processo da lista dos processos em execução e liberar todos os recursos. Algumas linguagens de programação trabalham com o conceito de exceção para impedir que o processo seja terminado, nestes casos a instrução ilegal é capturada (possivelmente por um interpretador ou máquina virtual) antes de ser passada a CPU. Cancelamento O cancelamento de um processo é um evento externo ao processo (provocado pelo SO ou outro processo). Quando esse evento ocorre, o processo tem o seu fluxo normal de execução interrompido e é desviado para uma função que vai tratar da finalização do processo. Um exemplo desta situação ocorre quando você pressiona o [X] para fechar o MS/Word. Nesta situação, o MS/Word é desviado para uma rotina que, dentre outras coisas, vai questionar sobre o salvamento dos documentos não salvos. Em alguns casos é possível interromper o processo abruptamente (sem o devido tratamento). Um exemplo deste tipo desta última situação ocorre quando você fecha uma janela que não está mais respondendo.

2.4 Hierarquia de Processos A possibilidade de criar um processo a partir de outro, dá origem a uma ralação do tipo pai-filho entre os processos. Um processo quando criado recebe um identificador (Process ID ou PID) perante o Sistema Operacional. Nos sistemas operacionais que possuem (O Windows não possui) esse relacionamento os processos costumam guardar em seus atributos também o (Parent Process ID ou PPID) que é o PID do processo que o criou. Usando esse atributo dos processo é possível mapear todos os processo do sistema na forma de uma arvore de processos. Essa relação existente entre os processos dá origem a chamada Hierarquia de Processos a listagem abaixo mostra a arvore de processos para um sistema GNU/Linux distribuição Debian (saída obtida pelo comando pstree -p). init(1)-+-acpid(1823) |-atd(1888)

|-cron(1908) |-dhclient3(1839) |-getty(1927) |-getty(1929) |-getty(1931) |-getty(1933) |-getty(1935) |-login(1925)---bash(1951)---pstree(1961) |-named(1863)-+-{named}(1864) |

|-{named}(1865)

|

`-{named}(1866)

|-portmap(1606) |-rpc.statd(1617) |-rsyslogd(1809)-+-{rsyslogd}(1811) |

`-{rsyslogd}(1812)

`-udevd(864)

2.5 Estados de Processos (Ciclo de Vida dos Processos) Durante a sua vida, um processo poderá está em um dos três estados a seguir: •

Pronto (ready): O processo está pronto para ganhar a CPU, tudo o que ele precisa é ser escolhido, dentre os processos existentes, para ganhar a CPU e continuar processando. Por favor não confundir esse estado com finalizado.



Executando (running): Neste estado, o processo estará ocupando a CPU. O número máximo de processos neste estado em um Sistema será igual a quantidade de CPUs disponíveis.



Bloqueado (blocked): Neste estado, o processo estará aguardando algo necessário para continuar a sua execução. Em geral o processo solicitou algo ao SO através de uma chamada de sistema blocante.

O diagrama abaixo mostra a transição entre esses estados:

Transição 1: Ocorre quando um processo da fila de prontos é escolhido (escalonado) para ocupar a CPU. Transição 2: Essa transição ocorre em consequência de uma interrupção de hardware. Um dispositivo requer atenção. Em SOs preemptivos o relógio de hardware gera uma interrupção para indicar que a fatia de tempo destinada ao processo acabou. Transição 3: Ocorre quando o processo solicita algo ao sistema operacional. Por exemplo Entrada e Saída, mais memória, etc.

Transição 4: Ocorre quando a solicitação do processo está atendida pelo SO. O processo irá ocupar uma posição na fila de prontos. OBS.: Observe que esta é uma sugestão do Tanenbaum e que é seguida por grande parte dos escritores sobre o tema. O fato é que os SOs da atualidade apresentam, muitas vezes com outra nomeclatura, em seu conjunto de estados, também esses estados sugeridos acima. Por exemplo, o Windows apresenta além dos estados acima: Exit (marcado para ser removido da memória), New (Ainda não foi aceito) e Suspended (pausado). Abaixo uma figura com os estados de processo do Windows tirada desta referência citada. Observe a semelhança do ciclo Ready-Running-Blocked, com o ciclo descrito anteriormente.

Abaixo uma animação com os estados dos processo para o Linux.

2.6 Interrupções O termo interrupção aplica-se a um evento inesperado que provoca um desvio no fluxo de execução atual (o processo que está usando a CPU é interrompido e o fluxo é desviado para o sistema operacional, esse ponto de desvio faz parte do espaço de instruções do SO é conhecido como rotina de tratamento de interrupção ou interrupt handler). Em geral o recurso de interrupção está disponível em qualquer sistema computacional, essa é uma funcionalidade provida pelo hardware em questão. A interrupção pode ocorrer tanto em nível de hardware (imprevisível) ou Software (Normalmente Previsível). A interrupção de Hardware ocorre por solicitação de algum dispositivo que necessita de atenção imediata, como por exemplo atualização da posição do ponteiro na tela provocada por uma movimentação do mouse. Já interrupção de Software, é provocada pelo processo e ocorre quando um programa solicita algum serviço do sistema operacional. Anteriormente vimos que para um processo executar uma chamada de sistema é necessário a execução da instrução int 80h, essa instrução provoca o chaveamento do controle para o SO. Esse tipo de interrupção é conhecida na literatura como interrupção de software e pelo fato de ser uma instrução dada pelo programador da aplicação, podemos dizer que esse tipo de interrupção é previsível.

Para garantir que o processo que foi interrompido, retorne ao ponto de onde parou, quando esse ocupar novamente a CPU, é necessário que o tratador de interrupção faça o salvamento do contexto do processo que foi interrompido. Abaixo uma lista com todos os procedimentos executados por uma rotina de tratamento de interrupção (Tanenbaum): •

Hardware Empilha o contador de Programa atual (Registrador PC);



Hardware Carrega novo contador PC a partir do vetor de interrupção;



Rotina de Baixo nível Salva os Registradores;



O procedimento para tratamento de interrupção é executado (pode ser em C);



O escalonador de processos é acionado para escolher o próximo processo que ocupará a CPU;



O contexto do processo escolhido é restaurado;



O Controle é transferido para o processo escolhido.

3 GERENCIA DE MEMÓRIA 3.1 Gerenciador de memória O problema principal que o gerente de memória deve resolver é como fornecer ao processo acesso à memória, de maneira escalável e segura, assegurando a mínima ociosidade dos recursos de HW (CPU, I/O, etc.). Dois detalhes do capítulo anterior são pertinentes aqui: •

Um processo é uma instância de programa que para ser executado deve está necessariamente na memória RAM.



Outro detalhe é que o processo é composto das instruções que são carregadas do arquivo do programa, e dos dados derivados da execução das instruções.

Lembrete: O sistema Operacional sempre estará na memória, no todo ou em partes.

3.2 Acesso à memória

Em todo computador existe instruções que suportam a manipulação de informações na memória. As operações típicas são leitura ou escrita que referenciam células específicas de memória. Para fazer este trabalho de interface com a memória as CPUs contam com dois registradores específicos: o MAR (ligado ao barramento de endereços) e o MBR (ligado ao barramento de dados). O MAR (Memory Address Register), tem o papel de selecionar a célula de memória que será afetada (leitura/escrita) e o registrador MBR (Memory Buffer Register) tem o papel de receber o dado que vem da memória, em caso de leitura. Em caso de escrita, esse registrador deve conter o dado que será armazenado no endereço informado pelo MAR. A unidade de controle (UC), ligada ao barramento de controle, através da decodificação da instrução vai informar se a operação é de leitura ou escrita.

3.3 Problemas de Gerenciamento de Memória Todos os problemas relacionados a Gerencia de Memória tem a mesma origem: O programador. O programador quer cada vez mais e mais memória e mais rápida; O programador ou é desonesto ou é relapso, de forma que não devemos permitir que ele possa acessar memória de outros processos ou do SO. É importante entender que quanto mais rápida a memória, mais cara ela será. O ideal seria ter um computador com memória rápida em abundância, porém ele seria muito caro. O que os engenheiros de computação tentam fazer é o balanceamento entre os tipos de memória: Uma pequena quantidade de memória muito rápida (cache, registrador), com uma quantidade maior de memória menos rápida (RAM), e abundancia de memória lenta (HD), porém persistente. Esse balanceamento é conhecido como hierarquia de memória.

3.4 Gerente de Memória O gerente de memória é o módulo do SO que implementa os serviços de memória que um processo precisa. Por exemplo: •

Alocar memória para um processo recém-criado, permitindo ou não o compartilhamento de memória.



Fornecer a possibilidade de um processo crescer ou diminuir em termos de memória. Exemplo: em C o programador faz uma chamada a uma função malloc ou free, ou em java, quando um programador estancia uma determinada classe;



Em alguns SOs, permitir que a demanda por memória seja maior que a quantidade de memória física disponível, através do uso de uma área de troca em disco;



Gerenciar as áreas de memória livre;



E liberar espaço na memória, antes usados pelos processos que terminaram.



Gerenciamento Básico de Memória

3.5 Monoprogramação O Fato de se ter um sistema monoprogramado (um só processo na memória) não dispensa gerência. Esse processo pode precisar crescer, ou simplesmente ocupar um maior espaço que a RAM disponível. Outro detalhe que pode ser percebido é que o Sistema Operacional também deve ocupar a RAM, pois o processo sempre irá requisitar serviços ao SO, e para atendê-los, o SO deve necessariamente está em alguma memória acessível rapidamente pela CPU.

As figuras acima ilustram três formas de disponibilizar memória física para o processo. Na da esquerda o Sistema Operacional e Processo Compartilham a RAM. Na do centro o Sistema Operacional em ROM e Processo em RAM → Alguns computadores têm a capacidade de executar instruções direto da ROM. Na da direita SO e Processo em RAM e Drivers em ROM.

3.6 Modelo de Multiprogramação

Esse modelo ajuda a entender as vantagens de se manter N processos na memória ao mesmo tempo, baseado em seu percentual de E/S. Se no desenvolvimento dos processos os engenheiros tivessem conseguido resolver o problema de E/S ser invariavelmente mais lenta que a CPU, talvez os modelos de gerenciamento de memória fossem construídos de outra forma. O Fato é que todo o processo faz E/S e neste tempo, se não estiver disponível na memória outro processo, a CPU ficará ociosa. Desta forma, o modelo de multiprogramação ajuda reduzir o desperdício de tempo de CPU. Mas qual é o grau mínimo de multiprogramação (quantos processos podem ser colocados na memória) que pode garantir um uso efetivo (100%) de CPU? É importante lembrar que o modelo proposto acima é ideal (o percentual de E/S sempre varia de processo para o outro).

3.7 Gerenciamento Básico - Multiprogramação 3.7.1 Particionamento da Memória - Partições de Tamanho Fixo Para que haja o compartilhamento de tempo da CPU (multitarefa) entre os processos, estes devem está necessariamente na memória (Multiprogramação). A solução trivial para gerenciar a memória para permitir que múltiplos processos a ocupem é o seu particionamento. O sistema operacional precisa saber onde estão os processos para poder passar o controle para eles, e também não deve permitir que um processo acesse dados de outro processo (tarefa impossível sem ajuda do hardware). Essa solução por ser trivial tem alguns problemas: •

Fragmentação Interna: O ideal era que o processo que fosse ocupar uma determinada partição fosse exatamente do tamanho da partição, raramente isso ocorre, o que acontece na realidade é que o processo sempre será menor que a partição, e isso gera um espaço interno que não poderá ser usado por outro processo.



Número fixo de partições: limita o sistema operacional a executar um número máximo de processos.

Filas para acesso as partições

É comum em sistemas multiprogramados que exista vários processos sendo criados num dado instante. Desta forma o sistema operacional deve controlar a forma como as partições deverão ser entregue ao processo. A primeira alternativa é manter uma fila de admissão com os processos e a medida que eles entrarem na fila procurar uma partição vazia para acomodar esse processo. É importante lembrar que quanto mais próximo o tamanho do processo e da partição, menor será o desperdício, a diante discutiremos alguns algoritmos para escolha da partição. Na figura (b) podemos ver um esquema desta técnica. Procurar uma partição que gere o menor desperdício possível, é uma tarefa que pode requerer um certo tempo dependendo do tamanho da quantidade de partições. Se for possível criar fila para as partições esse tempo será reduzido, uma vez que, na criação do processo já será atribuído a ele um tamanho de partição. Observe que esta técnica pode provocar uma espera desnecessária para o processo uma vez que pode existir partições ociosas numa fila para processos maiores.

3.7.2 Particionamento da Memória - Partiçoes de Tamanho Variável Nesta técnica, um processo pode ter o seu inicio e fim em qualquer ponto da memória em que haja espaço livre continuo para alocá-lo. É uma técnica mais flexível que o particionamento fixo e resolve os problemas de fragmentação interna e de limite no número de processos. A técnica de partições variáveis é implementada normalmente com troca de processos. Problemas e Soluções da Troca de Processos O fato do processo poder ocupar qualquer espaço na memória, vai gerando espaços vazios na memória, conforme os processos vão sendo criados e destruídos. Esses espaços gerados são muito pequenos para serem usados por novos processos, esse problema é conhecido como fragmentação externa. Para resolver o problema da fragmentação externa, de tempo em tempos, o sistema operacional pode usar tempo de CPU para agrupar todos os processos no inicio da memória, gerando um espaço livre maior, essa técnica é conhecida como compactação de memória. Na vida real os processos são criados com um tamanho e ao longo do tempo tem o seu tamanho de memória normalmente aumentado (alocação dinâmica de memória). Se um processo está alocado entre dois processos, não há espaço para crescer, e desta forma pode ser trabalhoso para o sistema

operacional tratar solicitações de memória por parte do processo. Para resolver esse problema, pode se usar espaço extra (maior que o necessário) na hora de alocar o processo na memória. A figura abaixo tem duas abordagens para o espaço extra:

3.7.3 Troca de Processos Num sistema que trabalhe com processos em lote é simples usar partições fixas, O processo entra na sua partição e lá permanece até terminar sua execução. Em sistemas de tempo compartilhado interativos, um processo pode ficar indefinidamente esperando por uma resposta do usuário sem processar absolutamente nada, apenas ocupando memória. Em casos como este pode existir vários processos na memória e a CPU pode ainda está ociosa. Em situações como essa podemos usar a troca de processo para guardar no disco, um processo que esteja esperando uma resposta do usuário e assim liberar memória para um processo que precise executar. Quando o sistema envia o processo todo para o disco essa técnica é conhecida como SWAPPING. Quando é possível transferir partes do processo para o disco essa técnica é conhecida como paginação. O uso da troca de processo permite que um sistema possa ter em execução, uma quantidade de processos maior que a quantidade de memória física disponível.

3.7.4 Relocação Uma variável definida em linguagem de alto nível sempre será definida em baixo nível como um endereço de memória. Para exemplificar podemos citar o seguinte trecho de código: I=25; Podemos supor que este código será traduzido em baixo nível para a seguinte trecho, em arquitetura específica. MOV 25, $0x0100 A instrução acima, em assembly, vai mover o literal 25 para o endereço de memória 0x0100. Agora imagine que no modelo de particionamento esse processo poderá ser alocado em uma partição em que o endereço 0x1000 não faça parte. Ou seja o programador ou compilador não tem a mínima ideia onde, na memória, o código será carregado. Esse problema é conhecido como relocação de código e existe algumas técnicas que podem resolver esse problema, uma delas, usada anteriormente era o carregador relocador , que reescrevia as referencias à memória no executável, de forma que este refletisse os endereços da partição que será usada pelo processo.

3.7.5 Proteção Se por acaso algum endereço não pertencente ao espaço de endereçamento do processo for acessado pelo mesmo, isso será considerado uma falha de proteção. A seguir veremos as soluções disponíveis para resolver estes dois problemas.

3.7.6 Soluções para relocação e Proteção Soluções de Software + hardware O Problema da relocação pode ser resolvido por software (sem auxílio de hardware), bastando apenas reescrever as referências a memória, porém a questão da proteção, necessita de apoio de hardware. Fazer o programador trabalhar em relação a um zero, e depois adicionar um endereço de offset à referencia pode resolver o problema da relocação. Digamos que uma partição inicia no endereço 0x3000. No exemplo anterior poderíamos dizer que 0x0100 é um endereço relativo ao inicio da partição, durante o procedimento de carga a instrução seria modificada para 0x3100 (0x100 + 0x3000). Ainda em relação ao exemplo anterior, imagine que para o programa ser alocado será usado uma partição de 1K, ou seja, os endereços que poderão ser acessados vão variar de 0x0100 (endereço 0 da partição) até 0x0500 ( 0x1100+1k) endereço final da partição. No entanto no momento da tradução o endereço relocado foi 0x1100 que é maior que 0x0500 que é o endereço final da partição. Esse acesso claramente viola a proteção entre processos, um acesso deste tipo, se permitido ira modificar o conteúdo de uma posição de memória fora de sua partição, e possivelmente pertencendo a outro processo. A única maneira de evitar esse tipo de acesso é através de hardware. Uma solução criada pela IBM para proteção envolvia o uso de 4 BITs no registrador PSW (STATUS). Com isso a memória do computador era particionada em 16 Partições cada uma com 2K. O SO, tendo acesso privilégiado, escrevia o número da partição nestes bits, e entregava o controle ao processo. Se uma instrução fizesse acesso a um endereço cujo os 4 bits mais significativos fossem diferentes dos da PSW, seria gerada uma interrupção de hardware (GPF), e o processo em questão seria terminado. A comparação entre os bits do endereço e do PSW era feita via HW.

Para fins didáticos a figura acima ilustra uma memória de 64K com 16 partições de 4k. A idéia é descrever como funciona a proteção usando a PSW. Observe que temos 2 instruções, a primeira (A) acessa o endereço 0x0100 (256) do segmento, A segunda (B) o endereço 0x1000 (4096).

No exemplo a instrução pertence a um processo que será alocado na partição 3. Nesta partição os endereços vão de 0x3000 (12288) a 0x3FFF (16383). Antes da execução do código as referências precisam ser relocadas, desta forma o carregador irá rescrever os endereços para 0x3100 (12544=12288+256) e 0x4000 (16384=12288+4096) respectivamente. Dentro da CPU, antes do acesso à memória, é feita a comparação entre os 4 primeiros bits do endereço que está sendo acessado, e o conteúdo armazenado na PSW. O primeiro acesso passará normalmente. Já o segundo gerará uma falha pois o conteúdo da PSW (3) será diferente dos 4 primeiros bits do endereço (4). Soluções em hardware As soluções mostradas anteriormente se baseavam na modificação das referências à memória direto no código. Essas soluções em geral foram ruins pois tinham problemas em situações de troca do processo e cálculo de endereços. A solução por hardware é bem menos complexa, para o programador do sistema operacional, porém encarece o valor da CPU. Um hardware é responsável pela validação e tradução do endereço virtual (programador) para físico (célula de memória específica). Esse hardware é uma unidade de gerência de memória (MMU) elementar. A figura abaixo tem um esquema de funcionamento deste hardware. O SO antes de entregar ao CPU para o processo preenche os registradores BASE e LIMITE na MMU. Toda referência ao endereço é comparada com o registrador limite, caso seja maior é gerada uma interrupção (falha de proteção), se for menor é passada pelo módulo de soma e finalmente cai no barramento de endereços.

3.8 Overlays Pela lei de Parkinson, os programadores sempre querem mais memória, neste caso mais memória que o disponível. Em sistemas de troca de processos isso não é possível. Sendo assim uma solução que surgiu para esse problema é o chamado Overlay (Programadores clipper/DOS vão lembrar disto). Nesta técnica, o programador da aplicação é responsável por particionar o seu código de forma que ele seja chamado em partes. Cada parte quando é chamada substitui a outra na memória RAM da máquina. A solução definitiva para o problema do processo maior que a RAM é a Memória Paginada.

3.9 MEMÓRIA PAGINADA 3.9.1 Conceitos Desde os primeiros sistemas de gerencia de memória não era mais dado ao programador das aplicações a capacidade de acessar diretamente a memória física. Neste ponto surgiu o conceito de memória virtual. Esse espaço de endereçamento manipulado pelo programador, seja por definição de variáveis ou por alocação dinâmica, é conhecido como memória virtual.

Os dados manipulados pelo programa, são armazenados necessariamente em memória física (real), e neste ponto é necessário que haja alguma tradução. O hardware responsável por essa tradução é a unidade de gerência de memória ou MMU. Para o programador o espaço de endereçamento é continuo, porém para permitir que somente partes de um determinado programa esteja na memória, é necessário dividir o programa em fragmentos. Esses fragmentos são conhecidos como página. A pagina pode ser virtual, correspondente ao espaço do programador ou pode ser real, correspondente a pagina realmente ocupada na RAM, esse último tipo de página é conhecido como moldura de página. Em um esquema de gerência de memória por paginação existe uma estrutura que mantém o mapeamento entre páginas e suas respectivas molduras, essa estrutura é conhecida como tabela de páginas. Numa referencia a memória pode ocorrer duas situações: Se a página virtual estiver na memória (se na tabela existir uma moldura equivalente) o programa executa normalmente. Caso não haja moldura correspondente a MMU gera uma interrupção conhecida como PAGE_FAULT. E o programa é interrompido (Bloqueado) até que página em questão (que está em disco) seja alocada em alguma moldura. Mapeamento

A figura acima mostra como funciona o mapeamento de páginas em molduras. A maquina possui uma capacidade de endereçamento de 64K (16bits). Porém a RAM disponível é apenas de 32K. (é a mesma coisa para um Pentium que tem 4G de endereçamento mas tem apenas 64MB RAM). No exemplo, as páginas virtuais que estão com 'X', estão em disco, e qualquer tentativa de acessar um endereço pertencente a qualquer uma destas páginas vai gerar um PAGE_FALT. É importante observar que num esquema de paginação o tamanho da moldura e da página virtual sempre são os mesmos. (Na realidade o SO pode trabalhar com páginas de tamanhos que sejam múltiplos do tamanho da moldura). Unidade de Gerencia de Memória É um HARDWARE fica posicionado entre a CPU e a memória (registrador MAR e barramento de endereços), é responsável pela validação dos acessos (proteção) e tradução (relocação) das páginas virtuais em físicas. Não confundir com o sistema de gerencia de memória do sistema operacional. Pode gerar interrupções relacionadas a falta de páginas, como também falha proteção.

Conversão Endereço Virtual em Físico

A conversão de endereços ocorre apenas nos bits mais significativos. O esquema acima mostra a conversão com base no exemplo da figura anterior. Ou seja com um intervalo de endereçamento de 64k e páginas de 4k termos 16 páginas virtuais (015) isso interfere nos quatro bits mais significativos do endereço virtual, pois o resto dos bits (12) são usados para acessar individualmente o dado dentro da página. Vejamos como funciona a tradução: Na parte de baixo do esquema o endereço virtual vindo da CPU na parte de cima o endereço traduzido (real) que vai para o barramento de endereços. Vamos analisar o acesso ao endereço virtual 8196 (0010000000000100) Esse endereço está na página virtual 2 (0010). Segundo a tabela do esquema acima a pagina virtual 2 está na moldura 6 (0110) então o endereço será convertido para: (0110000000000100). O bit mais significativo pode ser ignorado uma vez que só existem 32K (15bits) endereços reais. Tabela de páginas Serve para guardar, dentre outros dados, o mapeamento página virtual, real. A cada referência à memória é feita uma tradução. Isso significa que o acesso a esta estrutura é critico para a performance do sistema. Uma tabela de página pode ter um número grande de páginas, por isso as técnicas de busca nesta tabela devem ser bem trabalhadas. Em geral existem formas de armazenar essa tabela, e quanto mais rápido for a memória em que estiver armazenada, maior vai ser a velocidade de acesso. Implementar essa tabela em hardware, traria ganhos na velocidade de tradução, porém a quantidade de memória necessária para esse armazenamento pode tornar o projeto caro. A abordagem mais barata é armazenar na RAM, porém o acesso é lento. Essa é a solução usada hoje, porém com certas modificações (mas a frente veremos como a TLB ajuda a melhorar a velocidade). Tabelas de Páginas Multinível A implementação linear da tabela de páginas leva a um problema: O número de entradas pode ser

muito grande. Por exemplo: Na arquitetura Intel as páginas são de 4k e o espaço de endereçamento é de 4G. O resultado é que uma tabela de página para um processo pode ter mais de um milhão de entradas. Desta forma cada acesso a memória implica numa busca nestas entradas. Um solução que surge para resolver esse problema é dividir essa busca em níveis, diminuindo assim o escopo da busca. Ex: Para 1M páginas é necessário 20bits. Se as páginas fossem organizadas em diretórios de 1K. Teriamos 1K diretórios com 1K páginas. Então para buscar um endereço seria necessário pesquisar primeiro a tabela de diretórios de páginas “raiz” com no máximo 1k entradas, e depois pesquisar na tabela de páginas posterior, também com 1k entradas. Desta forma ao invés de buscar em 1M entradas, a busca será feita em 2k entradas. Essa implementação de tabelas multinível pode ser feita para diversos níveis, é fácil perceber que quanto mais níveis existir menor será o número de entradas pesquisadas, porém a complexidade do sistema de paginação é aumentada. O sistema operacional GNU/Linux usa 3 níveis, porém existe uma modificação (patch) para usar 4 níveis. (ref: http://lwn.net/Articles/106177/). O MS/Windows usa dois níveis (Carece de referencias). Formato de uma Entrada na tabela de páginas A tabela de páginas além de possuir informações que permite o mapeamento da memória física em virtual, também guarda outras informações necessárias para o uso do sistema operacional. São elas: •

Número da moldura: Pagina Real



Presente/Ausente: Existe moldura equivalente? (esta na RAM)?



Proteção: Simples (rw), Sofisticado (rwx)



Referenciada: A pagina já foi usada?



Modificada: A pagina foi alterada?



Cache desabilitado: A página pode ser colocada na memória cache da(s) CPU(s).

TLB As TLBs, também conhecidas como memória associativa, são dispositivos de hardware cujo propósito é mapear endereços virtuais em endereços físicos sem passar pela tabela de páginas na memória. Usualmente, ela faz parte da MMU. Ela constitui-se de um pequeno número de entradas, muito rápidas, contendo as entradas das tabelas de páginas mais utilizadas. Quando um endereço virtual é enviado a MMU, ela primeiramente verifica se o seu número de página virtual está presente na TLB. Se o resultado for positivo, a moldura de página é tomada diretamente da TLB sem a necessidade de passar pela tabela de páginas. Caso contrário, a pesquisa é feita normalmente na tabela de páginas. Uma das entradas é removida da TLB e a entrada da tabela de páginas pesquisada é colocada em seu lugar.

3.9.2 Algoritmos de substituição de páginas Como visto anteriormente, sempre que uma página (endereço virtual) não estiver em uma moldura de página, uma interrupção ocorre e ela deve ser carregada para uma moldura antes de ser executada. No entanto, alguma página que está atualmente em uma moldura deve ser retirada (gravada em disco). Os algoritmos de substituição de páginas se preocupam em escolher a melhor página a ser retirada da moldura de forma a garantir o uso mais eficiente dos recursos do sistema. Algoritmo ótimo

Deve ser retirada a página que só será referenciada o mais tarde possível. Apesar de, teoricamente, ser um algoritmo interessante, é extremamente difícil prever quando uma página será referenciada; Substituição de página não recentemente utilizada o S.O. mantem uma coleção de estatísticas sobre as páginas referenciadas e/ou modificadas (através dos bits de referência e modificação das entradas da tabela de páginas) e dão preferência para a troca de páginas não referenciadas e/ou não modificadas; Substituição de página FIFO – first-in first-out A página mais antiga é removida. O principal problema desta abordagem é o fato de se remover uma página bastante utilizada; Substituição de página de segunda chance Uma modificação do algoritmo FIFO, que busca não substituir uma página antiga e bastante utilizada. A solução é inspecionar o bit R (referenciada) da página mais antiga; se o bit for 1 (foi referenciada) o bit será limpo e a pesquisa continua. Se todas as páginas tiverem sido referenciadas, o algoritmo FIFO acaba sendo executado e a página mais antiga (que agora estará com o bit R limpo) será substituída; Relógio O algoritmo do relógio trabalha da mesma forma que o segunda chance, a diferença é que a fila de páginas é circular, o que o torna mais performático uma vez que não é preciso reinserir páginas no final da fila, bastando apenas ir para o próximo. Substituição de página menos recentemente utilizada (MRU) A ideia é que as páginas que foram intensamente utilizadas nas últimas instruções provavelmente serão utilizadas de forma intensa no futuro próximo. Desta forma, deve ser removida a página que não foi utilizada por mais tempo. Conjunto do Trabalho Num sistema que use a paginação pura, ao iniciar um programa, nenhuma página seria colocada na memória, e a medida que existir a necessidade as páginas seriam carregadas, essa técnica é conhecida como paginação por demanda. A questão é que uma falta de página é um evento que atrasa processo, pois enquanto o tempo de execução de uma instrução é da ordem de nanosegundos, a busca de uma página no disco chega a demorar milisegundos. Sendo assim, os eventos de falta de páginas devem ser evitados ao máximo, isso pode ser feito fazendo boas escolhas sobre as páginas que devem ir a disco, e tentar escolher para seu lugar páginas que serão usadas num futuro próximo (pré-paginação). A ideia deste algoritmo é esta, tentar minimizar a ocorrência de eventos de faltas de páginas, mas como isso pode ser feito? Ao se analisar o comportamento de uso das páginas dos processos em geral percebe-se que em dado intervalo de tempo, o processo acessa somente uma pequena quantidade de suas páginas. Essa propriedade dos processos é conhecida como localidade de referencia. E as páginas são chamadas de conjunto do trabalho. A medida que o processo segue seu fluxo de execução, esse conjunto vai mudando lentamente. Então a cada falta de página, será escolhida, para ir para disco, uma página do processo que não pertence ao conjunto do trabalho. O problema é como decidir se uma página pertence a esse conjunto. A ideia original do algoritmo para resolver isto é marcar as páginas de forma a saber quais páginas foram usadas nas ultimas k referencias. Se uma página não foi usada nas ultimas k referencias então ela não pertence ao conjunto do trabalho. Na prática o que usado ao invés de k é o tempo, ou seja, se uma página não foi referenciada nos últimos t milissegundos ela não pertence ao

conjunto trabalho.

3.9.3 Questões de Projeto Vimos como funciona um sistema de paginação, agora veremos alguns dos aspectos que devem ser observados na hora de projetar um sistema destes: Politica de Alocação: Na hora da substituição da página, o escopo para escolha da página abrange todas as páginas de todos os processos ou somente páginas do processo que sofreu o page fault? Controle de Carga: Mesmo com o algoritmo de substituição de páginas ótimo, pode ocorrer eventos de paginação excessiva (thrashing). Isso deve ocorrer sempre que o conjunto de páginas necessárias for maior do que a RAM. Ou seja algum processo precisa de mais páginas e nenhum outro processo pode trabalhar com menos páginas, assim pode ser interessante colocar um processo todo em disco. Tamanho da página: Em geral o tamanho da página é definido pelo hardware porém é possível implementar em nível de SO, paginas que tenha tamanhos que seja múltiplos do tamanho de página fornecido pelo hardware. Espaços separados para instruções e Dados: Algumas poucas arquiteturas apresentam 2 memórias (Dados e programa). Nestes casos, a paginação deve ser feita para as duas memórias independentemente. Paginas compartilhadas: Em geral qualquer processo pode ser divido em dados e instruções. Diferentes instâncias de um programa podem ser carregadas na memória. Um sistema de gerencia de memória que permita que cada processo tenha sua própria área de dados mas possa compartilhar as instruções pode trazer uma grande economia de memória. Politica de Limpeza: O sistema de paginação funciona melhor se existir molduras livres. Sendo assim, ter um serviço responsável por inspecionar a memória em busca de possíveis páginas candidatas para ir a disco pode ser uma boa ideia. O serviço usará o algoritmo de substituição de páginas para achar essas páginas. Interface de Memória Virtual: Prover ou não prover uma forma do programador decidir como o sistema de paginação vai tratar as páginas do seu programa? Um exemplo disto é dar o poder ao programador de dizer quais paginas devem ser compartilhadas.

3.9.4 Questões de Implementação Tratamento de Falta de Página Quando um processo faz referencia a um endereço que não está em uma moldura, o processo não pode seguir até que o endereço referenciado esteja em alguma moldura. Aqui vamos descrever a sequencia de acontecimentos e ações que o SO precisa tomar para garantir que o processo em questão possa continuar. 1. Interrupção de HW (Page Falt): Uma interrupção é gerada e o controle volta para o sistema operacional. 2. Salvamento do Contexto: É a primeira coisa que o tratador da interrupção precisa fazer. Os dados do contexto do processo atual precisam ser salvos para que o processo possa continuar quando a página estiver disponível. 3. Tratamento da Interrupção: descobrir qual página precisa ser recuperada. Como fazer isso? a) Através de algum registrador no hardware específico para este fim. b) Ou através do PC (do contexto) recuperar a instrução para descobrir o endereço que causou a falta. 4. Verificação do endereço: O endereço é válido? o tipo de acesso que está sendo feito é valido?

Existe moldura disponível? Chamar o algoritmo de substituição? 5. Se a moldura precisar ser salva (foi modificada), vai ser necessário acionar o subsistema de E/S, assim o processo é marcado como bloqueado e outro processo deve ser escalonado, entretanto a moldura deve ser marcada como indisponível para que outro processo não tente acessá-lo. 6. A página faltante deve ser carregada em uma moldura, até esse momento o processo continua bloqueado. 7. Quando o disco informar o termino da operação a tabela de páginas deve ser atualizada para refletir essa nova situação (a pagina agora está em uma moldura). 8. É importante lembrar que a instrução que causou a falta deve ser reexecutada, pois do ponto de vista do contexto a mesma foi executada, o sistema operacional deve garantir que essa instrução será reexecutada quando o seu contexto for restaurado. 9. A página está disponível, o processo já pode ser executado, então deve-se marcá-lo como pronto. 10. O processo ao ser escalonado tem o seu contexto restaurado.

4 Gerencia de E/S 4.1 Introdução Como vimos anteriormente os processos fazem solicitações ao hardware através do SO. Esse hardware muitas vezes comunicam o sistema computacional ao mundo externo, representado normalmente por um dispositivo conhecido como periférico. Cada fabricante tem a sua forma de construir e de interfacear o periférico com o sistema de E/S. No entanto para isso não deve afetar a forma como o processo interage como o dispositivo. Por exemplo: O processo vai imprimir da mesma forma independente se a impressora é HP ou EPSON. Sendo assim o principal problema do Subsistema de E/S é fornecer uma interface para que os processos e os outros subsistemas do SO possam acessar o hardware de forma mais independente possível. Funções do Gerenciador de E/S Toda vez que um processo solicita uma escrita no disco por exemplo. O SO, através do Gerenciador de E/S é quem envia comandos para os dispositivos (através de seu controlador). Em geral, o hardware é mais lento que a CPU, e para que esta não fique monitorando, existe a interrupção, quando o hardware termina a sua tarefa, ele envia um sinal para a CPU que interrompe o que está fazendo para tomar as providencias relacionadas ao termino daquela tarefa. Os demais subsistemas do SO também fazem E/S. Por exemplo, o sistema de gerencia de memória escreve e ler páginas do disco. Sendo assim é mais prático solicitar ao sistema de E/S que implementar novamente rotinas de acesso ao hardware. O processo quer escrever da mesma forma, independentemente se a escrita está sendo feita em disco rígido, pen drive ou disquete. Sendo assim o SO deve prover uma interface uniforme para estes acessos. Princípios do Hardware de E/S Aqui não trataremos aspectos do projeto do hardware, e sim os aspectos relacionados a programação do sistema de gerenciamento de E/S. Mas é importante ter em mente que abstrair o funcionamento do hardware nem sempre é possível. A maioria dos sistemas operacionais oferece serviços de E/S tendo como base dois tipos distintos de dispositivo: Bloco e Caractere. A característica mais importante dos dispositivos de caractere é poder ler ou escrever apenas um caractere por vez. Esses dispositivos também não suportam o conceito de endereçamento. São exemplos deste tipo de dispositivos, modems, portas seriais e paralelas. Já os dispositivos de Bloco, são lidos em blocos, e têm o conceito de unidade de alocação. Ou seja, mesmo que seja escrito um byte no nível mais alto, esse byte no baixo nível vai ocupar uma unidade de alocação. Outra característica destes de dispositivo, é a possibilidade de escolher, através de endereçamento, qual bloco que será lido ou escrito. São exemplos destes tipos de dispositivos discos rígidos e CDROMs. É importante lembrar que existem exceções a essa regra.

Controladores de Dispositivo Os sistemas computacionais nunca trabalham diretamente sobre o dispositivo, ao invés disto, eles interagem com o controlador e esse por sua vez interage com o dispositivo. Sendo assim os controladores de dispositivo são responsáveis por fazer a interface entre o dispositivo ou periférico e o sistema computacional. Ficam conectados a algum barramento interno (PCI, ISA, USB). Os periféricos por sua vez se conectam á controladora através de um conector, ex: IDE, SATA, RS232, USB. O envio de comandos, leitura e escrita de dados é feito através de registradores internos do controlador. A seguir veremos as formas de escrever e ler destes registradores. Comunicação Ler ou escrever em dispositivo ou ainda mandar um comando consiste basicamente em escrever em registradores internos da sua controladora. A escrita nestes registradores é feita através de endereços e existem duas abordagens para isso. A primeira cria portas, que nada mais são que endereços, e para escrever nestas portas são necessárias instruções especiais, justamente para não confundir essa escrita com a escrita em memória. A segunda abordagem mapeia os registradores internos em endereços da memória, e para ler ou escrever destes endereços é preciso basicamente ler ou escrever em uma posição de memória. Desta forma não é necessários o uso de instruções especiais.

4.2 Esquemas de E/S

Na figura (a) temos o esquema com endereços especiais de E/S. Na figura (b) os espaços de endereçamento são os mesmos da RAM. Na figura (c) temos um método híbrido onde o controlador pode ser acessado tanto através de endereços de memória, como através de portas de E/S. O Pentium usa um esquema como este último. Os endereços de 0-64k são usados como portas de E/S e devem ser acessados através de instruções específicas (IN e OUT). Já os endereços 640K a 1M são usados para buffers relacionados a dispositivos.

Barramentos

A Figura (a) mostra a máquina de Von Neumann original previa um barramento único que iria interligar CPU, E/S e memória. Durante muitos anos os sistemas computacionais trabalharam desta forma. O problema com essa abordagem é que em um barramento uma comunicação é possível por vez, e desta forma a leitura e escrita na memória teria que concorrer com os demais dispositivos. Na Figura (b) podemos ver um modelo onde existe um barramento exclusivo para a memória. Isso torna o acesso à memória mais rápido uma vez que não existe concorrência.

4.3 DMA (Direct Memory Access) Para ler ou escrever dados na controladora é usada a CPU, essa cópia é feita palavra por palavra, esse tipo de abordagem acaba desperdiçar muitos ciclos de CPU, pois a CPU é sempre mais rápida que o dispositivo. Para resolver essa questão, a ideia é usar outro dispositivo para fazer essa transferência: O Controlador de DMA, a função dele é transferir dados entre o controlador de dispositivos e a memória, nos dois sentidos. O esquema de uso de DMA, pode ser distribuído, onde a controladora de dispositivo tem também um controlador de DMA. Outro esquema, mais usado, é ter um controlador de DMA, único na placa mãe, e este interage com os diferentes dispositivos. O controlador de DMA é controlado através dos seus registradores internos. Um típico controlador de DMA pode ter os seguintes registradores internos: •

Endereço de Memória: de onde ou para onde na memória



Contador de Palavras: Quantas palavras serão transferidas



Porta de E/S: de qual ou para qual dispositivo



Sentido: Leitura ou escrita

4.3.1 Funcionamento

O controlador de DMA é programado pela CPU através de seus registradores internos. Então sempre que a CPU desejar transferir dados entre memória e controlador de dispositivo através de DMA, os seguintes eventos ocorrem: 1. A CPU Programa o controlador: Informa endereço na memória onde o dado será armazenado, a quantidade de dados que será transferida, e o tipo de operação (leitura ou escrita); 2. O controlador de DMA solicita o dado a controladora de dispositivo; 3. O controlador transfere o dado direto para a memória. (existem esquemas onde o dado precisa passar por um registrador interno do controlador de DMA, isso tem vantagem de poder fazer transferências diretas entre dispositivos ou memória para memória. 4. O controlador de Disco confirma a transferência, o Controlador de DMA decrementa o contador e incrementa o endereço. Se o contador não for zero os eventos 2 a 4 ocorrem novamente. Se o contador for zero o controlador de DMA então interrompe a CPU.

5 Sistemas de Arquivos Todo sistema computacional deve ter uma forma de armazenamento de dados que possa garantir que a informação sobreviva ao desligamento do sistema. Os usuários querem ter uma forma efetiva de acessar seus arquivos sem ter que se preocupar onde os seus dados vão ficar. Para fixar o conceito imagine que na sua empresa existe um local para armazenar os seus documentos pessoais, e que lá existe um armário com uma divisória para cada funcionário. Em um primeiro caso poderíamos imaginar que os próprios funcionários teriam que armazenar os seus documentos. Neste caso, a empresa teria que contar com o bom senso de seus colaboradores para evitar eventuais desvios (documentação misturada, extravios, etc). Tanto nas empresas como em um sistema operacional, essa gerencia baseada no bom senso não é uma boa ideia. Segundo caso, imagine que a empresa contratou o FS (Francisco Silva), para armazenar seus documentos no armário, você agora só precisa entregar os seus papéis ao FS, e ele vai providenciar o armazenamento dentro dos padrões de conformidade. Quando você precisar da sua documentação basta pedir ao FS que ele encontra para você e entrega. Em temos de Sistemas Operacionais o FS, é o File System ou Sistema de Arquivos, responsável por prover o armazenamento e recuperação dos dados.

5.1 Introdução Para que os usuários possam acessar os seus dados, a maneira que os sistemas fazem isso hoje é fornecer duas abstrações: Arquivos e Diretórios (também chamados de pastas). Com essas abstrações os usuários conseguem localizar os seus dados em discos com grande números de blocos apenas informando o diretório e o nome do arquivo. Esse é o principal papel do sistema de arquivos. Conforme o tempo passa os discos aumentam e os arquivos também. Os bons sistemas de arquivos devem dar a possibilidade de armazenar grandes arquivos e acessar o seu conteúdo de forma rápida e eficiente. Outra característica de um bom sistema de arquivos é o fato de lidar com acessos concorrentes ao mesmo dado. Em algum momento vários processos podem acessar simultaneamente um determinado dado, para ler ou alterar o seu conteúdo. Nomeação Para acessar um determinado dado armazenado em sistema, o usuário (processo) deve informar o diretório e o nome do arquivo, a partir daí a localização do dado fica por conta do usuário. Os diferentes sistemas operacionais tratam essa nomeação de maneira semelhante, porém com algumas particularidades que vamos citar aqui. Os sistemas baseado no Unix, como por exemplo o Linux, possuem um esquema de nomeação onde a “caixa” (maiúscula ou minúscula) determina arquivos diferentes, por exemplo: Teste.txt, TESTE.TXT e teste.TXT são arquivos diferentes. Já para o MS/Windows esses arquivos correspondem ao mesmo conjunto de dados. Outra característica é a extensão: Sistemas baseados em Unix não se baseiam em extensão. Ou seja

o que vem depois do '.' é uma informação que é util apenas em nível de usuário. Pois o SO não vai tratar o arquivo de uma maneira especial tendo como base a sua extensão. Já o MS/Windows, por exemplo, só executa arquivos com determinadas extensões (com, exe, pif, bat, etc). Arquivos

Os sistemas operacionais mais modernos não se preocupam com a estruturação interna dos arquivos, a consequência disto é que, o processo do usuário tem que se preocupar com a forma que os arquivos estão estruturados. A Figura (a) ilustra essa situação. Windows e Linux se encaixam nesta categoria. Diferentemente dos atuais, nos sistemas operacionais mais antigos, o SO fornece uma estruturação elementar para o conteúdo dos arquivos. Dois exemplos são dados, Figura (b), estruturação em registros, e Figura (c) estruturação hierárquica. Tipos de Arquivos Muitos sistemas operacionais classificam os arquivos conforme a sua finalidade, outros podem classificar conforme o seu conteúdo. Porém todos os sistemas apresentam pelo menos dois tipos de arquivo: Texto com alguma codificação específica: ASC-II ou UTF-8, ou binários, arquivos com bytes não “imprimíveis”, que tem a sua finalidade compreendida apenas pelos programas que o usam.

More Documents from "Joao Lucas Mota"

April 2020 33
May 2020 30
November 2019 31