Apostila da disciplina de Fundamentos de Sistemas Operacionais Arquitetura de Sistemas Operacionais 2010 Apostila Fundamentos de Sistemas Operacionais . Capítulo 0 - A EVOLUÇÃO DOS SISTEMAS OPERACIONAIS ...................................................................... 04 0.1 A Primeira Geração (1945-1955): Válvulas e Painéis com Plugs ........................................................ 04 0.2 A Segunda Geração (1955 - 1965): Transistores e Sistemas Batch .................................................... 05 0.3 A Terceira Geração (1965 - 1980): CIs e Multiprogramação .............................................................. 08 0.4 A Quarta Geração (1980-1990): Computadores Pessoais .................................................................. 11 0.5 1990/97: a Era Windows ................................................................................................................ 13 Capítulo 1 - VISÃO GERAL ........................................................................................................................ 19 1.1 Introdução ..................................................................................................................................... 19 1.2 Funções básicas ............................................................................................................................. 19 1.3 Máquina de níveis .......................................................................................................................... 20 1.4 Tipos de Sistemas Operacionais ....................................................................................................... 22 1.4.1 SOs monoprogramáveis/monotarefa ................................................................................ 22 1.4.2 SOs multiprogramáveis / multitarefa ................................................................................ 23 1.4.2.1 Sistemas Batch ............................................................................................... 25 1.4.2.2 Sistemas de Tempo compartilhado ................................................................... 25 1.4.2.3 Sistemas de Tempo Real ................................................................................. 26 1.4.3 Sistemas com Múltiplos Processadores ............................................................................. 26 1.4.3.1 Sistemas fortemente acoplados ........................................................................ 27 1.4.3.2 Sistemas fracamente acoplados ....................................................................... 28 Capítulo 2 - CONCEITOS DE HARDWARE E SOFTWARE ........................................................................... 31 2.1 Introdução ..................................................................................................................................... 31 2.2 Hardware ....................................................................................................................................... 31 2.2.1 Processador ................................................................................................................... 31 2.2.2 Memória ........................................................................................................................ 32 2.2.3 Memória Cache .............................................................................................................. 32 2.2.4 Memória Principal e Secundaria ....................................................................................... 32 2.2.5 Dispositivos e Entrada/Saída ............................................................................................ 33 2.2.6 Barramentos ou Bus ........................................................................................................ 33 2.2.7 Pipeline .......................................................................................................................... 35 Capítulo 3 - CONCORRÊNCIA .................................................................................................................... 36 3.1 Introdução ..................................................................................................................................... 36 3.2 Interrupção e Exceção .................................................................................................................... 38 3.3 Operações de Entrada/Saída ........................................................................................................... 40 3.4 Buffering ....................................................................................................................................... 42 3.5 Spooling ........................................................................................................................................ 43 3.6 Reentrância .................................................................................................................................. 43 3.7 Proteção do Sistema ....................................................................................................................... 44 Capítulo 4 - ESTRUTURA DO SISTEMA OPERACIONAL ............................................................................ 45 4.1 Introdução .................................................................................................................................... 45 4.2 Principais Funções ......................................................................................................................... 46 4.3 System Calls ................................................................................................................................. 47 4.4 Modos de Acesso .......................................................................................................................... 48 4.5 Arquitetura Monolítica ................................................................................................................... 51 4.6 Arquitetura de Camadas ................................................................................................................ 52 4.7 Máquina Virtual ............................................................................................................................ 53 4.8 Arquitetura Microkernel ................................................................................................................. 55 4.9 Projeto do Sistema ...................................................................................................................... 57 Capítulo 5 - PROCESSO ............................................................................................................................. 58 5.1 Introdução ..................................................................................................................................... 58 5.2 Estrutura do Processo ................................................................................................................... 58 5.2.1 Contexto de hardware .................................................................................................... 59 5.2.2 Contexto de software .................................................................................................... 60 5.2.3 Espaço de endereçamento ............................................................................................. 62 5.2.4 Bloco de controle do processo ........................................................................................ 62 5.3 Estados do Processo ....................................................................................................................... 63 5.4 Mudanças de Estado de Processo ....................................................................................................64 5.5 Criação e Eliminação de Processos .................................................................................................. 66 5.6 Processos Independentes, Subprocessos e Threads ......................................................................... 68 2 Apostila Fundamentos de Sistemas Operacionais . 5.7 Processos Foreground e Background ............................................................................................... 70 5.8 Processos do Sistema Operacional .................................................................................................. 71 5.9 Processos CPU-Bound e I/O-Bound ................................................................................................ 72 5.10 Sinais ....................................................................................................................................... 73 Capítulo 6 – THREAD ................................................................................................................................ 75 6.1 Introdução ..................................................................................................................................... 75 6.2 Ambiente Monothread .................................................................................................................... 75 6.3 Ambiente Multithread ..................................................................................................................... 77 6.4 Arquitetura e Implementação ......................................................................................................... 82 6.4.1 Threads em Modo Usuário ............................................................................................. 82 6.4.2 Threads em Modo Kernel ............................................................................................... 84 6.4.3 Threads em Modo Híbrido ............................................................................................. 85 6.4.4 Scheduler Activations ..................................................................................................... 86 6.5 Modelos de Programação ................................................................................................................ 87 Capítulo 7 - Sincronização e Comunicação entre processos .................................................................. 88 7.1 Introdução ..................................................................................................................................... 88 7.2 Aplicações Concorrentes ............................................................................................................... 88 7.3 Especificação de Concorrência em Programas .................................................................................. 89 7.4 Problemas de Compartilhamento de Recursos ................................................................................... 91 7.5 Exclusão Mútua ............................................................................................................................. 93 7.5.A Starvation ................................................................................................................... 93 7.5.B Sincronização condicional .............................................................................................. 94 7.5.1 Soluções de Hardware ................................................................................................... 94 7.5.2 Soluções de software ...................................................................................................... 95 7.6 Semáforos .................................................................................................................................... 95 7.7 Monitores ...................................................................................................................................... 97 7.8 Troca de mensagens ..................................................................................................................... 98 7.9 Deadlock ....................................................................................................................................... 99 7.9.1 Prevenção do Deadlock ................................................................................................ 100 7.9.2 Detecção de Deadlock ................................................................................................... 100 7.9.3 Correção do Deadlock ............................................................................................... 100 Capítulo 8 - Gerência do Processador ................................................................................................... 102 8.1 Funções ................................................................................................................................ 102 8.2 Critérios de escalonamento ...................................................................................................... 102 8.3 Escalonamentos Não-Preemptivos e Preemptivos ........................................................................ 103 Capítulo 9 - Gerência de Memória / Memória Virtual .......................................................................... 107 9.1 Introdução ....................................................................................................................... 107 9.2 Funções ........................................................................................................................... 107 9.3 Alocação Contígua Simples ................................................................................................. 108 9.4 Segmentação de Programas ............................................................................................... 109 9.5 Alocação Particionada Estática ............................................................................................ 110 9.6 Alocação Particionada Dinâmica ........................................................................................... 111 9.7 Estratégias de Alocação de Partição ..................................................................................... 112 9.8 Swapping ......................................................................................................................... 113 9.9 Memória Virtual ................................................................................................................ 113 9.10 Algoritmos de substituição de páginas .................................................................................. 116 Capítulo 10 - Gerência de Sistemas de Arquivos .................................................................................. 118 10.1 Estrutura de Diretórios ..................................................................................................... 118 10.2 Sistemas de alocação de arquivos ...................................................................................... 118 10.3 Gerência de espaço livre ................................................................................................... 119 10.4 Proteção de acesso ............................................................................................................ 120 Bibliografia ............................................................................................................................................. 121 3 Apostila Fundamentos de Sistemas Operacionais . Capítulo 0 A EVOLUÇÃO DOS SISTEMAS OPERACIONAIS Os sistemas operacionais têm sido historicamente amarrados à arquitetura dos computadores nos quais iriam rodar. Por isso, veremos como eles evoluiram nas sucessivas gerações de computadores. Esse mapeamento entre gerações de computadores e gerações de sistemas operacionais é admissivelmente imaturo, mas tem algum sentido. O primeiro computador digital verdadeiro foi projetado pelo matemático inglês Charles Babbage (1792-1871). Embora Babbage tenha dispendido muito de sua vida e de sua fortuna tentando construir sua "máquina analítica", ele jamais conseguiu por o seu projeto em funcionamento porque era simplesmente um modelo matemático e a tecnologia da época não era capaz de produzir rodas, engrenagens, dentes e outras partes mecânicas para a alta precisão que necessitava. Desnecessário se dizer que a máquina analítica não teve um sistema operacional. 0.1 - A Primeira Geração (1945-1955): Válvulas e Painéis com Plugs Após os esforços sem sucesso de Babbage, pouco progresso se teve na construção de computadores digitais até a Segunda Guerra Mundial. Em torno de 1940, Howard Aiken em Harvard, John Von Neumann no Instituto para Estudos Avançados em Princeton, John Presper Eckert e William Mauchley na Universidade de Pennsylvania e Konrad Zuse na Alemanha, entre outros, tiveram sucesso na construção de máquinas calculadoras usando válvulas. Essas máquinas eram enormes, ocupando salas completas, com dezenas de milhares de válvulas, porém eram muito mais lentas do que os mais simples computadores pessoais de hoje. Naqueles dias primitivos, um pequeno grupo de pessoas construiu, programou, operou e deu manutenção a cada máquina. Toda a programação era feita em linguagem de máquina, sempre se conectando fios com plugs em painéis para controlar as funções básicas da máquina. As linguagens de programação não eram conhecidas (nem a linguagem Assembly). Nem se ouvia falar em sistemas operacionais. O modo usual de operação consistia no programador elaborar o programa numa folha e então ir à sala da máquina, inserir os plugs nos painéis do computador e gastar as próximas horas apelando que nenhuma das 20.000 ou mais válvulas não se queimasse durante a execução do programa. Na verdade, todos os problemas eram inerentemente sobre cálculos numéricos tais como gerações de tabelas de senos e cossenos. Por volta dos anos 50, essa rotina teve uma pequena evolução com a introdução de cartões perfurados. Era possível, a partir de então, se escrever programas em cartões e lê-los, em vez do uso de plugs em painéis; no mais, o procedimento era o mesmo. 4 Apostila Fundamentos de Sistemas Operacionais . 0.2 - A Segunda Geração (1955 - 1965): Transistores e Sistemas Batch A introdução do transistor em meados dos anos 50 mudou o quadro radicalmente. Os computadores tornaram-se bastante confiáveis para que pudessem ser produzidos e vendidos comercialmente na expectativa de que eles continuassem a funcionar por bastante tempo para realizar algumas tarefas usuais. A princípio havia uma clara separação entre projetistas, construtores, operadores, programadores e o pessoal de manutenção. Essas máquinas eram alocadas em salas especialmente preparadas com refrigeração e com apoio de operadores profissionais. Apenas grandes companhias, agências governamentais, ou universidades, dispunham de condições para pagar um preço de multimilhões de dólares por essas máquinas. Para rodar um job (isto é, um programa ou um conjunto de programas), primeiro o programador escrevia o programa no papel (em FORTRAN ou linguagem Assembly), e então perfurava-o em cartões. Daí, ele levava o "deck" de cartões à sala de recepção e o entregava a um dos operadores. Quando o computador encerrava a execução de um job, um operador apanhava a saída na impressora, a conduzia de volta à sala de recepção onde o programador poderia coletá-lo posteriormente. Então ele tomava um dos decks de cartões que tinha sido trazido da sala de recepção e produzia a sua leitura. Se o compilador FORTRAN era necessário, o operador tinha que pegá-lo de uma sala de arquivos e produzir a sua leitura. Muito tempo de computador era desperdiçado enquanto os operadores caminhavam pela sala da máquina para realizarem essas tarefas. Devido ao alto custo do equipamento, era de se esperar que as pessoas tentassem reduzir o tempo desperdiçado. A solução geralmente adotada era o sistema em "batch". A idéia original era colecionar uma bandeja completa de jobs na sala de recepção e então lê-los para uma fita magnética usando um computador pequeno e relativamente barato, por exemplo o IBM 1401, que era muito bom na leitura de cartões, na cópia de fitas e na impressão da saída, porém não era tão bom em cálculo numérico. Outros computadores, máquinas mais caras, tais como o IBM 7094, eram usados para a computação real. Essa situação é mostrada na figura 0.1. 5 Apostila Fundamentos de Sistemas Operacionais Unidade . Entrada Sistema da Fita de Fitas de Fita Saída de Fita Leitora de Impressora cartões 1401 (a) (b) 7094 (c) (d) 1401 (e) (f) Figura 0.1 - Um sistema “batch” antigo. ( a ) Programadores levam cartões ao 1401. ( b ) 1401 lê batch de jobs em fita. ( c ) A operadora acopla fita de entrada no 7094. ( d ) O 7094 faz o processamento. ( e ) A operadora acopla fita de saída no 1401. ( f ) O 1401 imprime a saída. Após cerca de uma hora coletando-se um lote de jobs, a fita era rebobinada e levada para a sala da máquina onde era montada numa unidade de fita. O operador então carregava um programa especial (o antecessor do sistema operacional de hoje), que lia o primeiro job da fita e o executava. A saída era escrita numa segunda fita, em vez de ser impressa. Após o fim da execução de cada job, o sistema operacional automaticamente lia o próximo job da fita e começava a executá-lo. Quando todo o "batch" era feito, o operador removia as fitas de entrada e de saída, substituia a fita de entrada pelo próximo "batch" e levava a fita de saída para um 1401 produzir a impressão "off-line" (isto é, não conectada ao computador principal). A estrutura de um job de entrada típico é mostrada na figura 0.2. Ele começa com um cartão $JOB, especificando o tempo máximo de execução em minutos, o número da conta e o nome do programador. A seguir vinha um cartão $FORTRAN, avisando ao sistema operacional para carregar o compilador FORTRAN da fita do sistema. Em seguida vinha um programa a ser compilado, acompanhado de um cartão $LOAD, informando ao sistema operacional para carregar o programa objeto já compilado. (Programas compilados eram sempre escritos em fitas selecionadas e tinham 6 Apostila Fundamentos de Sistemas Operacionais . de ser carregadas explicitamente). A seguir vinha um cartão $RUN, informando ao sistema operacional para executar o programa com os dados que vinham a seguir. Finalmente o cartão $END marcava o fim do job. Esses cartões de controle foram os precurssores das linguagens de controle de job (JCL) modernas e de interpretadores de comandos. Muitos computadores da segunda geração foram usados principalmente para cálculos científicos e de engenharia, tais como em solução de equações diferenciais parciais. Eles foram vastamente programados em FORTRAN e em linguagem Assembly. Sistemas operacionais típicos eram o FMS (Sistema Monitor FORTRAN) e IBSYS (Sistema Operacional da IBM para o 7094). $END Dados para o Programa $RUN $LOAD Programa Fortran $FORTRAN $JOB, 10, 429754, TANENBAUM Figura 0.2 - Estrutura de um típico job FMS 7 Apostila Fundamentos de Sistemas Operacionais . 0.3 - A Terceira Geração (1965 - 1980): CIs e Multiprogramação Nos anos 60, muitos fabricantes de computadores tinham duas linhas de produto distintas e totalmente incompatíveis. Por um lado havia os computadores científicos, em grande escala, orientado por palavras, tais como o 7094, que era usado para cálculos numéricos em ciência e engenharia. Por outro lado, havia os computadores comerciais, orientados por caracter, tais como o 1401, que era vastamente usado para classificação em fita e impressão, por bancos e companhias de seguros. O desenvolvimento e a manutenção de duas linhas de produto completamente diferentes era uma proposta cara para os fabricantes. Além do mais, os clientes em potencial para aquisição de novos computadores necessitavam inicialmente de uma máquina pequena, para mais tarde, com o crescimento, terem uma máquina maior em que pudessem rodar todos os seus programas mais rapidamente. A IBM, no intuito de resolver ambos os problemas de uma só tacada, introduziu o sistema /360. O 360 era uma série de máquinas compatíveis por software, variando de tamanho a partir do 1401 até o mais potente 7094. As máquinas diferiam apenas em preço e performance (capacidade de memória, velocidade do processador, número de periféricos I/O permitidos, e assim por diante). Já que todas as máquinas tinham a mesma arquitetura e o mesmo conjunto de instruções, pelo menos em teoria, programas escritos para uma máquina poderiam rodar em todas as outras. Além disso, o 360 foi projetado para manusear tanto computação comercial como computação científica. Assim, uma única família de máquinas poderia satisfazer às necessidades de todos os clientes. Em anos subsequentes, a IBM apresentou os sucessores compatíveis com a linha 360, usando uma tecnologia mais moderna, conhecidos como séries 370, 4300, 3080 e 3090. O 360 foi a primeira linha de computadores a usar (em pequena escala) circuitos integrados (CIs), fornecendo uma maior vantagem em preço/performance sobre as máquinas da segunda geração, que eram construidas de transistores individuais. Isso foi um sucesso imediato e a idéia de uma família de computadores compatíveis foi logo adotada por todos os outros fabricantes. Os descendentes dessas máquinas ainda hoje estão em uso em grandes centros de computação. A maior força da idéia de "uma família" foi simultaneamente a sua maior durabilidade. A intenção era que todo o software, incluindo o sistema operacional, deveria trabalhar em todos os modelos. Ele tinha de rodar em sistemas pequenos que muitas vezes já substituia 1401s para cópias de cartões em fitas e em sistemas muito grandes, que muitas vezes substituia 7094s para fazer cálculos demorados e outras computações pesadas. Ele deveria ser bom em sistemas com poucos periféricos e em sistemas com muitos periféricos. Ele tinha de trabalhar em ambientes comerciais e em ambientes científicos. Acima de tudo, ele tinha de ser eficiente em todos esses usos diferentes. 8 Apostila Fundamentos de Sistemas Operacionais . Não havia uma maneira através da qual a IBM (ou outra companhia) pudesse solucionar todas essas exigências conflitantes. O resultado foi um sistema operacional enorme e extraordinariamente complexo, provavelmente de dois ou três ordens de magnitude maior do que o FMS. Ele consistia de milhares de linhas de linguagem assembly escritas por centenas de programadores e continha centenas e centenas de depurações que necessitavam de contínuas versões a fim de corrigí-los. Cada nova versão fixava algumas depurações e introduzia outras novas, tal que o número de depurações provavelmente permanecia constante com o tempo. A despeito de seu enorme tamanho e de seus problemas, o OS/360 e os sistemas operacionais similares da terceira geração, produzidos por outros fabricantes, satisfizeram razoavelmente bem a seus clientes. Eles também popularizaram várias técnicas ausentes nos sistemas operacionais da segunda geração. Provavelmente, a mais importante dessas técnicas foi a multiprogramação. No 7094, quando o job que estava sendo executado tinha uma pausa esperando que uma operação em fita ou em qualquer outro periférico I/O fosse completada, a CPU simplesmente ficava ociosa até que a operação I/O fosse encerrada. Em cálculos científicos pesados, as operações de I/O não são frequentes, e essse tempo ocioso é insignificante. Em processamento de dados comerciais, as operações de I/O consomem frequentemente entre 80 a 90 porcento do tempo total, exigindo alguma providência sobre isso. A solução foi particionar a memória em várias partes, com um job diferente em cada partição, como mostrado na Fig. 3. Enquanto um job estava esperando que uma operação I/O fosse concluida, um outro job poderia usar a CPU. Se vários jobs pudessem ocupar a memória no mesmo instante, a CPU estaria sempre ocupada quase que em 100% do tempo. Ter múltiplos jobs na memória, por sua vez, requer hardware especial para proteger cada job contra danos e entrelaçamento entre eles, e o 360 e outros sistemas da terceira geração eram equipados com esse hardware. Job 3 Job 2 Partições da Memória Job 1 Sistema Operacional Figura 0.3 - Um sistema de multiprogramação com três jobs na memória 9 Apostila Fundamentos de Sistemas Operacionais . Um outro grande aspecto presente nos sistemas operacionais da terceira geração era a habilidade de ler jobs de cartões para o disco assim que eles eram trazidos à sala do computador. Assim, sempre que um job tinha a sua execução encerrada, o sistema operacional poderia carregar um novo job do disco numa nova partição vazia e executá-lo. Essa técnica é chamada de "spooling" (de "Simultaneous Perifheral Operation On Line") e também era usada para a saída. Com o "spooling", os 1401s não precisavam ser tão grandes e a utilização da fita diminuiu bastante. Apesar dos sistemas operacionais da terceira geração terem sido bem apropriados para a execução de programas envolvendo grandes cálculos científicos e de processamento de dados comerciais compactos, eles eram ainda, basicamente, sistemas em "batch". Muitos programadores sentiam saudades dos dias da primeira geração, quando eles tinham a máquina toda para eles por poucas horas, mas de tal forma que eles depuravam os seus programas rapidamente. Com os sistemas da terceira geração, o tempo entre a submissão do job e a obtenção da saída era frequentemente de várias horas, a ponto da ausência de uma única vírgula causar uma falha na compilação e o programador desperdiçava quase um dia. A vontade de ter um tempo de resposta menor abriu espaço para "timesharing", uma variante da multiprogramação, em que cada usuário tem um terminal "online". Num sistema "time-sharing", se 20 usuários estão conectados e 17 deles estão pensando, falando ou tomando café, a CPU pode ser alocada para os três jobs que querem serviço. Como as pessoas que depuram programas usualmente editam poucos comandos (como compilar um programa de cinco páginas) em vez de programas longos (como classificar mil registros em fita), o computador pode fornecer mais rápido, serviço interativo a um número maior de usuários e talvez também trabalhar com grandes jobs em "batch" paralelamente, enquanto a CPU está, por outro lado, ociosa. Embora a primeira série de sistemas em time-sharing (CTSS) foi desenvolvido no MIT num IBM 7094 especialmente modificado, ele não se tornou verdadeiramente popular até que a necessidade de proteção de hardware ficasse mais difundida durante a terceira geração. Após o sucesso do sistema CTSS, o MIT, o Laboratório Bell e a General Electric (então o maior fabricante de computadores) decidiram embarcar no desenvolvimento de um "computador utilitário", uma máquina que suportasse milhares de usuários em "time-sharing" simultaneamente. O seu modelo era baseado no sistema de distribuição de eletricidade - quando voce precisa de eletricidade, basta por um plug na tomada da parede e a quantidade que voce precise, terá. Os projetistas desse sistema, conhecido como MULTICS (MULTiplexed Information and Computing Service), tinham em mente uma grande máquina que fornecesse serviço de computação para todos em Boston. A idéia de que máquinas tão poderosas quanto o GE44 seriam vendidas como computadores pessoais por alguns milhares de dólares apenas vinte anos mais tarde era, naquela época, pura ficção científica. Para resumir, o MULTICS introduziu muitas idéias inovadoras na literatura da computação, mas a sua construção foi mais difícil do que se esperava. O Laboratório Bell saiu do projeto e a General Electric continuou sozinha. Eventualmente o MULTICS rodava o suficientemente bem para ser usado num ambiente de produção no MIT e em poucas outros lugares, mas a idéia de 10 Apostila Fundamentos de Sistemas Operacionais . um computador utilitário falhou. Mesmo assim, o MULTICS teve uma enorme influência nos sistemas subsequentes. Outro importante desenvolvimento durante a terceira geração foi o crescimento fenomenal de mini-computadores, começando com o DEC PDP-1 em 1961. O PDP-1 tinha apenas 4 K palavras de 18 bits mas a um custo de 120.000 dólares por máquina (menos que 5% do preço de um 7094) eles vendiam como bolinhos. Para certos tipos de trabalhos não-numéricos era quase tão rápido quanto o 7094 e fez surgir uma nova indústria. Foi rapidamente seguido por uma série de outros PDPs (que diferentes da família IBM, eram todos incompatíveis) culminando com o PDP-11. Um dos cientistas do Laboratório Bell que trabalhou no MULTICS, Ken Thompson, logo depois encontrou um pequeno PDP-7 que ninguém usava e começou a escrever uma versão simplificada mono-usuário do MULTICS. Brian Kernighan apelidou esse sistema de UNICS (UNiplexed Information and Computing Service), mas sua grafia foi mais tarde trocada para UNIX. Posteriormente foi levado para um PDP-11/20, onde funcionou bem o bastante para convencer a gerência do Laboratório Bell em investir no PDP-11/45 para continuar o trabalho. Outro cientista do Laboratório Bell, Dennis Ritchie, juntou-se a Thompson para reescrever o sistema numa linguagem de alto nível chamada C, projetada e implementada por Ritchie. O Laboratorio Bell licensiava o UNIX para Universidades quase de graça e dentro de poucos anos, centenas delas estavam usando-o. O UNIX logo estendeu-se para o Interdata 7/32, para o VAX, para o MOTOROLA 68000, e para muitos outros computadores. O UNIX tinha sido transportado para mais computadores do que qualquer outro sistema operacional da história e seu uso está ainda aumentando rapidamente. 0.4 - A Quarta Geração (1980-1990): Computadores Pessoais Com o desenvolvimento de circuitos LSI (Large Scale Integration), chips contendo milhares de transistores em um centimetro quadrado de silício, a era do computador pessoal começava. Em termos de arquitetura, os computadores pessoais não eram diferentes de minicomputadores da classe do PDP-11, mas em termos de preço eles eram certamente bem diferentes. Enquanto o minicomputador tornou possível um departamento de uma companhia ou uma universidade ter o seu próprio computador, o chip micropocessador tornou possível um indivíduo ter o seu próprio computador. A grande variedade de capacidade computacional disponível, especialmente a capacidade de computação altamente interativa com excelentes facilidades gráficas, fizeram crescer a indústria de produção de software para computadores pessoais. Muitos desses softwares eram "amigáveis ao usuário", significando que eles foram projetados para usuários que não tinham conhecimento 11 Apostila Fundamentos de Sistemas Operacionais . algum sobre computadores e além do mais não tinha outra intenção a não ser a de orientá-los no uso. Essa foi certamente a maior mudança do OS/360, cujo JCL era tão complexo que livros inteiros foram escritos sobre ele. Dois sistemas operacionais dominaram a utilização do computador pessoal: o MS-DOS, escrito pela Microsoft para o IBM PC e para outras máquinas que usavam a CPU Intel 8088 e seus sucessores, e UNIX, que é predominante em máquinas que usam a CPU da família Motorola 68000. Pode parecer irônico que o descendente direto do MULTICS, projetado para o gigante computador utilitário, ficou tão popular em computadores pessoais, mas principalmente mostra como foram boas as idéias sobre o MULTICS e o UNIX. Apesar da primeira versão do MS-DOS ser primitiva, em versões subsequentes foram incluidas diversas facilidades do UNIX, o que não é tão surpreendente já que a Microsoft é um dos maiores fornecedores do UNIX, usando o nome comercial XENIX. Um interessante desenvolvimento que começou em meados dos anos 80 foi o crescimento de redes de computadores pessoais rodando sistemas operacionais para rede e sistemas operacionais distribuidos. Num sistema operacional para rede, os usuários têm consciência da existência de múltiplos computadores e podem se conectar com máquinas remotas e copiar arquivos de uma máquina para outra. Cada máquina roda o seu próprio sistema operacional local e tem o seu próprio usuário (ou usuários). Um sistema operacional distribuido, em contraste, aparece para o usuário como um sistema tradicional de um único processador, mesmo sendo composto realmente de múltiplos processadores. Num verdadeiro sistema distribuido, os usuários não têm consciência de onde os seus programas estão sendo rodados ou onde seus arquivos estão localizados; tudo é manuseado automática e eficientemente pelo sistema operacional. Os sistemas operacionais em rede não são fundamentalmente diferentes dos sistemas operacionais de um único processador. Eles obviamente necessitam de um controlador de interface de rede e de algum software de alto nível para gerenciá-lo, bem como de programas para concluir com êxito uma conexão remota e o acesso a arquivos remotos, mas essas adições não mudam a estrutura essencial do sistema operacional. Os sistemas operacionais distribuidos requerem mais do que a adição de códigos a um sistema operacional de um processador porque sistemas distribuidos e centralizados diferem em modos críticos. Sistemas distribuidos, por exemplo, frequentemente admitem rodar programas em vários processadores ao mesmo tempo, e daí exigem algorítmos de escalonamento de processadores para otimimizar a quantidade de paralelismo que deve ser concluído com êxito. O atraso de comunicação em uma rede frequentemente significa que esses (e outros) algorítmos devem rodar com informação incompleta, desatualizada ou às vezes incorreta. Essa situação é radicalmente diferente de um sistema de um único processador no qual o sistema operacional tem a informação completa sobre o estado do sistema. 12 Apostila Fundamentos de Sistemas Operacionais . Tolerância a falhas é uma outra área em que os sistemas distribuidos são diferentes. É comum para um sistema distribuido ser projetado com a expectativa de que continuará rodando mesmo que parte do hardware deixe de funcionar. Desnecessário se dizer que uma exigência adicional ao projeto tem enormes implicações para o sistema operacional. 0.5 A Era Windows (1990/97) Se o mundo da computação estava procurando por um novo padrão ou não, ele encontrou um em maio de 1990, quando a Microsoft finalmente lançou o Windows 3.0. O Windows 3.0 era executado sobre o DOS e, portanto, oferecia compatibilidade com os programas DOS. Ele se beneficiava do processador 386, podendo fazer a multitarefa com programas DOS e também com programas Windows. A interface com o usuário foi projetada para se parecer com o Presentation Manager, trazendo um Gerenciador de Programas baseado em ícones e um Gerenciador de Arquivos em estilo árvore, incluindo avanços como ícones sombreados. Embora o Windows 3.0 tenha exigido revisões mínimas de praticamente todos os programas Windows existentes na época, não havia muito a ser revisado. Além do mais, imediatamente após a introdução do Windows 3.0, começaram a aparecer os aplicativos, liderados pela divisão de aplicativos da própria Microsoft e seguidos por praticamente todos os outros grandes desenvolvedores. Mesmo depois do anúncio do Windows 3.0, a Microsoft e a IBM continuavam falando sobre o OS/2 e, especialmente, sobre o OS/2 2.0, a primeira versão 32 bits real que viria a aparecer, finalmente, em 1992. Para contundir ainda mais as coisas, enquanto a IBM posicionava o OS/2 como o futuro sistema operacional para todos os usuários, a Microsoft posicionava o OS/2 como um topo de linha, apenas para os aplicativos missão crítica e baseados em servidor. Em vez disto, a Microsoft começou a falar sobre o OS/2 3.0 (não confundir com o posterior IBM OS/2 Warp 3.0), que adicionaria segurança e suporte avançados a multiprocessador, sendo capaz de executar aplicativos Windows e Posix diretamente. Neste cenário, o Windows NT era o núcleo sobre o qual se apoiariam o DOS, o Windows, o OS/2 e o Posix. As duas companhias finalmente separaram suas estratégias no início de 1991, com Jim Cannavino, da IBM, e Bill Gates, da Microsoft, brigando como um casal durante um divórcio litigioso. O OS/2 conquistou um forte nicho em algumas grandes aplicações corporativas, auxiliado por sua estabilidade e robustez, comparadas ao Windows 3.x. Mais tarde, a IBM faria uma última tentativa de fazer do OS/2 o principal sistema operacional com seu OS/2 Warp 3.0, mais orientado ao consumidor comum e lançado no final de 1994. Ele venderia milhões de cópias mas não diminuiria a grande inclinação da indústria pelo Windows. 13 Apostila Fundamentos de Sistemas Operacionais . A Microsoft viria a transformar seu antigo "OS/2 3.0" no Windows NT 3. 1, que foi lançado em 1993 sem o suporte gráfico ao OS/2 e recebido, inicialmente, como um sistema operacional para servidores de aplicativos, concorrendo, principalmente, com o OS/2 da IBM. Para a maioria dos usuários de PCs, a Microsoft ofereceu o Wlndows 3.1 avançado no final de 1991, que adicionava uma melhor integração de aplicativos, recursos arrastar-e-soltar e uma maior estabilidade. No início dos anos 90, ele se tornou o padrão dominante para os aplicativos para PC e a Microsoft ocupou o papel de líder na definição das especificações multimídia. A Microsoft viria a dominar muitas áreas mais na computação por esta mesma época. Seus produtos Visual Basic e Visual C++ venceram a grande concorrência da Borland no domínio de linguagens de programação. Além disto, os aplicativos Microsoft - liderados pelo pacote Office, contendo o Word, o Excel, o PowerPoint e, mais tarde, o Access tomaram grande parte do mercado de programas aplicativos (o que foi auxiliado, em parte, pelos atrasos nas versões do Lotus 1-2-3, WordPerfect e DBASE para Windows, sendo que este último foi adquirido pela Borland). Neste período, o Macintosh, da Apple, continuava a crescer e expandir-se e encontrou nichos nas artes gráficas, na multimídia e na educação. Mas, na maioria das empresas e órgãos governamentais, o principal sistema comercial era aquele que seguia os padrões do PC original. Àquela época, o termo compatível com IBM já tinha saído de moda, para ser substituído pelo Processador como a principal peça descritiva de hardware. A era do 286 já havia terminado no final de 1988, após a introdução do 386SX da Intel, um Processador que possuía os componentes internos de 32 bits do 386 e um barramento de dados 16 bits como o 286, o que o tornava barato. Este e o 386 original rebatizado como 386DX dominaram as vendas de computadores durante anos. Em abril de 1989, a Intel apareceu com seus processadores 486. Com 1,2 milhões de transistores, o 486 era, efetivamente, uma versão mais rápida e mais refinada do 386 somada a um co-processador matemático que executava todos os aplicativos escritos para o 386 sem quaisquer problemas. Desta vez, ninguém esperou pela IBM ou pela Compaq. Dezenas de desenvolvedores se apressaram para tornar disponíveis suas máquinas 486 o mais rápido possível após a introdução da Intel, e estas máquinas tinham uma velocidade de execução 50 vezes maior que o IBM PC original. A Intel introduziu seu Processador Pentium de 60 MHz em março de 1993, mas não eram apenas os processadores que continuavam a avançar. Os discos rígidos ficavam cada vez maiores e mais velozes. E a tecnologia de exibição gráfica progrediu das placas de vídeo de "buffer de quadro" para as aceleradores gráficas, que trabalhavam diretamente com o Windows a fim de aumentar os tempos de resposta de tela e melhorar os gráficos em geral. 14 Apostila Fundamentos de Sistemas Operacionais . Neste período, as redes locais corporativas realmente começaram a decolar. A IBM promovia, então, o Office Vision, que deveria ser executado em todas as plataformas SAA, inclusive sobre o OS/2. E praticamente todos os gigantes do Desenvolvimento de sistemas tinham suas estratégias multiplataforma para a automação de escritórios, como o All-In-One da DEC. Quase todos fracassariam dentro de um espaço de tempo relativamente curto. Quem realmente alcançou o sucesso foram os servidores de PC, que abrigavam seus próprios dados e podiam fazer ligações com grandes bases de dados corporativas. No fronte do hardware, o Compaq Systempro, introduzido em 1989, liderava os grandes aplicativos que antes viviam em minicomputadores e outros grandes sistemas. No lado do software, chegava ao mercado o SQL, e companhias como a Oracle e a Sybase começavam a ter como alvos os desenvolvedores para PC. As ferramentas de desenvolvimento rápido de aplicativos, ou RAD, logo facilitaram a criação de boas interfaces com o usuário para o acesso a dados corporativos. O correio eletrônico (email) é aceito no dia-a-dia das corporações com produtos, como o cc:Mail, mais tarde adquirido pela Lotus, e mais um punhado de concorrentes menores. Em dezembro de 1989, a Lotus mudou a fórmula com o Lotus Notes, o primeiro aplicativo de "groupware". Em 1994, a Microsoft e a Intel já vestiam o manto da liderança na indústria do PC, o Windows tinha-se estabelecido como o padrão para aplicativos e as redes estavam definitivamente no mercado comum. No começo de 1995, poderíamos esperar que novos sistemas operacionais da Microsoft e novos chips da Intel continuassem sendo o carro-chefe da computação ainda por muitos anos, levando-se em conta o histórico dos anos anteriores. Eles ainda são importantes, mas talvez a mudança mais importante destes últimos anos tenha vindo de um grupo de estudantes da Universidade de Illinois. Foi lá que, no início de 1993, Marc Andreessen, Eric Bina e outros que trabalhavam para o National Center for Supercomputing Applications (NCSA) apareceram com o Mosaic, uma ferramenta que seria utilizada para paginar a Internet. A Internet, é claro, já existia há muitos anos, datando do início dos anos 60, quando o órgão de Defesa de Projetos de Pesquisa Avançada (DARPA) do Pentágono estabeleceu as conexões com muitos computadores de universidades. Enquanto a Internet crescia, o governo transferiu seu controle para os sites individuais e comitês técnicos. E, em 1990, Tim Berners-Lee, então no laboratório de física CERN, em Genebra, Suíça, criou a Linguagem de Marcação de Hipertexto (HTML), uma maneira simples de ligar informações entre sites da Internet. Isto, por sua vez, gerou a World Wide Web (www), que apenas aguardava por um paginador gráfico para começar a crescer. Após o lançamento do Mosaic ao público, no final de 1993, repentinamente, a Internet - e, em particular, a Web - podiam ser acessadas por qualquer pessoa que tivesse um computador pessoal, fato auxiliado, em parte, nela possibilidade de transferir livremente a versão mais recente de vários paginadores 15 Apostila Fundamentos de Sistemas Operacionais . diferentes. E, dentro de pouco tempo, parecia que todo o mundo - e todas as companhias - estava inaugurando seu site na Web. Novas versões de paginadores da Web também chegaram rapidamente. A Netscape Corp. - uma nova companhia formada por Andreessen e Jim Clark, que havia sido um dos fundadores da Silicon Graphics - logo começou a dominar o ramo de paginadores Web. O Netscape Navigator acrescentou vários recursos, inclusive o suporte a extensões (o que, por sua vez, levou a diversas extensões multimídia) e a máquina virtual Java (que permitia aos desenvolvedores elaborar aplicativos Java que podiam ser executados dentro do paginador). A tremenda empolgação ocasionada pela explosão da World Wide Web chegou perto de eclipsar o maior anúncio da Microsoft neste período: o Windows 95. Introduzido em agosto de 1995, a estréia do software foi acompanhada por um entusiasmo maior do que qualquer outro anúncio de computação da era. O Windows 95 era a versão do Windows pela qual muitos usuários estiveram esperando. Ele permitia a utilização de aplicativos totalmente 32 bits, tinha a multitarefa preemptiva, era compatível com Plug-and-Play, suportava novos padrões de e-mail e comunicações e, logicarnente, trazia uma nova interface com o usuário. Na verdade, muitos usuários pensavam que a nova interface, que incluía um menu "Iniciar" e uma área de trabalho de programas com pastas e ícones, deixaria o Windows muito mais próximo do projeto Lisa original ou do Macintosh de dez anos atrás. A Microsoft passou anos prometendo um Windows 32 bits, chegando a dizer que ele estaria pronto em 1992, e os desenvolvedores passaram um longo tempo aguardando pelo "Chicago", como era conhecido o Windows 95 durante o desenvolvimento. Urna vez lançado, o Windows 95 rapidamente tornou-se o padrão para a computação de usuário final, fazendo com que muitos desenvolvedores tivessem suas versões de aplicativos 32 bits prontas no lançamento do SO ou imediatamente após. A Microsoft fez seguir ao Windows 95, menos de um ano mais tarde, o Windows NT 4.0, que incorporava a mesma interface com o usuário e executava a maioria dos mesmos aplicativos, utilizando interfaces de programação Win32. O Windows NT agradou rapidamente os gerentes de IT corporativos, devido a seu projeto mais estável. Mas ainda existe um grande espaço para avanços nos sistemas operacionais. Durante anos, os desenvolvedores de software falaram sobre as linguagens orientadas a objetos (como o C++) e sobre um sistema operacional mais orientado a objetos. Num projeto como este, dados e aplicativos deveriam ser divididos, para que os usuários pudessem trabalhar com os dados independentemente dos aplicativos individuais. O ideal seria que os dados pudessem ser disseminados ou distribuídos por diversos computadores. A Microsoft vem falando sobre este conceito há anos, em especial na palestra "A Informação nas Pontas de Seus Dedos" de Bill Gates, realizada em novembro de 1990, que enfatizava o conceito de que todos os dados de que um usuário pode necessitar poderiam, algum dia, ser acessados por meio de um computador pessoal, 16 Apostila Fundamentos de Sistemas Operacionais . independente do local onde os dados realmente residem. A idéia, disse ele, iria além dos aplicativos e consideraria apenas os dados. Este caminho levou à ênfase dada pela Microsoft aos documentos compostos, macros que funcionam através dos aplicativos, e a um novo sistema de arquivos. Algumas peças desta visão - chamada Cairo - fazem parte da interface do windows 95 e do OLE (Object Linking and Embedding). Outras ainda estão na prancheta de desenhos. É claro que os concorrentes da Microsoft continuaram seguindo seus próprios caminhos. Em 1989, a NEXT Computer de Steve Jobs apareceu com um SO orientado a objetos, destinado aos clientes corporativos e recentemente adquirido pela Apple Computer. No início dos anos 90, a IBM e a Apple fundiram dois de seus projetos - o SO "Pink" da Apple e o experimento IBM/Metaphor, chamado de Patriot Partners - para criar o Taligent. Este projeto resultou numa série um tanto extensa de estruturas, para uso dos desenvolvedores, na criação de aplicativos baseados em objetos. Mas, embora as estruturas tenham sido recentemente adicionadas ao OS/2, os planos para o Taligent como um SO isolado foram arquivados. Uma outra tecnologia baseada em objetos está em vários estágios de desenvolvimento. O OLE, da Microsoft, que permite a criação de documentos compostos, tem sido aprimorado e hoje faz parte da especificação ActiveX, da mesma companhia. A Apple, a IBM e outras companhias surgiam com uma especificação alternativa chamada OpenDoc e tais componentes são hoje conhecidos como LiveObjects. A IBM definiu um padrão para que os objetos trabalhassem em conjunto ao longo de uma rede chamada Systems Object Model (SOM, ou Modelo de Objeto de Sistema), que concorre com o Component Object Model (COM, ou Modelo de Objeto Componente), da Microsoft. Mas tudo isto tem sido eclipsado nos últimos meses pelo Java, da Sun Microsystems, que começou sua vida como uma variação do C++ projetada para uso na Internet. No ano passado, ele passou a incluir uma implementação de máquina virtual que foi incorporada aos paginadores da Netscape e da Microsoft e também à mais nova versão do sistema operacional da IBM, o OS/2 Warp. Muitos desenvolvedores estão atualmente desenvolvendo applets (pequenos aplicativos) e até mesmo aplicativos completos dentro do Java, na esperança de que isto venha a livrá-los de terem que se apoiar nos padrões Microsoft. Mais recentemente, a Sun, a Netscape e outras companhias estiveram promovendo a especifieação Java-Beans como um ótimo método de ligação de objetos. Na própria Web, um dos atuais esforços são as tecnologias e produtos que permitem o fornecimento automático do conteúdo sobre a Internet, para que os usuários não precisem pesquisar informações específicas. Apresentada pela primeira vez pela PointCast, que implementou uma tela de descanso que coleta as informações da várias fontes, esta abordagem está sendo perseguida por diversos concorrentes novos, como a Castanet e a BackWeb. E tanto a Netscape quanto a Microsoft agora prometem o fornecimento de conteúdo Internet em segundo plano, com a Microsoft sempre falando em fazer disto uma parte da área de trabalho do Windows. 17 Apostila Fundamentos de Sistemas Operacionais . Quantas e quais destas iniciativas em equipamentos, programas e rede terão sucesso? Como sempre, é difícil dizer. Mas está claro que a Internet e a Web serão os principais fatores nos próximos anos, assim como os inevitáveis avanços nas capacidades de hardware e software. 18 Apostila Fundamentos de Sistemas Operacionais . Capítulo 1 VISÃO GERAL 1.1 Introdução: Sistema Operacional nada mais é do que um conjunto de instruções executadas pelo processador. Sua função é controlar o funcionamento de um computador, gerenciando a utilização e o compartilhamento dos seus diversos recursos, como processadores, memórias e dispositivos de entrada e saída. Sem SO, usuário deveria conhecer profundamente o computador para poder interagir com ele. Implicaria em trabalho lento e com possibilidade de erros. A diferença entre um SO e aplicações convencionais é a maneira como as rotinas são executadas em função do tempo. O SO não tem início, meio e fim como as aplicações. Dependem de eventos assíncronos. Também pode ser chamado de Programa monitor, Executivo, supervisor ou Controlador. 1.2 Funções básicas: - Facilidade de acesso aos recursos do sistema: Usuário não precisa se preocupar como é feita a comunicação com monitores, discos, impressoras, etc. O SO é uma interface entre o usuário e os recursos do sistema. Este conceito de ambiente simulado pelo SO também é chamado de Máquina Virtual (figura 1.1) Compiladores, linkers, bibliotecas, depuradores e outras ferramentas são utilitários que facilitam a interação do usuário com o computador. - Compartilhamento de recursos de forma organizada e protegida: Em sistemas onde diversos usuários compartilham recursos, é necessário controlar o uso concorrente destes recursos. Ex: Impressora, a impressão de um usuário não deve interferir na do outro. O SO controla estes acessos concorrentes. O compartilhamento também permite redução de custos, quando diversos usuários podem compartilhar periféricos como impressoras, discos, etc. Dependendo do SO, podemos executar diversas tarefas ao mesmo tempo, como imprimir um documento e baixar um arquivo da Internet. E é o SO que controla estas atividades concorrentes. 19 Apostila Fundamentos de Sistemas Operacionais programadores e analistas . usuários programas, sistemas e aplicativos Usuários Sistema Operacional memória discos Hardware fitas UCP impressoras monitores Fig. 1.1 - Visão do Sistema Operacional 1.3 Máquina de níveis: Uma máquina, do ponto de vista do hardware, tem pouca utilidade. É através do software que esta máquina ganha utilidade (como armazenamento de dados, impressão, etc.) Uma operação efetuada por software pode ser implementada em hardware, bem como uma função executada pelo hardware pode ser simulada via software. Os primeiros computadores eram programados através de fios ligados em painéis, criando grandes dificuldades e exigindo grande conhecimento da máquina. A solução veio com o surgimento do SO, tornando a interação com o usuário mais simples, confiável e eficiente. (Figura 1.2) 20 Apostila Fundamentos de Sistemas Operacionais . usuários Sistema Operacional Hardware Fig. 1.2 - Visão do computador pelo usuário O computador pode ser visualizado como uma máquina de níveis ou máquina de camadas. Inicialmente vemos apenas dois níveis: hardware (nível 0) e SO (nível 1). Assim, o usuário pode enxergar a máquina como sendo apenas o SO, como se o hardware não existisse. Esta visão é chamada de máquina virtual. Na verdade não existem apenas dois níveis, e sim tanto quantos forem necessários para adequar o usuário às suas diversas aplicações. A figura 1.3 representa a estrutura da maioria dos computadores, podendo conter mais ou menos camadas. A linguagem utilizada em cada um destes níveis é diferente, variando da mais elementar (baixo nível) à mais sofisticada (alto nível). Aplicativos Utilitários Sistema Operacional Linguagem de Máquina Microprogramação Circuitos Eletrônicos Fig. 1.3 - Máquina de Níveis 21 Apostila Fundamentos de Sistemas Operacionais . 1.4 Tipos de Sistemas Operacionais: Os tipos de SOs e sua evolução estão diretamente relacionados com a evolução do hardware e das aplicações por ele suportadas. A figura 1.4 sintetiza os diversos tipos de SOs, cujas características, vantagens e desvantagens serão abordadas em seguida. Tipos de Sistemas Operacionais Sistemas Monoprogramáveis/ Monotarefa Sistemas Multiprogramáveis/ Multitarefa Sistemas com Múltiplos Processadores Fig. 1.4 - Tipos de Sistemas Operacionais 1.4.1 SOs monoprogramáveis/monotarefa: Os primeiros SOs eram voltados para a execução de um único programa. Qualquer outra aplicação deveria aguardar a aplicação concorrente terminar, para ser executada. Estes sistemas vieram a ser conhecidos como sistemas monoprogramáveis e se caracterizavam por permitir que o processador, a memória e os periféricos estejam exclusivamente dedicados à execução de um único programa. Este tipo de SO está relacionado aos primeiros computadores da década de 60. Voltou a ser utilizado na década de 70 em estações de trabalho. Nos sistemas monotarefas, como também são conhecidos, todos recursos do sistema ficam exclusivamente dedicados a uma única tarefa. Neste tipo de SO, o processador fica ocioso, por exemplo, quando espera a digitação de um dado. A memória é sub-utilizada caso não seja preenchida totalmente, e os periféricos, como discos e impressoras, estão dedicadas a um único usuário, nem sempre de forma integral (Figura 1.5). 22 Apostila Fundamentos de Sistemas Operacionais . UCP Memória Principal programa/ tarefa Dispositivos de E/ S Fig. 1.5 - Sistemas monoprogramáveis / monotarefa. 1.4.2 SOs multiprogramáveis / multitarefa: Os SOs multiprogramáveis ou multitarefas são uma evolução do SO monoprogramáveis. Neste tipo de SO os recursos computacionais são compartilhados entre diversos usuários e aplicações. Aqui várias aplicações compartilham esses mesmos recursos. Aqui também, enquanto um programa espera por uma operação de leitura ou gravação em disco, outros programas podem estar sendo processados neste intervalo de tempo. Neste exemplo, observamos o compartilhamento da memória e do processador. O SO se preocupa em gerenciar o acesso concorrente a seus diversos recursos, de forma ordenada e protegida, entre os diversos programas (Figura 1.6). 23 Apostila Fundamentos de Sistemas Operacionais programa/ tarefa . programa/ tarefa UCP Memória Principal Dispositivos de E/ S programa/ tarefa programa/ tarefa programa/ tarefa Fig. 1.6 – Sistemas multiprogramáveis / multitarefa A vantagem deste tipo de SO é a redução do tempo de resposta das aplicações processadas no ambiente e de custos, a partir do compartilhamento de recursos do sistema entre diferentes aplicações. Apesar de mais eficientes, os SO multiprogramável tem implementação muito mais complexa. Baseado no número de usuários que interagem com o sistema, o SO multiprogramável pode ser classificado como monousuário ou multiusuário. Os sistemas multiprogramáveis monousuário são encontrados em computadores pessoais e estações de trabalho, onde apenas um usuário interage com o sistema. Por exemplo, um usuário pode executar um editor de texto, ao mesmo tempo em que acessa a Internet e imprime um documento. Nos sistemas multiusuários, permite-se que diversos usuários conectarem-se ao sistema simultaneamente. A tabela 1.1 relaciona os tipos de sistemas em função do número de usuários. Monoprogramação / Monotarefa Multiprogramação / Multitarefa Um usuário Monousuário Monousuário Dois ou mais usuários Não disponível Multiusuário Tabela 1.1 – Sistemas x Usuários 24 Apostila Fundamentos de Sistemas Operacionais . Os SO multiprogramáveis ou multitarefa, podem ainda ser classificados pela forma com que suas aplicações são gerenciadas, podendo ser divididos em sistemas batch, de tempo compartilhado ou de tempo real. Um SO pode suportar um ou mais destes tipos de processamento, dependendo de sua implementação (Figura 1.7). Sistemas Multiprogramáveis/ Multi tarefa Sistemas Batch Sistemas de Tempo Compartilhado Sistemas de Tempo Real Fig. 1.7 – Tipos de sistemas multiprogramáveis / multitarefa. 1.4.2.1 Sistemas Batch: Os sistemas batch foram os primeiros SOs multiprogramáveis implantados na década de 60. Os programas, também chamados de jobs, eram executados através de cartões perfurados e armazenados em discos ou fitas, onde aguardavam para serem processados. Posteriormente, em função da disponibilidade de espaço na memória principal, os jobs eram executados, produzindo uma saída em disco ou fita. Este tipo de processamento se caracteriza por não exigir a atenção do usuário com a aplicação. Todas entradas e saídas eram implementadas por algum tipo de memória secundaria, geralmente discos. Cálculos numéricos, compilações, ordenações e backups são exemplos de aplicações batch. Estes sistemas podem ser bastante eficientes, por utilizar melhor o processador, entretanto, podem oferecer tempos de resposta longos. Atualmente não existem sistemas exclusivamente batch, sendo executados através de simulações quando necessário. 1.4.2.2 Sistemas de Tempo compartilhado: Os sistemas de tempo compartilhado (time-sharing), permitem que diversos programas sejam executados a partir da divisão de tempo do processador em pequenos intervalos, chamados de fatia de tempo (time-slice). Caso o tempo disponibilizado não seja suficiente para a conclusão do programa, este é interrompido pelo SO e substituído por um outro, enquanto fica aguardando por uma nova fatia de tempo. Este ambiente dá a impressão que todo o sistema esta dedicado, exclusivamente, para cada usuário. 25 Apostila Fundamentos de Sistemas Operacionais . Geralmente, nestes sistemas a interação com o usuário se dá através de terminais de vídeo, teclado e mouse. Estes sistemas possuem uma linguagem de controle própria, permitindo ao usuário comunicar-se diretamente com o SO através de comandos. Assim, é possível por exemplo, a verificar arquivos armazenados num disco, ou cancelar a execução de um programa. Devido a este tipo de interação, os sistemas de tempo compartilhado também são chamados de sistemas on-line. A maioria das aplicações comerciais atuais são processadas em sistemas de tempo compartilhado, pois oferecem tempos baixos de resposta aos usuários e menores custos, em função da utilização compartilhada de diversos recursos. 1.4.2.3 Sistemas de Tempo Real: Os sistemas de tempo real (real-time) são implementados de forma semelhante à dos sistemas de tempo compartilhado. A diferença é o tempo de resposta exigido no processamento das aplicações. Nos sistemas de tempo compartilhado, o tempo de resposta pode variar sem comprometer as aplicações em execução. Nos de tempo real, os tempos de resposta devem estar dentro de limites rígidos, que devem ser obedecidos, caso contrario poderão ocorrer sérios problemas. No sistema de tempo real não existe a idéia de fatia de tempo. Um programa utiliza o processador o tempo que necessitar, ou ate que apareça outro mais prioritário. A prioridade de execução é definida pela própria aplicação e não pelo SO. Estes sistemas podem ser encontrados em aplicações de controle de processos, como no monitoramento de refinarias de petróleo, controle de trafego aéreo, de usinas termoelétricas e nucleares, ou qualquer outra onde o tempo de resposta é fator fundamental. 1.4.3 Sistemas com Múltiplos Processadores: Os sistemas com múltiplos processadores caracterizam-se por possuir dois ou mais processadores interligados e trabalhando em conjunto. Este tipo de sistema permite que vários programas sejam executados ao mesmo tempo, ou que um único programa seja subdividido em partes para serem executados simultaneamente em mais de um processador. O uso de múltiplos processadores permitiu a criação de sistemas voltados para processamento científico (como a criação do mapa genético), no desenvolvimento aeroespacial, prospecção de petróleo, simulações, processamento de imagens, CAD e previsão do tempo. Pode-se afirmar que qualquer aplicação que faca uso intensivo do processador, será beneficiada pelo acréscimo de processadores ao sistema. A evolução destes sistemas se deve em grande parte, ao elevado custo de desenvolvimento de 26 Apostila Fundamentos de Sistemas Operacionais . processadores de alto desempenho. É menos custoso processadores menores do que desenvolver um mais poderoso. interligar diversos Além dos mesmos benefícios dos sistemas multiprogramáveis, o sistema com múltiplos processadores apresentam vantagens como: Escalabilidade : é a possibilidade de se aumentar o poder computacional do sistema, adicionando-se novos processadores. Disponibilidade : é a capacidade de manter o sistema em operação mesmo em caso de falha de uma ou mais maquinas. No caso de uma falha, as outras máquinas assumem suas funções de maneira transparente ao usuário, embora com menor poder de computação. Balanceamento de carga : é a possibilidade de distribuir o processamento entre os diversos processadores, melhorando assim o desempenho do sistema como um todo. Um fator-chave na criação de SOs com múltiplos processadores é a forma de comunicação entre eles e o grau de compartilhamento da memória e dos dispositivos de entrada e saída. Assim, podemos classificar os sistemas com múltiplos processadores em fortemente acoplados ou fracamente acoplados (Figura 1.8). Sistemas Com Múltiplos Processadores Sistemas Fortemente Acoplados Sistemas Fracamente Acoplados Fig. 1.8 – Tipos de Sistemas com múltiplos processadores 1.4.3.1 Sistemas fortemente acoplados: Nos sistemas fortemente acoplados (tightly coupled) vários processadores compartilham uma única memória física (shared memory) e dispositivos de entrada e saída, sendo gerenciados por um único SO (Figura 1.9). 27 Apostila Fundamentos de Sistemas Operacionais UCP . Memória Principal Dispositivos de E/ S UCP Dispositivos de E/ S Fig 1.9 – Sistemas fortemente acoplados Em virtude disso, este tipo de sistema também é chamado de multiprocessador. Os sistemas multiprocessadores dividem-se ainda em SMP (Symmetric MultiProcessor) e NUMA (Non-Uniform Memory Access). Os sistemas SMP possuem tempo uniforme de acesso à memória principal pelos diversos processadores. Os sistemas NUMA apresentam diversos conjuntos reunindo processadores e memória principal, sendo que cada conjunto é conectado aos outros através de uma rede de interconexão. O tempo de acesso à memória pelos processadores varia em função da sua localização física. Nos sistemas SMP e NUMA todos processadores têm as mesmas funções. Inicialmente, os sistemas multiprocessadores limitavam-se a sistemas de grande porte. Com a evolução dos computadores pessoais, os sistemas multitarefa também evoluíram para permitir a existência de vários processadores no modelo simétrico. Atualmente, sistemas como Unix, Linux, Windows 200 e Windows XP implementam esta funcionalidade. 1.4.3.2 Sistemas fracamente acoplados: Os sistemas fracamente acoplados (loosely coupled), caracterizam-se por possuir dois ou mais sistemas computacionais conectados através de linhas de comunicação. Cada sistema funciona de forma independente, possuindo seu próprio sistema operacional e gerenciando seus próprios recursos, como processador, memória e dispositivos de entrada e saída (Figura 1.10). 28 Apostila Fundamentos de Sistemas Operacionais . link de comunicação UCP Memória Principal UCP Dispositivos de E/ S Memória Principal Dispositivos de E/ S Fig. 1.10 Sistemas fracamente acoplados Em função destas características, os sistemas fracamente acoplados também são conhecidos como multicomputadores. Neste modelo, cada sistema computacional também pode ser formado por um ou mais processadores. Até meados dos anos 80, as aplicações eram centralizadas em sistemas de grande porte, com um ou mais processadores. Nesta configuração, os usuários utilizavam terminais não-inteligentes conectados a linhas seriais dedicadas ou mesmo a linhas telefônicas públicas para comunicação interativa com estes sistemas. No modelo centralizado, os terminais não tem poder de processamento. A solicitação de uma tarefa ao sistema é feita através de linhas de comunicação. A evolução dos computadores pessoais e também das telecomunicações, fez com que um novo modelo de computação surgisse, chamado de modelo de rede de computadores. Em uma rede existem dois ou mais sistemas independentes (hosts), interligados através de linhas de comunicação, oferecendo algum tipo de serviço aos demais. Assim, a informação deixa de ser centralizada em sistemas de grande porte e passa a ser distribuída pelos diversos sistemas da rede. Baseando-se no grau de integração dos hosts da rede, dividimos os sistemas fracamente acoplados em sistemas operacionais de rede e sistemas distribuídos. A diferença básica entre eles é a capacidade do SO criar uma imagem única dos serviços disponibilizados pela rede. Os sistemas operacionais de rede (SORs) permitem que um host compartilhe seus recursos como impressora ou disco, com os demais hosts da rede. Um exemplo disto são as redes locais. Nos sistemas distribuídos o sistema operacional esconde os detalhes dos hosts individuais e passa a tratá-los como um conjunto único, como se fosse um sistema fortemente acoplado. Nos sistemas distribuídos, uma aplicação pode ser dividida em 29 Apostila Fundamentos de Sistemas Operacionais . partes e cada parte pode ser executada por hosts diferentes da rede de computadores. Para os usuários e suas aplicações, é como se não existisse a rede de computadores, e sim um único sistema centralizado. Outro exemplo de sistema distribuído são os clusters. Em um cluster existem dois ou mais servidores ligados, através de uma conexão de alto desempenho. O usuário não conhece os nomes dos membros do cluster e não sabe quantos são. Basta solicitar um serviço ao cluster para obtê-lo. Este tipo de sistema é usado atualmente em sistemas de banco de dados e Web, garantindo alta disponibilidade, escalabilidade e balanceamento de carga à solução. 30 Apostila Fundamentos de Sistemas Operacionais . Capítulo 2 CONCEITOS DE HARDWARE E SOFTWARE 2.1 Introdução: Neste capítulo serão apresentados brevemente, conceitos básicos de hardware e software, para compreensão dos capítulos seguintes. 2.2 Hardware: Um sistema computacional é um conjunto de circuitos eletrônicos interligados, formado por Processador ou unidade central de processamento, memória principal e dispositivos de entrada/saída. Processador / UCP Unidade Lógica e Aritmética Unidade de Controle Memória Principal Registradores Dispositivos de E/ S Fig. 2.1 Sistema Computacional 2.2.1 Processador: Um processador é composto por unidade de controle, unidade lógica e aritmética, e registradores. A unidade de controle (UC) é responsável por gerenciar as atividades de todos os componentes do computador, como a gravação de dados em discos ou a busca de instruções na memória. A unidade lógica e aritmética (ULA), como o nome indica, é responsável pela realização de operações lógicas (testes e comparações) e aritméticas (somas e subtrações). 31 Apostila Fundamentos de Sistemas Operacionais . 2.2.2 Memória: A memória é composta por unidades de acesso chamadas células, sendo cada célula composta por um determinado número de bits. Atualmente, a grande maioria dos computadores utiliza o byte (8 bits) como tamanho de célula. Memórias voláteis precisam estar sempre energizadas para manter suas informações, o que não acontece com as não-voláteis. 0 instrução ou dado 1 endereços 2 16 2 -1 célula = 8 bits Fig. 2.2 – Memória principal com 64 Kbytes 2.2.3 Memória Cache: A memória cache é uma memória volátil de alta velocidade, porém com pequena capacidade de armazenamento. O tempo de acesso a um dado nela contido é muito menor que se o mesmo estivesse na memória principal. O propósito do uso da memória cache é minimizar a disparidade existente entre a velocidade com que o processador executa instruções e a velocidade com que dados são acessados na memória principal. 2.2.4 Memória Principal e Secundaria: A memória principal é um dispositivo de armazenamento, em geral volátil, onde são armazenados instruções e dados utilizados pelo processador durante a execução de programas. A memória secundária é um dispositivo não-volátil com maior capacidade de armazenamento, porém com menor velocidade de acesso aos seus dados armazenados. 32 Apostila Fundamentos de Sistemas Operacionais . Registradores Memória Cache maior capacidade de armazenamento Memória Principal maior custo e velocidade de acesso Memória Secundária Fig. 2.3 – Relação entre dispositivos de armazenamento 2.2.5 Dispositivos e Entrada/Saída: Os dispositivos de entrada e saída podem ser divididos em duas categorias: os que são utilizados como memória secundária e os que servem para a interface usuáriomáquina. Os dispositivos utilizados como memória secundária (discos e fitas magnéticas) caracterizam-se por ter capacidade de armazenamento bastante superior ao da memória principal. Seu custo é relativamente baixo, porém o tempo de acesso à memória secundária é bem superior ao da memória principal. Outros dispositivos têm como finalidade a comunicação usuário-máquina, como teclados, monitores de vídeo, impressoras e plotters. 2.2.6 Barramentos ou Bus: Barramentos é o meio físico de comunicação entre as unidades funcionais de um sistema computacional. Os barramentos processador-memória são de curta extensão e alta velocidade para que seja otimizada a transferência de informação entre processadores e memórias. Os barramentos de E/S possuem maior extensão, são mais lentos e permitem a conexão de diferentes dispositivos. O barramento de backplane tem a função de integrar os dois barramentos anteriores. 33 Apostila Fundamentos de Sistemas Operacionais . Memória Principal UCP Barramento processador-memória Barramento de E/ S Adaptador Barramento de E/ S Adaptador Fig. 2.4 – Barramentos processador-memória e de E/S Memória Principal UCP Barramento processador-memória Barramento de backplane Adaptador Adaptador Barramento de E/ S Barramento de E/ S Adaptador Fig. 2.5 – Barramento de Backplane 34 Apostila Fundamentos de Sistemas Operacionais . 2.2.7 Pipeline: É uma técnica que permite ao processador executar múltiplas instruções paralelamente em estágios diferentes. P1 P2 P3 P4 Unidade de busca da instrução Analisador da instrução Unidade de busca dos dados Unidade de execução da instrução P1 Instr.1 Instr.2 Instr.3 Instr.4 Instr.5 Instr.6 Instr.7 P2 Instr.1 Instr.2 Instr.3 Instr.4 Instr.5 Instr.6 P3 Instr.1 Instr.2 Instr.3 Instr.4 Instr.5 P4 Instr.1 Instr.2 Instr.3 Instr.4 tempo Fig. 2.6 – Arquitetura pipeline com quatro estágios 35 Apostila Fundamentos de Sistemas Operacionais . Capítulo 3 CONCORRÊNCIA 3.1 Introdução: Os sistemas operacionais podem ser vistos como um conjunto de rotinas que executam concorrentemente de forma ordenada. A possibilidade de o processador executar instruções em paralelo com operações de E/S permite que diversas tarefas sejam executadas concorrentemente. Concorrência é o princípio básico para projeto e implementação dos sistemas operacionais multiprogramáveis. Os SOs monoprogramáveis eram limitados por seus recursos não serem utilizados de forma eficiente, limitando seu desempenho. Muitos recursos (alguns de alto custo), permaneciam ociosos por longos períodos de tempo. O disperdício dos SOs monoprogramáveis pode ser representado na Figura 3.1a, pois enquanto uma leitura em disco é realizada, o processador permanece ocioso. O tempo de espera é relativamente longo, pois as operações de E/S são lentas comparadas às operações dos processadores. E/ S UCP 1 E/ S UCP livre 1 2 1 tempo (a) Sistema Monoprogramável tempo (b) Sistema Multiprogramável Fig. 3.1 – Sistema monoprogramável x sistema multiprogramável A tabela 3.1 apresenta um exemplo de um programa que lê registros de um arquivo e executa, em média, 100 instruções por registro lido. Neste caso, o processador gasta cerca de 93% do tempo esperando o dispositivo de E/S concluir sua operação para então continuar o processamento. Leitura de um registro 0,0015 s Execução de 100 instruções 0,0001 s Total 0,0016 s % utilização da CPU (0,0001 / 0,0015) = 0,066 = 6,6 % Tabela 3.1 Exemplo de utilização do sistema 36 Apostila Fundamentos de Sistemas Operacionais . A memória principal também é subutilizada se o programa não a ocupa totalmente, deixando áreas livres. Nos SOs multiprogramáveis, vários programas podem estar residentes em memória, concorrendo pela utilização do processador. Assim, o processador permanece menos tempo ocioso (Figura 3.1 b) e a memória é utilizada de forma mais eficiente, pois vários programas se revezam na utilização do processador. A utilização concorrente do processador deve ser implementada de forma que, quando um programa perde o uso do processador, e depois retorna sua execução, deverá continuá-la na instrução seguinte àquela em que fora interrompido. Para o usuário, parece que nada aconteceu. Em sistemas de tempo compartilhado, existe a impressão de o sistema está inteiramente dedicado a ele. Em sistema monoprogramáveis, temos periféricos (como impressoras e discos) parados por grandes períodos de tempo esperando ações de um único usuário. Na tabela 3.2 temos as vantagens de um sistema multiprogramável, com um disco, um terminal e uma impressora. Nesta configuração, são executados três programas distintos (Prog1, Prog2 e Prog3). Pela tabela, percebemos que Prog1 não realiza operações de E/S, ao contrário de Prog2 e Prog3. Características Utilização do processador Operações de E/S Tempo de processamento Memória utilizada Utilização de disco Utilização de terminal Utilização de impressora Prog1 Alta Poucas 5 min 50 Kb Não Não Não Prog2 Baixa Muitas 15 min 100 Kb Não Sim Não Prog3 Baixa Muitas 10 min 80 Kb Sim Não Sim Tabela 3.2 – Características de execução do programas Se fossem realizados num ambiente monoprogramável, seriam executados em seqüência, totalizando 30 minutos. Se fossem executados em ambiente multiprogramável, os ganhos são consideráveis, conforme mostra a tabela 3.3. Características Utilização do processador Utilização da memória Utilização de disco Utilização de impressora Tempo total de processamento Taxa de processamento Monoprogramação 17 % 30 % 33% 33 % 30 min. 6 progr. / hora Multiprogramação 33 % 67 % 67 % 67% 15 min. 12 progr. / hora Tabela 3.3 – Comparação entre Mono e multiprogramação 37 Apostila Fundamentos de Sistemas Operacionais . A seguir, será apresentado técnicas de implementação da concorrência, essencial num sistema multiprogramável. 3.2 Interrupção e Exceção: Durante a execução de um programa, eventos inesperados podem ocorrer, ocasionando um desvio forcado em seu fluxo de execução. Estes tipos de eventos são conhecidos por interrupção ou exceção e podem ser conseqüência da sinalização de algum hardware externo ou da execução de instruções do próprio programa. A diferença entre interrupção e exceção e dada pelo tipo de evento ocorrido. Porém, nem sempre é feita esta distinção. A interrupção permitiu a implementação da concorrência nos computadores. Com este mecanismo, o SO sincroniza a execução de todas suas rotinas e dos programas dos usuários, além de controlar dispositivos. Uma interrupção é gerada por algum evento externo ao programa e independe da instrução que esta sendo executada. Um exemplo de interrupção ocorre quando um disco avisa ao processador que uma operação de leitura ou escrita está completa. Neste caso, o processador interrompe o programa e trata o término da operação. Ao final da execução de cada instrução, a unidade de controle verifica a ocorrência de algum tipo de interrupção. Neste caso, o programa é desviado para uma rotina de tratamento de interrupção. Para o programa prosseguir posteriormente, as informações do programa executado são armazenadas em registradores no processador (Figura 3.2). Fig. 3.2 – Mecanismos de interrupção e exceção 38 Apostila Fundamentos de Sistemas Operacionais . A tabela 3.4 descreve o mecanismo de interrupção, que pode ser realizado por hardware ou software. Para cada tipo de interrupção existe uma rotina de tratamento associada. A identificação do tipo de interrupção é fundamental para determinar o endereço da rotina de tratamento. Existem dois métodos utilizados no tratamento das interrupções. O primeiro utiliza uma estrutura de dados chamada de vetor de interrupção, que contem o endereço inicial de todas as rotinas de tratamento existentes, associadas a cada evento. Um segundo método utiliza um registrador de status que armazena o tipo do evento ocorrido. Neste método, só existe uma única rotina de tratamento, que no seu inicio testa o registrador para identificar o tipo de interrupção e tratá-la de maneira adequada. Via Hardware Via Software 1. Um sinal de interrupção é gerado para o processador 2. Após o termino da execução da instrução corrente, o processador identifica o pedido de interrupção 3. Os conteúdos dos registradores apropriados são salvos 4. O processador identifica qual a rotina de tratamento que será executada e carrega um registrador com o endereço inicial desta rotina 5. A rotina de tratamento salva o conteúdo dos demais registradores na pilha de controle de programas 6. A rotina de tratamento é executada 7. Após o termino da execução da rotina, os registradores são restaurados, retomando a execução do programa interrompido Tabela 3.4 – Mecanismo de interrupção As interrupções são decorrentes de eventos assíncronos (não relacionados à instrução do programa). Estes eventos podem ser imprevisíveis e podem ocorrer múltiplas vezes. Isso possibilitaria a ocorrência de múltiplas interrupções simultâneas. Par evitar esta situação, é a rotina de tratamento inibir as demais interrupções. Assim, as interrupções posteriores seriam ignoradas durante a execução da rotina de tratamento, ou seja, não receberiam tratamento. Interrupções com estas características são chamadas de interrupções mascaráveis. Alguns processadores não permitem que interrupções sejam desabilitadas. Neste caso, o processador precisa saber a ordem em que ocorreram as interrupções concorrentes. Para isso, as interrupções devem possuir prioridades, em função de sua importância. Normalmente, um dispositivo denominado controlador déb pedidos de interrupção, é o responsável por avaliar as interrupções geradas e suas prioridades. 39 Apostila Fundamentos de Sistemas Operacionais . Uma exceção é semelhante a uma interrupção, sendo a principal diferença, o motivo pelo qual o evento é gerado. A exceção é resultado direto da execução de uma instrução do próprio programa, como a divisão de um número por zero, ou a ocorrência de um “overflow” e operações aritméticas. Portanto, a exceção é gerada por um evento síncrono. Tais eventos são portanto previsíveis e só podem ocorrer um único de cada vez. Um programa com este tipo de evento que seja reexecutado a exceção ocorrera sempre na mesma instrução. Assim como a interrupção, o programa interrompido é desviado para uma rotina de tratamento de exceção (Figura 3.2). O mecanismo de tratamento de exceções muitas vezes pode ser escrito pelo próprio programador. Assim, é possível evitar que um programa seja encerrado de ocorrer, por exemplo, um overflow. 3.3 Operações de Entrada/Saída: Nos primeiros SOs, existiam instruções especiais para controlar periféricos, denominadas de instruções de entrada/saída. Estas instruções continham detalhes de cada periférico, como por exemplo, na gravação de um bloco de dados em disco, deveria se especificar em qual trilha e setor ocorreria a gravação. Isto provocava uma forte dependência entre processador e os dispositivos de E/S. O controlador de interface surgiu para permitir que o processador fique mais independente dos periféricos. Desta forma, o processador não se comunica mais diretamente com os dispositivos de E/S, mas sim através do controlador (Figura 3.3). Memória Principal UCP Controlador Dispositivos de E/ S Fig. 3.3 – Controlador de dispositivos de E/S 40 Apostila Fundamentos de Sistemas Operacionais . O controlador simplificou as instruções de E/S, pois o processador não necessitaria utilizar instruções detalhadas. Com a utilização do controlador, existiam duas maneiras básicas pelas quais o processador gerenciava as operações de E/S: - Na primeira, o processador sincroniza-se com o periférico para o inicio da troca de dados entre eles, e depois de iniciada a transferência, o sistema ficava permanentemente testando o estado dos periféricos para saber quando a operação de E/S terminaria. Este tipo de controle, chamado de E/S controlada por programa, mantinha o processador ocupado até o fim da operação de E/S (busy wait). Isto provocava um desperdício de tempo, pois o processador executa uma instrução muito mais rapidamente que a realização de uma operação de E/S. - Na segunda, há uma evolução do modo anterior, onde uma vez iniciada a transferência de dados, o processador permanece livre para realizar outras tarefas. Neste caso, em determinados intervalos de tempo, o SO deve testar os dispositivos para saber do término da operação de E/S (polling). Esta técnica permitiu um tipo de paralelismo, sendo a base dos sistemas multiprogramáveis. O único inconveniente desta técnica, é que caso haja muitos periféricos, o processamento será interrompido freqüentemente. Com o mecanismo de interrupção, as operações de E/S se tornaram mais eficientes, uma vez que o controlador interrompe o processador para avisar o fim de uma operação, ao invés do processador ficar constantemente verificando as operações pendentes (E/S controlada por interrupção). Esta última técnica é mais eficiente do que a controlada por programa, pois elimina a necessidade do processador esperar pelo término da operação, alem de permitir varias operações de E/S simultâneas. Ainda existe um outro inconveniente neste processo. É o caso em que há uma grande quantidade de dados, onde as muitas intervenções do processador acabam por reduzir sua eficiência. Uma solução pensada para este problema, foi a técnica de transferência de dados chamada de DMA (Direct Memory Address). A técnica DMA permite que um bloco de dados seja transferido entre a memória principal e um dispositivo de E/S, sem a intervenção do processador, exceto no inicio e no fim da transferência. Quando o sistema deseja ler ou gravar um bloco de dados, o processador informa ao controlador sua localização, o dispositivo de E/S, a posição inicia da memória onde os dados serão lidos ou gravados e o tamanho do bloco. Com estas informações, o controlador realiza a transferência entre o periférico e a memória principal, e o processador será interrompido somente no final da operação. A área de memória utilizada pelo controlador na técnica de DMA é chamada de buffer de entrada/saída. 41 Apostila Fundamentos de Sistemas Operacionais . Na técnica de DMA, o controlador assume momentaneamente o controle do barramento. Neste caso, a utilização do barramento passa ser exclusiva do periférico, e o processador suspende temporariamente o acesso ao barramento. O processador pode nestas horas, realizar tarefas que não dependa, do barramento, como por exemplo, acesso à memória cache. 3.4 Buffering: A técnica de buffering consiste na utilização de uma área na memória principal, denominada buffer, para a transferência de dados entre os dispositivos de E/S e a memória. Esta técnica permite que em uma operação de leitura, o dado seja primeiramente transferido para o buffer, liberando imediatamente o dispositivo para uma nova leitura. Assim, enquanto o processador processa o dado localizado no buffer, o periférico realiza outra operação de leitura, simultaneamente. O mesmo mecanismo pode ser aplicado às gravações (Figura 3.4) Memória Principal gravação UCP gravação Controlador Buffer leitura leitura Fig. 3.4 – Operações de E/S utilizando buffer O objetivo desta técnica é manter, na maior parte do tempo, processador e dispositivos de E/S ocupados, pois minimiza o problema da disparidade da velocidade de processamento entre o processador e os dispositivos. A unidade de transferência utilizada no buffering é o registro, que tem seu tamanho definido em função: - Da natureza do dispositivo. Por exemplo, uma linha gerada por uma impressora, ou um caractere de um teclado. - Da aplicação, definido em arquivo. Logicamente o buffer deve permitir armazenar diversos registros, de forma que hajam dados lidos e ainda não processados, ou processados mas ainda não gravados. Isto torna o processo bastante eficiente, pois é possível compatibilizar a diferença existente entre o tempo em que o processador executa instruções e o tempo em que o dispositivo de E/S realiza suas operações de leitura e gravação. 42 Apostila Fundamentos de Sistemas Operacionais . 3.5 Spooling: A técnica de spooling (simultaneous peripheral operation on-line), introduzida no final dos anos 50, visa aumentar o grau de concorrência e a eficiência dos SOs. Semelhante à técnica de buffering, a técnica de spooling utiliza uma área em disco como se fosse um grande buffer. Neste caso, os dados podem ser lidos ou gravados em disco, enquanto programas são executados concorrentemente. Esta técnica esta presente na maioria dos SOs, utilizada no gerenciamento de impressão. No momento em que um comando de impressão é executado, as informações a serem impressas são gravadas antes em um arquivo em disco, conhecido como arquivo de spool, liberando o programa para outras atividades. Posteriormente, o SO se encarrega de direcionar o conteúdo do arquivo de spool para a impressora (Figura 3.5). Sistema Operacional Programa Arquivo de Spool Impressora Fig. 3.5 – Técnica de Spooling Esta técnica desvincula o programa do dispositivo impressor, impedindo que um programa reserve a impressora para uso exclusivo. O SO gerencia a seqüência de impressões solicitadas pelos programas, seguindo critérios de segurança e de uso eficiente das impressoras. 3.6 Reentrância: Em sistemas multiprogramáveis, vários usuários podem utilizar o mesmo aplicativo simultaneamente. Se cada um deles trouxesse o programa para uma área de memória, haveria desperdício de recursos (espaço em memória). Chamamos de reentrância, a capacidade de um código executável (código reentrante) ser compartilhado entre vários usuários, exigindo apenas uma cópia do programa em memória. Esta técnica permite que cada usuário possa estar em um ponto diferente do código reentrante, manipulando dados próprios e exclusivos de cada usuário (Figura 3.6). 43 Apostila Fundamentos de Sistemas Operacionais . usuário A usuário C código reentrante usuário B usuário D área de dados do usuário A área de dados do usuário B área de dados do usuário C área de dados do usuário D Memória Principal Fig. 3.6 – Reentrância 3.7 Proteção do Sistema: A complexidade de um sistema multiprogramável resulta em alguns problemas de proteção. Considerando que diversos usuários compartilham os mesmos recursos, como memória e processador, deve existir uma preocupação com a confiabilidade e integridade dos dados e programas dos usuários, alem do próprio SO. Como vários programas ocupam a memória simultaneamente, cada usuário possui uma área reservada onde seus dados e código são armazenados. O SO deve preservar estas informações. Caso um programa tente acessar uma posição de memória fora de sua área, um erro indicando a violação deve ocorrer. Semelhante ao compartilhamento de memória, um disco armazena arquivos de diferentes usuários. Novamente o SO deve garantir a integridade e confidencialidade dos dados de cada usuário. O compartilhamento de arquivos em disco permite que dois ou mais usuários acessem um mesmo arquivo simultaneamente pelo SO. No sistema multiprogramável, diversos programas compartilham o processador. Portanto, deve ser tarefa do SO também, o controle da utilização do processador, evitando que algum programa monopolize seu uso. Assim, o SO deve implementar diversos mecanismos de proteção que controlem o acesso concorrente aos diversos recursos do sistema. Estes mecanismos serão abordados em capítulos posteriores. 44 Apostila Fundamentos de Sistemas Operacionais . Capítulo 4 ESTRUTURA DO SISTEMA OPERACIONAL 4.1 Introdução: O Sistema Operacional é formado por um conjunto de rotinas que oferecem serviços aos usuários, às suas aplicações, e ao próprio sistema. Este conjunto de rotinas é denominado núcleo do sistema ou kernel. Não confundir o núcleo do sistema com aplicações, utilitários ou interpretadores de comando, que acompanham o SO. (Figura 4.1). As aplicações são utilizadas pelos usuários e não mostram os detalhes da interação com o sistema. Os utilitários, como compiladores e editores de texto, e os interpretadores de comando, permitem aos usuários, administradores e desenvolvedores, uma interação amigável com o sistema. Aplicativos Utilitários Núcleo do Sistema Operacional Hardware Fig. 4.1 – Sistema Computacional Uma das dificuldades de se compreender o funcionamento de um SO, é devido ao fato que ele não possui um início, um meio e um fim, como outras aplicações. Suas ações dependem de eventos que não ocorrem em uma ordem pré-definida. Muitos eventos estão relacionados ao hardware e ao próprio sistema. 45 Apostila Fundamentos de Sistemas Operacionais . 4.2 Principais Funções: As principais funções do núcleo na maioria dos SOs estão listadas a seguir: Tratamento de interrupções e exceções: já explicados anteriormente, em detalhes; Criação e eliminação de processos: função responsável por alocar em memória todos os recursos necessários à execução do processo. É esta função que aloca em memória, além do executável, o contexto do processo, o buffer de leitura/gravação (se necessário), além de listas e estruturas de controle utilizadas pelo sistema operacional. Nesta função também são estabelecidos vínculos físicos a arquivos em disco, fitas e outros periféricos que serão usados no processamento. Quando do fim da execução do programa, é esta função que desaloca todos os espaços em memória ocupados pelo processo, liberando-os para futuras alocações a outros processos; Escalonamento e controle de processos: função responsável por organizar a fila de acesso ao processador. Utiliza parâmetros do sistema e do perfil do usuário para estabelecer a ordem em que os processos permanecerão à espera pela liberação da CPU, para então entrarem em execução; Gerência de memória: função responsável por fornecer à função de criação/eliminação de processos os endereços em memória disponíveis para alocação; Gerência de sistemas de arquivos: responsável pelo gerenciamento dos arquivos, bem como seu compartilhamento pelos diversos usuários, implementando mecanismos de controle da segurança e direitos de acesso às áreas utilizadas pelos usuários nos diversos dispositivos; Gerência de dispositivos de E/S: responsável por gerenciar os dispositivos, prestando auxílio à criação/eliminação de processos e á gerência de sistemas de arquivos no que diz respeito ao endereçamento e associação de arquivos em periféricos; Suporte a redes e teleprocessamento: é esta função que executa todos os serviços de rede, fazendo o empacotamento das mensagens vindas dos terminais para a CPU central e vice-versa, além de controlar e confirmar o envio e recebimento de todas as mensagens que trafegam pela rede; Contabilização de uso do sistema: responsável por contabilizar o uso de todos os recursos do sistema consumidos pelos usuários e suas aplicações. São registrados: tempo de CPU, tempo corrido, quantidade de área alocada em memória, em disco, linhas impressas, páginas de papel, entre outros. Isto se faz necessário para servir de subsídio para análise de performance, estatísticas de 46 Apostila Fundamentos de Sistemas Operacionais . gastos com material de consumo e também para definição de custos de processamento.; Auditoria e segurança do sistema: função extremamente importante, pois detecta e registra (num arquivo especial de LOG) todas as ocorrências de erro e violação de direitos de acesso ao sistema, aos arquivos, à memória e a todos os recursos do sistema. O arquivo de LOG é usado pela gerência de sistemas, com o intuito de verificar e aperfeiçoar os mecanismos de segurança e proteção ao sistema. A forma como o código do sistema é organizado e o relacionamento com seus diversos componentes varia de acordo com o projeto do SO. 4.3 System Calls: Uma grande preocupação no projeto de um SO, é quanto a sua integridade, ou seja, a proteção do núcleo do sistema contra possíveis acessos não-autorizados. As system calls (chamadas ao sistema) podem ser entendidas como uma porta de entrada para o acesso ao núcleo do sistema e seus serviços. Quando um usuário ou aplicação necessitar de algum serviço do sistema, é feita uma chamada a uma de suas rotinas através de uma system call. O termo system call é típico de ambientes Unix, no ambiente Windows é conhecida como API (Application Program Inteface). Para cada serviço disponível existe uma system call associada. Cada SO tem seu próprio conjunto de chamadas diferentes (Figura 4.2). Isto explica o fato de uma aplicação desenvolvida para um SO, não pode ser executada em outro. System Call Aplicação Núcleo do Sistema Operacional Biblioteca Hardware Fig. 4.2 System call Algumas tentativas foram feitas para padronizar uma biblioteca de chamadas. O padrão POSIX (Portable Operating System Interface for Unix), por exemplo, foi estabelecido com a intenção de unificar as diversas versões do Unix existentes. Através de parâmetros fornecidos nas system calls, a solicitação é processada e uma resposta é retornada à aplicação, junto com um status indicando erro ou não. A ativação e comunicação entre programas e o SO, é semelhante a uma sub-rotina de um programa. 47 Apostila Fundamentos de Sistemas Operacionais . As system calls podem ser divididas em grupos de funções (Tabela 4.1) Muitos usuários e mesmo programadores não imaginam os detalhes que envolvem um simples comando de leitura de um arquivo, quando se utiliza uma linguagem de programação de alto nível. O compilador converte este comando de alto nível para uma system call específica, que quando executada, verifica a ocorrência de erros e retorna os dados ao programa, de forma transparente ao usuário. Funções System Calls Gerência de processos e threads Criação e eliminação de processos e threads Alteração das características de processos e threads Sincronização e comunicação entre processos e threads Obtenção de informação sobre processos e threads Gerência de memória Gerência de sistema de arquivos Alocação e desalocação de memória Gerência de dispositivos Criação e eliminação de arquivos e diretórios Alteração das características de arquivos e diretórios Abrir e fechar arquivos Obtenção de informações sobre arquivos e diretórios Alocação e desalocação de dispositivos Operações de entrada/saída em dispositivos Obtenção de informações sobre dispositivos Tabela 4.1 – Funções das system calls 4.4 Modos de Acesso: Algumas instruções não podem ser colocadas diretamente à disposição das aplicações, pois sua utilização indevida poderia ocasionar problemas à integridade do sistema. Por exemplo, uma aplicação que atualize um arquivo em disco. O programa por si só, não pode especificar diretamente as instruções que acessam seus dados no disco. Como o disco é um recurso compartilhado, quem gerencia exclusivamente sua utilização é o SO. Assim se evita que a aplicação acesse qualquer parte do disco indiscriminadamente, o que poderia comprometer a segurança e integridade do sistema de arquivos. 48 Apostila Fundamentos de Sistemas Operacionais . Portanto, fica claro que certas instruções só devem ser executadas pelo SO ou sob sua supervisão, impedindo assim riscos de segurança e integridade. As instruções que possuem o poder de comprometer o sistema são conhecidas como instruções privilegiadas, enquanto que as não-privilegiadas não oferecem estes riscos. Para que uma aplicação não execute uma instrução privilegiada, deve ser implementado no processador um mecanismo de proteção, conhecido como modos de acesso. São basicamente dois os modos de acesso implementados pelo processador. O primeiro é o modo usuário, onde a aplicação só poderá executar instruções nãoprivilegiadas, tendo acesso a um numero reduzido de instruções. O segundo modo é o modo kernel ou supervisor, onde a aplicação tem acesso ao conjunto total de instruções do processador. O modo de acesso de uma aplicação é determinado por um registrador de status do processador (chamado de PSW), Dependendo do conteúdo deste registrador, o hardware verifica se a instrução pode ou não ser executada pela aplicação. Apenas o SO deve ter acesso às instruções privilegiadas. Portanto, se uma aplicação necessitar executar uma instrução privilegiada, deve solicitar sua execução através de uma system call. A system call altera o status do PSW para modo kernel, e ao termino de sua execução retorna ao modo usuário (Figura 4.3). Caso uma aplicação tente executar uma instrução privilegiada em modo usuário, o processador sinaliza um erro, uma exceção será gerada e o programa será interrompido. No mesmo exemplo do acesso ao disco, para o programa atualizar um arquivo, a aplicação deve solicitar a operação ao SO por meio de uma system call, que altera o modo de acesso para kernel. Após efetuar a atualização, retorna-se ao modo usuário. O mecanismo de modos de acesso também é uma boa forma de proteger o próprio núcleo do sistema residente em memória. Por exemplo, se uma aplicação tivesse acesso à área de memória onde está o SO, um programador mal-intencionado ou um erro de programação poderia ter acessar esta área e acabar danificando o sistema. Com os modos de acesso, apenas em modo kernel poderia se efetuar tal tarefa. 49 Apostila Fundamentos de Sistemas Operacionais . Fig. 4.3 – Chamada a uma rotina do sistema 50 Apostila Fundamentos de Sistemas Operacionais . 4.5 Arquitetura Monolítica: A arquitetura monolítica pode ser comparada com uma aplicação formada por vários módulos que são compilados separadamente e depois linkados, formando um grande e único programa executável, onde os módulos podem interagir livremente. Os primeiros SOs baseavam-se neste modelo, tornando seu desenvolvimento e sua manutenção bastante difíceis. A estrutura monolítica apresenta simplicidade e bom desempenho, por isso foi usada no projeto do MS-DOS e nos primeiros sistemas Unix (Figura 4.4). aplicação aplicação Modo usuário Modo kernel System call Hardware Fig. 4.4 – Arquitetura monolítica 51 Apostila Fundamentos de Sistemas Operacionais . 4.6 Arquitetura de Camadas: Os sistemas operacionais tornaram-se mais complexos e maiores em tamanho, por isso, novas técnicas de programação estruturada e modular foram incorporadas ao seu projeto. Na arquitetura de camadas, o sistema é dividido em níveis sobrepostos. Cada camada oferece um conjunto de funções que podem ser utilizadas apenas pelas camadas superiores. O primeiro SO a usar este conceito, surgiu em 1968 na Holanda e utilizava seis camadas. Posteriormente, sistemas como MULTICS e OpenVMS também implementaram o conceito de camadas, agora sob forma concêntrica (Figura 4.5). Neste formato, as camadas mais internas são mais privilegiadas que as externas. Fig. 4.5 – Arquitetura em camadas do OpenVMS A vantagem da arquitetura em camadas é isolar as funções do SO, facilitando sua manutenção e depuração, alem de criar uma hierarquia de níveis de modos de acesso, protegendo as camadas mais internas. Porém, seu desempenho é inferior ao modelo monolítico. Cada nova camada implica uma mudança no modo de acesso. Por exemplo, no caso do OpenVMS, para ter acesso aos serviços oferecidos pelo kernel é preciso passar por três camadas ou três mudanças no modo de acesso. Atualmente, a maioria dos SOs utiliza o modelo de duas camadas, onde existem módulos de acesso usuário (não-privilegiado) e kernel (privilegiado). A maioria das versões do Unix e do Windows 2000 baseiam-se neste modelo. 52 Apostila Fundamentos de Sistemas Operacionais . 4.7 – Máquina Virtual: Um sistema computacional é formado por níveis, onde a camada de nível mais baixo é o hardware. Acima dele, encontramos o SO, que oferece suporte para as aplicações, como visto na Figura 4.1. O modelo de máquina virtual, ou virtual machine (VM), cria um nível intermediário entre o hardware e o SO, denominado gerência de máquinas virtuais (Figura 4.6). Este nível cria diversas maquinas virtuais independentes, onde cada uma oferece uma cópia virtual do hardware, incluindo os modos de acesso, interrupções, dispositivos de E/S, etc. Como cada VM é independente das demais, é possível que cada uma tenha seu próprio SO e que seus usuários executem suas aplicações como se todo o computador estivesse dedicado a cada um deles, Na década de 60, a IBM implantou este modelo no VM/370, permitindo aplicações batch, desenvolvidas em antigos sistemas OS/360 e aplicações de tempo compartilhado pudessem conviver na mesma máquina de forma transparente aos usuários e aplicações. VM 1 VM 2 VM n Além de permitir SOs diferentes no mesmo computador, este modelo cria o isolamento total entre cada VM, oferecendo grande segurança para cada uma delas. Por exemplo, se uma VM executar uma aplicação que comprometa o funcionamento de seu SO, as demais VMs não sofrerão qualquer problema. A maior desvantagem desta arquitetura é sua complexidade, que necessita compartilhar e gerenciar os recursos de hardware entre as diversas VMs. Ap1 Ap2 Apn SO1 SO2 SOn HV1 HV2 HVn Gerência de Máquinas Virtuais Hardware Fig. 4.6 – Máquina Virtual 53 Apostila Fundamentos de Sistemas Operacionais . Temos outro exemplo desta arquitetura, na linguagem Java, criada pela Sun Microsystems. Para executar um programa em Java, é necessário uma máquina virtual Java (ou Java Virtual Machine – JVM). Qualquer SO pode suportar uma aplicação Java, desde que exista uma JVM desenvolvida para ele. Assim, a aplicação não precisa ser compilada para cada sistema, tornando-se independente do hardware e SO utilizado (Figura 4.7). Aplicação Máquina Virtual Java Sistema Operacional Hardware Fig. 4.7 – Máquina Virtual Java 54 Apostila Fundamentos de Sistemas Operacionais . 4.8 Arquitetura Microkernel: Uma tendência nos SOs modernos é tornar o núcleo do sistema operacional o menor e mais simples possível. Para implementar esta idéia, os serviços do sistema são disponibilizados através de processos, onde cada um é responsável por oferecer um conjunto específico de funções, como gerência de arquivos, de processos, de memória e escalonamento. me n m ge sa Modo kernel n me Modo usuário sa g em Se uma aplicação desejar algum serviço, é realizada uma solicitação ao processo responsável. Neste caso, a aplicação é chamada de cliente e o processo que responde é chamado de servidor. A solicitação é feita enviando-se uma mensagem ao servidor, que responde com outra mensagem. A principal função do núcleo é realizar esta comunicação entre cliente e servidor (Figura 4.8). Microkernel Hardware Fig. 4.8 – Arquitetura Microkernel Este conceito de Arquitetura Microkernel surgiu na década de 80, com o sistema operacional Mach. O núcleo do Mach oferece basicamente quatro serviços: gerência de processos, gerência de memória, comunicação por troca de mensagens e operações de E/S, todos em modo usuário. 55 Apostila Fundamentos de Sistemas Operacionais . A utilização deste modelo permite que os servidores executem em modo usuário, ou seja, não tenham acesso direto a certos componentes do sistema. Apenas o núcleo do SO, responsável pela comunicação entre clientes e servidores, executa no modo kernel. Assim, se um erro ocorrer em um servidor, este poderá parar, mas o sistema não ficará inteiramente comprometido, aumentando sua disponibilidade (tempo em que está acessível). Como os servidores se comunicam através de troca de mensagens, não importa se os clientes e servidores são processados em um sistema com um processador, com múltiplos processadores (fortemente acoplados) ou ainda em um ambiente de sistema distribuído (fracamente acoplados). A implementação de sistemas microkernel em ambientes distribuídos permite que um cliente solicite um serviço e a resposta seja processada remotamente. Isso permite acrescentar novos servidores à medida que aumenta o numero de clientes, conferindo uma grande escalabilidade ao SO. Outra vantagem é que a arquitetura microkernel permite isolar as funções do sistema operacional por diversos processos servidores pequenos e dedicados a serviços específicos, tornado o núcleo menor, mais fácil de depurar e, conseqüentemente, aumentando sua confiabilidade. Na arquitetura microkernel, o sistema operacional passa a ser de mais fácil manutenção, flexível e de maior portabilidade. Apesar de todas as vantagens deste modelo, sua implementação, na prática, é muito difícil. Primeiro existe o problema de desempenho, devido à necessidade de mudança de modo de acesso a cada comunicação entre clientes e servidores. Outro problema é que certas funções do sistema operacional exigem acesso direto ao hardware, como operações de E/S. Na verdade, o que se implanta normalmente é a combinação do modelo de camadas com a arquitetura de microkernel. O núcleo do sistema, além de ser responsável pela comunicação entre cliente e servidor, passa a incorporar outras funções críticas, como escalonamento, tratamento de interrupções e gerência de dispositivos. 56 Apostila Fundamentos de Sistemas Operacionais . 4.9 Projeto do Sistema: O projeto de um SO é bastante complexo e deve atender inúmeros requisitos, algumas vezes conflitantes, como confiabilidade, portabilidade e facilidade de manutenção. O projeto irá depender muito da arquitetura do hardware utilizado e do tipo de sistema que se deseja construir: batch, tempo compartilhado, mono usuário ou multiusuário, tempo real, etc. Os primeiros SOs foram desenvolvidos em assembly e seus códigos possuíam cerca de um milhão de instruções (ex.: IBM OS/360). Com a evolução dos SOs e conseqüentemente o aumento do numero de linhas de código (cerca de 20 milhões no sistema MULTICS), novas técnicas de programação modular foram incorporadas ao projeto, além do uso de linguagens de alto nível, como PL/1 e Algol. Nos SOs atuais, o número de linhas de código chega perto dos 40 milhões (Windows 2000), sendo escrito grande parte em linguagem C/C++, utilizando em alguns casos, programação orientada a objetos. Existe uma série de vantagens na utilização de programação por objetos no projeto e na implementação de sistemas operacionais. Os principais benefícios são: - melhoria na organização das funções e recursos do sistema; redução no tempo de desenvolvimento; maior facilidade na manutenção e extensão do sistema e facilidade de implementação do modelo de computação distribuída. Além disso, o uso de uma linguagem de alto nível permite maior portabilidade, ou seja, o SO pode ser adaptado para outra arquitetura de hardware. Por outro lado, o uso de linguagens de alto nível em relação à programação assembly, apresenta perda de desempenho. Por isso, partes críticas do sistema, como device drivers, o escalonador e as rotinas de tratamento de interrupções, são ainda desenvolvidas em assembly. Um princípio fundamental no projeto de SOs é a separação no projeto do sistema das políticas e dos mecanismos. A política define o que deve ser feito, e o mecanismo define como implementar esta política. 57 Apostila Fundamentos de Sistemas Operacionais . Capítulo 5 PROCESSO 5.1 Introdução: O processo é a base para implantação de um SO multiprogramável. O processador executa instruções, sem distinguir qual programa se encontra em execução. A gerência de um ambiente multiprogramável é uma função exclusiva do SO, que deve controlar a execução dos diversos programas e o uso concorrente do processador. Assim, um programa deve estar associado a um processo. O termo processo surgiu após os SOs multiprogramáveis, sendo utilizado no lugar de tarefa ou job, por grande parte da literatura técnica. O SO deve controlar os processos. Através dos processos, um programa pode alocar recursos, compartilhar dados, trocar informações e sincronizar sua execução. Nos SOs multiprogramáveis, os processos são executados concorrentemente, compartilhando, entre outros recursos, o uso do processador, da memória e dos dispositivos de E/S. Em sistemas multiprocessados, além da concorrência de processos pelo uso do processador, existe também a execução simultânea de processos nos diferentes processadores. Aqui abordaremos os principais conceitos relacionados aos processos. 5.2 Estrutura do Processo: Inicialmente, pode-se entender um processo, como um programa em execução, porém, com um conceito mais abrangente. Imaginemos como os SOs multiprogramáveis atendem os diversos usuários e ainda mantém informações a respeito dos vários programas executados ao mesmo tempo. Em um sistema multiusuário, cada usuário é associado a um processo. Ao executar um programa, o usuário tem a impressão de possuir o processador e demais recursos exclusivamente para si. Na verdade, o processador executa o programa de um usuário durante um intervalo de tempo, e no instante seguinte poderá estar executando um outro programa de outro usuário. Para que esta troca ocorra sem traumas, é necessário que as informações do programa interrompido sejam guardadas, para que no seu retorno, nada seja perdido. Todas informações importantes e necessárias à execução de um programa fazem parte de um processo. 58 Apostila Fundamentos de Sistemas Operacionais . Um processo também pode ser definido como o ambiente em que o programa é executado. Este ambiente possui informações sobre a execução, de quanto de recursos do sistema cada programa pode utilizar, como espaço de memória, tempo do processador e área em disco. A execução de um mesmo programa pode variar dependendo do processo no qual ele é executado, ou seja, em função dos recursos disponíveis. A falta de recursos pode impedir um programa de ser executado com sucesso. Por exemplo, caso um programa precise utilizar uma área em disco superior ao seu limite, o SO deve interromper sua execução por falta de recursos. Um processo é formado por três partes, denominadas contexto de hardware, contexto de software e espaço de endereçamento, que juntas mantêm todas informações necessárias à execução de um programa (Figura 5.1). Contexto de Software Contexto de Hardware Programa Espaço de Endereçamento Fig. 5.1 – Estrutura do processo 5.2.1 Contexto de hardware: O contexto de hardware guarda o conteúdo dos registradores do processador. Quando um processo está em execução, o seu contexto de hardware está armazenado nos registradores do processador. Quando o processo perde a utilização da CPU, o sistema salva as informações no contexto de hardware do processo. O contexto de hardware é fundamental nos sistemas multiprogramáveis, onde o revezamento da CPU permite que os processos sejam interrompidos e posteriormente restaurados. A troca de um processo por outro no processador, executada pelo SO, é 59 Apostila Fundamentos de Sistemas Operacionais . chamada de mudança de contexto, e consiste em salvar o conteúdo dos registradores do processo que esta saindo e carregá-los com os valores referentes ao do novo processo que será executado (Figura 5.2). 5.2.2 Contexto de software: No contexto de software são especificadas as características e limites dos recursos que podem ser alocados pelo processo, como o numero Maximo de arquivos abertos simultaneamente, prioridade de execução e tamanho do buffer dos dispositivos de E/S. Muitas destas características são determinadas no momento da criação do processo, enquanto outras podem ser alteradas posteriormente. A maior parte das informações do contexto de software do provenientes de um arquivo do SO, conhecido como arquivo de contas. gerenciado pelo SO, são especificados os limites dos recursos que cada alocar. Outras informações presentes no contexto de software dinamicamente ao longo da execução dos processos. processo são Neste arquivo, processo pode são geradas Sistema Operacional Processo A Processo B executando Salva registradores do Processo A Carrega registradores do Processo B executando Salva registradores do Processo B Carrega registradores do Processo A executando Fig. 5.2 – Mudança de contexto 60 Apostila Fundamentos de Sistemas Operacionais . O contexto de software é composto por três grupos de informações sobre o respectivo processo: identificação, quotas e privilégios. Identificação Cada processo criado pelo sistema recebe uma identificação única (chamada de PID – Process IDentification), representada por um número e em alguns casos também através de um nome. É através do PID que o SO e outros processos podem fazer referência a qualquer processo existente, consultando e até alterando suas características. O processo possui também a identificação do usuário ou do processo que o criou (owner). Cada usuário possui uma identificação também única no sistema (representada pelo UID – User Identification), que é atribuída ao processo no momento de sua criação. A UID permite implementar modelos de segurança, onde apenas os objetos (processos, arquivos, áreas de memória, etc) que possuam um UID autorizado, podem ser acessados. Quotas As quotas são os limites de cada recurso do sistema que um processo pode alocar. Caso uma quota seja insuficiente, o processo poderá ser executado lentamente, interrompido ou mesmo não ser executado. Alguns exemplos de quotas presentes nos SOs modernos: - número máximo de arquivos abertos simultaneamente; - tamanho máximo de memória principal e secundaria que o processo pode alocar; - número máximo de operações de E/S pendentes; - tamanho máximo do buffer para operações de E/S; - numero máximo de processos, subprocessos e threads que podem ser criados. Privilégios Os privilégios ou direitos definem as ações que um processo pode fazer em ralação a ele mesmo, aos demais processos e ao SO. Privilégios que afetam o próprio processo permitem que suas características possam ser alteradas, como prioridade de execução, limites alocados na memória principal e secundaria, etc. Já os privilégios que afetam os demais processos permitem, alem da alteração de suas próprias características, alterar as de outros processos. Privilégios que afetam o sistema são mais amplos e poderosos, pois estão relacionados à gerência do ambiente, como a desativação do sistema, alteração de regras de segurança, criação de outros processos privilegiados, modificação de parâmetros de configuração do sistema, entre outros. A maioria dos SOs possui uma conta de acesso com todos privilégios disponíveis, afim de o administrador gerenciar o SO. Nos 61 Apostila Fundamentos de Sistemas Operacionais . sistemas Unix, existe a conta “root”, no Windows 2000 a conta “administrador” e no Open VMS a conta “system”. 5.2.3 Espaço de endereçamento: O espaço de endereçamento é a área de memória pertencente ao processo onde as instruções e dados do programa são armazenados para execução. Cada processo possui seu próprio espaço de endereçamento, que deve ser devidamente protegido do acesso dos demais processos. A Figura 5.3 ilustra as características da estrutura de um processo. nome PID owner (UID) registradores gerais prioridade de execução data/ hora de criação registrador PC Contexto de Software Contexto de Hardware registrador SP tempo de processador quotas Programa privilégios registrador de status Espaço de Endereçamento endereços de memória principal alocados Fig. 5.3 – Características da estrutura de um processo 5.2.4 Bloco de controle do processo: O processo é implementado pelo SO através de uma estrutura de dados chamada Bloco de controle de processos (Process Control Block – PCB). A partir do PCB, o SO mantém todas as informações sobre o contexto de hardware, contexto de software e espaço de endereçamento de cada processo (Fig. 5.4). 62 Apostila Fundamentos de Sistemas Operacionais . ponteiros Estado do processo Nome do processo Prioridade do processo Registradores Limites de memória Lista de arquivos abertos .. .. .. .. Fig. 5.4 – Bloco de controle do processo (PCB) 5.3 Estados do Processo: Em um estado multiprogramável, um processo não deve alocar o processador com exclusividade, de forma que possa ser compartilhado. Os processos passam por diferentes estados ao longo de seu processamento, em função de eventos gerados pelo SO ou pelo próprio processo. Um processo ativo pode encontra-se em três estados diferentes: Execução (running) – Um processo é dito no estado de execução quando está sendo processado pela CPU. Em sistemas com apenas um processador, somente um processo estará sendo executado em um dado instante. Os processos se alternam na utilização do processador, seguindo uma política estabelecida pelo SO. Em sistemas com multi-processadores, existe a possibilidade de mais de um processo ser executado ao mesmo tempo, como também é possível um mesmo processo ser executado simultaneamente em mais de um processador (processamento paralelo). Pronto (ready) – Um processo está no estado de pronto quando aguarda apenas para ser executado. O SO que determina a ordem e o critério pelos quais os processos neste estado devem utilizar o processador. Este mecanismo é conhecido como escalonamento. Em geral, existem vários processos no estado de pronto, organizados em listas encadeadas. Os processos devem estar ordenados pela sua importância (Figura 5.5). 63 Apostila Fundamentos de Sistemas Operacionais . Lista de processos em estado de pronto .. .. .. .. PCB# 5 .. .. .. .. PCB# 1 Lista de processos em estado de espera .. .. .. .. .. .. .. .. .. .. .. .. PCB# 9 PCB# 2 PCB# 4 Fig. 5.5 – Lista de PCBs nos estados de pronto e espera. Espera (wait) – Um processo no estado de espera aguarda por algum evento externo ou por algum recurso para prosseguir seu processamento. Por exemplo, o término de uma operação de E/S ou a espera de uma data ou hora para continuar sua execução. Em alguns SOs, este estado também pode ser chamado de bloqueado (blocked). O sistema organiza os vários processos no estado de espera também em listas encadeadas. Em geral os processos são separados em listas de espera associadas a cada tipo de evento (Figura 5.5). Neste caso, quando um evento acontece, todos processos da lista associada ao evento são transferidos para o estado de pronto. 5.4 Mudanças de Estado de Processo: Um processo muda de estado durante seu processamento em função de eventos originados por ele próprio (eventos voluntários) ou pelo SO (eventos involuntários). Basicamente apenas quatro mudanças de estados podem ocorrer: Pronto → Execução – Após a criação de um processo, o sistema o coloca em uma lista de processos no estado de pronto, aguardando para ser executado (Figura 5.6 a). Cada SO tem sua política de escalonamento. Execução → Espera – Um processo passa a estado de espera por eventos gerados pelo próprio processo, como uma operação de E/S, ou por eventos externos (Figura 5.6 b). Um evento externo é gerado, por exemplo, quando o SO suspende por um período de tempo a execução de um processo 64 Apostila Fundamentos de Sistemas Operacionais . Espera → Pronto – Ocorre quando uma operação solicitada é atendida ou o recurso esperado é concedido. Não existe a mudança de espera para execução diretamente (Figura 5.6 c). Execução → Pronto – Ocorre através de eventos gerados pelo sistema, como o termino da fatia de tempo que o processo possui para sua execução (Figura 5.6 d). Então, aguarda nova oportunidade para continuar seu processamento. Quando não houver espaço suficiente para todos os processos na memória principal, um processo em estado de pronto ou espera pode ser encontrado na memória secundária. Uma técnica conhecida como swapping retira processos da memória principal e os traz de volta segundo seus próprios critérios. Portanto, os processos em estado de espera e pronto, podem estar ou não residentes em memória principal (Figura 5.7). Estado de Execução d b a c Estado de Espera Estado de Pronto Fig. 5.6 – Mudanças de estados do processo 65 Apostila Fundamentos de Sistemas Operacionais . Estado de Execução Estado de Espera Estado de Pronto Estado de Espera Estado de Pronto residente não residente Fig. 5.7 – Mudanças de estado do processo (2). 5.5 Criação e Eliminação de Processos: Processos são criados e eliminados por diversas razões. A criação de um processo ocorre quando o SO adiciona um novo PCB à sua estrutura e reserva espaço na memória para uso. A partir da criação do PCB, o SO já reconhece a existência do processo, passando a gerenciá-lo e associar programas ao seu contexto para serem executados. No caso da eliminação de um processo, todos recursos associados a ele são desalocados e o PCB eliminado pelo SO. Além destes três processos, a maioria dos SOs estabelece mais dois estados para os momentos de criação e eliminação de um processo (Figura 5.8). 66 Apostila Fundamentos de Sistemas Operacionais Estado de Execução Estado de Espera . Estado de Término Estado de Pronto Estado de Criação Fig. 5.8 – Mudanças de estado do processo (3) Criação (new) – Um processo é considerado em estado de criação, quando o SO já criou um novo PCB, porém ainda não pode colocá-lo na lista de processos do estado de pronto. Alguns SOs limitam o número de processos ativos em função dos recursos disponíveis ou de desempenho. Esta limitação pode ocasionar que os processos criados permaneçam no estado de criação até que possam passar a ativos. A criação de processos pode ocorrer por razões como: - logon interativo: um processo é criado através do estabelecimento de uma sessão interativa por um usuário a partir de um terminal. - criação por um outro processo: um processo já existente pode criar outros processos, sendo estes novos independentes ou subprocessos. - criação pelo SO: o SO pode criar novos processos para oferecer algum serviço. Terminado (exit) – Um processo no estado terminado não poderá ter mais nenhum programa executado em seu contexto, porém, o SO ainda mantém suas informações de controle na memória. Neste estado, o processo não é mais considerado ativo, mas como o PCB ainda existe, o SO pode recuperar informações sobre a contabilização de uso de recursos do processo, como o tempo total do processador. Após a extração das informações, o processo pode deixar de existir. O término de um processo pode ocorrer devido a: - término normal da execução; - eliminação por um outro processo; - eliminação forcada por ausência de recursos disponíveis no sistema. 67 Apostila Fundamentos de Sistemas Operacionais . 5.6 Processos Independentes, Subprocessos e Threads: Processos, subprocessos e threads são maneiras diferentes de implementar a concorrência dentro de uma aplicação. Neste caso, busca-se dividir o código em partes para trabalharem de forma cooperativa. Por exemplo, um banco de dados que recebe consultas constantes e freqüentes. Aqui, a concorrência na aplicação proporciona um tempo de espera menor entre consultas, melhorando o desempenho da aplicação e beneficiando os usuários. O uso de processos independentes é a maneira mais simples de se implementar a concorrência em sistemas multiprogramáveis. Neste caso, não existe vínculo entre o processo criado e seu criador. A criação de um processo independente exige a locação de um PCB, possuindo contextos de hardware, software e espaço de endereçamento próprios. Subprocessos são criados dentro de uma estrutura hierárquica. E neste caso, o processo criador é denominado processo pai e o novo processo é chamado de subprocesso ou processo filho. O subprocesso pode criar outros subprocessos. Uma característica desta implementação é a de criar dependência entre processo criador e subprocesso. Se o processo deixar de existir, também os subprocessos o farão. Assim como os processos independentes, os subprocessos também exigem seu próprio PCB. A Figura 5.9 ilustra cinco processos numa estrutura hierárquica, cada qual com seus contextos e espaços de endereçamento. Além disso, os subprocessos também podem compartilhar quotas com o processo pai. Ou seja, quando o subprocesso é criado, o processo pai cede parte de suas quotas a ele. O uso de processos independentes e subprocessos demanda consumo de recursos, uma vez que recursos são alocados toda vez que um processo é criado, e também tempo do processador é utilizado para este trabalho. Assim também ocorre no seu término (desalocação de recursos). Mais ainda, a comunicação e sincronização entre processos é considerada pouco eficiente, visto que cada processo possui seu próprio espaço de endereçamento. O conceito de thread foi estabelecido com a intenção de reduzir o tempo gasto na criação, eliminação e troca de contexto de processos nas aplicações concorrentes, bem como economizar recursos do sistema como um todo. Num ambiente multithread, um único processo pode suportar múltiplos threads, cada qual associado a uma parte do código da aplicação (Figura 5.10). Neste caso não é necessário haver diversos processos para implementação da concorrência. Threads compartilham o processador da mesma maneira que um processo, ou seja, enquanto espera por uma operação de E/S, outro thread pode ser executado. 68 Apostila Fundamentos de Sistemas Operacionais . Processo A Processo C Processo B Processo E Processo D Figura 5.9 – Estrutura de Processos e Subprocessos Cada thread possui seu próprio contexto de hardware, porém compartilha o mesmo contexto de software e espaço de endereçamento. O compartilhamento deste último permite que a comunicação de threads dentro de um mesmo processo seja feita de forma simples e rápida. Este assunto será melhor detalhado no capítulo seguinte. Contexto de hardware Contexto de hardware Thread 1 Thread 2 Thread 3 Contexto de software Contexto de hardware Espaço de endereçamento Fig. 5.10 – Processo Multithread 69 Apostila Fundamentos de Sistemas Operacionais . 5.7 Processos Foreground e Background: Um processo possui sempre associado à sua estrutura, pelo menos dois canais de comunicação por onde são realizadas todas as entradas e saídas de dados ao longo do seu processamento. Os canais de entrada (input) e de saída (output) de dados podem estar associados a terminais, arquivos, impressoras e ate mesmo outros processos. Um processo foreground é aquele que permite a comunicação direta do usuário com o processo durante sua execução. Assim, ambos os canais estão associados a um terminal com teclado, mouse e monitor, permitindo portanto, a interação com o usuário (Figura 5.11a). O processamento interativo tem com base processos foreground. Um processo background é aquele onde não existe a comunicação com o usuário durante seu processamento (Figura 5.11b). Aqui, os canais de E/S não estão associados a nenhum dispositivo de E/S interativo, mas em geral a arquivos de E/S. O processamento batch, por exemplo, é realizado através de processos background. Quando o canal de saída de um processo estiver associado ao canal de entrada de outro processo, dizemos que existe um pipe ligando ambos os processos. Por exemplo, se um processo A gera uma listagem e o processo B tem como função ordenála, basta associar o canal de saída do processo A ao canal de entrada do processo B (Figura 5.12). (a) Processo Foreground entrada saída terminal terminal (b) Processo Background entrada saída arquivo de entrada arquivo de saída Fig. 5.11 – Processos foreground e background 70 Apostila Fundamentos de Sistemas Operacionais . saída do Processo A entrada do Processo A saída do Processo B entrada do Processo B Processo A Processo B Figura 5.12 – Pipe 5.8 Processos do Sistema Operacional: O conceito de processo, além de estar associado a aplicações de usuários, podem também ser implementados na própria arquitetura do SO. Como visto em capítulo anterior, a arquitetura microkernel implementa uso intensivo de processos que disponibilizam serviços para processos das aplicações e do próprio SO. Quando processos são utilizados para a implementação de serviços do sistema, estamos retirando códigos de seu núcleo, tornando-o menor e mais estável. No caso de um ou mais serviços não serem desejados, basta não ativar os processos responsáveis, o que permitirá liberar memória para os processos dos usuários. Alguns serviços que o SO pode implementar através de processos são: auditoria e segurança; serviços de rede; contabilização do uso de recursos; contabilização de erros; gerência de impressão; gerência de jobs match; temporização; comunicação de eventos; interface de comandos. 71 Apostila Fundamentos de Sistemas Operacionais . 5.9 Processos CPU-Bound e I/O-Bound: Os processos podem ser classificados como CPU-Bond ou I/O-Bond, de acordo com a utilização do processador e dos dispositivos de E/S. Um processo é definido como CPU-Bound (ligado à CPU), quando passa a maior parte do tempo no estado de execução, ou seja, utilizando o processador (Figura 5.13a). Este tipo de processo realiza poucas operações de leitura e gravação e é encontrado em aplicações científicas que efetuam muitos cálculos. Por outro lado, um processo I/O-Bound (ligado à E/S) passa a maior parte do tempo no estado de espera, pois realiza grande número de operações de E/S (Figura 5.13b). Aplicações comerciais, que se baseiam em leitura, processamento e gravação são exemplos de processos deste tipo, assim como também os processos interativos, pela forma de comunicação entre o usuário e o sistema, normalmente lenta, devido ao uso de terminais. E/ S E/ S UCP UCP tempo (a) CPU-bound tempo (b) I/ O-bound Fig. 5.13 – Processos CPU-bound x I/O-bound 72 Apostila Fundamentos de Sistemas Operacionais . 5.10 Sinais: Sinais são um mecanismo que permite notificar processos de eventos gerados pelo sistema operacional ou por outros processos. O uso de sinais é fundamental para a gerência de processos, além de possibilitar a comunicação e sincronização entre processos. Por exemplo, ao se teclar simultaneamente as teclas Ctrl e C, para interromper um programa, o SO gera um sinal ao processo, sinalizando a ocorrência do evento. O processo identificando o sinal, uma rotina especial de tratamento é executada (Figura 5.14). interrupção sinal Sistema Operacional [ctrl-C] Fig. 5.14 – Uso de Sinais Processo Os sinais podem ser usados em conjunto com temporizadores com a intenção de sinalizar ao processo algum evento associado ao tempo. Por exemplo, um processo que deve ser avisado periodicamente sobre uma tarefa a realizar, como monitorar uma fila de pedidos. Após a realização da tarefa, o processo retorna à espera do próximo sinal. A maior parte dos eventos associados a sinais são gerados pelo SO ou pelo hardware, como as exceções, interrupções, limites de quotas excedidos e alarmes de tempo. Em outros casos, os eventos são gerados a partir de outros processos com o propósito de sincronizar suas execuções. A geração de um sinal ocorre quando o SO, a partir da ocorrência de eventos síncronos ou assíncronos, notifica o processo através de bits de sinalização localizados no seu PCB. Um processo não responde instantaneamente a um sinal. Os sinais ficam pendentes até que o processo seja escalonado, quando então serão tratados. Por exemplo, na eliminação de um processo, o sistema ativa o bit associado a este evento. O processo só será excluído do sistema quando for selecionado para execução. Assim, é possível que o processo demore algum tempo até ser eliminado de fato. O tratamento de um sinal é bem semelhante ao de uma interrupção. Quando um sinal é tratado, o contexto do processo é salvo e a execução desviada para um código de tratamento de sinal (signal handler), geralmente no núcleo do SO. Após a execução do 73 Apostila Fundamentos de Sistemas Operacionais . tratador de sinais, o programa pode voltar a ser processado do ponto onde foi interrompido. Às vezes, o próprio processo pode tratar o sinal através de um tradutor de sinais definido no código do programa. É possível também que um processo bloqueie temporariamente ou ignore por completo alguns sinais. Apesar do mecanismo ser parecido com o do tratamento de interrupções e exceções, os propósitos são diferentes. O sinal está para o processo assim como as interrupções e exceções estão para o sistema operacional (Figura 5.15) Processo Processo Sinais Sistema Operacional Interrupções Exceções Hardware Fig. 5.15 – Sinais, interrupções e exceções. 74 Apostila Fundamentos de Sistemas Operacionais . Capítulo 6 THREAD 6.1 Introdução: Até o final dos anos 70, os SOs suportavam processos com apenas um thread (monothread), ou seja, um processo com apenas um programa fazendo parte de seu contexto. Em 1979, introduziu-se o conceito de processos “ligthweight” (peso leve), onde o espaço de endereçamento de um processo era compartilhado por vários programas. Porém, esta idéia não foi utilizada comercialmente, e apenas na metade da década de 80, com o SO Mach, ficou clara a separação entre os conceitos de processo e thread. Com o conceito de múltiplos threads (multithread), pode-se projetar aplicações concorrentes de forma eficiente, pois um processo pode ter diferentes partes de seu código sendo executadas em paralelo. Como os threads de um mesmo processo compartilham o mesmo espaço de endereçamento, a comunicação entre threads não envolve mecanismos lentos de intercomunicação entre processos, aumentando assim o desempenho da comunicação. O desenvolvimento de programas que exploram os benefícios da programação multithread não é simples. A presença do paralelismo introduz um novo conjunto de problemas, como a comunicação e sincronização de threads. Existem diferentes modelos para a implementação de threads em um SO, onde desempenho, flexibilidade e custos devem ser avaliados. Atualmente, o conceito de multithread pode ser encontrado em sistemas como Sun Solaris e Windows 2000. A utilização comercial de sistemas multithreads tem crescido devido ao aumento de popularidade de sistemas com multiprocessadores, do modelo cliente-servidor w dos sistemas distribuídos. 6.2 Ambiente Monothread: Um programa é uma seqüência de instruções, compostas de desvios, repetições e chamadas a procedimentos e funções. Em um ambiente monothread, um processo suporta apenas um programa em seu espaço de endereçamento. Neste ambiente, aplicações concorrentes são implementadas apenas com o uso de múltiplos processos independentes ou subprocessos. O uso de processos independentes e subprocessos permite dividir uma aplicação em partes que podem trabalhar de forma concorrente. Por exemplo, um usuário pode estar lendo seus e-mails antigos, ao mesmo tempo em que estaria enviando e recebendo e-mails atuais. Co o uso de múltiplos processos, cada funcionalidade do software implicaria na criação de um novo processo para atendê-lo, aumentando o desempenho da aplicação (Figura 6.1). 75 Apostila Fundamentos de Sistemas Operacionais . Um problema é que o uso de processos no desenvolvimento de aplicações concorrentes demanda consumo de diversos recursos do sistema. Sempre que um novo processo é criado, o sistema deve alocar recursos para cada processo, consumindo tempo de processador neste trabalho. No caso do término do processo, o sistema dispensa tempo para desalocar recursos previamente alocados. Outro problema a ser considerado é quanto ao compartilhamento do espaço de endereçamento. Como cada processo possui seu próprio espaço de endereçamento, a comunicação entre processos torna-se difícil e lenta, pois utiliza mecanismos como pipes, sinais, semáforos, memória compartilhada ou troca de mensagem. Além disso, o compartilhamento de recursos comuns aos processos concorrentes, como memória e arquivos abertos, não é simples. Na Figura 6.2 existem três processos monothread, cada um com seu próprio contexto de hardware, de software e espaço de endereçamento. Subprocessos Processos Independentes Fig. 6.1 – Concorrência com subprocessos e processos independentes São exemplos de sistemas monothread o MS-DOS e as primeiras versões do Windows. Mesmo em ambientes multiprogramáveis e multiusuários, encontra-se exemplos de implementações monothread, como nas versões mais antigas dos sistemas VAX/VMS e Unix. Thread Thread Thread Fig. 6.2 – Ambiente monothread 76 Apostila Fundamentos de Sistemas Operacionais . 6.3 Ambiente Multithread: Em um ambiente multithread, ou seja, com múltiplos threads, não existe a idéia de programas associados a processos, mas sim a threads. O processo, neste ambiente, tem pelo menos um thread em execução, mas pode compartilhar o seu espaço de endereçamento com inúmeros outros threads. Na Figura 6.3 existe apenas um processo com três threads de execução compartilhando o mesmo espaço de endereçamento. Contexto de hardware Contexto de hardware Thread 1 Thread 2 Thread 3 Contexto de software Contexto de hardware Espaço de endereçamento Fig. 6.3 – Ambiente Multithread De forma simplificada, um thread pode ser definido como uma sub-rotina de um programa que pode ser executada de forma assíncrona, ou seja, executada paralelamente ao programa chamador. O programador deve especificar os threads, associando-os às sub-rotinas assíncronas. Assim, um ambiente multithread possibilita a execução concorrente de sub-rotinas dentro de um mesmo processo. Na Figura 6.4 existe um programa principal que realiza a chamada de suas subrotinas assíncronas (Sub_1 e Sub_2). Inicialmente, o processo é criado apenas com o Thread_0 para a execução do programa principal. Quando o programa principal chama as duas sub-rotinas, são criados os Thread_1 e Thread_2, e executados independentemente do programa principal. Neste processo, os três threads são executados concorrentemente. 77 Apostila Fundamentos de Sistemas Operacionais . No ambiente multithread, cada processo pode responder a varias solicitações concorrentemente ou mesmo simultaneamente, caso haja mais de um processador. A grande vantagem no uso de threads é a possibilidade de minimizar a alocação de recursos do sistema, alem de diminuir o overhead na criação, troca e eliminação de processos. Threads compartilham o processador da mesma maneira que processos e passam pelas mesmas mudanças de estado (execução, espera e pronto). Por exemplo, enquanto um thread espera por uma operação de E/S, outro thread pode ser executado. Para permitir a troca de contexto entre os diversos threads, cada um possui seu próprio contexto de hardware, com o conteúdo dos registradores gerais, PC e SP. Quando um thread está sendo executado, seu contexto hardware está armazenado nos registradores do processador. No momento em que o thread perde a utilização do processador, as informações são atualizadas no seu contexto de hardware. Processo Variáveis Programa Principal Thread_1 PC SP Contexto de Hardware ... Espaço de endereçamento Call Sub_1 Thread_2 Sub_1 Ret PC SP Thread_3 Sub_2 PC SP Contexto de Hardware Fim Contexto de Hardware Call Sub_2 ... Ret Fig. 6.4 – Aplicação Multithread (a) 78 Apostila Fundamentos de Sistemas Operacionais . Dentro de um mesmo processo, threads compartilham o mesmo contexto de software e espaço de endereçamento com os demais threads, porém cada thread possui seu contexto de hardware individual. Threads são implementados internamente através de uma estrutura de dados denominada bloco de controle do thread (Thread Control Block – TCB). O TCB armazena, além do contexto de hardware, mais algumas informações relacionadas exclusivamente ao thread, como prioridade, estado de execução e bits de estado. Em ambientes monothread, o processo é ao mesmo tempo a unidade de alocação de recursos e a unidade de escalonamento. A independência entre os conceitos de processo e thread permite separar a unidade de alocação de recursos da unidade de escalonamento, que em ambientes monothread estão fortemente relacionadas. Em um ambiente multithread, a unidade de alocação de recursos é o processo, onde todos os seus threads compartilham o espaço de endereçamento, descritores de arquivos de dispositivos de E/S. Por outro lado, cada thread representa uma unidade de escalonamento independente. Neste caso, o sistema não seleciona um processo para a execução, mas sim um de seus threads. A grande diferença entre aplicações mono e multithread está no uso do espaço de endereçamento. Processos independentes e subprocessos possuem espaços de endereçamento individuais e protegidos, enquanto threads compartilham o espaço dentro de um mesmo processo. Isso permite que o compartilhamento de dados entre threads de um mesmo processo seja mais simples e rápido, se comparado a ambientes monothreads. Como threads de um mesmo processo compartilham o mesmo espaço de endereçamento, não existe qualquer proteção no acesso à memória, permitindo que um thread possa alterar facilmente dados de outros. Para que os threads trabalhem de forma cooperativa, é fundamental que a aplicação implemente mecanismos de comunicação e sincronização entre threads, a fim de garantir o acesso seguro aos dados compartilhados na memória. O uso de multithreads proporciona uma serie de benefícios. Programas concorrentes com múltiplos threads são mais rápidos do que programas concorrentes implementados com múltiplos processos, pois operações de criação, troca de contexto e eliminação dos threads geram menor overhead (Tabela 6.1). Como os threads dentro de um processo dividem o mesmo espaço de endereçamento, a comunicação entre eles pode ser realizada de forma rápida e eficiente. Além disso, threads em um mesmo processo podem compartilhar facilmente outros recursos, como descritores de arquivos, temporizadores, sinais, atributos de segurança, etc. 79 Apostila Fundamentos de Sistemas Operacionais . Implementação Tempo de criação (μs) Processo Processo Lightweight Thread 1700 350 52 Tempo de sincronização (μs) 200 390 66 Tabela 6.1 – Latência de Processos e Threads A utilização do processador, dos discos e outros periféricos pode ser feita de forma concorrente pelos diversos threads, significando melhor utilização dos recursos computacionais disponíveis. Em algumas aplicações, a utilização de threads pode melhorar o desempenho da aplicação apenas executando tarefas em background enquanto operações E/S estão sendo processadas (Figura 6.5). Aplicações como editores de texto, planilhas, aplicativos gráficos e processadores de imagem são especialmente beneficiados quando desenvolvidos com base em threads. Em ambientes cliente-servidor, threads são essenciais para solicitação de serviços remotos. Em um ambiente monothread, se uma aplicação solicita um serviço remoto, ela pode ficar esperando indefinidamente, enquanto aguarda pelo resultado. Em um ambiente multithread, um thread pode solicitar o serviço remoto, enquanto a aplicação pode continuar realizando outras atividades. Já para o processo que atende a solicitação, múltiplos threads permitem que diversos pedidos sejam atendidos simultaneamente (Figura 6.6). Thread de entrada Buffer Thread de exibição Thread de gravação Fig. 6.5 – Aplicação Multithread (b) 80 Apostila Fundamentos de Sistemas Operacionais . Processo servidor Solicitações Thread Thread Thread Processo cliente Processo cliente Processo cliente Fig. 6.6 – Aplicação multithread (c) Não apenas aplicações tradicionais podem fazer uso dos benefícios do multithreading. O núcleo do SO também pode ser implementado com o uso desta técnica de forma vantajosa, como na arquitetura microkernel, apresentada em capítulo anterior. 81 Apostila Fundamentos de Sistemas Operacionais . 6.4 Arquitetura e Implementação: O conjunto de rotinas disponíveis para que uma aplicação utilize as facilidades dos threads é chamado de pacote de threads. Existem diferentes abordagens na implementação deste pacote em um SO, o que influenciará no desempenho, na concorrência e na modularidade das aplicações multithread. Threads podem ser oferecidos por uma biblioteca de rotinas fora do núcleo do SO (modo usuário), pelo próprio núcleo do sistema (modo kernel), por uma combinação de ambos (modo híbrido) ou por um modelo conhecido como “scheduler activations”. A Tabela 6.2 resume as diversas arquiteturas para diferentes ambientes operacionais. Ambientes Distributed Computing Environment (DCE) Compaq Open VMS versão 6 MS Windows 2000 Compaq Unix Compaq Open VMS versão 7 Sun Solaris versão 2 University of Washington FastThreads Arquitetura Modo Usuário Modo Usuário Modo Kernel Modo Kernel Modo Kernel Modo Híbrido Scheduler Activations Tabela 6.2 – Arquitetura de threads para diversos ambientes operacionais Uma das grandes dificuldades para a utilização de threads foi a ausência de um padrão. Em 1995, o padrão POSIX P1003.1c foi aprovado e posteriormente atualizado para a versão POSIX 1003.4 a. Com este padrão, também conhecido como Pthreads, aplicações comerciais multithreading tornaram-se mais simples e de fácil implementação. O padrão Pthreads é largamente utilizado em ambientes Unix, como o Sun Solaris Pthreads e o DECthreads para Digital OSF/1. 6.4.1 Threads em Modo Usuário: Threads em modo usuário (TMU) são implementados pela aplicação e não pelo SO. Para isso, deve existir uma biblioteca de rotinas que possibilite a aplicação realizar tarefas como criação/eliminação de threads, troca de mensagens entre threads e uma política de escalonamento. Neste modo, o SO não sabe da existência de múltiplos threads, sendo responsabilidade exclusiva da aplicação gerenciar e sincronizar os diversos threads existentes. A vantagem deste modelo é a possibilidade de implementar aplicações multithreads mesmo em SOs que não suportem threads. Utilizando biblioteca, múltiplos threads podem ser criados, compartilhando o mesmo espaço de endereçamento do processo, além de outros recursos (Figura 6.7). TMU são rápidos e eficientes por dispensarem acessos ao kernel do SO, evitando assim a mudança de modo de acesso (usuário-kernel-usuário). 82 Apostila Fundamentos de Sistemas Operacionais . Thread 4 Thread 3 Thread 2 Thread 1 Thread 0 Os TMU possuem uma grande limitação, pois o SO gerencia cada processo como se existisse apenas um thread. Quando o thread chama uma rotina do sistema que o coloca em estado de espera (rotina bloqueante), todo o processo é colocado no estado de espera, mesmo havendo outros threads prontos para execução. Para contornar esta limitação, a biblioteca tem que possuir rotinas que substituam as rotinas bloqueantes por outras que não possam causar o bloqueio de um thread (rotinas não-bloqueantes). Todo este controle é transparente para o usuário e para o SO. Modo usuário Biblioteca Kernel Modo kernel Figura 6.7 – Threads em modo usuário Talvez um dos maiores problemas na implementação de TMU seja o tratamento individual de sinais. Como o sistema reconhece apenas processos e não threads, os sinais enviados para um processo devem ser reconhecidos e encaminhados a cada thread para tratamento. No caso do recebimento de interrupções de clock, fundamental para implementação do tempo compartilhado, esta limitação é crítica. Neste caso, os sinais de temporização devem ser interceptados, para que se possa interromper o thread em execução e realizar a troca de contexto. Em relação ao escalonamento em ambientes com múltiplos processadores, não é possível que múltiplos threads de um processo possam ser executados em diferentes processadores simultaneamente, pois o sistema seleciona apenas processos para execução e não threads. Esta restrição limita drasticamente o grau de paralelismo da aplicação, já que os threads de um mesmo processo podem ser executados em somente um processador de cada vez. 83 Apostila Fundamentos de Sistemas Operacionais . 6.4.2 Threads em Modo Kernel: Threads em Modo Kernel (TMK) são implementados diretamente pelo núcleo do SO, através de chamadas a rotinas do sistema que oferecem todas as funções de gerenciamento e sincronização (Fig. 6.8). O S.O. sabe da existência de cada thread e pode escaloná-los individualmente. No caso de múltiplos processadores, os threads de um mesmo processo podem ser executados simultaneamente. Thread 4 Thread 3 Thread 2 Thread 0 Thread 1 O grande problema para pacotes em modo kernel é o seu baixo desempenho. Enquanto nos pacotes em modo usuário todo tratamento é feito sem ajuda do SO, ou seja, sem a mudança do modo de acesso (usuário-kernel-usuário), pacotes em modo kernel utilizam chamadas a rotinas do sistema e, conseqüentemente, várias mudanças no modo de acesso. A Tabela 6.3 compara o desempenho de duas operações distintas envolvendo a criação, escalonamento, execução e eliminação de um processo/thread. Modo usuário Biblioteca Modo kernel Kernel Figura 6.8 – Threads em Modo Kernel Implementação Subprocessos Threads em Modo Kernel Threads em Modo Usuário Operação 1 (μs) 11.300 948 34 Operação 2 (μs) 1.840 441 37 Tabela 6.3 – Comparação entre tempos de latência 84 Apostila Fundamentos de Sistemas Operacionais . 6.4.3 Threads em Modo Híbrido: A arquitetura de threads em modo híbrido combina as vantagens de threads implementados em modo usuário (TMU) e threads em modo kernel (TMK). Um processo pode ter vários TMKs, e por sua vez, um TMK pode ter vários TMUs. O núcleo do SO reconhece os TMKs e pode escaloná-los individualmente. Um TMU pode ser executado em um TMK, em um determinado momento, e no instante seguinte ser executado em outro. O programador desenvolve a aplicação em termos de TMU e especifica quantos TMK estão associados ao processo. Os TMUs são mapeados em TMK enquanto o processo está sendo executado. O programador pode utilizar apenas TMK, TMU ou uma combinação de ambos (Figura 6.9). TMU 5 TMU 4 TMU 3 TMU 2 TMU 1 TMU 0 O modo híbrido, apesar de maior flexibilidade, apresenta problemas herdados de ambas as implementações. Por exemplo, quando um TMK realiza uma chamada bloqueante, todos os TMUs são colocados no estado de espera. TMUs que desejam utilizar vários processos deve utilizar diferentes TMKs, o que influenciará no desempenho. Modo usuário Biblioteca TMK 0 TMK 1 TMK 2 TMK 3 Modo kernel Kernel Figura 6.9 – Threads em modo híbrido 85 Apostila Fundamentos de Sistemas Operacionais . 6.4.4 Scheduler Activations: Os problemas apresentados no pacote de threads em modo híbrido existem devido à falta de comunicação entre threads em modo usuário e em modo kernel. O modelo ideal deveria utilizar as facilidades do pacote em modo kernel com o desempenho e flexibilidade do modo usuário. Introduzido no início dos anos 90, este pacote combina o melhor das duas arquiteturas, mas em vez de dividir os threads em modo usuário entre os de modo kernel, o núcleo do sistema troca informações com a biblioteca de threads utilizando uma estrutura de dados chamada scheduler activations (Figura 6.10). Thread 4 Thread 3 Thread 2 Thread 1 Thread 0 A maneira de alcançar um melhor desempenho é evitar as mudanças de modos de acesso desnecessárias (usuário-kernel-usuário). Caso um thread utilize uma chamada ao sistema que o coloque no estado de espera, não é necessário que o kernel seja ativado, bastando que a própria biblioteca em modo usuário escalone outro thread. Isto é possível porque a biblioteca em modo usuário e o kernel se comunicam e trabalham de forma cooperativa. Cada camada implementa seu escalonamento de forma independente, porem trocando informações quando necessário. Modo usuário Biblioteca Kernel Modo kernel Figura 6.10 – Scheduler activations 86 Apostila Fundamentos de Sistemas Operacionais . 6.5 Modelos de Programação: O desenvolvimento de aplicações multithread não é simples, pois exige que a comunicação e o compartilhamento de recursos entre os diversos threads seja feito de forma sincronizada para evitar problemas de inconsistências e deadlock. Além das dificuldades naturais no desenvolvimento de aplicações concorrentes, o procedimento de depuração é bastante complexo. Um fator importante em aplicações multithread é o numero total de threads e a forma como são criados e eliminados. Se uma aplicação cria um numero excessivo de threads, poderá ocorrer um overhead no sistema, ocasionando uma queda de desempenho. Dependendo da implementação, a definição do numero de threads pode ser dinâmica ou estática. Quando a criação/eliminação é dinâmica, os threads são criados/eliminados conforme a demanda da aplicação, oferecendo grande flexibilidade. Já em ambientes estáticos, o número de threads é definido na criação do processo onde a aplicação será executada. Para obter os benefícios do uso de threads, uma aplicação deve permitir que partes diferentes de seu código sejam executadas em paralelo de forma independente. Se um aplicativo realiza várias operações de E/S e trata eventos assíncronos, a programação multithread aumenta seu desempenho até mesmo em ambientes com um fraco processador. Sistemas gerenciadores de banco de dados (SGBDs), servidores de arquivo ou impressão são exemplos onde o uso de múltiplos threads proporciona grandes vantagens e benefícios. 87 Apostila Fundamentos de Sistemas Operacionais . Capítulo 7 Sincronização e Comunicação entre processos 7.1 Introdução: Com o surgimento dos SOs multiprogramáveis, tornou-se possível estruturar aplicações de maneira que partes diferentes do código do programa pudessem ser executadas concorrentemente. Este tipo de aplicação foi denominada de aplicação concorrente. Em sistemas com um único processador, os processos alternam sua execução segundo escalonamento estabelecido pelo SO e mesmo assim aplicações concorrentes obtêm melhoras em seu desempenho. Em sistemas com múltiplos processadores, estendem-se estas vantagens com a possibilidade do paralelismo na execução de instruções. Os processos de uma aplicação concorrente podem compartilhar recursos, como arquivos registros, dispositivos de E/S e áreas de memória. Este compartilhamento pode gerar situações indesejáveis, capazes de comprometer a execução das aplicações. Para evitar este tipo de problema, os processos devem ter suas ações sincronizadas, através de mecanismos oferecidos pelo SO. 7.2 Aplicações Concorrentes: Em aplicações concorrentes, pode ser necessário que os processos comuniquemse entre si. Esta comunicação pode ser implementada através de variáveis compartilhadas na memória principal ou trocas de mensagens. Mais uma vez, é necessário que haja sincronização entre a execução dos processos concorrentes. A figura 7.1 apresenta um exemplo onde dois processos concorrentes compartilham um buffer para troca de informações. Aqui, um processo só poderá gravar dados no buffer se ele estiver vazio, e só poderá ler um dado do buffer caso haja um dado a ser lido. Em ambos os casos, os processos deverão esperar até que o buffer esteja pronto para as operações de gravação e leitura. Sincronização gr Processo gravador av aç ão i tu le ra Processo leitor dado Buffer Figura 7.1 – Sincronização e Comunicação entre processos 88 Apostila Fundamentos de Sistemas Operacionais . Os mecanismos que realizam a comunicação entre processos concorrentes e o acesso a recursos compartilhados são chamados de mecanismos de sincronização. Em SOs multiprogramáveis estes mecanismos são fundamentais para garantir a integridade e confiabilidade na execução de aplicações concorrentes. 7.3 Especificação de Concorrência em Programas: Existem varias notações para especificar quais partes de um programa que devem ser executadas concorrentemente. Técnicas mais recentes tentam expressar a concorrência no código dos programas de uma forma mais clara e estruturada. As primeiras notações para especificar uma concorrência em um programa foram os comandos FORK e JOIN. O exemplo abaixo exemplifica de forma simplificada este uso. PROGRAMA A; . . FORK B; . . JOIN B; . . END. END. PROGRAMA B; . . . . . O Programa A começa a ser executado e, ao encontrar o comando FORK, faz com que seja criado um outro processo para execução do Programa B, concorrentemente ao Programa A. O comando JOIN faz com que o Programa A sincronize-se com o B, ou seja, o Programa A só continuará a ser executado após o término da execução de B. Os comandos FORK e JOIN são poderosos e práticos, sendo utilizados de forma semelhante no Sistema Unix. Outra forma mais clara e simples de expressar concorrência em um programa é com o uso dos comandos PARBEGIN e PAREND (1965), que posteriormente foram chamados de COBEGIN e COEND. Aqui continuaremos a utilizar os comandos PARBEGIN e PAREND. A figura 7.2 demonstra o uso dos comandos PARBEGIN e PAREND. 89 Apostila Fundamentos de Sistemas Operacionais . Processo principal PARBEGIN Comando_1; Comando_2; . . Comando_n; PAREND Processo 1 Processo 2 Processo n Processo principal Figura 7.2 – Concorrência em programas Para exemplificar o uso destes comandos, o programa chamado EXPRESSAO realiza um cálculo do valor da expressão descrita a seguir: X := SQRT (1024) + (35.4 * 0.23) – (302 / 7) Os comandos situados entre PARBEGIN e PAREND são executados concorrentemente. O cálculo final de X só poderá ser realizado quando todas as variáveis dentro da estrutura estiverem sido calculadas. PROGRAM Expressao; VAR X, Temp1, Temp2, Temp3 : REAL; BEGIN PARBEGIN Temp1 := SQRT (1024); Temp2 := 35.4 * 0.23; Temp3 := 302 / 7; PAREND; X := Temp1 + Temp2 - Temp3; WRITELN ('x = ', X); END. 90 Apostila Fundamentos de Sistemas Operacionais . 7.4 Problemas de Compartilhamento de Recursos: Para melhor compreensão da importância da sincronização entre processos concorrentes, são apresentados alguns exemplos-problema de compartilhamento de recursos. O primeiro problema é analisado a partir do programa Conta_Corrente, que atualiza o saldo bancário de um cliente após o lançamento de débito ou crédito no arquivo de contas-correntes Arq_Contas. Neste arquivo são armazenados os saldos de todos os correntistas do banco. O programa lê o registro do cliente no arquivo (Reg_Cliente), lê o valor a ser depositado ou retirado (Valor_Dep_Ret) e, em seguida atualiza o saldo no arquivo de contas. PROGRAM Conta_Corrente; . . READ (Arq_Contas, Reg_Cliente); READLN (Valor_Dep_Ret); Reg_Cliente.Saldo := Reg_Cliente.Saldo + Valor_Dep_Ret; WRITE (Arq_Contas, Reg_Cliente); . . END. Considerando processos concorrentes pertencentes a dois funcionários do banco que atualizam o saldo de um mesmo cliente simultaneamente, a situação de compartilhamento do recurso pode ser analisada. O processo do primeiro funcionário (Caixa 1) lê o registro do cliente e soma ao campo Saldo o valor do lançamento de débito. Antes de gravar o novo saldo no arquivo, o processo do segundo funcionário (Caixa 2) lê o registro do mesmo cliente, que está sendo atualizado, para realizar outro lançamento, desta vez de crédito. Independente de qual processo atualize primeiro o saldo no arquivo, o dado gravado estará inconsistente. Acompanhe: Caixa 1 1 1 2 2 2 1 2 Comando READ READLN := READ READLN := WRITE WRITE Saldo Arquivo 1.000 1.000 1.000 1.000 1.000 1.000 800 1.300 Valor Dep/Ret * -200 -200 * +300 +300 -200 +300 Saldo Memória 1.000 1.000 800 1.000 1.000 1.300 800 1.300 91 Apostila Fundamentos de Sistemas Operacionais . Outro exemplo simples é a situação onde dois processos (A e B) executam um comando de atribuição. O processo A soma 1 à variável X e o processo B subtrai 1 da mesma variável. Suponha que inicialmente a variável X possua o valor 2. Processo A X: = X + 1 ; Processo B X: = X – 1; Seria razoável pensar que no final das operações a variável continuasse valendo 2, porem nem sempre isso será verdade. Decompondo em operações mais elementares, usando uma linguagem de alto nível, temos: Processo A Load x, Ra Add 1,Ra Store Ra,x Processo B Load x, Rb Sub 1,Rb Store Rb,x Considere que o Processo A carregue o valor de X no Registrador Ra, some 1, e no momento em que vai armazenar o novo valor de X, seja interrompido. Neste instante, inicia-se o Processo B, que carrega o valor de X em Rb e subtrai o valor 1. Agora o Processo B é interrompido e o A volta a ser executado, atribuindo o valor 3 à variável X e finalizando sua execução. O Processo B retorna sua execução, atribui o valor 1 a X e sobrepõe o valor anteriormente gravado pelo Processo ª O resultado final será inconsistente. Resumindo: Processo A A B B A B Comando Load X,Ra Add 1,Ra Load X,Rb Sub 1,Rb Store Ra,X Store Rb,X X 2 2 2 2 3 1 Ra 2 3 * * 3 * Rb * * 2 1 * 1 Através destes exemplos, conclui-se que quando dois ou mais processos compartilham um mesmo recurso, alguns mecanismos devem evitar que este tipo de problema ocorra (conhecidos como race conditions – condições de corrida). 92 Apostila Fundamentos de Sistemas Operacionais . 7.5 Exclusão Mútua: Para que sejam evitados problemas desta natureza, onde dois processos manipulem o mesmo arquivo ou a mesma variável de memória simultaneamente, enquanto um processo estiver acessando determinado recurso, todos os outros que queiram acessar esse mesmo recurso deverão esperar. Isso se chama EXCLUSÃO MUTUA (Mutual Exclusion). A exclusão mútua deverá agir apenas sobre os processos que estão concorrendo em um determinado recurso. Quando desenvolvemos um programa, que faça tratamento de exclusão mútua, este deverá terá uma seção chamada REGIÃO CRÍTICA (Critical Region). Nesta região existe uma série de procedimentos e protocolos que o programa deverá fazer para que o sistema operacional libere o recurso para o mesmo. Toda vez que um processo desejar executar instruções de sua região crítica, obrigatoriamente devera executar antes um protocolo de entrada nessa região. Da mesma forma, ao sair da região crítica um protocolo de saída deverá ser executado. A região critica deve ser sempre usada quando seu programa for fazer uso de recursos que são passiveis de compartilhamento com algum outro suposto programa na memória. É nela também que os processos encontram-se em um momento mais critico, pois qualquer erro ocorrido ali dentro pode fazer com que dois ou mais processos colidam gerando falhas e derrubando o sistema. Assim, para garantir a implementação da exclusão mútua, os processos envolvidos devem fazer acesso aos recursos de forma sincronizada. Diversas soluções foram criadas com este propósito; porém, ainda existem duas situações que devem ser evitadas. 7.5.A Starvation: A primeira situação indesejada é conhecida como starvation (ou espera indefinida). Quem determina as prioridades dos processos é o sistema operacional. Neste caso existem duas formas do sistema operacional determinar qual será a vez de quem. Ou por escolha aleatória ou por prioridades. Quando a escolha é aleatória, existirá a probabilidade de um processo nunca ser escolhido. Quando for uma escolha por prioridades, um processo de menor prioridade nunca receberá o acesso ao recurso, e ai este processo nunca executará sua rotina. Uma solução bastante simples é a criação de filas de pedidos de alocação para cada recurso, utilizando o esquema FIFO (First In First Out). Sempre que um processo solicita um recurso, o pedido é colocado no final da fila associada ao recurso. Quando o recurso é liberado, o sistema seleciona o primeiro processo da fila. 93 Apostila Fundamentos de Sistemas Operacionais 7.5.B . Sincronização condicional: Sincronização Condicional é uma situação onde o acesso a um recurso compartilhado exige a sincronização de processos vinculada a uma condição de acesso. Quando um recurso não está pronto para ser utilizado, o processo que vai acessar o recurso ficará em estado de espera até que o mesmo esteja pronto. Existe o risco deste recurso nunca ficar pronto por já estar com problemas. Ai todo o sistema fica esperando o recurso resolver sua vida. Um exemplo disto é o caso do uso de Buffers para leitura e gravação de dados feita pelos processos. Uma possível falha na memória que impeça o acesso aos buffers e todo o sistema estará parado... Diversas soluções foram propostas para garantir a exclusão mútua de processos concorrentes. A seguir, algumas soluções de hardware e software serão apresentadas, com comentários sobre suas vantagens e desvantagens. 7.5.1 Soluções de Hardware: Desabilitação de interrupções Faz com que o processo, antes de entrar em sua região crítica desabilite todas as interrupções externas e a reabilite após deixar a região critica. Como a mudança de contexto de processos só pode ser realizada através de interrupções, o processo que as desabilitou terá acesso exclusivo garantido. Apesar de simples, esta solução apresenta limitações. Primeiramente, a multiprogramação fica comprometida, uma vez a concorrência entre processos entre processos tem como base o uso da interrupção. Um caso mais grave poderia ocorrer caso um processo desabilitasse as interrupções e não tornasse a habilitá-las. Neste caso, o sistema provavelmente teria seu funcionamento comprometido. Em sistemas com múltiplos processadores esta solução torna-se ineficiente devido ao tempo de propagação quando um processador sinaliza aos demais que as interrupções devem ser habilitadas ou desabilitadas. Ainda, o mecanismo de clock do sistema é implementado através de interrupções, portanto esta solução deve ser implementada com muito critério. Apesar destas limitações, esta solução pode ser útil quando se deseja que a execução de parte do núcleo do SO ocorra sem que haja interrupção. Desta forma, o sistema pode garantir que não ocorrerão problemas de inconsistência em suas estruturas de dados durante a execução de algumas rotinas. Instrução test-and-set Muitos processadores possuem uma instrução especial onde um processo apenas lê o conteúdo de uma variável, e armazena seu valor em outra área podendo neste caso fazer todas as manipulações necessárias e devidas sem precisar de prioridades ou esperar que a variável original seja liberada. Esta instrução é chamada de test-and-set e 94 Apostila Fundamentos de Sistemas Operacionais . tem como característica ser executada sem interrupção, ou seja, trata-se de uma instrução invisível. Assim garante-se que dois processos não manipulem uma variável compartilhada ao mesmo tempo, possibilitando a implementação da exclusão mútua. O uso desta instrução especial oferece vantagens, como a simplicidade de implementação da exclusão mútua em múltiplas regiões críticas e o uso da solução em arquiteturas com múltiplos processadores. A principal desvantagem é a possibilidade do starvation, pois a seleção do processo para o acesso ao recurso é arbitrária. 7.5.2 Soluções de software: Diversos algoritmos foram propostos na tentativa de implementar a exclusão mútua através de soluções de software. As primeiras soluções tratavam apenas da exclusão mútua para dois processos e, inicialmente, apresentavam alguns problemas. A evolução ocorreu até uma solução definitiva para a exclusão mútua para N processos. Além da exclusão mútua, que soluciona os problemas de compartilhamento de recursos, existem outros fatores fundamentais para a solução de problemas de sincronização: - O número de processadores e o tempo de execução dos processos. - Um processo fora de sua região crítica não pode impedir que outros processos entrem em suas próprias regiões críticas. - Um processo não pode permanecer indefinidamente esperando para entrar em sua região crítica. Todas as soluções que foram apresentadas para contornar estes inconvenientes apresentavam problemas da ESPERA OCUPADA, Na espera ocupada, todas vezes que um processo tenta entrar em sua região crítica ele são impedidas por já existir um outro processo usando o recurso, fazendo o sistema ficar parado esperando que o mesmo tenha acesso a este respectivo recurso. 7.6 Semáforos: O conceito de semáforos foi proposto em 1965, sendo apresentado como um mecanismo de sincronização que permitia implementar, de forma simples, a exclusão mútua sincronização condicional entre processos. O semáforo é uma variável que fica associada a um recurso compartilhado, indicando quando este está sendo acessado por um outro processo. Ela terá seu valor alterado quando o processo entra e quando sai da região crítica de forma que se um outro processo entrar em sua região critica ele possa checar antes este valor para saber se o recurso esta ou não disponível. Quando o processo tiver seu acesso impedido, ele será colocado em uma fila de espera associada ao semáforo aguardando sua vez de utilizar o recurso. Todos os processos da fila terão acesso ao recurso na ordem de chegada. O semáforo pode ser usado também para implementar sincronizações condicionais. Isto consiste em um processo que necessita ser notificado sobre a 95 Apostila Fundamentos de Sistemas Operacionais . ocorrência de um evento. Pode-se usar o semáforo para notificar este processo sobre a ocorrência deste evento. Outro tipo de semáforo usado é SEMÁFORO CONSUMIDOR onde ele pode informar ao processo se o buffer está cheio ou está vazio. SEMÁFORO CONTADOR é aquele que notifica os processos sobre o uso dos recursos. Sempre que um processo usa um recurso qualquer, este semáforo é incrementado sempre que um processo liberar um recurso ele será decrementado. Este semáforo é útil para evitar que um processo na região crítica sem que haja recursos disponíveis no sistema. O uso de semáforos exige do programador muito cuidado, pois qualquer engano pode gerar bugs em seu programa que o levem a falhas de sincronização ocasionando quedas e travamento geral do sistema. DO 0) D O = (S W N (S > N W 0) Processo deseja entrar na região crítica UP (S) - processo sai da região crítica Libera processo da fila de espera Processo acessa a região crítica Fila de espera de processos Fig. 7.3 – Utilização do semáforo binário na exclusão mútua 96 Apostila Fundamentos de Sistemas Operacionais . 7.7 Monitores: Monitores são mecanismos de sincronização de alto nível que tornam mais simples o desenvolvimento de aplicações concorrentes. Este conceito foi proposto em 1972. Basicamente, são mecanismos de sincronização compostos de um conjunto de procedimentos, variáveis e estrutura de dados definidos dentro de um módulo cuja finalidade é a implementação automática da exclusão mútua entre seus procedimentos. Somente um processo pode estar executando um dos procedimentos do monitor em um determinado instante. Toda vez que um processo chamar um destes procedimentos, o monitor verifica se já existe outro processo executando algum procedimento do monitor. Caso exista, o processo fica aguardando a sua vez ate que tenha permissão para executá-lo. A implementação da exclusão mútua nos monitores é realizada pelo compilador do programa e não mais pelo programador. Para isto ele irá colocar todas as regiões críticas do programa em forma de procedimentos no monitor e o compilador se encarregará de garantir a exclusão mútua destes procedimentos. A comunicação do processo com o monitor passa a ser feita através de chamadas a seus procedimentos e dos parâmetros passados para eles. Outra característica do monitor é que os processos, quando não puderem acessar estes procedimentos, ficarão aguardando em uma fila de espera e enquanto isto, eles poderão executar outros procedimentos. Como ele é escrito em uma linguagem de programação, o compilador das outras demais linguagens deverão ser capazes de reconhecê-la e implementá-la. São raras as linguagens que permitem tal implementação criando uma limitação para o uso deste recurso. Declaração de variáveis globais Procedimentos Monitor Proc. 1 Proc. 2 Fila de entrada Proc. n Inicialização de variáveis Fig. 7.4 – Estrutura do monitor 97 Apostila Fundamentos de Sistemas Operacionais . 7.8 Troca de mensagens: A troca de mensagens é um mecanismo de comunicação e sincronização entre os processos, implementado pelo sistema operacional através de duas rotinas do sistema SEND e RECEIVE. A rotina SEND é a responsável pelo envio de uma mensagem para o processo receptor enquanto a rotina RECEIVE por receber a mensagem do processo transmissor. Tais procedimentos mesmo não sendo mutuamente exclusivos permitem a comunicação entre os processos e a sincronização entre eles, pois uma mensagem somente poderá ser lida depois de ter sido enviada e ela somente será envidada após a ocorrência de um evento. No sistema de troca de mensagens, existe a possibilidade da mensagem se perder. Para isto foi implementado o recurso de que o processo receptor ao recebê-la deverá enviar ao processo transmissor uma mensagem de recebimento. Caso o transmissor não receber esta mensagem em um certo espaço de tempo ele irá retransmitir esta mensagem. A comunicação entre processos pode ser feita diretamente. Bastando que o processo que deseja enviar uma mensagem enderece explicitamente o nome do receptor. Esta característica chama-se ENDEREÇAMENTO DIRETO e só é permitida à comunicação entre dois processos. Existe também o ENDEREÇAMENTO INDIRETO que é um mecanismo que consiste no uso de uma área compartilhada, onde as mensagens podem ser colocadas pelo processo transmissor e retiradas por qualquer processo. Existem duas formas de comunicação entre os processos: COMUNICAÇÃO SINCRONA e COMUNICAÇÃO ASSINCRONA. Uma comunicação é dita Síncrona, quando um processo envia uma mensagem e fica esperando até que o processo receptor leia a mensagem e mande a notificação de recebimento. Uma comunicação assíncrona é aquela em que o processo que envia a mensagem não espera notificação de recebimento. Processo A Processo B Fig. 7.5 – Comunicação direta 98 Apostila Fundamentos de Sistemas Operacionais Processo A . Processo B Mailbox ou Port Fig. 7.6 – Comunicação indireta 7.9 Deadlock: O Deadlock existe em qualquer sistema multiprogramável. Dizemos que um processo está em Deadlock quando este para de responder porque está esperando por um evento que nunca ocorrerá. Esta situação é conseqüência do problema da exclusão mútua. Existem as condições onde o Deadlock irá ocorrer: - Cada recurso só pode estar alocado a um único processo em um determinado instante. (Exclusão mútua) - Um processo além dos recursos já alocados, pode estar esperando por outros recursos. - Um recurso não pode ser liberado de um processo porque outros processos desejam o mesmo recurso (Não-preempção) - Um processo pode ter de esperar por um recurso alocado a outro processo e vice-versa (Espera circular). Processo A Processo A solicita o Recurso 2 Recurso 1 alocado ao Processo A Recurso 2 Recurso 1 Processo B Recurso 2 alocado ao Processo B Processo B solicita o Recurso 1 Fig. 7.7 – Espera circular 99 Apostila Fundamentos de Sistemas Operacionais . 7.9.1 Prevenção do Deadlock: Para prevenir o Deadlock é preciso garantir que uma das quatro condições acima citada nunca ocorra, dentre as diversas situações já citadas pode ser feito um minucioso trabalho de determinar muito bem que recursos, quais recursos e quando estes recursos deverão ser disponibilizados aos processos. 7.9.2 Detecção de Deadlock: Em sistemas que não possuam mecanismos que previnam a ocorrência de deadlocks, é necessário um esquema de detecção e correção do problema. A Detecção do Deadlock é um mecanismo que determina a existência deste e identifica os recursos envolvidos no problema. Um exemplo deste tipo de detector é o próprio Gerenciador de tarefas do Windows que detecta o aplicativo que parou de responder ao sistema causado, possivelmente, por um deadlock, como podemos ver logo abaixo: Fig. 7.8 – Exemplo de Deadlock 7.9.3 Correção do Deadlock: Geralmente o problema é resolvido eliminando os processos envolvidos e desalojando os recursos para ele já garantidos. É aquele processo em que você dá um Alt+Ctrl+Del no Windows e aparece uma janela informando o aplicativo que não responde. Este aplicativo pode estar em um processo de Deadlock, neste caso você manda finalizar o aplicativo e tudo voltará ao normal. Muitas vezes este mecanismo não resolve e pelo contrário gera novos problemas. Se você finalizar um processo que esteja 100 Apostila Fundamentos de Sistemas Operacionais . intimamente envolvido com o sistema operacional ou que esteja usando recursos de baixo nível do mesmo, você poderá vir a deixá-lo instável ou travado. Abaixo vemos a caixa de dialogo do Windows que tentará fechar o processo que pode estar parado por falta de comunicação com o sistema. Fig. 7.9 – Correção do Deadlock O problema do Deadlock é um problema que tende a tornar-se mais critico à medida que os sistemas operacionais evoluem no sentido de implementar o paralelismo e permitir a alocação dinâmica de um numero maior de recursos e a execução de um numero maior de processos simultaneamente. Os usuários sentem muita saudade dos computadores que rodavam o DOS nos bons tempos quando quase não davam problemas. Mas é bom lembrar que o DOS era um sistema operacional monotarefa e monousuário onde praticamente tínhamos apenas um único processo rodando de cada vez. Neste caso não existiam os problemas que um ambiente multitarefa e multiusuário tem hoje. Todos os recursos do sistema estavam exclusivamente disponíveis para aquele processo e, portanto ele tinha total e plena liberdade de fazer com estes o que bem entendia. Hoje os sistemas operacionais são mais complexos rodando em maquinas mais críticas devido à velocidade de processamento tendo um maior numero de aplicações que rodam simultaneamente e demandando praticamente todos os recursos do sistema ao mesmo tempo. Muitos destes programas trabalham não só com um, mas com vários processos simultaneamente o que aumentam as chances de colisões entre eles ou com os recursos do sistema. 101 Apostila Fundamentos de Sistemas Operacionais . Capítulo 8 Gerência do Processador 8.1 Funções: Com o surgimento dos sistemas multiprogramáveis, onde múltiplos processos poderiam permanecer na memória e disputar o uso de um único processador, a gerência do processador tornou-se uma das atividades mais importantes em um sistema operacional. A partir do momento em que vários processos podem estar no estado de pronto, devem ser estabelecidos critérios para definir qual processo será escolhido para fazer uso do processador. Tais critérios compõem a política de escalonamento, que é a base da gerência do processador e da multiprogramação em um sistema operacional. Dentre as funções da gerência do processador, podemos citar: manter o processador ocupado a maior parte do tempo. balancear o uso da CPU entre processos, privilegiar a execução de aplicações críticas, maximizar o throughput e oferecer tempos de resposta razoáveis aos usuários interativos. Cada sistema operacional possui sua política de escalonamento adequada ao seu propósito e às suas características. Sistemas de tempo compartilhado, por exemplo, possuem requisitos de escalonamento distintos dos sistemas de tempo real. 8.2 Critérios de escalonamento: - Utilização do processador: corresponde a uma taxa de utilização, que na maioria dos sistemas varia entre 30 e 90%. Uma utilização abaixo dos 30% indicaria um sistema ocioso, com carga de processamento baixa, enquanto uma taxa de utilização acima dos 90% pode indicar um sistema bastante carregado, próximo da sua capacidade máxima (em alguns casos tal situação pode levar a um crash – travamento do sistema). - Throughput: é o número de processos executados em um determinado intervalo de tempo. Quanto maior o throughput, maior o número de tarefas executadas em função do tempo. A maximização do throughput é desejada na maioria dos sistemas. - Tempo de Processador: é o tempo que um processo leva no estado de execução, durante seu processamento. As políticas de escalonamento não interferem neste parâmetro, sendo este tempo função apenas do código executável e da entrada/saída de dados. 102 Apostila Fundamentos de Sistemas Operacionais . - Tempo de Espera (pela CPU): é todo o tempo que o processo permanece na fila de pronto, aguardando a liberação da CPU para ser executado. A redução deste tempo de espera é desejada pela maioria das políticas de escalonamento. - Tempo de Turnaround: é o tempo total que o processo permaneceu no sistema, desde sua criação até o momento em que é encerrado. São contados os tempos de alocação de memória, espera na fila de pronto e interrupção (E/S). - Tempo de Resposta: é o tempo decorrido entre uma requisição ao sistema e o instante em que a resposta começa a ser exibida. Em sistemas interativos, como aplicações on-line ou acesso à Web, os tempos de resposta devem ser da ordem de apenas poucos segundos. 8.3 Escalonamentos Não-Preemptivos e Preemptivos: Escalonamentos do tipo não-preemptivos são aqueles onde o sistema operacional não pode interromper o processo em execução para retirá-lo da CPU. Assim sendo, se nenhum evento externo ocorresse durante a execução do processo, este permanecia na CPU até terminar ou então alguma instrução do próprio programa o desviasse para o estado de espera (operação de E/S). Já os escalonamentos preemptivos são caracterizados pela possibilidade de o sistema operacional interromper o processo em execução para retirá-lo da CPU e dar lugar a outro. Neste caso o processo retirado da CPU volta ao estado de pronto, onde permanece aguardando nova oportunidade de ocupar a CPU. Com o uso da preempção, é possível ao sistema priorizar a execução de processos, como no caso de aplicações em tempo real. Outro benefício é a possibilidade de implementar políticas de escalonamento que compartilhem o processador de uma maneira mais uniforme, balanceando o uso da CPU entre os processos. São escalonamentos não-preemptivos: - FIFO: o processo que chegar primeiro à fila de pronto é selecionado para execução, e permanece utilizando o processador até terminar sua execução ou ser interrompido por E/S. Neste caso, o próximo processo da fila de pronto é selecionado para execução. Todo processo que chega à fila de pronto entra no final desta fila, conservando a ordem de chegada na fila, até ser escalonado novamente. Apesar de simples, este escalonamento apresenta algumas deficiências, principalmente no que diz respeito à dificuldade de se prever o início da execução de um processo, já que a ordem de chegada á fila de pronto deve ser observada à risca. Outro problema é quanto aos tipos de processo, onde os CPU-bound levam vantagem no uso do processador em relação aos do tipo I/Obound, pois o sistema não trata este tipo de diferença. O escalonamento FIFO foi inicialmente implementado em sistemas monoprogramáveis, sendo ineficiente se aplicado em sistemas interativos de tempo compartilhado. 103 Apostila Fundamentos de Sistemas Operacionais . Abaixo, um exemplo de escalonamento utilizando o método FIFO: a ordem de chegada dos processos (A, B, C) na fila de pronto foi obedecida, e, não tendo sido interrompidos por E/S, os processos executaram inteiramente até terminar, de acordo com seus tempos necessários para execução. 0 10 Processo A 10 u.t. - 18 Processo B 8 u.t. 27 Processo C 9 u.t. SJF (Shortest Job First): este escalonamento seleciona o processo que tiver o menor tempo de processador ainda por executar. Desta forma, o processo que estiver na fila de pronto com menor necessidade de tempo de CPU para terminar o seu processamento será o escolhido para ocupar a CPU. Funciona com um parâmetro passado ao sistema via contexto de software, onde o tempo estimado para o processo é informado baseando-se em estatísticas de execuções anteriores. Como exemplo, vamos utilizar os mesmos processos executados no escalonamento FIFO acima, com seus respectivos tempos de execução em u.t. (unidades de tempo): processo A com 10 u.t., processo B com 8 u.t, e o processo C com 9 u.t. Como neste escalonamento o que importa é o tempo de execução, a nova ordem de escalonamento para utilização da CPU será B, C e A, como segue: 0 8 Processo B Processo C - 17 27 Processo A Cooperativo: este escalonamento busca aumentar o grau de concorrência no processador. Neste caso, um processo em execução pode voluntariamente liberar o processador retornando à fila de pronto, possibilitando que um novo processo seja escalonado, permitindo melhor distribuição do tempo do processador. A liberação da CPU é uma tarefa exclusiva do programa em execução, que de maneira cooperativa libera o processador para um outro processo. Neste mecanismo, o processo em execução verifica periodicamente uma fila de mensagens para saber se existem outros processos na fila de pronto. Porém, como a interrupção do processo não depende do sistema operacional, situações indesejáveis podem ocorrer, como por exemplo, se um programa em execução não verificar a fila de mensagens, os demais programas não terão chance de executar enquanto a CPU não for liberada. As primeiras versões do Windows chegaram a utilizar este tipo de escalonamento. 104 Apostila Fundamentos de Sistemas Operacionais . São escalonamentos preemptivos: Circular: é um tipo de escalonamento projetado especialmente para sistemas em tempo compartilhado. É muito semelhante ao FIFO (obedece a ordem de chegada á fila de PRONTO), mas quando um processo passa para o estado de execução há um limite de tempo para o uso contínuo do processador, chamado fatia de tempo (time-slice) ou quantum. Assim, toda vez que um processo é selecionado para execução uma nova fatia de tempo lhe é concedida. Caso esta fatia de tempo expire, o sistema operacional interrompe o processo, salva seu contexto e o direciona para a fila de PRONTO. Este mecanismo é conhecido como preempção por tempo. A principal vantagem deste escalonamento é não permitir que um processo monopolize a CPU. Outrossim, uma desvantagem é que os processos CPU-bound são beneficiados no uso do processador em relação aos processos I/O-bound, pois tendem a utilizar totalmente a fatia de tempo recebida. A figura a seguir mostra o escalonamento circular com 3 processos, onde a fatia de tempo é igual a 2 u.t. No exemplo não estão sendo levados em consideração tempos de troca de contexto entre os processos, nem o tempo perdido em operações de E/S. Os processos A, B e C, gastam 10 u.t, 6 u.t e 3 u.t., respectivamente. - A B C 2 - 4 6 8 10 11 17 u.t. Por Prioridades: funciona com base num valor associado a cada processo, denominado prioridade de execução. O processo com maior prioridade na fila de PRONTO é sempre o escolhido para ocupar o processador, sendo os processos com prioridades iguais escalonados pelo critério FIFO. Neste escalonamento o conceito da fatia de tempo não existe. Como conseqüência disto, um processo em execução não pode sofrer preempção por tempo. Neste escalonamento a perda do uso do processador somente ocorrerá no caso de uma mudança voluntária para o estado de espera (interrupção por E/S), ou quando um outro processo de prioridade maior passa (ou chega) para o estado de pronto. Neste caso o sistema operacional interrompe o processo em execução, salva seu contexto e o coloca na fila de pronto, dando lugar na CPU ao processo prioritário. Este mecanismo é chamado de preempção por prioridade. A figura a seguir mostra a execução dos processos A, B e C, com tempos de execução de 10, 4 e 3 u.t. respectivamente, e 105 Apostila Fundamentos de Sistemas Operacionais . valores de prioridades de 2, 1 e 3, também respectivamente. Na maioria dos sistemas, valores menores correspondem à MAIOR prioridade. Assim, a ordem de execução será invertida para B, A e C. A B C 4 14 17 u.t. A prioridade de execução faz parte do contexto de software do processo, e pode ser estática (quando não pode ser alterada durante a existência do processo) ou dinâmica (quando pode ser alterada durante a existência do processo). Este escalonamento é muito usado em sistemas de tempo real, com aplicações de controle de processos, controle de tráfego (sinais de trânsito, de trens/metrô, aéreo), robótica, entre outros. - Escalonamento Circular com Prioridades: implementa o conceito de fatia de tempo e de prioridade de execução associada a cada processo. Neste escalonamento, um processo permanece no estado de execução até que termine seu processamento, ou voluntariamente passe para o estado de espera (interrupção por E/S), ou sofra uma preempção por tempo ou prioridade. A principal vantagem deste escalonamento é permitir um melhor balanceamento no uso do processador, com a possibilidade de diferenciar o grau de importância dos processos através da prioridade (o Windows utiliza este escalonamento). - Por Múltiplas Filas: Este escalonamento implementa várias filas de pronto, cada uma com prioridade específica. Os processos são associados às filas de acordo com características próprias, como importância da aplicação, tipo de processamento ou área de memória necessária. Assim, não é o processo que detém a prioridade, mas sim a fila. O processo em execução sofre preempção caso um outro processo entre em uma fila de maior prioridade. O sistema operacional só pode escalonar processos de uma fila quando todas as outras filas de maior prioridade estejam vazias. Os processos sempre voltam para a mesma fila de onde saíram. - Por Múltiplas Filas com Realimentação: semelhante ao anterior, porém permitindo ao processo voltar para uma outra fila de maior ou menor prioridade, de acordo com seu comportamento durante o processamento. O sistema operacional identifica dinamicamente o comportamento de cada processo e o redireciona para a fila mais conveniente ao longo de seu processamento. É um algoritmo generalista, podendo ser implementado na maioria dos sistemas operacionais. 106 Apostila Fundamentos de Sistemas Operacionais . Capítulo 9 Gerência de Memória / Memória Virtual 9.1 Introdução: Historicamente, a memória principal sempre foi vista como um recurso escasso e caro. Uma das maiores preocupações dos projetistas foi desenvolver sistemas operacionais que não ocupassem muito espaço de memória e, ao mesmo tempo, otimizassem a utilização dos recursos computacionais. Mesmo atualmente, com a redução do custo e o aumento considerável da capacidade da memória principal, seu gerenciamento é dos fatores mais importantes no projeto e implementação dos sistemas operacionais. Enquanto nos sistemas monoprogramáveis a gerência de memória não é muito complexa, nos sistemas multiprogramáveis essa gerência se torna crítica, devido à necessidade de se maximizar o número de usuários e aplicações utilizando eficientemente o espaço da memória principal. 9.2 Funções: Geralmente, os programas são armazenados em memórias secundárias, de uso permanente e não voláteis, como discos ou fitas. Como o processador somente executa o que está na memória principal, o sistema operacional deve sempre transferir programas da memória secundária para a principal antes de serem executados.Como o tempo de acesso às memórias secundárias é muito superior ao tempo de acesso à memória principal, o sistema operacional deve buscar reduzir o número de operações de E/S (acessos à memória secundária) a fim de não comprometer o desempenho do sistema. A gerência de memória deve tentar manter na memória principal o maior número de processos residentes, permitindo maximizar o compartilhamento do processador e demais recursos computacionais. Mesmo não havendo espaço livre, o sistema deve permitir que novos processos sejam aceitos e executados. Outra preocupação na gerência de memória é permitir a execução de programas maiores do que a memória física disponível. Em um ambiente de multiprogramação o sistema operacional deve proteger as áreas de memória ocupadas por cada processo, além da área onde reside o próprio sistema. Caso um programa tente realizar algum acesso indevido à memória, o sistema deve, de alguma forma, impedir o acesso. 107 Apostila Fundamentos de Sistemas Operacionais 9.3 . Alocação Contígua Simples: Este tipo de alocação foi implementado nos primeiros sistemas operacionais, embora ainda nos dias de hoje esteja presente em alguns sistemas monoprogramáveis. Nesse modelo, a memória principal é dividida em duas partes, uma para o sistema operacional e a outra para o programa do usuário. Dessa forma, o programador deve desenvolver suas aplicações preocupado apenas em não ultrapassar o espaço de memória disponível. Neste esquema o usuário tem total controle sobre toda a memória, exceto naquela área onde reside o sistema operacional, cujo endereçamento é protegido por um registrador, impedindo acesso indevido pelo usuário. Sistema Operacional X registrador Área para programas Proteção na alocação contígua simples Apesar de simples implementação e código reduzido, a alocação contígua simples não permite a utilização eficiente dos recursos do sistema, pois apenas um usuário pode dispor destes recursos. Há inclusive um desperdício de espaço de memória, caso o programa não venha a ocupar toda a área reservada para ele. Sistema Operacional Programa do Usuário Sub-utilização Área livre da memória principal 108 Apostila Fundamentos de Sistemas Operacionais 9.4 . Segmentação de Programas: Na alocação contígua simples todos os programas estão limitados ao tamanho da memória principal disponível para o usuário. Uma solução encontrada para o problema é dividir o programa em módulos, de modo que seja possível a execução independente de cada módulo, utilizando a mesma área de memória. A esta técnica dá-se o nome de segmentação ou overlay. Consideremos um programa que tenha três módulos: um principal, um de cadastramento e outro de impressão, sendo os módulos de cadastramento e impressão independentes. A independência significa que quando um módulo estiver na memória para execução, o outro não necessariamente precisa estar presente.O módulo principal é comum aos outros dois, logo, precisa estar na memória durante todo o tempo da execução do programa. Na figura a seguir, a memória é insuficiente para armazenar todo o programa, que totaliza 9 KB. A técnica de overlay utiliza uma área de memória comum para armazenar o módulo principal do programa e uma outra área na mesma memória, chamada área de overlay, que será compartilhada entre os módulos de cadastramento e impressão. Assim, sempre que um dos módulos for referenciado no módulo principal, o sistema o carregará da memória secundária para a área de overlay, sobrepondo o módulo antigo na memória. Memória Principal Sistema 2 KB Operacional 3 KB 4 KB Cadastramento Módulo Principal 4 KB Área de Overlay Impressão 2 KB 1 KB Área livre 2 KB Técnica de Overlay A definição das áreas de overlay é função do programador, através de comandos específicos da linguagem de programação utilizada. A grande vantagem da utilização desta técnica consiste em se poder executar programas maiores do que a memória física disponível. 109 Apostila Fundamentos de Sistemas Operacionais 9.5 . Alocação Particionada Estática: Os sistemas operacionais evoluíram no sentido de proporcionar melhor aproveitamento dos recursos disponíveis. Nos sistemas monoprogramáveis o processador permanece grande parte do tempo ocioso e a memória principal é subutilizada. Os sistemas multiprogramáveis já são muito mais eficientes no uso do processador, necessitando que vários processos estejam na memória principal ao mesmo tempo e que novas formas de gerência de memória sejam implementadas. Nos primeiros sistemas multiprogramáveis a memória era dividida em blocos de tamanho fixo, chamados partições.O tamanho dessas partições, estabelecido em tempo de inicialização do sistema, era definido em função do tamanho dos programas que executariam no ambiente.Sempre que fosse necessária a alteração do tamanho de uma partição, o sistema deveria ser inicializado novamente com uma nova configuração. Sistema Operacional Partição 1 2 KB Partição 2 5 KB Partição 3 8 KB Inicialmente, os programas só podiam ser carregados e executados em apenas uma partição específica, mesmo que as outras estivessem disponíveis. Esta limitação se devia aos compiladores e linkeditores, que geravam apenas código absoluto. No código absoluto, todas as referências a endereços no programa são posições físicas na memória, ou seja, o programa só poderia ser carregado a partir do endereço de memória especificado no seu próprio código. A esse tipo de alocação de memória chamou-se alocação particionada estática absoluta. Coma evolução dos compiladores, linkeditores, montadores e loaders, o código gerado deixou de ser absoluto e passou a ser relocável . No código relocável, todas as referências a endereços no programa são relativas ao início do código e não a endereços fixos na memória. Desta forma, os programas puderam ser alocados em qualquer partição livre, independente do endereço inicial e da partição para a qual o código foi criado. A esse tipo de alocação deu-se o nome de alocação particionada estática relocável. Tanto nos sistemas de alocação absoluta como nos de alocação relocável, os programas, normalmente, não ocupam totalmente as partições onde são alocados, deixando algum espaço livre dentro das partições. Este tipo de problema, decorrente do esquema de alocação fixa de partições, é chamado fragmentação interna. 110 Apostila Fundamentos de Sistemas Operacionais . A figura a seguir mostra um gráfico representando o particionamento estático de uma memória e sua fragmentação interna. Memória Principal Sistema Operacional Programa C 1 KB Programa A 3 KB Fragmentação interna Programa E 5 KB Alocação particionada estática com fragmentação interna 9.6 Alocação Particionada Dinâmica: A alocação particionada estática deixou clara a necessidade de uma nova forma de gerência de memória principal, onde o problema da fragmentação interna fosse reduzido e, conseqüentemente, o grau de compartilhamento da memória aumentado. Na alocação particionada dinâmica, foi eliminado o conceito de partições de tamanho fixo. Nesse esquema, cada programa, ao ser carregado, utilizaria o espaço necessário à sua execução, tornando esse espaço a sua partição. Assim, como os programas utilizam apenas o espaço de que necessitam, no esquema de alocação particionada dinâmica o problema da fragmentação interna deixa de existir. Porém, com o término de alguns programas e o início de outros, passam a existir na memória blocos cada vez menores na memória, não permitindo o ingresso de novos programas. A este tipo de problema dá-se o nome de fragmentação externa. Para resolver o problema da fragmentação externa, os fabricantes tentaram duas soluções: - reunião de todos os blocos livres adjacentes, formando uma grande área livre, que se transforma em uma nova partição; - realocação de todas as partições ainda ocupadas para a parte inicial da memória, eliminando os blocos livres entre elas, formando uma grande área 111 Apostila Fundamentos de Sistemas Operacionais . livre no final da memória, que podia ser distribuída entre os processos ainda por executar. A este tipo de compactação de espaços livres foi dado o nome de alocação particionada dinâmica com realocação. Estas soluções ajudaram a diminuir os problemas da fragmentação (tanto interna como externa), mas, devido á complexidade dos algoritmos, nem todos os sistemas operacionais as utilizaram. 9.7 Estratégias de Alocação de Partição: Os sistemas operacionais implementam basicamente três estratégias para determinar em qual área livre um programa será alocado para execução. Essas estratégias tentam evitar ou diminuir o problema da fragmentação. A melhor estratégia a ser adotada por um sistema depende de uma série de fatores, sendo o mais importante o tamanho dos programas executados no ambiente. Independentemente do algoritmo utilizado, o sistema possui uma lista das áreas livres, com endereço e tamanho de cada área. São estratégias de alocação: - Best-fit: é escolhida a melhor partição, ou seja, aquela que deixa o menor espaço sem utilização. Uma grande desvantagem desta estratégia é que, como são alocados primeiramente as partições menores, deixando pequenos blocos, a fragmentação aparece mais rapidamente. - Worst-fit: aloca o programa na pior partição, ou seja, aquela que deixa o maior espaço livre. Esta técnica, apesar de aproveitar primeiro as partições maiores, acaba deixando espaços livres grandes o suficiente para que outros programas utilizem esses espaços, permitindo que um número maior de processos se utilizem da memória, diminuindo ou retardando a fragmentação. - First-fit: esta estratégia aloca o programa na primeira partição que o couber, independente do espaço livre que vai deixar. Das três estratégias, esta é a mais rápida, consumindo menos recursos do sistema. 112 Apostila Fundamentos de Sistemas Operacionais 9.8 . Swapping: É uma técnica aplicada à gerência de memória que visa dar maior taxa de utilização à memória principal, melhorando seu compartilhamento. Visa também resolver o problema da falta de memória principal num sistema. Toda vez que um programa precisa ser alocado para execução e não há espaço na memória principal, o sistema operacional escolhe entre os processos alocados que não tem previsão de utilizar a CPU nos próximos instantes (quase sempre entre aqueles que estão em interrupção de E/S ou no final da fila de pronto), e “descarrega” este processo da memória para uma área especial em disco, chamada arquivo de swap, onde o processo fica armazenado temporariamente. Durante o tempo em que o processo fica em swap, o outro que necessitava de memória entra em execução ocupando o espaço deixado pelo que saiu. Pouco antes de chegar a vez do processo armazenado em swap utilizar a CPU, o sistema escolhe um outro processo para descarregar para swap e devolve o anterior da área de swap para a memória principal, para que este possa ser executado novamente. E vai trabalhando assim até que os processos vão terminando. O problema dessa técnica é que pode provocar um número excessivo de acesso à memória secundária (disco), levando o sistema a uma queda de desempenho. 9.9 Memória Virtual: Anteriormente foram apresentadas diversas técnicas de gerenciamento de memória que evoluíram no sentido de maximizar o número de processos residentes na memória principal e reduzir o problema da fragmentação, porém os esquemas vistos se mostraram muitas vezes ineficientes. Além disso, o tamanho dos programas e de suas estruturas de dados estava limitado ao tamanho da memória disponível. Como vimos, a utilização da técnica de overlay para contornar este problema é de difícil implementação na prática e nem sempre uma solução garantida e eficiente. Memória virtual é uma técnica sofisticada e poderosa de gerência de memória onde as memórias principal e secundária são combinadas, dando ao usuário a impressão de que existe muito mais memória do que a capacidade real de memória principal. O conceito de memória virtual baseia-se em não vincular o endereçamento feito pelo programa aos endereços físicos da memória principal. Desta forma, o programa e suas estruturas de dados deixam de estar limitados ao tamanho da memória física disponível, pois podem possuir endereços vinculados à memória secundária, que funciona como uma extensão da memória principal. Outra vantagem desta técnica é permitir um número maior de processos compartilhando a memória principal, já que apenas partes de cada processo estarão residentes. Isto leva a uma utilização mais eficiente do processador, além de minimizar (ou quase eliminar) o problema da fragmentação. 113 Apostila Fundamentos de Sistemas Operacionais . A seguir, os conceitos que envolvem a gerência de memória virtual, incluindo a paginação: - espaço de endereçamento virtual: é o conjunto de endereços virtuais que - Espaço de endereçamento real: analogamente, é o conjunto de endereços - Mapeamento: como o espaço de endereçamento virtual não tem nenhuma - Tabela de endereçamento de páginas: estrutura mantida pelo sistema - Memória virtual por paginação: é a técnica de gerência de memória onde - Page fault: é a falha de página. Sempre que o processo referencia um um processo pode endereçar. reais que um processo pode endereçar. relação com o espaço de endereçamento real, um programa pode fazer referência a um endereço virtual que esteja fora dos limites da memória principal (real), ou seja, os programas e suas estruturas de dados não estão mais limitados ao tamanho da memória física disponível. Quando um programa é executado, apenas uma parte do seu código fica residente na memória principal, permanecendo o restante na memória virtual até o momento de ser referenciado. Este esquema de endereçamento virtual é ignorado pelo programador no desenvolvimento das aplicações. Cabe ao compilador e ao linkeditor gerar códigos executáveis em função do endereçamento virtual, e o sistema operacional se incumbe de administrar os detalhes durante a sua execução. O processador apenas executa instruções e referencia dados residentes no espaço de endereçamento real. Portanto, deve existir um mecanismo que transforme os endereços virtuais em endereços reais. Este mecanismo é o que chamamos de mapeamento, e consiste em permitir a tradução do endereço virtual em endereço real. Como conseqüência, um programa não mais precisa estar necessariamente em endereços contíguos na memória real para ser executado. para armazenar, entre outras informações, o mapeamento. É única e exclusiva para cada processo, relacionando os endereços virtuais do processo ás suas posições na memória real. o espaço de endereçamento virtual e o espaço de endereçamento real são divididos em blocos do mesmo tamanho chamados páginas. As páginas do espaço virtual são chamadas páginas virtuais, enquanto as páginas do espaço real são chamadas páginas reais ou frames. endereço virtual, o sistema verifica se a página correspondente já está carregada na memória real. Se não estiver, acontece o page fault. Neste caso, o sistema deve transferir a página virtual para um endereço na memória real. Esta transferência é chamada de paginação. O número de page faults gerados por um processo em um determinado intervalo de tempo é chamado de taxa de paginação do processo. Se esta taxa atingir valores elevados, pode haver um comprometimento do desempenho do sistema. Um page fault provoca 114 Apostila Fundamentos de Sistemas Operacionais . uma interrupção no processo, pois há a necessidade de acessar operações de E/S. Assim, sempre que acontece a paginação, uma interrupção de E/S fará com que o processo em execução seja interrompido e colocado em estado de espera até que sua intervenção de E/S seja realizada, quando então o processo voltará à fila de pronto e entrará em execução de acordo com o escalonamento normal. Enquanto o sistema trata a interrupção deste processo, um outro ocupará a CPU. - Working-set: é o conjunto de páginas de um processo, em memória real, em - Thrashing: é o efeito causado pelo excesso de page faults durante a - Tamanho - Políticas de busca de páginas: definem como as páginas serão carregadas da memória virtual para a memória real. A política por demanda estabelece um determinado instante. Este conceito surgiu com o objetivo de reduzir o problema do thrashing e está relacionado ao princípio da localidade. Existem dois tipos de localidade que são observados durante a execução da maioria dos programas. A localidade espacial é a tendência de que, após uma referência a um endereço de memória, sejam realizadas novas referências a endereços próximos ou adjacentes. A localidade espacial é a tendência de que, após a referência a uma posição de memória, esta mesma posição seja referenciada novamente num curto intervalo de tempo. A partir desse princípio de localidade, o processador tenderá a concentrar suas referências a um conjunto de páginas do processo durante um determinado período de tempo. Imagine um loop principal de um programa que ocupe três páginas. A tendência é que estas três páginas tenham um alto índice de referências durante a execução do programa. execução de um processo. Pode acontecer a nível de programa ou de sistema. A nível de programa, pode ser provocado por um programa mal escrito, com desvios incondicionais espalhados por seu código (desobedecendo portanto aos princípios da localidade), ou por um limite de working-set muito pequeno (que não comporte o loop principal do programa, por exemplo). A solução para estes casos é reescrever o programa ou aumentar o limite do workingset. No caso de thrashing de sistema, significa que há mais páginas sendo requeridas na memória real do que ela pode realmente suportar. A solução é aumentar o tamanho da memória física. da página: deve estar entre 512 bytes e 128KB, aproximadamente. Páginas menores promovem maior compartilhamento da memória, permitindo que mais programas possam ser executados. Páginas maiores diminuem o grau de compartilhamento da memória, com menos programas disputando o processador. Assim conclui-se que quanto menor o tamanho da página, MAIOR é o grau de compartilhamento da memória e da CPU. que uma página somente será carregada quando for referenciada. Este mecanismo é conveniente, pois leva para a memória real somente as páginas realmente necessárias à execução do programa, ficando as outras na memória 115 Apostila Fundamentos de Sistemas Operacionais . virtual. A outra política, chamada paginação antecipada, funciona carregando antecipadamente várias páginas da memória virtual para a principal, na tentativa de economizar tempo de E/S. Nem sempre o sistema acerta na antecipação, mas o índice de acertos é quase sempre maior que o de erros. - Políticas de alocação de páginas: determinam quantos frames cada processo pode manter na memória real. A política de alocação fixa determina um limite de working-set igual para todos os processos, e pode ser vista como uma política injusta, na medida em que processos maiores normalmente necessitam de um working-set maior. A outra política é a variável, que define um limite de working-set diferente e variável para cada processo, em função de seu tamanho, taxa de paginação ou até mesmo da taxa de ocupação da memória principal. - Políticas de substituição de páginas: definem onde serão trocadas as páginas, quando se fizer necessária uma substituição. Na política local, somente as páginas do processo que gerou o page fault são candidatas a serem substituídas.Já na política global, todas as páginas alocadas na memória principal são candidatas à substituição, independente do processo que gerou o page fault. Como uma página de qualquer processo pode ser escolhida, pode ser que este processo sofra um aumento temporário da taxa de paginação em função da diminuição das suas páginas alocadas em memória. 9.10 Algoritmos de substituição de páginas: O maior problema na gerência de memória virtual por paginação não é decidir quais páginas carregar para a memória real, mas sim quais páginas liberar. Quando há a necessidade de carregar uma página, o sistema deve selecionar entre as diversas páginas alocadas na memória qual delas deverá ser liberada pelo processo. Os algoritmos de substituição de páginas têm o objetivo de selecionar os frames que tenham as menores chances de serem referenciados num futuro próximo. Caso contrário, o frame poderia retornar diversas vezes para a memória real, provocando excesso de page faults. São algoritmos de substituição de páginas: - algoritmo ótimo: impossível de ser implementado, pois o processador não - Algoritmo aleatório: escolhe qualquer página, entre as alocadas na consegue prever com segurança qual frame não será mais referenciado durante a execução do programa. Tal fato deve-se à lógica do programa e aos dados que ele manipula, desconhecidos pelo processador. memória, para fazer a substituição. Em função de sua baixa eficiência, este algoritmo não muito utilizado, embora consuma poucos recursos do sistema. 116 Apostila Fundamentos de Sistemas Operacionais . - Algoritmo FIFO (first in, first out): escolhe a página que está há mais - Algoritmo LFU (least frequently used): elege a página menos - Algoritmo LRU (least recently used): elege a página menos recentemente - Algoritmo NRU (not recently used): elege a página menos recentemente tempo na memória principal para fazer a troca. É um algoritmo de simples implementação, mas corre o risco de retirar uma página que, embora tenha sido carregada há mais tempo, esteja sendo muito utilizada. Por essa razão não é muito usado. freqüentemente usada para efetuar a troca. Através de um contador, armazenado na tabela de endereçamento de páginas, mo sistema identifica quantas referências cada página teve e utiliza esta informação para escolher a página. usada para fazer a troca. O sistema mantém na tabela de endereçamento de páginas um campo onde são armazenadas a data e a hora da última referência de cada página, e com base nestas informações faz a seleção. usada para efetuar a troca. O sistema exclui da decisão a página mais recente e escolhe entre as outras, pelo método FIFO, qual página deve sair. 117 Apostila Fundamentos de Sistemas Operacionais . Capítulo 10 Gerência de Sistemas de Arquivos 10.1 Estrutura de Diretórios: É como o Sistema organiza logicamente os arquivos. Contém entradas associadas aos arquivos, com as informações de localização, nome, organização e outros atributos: Nível único: é a implementação mais simples de uma estrutura de diretórios, onde existe um único diretório contendo todos os arquivos do disco. É muito limitado, não permitindo a criação de arquivos com o mesmo nome. Diretório pessoal: Evolução do modelo anterior, permite a cada usuário ter ser “diretório” particular, sem a preocupação de conhecer os outros arquivos do disco. Neste modelo há um diretório “master” que indexa todos os diretórios particulares dos usuários, provendo o acesso a cada um. Múltiplos níveis (ÁRVORE): É o modelo utilizado hoje em dia em quase todos os Sistemas Operacionais. Nesta modalidade cada usuário pode criar vários níveis de diretórios (ou sub-diretórios), sendo que cada diretório pode conter arquivos e sub-diretórios. O número de níveis possíveis depende do Sistema Operacional. 10.2 Sistemas de alocação de arquivos: FAT: sistema criado no MS-DOS e depois utilizado no Windows. Usa listas encadeadas, tem um limite de área utilizável em partições de 2 GB, caracteriza-se por um baixo desempenho no acesso e armazenamento. FAT32: igual ao FAT no que diz respeito a organização e desempenho, mas pode trabalhar com partições de até 2TB. NTFS: NT File System, original da plataforma Windows NT/2000/XP. Opera com uma estrutura em árvore binária, oferecendo alto grau de segurança e desempenho: - nomes de arquivo com até 255 caracteres, podendo conter maiúsculas, minúsculas e espaços em branco; dispensa ferramentas de recuperação de erros; bom sistema de proteção de arquivos; criptografia; suporta discos de até 264 bytes. UNIX: Usa diretório hierárquico, com um raiz e outros diretórios subordinados. Neste Sistema Operacional todos os arquivos são considerados apenas como uma “seqüência” de bytes, sem significado para o Sistema. É responsabilidade da 118 Apostila Fundamentos de Sistemas Operacionais . aplicação controlar os métodos de acesso aos arquivos. O UNIX utiliza também alguns diretórios padronizados, de exclusividade do Sistema. 10.3 Gerência de espaço livre: São três as formas de se implementar estruturas de espaços livres. Uma delas é através de uma tabela denominada mapa de bits, onde cada entrada da tabela é associada a um bloco do disco representado por um bit, que estando com valor 0 indica que o espaço está livre, e com valor 1 representa um espaço ocupado. Gasta muita memória, pois para cada bloco do disco há uma entrada na tabela. A segunda forma é utilizando uma lista encadeada dos blocos livres do disco. Desse modo, cada bloco possui uma área reservada para armazenar o endereço do próximo bloco livre. Apresenta problemas de lentidão no acesso, devido às constantes buscas seqüenciais na lista. A terceira forma é a tabela de blocos livres. Nesta, leva em consideração que blocos contíguos de dados geralmente são alocados/liberados simultaneamente. Desta forma, pode-se enxergar o disco como um conjunto de segmentos de blocos livres. Assim, pode-se manter uma tabela com o endereço do primeiro bloco de cada segmento e o número de blocos contíguos que se seguem. - Alocação contígua: armazena o arquivo em blocos seqüencialmente dispostos no disco. O arquivo é localizado através do endereço do primeiro bloco de sua extensão em blocos. O principal problema neste tipo de alocação é a existência de espaço livre para novos arquivos, que deve ser contígua. Utiliza as estratégias best-fit, worst-fit e first-fit (já conhecidas) para definir onde o arquivo será alocado. Causa alto índice de fragmentação no disco. - Alocação encadeada: nesta modalidade o arquivo é organizado como um conjunto de blocos ligados logicamente no disco, independente de sua localização física, onde cada bloco possui um ponteiro para o bloco seguinte. A fragmentação não representa problemas na alocação encadeada, pois os blocos livres para alocação do arquivo não necessariamente precisam estar contíguos. O que acontece é a quebra do arquivo em vários pedaços, o que aumenta o tempo de acesso. Neste tipo de alocação só se permite acesso seqüencial aos blocos do arquivo, sendo esta uma das principais desvantagens da técnica. Outra desvantagem é a perda de espaço nos blocos com o armazenamento dos ponteiros. - Alocação indexada: esta técnica soluciona a limitação da alocação encadeada, no que diz respeito ao acesso, pois permite acesso direto aos blocos do arquivo. Isso é conseguido mantendo-se os ponteiros de todos os blocos do arquivo em uma única estrutura chamada bloco de índice. Este tipo de alocação, além de permitir acesso direto aos blocos, não utiliza informações de controle nos blocos de dados. 119 Apostila Fundamentos de Sistemas Operacionais . 10.4 Proteção de acesso: Considerando-se que os meios de armazenamento são compartilhados por vários usuários, é fundamental que mecanismos de proteção sejam implementados para garantir a integridade e proteção individual dos arquivos e diretórios: - Senha de acesso: mecanismo de simples implementação, mas apresenta duas desvantagens: não é possível determinar quais os tipos de operação podem ser efetuadas no arquivo, e, se este for compartilhado, todos os usuários que o utilizam devem conhecer a senha de acesso. - Grupos de usuário: é muito utilizada em muitos Sistemas Operacionais. Consiste em associar cada usuário a um grupo. Os grupos são organizados logicamente com o objetivo de compartilhar arquivos e diretórios no disco. Este mecanismo implementa três níveis de proteção: OWNER (dono), GROUP (grupo) e ALL (todos). Na criação do arquivo o usuário especifica se o arquivo pode ser acessado somente pelo seu criador, pelo grupo ou por todos os usuários, além de definir que tipos de acesso podem ser realizados (leitura, escrita, execução e eliminação) - Lista de controle de acesso: é uma lista associada ao arquivo onde são especificados quais os usuários e os tipos de acesso permitidos. O tamanho dessa estrutura pode ser bastante extenso se considerarmos que um arquivo pode ser compartilhado por vários usuários. Além deste problema, há o inconveniente de se fazer acesso seqüencial à lista toda vez que um acesso é solicitado. Em determinados sistemas de arquivos pode-se utilizar uma combinação de proteção por grupos de usuários ou por listas de acesso, oferecendo assim maior flexibilidade ao mecanismo de proteção de arquivos e diretórios. 120 Apostila Fundamentos de Sistemas Operacionais . BIBLIOGRAFIA Arquitetura de Sistemas Operacionais Francis B. Machado Ed. LTC Sistemas Operacionais: Conceitos e Aplicações Abraham Silberschatz Ed. Campus Sistemas Operacionais Modernos A.S. Tanenbaum Operating Systems: Design And Implementation Andrew S. Tanenbaum - Editora Prentice Hall 121