当前位置: 首页 > news >正文

Linux 内核编码规范(Kernel Coding Style)完整版详解

简介

本文基于 Linux 官方Documentation/process/coding-style.rst整理,完整梳理 Linux 内核强制遵循的 C 语言编码风格、缩进、括号、命名、函数、宏定义、内存分配、注释等全套规范,适合嵌入式 Linux 驱动、内核开发、内核源码阅读、企业 Linux 项目编码规范参考,可直接作为团队编码规约使用。

Linux 内核编码风格极具个人色彩但已成行业标准,不强制个人审美,但所有内核提交、维护代码必须严格遵守;同时官方建议直接忽略 GNU 编码标准,以 K&R 风格 + Linux 定制规则为准。

一、缩进规范(Indentation)

  1. Tab 固定 8 个字符,缩进层级同样为 8 字符;禁止使用 4/2 字符缩进,类比「把 π 定义为 3」一样不专业。
  2. 设计初衷:长时间调试时,大缩进能清晰区分代码块起止;若缩进层级超过 3 层,说明代码逻辑臃肿,需要重构。
  3. switch-case 对齐规则switchcase同列对齐,不二次缩进 case。
switch (suffix) { case 'G': case 'g': mem <<= 30; break; case 'M': case 'm': mem <<= 20; break; case 'K': case 'k': mem <<= 10; fallthrough; default: break; }
  1. 禁止单行多条语句、禁止单行多赋值,内核风格拒绝晦涩技巧表达式。
  2. 除注释、文档、Kconfig 外,缩进只允许用 Tab,禁止空格;代码行末尾严禁保留空白字符。

二、长行与字符串换行(Breaking long lines and strings)

  1. 单行代码建议严格 80 列宽度限制
  2. 超过 80 列需合理拆分,仅当超宽能显著提升可读性时例外。
  3. 换行后缩进层级明显靠右,常用对齐到函数左括号的风格;函数长参数列表遵循同样规则。
  4. 用户态可见字符串(printk 日志等)禁止拆分换行,会导致 grep 检索失效。

三、括号与空格规范(Braces and Spaces)

3.1 括号位置(K&R 风格)

  1. if/switch/for/while/do等语句:左大括号放在行尾,右大括号独占一行。
if (x is true) { we do y }
  1. 函数特殊规则:函数左大括号另起一行顶格
int function(int x) { body of function }
  1. 特殊接续场景:do-whileif-else if-else右大括号不独占空行,紧跟后续关键字。
do { body of do-loop } while (condition); if (x == y) { .. } else if (x > y) { ... } else { .... }
  1. 单语句可省略大括号;但条件分支只要有一个分支是多语句,所有分支必须加括号;循环体内即使简单嵌套也建议加括号。

3.2 空格使用规则

  1. 关键字后加空格:if、switch、case、for、do、while
  2. 关键字特例不加空格sizeof、typeof、alignof、__attribute__
// 正确 s = sizeof(struct file); // 错误:括号内侧加空格 s = sizeof( struct file );
  1. 指针*紧贴变量 / 函数名,不紧贴类型:
char *linux_banner; unsigned long long memparse(char *ptr, char **retptr);
  1. 二元 / 三元运算符两侧加单个空格= + - < > * / % | & ^ <= >= == != ? :
  2. 一元运算符不加空格& * + - ~ ! sizeof typeof等;自增自减++ --前后无多余空格。
  3. 结构体成员符.->前后不加空格
  4. 代码行末尾严禁尾随空格。

四、命名规范(Naming)

  1. C 语言崇尚简洁,拒绝冗长驼峰命名,临时变量用tmp、i、j等简短表意名即可。
  2. 全局变量 / 全局函数必须语义完整,禁止foo、cntusr这类模糊命名,如统计在线用户应命名count_active_users()
  3. 严禁匈牙利命名法:编译器可做类型检查,无需在名字中编码类型。
  4. 局部变量简短精准,循环计数器直接用i即可;函数臃肿才需要刻意拉长局部变量名。
  5. 术语替换规范:
    • master/slave替换:primary/secondary、leader/follower、controller/device
    • blacklist/whitelist替换:denylist/allowlist、blocklist/passlist
    • 仅兼容旧 ABI / 硬件协议时可保留旧术语,新代码强制使用替代词。

五、Typedef 使用规范

原则:能不用 typedef 就坚决不用,仅允许以下 5 种场景:

  1. 完全透明隐藏的对象:如pte_t,只能通过官方接口访问内部成员。
  2. 明确整型类型:规避int/long平台差异,如u8/u16/u32/u64
  3. 配合 sparse 做静态类型检查,创建独立类型。
  4. 兼容 C99 标准类型的内核自定义别名。
  5. 用户态内核交互结构体:使用__u32等兼容类型。

禁止场景:结构体、普通指针不要 typedef,会降低代码可读性、隐藏真实类型。

