Estilo de código do Kernel / Kernel Coding Style

(Nota do Tradutor: Este documento é pra ser uma tradução do Documents/CodingStyle do kernel, mas com várias adaptações e inclusões)

Coding Style do Kernel Linux

Este é um documento curto descrevendo o Coding Style preferido (n.t. praticamente obrigatório) do Kernel Linux. Coding Style é muito pessoal e eu (n.t. não eu, o autor original) não vou forçar minhas opiniões em ninguém, mas isso vale para tudo que eu tiver que manter, e eu prefiro para várias outras coisas também. Por favor considere os pontos apresentados aqui.

Primeiro, sugiro imprimir uma cópia do GNU coding standarts e não ler, mas quemá-los. É um bom gesto simbólico. [n.t. EH]

De qualquer modo, aí vai:

Capítulo 1: Identação

Tabs são 8 caracteres, então a identação usa 8 caracteres. (n.t. Tabs aparecem como 8 espacos na tela, no código deve ser usado apenas tabs e isso ocupa 1 caracter '\t')
Existem movimentos hereges que tentam definir a identação como 4 ou 2 caracteres, e isso é como definir o valor de Pi como 3. (n.t. Em outras linguagens e ocasiões se usa diferente, no kernel, use tab como 8 espacos)

Rationale: Toda a idéia por trás da identação é definir claramente aonde um bloco de código começa e termina. Especialmente depois de olhar para a tela por 20 horas seguidas, você achará bem mais fácil ver o funcionamento da identação se ela for grande.

Agora, algumas pessoas vão falar que tendo identações de 8 caracteres faz o código se mover muito para a direita e fica difícil de ler em um terminal de 80 caracteres. A resposta a isso é que se são necessários mais de 3 níveis de identação, é necessário repensar e corrigir o código.

Resumidamente, tabs de 8 caracteres deixam as coisas mais fáceis de ler, e tem o benefício adicional de alertá-lo quando as funções estão aninhadas muito profundamente. Aceite o alerta.

A maneira preferida de reduzir a necessidade de múltiplos níveis de identação é alinhar o "switch" e seus "case" filhos em uma mesma coluna ao invés de identar duas vezes. Exemplo:

  1. switch (suffix) {
  2. case 'G':
  3. case 'g':
  4. mem <<= 30;
  5. break;
  6. case 'M':
  7. case 'm':
  8. mem <<= 20;
  9. break;
  10. case 'K':
  11. case 'k':
  12. mem <<= 10;
  13. /* fall through */
  14. default:
  15. break;
  16. }

Não coloque vários comandos numa mesma linha a menos que você tenha algo a esconder (n.t. ou seja, nunca).
  1. if (condition) do_this;
  2. do_something_everytime;

Também não coloque diversas atribuições numa mesma linha. O Coding Style é simples, nada de expressões macetosas ou espertinhas.

Nunca use espaços para identação, com exceção de comentários, documentos e dentro do Kconfig.

Arranje um editor decente e não deixe espaços em branco no fim das linhas

Capítulo 2: Quebrando linhas e comandos longos

O Coding style está para garantir a legibilidade e mantenabilidade usando ferramentas comuns disponíveis.

O limite do tamanho das linhas é 80 colunas e isso é um limite fortemente favorecido (n.t. não ultrapasse 80 colunas, é sério)

Linhas maiores que 80 colunas devem ser quebradas em pedaços sensatos. Os restos devem ser menores que o original e colocados bem mais a direita. O mesmo se aplica a headers de função com vários argumentos (n.t. não quebrar em um parâmetro por linha). Longas strings também devem ser quebradas em strings menores. A única exceção é quando exceder 80 colunas não esconde informação e melhora a legibilidade.

  1. void fun(int a, int b, int c)
  2. {
  3. if (condition)
  4. printk(KERN_WARNING "Warning this is a long printk with "
  5. "3 parameters a: %u b: %u "
  6. "c: %u \n", a, b, c);
  7. else
  8. next_statement;
  9. }

Capítulo 3: Colocando espaços e chaves

A outra questão que sempre aparece em estilos de C é a localização das chaves. Diferente da identação, não há muitas razões técnicas para escolher um sistema em favor de outro, mas a maneira certa, mostradas pelos profetas K&R, é colocar as chaves no fim da linha, e colocar a chave que fecha antes, assim:

  1. if (x is true) {
  2. we do y
  3. }

