13501c960SWu XiangCheng.. include:: ../disclaimer-zh_CN.rst 23501c960SWu XiangCheng 33501c960SWu XiangCheng:Original: Documentation/kernel-hacking/hacking.rst 43501c960SWu XiangCheng 53501c960SWu XiangCheng:译者: 63501c960SWu XiangCheng 73501c960SWu XiangCheng 吴想成 Wu XiangCheng <bobwxc@email.cn> 83501c960SWu XiangCheng 93501c960SWu XiangCheng============== 103501c960SWu XiangCheng内核骇客指北 113501c960SWu XiangCheng============== 123501c960SWu XiangCheng 133501c960SWu XiangCheng:作者: Rusty Russell 143501c960SWu XiangCheng 153501c960SWu XiangCheng引言 163501c960SWu XiangCheng===== 173501c960SWu XiangCheng 183501c960SWu XiangCheng欢迎咱优雅的读者们来阅读Rusty的非常不靠谱的Linux内核骇客(Hacking)指南。本文 193501c960SWu XiangCheng描述了内核代码的常见例程和一般要求:其目标是引导有经验的C程序员入门Linux内核 203501c960SWu XiangCheng开发。我回避了实现细节:这是代码要做的,也忽略了很多有用的例程。 213501c960SWu XiangCheng 223501c960SWu XiangCheng在你读这篇文章之前,请理解我从来没有想过要写这篇文章,因为我的资历太低了; 233501c960SWu XiangCheng但我一直想读这样的文章,自己写是唯一的方法。我希望它能成长为一个最佳实践、 243501c960SWu XiangCheng通用起点和其他信息的汇编。 253501c960SWu XiangCheng 263501c960SWu XiangCheng玩家 273501c960SWu XiangCheng======= 283501c960SWu XiangCheng 293501c960SWu XiangCheng在任何时候,系统中的每个CPU都可以: 303501c960SWu XiangCheng 313501c960SWu XiangCheng- 与任何进程无关,服务于硬件中断; 323501c960SWu XiangCheng 333501c960SWu XiangCheng- 与任何进程无关,服务于软件中断(softirq)或子任务(tasklet); 343501c960SWu XiangCheng 353501c960SWu XiangCheng- 运行于内核空间中,与进程(用户上下文)相关联; 363501c960SWu XiangCheng 373501c960SWu XiangCheng- 在用户空间中运行进程。 383501c960SWu XiangCheng 393501c960SWu XiangCheng它们之间有优先级顺序。最下面的两个可以互相抢占,但上面为严格的层次结构: 403501c960SWu XiangCheng每个层级只能被上方的抢占。例如,当一个软中断在CPU上运行时,没有其他软中断 413501c960SWu XiangCheng会抢占它,但是硬件中断可以抢占它。不过,系统中的任何其他CPU都是独立执行的。 423501c960SWu XiangCheng 433501c960SWu XiangCheng我们将会看到许多方法,用户上下文可以阻止中断,从而成为真正的不可抢占。 443501c960SWu XiangCheng 453501c960SWu XiangCheng用户上下文 463501c960SWu XiangCheng------------ 473501c960SWu XiangCheng 483501c960SWu XiangCheng用户上下文是指当您从系统调用或其他陷阱进入时:就像用户空间一样,您可以被更 493501c960SWu XiangCheng重要的任务和中断抢占。您可以通过调用 :c:func:`schedule()` 进行睡眠。 503501c960SWu XiangCheng 513501c960SWu XiangCheng.. note:: 523501c960SWu XiangCheng 533501c960SWu XiangCheng 在模块加载和卸载以及块设备层上的操作时,你始终处于用户上下文中。 543501c960SWu XiangCheng 553501c960SWu XiangCheng在用户上下文中,当前 ``current`` 指针(指示我们当前正在执行的任务)是有效的, 563501c960SWu XiangCheng且 :c:func:`in_interrupt()` ( ``include/linux/preempt.h`` )值为非(false)。 573501c960SWu XiangCheng 583501c960SWu XiangCheng.. warning:: 593501c960SWu XiangCheng 603501c960SWu XiangCheng 请注意,如果您禁用了抢占或软中断(见下文),:c:func:`in_interrupt()` 会 613501c960SWu XiangCheng 返回假阳性。 623501c960SWu XiangCheng 633501c960SWu XiangCheng硬件中断(Hard IRQs) 643501c960SWu XiangCheng---------------------- 653501c960SWu XiangCheng 663501c960SWu XiangCheng像定时器、网卡和键盘等都是可能在任意时刻产生中断的真实硬件。内核运行中断 673501c960SWu XiangCheng处理程序,为硬件提供服务。内核确保处理程序永远不会重入:如果相同的中断到达, 683501c960SWu XiangCheng它将被排队(或丢弃)。因为它会关闭中断,所以处理程序必须很快:通常它只是 693501c960SWu XiangCheng确认中断,标记一个“软件中断”以执行并退出。 703501c960SWu XiangCheng 71fe450eebSChangbin Du您可以通过 in_hardirq() 返回真来判断您处于硬件中断状态。 723501c960SWu XiangCheng 733501c960SWu XiangCheng.. warning:: 743501c960SWu XiangCheng 753501c960SWu XiangCheng 请注意,如果中断被禁用,这将返回假阳性(见下文)。 763501c960SWu XiangCheng 773501c960SWu XiangCheng软件中断上下文:软中断(Softirqs)与子任务(Tasklets) 783501c960SWu XiangCheng------------------------------------------------------- 793501c960SWu XiangCheng 803501c960SWu XiangCheng当系统调用即将返回用户空间或硬件中断处理程序退出时,任何标记为挂起(通常通 813501c960SWu XiangCheng过硬件中断)的“软件中断”将运行( ``kernel/softirq.c`` )。 823501c960SWu XiangCheng 833501c960SWu XiangCheng此处完成了许多真正的中断处理工作。在向SMP过渡的早期,只有“bottom halves下半 846de8d58cSWu XiangCheng部”(BHs)机制,无法利用多个CPU的优势。在从那些一团糟的旧电脑切换过来后不久, 853501c960SWu XiangCheng我们放弃了这个限制,转而使用“软中断”。 863501c960SWu XiangCheng 873501c960SWu XiangCheng``include/linux/interrupt.h`` 列出了不同的软中断。定时器软中断是一个非常重要 883501c960SWu XiangCheng的软中断( ``include/linux/timer.h`` ):您可以注册它以在给定时间后为您调用 893501c960SWu XiangCheng函数。 903501c960SWu XiangCheng 913501c960SWu XiangCheng软中断通常是一个很难处理的问题,因为同一个软中断将同时在多个CPU上运行。因此, 923501c960SWu XiangCheng子任务( ``include/linux/interrupt.h`` )更常用:它们是动态可注册的(意味着 933501c960SWu XiangCheng您可以拥有任意数量),并且它们还保证任何子任务都只能在一个CPU上运行,不同的 943501c960SWu XiangCheng子任务也可以同时运行。 953501c960SWu XiangCheng 963501c960SWu XiangCheng.. warning:: 973501c960SWu XiangCheng 986de8d58cSWu XiangCheng “tasklet”这个名字是误导性的:它们与“任务”无关。 993501c960SWu XiangCheng 1003501c960SWu XiangCheng你可以使用 :c:func:`in_softirq()` 宏( ``include/linux/preempt.h`` )来确认 1013501c960SWu XiangCheng是否处于软中断(或子任务)中。 1023501c960SWu XiangCheng 1033501c960SWu XiangCheng.. warning:: 1043501c960SWu XiangCheng 1053501c960SWu XiangCheng 注意,如果持有 :ref:`bottom half lock <local_bh_disable_zh>` 锁,这将返回 1063501c960SWu XiangCheng 假阳性。 1073501c960SWu XiangCheng 1083501c960SWu XiangCheng一些基本规则 1093501c960SWu XiangCheng================ 1103501c960SWu XiangCheng 1113501c960SWu XiangCheng缺少内存保护 1123501c960SWu XiangCheng 如果你损坏了内存,无论是在用户上下文还是中断上下文中,整个机器都会崩溃。 1133501c960SWu XiangCheng 你确定你不能在用户空间里做你想做的事吗? 1143501c960SWu XiangCheng 1153501c960SWu XiangCheng缺少浮点或MMX 1163501c960SWu XiangCheng FPU上下文不会被保存;即使在用户上下文中,FPU状态也可能与当前进程不一致: 1173501c960SWu XiangCheng 您会弄乱某些用户进程的FPU状态。如果真的要这样做,就必须显式地保存/恢复 1183501c960SWu XiangCheng 完整的FPU状态(并避免上下文切换)。这通常不是个好主意;请优先用定点算法。 1193501c960SWu XiangCheng 1203501c960SWu XiangCheng严格的堆栈限制 1213501c960SWu XiangCheng 对于大多数32位体系结构,根据配置选项的不同内核堆栈大约为3K到6K;对于大 1223501c960SWu XiangCheng 多数64位机器,内核堆栈大约为14K,并且经常与中断共享,因此你无法使用全部。 1233501c960SWu XiangCheng 应避免深度递归和栈上的巨型本地数组(用动态分配它们来代替)。 1243501c960SWu XiangCheng 1253501c960SWu XiangChengLinux内核是可移植的 1263501c960SWu XiangCheng 就这样吧。您的代码应该是纯64位的,并且不依赖于字节序(endian)。您还应该 1273501c960SWu XiangCheng 尽量减少CPU特定的东西,例如内联汇编(inline assembly)应该被干净地封装和 1283501c960SWu XiangCheng 最小化以便于移植。一般来说,它应该局限于内核树中有体系结构依赖的部分。 1293501c960SWu XiangCheng 1303501c960SWu XiangCheng输入输出控制(ioctls):避免编写新的系统调用 1313501c960SWu XiangCheng============================================== 1323501c960SWu XiangCheng 1333501c960SWu XiangCheng系统调用(system call)通常看起来像这样:: 1343501c960SWu XiangCheng 1353501c960SWu XiangCheng asmlinkage long sys_mycall(int arg) 1363501c960SWu XiangCheng { 1373501c960SWu XiangCheng return 0; 1383501c960SWu XiangCheng } 1393501c960SWu XiangCheng 1403501c960SWu XiangCheng 1413501c960SWu XiangCheng首先,在大多数情况下,您无需创建新的系统调用。创建一个字符设备并为其实现适当 1423501c960SWu XiangCheng的输入输出控制(ioctls)。这比系统调用灵活得多,不必写进每个体系结构的 1433501c960SWu XiangCheng``include/asm/unistd.h`` 和 ``arch/kernel/entry.S`` 文件里,而且更容易被Linus 1443501c960SWu XiangCheng接受。 1453501c960SWu XiangCheng 1463501c960SWu XiangCheng如果您的程序所做的只是读取或写入一些参数,请考虑实现 :c:func:`sysfs()` 接口。 1473501c960SWu XiangCheng 1483501c960SWu XiangCheng在输入输出控制中,您处于进程的用户上下文。出现错误时,返回一个负的错误参数 1493501c960SWu XiangCheng(errno,请参阅 ``include/uapi/asm-generic/errno-base.h`` 、 1503501c960SWu XiangCheng``include/uapi/asm-generic/errno.h`` 和 ``include/linux/errno.h`` ),否则返 1513501c960SWu XiangCheng回0。 1523501c960SWu XiangCheng 1533501c960SWu XiangCheng在睡眠之后,您应该检查是否出现了信号:Unix/Linux处理信号的方法是暂时退出系统 1543501c960SWu XiangCheng调用,并返回 ``-ERESTARTSYS`` 错误。系统调用入口代码将切换回用户上下文,处理 1553501c960SWu XiangCheng信号处理程序,然后系统调用将重新启动(除非用户禁用了该功能)。因此,您应该准 1563501c960SWu XiangCheng备好处理重新启动,例如若您处理某些数据结构到一半。 1573501c960SWu XiangCheng 1583501c960SWu XiangCheng:: 1593501c960SWu XiangCheng 1603501c960SWu XiangCheng if (signal_pending(current)) 1613501c960SWu XiangCheng return -ERESTARTSYS; 1623501c960SWu XiangCheng 1633501c960SWu XiangCheng 1643501c960SWu XiangCheng如果你要做更长时间的计算:优先考虑用户空间。如果你真的想在内核中做这件事,你 1653501c960SWu XiangCheng应该定期检查你是否需要让出CPU(请记得每个CPU都有协作多任务)。 1663501c960SWu XiangCheng习惯用法:: 1673501c960SWu XiangCheng 1683501c960SWu XiangCheng cond_resched(); /* Will sleep */ 1693501c960SWu XiangCheng 1703501c960SWu XiangCheng 1713501c960SWu XiangCheng接口设计的小注释:UNIX系统调用的格言是“提供机制而不是策略 1723501c960SWu XiangChengProvide mechanism not policy”。 1733501c960SWu XiangCheng 1743501c960SWu XiangCheng死锁的“配方” 1753501c960SWu XiangCheng==================== 1763501c960SWu XiangCheng 1773501c960SWu XiangCheng您不能调用任何可能睡眠的程序,除非: 1783501c960SWu XiangCheng 1793501c960SWu XiangCheng- 您处于用户上下文中。 1803501c960SWu XiangCheng 1813501c960SWu XiangCheng- 你未拥有任何自旋锁。 1823501c960SWu XiangCheng 1833501c960SWu XiangCheng- 您已经启用中断(实际上,Andi Kleen说调度代码将为您启用它们,但这可能不是 1843501c960SWu XiangCheng 您想要的)。 1853501c960SWu XiangCheng 1863501c960SWu XiangCheng注意,有些函数可能隐式地睡眠:常见的是用户空间访问函数(\*_user)和没有 1873501c960SWu XiangCheng``GFP_ATOMIC`` 的内存分配函数。 1883501c960SWu XiangCheng 1893501c960SWu XiangCheng您应该始终打开 ``CONFIG_DEBUG_ATOMIC_SLEEP`` 项来编译内核,如果您违反这些 1903501c960SWu XiangCheng规则,它将警告您。如果你 **真的** 违反了规则,你最终会锁住你的电脑。 1913501c960SWu XiangCheng 1923501c960SWu XiangCheng真的会这样。 1933501c960SWu XiangCheng 1943501c960SWu XiangCheng 1953501c960SWu XiangCheng常用函数/程序 1963501c960SWu XiangCheng=============== 1973501c960SWu XiangCheng 1983501c960SWu XiangCheng:c:func:`printk()` 1993501c960SWu XiangCheng------------------ 2003501c960SWu XiangCheng 2013501c960SWu XiangCheng定义于 ``include/linux/printk.h`` 2023501c960SWu XiangCheng 2033501c960SWu XiangCheng:c:func:`printk()` 将内核消息提供给控制台、dmesg和syslog守护进程。它对于调 2043501c960SWu XiangCheng试和报告错误很有用,并且可以在中断上下文中使用,但是使用时要小心:如果机器 2053501c960SWu XiangCheng的控制台中充斥着printk消息则会无法使用。它使用与ANSI C printf基本兼容的格式 2063501c960SWu XiangCheng字符串,并通过C字符串串联为其提供第一个“优先”参数:: 2073501c960SWu XiangCheng 2083501c960SWu XiangCheng printk(KERN_INFO "i = %u\n", i); 2093501c960SWu XiangCheng 2103501c960SWu XiangCheng 2113501c960SWu XiangCheng参见 ``include/linux/kern_levels.h`` ;了解其他 ``KERN_`` 值;syslog将这些值 2123501c960SWu XiangCheng解释为级别。特殊用法:打印IP地址使用:: 2133501c960SWu XiangCheng 2143501c960SWu XiangCheng __be32 ipaddress; 2153501c960SWu XiangCheng printk(KERN_INFO "my ip: %pI4\n", &ipaddress); 2163501c960SWu XiangCheng 2173501c960SWu XiangCheng 2183501c960SWu XiangCheng:c:func:`printk()` 内部使用的1K缓冲区,不捕获溢出。请确保足够使用。 2193501c960SWu XiangCheng 2203501c960SWu XiangCheng.. note:: 2213501c960SWu XiangCheng 2223501c960SWu XiangCheng 当您开始在用户程序中将printf打成printk时,就知道自己是真正的内核程序员了 2233501c960SWu XiangCheng :) 2243501c960SWu XiangCheng 2253501c960SWu XiangCheng.. note:: 2263501c960SWu XiangCheng 2273501c960SWu XiangCheng 另一个注释:最初的unix第六版源代码在其printf函数的顶部有一个注释:“printf 2283501c960SWu XiangCheng 不应该用于叽叽喳喳”。你也应该遵循此建议。 2293501c960SWu XiangCheng 2303501c960SWu XiangCheng:c:func:`copy_to_user()` / :c:func:`copy_from_user()` / :c:func:`get_user()` / :c:func:`put_user()` 2313501c960SWu XiangCheng--------------------------------------------------------------------------------------------------- 2323501c960SWu XiangCheng 2333501c960SWu XiangCheng定义于 ``include/linux/uaccess.h`` / ``asm/uaccess.h`` 2343501c960SWu XiangCheng 2353501c960SWu XiangCheng**[睡眠]** 2363501c960SWu XiangCheng 2373501c960SWu XiangCheng:c:func:`put_user()` 和 :c:func:`get_user()` 用于从用户空间中获取和向用户空 2383501c960SWu XiangCheng间中传出单个值(如int、char或long)。指向用户空间的指针永远不应该直接取消 2393501c960SWu XiangCheng引用:应该使用这些程序复制数据。两者都返回 ``-EFAULT`` 或 0。 2403501c960SWu XiangCheng 2413501c960SWu XiangCheng:c:func:`copy_to_user()` 和 :c:func:`copy_from_user()` 更通用:它们从/向用户 2423501c960SWu XiangCheng空间复制任意数量的数据。 2433501c960SWu XiangCheng 2443501c960SWu XiangCheng.. warning:: 2453501c960SWu XiangCheng 2463501c960SWu XiangCheng 与 :c:func:`put_user()` 和 :c:func:`get_user()` 不同,它们返回未复制的 2473501c960SWu XiangCheng 数据量(即0仍然意味着成功)。 2483501c960SWu XiangCheng 2496de8d58cSWu XiangCheng【是的,这个讨厌的接口真心让我尴尬。火爆的口水仗大概每年都会发生。 2503501c960SWu XiangCheng—— Rusty Russell】 2513501c960SWu XiangCheng 2523501c960SWu XiangCheng这些函数可以隐式睡眠。它不应该在用户上下文之外调用(没有意义)、调用时禁用中断 2533501c960SWu XiangCheng或获得自旋锁。 2543501c960SWu XiangCheng 2553501c960SWu XiangCheng:c:func:`kmalloc()`/:c:func:`kfree()` 2563501c960SWu XiangCheng------------------------------------- 2573501c960SWu XiangCheng 2583501c960SWu XiangCheng定义于 ``include/linux/slab.h`` 2593501c960SWu XiangCheng 2603501c960SWu XiangCheng**[可能睡眠:见下]** 2613501c960SWu XiangCheng 2623501c960SWu XiangCheng这些函数用于动态请求指针对齐的内存块,类似用户空间中的malloc和free,但 2633501c960SWu XiangCheng:c:func:`kmalloc()` 需要额外的标志词。重要的值: 2643501c960SWu XiangCheng 2653501c960SWu XiangCheng``GFP_KERNEL`` 2663501c960SWu XiangCheng 可以睡眠和交换以释放内存。只允许在用户上下文中使用,但这是分配内存最可靠 2673501c960SWu XiangCheng 的方法。 2683501c960SWu XiangCheng 2693501c960SWu XiangCheng``GFP_ATOMIC`` 2703501c960SWu XiangCheng 不会睡眠。较 ``GFP_KERNEL`` 更不可靠,但可以从中断上下文调用。你 **应该** 2713501c960SWu XiangCheng 有一个很好的内存不足错误处理策略。 2723501c960SWu XiangCheng 2733501c960SWu XiangCheng``GFP_DMA`` 2743501c960SWu XiangCheng 分配低于16MB的ISA DMA。如果你不知道那是什么,那你就不需要了。非常不可靠。 2753501c960SWu XiangCheng 2763501c960SWu XiangCheng如果您看到一个从无效上下文警告消息调用的睡眠的函数,那么您可能在没有 2773501c960SWu XiangCheng``GFP_ATOMIC`` 的情况下从中断上下文调用了一个睡眠的分配函数。你必须立即修复, 2783501c960SWu XiangCheng快点! 2793501c960SWu XiangCheng 2803501c960SWu XiangCheng如果你要分配至少 ``PAGE_SIZE`` ( ``asm/page.h`` 或 ``asm/page_types.h`` ) 2813501c960SWu XiangCheng字节,请考虑使用 :c:func:`__get_free_pages()` ( ``include/linux/gfp.h`` )。 2823501c960SWu XiangCheng它采用顺序参数(0表示页面大小,1表示双页,2表示四页……)和与上述相同的内存 2833501c960SWu XiangCheng优先级标志字。 2843501c960SWu XiangCheng 2853501c960SWu XiangCheng如果分配的字节数超过一页,可以使用 :c:func:`vmalloc()` 。它将在内核映射中分 2863501c960SWu XiangCheng配虚拟内存。此块在物理内存中不是连续的,但是MMU(内存管理单元)使它看起来像 2873501c960SWu XiangCheng是为您准备好的连续空间(因此它只是看起来对cpu连续,对外部设备驱动程序则不然)。 2883501c960SWu XiangCheng如果您真的需要为一些奇怪的设备提供大量物理上连续的内存,那么您就会遇到问题: 2893501c960SWu XiangChengLinux对此支持很差,因为正在运行的内核中的内存碎片化会使它变得很困难。最好的 2903501c960SWu XiangCheng方法是在引导过程的早期通过 :c:func:`alloc_bootmem()` 函数分配。 2913501c960SWu XiangCheng 2923501c960SWu XiangCheng在创建自己的常用对象缓存之前,请考虑使用 ``include/linux/slab.h`` 中的slab 2933501c960SWu XiangCheng缓存。 2943501c960SWu XiangCheng 2953501c960SWu XiangCheng:c:macro:`current` 2963501c960SWu XiangCheng------------------ 2973501c960SWu XiangCheng 2983501c960SWu XiangCheng定义于 ``include/asm/current.h`` 2993501c960SWu XiangCheng 3003501c960SWu XiangCheng此全局变量(其实是宏)包含指向当前任务结构(task structure)的指针,因此仅在 3013501c960SWu XiangCheng用户上下文中有效。例如,当进程进行系统调用时,这将指向调用进程的任务结构。 3023501c960SWu XiangCheng在中断上下文中不为空(**not NULL**)。 3033501c960SWu XiangCheng 3043501c960SWu XiangCheng:c:func:`mdelay()`/:c:func:`udelay()` 3053501c960SWu XiangCheng------------------------------------- 3063501c960SWu XiangCheng 3073501c960SWu XiangCheng定义于 ``include/asm/delay.h`` / ``include/linux/delay.h`` 3083501c960SWu XiangCheng 3093501c960SWu XiangCheng:c:func:`udelay()` 和 :c:func:`ndelay()` 函数可被用于小暂停。不要对它们使用 3103501c960SWu XiangCheng大的值,因为这样会导致溢出——帮助函数 :c:func:`mdelay()` 在这里很有用,或者 3113501c960SWu XiangCheng考虑 :c:func:`msleep()`。 3123501c960SWu XiangCheng 3133501c960SWu XiangCheng:c:func:`cpu_to_be32()`/:c:func:`be32_to_cpu()`/:c:func:`cpu_to_le32()`/:c:func:`le32_to_cpu()` 3143501c960SWu XiangCheng----------------------------------------------------------------------------------------------- 3153501c960SWu XiangCheng 3163501c960SWu XiangCheng定义于 ``include/asm/byteorder.h`` 3173501c960SWu XiangCheng 3183501c960SWu XiangCheng:c:func:`cpu_to_be32()` 系列函数(其中“32”可以替换为64或16,“be”可以替换为 3193501c960SWu XiangCheng“le”)是在内核中进行字节序转换的常用方法:它们返回转换后的值。所有的变体也 3203501c960SWu XiangCheng提供反向转换函数: 3213501c960SWu XiangCheng:c:func:`be32_to_cpu()` 等。 3223501c960SWu XiangCheng 3233501c960SWu XiangCheng这些函数有两个主要的变体:指针变体,例如 :c:func:`cpu_to_be32p()` ,它获取 3243501c960SWu XiangCheng指向给定类型的指针,并返回转换后的值。另一个变体是“in-situ”系列,例如 3253501c960SWu XiangCheng:c:func:`cpu_to_be32s()` ,它转换指针引用的值,并返回void。 3263501c960SWu XiangCheng 3273501c960SWu XiangCheng:c:func:`local_irq_save()`/:c:func:`local_irq_restore()` 3283501c960SWu XiangCheng-------------------------------------------------------- 3293501c960SWu XiangCheng 3303501c960SWu XiangCheng定义于 ``include/linux/irqflags.h`` 3313501c960SWu XiangCheng 3323501c960SWu XiangCheng 3333501c960SWu XiangCheng这些程序禁用本地CPU上的硬中断,并还原它们。它们是可重入的;在其一个 3343501c960SWu XiangCheng``unsigned long flags`` 参数中保存以前的状态。如果您知道中断已启用,那么可 3353501c960SWu XiangCheng直接使用 :c:func:`local_irq_disable()` 和 :c:func:`local_irq_enable()`。 3363501c960SWu XiangCheng 3373501c960SWu XiangCheng.. _local_bh_disable_zh: 3383501c960SWu XiangCheng 3393501c960SWu XiangCheng:c:func:`local_bh_disable()`/:c:func:`local_bh_enable()` 3403501c960SWu XiangCheng-------------------------------------------------------- 3413501c960SWu XiangCheng 3423501c960SWu XiangCheng定义于 ``include/linux/bottom_half.h`` 3433501c960SWu XiangCheng 3443501c960SWu XiangCheng 3453501c960SWu XiangCheng这些程序禁用本地CPU上的软中断,并还原它们。它们是可重入的;如果之前禁用了 3463501c960SWu XiangCheng软中断,那么在调用这对函数之后仍然会禁用它们。它们阻止软中断和子任务在当前 3473501c960SWu XiangChengCPU上运行。 3483501c960SWu XiangCheng 3493501c960SWu XiangCheng:c:func:`smp_processor_id()` 3503501c960SWu XiangCheng---------------------------- 3513501c960SWu XiangCheng 3523501c960SWu XiangCheng定义于 ``include/linux/smp.h`` 3533501c960SWu XiangCheng 3543501c960SWu XiangCheng:c:func:`get_cpu()` 禁用抢占(这样您就不会突然移动到另一个cpu)并返回当前 3553501c960SWu XiangCheng处理器号,介于0和 ``NR_CPUS`` 之间。请注意,CPU编号不一定是连续的。完成后, 3563501c960SWu XiangCheng使用 :c:func:`put_cpu()` 再次返回。 3573501c960SWu XiangCheng 3583501c960SWu XiangCheng如果您知道您不能被另一个任务抢占(即您处于中断上下文中,或已禁用抢占),您 3593501c960SWu XiangCheng可以使用 :c:func:`smp_processor_id()`。 3603501c960SWu XiangCheng 3613501c960SWu XiangCheng``__init``/``__exit``/``__initdata`` 3623501c960SWu XiangCheng------------------------------------ 3633501c960SWu XiangCheng 3643501c960SWu XiangCheng定义于 ``include/linux/init.h`` 3653501c960SWu XiangCheng 3663501c960SWu XiangCheng引导之后,内核释放一个特殊的部分;用 ``__init`` 标记的函数和用 ``__initdata`` 3673501c960SWu XiangCheng标记的数据结构在引导完成后被丢弃:同样地,模块在初始化后丢弃此内存。 3683501c960SWu XiangCheng``__exit`` 用于声明只在退出时需要的函数:如果此文件未编译为模块,则该函数将 3693501c960SWu XiangCheng被删除。请参阅头文件以使用。请注意,使用 :c:func:`EXPORT_SYMBOL()` 或 3703501c960SWu XiangCheng:c:func:`EXPORT_SYMBOL_GPL()` 将标记为 ``__init`` 的函数导出到模块是没有意义 3713501c960SWu XiangCheng的——这将出问题。 3723501c960SWu XiangCheng 3733501c960SWu XiangCheng 3743501c960SWu XiangCheng:c:func:`__initcall()`/:c:func:`module_init()` 3753501c960SWu XiangCheng---------------------------------------------- 3763501c960SWu XiangCheng 3773501c960SWu XiangCheng定义于 ``include/linux/init.h`` / ``include/linux/module.h`` 3783501c960SWu XiangCheng 3793501c960SWu XiangCheng内核的许多部分都作为模块(内核的可动态加载部分)良好服务。使用 3803501c960SWu XiangCheng:c:func:`module_init()` 和 :c:func:`module_exit()` 宏可以简化代码编写,无需 3813501c960SWu XiangCheng``#ifdef`` ,即可以作为模块运行或内置在内核中。 3823501c960SWu XiangCheng 3833501c960SWu XiangCheng:c:func:`module_init()` 宏定义在模块插入时(如果文件编译为模块)或在引导时 3843501c960SWu XiangCheng调用哪个函数:如果文件未编译为模块,:c:func:`module_init()` 宏将等效于 3853501c960SWu XiangCheng:c:func:`__initcall()` ,它通过链接器的魔力确保在引导时调用该函数。 3863501c960SWu XiangCheng 3873501c960SWu XiangCheng该函数可以返回一个错误值,以导致模块加载失败(不幸的是,如果将模块编译到内核 3883501c960SWu XiangCheng中,则此操作无效)。此函数在启用中断的用户上下文中调用,因此可以睡眠。 3893501c960SWu XiangCheng 3903501c960SWu XiangCheng:c:func:`module_exit()` 3913501c960SWu XiangCheng----------------------- 3923501c960SWu XiangCheng 3933501c960SWu XiangCheng 3943501c960SWu XiangCheng定义于 ``include/linux/module.h`` 3953501c960SWu XiangCheng 3963501c960SWu XiangCheng这个宏定义了在模块删除时要调用的函数(如果是编译到内核中的文件,则无用武之地)。 3973501c960SWu XiangCheng只有在模块使用计数到零时才会调用它。这个函数也可以睡眠,但不能失败:当它返回 3983501c960SWu XiangCheng时,所有的东西都必须清理干净。 3993501c960SWu XiangCheng 4003501c960SWu XiangCheng注意,这个宏是可选的:如果它不存在,您的模块将不可移除(除非 ``rmmod -f`` )。 4013501c960SWu XiangCheng 4023501c960SWu XiangCheng:c:func:`try_module_get()`/:c:func:`module_put()` 4033501c960SWu XiangCheng------------------------------------------------- 4043501c960SWu XiangCheng 4053501c960SWu XiangCheng定义于 ``include/linux/module.h`` 4063501c960SWu XiangCheng 4073501c960SWu XiangCheng这些函数会操作模块使用计数,以防止删除(如果另一个模块使用其导出的符号之一, 4083501c960SWu XiangCheng则无法删除模块,参见下文)。在调用模块代码之前,您应该在该模块上调用 4093501c960SWu XiangCheng:c:func:`try_module_get()` :若失败,那么该模块将被删除,您应该将其视为不存在。 4103501c960SWu XiangCheng若成功,您就可以安全地进入模块,并在完成后调用模块 :c:func:`module_put()` 。 4113501c960SWu XiangCheng 4123501c960SWu XiangCheng大多数可注册结构体都有所有者字段,例如在 4133501c960SWu XiangCheng:c:type:`struct file_operations <file_operations>` 结构体中,此字段应设置为 4143501c960SWu XiangCheng宏 ``THIS_MODULE`` 。 4153501c960SWu XiangCheng 4163501c960SWu XiangCheng等待队列 ``include/linux/wait.h`` 4173501c960SWu XiangCheng==================================== 4183501c960SWu XiangCheng 4193501c960SWu XiangCheng**[睡眠]** 4203501c960SWu XiangCheng 4213501c960SWu XiangCheng等待队列用于等待某程序在条件为真时唤醒另一程序。必须小心使用,以确保没有竞争 4223501c960SWu XiangCheng条件。先声明一个 :c:type:`wait_queue_head_t` ,然后对希望等待该条件的进程声明 4233501c960SWu XiangCheng一个关于它们自己的 :c:type:`wait_queue_entry_t` ,并将其放入队列中。 4243501c960SWu XiangCheng 4253501c960SWu XiangCheng声明 4263501c960SWu XiangCheng----- 4273501c960SWu XiangCheng 4283501c960SWu XiangCheng使用 :c:func:`DECLARE_WAIT_QUEUE_HEAD()` 宏声明一个 ``wait_queue_head_t`` , 4293501c960SWu XiangCheng或者在初始化代码中使用 :c:func:`init_waitqueue_head()` 程序。 4303501c960SWu XiangCheng 4313501c960SWu XiangCheng排队 4323501c960SWu XiangCheng----- 4333501c960SWu XiangCheng 4343501c960SWu XiangCheng将自己放在等待队列中相当复杂,因为你必须在检查条件之前将自己放入队列中。有一 4353501c960SWu XiangCheng个宏可以来执行此操作: :c:func:`wait_event_interruptible()` 4363501c960SWu XiangCheng( ``include/linux/wait.h`` )第一个参数是等待队列头,第二个参数是计算的表达 4373501c960SWu XiangCheng式;当该表达式为true时宏返回0,或者在接收到信号时返回 ``-ERESTARTSYS`` 。 4383501c960SWu XiangCheng:c:func:`wait_event()` 版本会忽略信号。 4393501c960SWu XiangCheng 4403501c960SWu XiangCheng唤醒排队任务 4413501c960SWu XiangCheng------------- 4423501c960SWu XiangCheng 4433501c960SWu XiangCheng调用 :c:func:`wake_up()` ( ``include/linux/wait.h`` ),它将唤醒队列中的所有 4443501c960SWu XiangCheng进程。例外情况:如果有一个进程设置了 ``TASK_EXCLUSIVE`` ,队列的其余部分将不 4453501c960SWu XiangCheng会被唤醒。这个基本函数的其他变体也可以在同一个头文件中使用。 4463501c960SWu XiangCheng 4473501c960SWu XiangCheng原子操作 4483501c960SWu XiangCheng========= 4493501c960SWu XiangCheng 4503501c960SWu XiangCheng某些操作在所有平台上都有保证。第一类为操作 :c:type:`atomic_t` 4513501c960SWu XiangCheng( ``include/asm/atomic.h`` )的函数;它包含一个有符号整数(至少32位长), 4523501c960SWu XiangCheng您必须使用这些函数来操作或读取 :c:type:`atomic_t` 变量。 4533501c960SWu XiangCheng:c:func:`atomic_read()` 和 :c:func:`atomic_set()` 获取并设置计数器,还有 4543501c960SWu XiangCheng:c:func:`atomic_add()` ,:c:func:`atomic_sub()` ,:c:func:`atomic_inc()` , 4553501c960SWu XiangCheng:c:func:`atomic_dec()` 和 :c:func:`atomic_dec_and_test()` (如果递减为零, 4563501c960SWu XiangCheng则返回true)。 4573501c960SWu XiangCheng 4583501c960SWu XiangCheng是的。它在原子变量为零时返回true(即!=0)。 4593501c960SWu XiangCheng 4603501c960SWu XiangCheng请注意,这些函数比普通的算术运算速度慢,因此不应过度使用。 4613501c960SWu XiangCheng 4623501c960SWu XiangCheng第二类原子操作是在 ``unsigned long`` ( ``include/linux/bitops.h`` )上的 4633501c960SWu XiangCheng原子位操作。这些操作通常采用指向位模式(bit pattern)的指针,第0位是最低有效 4643501c960SWu XiangCheng位。:c:func:`set_bit()`,:c:func:`clear_bit()` 和 :c:func:`change_bit()` 设置、 4653501c960SWu XiangCheng清除和更改给定位。:c:func:`test_and_set_bit()` ,:c:func:`test_and_clear_bit()` 4663501c960SWu XiangCheng和 :c:func:`test_and_change_bit()` 执行相同的操作,但如果之前设置了位,则返回 4673501c960SWu XiangChengtrue;这些对于原子设置标志特别有用。 4683501c960SWu XiangCheng 4693501c960SWu XiangCheng可以使用大于 ``BITS_PER_LONG`` 位的位索引调用这些操作。但结果在大端序平台上 4703501c960SWu XiangCheng不太正常,所以最好不要这样做。 4713501c960SWu XiangCheng 4723501c960SWu XiangCheng符号 4733501c960SWu XiangCheng===== 4743501c960SWu XiangCheng 4753501c960SWu XiangCheng在内核内部,正常的链接规则仍然适用(即除非用static关键字将符号声明为文件范围, 4763501c960SWu XiangCheng否则它可以在内核中的任何位置使用)。但是对于模块,会保留一个特殊可导出符号表, 4773501c960SWu XiangCheng该表将入口点限制为内核内部。模块也可以导出符号。 4783501c960SWu XiangCheng 4793501c960SWu XiangCheng:c:func:`EXPORT_SYMBOL()` 4803501c960SWu XiangCheng------------------------- 4813501c960SWu XiangCheng 4823501c960SWu XiangCheng定义于 ``include/linux/export.h`` 4833501c960SWu XiangCheng 4843501c960SWu XiangCheng这是导出符号的经典方法:动态加载的模块将能够正常使用符号。 4853501c960SWu XiangCheng 4863501c960SWu XiangCheng:c:func:`EXPORT_SYMBOL_GPL()` 4873501c960SWu XiangCheng----------------------------- 4883501c960SWu XiangCheng 4893501c960SWu XiangCheng定义于 ``include/linux/export.h`` 4903501c960SWu XiangCheng 4913501c960SWu XiangCheng 4923501c960SWu XiangCheng类似于 :c:func:`EXPORT_SYMBOL()`,只是 :c:func:`EXPORT_SYMBOL_GPL()` 导出的 4933501c960SWu XiangCheng符号只能由具有由 :c:func:`MODULE_LICENSE()` 指定GPL兼容许可证的模块看到。这 4943501c960SWu XiangCheng意味着此函数被认为是一个内部实现问题,而不是一个真正的接口。一些维护人员和 4953501c960SWu XiangCheng开发人员在添加一些新的API或功能时可能却需要导出 EXPORT_SYMBOL_GPL()。 4963501c960SWu XiangCheng 4973501c960SWu XiangCheng:c:func:`EXPORT_SYMBOL_NS()` 4983501c960SWu XiangCheng---------------------------- 4993501c960SWu XiangCheng 5003501c960SWu XiangCheng定义于 ``include/linux/export.h`` 5013501c960SWu XiangCheng 5023501c960SWu XiangCheng这是 ``EXPORT_SYMBOL()`` 的变体,允许指定符号命名空间。符号名称空间记录于 5033501c960SWu XiangChengDocumentation/core-api/symbol-namespaces.rst 。 5043501c960SWu XiangCheng 5053501c960SWu XiangCheng:c:func:`EXPORT_SYMBOL_NS_GPL()` 5063501c960SWu XiangCheng-------------------------------- 5073501c960SWu XiangCheng 5083501c960SWu XiangCheng定义于 ``include/linux/export.h`` 5093501c960SWu XiangCheng 5103501c960SWu XiangCheng这是 ``EXPORT_SYMBOL_GPL()`` 的变体,允许指定符号命名空间。符号名称空间记录于 5113501c960SWu XiangChengDocumentation/core-api/symbol-namespaces.rst 。 5123501c960SWu XiangCheng 5133501c960SWu XiangCheng程序与惯例 5143501c960SWu XiangCheng=========== 5153501c960SWu XiangCheng 5163501c960SWu XiangCheng双向链表 ``include/linux/list.h`` 5173501c960SWu XiangCheng----------------------------------- 5183501c960SWu XiangCheng 5193501c960SWu XiangCheng内核头文件中曾经有三组链表程序,但这一组是赢家。如果你对一个单链表没有特别迫切的 5203501c960SWu XiangCheng需求,那么这是一个不错的选择。 5213501c960SWu XiangCheng 5223501c960SWu XiangCheng通常 :c:func:`list_for_each_entry()` 很有用。 5233501c960SWu XiangCheng 5243501c960SWu XiangCheng返回值惯例 5253501c960SWu XiangCheng------------ 5263501c960SWu XiangCheng 5273501c960SWu XiangCheng对于在用户上下文中调用的代码,违背C语言惯例是很常见的,即返回0表示成功,返回 5283501c960SWu XiangCheng负错误值(例如 ``-EFAULT`` )表示失败。这在一开始可能是不直观的,但在内核中 5293501c960SWu XiangCheng相当普遍。 5303501c960SWu XiangCheng 5313501c960SWu XiangCheng使用 :c:func:`ERR_PTR()` ( ``include/linux/err.h`` )将负错误值编码到指针中, 5323501c960SWu XiangCheng然后使用 :c:func:`IS_ERR()` 和 :c:func:`PTR_ERR()` 将其再取出:避免为错误值 5333501c960SWu XiangCheng使用单独的指针参数。挺讨厌的,但的确是个好方式。 5343501c960SWu XiangCheng 5353501c960SWu XiangCheng破坏编译 5363501c960SWu XiangCheng---------- 5373501c960SWu XiangCheng 5383501c960SWu XiangChengLinus和其他开发人员有时会更改开发内核中的函数或结构体名称;这样做不仅是为了 5393501c960SWu XiangCheng让每个人都保持警惕,还反映了一个重大的更改(例如,不能再在打开中断的情况下 5406de8d58cSWu XiangCheng调用,或者执行额外的检查,或者不执行以前捕获的检查)。通常这会附带发送一个 5416de8d58cSWu XiangCheng相当全面的注释到相应的内核邮件列表中;请搜索存档以查看。简单地对文件进行全局 5426de8d58cSWu XiangCheng替换通常只会让事情变得 **更糟** 。 5433501c960SWu XiangCheng 5443501c960SWu XiangCheng初始化结构体成员 5453501c960SWu XiangCheng------------------ 5463501c960SWu XiangCheng 5473501c960SWu XiangCheng初始化结构体的首选方法是使用指定的初始化器,如ISO C99所述。 5483501c960SWu XiangCheng例如:: 5493501c960SWu XiangCheng 5503501c960SWu XiangCheng static struct block_device_operations opt_fops = { 5513501c960SWu XiangCheng .open = opt_open, 5523501c960SWu XiangCheng .release = opt_release, 5533501c960SWu XiangCheng .ioctl = opt_ioctl, 5543501c960SWu XiangCheng .check_media_change = opt_media_change, 5553501c960SWu XiangCheng }; 5563501c960SWu XiangCheng 5573501c960SWu XiangCheng 5583501c960SWu XiangCheng这使得很容易查找(grep),并且可以清楚地看到设置了哪些结构字段。你应该这样做, 5593501c960SWu XiangCheng因为它看起来很酷。 5603501c960SWu XiangCheng 5613501c960SWu XiangChengGNU 扩展 5623501c960SWu XiangCheng---------- 5633501c960SWu XiangCheng 5643501c960SWu XiangChengLinux内核中明确允许GNU扩展。请注意,由于缺乏通用性,一些更复杂的版本并没有 5653501c960SWu XiangCheng得到很好的支持,但以下内容被认为是标准的(有关更多详细信息,请参阅GCC info页 5663501c960SWu XiangCheng的“C 扩展”部分——是的,实际上是info页,手册页只是info中内容的简短摘要)。 5673501c960SWu XiangCheng 5683501c960SWu XiangCheng- 内联函数 5693501c960SWu XiangCheng 5703501c960SWu XiangCheng- 语句表达式(Statement expressions)(即({ 和 })结构)。 5713501c960SWu XiangCheng 5723501c960SWu XiangCheng 5733501c960SWu XiangCheng- 声明函数/变量/类型的属性(__attribute__) 5743501c960SWu XiangCheng 5753501c960SWu XiangCheng- typeof 5763501c960SWu XiangCheng 5773501c960SWu XiangCheng- 零长度数组 5783501c960SWu XiangCheng 5793501c960SWu XiangCheng- 宏变量 5803501c960SWu XiangCheng 5813501c960SWu XiangCheng- 空指针运算 5823501c960SWu XiangCheng 5833501c960SWu XiangCheng- 非常量(Non-Constant)初始化程序 5843501c960SWu XiangCheng 5853501c960SWu XiangCheng- 汇编程序指令(在 arch/ 和 include/asm/ 之内) 5863501c960SWu XiangCheng 5873501c960SWu XiangCheng- 字符串函数名(__func__)。 5883501c960SWu XiangCheng 5893501c960SWu XiangCheng- __builtin_constant_p() 5903501c960SWu XiangCheng 5913501c960SWu XiangCheng在内核中使用long long时要小心,gcc为其生成的代码非常糟糕:除法和乘法在i386上 5923501c960SWu XiangCheng不能工作,因为内核环境中缺少用于它的gcc运行时函数。 5933501c960SWu XiangCheng 5943501c960SWu XiangChengC++ 5953501c960SWu XiangCheng--- 5963501c960SWu XiangCheng 5973501c960SWu XiangCheng在内核中使用C++通常是个坏主意,因为内核不提供必要的运行时环境,并且不为其 5983501c960SWu XiangCheng测试包含文件。不过这仍然是可能的,但不建议。如果你真的想这么做,至少别用 5993501c960SWu XiangCheng异常处理(exceptions)。 6003501c960SWu XiangCheng 6013501c960SWu XiangCheng#if 6023501c960SWu XiangCheng--- 6033501c960SWu XiangCheng 6043501c960SWu XiangCheng通常认为,在头文件(或.c文件顶部)中使用宏来抽象函数比在源代码中使用“if”预 6053501c960SWu XiangCheng处理器语句更干净。 6063501c960SWu XiangCheng 6073501c960SWu XiangCheng把你的东西放进内核里 6083501c960SWu XiangCheng====================== 6093501c960SWu XiangCheng 6103501c960SWu XiangCheng为了让你的东西更正式、补丁更整洁,还有一些工作要做: 6113501c960SWu XiangCheng 6126de8d58cSWu XiangCheng- 搞清楚你修改的代码属于谁。查看源文件的根目录、 ``MAINTAINERS`` 文件以及 6133501c960SWu XiangCheng ``CREDITS`` 文件的最后一部分。你应该和此人协调,确保你没有重新发明轮子, 6143501c960SWu XiangCheng 或者尝试一些已经被拒绝的东西。 6153501c960SWu XiangCheng 6163501c960SWu XiangCheng 确保你把你的名字和电子邮件地址放在你创建或修改的任何文件的顶部。当人们发 6173501c960SWu XiangCheng 现一个缺陷,或者想要做出修改时,这是他们首先会看的地方。 6183501c960SWu XiangCheng 6193501c960SWu XiangCheng- 通常你需要一个配置选项来支持你的内核编程。在适当的目录中编辑 ``Kconfig`` 。 6203501c960SWu XiangCheng 配置语言很容易通过剪切和粘贴来使用,在 6213501c960SWu XiangCheng Documentation/kbuild/kconfig-language.rst 中有完整的文档。 6223501c960SWu XiangCheng 6233501c960SWu XiangCheng 在您对选项的描述中,请确保同时照顾到了专家用户和对此功能一无所知的用户。 6243501c960SWu XiangCheng 在此说明任何不兼容和问题。结尾一定要写上“如有疑问,就选N”(或者是“Y”); 6253501c960SWu XiangCheng 这是针对那些看不懂你在说什么的人的。 6263501c960SWu XiangCheng 6273501c960SWu XiangCheng- 编辑 ``Makefile`` :配置变量在这里导出,因此通常你只需添加一行 6283501c960SWu XiangCheng “obj-$(CONFIG_xxx) += xxx.o”。语法记录在 6293501c960SWu XiangCheng Documentation/kbuild/makefiles.rst 。 6303501c960SWu XiangCheng 6316de8d58cSWu XiangCheng- 如果你认为自己做了一些有意义的事情,可以把自己放进 ``CREDITS`` ,通常不 6326de8d58cSWu XiangCheng 止一个文件(无论如何你的名字都应该在源文件的顶部)。 ``MAINTAINERS`` 6336de8d58cSWu XiangCheng 意味着您希望在对子系统进行更改时得到询问,并了解缺陷;这意味着对某部分 6346de8d58cSWu XiangCheng 代码做出更多承诺。 6353501c960SWu XiangCheng 636*9d4e2eedSLukas Bulwahn- 最后,别忘记去阅读 Documentation/process/submitting-patches.rst。 6373501c960SWu XiangCheng 6383501c960SWu XiangChengKernel 仙女棒 6393501c960SWu XiangCheng=============== 6403501c960SWu XiangCheng 6413501c960SWu XiangCheng浏览源代码时的一些收藏。请随意添加到此列表。 6423501c960SWu XiangCheng 6433501c960SWu XiangCheng``arch/x86/include/asm/delay.h``:: 6443501c960SWu XiangCheng 6453501c960SWu XiangCheng #define ndelay(n) (__builtin_constant_p(n) ? \ 6463501c960SWu XiangCheng ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ 6473501c960SWu XiangCheng __ndelay(n)) 6483501c960SWu XiangCheng 6493501c960SWu XiangCheng 6503501c960SWu XiangCheng``include/linux/fs.h``:: 6513501c960SWu XiangCheng 6523501c960SWu XiangCheng /* 6533501c960SWu XiangCheng * Kernel pointers have redundant information, so we can use a 6543501c960SWu XiangCheng * scheme where we can return either an error code or a dentry 6553501c960SWu XiangCheng * pointer with the same return value. 6563501c960SWu XiangCheng * 6573501c960SWu XiangCheng * This should be a per-architecture thing, to allow different 6583501c960SWu XiangCheng * error and pointer decisions. 6593501c960SWu XiangCheng */ 6603501c960SWu XiangCheng #define ERR_PTR(err) ((void *)((long)(err))) 6613501c960SWu XiangCheng #define PTR_ERR(ptr) ((long)(ptr)) 6623501c960SWu XiangCheng #define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) 6633501c960SWu XiangCheng 6643501c960SWu XiangCheng``arch/x86/include/asm/uaccess_32.h:``:: 6653501c960SWu XiangCheng 6663501c960SWu XiangCheng #define copy_to_user(to,from,n) \ 6673501c960SWu XiangCheng (__builtin_constant_p(n) ? \ 6683501c960SWu XiangCheng __constant_copy_to_user((to),(from),(n)) : \ 6693501c960SWu XiangCheng __generic_copy_to_user((to),(from),(n))) 6703501c960SWu XiangCheng 6713501c960SWu XiangCheng 6723501c960SWu XiangCheng``arch/sparc/kernel/head.S:``:: 6733501c960SWu XiangCheng 6743501c960SWu XiangCheng /* 6753501c960SWu XiangCheng * Sun people can't spell worth damn. "compatibility" indeed. 6763501c960SWu XiangCheng * At least we *know* we can't spell, and use a spell-checker. 6773501c960SWu XiangCheng */ 6783501c960SWu XiangCheng 6793501c960SWu XiangCheng /* Uh, actually Linus it is I who cannot spell. Too much murky 6803501c960SWu XiangCheng * Sparc assembly will do this to ya. 6813501c960SWu XiangCheng */ 6823501c960SWu XiangCheng C_LABEL(cputypvar): 6833501c960SWu XiangCheng .asciz "compatibility" 6843501c960SWu XiangCheng 6853501c960SWu XiangCheng /* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */ 6863501c960SWu XiangCheng .align 4 6873501c960SWu XiangCheng C_LABEL(cputypvar_sun4m): 6883501c960SWu XiangCheng .asciz "compatible" 6893501c960SWu XiangCheng 6903501c960SWu XiangCheng 6913501c960SWu XiangCheng``arch/sparc/lib/checksum.S:``:: 6923501c960SWu XiangCheng 6933501c960SWu XiangCheng /* Sun, you just can't beat me, you just can't. Stop trying, 6943501c960SWu XiangCheng * give up. I'm serious, I am going to kick the living shit 6953501c960SWu XiangCheng * out of you, game over, lights out. 6963501c960SWu XiangCheng */ 6973501c960SWu XiangCheng 6983501c960SWu XiangCheng 6993501c960SWu XiangCheng致谢 7003501c960SWu XiangCheng===== 7013501c960SWu XiangCheng 7023501c960SWu XiangCheng感谢Andi Kleen提出点子,回答我的问题,纠正我的错误,充实内容等帮助。 7033501c960SWu XiangCheng感谢Philipp Rumpf做了许多拼写和清晰度修复,以及一些优秀的不明显的点。 7043501c960SWu XiangCheng感谢Werner Almesberger对 :c:func:`disable_irq()` 做了一个很好的总结, 7053501c960SWu XiangChengJes Sorensen和Andrea Arcangeli补充了一些注意事项。 7063501c960SWu XiangCheng感谢Michael Elizabeth Chastain检查并补充了配置部分。 7073501c960SWu XiangCheng感谢Telsa Gwynne教我DocBook。 708