Linux内核编码风格权威总结:从缩进到底层设计,让你的代码更“内核味”

电子说

1.4w人已加入

描述

 

 

作为全球最庞大的开源项目之一,Linux 内核的代码量早已突破千万行。要让来自世界各地的开发者高效协作,一套统一、严谨的编码风格必不可少 —— 这不仅是 代码颜值” 的要求,更是可读性、可维护性的核心保障。

 

 

今天,我们就基于 Linux 内核官方文档(https://www.kernel.org/doc/html/latest/process/coding-style.html),提炼一份「内核级」编码指南,无论你是刚接触内核开发的新手,还是想规范代码风格的老兵,都能从中找到实用参考。

内核

 

 

 

一、基础格式:缩进与换行,细节定成败

 

内核编码风格对基础格式” 的要求近乎 严苛,核心原则是 **“视觉清晰,无歧义”**

 

 

1. 缩进:个字符的 Tab,不是空格!

 

必须用Tab键缩进,且每个 Tab 对应个字符(拒绝 2/4 字符缩进,文档戏称 把 π 定义为 一样离谱)。

 

 

理由:深层嵌套时更容易区分代码块,同时警告你嵌套过深(超过 层就该重构了)。

 

 

例外:仅在注释、文档和 Kconfig 中可使用空格,其他场景绝对禁止用空格缩进。

 

 

2. switch-case 对齐:别搞 双重缩进

 

switch 语句的case标签需与switch对齐,而非缩进一层,避免代码过度右移:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
// 正确示例switch (suffix) {case 'G'case 'g':  // case与switch同列        mem <<= 30;  // 逻辑代码缩进8字符        break;case 'M'case 'm':        mem <<= 20;        break;default:        break;}

3. 行宽:80 列是 软上限

 

单行长建议不超过 80 列,超长时需拆分(如函数参数、长表达式),拆分后子句需 右移且短于父句

 

 

例外:用户可见字符串(如 printk 消息)不能拆分 —— 否则会破坏grep搜索功能。

 

 

二、大括号与空格:内核风格的标志性符号

 

大括号({})和空格的位置,是内核代码辨识度” 的关键,严格遵循K&R 风格Kernighan & Ritchie语言之父)。

 

 

1. 大括号位置:函数单独成行,其他 尾随行末

 

非函数块if/switch/for/while):左大括号在语句末尾,右大括号单独成行:

 

 

  •  
  •  
  •  
  •  
  •  
