Capítulo 18 Tópicos Avançados

18.1. Como eu posso aprender mais sobre as características internas do FreeBSD?
18.2. Como posso contribuir com o projeto FreeBSD?
18.3. O que são SNAPs e RELEASEs?
18.4. Como faço a minha própria distribuição personalizada?
18.5. Por que o make world sobrescreve os binários já instalados?
18.6. Por que quando meu sistema inicializa, ele diz “(bus speed defaulted)”?
18.7. Posso acompanhar a série -CURRENT mesmo tendo acesso limitado à Internet?
18.8. Como o FreeBSD foi dividido em arquivos de 240k?
18.9. Eu escrevi uma extensão para o kernel; a quem eu envio?
18.10. Como as placas Plug N Play ISA são detectadas e inicializadas?
18.11. Vocês podem definir um número principal para um driver de dispositivo que eu escrevi?
18.12. E sobre políticas alternativas de layout de diretórios?
18.13. O que fazer com os dados que eu vejo quando tenho um kernel panic?
18.14. Por que a dlsym() não funciona mais nos executáveis ELF?
18.15. Como eu posso aumentar ou reduzir o espaço de endereçamento disponível para o kernel?

18.1. Como eu posso aprender mais sobre as características internas do FreeBSD?

Atualmente não há nenhum livro específico sobre as características internas do Sistema Operacional FreeBSD. Contudo, a maior parte do conhecimento genérico sobre UNIX pode ser aplicado diretamente a ele. Além disso existem livros específicos para sistemas BSD cuja leitura é recomendada.

Para uma lista, verifique a sessão de bibliografia sobre características internas dos sistemas operacionais no Manual do FreeBSD.

18.2. Como posso contribuir com o projeto FreeBSD?

Por gentileza, consulte o artigo Contribuindo com o Projeto FreeBSD para obter algumas dicas sobre o assunto. Toda ajuda é mais que bem vinda!

18.3. O que são SNAPs e RELEASEs?

Atualmente existem três séries ativas/semi-ativas no Repositório CVS do projeto FreeBSD (a RELENG_2 que é provavelmente alterada somente duas vezes ao ano, sendo esta a razão de termos somente três séries em desenvolvimento):

  • RELENG_2_2 ou 2.2-STABLE

  • RELENG_3 ou 3.X-STABLE

  • RELENG_4 ou 4-STABLE

  • HEAD ou -CURRENT ou 5.0-CURRENT

HEAD não é um nome de uma tag de série, como os outros dois; é somente uma constante simbólica para “o atual desenvolvimento corrente, mas não de série” a qual nós simplesmente nos referimos como “-CURRENT”.

Neste momento, a “-CURRENT” se refere ao desenvolvimento atual do FreeBSD 5.0. A série 4-STABLE, RELENG_4 originou-se da “-CURRENT” em Março de 2000.

A série 2.2-STABLE, RELENG_2_2, originou-se da “-CURRENT” em Novembro de 1996, e foi praticamente descontinuada.

18.4. Como faço a minha própria distribuição personalizada?

Por gentileza, consulte o artigo sobre a Engenharia de Releases..

18.5. Por que o make world sobrescreve os binários já instalados?

Porque essa é a idéia geral sobre como ele deve funcionar; como seu nome sugere, o make world reconstrói todo o sistema binário a partir do zero, garantindo que o usuário tenha um ambiente limpo e consistente ao final da operação (é por isso que o processo demora tanto).

Se a variável de ambiente DESTDIR estiver definida enquanto um make world ou make installworld estiver sendo executado, os binários recém criados serão distribuídos no diretório definido em ${DESTDIR}, criando no mesmo uma réplica do conteúdo do / do sistema. Algumas alterações aleatórias nas bibliotecas compartilhadas podem ocasionar falhas na hora de reconstruir o sistema com o make world.

18.6. Por que quando meu sistema inicializa, ele diz “(bus speed defaulted)”?

Os controladores SCSI Adaptec 1542 permitem que o usuário defina a velocidade de acesso ao barramento por meio de software. Algumas versões mais antigas deste dispositivo tentavam determinar automaticamente a maior velocidade possível e tentavam ajustar sua velocidade à esse limite máximo. Descobriu-se contudo, que esse comportamento as vezes era prejudicial, e fazia com que algumas máquinas não funcionassem de forma adequada, por este motivo essa característica agora vem desabilitada por default, para ativá-la é necessário definir a opção TUNE_1542 no kernel do FreeBSD. Essa opção, em sistemas onde ela se aplica, provavelmente assegura que seus discos sejam acessados de forma mais rápida e eficiente; contudo, em sistemas onde o uso desse algoritmo é inviável, pode resultar em perda de dados.

