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 章)
- 缩进:Tab = 8 字符,缩进即 8 字符。理由:缩进的本质是清晰界定控制块起止;超过 3 层嵌套说明程序本身该重构了——大缩进顺带充当”嵌套过深”的报警器。
- 大括号:控制语句开括号行尾、闭括号独行(K&R 式);函数例外——开括号独占下一行开头。
do...while的while和if...else的else接在闭括号后,不换行。附带好处:省空行(换行是 25 行终端上不可再生资源)。 - 命名:反对大小写混排的”可爱名”(thisVariableIsATemporaryCounter → tmp);但全局变量/函数必须有描述性名字(count_active_users 而非 cntusr);痛斥匈牙利命名法为”脑损伤”。局部变量名要短。
- 函数:短小、只做一件事,1-2 屏(80x24)以内;函数长度与复杂度成反比;局部变量不超过 5-10 个(人脑跟踪上限约 7 个)。
- 注释:注释说 WHAT 不说 HOW;永远不要用注释解释烂代码怎么工作;尽量避免函数体内注释,注释放函数头(做什么、为什么)。
- 工具:emacs 加
linux-c-mode(K&R + c-basic-offset 8),源码首两行加-*- linux-c -*-自动触发;或indent -kr -i8。“indent 治不了烂编程”。 - 配置脚本:Config.in 代码缩进 3、选项依赖文本缩进 2;不稳定选项包 CONFIG_EXPERIMENTAL,会毁数据的标注 (DANGEROUS)。
- 数据结构:跨线程可见的数据结构必须引用计数。内核没有垃圾回收;锁保证一致性、引用计数管生命周期,二者不可互替。多级引用计数例子:
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 驱动、数据采集卡项目笔记可直接套用。
关联
- Cypress-CyUSB3驱动编程参考手册(多线程 IOCTL 数据处理场景)
- 从此乱码是路人-Qt字符编码笔记(同为 C/C++ 工程规范类沉淀)
- 待建:《CSC-STD-003-85 可信数据库系统解释》同课程 standards 目录文献,见 0C103AdvancedInternetProgramming 课程资料索引