docs: pt_BR: Translate 4.coding.rst into Portuguese

Translate the chapter regarding coding standards and patch formatting
rules ("4.Coding.rst") into Brazilian Portuguese.

This translation helps Portuguese-speaking developers better understand
the core guidelines required for kernel code contributions.

Signed-off-by: Daniel Pereira <danielmaraboo@gmail.com>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Message-ID: <20260614235044.42810-2-danielmaraboo@gmail.com>
This commit is contained in:
Daniel Pereira
2026-06-14 20:50:40 -03:00
committed by Jonathan Corbet
parent c2d0e81cb6
commit a5d47da390
2 changed files with 442 additions and 0 deletions

View File

@@ -0,0 +1,440 @@
.. SPDX-License-Identifier: GPL-2.0
Escrever o código corretamente
==============================
Embora haja muito o que se dizer sobre um processo de design sólido e orientado
à comunidade, a prova de qualquer projeto de desenvolvimento de kernel está no
código resultante. É o código que será examinado por outros desenvolvedores e
mesclado (ou não) na árvore principal (*mainline*). Portanto, é a qualidade
deste código que determinará o sucesso final do projeto.
Esta seção examinará o processo de codificação. Começaremos analisando uma série
de maneiras pelas quais os desenvolvedores de kernel podem errar. Em seguida, o
foco mudará para como fazer as coisas do jeito certo e as ferramentas que podem
ajudar nessa busca.
Armadilhas
----------
Estilo de Codificação
*********************
O kernel há muito possui um estilo de codificação padrão, descrito em
:ref:`Documentation/process/coding-style.rst <codingstyle>`. Por grande parte
desse tempo, as políticas descritas naquele arquivo eram consideradas, no
máximo, como recomendações. Como resultado, há uma quantidade substancial
de código no kernel que não cumpre as diretrizes de estilo de codificação.
A presença desse código leva a dois riscos independentes para os
desenvolvedores do kernel.
O primeiro deles é acreditar que os padrões de codificação do kernel não importam
e não são exigidos. A verdade é que adicionar novo código ao kernel é muito
difícil se esse código não estiver escrito de acordo com o padrão; muitos
desenvolvedores solicitarão que o código seja reformatado antes mesmo de
revisá-lo. Uma base de código tão grande quanto a do kernel exige certa
uniformidade para tornar possível que os desenvolvedores entendam rapidamente
qualquer parte dela. Portanto, não há mais espaço para códigos com formatações
estranhas.
Ocasionalmente, o estilo de codificação do kernel entrará em conflito com o
estilo exigido por um empregador. Nesses casos, o estilo do kernel terá que
vencer para que o código possa ser mesclado. Colocar código no kernel significa
abrir mão de um certo grau de controle de várias maneiras — incluindo o controle
sobre como o código é formatado.
A outra armadilha é presumir que o código já presente no kernel necessita
urgentemente de correções de estilo de codificação. Os desenvolvedores podem
começar a gerar patches de reformatação como uma forma de ganhar familiaridade
com o processo, ou como um meio de incluir seus nomes nos logs de alterações
(*changelogs*) do kernel ou ambos. No entanto, patches puramente de estilo de
codificação são vistos como ruído pela comunidade de desenvolvimento; eles tendem
a receber uma recepção fria. Portanto, é melhor evitar esse tipo de patch. É
natural corrigir o estilo de um trecho de código ao trabalhar nele por outros
motivos, mas mudanças de estilo de codificação não devem ser feitas apenas por
fazer.
O documento de estilo de codificação também não deve ser lido como uma lei
absoluta que nunca pode ser transgredida. Se houver um bom motivo para ir contra
o estilo (uma linha que se torna muito menos legível se for dividida para caber
no limite de 80 colunas, por exemplo), simplesmente faça isso.
Note que você também pode usar a ferramenta ``clang-format`` para ajudá-lo com
essas regras, para reformatar rapidamente partes do seu código de forma automática
e para revisar arquivos completos a fim de identificar erros de estilo de
codificação, erros de digitação e possíveis melhorias. Ela também é útil para
ordenar ``#includes``, alinhar variáveis/macros, reajustar o fluxo de textos e
outras tarefas semelhantes. Veja o arquivo
:ref:`Documentation/dev-tools/clang-format.rst <clangformat>` para mais detalhes.
Algumas configurações básicas do editor, como indentação e fins de linha,
serão definidas automaticamente se você estiver usando um editor compatível
com o EditorConfig. Consulte o site oficial do EditorConfig para obter mais
informações: https://editorconfig.org/
Camadas de Abstração
********************
Os professores de Ciência da Computação ensinam os alunos a fazerem uso
extensivo de camadas de abstração em nome da flexibilidade e da ocultação de
informações. Certamente o kernel faz uso extensivo de abstração; nenhum
projeto que envolva vários milhões de linhas de código poderia fazer o
contrário e sobreviver. No entanto, a experiência tem mostrado que a
abstração excessiva ou prematura pode ser tão prejudicial quanto a otimização
prematura. A abstração deve ser usada até o nível necessário e não além.
Em um nível simples, considere uma função que possui um argumento que é
sempre passado como zero por todos os chamadores. Alguém poderia manter esse
argumento caso alguém eventualmente precise usar a flexibilidade extra que ele
oferece. A essa altura, no entanto, as chances são grandes de que o código que
implementa esse argumento extra tenha sido quebrado de alguma forma sutil que
nunca foi percebida — porque ele nunca foi usado. Ou, quando surge a
necessidade de flexibilidade extra, ela não ocorre de uma forma que corresponda
à expectativa inicial do programador. Os desenvolvedores do kernel enviam
patches rotineiramente para remover argumentos não utilizados; eles não devem,
em geral, ser adicionados em primeiro lugar.
Camadas de abstração que ocultam o acesso ao hardware — frequentemente para
permitir que a maior parte de um driver seja usada com múltiplos sistemas
operacionais — são especialmente malvistas. Essas camadas obscurecem o código
e podem impor uma penalidade de desempenho; elas não pertencem ao kernel
Linux.
Por outro lado, se você se pegar copiando quantidades significativas de código
de outro subsistema do kernel, é hora de perguntar se faria sentido, de fato,
extrair parte desse código em uma biblioteca separada ou implementar essa
funcionalidade em um nível superior. Não há valor em duplicar o mesmo código
por todo o kernel.
Uso de #ifdef e do pré-processador em geral
*******************************************
O pré-processador C parece apresentar uma forte tentação para alguns
programadores C, que o veem como uma forma de codificar eficientemente uma grande
quantidade de flexibilidade em um arquivo-fonte. No entanto, o pré-processador
não é C, e o uso pesado dele resulta em um código muito mais difícil de ser lido
por outros e mais difícil para o compilador verificar a correção. O uso pesado
do pré-processador é quase sempre um sinal de código que precisa de algum
trabalho de limpeza.
A compilação condicional com #ifdef é, de fato, um recurso poderoso, e é
utilizada dentro do kernel. Mas há pouco desejo de ver um código que seja
salpicado liberalmente com blocos #ifdef. Como regra geral, o uso de #ifdef
deve ser confinado a arquivos de cabeçalho (headers) sempre que possível. O
código compilado condicionalmente pode ser confinado a funções que, se o código
não estiver presente, simplesmente se tornam vazias. O compilador irá então,
silenciosamente, otimizar e remover a chamada para a função vazia. O resultado
é um código muito mais limpo e fácil de acompanhar.
As macros do pré-processador C apresentam uma série de riscos, incluindo a
possível avaliação múltipla de expressões com efeitos colaterais e a falta de
segurança de tipos. Se você se sentir tentado a definir uma macro, considere a
criação de uma função inline em seu lugar. O código resultante será o mesmo,
mas as funções inline são mais fáceis de ler, não avaliam seus argumentos
múltiplas vezes e permitem que o compilador realize a checagem de tipos nos
argumentos e no valor de retorno.
Funções Inline
**************
No entanto, as funções inline apresentam um perigo próprio. Os programadores
podem ficar encantados com a eficiência percebida inerente a evitar uma chamada
de função e encher um arquivo de código-fonte com funções inline. Essas
funções, contudo, podem na verdade reduzir o desempenho. Como seu código é
replicado em cada local de chamada, elas acabam inflando o tamanho do kernel
compilado. Isso, por sua vez, cria pressão nos caches de memória do
processador, o que pode desacelerar a execução drasticamente. As funções
inline, como regra, devem ser bastante pequenas e relativamente raras. O custo
de uma chamada de função, afinal de contas, não é tão alto; a criação de um
grande número de funções inline é um exemplo clássico de otimização prematura.
Em geral, os programadores de kernel ignoram os efeitos de cache por sua própria
conta e risco. O clássico compromisso entre tempo e espaço (tradeoff) ensinado
nas aulas introdutórias de estruturas de dados frequentemente não se aplica ao
hardware contemporâneo. Espaço *é* tempo, no sentido de que um programa maior
será executado mais lentamente do que um que seja mais compacto.
Compiladores mais recentes desempenham um papel cada vez mais ativo em decidir
se uma determinada função deve ou não ser realmente inline. Portanto, a inserção
liberal da palavra-chave "inline" pode não apenas ser excessiva; ela também pode
ser irrelevante.
Mecanismo de Trava
******************
Em maio de 2006, a pilha de rede "Devicescape" foi, com grande alarde, lançada
sob a GPL e disponibilizada para inclusão no kernel mainline. Essa doação foi uma
notícia bem-vinda; o suporte para redes sem fio no Linux era considerado abaixo do
padrão, na melhor das hipóteses, e a pilha da Devicescape oferecia a promessa de
corrigir essa situação. No entanto, esse código só entrou de fato no mainline em
junho de 2007 (2.6.22). O que aconteceu?
Esse código mostrava vários sinais de ter sido desenvolvido a portas fechadas em
ambiente corporativo. Mas um grande problema em particular era que ele não havia
sido projetado para funcionar em sistemas multiprocessados. Antes que essa pilha
de rede (agora chamada de mac80211) pudesse ser integrada, um esquema de locking
(bloqueio) precisou ser adaptado a ela.
Era uma vez uma época em que o código do kernel Linux podia ser desenvolvido sem
pensar nos problemas de concorrência apresentados por sistemas multiprocessados.
Hoje, no entanto, este documento está sendo escrito em um laptop dual-core.
Mesmo em sistemas com um único processador, o trabalho feito para melhorar a
capacidade de resposta aumentará o nível de concorrência dentro do kernel. Os
dias em que o código do kernel podia ser escrito sem pensar em locking ficaram
há muito tempo no passado.
Qualquer recurso (estruturas de dados, registradores de hardware, etc.) que
possa ser acessado concorrentemente por mais de uma linha de execução deve ser
protegido por uma trava (lock). O novo código deve ser escrito com esse
requisito em mente; adaptar o locking após o fato é uma tarefa consideravelmente
mais difícil. Os desenvolvedores do kernel devem dedicar um tempo para
compreender as primitivas de locking disponíveis bem o suficiente para escolher
a ferramenta certa para o trabalho. Códigos que mostrem falta de atenção à
concorrência terão um caminho difícil para entrar no mainline.
Regressions
***********
Um perigo final que vale a pena mencionar é este: pode ser tentador fazer uma
alteração (que pode trazer grandes melhorias) que faça algo quebrar para os
usuários existentes. Esse tipo de alteração é chamado de "regressão", e as
regressões tornaram-se totalmente indesejadas no kernel mainline. Com poucas
exceções, as alterações que causarem regressões serão revertidas se a regressão
não puder ser corrigida em tempo hábil. É muito melhor evitar a regressão em
primeiro lugar.
Muitas vezes argumenta-se que uma regressão pode ser justificada se ela fizer as
coisas funcionarem para mais pessoas do que os problemas que ela cria. Por que
não fazer uma alteração se ela trouxer uma nova funcionalidade para dez sistemas
para cada um que ela quebrar? A melhor resposta para essa pergunta foi expressa
por Linus em julho de 2007:
::
Portanto, nós não corrigimos bugs introduzindo novos problemas. Esse caminho
leva à loucura, e ninguém nunca sabe se você está realmente fazendo algum
progresso real. São dois passos para frente, um passo para trás, ou um passo
para frente e dois passos para trás?
(https://lwn.net/Articles/243460/).
Um tipo de regressão especialmente indesejado é qualquer tipo de alteração na
ABI do espaço do usuário (user-space ABI). Uma vez que uma interface tenha sido
exportada para o espaço do usuário, ela deve receber suporte indefinidamente.
Esse fato torna a criação de interfaces de espaço do usuário particularmente
desafiadora: já que elas não podem ser alteradas de maneiras incompatíveis, elas
devem ser feitas corretamente na primeira vez. Por essa razão, exige-se sempre
muita reflexão, documentação clara e uma ampla revisão para as interfaces do
espaço do usuário.
Ferramentas de verificação de código
------------------------------------
Por enquanto, pelo menos, a escrita de código livre de erros continua sendo um
ideal que poucos de nós conseguem alcançar. O que podemos esperar fazer, no
entanto, é capturar e corrigir o máximo possível desses erros antes que nosso
código entre no kernel mainline. Para esse fim, os desenvolvedores do kernel
reuniram um conjunto impressionante de ferramentas que podem capturar uma ampla
variedade de problemas obscuros de forma automatizada. Qualquer problema
capturado pelo computador é um problema que não afligirá um usuário mais tarde,
portanto, é lógico que as ferramentas automatizadas devem ser usadas sempre que
possível.
O primeiro passo é simplesmente prestar atenção aos avisos (warnings) produzidos
com o compilador. As versões contemporâneas do gcc podem detectar (e alertar
sobre) um grande número de erros potenciais. Com bastante frequência, esses
avisos apontam para problemas reais. O código enviado para revisão deve, como
regra, não produzir nenhum aviso do compilador. Ao silenciar os avisos, tome o
cuidado de entender a real causa e tente evitar "correções" que façam o aviso
desaparecer sem resolver a sua origem.
Note que nem todos os avisos do compilador ficam ativados por padrão. Compile o
kernel com "make KCFLAGS=-W" para obter o conjunto completo.
O kernel fornece várias opções de configuração que ativam recursos de
depuração; a maioria delas é encontrada no submanu "kernel hacking". Várias
dessas opções devem ser ativadas para qualquer kernel usado para fins de
desenvolvimento ou teste. Em particular, você deve ativar:
- FRAME_WARN para obter avisos sobre quadros de pilha (stack frames) maiores
que um determinado valor. A saída gerada pode ser volumosa, mas não é
necessário se preocupar com os avisos de outras partes do kernel.
- DEBUG_OBJECTS adicionará código para rastrear o tempo de vida de vários
objetos criados pelo kernel e alertará quando as ações forem feitas fora de
ordem. Se você estiver adicionando um subsistema que cria (e exporta) seus
próprios objetos complexos, considere adicionar suporte à infraestrutura de
depuração de objetos.
- DEBUG_SLAB pode encontrar uma variedade de erros de alocação e uso de
memória; ele deve ser usado na maioria dos kernels de desenvolvimento.
- DEBUG_SPINLOCK, DEBUG_ATOMIC_SLEEP e DEBUG_MUTEXES encontrarão uma série de
erros comuns de locking (bloqueio).
Existem várias outras opções de depuração, algumas das quais serão discutidas
abaixo. Algumas delas têm um impacto significativo no desempenho e não devem ser
usadas o tempo todo. Mas um tempo gasto aprendendo as opções disponíveis
provavelmente se pagará muitas vezes em pouco tempo.
Uma das ferramentas de depuração mais pesadas é o verificador de locking, ou
"lockdep". Esta ferramenta rastreará a aquisição e a liberação de cada trava
(spinlock ou mutex) no sistema, a ordem em que as travas são adquiridas umas em
relação às outras, o ambiente de interrupção atual e muito mais. Ela pode,
então, garantir que as travas sejam sempre adquiridas na mesma ordem, que as
mesmas suposições de interrupção se apliquem em todas as situações e assim por
diante. Em outras palavras, o lockdep pode encontrar uma série de cenários nos
quais o sistema poderia, em raras ocasiões, entrar em deadlock. Esse tipo de
problema pode ser doloroso (tanto para desenvolvedores quanto para usuários) em
um sistema implantado; o lockdep permite que eles sejam encontrados de maneira
automatizada e antecipada. Códigos com qualquer tipo de locking não trivial
devem ser executados com o lockdep ativado antes de serem enviados para inclusão.
Como um programador de kernel diligente, você irá, sem dúvida, verificar o
status de retorno de qualquer operação (como uma alocação de memória) que possa
falhar. O fato, porém, é que os caminhos de recuperação de falha resultantes
estão, provavelmente, completamente não testados. Código não testado tende a ser
código quebrado; você poderia estar muito mais confiante em seu código se todos
esses caminhos de tratamento de erros tivessem sido exercitados algumas vezes.
O kernel fornece um framework de injeção de falhas (fault injection) que pode
fazer exatamente isso, especialmente onde alocações de memória estão
envolvidas. Com a injeção de falhas ativada, uma porcentagem configurável das
alocações de memória será forçada a falhar; essas falhas podem ser restritas a
um intervalo específico de código. Executar o código com a injeção de falhas
ativada permite ao programador ver como o código responde quando as coisas vão
mal. Veja Documentation/fault-injection/fault-injection.rst para mais
informações sobre como usar esse recurso.
Outros tipos de erros podem ser encontrados com a ferramenta de análise estática
"sparse". Com o sparse, o programador pode ser alertado sobre confusões entre
endereços do espaço do usuário e do espaço do kernel, mistura de quantidades
big-endian e small-endian, a passagem de valores inteiros onde um conjunto de
sinalizadores de bits (bit flags) é esperado, e assim por diante. O sparse deve
ser instalado separadamente (ele pode ser encontrado em
https://sparse.wiki.kernel.org/index.php/Main_Page se a sua distribuição não o
incluir como pacote); ele pode então ser executado no código adicionando "C=1"
ao seu comando make.
A ferramenta "Coccinelle" (http://coccinelle.lip6.fr/) é capaz de encontrar uma
ampla variedade de potenciais problemas de codificação; ela também pode propor
correções para esses problemas. Uma quantidade considerável de "patches
semânticos" para o kernel foi empacotada sob o diretório scripts/coccinelle;
executar "make coccicheck" passará por esses patches semânticos e relatará
quaisquer problemas encontrados. Veja
:ref:`Documentation/dev-tools/coccinelle.rst <devtools_coccinelle>`
para mais informações.
Outros tipos de erros de portabilidade são encontrados mais facilmente ao
compilar seu código para outras arquiteturas. Se você por acaso não tiver um
sistema S/390 ou uma placa de desenvolvimento Blackfin à mão, ainda assim poderá
realizar a etapa de compilação. Um grande conjunto de compiladores cruzados
(cross-compilers) para sistemas x86 pode ser encontrado em:
https://www.kernel.org/pub/tools/crosstool/
Um tempo gasto instalando e usando esses compiladores ajudará a evitar
constrangimentos mais tarde.
Documentação
-------------
A documentação frequentemente tem sido mais a exceção do que a regra no
desenvolvimento do kernel. Mesmo assim, uma documentação adequada ajudará a
facilitar a integração de novos códigos ao kernel, tornará a vida mais fácil para
outros desenvolvedores e será útil para os seus usuários. Em muitos casos, a
adição de documentação tornou-se essencialmente obrigatória.
A primeira parte da documentação de qualquer patch é o seu log de alterações
(changelog) associado. As entradas do log devem descrever o problema que está
sendo resolvido, a forma da solução, as pessoas que trabalharam no patch,
quaisquer efeitos relevantes no desempenho e qualquer outra coisa que possa ser
necessária para entender o patch. Certifique-se de que o changelog diga o
*porquê* de o patch valer a pena ser aplicado; um número surpreendente de
desenvolvedores falha em fornecer essa informação.
Qualquer código que adicione uma nova interface de espaço do usuário — incluindo
novos arquivos sysfs ou /proc — deve incluir a documentação dessa interface, de
modo a permitir que os desenvolvedores do espaço do usuário saibam com o que
estão trabalhando. Veja Documentation/ABI/README para uma descrição de como essa
documentação deve ser formatada e quais informações precisam ser fornecidas.
O arquivo :ref:`Documentation/admin-guide/kernel-parameters.rst
<kernelparameters>` descreve todos os parâmetros de boot do kernel. Qualquer
patch que adicione novos parâmetros deve adicionar as entradas apropriadas a
este arquivo.
Quaisquer novas opções de configuração devem ser acompanhadas por um texto de
ajuda que explique claramente as opções e quando o usuário pode querer
selecioná-las.
As informações de API interna de muitos subsistemas são documentadas por meio de
comentários com formatação especial; esses comentários podem ser extraídos e
formatados de várias maneiras pelo script "kernel-doc". Se você estiver
trabalhando em um subsistema que possui comentários kerneldoc, você deve
mantê-los e adicioná-los, conforme apropriado, para funções disponíveis
externamente. Mesmo em áreas que não tenham sido documentadas dessa forma, não há
mal nenhum em adicionar comentários kerneldoc para o futuro; de fato, esta pode
ser uma atividade útil para desenvolvedores iniciantes de kernel. O formato
desses comentários, junto com algumas informações sobre como criar modelos de
kerneldoc, pode ser encontrado em :ref:`Documentation/doc-guide/ <doc_guide>`.
Qualquer pessoa que leia uma quantidade significativa de código existente do
kernel notará que, frequentemente, os comentários chamam a atenção por sua
ausência. Mais uma vez, as expectativas para códigos novos são mais altas do que
eram no passado; integrar código sem comentários será mais difícil. Dito isso,
há pouco interesse em códigos comentados de forma prolixa. O código deve, por si
só, ser legível, com os comentários explicando os aspectos mais sutis.
Certas coisas devem sempre ser comentadas. O uso de barreiras de memória
(memory barriers) deve ser acompanhado por uma linha explicando por que a
barreira é necessária. As regras de locking (bloqueio) para estruturas de dados
geralmente precisam ser explicadas em algum lugar. Grandes estruturas de dados
precisam de uma documentação abrangente em geral. Dependências não óbvias entre
trechos distintos de código devem ser apontadas. Qualquer coisa que possa tentar
um "faxineiro de código" (code janitor) a fazer uma "limpeza" incorreta precisa
de um comentário dizendo por que foi feita daquela maneira. E assim por diante.
Alterações de API interna
-------------------------
A interface binária fornecida pelo kernel para o espaço do usuário não pode ser
quebrada, exceto sob as circunstâncias mais graves. Por outro lado, as
interfaces de programação internas do kernel são altamente fluidas e podem ser
alteradas quando surgir a necessidade. Se você se encontrar tendo que criar uma
gambiarra para contornar uma API do kernel, ou simplesmente deixando de usar uma
funcionalidade específica porque ela não atende às suas necessidades, isso pode
ser um sinal de que a API precisa mudar. Como desenvolvedor de kernel, você tem
o poder de fazer tais alterações.
Existem, é claro, algumas pegadinhas. Alterações de API podem ser feitas, mas
precisam ser bem justificadas. Portanto, qualquer patch que faça uma alteração de
API interna deve ser acompanhado por uma descrição do que é a mudança e do porquê
ela é necessária. Esse tipo de alteração também deve ser separado em um patch
independente, em vez de ser enterrado dentro de um patch maior.
A outra pegadinha é que o desenvolvedor que altera uma API interna é geralmente
encarregado da tarefa de corrigir qualquer código dentro da árvore do kernel que
tenha sido quebrado pela mudança. Para uma função amplamente utilizada, esse
dever pode levar a literalmente centenas ou milhares de alterações — muitas das
quais provavelmente entrarão em conflito com o trabalho que está sendo feito por
outros desenvolvedores. Desnecessário dizer que isso pode ser um grande
trabalho, então é melhor ter certeza de que a justificativa é sólida. Note que
a ferramenta Coccinelle pode ajudar com alterações de API de amplo alcance.
Ao fazer uma alteração incompatível de API, deve-se, sempre que possível,
garantir que o código que não foi atualizado seja capturado pelo compilador.
Isso ajudará você a ter certeza de que encontrou todos os usos dessa interface
dentro da árvore (in-tree). Isso também alertará os desenvolvedores de códigos
fora da árvore (out-of-tree) de que há uma mudança à qual eles precisam
responder. Dar suporte a código fora da árvore não é algo com que os
desenvolvedores do kernel precisem se preocupar, mas também não temos que
tornar a vida dos desenvolvedores fora da árvore mais difícil do que precisa ser.

View File

@@ -20,3 +20,5 @@ conhecimento profundo de programação de kernel para ser compreendida.
1.Intro
2.Process
3.Early-stage
4.Coding