18.7. Posso acompanhar a série -CURRENT mesmo tendo acesso limitado à Internet?

Sim, é possível acompanhar a série de desenvolvimento sem precisar baixar sempre todo o codigo fonte do sistema, basta utilizar o recurso de CTM.

18.8. Como o FreeBSD foi dividido em arquivos de 240k?

O comando split que acompanha as novas versões dos sistemas BSD têm uma opção -b que permite dividir os arquivos em limites arbitrários de bytes.

Eis um exemplo tirado do /usr/src/Makefile.

bin-tarball:
(cd ${DISTDIR}; \
tar cf - . \
gzip --no-name -9 -c | \
split -b 240640 - \
${RELEASEDIR}/tarballs/bindist/bin_tgz.)

18.9. Eu escrevi uma extensão para o kernel; a quem eu envio?

Por gentileza, consulte o artigo “Contribuindo com o Projeto FreeBSD” para obter mais informações sobre como enviar código fonte ao projeto.

E obrigado pelo seu interesse! :)

18.10. Como as placas Plug N Play ISA são detectadas e inicializadas?

Por: Frank Durda IV

Simplificando, existem poucas portas de E/S que todas as placas PnP respondem quando o sistema indaga se algum dispositivo está usando-a. Então, quando a rotina de procura do PnP começa, ele pergunta se há alguma placa PnP presente, e todas as placas PnP respondem com seus respectivos números de modelo para uma leitura de E/S da mesma porta. A rotina de procura recebe então um sinal wired-OR representando um “sim” à pergunta em questão. Ao menos um bit positivo constitui essa resposta. Então o código de procura é capaz de fazer com que as placas com o modelo de identificação (atribuído pela Microsoft/Intel) inferior a X sejam colocados em modo “off-line”. Ele então irá verificar se alguma placa respondeu a consulta. Se a resposta for 0 o sistema assume que não há placas com identificação acima de X. Depois a rotina de busca verifica se há alguma placa cujo ID é inferior a X. Se a resposta for positiva, a rotina de busca sabe que ainda existem placas identificadas com um valor menor que X. Aí a busca tenta identificar placas com ID superior à X (limite / 4) e ordena que entrem em modo off-line. Repete-se o ciclo de pesquisas e identificações nessa forma semi-binária até que um número necessário de interações seja concluído. Ao final do processo o sistema terá identificado todas as placas PnP presentes na máquina em questão, com o número de interações necessárias sempre menor que 2^64.

Os IDs (códigos de identificação) são dois campos de 32-bits (portanto, 2^64) acrescidos de 8 bits que é o checksum (verificação de consistência de dados). Os primeiros 32 bits identificam o fabricante da placa. Nenhum fabricante assume isso, mas podemos perceber que diferentes tipos de placas do mesmo fabricante costumam ter diferentes identificações de 32-bit. O motivo correto, não se sabe, mas percebe-se que 32 bits exclusivos para os fabricantes chega a ser um exagero.

Os últimos 32 bits é um número serial que torna a identificação dessa placa única. O fabricante não pode nunca produzir uma placa que tenha os 32 bits finais iguais, a não ser que os 32 bits iniciais sejam distintos. Dessa forma é possível existir várias placas do mesmo tipo e fabricante, e ainda assim todos os 64 bits dessas placas serem únicos.

Os grupos de 32 bits nunca podem ser todos zero. Isso permite ao wired-OR identificar bits não nulos durante a procura binária inicial.

Uma vez que o sistema tenha identificado todas as IDs presentes, ele vai reativar cada placa, uma por vez (pela mesma porta de E/S) e achar os recursos que cada uma necessita, quais opções de interrupções estão disponíveis, etc. Uma busca é feita em todas as placas para obter estas informações.

Tal informação é então combinada com as informações encontradas nos arquivos ECU, no sistema, ou então da MLB BIOS. O suporte da BIOS PnP e da ECU costuma ser sintética, portanto os periféricos não são exatamente PnP como é dito. Contudo, ao examinar as informações da da BIOS e da ECU, as rotinas de busca podem identificar dispositivos ditos PnP e evitar que eles requeiram recursos também necessários por outros dispositivos, que por sua vez não podem realocar tais valores automaticamente.

Os dispositivos PnP são visitados mais uma vez e recebem seus endereços de E/S, DMA, IRQ e endereçamentos atribuídos na memória. Os dispositivos permaneceram naquela ordem até a próxima inicialização do sistema, apesar de que nada impede que eles sejam movidos quando se desejar.