Isso se aplica a todos os blocos de código que não são funções (if, switch, for,
while, do) - menos o case dentro do switch - . Exemplo:
  1. switch (action) {
  2. case KOBJ_ADD:
  3. return "add";
  4. case KOBJ_REMOVE:
  5. return "remove";
  6. case KOBJ_CHANGE:
  7. return "change";
  8. default:
  9. return NULL;
  10. }

Entretanto, há um caso especial, funções. Neste caso, usar a chave no início da linha seguinte, assim:
  1. int function(int x)
  2. {
  3. body of function
  4. }

Hereges dizem por aí que essa inconsistência é... inconsistente, mas todos os bons de cabeça sabem que a) K&R estão _CERTOS_ e b) K&R estão certos. Além do mais, funções são um caso especial em si pois não podem ser aninhadas em C.

Note que a chave que fecha fica sozinha na linha, exceto nos casos que é seguida por uma continuação do mesmo comando, por exemplo, if / else, do / while, assim:

  1. do {
  2. body of do-loop
  3. } while (condition);

e
  1. if (x == y) {
  2. ..
  3. } else if (x > y) {
  4. ...
  5. } else {
  6. ....
  7. }

Rationale: K&R.

Também note que essa colocação de chaves também minimiza o número de linhas (quase-)vazias, sem perda na legibilidade. Assim, como a tela tem um tamanho limitado de linhas, você tem mais linhas vazias para colocar comentários.

Não use chaves quando apenas um comando basta.

  1. if (condition)
  2. action();

Isso não se aplica se apenas um branch de um condicional é um comando único. Use chaves em ambos os ramos.

  1. if (condition) {
  2. do_this();
  3. do_that();
  4. } else {
  5. otherwise();
  6. }

3.1: Espaços

O estilo do kernel linux para uso de espaços depende pricipalmente no uso de função vs. keyword. Use um espaço após a maioria das keywords, (exceções: sizeof, typeof, alignof, __attribute__, pois se parecem com funções e são usadas com parênteses no Linux apesar de não ser obrigatório na linguagem)

Use um espaço após as seguintes keywords: if, switch, case, for, do, while

Mas não com sizeof, typeof, alignof, or __attribute__. Exemplo,

  1. s = sizeof(struct file);

Não adicione espaços ao redor de expressões dentro de parênteses. Exemplo de como não fazer:

  1. s = sizeof( struct file ); /* feio, feio! */

Quando declarando ponteiros ou uma função que retorna um ponteiro, recomenda-se deixar o '*' adjacente ao nome do dado ou da função e não ao nome do tipo. Exemplo:

  1. char *linux_banner;
  2. unsigned long long memparse(char *ptr, char **retptr);
  3. char *match_strdup(substring_t *s);

(n.t.: há uma paquena controvérsia nessa regra já que o tipo é, teoricamente, o tipo acrescido do *, exemplo,"char *" Mas essa regra preza pela legibilidade, outros coding styles podem usar char* ptr)

Use um espaço ao redor (em cada lado) dos operadores binários e ternários, como:

= + - < > * / % | & ^ <= >= == != ? :

mas sem espaço após operadores unários:

& * + - ~ ! sizeof typeof alignof __attribute__ defined

Nenum espaço antes ou depois dos operadores unários de incremento/decremento:

++ --

E nenhum espaço ao redor de '.' e '->'

Não deixe espaços em branco no fim das linhas. Alguns editores com identação "esperta" inserem novos espaços no começo das linhas conforme apropriado, mas esses espaços podem ficar sobrando no final se nada for digitado. (n.t. normalmente ficam linhas em branco apenas com espaço e \n, estas devem ser eliminadas)

Git (n.t. outras como quilt avisam e outras ferramentas de edição eliminam também se apropriadamente configuradas) te avisa sobre patches que adicionam espaços em branco extras, entretanto, isto pode causar problemas ao aplicar uma série de patches que incuiram as linhas e espaços em brancos no contexto.

Capítulo 4: Nomeando coisas

