You are on page 1of 110

Módulo 8

Sistema Operacional

Lição 1
Introdução ao Solaris

Versão 1.0 - Mar/2008


JEDITM

Autor Necessidades para os Exercícios


- Sistemas Operacionais Suportados
NetBeans IDE 5.5 para os seguintes sistemas operacionais:
• Microsoft Windows XP Profissional SP2 ou superior
• Mac OS X 10.4.5 ou superior
Equipe • Red Hat Fedora Core 3
Rommel Feria • Solaris™ 10 Operating System (SPARC® e x86/x64 Platform Edition)
John Paul Petines NetBeans Enterprise Pack, poderá ser executado nas seguintes plataformas:
• Microsoft Windows 2000 Profissional SP4
• Solaris™ 8 OS (SPARC e x86/x64 Platform Edition) e Solaris 9 OS (SPARC e
x86/x64 Platform Edition)
• Várias outras distribuições Linux

Configuração Mínima de Hardware


Nota: IDE NetBeans com resolução de tela em 1024x768 pixel
Sistema Operacional Processador Memória HD Livre
Microsoft Windows 500 MHz Intel Pentium III 512 MB 850 MB
workstation ou equivalente
Linux 500 MHz Intel Pentium III 512 MB 450 MB
workstation ou equivalente
Solaris OS (SPARC) UltraSPARC II 450 MHz 512 MB 450 MB
Solaris OS (x86/x64 AMD Opteron 100 Série 1.8 GHz 512 MB 450 MB
Platform Edition)
Mac OS X PowerPC G4 512 MB 450 MB

Configuração Recomendada de Hardware

Sistema Operacional Processador Memória HD Livre


Microsoft Windows 1.4 GHz Intel Pentium III 1 GB 1 GB
workstation ou equivalente
Linux 1.4 GHz Intel Pentium III 1 GB 850 MB
workstation ou equivalente
Solaris OS (SPARC) UltraSPARC IIIi 1 GHz 1 GB 850 MB
Solaris OS (x86/x64 AMD Opteron 100 Series 1.8 GHz 1 GB 850 MB
Platform Edition)
Mac OS X PowerPC G5 1 GB 850 MB

Requerimentos de Software
NetBeans Enterprise Pack 5.5 executando sobre Java 2 Platform Standard Edition
Development Kit 5.0 ou superior (JDK 5.0, versão 1.5.0_01 ou superior), contemplando
a Java Runtime Environment, ferramentas de desenvolvimento para compilar, depurar,
e executar aplicações escritas em linguagem Java. Sun Java System Application Server
Platform Edition 9.
• Para Solaris, Windows, e Linux, os arquivos da JDK podem ser obtidos para
sua plataforma em http://java.sun.com/j2se/1.5.0/download.html
• Para Mac OS X, Java 2 Plataform Standard Edition (J2SE) 5.0 Release 4, pode
ser obtida diretamente da Apple's Developer Connection, no endereço: http://
developer.apple.com/java (é necessário registrar o download da JDK).

Para mais informações: http://www.netbeans.org/community/releases/55/relnotes.html

Sistema Operacional 2
JEDITM

Colaboradores que auxiliaram no processo de tradução e revisão


Aécio Júnior Carlos Fernandes Gonçalves Massimiliano Giroldi
Alberto Ivo da Costa Vieira Denis Mitsuo Nakasaki Paulo Oliveira Sampaio Reis
Alexandre Mori Felipe Gaúcho Ronie Dotzlaw
Alexis da Rocha Silva Jacqueline Susann Barbosa Seire Pareja
Allan Wojcik da Silva João Vianney Barrozo Costa Thiago Magela Rodrigues Dias
Antonio José Rodrigues Alves Ramos Luiz Fernandes de Oliveira Junior Vinícius Gadis Ribeiro
Angelo de Oliveira Marco Aurélio Martins Bessa
Bruno da Silva Bonfim Maria Carolina Ferreira da Silva

Auxiliadores especiais

Revisão Geral do texto para os seguintes Países:


• Brasil – Tiago Flach
• Guiné Bissau – Alfredo Cá, Bunene Sisse e Buon Olossato Quebi – ONG Asas de Socorro

Coordenação do DFJUG

• Daniel deOliveira – JUGLeader responsável pelos acordos de parcerias


• Luci Campos - Idealizadora do DFJUG responsável pelo apoio social
• Fernando Anselmo - Coordenador responsável pelo processo de tradução e revisão,
disponibilização dos materiais e inserção de novos módulos
• Rodrigo Nunes - Coordenador responsável pela parte multimídia
• Sérgio Gomes Veloso - Coordenador responsável pelo ambiente JEDITM (Moodle)

Agradecimento Especial
John Paul Petines – Criador da Iniciativa JEDITM
Rommel Feria – Criador da Iniciativa JEDITM

Original desta por – McDougall e Mauro – Solaris Internals. Sun Microsystems. 2007.

Sistema Operacional 3
JEDITM

1. Objetivos
Neste módulo utilizaremos o Sistema Operacional Solaris desenvolvido pela Sun Microsystems.

Figura 1: Tela de Login no Solaris

A linha Solaris foi lançada em 1992, quando a versão 4 do SunOS - baseada no Free BSD - foi
substituída por uma nova versão chamada de Solaris 2, baseada em UNIX System V.
Internamente, a nova versão continuou sendo chamada de SunOS versão 5.0, mas foi promovida
no mercado como Solaris 2, com a versão anterior SunOS v.4.1 renomeada para Solaris 1. A
primeira versão do Solaris 10 (SunOS 5.10) foi lançada em janeiro de 2005 e vem recebendo
periódicas atualizações. A versão mais atual é a Solaris 10 8/07 (referente a outubro de 2007).
Ao final desta lição, o estudante será capaz de:
• Conhecer os principais aspectos de segurança do Solaris
• Aprender boas práticas de segurança do Solaris
• Observar as práticas de segurança do Solaris aplicadas à linguagem Java

Sistema Operacional 4
JEDITM

2. Sistema Operacional Solaris


O desenvolvimento do sistema operacional Solaris baseia-se nas seguintes áreas chave:
● Confiabilidade – desenvolvimento baseado em detecção de falhas e erros, isolamento e
recuperação, e gerenciamento de serviços combinado com um rigoroso conjunto de
padrões estritamente reforçados para a integração de novos códigos no sistema
operacional Solaris.
● Desempenho e Escalabilidade – grande habilidade em rodar uma ampla gama de
processos em sistemas, desde sistemas para computadores mono processados e servidores
montados em paralelo a servidores multiprocessados de alta demanda.
● Gerenciabilidade – ferramentas e aplicações para apoiar a administração de tarefas
cotidianas e também para gerenciar o sistema Solaris.
● Observabilidade – características que combinam o software administração para
monitorar, analisar o desempenho e comportamento de aplicações com o núcleo do
Solaris.
● Gerenciamento de Recursos – gerenciamento dos recursos disponíveis de hardware
para atender eficientemente aos requisitos de desempenho, habilitando uma variedade de
processos a rodar no sistema operacional Solaris.

Sistema Operacional 5
JEDITM

3. Introdução ao Solaris 10
Discutiremos algumas características da última versão do Sistema Operacional Solaris.
● Auto-Tratamento – combina as ferramentas Solaris Fault Manager e Solaris Service
Manager, que são capazes de agir quando ocorre um problema de hardware ou software.
Existem mecanismos para detectar e isolar os eventos. Uma vez que o erro for detectado,
pode ser feita uma desativação dinâmica dos componentes afetados.
● Framework de Gerenciamento de Serviço (SMF – Service Manager Framework) –
fornece um conjunto de comandos organizados que auxiliam a iniciar, interromper e
reiniciar os serviços do sistema.
● Zonas do Solaris – permite a criação e o gerenciamento de computadores virtuais co-
existentes em uma única instância dentro do núcleo do Solaris. Cada zona tem seu próprio
ambiente, além de processos e usuários que são independentes uns dos outros. Também
pode ser criada uma zona para rodar uma aplicação específica, fornecendo um ambiente
virtual especificamente customizado para esta aplicação.
● União dos Recursos Dinâmicos – permite aos usuários designar um processador em
particular ou partes de tempo do processador a um usuário em particular. Um
administrador pode definir, por exemplo, um usuário pode destinar para um conjunto de
recursos setenta por cento do tempo da CPU de dois processadores, aos quais podem ser
atribuídos para um processo em particular.
● Controle de Memória Física – usuários podem limitar a quantidade de memória física
que um processo pode usar.
● Mecanismo de Rastreamento Dinâmico – permite que os usuários testem o
comportamento das aplicações e do núcleo enquanto rodam os aplicativos, sem a
necessidade de modificação no código.
● Gerenciamento de Direitos a Processos – permite ao administrador atribuir direitos
específicos a usuários específicos, não apenas as permissões “tudo ou nada” do
administrador em relação ao usuário.
● Desempenho da Rede – foi implementada uma melhora significativa na rede TCP/IP, com
ênfase no aumento no rendimento e na eficiência.
● Suporte a Arquitetura x64 – foi ampliado o suporte para o novo processador Intel de 64
bits, permitindo que o Solaris 10 suporte tanto o processamento em 32 bits quanto o novo
de 64 bits.

Sistema Operacional 6
JEDITM

4. Esquema geral da arquitetura do núcleo do Solaris

Figura 2: Arquitetura do núcleo do Solaris

O núcleo do Solaris é dividido em módulos que fornecem as seguintes funcionalidades:


● Interface de Chamada de Sistema – fornece uma interface para que programas do
usuário acessem funções do núcleo do sistema.
● Execução de Processo e Planejamento – executa o gerenciamento de processos. Os
processos são programas em execução, aos quais é atribuído um tempo do processador, de
acordo com um determinado algoritmo de planejamento. Este módulo lida também com a
criação e limpeza do processo, quando um processo finaliza a execução.
● Gerenciamento de Memória – o sistema de memória não apenas lida com a alocação física
da memória para os processos, mas também gerencia a memória virtual armazenada no
disco rígido, bem como com a alocação de memória para os processos do núcleo.
● Camada de Tradução de Endereço de Hardware – esta seção fornece um mapeamento
do espaço de endereço de diferentes dispositivos, não apenas da memória física. Por
exemplo, um endereço de memória poderia, ao invés de apontar para um endereço físico de
memória, apontar para a memória virtual armazenada no disco rígido. Além disso,
dispositivos podem ser mapeados para endereços de memória; a recuperação de um valor de
um endereço deste tipo é na verdade uma requisição de leitura no dispositivo. A tradução do
hardware em endereços de memória abstrai a complexidade da camada de gerenciamento de
memória.
● Gerenciamento de Recursos – o gerenciamento de recursos suporta a alocação de
recursos do sistema como memória, dispositivos de leitura e escrita (I/O) e tempo de
processamento (CPU). Este módulo tenta maximizar a utilização dos recursos do sistema.
Utilizado com fuso horário, este módulo pode fornecer um ambiente isolado de recursos
disponíveis apenas a um determinado fuso horário.
● Gerenciamento de Sistema de Arquivos – gerencia a entrada e saída (I/O) de dados do
sistema de arquivos. O Solaris 10 pode manipular diferentes sistemas de arquivos ao mesmo
tempo.
● I/O Bus e Drivers Nexus – o acesso direto a dispositivos é gerenciado por este módulo.
Para cada dispositivo conectado ao sistema, o Solaris carrega um arquivo específico para as
nuances do dispositivo. O resultado deste gerenciamento é uma visão consistente do sistema
de entrada e saída para o resto do núcleo.
● Serviços do Núcleo (ciclos, temporizador, entre outros) – este módulo fornece as
funcionalidades utilizadas regularmente pelo núcleo, tais como ciclos de máquina, e
bibliotecas de sincronização.
● Rede – gerencia a comunicação do sistema operacional com outros computadores através de
uma rede de computadores.

Sistema Operacional 7
Módulo 8
Sistema Operacional

Lição 2
Instalação do Solaris

Versão 1.0 - Mar/2008


JEDITM

1. Objetivos
A palavra mudança, para muitas pessoas, é terrível. É incrível pensar que estamos em uma área
que sofre mudanças constantes. Entretanto, a grande maioria das pessoas nesta área não está
acostumada a estas. Analise se está nesta categoria respondendo às seguintes perguntas:
● Como desenvolvedor de Java, consegue usar desde um bloco de notas até o NetBeans, não
se importando em mudar de editor quando necessário?
● Quando sai uma nova versão de qualquer aplicativo, não se preocupa em fazer a
atualização pois já está a muito tempo usando a versão BETA ou DEVELOPER?
● Já leu e executou alguns exemplos em outros ambientes Java (JSE, JEE e JME) e pesquisou
sobre tecnologias decorrentes, como, por exemplo, JavaFX ou JavaSpot?
Se respondeu “não” a pelo menos uma dessas pergunta, cuidado, repito que esta é uma área em
constante mudança e com certeza precisamos nos adaptar rapidamente a qualquer ambiente.
Ao final desta lição, o estudante será capaz de:
• Particionar o HD com fdisk ou GParted para criar uma partição Solaris
• Instalar o Solaris em conjunto com o Windows

Sistema Operacional 4
JEDITM

2. Tarefas de Pré-instalação
Trocar de sistema operacional não é uma decisão simples como escolher almoçar um prato de
salada ao invés de um sanduíche em uma Fast-Food, pois podemos voltar ao sanduíche quando
desejarmos e sem complicação. Então, o que queremos lhe propor é uma convivência pacífica
entre os sistemas operacionais Windows XP e o Solaris no mesmo computador. Deste modo,
podemos nos adaptar de uma forma tranqüila e sem pressões.
Entretanto a instalação de um Sistema Operacional, mesmo atualmente, com todas as facilidades
dos programas, não é um processo simples para um usuário leigo. Recomendamos fortemente
que estes processos sejam realizados somente com total certeza do que está fazendo. Existe o
risco da perda total dos dados contidos no computador.
Como primeiro passo, iremos preparar seu HD para a instalação através de uma das seguintes
maneiras:
1. Particionar o HD usando o software fdisk
2. Reparticionar o HD usando o software Norton PartitionMagictm da Symantec
3. Reparticionar o HD usando o software gratuito GParted que pode ser encontrado no CD de
recuperação do Linux (SystemRescueCD)
Para segundo passo, precisamos instalar o Solaris, que pode ser feita:
1. Através do DVD de instalação
2. Através do download realizado nos endereços:
1. http://www.sun.com/solaris/get
2. http://opensolaris.org/os/downloads

2.1. Particionar o HD usando o software fdisk

Este é um processo que só deve ser executado em um computador que ainda não tenha um
sistema operacional ou que possua espaço no HD. Para executar o aplicativo fdisk, é necessário o
CD original do Windows XP. Após reiniciar o computador através do CD do Windows selecione a
opção "Iniciar com suporte a CD-ROM".
ATENÇÃO – todo o processo pode causar perda dos dados.
Após acessar a console do computador, execute o comando fdisk e as seguintes opções serão
mostradas:
1. Criar uma partição ou uma unidade lógica do DOS – para criar uma nova partição
2. Definir uma partição ativa – quando o computador for iniciado, este irá procurar pela
partição ativa do HD para iniciar o sistema operacional
3. Excluir uma partição ou unidade lógica do DOS – elimina uma partição
4. Exibir as informações sobre as partições – mostra as partições do seus HD
Ao acessar a opção 1, surgem as seguintes opções:
1. Criar uma partição primária do DOS
2. Criar uma partição estendida do DOS
3. Criar unidades lógicas na partição estendida do DOS
ATENÇÃO, o aplicativo fdisk só deve ser usado quando seu HD não estiver completamente
particionado, pois o aplicativo não realiza uma recolocação do espaço.

Sistema Operacional 5
JEDITM

2.2. Usar o software Norton PartitionMagictm

Para utilizar um reparticionador recomenda-se que primeiro seja realizada uma desfragmentação
no HD, isso permite que os arquivos particionados sejam concentrados.
ATENÇÃO – pode causar perda de dados.
O Norton PartitionMagictm da Symantec permite a reorganização do HD através da criação,
redimensionamento, cópia e mesclagem das partições do disco. Permite também separar sistemas
operacionais, aplicativos e documentos, entre outros.
Para maiores informações e aquisição do produto consulte o endereço:
http://www.symantec.com/region/br/home_office/products/system_performance/pm80/

2.3. Usar o software gratuito GParted

ATENÇÃO! É recomendado que, antes de prosseguir, seja realização de uma cópia de segurança
de todos os dados no computador.
Para utilizar um reparticionador recomenda-se que primeiro seja realizada uma desfragmentação
no HD, isso permite que os arquivos particionados sejam concentrados.
ATENÇÃO! O processo a seguir pode causar a perda dos dados contidos no computador.
Após realizar a desfragmentação do HD, faça download da imagem ISO do SystemRescueCD no
endereço http://www.sysresccd.org/ e grave um CD-R com esta imagem. Reinicie seu
computador através deste CD e a seguinte janela será mostrada:

Figura 1: Janela inicial do SystemRescueCD

Pressione Enter para continuar o processo.

Sistema Operacional 6
JEDITM

Figura 2: Selecionar o KeyMap

Selecione o KeyMap apropriado a seu teclado. E pressione Enter.

Figura 3: Selecionar o KeyMap

Após a busca das informações, entramos no sistema propriamente dito, digite Xorg ou Xvesa
para entrar no ambiente gráfico. Caso não funcione, digite startx.

Sistema Operacional 7
JEDITM

Figura 4: Menu principal

Na janela do xterm digite gparted. Com o aplicativo aberto, selecione o botão Resize/Move
para redimensionar o tamanho do HD.

Figura 5: Janela do gparted

Utilize o mouse para redimensionar o tamanho do HD.

Sistema Operacional 8
JEDITM

Figura 6: Redimensionando o espaço no HD

Selecione o novo espaço aberto.

Figura 7: Definindo a nova partição

No menu selecione a opção Partition/New.

Sistema Operacional 9
JEDITM

Figura 8: Criando uma nova partição

Uma nova partição será alocada no espaço.

Figura 9: Partição criada

Selecione o botão Apply.

Sistema Operacional 10
JEDITM

Figura 10: Aplicando as definições

Aguarde o término.

Figura 11: Finalizando a aplicação das definições

Deste modo, uma nova partição está pronta para ser utilizada para o novo sistema.

Sistema Operacional 11
JEDITM

Figura 12: Pronta para instalação

Sistema Operacional 12
JEDITM

3. Instalação do Solaris
ATENÇÃO! Os passos a seguir podem causar a perda de dados contidos na partição do Windows.
Recomenda-se a execução de uma cópia de segurança.
Agora que já temos uma partição livre e disponível, podemos instalar sem problemas o novo
sistema operacional. Reinicie o computador com o DVD do Solaris no driver.
A seguinte janela será mostrada para iniciarmos todo o processo.

Figura 13: Selecionando o tipo de instalação

Selecionar o modo de instalação.

Figura 14: Selecionando o modo de instalação

Em seguida será verificada as condições do seu computador e os locais de instalação.

Sistema Operacional 13
JEDITM

Figura 15: Verificando o espaço para instalação

Pressione 1 e aguarde a conclusão da operação e abertura da seguinte janela:

Figura 16: Selecionando o teclado

Selecionar o tipo do teclado e aguarde o ambiente gráfico ser aberto.

Sistema Operacional 14
JEDITM

Figura 17: Selecionando o tipo de instalação

Esta mensagem informa que a próxima janela contém uma pergunta que deve ser respondida em
30 segundos para verificar que o ambiente gráfico foi iniciado corretamente.

Figura 18: Aguarde a inicialização do ambiente gráfico

Caso o ambiente gráfico seja ativo corretamente, pressionar Enter.

Sistema Operacional 15
JEDITM

Figura 19: Selecionando o idioma

Selecionar o idioma padrão para o processo de instalação.

Figura 20: Iniciando a instalação

Selecionar a partição para o Solaris. Atenção.

Sistema Operacional 16
JEDITM

Figura 21: Selecione a partição para instalar

Selecionar a data e hora atuais e a zona geográfica.

Figura 22: Data, hora e zona local

Selecione o idioma padrão.

Sistema Operacional 17
JEDITM

Figura 23: Idioma

Definir a senha para o administrador do sistema (root). É recomendado que um usuário seja
criado para utilização do sistema. E definir o nome para o computador.

Figura 24: Identificação

Confirmar o processo para iniciar a instalação.

Sistema Operacional 18
JEDITM

Figura 25: Pronto para instalar

Aguardar o término do processo.

Figura 26: Aguarde a finalização do processo

Seja bem-vindo a um novo mundo de mudanças.

Sistema Operacional 19
Módulo 8
Sistema Operacional

Lição 3
Comandos Básicos e Scripting

Versão 1.0 - Mar/2008


JEDITM

1. Objetivos
A aprendizagem de um sistema operacional vai além do conhecimento da interface gráfica. A
interface gráfica é o bastante para usuários comuns, mas o terminal permite a execução de
comandos mais poderosos. Nessa lição veremos alguns comandos básicos do terminal do Solaris e
também uma introdução sobre shell scripting.
Ao final desta lição, o estudante será capaz de:
• Utilizar alguns comandos básicos em um Terminal
• Discutir a criação de scripts básicos e avançados
• Executar comandos básicos para administração do ambiente

Sistema Operacional 4
JEDITM

2. Terminal
Podemos abrir um terminal pressionando o botão direito no desktop e selecionando a opção 'Open
Terminal'. O prompt é onde os comandos serão executados. Nos exemplos mostrados, iremos
representar o comando de terminal pelo símbolo $. O símbolo $ não deve ser digitado.

Figura 1: Janela de Terminal no Solaris

Mais à frente nessa lição, será solicitado a troca para o usuário definido por padrão como root. O
usuário root, também conhecido como superusuário, é o administrador do sistema, com acesso a
comandos que afetam a todo o sistema. O prompt do terminal para o superusuário muda de
forma a refletir isso. Para representar o terminal do superusuário, usaremos o símbolo #. De
modo semelhante, não devemos digitar o símbolo # inicial.
Comandos e nomes de arquivos um ambiente Unix, como o caso do Solaris e Linux, são sensíveis
a letras maiúsculas e minúsculas. Ao executar, por exemplo, o comando para mostrar os arquivos
de um diretório como Ls, ao invés de ls, será mostrado um erro informando que o comando não
existe.

