(Nota do Tradutor: Este documento é pra ser uma tradução do Documents/CodingStyle do kernel, mas com várias adaptações e inclusões)
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:
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:
switch (suffix) { case 'G': case 'g': mem <<= 30; break; case 'M': case 'm': mem <<= 20; break; case 'K': case 'k': mem <<= 10; /* fall through */ default: break; }
if (condition) do_this; do_something_everytime;
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
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.
void fun(int a, int b, int c) { if (condition) printk(KERN_WARNING "Warning this is a long printk with " "3 parameters a: %u b: %u " "c: %u \n", a, b, c); else next_statement; }
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:
if (x is true) { we do y }
switch (action) { case KOBJ_ADD: return "add"; case KOBJ_REMOVE: return "remove"; case KOBJ_CHANGE: return "change"; default: return NULL; }
int function(int x) { body of function }
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:
do { body of do-loop } while (condition);
if (x == y) { .. } else if (x > y) { ... } else { .... }
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.
if (condition) action();
Isso não se aplica se apenas um branch de um condicional é um comando único. Use chaves em ambos os ramos.
if (condition) { do_this(); do_that(); } else { otherwise(); }
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,
s = sizeof(struct file);
Não adicione espaços ao redor de expressões dentro de parênteses. Exemplo de como não fazer:
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:
char *linux_banner; unsigned long long memparse(char *ptr, char **retptr); char *match_strdup(substring_t *s);
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.
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)
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.
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:
int system_is_up(void) { return system_state == SYSTEM_RUNNING; } 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.
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
int fun(int a) { int result = 0; char *buffer = kmalloc(SIZE); if (buffer == NULL) return -ENOMEM; if (condition1) { while (loop1) { ... } result = 1; goto out; } ... out: kfree(buffer); return result; }
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:
(defun c-lineup-arglist-tabs-only (ignored) "Line up argument lists by tabs, not spaces" (let* ((anchor (c-langelem-pos c-syntactic-element)) (column (c-langelem-2nd-pos c-syntactic-element)) (offset (- (1+ column) anchor)) (steps (floor offset c-basic-offset))) (* (max steps 1) c-basic-offset))) (add-hook 'c-mode-hook (lambda () (let ((filename (buffer-file-name))) ;; Enable kernel mode for the appropriate files (when (and filename (string-match "~/src/linux-trees" filename)) (setq indent-tabs-mode t) (c-set-style "linux") (c-set-offset 'arglist-cont-nonempty '(c-lineup-gcc-asm-reg 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
" tab = 1 caracter \t mostrado como 8 espacos e não incluir espaços no final " set ts=8 set sw=8 noet " para forcar 80 colunas - recomendado mas a melhor regua é o olho " set textwidth=80 " para ver os tabs, espacos e trailing spaces (copiar e colar) " set list set lcs=tab:>·,extends:<,precedes:>,trail:· "para o VIM fazer identacao automatica " set smartindent " muda cor de fundo quando estiver chegando próximo da 80 coluna " au BufWinEnter * if &textwidth > 8 \ | endif " validação da função k[mzc]alloc e memset através de cor " " blog dica: kernelslacker.livejournal.com/129237.html?thread=628693 " highlight kmalloc ctermbg=red guibg=red match kmalloc /k[mzc]alloc(GFP_/ highlight memset ctermbg=red guibg=red 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
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:
config AUDIT bool "Auditing support" depends on NET help Enable auditing infrastructure that can be used with another kernel subsystem, such as SELinux (which requires this for logging of avc messages output). Does not do system-call auditing without CONFIG_AUDITSYSCALL.
config SLUB depends on EXPERIMENTAL && !ARCH_USES_SLAB_PAGE_STRUCT bool "SLUB (Unqueued Allocator)" ...
Enquanto features realmente perigosas (exemplo, suporte a escrita para certos filesystems) devem anunciar isto ostensivamente na sua string de prompt:
config ADFS_FS_RW bool "ADFS write support (DANGEROUS)" depends on ADFS_FS ...
Para a documentação completa dos arquivos de configuração ver o arquivo: Documentation/kbuild/kconfig-language.txt.
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.
Nomes de macros definindo constantes e labels em enums são escritos em maiúsculas:
#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:
#define macrofun(a, b, c) \ do { \ if (a == 5) \ do_this(b, c); \ } 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
#define FOO(x) \ do { \ if (blah(x) < 0) \ return -EBUGGERED; \ } 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
#define FOO(val) bar(index, val)
3) macros com argumentos usados como l-values (n.t. l-value é algo que fica do lado esquerdo de um '=')
FOO(x) = y;
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
#define CONSTANT 0x4000 #define CONSTEXP (CONSTANT | 3)
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.