C é uma linguagem espartana, e logo os nomes deme ser curtos também. Ao contrário de Modula-2 e Pascal, programadores C não usam nomes bonitinhos como EssaVariavelEhUmContadorTemporario. Um programador C chama a variável de tmp, o que é muito mais simples e não mais difícil de entender.

Apesar de nomes com maiúsculas e minúsculas serem desencorajados (n.t. dica, não use letras maiúsculas em variáveis se não quiser correr o risco de entrar em combustão "espontânea") nomes descritivos para variáveis globais são obrigatórios. Chamar uma variável global de "foo" é ofensivo.

(n.t. deve-se usar static em variáveis globais "locais" pois daí elas são visíveis apenas no módulo correspondente. Variáveis "globalmente globais" não são declaradas com static e deve ter sua necessidade muito bem analisada)

Variáveis globais (para serem usadas quando _realmente_necessário_) precisam ter nomes descritivos bem como funções globais. Pro exemplo, uma função que conta o número de usuários ativos, deve ser chamada "count_active_users()" ou similar, não deve ser chamada "cntusr()"

Encodificar o tipo da função no nome (notação Húngara) é idiota - o compilador sabe e checa os tipos e acaba confundindo o programador.

Variáveis locais devem ser curtas e diretas. Se você tem um contador de loop qualquer, pode chamar de 'i'. Chamar de "loop_conter" é improdutivo, se há a chance de ele ser mal-entendido. Do mesmo jeito, uma variável que ganhe um valor temporário pode ser chamado de tmp

Se você tem medo de misturar seus nomes de variáveis, é porque você tem outro problema: crescimento excessivo das funções (ver capítulo 6)

Capítulo 5: Typedefs

Não use coisas como "vps_t".

É um erro usar typedefs para estruturas e ponteiros, quando você vê um:

vps_t a;

No código, o que significa?!

Já se você vê algo assim:

struct virtual_container *a;

dá pra saber o que quer dizer "a"

Vários dizem que typedefs ajudam a legibilidade. Não é, eles são úteis para:

(a) Objetos totalmente opacos (onde o typedef ajuda a esconder o que é o objeto).

Exemplo: "pte_t" etc.

N.t. : objetos opacos são objetos que por um motivo ou outro tem seu funcionamento interno escondido do resto do kernel, sendo possível apenas o seu uso com funções de acesso. O intuito é de evitar quebras de API possibilitando a evolução do funcionamento interno para funções essenciais dentro do kernel. O caso clássico é o atomic_t

(b) Tipos inteiros claros, aonde a abstração ajuda a evitar confusão entre int ou long.

Os u8/u16/u32 são perfeitamente apropriados

NOTA! É preciso que haja uma razão para isso, e não apenas para chamar um int de um nome bonitinho como em:

typedef unsigned long myflags_t;

mas apenas nas situações onde em algumas ocasiões se usa int, outras long, pode'se usar um typedef para esconder essa complexidade.

(c) para criar um novo tipo com o intuito de checagem de tipos.

(d) Novos tipos que são identicos a tipos do C99 em ocasiões especiais apenas.

Exemplo clássico, os tipos u8/u16/u32/u64 que são idênticos aos tipos C99 dependendo da ARCH em que se está. (n.t. simplificado)

Quando editando código existente que já usa um ou outro conjunto de tipos, usar o padrão existente.

(e) Compatibilidade com userspace

Em certas estruturas, visíveis do userspace, não podemos exigir o uso de tipos C99, logo se usam tipos compartilhados equivalentes (_u32 por exemplo) que são tipos compartilhados com userspace

Pode ser que haja outros casos mas a regra geral é nunca usar typedefs a menos que consiga encaixar numa das regras acima.

No geral, ponteiros ou estruturas com membros que podem ser facilmente acessados não devem ser typedefs.

Capítulo 6: Funções

Funções devem ser curtas e simples, e fazer apenas uma coisa bem feita. Devem caber em uma ou duas telas de texto (considere a tela 80x25) e fazer apenas uma coisa e fazê-la bem.

O comprimento máximo delas deve ser inversamente proporcional a complexidade e ao nível de identação. Ou seja, funções simples podem ser maiores, funções muito complexas e compridas devem ser quebradas em pedaços mais simples e curtos.