2.1. Comandos de ajuda

Cada comando no ambiente terminal possui uma documentação de ajuda. Podemos acessar essa
ajuda através do comando man (manual) seguido pelo comando. Por exemplo, para solicitar a
ajuda sobre o comando ls.
$ man ls

Podemos navegar pelo editor de ajuda utilizando as teclas para cima e para baixo. Para finalizar o
editor de ajuda, pressionamos a tecla q (quit).

Sistema Operacional 5
JEDITM

2.2. Navegar no sistema de arquivos

2.2.1. Visualizar os arquivos do diretório Atual


O comando ls (list sources) lista arquivos no diretório corrente. Podemos também mostrar os
arquivos selecionados utilizando o comando ls. Da seguinte forma:
ls jedi.txt

Se nenhum arquivo no diretório corrente tiver o nome jedi.txt, então o comando não produz
nenhuma saída.
O comando ls permite a utilização de curingas para visualizar arquivos que atendam a um certo
critério. Apresentamos a seguir alguns curingas mais utilizados:
● * - representa nenhum ou vários caracteres. Por exemplo:
○ ls a* - mostrar os arquivos iniciados pela letra a
○ ls *.java - listar os arquivos que terminem com .java
○ ls * - listar os arquivos, neste caso o curinga é desnecessário pois este é o
comportamento padrão deste comando
● ? - representa um único caractere. Por exemplo:
○ ls ?a? - mostrar os arquivos que contém uma letra a no meio de um nome
Como a maioria dos comandos, o comando ls pode ter opções adicionais, que são iniciadas com o
símbolo '-' (sinal de menos). Por exemplo, para visualizar arquivos no formato longo, que mostra
informações adicionais do arquivo, devemos usar a opção -l. Para ver todos os arquivos (incluindo
arquivos ocultos) podemos usar a opção -a. Para ambas, podemos digitar:
$ ls -a -l

Ou então:
$ ls -al

Executar o comando ls com a opção -a faz com que os arquivos ocultos sejam incluídos na
listagem. Arquivo ocultos são arquivos cujo o nome é iniciado por um ponto. Por exemplo, o
arquivo .bashrc é um arquivo oculto, um script que é executado sempre que uma janela de
terminal é aberta. Podemos criar arquivos ocultos simplesmente utilizando um ponto no inicio do
nome do arquivo.
Algumas opções consistem de palavras ao invés de letras simples. Algumas opções podem
necessitar de parâmetros, os quais são especificados por um símbolo '=' (sinal de igual). O
seguinte exemplo que mostra todos os arquivos, utiliza a opção –sort, com o parâmetro size,
para ordenar pelo tamanho dos arquivos.
$ ls -al –sort=size

Existem muitas outras opções para o comando ls que podem ser verificadas utilizando o comando
man.

2.2.2. Permissões de arquivos


Uma breve descrição da saída do comando ls -l é necessária. Em particular, iremos discutir
permissões de arquivos.

Figura 2: Sistema de Permissões

Sistema Operacional 6
JEDITM

O primeiro espaço na lista de controle de acesso descreve o tipo do arquivo que está sendo
listado:
● Um espaço em branco indica que é um arquivo comum
● Um d indica um diretório
● Um I ou s indica um caminho simbólico, do tipo hardware ou software, para outro arquivo,
respectivamente
● Exclusivamente para dispositivos, um b indica que o arquivo representa um dispositivo de
bloco enquanto que um c indica que o arquivo aponta para um dispositivo de caractere
Existem três permissões possíveis para as listas de controle:
● Para um arquivo ser lido, deve possuir a permissão r
● Para um arquivo ser alterado, deve possuir a permissão w
● Para um arquivo ser executado, deve possuir a permissão x (em ambiente tipo Unix não
existe a diferença entre um arquivo executável é um arquivo texto, diferente de ambiente
tipo DOS)
A lista de controle de acesso é dividida em três partes:
● Os primeiros três caracteres referem-se ao usuário que criou o arquivo
● Os próximos três caracteres contém a permissão do grupo de usuários que pertencem ao
mesmo grupo do usuário que criou o arquivo
● As últimas três letras indicam as permissões para usuários que não estão no grupo do
usuário que criou o arquivo
Considere as seguintes permissões de acesso para um arquivo qualquer: rwxrw-r--
As primeiras três letras determinam as permissões do usuário. O r nesse grupo de letras significa
que o arquivo é legível pelo criador do arquivo. O w que o arquivo pode ter seu conteúdo
modificado (não sendo apenas de leitura). A permissão x significa que o arquivo pode ser
executado. No exemplo, o usuário que criou o arquivo pode ler, escrever e executar este.
Usuários nos sistemas UNIX podem pertencer a grupos de usuários. Os próximos três caracteres
determinam quais permissões outros usuários que pertencem ao mesmo grupo do criador do
arquivo possuem. No exemplo, a permissão ausente, caractere x, significa que os outros usuários
do grupo não podem executar o arquivo.
Para finalizar, os últimos três caracteres determinam as permissões para os usuários que não
pertencem ao grupo do criador do arquivo. No exemplo, podem apenas ler o arquivo.
Permissões de arquivos podem ser modificadas com o comando chmod (change mode). Aceita
dois parâmetros, o nome do arquivo a ser modificado e a nova permissão utilizando três dígitos
em formato octal.
Se um digito octal for convertido para binário, então o número binário consiste de três dígitos.
O comando chmod necessita de três dígitos em formato octal:
● O primeiro para a permissão do usuário
● O segundo para permissões do grupo
● O terceiro para permissões de outros usuários.
Na montagem final da lista de controle de acesso convertendo o conjunto para binários, o binário
1 significa que a permissão é concedida e o binário 0 que não existe permissão para a posição.
Por exemplo, para um arquivo hello.txt, a permissão rwxrw-r pode ser configurada usando:
$ chmod 764 hello.txt

A seguir temos uma tabela para a conversão do tipos:


Octal Binário Permissões
0 000 ---

Sistema Operacional 7
JEDITM

1 001 --x
2 010 -w-
3 011 -wx
4 100 r--
5 101 r-x
6 110 rw-
7 111 rwx
Figura 3: Conversão Binário, Octal e Permissões

E a permissão rw-r-xr-- pode ser configura usando:


$ chmod 654 hello.txt

2.2.3. Obter a localização atual e mudar de diretório


Ao entrar no terminal, por padrão, o diretório corrente é o home. Para descobrir qual é o diretório
corrente, usamos o comando pwd (present work directory):
$ pwd
/export/home/alice

O sistema de arquivo do Solaris começa no diretório root, ou diretório /. Para alcançar o diretório
home do usuário logado a partir do diretório /, primeiramente devemos acessar o diretório
export, e em seguida o diretório home, e nesse diretório acessar o diretório do usuário logado
(que possui o mesmo nome deste).
Para mudar o diretório corrente, utilizamos o comando cd (change diretory) seguido do nome do
diretório que deve ser acessado. Como não existe nenhum diretório dentro do diretório home,
primeiramente devemos ir para o diretório /, conforme o comando a seguir:
$ cd /

Ao executar o comando ls nesse diretório, serão mostrado todos os diretórios e arquivos que
estão no nível mais alto dos diretórios do sistema. A figura a seguir mostra um sistema de
arquivo típico para sistemas baseados em UNIX, tal como o Solaris.

Figura 4: Sistema de arquivos padrão

Sistema Operacional 8
JEDITM

Por exemplo, para acessar o diretório etc, a partir da raiz, podemos digitar:
$ cd etc

Ao executar o comando pwd, será mostrado o caminho do diretório etc. Como forma de exercitar
estes comandos, entre nos subdiretórios de etc e verifique quais são seus arquivos.
Para subir um nível de diretório, podemos digitar o seguinte comando:
$ cd ..

Ponto seguido de ponto é uma referência para o diretório pai do diretório atual. Sendo assim, se o
usuário estiver, por exemplo, em etc, poderá digitar esse comando para retornar ao diretório /.
Há duas maneiras para especificar um diretório. Podemos especificar seu nome relativo. Por
exemplo, para navegar do diretório / ao diretório defaults, que está dentro do diretório etc,
podemos digitar:
$ cd etc/defaults

Podemos também especificar o nome absoluto de um diretório, que é o caminho completo do


diretório sempre a partir da raiz. Por exemplo, estando em qualquer diretório do sistema, o
usuário pode retornar ao diretório home através do comando:
$ cd /export/home/<username>

Se o nome do usuário for “alice”, então o comando seria da seguinte forma:


$ cd /export/home/alice

Observe que digitando apenas o comando cd fará com que o diretório home se torne o diretório
corrente.

2.2.4. Sistema de arquivos Solaris


Voltemos à raiz do sistema Solaris. Embora existam muitos diretórios que podem mudar de
acordo com a instalação, os seguintes diretórios são padrões:
● /export/home – O diretório home para todos os usuários. Pode também ser apenas
/home
● /usr – Arquivos executáveis do sistema
● /etc – Arquivos de configuração
● /var – Diretório para arquivos temporários
● /proc – Diretório especial do sistema de arquivos para mostrar todas as aplicações que
estão rodando no momento
● /mnt – Diretório onde as mídias removíveis são montadas, tais como, disquetes, CDs e
pen drives
● /dev – Dispositivos são representados como arquivos no sistema Solaris e contidos neste
diretório

2.2.5. Obter a utilização e o espaço livre no disco


O comando du (disk usage) mostra quanto espaço está sendo gasto pelos arquivos num
determinado diretório, incluindo sub-diretórios. Geralmente é usado com o atributo -h, para
mostrar uma listagem mais completa.
$ du -h /etc
5K /etc/certs
5K /etc/cron.d
5K /etc/crypto/certs
1K /etc/crypto/crls
11K /etc/crypto
63K /etc/default

Sistema Operacional 9
JEDITM

3K /etc/devices
...
56M /etc

O comando df (disk free) mostra, para cada disco, sua capacidade, quanto está em uso e quanto
está livre. Assim como o comando du, o comando df também é, normalmente, utilizado com a
opção -h.
$ df -h
Filesystem size used avail capacity Mounted on
/dev/dsk/c0t0d0s0 13G 3.8G 9.3G 30% /
/devices 0K 0K 0K 0% /devices
ctfs 0K 0K 0K 0% /system/contract
proc 0K 0K 0K 0% /proc
mnttab 0K 0K 0K 0% /etc/mnttab
swap 2.4G 1.1M 2.4G 1% /etc/svc/volatile
objfs 0K 0K 0K 0% /system/object
fd 0K 0K 0K 0% /dev/fd
swap 2.4G 0K 2.4G 0% /tmp
swap 2.4G 48K 2.4G 1% /var/run
/dev/dsk/c0t0d0s7 19G 8.1G 11G 43% /export/home

2.2.6. Localizar arquivos


Para procurar um arquivo, o usuário pode utilizar o comando find o qual procura pelo nome do
arquivo (especificado com a opção -name) de um dado diretório, e, recursivamente, por todos os
subdiretórios. É permitido o uso de curingas neste comando.
O exemplo abaixo procura todos os arquivos que começam pela palavra profile e estão dentro do
diretório /etc.
$ find /etc -name profile*

Existem muitas opções para se usar juntamente com este comando. Para maiores informações,
basta rodar o comando man.

2.3. Modificar o sistema de arquivos

Usuários podem fazer mudanças apenas em seus diretórios. Somente o usuário root pode fazer
mudanças nos diretórios do sistema.

2.3.1. Copiar arquivos


O usuário pode copiar arquivos através do comando cp (copy), o qual possui dois argumentos: o
arquivo fonte e o de destino. Lembrando que pode conter diretórios e curingas nos nomes dos
arquivos.
Por exemplo, para copiar o arquivo passwd do diretório /etc para o diretório atual, executamos:
$ cp /etc/passwd .

