1 .. include:: ../disclaimer-zh_CN.rst 1 .. include:: ../disclaimer-zh_CN.rst 2 2 3 :Original: Documentation/kernel-hacking/hackin 3 :Original: Documentation/kernel-hacking/hacking.rst 4 4 5 :译者: 5 :译者: 6 6 7 吴想成 Wu XiangCheng <bobwxc@email.cn> 7 吴想成 Wu XiangCheng <bobwxc@email.cn> 8 8 9 ============== 9 ============== 10 内核骇客指北 10 内核骇客指北 11 ============== 11 ============== 12 12 13 :作者: Rusty Russell 13 :作者: Rusty Russell 14 14 15 引言 15 引言 16 ===== 16 ===== 17 17 18 欢迎咱优雅的读者们来阅读Rusty的 18 欢迎咱优雅的读者们来阅读Rusty的非常不靠谱的Linux内核骇客(Hacking)指南。本文 19 描述了内核代码的常见例程和一般 19 描述了内核代码的常见例程和一般要求:其目标是引导有经验的C程序员入门Linux内核 20 开发。我回避了实现细节:这是代 20 开发。我回避了实现细节:这是代码要做的,也忽略了很多有用的例程。 21 21 22 在你读这篇文章之前,请理解我从 22 在你读这篇文章之前,请理解我从来没有想过要写这篇文章,因为我的资历太低了; 23 但我一直想读这样的文章,自己写 23 但我一直想读这样的文章,自己写是唯一的方法。我希望它能成长为一个最佳实践、 24 通用起点和其他信息的汇编。 24 通用起点和其他信息的汇编。 25 25 26 玩家 26 玩家 27 ======= 27 ======= 28 28 29 在任何时候,系统中的每个CPU都可 29 在任何时候,系统中的每个CPU都可以: 30 30 31 - 与任何进程无关,服务于硬件中 31 - 与任何进程无关,服务于硬件中断; 32 32 33 - 与任何进程无关,服务于软件中 33 - 与任何进程无关,服务于软件中断(softirq)或子任务(tasklet); 34 34 35 - 运行于内核空间中,与进程(用 35 - 运行于内核空间中,与进程(用户上下文)相关联; 36 36 37 - 在用户空间中运行进程。 37 - 在用户空间中运行进程。 38 38 39 它们之间有优先级顺序。最下面的 39 它们之间有优先级顺序。最下面的两个可以互相抢占,但上面为严格的层次结构: 40 每个层级只能被上方的抢占。例如 40 每个层级只能被上方的抢占。例如,当一个软中断在CPU上运行时,没有其他软中断 41 会抢占它,但是硬件中断可以抢占 41 会抢占它,但是硬件中断可以抢占它。不过,系统中的任何其他CPU都是独立执行的。 42 42 43 我们将会看到许多方法,用户上下 43 我们将会看到许多方法,用户上下文可以阻止中断,从而成为真正的不可抢占。 44 44 45 用户上下文 45 用户上下文 46 ------------ 46 ------------ 47 47 48 用户上下文是指当您从系统调用或 48 用户上下文是指当您从系统调用或其他陷阱进入时:就像用户空间一样,您可以被更 49 重要的任务和中断抢占。您可以通 49 重要的任务和中断抢占。您可以通过调用 :c:func:`schedule()` 进行睡眠。 50 50 51 .. note:: 51 .. note:: 52 52 53 在模块加载和卸载以及块设备层 53 在模块加载和卸载以及块设备层上的操作时,你始终处于用户上下文中。 54 54 55 在用户上下文中,当前 ``current`` 指 55 在用户上下文中,当前 ``current`` 指针(指示我们当前正在执行的任务)是有效的, 56 且 :c:func:`in_interrupt()` ( ``include/lin 56 且 :c:func:`in_interrupt()` ( ``include/linux/preempt.h`` )值为非(false)。 57 57 58 .. warning:: 58 .. warning:: 59 59 60 请注意,如果您禁用了抢占或软 60 请注意,如果您禁用了抢占或软中断(见下文),:c:func:`in_interrupt()` 会 61 返回假阳性。 61 返回假阳性。 62 62 63 硬件中断(Hard IRQs) 63 硬件中断(Hard IRQs) 64 ---------------------- 64 ---------------------- 65 65 66 像定时器、网卡和键盘等都是可能 66 像定时器、网卡和键盘等都是可能在任意时刻产生中断的真实硬件。内核运行中断 67 处理程序,为硬件提供服务。内核 67 处理程序,为硬件提供服务。内核确保处理程序永远不会重入:如果相同的中断到达, 68 它将被排队(或丢弃)。因为它会 68 它将被排队(或丢弃)。因为它会关闭中断,所以处理程序必须很快:通常它只是 69 确认中断,标记一个“软件中断” 69 确认中断,标记一个“软件中断”以执行并退出。 70 70 71 您可以通过 in_hardirq() 返回真来判 71 您可以通过 in_hardirq() 返回真来判断您处于硬件中断状态。 72 72 73 .. warning:: 73 .. warning:: 74 74 75 请注意,如果中断被禁用,这将 75 请注意,如果中断被禁用,这将返回假阳性(见下文)。 76 76 77 软件中断上下文:软中断(Softirqs 77 软件中断上下文:软中断(Softirqs)与子任务(Tasklets) 78 ---------------------------------------------- 78 ------------------------------------------------------- 79 79 80 当系统调用即将返回用户空间或硬 80 当系统调用即将返回用户空间或硬件中断处理程序退出时,任何标记为挂起(通常通 81 过硬件中断)的“软件中断”将运 81 过硬件中断)的“软件中断”将运行( ``kernel/softirq.c`` )。 82 82 83 此处完成了许多真正的中断处理工 83 此处完成了许多真正的中断处理工作。在向SMP过渡的早期,只有“bottom halves下半 84 部”(BHs)机制,无法利用多个CPU !! 84 部”(BHs)机制,无法利用多个CPU的优势。在从那些一团糟的就电脑切换过来后不久, 85 我们放弃了这个限制,转而使用“ 85 我们放弃了这个限制,转而使用“软中断”。 86 86 87 ``include/linux/interrupt.h`` 列出了不同 87 ``include/linux/interrupt.h`` 列出了不同的软中断。定时器软中断是一个非常重要 88 的软中断( ``include/linux/timer.h`` ) 88 的软中断( ``include/linux/timer.h`` ):您可以注册它以在给定时间后为您调用 89 函数。 89 函数。 90 90 91 软中断通常是一个很难处理的问题 91 软中断通常是一个很难处理的问题,因为同一个软中断将同时在多个CPU上运行。因此, 92 子任务( ``include/linux/interrupt.h`` ) 92 子任务( ``include/linux/interrupt.h`` )更常用:它们是动态可注册的(意味着 93 您可以拥有任意数量),并且它们 93 您可以拥有任意数量),并且它们还保证任何子任务都只能在一个CPU上运行,不同的 94 子任务也可以同时运行。 94 子任务也可以同时运行。 95 95 96 .. warning:: 96 .. warning:: 97 97 98 “tasklet”这个名字是误导性的 !! 98 “tasklet”这个名字是误导性的:它们与“任务”无关,可能更多与当时 >> 99 阿列克谢·库兹涅佐夫享用的糟糕伏特加有关。 99 100 100 你可以使用 :c:func:`in_softirq()` 宏( 101 你可以使用 :c:func:`in_softirq()` 宏( ``include/linux/preempt.h`` )来确认 101 是否处于软中断(或子任务)中。 102 是否处于软中断(或子任务)中。 102 103 103 .. warning:: 104 .. warning:: 104 105 105 注意,如果持有 :ref:`bottom half lo 106 注意,如果持有 :ref:`bottom half lock <local_bh_disable_zh>` 锁,这将返回 106 假阳性。 107 假阳性。 107 108 108 一些基本规则 109 一些基本规则 109 ================ 110 ================ 110 111 111 缺少内存保护 112 缺少内存保护 112 如果你损坏了内存,无论是在用 113 如果你损坏了内存,无论是在用户上下文还是中断上下文中,整个机器都会崩溃。 113 你确定你不能在用户空间里做你 114 你确定你不能在用户空间里做你想做的事吗? 114 115 115 缺少浮点或MMX 116 缺少浮点或MMX 116 FPU上下文不会被保存;即使在用 117 FPU上下文不会被保存;即使在用户上下文中,FPU状态也可能与当前进程不一致: 117 您会弄乱某些用户进程的FPU状态 118 您会弄乱某些用户进程的FPU状态。如果真的要这样做,就必须显式地保存/恢复 118 完整的FPU状态(并避免上下文切 119 完整的FPU状态(并避免上下文切换)。这通常不是个好主意;请优先用定点算法。 119 120 120 严格的堆栈限制 121 严格的堆栈限制 121 对于大多数32位体系结构,根据 122 对于大多数32位体系结构,根据配置选项的不同内核堆栈大约为3K到6K;对于大 122 多数64位机器,内核堆栈大约为1 123 多数64位机器,内核堆栈大约为14K,并且经常与中断共享,因此你无法使用全部。 123 应避免深度递归和栈上的巨型本 124 应避免深度递归和栈上的巨型本地数组(用动态分配它们来代替)。 124 125 125 Linux内核是可移植的 126 Linux内核是可移植的 126 就这样吧。您的代码应该是纯64 127 就这样吧。您的代码应该是纯64位的,并且不依赖于字节序(endian)。您还应该 127 尽量减少CPU特定的东西,例如内 128 尽量减少CPU特定的东西,例如内联汇编(inline assembly)应该被干净地封装和 128 最小化以便于移植。一般来说, 129 最小化以便于移植。一般来说,它应该局限于内核树中有体系结构依赖的部分。 129 130 130 输入输出控制(ioctls):避免编写 131 输入输出控制(ioctls):避免编写新的系统调用 131 ============================================== 132 ============================================== 132 133 133 系统调用(system call)通常看起来 134 系统调用(system call)通常看起来像这样:: 134 135 135 asmlinkage long sys_mycall(int arg) 136 asmlinkage long sys_mycall(int arg) 136 { 137 { 137 return 0; 138 return 0; 138 } 139 } 139 140 140 141 141 首先,在大多数情况下,您无需创 142 首先,在大多数情况下,您无需创建新的系统调用。创建一个字符设备并为其实现适当 142 的输入输出控制(ioctls)。这比系 143 的输入输出控制(ioctls)。这比系统调用灵活得多,不必写进每个体系结构的 143 ``include/asm/unistd.h`` 和 ``arch/kernel/ent 144 ``include/asm/unistd.h`` 和 ``arch/kernel/entry.S`` 文件里,而且更容易被Linus 144 接受。 145 接受。 145 146 146 如果您的程序所做的只是读取或写 147 如果您的程序所做的只是读取或写入一些参数,请考虑实现 :c:func:`sysfs()` 接口。 147 148 148 在输入输出控制中,您处于进程的 149 在输入输出控制中,您处于进程的用户上下文。出现错误时,返回一个负的错误参数 149 (errno,请参阅 ``include/uapi/asm-generi 150 (errno,请参阅 ``include/uapi/asm-generic/errno-base.h`` 、 150 ``include/uapi/asm-generic/errno.h`` 和 ``inc 151 ``include/uapi/asm-generic/errno.h`` 和 ``include/linux/errno.h`` ),否则返 151 回0。 152 回0。 152 153 153 在睡眠之后,您应该检查是否出现 154 在睡眠之后,您应该检查是否出现了信号:Unix/Linux处理信号的方法是暂时退出系统 154 调用,并返回 ``-ERESTARTSYS`` 错误。 155 调用,并返回 ``-ERESTARTSYS`` 错误。系统调用入口代码将切换回用户上下文,处理 155 信号处理程序,然后系统调用将重 156 信号处理程序,然后系统调用将重新启动(除非用户禁用了该功能)。因此,您应该准 156 备好处理重新启动,例如若您处理 157 备好处理重新启动,例如若您处理某些数据结构到一半。 157 158 158 :: 159 :: 159 160 160 if (signal_pending(current)) 161 if (signal_pending(current)) 161 return -ERESTARTSYS; 162 return -ERESTARTSYS; 162 163 163 164 164 如果你要做更长时间的计算:优先 165 如果你要做更长时间的计算:优先考虑用户空间。如果你真的想在内核中做这件事,你 165 应该定期检查你是否需要让出CPU( 166 应该定期检查你是否需要让出CPU(请记得每个CPU都有协作多任务)。 166 习惯用法:: 167 习惯用法:: 167 168 168 cond_resched(); /* Will sleep */ 169 cond_resched(); /* Will sleep */ 169 170 170 171 171 接口设计的小注释:UNIX系统调用的 172 接口设计的小注释:UNIX系统调用的格言是“提供机制而不是策略 172 Provide mechanism not policy”。 173 Provide mechanism not policy”。 173 174 174 死锁的“配方” 175 死锁的“配方” 175 ==================== 176 ==================== 176 177 177 您不能调用任何可能睡眠的程序, 178 您不能调用任何可能睡眠的程序,除非: 178 179 179 - 您处于用户上下文中。 180 - 您处于用户上下文中。 180 181 181 - 你未拥有任何自旋锁。 182 - 你未拥有任何自旋锁。 182 183 183 - 您已经启用中断(实际上,Andi Kle 184 - 您已经启用中断(实际上,Andi Kleen说调度代码将为您启用它们,但这可能不是 184 您想要的)。 185 您想要的)。 185 186 186 注意,有些函数可能隐式地睡眠: 187 注意,有些函数可能隐式地睡眠:常见的是用户空间访问函数(\*_user)和没有 187 ``GFP_ATOMIC`` 的内存分配函数。 188 ``GFP_ATOMIC`` 的内存分配函数。 188 189 189 您应该始终打开 ``CONFIG_DEBUG_ATOMIC_S 190 您应该始终打开 ``CONFIG_DEBUG_ATOMIC_SLEEP`` 项来编译内核,如果您违反这些 190 规则,它将警告您。如果你 **真的* 191 规则,它将警告您。如果你 **真的** 违反了规则,你最终会锁住你的电脑。 191 192 192 真的会这样。 193 真的会这样。 193 194 194 195 195 常用函数/程序 196 常用函数/程序 196 =============== 197 =============== 197 198 198 :c:func:`printk()` 199 :c:func:`printk()` 199 ------------------ 200 ------------------ 200 201 201 定义于 ``include/linux/printk.h`` 202 定义于 ``include/linux/printk.h`` 202 203 203 :c:func:`printk()` 将内核消息提供给控 204 :c:func:`printk()` 将内核消息提供给控制台、dmesg和syslog守护进程。它对于调 204 试和报告错误很有用,并且可以在 205 试和报告错误很有用,并且可以在中断上下文中使用,但是使用时要小心:如果机器 205 的控制台中充斥着printk消息则会无 206 的控制台中充斥着printk消息则会无法使用。它使用与ANSI C printf基本兼容的格式 206 字符串,并通过C字符串串联为其提 207 字符串,并通过C字符串串联为其提供第一个“优先”参数:: 207 208 208 printk(KERN_INFO "i = %u\n", i); 209 printk(KERN_INFO "i = %u\n", i); 209 210 210 211 211 参见 ``include/linux/kern_levels.h`` ;了 212 参见 ``include/linux/kern_levels.h`` ;了解其他 ``KERN_`` 值;syslog将这些值 212 解释为级别。特殊用法:打印IP地 213 解释为级别。特殊用法:打印IP地址使用:: 213 214 214 __be32 ipaddress; 215 __be32 ipaddress; 215 printk(KERN_INFO "my ip: %pI4\n", &ipaddre 216 printk(KERN_INFO "my ip: %pI4\n", &ipaddress); 216 217 217 218 218 :c:func:`printk()` 内部使用的1K缓冲区 219 :c:func:`printk()` 内部使用的1K缓冲区,不捕获溢出。请确保足够使用。 219 220 220 .. note:: 221 .. note:: 221 222 222 当您开始在用户程序中将printf打 223 当您开始在用户程序中将printf打成printk时,就知道自己是真正的内核程序员了 223 :) 224 :) 224 225 225 .. note:: 226 .. note:: 226 227 227 另一个注释:最初的unix第六版 228 另一个注释:最初的unix第六版源代码在其printf函数的顶部有一个注释:“printf 228 不应该用于叽叽喳喳”。你也应 229 不应该用于叽叽喳喳”。你也应该遵循此建议。 229 230 230 :c:func:`copy_to_user()` / :c:func:`copy_from_ 231 :c:func:`copy_to_user()` / :c:func:`copy_from_user()` / :c:func:`get_user()` / :c:func:`put_user()` 231 ---------------------------------------------- 232 --------------------------------------------------------------------------------------------------- 232 233 233 定义于 ``include/linux/uaccess.h`` / ``asm/ 234 定义于 ``include/linux/uaccess.h`` / ``asm/uaccess.h`` 234 235 235 **[睡眠]** 236 **[睡眠]** 236 237 237 :c:func:`put_user()` 和 :c:func:`get_user()` 238 :c:func:`put_user()` 和 :c:func:`get_user()` 用于从用户空间中获取和向用户空 238 间中传出单个值(如int、char或long 239 间中传出单个值(如int、char或long)。指向用户空间的指针永远不应该直接取消 239 引用:应该使用这些程序复制数据 240 引用:应该使用这些程序复制数据。两者都返回 ``-EFAULT`` 或 0。 240 241 241 :c:func:`copy_to_user()` 和 :c:func:`copy_fro 242 :c:func:`copy_to_user()` 和 :c:func:`copy_from_user()` 更通用:它们从/向用户 242 空间复制任意数量的数据。 243 空间复制任意数量的数据。 243 244 244 .. warning:: 245 .. warning:: 245 246 246 与 :c:func:`put_user()` 和 :c:func:`get_ 247 与 :c:func:`put_user()` 和 :c:func:`get_user()` 不同,它们返回未复制的 247 数据量(即0仍然意味着成功) 248 数据量(即0仍然意味着成功)。 248 249 249 【是的,这个讨厌的接口真心让我 !! 250 【是的,这个愚蠢的接口真心让我尴尬。火爆的口水仗大概每年都会发生。 250 —— Rusty Russell】 251 —— Rusty Russell】 251 252 252 这些函数可以隐式睡眠。它不应该 253 这些函数可以隐式睡眠。它不应该在用户上下文之外调用(没有意义)、调用时禁用中断 253 或获得自旋锁。 254 或获得自旋锁。 254 255 255 :c:func:`kmalloc()`/:c:func:`kfree()` 256 :c:func:`kmalloc()`/:c:func:`kfree()` 256 ------------------------------------- 257 ------------------------------------- 257 258 258 定义于 ``include/linux/slab.h`` 259 定义于 ``include/linux/slab.h`` 259 260 260 **[可能睡眠:见下]** 261 **[可能睡眠:见下]** 261 262 262 这些函数用于动态请求指针对齐的 263 这些函数用于动态请求指针对齐的内存块,类似用户空间中的malloc和free,但 263 :c:func:`kmalloc()` 需要额外的标志词 264 :c:func:`kmalloc()` 需要额外的标志词。重要的值: 264 265 265 ``GFP_KERNEL`` 266 ``GFP_KERNEL`` 266 可以睡眠和交换以释放内存。只 267 可以睡眠和交换以释放内存。只允许在用户上下文中使用,但这是分配内存最可靠 267 的方法。 268 的方法。 268 269 269 ``GFP_ATOMIC`` 270 ``GFP_ATOMIC`` 270 不会睡眠。较 ``GFP_KERNEL`` 更不 271 不会睡眠。较 ``GFP_KERNEL`` 更不可靠,但可以从中断上下文调用。你 **应该** 271 有一个很好的内存不足错误处理 272 有一个很好的内存不足错误处理策略。 272 273 273 ``GFP_DMA`` 274 ``GFP_DMA`` 274 分配低于16MB的ISA DMA。如果你不 275 分配低于16MB的ISA DMA。如果你不知道那是什么,那你就不需要了。非常不可靠。 275 276 276 如果您看到一个从无效上下文警告 277 如果您看到一个从无效上下文警告消息调用的睡眠的函数,那么您可能在没有 277 ``GFP_ATOMIC`` 的情况下从中断上下文 278 ``GFP_ATOMIC`` 的情况下从中断上下文调用了一个睡眠的分配函数。你必须立即修复, 278 快点! 279 快点! 279 280 280 如果你要分配至少 ``PAGE_SIZE`` ( ``a 281 如果你要分配至少 ``PAGE_SIZE`` ( ``asm/page.h`` 或 ``asm/page_types.h`` ) 281 字节,请考虑使用 :c:func:`__get_free_p 282 字节,请考虑使用 :c:func:`__get_free_pages()` ( ``include/linux/gfp.h`` )。 282 它采用顺序参数(0表示页面大小, 283 它采用顺序参数(0表示页面大小,1表示双页,2表示四页……)和与上述相同的内存 283 优先级标志字。 284 优先级标志字。 284 285 285 如果分配的字节数超过一页,可以 286 如果分配的字节数超过一页,可以使用 :c:func:`vmalloc()` 。它将在内核映射中分 286 配虚拟内存。此块在物理内存中不 287 配虚拟内存。此块在物理内存中不是连续的,但是MMU(内存管理单元)使它看起来像 287 是为您准备好的连续空间(因此它 288 是为您准备好的连续空间(因此它只是看起来对cpu连续,对外部设备驱动程序则不然)。 288 如果您真的需要为一些奇怪的设备 289 如果您真的需要为一些奇怪的设备提供大量物理上连续的内存,那么您就会遇到问题: 289 Linux对此支持很差,因为正在运行 290 Linux对此支持很差,因为正在运行的内核中的内存碎片化会使它变得很困难。最好的 290 方法是在引导过程的早期通过 :c:fun 291 方法是在引导过程的早期通过 :c:func:`alloc_bootmem()` 函数分配。 291 292 292 在创建自己的常用对象缓存之前, 293 在创建自己的常用对象缓存之前,请考虑使用 ``include/linux/slab.h`` 中的slab 293 缓存。 294 缓存。 294 295 295 :c:macro:`current` 296 :c:macro:`current` 296 ------------------ 297 ------------------ 297 298 298 定义于 ``include/asm/current.h`` 299 定义于 ``include/asm/current.h`` 299 300 300 此全局变量(其实是宏)包含指向 301 此全局变量(其实是宏)包含指向当前任务结构(task structure)的指针,因此仅在 301 用户上下文中有效。例如,当进程 302 用户上下文中有效。例如,当进程进行系统调用时,这将指向调用进程的任务结构。 302 在中断上下文中不为空(**not NULL** 303 在中断上下文中不为空(**not NULL**)。 303 304 304 :c:func:`mdelay()`/:c:func:`udelay()` 305 :c:func:`mdelay()`/:c:func:`udelay()` 305 ------------------------------------- 306 ------------------------------------- 306 307 307 定义于 ``include/asm/delay.h`` / ``include/ 308 定义于 ``include/asm/delay.h`` / ``include/linux/delay.h`` 308 309 309 :c:func:`udelay()` 和 :c:func:`ndelay()` 函 310 :c:func:`udelay()` 和 :c:func:`ndelay()` 函数可被用于小暂停。不要对它们使用 310 大的值,因为这样会导致溢出—— 311 大的值,因为这样会导致溢出——帮助函数 :c:func:`mdelay()` 在这里很有用,或者 311 考虑 :c:func:`msleep()`。 312 考虑 :c:func:`msleep()`。 312 313 313 :c:func:`cpu_to_be32()`/:c:func:`be32_to_cpu() 314 :c:func:`cpu_to_be32()`/:c:func:`be32_to_cpu()`/:c:func:`cpu_to_le32()`/:c:func:`le32_to_cpu()` 314 ---------------------------------------------- 315 ----------------------------------------------------------------------------------------------- 315 316 316 定义于 ``include/asm/byteorder.h`` 317 定义于 ``include/asm/byteorder.h`` 317 318 318 :c:func:`cpu_to_be32()` 系列函数(其中 319 :c:func:`cpu_to_be32()` 系列函数(其中“32”可以替换为64或16,“be”可以替换为 319 “le”)是在内核中进行字节序转 320 “le”)是在内核中进行字节序转换的常用方法:它们返回转换后的值。所有的变体也 320 提供反向转换函数: 321 提供反向转换函数: 321 :c:func:`be32_to_cpu()` 等。 322 :c:func:`be32_to_cpu()` 等。 322 323 323 这些函数有两个主要的变体:指针 324 这些函数有两个主要的变体:指针变体,例如 :c:func:`cpu_to_be32p()` ,它获取 324 指向给定类型的指针,并返回转换 325 指向给定类型的指针,并返回转换后的值。另一个变体是“in-situ”系列,例如 325 :c:func:`cpu_to_be32s()` ,它转换指针引 326 :c:func:`cpu_to_be32s()` ,它转换指针引用的值,并返回void。 326 327 327 :c:func:`local_irq_save()`/:c:func:`local_irq_ 328 :c:func:`local_irq_save()`/:c:func:`local_irq_restore()` 328 ---------------------------------------------- 329 -------------------------------------------------------- 329 330 330 定义于 ``include/linux/irqflags.h`` 331 定义于 ``include/linux/irqflags.h`` 331 332 332 333 333 这些程序禁用本地CPU上的硬中断, 334 这些程序禁用本地CPU上的硬中断,并还原它们。它们是可重入的;在其一个 334 ``unsigned long flags`` 参数中保存以前 335 ``unsigned long flags`` 参数中保存以前的状态。如果您知道中断已启用,那么可 335 直接使用 :c:func:`local_irq_disable()` 和 336 直接使用 :c:func:`local_irq_disable()` 和 :c:func:`local_irq_enable()`。 336 337 337 .. _local_bh_disable_zh: 338 .. _local_bh_disable_zh: 338 339 339 :c:func:`local_bh_disable()`/:c:func:`local_bh 340 :c:func:`local_bh_disable()`/:c:func:`local_bh_enable()` 340 ---------------------------------------------- 341 -------------------------------------------------------- 341 342 342 定义于 ``include/linux/bottom_half.h`` 343 定义于 ``include/linux/bottom_half.h`` 343 344 344 345 345 这些程序禁用本地CPU上的软中断, 346 这些程序禁用本地CPU上的软中断,并还原它们。它们是可重入的;如果之前禁用了 346 软中断,那么在调用这对函数之后 347 软中断,那么在调用这对函数之后仍然会禁用它们。它们阻止软中断和子任务在当前 347 CPU上运行。 348 CPU上运行。 348 349 349 :c:func:`smp_processor_id()` 350 :c:func:`smp_processor_id()` 350 ---------------------------- 351 ---------------------------- 351 352 352 定义于 ``include/linux/smp.h`` 353 定义于 ``include/linux/smp.h`` 353 354 354 :c:func:`get_cpu()` 禁用抢占(这样您 355 :c:func:`get_cpu()` 禁用抢占(这样您就不会突然移动到另一个cpu)并返回当前 355 处理器号,介于0和 ``NR_CPUS`` 之间 356 处理器号,介于0和 ``NR_CPUS`` 之间。请注意,CPU编号不一定是连续的。完成后, 356 使用 :c:func:`put_cpu()` 再次返回。 357 使用 :c:func:`put_cpu()` 再次返回。 357 358 358 如果您知道您不能被另一个任务抢 359 如果您知道您不能被另一个任务抢占(即您处于中断上下文中,或已禁用抢占),您 359 可以使用 :c:func:`smp_processor_id()`。 360 可以使用 :c:func:`smp_processor_id()`。 360 361 361 ``__init``/``__exit``/``__initdata`` 362 ``__init``/``__exit``/``__initdata`` 362 ------------------------------------ 363 ------------------------------------ 363 364 364 定义于 ``include/linux/init.h`` 365 定义于 ``include/linux/init.h`` 365 366 366 引导之后,内核释放一个特殊的部 367 引导之后,内核释放一个特殊的部分;用 ``__init`` 标记的函数和用 ``__initdata`` 367 标记的数据结构在引导完成后被丢 368 标记的数据结构在引导完成后被丢弃:同样地,模块在初始化后丢弃此内存。 368 ``__exit`` 用于声明只在退出时需要 369 ``__exit`` 用于声明只在退出时需要的函数:如果此文件未编译为模块,则该函数将 369 被删除。请参阅头文件以使用。请 370 被删除。请参阅头文件以使用。请注意,使用 :c:func:`EXPORT_SYMBOL()` 或 370 :c:func:`EXPORT_SYMBOL_GPL()` 将标记为 ``_ 371 :c:func:`EXPORT_SYMBOL_GPL()` 将标记为 ``__init`` 的函数导出到模块是没有意义 371 的——这将出问题。 372 的——这将出问题。 372 373 373 374 374 :c:func:`__initcall()`/:c:func:`module_init()` 375 :c:func:`__initcall()`/:c:func:`module_init()` 375 ---------------------------------------------- 376 ---------------------------------------------- 376 377 377 定义于 ``include/linux/init.h`` / ``includ 378 定义于 ``include/linux/init.h`` / ``include/linux/module.h`` 378 379 379 内核的许多部分都作为模块(内核 380 内核的许多部分都作为模块(内核的可动态加载部分)良好服务。使用 380 :c:func:`module_init()` 和 :c:func:`module_ex 381 :c:func:`module_init()` 和 :c:func:`module_exit()` 宏可以简化代码编写,无需 381 ``#ifdef`` ,即可以作为模块运行或 382 ``#ifdef`` ,即可以作为模块运行或内置在内核中。 382 383 383 :c:func:`module_init()` 宏定义在模块插 384 :c:func:`module_init()` 宏定义在模块插入时(如果文件编译为模块)或在引导时 384 调用哪个函数:如果文件未编译为 385 调用哪个函数:如果文件未编译为模块,:c:func:`module_init()` 宏将等效于 385 :c:func:`__initcall()` ,它通过链接器 386 :c:func:`__initcall()` ,它通过链接器的魔力确保在引导时调用该函数。 386 387 387 该函数可以返回一个错误值,以导 388 该函数可以返回一个错误值,以导致模块加载失败(不幸的是,如果将模块编译到内核 388 中,则此操作无效)。此函数在启 389 中,则此操作无效)。此函数在启用中断的用户上下文中调用,因此可以睡眠。 389 390 390 :c:func:`module_exit()` 391 :c:func:`module_exit()` 391 ----------------------- 392 ----------------------- 392 393 393 394 394 定义于 ``include/linux/module.h`` 395 定义于 ``include/linux/module.h`` 395 396 396 这个宏定义了在模块删除时要调用 397 这个宏定义了在模块删除时要调用的函数(如果是编译到内核中的文件,则无用武之地)。 397 只有在模块使用计数到零时才会调 398 只有在模块使用计数到零时才会调用它。这个函数也可以睡眠,但不能失败:当它返回 398 时,所有的东西都必须清理干净。 399 时,所有的东西都必须清理干净。 399 400 400 注意,这个宏是可选的:如果它不 401 注意,这个宏是可选的:如果它不存在,您的模块将不可移除(除非 ``rmmod -f`` )。 401 402 402 :c:func:`try_module_get()`/:c:func:`module_put 403 :c:func:`try_module_get()`/:c:func:`module_put()` 403 ---------------------------------------------- 404 ------------------------------------------------- 404 405 405 定义于 ``include/linux/module.h`` 406 定义于 ``include/linux/module.h`` 406 407 407 这些函数会操作模块使用计数,以 408 这些函数会操作模块使用计数,以防止删除(如果另一个模块使用其导出的符号之一, 408 则无法删除模块,参见下文)。在 409 则无法删除模块,参见下文)。在调用模块代码之前,您应该在该模块上调用 409 :c:func:`try_module_get()` :若失败,那 410 :c:func:`try_module_get()` :若失败,那么该模块将被删除,您应该将其视为不存在。 410 若成功,您就可以安全地进入模块 411 若成功,您就可以安全地进入模块,并在完成后调用模块 :c:func:`module_put()` 。 411 412 412 大多数可注册结构体都有所有者字 413 大多数可注册结构体都有所有者字段,例如在 413 :c:type:`struct file_operations <file_operatio 414 :c:type:`struct file_operations <file_operations>` 结构体中,此字段应设置为 414 宏 ``THIS_MODULE`` 。 415 宏 ``THIS_MODULE`` 。 415 416 416 等待队列 ``include/linux/wait.h`` 417 等待队列 ``include/linux/wait.h`` 417 ==================================== 418 ==================================== 418 419 419 **[睡眠]** 420 **[睡眠]** 420 421 421 等待队列用于等待某程序在条件为 422 等待队列用于等待某程序在条件为真时唤醒另一程序。必须小心使用,以确保没有竞争 422 条件。先声明一个 :c:type:`wait_queue_h 423 条件。先声明一个 :c:type:`wait_queue_head_t` ,然后对希望等待该条件的进程声明 423 一个关于它们自己的 :c:type:`wait_queu 424 一个关于它们自己的 :c:type:`wait_queue_entry_t` ,并将其放入队列中。 424 425 425 声明 426 声明 426 ----- 427 ----- 427 428 428 使用 :c:func:`DECLARE_WAIT_QUEUE_HEAD()` 宏 429 使用 :c:func:`DECLARE_WAIT_QUEUE_HEAD()` 宏声明一个 ``wait_queue_head_t`` , 429 或者在初始化代码中使用 :c:func:`ini 430 或者在初始化代码中使用 :c:func:`init_waitqueue_head()` 程序。 430 431 431 排队 432 排队 432 ----- 433 ----- 433 434 434 将自己放在等待队列中相当复杂, 435 将自己放在等待队列中相当复杂,因为你必须在检查条件之前将自己放入队列中。有一 435 个宏可以来执行此操作: :c:func:`wai 436 个宏可以来执行此操作: :c:func:`wait_event_interruptible()` 436 ( ``include/linux/wait.h`` )第一个参 437 ( ``include/linux/wait.h`` )第一个参数是等待队列头,第二个参数是计算的表达 437 式;当该表达式为true时宏返回0, 438 式;当该表达式为true时宏返回0,或者在接收到信号时返回 ``-ERESTARTSYS`` 。 438 :c:func:`wait_event()` 版本会忽略信号 439 :c:func:`wait_event()` 版本会忽略信号。 439 440 440 唤醒排队任务 441 唤醒排队任务 441 ------------- 442 ------------- 442 443 443 调用 :c:func:`wake_up()` ( ``include/linux 444 调用 :c:func:`wake_up()` ( ``include/linux/wait.h`` ),它将唤醒队列中的所有 444 进程。例外情况:如果有一个进程 445 进程。例外情况:如果有一个进程设置了 ``TASK_EXCLUSIVE`` ,队列的其余部分将不 445 会被唤醒。这个基本函数的其他变 446 会被唤醒。这个基本函数的其他变体也可以在同一个头文件中使用。 446 447 447 原子操作 448 原子操作 448 ========= 449 ========= 449 450 450 某些操作在所有平台上都有保证。 451 某些操作在所有平台上都有保证。第一类为操作 :c:type:`atomic_t` 451 ( ``include/asm/atomic.h`` )的函数; 452 ( ``include/asm/atomic.h`` )的函数;它包含一个有符号整数(至少32位长), 452 您必须使用这些函数来操作或读取 453 您必须使用这些函数来操作或读取 :c:type:`atomic_t` 变量。 453 :c:func:`atomic_read()` 和 :c:func:`atomic_se 454 :c:func:`atomic_read()` 和 :c:func:`atomic_set()` 获取并设置计数器,还有 454 :c:func:`atomic_add()` ,:c:func:`atomic_sub( 455 :c:func:`atomic_add()` ,:c:func:`atomic_sub()` ,:c:func:`atomic_inc()` , 455 :c:func:`atomic_dec()` 和 :c:func:`atomic_dec 456 :c:func:`atomic_dec()` 和 :c:func:`atomic_dec_and_test()` (如果递减为零, 456 则返回true)。 457 则返回true)。 457 458 458 是的。它在原子变量为零时返回true 459 是的。它在原子变量为零时返回true(即!=0)。 459 460 460 请注意,这些函数比普通的算术运 461 请注意,这些函数比普通的算术运算速度慢,因此不应过度使用。 461 462 462 第二类原子操作是在 ``unsigned long`` 463 第二类原子操作是在 ``unsigned long`` ( ``include/linux/bitops.h`` )上的 463 原子位操作。这些操作通常采用指 464 原子位操作。这些操作通常采用指向位模式(bit pattern)的指针,第0位是最低有效 464 位。:c:func:`set_bit()`,:c:func:`clear_bit 465 位。:c:func:`set_bit()`,:c:func:`clear_bit()` 和 :c:func:`change_bit()` 设置、 465 清除和更改给定位。:c:func:`test_and_s 466 清除和更改给定位。:c:func:`test_and_set_bit()` ,:c:func:`test_and_clear_bit()` 466 和 :c:func:`test_and_change_bit()` 执行相 467 和 :c:func:`test_and_change_bit()` 执行相同的操作,但如果之前设置了位,则返回 467 true;这些对于原子设置标志特别有 468 true;这些对于原子设置标志特别有用。 468 469 469 可以使用大于 ``BITS_PER_LONG`` 位的位 470 可以使用大于 ``BITS_PER_LONG`` 位的位索引调用这些操作。但结果在大端序平台上 470 不太正常,所以最好不要这样做。 471 不太正常,所以最好不要这样做。 471 472 472 符号 473 符号 473 ===== 474 ===== 474 475 475 在内核内部,正常的链接规则仍然 476 在内核内部,正常的链接规则仍然适用(即除非用static关键字将符号声明为文件范围, 476 否则它可以在内核中的任何位置使 477 否则它可以在内核中的任何位置使用)。但是对于模块,会保留一个特殊可导出符号表, 477 该表将入口点限制为内核内部。模 478 该表将入口点限制为内核内部。模块也可以导出符号。 478 479 479 :c:func:`EXPORT_SYMBOL()` 480 :c:func:`EXPORT_SYMBOL()` 480 ------------------------- 481 ------------------------- 481 482 482 定义于 ``include/linux/export.h`` 483 定义于 ``include/linux/export.h`` 483 484 484 这是导出符号的经典方法:动态加 485 这是导出符号的经典方法:动态加载的模块将能够正常使用符号。 485 486 486 :c:func:`EXPORT_SYMBOL_GPL()` 487 :c:func:`EXPORT_SYMBOL_GPL()` 487 ----------------------------- 488 ----------------------------- 488 489 489 定义于 ``include/linux/export.h`` 490 定义于 ``include/linux/export.h`` 490 491 491 492 492 类似于 :c:func:`EXPORT_SYMBOL()`,只是 : 493 类似于 :c:func:`EXPORT_SYMBOL()`,只是 :c:func:`EXPORT_SYMBOL_GPL()` 导出的 493 符号只能由具有由 :c:func:`MODULE_LICEN 494 符号只能由具有由 :c:func:`MODULE_LICENSE()` 指定GPL兼容许可证的模块看到。这 494 意味着此函数被认为是一个内部实 495 意味着此函数被认为是一个内部实现问题,而不是一个真正的接口。一些维护人员和 495 开发人员在添加一些新的API或功能 496 开发人员在添加一些新的API或功能时可能却需要导出 EXPORT_SYMBOL_GPL()。 496 497 497 :c:func:`EXPORT_SYMBOL_NS()` 498 :c:func:`EXPORT_SYMBOL_NS()` 498 ---------------------------- 499 ---------------------------- 499 500 500 定义于 ``include/linux/export.h`` 501 定义于 ``include/linux/export.h`` 501 502 502 这是 ``EXPORT_SYMBOL()`` 的变体,允许 503 这是 ``EXPORT_SYMBOL()`` 的变体,允许指定符号命名空间。符号名称空间记录于 503 Documentation/core-api/symbol-namespaces.rst 504 Documentation/core-api/symbol-namespaces.rst 。 504 505 505 :c:func:`EXPORT_SYMBOL_NS_GPL()` 506 :c:func:`EXPORT_SYMBOL_NS_GPL()` 506 -------------------------------- 507 -------------------------------- 507 508 508 定义于 ``include/linux/export.h`` 509 定义于 ``include/linux/export.h`` 509 510 510 这是 ``EXPORT_SYMBOL_GPL()`` 的变体,允 511 这是 ``EXPORT_SYMBOL_GPL()`` 的变体,允许指定符号命名空间。符号名称空间记录于 511 Documentation/core-api/symbol-namespaces.rst 512 Documentation/core-api/symbol-namespaces.rst 。 512 513 513 程序与惯例 514 程序与惯例 514 =========== 515 =========== 515 516 516 双向链表 ``include/linux/list.h`` 517 双向链表 ``include/linux/list.h`` 517 ----------------------------------- 518 ----------------------------------- 518 519 519 内核头文件中曾经有三组链表程序 520 内核头文件中曾经有三组链表程序,但这一组是赢家。如果你对一个单链表没有特别迫切的 520 需求,那么这是一个不错的选择。 521 需求,那么这是一个不错的选择。 521 522 522 通常 :c:func:`list_for_each_entry()` 很有 523 通常 :c:func:`list_for_each_entry()` 很有用。 523 524 524 返回值惯例 525 返回值惯例 525 ------------ 526 ------------ 526 527 527 对于在用户上下文中调用的代码, 528 对于在用户上下文中调用的代码,违背C语言惯例是很常见的,即返回0表示成功,返回 528 负错误值(例如 ``-EFAULT`` )表示失 529 负错误值(例如 ``-EFAULT`` )表示失败。这在一开始可能是不直观的,但在内核中 529 相当普遍。 530 相当普遍。 530 531 531 使用 :c:func:`ERR_PTR()` ( ``include/linux 532 使用 :c:func:`ERR_PTR()` ( ``include/linux/err.h`` )将负错误值编码到指针中, 532 然后使用 :c:func:`IS_ERR()` 和 :c:func:`P 533 然后使用 :c:func:`IS_ERR()` 和 :c:func:`PTR_ERR()` 将其再取出:避免为错误值 533 使用单独的指针参数。挺讨厌的, 534 使用单独的指针参数。挺讨厌的,但的确是个好方式。 534 535 535 破坏编译 536 破坏编译 536 ---------- 537 ---------- 537 538 538 Linus和其他开发人员有时会更改开 539 Linus和其他开发人员有时会更改开发内核中的函数或结构体名称;这样做不仅是为了 539 让每个人都保持警惕,还反映了一 540 让每个人都保持警惕,还反映了一个重大的更改(例如,不能再在打开中断的情况下 540 调用,或者执行额外的检查,或者 !! 541 调用,或者执行额外的检查,或者不执行以前捕获的检查)。通常这会附带一个linux 541 相当全面的注释到相应的内核邮件 !! 542 内核邮件列表中相当全面的注释;请搜索存档以查看。简单地对文件进行全局替换通常 542 替换通常只会让事情变得 **更糟** !! 543 会让事情变得 **更糟** 。 543 544 544 初始化结构体成员 545 初始化结构体成员 545 ------------------ 546 ------------------ 546 547 547 初始化结构体的首选方法是使用指 548 初始化结构体的首选方法是使用指定的初始化器,如ISO C99所述。 548 例如:: 549 例如:: 549 550 550 static struct block_device_operations opt_ 551 static struct block_device_operations opt_fops = { 551 .open = opt_open, 552 .open = opt_open, 552 .release = opt_release, 553 .release = opt_release, 553 .ioctl = opt_ioctl, 554 .ioctl = opt_ioctl, 554 .check_media_change = opt_media_ch 555 .check_media_change = opt_media_change, 555 }; 556 }; 556 557 557 558 558 这使得很容易查找(grep),并且可 559 这使得很容易查找(grep),并且可以清楚地看到设置了哪些结构字段。你应该这样做, 559 因为它看起来很酷。 560 因为它看起来很酷。 560 561 561 GNU 扩展 562 GNU 扩展 562 ---------- 563 ---------- 563 564 564 Linux内核中明确允许GNU扩展。请注 565 Linux内核中明确允许GNU扩展。请注意,由于缺乏通用性,一些更复杂的版本并没有 565 得到很好的支持,但以下内容被认 566 得到很好的支持,但以下内容被认为是标准的(有关更多详细信息,请参阅GCC info页 566 的“C 扩展”部分——是的,实际 567 的“C 扩展”部分——是的,实际上是info页,手册页只是info中内容的简短摘要)。 567 568 568 - 内联函数 569 - 内联函数 569 570 570 - 语句表达式(Statement expressions) 571 - 语句表达式(Statement expressions)(即({ 和 })结构)。 571 572 572 573 573 - 声明函数/变量/类型的属性(__attr 574 - 声明函数/变量/类型的属性(__attribute__) 574 575 575 - typeof 576 - typeof 576 577 577 - 零长度数组 578 - 零长度数组 578 579 579 - 宏变量 580 - 宏变量 580 581 581 - 空指针运算 582 - 空指针运算 582 583 583 - 非常量(Non-Constant)初始化程序 584 - 非常量(Non-Constant)初始化程序 584 585 585 - 汇编程序指令(在 arch/ 和 include/a 586 - 汇编程序指令(在 arch/ 和 include/asm/ 之内) 586 587 587 - 字符串函数名(__func__)。 588 - 字符串函数名(__func__)。 588 589 589 - __builtin_constant_p() 590 - __builtin_constant_p() 590 591 591 在内核中使用long long时要小心,gcc 592 在内核中使用long long时要小心,gcc为其生成的代码非常糟糕:除法和乘法在i386上 592 不能工作,因为内核环境中缺少用 593 不能工作,因为内核环境中缺少用于它的gcc运行时函数。 593 594 594 C++ 595 C++ 595 --- 596 --- 596 597 597 在内核中使用C++通常是个坏主意, 598 在内核中使用C++通常是个坏主意,因为内核不提供必要的运行时环境,并且不为其 598 测试包含文件。不过这仍然是可能 599 测试包含文件。不过这仍然是可能的,但不建议。如果你真的想这么做,至少别用 599 异常处理(exceptions)。 600 异常处理(exceptions)。 600 601 601 #if 602 #if 602 --- 603 --- 603 604 604 通常认为,在头文件(或.c文件顶 605 通常认为,在头文件(或.c文件顶部)中使用宏来抽象函数比在源代码中使用“if”预 605 处理器语句更干净。 606 处理器语句更干净。 606 607 607 把你的东西放进内核里 608 把你的东西放进内核里 608 ====================== 609 ====================== 609 610 610 为了让你的东西更正式、补丁更整 611 为了让你的东西更正式、补丁更整洁,还有一些工作要做: 611 612 612 - 搞清楚你修改的代码属于谁。查 !! 613 - 搞清楚你在谁的地界儿上干活。查看源文件的顶部、 ``MAINTAINERS`` 文件以及 613 ``CREDITS`` 文件的最后一部分。你 614 ``CREDITS`` 文件的最后一部分。你应该和此人协调,确保你没有重新发明轮子, 614 或者尝试一些已经被拒绝的东西 615 或者尝试一些已经被拒绝的东西。 615 616 616 确保你把你的名字和电子邮件地 617 确保你把你的名字和电子邮件地址放在你创建或修改的任何文件的顶部。当人们发 617 现一个缺陷,或者想要做出修改 618 现一个缺陷,或者想要做出修改时,这是他们首先会看的地方。 618 619 619 - 通常你需要一个配置选项来支持 620 - 通常你需要一个配置选项来支持你的内核编程。在适当的目录中编辑 ``Kconfig`` 。 620 配置语言很容易通过剪切和粘贴 621 配置语言很容易通过剪切和粘贴来使用,在 621 Documentation/kbuild/kconfig-language.rst 622 Documentation/kbuild/kconfig-language.rst 中有完整的文档。 622 623 623 在您对选项的描述中,请确保同 624 在您对选项的描述中,请确保同时照顾到了专家用户和对此功能一无所知的用户。 624 在此说明任何不兼容和问题。结 625 在此说明任何不兼容和问题。结尾一定要写上“如有疑问,就选N”(或者是“Y”); 625 这是针对那些看不懂你在说什么 626 这是针对那些看不懂你在说什么的人的。 626 627 627 - 编辑 ``Makefile`` :配置变量在这 628 - 编辑 ``Makefile`` :配置变量在这里导出,因此通常你只需添加一行 628 “obj-$(CONFIG_xxx) += xxx.o”。语法 629 “obj-$(CONFIG_xxx) += xxx.o”。语法记录在 629 Documentation/kbuild/makefiles.rst 。 630 Documentation/kbuild/makefiles.rst 。 630 631 631 - 如果你认为自己做了一些有意义 !! 632 - 如果你做了一些有意义的事情,那可以把自己放进 ``CREDITS`` ,通常不止一个 632 止一个文件(无论如何你的名字 !! 633 文件(无论如何你的名字都应该在源文件的顶部)。维护人员意味着您希望在对 633 意味着您希望在对子系统进行更 !! 634 子系统进行更改时得到询问,并了解缺陷;这意味着对某部分代码做出更多承诺。 634 代码做出更多承诺。 << 635 635 636 - 最后,别忘记去阅读 Documentation/p !! 636 - 最后,别忘记去阅读 Documentation/process/submitting-patches.rst , >> 637 也许还有 Documentation/process/submitting-drivers.rst 。 637 638 638 Kernel 仙女棒 639 Kernel 仙女棒 639 =============== 640 =============== 640 641 641 浏览源代码时的一些收藏。请随意 642 浏览源代码时的一些收藏。请随意添加到此列表。 642 643 643 ``arch/x86/include/asm/delay.h``:: 644 ``arch/x86/include/asm/delay.h``:: 644 645 645 #define ndelay(n) (__builtin_constant_p(n) 646 #define ndelay(n) (__builtin_constant_p(n) ? \ 646 ((n) > 20000 ? __bad_ndelay() : __ 647 ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ 647 __ndelay(n)) 648 __ndelay(n)) 648 649 649 650 650 ``include/linux/fs.h``:: 651 ``include/linux/fs.h``:: 651 652 652 /* 653 /* 653 * Kernel pointers have redundant informat 654 * Kernel pointers have redundant information, so we can use a 654 * scheme where we can return either an er 655 * scheme where we can return either an error code or a dentry 655 * pointer with the same return value. 656 * pointer with the same return value. 656 * 657 * 657 * This should be a per-architecture thing 658 * This should be a per-architecture thing, to allow different 658 * error and pointer decisions. 659 * error and pointer decisions. 659 */ 660 */ 660 #define ERR_PTR(err) ((void *)((long)( 661 #define ERR_PTR(err) ((void *)((long)(err))) 661 #define PTR_ERR(ptr) ((long)(ptr)) 662 #define PTR_ERR(ptr) ((long)(ptr)) 662 #define IS_ERR(ptr) ((unsigned long)( 663 #define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) 663 664 664 ``arch/x86/include/asm/uaccess_32.h:``:: 665 ``arch/x86/include/asm/uaccess_32.h:``:: 665 666 666 #define copy_to_user(to,from,n) 667 #define copy_to_user(to,from,n) \ 667 (__builtin_constant_p(n) ? 668 (__builtin_constant_p(n) ? \ 668 __constant_copy_to_user((to),(fro 669 __constant_copy_to_user((to),(from),(n)) : \ 669 __generic_copy_to_user((to),(from 670 __generic_copy_to_user((to),(from),(n))) 670 671 671 672 672 ``arch/sparc/kernel/head.S:``:: 673 ``arch/sparc/kernel/head.S:``:: 673 674 674 /* 675 /* 675 * Sun people can't spell worth damn. "com 676 * Sun people can't spell worth damn. "compatibility" indeed. 676 * At least we *know* we can't spell, and 677 * At least we *know* we can't spell, and use a spell-checker. 677 */ 678 */ 678 679 679 /* Uh, actually Linus it is I who cannot s 680 /* Uh, actually Linus it is I who cannot spell. Too much murky 680 * Sparc assembly will do this to ya. 681 * Sparc assembly will do this to ya. 681 */ 682 */ 682 C_LABEL(cputypvar): 683 C_LABEL(cputypvar): 683 .asciz "compatibility" 684 .asciz "compatibility" 684 685 685 /* Tested on SS-5, SS-10. Probably someone 686 /* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */ 686 .align 4 687 .align 4 687 C_LABEL(cputypvar_sun4m): 688 C_LABEL(cputypvar_sun4m): 688 .asciz "compatible" 689 .asciz "compatible" 689 690 690 691 691 ``arch/sparc/lib/checksum.S:``:: 692 ``arch/sparc/lib/checksum.S:``:: 692 693 693 /* Sun, you just can't beat me, yo 694 /* Sun, you just can't beat me, you just can't. Stop trying, 694 * give up. I'm serious, I am goi 695 * give up. I'm serious, I am going to kick the living shit 695 * out of you, game over, lights o 696 * out of you, game over, lights out. 696 */ 697 */ 697 698 698 699 699 致谢 700 致谢 700 ===== 701 ===== 701 702 702 感谢Andi Kleen提出点子,回答我的问 703 感谢Andi Kleen提出点子,回答我的问题,纠正我的错误,充实内容等帮助。 703 感谢Philipp Rumpf做了许多拼写和清晰 704 感谢Philipp Rumpf做了许多拼写和清晰度修复,以及一些优秀的不明显的点。 704 感谢Werner Almesberger对 :c:func:`disable_i 705 感谢Werner Almesberger对 :c:func:`disable_irq()` 做了一个很好的总结, 705 Jes Sorensen和Andrea Arcangeli补充了一些 706 Jes Sorensen和Andrea Arcangeli补充了一些注意事项。 706 感谢Michael Elizabeth Chastain检查并补 707 感谢Michael Elizabeth Chastain检查并补充了配置部分。 707 感谢Telsa Gwynne教我DocBook。 708 感谢Telsa Gwynne教我DocBook。
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.