                       Emulac,ao de Linux (R) no FreeBSD

  Roman Divacky

     <rdivacky@FreeBSD.org>

   Revisao: cc3f22ceb1

   Adobe, Acrobat, Acrobat Reader, Flash and PostScript are either registered
   trademarks or trademarks of Adobe Systems Incorporated in the United
   States and/or other countries.

   IBM, AIX, OS/2, PowerPC, PS/2, S/390, and ThinkPad are trademarks of
   International Business Machines Corporation in the United States, other
   countries, or both.

   FreeBSD is a registered trademark of the FreeBSD Foundation.

   Linux is a registered trademark of Linus Torvalds.

   NetBSD is a registered trademark of the NetBSD Foundation.

   RealNetworks, RealPlayer, and RealAudio are the registered trademarks of
   RealNetworks, Inc.

   Oracle is a registered trademark of Oracle Corporation.

   Sun, Sun Microsystems, Java, Java Virtual Machine, JDK, JRE, JSP, JVM,
   Netra, OpenJDK, Solaris, StarOffice, SunOS and VirtualBox are trademarks
   or registered trademarks of Sun Microsystems, Inc. in the United States
   and other countries.

   Many of the designations used by manufacturers and sellers to distinguish
   their products are claimed as trademarks. Where those designations appear
   in this document, and the FreeBSD Project was aware of the trademark
   claim, the designations have been followed by the "(TM)" or the "(R)"
   symbol.

   2019-12-11 23:12:58 +0000 por Danilo G. Baio.
   Resumo

   Essa tese master lida com a atualizac,ao da camada de emulac,ao do
   Linux(R) (o chamado Linuxulator). A tarefa foi atualizar a camada para
   casar com a funcionalidade do Linux(R) 2.6. Como uma referencia a
   implementac,ao, o kernel Linux(R) 2.6.16 foi escolhido. O conceito e
   perdidamente baseado na implementac,ao do NetBSD. Maior parte do trabalho
   foi feito no verao de 2006 como parte de um programa de estudante do
   Google Summer of Code. O foco foi trazer o suporte do NPTL (nova
   biblioteca de threads POSIX(R)) pra dentro da camada de emulac,ao,
   incluindo TLS (thread local storage), futexes (mutexes rapidos na camada
   de usuario), PID mangling, e algumas outras coisas menores. Muitos
   pequenos problemas foram identificados e corrigidos. Meu trabalho foi
   integrado dentro do repositorio de principal do FreeBSD e vai ser ligado
   ao 7.0R release. Nos, o time de desenvolvimento de emulac,ao estamos
   trabalhando na emulac,ao do Linux(R) 2.6 a camada de emulac,ao padr ao do
   FreeBSD.

   [ Documento HTML em partes / Documento HTML completo ]

     ----------------------------------------------------------------------

   Indice

   1. Introduc,ao

   2. Um olhar para dentro...

   3. Emulac,ao

   4. Parte da camada de emulac,ao -MD do Linux(R)

   5. Parte da camada de emulac,ao -MI do Linux(R)

   6. Conclusao

   7. Literaturas

1. Introduc,ao

   Nos ultimos anos, os sistemas operacionais baseados em codigo aberto
   UNIX(R) comec,aram a ser amplamente implantados em maquinas servidores e
   clientes. Entre esses sistemas operacionais eu gostaria de destacar dois:
   FreeBSD, por sua heranc,a BSD, base de codigo comprovada pelo tempo e
   muitos recursos interessantes e Linux(R) por sua ampla base de usuarios,
   entusiasta comunidade aberta de desenvolvedores e apoio de grandes
   empresas. O FreeBSD tende a ser usado em maquinas de classe servidor,
   tarefas de rede pesadas com menos uso em maquinas de classe desktop para
   usuarios comuns. Embora o Linux(R) tenha o mesmo uso em servidores, mas e
   muito mais usado por usuarios domesticos. Isto leva a uma situac,ao onde
   existem muitos programas binarios disponiveis apenas para Linux(R) que nao
   suportam o FreeBSD.

   Naturalmente, surge a necessidade da habilidade de executar binarios
   Linux(R) em um sistema FreeBSD e e com isso que esta tese trata: a
   emulac,ao do kernel do Linux(R) no sistema operacional FreeBSD.

   Durante o verao de 2006, a Google Inc. patrocinou um projeto que se
   concentrava em estender a camada de emulac,ao do Linux(R) (o chamado
   Linuxulator) no FreeBSD para incluir necessidades do Linux(R) 2.6. Esta
   tese e escrita como parte deste projeto.