Essa explicação é muito simplista, mas provavelmente você entendeu a idéia geral do comportamento PnP.

A Microsoft fez um exame sobre algumas das portas primárias de status de impressoras para fazer PnP, dentro da lógica que nenhuma placa poderia decodificar aqueles endereços para os ciclos opostos de E/S. Eu encontrei uma placa genuína de impressora IBM que enviou dados decodificados da porta de status durante o começo do período da proposta de revisão do PnP, mas a Microsoft “ficou brava”. Então eles resolveram fazer um envio para a porta de status da impressora, de forma a justar o endereço usado (naquele instante + 0x800) e uma terceira porta de E/S para a leitura que tecnicamente pode ser localizada em qualquer lugar entre 0x200 e 0x3ff.

18.11. Vocês podem definir um número principal para um driver de dispositivo que eu escrevi?

Isso depende se você pretende tornar o driver disponível para o público. Se sim, então por favor nos mande uma cópia do código-fonte do driver, mais as devidas modificações para o files.i386, um exemplo do arquivo de configuração, e os devidos códigos do MAKEDEV(8) para criar qualquer arquivo especial que seu dispositivo precise. Se você não pode, ou está impedido por causa de restrições de licença, então o character major number 32 e o block major number 8 estão reservados especificadamente para este propósito; por favor, use-os. De qualquer maneira, nós gostaríamos de obter maiores informações sobre seu driver na lista de discussão de assuntos técnicos relacionados ao FreeBSD.

18.12. E sobre políticas alternativas de layout de diretórios?

Em resposta a questão da política de formatos alternativos para diretórios, o esquema que está atualmente em uso está imutável desde quando eu o escrevi em 1983. Eu escrevi aquela política para o FFS (fast filesystem) original, e nunca o revisei. Ele funciona bem em evitar que os os grupos de cilindros sejam completamente preenchidos. Como muitos de vocês notaram, ele funciona mediocremente para procura. A maioria dos sistemas de arquivos são criados à partir de arquivos que foram criados por uma primeira procura em profundidade (depth first search, também conhecido como ftw). Estes diretórios acabam sendo distribuídos pelo grupo de cilindros, criando assim um cenário horrível em relação a futuras primeiras buscas de profundidade. Se pudéssemos saber o número total de diretórios a serem criados, a solução seria criar (total / fs_ncg) por grupo de cilindros antes de movê-los. Evidentemente, seria necessário criar um conjunto de métodos heurísticos para adivinhar esse número. Mesmo usando um pequeno número fixo, digamos 10, ele produziria um aumento na ordem de magnitude. Para diferenciar restaurações de operações normais (quando o algoritmo atual é provavelmente mais sensível), você poderia usar o agrupamento acima de 10 se eles fossem finalizados dentro de uma janela de dez segundos. De qualquer maneira, minha conclusão é que isso é uma área pronta para experimentações.

Kirk McKusick, Setembro de 1998

18.13. O que fazer com os dados que eu vejo quando tenho um kernel panic?

[Esta seção foi extraída de um e-mail escrito por Bill Paul na freebsd-current por Dag-Erling C. Smørgrav , que arrumou alguns problemas de impressão e adicionou os comentários entre chaves]

From: Bill Paul <[email protected]>
Subject: Re: the fs fun never stops
To: Ben Rosengart
Date: Sun, 20 Sep 1998 15:22:50 -0400 (EDT)
Cc: [email protected]

Ben Rosengart posted the following panic message]

> Fatal trap 12: page fault while in kernel mode
> fault virtual address   = 0x40
> fault code              = supervisor read, page not present
> instruction pointer     = 0x8:0xf014a7e5
                                ^^^^^^^^^^
> stack pointer           = 0x10:0xf4ed6f24
> frame pointer           = 0x10:0xf4ed6f28
> code segment            = base 0x0, limit 0xfffff, type 0x1b
>                         = DPL 0, pres 1, def32 1, gran 1
> processor eflags        = interrupt enabled, resume, IOPL = 0
> current process         = 80 (mount)
> interrupt mask          =
> trap number             = 12
> panic: page fault

[Quando] você vê uma mensagem como essa, não é suficiente somente reproduzí-la e enviá-la em um e-mail. O valor do ponteiro de instrução (instruction pointer) que eu destaquei acima é muito importante; infelizmente, ele também depende de configuração. Em outras palavras, os valores variam de acordo com a exata imagem do kernel que você estiver usando. Se você estiver usando uma imagem GENERIC do kernel de um dos snapshots, então é possível que alguém acompanhe a função ofensiva, mas se você está rodando um kernel customizado então só você pode nos dizer aonde a falha ocorreu.