Preste atenção se você tem uma função complexa, que é difícil de entender, tente limitar o seu comprimento. Use funções auxiliares com nomes descritivos, e marque-a como 'inline' se houver preocupação com a performance.

(n.t. reformulado os dois parágrafos anteriores)

Outra medida de tamanho de função é o número de variáveis locais, não devem exceder 5/10, ou provavelmente você está fazendo algo de errado. Repense a função e quebre em pedaços menores. Um cérebro humano comum (desses que vendem em jarros de vidro por aí) consegue dar conta de 7 informações ao mesmo tempo, mais do que isso, acaba confundindo. Eu sei que o seu código é lindo, brilhante e mágico, mas as pessoas precisam entender o que você fez daqui a duas semanas.

Em arquivos fonte, separe funções com uma linha em branco. Se a função é exportada, a macro EXPORT* para ela deve estar imediatamente após a função. Exemplo:

  1. int system_is_up(void)
  2. {
  3. return system_state == SYSTEM_RUNNING;
  4. }
  5. EXPORT_SYMBOL(system_is_up);

Em protótipos de função, inclua nomes de parâmetro com os tipos. Apesar de não ser obrigatório de acordo com a norma C, é preferível em Linux pois adiciona informações valiosas para o leitor.

Chapter 7: Saída centralizada de funções

Apesar de deprecado por algumas pessoas, o equivalente do goto é usado frequentemente pelos compiladores (internamente) na forma de um branch incondicional.

O comando goto vem a ser útil quando uma função sai de vários lugares e várias operações (como cleanups) devem ser feitas.

Tem as seguintes vantagens:

- branches incondicionais são mais fáceis de entender
- aninhamento é reduzido
- erros por não atualizar diversos pontos de saída da função são reduzidos
- compilador tem menos trabalho de eliminar código redundante

  1. int fun(int a)
  2. {
  3. int result = 0;
  4. char *buffer = kmalloc(SIZE);
  5.  
  6. if (buffer == NULL)
  7. return -ENOMEM;
  8.  
  9. if (condition1) {
  10. while (loop1) {
  11. ...
  12. }
  13. result = 1;
  14. goto out;
  15. }
  16. ...
  17. out:
  18. kfree(buffer);
  19. return result;
  20. }

Chapter 9: Você fez caquinha

Beleza, todo mundo faz. O seu amigo que manja de Unix (n.t. possivelmente um gordo de barba branca comprida not unlike santa) provavelmente já te contou que o "GNU Emacs" automaticamente formata os códigos C para você (n.t. o VIM faz também) e você notou que sim, ele faz isso, mas o default é pior que o desejado (na verdade, é pior que digitar loucamente no teclado)

Então ou você pode se livrar do GNU Emacs ou usar valores mais sãos. Para fazer isso coloque o seguinte no seu arquivo .emacs:

  1. (defun c-lineup-arglist-tabs-only (ignored)
  2. "Line up argument lists by tabs, not spaces"
  3. (let* ((anchor (c-langelem-pos c-syntactic-element))
  4. (column (c-langelem-2nd-pos c-syntactic-element))
  5. (offset (- (1+ column) anchor))
  6. (steps (floor offset c-basic-offset)))
  7. (* (max steps 1)
  8. c-basic-offset)))
  9.  
  10. (add-hook 'c-mode-hook
  11. (lambda ()
  12. (let ((filename (buffer-file-name)))
  13. ;; Enable kernel mode for the appropriate files
  14. (when (and filename
  15. (string-match "~/src/linux-trees" filename))
  16. (setq indent-tabs-mode t)
  17. (c-set-style "linux")
  18. (c-set-offset 'arglist-cont-nonempty
  19. '(c-lineup-gcc-asm-reg
  20. c-lineup-arglist-tabs-only))))))

Isso vai fazer o emacs se adaptar melhor ao estilo de código do Kernel.

n. t: o VIM sai "out-of-the-box" com settings já razoáveis, mas pode ser adaptado. Conforme sugestões recebidas pelo tradutor (coloque no .vimrc):