2. Um olhar para dentro...

   Nesta sec,ao vamos descrever cada sistema operacional em questao. Como
   eles lidam com syscalls, trapframes etc., todo o material de baixo nivel.
   Tambem descrevemos a maneira como eles entendem primitivas comuns UNIX(R),
   como o que e um PID, o que e uma thread, etc. Na terceira subsec,ao,
   falamos sobre como UNIX(R) em emuladores UNIX(R) pode ser feita em geral.

  2.1. O que e o UNIX (R)

   UNIX(R) e um sistema operacional com um longo historico que influenciou
   quase todos os outros sistemas operacionais atualmente em uso. Comec,ando
   na decada de 1960, seu desenvolvimento continua ate hoje (embora em
   projetos diferentes). O desenvolvimento de UNIX(R) logo se bifurcou em
   duas formas principais: as familias BSDs e System III/V. Eles se
   influenciaram mutuamente ao desenvolver um padrao UNIX(R) comum. Entre as
   contribuic,oes originadas no BSD, podemos nomear memoria virtual, rede
   TCP/IP, FFS e muitas outras. A ramificac,ao SystemV contribuiu para as
   primitivas de comunicac,ao entre processos SysV, copy-on-write, etc.
   UNIX(R) em si nao existe mais, mas suas ideias tem sido usadas por muitos
   outros sistemas operacionais amplos formando assim os chamados sistemas
   operacionais como UNIX(R). Hoje em dia os mais influentes sao Linux(R),
   Solaris e possivelmente (ate certo ponto) FreeBSD. Existem sistemas
   UNIX(R) de companhias derivados como (AIX, HP-UX etc.), mas estas foram
   cada vez mais migrados para os sistemas acima mencionados. Vamos resumir
   as caracteristicas tipicas do UNIX(R).

  2.2. Detalhes tecnicos

   Todo programa em execuc,ao constitui um processo que representa um estado
   da computac,ao. O processo de execuc,ao e dividido entre o espac,o do
   kernel e o espac,o do usuario. Algumas operac,oes podem ser feitas somente
   a partir do espac,o do kernel (lidando com hardware, etc.), mas o processo
   deve passar a maior parte de sua vida util no espac,o do usuario. O kernel
   e onde o gerenciamento dos processos, hardware e detalhes de baixo nivel
   acontecem. O kernel fornece uma API unificada padrao UNIX(R) para o
   espac,o do usuario. Os mais importantes sao abordados abaixo.

    2.2.1. Comunicac,ao entre o kernel e o processo de espac,o do usuario

   A API comum do UNIX(R) define uma syscall como uma forma de emitir
   comandos de um processo do espac,o do usuario para o kernel. A
   implementac,ao mais comum e usando uma instruc,ao de interrupc,ao ou
   especializada (pense em instruc,oes SYSENTER/SYSCALL para ia32). Syscalls
   sao definidos por um numero. Por exemplo, no FreeBSD, a syscall numero 85
   e a syscall swapon(2) e a syscall numero 132 e a syscall mkfifo(2).
   Algumas syscalls precisam de parametros, que sao passados do espac,o do
   usuario para o espac,o do kernel de varias maneiras (dependente da
   implementac,ao). Syscalls sao sincronas.

   Outra maneira possivel de se comunicar e usando uma trap. As traps ocorrem
   de forma assincrona apos a ocorrencia de algum evento (divisao por zero,
   falha de pagina, etc.). Uma trap pode ser transparente para um processo
   (falha de pagina) ou pode resultar em uma reac,ao como o envio de um
   signal (divisao por zero).

    2.2.2. Comunicac,ao entre processos

   Existem outras APIs (System V IPC, memoria compartilhada, etc.), mas a API
   mais importante e o signal. Os signals sao enviados por processos ou pelo
   kernel e recebidos por processos. Alguns signals podem ser ignorados ou
   manipulados por uma rotina fornecida pelo usuario, alguns resultam em uma
   ac,ao predefinida que nao pode ser alterada ou ignorada.

    2.2.3. Gerenciamento de processos

   As instancias do kernel sao processadas primeiro no sistema (chamado
   init). Todo processo em execuc,ao pode criar sua copia identica usando a
   syscall fork(2). Algumas versoes ligeiramente modificadas desta syscall
   foram introduzidas, mas a semantica basica e a mesma. Todo processo em
   execuc,ao pode se transformar em algum outro processo usando a syscall
   exec(3). Algumas modificac,oes desta syscall foram introduzidas, mas todas
   servem ao mesmo proposito basico. Os processos terminam suas vidas
   chamando a syscall exit(2). Todo processo e identificado por um numero
   unico chamado PID. Todo processo tem um processo pai definido
   (identificado pelo seu PID).

    2.2.4. Gerenciamento de threads

   O UNIX(R) tradicional nao define nenhuma API nem implementac,ao para
   threading, enquanto POSIX(R) define sua API de threading, mas a
   implementac,ao e indefinida. Tradicionalmente, havia duas maneiras de
   implementar threads. Manipulando-as como processos separados (threading
   1:1) ou envolver todo o grupo de thread em um processo e gerenciando a
   threading no espac,o do usuario (threading 1:N). Comparando as principais
   caracteristicas de cada abordagem:

   1:1 threading

     * - threads pesadas

     * - o agendamento nao pode ser alterado pelo usuario (ligeiramente
       mitigado pela API POSIX (R))

     * + nao necessita de envolvimento do syscall

     * + pode utilizar varias CPUs

   1: N threading

     * + threads leves

     * + agendamento pode ser facilmente alterado pelo usuario

     * - syscalls devem ser acondicionadas

     * - nao pode utilizar mais de uma CPU

  2.3. O que e o FreeBSD?

   O projeto FreeBSD e um dos mais antigos sistemas operacionais de codigo
   aberto atualmente disponiveis para uso diario. E um descendente direto do
   verdadeiro UNIX(R), portanto, pode-se afirmar que ele e um verdadeiro
   UNIX(R) embora os problemas de licenciamento nao permitam isso. O inicio
   do projeto remonta ao inicio dos anos 90, quando uma equipe de usuarios
   BSD corrigiu o sistema operacional 386BSD. Baseado neste patchkit surgiu
   um novo sistema operacional, chamado FreeBSD por sua licenc,a liberal.
   Outro grupo criou o sistema operacional NetBSD com diferentes objetivos em
   mente. Vamos nos concentrar no FreeBSD.

   O FreeBSD e um sistema operacional baseado no UNIX(R) com todos os
   recursos do UNIX(R). Multitarefa preemptiva, necessidades de multiusuario,
   rede TCP/IP, protec,ao de memoria, suporte a multiprocessamento simetrico,
   memoria virtual com VM mesclada e cache de buffer, todos eles estao la. Um
   dos recursos interessantes e extremamente uteis e a capacidade de emular
   outros sistemas operacionais UNIX(R)-like. A partir de dezembro de 2006 e
   do desenvolvimento do 7-CURRENT, as seguintes funcionalidades de emulac,ao
   sao suportadas:

     * Emulac,ao FreeBSD/i386 no FreeBSD/amd64

     * Emulac,ao de FreeBSD/i386 no FreeBSD/ia64

     * Emulac,ao-Linux(R) do sistema operacional Linux (R) no FreeBSD

     * Emulac,ao de NDIS da interface de drivers de rede do Windows

     * Emulac,ao de NetBSD do sistema operacional NetBSD

     * Suporte PECoff para executaveis PECoff do FreeBSD

     * Emulac,ao SVR4 do UNIX(R) System V revisao 4

   Emulac,oes ativamente desenvolvidas sao a camada Linux(R) e varias camadas
   FreeBSD-on-FreeBSD. Outros nao devem funcionar corretamente nem ser
   utilizaveis nos dias de hoje.

    2.3.1. Detalhes tecnicos

   O FreeBSD e o gostinho tradicional de UNIX(R) no sentido de dividir a
   execuc,ao dos processos em duas metades: espac,o do kernel e execuc,ao do
   espac,o do usuario. Existem dois tipos de entrada de processo no kernel:
   uma syscall e uma trap. Ha apenas uma maneira de retornar. Nas sec,oes
   subsequ:entes, descreveremos as tres portas de/para o kernel. Toda a
   descric,ao se aplica `a arquitetura i386, pois o Linuxulator so existe la,
   mas o conceito e semelhante em outras arquiteturas. A informac,ao foi
   retirada de [1] e do codigo fonte.

      2.3.1.1. Entradas do sistema

   O FreeBSD tem uma abstrac,ao chamada loader de classes de execuc,ao, que e
   uma entrada na syscall execve(2). Isto emprega uma estrutura sysentvec,
   que descreve uma ABI executavel. Ele contem coisas como tabela de
   traduc,ao de errno, tabela de traduc,ao de sinais, varias func,oes para
   atender `as necessidades da syscall (correc,ao de pilha, coredumping,
   etc.). Toda ABI que o kernel do FreeBSD deseja suportar deve definir essa
   estrutura, como e usado posteriormente no codigo de processamento da
   syscall e em alguns outros lugares. As entradas do sistema sao tratadas
   pelos manipuladores de traps, onde podemos acessar o espac,o do kernel e o
   espac,o do usuario de uma so vez.

      2.3.1.2. Syscalls

   Syscalls no FreeBSD sao emitidos executando a interrupc,ao 0x80 com o
   registrador %eax definido para um numero de syscall desejado com
   argumentos passados na pilha.

   Quando um processo emite uma interrupc,ao 0x80, a syscall manipuladora de
   trap int0x80 e proclamada (definida em sys/i386/i386/exception.s), que
   prepara argumentos (ou seja, copia-os para a pilha) para uma chamada para
   uma func,ao C syscall(2) (definida em sys/i386/i386/trap.c), que processa
   o trapframe passado. O processamento consiste em preparar a syscall
   (dependendo da entrada sysvec), determinando se a syscall e de 32 ou 64
   bits (muda o tamanho dos parametros), entao os parametros sao copiados,
   incluindo a syscall. Em seguida, a func,ao syscall real e executada com o
   processamento do codigo de retorno (casos especiais para erros ERESTART e
   EJUSTRETURN). Finalmente, um userret() e agendado, trocando o processo de
   volta ao ritmo do usuario. Os parametros para a syscall manipuladora atual
   sao passados na forma de argumentos struct thread *td , struct syscall
   args* onde o segundo parametro e um ponteiro para o copiado na estrutura
   de parametros.

      2.3.1.3. Armadilhas (Traps)

   O manuseio de traps no FreeBSD e similar ao manuseio de syscalls. Sempre
   que ocorre uma trap, um manipulador de assembler e chamado. E escolhido
   entre alltraps, alltraps com regs push ou calltrap, dependendo do tipo de
   trap. Este manipulador prepara argumentos para uma chamada para uma
   func,ao C trap() (definida em sys/i386/i386/trap.c), que entao processa a
   trap ocorrida. Apos o processamento, ele pode enviar um sinal para o
   processo e/ou sair para o espac,o do usuario usando userret().

      2.3.1.4. Saidas

   As saidas do kernel para o userspace acontecem usando a rotina assembler
   doreti, independentemente de o kernel ter sido acessado por meio de uma
   trap ou via syscall. Isso restaura o status do programa da pilha e retorna
   ao espac,o do usuario.

      2.3.1.5. primitivas UNIX(R)

   O sistema operacional FreeBSD adere ao esquema tradicional UNIX(R), onde
   cada processo possui um numero de identificac,ao unico, o chamado PID (ID
   do processo). Numeros PID sao alocados de forma linear ou aleatoria
   variando de 0 para PID_MAX. A alocac,ao de numeros PID e feita usando
   pesquisa linear de espac,o PID. Cada thread em um processo recebe o mesmo
   numero PID como resultado da chamada getpid(2).

   Atualmente existem duas maneiras de implementar o threading no FreeBSD. A
   primeira maneira e o threading M:N seguido pelo modelo de threading 1:1. A
   biblioteca padrao usada e o threading M:N (libpthread) e voce pode
   alternar no tempo de execuc,ao para threading 1:1 (libthr). O plano e
   mudar para a biblioteca 1:1 por padrao em breve. Embora essas duas
   bibliotecas usem as mesmas primitivas do kernel, elas sao acessadas por
   API(s) diferentes. A biblioteca M:N usa a familia kse_* das syscalls
   enquanto a biblioteca 1:1 usa a familia thr_* das syscalls. Por causa
   disso, nao existe um conceito geral de ID de threading compartilhado entre
   o kernel e o espac,o do usuario. Obviamente, as duas bibliotecas de
   threads implementam a API de ID de threading pthread. Todo threading do
   kernel (como descrito por struct thread) possui identificadores td tid,
   mas isso nao e diretamente acessivel a partir do espac,o do usuario e
   serve apenas as necessidades do kernel. Ele tambem e usado para a
   biblioteca de threading 1:1 como o ID de threading do pthread, mas a
   manipulac,ao desta e interna `a biblioteca e nao pode ser confiavel.

   Como dito anteriormente, existem duas implementac,oes de threads no
   FreeBSD. A biblioteca M:N divide o trabalho entre o espac,o do kernel e o
   espac,o do usuario. Thread e uma entidade que e agendada no kernel, mas
   pode representar varios numeros de threads do userspace. Threads M do
   userspace sao mapeadas para threads N do kernel, economizando recursos e
   mantendo a capacidade de explorar o paralelismo de multiprocessadores.
   Mais informac,oes sobre a implementac,ao podem ser obtidas na pagina do
   manual ou [1]. A biblioteca 1:1 mapeia diretamente um segmento userland
   para uma thread do kernel, simplificando muito o esquema. Nenhum desses
   designs implementa um mecanismo justo (tal mecanismo foi implementado, mas
   foi removido recentemente porque causou seria lentidao e tornou o codigo
   mais dificil de lidar).

  2.4. O que e Linux(R)

   Linux(R) e um kernel do tipo UNIX(R) originalmente desenvolvido por Linus
   Torvalds, e agora esta sendo contribuido por uma grande quantidade de
   programadores em todo o mundo. De seu simples comec,o ate hoje, com amplo
   suporte de empresas como IBM ou Google, o Linux(R) esta sendo associado ao
   seu rapido ritmo de desenvolvimento, suporte completo a hardware e seu
   benevolente modelo despota de organizac,ao.

   O desenvolvimento do Linux(R) comec,ou em 1991 como um projeto amador na
   Universidade de Helsinque na Finlandia. Desde entao, ele obteve todos os
   recursos de um sistema operacional semelhante ao UNIX: multiprocessamento,
   suporte multiusuario, memoria virtual, rede, basicamente tudo esta la.
   Tambem ha recursos altamente avanc,ados, como virtualizac,ao, etc.

   A partir de 2006, o Linux parece ser o sistema operacional de codigo
   aberto mais utilizado com o apoio de fornecedores independentes de
   software como Oracle, RealNetworks, Adobe, etc. A maioria dos softwares
   comerciais distribuidos para Linux(R) so pode ser obtido de forma binaria,
   portanto a recompilac,ao para outros sistemas operacionais e impossivel.

   A maior parte do desenvolvimento do Linux(R) acontece em um sistema de
   controle de versao Git. O Git e um sistema distribuido, de modo que nao
   existe uma fonte central do codigo Linux(R), mas algumas ramificac,oes sao
   consideradas proeminentes e oficiais. O esquema de numero de versao
   implementado pelo Linux(R) consiste em quatro numeros A.B.C.D. Atualmente,
   o desenvolvimento acontece em 2.6.C.D, onde C representa a versao
   principal, onde novos recursos sao adicionados ou alterados, enquanto D e
   uma versao secundaria somente para correc,oes de bugs.

   Mais informac,oes podem ser obtidas em [3].

    2.4.1. Detalhes tecnicos

   O Linux(R) segue o esquema tradicional do UNIX(R) de dividir a execuc,ao
   de um processo em duas metades: o kernel e o espac,o do usuario. O kernel
   pode ser inserido de duas maneiras: via trap ou via syscall. O retorno e
   tratado apenas de uma maneira. A descric,ao mais detalhada aplica-se ao
   Linux(R) 2.6 na arquitetura i386(TM). Esta informac,ao foi retirada de
   [2].

      2.4.1.1. Syscalls

   Syscalls em Linux(R) sao executados (no espac,o de usuario) usando macros
   syscallX onde X substitui um numero que representa o numero de parametros
   da syscall dada. Essa macro traduz um codigo que carrega o registro % eax
   com um numero da syscall e executa a interrupc,ao 0x80. Depois disso, um
   retorn da syscall e chamado, o que traduz valores de retorno negativos
   para valores errno positivos e define res para -1 em caso de erro. Sempre
   que a interrupc,ao 0x80 e chamada, o processo entra no kernel no
   manipulador de trap das syscalls. Essa rotina salva todos os registros na
   pilha e chama a entrada syscall selecionada. Note que a convenc,ao de
   chamadas Linux(R) espera que os parametros para o syscall sejam passados
   pelos registradores como mostrado aqui:

    1. parameter -> %ebx

    2. parameter -> %ecx

    3. parameter -> %edx

    4. parameter -> %esi

    5. parameter -> %edi

    6. parameter -> %ebp

   Existem algumas excec,oes, onde Linux(R) usa diferentes convenc,oes de
   chamada (mais notavelmente a syscall clone).

      2.4.1.2. Armadilhas (Traps)

   Os manipuladores de traps sao apresentados em arch/i386/kernel/traps.c e a
   maioria desses manipuladores vive em arch/i386/kernel/entry.S, onde a
   manipulac,ao das traps acontecem.

      2.4.1.3. Saidas

   O retorno da syscall e gerenciado pela syscall exit(3), que verifica se o
   processo nao esta concluido e verifica se usamos seletores fornecidos pelo
   usuario . Se isso acontecer, a correc,ao da pilha e aplicada e,
   finalmente, os registros sao restaurados da pilha e o processo retorna ao
   espac,o do usuario.

      2.4.1.4. primitivas UNIX(R)

   Na versao 2.6, o sistema operacional Linux(R) redefiniu algumas das
   primitivas tradicionais do UNIX(R), especialmente PID, TID e thread. O PID
   e definido para nao ser exclusivo para cada processo, portanto, para
   alguns processos (threading) getppid(2) retorna o mesmo valor. A
   identificac,ao exclusiva do processo e fornecida pelo TID. Isso ocorre
   porque o NPTL (Nova Biblioteca de threading POSIX(R)) define threading
   para serem processos normais (assim chamado threading 1:1). Gerar um novo
   processo no Linux(R) 2.6 acontece usando a syscall clone (as variantes do
   fork sao reimplementadas usando-o). Esta syscall clone define um conjunto
   de sinalizadores que afetam o comportamento do processo de clonagem em
   relac,ao `a implementac,ao do threading. A semantica e um pouco confusa,
   pois nao existe uma unica bandeira dizendo a syscall para criar uma
   thread.

   Flags de clone implementados sao:

     * CLONE_VM - os processos compartilham seu espac,o de memoria

     * CLONE_FS - compartilha umask, cwd e namespace

     * CLONE_FILES - compartilham arquivos abertos

     * CLONE_SIGHAND - compartilha manipuladores de sinais e bloqueia sinais

     * CLONE_PARENT - compartilha processo pai

     * CLONE_THREAD - ser a thread (mais explicac,oes abaixo)

     * CLONE_NEWNS - novo namespace

     * CLONE_SYSVSEM - compartilha SysV sob estruturas

     * CLONE_SETTLS - configura o TLS no enderec,o fornecido

     * CLONE_PARENT_SETTID - define o TID no processo pai

     * CLONE_CHILD_CLEARTID - limpe o TID no processo filho

     * CLONE_CHILD_SETTID - define o TID no processo filho

   CLONE_PARENT define o processo real para o processo pai do requisitante.
   Isso e util para threads porque, se a thread A criar a thread B, queremos
   que a thread B parenteada para o processo pai de todo o grupo de threads.
   CLONE_THREAD faz exatamente a mesma coisa que CLONE_PARENT, CLONE_VM e
   CLONE_SIGHAND, reescreve o PID para ser o mesmo que PID do requisitante,
   define o sinal de saida como none e entra no grupo de threads.
   CLONE_SETTLS configura entradas GDT para tratamento de TLS. O conjunto de
   flags CLONE_*_*TID define/limpa o enderec,o fornecido pelo usuario para
   TID ou 0.

   Como voce pode ver, o CLONE_THREAD faz a maior parte do trabalho e nao
   parece se encaixar muito bem no esquema. A intenc,ao original nao e clara
   (mesmo para autores, de acordo com comentarios no codigo), mas acho que
   originalmente havia uma flag de thread, que foi entao dividida entre
   muitas outras flags, mas essa separac,ao nunca foi totalmente concluida.
   Tambem nao esta claro para que serve esta partic,ao, uma vez que a glibc
   nao usa isso, portanto, apenas o uso do clone escrito `a mao permite que
   um programador acesse esses recursos.

   Para programas nao segmentados, o PID e o TID sao os mesmos. Para
   programas em threadings, os primeiros PID e TID da thread sao os mesmos e
   todos os threading criados compartilham o mesmo PID e sao atribuidos a um
   TID exclusivo (porque CLONE_THREAD e passado), o processo pai tambem e
   compartilhado para todos os processos que formam esse threading do
   programa.

   O codigo que implementa pthread_create(3) no NPTL define as flags de clone
   como este:

 int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL

  | CLONE_SETTLS | CLONE_PARENT_SETTID

 | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM
 #if __ASSUME_NO_CLONE_DETACHED == 0

 | CLONE_DETACHED
 #endif

 | 0);

   O CLONE_SIGNAL e definido como

 #define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)

   o ultimo 0 significa que nenhum sinal e enviado quando qualquer uma das
   threads finaliza.

  2.5. O que e emulac,ao

   De acordo com uma definic,ao de dicionario, emulac,ao e a capacidade de um
   programa ou dispositivo de imitar um outro programa ou dispositivo. Isto e
   conseguido fornecendo a mesma reac,ao a um determinado estimulo que o
   objeto emulado. Na pratica, o mundo do software ve tres tipos de emulac,ao
   - um programa usado para emular uma maquina (QEMU, varios emuladores de
   consoles de jogos etc.), emulac,ao de software de uma instalac,ao de
   hardware (emuladores OpenGL, emulac,ao de unidades de ponto flutuante
   etc.) e emulac,ao do sistema (no kernel do sistema operacional ou como um
   programa de espac,o do usuario).

   Emulac,ao e geralmente usada em um lugar, onde o uso do componente
   original nao e viavel nem possivel a todos. Por exemplo, alguem pode
   querer usar um programa desenvolvido para um sistema operacional diferente
   do que eles usam. Entao a emulac,ao vem a calhar. Por vezes, nao ha outra
   maneira senao usar emulac,ao - por ex. Quando o dispositivo de hardware
   que voce tenta usar nao existe (ainda/mais), entao nao ha outro caminho
   alem da emulac,ao. Isso acontece com frequencia ao transferir um sistema
   operacional para uma nova plataforma (inexistente). As vezes e mais barato
   emular.

   Olhando do ponto de vista da implementac,ao, existem duas abordagens
   principais para a implementac,ao da emulac,ao. Voce pode emular a coisa
   toda - aceitando possiveis entradas do objeto original, mantendo o estado
   interno e emitindo a saida correta com base no estado e/ou na entrada.
   Este tipo de emulac,ao nao requer condic,oes especiais e basicamente pode
   ser implementado em qualquer lugar para qualquer dispositivo/programa. A
   desvantagem e que a implementac,ao de tal emulac,ao e bastante dificil,
   demorada e propensa a erros. Em alguns casos, podemos usar uma abordagem
   mais simples. Imagine que voce deseja emular uma impressora que imprime da
   esquerda para a direita em uma impressora que imprime da direita para a
   esquerda. E obvio que nao ha necessidade de uma camada de emulac,ao
   complexa, mas a simples reversao do texto impresso e suficiente. As vezes,
   o ambiente de emulac,ao e muito semelhante ao emulado, portanto, apenas
   uma camada fina de alguma traduc,ao e necessaria para fornecer uma
   emulac,ao totalmente funcional! Como voce pode ver, isso e muito menos
   exigente de implementar, portanto, menos demorado e propenso a erros do
   que a abordagem anterior. Mas a condic,ao necessaria e que os dois
   ambientes sejam semelhantes o suficiente. A terceira abordagem combina os
   dois anteriores. Na maioria das vezes, os objetos nao fornecem os mesmos
   recursos, portanto, em um caso de emulac,ao, o mais poderoso e o menos
   poderoso que temos para emular os recursos ausentes com a emulac,ao
   completa descrita acima.

   Esta tese de mestrado lida com a emulac,ao de UNIX(R) em UNIX(R), que e
   exatamente o caso, onde apenas uma camada fina de traduc,ao e suficiente
   para fornecer emulac,ao completa. A API do UNIX(R) consiste em um conjunto
   de syscalls, que geralmente sao autonomas e nao afetam algum estado global
   do kernel.

   Existem algumas syscalls que afetam o estado interno, mas isso pode ser
   resolvido fornecendo algumas estruturas que mantem o estado extra.

   Nenhuma emulac,ao e perfeita e as emulac,oes tendem a nao ter algumas
   partes, mas isso geralmente nao causa nenhuma desvantagem seria. Imagine
   um emulador de console de jogos que emula tudo, menos a saida de musica.
   Nao ha duvida de que os jogos sao jogaveis e pode-se usar o emulador. Pode
   nao ser tao confortavel quanto o console original, mas e um compromisso
   aceitavel entre prec,o e conforto.

   O mesmo acontece com a API do UNIX(R). A maioria dos programas pode viver
   com um conjunto muito limitado de syscalls funcionando. Essas syscalls
   tendem a ser as mais antigas
   (read(2)/write(2),fork(2)family,signal(3)handling, exit(3), socket(2)
   API), portanto, e facil emular porque sua semantica e compartilhada entre
   todos os UNIX(R), que existem hoje.

3. Emulac,ao

  3.1. Como funciona a emulac,ao no FreeBSD

   Como dito anteriormente, o FreeBSD suporta a execuc,ao de binarios a
   partir de varios outros UNIX(R). Isso funciona porque o FreeBSD tem uma
   abstrac,ao chamada loader de classes de execuc,ao. Isso se encaixa na
   syscall execve(2), entao quando execve(2) esta prestes a executar um
   binario que examina seu tipo.

   Existem basicamente dois tipos de binarios no FreeBSD. Scripts de texto
   semelhantes a shell que sao identificados por #! como seus dois primeiros
   caracteres e binarios normais (normalmente ELF), que sao uma
   representac,ao de um objeto executavel compilado. A grande maioria
   (pode-se dizer todos eles) de binarios no FreeBSD e do tipo ELF. Os
   arquivos ELF contem um cabec,alho, que especifica a ABI do OS para este
   arquivo ELF. Ao ler essas informac,oes, o sistema operacional pode
   determinar com precisao o tipo de binario do arquivo fornecido.

   Toda ABI de OS deve ser registrada no kernel do FreeBSD. Isso tambem se
   aplica ao sistema operacional nativo do FreeBSD. Entao, quando execve(2)
   executa um binario, ele itera atraves da lista de APIs registradas e
   quando ele encontra a correta, ele comec,a a usar as informac,oes contidas
   na descric,ao da ABI do OS (sua tabela syscall, tabela de traduc,ao errno,
   etc.). Assim, toda vez que o processo chama uma syscall, ele usa seu
   proprio conjunto de syscalls em vez de uma global. Isso efetivamente
   fornece uma maneira muito elegante e facil de suportar a execuc,ao de
   varios formatos binarios.

   A natureza da emulac,ao de diferentes sistemas operacionais (e tambem
   alguns outros subsistemas) levou os desenvolvedores a invitar um mecanismo
   de evento manipulador. Existem varios locais no kernel, onde uma lista de
   manipuladores de eventos e chamada. Cada subsistema pode registrar um
   manipulador de eventos e eles sao chamados de acordo com sua necessidade.
   Por exemplo, quando um processo e encerrado, ha um manipulador chamado que
   possivelmente limpa o que o subsistema que ele precisa de limpeza.

   Essas facilidades simples fornecem basicamente tudo o que e necessario
   para a infra-estrutura de emulac,ao e, de fato, essas sao basicamente as
   unicas coisas necessarias para implementar a camada de emulac,ao do
   Linux(R).

  3.2. Primitivas comuns no kernel do FreeBSD

   Camadas de emulac,ao precisam de algum suporte do sistema operacional. Eu
   vou descrever algumas das primitivas suportadas no sistema operacional
   FreeBSD.

    3.2.1. Primitivas de Bloqueio

   Contribuido por: Attilio Rao <attilio@FreeBSD.org>

   O conjunto de primitivas de sincronizac,ao do FreeBSD e baseado na ideia
   de fornecer um grande numero de diferentes primitivas de uma maneira que a
   melhor possa ser usada para cada situac,ao especifica e apropriada.

   Para um ponto de vista de alto nivel, voce pode considerar tres tipos de
   primitivas de sincronizac,ao no kernel do FreeBSD:

     * operac,oes atomicas e barreiras de memoria

     * locks

     * barreiras de agendamento

   Abaixo, ha descric,oes para as 3 familias. Para cada bloqueio, voce deve
   verificar a pagina de manual vinculada (onde for possivel) para obter
   explicac,oes mais detalhadas.

      3.2.1.1. Operac,oes atomicas e barreiras de memoria

   Operac,oes atomicas sao implementadas atraves de um conjunto de func,oes
   que executam aritmetica simples em operandos de memoria de maneira atomica
   com relac,ao a eventos externos (interrupc,oes, preempc,ao, etc.).
   Operac,oes atomicas podem garantir atomicidade apenas em pequenos tipos de
   dados (na ordem de magnitude do tipo de dados C da arquitetura .long.),
   portanto raramente devem ser usados diretamente no codigo de nivel final,
   se nao apenas para operac,oes muito simples (como configurac,ao de flags
   em um bitmap, por exemplo). De fato, e bastante simples e comum escrever
   uma semantica errada baseada apenas em operac,oes atomicas (geralmente
   referidas como lock-less). O kernel do FreeBSD oferece uma maneira de
   realizar operac,oes atomicas em conjunto com uma barreira de memoria. As
   barreiras de memoria garantirao que uma operac,ao atomica ocorrera
   seguindo alguma ordem especificas em relac,ao a outros acessos `a memoria.
   Por exemplo, se precisarmos que uma operac,ao atomica acontec,a logo
   depois que todas as outras gravac,oes pendentes (em termos de instruc,oes
   reordenando atividades de buffers) forem concluidas, precisamos usar
   explicitamente uma barreira de memoria em conjunto com essa operac,ao
   atomica. Portanto, e simples entender por que as barreiras de memoria
   desempenham um papel fundamental na construc,ao de bloqueios de alto nivel
   (assim como referencias, exclusoes mutuas, etc.). Para uma explicac,ao
   detalhada sobre operac,oes atomicas, consulte atomic(9). E muito, no
   entanto, notar que as operac,oes atomicas (e as barreiras de memoria
   tambem) devem, idealmente, ser usadas apenas para construir bloqueios
   front-ending (como mutexes).

      3.2.1.2. Refcounts

   Refcounts sao interfaces para manipular contadores de referencia. Eles sao
   implementados por meio de operac,oes atomicas e destinam-se a ser usados
   apenas para casos em que o contador de referencia e a unica coisa a ser
   protegida, portanto, ate mesmo algo como um spin-mutex e obsoleto. Usar a
   interface de recontagem para estruturas, onde um mutex ja e usado,
   geralmente esta errado, pois provavelmente devemos fechar o contador de
   referencia em alguns caminhos ja protegidos. Uma manpage discutindo
   refcount nao existe atualmente, apenas verifique sys/refcount.h para uma
   visao geral da API existente.

      3.2.1.3. Locks

   O kernel do FreeBSD tem enormes classes de bloqueios. Cada bloqueio e
   definido por algumas propriedades peculiares, mas provavelmente o mais
   importante e o evento vinculado a detentores de contestac,ao (ou, em
   outros termos, o comportamento de threading incapazes de adquirir o
   bloqueio). O esquema de bloqueio do FreeBSD apresenta tres comportamentos
   diferentes para contendores:

    1. spinning

    2. blocking

    3. sleeping

  Nota:

   numeros nao sao casuais

      3.2.1.4. Spinning locks

   Spin locks permitem que os acumuladores rotacionarem ate que eles nao
   consigam adquirir um lock. Uma questao importante e quando um segmento
   contesta em um spin lock se nao for desmarcado. Uma vez que o kernel do
   FreeBSD e preventivo, isto expoe o spin lock ao risco de deadlocks que
   podem ser resolvidos apenas desabilitando as interrupc,oes enquanto elas
   sao adquiridas. Por essa e outras razoes (como falta de suporte `a
   propagac,ao de prioridade, falta de esquemas de balanceamento de carga
   entre CPUs, etc.), os spin locks tem a finalidade de proteger
   enderec,amentos muito pequenos de codigo ou, idealmente, nao serem usados
   se nao solicitados explicitamente ( explicado posteriormente).

      3.2.1.5. Bloqueio

   Os locks em blocos permitem que as tarefas dos acumuladores sejam
   removidas e bloqueados ate que o proprietario do bloqueio nao os libere e
   ative um ou mais contendores. Para evitar problemas de fome, os locks em
   bloco fazem a propagac,ao de prioridade dos acumuladores para o
   proprietario. Os locks em bloco devem ser implementados por meio da
   interface turnstile e devem ser o tipo mais usado de bloqueios no kernel,
   se nenhuma condic,ao especifica for atendida.

      3.2.1.6. Sleeping

   Sleep locks permitem que as tarefas dos waiters sejam removidas e eles
   adormecem ate que o suporte do lock nao os deixe cair e desperte um ou
   mais waiters. Como os sleep locks se destinam a proteger grandes
   enderec,amentos de codigo e a atender a eventos assincronos, eles nao
   fazem nenhuma forma de propagac,ao de prioridade. Eles devem ser
   implementados por meio da interface sleepqueue(9).

   A ordem usada para adquirir locks e muito importante, nao apenas pela
   possibilidade de deadlock devido a reversoes de ordem de bloqueio, mas
   tambem porque a aquisic,ao de lock deve seguir regras especificas
   vinculadas a naturezas de bloqueios. Se voce der uma olhada na tabela
   acima, a regra pratica e que, se um segmento contiver um lock de nivel n
   (onde o nivel e o numero listado proximo ao tipo de bloqueio), nao e
   permitido adquirir um lock de niveis superiores , pois isso quebraria a
   semantica especificada para um caminho. Por exemplo, se uma thread
   contiver um lock em bloco (nivel 2), ele podera adquirir um spin lock
   (nivel 1), mas nao um sleep lock (nivel 3), pois os locks em bloco sao
   destinados a proteger caminhos menores que o sleep lock ( essas regras nao
   sao sobre operac,oes atomicas ou agendamento de barreiras, no entanto).

   Esta e uma lista de bloqueio com seus respectivos comportamentos:

     * spin mutex - spinning - mutex(9)

     * sleep mutex - blocking - mutex(9)

     * pool mutex - blocking - mtx_pool(9)

     * familia sleep - sleeping - sleep(9) pausa tsleep msleep msleep spin
       msleep rw msleep sx

     * condvar - sleeping - condvar(9)

     * wlock - blocking - rwlock(9)

     * sxlock - sleeping - sx(9)

     * lockmgr - sleeping - lockmgr(9)

     * semaforos - sleeping - sema(9)

   Entre esses bloqueios, apenas mutexes, sxlocks, rwlocks e lockmgrs sao
   destinados a tratar recursao, mas atualmente a recursao e suportada apenas
   por mutexes e lockmgrs.

      3.2.1.7. Barreiras de agendamento

   As barreiras de agendamento devem ser usadas para orientar o agendamento
   de threads. Eles consistem principalmente de tres diferentes stubs:

     * sec,oes criticas (e preempc,ao)

     * sched_bind

     * sched_pin

   Geralmente, eles devem ser usados apenas em um contexto especifico e,
   mesmo que possam substituir bloqueios, eles devem ser evitados porque eles
   nao permitem o diagnostico de problemas simples com ferramentas de
   depurac,ao de bloqueio (como witness(4)).

      3.2.1.8. Sec,oes criticas

   O kernel do FreeBSD foi feito basicamente para lidar com threads de
   interrupc,ao. De fato, para evitar latencia de interrupc,ao alta, os
   segmentos de prioridade de compartilhamento de tempo podem ser precedidos
   por threads de interrupc,ao (dessa forma, eles nao precisam aguardar para
   serem agendados como as visualizac,oes de caminho normais). Preempc,ao, no
   entanto, introduz novos pontos de corrida que precisam ser manipulados
   tambem. Muitas vezes, para lidar com a preempc,ao, a coisa mais simples a
   fazer e desativa-la completamente. Uma sec,ao critica define um pedac,o de
   codigo (delimitado pelo par de func,oes critical_enter(9) e
   critical_exit(9), onde e garantido que a preempc,ao nao acontec,a (ate que
   o codigo protegido seja totalmente executado) Isso pode substituir um
   bloqueio efetivamente, mas deve ser usado com cuidado para nao perder toda
   a vantagem essa preempc,ao traz.

      3.2.1.9. sched_pin/sched_unpin

   Outra maneira de lidar com a preempc,ao e a interface sched_pin(). Se um
   trecho de codigo e fechado no par de func,oes sched_pin() e sched_unpin(),
   e garantido que a respectiva thread, mesmo que possa ser antecipada,
   sempre ser executada na mesma CPU. Fixar e muito eficaz no caso particular
   quando temos que acessar por dados do cpu e assumimos que outras threads
   nao irao alterar esses dados. A ultima condic,ao determinara uma sec,ao
   critica como uma condic,ao muito forte para o nosso codigo.

      3.2.1.10. sched_bind/sched_unbind

   sched_bind e uma API usada para vincular uma thread a uma CPU em
   particular durante todo o tempo em que ele executa o codigo, ate que uma
   chamada de func,ao sched_unbind nao a desvincule. Esse recurso tem um
   papel importante em situac,oes em que voce nao pode confiar no estado
   atual das CPUs (por exemplo, em estagios iniciais de inicializac,ao), ja
   que voce deseja evitar que sua thread migre em CPUs inativas. Como
   sched_bind e sched_unbind manipulam as estruturas internas do agendador,
   elas precisam estar entre a aquisic,ao/liberac,ao de sched_lock quando
   usadas.

    3.2.2. Estrutura Proc

   Varias camadas de emulac,ao exigem alguns dados adicionais por processo.
   Ele pode gerenciar estruturas separadas (uma lista, uma arvore etc.)
   contendo esses dados para cada processo, mas isso tende a ser lento e
   consumir memoria. Para resolver este problema, a estrutura proc do FreeBSD
   contem p_emuldata, que e um ponteiro vazio para alguns dados especificos
   da camada de emulac,ao. Esta entrada proc e protegida pelo mutex proc.

   A estrutura proc do FreeBSD contem uma entrada p_sysent que identifica,
   qual ABI este processo esta executando. Na verdade, e um ponteiro para o
   sysentvec descrito acima. Portanto, comparando esse ponteiro com o
   enderec,o em que a estrutura sysentvec da ABI especificada esta
   armazenada, podemos efetivamente determinar se o processo pertence `a
   nossa camada de emulac,ao. O codigo normalmente se parece com:

 if (__predict_true(p->p_sysent != &elf_Linux(R)_sysvec))
           return;

   Como voce pode ver, usamos efetivamente o modificador __predict_true para
   recolher o caso mais comum (processo do FreeBSD) para uma operac,ao de
   retorno simples, preservando assim o alto desempenho. Este codigo deve ser
   transformado em uma macro porque atualmente nao e muito flexivel, ou seja,
   nao suportamos emulac,ao Linux(R)64 nem processa A.OUT Linux(R) em i386.

    3.2.3. VFS

   O subsistema FreeBSD VFS e muito complexo, mas a camada de emulac,ao
   Linux(R) usa apenas um pequeno subconjunto atraves de uma API bem
   definida. Ele pode operar em vnodes ou manipuladores de arquivos. Vnode
   representa um vnode virtual, isto e, representac,ao de um no no VFS. Outra
   representac,ao e um manipulador de arquivos, que representa um arquivo
   aberto da perspectiva de um processo. Um manipulador de arquivos pode
   representar um socket ou um arquivo comum. Um manipulador de arquivos
   contem um ponteiro para seu vnode. Mais de um manipulador de arquivos pode
   apontar para o mesmo vnode.

      3.2.3.1. namei

   A rotina namei(9) e um ponto de entrada central para a pesquisa e o nome
   do caminho. Ele percorre o caminho ponto a ponto do ponto inicial ate o
   ponto final usando a func,ao de pesquisa, que e interna ao VFS. A syscall
   namei(9) pode lidar com links simbolicos, absolutos e relativos. Quando um
   caminho e procurado usando namei(9) ele e inserido no cache de nomes. Esse
   comportamento pode ser suprimido. Essa rotina e usada em todo o kernel e
   seu desempenho e muito critico.

      3.2.3.2. vn_fullpath

   A func,ao vn_fullpath(9) faz o melhor esforc,o para percorrer o cache de
   nomes do VFS e retorna um caminho para um determinado vnode (bloqueado).
   Esse processo nao e confiavel, mas funciona bem nos casos mais comuns. A
   falta de confiabilidade e porque ela depende do cache do VFS (ele nao
   atravessa as estruturas intermediarias), nao funciona com hardlinks, etc.
   Essa rotina e usada em varios locais no Linuxulator.

      3.2.3.3. Operac,oes de vnode

     * fgetvp - dado um encadeamento e um numero de descritor de arquivo, ele
       retorna o vnode associado

     * vn_lock(9) - bloqueia um vnode

     * vn_unlock - desbloqueia um vnode

     * VOP_READDIR(9) - le um diretorio referenciado por um vnode

     * VOP_GETATTR(9) - obtem atributos de um arquivo ou diretorio
       referenciado por um vnode

     * VOP_LOOKUP(9) - procura um caminho para um determinado diretorio

     * VOP_OPEN(9) - abre um arquivo referenciado por um vnode

     * VOP_CLOSE(9) - fecha um arquivo referenciado por um vnode

     * vput(9) - decrementa a contagem de uso para um vnode e o desbloqueia

     * vrele(9) - diminui a contagem de uso para um vnode

     * vref(9) - incrementa a contagem de uso para um vnode

      3.2.3.4. Operac,oes do manipulador de arquivos

     * fget - dado uma thread e um numero de file descriptor, ele retorna o
       manipulador de arquivos associado e faz referencia a ele

     * fdrop - elimina uma referencia a um manipulador de arquivos

     * fhold - faz referencia a um manipulador de arquivos

4. Parte da camada de emulac,ao -MD do Linux(R)

   Esta sec,ao trata da implementac,ao da camada de emulac,ao do Linux(R) no
   sistema operacional FreeBSD. Ele primeiro descreve a parte dependente da
   maquina falando sobre como e onde a interac,ao entre o usuario e o kernel
   e implementada. Ele fala sobre syscalls, signals, ptrace, traps, correc,ao
   de pilha. Esta parte discute o i386, mas ele e escrito geralmente para que
   outras arquiteturas nao sejam muito diferentes. A proxima parte e a parte
   independente da maquina do Linuxulator. Esta sec,ao abrange apenas o
   tratamento de i386 e ELF. A.OUT esta obsoleto e nao foi testado.

  4.1. Manipulac,ao de Syscall

   A manipulac,ao de Syscall e principalmente escrita em linux_sysvec.c, que
   cobre a maioria das rotinas apontadas na estrutura sysentvec. Quando um
   processo Linux(R) executado no FreeBSD emite um syscall, a rotina syscall
   geral chama a rotina prepsyscall do linux para a ABI do Linux(R).

    4.1.1. Linux(R) prepsyscall

   Linux(R) passa argumentos via registradores de syscalls (isso porque ele e
   limitado a 6 parametros no i386) enquanto o FreeBSD usa uma pilha. A
   rotina prepsyscall do Linux(R) deve copiar parametros dos registradores
   para a pilha. A ordem dos registradores e: %ebx, %ecx, %edx, %esi, %edi,
   %ebp. O fato e que isso e verdadeiro apenas para a maioria das syscalls.
   Algumas (mais provavelmente clone) usam uma ordem diferente, mas e
   demasiadamente facil de arrumar inserindo um parametro dummy no prototype
   linux_clone.

    4.1.2. Escrevendo syscall

   Cada syscall implementada no Linuxulator deve ter seu prototipo com varios
   flags no syscalls.master. A forma do arquivo e:

 ...
         AUE_FORK STD            { int linux_fork(void); }
 ...
         AUE_CLOSE NOPROTO       { int close(int fd); }
 ...

   A primeira coluna representa o numero da syscall. A segunda coluna e para
   suporte de auditoria. A terceira coluna representa o tipo da syscall. E
   STD, OBSOL, NOPROTO e UNIMPL. STD e uma syscall padrao com prototipo e
   implementac,ao completos. OBSOL e obsoleto e define apenas o prototipo.
   NOPROTO significa que a syscall e implementado em outro lugar, portanto,
   nao precede o prefixo da ABI, etc. UNIMPL significa que a syscall sera
   substituida pela syscall nosys (uma syscall apenas imprime uma mensagem
   sobre a syscall nao sendo implementado e retornando ENOSYS).

   De um script syscalls.master, gera tres arquivos: linux_syscall.h,
   linux_proto.h e linux_sysent.c. O linux_syscall.h contem definic,oes de
   nomes de syscall e seus valores numericos, por exemplo:

 ...
 #define LINUX_SYS_linux_fork 2
 ...
 #define LINUX_SYS_close 6
 ...

   O linux_proto.h contem definic,oes de estrutura de argumentos para cada
   syscall, por exemplo:

 struct linux_fork_args {
   register_t dummy;
 };

   E finalmente, linux_sysent.c contem uma estrutura descrevendo a tabela de
   entrada do sistema, usada para realmente enviar um syscall, por exemplo:

 { 0, (sy_call_t *)linux_fork, AUE_FORK, NULL, 0, 0 }, /* 2 = linux_fork */
 { AS(close_args), (sy_call_t *)close, AUE_CLOSE, NULL, 0, 0 }, /* 6 = close */

   Como voce pode ver, linux_fork e implementado no proprio Linuxulator,
   entao a definic,ao e do tipo STD e nao possui argumento, que e exibido
   pela estrutura de argumento ficticia. Por outro lado, close e apenas um
   apelido para o verdadeiro close(2) do FreeBSD para que ele nao possua
   estrutura de argumentos do linux associada e na tabela de entrada do
   sistema ele nao e prefixado com linux, pois ele chama o verdadeiro
   close(2) no kernel.

    4.1.3. Dummy syscalls

   A camada de emulac,ao do Linux(R) nao esta completa, pois algumas syscalls
   nao estao implementadas corretamente e algumas nao estao implementadas. A
   camada de emulac,ao emprega um recurso para marcar syscalls nao
   implementadas com a macro DUMMY. Estas definic,oes ficticias residem em
   linux_dummy.c em uma forma de DUMMY(syscall); , que e entao traduzido para
   varios arquivos auxiliares de syscall e a implementac,ao consiste em
   imprimir uma mensagem dizendo que esta syscall nao esta implementada. O
   prototipo UNIMPL nao e usado porque queremos ser capazes de identificar o
   nome da syscall que foi chamado para saber o que e mais importante
   implementar na syscalls.

  4.2. Manuseio de signals

   A manipulac,ao de sinais e feita geralmente no kernel do FreeBSD para
   todas as compatibilidades binarias com uma chamada para uma camada
   dependente de compatibilidade. A camada de compatibilidade do Linux(R)
   define a rotina linux_sendsig para essa finalidade.

    4.2.1. Linux(R) sendsig

   Esta rotina primeiro verifica se o signal foi instalado com um SA_SIGINFO,
   caso em que chama a rotina linux_rt_sendsig. Alem disso, ele aloca (ou
   reutiliza um contexto de identificador de sinal ja existente) e cria uma
   lista de argumentos para o manipulador de signal. Ele traduz o numero do
   signal baseado na tabela de traduc,ao do signal, atribui um manipulador,
   traduz o sigset. Em seguida, ele salva o contexto para a rotina sigreturn
   (varios registradores, numero da trap traduzida e mascara de signal).
   Finalmente, copia o contexto do signal para o espac,o do usuario e prepara
   o contexto para que o manipulador de sinal real seja executado.

    4.2.2. linux_rt_sendsig

   Esta rotina e similar a linux_sendsig apenas a preparac,ao do contexto do
   sinal e diferente. Adiciona siginfo, ucontext e algumas partes do
   POSIX(R). Pode valer a pena considerar se essas duas func,oes nao poderiam
   ser mescladas com um beneficio de menos duplicac,ao de codigo e,
   possivelmente, ate mesmo execuc,ao mais rapida.

    4.2.3. linux_sigreturn

   Esta syscall e usada para retornar do manipulador de sinal. Ela faz
   algumas verificac,oes de seguranc,a e restaura o contexto do processo
   original. Tambem desmascara o sinal na mascara de sinal do processo.

  4.3. Ptrace

   Muitos derivados do UNIX(R) implementam a syscall ptrace(2) para permitir
   varios recursos de rastreamento e depurac,ao . Esse recurso permite que o
   processo de rastreamento obtenha varias informac,oes sobre o processo
   rastreado, como registros de despejos, qualquer memoria do espac,o de
   enderec,o do processo, etc. e tambem para rastrear o processo, como em uma
   instruc,ao ou entre entradas do sistema (syscalls e traps). ptrace(2)
   tambem permite definir varias informac,oes no processo de rastreamento
   (registros, etc.). ptrace(2) e um padrao de toda o UNIX(R) implementado na
   maioria dos UNIX(R)es em todo o mundo.

   Emulac,ao do Linux(R) no FreeBSD implementa a habilidade ptrace(2) em
   linux_ptrace.c. As rotinas para converter registradores entre Linux(R) and
   FreeBSD e a atual emulac,ao de syscall, syscall ptrace(2). A syscall e um
   longo bloco de trocas que implementa em contraparte no FreeBSD para todo
   comando ptrace(2). Os comandos ptrace(2) sao em sua maioria igual entre
   Linux(R) e FreeBSD entao uma pequena modificac,ao e necessaria. Por
   exemplo, PT_GETREGS em Linux(R) opera diretamente dos dados enquanto o
   FreeBSD usa um ponteiro para o dado e depois performa a syscall ptrace(2)
   (nativa), uma copia deve ser feita pra preservar a semantica do Linux(R).

   A implementac,ao de ptrace(2) no Linuxulator tem algumas fraquezas
   conhecidas. Houve panico ao usar o strace (que e um consumidor ptrace(2))
   no ambiente Linuxulator. PT_SYSCALL tambem nao esta implementado.

  4.4. Armadilhas (Traps)

   Sempre que um processo Linux(R) executado na camada de emulac,ao captura a
   propria trap, ela e tratada de forma transparente com a unica excec,ao da
   traduc,ao de trap. Linux(R) e o FreeBSD difere de opiniao sobre o que e
   uma trap, entao isso e tratado aqui. O codigo e realmente muito curto:

 static int
 translate_traps(int signal, int trap_code)
 {

   if (signal != SIGBUS)
     return signal;

   switch (trap_code) {

     case T_PROTFLT:
     case T_TSSFLT:
     case T_DOUBLEFLT:
     case T_PAGEFLT:
       return SIGSEGV;

     default:
       return signal;
   }
 }

  4.5. Correc,ao de pilha

   O editor de links em tempo de execuc,ao do RTLD espera as chamadas tags
   AUX na pilha durante uma execve, portanto, uma correc,ao deve ser feita
   para garantir isso. Naturalmente, cada sistema RTLD e diferente, portanto,
   a camada de emulac,ao deve fornecer sua propria rotina de correc,ao de
   pilha para fazer isso. O mesmo acontece com o Linuxulator. O
   elf_linux_fixup simplesmente copia tags AUX para a pilha e ajusta a pilha
   do processo de espac,o do usuario para apontar logo apos essas tags.
   Entao, a RTLD funciona de maneira inteligente.

  4.6. Suporte para A.OUT

   A camada de emulac,ao Linux(R) em i386 tambem suporta os binarios Linux(R)
   A.OUT. Praticamente tudo o que foi descrito nas sec,oes anteriores deve
   ser implementado para o suporte A.OUT (alem da traduc,ao de traps e o
   envio de sinais). O suporte para binarios A.OUT nao e mais mantido,
   especialmente a emulac,ao 2.6 nao funciona com ele, mas isso nao causa
   nenhum problema, ja que os ports linux-base provavelmente nao suportam
   binarios A.OUT. Esse suporte provavelmente sera removido no futuro. A
   maioria das coisas necessarias para carregar os binarios Linux(R) A.OUT
   esta no arquivo imgact_linux.c.

5. Parte da camada de emulac,ao -MI do Linux(R)

   Esta sec,ao fala sobre parte independente de maquina do Linuxulator. Ele
   cobre a infra-estrutura de emulac,ao necessaria para a emulac,ao do
   Linux(R) 2.6, a implementac,ao do TLS (thread local storage) (no i386) e
   os futexes. Entao falamos brevemente sobre algumas syscalls.

  5.1. Descric,ao do NPTL

   Uma das principais areas de progresso no desenvolvimento do Linux(R) 2.6
   foi o threading. Antes do 2.6, o suporte ao threading Linux(R) era
   implementado na biblioteca linuxthreads. A biblioteca foi uma
   implementac,ao parcial do threading POSIX(R). A segmentac,ao foi
   implementada usando processos separados para cada threading usando a
   syscall clone para permitir que eles compartilhem o espac,o de enderec,o
   (e outras coisas). A principal fraqueza desta abordagem era que cada
   thread tinha um PID diferente, o tratamento de sinal era quebrado (da
   perspectiva pthreads), etc. O desempenho tambem nao era muito bom (uso de
   sinais SIGUSR para sincronizac,ao de threads) , consumo de recursos do
   kernel, etc.) para superar esses problemas, um novo sistema de threading
   foi desenvolvido e denominado NPTL.

   A biblioteca NPTL focou em duas coisas, mas uma terceira coisa apareceu,
   entao e normalmente considerada parte do NPTL. Essas duas coisas eram a
   incorporac,ao de threads em uma estrutura de processo e futexes. A
   terceira coisa adicional foi o TLS, que nao e diretamente exigido pelo
   NPTL, mas toda a biblioteca de usuario do NPTL depende dele. Essas
   melhorias resultaram em muito melhor desempenho e conformidade com os
   padroes. O NPTL e uma biblioteca de threading padrao nos sistemas Linux(R)
   atualmente.

   A implementac,ao do FreeBSD Linuxulator se aproxima do NPTL em tres areas
   principais. O TLS, futexes e PID mangling, que serve para simular as
   threadings Linux(R). Outras sec,oes descrevem cada uma dessas areas.

  5.2. Infra-estrutura de emulac,ao do Linux(R) 2.6

   Estas sec,oes tratam da maneira como as threadings Linux(R) sao
   gerenciadas e como nos simulamos isso no FreeBSD.

    5.2.1. Determinac,ao de tempo de execuc,ao de emulac,ao 2.6

   A camada de emulac,ao do Linux(R) no FreeBSD suporta a configurac,ao de
   tempo de execuc,ao da versao emulada. Isso e feito via sysctl(8), a saber
   compat.linux.osrelease. A configurac,ao dessa sysctl(8) afeta o
   comportamento de tempo de execuc,ao da camada de emulac,ao. Quando
   definido como 2.6.x, ele configura o valor de linux_use_linux26 enquanto a
   configurac,ao para algo mais o mantem nao definido. Essa variavel (mais
   variaveis por prisao do mesmo tipo) determina se a infraestrutura 2.6
   (principalmente o PID) e usada no codigo ou nao. A configurac,ao da versao
   e feita em todo o sistema e isso afeta todos os processos Linux(R). A
   sysctl(8) nao deve ser alterada ao executar qualquer binario do Linux(R),
   pois pode causar danos .

    5.2.2. Processos e identificadores de threading Linux(R)

   A semantica de threading Linux(R) e um pouco confusa e usa uma
   nomenclatura inteiramente diferente do FreeBSD. Um processo em Linux(R)
   consiste em uma struct task incorporando dois campos identificadores - PID
   e TGID. O PID nao e um ID de processo, mas e um ID de thread. O TGID
   identifica um grupo de threads em outras palavras, um processo. Para o
   processo single-threaded, o PID e igual ao TGID.

   A thread no NPTL e apenas um processo comum que acontece de ter TGID
   diferente de PID e ter um lider de grupo diferente de si mesmo (e VM
   compartilhada, e claro). Tudo o mais acontece da mesma maneira que em um
   processo comum. Nao ha separac,ao de um status compartilhado para alguma
   estrutura externa como no FreeBSD. Isso cria alguma duplicac,ao de
   informac,oes e possivel inconsistencia de dados. O kernel Linux(R) parece
   usar a tarefa -> grupo de informac,oes em alguns lugares e informac,oes de
   tarefas em outros lugares e isso nao e muito consistente e parece propenso
   a erros.

   Cada threading NPTL e criada por uma chamada a syscall clone com um
   conjunto especifico de flags (mais na proxima subsec,ao). O NPTL
   implementa segmentac,ao rigida de 1:1.

   No FreeBSD nos emulamos threads NPTL com processos comuns do FreeBSD que
   compartilham espac,o de VM, etc. e a ginastica PID e apenas imitada na
   estrutura especifica de emulac,ao anexada ao processo. A estrutura anexada
   ao processo se parece com:

 struct linux_emuldata {
   pid_t pid;

   int *child_set_tid; /* in clone(): Child.s TID to set on clone */
   int *child_clear_tid;/* in clone(): Child.s TID to clear on exit */

   struct linux_emuldata_shared *shared;

   int pdeath_signal; /* parent death signal */

   LIST_ENTRY(linux_emuldata) threads; /* list of linux threads */
 };

   O PID e usado para identificar o processo do FreeBSD que liga esta
   estrutura. child_se_tid e child_clear_tid sao usadas para copia do
   enderec,o TID quando um processo existe e e criado. O ponteiro shared
   aponta para uma estrutura compartilhada entre as threads. A variavel
   pdeath_signal identifica o sinal de morte do processo pai e o ponteiro
   threads e usado para vincular essa estrutura `a lista de threads. A
   estrutura linux_emuldata_shared se parece com:

 struct linux_emuldata_shared {

   int refs;

   pid_t group_pid;

   LIST_HEAD(, linux_emuldata) threads; /* head of list of linux threads */
 };

   O refs e um contador de referencia sendo usado para determinar quando
   podemos liberar a estrutura para evitar vazamentos de memoria. O group_pid
   e para identificar o PID (=TGID) de todo o processo (=grupo de threads). O
   ponteiro threads e o cabec,alho da lista de threading no processo.

   A estrutura linux_emuldata pode ser obtida a partir do processo usando
   em_find. O prototipo da func,ao e:

 struct linux_emuldata * em_find (struct proc *, int bloqueado);

   Aqui, proc e o processo em que queremos a estrutura emuldata e o parametro
   locked determina se queremos ou nao bloquear. Os valores aceitos sao
   EMUL_DOLOCK e EMUL_DOUNLOCK. Mais sobre o bloqueio mais tarde.

    5.2.3. Maqueando PID

   Por causa da visao diferente descrita sabendo o que e um ID de processo e
   ID de thread entre o FreeBSD e o Linux(R) nos temos que traduzir a view de
   alguma forma. Nos fazemos isso pelo manuseio do PID. Isto significa que
   nos falsificamos o que um PID (=TGID) e um TID (=PID) e entre o kernel e o
   userland. A regra e que no kernel (no Linuxulator) PID=PID e TGID=grupo de
   id -> compartilhado e para userland nos apresentamos PID=shared ->
   group_pid e TID=proc -> p_pid. O membro PID da estrutura linux_emuldata e
   um PID do FreeBSD.

   O acima afeta principalmente syscalls getyscl, getppid, gettid. Onde
   usamos PID/TGID, respectivamente. Em copia de TIDs em child_clear_tid e
   child_set_tid copiamos o PID FreeBSD.

    5.2.4. syscall Clone

   A syscall clone e o modo como as threads sao criadas no Linux(R). O
   prototipo syscall e assim:

 int linux_clone(l_int flags, void *stack, void *parent_tidptr, int dummy,
 void * child_tidptr);

   O parametro flags informa a syscall como exatamente os processos devem ser
   clonados. Como descrito acima, o Linux(R) pode criar processos
   compartilhando varias coisas independentemente, por exemplo, dois
   processos podem compartilhar file descriptors, mas nao VM, etc. Ultimo
   byte do parametro flags e o sinal de saida do processo recem-criado. O
   parametro stack se nao NULL diz, onde esta a pilha de threading e se e
   NULL nos devemos copiar-na-escrita chamando a pilha de processos (isto e,
   faz a rotina normal de fork(2)). O parametro parent_tidptr e usado como um
   enderec,o para copiar o PID do processo (ou seja, o id do thread), uma vez
   que o processo esteja suficientemente instanciado, mas ainda nao seja
   executavel. O parametro dummy esta aqui devido `a convenc,ao de chamada
   muito estranha desta syscall em i386. Ele usa os registradores diretamente
   e nao deixa o compilador fazer o que resulta na necessidade de uma syscall
   falsa. O parametro child_tidptr e usado como um enderec,o para copiar o
   PID assim que o processo terminar de bifurcar e quando o processo
   terminar.

   O syscall prossegue definindo flags correspondentes dependendo dos flags
   passadas. Por exemplo, mapas CLONE_VM para RFMEM (compartilhamento de VM),
   etc. O unico nit aqui e CLONE_FS e CLONE_FILES porque o FreeBSD nao
   permite configurar isso separadamente, entao nos o falsificamos nao
   configurando RFFDG (copiando a tabela fd e outras informac,oes fs) se
   qualquer uma delas estiver definida. Isso nao causa nenhum problema,
   porque essas flags sao sempre definidas juntas. Depois de definir as
   flags, o processo e bifurcado usando a rotina fork1 interna, o processo e
   instrumentado para nao ser colocado em uma fila de execuc,ao, ou seja, nao
   deve ser definido como executavel. Depois que a bifurcac,ao e feita,
   possivelmente reparamos o processo recem-criado para emular a semantica
   CLONE_PARENT. A proxima parte esta criando os dados de emulac,ao. Threads
   no Linux(R) nao sinalizam seus processos pais, entao nos definimos o sinal
   de saida como 0 para desabilitar isso. Depois que a configurac,ao de
   child_set_tid e child_clear_tid e executada, habilitando a funcionalidade
   posteriormente no codigo. Neste ponto, copiamos o PID para o enderec,o
   especificado por parent_tidptr. A configurac,ao da pilha de processos e
   feita simplesmente reescrevendo o registro do quadro de linha % esp (% rsp
   no amd64). A proxima parte e configurar o TLS para o processo
   recem-criado. Depois disso, a semantica vfork(2) pode ser emulada e,
   finalmente, o processo recem-criado e colocado em uma fila de execuc,ao e
   copiando seu PID para o processo pai atraves do valor de retorno clone e
   feito.

   A syscall clone e capaz e de fato e usado para emulac,ao de syscalls
   fork() e vfork(2). O glibc mais novo em um caso de kernel 2.6 usa o clone
   para implementar syscalls fork(2) e vfork(2).

    5.2.5. Bloqueio

   O bloqueio e implementado como per-subsystem porque nao esperamos muita
   disputa sobre eles. Existem dois bloqueios: emul_lock usado para proteger
   a manipulac,ao de linux_emuldata e emul_shared_lock usado para manipular
   linux_emuldata_shared. O emul_lock e um mutex bloqueador nao toleravel,
   enquanto emul_shared_lock e um bloqueio travavel sx_lock. Devido ao
   bloqueio por subsistema, podemos unir alguns bloqueios e e por isso que o
   em-find oferece o acesso sem bloqueio.

  5.3. TLS

   Esta sec,ao trata do TLS tambem conhecido como armazenamento local de
   thread.

    5.3.1. Introduc,ao ao threading

   Threads na ciencia da computac,ao sao entidades com um processo que podem
   ser agendados independentemente de qualquer outro. As threads nos
   processos compartilham amplos dados de processos (file descriptors, etc.)
   mas tambem tem sua prropria pilha para seus proprios dados. Algumas vezes
   e preciso para um processamento amplo de dados dado uma thread. Imagine um
   nome de uma thread algo assim. A tradicional API de threading do UNIX(R),
   pthreads prove um caminho para isso em pthread_key_create(3),
   pthread_setspecific(3) and pthread_getspecific(3) onde a thread pode criar
   uma chave para os dados da thread local pthread_getspecific(3) ou
   pthread_getspecific(3) para manipular esses dados. Voce pode ver que esse
   nao e o caminho mais confortavel que poderia ser usado. Entao varios
   produtores de compiladores C/C++ introduziram um caminho melhor. Eles
   definiram uma nova chave modificadora de thread que especifica que a
   variavel e especifica de uma thread. Um novo metodo de acessar as
   variaveis foi desenvolvio como (ao menos no i386). O metodo pthreads tende
   a ser implementado no espac,o de usuario como uma tabela de lookup
   trivial. A performance como uma soluc,ao nao e muito boa. Entao o novo
   metodo (no i386) registradores de segmentos para enderec,ar um segmento,
   onde a area do TLS e armazenada, entao o atual acesso da variavel de uma
   thread e apenas adicionada ao registrador de segmentos para o
   enderec,amento via it. Os registradores de segmentos sao usualmente %gs e
   %fs agindo como seletores de segmento. Toda thread tem sua propria area
   onde os dados da thread local sao armazenados e o segmento deve ser
   carregado em toda troca de contexto. Esse metodo e muito rapido e usado em
   todo mundo em volta do UNIX(R) i386. Ambos FreeBSD e Linux(R) Implementam
   sua abordagem e seus resultados tem sido muito bons. Unico ponto negativo
   e ter que recarregar o segmento em toda troca de contexto que pode deixar
   o processo lento. FreeBSD tenta evitar essa sobrecarga usando apenas 1
   descritor de segmento enquanto Linux(R) usa 3. Interessante que isso quase
   nunca usa mais que 1 descritor (apenas o Wine parece usar 2) entao o
   Linux(R) paga esse prec,o desnecessario na troca de contexto.

    5.3.2. Segmentos em i386

   A arquitetura i386 implementa os entao chamados segmentos.Um segmento e
   uma descric,ao de um espac,o na memoria. A base de enderec,o (baixa) na
   area da memoria, o fim disso (teto), tipo, protec,ao, etc. A memoria
   descrita por um segmento pode ser acessada usando um seletor de segmento
   (%cs, %ds, %ss, %es, %fs, %gs). Por exemplo, deixe nos supor que temos um
   segmento com base no enderec,o 0x1234 e comprimento e esse codigo:

 mov %edx,%gs:0x10

   Isso carregara o conteudo do registro % edx na localizac,ao da memoria
   0x1244. Alguns registradores de segmento tem um uso especial, por exemplo
   % cs e usado para segmento de codigo e % ss e usado para o segmento de
   pilha, mas % fs e % gs geralmente nao sao usados. Os segmentos sao
   armazenados em uma tabela GDT global ou em uma tabela LDT local. O LDT e
   acessado por meio de uma entrada no GDT. O LDT pode armazenar mais tipos
   de segmentos. LDT pode ser por processo. Ambas as tabelas definem ate 8191
   entradas.

    5.3.3. Implementac,ao no Linux(R) i386

   Existem duas maneiras principais de configurar o TLS no Linux(R). Pode ser
   definido ao clonar um processo usando a syscall clone ou ele pode chamar
   set_thread_area. Quando um processo passa a flag CLONE_SETTLS para clone,
   o kernel espera que a memoria apontada pelo registrador % esi uma
   representac,ao Linux(R) do espac,o do usuario de um segmento, que e
   traduzido para a representac,ao da maquina de um segmento e carregado em
   um slot GDT. O slot GDT pode ser especificado com um numero ou -1 pode ser
   usado, o que significa que o proprio sistema deve escolher o primeiro slot
   livre. Na pratica, a grande maioria dos programas usa apenas uma entrada
   de TLS e nao se importa com o numero da entrada. Nos exploramos isso na
   emulac,ao e dependemos disso.

    5.3.4. Emulac,ao de TLS do Linux(R)

      5.3.4.1. i386

   O carregamento de TLS para o segmento atual acontece chamando
   set_thread_area enquanto o TLS e carregado para um segundo processo em
   clone e feito no bloco separado em clone. Essas duas func,oes sao muito
   semelhantes. A unica diferenc,a e o carregamento real do segmento GDT, que
   acontece na proxima troca de contexto para o processo recem-criado,
   enquanto set_thread_area deve carregar isso diretamente. O codigo
   basicamente faz isso. Ele copia o descritor de segmento de formulario
   Linux(R) da area de usuario. O codigo verifica o numero do descritor, mas
   como isso difere entre o FreeBSD e o Linux(R), maquiamos um pouco. Nos
   suportamos apenas indices de 6, 3 e -1. O numero 6 e genuino do Linux(R),
   3 e genuino do FreeBSD one e -1 significa uma auto selec,ao. Em seguida,
   definimos o numero do descritor como constante 3 e copiamos isso para o
   espac,o do usuario. Contamos com o processo em espac,o de usuario usando o
   numero do descritor, mas isso funciona na maior parte do tempo (nunca vi
   um caso em que isso nao funcionou), como o processo em espac,o de usuario
   normalmente passa em 1. Entao, convertemos o descritor da classe do
   Linux(R) para um formulario dependente da maquina (isto e, independente do
   sistema operacional) e copie isto para o descritor de segmento definido
   pelo FreeBSD. Finalmente podemos carrega-lo. Atribuimos o descritor `as
   threads PCB (bloco de controle de processo) e carregamos o segmento % gs
   usando load_gs. Este carregamento deve ser feito em uma sec,ao critica
   para que nada possa nos interromper. O caso CLONE_SETTLS funciona
   exatamente como este, apenas o carregamento usando load_gs nao e
   executado. O segmento usado para isso (segmento numero 3) e compartilhado
   para este uso entre os processos do FreeBSD e do Linux(R) para que a
   camada de emulac,ao Linux(R) nao adicione nenhuma sobrecarga sobre o
   FreeBSD.

      5.3.4.2. amd64

   A implementac,ao do amd64 e semelhante `a do i386, mas inicialmente nao
   havia um descritor de segmento de 32 bits usado para esse proposito (por
   isso nem usuarios nativos de TLB de 32 bits trabalhavam), entao tivemos
   que adicionar esse segmento e implementar seu carregamento em cada troca
   de contexto (quando a flag sinalizando uso de 32 bits esta definida). Alem
   disso, o carregamento de TLS e exatamente o mesmo, apenas os numeros de
   segmento sao diferentes e o formato do descritor e o carregamento diferem
   ligeiramente.

  5.4. Futexes

    5.4.1. Introduc,ao `a sincronizac,ao

   Threads precisam de algum tipo de sincronizac,ao e POSIX(R) fornece alguns
   deles: mutexes para exclusao mutua, bloqueios de leitura/gravac,ao para
   exclusao mutua com relac,ao de polarizac,ao de leituras e gravac,oes e
   variaveis de condic,ao para sinalizar um mudanc,a de status. E
   interessante observar que a API de thread POSIX(R) nao tem suporte para
   semaforos. Essas implementac,oes de rotinas de sincronizac,ao sao
   altamente dependentes do tipo de suporte a threading que temos. No modelo
   puro 1:M (espac,o de usuario), a implementac,ao pode ser feita apenas no
   espac,o do usuario e, portanto, ser muito rapida (as variaveis de
   condic,ao provavelmente serao implementadas usando sinais, ou seja, nao
   rapido) e simples. No modelo 1:1, a situac,ao tambem e bastante clara - as
   threading devem ser sincronizadas usando as facilidades do kernel (o que e
   muito lento porque uma syscall deve ser executada). O cenario M:N misto
   combina apenas a primeira e a segunda abordagem ou depende apenas do
   kernel. A sincronizac,ao de threads e uma parte vital da programac,ao
   ativada por threads e seu desempenho pode afetar muito o programa
   resultante. Benchmarks recentes no sistema operacional FreeBSD mostraram
   que uma implementac,ao sx_lock melhorada gerou 40% de acelerac,ao no ZFS
   (um usuario sx pesado), isso e algo in-kernel, mas mostra claramente quao
   importante e o desempenho das primitivas de sincronizac,ao. .

   Os programas em threading devem ser escritos com o minimo de contenc,ao
   possivel em bloqueios. Caso contrario, em vez de fazer um trabalho util, a
   threading apenas espera em um bloqueio. Devido a isso, os programas
   encadeados mais bem escritos mostram pouca contenc,ao de bloqueios.

    5.4.2. Introduc,ao a Futexes

   O Linux(R) implementa a segmentac,ao 1:1, ou seja, tem de utilizar
   primitivas de sincronizac,ao no kernel. Como afirmado anteriormente,
   programas encadeados bem escritos possuem pouca contenc,ao de bloqueio.
   Assim, uma sequencia tipica poderia ser executada como dois contador de
   referencia de mutex de aumento/reduc,ao atomico, que e muito rapido,
   conforme apresentado pelo exemplo a seguir:

 pthread_mutex_lock(&mutex);
 ....
 pthread_mutex_unlock(&mutex);

   O threading 1:1 nos forc,a a executar dois syscalls para as chamadas
   mutex, o que e muito lento.

   A soluc,ao que o Linux(R) 2.6 implementa e chamada de futexes. Futexes
   implementam a verificac,ao de contenc,ao no espac,o do usuario e chama
   primitivas do kernel apenas em um caso de contenc,ao. Assim, o caso tipico
   ocorre sem qualquer intervenc,ao do kernel. Isso produz uma implementac,ao
   de primitivas de sincronizac,ao razoavelmente rapida e flexivel.

    5.4.3. API do Futex

   A syscall do futex e assim:

 int futex(void *uaddr, int op, int val, struct timespec *timeout, void *uaddr2, int val3);

   Neste exemplo uaddr e um enderec,o do mutex no espac,o do usuario, op e
   uma operac,ao que estamos prestes a executar e os outros parametros tem
   significado por operac,ao.

   Futexes implementam as seguintes operac,oes:

     * FUTEX_WAIT

     * FUTEX_WAKE

     * FUTEX_FD

     * FUTEX_REQUEUE

     * FUTEX_CMP_REQUEUE

     * FUTEX_WAKE_OP

      5.4.3.1. FUTEX_WAIT

   Esta operac,ao verifica que no enderec,o uaddr o valor val e gravado. Se
   nao, EWOULDBLOCK e retornado, caso contrario, a thread e enfileirada no
   futex e e suspensa. Se o argumento timeout for diferente de zero, ele
   especificara o tempo maximo para a suspensao, caso contrario, a suspensao
   sera infinita.

      5.4.3.2. FUTEX_WAKE

   Esta operac,ao tem um futex em uaddr e acorda os primeiros futexes val
   enfileirados neste futex.

      5.4.3.3. FUTEX_FD

   Esta operac,ao associa um descritor de arquivo com um determinado futex.

      5.4.3.4. FUTEX_REQUEUE

   Esta operac,ao pega threads val enfileirados no futex em uaddr, acorda-os
   e pega as proximas threads val2 e enfileira-os no futex em uaddr2.

      5.4.3.5. FUTEX_CMP_REQUEUE

   Essa operac,ao faz o mesmo que FUTEX_REQUEUE, mas verifica se val3 e igual
   a val primeiro.

      5.4.3.6. FUTEX_WAKE_OP

   Esta operac,ao executa uma operac,ao atomica em val3 (que contem algum
   outro valor codificado) e uaddr. Entao, ele acorda threads val em futex em
   uaddr e se a operac,ao atomica retornar um numero positivo, ele ativa os
   threadings val2 em futex em uaddr2.

   As operac,oes implementadas em FUTEX_WAKE_OP:

     * FUTEX_OP_SET

     * FUTEX_OP_ADD

     * FUTEX_OP_OR

     * FUTEX_OP_AND

     * FUTEX_OP_XOR

  Nota:

   Nao existe um parametro val2 no prototipo do futex. O val2 e obtido do
   parametro struct timespec *timeout para as operac,oes FUTEX_REQUEUE,
   FUTEX_CMP_REQUEUE e FUTEX_WAKE_OP.

    5.4.4. Emulac,ao de Futex no FreeBSD

   A emulac,ao de futex no FreeBSD e retirada do NetBSD e posteriormente
   estendida por nos. Ele e colocado nos arquivos linux_futex.c e
   linux_futex.h. A estrutura futex se parece com:

 struct futex {
   void *f_uaddr;
   int f_refcount;

   LIST_ENTRY(futex) f_list;

   TAILQ_HEAD(lf_waiting_paroc, waiting_proc) f_waiting_proc;
 };

   E a estrutura waiting_proc e:

 struct waiting_proc {

   struct thread *wp_t;

   struct futex *wp_new_futex;

   TAILQ_ENTRY(waiting_proc) wp_list;
 };

      5.4.4.1. futex_get / futex_put

   Um futex e obtido usando a func,ao futex_get, que busca uma lista linear
   de futexes e retorna o encontrado ou cria um novo futex. Ao liberar um
   futex do uso, chamamos a func,ao futex_put, que diminui um contador de
   referencia do futex e, se o refcount chegar a zero, ele e liberado.

      5.4.4.2. futex_sleep

   Quando um futex enfileira uma thread para dormir, ele cria uma estrutura
   working_proc e coloca essa estrutura na lista dentro da estrutura do
   futex, entao apenas executa um tsleep(9) para suspender a threading. O
   sleep pode ser expirado. Depois de tsleep(9) retornar (a thread foi
   acordada ou expirou) a estrutura working_proc e removida da lista e e
   destruido. Tudo isso e feito na func,ao futex_sleep. Se nos formos
   acordados de futex_wake nos temos wp_new_futex setado entao nos dormimos
   nele. Desta forma, um novo enfileiramento e feito nesta func,ao.

      5.4.4.3. futex_wake

   Acordar uma thread em sleep em uma futex e performado na func,ao
   futex_wake. Primeiro nesta func,ao nos imitamos o comportamento estranho
   do Linux(R), onde ele acorda N threads para todas as operac,oes, a unica
   excec,ao e que as operac,oes REQUEUE sao executadas em threads N+1. Mas
   isso geralmente nao faz diferenc,a, pois estamos acordando todos as
   threads. Em seguida na func,ao no loop nos acordamos n threads, depois
   disso nos checamos se existe um novo futex para requeuering. Se assim for,
   nos enfileiramos novamente ate n2 threads no novo futex. Isso coopera com
   o futex_sleep.

      5.4.4.4. futex_wake_op

   A operac,ao FUTEX_WAKE_OP e bastante complicada. Primeiro nos obtemos dois
   futexes nos enderec,os uaddr e uaddr2 e entao executamos a operac,ao
   atomica usando val3 e uaddr2. Entao os waiters val no primeiro futex sao
   acordados e se a condic,ao de operac,ao atomica se mantem, nos acordamos o
   waiter val2 (ex timeout) no segundo futex.

      5.4.4.5. operac,ao atomica futex

   A operac,ao atomica usa dois parametros encoded_op e uaddr. A operac,ao
   codificada, codifica a operac,ao em si, comparando valor, argumento de
   operac,ao e argumento de comparac,ao. O pseudocodigo da operac,ao e como
   este:

 oldval = *uaddr2
 *uaddr2 = oldval OP oparg

   E isso e feito atomicamente. Primeiro, uma copia do numero em uaddr e
   executada e a operac,ao e concluida. O codigo manipula falhas de pagina e,
   se nenhuma falha de pagina ocorrer, oldval e comparado ao argumento cmparg
   com o comparador cmp.

      5.4.4.6. Bloqueio Futex

   A implementac,ao do Futex usa duas listas de lock que protegndo sx_lock e
   locks globais (Giant ou outra sx_lock). Cada operac,ao e executada
   bloqueada desde o inicio ate o final.

  5.5. Implementac,ao de varias syscalls

   Nesta sec,ao, descreverei algumas syscalls menores que merecem destaque,
   pois sua implementac,ao nao e obvia ou as syscalls sao interessantes de
   outro ponto de vista.

    5.5.1. *na familia de syscalls

   Durante o desenvolvimento do kernel 2.6.16 do Linux(R), os *at syscalls
   foram adicionados. Essas syscalls (openat, por exemplo) funcionam
   exatamente como suas contrapartes sem-menos, com a pequena excec,ao do
   parametro dirfd. Este parametro muda onde o arquivo dado, no qual a
   syscall deve ser executado, esta. Quando o parametro filename e absoluto
   dirfd e ignorado, mas quando o caminho para o arquivo e relativo, ele e
   checado. O parametro dirfd e um diretorio relativo ao qual o nome do
   caminho relativo e verificado. O parametro dirfd e um file descriptor de
   algum diretorio ou AT_FDCWD. Entao, por exemplo, a syscall openat pode ser
   assim:

 file descriptor 123 = /tmp/foo/, current working directory = /tmp/

 openat(123, /tmp/bah\, flags, mode)     /* opens /tmp/bah */
 openat(123, bah\, flags, mode)          /* opens /tmp/foo/bah */
 openat(AT_FDWCWD, bah\, flags, mode)    /* opens /tmp/bah */
 openat(stdio, bah\, flags, mode)        /* returns error because stdio is not a directory */

   Esta infra-estrutura e necessaria para evitar corridas ao abrir arquivos
   fora do diretorio de trabalho. Imagine que um processo consiste em duas
   threads, thread A e thread B. Thread A emite open (./tmp/foo/bah., Flags,
   mode) e antes de retornar ele se antecipa e a thread B e executada. A
   thread B nao se preocupa com as necessidades da thread A e renomeia ou
   remove o /tmp/foo/. Nos temos uma corrida. Para evitar isso, podemos abrir
   o /tmp/foo e usa-lo como dirfd para a syscall openat. Isso tambem permite
   que o usuario implemente diretorios de trabalho por thread.

   A familia do Linux(R) de *at syscalls contem: linux_openat, linux_mkdirat,
   linux_mknodat, linux_fchownat, linux_futimesat, linux_fstatat64,
   linux_unlinkat, linux_renameat, linux_linkat , linux_symlinkat,
   linux_readlinkat, linux_fchmodat e linux_faccessat. Tudo isso e
   implementado usando a rotina modificada namei(9) e a simples camada de
   quebra automatica.

      5.5.1.1. Implementac,ao

   A implementac,ao e feita alterando a rotina namei(9) (descrita acima) para
   obter o parametro adicional dirfd no sua estrutura nameidata , que
   especifica o ponto inicial da pesquisa do nome do caminho, em vez de usar
   o diretorio de trabalho atual todas as vezes. A resoluc,ao de dirfd do
   numero do file descriptor para um vnode e feita em *at syscalls nativo.
   Quando dirfd e AT_FDCWD, a entrada dvp na estrutura nameidata e NULL, mas
   dirfd e um numero diferente, obtemos um arquivo para este file descriptor,
   verificamos se este arquivo e valido e se ha vnode anexado a ele, entao
   obtemos um vnode. Entao nos verificamos este vnode por ser um diretorio.
   Na rotina real namei(9) simplesmente substituimos a variavel dvp vnode
   pela variavel dp na func,ao namei(9), que determina o ponto de partida. O
   namei(9) nao e usado diretamente, mas atraves de um rastreamento de
   diferentes func,oes em varios niveis. Por exemplo, o openat e assim:

 openat() --> kern_openat() --> vn_open() -> namei()

   Por esse motivo, kern_open e vn_open devem ser alterados para incorporar o
   parametro dirfd adicional. Nenhuma camada de compatibilidade e criada para
   esses, porque nao ha muitos usuarios disso e os usuarios podem ser
   facilmente convertidos. Esta implementac,ao geral permite ao FreeBSD
   implementar suas proprias *at syscalls. Isso esta sendo discutido agora.

    5.5.2. Ioctl

   A interface ioctl e bastante fragil devido `a sua generalidade. Nos temos
   que ter em mente que os dispositivos diferem entre Linux(R) e FreeBSD,
   entao alguns cuidados devem ser aplicados para fazer o trabalho de
   emulac,ao de ioctl corretamente. O manuseio ioctl e implementado em
   linux_ioctl.c, onde a func,ao linux_ioctl e definida. Esta func,ao
   simplesmente itera sobre conjuntos de manipuladores ioctl para encontrar
   um manipulador que implementa um dado comando. A syscall ioctl tem tres
   parametros, o file descriptor, comando e um argumento. O comando e um
   numero de 16 bits, que, em teoria, e dividido em alta classe determinante
   de 8 bits do comando ioctl e 8 bits baixos, que sao o comando real dentro
   do conjunto dado. A emulac,ao aproveita essa divisao. Implementamos
   manipuladores para cada conjunto, como sound_handler ou disk_handler. Cada
   manipulador tem um comando maximo e um comando minimo definido, que e
   usado para determinar qual manipulador e usado. Existem pequenos problemas
   com esta abordagem porque Linux(R) nao usa a divisao definida
   consistentemente, por isso as ioctls para um conjunto diferente estao
   dentro de um conjunto ao qual nao devem pertencer (ioctls genericos SCSI
   dentro do cdrom conjunto, etc.). O FreeBSD atualmente nao implementa
   muitos ioctls do Linux(R) (comparado ao NetBSD, por exemplo), mas o plano
   e portar os do NetBSD. A tendencia e usar o ioctls Linux(R) mesmo nos
   drivers nativos do FreeBSD, devido `a facil portabilidade dos aplicativos.

    5.5.3. Depurac,ao

   Cada syscall deve ser debugavel. Para isso, introduzimos uma pequena
   infra-estrutura. Nos temos o recurso ldebug, que informa se uma dada
   syscall deve ser depurada (configuravel atraves de um sysctl). Para
   impressao, temos as macros LMSG e ARGS. Essas sao usadas para alterar uma
   string imprimivel para mensagens uniformes de depurac,ao.

6. Conclusao

  6.1. Resultados

   Em abril de 2007, a camada de emulac,ao do Linux(R) e capaz de emular o
   kernel Linux(R) 2.6.16 muito bem. Os problemas remanescentes dizem
   respeito a futexes, inacabado na familia de syscalls *at, entrega de
   sinais problematicos, falta de epoll e inotify e provavelmente alguns bugs
   que ainda nao descobrimos. Apesar disso, somos capazes de executar
   basicamente todos os programas Linux(R) incluidos na colec,ao de ports do
   FreeBSD com o Fedora Core 4 em 2.6.16 e ha alguns relatos rudimentares de
   sucesso com o Fedora Core 6 em 2.6.16. O linux_base do Fedora Core 6 foi
   recentemente comprometido permitindo alguns testes adicionais da camada de
   emulac,ao e nos dando mais algumas dicas onde devemos nos esforc,ar para
   implementar o material que esta faltando.

   Nos podemos rodar os aplicativos mais usados como o www/linux-firefox,
   net-im/skype e alguns jogos da colec,ao dos ports. Alguns dos programas
   exibem mau comportamento na emulac,ao 2.6, mas isso esta atualmente sob
   investigac,ao e, espera-se, sera corrigido em breve. A unica grande
   aplicac,ao que se sabe que nao funciona e o Java(TM) Development Kit do
   Linux(R) e isto e devido ao requisito de epoll habilidade que nao esta
   diretamente relacionada ao kernel do Linux(R) 2.6.

   Esperamos habilitar a emulac,ao 2.6.16 por padrao algum tempo depois que o
   FreeBSD 7.0 for lanc,ado pelo menos para expor as partes da emulac,ao 2.6
   para alguns testes mais amplos. Feito isso, podemos mudar para o Fedora
   Core 6 linux_base, que e o plano final.

  6.2. Trabalho futuro

   O trabalho futuro deve focar na correc,ao dos problemas remanescentes com
   futexes, implementar o restante da familia de syscalls, corrigir a entrega
   de sinal e possivelmente implementar os recursos de epoll e inotify.

   Esperamos poder executar os programas mais importantes com perfeic,ao em
   breve, por isso poderemos alternar para a emulac,ao 2.6 por padrao e fazer
   do Fedora Core 6 o linux_base padrao porque o nosso atualmente usado
   Fedora Core 4 nao e mais suportado.

   O outro objetivo possivel e compartilhar nosso codigo com o NetBSD e o
   DragonflyBSD. O NetBSD tem algum suporte para emulac,ao 2.6, mas esta
   longe de ser concluido e nao foi realmente testado. O DragonflyBSD
   manifestou algum interesse em portar as melhorias do 2.6.

   Geralmente, como o Linux(R) se desenvolve, gostariamos de acompanhar seu
   desenvolvimento, implementando a syscalls recem-adicionado. Splice vem em
   mente primeiro. Algumas syscalls ja implementadas tambem sao altamente
   danificadas, por exemplo mremap e outras. Alguns aprimoramentos de
   desempenho tambem podem ser feitos, um lock mais refinado e outros.

  6.3. Equipe

   Eu colaborei neste projeto com (em ordem alfabetica):

     * John Baldwin <jhb@FreeBSD.org>

     * Konstantin Belousov <kib@FreeBSD.org>

     * Emmanuel Dreyfus

     * Scot Hetzel

     * Jung-uk Kim <jkim@FreeBSD.org>

     * Alexander Leidinger <netchild@FreeBSD.org>

     * Suleiman Souhlal <ssouhlal@FreeBSD.org>

     * Li Xiao

     * David Xu <davidxu@FreeBSD.org>

   Gostaria de agradecer a todas as pessoas por seus conselhos, revisoes de
   codigo e apoio geral.

7. Literaturas

    1. Marshall Kirk McKusick - George V. Nevile-Neil. Design and
       Implementation of the FreeBSD operating system. Addison-Wesley, 2005.

    2. https://tldp.org

    3. https://www.kernel.org
