Preview only show first 10 pages with watermark. For full document please download

Unix - Sistema Operacional

Unix - SO

   EMBED

  • Rating

  • Date

    December 2018
  • Size

    406KB
  • Views

    8,080
  • Categories


Share

Transcript

Sumário 1. Introdução a História do Sistema Operacional UNIX 4 2. UNIX Uma Visão Geral 8 2.1 Modularidade 8 2.2 Multitarefa (Multitasking) 8 2.3 Multiusuário (Multiuser) 9 2.4 Portabilidade 10 2.5 Conectividade e Comunicações 10 3. Estrutura do UNIX 12 4. Shell (Interpretador de Comandos) 14 5. Noções de Linha de Comando no UNIX 16 6. Arquivos de Inicialização 18 7. Gerenciamento de Arquivos 22 7.1 Atributos e Nomes de Arquivos 22 8. Gerenciamento de Processos e Escalonamento no UNIX 22 8.1 Blocos de Controle de Processos (BCP) 22 8.2 Escalonamento de CPU 24 9. Memória e Gerenciamento 25 10. Deadlocks 27 10.1 Condições para Ocorrências de Deadlocks 27 10.2 Algoritmo do Avestruz 28 10.3 Algoritmo do Banqueiro 28 10.4 Tratamento de Deadlocks 29 10.5 Detectar e Recuperar 29 11. Threads no UNIX 30 12. Kernel 32 13. Dispositivos de E/S 34 14. Comunicação Serial e Paralela 35 15. Configuração do TCP/IP 36 16. Serviços TCP/IP 17. Procedimentos de boot e o Init 18. Referências Bibliográficas 1. Introdução a História do Sistema Operacional UNIX Segundo Fernando Krahe[1], as raízes do UNIX datam dos meados dos anos 60, quando a AT&T, Honeywell, GE e o MIT embarcaram em um massivo projeto para desenvolvimento de um utilitário de informação, chamado Multics (Multiplexed Information and Computing Service). O sistema deveria ser multiusuário, com um sistema de arquivos de alta confiança, suporte a vários tipos de aplicações, e a vários ambientes de programação e interfaces de usuário. Multics era um sistema modular montado em uma bancada de processadores, memórias e equipamentos de comunicação de alta velocidade. Pelo desenho, partes do computador poderiam ser desligadas para manutenção sem que outras partes ou usuários fossem afetados. O objetivo era prover serviço 24 horas por dia 365 dias por ano - um computador que poderia ser tornado mais rápido adicionando mais partes. Em 1969, o projeto estava muito atrasado em relação ao seu cronograma e a AT&T resolveu abandona-lo. O projeto continuou no MIT. Neste mesmo ano, Ken Thompson e Dennis Ritchie decidiram desenvolver algumas idéias do Multics, eles reescreveram todo o sistema operacional para um computador bem menos potente, um DEC PDP-7, de 4 kbytes de memória. Thompson terminou o trabalho no verão de 1969, foi utilizada a linguagem BCPL (conhecida popularmente como Linguagem B "assembly"), que contava com funções básicas: editor de texto, montador (ou assembler, que transforma linguagem assembly em linguagem de máquina) e interpretador de comandos (um Shell) neste período o sistema era chamado de Unics (UNiplexed Information and Computing Service), numa alusão ao Multics, e foi logo rebatizado como Unix. A primeira versão do sistema "BELL LABs PDP-11" foi lançada em 1º Janeiro de 1970, data considerada como a de nascimento do sistema Unix, o que explica porque todos os relógios dos sistemas de exploração Unix começam a partir desta data. O projeto cresceu e surgiu a necessidade de se usar um sistema operacional compatível com diferentes plataformas de hardware, o que levou ao desenvolvimento de uma nova linguagem de programação, que pudesse ser portada com facilidade, sem ou com pouquíssimas modificações no source(código-fonte). Em 1972, Dennis Ritchie e Brian W.Kernighan, participaram ativamente do desenvolvimento da Linguagem C, (motivo pelo qual, é considerado como um dos seus inventores), a linguagem C superava as limitações da linguagem B, assim, o conjunto do sistema UNIX foi inteiramente reescrito em linguagem C em 1973 e batizado Unix Time-Sharing System (TSS). Em 1973 o UNIX foi reescrito em C, talvez o fato mais importante da história deste sistema operacional. Isto significava que o UNIX poderia ser portado para novo hardware em meses, e que mudanças eram fáceis. A linguagem C foi projetada para o sistema operacional UNIX, e portanto há uma grande sinergia entre C e UNIX. Como a lei impedia a empresa AT&T, e a Bell Labs, de comercializar outra coisa que não equipamentos telefônicos ou telegráficos, tomo-se a decisão de distribuir os fontes de UNIX para as universidades para fins educativos em 1973. Em 1975 foi lançada a V6, que foi a primeira versão de UNIX amplamente disponível fora dos domínios do Bell Labs, especialmente em universidades. Este foi o início da diversidade e popularidade do UNIX. Nesta época na Universidade de Berkley os alunos começaram a fazer modificações ao sistema. Começaram a surgir novas versões, além da original da AT&T (rebatizada como System V); a primeira foi desenvolvida na Universidade de Berkeley, denominado BSD (Berkeley Software Distribution), liberada publicamente no final de 1977, precursor dos atuais e bem-sucedidos BSD's, (alguns anos depois com base no sistemas BSD surgiram novas "versões", como o freeBSD, netBSD e openBSD, que tinham o kernel baseado no sistema BSD). Assim dois ramos de desenvolvimento das fontes se formaram: AT&T que ia tornar-se System V de UNIX System Labs (USL); BSD (Berkeley Software Developpement) desenvolvido pela Universidade de Califórnia. Ainda em 1977 a AT&T deixou os fontes do UNIX à disposição das outras empresas, de modo que um grande número de UNIX-like foram desenvolvidos: AIX, Unix comercial baseado no System V desenvolvido em fevereiro de 1990 pela IBM; Sun Solaris, Unix comerciais baseado no System V e BSD desenvolvido pela Sun Microsystems ; HP-UX, Unix comercial baseado em BSD desenvolvido a partir de 1986 pela Hewlett Packard ; Ultrix, Unix comerciais desenvolvido pela Digital Equipment Corporation; IRIX, Unix comercial desenvolvido pela Silicon Graphics; Unixware, Unix comerciais desenvolvidos pela Novell; Unix SCO, Unix comercial baseado no System V desenvolvido a partir de 1979 pela Santa Cruz Operações e Hewlett Packard; Tru64 UNIX, Unix comercial desenvolvido pela Compaq. Em 1978 Berkley Software Distribuition lança a série 2.xBSD para PDP - 11 (a versão 2.11 foi lançada em 1992). Nesta versão saiu o csh. Neste ano também saiu a série 3BSD, que teve uma importante contribuição, virtual memory. Em 1979 saiu a V7 e o Unix foi portado para o novo VAX da Digital. Esta versão incluia C K&R completo, uucp, Bourne Shell. O kernel tinha meramente 40 bytes! Esta foi a primeira versão vendida comercialmente do sistema, mas usada principalmente por universidades. Em sua versão 7 de 1979, a evolução foi acompanhada de numerosas modificações notáveis como: a supressão do bridage ligado à dimensão dos arquivos, uma melhor mobilidade do sistema (funcionamento sobre numerosas plataformas materiais), a adição de numerosos utilitários. Em 1983 é lançado o System V da AT&T e o 4.2 BSD. O SV incluía o pacote IPC (shm, msg, sem) para comunicação entre processos. Surgiram outras versões do SV com a inclusão de novas características como sharedlibs no SVR4. O 4.2BSD foi talvez uma das mais importantes versões do UNIX. O seu software de conexão de redes tornava muito fácil a tarefa de conectar computadores UNIX a redes locais. Nessa versão é que foram integrados os softwares que implementam TCP/IP e sockets. A AT&T parou de disponibilizá-lo livremente, passou a ser disponibilizado por um preço muito alto, é depois de algum tempo uma serie de universidades, empresas e grupos de programadores, começaram a desenvolver uma serie de aplicativos para o Unix (desde jogos até aplicações comerciais). Tantas variedades de Unix surgindo a todo momento, com as mesmas características, mas com tendências a se divergirem fez surgir então em 1985 o padrão POSIX (Portable Operating System Interface for UniX), um conjunto de padrões definidos pelo IEEE(Institute of Electrical and Electronics Engineers). POSIX assim é conhecido igualmente sob o nome IEEE P1003. O POSIX não permitiu compatibilidade de rodar programas binários entre os vários Unix, mas sim facilidade de portar um programa de um Unix para outros, através de compilação de códigos-fonte em C. Em 1988 foi lançado o SVR4. Este sistema era um merge de releazes anteriores do SV, BSD e SunOs, uma implementação descendente de BSD. O 4.4BSD foi lançado em 1992 para várias plataformas: HP 9000/300, Sparc, 386, DEC e outras, mas não em VAX. Entre as novas características estão: Novo sistema de memória virtual baseado em Mach 2.5 Suporte ISO/OSI (baseado em ISODE) A Sun Microsystem também lançou a sua versão do UNIX a partir do BSD. Isto ocorreu até a versão SunOs 4.x. A nova versão, SunOs 5.x está baseada no SVR4, embora tenha herdado algumas características do SunOs 4.x. O novo sistema operacional da Sun, Solaris 2.x, é um SO que engloba SunOs 5.x, Open Network Computing e Open Windows. É o solaris que provê o pacote de compatibilidade entre os BSD/SunOs e o SVR4/SunOs 5.x. A Microsoft também lançou uma versão do UNIX, chamada XENIX, que rodava em PCs. Este sistema era inicialmente baseado na Versão 7, depois herdou características dos SIII e depois do SV. No início da década de 1990, a AT&T vendeu o código UNIX para a Novell. Em 1995, a Novell vendeu parcialmente alguns dos direitos do código UNIX à Santa Cruz Operation. Em 2000, a Santa Cruz Operation vendeu o código UNIX para a Caldera Systems, que mudou seu nome para SCO. Atualmente, Unix (ou *nix) é o nome dado a uma grande família de Sistemas Operacionais que partilham muitos dos conceitos dos Sistemas Unix originais, todos desenvolvidos sob a plataforma de padrões como o POSIX e outros. Alguns dos Sistemas derivados do Unix são: BSD (FreeBSD, OpenBSD e NetBSD), Solaris (anteriormente conhecido por SunOS), IRIX, AIX, HP-UX, Tru64, Linux (nas suas centenas de distribuições), e até o Mac OS X (baseado em um kernel Mach BSD chamado Darwin). Existem mais de quarenta sistemas operacionais *nix, rodando desde celulares a supercomputadores, de relógios de pulso a sistemas de grande porte. 2. O Que é o UNIX? O Unix é um sistema operacional multi-usuário e multi-tarefas, o que permite a um computador executar simultaneamente vários programas para um ou vários usuários. Multitarefa significa executar varias tarefas ou processos simultaneamente, porem, em um hardware monoprocessado, o sistema multi-thread não funciona, mas os processos são executados seqüencialmente de forma tão rápida que parecem estar sendo executados simultaneamente, O Unix escalona sua execução e reserva-lhes recursos computacionais (intervalo de tempo de processamento, espaço em memória RAM, espaço no disco rígido, etc.). Hoje em dia, os sistemas Unix estão presentes nos meios profissionais e universitários graças à sua grande estabilidade, ao seu nível de segurança elevado e ao respeito dos grandes padrões, nomeadamente em matéria de rede. O Unix apresenta também como característica o seu sistema de arquivos. Tudo neste sistema operacional é considerado um arquivo, até mesmo os dispositivos de I/O são tratados como arquivo pelo sistema. Além do conceito de arquivos, outros conceitos presentes no sistema são: processos, shell, diretórios e path. 2.1 Modularidade O UNIX é único em seu desenho modular, que permite usuários adicionar ou remover partes para adaptá-lo às suas necessidades específicas. Os módulos se encaixam com conexões-padrão. É possível tirar um módulo e substituí-lo por um outro ou expandir o sistema acrescentando vários módulos. De uma certa maneira, o sistema UNIX de cada pessoa é único. Muitos usários acrescentam ou eliminam módulos sempre que precisam, adaptando suas implementações às suas necessidades. Geralmente, é possível remover um módulo, sem prejudicar a operação do resto do sistema. Esta característica é muito útil nas implementações em microcomputadores, onde as unidades de disco têm capacidade limitada. A remoção de arquivos desnecessários abre espaço para mais arquivos de dados. 2.2 Multitarefa (Multitasking) A capacidade de multitasking do UNIX permite que mais de uma tarefa seja realizada simultaneamente. Esta é uma das principais características do UNIX. As tarefas que estão em execução concorrem pelos recursos da máquina que são controlados pelo sistema. É possível, portanto, que um programa que atualize um banco de dados seja rodando ao mesmo tempo que a impressão de um relatório esteja sendo realizada e uma tela de terminal esteja sendo enviada. O Unix é um sistema operacional de multitarefa preemptiva, ou seja, quando termina o intervalo de tempo (chamado quantum), a execução do processo e suspensa, salva o seu contexto (informações necessárias para a execução do processo), para que ele possa ser retomado posteriormente, e coloca em execução o próximo processo da fila de espera, também determina quando cada processo será executado, a duração de sua execução e a sua prioridade sobre os outros. A multitarefa, permite que o usuário e o computador fiquem livres para realizarem outras tarefas economizando tempo. Em uma interface gráfica com um ambiente de janelas pode-se disparar cada tarefa em uma janela shell. Já em um ambiente não gráfico, as tarefas que podem ser executadas sem a intervenção do usuário são colocadas em background. Em foreground deixa-se as tarefas que necessitam da intervenção do usuário, como a edição de um arquivo. 2.3 Multiusuário (Multiuser) O Unix permite que vários usuários utilizem um mesmo computador simultaneamente, geralmente por meio de terminais, semelhante ao Mainframe. Cada terminal é composto de um monitor, um teclado e, eventualmente, um mouse. Vários terminais podem ser conectados ao mesmo computador num sistema Unix. Há alguns anos eram usadas conexões seriais, mas atualmente é mais comum o uso de redes locais, principalmente para o uso de terminais gráficos. O Unix gerencia os pedidos que os usuários fazem, evitando a interferência de uns nos outros. Cada usuário possui direitos de propriedade e permissões exclusivas sobre arquivos. Quaisquer arquivos modificados pelo usuário conservarão esses direitos. Programas executados por um usuário comum estarão limitados em termos de quais arquivos poderão acessar. O sistema Unix possui dois tipos de usuários: o usuário root (também conhecido como super-usuário), semelhante ao administrador do Windows, que possui a missão de administrar o sistema, podendo manipular todos os recursos do sistema operacional; e os usuários comuns, que possuem direitos limitados. Para que o sistema opere adequadamente em modo multiusuário, existem alguns mecanismos: Sistema de autenticação de usuário (o programa login, por exemplo, autentica o usuário verificando uma base de dados, normalmente armazenada no arquivo /etc/password); Sistema de permissões e propriedades sobre arquivos (os direitos anteriormente citados); Proteção de memória, impede que um processo de usuário acesse dados ou interfira com outro processo. Esse mecanismo é feito com a ajuda do hardware, que divide o ambiente de processamento e memória em modo supervisor (ou modo Unix 4 núcleo) e modo usuário. 2.4 Portabilidade A portabilidade é a possibilidade dos softwares que operam em uma máquina operarem em uma outra diferente. Há dois tipos de portabilidade a serem considerados: Portabilidade do sistema operacional Portabilidade dos aplicativos Mais de 90% do programa Kernel está escrito em C e menos de 10% em linguagem de máquina. Assim, na pior das hipóteses, apenas 10% do programa Kernel terá de ser reescrito ao se deslocar o Kernel para uma máquina com arquitetura diferente. Os programas aplicativos escritos em linguagem de nível mais alto, como C, são facilmente portáveis para vários sistemas UNIX. Basta que sejam recompilados, exigindo, as vezes, poucas alterações. O Unix Possui vários interpretadores de comandos (Shell) bem como um grande número de comandos e numerosos utilitários (compiladores para numerosas linguagens, tratamentos de texto, serviço de mensagens eletrônicas,etc). Também possui uma grande mobilidade, o que permite sua instalação em quase todas as plataformas materiais. 2.5 Conectividade e comunicações No UNIX temos dois tipos de comunicações, quais sejam, comunicação entre programas e comunicação entre usuários. A comunicação entre programas é realizada através de mensagens, semáforos ou memória compartilhada. Estes mecanismos, também conhecidos por IPC, interprocess comunications, são extensões do System V. A comunicação entre usuários pode se realizar de diversas maneiras, entre elas há o Mail, que é um programa para enviar e receber mensagens eletrônicas, o Write, que escreve uma mensagem no terminal de outra pessoa logada no sistema, o Wall, que escreve mensagens nos terminais de todas as pessoas logadas no sistema, o Talk, que permite uma conversa em tempo real. Há também a comunicação entre os sistemas, tais como UUCP (UNIX to UNIX Copy Protocol). 3. Estrutura do UNIX Um sistema Unix é composto basicamente de duas partes: núcleo - o núcleo do sistema operacional, a parte que relaciona-se diretamente com o hardware, e que executa num espaço de memória privilegiado, agenda processos, gerencia a memória, controla o acesso a arquivos e a dispositivos de hardware (estes, por meio dos controladores de disposito - drivers - e interrupções). O acesso ao núcleo é feito por chamadas de sistema, que são funções fornecidas pelo núcleo; essas funções são disponibilizadas para as aplicações por bibliotecas de sistema C (libc). programas de sistema - são aplicações, que executam em espaços de memória não privilegiados, e que fazem a interface entre o usuário e o núcleo. Consistem, principalmente, de: Conjunto de bibliotecas C (libc) Shell - um ambiente que permite que o usuário digite comandos. Programas utilitários diversos - são programas usados para manipular arquivos, controlar processos, etc. Ambiente gráfico (GUI) Graphics User Interface - eventualmente utiliza-se também um ambiente gráfico para facilitar a interação do usuário com o sistema. Em um sistema Unix, o espaço de memória utilizado pelo núcleo é denominado espaço do núcleo ou supervisor (em inglês: Kernel Space); a área de memória para os outros programas é denominada espaço do usuário (User Space). Essa separação é um mecanismo de proteção que impede que programas comuns interfiram com o sistema operacional. Para um melhor entendimento, o sistema operacional UNIX pode ser representado pelas quatro partes básicas, como ilustrado na figura[2] abaixo: Kernel: é o núcleo do sistema operacional, controla o hardware traduzindo comandos UNIX em instruções de hardware. O usuário não trabalha diretamente com o Kernel. Sistema de arquivos: é o modo do UNIX armazenar informações de qualquer tipo, como por exemplo, gráficos, textos, etc. Shell: é um programa que atua como interface entre o Kernel e o usuário. Aplicativos: são programas que podem ser invocados pelo Shell para realizar diversas tarefas 4. Shell O SHELL é um interpretador de comandos interativo e constitui a principal interface entre o usuário e o sistema UNIX. Na SUN há dois tipos, que podem ser chamados pelo usuário: o "sh", "csh", "tcsh", "bash", e outros. É a partir do SHELL que os processos podem ser chamados. SHELL possui variáveis que armazenam informações sobre o sistema. Elas devem ser inicializadas logo no início da sessão. Algumas variáveis são: PATH: guarda o nome dos diretórios permitidos na busca de comandos; HOME: nome do diretório de entrada do usuário; O intérpretador de comandos (Shell) é a interface entre o usuário e o sistema de exploração, daí o seu nome inglês "Shell", que significa "casca". Assim, encarregado de ser o intermediário entre o sistema de exploração e o usuário graças às linhas de comandos escritas por este, a função do Shell consiste em ler a linha de comando, interpretar o seu significado, executar o comando e seguidamente dar o resultado nas saídas. O Shell é assim um arquivo executável, encarregado de interpretar os comandos, transmiti-los ao sistema e devolver o resultado. Existem vários shells, sendo o mais corrente o sh (chamado "Bourne shell"), bash ("Bourne again shell"), csh ("C Shell"), Tcsh ("Tenex C shell"), ksh ("Korn shell") e zsh ("Zero shell"). O seu nome corresponde geralmente ao nome do executável. Cada utilizador possui um shell por padrão, que será lançado quando da abertura de uma janela de comando. Por padrão, o Shell está no ficheiro de configuração /etc/passwd no último campo da linha que corresponde ao utilizador. É possível alterar o shell numa sessão, executando muito simplesmente o ficheiro executável correspondente, por exemplo: /bin/bash Janela de comando (rápida) O Shell inicia lendo a sua configuração global (num ficheiro do directório /etc/), seguidamente lendo a configuração própria ao utilizador (num ficheiro escondido, cujo nome começa por um ponto, situado no directório básico do utilizador, ou seja /home/nom_de_l_utilisateur/.fichier_de_configuration), por último mostra a jenale de comando (em inglês prompt) do seguinte modo: machine:/repertoire/courant$ Por padrão, na maior parte dos shells, o prompt é composto pelo nome da máquina, seguido de dois pontos (:), do directório corrente, seguidamente de um carácter que indica o tipo de utilizador conectado : "$" indica que se trata de um utilizador normal; "#" indica que se trata do administrador, chamado "root". 5. Noção de linha de comando no UNIX Uma linha de comando é uma cadeia de caracteres constituída por uma comando, correspondente a um arquivo executável do sistema ou um comando Shell, bem como os argumentos (parâmetros) opcionais: ls -al /home/jf/ No comando acima, ls é o nome do comando, - Al e /home/jf/ são argumentos. Os argumentos que começam por "-" designam-se opções. Para cada comando existem geralmente diversas opções que podem ser detalhadas escrevendo um dos comandos seguintes: commande --help commande -? man commande Entrada/saída standard, quando da execução de um comando, um dossiê é criado. Este vai então abrir três fluxos: stdin, chamado entrada standard, no qual o processo vai ler os dados de entrada. Por defeito, stdin corresponde ao teclado; STDIN é identificado pelo número 0; stdout, chamado saída standard, no qual o processo vai escrever os dados de saída. Por defeito, stdout corresponde ao monitor; STDOUT é identificado pelo número 1; stderr, chamado erro standard, no qual o processo vai escrever as mensagens de erro. Por defeito stderr corresponde ao monitor. STDERR é identificado pelo número 2; 6. Arquivos de inicialização No UNIX temos alguns arquivos que são executados todas as vezes que abrimos uma Shell, ou quando inicializamos uma nova sessão. Estes arquivos são chamados de arquivos de inicialização. Através deles, o usuário pode personalizar alguns comandos que mais utiliza, colocando alguns 'apelidos' referentes a estes comandos, e/ou alterar o ambiente de trabalho da sua própria conta. Alguns exemplos de arquivos de inicialização são: C Shell: .login e .cshrc Bash: .bash_profile e .bashrc Os arquivos, .login e .bash_profile são executados pelo Shell somente ao iniciar a sessão. Estes arquivos devem conter comandos que precisam ser executados somente uma vez durante o login. Os arquivos .cshrc e .bashrc são executados toda vez que uma nova cópia do Shell for chamada. Para exemplificar abaixo temos um arquivo .cshrc que contém 'apelidos' para alguns dos comandos do UNIX. O usuário 'apelidou' o comando history de h. Assim, ao invés dele digitar history, basta digitar h. Como esta alteração foi feita no arquivo .cshrc, este 'apelido' será válido todas as vezes que ele abrir uma nova cópia do Shell. # /etc/cshrc # csh configuration for all shell invocations. limit coredumpsize 0k alias h history alias l ls -lF alias psa 'ps xua " more' set history = 100 save history = 100 Após modificar os arquivos de inicialização, deve-se usar o comando source, para que as modificações comecem a funcionar. Neste exemplo, temos: % source .cshrc Devido a importância dos arquivos de inicialização, é aconselhável que antes de efetuar qualquer modificação nos mesmos, faça uma cópia de segurança, para facilitar a reparação de algum erro. 7. Gerenciamento de arquivos O UNIX armazena toda informação em arquivos no disco rígido. Os arquivos no UNIX possuem diferentes tipos: arquivo texto, diretório, executável, etc. Estes arquivos têm nome, nome do proprietário, respectivo grupo a que pertence, permissões de manipulação, tamanho e data da última modificação. Através do comando 'ls -laF' obtemos as informações sobre os arquivos da pasta atual. Para facilitar o gerenciamento de arquivos, o UNIX utiliza um sistema de diretórios com nomes particulares, cada qual pode conter arquivos e subdiretórios próprios. Os diretórios do UNIX apresentam uma hierarquia representada na forma de uma árvore invertida. O diretório base do UNIX é chamado de root, sendo representado pelo símbolo '/'. Abaixo do diretório root, o UNIX cria diversos subdiretórios com objetivos específicos, a seguir são listados os diretórios mais importantes: "/ "diretório raíz, root. " "/etc "contém arquivos de configuração do sistema. " "/Bin "contém os principais executáveis do sistema (binários). " "/usr "contém os programas instalados, aplicativos, documentação do " " "sistema e dos programas. " "/dev "contém informações sobre todos os dispositivos (devices) do " " "sistema. " "/home "contém os diretórios de usuários. " "/lib "contém as bibliotecas do sistema. " "/tmp "contém todos os arquivos temporários. " "/var "contém os arquivos de informação variável, que são alterados com " " "freqüência. " "/sbin "arquivos de sistema essenciais. " "/boot "contém arquivos de boot ou inicialização. " "/lost+fou"arquivos recuperados. " "nd " " "/mnt "diretório de acesso aos drives, ponto de montagem de partição " " "temporária. " 7.1 Atributos e Nomes de Arquivos Conforme texto do IME-USP3, no UNIX cada arquivo (inclusive diretórios, que são casos particulares de arquivos), conta com um conjunto de atributos de leitura, escrita e execução, que são setáveis através do comando chmod, e podem ser exibidos pelo comando: s-I: $ Is –I /Bin/cat -rwxr-xr-x 1root root 16584 Dec 16 20:09 /bin/cat A string –rwxr-xr-x representa os atributos do arquivo/Bin/cat. O primeiro caracter (-) significa que se trata de um arquivo regular, em contraposição aos diretórios (d), device special files (c) e links simbólicos (I). Os nove caracteres restantes informam as permissões de leitura, escrita e execução desse arquivo relativas ao seu proprietário, ao seu grupo, e a todos os demais. O proprietário e o grupo são informados logo à direita, e no caso são o usuário root e o grupo root. 3 Unix: Conceitos e Comandos Básicos Note que no UNIX não existem os atributos de arquivos ocultos("hidden") e do sistema ("system"), suportados no MS-DOS. Não obstante, arquivos cujo primeiro caractere é ponto (".") normalmente são omitidos pelo Is, a não ser que se utilize a opção –a (de "all"), conforme comentamos no início. Um atributo importantíssimo existente no UNIX é o chamado setuid, através do qual um processo adquire ao ser executado os privilégios do owner do arquivo. Isso é frequentemente utilizado por programas que são disparados por usuários não privilegiados mas que por algum motivo necessitam dos privilégios do super-usuário, por exemplo: $ Is –I /usr/sbin/pppd -rwsr-xr-x 1root Bin 104876 Apr 27 1998 /usr/sbin/pppd Sem os privilégios de super-usuário, um usuário comum não conseguiria configurar a interface ppp e nem alterar a tabela de rotas do sistema, no momento em que se conecta ao provedor internet. Assim, o pppd, ao ser executado, requisitará os privilégios do owner do arquivo, que no caso é o superusuário, e dessa forma ele poderá realizar essas operações. No UNIX via de regra são suportados nomes "longos" (até 64 ou às vezes até 256 caracteres), e o "." Não é um separador entre o nome e a extensão do arquivo, mas um caractere como os outros. Não obstante, a noção de "sufixo" é utilizada informalmente para facilitar a identificação de alguns formatos de arquivos, por exemplo: .tar Archive produzido com tar .zip Archive comprimido produzido com zip .Z Archive comprimido com compress .gz Archive comprimido com gzip Archives (o termo não tem correspondente em português) são concatenações de vários arquivos ou de subárvores inteiras num único arquivo. Em UNIX, tipicamente são produzidos com tar: $ tar cvf /tmp/etc.tar / etc $ tar tvf /tmp/etc.tar / etc $ cd /tmp; tar xvf etc.tar O primeiro comando irá criar o archive / tmp/etc.tar, composto por toda a subárvore/etc. O segundo exibirá o conteúdo desse archive. O terceiro irá extrair todo o seu conteúdo dentro do diretório /tmp, ou seja, recriará aquela mesma subárvore como uma subárvore do diretório /tmp. 8. Gerenciamento de Processos e Escalonamento no Unix Na visão mais simples, processo é uma instância de um programa em execução. Um programa, para ser executado, necessita ser carregado na memória; a área de memória utilizada é dividida em três partes: código (text), dados inicializados (data) e pilha (stack). Por ser multitarefa, o Unix utiliza uma estrutura chamada tabela de processos, que contém informações sobre cada processo, tais como: identificação do processo (PID), dono, área de memória utilizada, estado (status). Apenas um processo pode ocupar o processador em cada instante - o processo encontra-se no estado "executando" (running). Os outros processos podem estar "prontos" (ready), aguardando na fila de processos, ou então estão "dormindo" (asleep), esperando alguma condição que permita sua execução. Um processo em execução pode ser retirado do processador por duas razões: necessita acessar algum recurso, fazendo uma chamada de sistema - neste caso, após sua retirada do processador, seu estado será alterado para "dormindo", até que o recurso seja liberado pelo núcleo; o núcleo pode interromper o processo (preempção) - neste caso, o processo irá para a fila de processos (estado "pronto"), aguardando nova oportunidade para executar - ou porque a fatia de tempo esgotou- se, ou porque o núcleo necessita realizar alguma tarefa. Existem quatro chamadas de sistema principais associadas a processos: fork, exec, exit e wait; Fork é usada para criar um novo processo, que irá executar o mesmo código (programa) do programa chamador (processo-pai); Exec irá determinar o código a ser executado pelo processo chamado (processo-filho); Exit termina o processo; Wait faz a sincronização entre a finalização do processo-filho e o processo-pai. 8.1 Blocos de Controle de Processos (BCP) Os processos possuem um espaço de endereçamento que é divido em segmentos de texto, de dados e de pilha. Os segmentos de dados e de pilha estão sempre no mesmo espaço de endereçamento, podendo se expandir separadamente e, geralmente, em direções opostas. Os de texto, às vezes estão em um espaço de endereçamento diferente dos dados e pilha, e geralmente é somente de leitura. A estrutura de texto registra quantos processos estão usando o segmento de texto, incluindo um ponteiro para uma lista de suas estruturas de processo, e onde a tabela de página para o segmento de texto pode ser encontrada no disco, em caso de swapping. A estrutura de texto em si sempre está residente na memória principal. Os segmentos de texto, dados e de pilha para os processos podem ser submetidos ao swapping. Quando os segmentos são trazidos à memória, eles são paginados. As tabelas de página gravam informações do mapeamento da memória virtual do processo para a memória física. A estrutura de processo contém ponteiros para a tabela de página, para uso quando o processo está residente na memória principal, ou o endereço do processo no dispositivo de swap, quando o processo passa pelo swapping. De relevância mais óbvia ao usuário comum, o diretório corrente e a tabela de arquivos abertos são mantidos na estrutura de usuário. Todo processo tem uma fase de usuário e uma fase de sistema. Boa parte do trabalho é feito no modo usuário, mas, quando uma chamada ao sistema é feita, ela é realizada no modo de sistema. As fases nunca executam ao mesmo tempo. Quando um processo está executando em modo sistema, uma pilha de kernel para esse processo é usada, em vez da pilha de usuário que pertence a esse processo. A pilha de kernel e a estrutura de usuário juntas compõem o segmento de dados do sistema para o processo. A chamada ao sistema fork aloca uma nova estrutura de processo (com novo identificador de processo) para o processo filho, e copia a estrutura de usuário. Não há a necessidade de uma nova estrutura de texto, os processos compartilham seu texto. Contadores e listas apropriados são simples segmentos de dados e de pilha do processo filho. Já a chamada ao sistema vfork não copia os dados e a pilha para o novo processo, em vez disso, o novo processo simplesmente compartilha a tabela de página do processo anterior. Uma nova estrutura de usuário e de processo são criadas. Quando o processo pai é grande, vfork pode gerar economias substanciais de tempo de CPU do sistema. No entanto, é uma chamada ao sistema consideravelmente perigosa, já que qualquer mudança na memória ocorre nos dois processos até que execve ocorra. A chamada ao sistema execve não cria um novo processo ou uma nova estrutura de usuário. O texto e os dados do processo são substituídos. Os arquivos abertos são preservados, bem como a maioria das propriedades de tratamento de sinais. 8.2 Escalonamento de CPU O escalonamento de CPU no UNIX foi criado para beneficiar os processos interativos. Todo processo tem uma prioridade de escalonamento associada a ele. Os processos que fazem I/O de disco ou outras tarefas importantes têm prioridades menores do que zero (número maiores indicam prioridade menor) e não podem ser interrompidos por sinais. Os processos de usuários comuns têm menos prioridades de executar que os processos de sistema. Quanto mais tempo de CPU um processo acumula, menor (ou mais positiva) se torna a sua prioridade, e vice-versa. Assim, existe um feedback negativo no escalonamento de CPU e é difícil para um único processo usar todo o tempo da CPU. O envelhecimento de processos é usado para evitar paralisação. Não existe preempção de um processo por outro dentro do kernel. Um processo pode abandonar a CPU porque está esperando por I/O ou porque sua fatia de tempo expirou. Quando um processo escolhe abandonar a CPU, ele é suspenso em um evento. A experiência sugere que o escalonador UNIX tem melhor desempenho com jobs limitados por I/O, como pode ser observado quando existem vários Jobs limitados por CPU em execução. Os processos são representados por duas estruturas: a estrutura de processo e a estrutura de usuário. O escalonamento de CPU é um algoritmo com prioridades calculadas dinamicamente, que se reduz ao Round-Robin, em casos extremos. O escalonamento Round-Robin é realizado pelo mecanismo de timeout, que informa ao driver de interrupção de clock para chamar uma sub- rotina do kernel apor um intervalo específico. A sub-rotina a ser chamada nesse caso causa o reescalonamento e, em seguida, submete novamente um timeout para si mesma. 9. MEMÓRIA E GERENCIAMENTO As primeiras versões do Unix utilizavam basicamente a técnica de swapping para o gerenciamento de memória. Apenas a partir da versão 3BSD, o Unix passou a utilizar paginação por demanda. Atualmente, a grande maioria das versões do Unix, tanto BSD como System V, implementa gerenciamento de memória virtual por paginação com swapping. O espaço de endereçamento dos processos no Unix é dividido em três segmentos: texto, dados e pilha. O segmento de texto corresponde à área onde está o código executável dos programas, sendo uma área protegida contra gravação. O segmento de texto é estático e pode ser compartilhado por vários processos, utilizando o esquema de memória compartilhada. O segmento de dados corresponde às variáveis do programa, como tipos numéricos, vetores e strings. A área de dados é dinâmica, podendo aumentar ou diminuir durante a execução do programa. A pilha armazena informações de controle do ambiente do processo, como parâmetros passados a um procedimento ou system call. A área de pilha cresce dinamicamente do endereço virtual mais alto para o mais baixo. O Unix implementa o esquema de paginação por demanda como política de busca de páginas. Nesse esquema, páginas do processo são trazidas do disco para a memória principal apenas quando são referenciadas. O sistema mantém uma lista de páginas livres com todos os frames disponíveis na memória e gerencia os frames de todos os processos em uma lista de páginas em uso. Quando um processo faz referência a uma página que não se encontra na lista de páginas em uso, ocorre um page fault. O sistema identifica se a página está na memória através do bit de validade. Nesse caso, a gerência de memória retira uma página da lista de páginas livres e transfere para a lista de páginas em uso. Como pode ser observado, o Unix utiliza uma política global de substituição de páginas. O sistema implementa uma variação do algoritmo FIFO (First In First Out) circular como política de substituição de páginas. Esse algoritmo, conhecido como Two-Handed Clock, utiliza dois ponteiros, ao contrário do FIFO circular, que implementa apenas um. O primeiro ponteiro fica à frente do segundo um certo número de frames na lista de páginas em uso. Enquanto o primeiro ponteiro desliga o bit de referência das páginas, o segundo verifica o seu estado. Se o bit de referencia continuar desligado, significa que a página não foi referenciada desde o momento em que o primeiro ponteiro desligou o bit, fazendo com que essa página seja selecionada. Apesar de estarem liberadas para outros processos, as páginas selecionadas permanecem um certo tempo intactas na lista de páginas livres. Dessa forma, é possível que as páginas retornem aos processos de onde foram retiradas, eliminando-se a necessidade de acesso a disco. O deamon page também é responsável por implementar a política de substituição de páginas. Em casos onde o sistema não consegue manter um número suficiente de páginas livres o mecanismo de swapping é ativado. Nesse caso, o daemon swapper seleciona, inicialmente os processos que estão a mais tempo inativos. Em seguida, o swapper seleciona dentre os quatro processos quem mais consomem memória principal aquele que estiver a mais tempo inativo. Esse mecanismo é repetido até que a lista de páginas livres retorne ao seu tamanho normal. Para controlar todas as páginas e listas na memória principal, a gerência de memória mantêm uma estrutura de mapeamento dos frames na memória (core map), com informações sobre todas as páginas livres e em uso. O core map fica residente na parte não paginável da memória principal, juntamente com o kernel do sistema. 10. Deadlocks Deadlock é "Uma ocorrência em que cada processo do conjunto esta esperando por um evento que somente outro processo pertencente ao conjunto poderá fazer acontecer." Ressalta que a maior parte das situações de deadlock envolve a espera por recursos e que, pela definição, nenhum processo do conjunto poderá evitar que todos os processos do conjunto fiquem eternamente bloqueados. 10.1 Condições para Ocorrência de Deadlocks Exclusão Mútua, se nenhum recurso for atribuído exclusivamente a um único processo, nunca haverá deadlock. O princípio é evitar alocar um recurso quando ele não for absolutamente necessário e tentar assegurar que o menor número possível de processos possa de fato requisitar o recurso. Exemplo: O uso de spool permitindo que somente um recurso da impressora seja usado por processo. Condição de posse e espera, basta requerer que todos os processos solicitem os seus recursos antes de iniciar a execução. No entanto, um processo nunca tem que esperar por aquilo que precisa. Um dos problemas é não poder não saber quantos e quais recursos vão precisar no início da execução e também reter recursos que outros processos poderiam estar usando. Nenhuma preempção, seria tomar o recurso a força, contudo é uma solução pouco promissora, de certa forma inviável. Espera circular, pode ser eliminado de várias maneiras, uma delas é ter uma regra que diz que um processo é intitulado somente para um único recurso em qualquer momento, ou atribuir uma ordem para chamada para aquele recurso. 1.2.6. Impedimento do Impasse. Anteriormente o deadlock não foi evitado pela imposição de regras arbitrárias aos processos, mas pela análise cuidadosa de cada solicitação de recurso para ver se ele poderia ser concedido seguramente. Para evitar dinamicamente o deadlock podem-se adotar algumas soluções: Alocação individual de recursos - à medida que processo necessita; Escalonamento cuidadoso – impões um alto custo e conhecimento prévio dos recursos que serão utilizados; Algoritmos: 10.2 Algoritmo do Avestruz Este "Algoritmo do Avestruz" é uma forma humorística do autor Tanenbaum para comentar a abordagem dos sistemas UNIX, que simplesmente ignoram o problema, em razão da baixíssima probabilidade de ocorrer tal situação[3]. 10.3 Algoritmo do Banqueiro O algoritmo do banqueiro (algoritmo de Dijkstra), considera cada requisição no momento em que ela ocorre, verificando se essa requisição leva a um estado seguro; Se sim, a requisição é atendida, se não o atendimento é adiado para outro momento; Premissas adotadas por um banqueiro para garantir ou não crédito (recursos) para seus clientes (processos). Nem todos os clientes (processos) precisam de toda a linha de credito (recursos) disponível para eles. Vantagens e desvantagens do algoritmo do banqueiro: - Pouco utilizado, pois é difícil saber quais recursos serão necessários; - O número de processos é dinâmico e pode variar constantemente, tornando o algoritmo custoso; - Na teoria o algoritmo é ótimo. 10.4 Tratamento de Deadlocks Ignorar o problema: algoritmo da avestruz (Tanenbaum). Detectar e recuperar. Evitar (avoidance: solução dinãmica). Prevenir (prevention: solução estática). O Unix tenta evitar situações de deadlock, mas não as elimina completamente. 10.5 Detectar e Recuperar Detectar deadlock consiste em um algoritmo que basicamente testa se há alguma ordem de terminação dos processos e solicitação de recursos. Recuperar a situação: preempção de recursos; roll-back de processos; terminação de processos. 11. Threads no UNIX Conforme Miguel Pimenta Monteiro[4], somente ha pouco tempo a especificação POSIX da API do Unix definiu uma interface standard para a criação e terminação de threads. Esta interface é geralmente implementada como uma biblioteca (libpthreads.a), podendo ou não ser diretamente suportada pelo kernel do sistema operacional. Existem versões do Unix em que os threads são diretamente implementados no kernel e escalonados independentemente são os (kernel level threads), enquanto que em outras versões o que o kernel vê são apenas processos, "existindo" os threads unicamente na biblioteca de suporte, e escalonados para execução apenas na "fatia de tempo" dos respectivos processos são os (user level threads). Quando um programa começa a executar na função main() constitui um novo processo e pode considerar-se que contém já um thread. Novos threads são criados através da chamada de um serviço e começam a executar numa função que faz parte do código do programas. Cada novo thread é representado por um identificador (thread identifier ou tid) de tipo pthread_t. É necessário passar o endereço de uma variável desse tipo, como primeiro parâmetro de pthread_create(), para receber o respectivo tid. O segundo parâmetro serve para indicar uma série de atributos e propriedades que o novo thread deverá ter, se fornecer o valor NULL, o novo thread terá as propriedades por padrão, adequadas na maior parte dos casos. O terceiro parâmetro informa a função de início do thread. É uma função que deve existir com o seguinte protótipo: void * nome_da_função(void *arg). A função aceita um apontador genérico como parâmetro, que serve para passar qualquer informação, e retorna também um apontador genérico, em vez de um simples código de terminação. Se o valor de retorno for usado, é necessário que aponte para algo que não deixe de existir quando o thread termina. Finalmente o quarto parâmetro é o valor do apontador a ser passado à função de inicio, como seu parâmetro. Uma vez criado o novo thread ele passa a executar concorrentemente com o principal e com outros que porventura sejam criados. Quando um thread termina pode retornar um apontador para uma área de memória que contenha qualquer tipo de resultado. Essa área de memória deve sobreviver o thread, ou seja, não pode ser nenhuma variável local, porque essas deixam de existir quando o thread termina. Por padrão, quando um thread termina, o seu valor de retorno (o apontador genérico) não é destruído, ficando retido em memória até que algum outro thread execute um pthread_join() sobre ele. É possível criar threads que não são joinable e que por isso, quando terminam, libertam todos os seus recursos, incluindo o valor de retorno. No entanto não é possível esperar por esses threads com pthread_join(). Quando se cria um novo thread é possível especificar uma série de atributos e propriedades passando-os a pthread_create() através de uma variável do tipo pthread_attr_t. Essa variável terá de ser previamente inicializada com o serviço pthread_attr_init() e depois modificada através da chamada de serviços específicos para cada atributo[5]. 12. Kernel Conforme texto do IME-USP6, o Kernel do Unix (e de virtualmente qualquer outro sistema operacional) possui um papel de que convém ter noções, a fim de se poder compreender melhor o funcionamento do sistema, realizar diagnósticos e procedimentos administrativos como adição de componentes de hardware. Algum conhecimento do papel do Kernel é importante também para se ter uma noção mais clara do uso de arquivos especiais e do diretório /proc. O Kernel ordinariamente reside no filesystem como um outro arquivo qualquer. No Linux, ele é em geral o arquivo /vnlinuz ou /boot/vmlinuz, ou ainda /boot/vmlinuz-2.0.36. Ele é um programa, ainda que um pouco diferente dos programas de aplicaçãocom o /Bin/Is. O Kernel é carregado e posto em execução no boot da máquina, e a sua execução somente se encerra com o shutdown. De forma simplificada, o seu papel é num primeiro momento reconhecer o hardware e inicializar os respectivos drivers. Em seguida ele entra num estado administrativo onde funciona como intermediário entre as aplicações e o hardware. Por exemplo, quando uma aplicação necessita alocar mais memória, ela solicita isso ao Kernel. É o Kernel que distribui o tempo de CPU aos vários processos ativos. É ele que habitualmente realiza a entrada e saída de dados nas diferentes portas de comunicação. É por isso que a adição de hardware novo a uma máquina pode requerer a substituição ou ao menos a reconfiguração do Kernel. Os Kernels mais recentes do Linux oferecem vários mecanismos de configuração que os tornam sobremaneira flexíveis, a ponto de ser rara a necessidade de substituição do Kernel. Os dois mecanismos fundamentais de se configurar a operação do Kernel são a passagem de parâmetros no momento do boot (realizada pelo LILO) e a carga de módulos, feita manualmente ou por mecanismos automáticos como o Kerneld. O diálogo entre as aplicações e o Kernel realiza-se fundamentalmente através dos system calls, que são serviços que o Kernel oferece, como por exemplo read(2). Os device special files são maneiras de se referir ao Kernel os dispositivos físicos ou lógicos com que se pretende operar, por exemplo a primeira porta serial ou a segunda unidade de fita, ou o disco principal do sistema. Neles, o importante não é o nome, mas sim os números de dispositivos ou mais precisamente o major e o minor device numbers. Device special files são criados através do comando mknod, ou através de interfaces mais amigáveis, como o comando MAKEDEV. 6 Fonte: Unix: Conceitos e Comandos Básicos Os sistemas Unix-Like mais recentes oferecem um outro mecanismo de comunicação com o Kernel, que é o filesystem /proc. As entradas desse filesystem são pseudo-arquivos cujo conteúdo reflete o estado atual de inúmeras estruturas de dados internas do Kernel. Assim, um programa de aplicação passa a poder comunicar-se com o Kernel através dos mecanismos ordinários de leitura e escrita de arquivos. Em muitos casos a comunicação entre as aplicações e o Kernel é intermediada por bibliotecas, principalmente a libc. Elas oferecem serviços de mais alto nível que os system calls do Kernel, tornando mais simples o trabalho de programação. 13. Dispositivos de E/S Processos num sistema UNIX habitualmente possuem três dispositivos de I/O "padrões", a "saída padrão", a "entrada padrão" e a "saída de erros". O Unix permite que esses dispositivos (e outros) sejam definidos no momento da execução, podendo escolher o console, um "pipe" (pipe é um canal de comunicação FIFO(fist in, first out - primeiro a entrar, primeiro a sair) em modo simplex que pode ser usado para comunicação unidirecional entre processos. Uma implementação é geralmente integrada no subsistema de entrada e saída I/O de um sistema operacional), a impressora, um circuito virtual de rede conectando duas máquinas, uma linha física serial ou outras coisas. Usuários de MS-DOS talvez já tenham feito coisas como C>DIR>PRN, para imprimir a saída de um comando. Essa sintaxe foi herdada do UNIX daí a semelhança $ Is –I>dev/lp1. Naturalmente tanto no MS-DOS quanto no Unix existem formas mais apropriadas para se imprimir algo, mas no momento esse exemplo convém. Shell do estilo "Bourne" (sh, ksh,bash) permitem redirecionamento da saída de erros através da seguinte sintaxe: $ rm /bin/Is 2>/tmp/error O "2" deve-se a que, internamente, a saída de erros corresponde ao descritor 2 (o 0 é a entrada padrão e o 1 é a saída padrão). A entrada pode ser redirecionada de forma semelhante: $ wc . Acesso em 25/10/2010 Malanovicz, A. V. at all. Deadlock (Tanenbaum X Silberschatz) Disponível em: Acesso em 27/10/2010 Monteiro, MP. Sistemas Operativos. Disponível em: < http://paginas.fe.up.pt/~pfs/aulas/so2001/ap/cap4.pdf>. Acesso em 27/10/2010 ----------------------- [1] Fonte: Fernando Krahe. Nivelamento UNIX [2] Fonte da ilustração: http://www.cenapad.unicamp.br/servicos/treinamentos/tutorial_unix/unix_tutor -4.html [3] Fonte da Ilustração: http://www.inf.ufrgs.br/gppd/disc/inf01151/trabalhos/sem2000- 2/deadlock/deadlock.htm [4] Fonte: Miguel Pimenta Monteiro - Sistemas Operativos [5] Fonte da ilustração: https://computing.llnl.gov/tutorials/pthreads/ [6] Fonte: Miguel Pimenta Monteiro - Sistemas Operativos