Processo de desenvolvimento do Kernel Linux

kernel linux
Este post faz parte da série Kernel Linux. Leia também os outros posts da série:

Introdução

 

O Sistema Operacional GNU/Linux é usado em larga escala no desenvolvimento de sistemas embarcados. Vários equipamentos e produtos utilizam este SO, desde celulares até equipamentos industriais. Este artigo tem por objetivo explicar o processo de desenvolvimento do Kernel Linux. O artigo se inicia com um breve histórico do kernel Linux e suas versões. Em seguida é explicado o ciclo de desenvolvimento atual do mesmo, apresentado os diversos repositórios e subsistemas envolvidos no processo.

 

O seguinte vocabulário precisa estar dominado para a leitura satisfatória deste artigo:

 

  • Patch - arquivo contendo diferenças textuais entre arquivos. Utilizado para descrever diferenças em código-fonte de linguagens de programação. Este arquivo é gerado automaticamente por ferramentas de controle de versão e pelo utilitário Unix diff;
  • Árvore (Source Tree) - repositório do código-fonte de um sistema de controle de versão;
  • branch (ramo) -  ramificação de um código fonte onde geralmente são implementadas novas funcionalidades;
  • Kernel mainline - Kernel Linux “vanilla” ou principal, originado da árvore principal de desenvolvimento, que é gerenciada por Linus Torvalds;
  • merge (mesclar) - é a junção de arquivos de código fonte. Esta operação é realizada por ferramentas de controle de versão como o git. Um exemplo de operação de merge é o merge entre dois branchs;
  • Problemas de regressão (regressions) - bugs em funcionalidades que antes estavam corretas e são causados por um determinado evento.

 

 

Breve histórico do kernel Linux

 

Como muitos sabem, a história do Linux começa com o Finlândes Linus Torvalds, um estudante com 21 anos de idade na Universidade de Helsinki, Finlândia. Linus estava trabalhando em sua ideia de desenvolver um sistema operacional como projeto pessoal, baseado no sistema operacional Minix.

 

A coisa realmente começou a ganhar corpo quando Linus estava tentando arranjar as especificações do POSIX e então enviou a seguinte mensagem em um grupo de discussão do MINIX:

 

Olá todo mundo que utiliza o minix -

Eu estou fazendo um sistema operacional (livre) (apenas como passatempo, não vai ser grande e profissional como o GNU) para clones AT 386(486). Ele têm amadurecido desde Abril e está começando a ficar pronto. Eu gostaria de quaisquer comentários sobre coisas que as pessoas gostam/desgostam no minix, já que meu SO lembra um pouco o minix (mesma disposição física do sistema de arquivos (devido a razões práticas) entre outras coisas).

 

Eu já portei o bash (1.08) e o gcc (1.40), tudo parece estar funcionando. Isso significa que eu vou conseguir algo prático dentro de alguns meses e eu gostaria de saber que características as pessoas gostariam que houvesse. Todas sugestões são bem vindas, mas eu não prometo que serão incorporadas 🙂

 

Linus ([email protected])

 