shell> cat ~/.vimrc

  1. " tab = 1 caracter \t mostrado como 8 espacos e não incluir espaços no final "
  2. set ts=8
  3. set sw=8 noet
  4.  
  5. " para forcar 80 colunas - recomendado mas a melhor regua é o olho "
  6. set textwidth=80
  7.  
  8. " para ver os tabs, espacos e trailing spaces (copiar e colar) "
  9. set list
  10. set lcs=tab:>·,extends:<,precedes:>,trail:·
  11.  
  12. "para o VIM fazer identacao automatica "
  13. set smartindent
  14.  
  15. " muda cor de fundo quando estiver chegando próximo da 80 coluna "
  16. au BufWinEnter * if &textwidth > 8
  17. \ | let w:m1=matchadd('MatchParen', printf('\%%<%dv.\%%>%dv', &textwidth+1, &textwidth-8), -1)
  18. \ | let w:m2=matchadd('ErrorMsg', printf('\%%>%dv.\+', &textwidth), -1)
  19. \ | endif
  20.  
  21. " validação da função k[mzc]alloc e memset através de cor "
  22. " blog dica: kernelslacker.livejournal.com/129237.html?thread=628693 "
  23. highlight kmalloc ctermbg=red guibg=red
  24. match kmalloc /k[mzc]alloc(GFP_/
  25.  
  26. highlight memset ctermbg=red guibg=red
  27. match memset /memset.*\,\(\ \|\)0\(\ \|\));/

Mesmo que você não consiga fazer o seu editor formatar corretamente, nem tudo está perdido, use "indent"

Agora, o GNU indent usa os mesmos parâmetros lesados que o Emacs, logo é necessário fornecer opções adequadas ao programa. Mas não é difícil, pois as opções adequadas ("-kr -i8") foram colocadas num script que está na árvore do Kernel em scripts/Lindent

"indent" tem várias opções, e especialmente quando se trata de reformatação de comentários, é melhor ver a man page. Mas lembre-se, "lindent" não é substituto para boa programação

Capítulo 10: arquivos de configuração do Kconfig

Para todos os arquivos de configuração Kconfig* espalhados por toda a árvore do kernel a identação varia bastante. Linhas sob uma definição "config" são identadas com 1 tab, e texto de ajuda é identado com 2 espaços. Exemplo:

  1. config AUDIT
  2. bool "Auditing support"
  3. depends on NET
  4. help
  5. Enable auditing infrastructure that can be used with another
  6. kernel subsystem, such as SELinux (which requires this for
  7. logging of avc messages output). Does not do system-call
  8. auditing without CONFIG_AUDITSYSCALL.

Features que podem ser consideradas instáveis devem ser definidas dependende de "EXPERIENTAL":
  1. config SLUB
  2. depends on EXPERIMENTAL && !ARCH_USES_SLAB_PAGE_STRUCT
  3. bool "SLUB (Unqueued Allocator)"
  4. ...

Enquanto features realmente perigosas (exemplo, suporte a escrita para certos filesystems) devem anunciar isto ostensivamente na sua string de prompt:

  1. config ADFS_FS_RW
  2. bool "ADFS write support (DANGEROUS)"
  3. depends on ADFS_FS
  4. ...

Para a documentação completa dos arquivos de configuração ver o arquivo: Documentation/kbuild/kconfig-language.txt.

Capítulo 11: Estruturas de dados

Estruturas de dados que tem visibilidade fora do ambiente single-threaded em que são criadas e destruídas devem sempre ter um contador de referência. No kernel, não existe garbage-collecting (e fora é normalmente lento e ineficiente) o que significa que vice absolutamente _deve_ ter as referências de uso contadas.

(n.t.: Contador de referência 101: Um contador de referência é um membro de uma estrutura/classe que marca o número de usuários da estrutura. Normalmente tem um contador, e funções get() e put(). Sempre que você usar uma estrutura, chamar get() e quando não precisar mais dela, put() (essas funções devem ser seguras para multithread obviamente) Normalmente quando o último cliente dá put() a estrutura se auto-destrói, dependendo da situação.)

Usando um contador de referência você evita grande parte da necessidade de locks (com a exceção de get() e put()) e permite o uso por múltiplos clientes, que não precisam se preocupar com o gerenciamento da estrutura em si (além de chamar get() e put() adequadamente)

Note que o uso de locking não é um substituto para contagem de referência. Locking mantém a consistência enquanto contagem de referência é uma técnica de gerenciamento de memória. Normalmente se precisa dos dois, e não se deve confundir um com outro.