if (count > 0) {  // 左括号在末尾        do_something();else {          // else与右括号同列        do_nothing();}

函数定义:左大括号必须在新行开头,与函数名对齐:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
// 正确示例int count_active_users(void){  // 左括号单独成行        int count = 0;        // 业务逻辑        return count;}

2. 空格使用:关键字要空格,函数 运算符看场景

 

关键字后加空格if/switch/case/for/while 后必须加空格(如if (x),而非if(x))。

 

 

函数 / 运算符不加空格

 

 

sizeof/typeof/alignof 后不加空格(如sizeof(struct file),而非sizeof (struct file));

 

 

指针*贴近变量名(如char *buf,而非char* buf);

 

 

二元运算符(=/+/-/< 等)前后加空格,一元运算符(&/*/~ 等)后不加(如*ptr,而非* ptr)。

 

 

禁止尾随空格:行尾不能留空格(Git 会警告,编辑器可配置自动删除)。

 

 

三、命名规范:见名知意,拒绝花里胡哨

 

内核命名的核心是 **“简洁 区分作用域”**,杜绝冗余和歧义。

 

 

1. 全局变量 函数:长且 descriptive

 

全局符号(跨文件访问)必须见名知意,禁止缩写(如count_active_users()而非cntusr())。

 

 

示例:struct user *active_user_list(全局变量,明确表示活跃用户列表)。

 

 

2. 局部变量:短且简洁

 

局部变量(函数内使用)用短名即可,如循环计数器用i,临时变量用tmp,无需过度描述(如loop_counter反而冗余)。

 

 

3. 禁用 匈牙利命名

 

不要在变量名中加类型前缀(如iCountpBuf—— 编译器会检查类型,人类加前缀只会增加冗余,还可能因类型修改导致命名失效。

 

 

4. 术语替代:拒绝 “master/slave” 等争议词

 

替代方案:master→primary/mainslave→secondary/replicablacklist→denylistwhitelist→allowlist

 

 

四、函数设计:短、精、单职责,拒绝大而全

 

内核函数的设计哲学是 **“做一件事,且做好”**,具体要求如下:

 

 

1. 长度与复杂度:控制在 “2 屏内

 

函数长度建议不超过 2 个屏幕(约 40-50 行),复杂逻辑必须拆分成 helper 函数(用static inline优化性能)。

 

 

局部变量不超过 5-10 个:人类大脑难以同时处理超过 个变量,过多则需拆分函数。

 

 

2. 函数原型:参数必须带名

 

函数声明时,参数需写清名称(而非仅写类型),方便读者理解用途:

 

 

  •  
  •  
  •  
  •  
// 正确:带参数名void process_user(struct user *u, int action);// 错误:仅写类型void process_user(struct user *, int);

3. 统一退出:用 goto 做 清理操作

 

当函数有多个退出点(如分配内存后需释放),推荐用goto统一清理,避免重复代码:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
int init_buffer(void){        char *buf = kmalloc(SIZE, GFP_KERNEL);        if (!buf)                return -ENOMEM;  // 直接退出(无清理)        if (init_data(buf) != 0) {                goto out_free_buf;  // 跳转到清理逻辑        }        // 正常逻辑        return 0;out_free_buf:  // 清理标签:命名需明确(如“释放buf”)        kfree(buf);        return -EIO;}

注意:goto 标签需语义化(如out_free_buf),拒绝err1/err2这类无意义名称。

 

 

五、注释:说做什么,而非 怎么做

 

内核注释的核心是 **“辅助理解,不冗余”**,拒绝 解释烂代码

 

 

1. 注释原则:“What> How”

 

不要解释代码逻辑(如“i 自增 1”),而要说明 为什么这么做” 或 做什么(如 统计活跃用户数,排除僵尸进程)。

 

 

函数注释:放在函数前,说明功能、参数含义、返回值(推荐用内核 doc 格式,见Documentation/doc-guide/)。

 

 

2. 多行注释格式:左对齐星号

 

多行注释需用“/* ... */”,且每行开头加*,保持左对齐:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
/* * 统计系统中活跃的用户数量 * 排除条件:1. 僵尸进程对应的用户;2. 离线超过24小时的用户 * 返回值:活跃用户数(>=0) */int count_active_users(void){        // ...}

3. 数据注释:每行一个变量,加说明

 

声明变量时,每行只定义一个,并用注释说明用途:

 

 

  •  
  •  
  •  
  •  
  •  
// 正确:每行一个,带注释int user_count;    // 总用户数char *user_name;   // 当前用户名// 错误:多变量一行,无注释int user_count, *user_name;

六、实用避坑指南:这些禁忌” 别踩

 

1. typedef:非必要不使用

 

仅在以下场景可用 typedef,其他情况禁止(如struct不要 typedef):

 

 

完全 opaque 类型(如pte_t,只能通过接口访问,不暴露内部结构);

 

 

明确的整数类型(如u8/u16/u32,避免int/long混淆);

 

 

稀疏检查(sparse 工具用于类型校验)。

 

 

2. 内存分配:安全优先,避免错误

 

sizeof(*p)而非硬写类型:分配结构体指针时,用kmalloc(sizeof(*p), ...),避免类型修改导致的错误:

 

 

  •  
  •  
  •  
  •  
// 正确:无需关心p的类型struct user *p = kmalloc(sizeof(*p), GFP_KERNEL);// 错误:类型修改后需同步修改这里struct user *p = kmalloc(sizeof(struct user), GFP_KERNEL);

数组分配用kmalloc_array/kcalloc:自动检查n * sizeof(...)溢出,返回 NULL 避免越界。

 

 

3. 禁用 BUG (),用 WARN () 替代

 

不要用BUG()/BUG_ON()(会直接崩溃内核),优先用WARN_ON_ONCE()(打印警告但不崩溃,且只打印一次)。

 

 

仅在严重内存损坏,无法继续运行” 时用BUG(),且需附带充分理由。

 

 

七、工具推荐:自动对齐,告别手动调整

 

内核开发有成熟工具链,帮你自动符合风格:

 

 

1.indent 命令:内核推荐indent -kr -i8K&R 风格,字符缩进),或直接用内核脚本scripts/Lindent

 

 

2.clang-format:支持自动格式化代码,还能排序#include、对齐宏定义,配置见Documentation/dev-tools/clang-format.rst

 

 

3.编辑器配置

 

 

Emacs:将文档中的.emacs配置代码加入,自动适配内核风格;

 

 

Vim:添加set sw=8 ts=8 expandtab!Tab=8 字符,不自动转空格)。

 

 

总结:风格的本质是协作

 

Linux 内核编码风格看似 教条,实则是千万开发者协作的 共同语言”—— 它不追求 个性化,而是通过统一规则降低沟通成本,让代码 读起来像一个人写的

 

 

最后记住:风格不是束缚,而是 助力。初期可能需要刻意适应,但一旦形成习惯,你的代码会更易维护、更易被内核社区接纳。

 

 

如果在实践中遇到风格相关的坑,欢迎在评论区分享~

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分