O que você deve fazer é isso:

  1. Anote o valor do ponteiro de instrução. Observe que o 0x8: parte do começo não é significante. Nesse caso é o 0xf0xxxxxx que nós queremos.

  2. Quando o sistema reinicializar, faça o seguinte:

    % nm -n /kernel.that.caused.the.panic | grep f0xxxxxx
    
    Onde f0xxxxxx é o valor do ponteiro de instrução. As chances são que você não terá um resultado exato visto que os símbolos na tabela de símbolos do kernel são para os pontos de entrada (entry points) de funções e o endereço do ponteiro de instrução estarão em algum lugar dentro de uma função, não no começo. Se você não receber um resultado exato, omita o último dígito do valor do ponteiro de instrução e tente novamente, ex:
    % nm -n /kernel.that.caused.the.panic | grep f0xxxxx
    
    Se isso não produz nenhum resultado, corte outro dígito. Repita até que você tenha algum tipo de retorno. O resultado será uma possível lista de funções que causaram o panic. Isso é menos do que um mecanismo exato para rastreamento de um ponto de falha, mas é melhor que nada.

Eu vejo pessoas constantemente mostrando mensagens de panic como essa, mas eu raramente vejo alguém comparar o ponteiro de instrução com uma função na tabela de símbolos do kernel.

A melhor maneira de rastrear a causa de um panic é guardar as mensagens de falha (crash dump), e então usar o gdb(1) para gerar um stack trace da falha.