六、函数设计规范(Functions)

  1. 函数短小精悍、单一职责,尽量控制在 1~2 个 80x24 屏幕内。
  2. 函数复杂度越高、缩进越深,长度限制越严格;复杂逻辑拆分为辅助小函数,可由编译器自动内联。
  3. 局部变量数量建议不超过 5~10 个,过多说明函数逻辑需要拆分。
  4. 源文件中函数之间用一个空行分隔;导出函数EXPORT_SYMBOL紧跟函数右大括号。
  5. 函数原型必须携带参数名,不使用多余extern关键字。

七、函数集中退出与 GOTO 规范

  1. 内核不排斥 goto,多出口且需要统一资源清理时优先用 goto
  2. 标签命名语义化:如out_free_buffer,禁止err1、err2无意义编号标签。
  3. 优势:减少嵌套层级、统一清理逻辑、避免修改时漏改分支、简化编译器优化。
  4. 资源释放要分层标签,避免空指针释放 bug,尽量覆盖所有异常退出路径。
int fun(int a) { int result = 0; char *buffer; buffer = kmalloc(SIZE, GFP_KERNEL); if (!buffer) return -ENOMEM; if (condition1) { result = 1; goto out_free_buffer; } out_free_buffer: kfree(buffer); return result; }

八、注释规范(Commenting)

  1. 注释只说明做什么、为什么绝不解释代码怎么实现;烂代码不要靠注释补救,直接重构。
  2. 函数内部尽量少加注释,复杂逻辑拆函数,注释写在函数头部。
  3. 内核 API 函数必须使用kernel-doc规范注释。
  4. 多行注释标准格式:
/* * This is the preferred style for multi-line * comments in the Linux kernel source code. * Please use it consistently. */
  1. 网络驱动目录net/drivers/net/多行注释省略首行空行。
  2. 变量单行定义、单行注释,不要逗号连写多个变量,预留注释空间。

九、编辑器配置与代码格式化

  1. Emacs 默认格式不符合内核规范,需自定义.emacs配置 8 字符缩进、仅 Tab 缩进、显示尾随空格。
  2. 可使用indent -kr -i8或 内核脚本scripts/Lindent一键格式化。
  3. 推荐clang-format工具:自动格式化、排序头文件、对齐变量、检测风格错误。

十、Kconfig 配置文件规范

  1. config下内容缩进 1 个 Tab,help 文本额外缩进 2 个空格。
  2. 高危功能必须在提示中标注(DANGEROUS)
  3. 详细语法参考Documentation/kbuild/kconfig-language.rst

十一、数据结构规范

  1. 跨线程访问的数据结构必须做引用计数,内核无 GC 全靠手动管理生命周期。
  2. 引用计数 ≠ 锁:锁保证数据一致性,引用计数防止结构体被提前释放,通常二者搭配使用。
  3. 支持多级引用计数,如mm_structmm_users / mm_count
  4. 只要其他线程能获取到该结构体指针,不加引用计数基本必有内存野指针 bug。

十二、宏、枚举与 RTL 规范

  1. 常量宏、枚举标签全大写;功能类宏可小写,优先用 inline 函数替代函数式宏。
  2. 多语句宏必须包裹do-while(0)
  3. 宏四大禁忌:
    • 影响函数控制流(随意 return)
    • 依赖局部魔法变量名
    • 宏参数作为左值赋值
    • 忽略运算符优先级,表达式不套括号
  4. 宏内部局部变量加前缀,避免命名冲突。

十三、内核打印信息规范

  1. 日志拼写严谨、语句简洁无歧义,句尾不加点号。
  2. 优先使用内核标准打印接口:
    • 设备相关:dev_err()、dev_warn()、dev_info()
    • 通用日志:pr_err()、pr_warn()、pr_info()
  3. pr_debug()、dev_dbg()默认不编译,需定义DEBUG或开启动态调试;调试日志和普通日志分开处理。

十四、内存分配规范

  1. 优先使用内核标准分配器:kmalloc、kzalloc、kmalloc_array、kcalloc、vmalloc、vzalloc
  2. 结构体分配标准写法(避免类型修改漏改 sizeof):
p = kmalloc(sizeof(*p), GFP_KERNEL);
  1. 数组分配用kmalloc_array,清零数组用kcalloc,自动检测溢出。
  2. 无需强制转换void*返回值,C 语言自动隐式转换。
  3. 分配失败默认栈打印日志,无需额外手动打印失败信息。

十五、Inline 内联函数禁忌

  1. 不要滥用 inline,过度内联会增大内核镜像、降低 CPU 缓存命中率,整体系统变慢。
  2. 常规规则:超过 3 行代码不建议加 inline
  3. 仅编译期常量参数、替代宏的场景适合用 inline;静态单例函数 GCC 会自动内联,无需手动加关键字。

十六、函数返回值与命名约定

  1. 动作命令式函数:返回错误码(0 成功、负数失败),如add_work()
  2. 谓词判断式函数:返回布尔(0 失败、非 0 成功),如pci_dev_present()
  3. 指针类函数:用NULLERR_PTR标识失败,返回计算结果本身。
  4. 导出函数严格遵守该约定,私有静态函数建议遵循。

