第二百零三章:邮件列表
飞升期涉及内核源码:
一
林小源离开补丁殿,来到一片浩瀚的信息之海。海面上漂浮着无数发光的信笺,每一封都在缓缓飘向远方。海的中央竖着一块巨大的石碑,上书"LKML"三个大字。
一位穿着灰色长袍的信使从海面上踏浪而来,手中握着一沓邮件。他的脸上带着风尘仆仆的疲惫,但眼神依然锐利。
"你要往内核提交补丁,"信使开口,声音沙哑,"就必须学会用邮件列表。"
林小源环顾四周,看着满天飞舞的信笺:"这些邮件……都是补丁?"
"不只是补丁,"信使摇头,"邮件列表是内核开发的全部通信。补丁、讨论、审查、争论——一切都在邮件中进行。没有聊天室,没有即时消息,只有邮件。"
信使将手中的邮件递给林小源。第一封来自 列表,是一段网络驱动的修改;第二封来自 linux-fsdevel,是文件系统的讨论;第三封来自 linux-mm,是内存管理的审查意见。
"子系统有自己的邮件列表,"信使解释道,"补丁要发到正确的列表。发错了,维护者可能看不到。"
他又抖开一张羊皮纸,上面写着 。
"先找人,再发信。"信使说,"看 里的维护者、列表和树,再用 辅助确认。默认可以抄送 linux-kernel,但不要把不相关的人和列表拖进来。若改了用户空间接口,还要让 linux-api 和手册维护者知道;若是严重安全问题,不能先发公开列表,要走安全报告路径。"
林小源望着邮件之海,忽然意识到:发补丁不是把信扔进大海,而是把一封需要被正确的人读到的信,送到正确的渡口。
/*
* 邮件列表:
*
* LKML (Linux Kernel Mailing List):
* linux-kernel@vger.kernel.org
* 内核主要邮件列表
*
* 子系统邮件列表:
* netdev — 网络子系统
* linux-fsdevel — 文件系统
* linux-mm — 内存管理
*
* 邮件礼仪:
* 1. 纯文本邮件
* 2. 不要 HTML
* 3. 不要附件 (补丁用内联)
* 4. 引用适当
* 5. 不要顶部回复
*
* 补丁邮件:
* Subject: [PATCH 0/3] 子系统: 系列描述
* Subject: [PATCH 1/3] 子系统: 第一个补丁
* Subject: [PATCH 2/3] 子系统: 第二个补丁
* Subject: [PATCH 3/3] 子系统: 第三个补丁
*
* 工具:
* git send-email — 发送补丁
* mutt — 邮件客户端
* Thunderbird — 邮件客户端
*/
/* 模拟邮件 */
struct email {
char from[64];
char to[64];
char subject[128];
char body[512];
};
void print_email(struct email *e) {
printf("From: %s\n", e->from);
printf("To: %s\n", e->to);
printf("Subject: %s\n\n", e->subject);
printf("%s\n", e->body);
}
printf("=== 邮件列表 — 内核的通信 ===\n\n");
printf("内核开发通过邮件列表协作:\n\n");
/* 示例邮件 */
struct email patch_email = {
.from = "林小源 <linxiaoyuan@kernel.org>",
.to = "linux-kernel@vger.kernel.org",
.subject = "[PATCH] ext4: fix memory leak in ext4_fill_super",
.body = "Fix a memory leak in ext4_fill_super() where\n"
"the super block buffer head was not released\n"
"on error path.\n\n"
"Signed-off-by: 林小源 <linxiaoyuan@kernel.org>\n"
"---\n"
" fs/ext4/super.c | 5 +++--\n"
" 1 file changed, 3 insertions(+), 2 deletions(-)"
};
printf("--- 补丁邮件示例 ---\n");
print_email(&patch_email);
printf("\n--- 邮件列表 ---\n");
printf("LKML:\n");
printf(" linux-kernel@vger.kernel.org\n");
printf(" 内核主要邮件列表\n\n");
printf("子系统邮件列表:\n");
printf(" netdev — 网络子系统\n");
printf(" linux-fsdevel — 文件系统\n");
printf(" linux-mm — 内存管理\n\n");
printf("--- 邮件礼仪 ---\n");
printf("1. 纯文本邮件\n");
printf(" 不要 HTML\n\n");
printf("2. 不要附件\n");
printf(" 补丁用内联\n\n");
printf("3. 引用适当\n");
printf(" 只引用相关部分\n\n");
printf("4. 不要顶部回复\n");
printf(" 在引用下方回复\n\n");
printf("5. 找对收件人\n");
printf(" MAINTAINERS + scripts/get_maintainer.pl\n");
printf(" 抄送相关维护者和列表\n\n");
printf("--- 补丁系列 ---\n");
printf("[PATCH 0/3] 系列描述\n");
printf("[PATCH 1/3] 第一个补丁\n");
printf("[PATCH 2/3] 第二个补丁\n");
printf("[PATCH 3/3] 第三个补丁\n\n");
printf("--- 工具 ---\n");
printf("git send-email:\n");
printf(" 发送补丁\n");
printf(" 配置 SMTP\n\n");
printf("mutt:\n");
printf(" 终端邮件客户端\n\n");
printf("Thunderbird:\n");
printf(" 图形邮件客户端\n");
printf(" 需要配置纯文本\n");#include <stdio.h>
#include <string.h>
/*
* 邮件列表:
*
* LKML (Linux Kernel Mailing List):
* linux-kernel@vger.kernel.org
* 内核主要邮件列表
*
* 子系统邮件列表:
* netdev — 网络子系统
* linux-fsdevel — 文件系统
* linux-mm — 内存管理
*
* 邮件礼仪:
* 1. 纯文本邮件
* 2. 不要 HTML
* 3. 不要附件 (补丁用内联)
* 4. 引用适当
* 5. 不要顶部回复
*
* 补丁邮件:
* Subject: [PATCH 0/3] 子系统: 系列描述
* Subject: [PATCH 1/3] 子系统: 第一个补丁
* Subject: [PATCH 2/3] 子系统: 第二个补丁
* Subject: [PATCH 3/3] 子系统: 第三个补丁
*
* 工具:
* git send-email — 发送补丁
* mutt — 邮件客户端
* Thunderbird — 邮件客户端
*/
/* 模拟邮件 */
struct email {
char from[64];
char to[64];
char subject[128];
char body[512];
};
void print_email(struct email *e) {
printf("From: %s\n", e->from);
printf("To: %s\n", e->to);
printf("Subject: %s\n\n", e->subject);
printf("%s\n", e->body);
}
int main() {
printf("=== 邮件列表 — 内核的通信 ===\n\n");
printf("内核开发通过邮件列表协作:\n\n");
/* 示例邮件 */
struct email patch_email = {
.from = "林小源 <linxiaoyuan@kernel.org>",
.to = "linux-kernel@vger.kernel.org",
.subject = "[PATCH] ext4: fix memory leak in ext4_fill_super",
.body = "Fix a memory leak in ext4_fill_super() where\n"
"the super block buffer head was not released\n"
"on error path.\n\n"
"Signed-off-by: 林小源 <linxiaoyuan@kernel.org>\n"
"---\n"
" fs/ext4/super.c | 5 +++--\n"
" 1 file changed, 3 insertions(+), 2 deletions(-)"
};
printf("--- 补丁邮件示例 ---\n");
print_email(&patch_email);
printf("\n--- 邮件列表 ---\n");
printf("LKML:\n");
printf(" linux-kernel@vger.kernel.org\n");
printf(" 内核主要邮件列表\n\n");
printf("子系统邮件列表:\n");
printf(" netdev — 网络子系统\n");
printf(" linux-fsdevel — 文件系统\n");
printf(" linux-mm — 内存管理\n\n");
printf("--- 邮件礼仪 ---\n");
printf("1. 纯文本邮件\n");
printf(" 不要 HTML\n\n");
printf("2. 不要附件\n");
printf(" 补丁用内联\n\n");
printf("3. 引用适当\n");
printf(" 只引用相关部分\n\n");
printf("4. 不要顶部回复\n");
printf(" 在引用下方回复\n\n");
printf("5. 找对收件人\n");
printf(" MAINTAINERS + scripts/get_maintainer.pl\n");
printf(" 抄送相关维护者和列表\n\n");
printf("--- 补丁系列 ---\n");
printf("[PATCH 0/3] 系列描述\n");
printf("[PATCH 1/3] 第一个补丁\n");
printf("[PATCH 2/3] 第二个补丁\n");
printf("[PATCH 3/3] 第三个补丁\n\n");
printf("--- 工具 ---\n");
printf("git send-email:\n");
printf(" 发送补丁\n");
printf(" 配置 SMTP\n\n");
printf("mutt:\n");
printf(" 终端邮件客户端\n\n");
printf("Thunderbird:\n");
printf(" 图形邮件客户端\n");
printf(" 需要配置纯文本\n");
return 0;
}收件之试
发送补丁前,查找相关维护者和邮件列表的核心文件名是什么?
二
信使从海面上捞起一封邮件,邮件的正文被各种花哨的格式包围——彩色字体、嵌入图片、HTML 标签。
"你看这封邮件,"信使皱着眉头,"用 Thunderbird 发的 HTML 邮件。在我的终端里,全是乱码。在 Greg 的终端里,也全是乱码。在 Linus 的终端里——"他顿了顿,"Linus 根本不会打开这种邮件。"
林小源接过那封邮件,果然看到一堆毫无意义的标签和转义字符。
"内核开发者用终端读邮件,"信使将那封邮件扔回海里,"纯文本,没有例外。你的邮件在所有客户端都必须能正确显示,这就是纯文本的意义——兼容。"
信使又从怀中取出一封简洁的纯文本邮件,递给林小源。这封邮件只有干净的文字,没有多余的格式,每一行都清晰可读。
"这才是内核社区的邮件该有的样子。"
信使又伸手一抓,抓出一封被折断的补丁。它原本是纯文本,却被邮件客户端自动换行,diff 里的空格被吞掉,行首的制表符变成了空格。
"这封更危险,"信使说,"它看起来像补丁,却无法应用。审查者不能在破碎的 diff 上逐行评论,维护者也不会替你修邮件客户端。发给别人之前,先发给自己,确认 git am 能吃下去。最稳妥的办法,是用 git send-email。"
纯文之试
为了避免补丁被 HTML、附件或自动换行破坏,内核社区强烈推荐用哪个 git 工具发送补丁?
三
林小源注意到海面上有一组特殊的信笺,它们被一根金色的丝线串联在一起,依次排列。
"补丁系列,"信使指着那组信笺,"当你的修改涉及多个补丁时,就要用补丁系列来组织。"
林小源数了数,一共四封邮件。第一封标着 [PATCH 0/3],后面三封分别标着 [PATCH 1/3]、[PATCH 2/3]、[PATCH 3/3]。
"第零封是封面信,"信使解释道,"它描述整个系列的目的、背景、测试结果。它不包含代码修改,只包含说明。后面三封才是真正的补丁。"
信使轻轻一拉金色丝线,四封邮件同时展开,形成了一个完整的画面。"编号让审查者知道总共有几个补丁,每个补丁在系列中的位置。如果审查者说'第二个补丁有问题',你就知道是 [PATCH 2/3]。"
林小源默默记下:补丁系列是组织复杂修改的方式,编号是沟通的语言。
"版本也是语言。"信使继续说。他把那组信笺翻到背面,那里写着 v1 -> v2、v2 -> v3。
"当你根据审查意见重发,不要让审查者去旧邮件里翻记忆。封面信或补丁分隔线 --- 之后,要写清楚这一版改了什么:删了哪个 helper,补了哪项测试,为什么保留某个选择。版本说明不会进入永久 changelog,却能让审查者快速重新进入上下文。"
林小源看着那些版本记录,心里忽然一松。被打回不等于失败。每一个 v2、v3,都是补丁在审查中被锻打后的新形态。
版本之试
补丁重发时,用来说明 v1 到 v2、v2 到 v3 改动的说明通常放在分隔线之后;这条分隔线由哪三个字符组成?
道藏笔记
内核启示
内核开发通过邮件列表协作。
邮件列表:
- LKML — 内核主要邮件列表
- netdev — 网络子系统
- linux-fsdevel — 文件系统
- linux-mm — 内存管理
邮件礼仪:
- 纯文本邮件
- 不要 HTML
- 不要附件
- 引用适当
- 不要顶部回复
- 保留相关 Cc
- 先确认邮件客户端不会破坏补丁
补丁系列:
- [PATCH 0/n] 封面信
- [PATCH 1/n] 补丁 1
- [PATCH 2/n] 补丁 2
- v2/v3 重发时说明版本差异
---之后的版本记录不进入永久 changelog
邮件列表是桥梁——连接全球的开发者。
道藏复核
补丁系列中用于描述整个系列背景、测试结果和版本变化的第零封邮件通常标成什么编号?