1 .. include:: ../disclaimer-zh_CN.rst 1 .. include:: ../disclaimer-zh_CN.rst 2 2 3 :Original: :ref:`Documentation/process/4.Codin 3 :Original: :ref:`Documentation/process/4.Coding.rst <development_coding>` 4 !! 4 :Translator: Alex Shi <alex.shi@linux.alibaba.com> 5 :Translator: << 6 << 7 时奎亮 Alex Shi <alex.shi@linux.alibaba.com << 8 << 9 :校译: << 10 << 11 吴想成 Wu XiangCheng <bobwxc@email.cn> << 12 5 13 .. _cn_development_coding: 6 .. _cn_development_coding: 14 7 15 使代码正确 8 使代码正确 16 ====================== 9 ====================== 17 10 18 虽然一个坚实的、面向社区的设计 !! 11 虽然对于一个坚实的、面向社区的设计过程有很多话要说,但是任何内核开发项目的 19 的证明都反映在代码中。它是将由 !! 12 证明都在生成的代码中。它是将由其他开发人员检查并合并(或不合并)到主线树中 20 的代码。所以这段代码的质量决定 13 的代码。所以这段代码的质量决定了项目的最终成功。 21 14 22 本节将检查编码过程。我们将从内 !! 15 本节将检查编码过程。我们将从内核开发人员出错的几种方式开始。然后重点将转移 23 到正确的做法和相关有用的工具上 !! 16 到正确的事情和可以帮助这个任务的工具上。 24 17 25 陷阱 18 陷阱 26 ---- 19 ---- 27 20 28 代码风格 !! 21 编码风格 29 ******** 22 ******** 30 23 31 内核长期以来都有其标准的代码风 !! 24 内核长期以来都有一种标准的编码风格,如 32 :ref:`Documentation/translations/zh_CN/process 25 :ref:`Documentation/translations/zh_CN/process/coding-style.rst <cn_codingstyle>` 33 中所述。在多数时候,该文档中描 !! 26 中所述。在大部分时间里,该文件中描述的政策被认为至多是建议性的。因此,内核 34 大量不符合代码风格准则的代码。 !! 27 中存在大量不符合编码风格准则的代码。代码的存在会给内核开发人员带来两个独立 35 !! 28 的危害。 36 首先,相信内核代码标准并不重要 !! 29 37 编写代码,那么新代码将很难加入 !! 30 首先,要相信内核编码标准并不重要,也不强制执行。事实上,如果没有按照标准对代 38 对代码进行重新格式化。一个像内 !! 31 码进行编码,那么向内核添加新代码是非常困难的;许多开发人员甚至会在审查代码之 39 开发人员能够快速理解其中的任何 !! 32 前要求对代码进行重新格式化。一个与内核一样大的代码库需要一些统一的代码,以使 40 !! 33 开发人员能够快速理解其中的任何部分。所以已经没有空间来存放奇怪的格式化代码了。 41 内核的代码风格偶尔会与雇主的强 !! 34 42 之前遵从内核代码风格。将代码放 !! 35 偶尔,内核的编码风格会与雇主的强制风格发生冲突。在这种情况下,内核的风格必须 43 包括控制代码样式。 !! 36 在代码合并之前获胜。将代码放入内核意味着以多种方式放弃一定程度的控制权——包括 44 !! 37 控制代码的格式化方式。 45 另一个危害是认为已经在内核中的 !! 38 46 重新格式化补丁,作为熟悉开发过 !! 39 另一个陷阱是假定已经在内核中的代码迫切需要编码样式的修复。开发人员可能会开始 47 的一种方式,或者两者兼而有之。 !! 40 生成重新格式化补丁,作为熟悉过程的一种方式,或者作为将其名称写入内核变更日志 48 往受到冷遇。因此,最好避免编写 !! 41 的一种方式,或者两者兼而有之。但是纯编码风格的修复被开发社区视为噪音;它们往 49 同时顺带修复其样式是很自然的, !! 42 往受到冷遇。因此,最好避免使用这种类型的补丁。由于其他原因,在处理一段代码的 50 !! 43 同时修复它的样式是很自然的,但是编码样式的更改不应该仅为了更改而进行。 51 代码风格文档也不应该被视为绝对 !! 44 52 样式(例如为了80列限制拆分行会 !! 45 编码风格的文档也不应该被视为绝对的法律,这是永远不会被违反的。如果有一个很好 53 !! 46 的理由反对这种样式(例如,如果拆分为适合80列限制的行,那么它的可读性就会大大 54 注意您还可以使用 ``clang-format`` 工 !! 47 降低),那么就这样做。 55 化部分代码,和审阅完整的文件以 !! 48 56 可以方便地排序 ``#includes`` 、对齐 !! 49 请注意,您还可以使用 ``clang-format`` 工具来帮助您处理这些规则,自动重新格式 57 信息,请参阅文档 :ref:`Documentation/d !! 50 化部分代码,并查看完整的文件,以发现编码样式错误、拼写错误和可能的改进。它还 >> 51 可以方便地进行排序,包括对齐变量/宏、回流文本和其他类似任务。有关详细信息,请 >> 52 参阅文件 :ref:`Documentation/process/clang-format.rst <clangformat>` 58 53 59 抽象层 54 抽象层 60 ****** 55 ****** 61 56 62 计算机科学教授教学生以灵活性和 57 计算机科学教授教学生以灵活性和信息隐藏的名义广泛使用抽象层。当然,内核广泛 63 地使用了抽象;任何涉及数百万行 !! 58 地使用了抽象;任何涉及数百万行代码的项目都不能做到这一点并存活下来。但经验 64 表明,过度或过早的抽象可能和过 !! 59 表明,过度或过早的抽象可能和过早的优化一样有害。抽象应用于所需的级别, 65 不要过度。 60 不要过度。 66 61 67 简单点,先考虑一个调用时始终只 !! 62 在一个简单的级别上,考虑一个函数的参数,该参数总是由所有调用方作为零传递。 68 以在需要使用它时提供的额外灵活 !! 63 我们可以保留这个论点: 以防有人最终需要使用它提供的额外灵活性。不过,到那时, 69 可能以某种从未被注意到的微妙方 !! 64 实现这个额外参数的代码很有可能以某种从未被注意到的微妙方式被破坏——因为它从 70 的灵活性时,它并未以符合程序员 !! 65 未被使用过。或者,当需要额外的灵活性时,它不会以符合程序员早期期望的方式来 71 补丁来删除未使用的参数;一般来 !! 66 这样做。内核开发人员通常会提交补丁来删除未使用的参数;一般来说,首先不应该 >> 67 添加这些参数。 72 68 73 隐藏硬件访问的抽象层——通常为 !! 69 隐藏硬件访问的抽象层——通常允许大量的驱动程序在多个操作系统中使用——尤其不受 74 欢迎。这样的层使代码变得模糊, 70 欢迎。这样的层使代码变得模糊,可能会造成性能损失;它们不属于Linux内核。 75 71 76 另一方面,如果您发现自己从另一 !! 72 另一方面,如果您发现自己从另一个内核子系统复制了大量的代码,那么现在是时候 77 了解一下:是否需要将这些代码中 !! 73 问一下,事实上,将这些代码中的一些提取到单独的库中,或者在更高的层次上实现 78 实现这些功能。在整个内核中复制 !! 74 这些功能是否有意义。在整个内核中复制相同的代码没有价值。 79 75 80 #ifdef 和预处理 76 #ifdef 和预处理 81 *************** 77 *************** 82 78 83 C预处理器似乎给一些C程序员带来 !! 79 C预处理器似乎给一些C程序员带来了强大的诱惑,他们认为它是一种有效地将大量灵 84 源代码中的方法。但是预处理器不 !! 80 活性编码到源文件中的方法。但是预处理器不是C,大量使用它会导致代码对其他人来 85 对编译器来说更难检查正确性。使 !! 81 说更难读取,对编译器来说更难检查正确性。大量的预处理器几乎总是代码需要一些 86 清理工作的标志。 82 清理工作的标志。 87 83 88 使用#ifdef的条件编译实际上是一个 !! 84 使用ifdef的条件编译实际上是一个强大的功能,它在内核中使用。但是很少有人希望 89 看到代码被铺满#ifdef块。一般规定 !! 85 看到代码被大量地撒上ifdef块。作为一般规则,ifdef的使用应尽可能限制在头文件 90 编译代码可以限制函数,如果代码 !! 86 中。有条件编译的代码可以限制函数,如果代码不存在,这些函数就会变成空的。然后 91 悄悄地优化对空函数的调用。使得 !! 87 编译器将悄悄地优化对空函数的调用。结果是代码更加清晰,更容易理解。 92 !! 88 93 C预处理器宏存在许多危险性,包括 !! 89 C预处理器宏存在许多危险,包括可能对具有副作用且没有类型安全性的表达式进行多 94 重评估。如果您试图定义宏,请考 !! 90 重评估。如果您试图定义宏,请考虑创建一个内联函数。结果相同的代码,但是内联 95 函数更容易阅读,不会多次计算其 !! 91 函数更容易读取,不会多次计算其参数,并且允许编译器对参数和返回值执行类型检查。 96 92 97 内联函数 93 内联函数 98 ******** 94 ******** 99 95 100 不过,内联函数本身也存在风险。 96 不过,内联函数本身也存在风险。程序员可以倾心于避免函数调用和用内联函数填充源 101 文件所固有的效率。然而,这些功 97 文件所固有的效率。然而,这些功能实际上会降低性能。因为它们的代码在每个调用站 102 点都被复制一遍,所以最终会增加 !! 98 点都被复制,所以它们最终会增加编译内核的大小。反过来,这会对处理器的内存缓存 103 造成压力,从而大大降低执行速度 !! 99 造成压力,从而大大降低执行速度。通常,内联函数应该非常小,而且相对较少。毕竟, 104 函数调用的成本并不高;大量创建 !! 100 函数调用的成本并不高;大量内联函数的创建是过早优化的典型例子。 105 101 106 一般来说,内核程序员会自冒风险 !! 102 一般来说,内核程序员会忽略缓存效果,这会带来危险。在开始的数据结构课程中,经 107 时间/空间权衡通常不适用于当代硬 !! 103 典的时间/空间权衡通常不适用于当代硬件。空间就是时间,因为一个大的程序比一个 108 更紧凑的程序运行得慢。 104 更紧凑的程序运行得慢。 109 105 110 较新的编译器越来越激进地决定一 !! 106 最近的编译器在决定一个给定函数是否应该被内联方面扮演着越来越积极的角色。 111 “inline”关键字可能不仅仅是过度 !! 107 因此,“inline”关键字的自由放置可能不仅仅是过度的,它也可能是无关的。 112 108 113 锁 109 锁 114 ** 110 ** 115 111 116 2006年5月,“deviceescape”网络堆栈 !! 112 2006年5月,“deviceescape”网络堆栈在GPL下发布,并被纳入主线内核。这是一个受 117 这是一个受欢迎的消息;Linux中对 !! 113 欢迎的消息;对Linux中无线网络的支持充其量被认为是不合格的,而deviceescape 118 Deviceescape堆栈承诺修复这种情况。 !! 114 堆栈提供了修复这种情况的承诺。然而,直到2007年6月(2.6.22),这段代码才真 119 正进入主线。发生了什么? 115 正进入主线。发生了什么? 120 116 121 这段代码出现了许多闭门造车的迹 !! 117 这段代码显示了许多闭门造车的迹象。但一个特别大的问题是,它并不是设计用于多 122 设计。在合并这个网络堆栈(现在 !! 118 处理器系统。在合并这个网络堆栈(现在称为mac80211)之前,需要对其进行一个锁 123 改造。 !! 119 方案的改造。 124 120 125 曾经,Linux内核代码可以在不考虑 121 曾经,Linux内核代码可以在不考虑多处理器系统所带来的并发性问题的情况下进行 126 开发。然而现在,这个文档就是在 !! 122 开发。然而,现在,这个文件是写在双核笔记本电脑上的。即使在单处理器系统上, 127 为提高响应能力所做的工作也会提 123 为提高响应能力所做的工作也会提高内核内的并发性水平。编写内核代码而不考虑锁 128 的日子早已远去。 !! 124 的日子已经过去很长了。 129 125 130 可以由多个线程并发访问的任何资 126 可以由多个线程并发访问的任何资源(数据结构、硬件寄存器等)必须由锁保护。新 131 的代码应该谨记这一要求;事后修 !! 127 的代码应该记住这一要求;事后改装锁是一项相当困难的任务。内核开发人员应该花 132 时间充分了解可用的锁原语,以便 !! 128 时间充分了解可用的锁原语,以便为作业选择正确的工具。显示对并发性缺乏关注的 133 很难进入主线。 !! 129 代码进入主线将很困难。 134 130 135 回归 131 回归 136 **** 132 **** 137 133 138 最后一个值得一提的危险是回归: !! 134 最后一个值得一提的危险是:它可能会引起改变(这可能会带来很大的改进),从而 139 (这也可能会带来很大的改进)。 !! 135 导致现有用户的某些东西中断。这种变化被称为“回归”,回归已经成为主线内核最不 140 最不受欢迎的问题。除了少数例外 !! 136 受欢迎的。除少数例外情况外,如果回归不能及时修正,会导致回归的变化将被取消。 141 将被取消。最好首先避免回归发生 !! 137 最好首先避免回归。 142 138 143 人们常常争论,如果回归带来的功 !! 139 人们常常争论,如果回归让更多人可以工作,远超过产生问题,那么回归是合理的。 144 如果它破坏了一个系统却为十个系 !! 140 如果它破坏的一个系统却为十个系统带来新的功能,为什么不进行更改呢?2007年7月, 145 Linus对这个问题给出了最佳答案: 141 Linus对这个问题给出了最佳答案: 146 142 147 :: 143 :: >> 144 所以我们不会通过引入新问题来修复错误。那样的谎言很疯狂,没有人知道 >> 145 你是否真的有进展。是前进两步,后退一步,还是向前一步,向后两步? 148 146 149 所以我们不会通过引入新问 !! 147 (http://lwn.net/articles/243460/) 150 是否真的有进展。是前进两 << 151 << 152 (http://lwn.net/Articles/243460/) << 153 148 154 特别不受欢迎的一种回归类型是用 !! 149 一种特别不受欢迎的回归类型是用户空间ABI的任何变化。一旦接口被导出到用户空间, 155 就必须无限期地支持它。这一事实 150 就必须无限期地支持它。这一事实使得用户空间接口的创建特别具有挑战性:因为它们 156 不能以不兼容的方式进行更改,所 !! 151 不能以不兼容的方式进行更改,所以必须第一次正确地进行更改。因此,用户空间界面 157 的思考、清晰的文档和广泛的审查 !! 152 总是需要大量的思考、清晰的文档和广泛的审查。 158 153 159 154 160 代码检查工具 155 代码检查工具 161 ------------ 156 ------------ 162 157 163 至少目前,编写无错误代码仍然是 158 至少目前,编写无错误代码仍然是我们中很少人能达到的理想状态。不过,我们希望做 164 的是,在代码进入主线内核之前, 159 的是,在代码进入主线内核之前,尽可能多地捕获并修复这些错误。为此,内核开发人 165 员已经提供了一系列令人印象深刻 !! 160 员已经组装了一系列令人印象深刻的工具,可以自动捕获各种各样的模糊问题。计算机 166 发现的任何问题都是一个以后不会 161 发现的任何问题都是一个以后不会困扰用户的问题,因此,只要有可能,就应该使用 167 自动化工具。 162 自动化工具。 168 163 169 第一步是注意编译器产生的警告。 !! 164 第一步只是注意编译器产生的警告。当代版本的GCC可以检测(并警告)大量潜在错误。 170 通常,这些警告都指向真正的问题 !! 165 通常,这些警告都指向真正的问题。提交以供审阅的代码通常不会产生任何编译器警告。 171 在消除警告时,注意了解真正的原 !! 166 在消除警告时,注意了解真正的原因,并尽量避免“修复”,使警告消失而不解决其原因。 172 167 173 请注意,并非所有编译器警告都默 168 请注意,并非所有编译器警告都默认启用。使用“make KCFLAGS=-W”构建内核以 174 获得完整集合。 169 获得完整集合。 175 170 176 内核提供了几个配置选项,可以打 171 内核提供了几个配置选项,可以打开调试功能;大多数配置选项位于“kernel hacking” 177 子菜单中。对于任何用于开发或测 172 子菜单中。对于任何用于开发或测试目的的内核,都应该启用其中几个选项。特别是, 178 您应该打开: 173 您应该打开: 179 174 180 - FRAME_WARN 获取大于给定数量的堆 !! 175 - 启用 ENABLE_MUST_CHECK and FRAME_WARN 以获得一组额外的警告,以解决使用不 181 这些警告生成的输出可能比较冗 !! 176 推荐使用的接口或忽略函数的重要返回值等问题。这些警告生成的输出可能是冗长 182 !! 177 的,但您不必担心来自内核其他部分的警告。 183 - DEBUG_OBJECTS 将添加代码以跟踪内 !! 178 184 时发出警告。如果你要添加创建 !! 179 - DEBUG_OBJECTS 将添加代码,以跟踪内核创建的各种对象的生存期,并在出现问题时 185 考虑打开对象调试基础结构的支 !! 180 发出警告。如果要添加创建(和导出)自己的复杂对象的子系统,请考虑添加对对象 >> 181 调试基础结构的支持。 186 182 187 - DEBUG_SLAB 可以发现各种内存分配 183 - DEBUG_SLAB 可以发现各种内存分配和使用错误;它应该用于大多数开发内核。 188 184 189 - DEBUG_SPINLOCK, DEBUG_ATOMIC_SLEEP 和 DEBU !! 185 - DEBUG_SPINLOCK, DEBUG_ATOMIC_SLEEP and DEBUG_MUTEXES 会发现许多常见的 190 锁错误。 !! 186 锁定错误. 191 187 192 还有很多其他调试选项,其中一些 !! 188 还有很多其他调试选项,其中一些将在下面讨论。其中一些具有显著的性能影响,不应 193 一直使用。在学习可用选项上花费 !! 189 一直使用。但是,在学习可用选项上花费的一些时间可能会在短期内得到多次回报。 194 190 195 其中一个较重的调试工具是锁检查 !! 191 其中一个较重的调试工具是锁定检查器或“lockdep”。该工具将跟踪系统中每个锁 196 (spinlock或mutex)的获取和释放、获 192 (spinlock或mutex)的获取和释放、获取锁的相对顺序、当前中断环境等等。然后, 197 它可以确保总是以相同的顺序获取 !! 193 它可以确保总是以相同的顺序获取锁,相同的中断假设适用于所有情况,等等。换句话 198 说,lockdep可以找到许多导致系统死 !! 194 说,lockdep可以找到许多场景,在这些场景中,系统很少会死锁。在部署的系统中, 199 很痛苦(对于开发人员和用户而言 !! 195 这种问题可能会很痛苦(对于开发人员和用户而言);LockDep允许提前以自动方式 200 任何类型的非普通锁的代码在提交 !! 196 发现问题。具有任何类型的非普通锁定的代码在提交包含前应在启用lockdep的情况 >> 197 下运行。 201 198 202 作为一个勤奋的内核程序员,毫无 199 作为一个勤奋的内核程序员,毫无疑问,您将检查任何可能失败的操作(如内存分配) 203 的返回状态。然而,事实上,最终 !! 200 的返回状态。然而,事实上,最终的故障恢复路径可能完全没有经过测试。未测试的 204 代码往往会出问题;如果所有这些 !! 201 代码往往会被破坏;如果所有这些错误处理路径都被执行了几次,那么您可能对代码 205 更有信心。 202 更有信心。 206 203 207 内核提供了一个可以做到这一点的 204 内核提供了一个可以做到这一点的错误注入框架,特别是在涉及内存分配的情况下。 208 启用故障注入后,内存分配的可配 !! 205 启用故障注入后,内存分配的可配置百分比将失败;这些失败可以限制在特定的代码 209 范围内。在启用了故障注入的情况 206 范围内。在启用了故障注入的情况下运行,程序员可以看到当情况恶化时代码如何响 210 应。有关如何使用此工具的详细信 207 应。有关如何使用此工具的详细信息,请参阅 211 Documentation/fault-injection/fault-injection. 208 Documentation/fault-injection/fault-injection.rst。 212 209 213 “sparse”静态分析工具可以发现其 !! 210 使用“sparse”静态分析工具可以发现其他类型的错误。对于sparse,可以警告程序员 214 和内核空间地址之间的混淆、大端 !! 211 用户空间和内核空间地址之间的混淆、big endian和small endian数量的混合、在需 215 整数值等等。sparse必须单独安装(如 !! 212 要一组位标志的地方传递整数值等等。sparse必须单独安装(如果您的分发服务器没 216 可以在 https://sparse.wiki.kernel.org/index !! 213 有将其打包,可以在 https://sparse.wiki.kernel.org/index.php/Main_page)找到, 217 然后可以通过在make命令中添加“C=1 214 然后可以通过在make命令中添加“C=1”在代码上运行它。 218 215 219 “Coccinelle”工具 :ref:`http://coccinelle 216 “Coccinelle”工具 :ref:`http://coccinelle.lip6.fr/ <devtools_coccinelle>` 220 能够发现各种潜在的编码问题;它 217 能够发现各种潜在的编码问题;它还可以为这些问题提出修复方案。在 221 scripts/coccinelle目录下已经打包了相 218 scripts/coccinelle目录下已经打包了相当多的内核“语义补丁”;运行 222 “make coccicheck”将运行这些语义补 219 “make coccicheck”将运行这些语义补丁并报告发现的任何问题。有关详细信息,请参阅 223 :ref:`Documentation/dev-tools/coccinelle.rst < 220 :ref:`Documentation/dev-tools/coccinelle.rst <devtools_coccinelle>` 224 221 225 222 226 其他类型的可移植性错误最好通过 223 其他类型的可移植性错误最好通过为其他体系结构编译代码来发现。如果没有S/390系统 227 或Blackfin开发板,您仍然可以执行 !! 224 或Blackfin开发板,您仍然可以执行编译步骤。可以在以下位置找到一组用于x86系统的 228 交叉编译器: !! 225 大型交叉编译器: 229 226 230 https://www.kernel.org/pub/tools/cross 227 https://www.kernel.org/pub/tools/crosstool/ 231 228 232 花一些时间安装和使用这些编译器 229 花一些时间安装和使用这些编译器将有助于避免以后的尴尬。 233 230 234 文档 231 文档 235 ---- 232 ---- 236 233 237 文档通常比内核开发规则更为例外 234 文档通常比内核开发规则更为例外。即便如此,足够的文档将有助于简化将新代码合并 238 到内核中的过程,使其他开发人员 235 到内核中的过程,使其他开发人员的生活更轻松,并对您的用户有所帮助。在许多情况 239 下,添加文档已基本上是强制性的 !! 236 下,文件的添加已基本上成为强制性的。 240 237 241 任何补丁的第一个文档是其关联的 238 任何补丁的第一个文档是其关联的变更日志。日志条目应该描述正在解决的问题、解决 242 方案的形式、处理补丁的人员、对 239 方案的形式、处理补丁的人员、对性能的任何相关影响,以及理解补丁可能需要的任何 243 其他内容。确保变更日志说明了*为 !! 240 其他内容。确保changelog说明了为什么补丁值得应用;大量开发人员未能提供这些信息。 244 241 245 任何添加新用户空间接口的代码— !! 242 任何添加新用户空间界面的代码(包括新的sysfs或/proc文件)都应该包含该界面的 246 的文档,该文档使用户空间开发人 !! 243 文档,该文档使用户空间开发人员能够知道他们在使用什么。请参阅 247 Documentation/ABI/README,了解如何此文 !! 244 Documentation/ABI/README,了解如何格式化此文档以及需要提供哪些信息。 248 245 249 文档 :ref:`Documentation/admin-guide/kernel- !! 246 文件 :ref:`Documentation/admin-guide/kernel-parameters.rst <kernelparameters>` 250 描述了内核的所有引导时间参数。 !! 247 描述了内核的所有引导时间参数。任何添加新参数的补丁都应该向该文件添加适当的 251 条目。 248 条目。 252 249 253 任何新的配置选项都必须附有帮助 !! 250 任何新的配置选项都必须附有帮助文本,帮助文本清楚地解释了这些选项以及用户可能 254 希望何时使用它们。 !! 251 希望何时选择它们。 255 252 256 许多子系统的内部API信息通过专门 253 许多子系统的内部API信息通过专门格式化的注释进行记录;这些注释可以通过 257 “kernel-doc”脚本以多种方式提取和 254 “kernel-doc”脚本以多种方式提取和格式化。如果您在具有kerneldoc注释的子系统中 258 工作,则应该维护它们,并根据需 255 工作,则应该维护它们,并根据需要为外部可用的功能添加它们。即使在没有如此记录 259 的领域中,为将来添加kerneldoc注释 256 的领域中,为将来添加kerneldoc注释也没有坏处;实际上,这对于刚开始开发内核的人 260 来说是一个有用的活动。这些注释 257 来说是一个有用的活动。这些注释的格式以及如何创建kerneldoc模板的一些信息可以在 261 :ref:`Documentation/doc-guide/ <doc_guide>` 258 :ref:`Documentation/doc-guide/ <doc_guide>` 上找到。 262 259 263 任何阅读大量现有内核代码的人都 !! 260 任何阅读大量现有内核代码的人都会注意到,注释的缺失往往是最值得注意的。再一次, 264 对新代码的要求比过去更高;合并 !! 261 对新代码的期望比过去更高;合并未注释的代码将更加困难。这就是说,人们几乎不希望 265 详细注释的代码。代码本身应该是 !! 262 用语言注释代码。代码本身应该是可读的,注释解释了更微妙的方面。 266 263 267 某些事情应该总是被注释。使用内 264 某些事情应该总是被注释。使用内存屏障时,应附上一行文字,解释为什么需要设置内存 268 屏障。数据结构的锁规则通常需要 !! 265 屏障。数据结构的锁定规则通常需要在某个地方解释。一般来说,主要数据结构需要全面 269 的文档。应该指出代码中分立的位 !! 266 的文档。应该指出单独代码位之间不明显的依赖性。任何可能诱使代码看门人进行错误的 270 错误的“清理”的事情都需要一个 !! 267 “清理”的事情都需要一个注释来说明为什么要这样做。等等。 271 268 272 269 273 内部API更改 270 内部API更改 274 ----------- 271 ----------- 275 272 276 内核提供给用户空间的二进制接口 !! 273 内核提供给用户空间的二进制接口不能被破坏,除非在最严重的情况下。相反,内核的 277 是高度流动的,当需要时可以更改 !! 274 内部编程接口是高度流动的,当需要时可以更改。如果你发现自己不得不处理一个内核 278 仅因为它不满足你的需求导致无法 !! 275 API,或者仅仅因为它不满足你的需求而不使用特定的功能,这可能是API需要改变的一 279 作为内核开发人员,您有权进行此 !! 276 个标志。作为内核开发人员,您有权进行此类更改。 280 !! 277 281 的确可以进行API更改,但更改必须 !! 278 当然, 可以进行API更改,但它们必须是合理的。因此,任何进行内部API更改的补丁都 282 附带关于更改内容和必要原因的描 !! 279 应该附带一个关于更改内容和必要原因的描述。这种变化也应该分解成一个单独的补丁, 283 埋在一个更大的补丁中。 !! 280 而不是埋在一个更大的补丁中。 284 281 285 另一个要点是,更改内部API的开发 282 另一个要点是,更改内部API的开发人员通常要负责修复内核树中被更改破坏的任何代码。 286 对于一个广泛使用的函数,这个责 !! 283 对于一个广泛使用的函数,这个职责可以导致成百上千的变化,其中许多变化可能与其他 287 开发人员正在做的工作相冲突。不 !! 284 开发人员正在做的工作相冲突。不用说,这可能是一项大工作,所以最好确保理由是 288 可靠的。请注意,coccinelle工具可以 285 可靠的。请注意,coccinelle工具可以帮助进行广泛的API更改。 289 286 290 在进行不兼容的API更改时,应尽可 287 在进行不兼容的API更改时,应尽可能确保编译器捕获未更新的代码。这将帮助您确保找 291 到该接口的树内用处。它还将警告 288 到该接口的树内用处。它还将警告开发人员树外代码存在他们需要响应的更改。支持树外 292 代码不是内核开发人员需要担心的 289 代码不是内核开发人员需要担心的事情,但是我们也不必使树外开发人员的生活有不必要 293 的困难。 290 的困难。
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.