十七、Bool 类型使用规范

  1. 内核bool基于 C99_Bool,仅存 0/1,推荐用true/false替代 1/0。
  2. 适合存储布尔状态、提升可读性;缓存行对齐、结构体大小敏感场景禁用 bool
  3. 多布尔位域优先用位域或u8统一存储;大量布尔参数合并为 flags 位传参。

十八、禁止重复造内核宏

复用include/linux/kernel.h现有宏,不要自己重写:

  • 数组长度:ARRAY_SIZE(x)
  • 结构体成员大小:sizeof_field(t, f)
  • 类型安全min/max宏等

十九、禁止编辑器魔性配置

源码中禁止写入 Emacs/Vim 编辑器行配置、模式标记,每个人本地编辑器配置独立,不要污染工程代码。

二十、内联汇编规范

  1. 仅架构相关底层代码使用,能用 C 实现绝不随便嵌汇编。
  2. 通用汇编逻辑封装为辅助函数,复杂汇编单独写.S文件,C 头文件用asmlinkage声明原型。
  3. 多指令内联汇编每行单独字符串,加\n\t格式化汇编输出;合理使用volatile防止编译器优化删除。

二十一、条件编译规范

  1. .c文件尽量少用#ifdef,改为头文件定义空桩函数,C 文件无条件调用,编译器自动优化。
  2. 优先整函数条件编译,不要在表达式中间加预编译判断。
  3. 未使用变量 / 函数用__maybe_unused修饰,而非嵌套#ifdef
  4. 推荐用IS_ENABLED(CONFIG_XXX)替代#ifdef,编译器常量折叠,同时做语法检查。
  5. #endif后注释对应宏名,提升可读性。
http://www.jsqmd.com/news/818049/

相关文章:

  • 当大模型不再吐 Markdown:从 Claude 团队的 HTML 实践看 AI 输出范式转变
  • 神经形态计算与脉冲神经网络硬件实现解析
  • Perplexity API文档搜索失效了?不是Bug,是这6个语义解析盲区在作祟(附可复用的调试Checklist)
  • Auto_Simulated_Universe:崩坏星穹铁道模拟宇宙自动化工具完全指南
  • 【电动车】基于matlab粒子群算法模拟光伏的电动车充电站(电池健康状况通过CRF、ECL和SoH来量化)【含Matlab源码 15440期】
  • MAC系统安装SVN教程
  • Unity 游戏与 AR 项目开发实践分享
  • 利用Taotoken多模型聚合能力构建高容错的AI应用架构
  • ROFL-Player:英雄联盟回放文件解析与多版本客户端管理的技术架构深度解析
  • 企业还在用if-else做自动化?这3类业务场景已全面被AI Agent接管,延迟部署将丧失决策先机
  • 亚远景热烈祝贺凌骁能源通过ASPICE CL2评估
  • 亚马逊毛绒玩具TIC审核
  • IP数据库下载完全指南:免费与商业IP定位库对比
  • YOLO11涨点优化:数据增强 | 引入Copy-Paste实例叠加增强,暴力扩充小目标样本,专治长尾分布
  • 2026巴中市通江县黄金回收白银回收铂金回收店铺实力排行榜TOP5; K金+金条+银条+首饰回收靠谱门店及联系方式推荐_转自TXT - 盛世金银回收
  • PAM8302 D类音频放大器:高效低功耗设计、BTL输出与实战应用指南
  • TikTok 短视频生成工具哪家好?2026 深度评测:专业运营到个人创作
  • 利用taotoken模型广场为智能客服场景选择合适的大模型
  • 5个简单步骤掌握AI换脸技术:roop-unleashed深度合成完全指南
  • 大模型推理芯片性能分析与设计
  • 基于LEAP模型在能源环境发展、碳排放建模预测及分析中实践应用
  • 别再手动写review comment了!用Claude+GitHub Actions实现Python PR自动审查闭环(含CI/CD集成模板下载)
  • 2026白城市黄金回收白银回收铂金回收店铺实力排行榜TOP5; K金+金条+银条+首饰回收靠谱门店及联系方式推荐_转自TXT - 盛世金银回收
  • ComfyUI 整合包 V8 中文版|2026 最新版 开箱即用|零门槛 AI 绘画 / AI 视频|新手到进阶全流程教程
  • 别再乱插了!Type-C充电头能插进Type-F插座吗?聊聊各国插头背后的安全设计与兼容性“潜规则”
  • 【无人船】基于matlab A星算法融合DWA限制内陆水域无人水型导航路径规划【含Matlab源码 15445期】
  • 专业维修的仪陇县新政镇汽车维修
  • 别再折腾驱动了!一招搞定Linux下Intel AX210网卡的固件版本冲突问题
  • 软件测试的“全栈化”趋势:只会一种测试类型正在成为短板
  • 利用Taotoken用量看板精细化管理团队AI调用成本