Linux 内核编码风格 + GNU 编码标准(两份历史文献对照)

来源:/田浩然上传的资料/0C103AdvancedInternetProgramming/lectures/lecture3/LinuxKernelCodingStyle.txt(Linus Torvalds 执笔的早期内核 Documentation/CodingStyle,约 11KB)与同目录 gnu-coding-std.txt(GNU coding standards,1989-02-10 版,约 11.7KB)。均为 Advanced Internet Programming 课程第 3 讲参考文献。

这两份文档是 C 语言工程文化史上立场截然相反的两大流派原始文献,放在一起读极有味道:GNU 标准(Stallman 执笔)系统化、讲工具链兼容性;Linux 风格(Linus 执笔)短小、充满个人战斗宣言,开篇第一句就是”建议打印一份 GNU 编码标准,然后不要读它,烧掉,是个很有象征意义的姿态”。

一、Linux 内核编码风格要点(8 章)

  1. 缩进:Tab = 8 字符,缩进即 8 字符。理由:缩进的本质是清晰界定控制块起止;超过 3 层嵌套说明程序本身该重构了——大缩进顺带充当”嵌套过深”的报警器。
  2. 大括号:控制语句开括号行尾、闭括号独行(K&R 式);函数例外——开括号独占下一行开头。do...while 的 while 和 if...else 的 else 接在闭括号后,不换行。附带好处:省空行(换行是 25 行终端上不可再生资源)。
  3. 命名:反对大小写混排的”可爱名”(thisVariableIsATemporaryCounter → tmp);但全局变量/函数必须有描述性名字(count_active_users 而非 cntusr);痛斥匈牙利命名法为”脑损伤”。局部变量名要短。
  4. 函数:短小、只做一件事,1-2 屏(80x24)以内;函数长度与复杂度成反比;局部变量不超过 5-10 个(人脑跟踪上限约 7 个)。
  5. 注释:注释说 WHAT 不说 HOW;永远不要用注释解释烂代码怎么工作;尽量避免函数体内注释,注释放函数头(做什么、为什么)。
  6. 工具:emacs 加 linux-c-mode(K&R + c-basic-offset 8),源码首两行加 -*- linux-c -*- 自动触发;或 indent -kr -i8。“indent 治不了烂编程”。
  7. 配置脚本:Config.in 代码缩进 3、选项依赖文本缩进 2;不稳定选项包 CONFIG_EXPERIMENTAL,会毁数据的标注 (DANGEROUS)。
  8. 数据结构:跨线程可见的数据结构必须引用计数。内核没有垃圾回收;锁保证一致性、引用计数管生命周期,二者不可互替。多级引用计数例子:mm_struct 的 mm_users/mm_count、super_block 的 s_count/s_active。金句:“如果别的线程能找到你的数据结构而你没引用计数,你几乎肯定有 bug。“

二、GNU 编码标准要点(1989)

  • 参考标准:绝不允许在 GNU 工作期间参考 Unix 源码;鼓励反其道设计(求快/求简/求通用/动态分配替代固定表)。
  • 兼容性:对 Berkeley Unix 向上兼容;纯用户特性若 BSD 做得差可整个替换(vi→Emacs,但也提供 vi 克隆)。
  • 排版:函数体开括号必须 0 列、函数名必须 0 列起(ctags/etags 等工具依赖);if-else 采用 Allman 倾向的独立行括号——与 K&R/Linux 正好相反;断行在操作符前;用 formfeed 分页。
  • 注释:程序开头一句说明用途;每个函数注释说明功能、参数含义与合法取值;static 变量必须有注释;句子结尾两个空格(配合 Emacs 句子命令);参数值用全大写参数名指代。
  • 语法:显式声明所有函数参数;extern 声明集中放文件头或头文件;禁止在 if 条件里做赋值。
  • 命名:下划线分词、全小写(宏与枚举常量用大写),如 ignore_space_change_flag;命令行选项开关变量按选项含义命名并在注释里带上选项字母。
  • 语义(防御式编程鼻祖条款):禁止任意长度限制(动态分配);不丢弃输入中的 null 字符;每个系统调用查错误返回且错误信息带 perror 文本+文件名+程序名;每次 malloc/realloc 检查返回 NULL(realloc 缩小也可能失败);不依赖 free 后内存内容。
  • 可移植性:GNU 只需跑在 GNU 内核+GCC 上,不必为其他 Unix 分心;需处理字节序与对齐;可假设 1MB 内存合理。

三、两派冲突对照表

议题GNU (1989)Linux (Linus)
函数开括号0 列独占行同为 0 列独占行(少数一致点)
控制块括号独立行(Allman 式)行尾(K&R 式)
命名下划线全小写长名局部短名、全局描述名,反对大小写混排
缩进未强制 8(GNU 惯例 2)铁令 8空格
if 内赋值明令禁止未提及(内核实践中常见)
终极仲裁Emacs 工具链K&R 与终端屏幕

共同底色:函数短小、注释说 WHAT、拒绝任意限制、把代码风格与工具链(ctags/indent/Emacs)绑定看待。这些条款在 30 多年后的今天仍是 MISRA/内核代码审查条文的祖先。

可行动点

  • 嵌入式 Qt/内核模块混合团队可采折中:内核侧 indent -kr -i8,应用侧统一 clang-format;关键是 CI 中强制。
  • 引用计数 vs 锁的心智模型(第 8 章)适用于任何多线程设备驱动/采集软件——本 vault 中 CYUSB3 驱动、数据采集卡项目笔记可直接套用。

关联