No comando o “.” é uma referência ao diretório atual. E para copiar todos os arquivos do diretório
/etc para o diretório /home:
$ cp /etc/* /export/home/<username>

Se o destino for um nome de arquivo e não um diretório, o comando cp copia o arquivo e o


renomeia. Por exemplo, para um arquivo chamado a.txt, podemos realizar uma cópia para um
arquivo chamado b.txt:
$ cp a.txt b.txt

Também podem ser feitas cópias de diretórios inteiros, usando a opção -r (recursive). Cópias
recursivas incluem subdiretórios do diretório especificado. Por exemplo, para copiar todo o
conteúdo do /etc para o diretório home do usuário:

Sistema Operacional 10
JEDITM

$ cp -r /etc /export/home/<username>

Uma vez copiado, aparecerá um novo diretório /etc no diretório /home.

2.3.2. Mover arquivos


Para mover arquivos, usamos o comando mv (move) que contém opções e funcionalidades
similares ao comando cp, só que ao término de uma cópia bem sucedida, o comando exclui os
arquivos ou diretórios originais.
Por exemplo, para mover o arquivo a.txt para c.txt:
$ mv a.txt c.txt

2.3.3. Eliminar arquivos


Para eliminar arquivos, usamos o comando rm (remove) e o nome do arquivo a ser eliminado.
Por exemplo, o seguinte comando elimina o arquivo c.txt:
$ rm c.txt

o comando rm pode ser usado com curingas. Por exemplo, para remover todos os arquivos que
estão em um diretório tempo, dentro do diretório home do usuário:
$ rm /export/home/<username>/temp/*

Note que isto remove apenas arquivos. A remoção de diretórios será discutida a seguir.

2.3.4. Criar e eliminar diretórios


Para criar diretórios, usamos o comando mkdir (make directory). Por exemplo, criar um diretório
lesson1:
$ mkdir lesson1

Também é possível especificar o caminho completo, a partir da raiz. Por exemplo, criar um
subdiretório exercise1 dentro do diretório lesson1:
$ mkdir /export/home/<username>/lesson1/exercise1

Para eliminar diretórios, usamos o comando rmdir (remove directory). Este comando permite
usar o caminho relativo ou absoluto. Por exemplo, remover o diretório exercise1:
$ rmdir /export/home/<username>/lesson1/exercise1

Note que o comando rmdir só pode ser utilizado se o diretório já estiver vazio, sem arquivos.
Para remover um diretório, devemos primeiro utilizar o comando rm e em seguida o comando
rmdir para remover o diretório.
No entanto, existe uma solução para este problema. Usar o comando rm com as opções -rf. A
opção -r indica recursividade, isso significa que todos subdiretórios serão removidos, e a opção -f
é utilizada para forçar essa remoção, sem perguntas de confirmação. O seguinte exemplo remove
o diretório lesson1 com todo seu conteúdo sem questionar a ação.
$ rm -rf /export/home/<username>/lesson1

Note que este é um comando bastante perigoso. Se o usuário estiver no diretório raiz e rodar o
comando 'rm -rf .', pode remover todo o sistema de arquivos se tiver permissão para tal. E não
existe nenhuma forma simples para desfazer essa ação no Solaris. Portanto, o usuário deve ter
EXTREMO cuidado ao utilizar este comando e ter total certeza do que está fazendo.

2.4. Direcionar a saída de um comando para um arquivo

2.4.1. Redirecionamentos
As saídas dos comandos podem ser redirecionadas para arquivos. Por exemplo, a listagem
completa do conteúdo do diretório /etc pode exceder a capacidade da tela, então, o usuário pode

Sistema Operacional 11
JEDITM

salvar esta saída em um arquivo texto para ser visualizado através de um editor de texto. Para
isto, basta usar o comando junto ao operador “>” (sinal de maior que) seguido do nome do
arquivo texto onde a saída será armazenada. Por exemplo, para listar todos os arquivos do
diretório /etc e colocar seu conteúdo no arquivo list.txt:
$ ls -l /etc > list.txt

Depois de executado, o usuário pode visualizá-lo através de qualquer editor de texto e até
guardá-lo para uma análise posterior. Este redirecionamento funciona com qualquer comando.
O operador “>” sobrescreve o conteúdo antigo do arquivo destino. Para concatenar a saída para
um arquivo que já possui um conteúdo, utilizamos o operador “>>”. Por exemplo, adicionar a
saída de um novo comando ls ao arquivo list.txt (criado no exemplo anterior), sem eliminar o
conteúdo anterior:
$ ls -l /usr >> list.txt

O comando para concatenar é geralmente usado durante um processo de log, na qual a saída dos
programas, tais como, mensagens do sistema operacional, são armazenadas por um longo
período de tempo para uma eventual auditoria.
O comando echo, mostra (na tela ou em arquivo) o parâmetro passado. Por exemplo:
$ echo 'Hello world!'
Hello world!

$ echo 'Listing the contents of /etc' > list.txt


$ ls -l /etc >> list.txt
$ echo 'Listing the contents of /usr' >> list.txt
$ ls -l /usr >> list.txt

Para ignorar totalmente a saída de um comando, o usuário pode redirecioná-la para um arquivo
especial chamado /dev/null. Este é um arquivo especial que simplesmente descarta qualquer
saída enviada para ele. Suprimir uma saída de comando pode mudar a forma como um programa
se comporta. Por exemplo, o seguinte comando suprime totalmente a saída do comando ls.
$ ls /etc > /dev/null 2> /dev/null

2.4.2. Error streams


Output stream e Error stream agem de formas diferentes nos sistemas UNIX. Output stream é a
saída esperada pelo usuário enquanto que Error stream é a saída quando ocorre um determinado
problema.
Linguagens de programação freqüentemente oferecem comandos para enviar informação. Java
possue dois objetos diferentes: System.out para uma saída comum e System.err para uma
saída com erros.
É possível ver em ação o error stream quando executamos o seguinte comando:
$ ls nosuchfile.txt > output.txt
ls: nosuchfile.txt: No such file found

O erro do comando ls é enviado para a saída padrão, e não para o arquivo output.txt. O
operando “>” redireciona somente o output stream do comando. Se também for necessário
redirecionar o error stream, utilizamos o operando “2>”:
$ ls nosuchfile.txt > output.txt 2> error.txt

2.4.3. Paginar a saída de conteúdo


O comando more pode ser utilizado para visualizar conteúdo na tela. Este comando paralisa a
saída de forma que seja possível ver uma tela cheia por vez. É possível concatenar o comando
more com o comando ls utilizando o comando “|” (sinal de pipe).
$ ls -l /etc | more

Sistema Operacional 12
JEDITM

O comando more, no entanto, é limitado podendo ser visualizado somente no sentido para a
frente. O comando less é similar ao comando more, entretanto permite a visualização da saída
em ambos sentidos, para frente e para trás.
$ ls -l /etc | less

2.5. Variáveis de ambiente

Variáveis de ambiente são definidas para uso pelo sistema operacional. As variáveis são
identificadas pelo símbolo $ (sinal de cifrão) em seu início. Por exemplo, a variável $PATH lista os
diretórios nos quais o terminal procura por arquivos executáveis quando o usuário executa algum
comando. O seguinte exemplo verifica o valor da variável $PATH:
$ echo $PATH

Outros exemplos de variáveis de ambiente são $HOME, $USER e $PWD que mostram o diretório
padrão, o usuário atual e o diretório atual, respectivamente. Para descobrir seus valores, execute
o comando echo no terminal. A seguir vemos como mostrar esses valores, e também como
mesclar mensagens com as variáveis em um único comando echo:
$ echo 'Olá! Sou ' $USER '. ' $HOME ' é meu diretório padrão.'

Resultando, por exemplo:


Olá! Sou alice. /export/home/alice é meu diretório padrão.

É possível utilizar aspas simples ou duplas no comando echo. A diferença é que ao colocar uma
variável dentro de aspas duplas, seu valor é retornado.
$ echo 'Olá! Meu nome é $USER'
Olá! Meu nome é $USER
$ echo "Olá! Meu nome é $USER"
Olá! Meu nome é alice.

Como pode ser visto, colocando o símbolo $ entre aspas duplas no comando echo significa que as
palavras sejam consideradas como variáveis. Para o símbolo $ ser impresso, basta colocar um
símbolo \ (sinal de barra contrária) antes do símbolo $, isto é, \$.
As variáveis também podem ser definidas. Por exemplo, para uma variável de boas-vindas
definida pelo próprio usuário, basta digitar:
$ greeting='Bem Vindo!'

Note que não é necessário colocar o símbolo $ se estiver atribuindo um valor a uma variável
(cuidado pois o símbolo $ exibido acima corresponde ao prompt). Também não poderá haver
espaços entre os dois lados do operador de atribuição. Os nomes das variáveis são sensíveis a
maiúsculas e minúsculas.
É possível utilizar as variáveis criadas da mesma forma que as variáveis de ambiente.
$ echo $greeting ' Como está ' $USER '?'
Bem Vindo! Como está alice?

É possível também mudar o valor das variáveis existentes. Por exemplo, se for desejável adicionar
o diretório /home à variável $PATH, basta digitar:
$ PATH=$PATH:/export/home/<username>

Deve ser observado que as variáveis definidas pelo usuário são acessíveis somente dentro do
terminal onde foram declaradas. Se houver outras janelas de terminal abertas, não será possível
acessar estas variáveis definidas. Além disso, assim que o terminal é fechado, as variáveis criadas
desaparecerão e as variáveis ambiente retornarão aos seus valores originais.

Sistema Operacional 13
JEDITM

3. Scripts
Um script é um arquivo que pode conter diversas instruções seqüenciais para execução no
terminal (comparando com o ambiente DOS, é um arquivo .bat). Algumas tarefas dos sistemas
envolvem um encadeamento sucessivo de comandos simples e, ao colocá-los em um único script,
economiza-se tempo e esforço ao executar um único comando.
O script vai além de uma simples cadeia de comandos. A maioria das linguagens de script tem
suas próprias construções de programação, tais como sentenças com comandos de decisão e
repetição, e podem aceitar entrada de dados do usuário.
Na interface de console existe o que chamamos de interpretador de comandos, conhecido como
shell. Existem vários tipos de shells, tais como, ksh ou korn shell, ou csh (c shell), cada um
possui uma sintaxe distinta. Utilizaremos aqui a linguagem denominada bash. Bash é uma sigla
para Bourne-again shell, que é uma revisão da linguagem Bourne shell. Entretanto, o
conhecimento aprendido pode ser aplicável a outros shells.

3.1. Criando um Bash Script

Em exemplos anteriores listamos o conteúdo dos diretórios /etc e /usr e enviamos para um
arquivo chamado list.txt. Abaixo vemos um script que executa as instruções de uma forma
conjunta. Pode ser utilizado em qualquer editor de texto para escrever este arquivo que receberá
o nome de myscript.
#!/bin/bash
# this is my first bash script.
echo 'Listing the contents of /etc' > list.txt
ls -l /etc >> list.txt
echo 'Listing the contents of /usr' >> list.txt
ls -l /usr >> list.txt

Para executar este arquivo, devemos primeiro modificar suas permissões. Para descobrir quais são
as permissões do script, usa-se o comando ls -l.
$ ls -l myscript
-rw-r--r-- 1 alice alice 6 Nov 12 8:40 myscript

As permissões para myscript são de leitura e escrita para o proprietário do arquivo (alice) e
somente de leitura para outros usuários. Para tornar o script executável, adicionamos a
permissão através do comando chmod, da seguinte maneira:
$ chmod 755 myscript
$ ls -l myscript
-rwxr-xr-x 1 alice alice 6 Nov 12 8:42 myscript

Agora todos usuários possuem a permissão para executar o arquivo myscript. Para executar o
script, digitamos:
$ ./myscript

3.1.1. Comentários
No arquivo myscript, vemos linhas que são iniciadas pelo símbolo #. Estas linhas são
comentários. No entanto, a primeira linha, iniciada pelo símbolo #! do script indica que este é um
script bash, cujo o executável está no diretório /bin. Deve haver pelo menos um espaço entre o
símbolo # e a primeira letra do comentário.
A primeira linha do script bash não é exatamente um comentário mas um indicador da linguagem
de script a ser utilizada:
● #!/bin/bash - informa ao SO para executar o script utilizando o bash
● #!/bin/ksh - informa ao SO para executar o script utilizando o korn shell (outro tipo de
linguagem de script)

Sistema Operacional 14
JEDITM

3.1.2. Bash Scripts embutidos


A maioria dos sistemas baseados em UNIX utilizam scripts extensivamente durante sua execução.
Por exemplo, quando o bash é iniciado durante o boot do sistema, ele executa comandos do
/etc/profile. A variável $PATH e outras são definidas neste momento.
Quando o usuário realiza a entrada no sistema, os arquivos ocultos .bash_profile, .bash_login
e .profile são lidos e executados. Quando um terminal é iniciado, são executados os comandos
do script .bashrc do usuário corrente. Quando uma sessão é finalizada, o bash executa os
comandos do arquivo .bash_logout.

3.2. Scripting avançado

Iremos agora descobrir como as variáveis, sentenças de decisão e de repetição podem ser
utilizadas. Também veremos como conseguir entrada de dados pelo executor do script.

3.2.1. Substituição de variáveis


Conforme discutido anteriormente, uma variável é indicada pelo símbolo $. O símbolo $ é um
comando que indica que o valor da variável deve ser substituído naquela posição quando o
comando for executado.
Por exemplo, considere os seguintes comandos
$ x=42
$ echo $x

A variável x contém o valor 42. O comando echo $x é substituído internamente por echo 42.
Como na maioria das vezes as variáveis são utilizadas desta maneira, é necessário lembrar que o
símbolo $ não aparece durante a atribuição de uma variável ou quando uma variável for
exportada.
Esta substituição pode ser vista com o caractere de nova linha. Como na maioria das linguagens
de programação, uma nova linha pode ser impressa com uma barra contrária e o caractere n (isto
é, \n). Entretanto, colocar \n em uma string no comando echo não produz uma nova linha.
Para se fazer isto, deve-se utilizar $'\n'. Será inserida uma nova linha na posição onde \n
aparecer.
$ echo 'hello \n world'
hello \n world
$ echo 'hello' $'\n' 'world'
Hello
world

É possível utilizar variáveis para armazenar a saída de alguns programas colocando o símbolo “'”
(sinal de aspas simples) no comando a ser executado. Por exemplo, o script a seguir armazena a
saída do comando ls /etc em uma variável e a exibe.
#!/bin/bash
x='ls /etc'
echo "Our variable contains the following files"
echo $x

3.2.2. Variáveis regulares


As variáveis em scripts são declaradas de forma usual. Contudo, não são visíveis fora do escopo
do script a menos que sejam exportadas. Por exemplo, pode-se notar no script /etc/profile que
ele exporta a variável $PATH.

3.2.3. Variáveis posicionais da entrada de dados pelo usuário


As variáveis especiais $1 a $9 substituem argumentos no arquivo de script. Os argumentos são
as palavras informadas e separadas por espaços após o nome do script (assim como na
linguagem Java). Esta é uma das formas pelas quais o usuário pode entrar com dados. Vejamos o
script abaixo, salvo em um arquivo chamado argtest. A quantidade de argumentos passados

Sistema Operacional 15
JEDITM

para o script é representado pela variável $#.


#!/bin/bash
echo 'My first argument' $1
echo 'My second argument' $2
echo 'Number of arguments passed' $#

Abaixo estão alguns exemplos para o argtest.


$ ./argtest
My first argument
My second argument
Number of arguments passed 0

$ ./argtest hello
My first argument hello
My second argument
Number of arguments passed 1

$ ./argtest hello world star


My first argument hello
My second argument world
Number of arguments passed 3

Para variáveis posicionais acima de $9, o valor deverá ser colocado dentro de colchetes, por
exemplo ${10}, ${11} e assim por diante.

3.2.4. Comando read


Podemos obter entradas para o script através do comando read. Por exemplo, o script abaixo
solicita um nome:
#!/bin/bash
echo "Enter name"
read n
echo "Hello," $n "!"

3.2.5. Código de erro


Todos os comandos na maioria de sistemas UNIX possuem código de erro. O valor do código de
erro varia entre 0 e 255. Por convenção, um programa retorna 0 após uma execução com
sucesso. Qualquer outro valor informa que ocorreram problemas.
Em bash, para se obter o código de erro da última execução, utilizamos a variável $?. Por
exemplo, o script a seguir mostra o código de erro do comando ls após a procura por um arquivo
específico.
#!/bin/bash
ls $1
echo 'The errorcode of ls command is: ' $?

Abaixo temos a saída da execução (o arquivo foi salvo como lstest)


$ ./lstest
<list of files>
The errorcode of ls command is: 0
$ ./lstest nosuchfile.txt
No such file or directory
The errorcode of ls command is: 1

Especificamos o código de erro do script pelo comando exit. Por exemplo, no final do lstest,
retornamos o código de erro do script como sendo o código de erro do comando ls. Observe que
salvamos o valor da variável $? em outra variável, pois $? retorna o código de erro do último
comando executado. Se não fosse salva, seria retornado o código de erro do comando echo.
#!/bin/bash
ls $1

Sistema Operacional 16
JEDITM

output=$?
echo 'The errorcode of ls command is: ' $output
exit $output

3.2.6. Operadores
Os operadores aritméticos são usualmente +, -, * ou /. O operador % retorna o resto de uma
divisão. O resultado de uma expressão aritmética pode ser atribuída a uma variável usando o
comando let. Por exemplo:
$ x=5
$ let "x = $x + 1"
$ echo $x
6

Podemos utilizar também o operador ((<expressão>)), que avalia a expressão dentro dos
parênteses duplos. Note que o $ encontra-se antes do abrir parênteses para ocorrer a substituição
do valor.
$ x=$((5 + 5))
$ echo $x
10

A linguagem bash não utiliza números decimais e os transforma para valores do tipo Strings.

3.2.7. Estrutura condicional


Em bash, 0 é um valor verdadeiro e 1 representa falso. Isto reflete o valor de retorno de todos os
comandos no Solaris. Por convenção, um programa retorna 0 após uma execução com sucesso e
1 caso contrário.
Podemos testar uma determinada condição utilizando a sentença if. Diferente de outras
linguagens de programação, a condição lógica é envolvida por um conjunto de colchetes e o
comando é encerrado com fi. De modo similar a outras linguagens de programação, podemos ter
a declaração if, if-else e if-elseif-else. A seguir a sintaxe da sentença if-elseif-else:
if [condition] then
<statements>
elseif [condition] then
<statements>
else
<statements>
fi

A seguir mostramos algumas condições que podem fazer parte da estrutura condicional if.
Observe que "$a" e "$b" podem ser variáveis ou números.
Operador Definição
if ["$a" -eq "$b"] Igualdade entre números (igual a)
if ["$a" = "$b"] Igualdade entre Strings (igual a)
if ["$a -ne "$b"] Diferença entre números (não igual a)
if ["$a" != "$b"] Diferença entre Strings (não igual a)
-gt, -ge, -lt, -le Maior que, maior ou igual a, menor que, menor ou igual a
-n, -z Comparação de não nulo ou nulo. Por exemplo, if [-n "$1"] verifica se o
primeiro argumento não é nulo.
if [<cond1>] && [<cond2>] Operador E
if [<cond1>] || [<cond2>] Operador OU
if [!<cond>] Operador de negação. Por exemplo, if [! "$a" -gt "$b"] significa que estamos
perguntando se a negação do valor de a é maior que o valor de b
-f Verifica se um nome especifico de arquivo existe. Por exemplo if [-f
"hello.txt"] verifica se o arquivo hello.txt existe.
-r, -w, -x verifica se o arquivo possui privilégios de leitura, escrita e execução.
Figura 5: Condições para o comando if

Sistema Operacional 17
JEDITM

Por exemplo, retornando ao script myscript, podemos passar como argumento o nome do
arquivo que desejamos salvar, o conteúdo de /etc e /usr. Se nenhum argumento for passado,
isto é, $1 será nulo, então devemos retornar um erro.
#!/bin/bash
# this is my first bash script.
if [-n $1] then
echo 'Listing the contents of /etc' > $1
ls -l /etc >> $1
echo 'Listing the contents of /usr' >> $1
ls -l /usr >> $1
else
echo 'You should specify a parameter'
fi

3.2.8. Estruturas de repetição


Em linguagem bash, temos duas estruturas de repetição: for e while.
Estrutura de repetição for
A sintaxe do comando for é:
for <var> in <list> do
<statements>
done

O parâmetro list é uma lista de valores, no qual cada valor desta lista será repassado para a
variável var. Na primeira interação, var assume o valor do primeiro elemento da lista, na
segunda interação var assume o segundo elemento da lista e deste modo sucessivamente.
A seguir um exemplo do comando for que percorre os dias da semana.
for days in "Seg" "Ter" "Qua" "Qui" "Sex" "Sab" "Dom" do
echo $days
done

Estrutura de repetição while


Realiza a interação enquanto uma condição for verdadeira. A sintaxe para o comando while é:
while [condition] do
<statements>
done

Por exemplo, o código a seguir mostra o texto "hello world" um determinado número de vezes,
baseado no argumento passado.
#!/bin/bash
y=0
while [$y -lt $1] do
echo 'hello world'
let "y = y + 1"
done

Sistema Operacional 18
JEDITM

4. Comandos básicos de administração


4.1. Alterar para o super usuário: root

A administração do sistema só pode ser realizada pelo super usuário, isto é, o usuário root. Por
exemplo, a edição dos arquivos scripts de profile de configuração no diretório /etc só pode ser
feito pelo usuário root. Para acessar a conta root, devemos iniciar o sistema com este usuário ou
utilizar o comando su (substitute user ou switch user) para modificar o usuário.
$ su
Enter password: *******
#

Note que o sinal do prompt foi modificado para refletir que agora o usuário possui o status do
super usuário. O comando su também pode ser utilizado para alterar para qualquer usuário.
$ su bob
Enter password: *******
$ (<-- bob é o usuário corrente)

4.2. Administração de usuários

A seguir veremos alguns comandos que o usuário root pode utilizar para administrar os usuários
do sistema.
Adicionar novos usuários
Para adicionar novos usuários, utilizamos o comando useradd (user add). Por exemplo, a
instrução a seguir adiciona o usuário “alice”
# useradd -d /export/home/alice -m -s /bin/bash alice
As opções adicionais são:
● -d especifica um diretório home para o usuário. Deve ser configurado em /export/home
● -m o diretório será criado manualmente
● -s especifica o tipo shell que será utilizado pelo usuário alice, nesse caso o bash
Eliminar usuários
Para eliminar usuários, utilizamos o comando userdel (user delete). Por exemplo, para remover
o usuário “alice”.
# userdel -r alice

As opções adicionais são:


● -r se deve ocorrer a remoção do diretório do usuário
Trocar a senha dos usuários
Para trocar a senha do usuário utilizamos o comando passwd (password). Por exemplo, para
trocar a senha do usuário “alice” digite:
# passwd alice

Se nenhum parâmetro for passado para o comando, então é trocada a senha do usuário corrente.
Esta é a forma utilizada pelos usuários que não possuem privilégio de administradores do sistema
para trocar suas senhas.
Resumidamente, usamos:

● useradd <username> - para criar novo determinado usuário


● userdel <username> - para eliminar um determinado usuário
● passwd <username> - para modificar a senha de um determinado usuário.

Sistema Operacional 19
Módulo 8
Sistema Operacional

Lição 4
Processo no Solaris

Versão 1.0 - Mar/2008


JEDITM

1. Objetivos
Um processo pode ser definido como um programa em execução. Conforme já discutido
anteriormente, programas existem apenas como instruções em um arquivo até que sejam
executados pela CPU. Quando um programa é executado, as instruções são carregados para a
memória principal e, assim, torna-se um processo. Este capítulo discute como Solaris implementa
processos.
Ao final desta lição, o estudante será capaz de:
• Conhecer sobre os componentes de um processo
• Compreender como funciona a estrutura de processos no Solaris

Sistema Operacional 4
JEDITM

2. Componentes do Processo
Um serviço que está sendo executado no sistema Solaris é definido como processo. O sistema
operacional mantém o controle de múltiplos processos. Estes podem ser provenientes de um
usuário com múltiplos processos ou de múltiplos usuários de um modo simultâneo. Cada
processo, no sistema Solaris, recebe uma identificação única, denominada PID. Uma lista de
processos é armazenada em uma tabela de processos. A tabela de processo pode ser visualizada
através do comando ps (processes).
# ps -ef

UID PID PPID C STIME TTY TIME CMD


root 0 0 0 Nov 20 ? 0:11 sched
root 1 0 0 Nov 20 ? 0:02 /sbin/init
root 2 0 0 Nov 20 ? 0:00 pageout
root 3 0 0 Nov 20 ? 11:05 fsflush
root 215 1 0 Nov 20 ? 0:00 /usr/sbin/cron
root 7 1 0 Nov 20 ? 0:13 /lib/svc/bin/svc.startd
root 9 1 0 Nov 20 ? 0:28 /lib/svc/bin/svc.configd
root 124 1 0 Nov 20 ? 0:26 /usr/sbin/nscd
root 101 1 0 Nov 20 ? 0:00 /usr/lib/snmp/snmpdx -y -c /etc/snmp/conf
root 1840 1836 0 22:17:30 pts 0:00 sh

Um processo pode consistir de vários serviços (threads) de usuários. Um serviço é um trecho de


código em execução que roda dentro de um processo. Um processo tradicional (tal como um
programa em linguagem Java) teria um único serviço executando. Um processo em Solaris pode
suportar mais de um serviço executando simultaneamente (característica multithread).
Posteriormente discutiremos a forma de programar aplicações multithread.

Figura 1: Processos do Sistema Solaris

Cada processo do usuário está associado no núcleo do sistema Solaris através de um Light Weight
Process ou LWP. Um LWP permite que cada processo acesse as funções do Kernel de modo
independente de outros processos.
Cada LWP é executado por um Kernel Thread. O Kernel Thread é a menor unidade de
sincronização no Solaris. Em essência, processos, embora construídos com LWP, serão
executados no Kernel Threads.
De modo a otimizar o tempo de início de um processo, o Kernel mantém LWP's sempre prontos
para aceitar um novo processo.

Sistema Operacional 5
JEDITM

3. Estrutura de Processos
O Solaris armazena informações sobre cada processo em execução. A informação armazenada é
resumida pela figura a seguir.

Figura 2: Sistema de Permissões

● Source File - contém as informações sobre o arquivo executável deste processo.


● Memory – indica as páginas de memória onde reside o processo.
● Process Family Tree – mostra toda a estrutura de processos, o processo que criou este
(processo pai) e os processos filhos gerados por este.
● Credentials - indica o código e grupo id do usuário que iniciou o processo. Isto pode ser
usado para verificar a política de segurança do sistema.
● CPU Utilization - indica o tempo que o processo está executando, seja em modo usuário
ou executando no núcleo do sistema.
● Session – os processos também podem ser agrupados por sessões, por exemplo,
processos que pertencem aos usuários. Este campo é utilizado para armazenar
informações sobre a origem de sessão que este processo pertencente.
● PID – Process ID. Cada processo possui um identificador numérico único no sistema.
● Signal Support – os sinais são uma forma de um processo poder ser informado sobre
determinado evento, por exemplo, quando o sistema está para ser finalizado. Este é um
ponteiro para uma estrutura que indica como os sinais estão sendo manipulados.
● /proc Support - processos são representados no sistema de arquivos como arquivos no
diretório /proc. O nome do arquivo deste processo é o PID.
● Thread List - um ponteiro para uma estrutura que mantém a lista de serviços do usuário
que compõem este processo.
● Pooling - armazena informações sobre comportamento do processo, tais como, o estado
de monitoramento do processo e o uso de recursos.
● Resources – armazena informações sobre quais recursos estão em uso e quais serão
disponibilizados para o processo.
● Open File List - lista de arquivos que o processo está utilizando.

Sistema Operacional 6
Módulo 8
Sistema Operacional

Lição 5
Java Thread

Versão 1.0 - Mar/2008


JEDITM

1. Objetivos
Nesta lição discutiremos sobre Threads na linguagem Java. Veremos os comandos de
sincronização e soluções para os problemas de sincronização discutidos na lição anterior.
Ao final desta lição, o estudante será capaz de:
• Criar Threads em Java
• Fazer o uso da palavras-chave synchronized
• Utilizar os métodos wait e notify de java.lang.Object
• Manipular objetos de concorrência de alto nível

Sistema Operacional 4
JEDITM

2. Criando uma Thread em Java


Thread, em linguagem Java, é a capacidade de diferentes partes de seu programa poder ser
executada de modo simultâneo. Por exemplo, pode-se criar uma aplicação que aceite entradas de
diferentes usuários ao mesmo tempo, cada um deles manipulando uma thread. A maior parte de
aplicações de rede envolvem threads. Poderíamos necessitar criar uma thread que espere por
uma determinada entrada enquanto o programa gera um relatório de saída.
Há duas maneiras para se criar threads, por herança da classe thread ou pela implementação de
uma interface chamada Runnable. Ao ser iniciada, a thread executa as instruções contidas no
método run().

2.1. Estendendo a classe Thread (is a)

Criaremos uma thread que mostrará 500 vezes um determinado número passado como
argumento pelo construtor da classe.
class MyThread extends Thread {
private int i;
MyThread(int i) {
this.i = i;
}
public void run() {
for (int ctr=0; ctr < 500; ctr++) {
System.out.print(i);
}
}
}

Primeiramente veremos a diferença entre uma execução paralela e não-paralela. Vejamos a


seguinte classe:
class MyThreadDemo {
public static void main(String args[]) {
MyThread t1 = new MyThread(1);
MyThread t2 = new MyThread(2);
MyThread t3 = new MyThread(3);
t1.run();
t2.run();
t3.run();
System.out.print("Main ends");
}
}

Conforme visto, após a execução da classe MyThreadDemo, foi chamado o método run do objeto
t1 que mostra 500 números 1. Em seguida, a chamada ao método run do objeto t2 que mostra
500 números 2. Por fim, a chamada ao método run do objeto t3 que mostra 500 números 3. O
texto “Main ends” aparece na parte final, como última mensagem da execução desta classe. Não
ocorre nenhuma execução simultânea, isto é o que denominamos de execução não-paralela,
conforme pode ser visto na figura a seguir.

Sistema Operacional 5
JEDITM

Figura 1: Execução não-paralela

Para executar através das threads, chamamos o método start() em vez de chamar diretamente o
método run().
class MyThreadDemo {
public static void main(String args[]) {
MyThread t1 = new MyThread(1);
MyThread t2 = new MyThread(2);
MyThread t3 = new MyThread(3);
t1.start();
t2.start();
t3.start();
System.out.print("Main ends");
}
}

Como podemos ver ao executar esta classe, os objetos threads t1, t2 e t3 agora estão rodando
de modo simultâneo e os números 1, 2 e 3 são mostrados intercaladamente. Interessante
observar o aparecimento do texto "Main ends" no meio da seqüência de saída. Isso indica que o
método main continua sendo executado enquanto as threads estão rodando.

Figura 2: Execução das Threads

Sistema Operacional 6
JEDITM

2.2. Implementando a interface Runnable (has a)

Outra maneira para se obter os benefícios de uma thread é implementar a interface runnable. Isto
pode ser útil se sua classe necessita de herança. Lembre-se que a linguagem Java não permite
herança múltipla. Modificamos nossa classe MyThread para implementar a interface runnable.
Esta interface possui um único método abstrato que, obrigatoriamente, deve ser implementar, o
método public void run().
class MyThread implements Runnable {
... <thread body is mostly the same>
}

A diferença principal está na construção do objeto da classe Thread que possuem um objeto da
classe MyThread.
Thread t1 = new Thread(new MyThread(1));

Um objeto de MyThread agora é passado como um argumento para o construtor de um objeto


Thread.

2.3. Pausar threads

Threads podem ser pausadas pelo método sleep(). Por exemplo, para interromper a execução de
MyThread por meio segundo antes de imprimir o próximo número, adiciona-se as seguintes linhas
de código:
for (int ctr=0; ctr < 500; ctr++) {
System.out.print(i);
try {
Thread.sleep(500); // 500 milissegundos
} catch (InterruptedException e) { }
}

O método sleep(long time) é estático dentro da classe Thead e pode ser invocado por qualquer
thread, inclusive a do método principal. Por exemplo, se quisermos pausar por um segundo o
processo antes de iniciar o objeto t2 no nosso método principal, podemos ter:
public static void main(String args[]) {
...
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) { }
t2.start();
...
}

2.4. Concordância de Threads

É possível interromper uma thread até que outra termine sua execução, chamamos, para isso, o
método join(). Por exemplo, para fazer a thread principal parar de rodar até que o objeto t1
termine, podemos escrever:
public static void main(String args[]) {
...
t1.start();
try {
t1.join();
} catch (InterruptedException e) { }
t2.start();
...
}

Isto determina que a thread t1 termine de executar antes de iniciar a thread t2.

Sistema Operacional 7
JEDITM

3. Palavra-chave Synchronized
Implementaremos uma solução para um problema de Sessão Crítica usando Java. Lembre-se
que somente um processo pode entrar na sessão crítica do sistema, todos os outros processos
devem aguardar. Nenhum chaveamento de contexto é permitido na sessão crítica.
Vejamos o seguinte problema: em vez de mostrar uma contínua sucessão de números, MyThread
chama o método print10() na classe MyPrinter. O método print10() imprime números contínuos
em uma única linha antes de iniciar uma nova linha.
Nossa meta é ter estes 10 números contínuos impressos sem que ocorra nenhum chaveamento de
contexto. Em outras palavras, nossa saída deve ser:
...
1111111111
1111111111
2222222222
3333333333
1111111111
...

3.1. Definir a solução

A seguir, definimos a classe MyPrinter que possui o método print10():


class MyPrinter {
public void print10(int value) {
for (int i = 0; i < 10; i++) {
System.out.print(value);
}
System.out.println(""); // uma nova linha após 10 números
}
}

Em vez de imprimir os números diretamente, utilizamos o método print10() na classe MyThread,


como mostrado a seguir:
class MyThread extends Thread {
int i;
MyPrinter p;
MyThread(int i) {
this.i = i;
p = new MyPrinter();
}
public void run() {
for (int ctr=0; ctr < 500; ctr++) {
p.print10(i);
}
}
}

Visualizamos a saída de uma única thread.


class MyThreadDemo {
public static void main(String args[]) {
MyThread t1 = new MyThread(1);
// MyThread t2 = new MyThread(2);
// MyThread t3 = new MyThread(3);
t1.start();
// t2.start();
// t3.start();
System.out.print("Main ends");
}
}

Ao ser executada a classe MyThread, teremos:

Sistema Operacional 8
JEDITM

> java MyThreadDemo


1111111111
1111111111
1111111111
1111111111
1111111111
...

Contudo, ao rodar as outras threads, podemos ter uma saída semelhante a esta:

> java MyThreadDemo


1111111111
111112222222
1111
22233333332
...

Não alçamos nosso objetivo de imprimir os 10 números de forma consecutiva quando todas as
threads executam o método print10 ao mesmo tempo. O resultado final é que teremos mais de
um número aparecendo em uma única linha.

Não deve haver um interruptor do contexto ao mostrarmos os 10 números consecutivos. Assim,


uma solução para este problema é também uma solução para o problema de sessão crítica.

3.2. Monitores em Java

Java usa uma construção de monitor para resolver o problema de sessão crítica. Somente uma
única thread pode funcionar dentro de um monitor. Para alternar um objeto em um monitor,
colocamos a palavra-chave synchronized sobre as assinaturas dos métodos. Somente uma única
thread pode executar em um método sincronizado dentro de um objeto.
class MyPrinter {
public synchronized void print10(int value) {
for (int i = 0; i < 10; i++) {
System.out.print(value);
}
System.out.println(""); // nova linha depois de 10 números
}
}

Entretanto, se transformarmos nosso método print10() em um método sincronizado, ainda assim


este não trabalhará como esperado. Para encontrar um modo de fazê-lo funcionar corretamente,
necessitamos descobrir como a palavra-chave synchronized trabalha.
Cada objeto em Java possui um bloqueio. Quando uma thread é executada em um método
sincronizado, o objeto é bloqueado. Ao obter um bloqueio a thread começa a funcionar no método
sincronizado, caso contrário, espera até que a thread que possui o bloqueio seja liberada.

Figura 3: Método sincronizado

Nosso exemplo não trabalha corretamente, pois cada thread possui uma cópia própria do objeto
MyPrinter.

Sistema Operacional 9
JEDITM

class MyThread extends Thread {


int i;
MyPrinter p;
MyThread(int i) {
this.i = i;
p = new MyPrinter(); // cada MyThread cria sua MyPrinter!
}
public void run() {
for (int ctr=0; ctr < 500; ctr++) {
p.print10(i);
}
}
}

Como podemos ver na figura a seguir, cada thread possui seu próprio bloqueio, deste modo, todas
funcionam através de um método sincronizado:

Figura 4: Diagrama dos objetos

A solução é um único objeto MyPrinter para compartilhamento por todas as threads.

Figura 5: Solução com um único objeto

Uma maneira de visualizar todos os objetos é através de portas. Uma thread tenta ver se uma

Sistema Operacional 10
JEDITM

porta está aberta. Uma vez que a thread atravessa esta porta, esta é travada. Nenhuma outra
thread pode entrar nesta porta porque a primeira a travou por dentro. As outras thread podem
entrar se a thread que está dentro destravar a porta e sair. Um alinhamento ocorrerá somente se
todos os objetos tiverem uma única porta na sessão crítica.
Reinterando, somente uma única thread pode rodar no método sincronizado de um objeto. Se um
objeto tiver um método sincronizado e um método não-sincronizado, somente uma thread pode
rodar no método sincronizado e múltiplas threads podem rodar no método não-sincronizado.
Threads a serem sincronizadas devem compartilhar o mesmo objeto monitor. Ao executar agora a
classe MyThreadDemo, o resultado é mostrado corretamente através dos métodos sincronizados.

3.3. Blocos sincronizados

Além dos métodos sincronizados, Java permite blocos sincronizados. Podemos especificar um
bloqueio intrínseco. Os blocos sincronizados permitem a flexibilidade de que o bloqueio intrínseco
venha de outro objeto ao invés do objeto atual. Também podemos ter partes de um método que
possui um bloqueio diferente de outro.
Considere a seguinte classe MyPrinter:
class MyPrinter {
Object lock1 = new Object();
Object lock2 = new Object();
public void print10(int value) {
synchronized(lock1) {
for (int i = 0; i < 10; i++) {
System.out.print(value);
}
System.out.println(""); // nova linha após 10 números
}
}
public int squareMe(int i) {
synchronized (lock2) {
return i * i;
}
}
}

Somente uma thread pode rodar dentro do bloco sincronizado dentro dos métodos print10() e
squareMe(). Entretanto, duas threads diferentes podem rodar em ambos os blocos sincronizados
ao mesmo tempo pois usamos bloqueios diferentes para cada um deles (porque não interferem
realmente um com o outro). Os blocos sincronizados permitem também que outra parte de um
método sejam executadas em paralelo com outras, como mostrado na figura a seguir:

Figura 6: Diagrama de Sincronização

Sistema Operacional 11
JEDITM

4. Métodos wait e notify


Neste ponto, já é possível utilizar algumas implementações para problemas relacionados a
sincronização. Discutiremos o problema do produtor-consumidor. O produtor irá armazenar
em um array compartilhado um valor inteiro gerado de forma aleatória que será obtido pelo
consumidor. Para propósitos de discussão, será permitido que este array armazene um número
muito grande de números inteiros. O produtor irá produzir, desta forma, um total de 100
números inteiros.

4.1. Blocos sincronizados

Primeiramente, criamos a classe para que possa ser realizado o armazenamento do array. Não
será aceito que os métodos insertValue() e getValue() sejam executados simultaneamente.
Assim, estes métodos serão sincronizados (terão o modificador synchronized). Tanto o produtor
quanto o consumidor estarão tratando do mesmo objeto.
class SharedVars {
int array[] = new int[100];
int top;

// Método responsável pela atribuição do valor


public synchronized void insertValue(int value) {
if (top < array.length) {
array[top] = value;
top++;
}
}
// Método responsável por recuperar o valor
public synchronized int getValue() {
if (top > 0) {
top--;
return array[top];
} else {
return -1;
}
}
}

A seguir, será definida a classe que representa o produtor.


class Producer extends Thread {
SharedVars sv;
Producer(SharedVars sv) {
// Referência de Produtor para os objetos SharedVars
this.sv = sv;
}
public void run() {
for (int i = 0; i < 100; i++) {
// Atribui um número aleatório entre 0 a 200
int value = (int)(Math.random() * 200);
System.out.println("Producer inserts " + value);
sv.insertValue(value);
try {
// Espera um determinado tempo antes de realizar uma nova inserção
Thread.sleep((int)(Math.random() * 10000));
} catch (InterruptedException e) { }
}
}
}

E a classe que representa o consumidor:


class Consumer extends Thread {
SharedVars sv;
Consumer(SharedVars sv) {

Sistema Operacional 12
JEDITM

// Referência de Consumidor para os objetos SharedVars


this.sv = sv;
}
public void run() {
for (int i = 0; i < 100; i++) {
int value = sv.getValue();
System.out.println("Consumer got:" + value);
try {
// Espera um tempo antes antes de obter o valor novamente
Thread.sleep((int)(Math.random() * 10000));
}catch (InterruptedException e) { }
}
}
}

Para finalizar, temos a seguinte classe principal:


class ProducerConsumerDemo {
public static void main(String args[]) {
SharedVars sv = new SharedVars();
Producer p = new Producer(sv);
Consumer c = new Consumer(sv);
p.start();
c.start();
}
}

A execução desta classe pode gerar a seguinte saída:


Producer inserts value: 15
Consumer got: 15
Consumer got: -1
Producer inserts value: 50
Producer inserts value: 75
Consumer got: 75
...

É importante notar que, caso seja realizada uma tentativa de recuperar um valor em uma posição
no array que não possua nenhum valor, será obtido o valor -1. É possível notar isto se o
produtor gerar um novo valor a cada 10 segundos e caso o consumidor recuperar este valor a
cada 5 segundos. Em algum ponto o consumidor irá tentar realizar o acesso a uma posição vazia
do array.
Então, é melhor que o consumidor espere até que o produtor gere algum valor no lugar de
obter o valor -1.

4.2. método wait()

O método wait() é definido na classe java.lang.Object e é herdado por todos os objetos. A


chamada a este método fará com que a thread suspenda suas atividades. Entretanto, se uma
thread realizada uma chamada a este método, esta possuir um bloqueio para o objeto que está
realizando esta chamada. Se for realizada uma chamada na forma this.wait() este deve estar em
um bloco sincronizado, da seguinte forma:
synchronized(this);

De modo semelhante, outros métodos que realizam uma interrupção da execução de uma thread,
tais como, join() e sleep(), o método wait() deve estar protegido por um bloco try-catch que
controle a exceção tipo InterruptedException.
O código a seguir mostra a modificação para o método getValue() na classe SharedVars:
// chamado pela thread consumidor
public int getValue() {
synchronized(this) {
if (top <= 0) {

Sistema Operacional 13
JEDITM

try {
this.wait();
} catch (InterruptedException e) { }
}
top--;
return array[top];
}}

Figura 7: Diagrama de Sincronização

Figura 8: Diagrama de Sincronização

4.3. método notify()

Todas as threads que invocam o método wait() em um objeto são posicionadas em um


agrupamento de threads. O retorno de uma thread interrompida é feito quando outra thread que
está executando chama o método notify() do mesmo objeto. Quando isso é feito a primeira
thread do pool retoma suas atividades.
Por exemplo, após o produtor realizar uma atribuição no array, pode notificar o consumidor
pela chamada ao método notify() no objeto SharedVars. Lembrando de que tanto produtor
quanto consumidor possuem uma mesma referência para o objeto SharedVars. Ou seja, o
produtor invoca o método notify() a partir do método insertValue() e informa ao consumidor
que se encontra em espera através do objeto SharedVars.

Sistema Operacional 14
JEDITM

A seguir, teremos o novo método insertValue():


// Chamado pela Produtor
public void insertValue(int value) {
synchronized(this) {
if (top < array.length) {
array[top] = value;
top++;
if (top == 1) { // Se o array estiver vazio
this.notify();
}
}
}
}

Figura 9: Diagrama de Sincronização

Figura 10: Diagrama de Sincronização

Ao executar novamente o exemplo, podemos observar que, mesmo se produtor estiver lento, o
consumidor será obrigado a esperar até que seja gerado um valor antes de obtê-lo.
Devemos considerar também que podemos ter um array lotado, desta forma, será a vez do
produtor esperar até que consumidor obtenha alguns valores antes de gerar novos valores.
Realize esta implementação como uma forma de exercício.

Sistema Operacional 15
JEDITM

5. Alto Nível de Concorrência dos Objetos


Os métodos discutidos até o momento, foram inseridos desde as primeiras versões da linguagem
Java. Entretanto, com a chegada da Java 5.0 novas classes foram adicionadas ao pacote
java.util.concurrent para auxiliar na criação e manipulação de threads.

5.1. Bloquear Objetos

Qualquer objeto em linguagem Java pode ser usado para efetuar um bloqueio durante uma
sincronização. Porém, o pacote java.util.concurrent.locks define classes que possuem adicionais
características para proceder este bloqueio. A interface básica deste pacote é a Lock.
Um objeto que implementa Lock só pode ser utilizado por uma única thread de cada vez. Para
efetuar um bloqueio, a thread deve chamar o método lock(). Para liberar este bloqueio, deve
chamar o método unlock(). Em vez de fechar implicitamente e destravar um bloco sincronizado ou
método, um bloqueio explícito provê a flexibilidade de chamar manualmente os métodos lock() e
unlock().
Um bloqueio poderá também chamar um método tryLock(). Este método retorna um valor lógico.
Verdadeiro se conseguiria realizar um bloqueio, ou falso se não. O programa não irá bloquear
enquanto tryLock() não obtiver sucesso, diferente do método lock() e do comportamento de uma
thread que tenta entrar em um bloco ou método sincronizado. Além disso, um polimórfico do
método tryLock() permite especificar a quantidade de tempo que uma thread deve esperar para
estabelecer um bloqueio antes de retornar verdadeiro ou falso.
A seguir teremos uma classe de implementação para MyPrinter (em nosso exemplo de
MyThread2) usando bloqueios. Observe que não existe nenhum desbloqueio implícito, isso
acontece quando o bloco sincronizado termina. Assim, qualquer chamada aos métodos lock() ou
tryLock() deve ser seguida por uma chamada ao método unlock().
import java.util.concurrent.locks.*;

class MyPrinter {
// ReentrantLock é uma implementação da interface lock
final Lock l = new ReentrantLock();

public void print10(int value) {


boolean gotLock = false;
while (!gotLock) {
gotLock = l.tryLock();
if (!gotLock) {
System.out.println("Unable to get lock, try again next time");
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) { }
}
}
for (int i = 0; i < 10; i++) {
System.out.print(value);
}
System.out.println(""); // Uma nova linha após 10 números
l.unlock();
}
}

Os métodos wait() e notify() só podem ser chamados no bloqueio intrínseco de um método ou


bloco sincronizado. Para fazer isso, podemos usar seu método newCondition() que devolve um
objeto do tipo Condition. Um objeto Condition tem um método denominado await() que é o
equivalente a chamada ao método wait(). Para retornar uma thread que está em espera o objeto
Condition tem um método denominado signal() que é o equivalente a uma chamada ao método
notify().
Por exemplo, adicionaremos as seguintes linhas a classe SharedVars usando bloqueios e

Sistema Operacional 16
JEDITM

Conditions.
import java.util.concurrent.locks.*;
class SharedVars {
final Lock l = new ReentrantLock();
final Condition emptyCondition = l.newCondition();
int array[] = new int[100];
int top;

// método para inserir um novo valor


public void insertValue(int value) {
l.lock() // realiza o bloqueio
if (top < array.length) {
array[top] = value;
top++;
if (top == 1) {
emptyCondition.signal();
}
}
l.unlock();
}
// método para obter um valor
public int getValue() {
l.lock();
if (top <= 0) {
try {
emptyCondition.await();
} catch (InterruptedException e) { }
}
top--;
l.unlock();
return array[top];
}
}

Existem outras classes no pacote java.util.concurrent.locks. Uma delas é a classe


ReentrantReadWriteLock que classifica os métodos que retornam os bloqueios. Estes bloqueios
podem ser usados como uma solução ao problema produtor-consumidor.

5.2. Executores

Ao criamos uma thread, declaramos manualmente quando esta thread será executada. Por
exemplo, a thread seguinte:
class MyThread implements Runnable { ... }

Manualmente, iniciamos a thread através das seguintes instruções:


Thread t1 = new Thread(mt1);
t1.start();

Uma classe que implementa a interface Executor pode iniciar automaticamente uma thread. Por
exemplo, em vez das instruções anteriores, teríamos:
MyThread mt1 = new MyThread();
Executor e = new ThreadPoolExecutor(10); // accepts max 10 threads
e.execute(mt1);
e.execute(otherThreads);

Nosso exemplo utiliza uma classe denominada ThreadPoolExecutor. Esta classe executa nossas
threads inseridas (pelo método execute()) através de um pool de threads em execução. Estas
threads em execução reduz a necessidade do overhead para a criação de uma thread, uma
threads é passada como um parâmetro para uma linha que já se encontra em execução.
Um parâmetro especificado no construtor indica quantas threads podem rodar no Executor de

Sistema Operacional 17
JEDITM

modo simultâneo. Se um Executor não for usado, então as threads são executadas no momento
em que são iniciadas. Se houver muitas threads executando simultaneamente, o sistema pode
chegar a não suportar e todas as threads serão interrompidas. Um executor permite que um
número de máximo de threads em execução seja especificado e permite que o sistema controle
este número, criando um agrupamento daquelas que não pode.
Métodos adicionais indicam como novas threads devem ser colocadas no pool ou quando terminar
as threads que estão inativas. Uma subclasse de ThreadPoolExecutor, é a
ScheduledThreadPoolExecutor que possui métodos para permitir que uma thread em particular
inicie em um determinado momento, e seja repetida periodicamente.

5.3. Coleções Concorrentes

O pacote java.util.concurrent inclui uma coleção de classes que podem ser usadas com programas
multithread, sem termos que nos preocupar com problemas de sincronização. Por exemplo, a
classe de BlockingQueue cria uma FIFO (first in – first out) que bloqueia automaticamente
threads que tentam entrar em um pool cheio ou retorna valores vazio. A execução retoma
automaticamente quando outra thread remove um valor em um pool cheio ou acrescenta novos
dados a um vazio.

5.4. Variáveis Atômicas

Como vimos no problema produtor-consumidor, as instruções:


array[top] = <new value>;
top++;

Devem ser executadas sem intervalos. Este processo é chamado de execução atômica. Cada linha
de instrução deve ser executada como um todo. Porém, nem mesmo únicas instruções são
atômicas. Considere a seguinte instrução:
top++;

Esta instrução está dividida em três partes quando é executada na CPU:


1. Obtém o valor de top
2. Adiciona 1 para o valor
3. Salva o valor no endereço de top
Estas três instruções devem ser executadas sem deixar que nenhuma outra instrução seja
executada. Um modo para assegurarmos isso, é proteger estas instruções por um método
sincronizado.
class SharedVars {
int top;
public synchronized inctop() { top++; }
}

A maioria das outras operações aritméticas não são atômicas, isso significa que cada operação
matemática deve lidar com variáveis compartilhadas, e tem que estar em um bloco sincronizado
para serem seguras. Entretanto, isto pode incluir instruções para tarefas compartilhadas. Para
evitar todo este trabalho, o pacote java.util.concurrent.atomic fornece classes preparadas para
trabalhar de forma segura em ambiente multithread, ou seja, são thread-safe.
Por exemplo, a classe AtomicInteger fornece os seguintes métodos:
● void incrementAndGet() – executa atomicamente uma operação de incremento
● void addAndGet(int delta) – acrescenta um valor delta a AtomicInteger
Também existem implementações, tais como, um array de tipos inteiros ou um objeto de
referência. A maioria das classes em java.util é thread-safe.

Sistema Operacional 18
Módulo 8
Sistema Operacional

Lição 6
Observabilidade

Versão 1.0 - Mar/2008


JEDITM

1. Objetivos
A Sun fornece freqüentemente aos desenvolvedores acesso imediato às novas tecnologias, assim
como ferramentas para rastreabilidade. Estas permitem que os usuários possam analisar
profundamente os detalhes da implementação interna do software, do núcleo e do usuário.
Neste ponto já discutimos os conceitos teóricos do sistema operacional. Nesta lição veremos
comandos que torna possível observar como estes conceitos são executados no sistema
operacional. Discutiremos alguns comandos básicos do poderoso conjunto de ferramentas
DTrace.
Ao final desta lição, o estudante será capaz de:
• Trabalhar com o conjunto de ferramentas DTrace
• Realizar comandos no Solaris que permitem ver em ação: CPU, processos, memória e E/S

Sistema Operacional 4
JEDITM

2. DTrace
DTrace é uma poderoso conjunto de ferramentas que foi incluído no Solaris 10 e permite
visualizar o comportamento atual de um sistema durante sua execução. Dtrace foi desenvolvido
como uma ferramenta para diagnóstico do sistema. Antes do DTrace, para tentar ver como
determinado programa executava, era necessário inserir diversas instruções na saída de seu
código. Essa prática é difícil de ser realizada quando necessitamos conhecer como um programa
está em área de memória ou quão freqüentemente o arquivo executável chama uma determinada
função do núcleo do sistema, ou mesmo como o programa acessa o disco rígido. Isso pode
envolver realmente a edição do código fonte do próprio sistema operacional.
Para evitar esses problemas, DTrace usa probes (sondas) que estão conectados dentro do
Sistema Operacional Solaris. Estes probes mantêm um registro de tudo, desde processos em uso
de memória a quão frequentemente interrupções são chamadas pelo programa e a dados que são
gravados no disco. Existem mais de 30.000 probes no Solaris. Probes não interferem na
execução. Habilitar certos probes com DTrace nos permite um olhar dentro do núcleo do sistema
operacional enquanto este está sendo executado.

2.1. Probes BEGIN e END

Iniciaremos o aprendizado com um comando muito simples. Vamos chamar o DTrace com a
opção -n:
# dtrace -n BEGIN

dtrace: description 'BEGIN' matched 1 probe


CPU ID FUNCTION:NAME
0 1 :BEGIN

O probe BEGIN é iniciado quando um processo é iniciado. É necessário pressionar CTRL+C para
sair do DTrace. Agora consideraremos outro probe, chamado END, que é ativado sempre que um
processo finaliza. Quando é executado, o probe END não aparece imediatamente. Pois o processo
ainda não acabou.
É necessário parar o DTrace para ver a ativação do probe END pressionando CTRL+C:
# dtrace -n BEGIN -n END

dtrace: description 'BEGIN' matched 1 probe


dtrace: description 'END' matched 1 probe
CPU ID FUNCTION:NAME
2 1 :BEGIN
^C
2 2 :END

Até agora, vimos apenas probes que são ativados quando DTrace começa ou termina, sem
nenhuma funcionalidade adicional. Escreveremos um script para que o DTrace possa executar
quando o probe BEGIN ou END for iniciado.

2.2. Alô mundo

DTrace aceita um script escrito na linguagem D, similar à linguagem C ou C++. A sintaxe básica
de um script em D se parece com:
<probe description>
/<predicate>/
{
<código>
}

A sessão será executada quando o probe listado na descrição for ativado e o predicado (que é
uma expressão condicional) for verdadeiro. Com essa idéia, considere o script a seguir para

Sistema Operacional 5
JEDITM

DTrace, o qual será salvo com o nome de alo.d. Podemos ver que a instrução “Alô Mundo” será
executada quando o probe BEGIN iniciar, e a instrução “Adeus” na ativação do probe END.
BEGIN
{
trace("Alô Mundo!");
}

END
{
trace("Adeus!");
}

Executaremos agora o DTrace utilizando a opção -s para informar o nome do arquivo script que
iremos rastrear.
# dtrace -s alo.d
dtrace: script 'alo.d' matched 2 probes
CPU ID FUNCTION:NAME
0 1 :BEGIN Alô Mundo!
^C
2 2 :END Adeus!

Note como modificamos o comportamento do DTrace para mostrar seu início e fim. Rastrear um
programa significava ter que modificá-lo, editando-o para inserir instruções de saída. Com
DTrace, podemos rastrear um programa sem ter que modificar isso simplesmente utilizando o
probe correto.

2.3. Organização dos probes

Podemos listar todos os probes através da opção -l. Existem mais de 30.000 probes disponíveis
no Solaris.
# dtrace -l
ID PROVEDOR MÓDULO NOME DA FUNÇÃO
1 dtrace BEGIN
2 dtrace END
3 dtrace ERROR
4 vminfo fasttrap fasttrap_uwrite softlock
5 vminfo fasttrap fasttrap_uread softlock
6 nfsmapid229 nfsmapid check_domain daemon-domain

Probes são identificados pelos ID e pelo seu nome, que é composto pelos seguintes valores
separados por dois-pontos:
● Provedor
● Módulo
● Função
● Nome
Por exemplo, probe de ID 5, pode ser também referido como:
vminfo:fasttrap:fasttrap_uread:softlock

Provedores são módulos do núcleo que contêm o código dos probes. Módulo e Função indicam
o módulo do núcleo, biblioteca do usuário ou nome de função que o probe é projetado para
rastrear. A última parte e o Nome do probe é a descrição do que é designado a fazer.
Aqui estão alguns exemplos de Provedores:
● DTrace – probes relacionados ao DTrace
● Lockstat – probes para sincronização em nível de núcleo do sistema
● Profile – probes que executam todos os intervalos especificados que podem ser usados

Sistema Operacional 6
JEDITM

para obter uma amostra do sistema em execução


● Syscall – probes para cada entrada e retorno de cada chamada de sistema no programa
● VMinfo – probes de memória virtual
● Proc – probes de processo, threads e criação e finalização de LWP
● Sched – probes de agendamento de processo
Para especificar um probe em um script D, simplesmente coloque seu nome completo na parte de
descrição do probe. Caso não seja preeenchida partes da descrição, o código iniciará todos os
probes que coincidam.
Por exemplo, o script a seguir roda o código sempre que uma página for carregada da memória
virtual para a memória principal, bem como o código que irá executar em qualquer probe provido
pelo syscall.
vminfo:genunix:pageio_setup:pgin
{
trace("Pagina em que ocorreu");
}
syscall:::
{
trace("Rodando um probe syscall");
}

2.4. Variáveis

Variáveis no DTrace não possuem tipos de dados, isso significa que este é determinado durante a
primeira atribuição de valor. Por exemplo, i = 0 cria uma variável, do tipo inteiro, denominada i
com o valor 0, enquanto msg = "Alô" cria uma variável denominada msg do tipo String com o
valor "Alô".
O dtrace:::BEGIN é comumente usado para inicializar variáveis e são acessíveis somente
enquanto o processo estiver rodando.
Para mostrar as variáveis em ação, o script DTrace a seguir, chamado countdown.d, mostra o
funcionamento de variáveis, e o probe profile:::tick-1sec, que roda a cada segundo.
dtrace:::BEGIN
{
ctr = 10;
}

profile:::tick-1sec
{
trace(ctr);
ctr--;
}

dtrace:::END
{
trace("Obrigado por usar meu programa.");
}

A saída de countdown.d é a seguinte:


# dtrace -s countdown.d
dtrace: script 'countdown.d' find 3 probes
CPU ID FUNCTION:NAME
2 41214 :tick-1sec 10
2 41214 :tick-1sec 9
2 41214 :tick-1sec 8
2 41214 :tick-1sec 7
2 41214 :tick-1sec 6
2 41214 :tick-1sec 5
2 41214 :tick-1sec 4

Sistema Operacional 7
JEDITM

2 41214 :tick-1sec 3
2 41214 :tick-1sec 2
2 41214 :tick-1sec 1
2 41214 :tick-1sec 0
2 41214 :tick-1sec -1
2 41214 :tick-1sec -2
^C
2 2 :END Obrigado por usar meu programa

2.5. Predicados

Predicados agem como uma instrução para um script. O código do script só é executado
juntamente com o probe e o predicado é então comparado.
Realizamos a seguinte modificação no script countdown.d:
dtrace:::BEGIN
{
ctr = 10;
}

profile:::tick-1sec
/ ctr > 0 /
{
trace(ctr);
ctr--;
}

profile:::tick-1sec
/ ctr == 0/
{
trace(ctr);
exit(0);
}

dtrace:::END
{
trace("O tempo acabou!");
}

Nosso primeiro profile:::tick-1sec executará quando a condição "ctr maior que 0" for verdadeira,
enquanto o segundo só irá rodar quando a condição "ctr igual a 0" for verdadeira. A função exit()
finaliza o trace.
# dtrace -s countdown.d
dtrace: script 'countdown.d' encontrou 4 probes
CPU ID FUNCTION:NAME
2 41214 :tick-1sec 10
2 41214 :tick-1sec 9
2 41214 :tick-1sec 8
2 41214 :tick-1sec 7

2.6. printf

O comando printf mostra o valor de um determinado atributo na tela. Sua sintaxe é semelhante
à da linguagem C/C++. Para mostrar uma lista de strings utilizando a função printf(), utilizamos
uma vírgula para separá-las:
printf("O tempo restante é %d segs. O que posso dizer é %s", ctr, msg);

O formato String é apresentado na tela através da substituição dos atributos denotados pelo
símbolo %, seguido de um caractere que identifica o tipo de atributo.
● %d significa que um valor inteiro será exibido naquela posição.

Sistema Operacional 8
JEDITM

● %s significa que uma String será exibida naquela posição.


● %f significa que um número decimal será exibido naquela posição.
● %x exibe números inteiros como caracteres em hexadecimal.
● %% imprime um percentual na posição.
Modificamos o script countdown.d para exemplificar o uso da função printf():
dtrace:::BEGIN
{
ctr = 10;
msg = "Adeus"
}

profile:::tick-1sec
/ ctr > 0 /
{
printf("Tempo restante de %d segs\n",ctr);
ctr--;
}

profile:::tick-1sec
/ ctr == 0/
{
printf("Tempo restante de %d segs\n", ctr);
exit(0);
}

dtrace:::END
{
printf("Tempo acabando! %d segs. Tudo que posso dizer é %s\n", ctr, msg);
}

Para melhorar a visualização, podemos utilizar a opção -q para listar as strings.


# dtrace -q -s countdown.d
Tempo restante 10 segs
Tempo restante 9 segs
Tempo restante 8 segs
Tempo restante 7 segs
Tempo restante 6 segs
Tempo restante 5 segs
Tempo restante 4 segs
Tempo restante 3 segs
Tempo restante 2 segs
Tempo restante 1 segs
Tempo restante 0 segs
Tempo acabado! 0 segs. Tudo que posso dizer é Adeus

2.7. Scripts no DTrace – parte 1

Considere o seguinte script salvo com o nome syscall.d. Syscall:::entry é o conjunto de probes
disparados sempre que uma aplicação chama uma função do núcleo do sistema. Execname, PID
e probefunc são atributos no DTrace. Execname é o nome do processo que originou este
probe. PID é a identificação do processo. Probefunc é o nome da função do probe, indicando
qual função do sistema foi chamada.
Adicionaremos um predicado para considerar apenas os processos em bash.
syscall:::entry
/ execname == “bash” /
{
printf("%s(%d) chamado %s\n", execname, pid, probefunc);
}

Executando o script, é possível que não ocorre nenhuma saída inicial. Isto porque o script bash

Sistema Operacional 9
JEDITM

atual que está em execução é o DTrace. Para se obter uma saída inicie outra janela do terminal
e execute alguns comandos em bash. Como por exemplo:
# dtrace -q -s syscall.d
Bash(1035) chamado read
Bash(1035) chamado read
Bash(1035) chamado write
Bash(1035) chamado read
Bash(1091) chamado getpid
Bash(1091) chamado lwp_self
Bash(1091) chamado lwp_sigmask
Bash(1091) chamado getpid
Bash(1091) chamado schedctl
...

Para investigar essas chamadas de sistema ao núcleo e descobrir como o sistema funciona, uma
boa dica é verificar o código-fonte dessas funções.

2.8. Funções agregadas

Talvez seja necessário saber quais funções foram chamadas, mas como saber quantas vezes cada
função foi chamada? Podemos utilizar funções agregadas para analisar os dados.
Para ilustrar um exemplo, modificamos o script syscall.d para produzir um total de probefunc
que foram chamadas pelo bash.
syscal:::entry
/execname == "bash"/
{
printf("%s(%d) chamado %s\n", execname, pid, probefunc);
@[probefunc] = count();
}
A execução prossegue normal, mas no final do script obtemos a seguinte saída, que mostra
quantas vezes cada função do sistema foi chamada.
^C
exece 1
fork1 1
lwp_self 1
schedctl 1
setcontext 1
stat64 1
waitsys 1
getpid 2
gtime 3
read 3
write 4
setpgrp 6
ioctl 15
lwp_sigmask 22
sigaction 33

A sintaxe básica da função agregada é:


@name[key] = aggfunc(args)

A parte esquerda da expressão cria um array. Um vetor agregado é um conjunto que, em vez de
índices, utiliza strings para referenciar seus elementos. Por exemplo, @vendas["seg"]=14; if
(@vendas["ter"]> 10), etc.
O @ indica que estamos definindo uma função agregada. O name é um nome qualquer para o
agregado e key é um atributo cujos valores tornam-se índices do array.
O lado direito da expressão que define uma função agregada, pode ser:
● count() - quantas vezes ocorreu um evento.

Sistema Operacional 10
JEDITM

● sum(exp) – executa a soma da expressão.


● avg(exp) – executa a média aritmética da expressão.
● min(exp), max(exp) – mínimo e máximo, respectivamente, de uma expressão.
● quantize(exp) – cria um gráfico de uma expressão.
Não é necessário exibir explicitamente uma função agregada, pois o DTrace automaticamente
assume que, todas as funções agregadas são impressas no final da execução do script.

2.9. Scripts no DTrace – parte 2

Em vez de apenas contar quantas vezes uma chamada ao sistema foi executada, também
podemos conhecer quanto tempo levou para executar. Para tornar isso simples, consideraremos
apenas a função read. Nosso próximo script timestamp.d utiliza dois probes, syscall::read:entry
e syscall::ready:exit. Também utilizamos o atributo timestamp que retorna a hora atual.
Para achar a duração, gravamos a timestemp na syscall:::entry e a subtraímos da nova
timestamp localizada na syscall:::exit.
syscall::read:entry
{
t = timestamp;
}

syscall::read:return
{
delay = timestamp - t;
printf("%s(%d) tempo em %s: %d nsecs\n", execname, pid, probefunc, delay);
t = 0;
}

O problema com este código é que vários processos o chamam e modificam o valor do atributo t,
que corresponde ao tempo em que o sistema começou a funcionar.
Para resolver este problema, DTrace verifica sua estrutura de atributos. Qualquer atributo
inserido no DTrace é exclusivo de uma thread. Por exemplo, ao declarar um atributo self->t,
então este é único para cada thread.
A seguir, o script timestamp.d modificado:
syscall::read:entry
{
self->t = timestamp;
}

syscall::read:return
{
self->delay = timestamp - self->t;
printf("%s(%d) tempo no método %s: %d nsecs\n", execname, pid, probefunc,
self->delay);
self->t = 0;
}

O código de saída poderia ser mostrado da maneira:


# dtrace -q -s timestamp.d
Xsun(485) tempo em read: 33261 nsecs
Xsun(485) tempo em read: 23697 nsecs
Xsun(485) tempo em read: 25137 nsecs
sshd(1405) tempo em read: 46509 nsecs
Xsun(485) tempo em read: 27892 nsecs
Xsun(485) tempo em read: 21706 nsecs
sshd(1405) tempo em read: 26893 nsecs
sshd(1405) tempo em read: 13417 nsecs
sshd(1405) tempo em read: 21497 nsecs
...

Sistema Operacional 11
JEDITM

Para tornar os dados mais significativos, podemos utilizar a função agregada quantize.
syscall::read:entry
{
self->t = timestamp;
}

syscall::read:return
{
self->delay = timestamp - t;
@[execname] = quantize[self->delay];
self->t = 0;
}

A saída do código é:
# dtrace -q -s timestamp.d
^C
nfsmapid
valor ------------- Distribuição ------------- count
4096 | 0
8192 |@@@@@@@@@@@@@@@@@@@@ 1
16384 | 0
32768 |@@@@@@@@@@@@@@@@@@@@ 1
65536 | 0

sshd
valor ------------- Distribuição ------------- count
8192 | 0
16384 |@@@@@@@@@@@@@@@@@@@@ 1
32768 |@@@@@@@@@@@@@@@@@@@@ 1
65536 | 0

Xsun
valor ------------- Distribuição ------------- count
8192 | 0
16384 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 41
32768 |@@@@ 4
65536 | 0

2.10. DTrace toolkit

Não iremos só discutir uma visão geral do DTrace através da linguagem D. Para saber mais sobre
o DTrace, acesse o manual em:
http://docs.sun.com/app/docs/doc/817-6223

Para tornar as coisas simples, um toolkit do DTrace pode ser baixado e instalado. O toolkit
DTrace contém uma grande quantidade de scripts prontos, que monitoram diversos probes.
O toolkit DTrace pode ser baixado no endereço:
http://www.opensolaris.org/os/community/dtrace/dtracetoolkit/

Vejamos como instalar e executar a versão DTraceToolkit-0.99.tar.gz. Uma vez realizado o


download do arquivo, execute os seguintes comandos:
gunzip DtraceToolkit-0.99.tar.gz
tar xvf DTraceToolkit-0.99.tar
cd DTraceToolkit-0.99
./install

DTraceToolkit será instalado por padrão no diretório /opt/DDT. Analisaremos a seguir o conteúdo
deste diretório:
● apps/ - scripts específicos de aplicação

Sistema Operacional 12
JEDITM

● cpu / - scripts para análise de CPU


● disk/ - scripts para análise de I/O
● docs/ - documentação
● mem/ - scripts para análise de memória
● proc/ - scripts para análise de processos
Casa script possui uma página principal associada, embora não seja automaticamente instalado.
Para acessar a página principal dos scripts, executamos o comando man com as seguintes
opções:
# man -M <DTT Man directory> <command>

Por padrão, o diretório é /opt/DTT/Man. Por exemplo, para solicitar auxílio sobre o script
runocc.d, que verifica o pool de ocupação da CPU, executamos o seguinte comando:
# man -M /opt/DTT/Man runocc.d

Atualmente, está em desenvolvimento o monitoramento para programas Java, bem como


monitoramento para JavaScript e aplicações WEB. Quando estiverem prontos, poderão ser usado
para rastrear programas Java sem ter de modificar o código fonte.

Sistema Operacional 13
JEDITM

3. Informações da CPU
3.1. Comando vmstat

O comando vmstat lista informações sobre o comportamento global da CPU desde a sua
inicialização. Executando este comando, podemos produzir a seguinte saída:
# vmstat
kthr memory page disk faults cpu
r b w swap free re mf pi po fr de sr f0 s0 s2 s6 in sy cs us sy id
0 0 0 2678760 1842984 0 1 0 0 0 0 0 0 0 0 0 508 30 46 0 1 99

Veremos com mais detalhes estes campos:


● kthr – número de threads nos seguintes estados:
○ r – número de threads no núcleo executando queue
○ b – número de threads bloqueadas esperando, por exemplo, I/O, recursos do sistema,
entre outros
○ w – número de pares de troca LWP que estão esperando para finalizar o processo de
um recurso
● memory – memória utilizada em kilobytes
○ swap – espaço em área de troca
○ free – memória livre
● page – informação sobre como a memória está sendo utilizada
● disk – informação sobre as operações em disco por segundo
● faults – informação sobre os traps do sistema
● cpu – percentual de uso da CPU
○ us – utilizada pelo usuário
○ sy – tempo do sistema
○ id – tempo livre
Também é possível executar o vmstat com a informação do intervalo de saída, que executa o
comando após cada intervalo específico, para obter uma visão global dos processos que estão em
execução.
# vmstat 5
kthr memory page disk faults cpu
r b w swap free re mf pi po fr de sr f0 s0 s2 s6 in sy cs us sy id
0 0 0 2678752 1842968 0 1 0 0 0 0 0 0 0 0 0 508 30 46 0 1 99
0 0 0 2657392 1820800 0 5 0 0 0 0 0 0 0 0 0 503 50 49 0 1 99
0 0 0 2657392 1820800 0 0 0 0 0 0 0 0 0 0 0 502 34 44 0 1 99
0 0 0 2657392 1820800 0 0 0 0 0 0 0 0 0 0 0 504 44 50 0 1 99
0 0 0 2657392 1820800 0 0 0 0 0 0 0 0 0 0 0 508 70 67 0 1 99
0 0 0 2657384 1819848 2 24 234 0 0 0 0 0 0 33 0 609 663 153 0 2 98
0 0 0 2656272 1815528 3 27 2557 0 0 0 0 0 0 328 0 1471 4262 722 1 6 93
0 0 0 2649720 1791392 0 0 3103 0 0 0 0 0 0 438 0 1896 4418 890 1 7 92
0 0 0 2644960 1771032 0 0 3289 0 0 0 0 0 0 459 0 1880 4639 960 1 7 92
0 0 0 2639400 1750232 3 0 2773 0 0 0 0 0 0 395 0 1671 6227 844 1 10 89

Podemos computar o uso da CPU utilizada, subtraindo o tempo livre (cpu id) de 100.

3.2. uptime

Para descobrir há quanto tempo seu computador está ativo, bem como a média de carga na CPU,
executamos simplesmente o comando uptime:

Sistema Operacional 14
JEDITM

# uptime
10:50am up 3 day(s), 5 min(s), 2 users, load average: 0.11, 0.04, 0.02

Note a coluna que contém a média de carga (load average): está em 1, 5 e 15 minutos de média
para carregar na CPU. Estes números são um reflexo de como muitos processos são executados
no computador. Por exemplo, 1.00 é 100% de utilização de CPU em um único processador, mas
seria a metade em um computador com dois processadores. Se for obtido mais do que o
processador consegue executar, significa que está ocorrendo uma saturação de CPU.

3.3. Scripts no DTrace – Parte 3

No diretório /opt/DTT/cpu, podemos executar um script em DTrace para recuperar as


informações sobre a CPU dos computadores:
● cputypes.d – lista a informação sobre cada CPU
● loads.d – mostra a média de carga
● intbycpu.d – mostra o número de interrupções manipuladas por cada CPU
● runocc.d – mostra as execuções que estão em uma queue

3.4. Script shellsnoop

A aplicação shellsnoop usa o DTrace para mostrar o que está sendo exibido em outros
terminais. A saída a seguir mostra um usuário alterando sua senha:
# ./shellsnoop
PID PPID CMD DIR TEXT
1412 1411 bash W #
1412 1411 bash R p
1412 1411 bash W p
1412 1411 bash R a
1412 1411 bash W a
1412 1411 bash R s
1412 1411 bash W s
1412 1411 bash R s
1412 1411 bash W s
1412 1411 bash R w
1412 1411 bash W w
1412 1411 bash R d
1412 1411 bash W d
1412 1411 bash R
1412 1411 bash W

1734 1412 passwd W passwd


1734 1412 passwd W : Changing password for
1734 1412 passwd W mario
1734 1412 passwd W
1734 1412 passwd W New Password:
1734 1412 passwd W
1734 1412 passwd W Re-enter new Password:
1734 1412 passwd W
1734 1412 passwd W passwd: password successfully changed for mario
1734 1412 passwd W

1412 1411 bash W #

A senha não é exibida, shellsnoop mostra unicamente o que aparece na tela do terminal.

Sistema Operacional 15
JEDITM

4. Processos
4.1. Comando ps

O comando ps é um comando padrão para listar informações dos processos.


# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 0 0 0 Dec 10 ? 0:12 sched
root 1 0 0 Dec 10 ? 0:01 /sbin/init
root 2 0 0 Dec 10 ? 0:00 pageout
root 3 0 1 Dec 10 ? 6:29 fsflush
root 1412 1411 0 08:14:12 pts/3 0:01 bash
root 7 1 0 Dec 10 ? 0:09 /lib/svc/bin/svc.startd
root 9 1 0 Dec 10 ? 0:24 /lib/svc/bin/svc.configd
root 95 1 0 Dec 10 ? 0:00 /usr/lib/snmp/snmpdx -y -c /etc/snmp
daemon 220 1 0 Dec 10 ? 0:00 /usr/sbin/rpcbind
root 214 1 0 Dec 10 ? 0:00 /usr/sbin/cron
...

A opção -e mostra todos os processos, e a opção -f lista os processos que contém as colunas
completas.
● UID – identificação do usuário do processo
● PID – identificação do processo
● PPD – identificação do processo pai
● C – coluna obsoleta. Corresponde à utilização do processo agendado
● STIME – tempo de inicialização de um processo
● TTY – terminal de controle (? se não for controlado por um terminal)
● TIME – tempo que um processo está executando na CPU
● CMD – comando usado para inicializar o processo

4.2. Scripts no DTrace – Parte 4

Os scripts de processo são encontrados no diretório /opt/DTT/proc. Vejamos a seguir alguns


dos mais usados para monitorar os processos:
● sampleproc – um arquivo executável que usa DTrace para a inspeção em muitas CPUs
na qual a aplicação é executada
● writebytes.d e readbytes.d – como os bytes são lidos e escritos pelo processo
● syscallbyproc.d e syscallbypid.d – sistema de chamadas por processo ou por
identificação do processo
● filebyproc.d – lista de arquivos abertos por um processo
● crash.d – relatório sobre aplicações que falharam

Sistema Operacional 16
JEDITM

5. Memória
5.1. pmap -x

O comando pmap associado à opção -x mostra uma visão da memória com a identificação do
processo. O exemplo a seguir mostra um mapa de um processo na memória com ID 1412. Para
explorar o endereço de memória de um processo em particular, temos que usar o comando ps
para procurar o próprio ID do processo.
# pmap -x 1412
1412: bash
Address Kbytes RSS Anon Locked Mode Mapped File
00010000 648 624 - - r-x-- bash
000C0000 80 48 16 - rwx-- bash
000D4000 168 168 64 - rwx-- [ heap ]
FF100000 864 856 - - r-x-- libc.so.1
...

5.2. Scripts no DTrace – Parte 5

Os scripts de memória são encontrados no diretório /opt/DTT/mem. A seguir são mostrados


alguns scripts mais comumente utilizados para analisar a memória:
● vmstat.d – utilizado para escrever em D
● xvmstat – um arquivo executável (./xvmstat) que usa DTrace para mostrar mais
informações em relação ao vmstat, tais como memória RAM livre, memória virtual livre
entre outros
● swapinfo.d – mostra informações da memória virtual
● minfbypid.d – detecta um grande consumidor de memória

Sistema Operacional 17
JEDITM

6. Disco Rígido
6.1. Scripts no DTrace – Parte 6

Scripts para controle do disco rígido são localizados em /opt/DTT/disk. A seguir, são mostrados
alguns scripts mais utilizados para analisar o disco rígido:
● iofile.d – mostra o tempo de espera para entrada e saída
● diskhits – uma queue sendo executada, através de um filename, verifica a entrada e
saída e a média de carga de um arquivo
● iotop – um arquivo executável que lista os eventos de entrada e saída do disco por
processo
● iosnoop – um arquivo executável que monitora eventos de entrada e saída para um
determinado userid, processid ou filename
A seguir um exemplo de saída de um iosnoop mostrando os arquivos editados por um
determinado comando:
# ./iosnoop
UID PID D BLOCK SIZE COMM PATHNAME
0 3 W 28720 2560 fsflush <none>
0 1726 R 71712 8192 vi /export/home/alice/temp.txt
0 1726 R 12227792 8192 vi /export/home/alice/temp.txt
0 1726 R 11606832 8192 vi <none>
0 1726 R 12227936 8192 vi /export/home/alice/temp.txt
0 1726 W 1081232 8192 vi /var/tmp/ExtBaWxd
0 1726 W 1372256 90112 vi /var/tmp/ExtBaWxd
0 1726 W 11627936 958464 vi /var/tmp/ExtBaWxd
0 1726 R 12228880 8192 vi /export/home/alice/temp.txt
0 1726 R 12229184 8192 vi /export/home/alice/temp.txt

6.2. Chime

Chime é uma interface gráfica para visualizar DTrace.

Figura 1: Janelas do Chime

O pacote, bem as instruções para instalação podem ser encontrados no site da comunidade Open
Solaris. No endereço: http://www.opensolaris.org/os/project/dtrace-chime/.

Sistema Operacional 18
Módulo 8
Sistema Operacional

Lição 7
ZFS

Versão 1.0 - Mar/2008


JEDITM

1. Objetivos
ZFS foi desenvolvido pela equipe de Jeff Bonwick como um novo sistema de arquivo para o
Solaris. Seu desenvolvimento foi anunciado em setembro de 2004, foi incluído como parte da
distribuição oficial do OpenSolaris em novembro de 2005 e distribuído com a atualização 6/06
do Solaris 10 em junho de 2006.
ZFS promete para ser a “última palavra em sistemas de arquivos”. Desenvolvedores de ZFS o
descrevem na página WEB da comunidade Solaris como “um novo tipo de sistema de arquivo que
provê uma administração simples, semântica transacional, integridade de dados end-to-end e
melhor escalabilidade”1.
Discutiremos nesta lição estas características do ZFS e mostraremos suas melhorias sobre o
sistema de arquivo atualmente em uso.
Ao final desta lição, o estudante será capaz de:
• Compreender o conceito de sistema de arquivos
• Utilizar o aplicativo ZFS
• Realizar agrupamento, espelhamento, fotografias e clones no disco de sistema Solaris

1 http://www.opensolaris.org/os/community/zfs/ publicado em 21 de agosto de 2007.

Sistema Operacional 4
JEDITM

2. Capacidade
A capacidade de um sistema de arquivo pode ser descrita pelo número de bits em uso pelo
sistema que é utilizado para armazenar informações sobre de arquivos. Atualmente os sistemas
de arquivo são de 64 bits. Isso significando que são usados 64 bits de dados para armazenar a
informação de cada arquivo, como local para o dispositivo, permissões de arquivo, conteúdos de
diretório, entre outros. Sendo que um sistema de arquivo de 64 bits denota um arquivo de
tamanho máximo teórico de sistema com 264 bytes, temos aproximadamente 1011 Gb ou 1 bilhão
de discos rígidos (de 100 Gb cada).
Este é um tamanho máximo teórico. A seguir temos as limitações de sistema de arquivos para
cada sistema conhecido.
Sistema de Arquivo Sistema Operacional Capacidade
FAT16 DOS-Win3.11 2 Gb
FAT32 Win95-WinME 8 Tb (ou 8000 Gb)
ext3 Linux 32 Tb (32000Gb)
NTFS WinXP-Vista 2 Tb (2000 Gb)
Figura 1: Capacidade teórica dos sistemas de arquivos

Isto pode parecer bastante grande, entretanto atualmente existem algumas companhias que
possuem dados na escala de petabyte, aproximadamente 106 no valor de GB de espaço
armazenado. Se a tendência na Lei de Moore continuar, então, na metade da próxima década
podemos ver que o limite de 64 bits será suplantado1. Realmente o armazenamento mundial já
excedeu os 161 exabytes (1018 bytes) e alcançará 988 exabytes antes de 20102. Nosso
armazenamento mundial já foi além dos 1011 Gb do limite teórico de sistemas de 64 bits, ou seja,
um bilhão de vezes acima.
ZFS é o primeiro sistema de arquivos de 128 bits. Sendo que um sistema de arquivos de 128 bits
que pode armazenar teoricamente 2128 bytes. Isto corresponde a 1024 Gb, ou seja, um trilhão de
trilhão de gigabytes ou 10.000 vezes maior do que o tamanho total de armazenamento mundial.
Os desenvolvedores usam limites físicos de computação para armazenar 128-bits completos em
um sistema de arquivo, um usuário iria necessitar de um dispositivo de armazenamento com uma
massa mínima de 136 bilhões quilogramas, e isso seria energia mais do que suficiente para ferver
os oceanos.

1 http://blogs.sun.com/bonwick/entry/128_bit_storage_are_you de 22 de agosto de 2007.


2 Cooporação EMC. The Expanding Digital Universe: A Forecast of Worldwide Information Growth
Through 2010

Sistema Operacional 5
JEDITM

3. Armazenamento Agrupado
3.1. O novo problema do disco rígido

Suponhamos a situação de um computador com um longo tempo de vida, este está conectado à
internet, provavelmente, ocorreria o problema de falta de espaço em disco. Considerando a
compra de um novo disco rígido, podemos adotar duas estratégias:
Como uma primeira estratégia configuramos o novo dispositivo como um disco rígido secundário,
(como um novo drive para o sistema operacional Windows ou montado como um novo diretório
em sistemas do tipo Unix, tais como Linux e Solaris). Poderíamos colocar grandes arquivos ou
aplicações neste disco rígido novo. E, se este for o caso, então os usuários de computador devem
lembrar do drive para guardar os arquivos. As aplicações começariam a manipular diferentes
diretórios e seu filesystem ficaria completamente desorganizado.
Para evitarmos esse problema descrito, a segunda estratégia consistiria em copiar o conteúdo do
antigo disco rígido para o novo (ou, pelo menos, os documentos do usuário). Porém, deste modo
teríamos alguns problemas. Se o usuário ou uma aplicação não puder localizar um determinado
arquivo porque foi movido para outro diretório (nenhum problema para computadores com Linux
ou Solaris). Entretanto, e se alguns arquivos fossem deixados para trás?
No fim, acrescentar um novo disco rígido no seu computador causa uma grande quantidade de
dores de cabeça devido a uma nova reorganização.

3.2. A resposta através de ZFS: Armazenamento agrupado

Em outros sistemas de arquivo existe um único dispositivo, e meios de dispositivos múltiplos que
possuem um Gerenciador de Volume. Gerenciadores de volume mostram os dispositivos
diferentes como "drives" em sistemas Windows. Em sistemas Linux, o diretório raiz atravessa
todos os dispositivos, e estes dispositivos (ou diretórios nesses dispositivos) podem ser montados
como diretórios no diretório de raiz.
Por outro lado, ZFS usa um sistema de armazenamento agrupado. Durante a organização de um
agrupamento de ZFS, é possível nomear um, dois ou mais dispositivos a este. Toda a capacidade
de todos os dispositivos é acessível pelo agrupamento.
Este agrupamento permite que seja montado como um diretório no filesystem regular do Solaris.
Do ponto de vista do usuário, estará economizando um diretório no sistema de Solaris. Porém,
internamente, podem ser economizados dados em um dispositivo e podem ser estendidos ao
próximo dispositivo se houver espaço faltando. Isto pode ser refletido até mesmo para outro disco
ou ser distribuído para vários discos rígidos obtendo uma maior redundância. O usuário deve
apenas se preocupar onde e como o dados são armazenados.
Para colocar isto de forma simples, “ZFS faz com o armazenamento o que a memória virtual fez
com a RAM”1. Como visto em lições anteriores, a memória virtual pode ser resumida em: detalhes
de aplicações e como elas são armazenadas. Uma aplicação não sabe onde está em memória, se
foi alocada em memória contínua ou não. De fato, está na memória, entretanto pode estar
temporariamente armazenada no disco rígido. Ocorre o mesmo com arquivos ZFS. O usuário não
necessita se preocupar como o arquivo é armazenado de fato, a menos que este diretório possa
ser acessado.
Adicionar um novo dispositivo ao ZFS significa que estamos adicionando no agrupamento do
armazenamento. O computador irá automaticamente pegar esta nova capacidade disponível e
usá-lo sem a necessidade de transferir ou reorganizar o filesystem. É possível ter no máximo 264
dispositivos em um único agrupamento.

1 Jeff Bonwick. ZFS: The Last Word in File Systems. Sun Microsystems

Sistema Operacional 6
JEDITM

4. Integridade de Dados
Armazenamento secundário está longe de ser um meio seguro de manter seus dados intactos.
Problemas podem ocorrer e destruir a informação a qualquer momento.
A perda de bits acontece quando se desgasta o meio magnético, seu disco rígido começa a falhar
devido a este desgaste. Pode ocorrer o que denominamos de fantasma. Um fantasma pode
escrever e o disco rígido reivindicar a escrita com dados que de fato não existem. Resultado que
pode acidentalmente tentar ler ou escrever dados em uma porção errada do disco. Isto acaba
tornando áreas completas do disco ilegíveis.
ZFS tem diversas características que mantêm a integridade dos dados.

4.1. Checksums end-to-end1

Sistema de arquivo armazenam informação em blocos. Sistemas de arquivo tradicionais


acrescentam blocos em checksums aos arquivos e estes blocos podem prover correções de erro.
Isto é, podem descobrir trechos com problemas de fantasmas, como o que acontece quando os
dados e o checksum não são compatíveis.

Figura 2: Dados não emparelhados com o checksum

Porém, o que acorre quando são colocados os dados corretos com o checksum correto na porção
errada do disco rígido? Na realidade, este sistema pode descobrir um único pedaço com problema
de fantasmas e nada mais.

Figura 3: Pedaço errado do sistema de arquivos

ZFS vai um passo adiante e coloca um checksum em cada nível do bloco.


Inserindo checksums em todo o caminho até o bloco pai não só asseguramos que cada dado é
bloqueado de forma consistente, mas também mantemos um agrupamento inteiro de forma

1 Summation Check - verificação do numérica dos bits que estão sendo transferidos para
descobrir erros na transferência

Sistema Operacional 7
JEDITM

consistente. Qualquer operação que resulte em qualquer lugar em um checksum errado ao longo
dos meios da árvore indica que o agrupamento inteiro está de alguma forma incompatível e
necessita de correção.

Figura 4: Agrupamento inválido

E ainda, ZFS separa os dados do checksum. Em sistemas de arquivo tradicionais, onde os dados e
o checksum são armazenados no mesmo bloco, existe uma chance de um erro se o disco rígido
modificar tanto os dados quanto o checksum de forma que o sistema não pode mais determinar
se ocorreu algum erro. Fisicamente separados, dados e checksum, erros no disco rígido irão afetar
somente os dados ou o checksum, sendo fácil de serem detectados.

4.2. Limpeza do disco

Para assegurar que os dados continuem consistentes, ZFS faz uma verificação continua de todos
os blocos de dados em um processo conhecido como “Limpeza do Disco” (disk scrubbing). ZFS
percorre o disco inteiro verificando se os dados combinam com os respectivos checksums. Em
caso de erros, ZFS está apto a corrigir a informação automaticamente, derivando a partir do
checksum, ou através de um espelho.

4.3. Transações de escrita e leitura na entrada e saída

Já aconteceu que ao salvar um documento (normalmente muito importante) ocorre uma falha de
energia? Para nosso horror, descobrimos que o arquivo do documento foi corrompido e devemos
refazê-lo do zero.
O arquivo foi corrompido porque o arquivo foi deixado em um estado inconsistente. O arquivo
consiste de diversos blocos de dados de uma nova versão assim como os dados da antiga versão,
os quais deveriam ser sobrescritos se não ocorresse a falha de energia.
Alguns sistemas tradicionais de arquivo fazem uso de um sistema de notícias (journal). Todas as
operações de entrada e saída são escritas primeiramente neste sistema de publicação antes de
serem executadas. Isso garante que em caso de falha de energia ou outro erro, o sistema
simplesmente continue suas operações escrevendo no arquivo de publicação antes que se torne
inconsistente novamente. As operações devem ser atômicas. Ou todas são executadas com
sucesso, ou são novamente executadas a partir do arquivo de publicação em caso de estar

Sistema Operacional 8
JEDITM

corrompido, ou não são executadas.


Fazer uso do sistema de publicação, entretanto, diminui a velocidade da execução de entrada e
saída devido ao passo extra de se tomar nota de todas as instruções que serão executadas. Após
um longo período de tempo, o arquivo de publicação ocupará um espaço significante no disco
rígido.
ZFS dá um passo extra para implementar este tipo de transação. Nenhum dado está de fato
sendo sobrescrito pelo ZFS, quaisquer modificações do sistema toma o lugar de uma cópia dos
dados. Qualquer falha de sistema irá afetar somente a cópia dos dados. No caso de uma falha, o
sistema recupera os blocos de dados originais antes de executar as operações. Apenas quando as
modificações atingem o bloco raiz é que ocorre a gravação e conclusão das modificações no
sistema. Ou todas as instruções de entrada e saída agrupadas são executadas ou a transação não
acontece.

Figura 5: Novos dados chegam e os antigos não são alterados

Figura 6: Modificações são realmente gravadas

Sistema Operacional 9
JEDITM

Figura 7: Em caso de falha permanece os dados consistentes

4.4. Fotografias de tempo linear

Como efeito colateral dos sistemas de escrita e leitura, fotografias de sistema de arquivo são
automaticamente realizadas depois de qualquer operação do sistema. Fotografias são uma cópia
do sistema em algum momento no passado as quais podem ser usadas para propósitos de
backup. Cada operação em ZFS automaticamente cria uma fotografia do antigo sistema. De fato é
mais rápido criar uma fotografia do que ter aquele passo extra de substituir os dados antigos.

Sistema Operacional 10
JEDITM

5. Espelhamento e RAID-Z
5.1. Espelhamento

ZFS possibilita, sem muito esforço, configurar um sistema de arquivo espelhado. Um sistema de
arquivo espelhado usa um segundo disco rígido para replicar completamente os dados de um
outro disco rígido. Espelhos são freqüentemente usados se o primeiro disco rígido falhar, o
sistema continua operando com dados contidos no segundo disco rígido. Além disso, espelhos
tornam a leitura duas vezes mais rápida, os dados podem ser retornados do segundo disco rígido
enquanto o primeiro estiver ocupado.
Implementações de espelhamento tradicionais não diferenciam blocos ruins. Mesmo que uma
cópia backup exista no espelho, isso não possibilita dizer se um bloco foi, de algum modo,
corrompido. Ao fazer o checksum de todos os blocos, o ZFS determina silenciosamente se um
bloco foi corrompido. Se isso ocorre, o ZFS recupera automaticamente o bloco correto do segundo
disco e repara o bloco ruim do primeiro disco. Isso é feito de modo transparente, sem informar ao
usuário qualquer problema.

5.2. RAID-Z

RAID é uma sigla para Redundant Array of Inexpensive Disks (Conjunto Redundante de Discos
Baratos). Configurar um sistema RAID significa adicionar novos discos rígidos, seguindo um
esquema particular (chamado nível RAID) de como a informação será armazenada nestes discos.
Há 5 níveis tradicionais de RAID:
● RAID 0 – os dados são simplesmente colocados em trilhas de múltiplos discos. Uma trilha
consiste de vários blocos agrupados de dados. Fornece a vantagem de melhor
performance, pois as leituras podem ser feitas em paralelo. No entanto, não provê nenhum
tipo de segurança ao dado, uma falha no disco resulta em falha de todos os dados.
● RAID 1 – os dados são espelhados ou completamente duplicados em um segundo disco,
ou múltiplos. Leituras podem também ser feitas em paralelo, assim como prover segurança
aos dados já que se um disco falhar os dados podem ainda ser lidos de um segundo disco.
Entretanto, esse nível de RAID é caro, necessitamos ter o dobro da quantidade de disco
rígido para armazenar os dados.
● RAID 2 – os dados são escritos um bit por vez em cada disco, com um disco dedicado
especialmente para armazenar a informação de paridade para a recuperação desses dados.
Esse nível não é usado já que é inviável armazenar bit a bit.
● RAID 3 – assim como o RAID 2, divide os dados entre discos com uma paridade de disco
especial. Entretanto, os dados dessa vez são divididos em trilhas.
● RAID 4 – divide os dados em blocos de dados do sistema de arquivo ao invés de colocar
os blocos em trilhas de múltiplos discos. Um disco de paridade dedicado contém
informação que pode ser usada para detectar e corrigir erros de dados nos discos.
O problema com o RAID 4 é que qualquer escrita feita em um bloco de dados significa ter que
novamente computar a paridade. Isso significa que qualquer escrita irá envolver o processo de
duas escritas, a escrita dos dados e a da uma nova paridade no bloco. Isso causa um gargalo na
paridade de disco. Um novo tipo denominado de RAID 5 distribui a paridade de disco e permite
um tempo mais rápido de escrita.
O problema com RAID 4 e 5 é que sempre que qualquer bloco de dados for escrito, outros blocos
na mesma trilha devem ser lidos para recalcular o bloco de paridade. E isso diminui a velocidade
de escrita. Os dados podem se tornar corrompidos se uma falha de energia ocorrer enquanto o
bloco de paridade estiver sendo computado. Como o novo bloco de paridade ainda não foi escrito
corretamente, então os blocos de dados não batem com o bloco de paridade. Quando isso ocorre,
RAID assume incorretamente que o dado está corrompido.
ZFS inclui o modelo RAID-Z. RAID-Z é uma implementação modificada do RAID 5. Este novo

Sistema Operacional 11
JEDITM

modelo configura cada bloco de dados do sistema de arquivo para ser sua própria trilha. Dessa
forma, para se recalcular a paridade, é preciso ter que carregar somente um único bloco de
dados, não é mais necessário ler qualquer outro bloco. E como as operações do sistema de
arquivo são agora baseadas em transações, a questão de paridade é evitada, ou o bloco inteiro
(incluindo a paridade) é escrito, ou ainda, não é escrito de forma alguma.
Striping dinâmico também é uma característica adicional de RAID-Z. Antigas implementações
de RAID faziam que, uma vez fixado o número de discos rígidos, quando o sistema era
organizado, sua faixa de tamanho também era. Quando um novo disco for adicionado, todos os
novos dados são acertados para usar este novo disco. Não existe nenhuma necessidade de se
migrar os antigos dados, com o passar do tempo estes dados migram para o novo formato de
faixa. Esta migração é feita automaticamente.

Sistema Operacional 12
JEDITM

6. Administração ZFS
6.1. Convenção de nomes do disco

Antes de podermos discutir a administração do ZFS, devemos nos familiarizar primeiro com a
notação de disco usada no sistema Solaris.
Todos os dispositivos em Solaris são representados como arquivos. Estes arquivos são
armazenados no diretório /devices.
# ls /devices
iscsi pci@1f,2000:devctl pci@1f,4000:devctl pseudo:devctl
iscsi:devctl pci@1f,2000:intr pci@1f,4000:intr scsi_vhci
options pci@1f,2000:reg pci@1f,4000:reg scsi_vhci:devctl
pci@1f,2000 pci@1f,4000 pseudo

Estes são todos os dispositivos que estão conectados ao sistema, inclusive o teclado, monitor,
dispositivos de USB e outros. Para diferenciar entre discos rígidos (inclusive drives de CD) e
outros dispositivos, Solaris provê um diretório separado para os discos rígidos, denominado
/dev/rdsk.
Ao listar o conteúdo deste diretório, veremos os seguintes arquivos com formatos do tipo:
c#d#s# ou c#t#d#s# ou c#d#p#. Estes descrevem o endereço completo de um trecho do disco.
# ls /dev/dsk
c0d0p0 c0d0s7 c1t0d0s4 c1t1d0s15 c1t2d0s12 c1t3d0s1 c1t4d0p3
c0d0p1 c0d0s8 c1t0d0s5 c1t1d0s2 c1t2d0s13 c1t3d0s10 c1t4d0p4
c0d0p2 c0d0s9 c1t0d0s6 c1t1d0s3 c1t2d0s14 c1t3d0s11 c1t4d0s0
c0d0p3 c1t0d0p0 c1t0d0s7 c1t1d0s4 c1t2d0s15 c1t3d0s12 c1t4d0s1
c0d0p4 c1t0d0p1 c1t0d0s8 c1t1d0s5 c1t2d0s2 c1t3d0s13 c1t4d0s10
c0d0s0 c1t0d0p2 c1t0d0s9 c1t1d0s6 c1t2d0s3 c1t3d0s14 c1t4d0s11
c0d0s1 c1t0d0p3 c1t1d0p0 c1t1d0s7 c1t2d0s4 c1t3d0s15 c1t4d0s12
c0d0s10 c1t0d0p4 c1t1d0p1 c1t1d0s8 c1t2d0s5 c1t3d0s2 c1t4d0s13
c0d0s11 c1t0d0s0 c1t1d0p2 c1t1d0s9 c1t2d0s6 c1t3d0s3 c1t4d0s14
c0d0s12 c1t0d0s1 c1t1d0p3 c1t2d0p0 c1t2d0s7 c1t3d0s4 c1t4d0s15
c0d0s13 c1t0d0s10 c1t1d0p4 c1t2d0p1 c1t2d0s8 c1t3d0s5 c1t4d0s2
c0d0s14 c1t0d0s11 c1t1d0s0 c1t2d0p2 c1t2d0s9 c1t3d0s6 c1t4d0s3
c0d0s15 c1t0d0s12 c1t1d0s1 c1t2d0p3 c1t3d0p0 c1t3d0s7 c1t4d0s4
c0d0s2 c1t0d0s13 c1t1d0s10 c1t2d0p4 c1t3d0p1 c1t3d0s8 c1t4d0s5
c0d0s3 c1t0d0s14 c1t1d0s11 c1t2d0s0 c1t3d0p2 c1t3d0s9 c1t4d0s6
c0d0s4 c1t0d0s15 c1t1d0s12 c1t2d0s1 c1t3d0p3 c1t4d0p0 c1t4d0s7
c0d0s5 c1t0d0s2 c1t1d0s13 c1t2d0s10 c1t3d0p4 c1t4d0p1 c1t4d0s8
c0d0s6 c1t0d0s3 c1t1d0s14 c1t2d0s11 c1t3d0s0 c1t4d0p2 c1t4d0s9

● c# - representa o número de controlador. Eles são numerados c0, c1, c2 e assim


sucessivamente. E conectam discos rígidos a um controlador.
● d# - representa número de discos para aquele controlador.
● s# - representa número de trechos. Estes números podem ir de 0 a 15.
● p# - representa número da partição, algumas vezes é usado ao invés do número de
trechos. Números de partição podem ir de 0 a 4.
A seção de inicialização mostra normalmente quatro dispositivos. Estes dispositivos são: master
primário, slave primário, master secundário e slave secundário. Seu disco rígido primário é
freqüentemente o dispositivo de master primário. Um drive de CD ou DVD é colocado
freqüentemente como o dispositivo slave primário. Ao possuir discos rígidos adicionais, estes
normalmente são o master e o slave secundário.
Para Solaris, esta poderia ser uma anotação para estes discos:
● mestre primário: c0d0s0 (controlador 0, disco 0, parte 0)
● escravo primário: c0d1s0 (controlador 0, disco 1, parte 0)
● mestre secundário: c1d0s0 (controlador 1, disco 0, parte 0)
● escravo secundário: c1d1s0 (controlador 1, disco 1, parte 0)

Sistema Operacional 13
JEDITM

Alguns computadores podem ter uma interface SCSI. SCSI permite até 16 dispositivos
conectados a um único controlador. Freqüentemente, os controladores SCSI começam com o
número 2, já que 0 e 1 são, respectivamente, os controladores primários e secundários.
Computadores SCSI usam o valor t# para indicar um disco. Este pode variar de 0 a 15.
Endereços SCSI também podem usar a notação d#, mas sempre é fixo para zero (d0). por
exemplo, o arquivos de c2t2d0s0 até c2t2d0s15 representam todas as partes do dispositivo
nomeados para o t2 no controlador 2.

6.2. Administração do agrupamento

Agrupamentos são mantidos pelo comando zpool. Comandos de zpool permitem criação,
exclusão e adição de novos dispositivos, inscrição e modificação de agrupamentos.
Para criar um agrupamento básico em ZFS, executamos o comando zpool. A sintaxe básica é:
# zpool create <poolname> <vdev> <dispositivos>

Poolname é o nome do agrupamento a criar. O parâmetro vdev descreve que característica de


armazenamento deve usar o agrupamento. Dispositivos indicam que discos rígidos devem ser
usados no agrupamento. É possível criar agrupamento de pedaços de disco (ou até mesmo
arquivos) entretanto, isso é feito normalmente com discos inteiros.
Observe que é possível utilizar parte de um disco para formatá-lo como agrupamento do disco.
Por exemplo, o seguinte comando cria um zpool básico nomeado como myfirstpool1 que usa o
disco master secundário.
# zpool create myfirstpool1 disk c1d0

Para criar um agrupamento espelho, simplesmente substituímos a palavra-chave disk por mirror.
Devemos prover mais que um disco para criar um espelho.
# zpool create mymirroedpool mirror c1d0 c1d1

Uma coleção de discos pode ser organizada em uma configuração do tipo RAID-Z inserindo o
valor raidz no parâmetro <vdev>. O número indicado de discos para RAID-Z está entre 3 a 9.
# zpool create myraidzpool raidz c0d1 c1d0 c1d1

ZFS permite criar agrupamentos de arquivos regulares. Em vez de especificar um disco, pode-se
especificar um arquivo regular. Por exemplo:
# zpool create mypool1 file /export/home/alice/mypoolfile
# zpool create mypool2 mirror /export/home/alice/p1 /export/home/alice/p2

Esta característica permite testar as características do ZFS sem precisar ter discos adicionais.
Usaremos isto em futuros exercícios. Para acrescentar um dispositivo a um agrupamento:
# zpool add myfirstpool disk c1d0

Pode-se acrescentar um espelho ou um dispositivo adicional de RAID-Z a um agrupamento


existente simplesmente substituindo o disco por um espelho ou raidz. O comando a seguir lista
todos os agrupamentos ZFS disponíveis e as informações de uso espacial e ßstatus:
# zpool list

Um agrupamento pode ter 3 valores de estado: online, degraded ou faulted. Um zpool online tem
todos seus dispositivos em bom estado de funcionamento. Um agrupamento degraded tem um
dispositivo falho que ainda pode ser recuperado por redundância. Um dispositivo faulted significa
que falhou e seus dados não podem mais ser recuperados.
Exportar um agrupamento significa por os dispositivos em uma área de transferência. Para
exportar um agrupamento, executamos o comando:
# zpool export mypool

Sistema Operacional 14
JEDITM

E devemos importar os dispositivos a um novo computador, através do comando:


# zpool import mypool

E finalmente, para eliminar um agrupamento, executamos o comando:


# zpool destroy mypool

Isto elimina um agrupamento e faz com que os dispositivos que faziam parte deste fiquem
disponíveis para outros usos.

6.3. Uso Básico de Agrupamento em ZFS

Uma vez que criamos um agrupamento, podemos montá-lo para fazer parte permanente do
sistema de arquivos do Solaris. Os usuários economizariam a montagem do diretório a partir do
momento que não precisam conhecer que este diretório está usando o ZFS. Também é
desnecessário saber que os diretórios são espelhados ou armazenados usando RAID-Z.
Para montar um agrupamento como parte do sistema de arquivos do Solaris, usamos o comando:
# zfs set mountpoint=/target/directory/in/regular/filesystem poolname

Por exemplo, montaremos um agrupamento mypool para guardar o diretórios dos usuários.
# zfs set mountpoint=/export/home mypool

Podemos criar diretórios adicionais em mypool. Por exemplo, criamos diretórios para user1 e
user2:
# zfs create mypool/user1
# zfs create mypool/user2

Como já montamos mypool para /export/home, user1 está automaticamente montado como
/export/home/user1 e user2 como /export/home/user2.
Há outras opções adicionais, tais como, compressão, cotas de disco e garantias espaciais de disco
que podem ser ativadas com os seguintes comandos.
# zfs set compression=on mypool
# zfs set quota=5g mypool/user1
# zfs set reservation=10g mypool/user2

6.4. Fotografias e Clones

Como foi discutido, ZFS permite a criação de fotografias (snapshots). Fotografias são cópias de
leitura de um sistema de arquivo em um determinado ponto que pode ser usado para propósitos
de backup.
Para criar uma fotografia, executamos o comando zfs snapshot. Indicamos o diretório ZFS que
desejamos obter uma fotografia juntamente com um nome deste. Por exemplo, o seguinte
comando cria uma fotografia do diretório de projetos de user1 e nomeado como ver3backup.
# zfs snapshot mypool/user1/projects@ver3backup

Devido ao modo como ZFS armazena dados, são criadas fotografias imediatamente e não
demandam nenhum espaço adicional. Não existe nenhum processo adicional necessário, ZFS
preserva os dados originais e os bloqueia sempre que são feitas mudanças no diretório informado.
Todas as fotografias são armazenadas dentro do diretório zfs/snapshot localizado na raiz de
cada filesystem. Isto permite que os usuários possam conferir suas fotografias sem ter que ser
um administrador do sistema. Por exemplo, a fotografia que criamos está armazenada em:
/export/home/user1/.zfs/snapshot/ver3backup

Na qual o user1 pode acessar sem ter que ser um administrador de sistema.
Além disso, podemos executar um rollback no diretório a partir de uma fotografia. Rollback quer

Sistema Operacional 15
JEDITM

dizer que podemos, a qualquer momento, desfazer todas as mudanças realizadas no diretório a
partir da fotografia. Por exemplo, user1 pode cometer diversos erros na versão 4 do projeto,
deste modo, haveria uma necessidade de retornar à versão 3. Para reverter a situação a uma
fotografia denominada ver3backup, executamos o comando:
# zfs rollback -r mypool/user1/projects@ver3backup

Clones em ZFS são cópias de fotografias que podem ser escritas. Para se criar um clone,
indicamos a fotografia e o diretório para a cópia.
# zfs clone mypool/user1/project@ver3backup mypool/user1/project/ver3copy

Existem ainda muitos comandos para o aplicativo ZFS. Podemos verificar como utilizá-los
acessando o manual, através do comando:
# man zfs

Sistema Operacional 16
Módulo 8
Sistema Operacional

Lição 8
Regiões no Solaris

Versão 1.0 - Mar/2008


JEDITM

1. Objetivos
Uma região (zone) é um ambiente virtualizado no sistema operacional criado por uma única
instância. Na essência, é criado um "computador dentro de um computador"; um sistema
completamente virtual que existe sobre o sistema físico.
Nesta seção, discutiremos as regiões do Solaris. Começaremos com uma discussão sobre o que às
regiões são e quais vantagens de sua utilização sobre um sistema provedor de serviços. Essa lição
fornece uma visão geral ao mostrar quais comandos são necessários para criar uma região.
Finalmente, terminaremos com uma discussão sobre algumas opções de regiões comumente
utilizadas.
Ao final desta lição, o estudante será capaz de:
• Explicar o que são regiões no Solaris
• Criar uma região básica
• Estar familiarizado com alguns comandos avançados para a criação de regiões

Sistema Operacional 4
JEDITM

2. Introdução
2.1. Utilização típica para regiões

As regiões permitem múltiplas instâncias separadas do Solaris existindo independentemente uma


da outra em um só computador, o que pode ser bem vantajoso em certas situações. Considere um
escritório que necessite de um servidor WEB, de banco de dados, de desenvolvimento, de email e
de arquivo. Essa companhia possui duas opções:
● Opção 1: Um computador por serviço. Uma companhia executando esta opção não teria
escolha a não ser obter cinco computadores.
● Opção 2: Ter todos esses serviços em um só computador ou mais de um serviço por
computador. Nessa opção, há margem para haver alguns problemas de configuração,
particularmente se alguns desses serviços tiverem conflitos uns com os outros.
As regiões permitem uma terceira opção. Os serviços podem ser instalados cada um em sua
própria instância do Solaris, sendo executados em um único computador físico. Isso combina o
melhor dos dois mundos. Primeiramente, não há mais necessidade de comprar computadores
múltiplos já que as regiões permitem computadores virtuais sendo executados em um só sistema
autônomo. Em segundo lugar, como cada serviço existe em seu próprio sistema autônomo, não há
mais necessidade de se preocupar com conflitos de configuração; um serviço pode ser instalado
em uma região que está configurada precisamente com as necessidades desse serviço.

2.2. Vantagens

A utilização de regiões fornece as seguintes vantagens:

2.2.1. Segurança
Se um serviço de rede é feito para ser executado em uma região, invasores que pretendam
utilizar as vulnerabilidades deste serviço para ganhar acesso ao sistema somente poderão acessar
a região do serviço. Qualquer dano causado será limitado a essa região. Acessar outra região
seria como invadir um segundo computador, apesar do fato de que essas regiões na verdade são
executadas no mesmo computador físico.
Uma região pode ter usuários diferentes de outras regiões. O administrador do sistema global
pode escolher dar acesso aos usuários apenas a serviços específicos e não a todo sistema.
A utilização de regiões permite um segundo nível de administração, o superusuário da região.
Este usuário tem direitos de administrar apenas para uma região específica. Isso permite que o
administrador do sistema global (o superusuário da região global principal) delegue tarefas
administrativas da região de outros usuários enquanto mantém a habilidade de promover as
grandes mudanças necessárias no sistema.

2.2.2. Isolamento
As regiões trabalham isoladamente, não estão cientes da existência de outras regiões. Desta
forma, processos sendo executados em uma determinada região são incapazes de influenciar
processos em outras regiões. A configuração de uma região é importante apenas para esta região
e não afeta outras regiões. Uma aplicação sendo executada em uma região pode ter o benefício
de ser executada em um ambiente criado especificamente para ela, por isso, não há mais
necessidade de preocupações com detalhes de configurações.

2.2.3. Virtualização
A configuração do sistema global é irrelevante para uma região. As regiões podem ter suas
próprias customizações de ambiente virtual. Este ambiente pode até mesmo ser copiado em
diferentes máquinas, permitindo assim que uma região tenha o mesmo ambiente virtual mesmo
em diferentes máquinas com diferentes configurações.

Sistema Operacional 5
JEDITM

2.2.4. Granularidade
Há muitas opções disponíveis para configurar o nível de isolamento de uma região. Uma região
pode ter sua própria CPU, memória, espaço de disco rígido, ou ter estes recursos compartilhados
entre diferentes regiões mesmo sem saber que nenhum compartilhamento está sendo feito.

2.2.5. Ambiente
Uma aplicação não precisa ser recompilada para ser executada em outra região. O Solaris fornece
as mesmas bibliotecas e interfaces para todas as regiões. Existem apenas algumas restrições que
não permitem programas executar grandes mudanças enquanto dentro de uma região.
Adicionalmente, uma aplicação não é afetada pela existência de outras regiões, e é praticamente
inconsciente dessas outras regiões. As regiões são criadas para que uma aplicação possa ser
executada em uma instalação autônoma do Solaris.

2.3. Os dois tipos de região

Existem dois tipos de região no Solaris. A região global e as regiões não-globais.


A região global é a instalação padrão que é utilizada pelo Solaris. Toda a administração do
sistema é executada a partir desta região. Esta é a única região que está ciente de todas as
outras regiões que estão sendo executadas no sistema. É a partir desta região que a
administração de regiões é realizada.
As regiões não-globais são as regiões adicionais que são configuradas a partir da região global.
Essas são as regiões que são executadas sobre a região global. As regiões não-globais não estão
cientes de que são regiões ou da existência de outras regiões. Assim, a manutenção desta região
não é possível a partir de uma região não-global.
O superusuário em uma região global pode ser considerado o superusuário global, habilitado a
executar grandes mudanças no sistema, incluindo mudanças que afetam as outras regiões. O
superusuário em uma região não-global só pode administrar aquela região.

2.4. Mapeamento de tarefas por região

Para configurar regiões para um computador, as seguintes tarefas precisam ser realizadas:
1. Identificar quais aplicações serão executadas na região.
2. Determinar quantas regiões serão configuradas (pode-se ter um máximo de 8.192 regiões.
Quanto menos regiões, melhor para o sistema).
3. Determinar se uma região requer características de gerenciamento avançado de recursos.
4. Executar os passos de pré-configuração, tais como, determinar o endereço de IP da região,
nome do servidor, usuários para esta região, entre outros.
5. Gravar a configuração da região utilizando o comando zonecfg.
6. Configurar e instalar a região.
7. Carregar a região utilizando o comando zoneadm.
8. Acessar a região utilizando zlogin e customizar cada uma de acordo com o planejamento
inicial de configuração.

Sistema Operacional 6
JEDITM

3. Configurando uma região básica


Veremos agora como configurar uma região básica no Solaris. Mostraremos os comandos
necessários para realizar esta tarefa e conheceremos mais informações sobre regiões enquanto a
configuramos.

3.1. Iniciação do zonecfg

Para configurar uma região utilizarmos o comando zonecfg. Para executar este comando é
necessário estar conectado como superusuário e executá-lo com as seguintes opções:
# zonecfg -z myfirstzone

A opção -z indica o nome da região que se deseja configurar; neste caso, myfirstzone. Após
executar este comando, a tela exibirá o alerta do zonecfg do seguinte modo:
zonecfg:myfirstzone>

Indicando que o zonecfg está sendo executado e pronto para receber comandos. Configurar uma
região quer dizer executar comandos zonecfg. Como o alerta indica, esses comandos serão
utilizados para configurar myfirstzone.
Há dois modos do zonecfg:
1. Modo global – comandos zonecfg que são executados no modo global são comandos que
configuram os atributos da região, tais como onde a região deve ser instalada ou se uma
região deve ou não ser automaticamente carregada ao iniciar.
2. Modo recurso – significa que os próximos comandos são utilizados para descrever
recursos na região, tais como os arquivos de sistema ou rede.
Configurar uma região implica alternar entre estes dois modos, executando comandos que
indicam os atributos da região e comandos que descrevem seus recursos. Quando o zonecfg é
iniciado, está automaticamente no modo global.

3.2. Configuração de região básica

A figura seguinte mostra os diferentes comandos necessários para configurar myfirstzone. A


seguir discutiremos estes comandos de forma mais detalhada.
zonecfg:myfirstzone> create
zonecfg:myfirstzone> set zonepath=/export/home/myfirstzone
zonecfg:myfirstzone> set autoboot=true
zonecfg:myfirstzone> add fs
zonecfg:myfirstzone:fs> set dir=/usr/local
zonecfg:myfirstzone:fs> set special=/opt/local
zonecfg:myfirstzone:fs> set type=lofs
zonecfg:myfirstzone:fs> end
zonecfg:myfirstzone> add net
zonecfg:myfirstzone:net> set address=192.168.0.1/24
zonecfg:myfirstzone:net> set physical=hme0
zonecfg:myfirstzone:net> end
zonecfg:myfirstzone> add attr
zonecfg:myfirstzone:attr> set type=string
zonecfg:myfirstzone:attr> set value="my first zone"
zonecfg:myfirstzone:attr> end
zonecfg:myfirstzone> verify
zonecfg:myfirstzone> commit
zonecfg:myfirstzone> exit

3.3. Criação da configuração de uma região

Inicia-se a criação da configuração de um região com o comando create.

Sistema Operacional 7
JEDITM

zonecfg:myfirstzone> create

Caso seja informado o nome de uma região não existente no comando zonecfg, o sistema
solicitará a execução do comando create.

3.4. Configuração da raiz de diretório da região

A raiz de diretório de uma região existe em um subdiretório do arquivo de sistema da região


global. Podemos especificá-la através do comando zonepath:
zonecfg:myfirstzone> set zonepath=/export/home/myfirstzone

O comando informa ao zonecfg que a raiz do diretório myfirstzone estará localizada em


/export/home/myfirstzone. Necessariamente este diretório deve existir.

3.5. Carregamento automático

O comando set autoboot especifica quando uma região será ou não iniciada automaticamente
quando a região global for iniciada.
zonecfg:myfirstzone> set autoboot=true

Se esta opção não for enviada, então a região deverá ter uma inicialização manual usando o
comando zoneadm.

3.6. Arquivo de sistema da região

Por padrão, certos diretórios da região global são herdados pelas regiões não-globais para
fornecer um sistema de trabalho à região. O diretórios inherit-pkg-dir (/lib, /platform, /sbin e
/usr) não são copiados para dentro da região. O que a região recebe é um endereço somente de
leitura para esses diretórios na região global. Permite que a região economize espaço de arquivo.
Fora isso, quaisquer mudanças na região global particularmente para estes diretórios também
refletirão nos arquivos de sistema das regiões não-globais.
Para adicionar arquivos de sistemas, utilizamos o comando add fs:
zonecfg:myfirstzone> add fs

O comando add fs alterna do modo global para o modo de recurso. Como dito, o modo de recurso
significa que comandos adicionais especificados são utilizados para descrever um recurso para a
região. O alerta zonecfg também muda para mostrar que está sendo editado um recurso de
arquivo de sistema.
zonecfg:myfirstzone> add fs
zonecfg:myfirstzone:fs>

3.7. Adicionar um sistema de arquivos

A seguir estão os comandos para definir um recurso de sistema de arquivos para myfirstzone:
zonecfg:myfirstzone> add fs
zonecfg:myfirstzone:fs> set dir=/usr/local
zonecfg:myfirstzone:fs> set special=/opt/local
zonecfg:myfirstzone:fs> set type=lofs
zonecfg:myfirstzone:fs> end

O comando especial set especifica qual diretório da região global deve ser adicionado. Este
diretório é colocado na região de sistema de arquivos no diretório especificado pelo set dir. No
nosso exemplo, /opt/local, situado na região global, é colocado em /usr/local em myfirstzone.
O comando set type indica qual o tipo de sistema de arquivos do diretório a ser montado. O tipo
de sistema de arquivos explica como o núcleo lidará com este diretório. O sistema de arquivos
virtual loopback (lofs) indica que o diretório montado é o mesmo diretório no sistema de arquivos

Sistema Operacional 8
JEDITM

global, entretanto, é acessado através de um caminho diferente. Isto é semelhante ao inherit-


pkg-directories.
Devido a isso, todas as mudanças feitas no diretório global refletem também no diretório da
região. Para o exemplo, se um arquivo for adicionado ao diretório /opt/local na região global
seguido de /usr/local em myfirstzone este irá mudar também.
Observamos que o diretório /opt/local na região global deve ser um diretório existente.
Para terminar a configuração do sistema de arquivo, temos o comando:
zonecfg:myfirstzone:fs> end
zonecfg:myfirstzone>

Isto muda a zonecfg de volta ao modo global como indicado pela mudança do prompt. Podemos
agora adicionar novos sistemas de arquivos com comando add fs.

3.8. Adicionar uma rede

Uma região pode ter seu próprio endereço IP individual. Não há necessidade de interfaces de rede
adicionais para cada novo endereço IP definido; a região global gerencia automaticamente o
roteamento. Podemos usar o comando add net para definir este gerenciamento.
Vejamos a seqüência de instruções para configurar a rede myfirstzone.
zonecfg:myfirstzone> add net
zonecfg:myfirstzone:net> set address=192.168.0.1/24
zonecfg:myfirstzone:net> set physical=hme0
zonecfg:myfirstzone:net> end

Nosso exemplo configura o endereço IP de myfirstzone para 192.168.0.1 com uma máscara de
rede definida como /24 (255.255.255.0). Este endereço IP está vinculado a uma placa de rede
hme0 (este resultado pode ser diferente nas diversas configurações).

3.9. Descrição da região

Um recurso para a especificação da região é o comando add attr, que podemos utilizar para
acrescentar um comentário para a região.
zonecfg:myfirstzone> add attr
zonecfg:myfirstzone:attr> set type=string
zonecfg:myfirstzone:attr> set value="my first zone"
zonecfg:myfirstzone:attr> end

3.10. Finalizar a configuração

Podemos utilizar os seguintes comandos para finalizar a configuração.


zonecfg:myfirstzone> verify
zonecfg:myfirstzone> commit
zonecfg:myfirstzone> exit

O comando verify verifica se a configuração da região foi digitada corretamente. Este comando
permite visualizar erros. O comando commit salva a configuração da região. A região ainda não
está instalada, apenas a configuração é armazenada. E o comando exit sai de zonecfg.

3.11. Eliminar atributos

Se errar a configuração de uma região, é possível utilizar o comando remove. Este comando
possui duas maneiras que dependem da modalidade de zonecfg.
No modo global, remove <resource-type> especifica a eliminação de um determinado recurso.
Por exemplo, o seguinte comando elimina o comentário:
zonecfg:myfirstzone> remove attr

Sistema Operacional 9
JEDITM

No modo de recurso, remove <property-name> <property-value> elimina um elemento


daquela descrição dos recursos. Por exemplo, podemos eliminar um endereço IP com este
comando. Para fazer isso precisamos estar no recurso net.
zonecfg:myfirstzone:net> remove address=192.168.0.1/24

Este é o mesmo formato do comando remove quando se deseja eliminar qualquer um dos
atributos globais. O exemplo a seguir elimina o atributo autoboot:
zonecfg:myfirstzone> remove autoboot=true

Quaisquer alterações feitas à configuração devem ser atualizadas.

3.12. Comandos zonecfg adicionais

Para visualizar a configuração de uma região, pode-se utilizar o comando export.


O comando zonecfg inclui um auxílio para seus comandos internos, que podemos utilizar para
pedir informações adicionais. Digitando simplemente help exibirá todos seus comandos.
Digitando help <comando> exibirá todas as informações sobre um comando específico.

Sistema Operacional 10
JEDITM

4. Zoneadm
O comando zoneadm é usado para administrar as regiões. Este comando vem com sub-
comandos que são utilizados para indicar o que fazer com uma região específica (por meio da
opção -z). Discutiremos esses comandos com os quais continuaremos com a configuração básica
da região.

4.1. Instalação e inicialização

Depois de uma região ter sido configurada corretamente, esta pode ser instalada através do
comando:
# zoneadm -z myfirstzone install

O sistema irá instalar a myfirstzone. Na tela serão mostrados os pacotes que estão sendo
copiados para a nova região. Após o término da instalação, podemos iniciar a região através do
comando:
# zoneadm -z myfirstzone boot

Se não definirmos a propriedade da região para um autoboot, este deverá ser o comando que
utilizaremos para iniciar a região.

4.2. Listagem de regiões

Para listar as regiões existentes podemos utilizar o sub-comando list:


#zoneadm list -v
ID NAME STATUS
0 global running
1 myfirstzone installed

O status pode assumir um dos seguintes valores para a região:


● configured – foi devidamente configurada com zonecfg, entretanto ainda não foi
instalada
● incomplete – está no processo de instalação
● installed – foi instalada, entretanto ainda não está funcionando (não foi atribuída uma
plataforma virtual à região)
● ready – foi atribuída a uma plataforma virtual e está pronta para aceitar processos de
usuário
● running – está rodando com processos de usuário
● shutting down – está sendo encerrada

4.3. Comandos zoneadm adicionais

● zoneadm -z myfirstzone halt – interrompe a região (e a coloca em estado ready)


● zoneadm -z myfirstzone reboot – reinicializa a região
● zoneadm -z myfirstzone uninstall – remove a instalação da região
● zoneadm -z myfirstzone delete – remove a configuração da região

Sistema Operacional 11
JEDITM

5. Logging in
Nesta fase, a instalação de myfirstzone ainda não está concluída. Para concluirmos sua
instalação, devemos acessar a região através do comando:
# zlogin -C myfirstzone

A opção -C é a forma para acessar uma região que não está em estado running. Mais tarde,
quando a instalação for concluída, não será mais necessário informar esta opção.
Como primeiro acesso, será necessário inserir informações sobre o sistema, semelhante às caixas
de diálogo encontradas durante a instalação do Solaris. São feitas perguntas como a linguagem
do sistema, o hostname da região, a senha do administrador (para o administrador da região).
Depois desse diálogo ter sido concluído, a região vai agora entrar em estado running e pode
aceitar processos do usuário.
Nesta fase, é possível acessar uma região e realizar sua personalização. Tais como, adicionar
novos usuários, instalar novos programas e configurar os serviços que serão necessários.

Sistema Operacional 12
JEDITM

6. Eliminar uma região


Executamos os seguintes passos para eliminar uma região.
Primeiro devemos encerrar a região:
# zlogin myfirstzone shutdown

Em seguida, eliminar a instalação da região. A opção -F corresponde forçar uma desinstalação:


# zoneadm -z myfirstzone uninstall -F

E eliminar a configuração da região:


# zoneadm -z myfirstzone delete -F

Sistema Operacional 13
Módulo 8
Sistema Operacional

Apêndice A
Exercícios

Versão 1.0 - Mar/2008


JEDITM

1. Objetivos
Neste apêndice veremos exercícios que podem ser realizados para reforçar o conhecimento sobre
cada lição deste módulo.
Não será apresentada a resolução dos mesmos, cabe ao estudante tirar suas próprias conclusões.

Sistema Operacional 4
JEDITM

2. Exercícios
2.1. Lição 1 – Introdução ao Solaris

1. Abrir a lista de processos no seu sistema operacional atual (Ctrl-Alt-Del no WindowsXP,


ps aux no terminal em sistemas Unix) e observe os diferentes processos que estão sendo
executados no sistema. Pesquise cada um desses processos. Quais processos são parte do
sistema operacional, e quais não são? Por que?
2. Além dos objetivos aqui discutidos para um sistema operacional, que outros objetivos
deveriam ser incluídos?
3. Sistemas Operacionais, tais como, Linux, MacOS, Windows e Solaris são conhecidos.
Entretanto, existem muitos outros sistemas operacionais. Pesquise e faça uma lista de pelo
menos outros cinco sistemas e uma breve descrição de cada.
4. Uma técnica chamada de virtualização permite um sistema operacional rodar como uma
aplicação sobre um outro sistema operacional. Leia mais sobre este assunto e o descreva
com suas palavras.

2.2. Lição 2 - Instalação

1. Considerar o efeito da Internet no seu cotidiano. Como deveriam evoluir os sistemas


operacionais considerando este fato?
2. Que outras aplicações um sistema robusto demandaria? E um sistema distribuído?

2.3. Lição 3 – Comandos Básicos e Scripting

1. Criar um script para copiar um arquivo. Deverá mostrar uma mensagem de erro caso o
arquivo não exista. Por exemplo:
$ ./politecp /etc/passwd /export/home/alice
Deseja que eu copie o arquivo /etc/passwd para /export/home/alice
Cópia finalizada! Tenha um bom dia.

2. Criar um script para aceitar um número informado pelo usuário e verificar se este é maior
ou menor do que um determinado número.
Entre o número [0-100]: 53
Muito grande
Entre o número [0-100]: 23
Muito pequeno
Entre o número [0-100]: 42
Parabéns, você acertou em 3 tentativas!

3. Criar um script para listar todo o conteúdo do diretório /usr/sbin em ordem decrescente
de tamanho e direcionar a saída para um determinado arquivo.
4. Criar um script para modificar a lista de controle de acesso de um arquivo por meio de um
conjunto de perguntas. Exemplo:
$ ./mychangemod test.txt
Você quer que este arquivo possa ser lido pelo usuário [s/n]? s
Você quer que este arquivo possa ser gravado pelo usuário [s/n]? s
Você quer que este arquivo possa ser executado pelo usuário [s/n]? n
Você quer que este arquivo possa ser lido pelo grupo [s/n]? s
Você quer que este arquivo possa ser gravado pelo grupo [s/n]? s
Você quer que este arquivo possa ser executado pelo grupo [s/n]? n
Você quer que este arquivo possa ser lido por outros [s/n]? s
Você quer que este arquivo possa ser gravado por outros [s/n]? n
Você quer que este arquivo possa ser executado por outros [s/n]? n
Rodando chmod 664 test.txt
Ok

Sistema Operacional 5
JEDITM

5. Criar um programa Java que use System.out e System.err. Use os comandos de


redirecionamento para mostrar qual saída é a regular e qual é a de erro.

2.4. Lição 4 – Processo no Solaris

1. Dados os seguintes processos e seus tempos de de CPU, todos chegando ao tempo 0.


● P1 – 5 segundos
● P2 – 15 segundos
● P3 – 9 segundos
● P4 – 6 segundos
● P5 – 2 segundos
Apresente a tabela de execução de processos para esses processos, segundo o esquema de
escalamento abaixo:
a) Primeiro a chegar e primeiro servido
b) Trabalho mais curto
c) Cíclico com Quantum de Tempo de 2
2. Bob, que é um barbeiro que corta cabelos de acordo com um esquema de prioridades, com
preempção, mas sem envelhecimento, recebeu os seguintes clientes ao abrir a barbearia
às 8 da manhã:

Nome Tempo de Corte Prioridade Hora de Chegada


Alfred 50 min. Média 8:00
Eugene 5 min. Muito alta 8:30
Vincent 10 min. Média 8:45
Dennis 5 min. Baixa 9:00
Jerico 25 min. Alta 9:10

Apresente o Gráfico de Execução de Cortes de Cabelo de Bob, tempo de espera e tempo de


troca por cliente, e Utilização do Barbeiro (utilização de CPU) dados os seguintes fatos
adicionais:
● Divida a execução de Cortes de Cabelo em fatias de 5 minutos cada
● Se tiver que escolher entre clientes de mesma prioridade, escolher o que chegou
primeiro
● Tempo de mudança de contexto de Bob: 5 min. A mudança de contexto acontece
sempre que Bob muda de cliente, independente de ter ou não terminado o corte do
cliente
● No início de cada fatia de 5 minutos, Bob escolhe a próxima pessoa para cortar o
cabelo, ou seja, a decisão de cortar o cabelo de alguém é tomada antes que a
mudança de contexto ocorra
● Se o seu cabelo não está sendo cortado então você está esperando, incluir este
tempo de Mudança de Contexto
3. Charlie, parceiro de Bob, que faz alocação de tempo por processo cíclico com intervalo de
tempo de 20 minutos e 5 minutos de tempo de mudança de contexto, recebeu os
seguintes clientes um dia, na seguinte ordem, às 8 da manhã:

Nome Tempo de Corte

Sistema Operacional 6
JEDITM

Steve 50 min.
Bert 5 min.
John 10 min.
Mark 5 min.
Henry 25 min.

4. Apresente o Gráfico de Execução de Cortes de Cabelo de Charlie, tempo de espera e tempo


de troca por cliente, e Utilização do Barbeiro.

2.5. Lição 5 – Java Thread

1. Há três crianças querendo usar um balanço. Contudo, somente uma delas pode usar o
balanço por vez. Cada criança alterna entre ter vontade de se balançar ou fazer uma pausa
para recuperar o fôlego, enquanto outra criança usa o balanço. Implemente isto com o uso
de threads. Faça com que cada thread de criança tenha um nome associado via construtor
(i.e., Kid k1 = new Kid(“alice”)). Use um retardo aleatório de no máximo 10 segundos para
usar o balanço e para tomar fôlego. A saída deverá ser como a mostrada abaixo:
Alice: Eu quero usar o balanço
Alice: Eu uso o balanço por 5940 mili-segundos
Bob: Eu quero usar o balanço
Charlie: Eu quero usar o balanço
Alice: Eu me cansei. Pausa de 4430 mili-segundos
Bob: Eu uso o balanço por 4361 mili-segundos
Alice: Eu quero usar o balanço
Bob: Eu me cansei. Pausa de 6940 mili-segundos
Charlie: Eu uso o balanço por 4301 mili-segundos
...

2. Crie duas threads, MyInput e MyOutput. MyInput é uma thread que aceita nomes de
usuários. As entradas deste usuário são impressas por MyOutput. (Note que MyOutput tem
que esperar que você digite seus comandos antes de você poder imprimi-los). MyInput e
MyOutput fazem isto sucessivamente até você digitar fim.
MyOutput: Aguardando entrada do usuário
MyInput: Por favor entre um nome: Alice
MyOutput: Olá Alice!
MyOutput: Aguardando entrada do usuário
MyInput: Por favor entre um nome: Bob
MyOutput: Olá Bob!
MyOuput: Aguardando entrada do usuário
MyInput: Por favor entre um nome: fim
MyInput terminando...
MyOutput terminando...

3. Alice e Bob estão no escritório. Há uma porta pela qual eles têm que passar de vez em
quando. Como Bob é bem educado, se ele chega até à porta, ele se assegura de sempre
abrir a porta e esperar que Alice passe pela porta antes dele. Alice, por si só, simplemente
passa pela porta. Sua tarefa é escrever uma thread Alice e Bob que imite este
comportamento. Para simular ter que passar por aquela porta, inclua um retardo aleatório
em cada thread, de no máximo 5 segundos. A saída do seu código deve ser como se
segue:
Alice: Eu passo pela porta
Alice: Eu passo pela porta
Bob: Eu chego até a porta e espero por Alice...
Alice: Eu passo pela porta
Bob: Eu sigo a Alice
Alice: Eu passo pela porta
Bob: Eu chego até a porta e espero por Alice...
...

Sistema Operacional 7
JEDITM

4. Modifique o problema Produtor-Consumidor para considerar o cenário de buffer cheio pela


limitação do tamanho do vetor em 10 e fazendo o Consumidor mais lento do que o
Produtor.
5. Implemente uma solução para o problema Leitores-Escritores através da criação de 2
threads de Leitor e 2 threads de Escritor e um objeto compartilhado MyFile com os
métodos read() e write(). Novamente, muitas threads poderiam estar lendo o arquivo, mas
somente uma das threads poderá estar escrevendo no arquivo. Para simular os retardos de
“leitura” e “escrita”, use Thread.sleep() fazendo com que cada thread pause por no
máximo 10 segundos (retardo aleatório) durante a leitura e a escrita. Sua saída deverá se
parecer com a seguinte:
Reader1: Eu quero ler o arquivo
Reader1: Lendo arquivo por 1420 mili-segundos
Reader2: Eu quero ler o arquivo
Reader2: Lendo arquivo por 4504 mili-segundos
Writer1: Eu quero escrever no arquivo
Writer2: Eu quero escrever no arquivo
Reader1: Leitura do arquivo terminada
Reader2: Leitura do arquivo terminada
Writer1: Escrevendo no arquivo por 5930 mili-segundos
Reader1: Eu quero ler o arquivo
Reader2: Eu quero ler o arquivo
Writer1: Escrita no arquivo terminada
Writer2: Escrevendo no arquivo por 6041 mili-segundos
Writer2: Escrita no arquivo terminada
Reader1: Lendo arquivo por 9543 mili-segundos
Reader2: Lendo arquivo por 3461 mili-segundos
...

2.6. Lição 6 – Observabilidade

1. Há uma ponte de faixa única perto da sua escola. Um dia você encontrou ao acaso o
seguinte cenário, envolvendo 6 carros.

a) Qual é o Gráfico de Alocação de Recursos para esta ponte? (Dica: A ponte tem o
comprimento exato de seis recursos)
b) A situação foi resolvida empurrando dois carros para fora da ponte. Que esquema de
recuperação de Deadlock é este?
c) Dê dois esquemas para prevenir esta situação de ocorrer novamente no futuro. Indique
que condição necessária é eliminada.
2. Você ganhou uma quantidade expressiva de dinheiro numa loteria e decidiu aplicar em um
banco. Seu critério primário na escolha do banco é se há dinheiro suficiente para satisfazer
todas as suas necessidades.

Nome Empréstimo Atual Teto do Empréstimo


Alice $ 5000 $ 7000
Verde-2 $0 $ 9000
Azul-3 $ 3000 $ 15000
Amarelo-4 $ 1000 $ 20000
Rosa-5 $ 1000 $ 2000

Sistema Operacional 8
JEDITM

Quantia disponível: $ 3000

2.7. Lição 7 – ZFS

1. Crie um nome de usuário douglas com o diretório home em /export/home/douglas.


Agora, faça deste diretório um zpool espelhado usando dois arquivos, pool1 e pool2,
localizados no diretório raiz (root). Além disso, faça com que este pool tenha uma cota de
disco de 1GB.

2.8. Lição 8 – Zonas no Solaris

1. Prepare uma zona cujo diretório raiz esteja armazenado no diretório /mysecondzone na
zona global. Faça com que esta zona monte um diretório especial chamado /shared tanto
no diretório raiz da zona global quanto na zona local. Demonstre (depois da instalação)
que quaisquer mudanças feitas na zona global são também vistas na zona local.

Sistema Operacional 9
JEDITM

Parceiros que tornaram JEDITM possível

Instituto CTS
Patrocinador do DFJUG.

Sun Microsystems
Fornecimento de servidor de dados para o armazenamento dos vídeo-aulas.

Java Research and Development Center da Universidade das Filipinas


Criador da Iniciativa JEDITM.

DFJUG
Detentor dos direitos do JEDITM nos países de língua portuguesa.

Politec
Suporte e apoio financeiro e logístico a todo o processo.

Instituto Gaudium
Fornecimento da sua infra-estrutura de hardware de seus servidores para que os
milhares de alunos possam acessar o material do curso simultaneamente.

Sistema Operacional 10

You might also like