第二百零六章:文档之道
飞升期涉及内核源码:
一
林小源离开试炼场,踏入一片寂静的书海。无数书卷悬浮在半空中,每一卷都散发着柔和的光芒。书卷的封面上标注着不同的路径——Documentation/process/、Documentation/driver-api/、Documentation/admin-guide/。
一位年轻的书童坐在书海中央的石凳上,手中捧着一卷泛黄的古籍。他的眼睛因为长年阅读而微微眯起,但目光中透着温和与耐心。
"你是来学写文档的?"书童放下古籍,微笑着看向林小源。
林小源点头。他环顾四周,书卷的数量多得令人眩晕:"这些文档……都是内核的?"
"每一卷都是,"书童站起身,从空中取下一卷书,"Documentation/ 是内核的文档主目录。开发流程、API 说明、用户指南、驱动文档——全部在这里。"
书童翻开手中的书卷,露出其中的 RST 格式。"内核文档用 RST 格式编写,用 Sphinx 生成 HTML 和 PDF。你写好 RST 文件,放进 Documentation/ 目录,更新索引,然后 make htmldocs 就能生成可阅读的文档。"
林小源接过书卷,感受到纸页上文字的重量。每一个字都是前人留下的路标。
/*
* 内核文档:
*
* 文档位置:
* Documentation/ — 主文档目录
* Documentation/process/ — 开发流程
* Documentation/dev-tools/ — 开发工具
* Documentation/driver-api/ — 驱动 API
*
* 文档格式:
* RST (reStructuredText) — 格式
* Sphinx — 文档生成工具
*
* 文档类型:
* 1. 开发流程文档
* 提交补丁、编码规范
*
* 2. API 文档
* 内核 API 说明
*
* 3. 用户文档
* 内核配置、启动参数
*
* 4. 驱动文档
* 设备驱动说明
*
* 编写文档:
* 内核注释
* Documentation/ 目录
* RST 格式
*
* 生成文档:
* make htmldocs
* make pdfdocs
*/
/* 模拟文档条目 */
struct doc_entry {
char path[128];
char description[256];
};
struct doc_entry docs[] = {
{"Documentation/process/submitting-patches.rst", "补丁提交流程"},
{"Documentation/process/coding-style.rst", "编码规范"},
{"Documentation/process/code-of-conduct.rst", "行为准则"},
{"Documentation/driver-api/driver-model.rst", "设备模型"},
{"Documentation/admin-guide/kernel-parameters.rst", "内核参数"},
};
printf("=== 文档之道 — 内核的说明书 ===\n\n");
printf("文档是内核的说明书:\n\n");
printf("--- 文档位置 ---\n");
int n = sizeof(docs) / sizeof(docs[0]);
for (int i = 0; i < n; i++) {
printf("%s\n", docs[i].path);
printf(" %s\n", docs[i].description);
}
printf("\n--- 文档格式 ---\n");
printf("RST (reStructuredText):\n");
printf(" 标记语言\n");
printf(" 类似 Markdown\n\n");
printf("Sphinx:\n");
printf(" 文档生成工具\n");
printf(" 生成 HTML、PDF\n\n");
printf("--- 文档类型 ---\n");
printf("1. 开发流程文档:\n");
printf(" 提交补丁、编码规范\n\n");
printf("2. API 文档:\n");
printf(" 内核 API 说明\n\n");
printf("3. 用户文档:\n");
printf(" 内核配置、启动参数\n\n");
printf("4. 驱动文档:\n");
printf(" 设备驱动说明\n\n");
printf("--- 编写文档 ---\n");
printf("内核注释:\n");
printf(" 在代码中添加注释\n");
printf(" 解释\"为什么\"\n\n");
printf("Documentation/ 目录:\n");
printf(" 添加 RST 文件\n");
printf(" 更新索引\n\n");
printf("--- 生成文档 ---\n");
printf("make htmldocs:\n");
printf(" 生成 HTML 文档\n\n");
printf("make pdfdocs:\n");
printf(" 生成 PDF 文档\n\n");
printf("--- 文档重要性 ---\n");
printf("1. 可理解性:\n");
printf(" 帮助新人理解代码\n\n");
printf("2. 可维护性:\n");
printf(" 记录设计决策\n\n");
printf("3. 可用性:\n");
printf(" 帮助用户使用\n\n");
printf("--- 文档原则 ---\n");
printf("1. 清晰:\n");
printf(" 语言简洁明了\n\n");
printf("2. 完整:\n");
printf(" 覆盖所有功能\n\n");
printf("3. 更新:\n");
printf(" 代码改了文档也要改\n");#include <stdio.h>
#include <string.h>
/*
* 内核文档:
*
* 文档位置:
* Documentation/ — 主文档目录
* Documentation/process/ — 开发流程
* Documentation/dev-tools/ — 开发工具
* Documentation/driver-api/ — 驱动 API
*
* 文档格式:
* RST (reStructuredText) — 格式
* Sphinx — 文档生成工具
*
* 文档类型:
* 1. 开发流程文档
* 提交补丁、编码规范
*
* 2. API 文档
* 内核 API 说明
*
* 3. 用户文档
* 内核配置、启动参数
*
* 4. 驱动文档
* 设备驱动说明
*
* 编写文档:
* 内核注释
* Documentation/ 目录
* RST 格式
*
* 生成文档:
* make htmldocs
* make pdfdocs
*/
/* 模拟文档条目 */
struct doc_entry {
char path[128];
char description[256];
};
struct doc_entry docs[] = {
{"Documentation/process/submitting-patches.rst", "补丁提交流程"},
{"Documentation/process/coding-style.rst", "编码规范"},
{"Documentation/process/code-of-conduct.rst", "行为准则"},
{"Documentation/driver-api/driver-model.rst", "设备模型"},
{"Documentation/admin-guide/kernel-parameters.rst", "内核参数"},
};
int main() {
printf("=== 文档之道 — 内核的说明书 ===\n\n");
printf("文档是内核的说明书:\n\n");
printf("--- 文档位置 ---\n");
int n = sizeof(docs) / sizeof(docs[0]);
for (int i = 0; i < n; i++) {
printf("%s\n", docs[i].path);
printf(" %s\n", docs[i].description);
}
printf("\n--- 文档格式 ---\n");
printf("RST (reStructuredText):\n");
printf(" 标记语言\n");
printf(" 类似 Markdown\n\n");
printf("Sphinx:\n");
printf(" 文档生成工具\n");
printf(" 生成 HTML、PDF\n\n");
printf("--- 文档类型 ---\n");
printf("1. 开发流程文档:\n");
printf(" 提交补丁、编码规范\n\n");
printf("2. API 文档:\n");
printf(" 内核 API 说明\n\n");
printf("3. 用户文档:\n");
printf(" 内核配置、启动参数\n\n");
printf("4. 驱动文档:\n");
printf(" 设备驱动说明\n\n");
printf("--- 编写文档 ---\n");
printf("内核注释:\n");
printf(" 在代码中添加注释\n");
printf(" 解释\"为什么\"\n\n");
printf("Documentation/ 目录:\n");
printf(" 添加 RST 文件\n");
printf(" 更新索引\n\n");
printf("--- 生成文档 ---\n");
printf("make htmldocs:\n");
printf(" 生成 HTML 文档\n\n");
printf("make pdfdocs:\n");
printf(" 生成 PDF 文档\n\n");
printf("--- 文档重要性 ---\n");
printf("1. 可理解性:\n");
printf(" 帮助新人理解代码\n\n");
printf("2. 可维护性:\n");
printf(" 记录设计决策\n\n");
printf("3. 可用性:\n");
printf(" 帮助用户使用\n\n");
printf("--- 文档原则 ---\n");
printf("1. 清晰:\n");
printf(" 语言简洁明了\n\n");
printf("2. 完整:\n");
printf(" 覆盖所有功能\n\n");
printf("3. 更新:\n");
printf(" 代码改了文档也要改\n");
return 0;
}二
书童从空中取下另一卷书,翻开递给林小源。页面上是两种格式的对比——左边是 Markdown,右边是 RST。
"你可能会问,为什么不用 Markdown?"书童似乎看穿了林小源的心思,"Markdown 简单,但能力有限。RST 支持交叉引用——你可以从一个文档链接到另一个文档的特定章节。RST 支持代码块的语法高亮、表格、数学公式。Sphinx 从 RST 可以生成 HTML、PDF、epub,甚至 man page。"
林小源仔细对比着两种格式。RST 确实更强大,但也更复杂。书童指着页面上的一个交叉引用标记:"你看,这里引用了 submitting-patches.rst 中的某个章节。点击链接,读者就能直接跳转过去。Markdown 做不到这一点。"
"内核有数万份文档,"书童合上书卷,"如果没有交叉引用,读者就会在文档的海洋中迷失。RST 和 Sphinx 是内核文档的基石。"
三
书童带林小源走到书海的边缘,那里有一位年轻的修士正在苦苦翻阅源代码,眉头紧锁。
"他是一位新人,"书童低声说,"他想理解内核的设备模型,但没有文档。他只能读源码。"
林小源看着那位新人,看到他在 drivers/ 目录下迷失方向,看到他在 include/linux/ 中找不到自己需要的头文件,看到他因为不理解某个函数的调用约定而写出了一段有 bug 的代码。
"这就是文档的价值,"书童的声音变得沉重,"没有文档的代码,新人需要花几周甚至几个月才能理解。有了文档,几天就够了。文档不是可有可无的装饰,它是代码和理解之间的桥梁。"
书童转身面对林小源,目光认真:"你学到了内核的很多知识,但如果你不把它们写下来,下一个林小源就要重新走一遍你走过的路。写文档,就是为后来者铺路。"
林小源望着那片浩瀚的书海,心中涌起一股使命感。代码会过时,但文档会传承。
道藏笔记
内核启示
文档是内核的说明书。
文档位置:
- Documentation/ — 主文档目录
- Documentation/process/ — 开发流程
- Documentation/dev-tools/ — 开发工具
文档格式:
- RST — 标记语言
- Sphinx — 文档生成工具
文档类型:
- 开发流程 — 提交补丁、编码规范
- API 文档 — 内核 API
- 用户文档 — 配置、参数
- 驱动文档 — 设备驱动
生成文档:
- make htmldocs — HTML
- make pdfdocs — PDF
文档是桥梁——连接代码和理解。
文档之试
文档之道中,内核文档主要存放在哪个顶层目录下?