PS. Sim - ele é livre de qualquer código do minix e possui um sistema de arquivos com múltiplas linhas de execução (multi-threaded), ele NÃO é portável (usa troca de contexto do 386 etc) e provavelmente nunca vai suportar nada além dos discos rígidos AT, já que é tudo que eu possuo :-(.

 

A partir dai, foram várias as contribuições para o desenvolvimento do Kernel Linux feitas por parte da comunidade.

 

 

Por que o mascote do kernel Linux é um pinguim?

 

kernel Linux - TUX pinguin
Figura 1 - TUX

 

Apenas por curiosidade, diz a lenda que Linus tem uma fixação por “aves aquáticas gordas que não voam”, e que ele alega ter contraído “pinguinite” depois de ser mordido por um pinguim: “Pinguinite faz com que você fique acordado durante a noite pensando sobre pinguins e sentindo grande afeição com relação a eles.”. Esta suposta doença que Linus tem é uma piada, mas ele realmente foi mordido por um pinguim em uma visita a um zoológico da Austrália enquanto ele estava no país para participar de uma conferência de usuários do Unix em 1993, para falar sobre o Linux.

 

Acontece que Linus estava a procura de uma figura divertida e simpática para associar com o Linux, e um pinguim gordo sentado depois de ter uma bela refeição certamente se encaixava como uma luva. O Tux “(T)orvalds (U)ni(X)”, como o conhecemos hoje, foi criado por Larry Ewing em 1996 usando a primeira versão oficial do GIMP.

 

 

Resumo das versões do kernel Linux

 

A versão 1.0 do Kernel Linux foi lançada em 14 de março 1994. Esta versão só suportava computadores com um processador baseados na arquitetura i386. A versão 1.2, lançada em março de 1995, ganhou suporte para outras arquiteturas de processadores, como o Alpha, SPARC e MIPS.

 

A versão 2.0 foi lançada em junho de 1996. Houve 41 lançamentos nessa série. A principal característica desta versão foi o suporte a SMP (Symetric Multiprocessing - isto é, suporte para múltiplos processadores em um único sistema), além do suporte a novas arquiteturas de processadores.

 

A versão 2.2, lançada em janeiro de 1999, removeu o spinlock global (um sistema de travas utilizado para controlar a concorrência em sistemas SMP), adicionou suporte às arquiteturas mk68k e PowerPC além de novos sistemas de arquivos (incluindo o NTFS em modo de somente-leitura).

 

A versão 2.4.0, lançada em janeiro de 2001, provia suporte para ISA Plug and Play, USB, e PCMCIA. O desenvolvimento para a versão 2.4.x mudou e muitas outras características foram adicionadas durante o desenvolvimento desta série, incluindo: Bluetooth, RAID e o sistema de arquivos Ext3. Mais a frente focaremos no processo de desenvolvimento desta série.

 

A versão 2.6.0, que utiliza o processo de desenvolvimento atual, foi lançada em dezembro de 2003. Dentre as características adicionadas na série 2.6.x, estão:

  • Integração do uClinux na árvore principal do Kernel;
  • Suporte a PAE (Physical Address Extension - para utilizar mais que 4GB de memória RAM);
  • Suporte às novas linhas de CPU;
  • Integração do ALSA (sistema de áudio do Linux) com a árvore principal do Kernel;
  • Suporte para até 232 usuários, suporte para até 229 IDs de processo (para arquiteturas 64-bits).

 

Esta série também aumentou consideravelmente o número de tipos de dispositivos suportados, melhorou o suporte a arquiteturas 64-bits, suporte para sistemas de arquivos com tamanhos de arquivos de até 16 terabytes, preempção do Kernel, suporte para biblioteca nativa de threads POSIX (NPTL) além do suporte a diversos novos sistemas de arquivos: FUSE, JFS, XFS, ext4 dentre outros.

 

A versão 3.0, série que implementa o sistema de numeração atual do kernel, foi lançada em julho de 2011. A grande mudança, segundo Linus, foi “Nada, absolutamente nada”. A mensagem que Linus passou foi: “Vamos fazer com que a próxima versão do Kernel não seja somente um número brilhante, mas um bom kernel também”. Esta versão foi lançada próxima ao aniversário de 20 anos do kernel Linux.

 

Em dezembro de 2012, Linus e a comunidade decidiram reduzir a complexidade do kernel removendo o suporte a processadores i386, fazendo com que a versão 3.7 fosse a última a suportar estes processadores.

 

No momento da escrita deste artigo, a versão mais nova estável do kernel é a versão 3.16. A versão 3.17 está em estágio de RC (release candidate).

 

 

Processo de desenvolvimento do kernel anterior à versão 2.6

 

O processo de desenvolvimento do kernel Linux anterior a versão 2.6 se dava na seguinte maneira:

 

Havia um branch estável (2.4), onde somente pequenas e seguras mudanças eram incluídas (merge), e um branch instável (2.5), onde grandes mudanças e limpezas de código eram permitidas. Estes dois branchs eram mantidos pelo mesmo grupo de pessoas, liderados por Linus Torvalds. Isto permitia que usuários sempre teriam uma versão 2.4 bem testada, com as últimas atualizações de segurança e correções de bugs para usar. No entanto, as pessoas teriam que esperar pelas funcionalidades que iam para o branch 2.5. A desvantagem deste modelo é que o kernel “estável” estava tão desatualizado que ele acabou não suportanto as atualizações de hardware recentes além da falta de funcionalidades desejadas.

 

No final da série 2.5.x, alguns mantedores sugeriram que fosse tentado portar suas mudanças para o kernel estável. O branch 2.5 foi então renomeado para 2.6. No entanto, ao invés de abrir um branch 2.7 instável, os desenvolvedores do kernel decidiram continuar colocando as grandes alterações no branch do kernel 2.6, que então lançaria novas versões estáveis em um passo mais rápido que a série 2.4, mas mais devagar que a 2.5. Isto criou o efeito desejado de  lançar novas funcionalidades mais rapidamente ao mesmo tempo em que o código é devidamente testado, pois é adicionado em pequenos lotes.

 

Deste modo, atualmente os desenvolvedores do kernel Linux usam um processo de lançamento de versões levemente periódico, com uma nova versão a cada 2 ou 3 meses.  Segue descritivo 4 das últimas versões: 

  • Linux 3.15 lançado em 8 Junho de 2014 (70 dias);
  • Linux 3.14 lançado em 30 de março de 2014 (70 dias);
  • Linux 3.13 lançado em 19 de janeiro de  2014 (78 dias);
  • Linux 3.12 lançado em 2 de novembro de 2013 (61 dias).

 

Atualmente, cada versão 3.x é uma versão major, com novas funcionalidades e alterações na API interna. É importante notar que a API do Kernel, diferentemente da API da libc, por exemplo, está em constante mudança. A árvore principal do kernel Linux, mainline, é mantida por Linus Torvalds até hoje.

 

 

Processo de desenvolvimento do kernel Linux utilizado atualmente

 

O processo de merge de patchs segue uma disciplina relativamente direta. No começo do desenvolvimento de cada ciclo, “uma janela de merge” é dita aberta. Neste momento, código que é considerado suficientemente estável e aceito pela comunidade de desenvolvimento é incluído no kernel mainline por meio de merges. Estes códigos não vêm do nada. Eles foram previamente coletados, testados e organizados. Este processo será discutido mais adiante.

 

A janela de merge dura aproximadamente duas semanas. Ao final deste período, Linus Torvalds declara a janela fechada e então disponibiliza o primeiro candidado a versão final deste kernel (RC - Release Candidate).  A disponibilização do -rc1 é um sinal que o tempo para fazer o merge de novas funcionalidades chegou ao fim, e o tempo para estabilizar a próxima versão chegou.

 

Durante as próximas 6 até 10 semanas, somente patches que corrigem problemas devem ser submetidos para o mainline. Raramente alterações significantes serão permitidas e desenvolvedores que insistirem em enviar uma alteração com a janela de merge fechada, tendem a receber uma mensagem nada amigável em resposta. Um exemplo de excessão permitida seria a integração de um driver para um hardware não suportado anteriormente e que não causa problemas de regressão em outras partes do código, isto é, o código utiliza puramente a API disponível naquele momento no kernel.

 

À medida que as correções de bug são feitas no mainline, o números de patchs enviados vai cair com o tempo. Linus lança novas -rc quase que semanalmente. Geralmente os rcs vão de rc6 até rc9 antes que o kernel seja considerado suficientemente estável e a versão final seja lançada. Neste ponto, o processo recomeça.

 

É muito importante que os projetos dos subsistemas tenha um responsável. Como exemplo de como as coisas podem ficar feias quando não se tem alguém gerenciando um projeto, podemos citar o caso do Buildroot. Entre 2005 e 2009 o Buildroot ficou sem mantenedor oficial, e todos os desenvolvedores tinham acesso de escrita no repositório. Este “caos” fez com que a qualidade do código caísse muito. A situação só melhorou em 2009, com a entrada do mantenedor Peter Korsgaard, que fez com que o sistema entrasse na linha, com releases periódicas e um grande esforço para limpeza do código.

 

Como os desenvolvedores decidem quando fechar o ciclo de desenvolvimento e criar uma versão estável? A mais importante métrica é a lista de problemas conhecidos de versões anteriores (regressions from previous releases). Nenhum bug é bem-vindo, mas aqueles que causam problemas em coisas que funcionavam anteriormente são considerados especialmente sérios. Por esta razão, patches que causam problemas são prováveis candidatos a serem retirados (reverted) durante o processo de estabilização.

 

Durante o processo de estabilização, o objetivo dos desenvolvedores é resolver o máximo número de problemas (regressions) antes que a versão estável seja lançada. No mundo real, este tipo de perfeição é difícil de ser alcançada. Existem muitas variáveis em um projeto deste tamanho. Existe um ponto onde demorar o lançamento da próxima versão, só torna o problema pior: a pilha de mudanças para a próxima janela de merge vai crescer, criando ainda mais problemas de regressão para a próxima rodada. Portanto, a maioria dos kernels é lançada com alguns problemas de regressão conhecidos, e com esperança, nenhum é sério.

 

Uma vez que o lançamento da versão estável é feita, a manutenção dela é passada para o “stable team”, consistindo atualmente de Greg Kroah-Hartman. O time estável vai lançar ocasionalmente atualizaçãoes para a release estável. Para ser considerado uma atualização de relase, um patch precisa (1) consertar um bug significante e (2) ter sido dado merge com o próximo kernel mainline. Os kernels vão tipicamente receber atualizações não mais que um ciclo de desenvolvimento a contar do seu lançamento.

 

Algumas versões de kernel são designadas para serem “long term”. Elas vão receber suporte por um período mais  longo. A tabela a seguir demonstra as últimas versões “long term”:

 

VersãoMantenedorData LançamentoFim de vida projetado
 3.14Greg Kroah-Hartman30-03-2014Agosto, 2016
3.12Jiri Slaby03-11-20132016
3.10Greg Kroah-Hartman30-06-2013Setembro, 2015

 

 

O ciclo de vida de um patch

 

Os patchs não vão diretamente do teclado do desenvolvedor para o kernel Linux mainline. Existe, ao contrário, um processo desenvolvido para garantir que cada patch seja revisado com relação à sua qualidade além de implementar uma mudança que é desejada de se ter no mainline. Este processo pode acontecer rapidamente, para pequenas mudanças, ou, em caso de mudanças controversas e grandes, alguns anos.

 

Os estágio que um patch passa são geralmente: 

  • Desenvolvimento: aqui é onde os requisitos reais para o patch são criados, e como eles serão satisfeitos e projetados. Esta parte é geralmente feita sem envolvimento da comunidade, embora seja melhor fazer isto o mais abertamente possível, pois pode salvar um monte de tempo com retrabalho.
  • Revisão primária: o patch é postado em uma lista de e-mail relevante e os desenvovedores nesta lista respondem com comentários que eles possam vir a ter. Neste ponto a maioria dos problemas com o patch é resolvida.
  • Revisão ampla: quando um patch está chegando próximo de ficar pronto para inclusão no mainline, ele deve ser aceito por algum mantedor de um subsistema relevante - apesar de isto não ser garantia de que ele será aceito no mainline. O patch será incluso na árvore do subsistema do mantedor e nas árvores “-next” (descritas a seguir). Quanto este processo funciona, isto leva a uma revisão mais extensiva do patch e a descoberta de qualquer problemas resultante da integração deste patch com o trabalho sendo feito por outras pessoas.
  • É importante notar que a maioria dos mantedores também possue outro emprego, e por isso, as vezes fazer o merge do seu patch pode não ser a maior das prioridades para eles. Se você recebe sugestões de mudanças no seu patch, você deve ou fazer estas mudanças ou justificar muito bem por que não fazê-las. Mesmo que seu patch não tenha reclamações após a revisão, mas não é incluso por merge pelo mantedor do subsistema ou device driver, você deve ser persistente em atualizar o patch para o Kernel atual para que ele seja aplicável e continue mandando ele para revisão e merge.
  • Merge no Kernel mainline. Eventualmente, será realizado o merge de um patch no repositório do mainline, que é gerenciado por Linus Torvalds. Mais problemas e comentários podem surgir neste momento. É importante que o desenvolvedor seja responsivo e arrume os problemas que possam surgir.
  • Versão estável. o numero de usuários que podem ser afetados por um patch agora se torna grande, então, mais uma vez, novos problemas podem surgir.
  • Versão de longo termo (LTS). enquanto é certamente possível para um desenvolvedor esquecer do código após o merge com o mainline,  este tipo de comportamento tende a levar uma impressão ruim por parte da comunidade. Dar merge no código elimina algumas responsabilidade de manutenção, pois outros que irão resolver possíveis problemas da API do kernel. O desenvovedor original de um código deve tomar responsabilidade por ele caso ele queira que aquilo continue útil a longo prazo. 

 

A figura a seguir resume o processo de desenvolvimento atual.

 

Processo de desenvolvimento atual do kernel Linux
Figura 2 - Processo de desenvolvimento atual do kernel Linux

  

Referência da imagem: link 

 

 

Como o código chega no kernel Linux mainline?

 

Existe apenas uma pessoa que pode fazer merge de patches no repositório do kernel Linux mainline: Linus Torvalds. Mas, dos milhares de patches que vão para uma nova versão de kernel, apenas alguns poucos são diretamente escolhidos por Linus. O projeto do kernel há muito tempo cresceu tanto que nenhum único desenvolvedor consegue inspecionar e selecionar cada patch sozinho. O jeito que os desenvolvedores do kernel Linux têm tratado este problema de crescimento é pelo meio de alguns mantenedores chaves construindo assim uma corrente de confiança.

 

A base de código do kernel é logicamente quebrada em um conjunto de subsistemas: rede, suporte específico de arquitetura (X86, ARM, MIPS…), gerenciamento de memória, dispositivos de vídeo, etc. A maioria dos subsistemas tem um mantenedor designado: um desenvolvedor que tem a responsabilidade geral pelo código dentro do subsistema. Estes mantedores de subsistemas são os guardiões da porção do kernel que eles gerenciam. Eles são usualmente os que vão aceitar um patch para inclusão no kernel Linux mainline.

 

Quando a janela de merge abre, mantedores de nível mais alto vão pedir ao Linus para que pegue os patches selecionados de seus repositórios por eles e faça merge com o mainline. Caso Linus concorde, o fluxo de patches vai fluir para seu repositório, se tornando parte do mainline. Em geral Linus confia na qualidade do código enviada pelos mantenedores.

 

Os mantenedores de subsistemas também podem pegar patches de outros mantenedores. Por exemplo, a árvore do subsistema de rede é construida de patches que acumulam primeiro em árvores dedicadas device drivers de dispositivos de rede, wireless, etc. Esta cadeia de repositórios pode ser longa, mas raramente passa de 2 ou 3 níveis. Como cada mantenedor na cadeia confia naqueles gerenciando essas árvores em nível mais baixo, este processo é conhecido como “cadeia de confiança”.

 

Portanto, está claro que patches devem ser enviados para mantedores, e não diretamente para o Linus.

 

A figura a seguir descreve o fluxo de um patch.

 

Kernel Linux - Fluxo de um patch
Figura 3 - Fluxo de um patch

 

Referencia da Imagem: link

 

 

Árvores “-next”

 

A cadeia de árvores de subsistemas guia o fluxo de patches no Kernel, mas ela também leva a uma pergunta interessante: e se alguém quiser olhar todos os patches que estão sendo preparados para a próxima janela de merge? Desenvolvedores sempre estão interessados em quais outras mudanças estão pendentes para ver se existe algum conflito pelo qual se preocupar; um patch que altera uma função do coração do Kernel, por exemplo vai conflitar com qualquer outros patches que usem a versão mais velha desta função. Revisores e testadores querem acesso a mudanças em sua forma integrada antes que todas estas mudanças entrem no Kernel mainline. Alguem poderia pegar todas as mudanças de todos os substistemas, mas esta seria uma grande tarefa, e sujeita a erros.

 

A resposta vem na forma de das árvores “-next”, onde árvores de subsistemas são coletadas para revisão e testes. A árvore primária para o próximo ciclo de merge de patches é a “linux-next”, mantida por Stephen Rothwell. A árvore linux-next é, por principio, uma foto do que o mainline é esperado ser após a próxima janela de merge fechar. As árvores “linux-next” são anunciadas nas listas de e-mail do Kernel do linux e na lista do “linux-next” quando estas estão construídas. Deste modo, estas árvores não evoluem como o mainline. Elas são sempre o merge das árvores mais atuais do diferentes subsistemas com o mainline.

 

 

Árvores Staging

 

A árvore do código fonte do Kernel contém o diretório “drivers/staging”, onde muitos subdiredórios de drivers ou sistemas de arquivos que estão no seu caminho para ser adicionados na árvore do Kernel residem. Eles continuam neste diretório enquanto precisar de trabalho; uma vez completos, eles podem ser movidos para o Kernel corretamente.  Este é um jeito de manter a trilha de drivers que não estão de acordo com os padrões de qualidade de código do Kernel, mas que alguém possa querer usar enquanto rastreiam seu desenvolvimento.

 

Greg Kroah-Hartman atualmente mantén a árvore “stagin”. Drivers que ainda precisam de trabalho são enviados a ele, com cada driver tendo seu próprio subdiretorio em “driver/stagin/”. Em conjunto com os arquivos do código fonte, um arquivo de “TODO” deve estar presente no diretório. O arquivo de “TODO” lista as pendencias que o driver precisa resolver para ser aceito no Kernel, bem como uma lista de pessoas que precisam ser copiadas para qualquer patch para o driver. As regras atuais requerem que driver incluidos no staging, devem no mínimo compilar corretamente.

 

Este processo pode ser um meio relativamente fácil de colocar novos driver no mainline, onde com sorte, eles vão chamar a atenção de outros desenvolvedores e melhorar rapidamente. Entrar no “staing” não é o fim da história.; código em “staing” que não passa pelo processo regular vai eventualmente ser removido. Distribuidores tendem a ser relutantes em habilitar driver em “staging”. Portanto, staging é, na melhor das hipóteses, uma parada no caminho para se tornar um driver mainline.

 

 

Próximo artigo

 

No próximo artigo será abordado o padrão de codificação do kernel Linux bem como algumas ferramentas que podemos utilizar para facilitar o processo de enviar patches.

 

 

Referências

 

http://en.wikipedia.org/wiki/Tux

http://lxr.free-electrons.com/source/Documentation/development-process/2.Process

Outros artigos da série

Kernel Linux: Padrão de codificação >>
Este post faz da série Kernel Linux. Leia também os outros posts da série:
NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

8
Deixe um comentário

avatar
 
7 Comment threads
1 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
6 Comment authors
Henrique RossiMinake LemosAilson JunioralesouzaCesar Junior Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
trackback

[…] “O Sistema Operacional Linux é usado em larga escala no desenvolvimento de sistemas embarcados. Vários equipamentos e produtos utilizam este SO, desde celulares até equipamentos industriais. Este artigo tem por objetivo explicar o processo de desenvolvimento do Kernel do Linux. O artigo se inicia com um breve histórico do sistema operacional Linux e suas versões. Em seguida é explicado o ciclo de desenvolvimento atual do mesmo, apresentado os diversos repositórios e subsistemas envolvidos no processo.” [referência: embarcados.com.br] […]

Minake Lemos
Visitante
MINAKE

Boa noite, venho deixar a minha observação referente a esse artigo achei muito bom dez, como sou novo no site gostaria de saber se já foi publicado a continuação desse artigo?

At. Minake

Henrique Rossi
Visitante

Boa tarde Minake,

Desculpa pela demora...já foi publicada uma continuação:

Kernel Linux: Padrão de codificação

Abraços

trackback

[…] No post anterior, o processo de desenvolvimento do Kernel do Linux foi exposto. Para dar continuidade nesta série de 3 artigos, agora serão apresentados alguns conceitos práticos e ferramentas que podem ser utilizadas para facilitar a vida dos desenvolvedores do Kernel. Serão apresentados os padrões de codificação do Kernel do Linux e um script para agilizar a verificação de códigos com relação a este padrão. No próximo artigo, e final, vou ensinar como enviar um patch para o Kernel. […]

Ailson Junior
Visitante
Ailson Junior

Espetacular.

alesouza
Visitante
alesouza

Sensacional! A ideia é muito boa...esperando pelo próximo artigo. =]

Cesar Junior
Visitante
Cesar Junior

Muito bom! Parabéns pelo Artigo

Andre Tenorio
Visitante
( André Tenório )

Muito bom...