Em qualquer caso, o método que eu normalmente uso é esse:

  1. Definir um arquivo de configuração do kernel, opcionalmente adiconando a options DDB se você acha que precisa do debugger do kernel para algo. (Eu uso isso principalmente para ajustar breakpoints se eu suspeito que há uma condição de laço infinito (infinite loop ou algo do tipo).

  2. Use config -g KERNELCONFIG configurar o diretório da construção.

  3. cd /sys/compile/ KERNELCONFIG; make

  4. Espere o kernel acabar de compilar.

  5. make install

  6. reboot

O processo do make(1) terá construído dois kernels. kernel e kernel.debug. O kernel foi instalado como /kernel, enquanto o kernel.debug pode ser usado como fonte símbolos de debug para o gdb(1).

Para ter certeza que você irá capturar o crash dump, você precisa editar o /etc/rc.conf e ajustar o dumpdev para apontar para sua partição swap. Isso fará com que os scripts rc(8) usem o comando dumpon(8) para habilitar os crash dumps. Você pode também executar o dumpon(8) manualmente. Depois de um panic, o crash dump pode ser recuperado usando o savecore(8); se variavel dumpdev estiver definida no /etc/rc.conf, os scripts rc(8) irão executar o savecore(8) automaticamente e colocar o crash dump em /var/crash.

Nota: Os crash dumps do FreeBSD são geralmente do mesmo tamanho da memória RAM física da sua máquina. Isto é, se você tem 64MB de RAM, você terá um crash dump de 64MB. Então você deve ter certeza que há espaço suficiente em /var/crash para alocar o dump. Alternativamente, você executa o savecore(8) manualmente e pode fazê-lo recuperar o crash dump para onde você tenha mais espaço. É possível limitar o tamanho do crash dump utilizando a opção options MAXMEM=(foo) para ajustar a quantia de memória que o kernel irá usar para algo um pouco mais sensível. Por exemplo, se você tem 128MB de RAM, você pode limitar o uso de memória do kernel para 16MB para que o tamanho do seu crash dump tenha somente 16MB ao invés de 128MB.

Uma vez que você recuperou o crash dump, você pode ter um stack trace com o gdb(1) como segue:

% gdb -k /sys/compile/KERNELCONFIG/kernel.debug /var/crash/vmcore.0
(gdb) where

Note que há várias telas com informações valiosas; seria ideal o uso do script(1) para capturar todas elas. Usando a imagem (unstripped) do kernel com todos os símbolos de debug deve mostrar a linha exata do código-fonte do kernel onde o panic ocorreu. Geralmente é mais interessante ler o stack trace de baixo para cima a fim de rastrear a exata seqüência de eventos que levaram ao crash. Você também pode usar o gdb(1) para exibir os conteúdos de várias variáveis ou estruturas a fim de examinar o estado do sistema no instante do crash.

Agora, se você é realmente louco e tem um segundo computador, você também pode configurar o gdb(1) para executar um debug remoto, tanto que você pode usar o gdb(1) em um sistema para debugar o kernel em outro sistema, incluindo o ajuste de breakpoints, rastreamento passo-a-passo pelo código do kernel, do mesmo modo que você pode fazer com um programa do modo de usuário normal. Eu ainda não brinquei com isso pois não tive a chance de configurar duas lado a lado com o único propósito de debugging.

[Adendo de Bill: "Eu esqueci de mencionar uma coisa: se você tem DDB habilitado e o kernel em modo de debug, você pode forçar um panic (e um crash dump) apenas digitando ´panic´ no prompt do ddb. Ele pode parar no debugger novamente durante a fase de panic. Se isso acontecer, digite ´continue´ e ele finalizará o crash dump."-ed]

18.14. Por que a dlsym() não funciona mais nos executáveis ELF?

A toolchain (cadeia de ferramentas) ELF não faz, por padrão, os símbolos definidos em um executável visível para o linkador dinâmico (dynamic linker). Conseqüentemente, a procura em nomes obtidos de chamadas com a dlsym() para dlopen(NULL, flags) irá falhar ao buscar tais símbolos.

Se a intenção é usar a dlsym() para buscar símbolos que possam existir nos executáveis principais do processo, é necessário linkar o programa com a opção -export-dynamic com o linker ELF (ld(1)).

18.15. Como eu posso aumentar ou reduzir o espaço de endereçamento disponível para o kernel?

Por padrão, o espaço de endereçamento (address space) do kernel é 256 MB no FreeBSD 3.x e 1 GB no FreeBSD 4.x. Em um servidor de rede com tráfego intensivo (por exemplo, um servidor FTP ou HTTP de muito tráfego) pode acontecer de, por exemplo, 256MB de memória não ser o suficiente.

Mas então, como aumentar esse espaço? Existem duas formas. Primeiro, é necessário dizer ao kernel que ele deve reservar uma grande quantidade de espaço em memória para ele mesmo. Segundo, considerando que o kernel é carregado no topo do espaço de endereçamento, é preciso diminuir o endereço de forma que não conflite com as páginas anteriores de memória, e que no lugar disso, ele seja carregado em seu novo local.

O primeiro objetivo é facilmente atingido aumentando as definições de valores do NKPDE no arquivo src/sys/i386/include/pmap.h. Aqui está o arquivo, como deve ser, para 1GB de endereço de memória:

#ifndef NKPDE
#ifdef SMP
#define NKPDE                   254     /* addressable number of page tables/pde's */
#else
#define NKPDE                   255     /* addressable number of page tables/pde's */
#endif  /* SMP */
#endif

Para achar o valor correto de NKPDE, divida o número desejado (em megabytes) por quatro, então subtraia um para máquinas mono processadas e dois para máquinas com SMP.

Para atingir o segundo objetivo é necessário descobrir o endereço correto de carregamento. Para isso basta subtrair o tamanho do espaço de endereçamento desejado (em bytes) de 0x100100000; o resultado é 0xc0100000 para um endereço de espaço de 1 GB. Ajuste LOAD_ADDRESS em src/sys/i386/conf/Makefile.i386 para esse valor, agora ajuste o contador de posiçõo listada no inicio do src/sys/i386/conf/kernel.script para o mesmo valor, como a seguir:

OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(btext)
SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/obj/elf/home/src/tmp/usr/i386-unknown-freebsdelf/lib);
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  . = 0xc0100000 + SIZEOF_HEADERS;
  .interp     : { *(.interp)    }

Agora recompile e reinstale seu kernel. Provavelmente aparecerão problemas com o ps(1) e com o top(1), executar um make world deve solucionar tais problemas (ou então, a recompilação manual da libkvm, do ps(1) e do top(1), depois de incluir o pmap.h alterado em /usr/include/vm/.

OBS: o tamanho do espaço em memória do kernel deve ser um múltiplo de quatro megabytes.

[Adendo por David Greenman : Acho que o endereço de espaço do kernel precisa ser uma potência de dois, mas eu não estou certo disso. O código do processo de inicialização antigo costumava mexer com os bits de endereço de alta ordem, o que implicava em uma granularidade de 256MB]

Este, e outros documentos, podem ser obtidos em ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

Para perguntas sobre FreeBSD, leia a documentação antes de contatar <[email protected]>.
Para perguntas sobre esta documentação, envie e-mail para <[email protected]>.