Muitas estruturas de dados tem, inclusive, dois níveis de contagem de referência, onde há usuários de diferentes "classes". Neste caso, o contador de subclasse abstrai todos os usuários contando apenas uma vez o contador principal e o liberando quando não houver mais usuários da subclasse.

Exemplos desse uso podem ser encontrados no gerenciamento de memória ("struct mm_struct": mm_users e mm_count) e no código do filesystem ("struct super_block":s_count e s_active)

Lembre-se: se outra thread pode achar sua estrutura, e você não tem um contador de referência, com muita certeza você tem um bug.

Capítulo 12: Macros, Enums e RTL

Nomes de macros definindo constantes e labels em enums são escritos em maiúsculas:

  1. #define CONSTANT 0x12345

Enums são preferidos quando definindo várias constantes relacionadas.

NOMES EM MAIÚSCULAS são preferidos mas macros que se parecem com funções podem ser escritas em minúsculas

Normalmente, funções inline são preferidas a macros que parecem funções

Macros com várias linhas devem ser colocadas dentro de um bloco do {} while:

  1. #define macrofun(a, b, c) \
  2. do { \
  3. if (a == 5) \
  4. do_this(b, c); \
  5. } while (0)

(n.t. Existem vários motivos para usar do {} while(0). O principal é manter o funcionamento aceitando ; no final. Só tem que se lembrar que é uma macro e não uma função e que não pode ser usada assim por exemplo: if (macro()) )

Coisas a serem evitadas quando usar macros:

1)Macros que afetam o fluxo do programa

  1. #define FOO(x) \
  2. do { \
  3. if (blah(x) < 0) \
  4. return -EBUGGERED; \
  5. } while(0)

É uma idéia muito ruim. Parece com uma função mas sai da função que o "chamou". Não confunda as pessoas que estão lendo o código.

2) macros que dependem de uma variável local com um nome específico

  1. #define FOO(val) bar(index, val)

Pode parecer uma boa idéia, mas é extremamente confuso quando alguém lê o código e é sujeito a quebras devido a mudanças inocentes. (n.t. Pior, como se trata de macro, não há "checagem de tipo de parâmetros". Ou seja, você vai receber um erro incompreensível apontando para a linha da macro, ou pior ainda, um funcionamento incorreto do programa)

3) macros com argumentos usados como l-values (n.t. l-value é algo que fica do lado esquerdo de um '=')

  1. FOO(x) = y;

Vai quebrar se FOO(x) virar uma função inline.

4) Esquecer as regras de precedência: macros definindo constantes usando expressões tem que envolvê-las em parênteses. Cuidado com questões similares de macros usando parâmetros

  1. #define CONSTANT 0x4000
  2. #define CONSTEXP (CONSTANT | 3)

Capítulo 13: Imprimindo mensagens de kernel

Desenvolvedores do Kernel gostam de ser vistos como letrados. Preocupe-se com a sintaxe das mensagens de kernel (n.t. todas as mensagens devem ser em inglês) Não use corruptelas como "dont" use "do not" ou "don't". Faça as mensagens curtas, claras e não ambíguas.

Mensagens do kernel não devem ser terminadas com um ponto.

Imprimir números em parênteses "(%d)" não adiciona valor e deve ser evitado. (n.t. ou seja, não precisa dos parênteses)

Existe várias macros de diagnósticos de drivers em que você deveria utilizar para garantir que as mensagens correspondem ao driver e dispositivo certo e são marcadas com o nível de debug certo: dev_err(), dev_warn(), dev_info(), assim por diante. Para mensagens que não são associadas com um dispositivo em particular, define pr_debug() and pr_info().

Elaborar boas mensagens de debu pode ser um desafio e tanto, e uma vez que você tenha-as, podem ser uma excelente ajuda para debug remoto. Tais mensagens devem ser compiladas quando o símbolo DEBUG não é definido (isto é, por default não devem aparecer) Quando você usa dev_dbg() ou pr_debug() isso se torna automático. Vários subsistemas tem opções de Kconfig que ligam -DDEBUG. Uma convenção similar usa VERBOSE_DEBUG para adicionar mensagens dev_vdbg() as mensagens já habilitadas com DEBUG.