sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/kernel-hacking/hackingmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget*/translations/zh_TW/kernel-hacking/hackingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget*/translations/it_IT/kernel-hacking/hackingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget*/translations/ja_JP/kernel-hacking/hackingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget*/translations/ko_KR/kernel-hacking/hackingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget*/translations/sp_SP/kernel-hacking/hackingmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhW/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/kernel-hacking/hacking.rsthKubh field_body)}(h)Documentation/kernel-hacking/hacking.rst h]h)}(h(Documentation/kernel-hacking/hacking.rsth]h(Documentation/kernel-hacking/hacking.rst}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h译者h]h译者}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh)}(h*吴想成 Wu XiangCheng h]h)}(h)吴想成 Wu XiangCheng h](h吴想成 Wu XiangCheng <}(hjhhhNhNubh reference)}(hbobwxc@email.cnh]hbobwxc@email.cn}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:bobwxc@email.cnuh1jhjubh>}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(h内核骇客指北h]h内核骇客指北}(hjKhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjFhhhhhK ubh)}(hhh]h)}(hhh](h)}(h作者h]h作者}(hj_hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj\hhhKubh)}(hRusty Russell h]h)}(h Rusty Russellh]h Rusty Russell}(hjqhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK hjmubah}(h]h ]h"]h$]h&]uh1hhj\ubeh}(h]h ]h"]h$]h&]uh1hhhhK hjYhhubah}(h]h ]h"]h$]h&]uh1hhjFhhhhhK ubjE)}(hhh](jJ)}(h引言h]h引言}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhKubh)}(hX<欢迎咱优雅的读者们来阅读Rusty的非常不靠谱的Linux内核骇客(Hacking)指南。本文 描述了内核代码的常见例程和一般要求:其目标是引导有经验的C程序员入门Linux内核 开发。我回避了实现细节:这是代码要做的,也忽略了很多有用的例程。h]hX<欢迎咱优雅的读者们来阅读Rusty的非常不靠谱的Linux内核骇客(Hacking)指南。本文 描述了内核代码的常见例程和一般要求:其目标是引导有经验的C程序员入门Linux内核 开发。我回避了实现细节:这是代码要做的,也忽略了很多有用的例程。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX在你读这篇文章之前,请理解我从来没有想过要写这篇文章,因为我的资历太低了; 但我一直想读这样的文章,自己写是唯一的方法。我希望它能成长为一个最佳实践、 通用起点和其他信息的汇编。h]hX在你读这篇文章之前,请理解我从来没有想过要写这篇文章,因为我的资历太低了; 但我一直想读这样的文章,自己写是唯一的方法。我希望它能成长为一个最佳实践、 通用起点和其他信息的汇编。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id2ah ]h"]引言ah$]h&]uh1jDhjFhhhhhKubjE)}(hhh](jJ)}(h玩家h]h玩家}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhKubh)}(h3在任何时候,系统中的每个CPU都可以:h]h3在任何时候,系统中的每个CPU都可以:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh bullet_list)}(hhh](h list_item)}(h1与任何进程无关,服务于硬件中断; h]h)}(h0与任何进程无关,服务于硬件中断;h]h0与任何进程无关,服务于硬件中断;}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hW与任何进程无关,服务于软件中断(softirq)或子任务(tasklet); h]h)}(hV与任何进程无关,服务于软件中断(softirq)或子任务(tasklet);h]hV与任何进程无关,服务于软件中断(softirq)或子任务(tasklet);}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK!hjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hF运行于内核空间中,与进程(用户上下文)相关联; h]h)}(hE运行于内核空间中,与进程(用户上下文)相关联;h]hE运行于内核空间中,与进程(用户上下文)相关联;}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK#hjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h"在用户空间中运行进程。 h]h)}(h!在用户空间中运行进程。h]h!在用户空间中运行进程。}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK%hj4ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]bullet-uh1jhhhKhjhhubh)}(hXI它们之间有优先级顺序。最下面的两个可以互相抢占,但上面为严格的层次结构: 每个层级只能被上方的抢占。例如,当一个软中断在CPU上运行时,没有其他软中断 会抢占它,但是硬件中断可以抢占它。不过,系统中的任何其他CPU都是独立执行的。h]hXI它们之间有优先级顺序。最下面的两个可以互相抢占,但上面为严格的层次结构: 每个层级只能被上方的抢占。例如,当一个软中断在CPU上运行时,没有其他软中断 会抢占它,但是硬件中断可以抢占它。不过,系统中的任何其他CPU都是独立执行的。}(hjThhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK'hjhhubh)}(hi我们将会看到许多方法,用户上下文可以阻止中断,从而成为真正的不可抢占。h]hi我们将会看到许多方法,用户上下文可以阻止中断,从而成为真正的不可抢占。}(hjbhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK+hjhhubjE)}(hhh](jJ)}(h用户上下文h]h用户上下文}(hjshhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjphhhhhK.ubh)}(h用户上下文是指当您从系统调用或其他陷阱进入时:就像用户空间一样,您可以被更 重要的任务和中断抢占。您可以通过调用 :c:func:`schedule()` 进行睡眠。h](h用户上下文是指当您从系统调用或其他陷阱进入时:就像用户空间一样,您可以被更 重要的任务和中断抢占。您可以通过调用 }(hjhhhNhNubh)}(h:c:func:`schedule()`h]hliteral)}(hjh]h schedule()}(hjhhhNhNubah}(h]h ](xrefcc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdoc)translations/zh_CN/kernel-hacking/hacking refdomainjreftypefunc refexplicitrefwarn reftargetscheduleuh1hhhhK0hjubh 进行睡眠。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK0hjphhubh)}(h`在模块加载和卸载以及块设备层上的操作时,你始终处于用户上下文中。h]h)}(hjh]h`在模块加载和卸载以及块设备层上的操作时,你始终处于用户上下文中。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK5hjubah}(h]h ]h"]h$]h&]uh1hhjphhhhhNubh)}(h在用户上下文中,当前 ``current`` 指针(指示我们当前正在执行的任务)是有效的, 且 :c:func:`in_interrupt()` ( ``include/linux/preempt.h`` )值为非(false)。h](h在用户上下文中,当前 }(hjhhhNhNubj)}(h ``current``h]hcurrent}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhH 指针(指示我们当前正在执行的任务)是有效的, 且 }(hjhhhNhNubh)}(h:c:func:`in_interrupt()`h]j)}(hjh]hin_interrupt()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj in_interruptuh1hhhhK7hjubh ( }(hjhhhNhNubj)}(h``include/linux/preempt.h``h]hinclude/linux/preempt.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh )值为非(false)。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK7hjphhubhwarning)}(hq请注意,如果您禁用了抢占或软中断(见下文),:c:func:`in_interrupt()` 会 返回假阳性。h]h)}(hq请注意,如果您禁用了抢占或软中断(见下文),:c:func:`in_interrupt()` 会 返回假阳性。h](hB请注意,如果您禁用了抢占或软中断(见下文),}(hj*hhhNhNubh)}(h:c:func:`in_interrupt()`h]j)}(hj4h]hin_interrupt()}(hj6hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj2ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj in_interruptuh1hhhhK` 锁,这将返回 假阳性。h]h)}(hc注意,如果持有 :ref:`bottom half lock ` 锁,这将返回 假阳性。h](h注意,如果持有 }(hjhhhNhNubh)}(h-:ref:`bottom half lock `h]hinline)}(hjh]hbottom half lock}(hjhhhNhNubah}(h]h ](jstdstd-refeh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftyperef refexplicitrefwarnjlocal_bh_disable_zhuh1hhhhKihjubh 锁,这将返回 假阳性。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKihjubah}(h]h ]h"]h$]h&]uh1j$hjhhhhhNubeh}(h]softirqs-taskletsah ]h"]I软件中断上下文:软中断(softirqs)与子任务(tasklets)ah$]h&]uh1jDhjhhhhhKNubeh}(h]id3ah ]h"]玩家ah$]h&]uh1jDhjFhhhhhKubjE)}(hhh](jJ)}(h一些基本规则h]h一些基本规则}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhKmubhdefinition_list)}(hhh](hdefinition_list_item)}(h缺少内存保护 如果你损坏了内存,无论是在用户上下文还是中断上下文中,整个机器都会崩溃。 你确定你不能在用户空间里做你想做的事吗? h](hterm)}(h缺少内存保护h]h缺少内存保护}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKqhjubh definition)}(hhh]h)}(h如果你损坏了内存,无论是在用户上下文还是中断上下文中,整个机器都会崩溃。 你确定你不能在用户空间里做你想做的事吗?h]h如果你损坏了内存,无论是在用户上下文还是中断上下文中,整个机器都会崩溃。 你确定你不能在用户空间里做你想做的事吗?}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKphjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKqhjubj)}(hXR缺少浮点或MMX FPU上下文不会被保存;即使在用户上下文中,FPU状态也可能与当前进程不一致: 您会弄乱某些用户进程的FPU状态。如果真的要这样做,就必须显式地保存/恢复 完整的FPU状态(并避免上下文切换)。这通常不是个好主意;请优先用定点算法。 h](j)}(h缺少浮点或MMXh]h缺少浮点或MMX}(hj9hhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKvhj5ubj)}(hhh]h)}(hX>FPU上下文不会被保存;即使在用户上下文中,FPU状态也可能与当前进程不一致: 您会弄乱某些用户进程的FPU状态。如果真的要这样做,就必须显式地保存/恢复 完整的FPU状态(并避免上下文切换)。这通常不是个好主意;请优先用定点算法。h]hX>FPU上下文不会被保存;即使在用户上下文中,FPU状态也可能与当前进程不一致: 您会弄乱某些用户进程的FPU状态。如果真的要这样做,就必须显式地保存/恢复 完整的FPU状态(并避免上下文切换)。这通常不是个好主意;请优先用定点算法。}(hjJhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKthjGubah}(h]h ]h"]h$]h&]uh1jhj5ubeh}(h]h ]h"]h$]h&]uh1jhhhKvhjhhubj)}(hXD严格的堆栈限制 对于大多数32位体系结构,根据配置选项的不同内核堆栈大约为3K到6K;对于大 多数64位机器,内核堆栈大约为14K,并且经常与中断共享,因此你无法使用全部。 应避免深度递归和栈上的巨型本地数组(用动态分配它们来代替)。 h](j)}(h严格的堆栈限制h]h严格的堆栈限制}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhK{hjdubj)}(hhh]h)}(hX-对于大多数32位体系结构,根据配置选项的不同内核堆栈大约为3K到6K;对于大 多数64位机器,内核堆栈大约为14K,并且经常与中断共享,因此你无法使用全部。 应避免深度递归和栈上的巨型本地数组(用动态分配它们来代替)。h]hX-对于大多数32位体系结构,根据配置选项的不同内核堆栈大约为3K到6K;对于大 多数64位机器,内核堆栈大约为14K,并且经常与中断共享,因此你无法使用全部。 应避免深度递归和栈上的巨型本地数组(用动态分配它们来代替)。}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKyhjvubah}(h]h ]h"]h$]h&]uh1jhjdubeh}(h]h ]h"]h$]h&]uh1jhhhK{hjhhubj)}(hXRLinux内核是可移植的 就这样吧。您的代码应该是纯64位的,并且不依赖于字节序(endian)。您还应该 尽量减少CPU特定的东西,例如内联汇编(inline assembly)应该被干净地封装和 最小化以便于移植。一般来说,它应该局限于内核树中有体系结构依赖的部分。 h](j)}(hLinux内核是可移植的h]hLinux内核是可移植的}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(hX6就这样吧。您的代码应该是纯64位的,并且不依赖于字节序(endian)。您还应该 尽量减少CPU特定的东西,例如内联汇编(inline assembly)应该被干净地封装和 最小化以便于移植。一般来说,它应该局限于内核树中有体系结构依赖的部分。h]hX6就这样吧。您的代码应该是纯64位的,并且不依赖于字节序(endian)。您还应该 尽量减少CPU特定的东西,例如内联汇编(inline assembly)应该被干净地封装和 最小化以便于移植。一般来说,它应该局限于内核树中有体系结构依赖的部分。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK~hjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubeh}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]id5ah ]h"]一些基本规则ah$]h&]uh1jDhjFhhhhhKmubjE)}(hhh](jJ)}(h?输入输出控制(ioctls):避免编写新的系统调用h]h?输入输出控制(ioctls):避免编写新的系统调用}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhKubh)}(h7系统调用(system call)通常看起来像这样::h]h6系统调用(system call)通常看起来像这样:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh literal_block)}(h9asmlinkage long sys_mycall(int arg) { return 0; }h]h9asmlinkage long sys_mycall(int arg) { return 0; }}hjsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1jhhhKhjhhubh)}(hX<首先,在大多数情况下,您无需创建新的系统调用。创建一个字符设备并为其实现适当 的输入输出控制(ioctls)。这比系统调用灵活得多,不必写进每个体系结构的 ``include/asm/unistd.h`` 和 ``arch/kernel/entry.S`` 文件里,而且更容易被Linus 接受。h](h首先,在大多数情况下,您无需创建新的系统调用。创建一个字符设备并为其实现适当 的输入输出控制(ioctls)。这比系统调用灵活得多,不必写进每个体系结构的 }(hjhhhNhNubj)}(h``include/asm/unistd.h``h]hinclude/asm/unistd.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 和 }(hjhhhNhNubj)}(h``arch/kernel/entry.S``h]harch/kernel/entry.S}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh. 文件里,而且更容易被Linus 接受。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hj如果您的程序所做的只是读取或写入一些参数,请考虑实现 :c:func:`sysfs()` 接口。h](hO如果您的程序所做的只是读取或写入一些参数,请考虑实现 }(hj3hhhNhNubh)}(h:c:func:`sysfs()`h]j)}(hj=h]hsysfs()}(hj?hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj;ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjsysfsuh1hhhhKhj3ubh 接口。}(hj3hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX 在输入输出控制中,您处于进程的用户上下文。出现错误时,返回一个负的错误参数 (errno,请参阅 ``include/uapi/asm-generic/errno-base.h`` 、 ``include/uapi/asm-generic/errno.h`` 和 ``include/linux/errno.h`` ),否则返 回0。h](h在输入输出控制中,您处于进程的用户上下文。出现错误时,返回一个负的错误参数 (errno,请参阅 }(hjdhhhNhNubj)}(h)``include/uapi/asm-generic/errno-base.h``h]h%include/uapi/asm-generic/errno-base.h}(hjlhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjdubh 、 }(hjdhhhNhNubj)}(h$``include/uapi/asm-generic/errno.h``h]h include/uapi/asm-generic/errno.h}(hj~hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjdubh 和 }(hjdhhhNhNubj)}(h``include/linux/errno.h``h]hinclude/linux/errno.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjdubh ),否则返 回0。}(hjdhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX在睡眠之后,您应该检查是否出现了信号:Unix/Linux处理信号的方法是暂时退出系统 调用,并返回 ``-ERESTARTSYS`` 错误。系统调用入口代码将切换回用户上下文,处理 信号处理程序,然后系统调用将重新启动(除非用户禁用了该功能)。因此,您应该准 备好处理重新启动,例如若您处理某些数据结构到一半。h](h在睡眠之后,您应该检查是否出现了信号:Unix/Linux处理信号的方法是暂时退出系统 调用,并返回 }(hjhhhNhNubj)}(h``-ERESTARTSYS``h]h -ERESTARTSYS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhX 错误。系统调用入口代码将切换回用户上下文,处理 信号处理程序,然后系统调用将重新启动(除非用户禁用了该功能)。因此,您应该准 备好处理重新启动,例如若您处理某些数据结构到一半。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(h9if (signal_pending(current)) return -ERESTARTSYS;h]h9if (signal_pending(current)) return -ERESTARTSYS;}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhKhjhhubh)}(h如果你要做更长时间的计算:优先考虑用户空间。如果你真的想在内核中做这件事,你 应该定期检查你是否需要让出CPU(请记得每个CPU都有协作多任务)。 习惯用法::h]h如果你要做更长时间的计算:优先考虑用户空间。如果你真的想在内核中做这件事,你 应该定期检查你是否需要让出CPU(请记得每个CPU都有协作多任务)。 习惯用法:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(h cond_resched(); /* Will sleep */h]h cond_resched(); /* Will sleep */}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhKhjhhubh)}(hx接口设计的小注释:UNIX系统调用的格言是“提供机制而不是策略 Provide mechanism not policy”。h]hx接口设计的小注释:UNIX系统调用的格言是“提供机制而不是策略 Provide mechanism not policy”。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]ioctlsah ]h"]?输入输出控制(ioctls):避免编写新的系统调用ah$]h&]uh1jDhjFhhhhhKubjE)}(hhh](jJ)}(h死锁的“配方”h]h死锁的“配方”}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhKubh)}(h6您不能调用任何可能睡眠的程序,除非:h]h6您不能调用任何可能睡眠的程序,除非:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hhh](j)}(h您处于用户上下文中。 h]h)}(h您处于用户上下文中。h]h您处于用户上下文中。}(hj.hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj*ubah}(h]h ]h"]h$]h&]uh1jhj'hhhhhNubj)}(h你未拥有任何自旋锁。 h]h)}(h你未拥有任何自旋锁。h]h你未拥有任何自旋锁。}(hjFhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjBubah}(h]h ]h"]h$]h&]uh1jhj'hhhhhNubj)}(h{您已经启用中断(实际上,Andi Kleen说调度代码将为您启用它们,但这可能不是 您想要的)。 h]h)}(hz您已经启用中断(实际上,Andi Kleen说调度代码将为您启用它们,但这可能不是 您想要的)。h]hz您已经启用中断(实际上,Andi Kleen说调度代码将为您启用它们,但这可能不是 您想要的)。}(hj^hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjZubah}(h]h ]h"]h$]h&]uh1jhj'hhhhhNubeh}(h]h ]h"]h$]h&]jRjSuh1jhhhKhjhhubh)}(h注意,有些函数可能隐式地睡眠:常见的是用户空间访问函数(\*_user)和没有 ``GFP_ATOMIC`` 的内存分配函数。h](hh注意,有些函数可能隐式地睡眠:常见的是用户空间访问函数(*_user)和没有 }(hjxhhhNhNubj)}(h``GFP_ATOMIC``h]h GFP_ATOMIC}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjxubh 的内存分配函数。}(hjxhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h您应该始终打开 ``CONFIG_DEBUG_ATOMIC_SLEEP`` 项来编译内核,如果您违反这些 规则,它将警告您。如果你 **真的** 违反了规则,你最终会锁住你的电脑。h](h您应该始终打开 }(hjhhhNhNubj)}(h``CONFIG_DEBUG_ATOMIC_SLEEP``h]hCONFIG_DEBUG_ATOMIC_SLEEP}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhR 项来编译内核,如果您违反这些 规则,它将警告您。如果你 }(hjhhhNhNubhstrong)}(h **真的**h]h真的}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh4 违反了规则,你最终会锁住你的电脑。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h真的会这样。h]h真的会这样。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id6ah ]h"]死锁的“配方”ah$]h&]uh1jDhjFhhhhhKubjE)}(hhh](jJ)}(h常用函数/程序h]h常用函数/程序}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhKubjE)}(hhh](jJ)}(h:c:func:`printk()`h]h)}(hjh]j)}(hjh]hprintk()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjprintkuh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhKubh)}(h$定义于 ``include/linux/printk.h``h](h 定义于 }(hjhhhNhNubj)}(h``include/linux/printk.h``h]hinclude/linux/printk.h}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX:c:func:`printk()` 将内核消息提供给控制台、dmesg和syslog守护进程。它对于调 试和报告错误很有用,并且可以在中断上下文中使用,但是使用时要小心:如果机器 的控制台中充斥着printk消息则会无法使用。它使用与ANSI C printf基本兼容的格式 字符串,并通过C字符串串联为其提供第一个“优先”参数::h](h)}(h:c:func:`printk()`h]j)}(hj@h]hprintk()}(hjBhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj>ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjprintkuh1hhhhKhj:ubhXt 将内核消息提供给控制台、dmesg和syslog守护进程。它对于调 试和报告错误很有用,并且可以在中断上下文中使用,但是使用时要小心:如果机器 的控制台中充斥着printk消息则会无法使用。它使用与ANSI C printf基本兼容的格式 字符串,并通过C字符串串联为其提供第一个“优先”参数:}(hj:hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(h printk(KERN_INFO "i = %u\n", i);h]h printk(KERN_INFO "i = %u\n", i);}hjgsbah}(h]h ]h"]h$]h&]jjuh1jhhhKhjhhubh)}(h参见 ``include/linux/kern_levels.h`` ;了解其他 ``KERN_`` 值;syslog将这些值 解释为级别。特殊用法:打印IP地址使用::h](h参见 }(hjuhhhNhNubj)}(h``include/linux/kern_levels.h``h]hinclude/linux/kern_levels.h}(hj}hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjuubh ;了解其他 }(hjuhhhNhNubj)}(h ``KERN_``h]hKERN_}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjuubhP 值;syslog将这些值 解释为级别。特殊用法:打印IP地址使用:}(hjuhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(h@__be32 ipaddress; printk(KERN_INFO "my ip: %pI4\n", &ipaddress);h]h@__be32 ipaddress; printk(KERN_INFO "my ip: %pI4\n", &ipaddress);}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhKhjhhubh)}(hZ:c:func:`printk()` 内部使用的1K缓冲区,不捕获溢出。请确保足够使用。h](h)}(h:c:func:`printk()`h]j)}(hjh]hprintk()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjprintkuh1hhhhKhjubhH 内部使用的1K缓冲区,不捕获溢出。请确保足够使用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hi当您开始在用户程序中将printf打成printk时,就知道自己是真正的内核程序员了 :)h]h)}(hi当您开始在用户程序中将printf打成printk时,就知道自己是真正的内核程序员了 :)h]hi当您开始在用户程序中将printf打成printk时,就知道自己是真正的内核程序员了 :)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhjhhhhhNubh)}(h另一个注释:最初的unix第六版源代码在其printf函数的顶部有一个注释:“printf 不应该用于叽叽喳喳”。你也应该遵循此建议。h]h)}(h另一个注释:最初的unix第六版源代码在其printf函数的顶部有一个注释:“printf 不应该用于叽叽喳喳”。你也应该遵循此建议。h]h另一个注释:最初的unix第六版源代码在其printf函数的顶部有一个注释:“printf 不应该用于叽叽喳喳”。你也应该遵循此建议。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhjhhhhhNubeh}(h]printkah ]h"]printk()ah$]h&]uh1jDhjhhhhhKubjE)}(hhh](jJ)}(hc:c:func:`copy_to_user()` / :c:func:`copy_from_user()` / :c:func:`get_user()` / :c:func:`put_user()`h](h)}(h:c:func:`copy_to_user()`h]j)}(hj# h]hcopy_to_user()}(hj% hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj! ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj copy_to_useruh1hhhhKhj ubh / }(hj hhhNhNubh)}(h:c:func:`copy_from_user()`h]j)}(hjF h]hcopy_from_user()}(hjH hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjD ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjcopy_from_useruh1hhhhKhj ubh / }hj sbh)}(h:c:func:`get_user()`h]j)}(hji h]h get_user()}(hjk hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjg ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjget_useruh1hhhhKhj ubh / }hj sbh)}(h:c:func:`put_user()`h]j)}(hj h]h put_user()}(hj hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjput_useruh1hhhhKhj ubeh}(h]h ]h"]h$]h&]uh1jIhj hhhhhKubh)}(h9定义于 ``include/linux/uaccess.h`` / ``asm/uaccess.h``h](h 定义于 }(hj hhhNhNubj)}(h``include/linux/uaccess.h``h]hinclude/linux/uaccess.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh / }(hj hhhNhNubj)}(h``asm/uaccess.h``h]h asm/uaccess.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(h **[睡眠]**h]j)}(hj h]h[睡眠]}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(hX:c:func:`put_user()` 和 :c:func:`get_user()` 用于从用户空间中获取和向用户空 间中传出单个值(如int、char或long)。指向用户空间的指针永远不应该直接取消 引用:应该使用这些程序复制数据。两者都返回 ``-EFAULT`` 或 0。h](h)}(h:c:func:`put_user()`h]j)}(hj h]h put_user()}(hj hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjput_useruh1hhhhKhj ubh 和 }(hj hhhNhNubh)}(h:c:func:`get_user()`h]j)}(hj h]h get_user()}(hj hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjget_useruh1hhhhKhj ubh 用于从用户空间中获取和向用户空 间中传出单个值(如int、char或long)。指向用户空间的指针永远不应该直接取消 引用:应该使用这些程序复制数据。两者都返回 }(hj hhhNhNubj)}(h ``-EFAULT``h]h-EFAULT}(hj> hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 或 0。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(h|:c:func:`copy_to_user()` 和 :c:func:`copy_from_user()` 更通用:它们从/向用户 空间复制任意数量的数据。h](h)}(h:c:func:`copy_to_user()`h]j)}(hj\ h]hcopy_to_user()}(hj^ hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjZ ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj copy_to_useruh1hhhhKhjV ubh 和 }(hjV hhhNhNubh)}(h:c:func:`copy_from_user()`h]j)}(hj h]hcopy_from_user()}(hj hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj} ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjcopy_from_useruh1hhhhKhjV ubhE 更通用:它们从/向用户 空间复制任意数量的数据。}(hjV hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubj%)}(h与 :c:func:`put_user()` 和 :c:func:`get_user()` 不同,它们返回未复制的 数据量(即0仍然意味着成功)。h]h)}(h与 :c:func:`put_user()` 和 :c:func:`get_user()` 不同,它们返回未复制的 数据量(即0仍然意味着成功)。h](h与 }(hj hhhNhNubh)}(h:c:func:`put_user()`h]j)}(hj h]h put_user()}(hj hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjput_useruh1hhhhKhj ubh 和 }(hj hhhNhNubh)}(h:c:func:`get_user()`h]j)}(hj h]h get_user()}(hj hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjget_useruh1hhhhKhj ubhN 不同,它们返回未复制的 数据量(即0仍然意味着成功)。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1j$hj hhhhhNubh)}(h{【是的,这个讨厌的接口真心让我尴尬。火爆的口水仗大概每年都会发生。 —— Rusty Russell】h]h{【是的,这个讨厌的接口真心让我尴尬。火爆的口水仗大概每年都会发生。 —— Rusty Russell】}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(h这些函数可以隐式睡眠。它不应该在用户上下文之外调用(没有意义)、调用时禁用中断 或获得自旋锁。h]h这些函数可以隐式睡眠。它不应该在用户上下文之外调用(没有意义)、调用时禁用中断 或获得自旋锁。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubeh}(h]-copy-to-user-copy-from-user-get-user-put-userah ]h"];copy_to_user() / copy_from_user() / get_user() / put_user()ah$]h&]uh1jDhjhhhhhKubjE)}(hhh](jJ)}(h%:c:func:`kmalloc()`/:c:func:`kfree()`h](h)}(h:c:func:`kmalloc()`h]j)}(hj1 h]h kmalloc()}(hj3 hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj/ ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjkmallocuh1hhhhKhj+ ubh/}(hj+ hhhNhNubh)}(h:c:func:`kfree()`h]j)}(hjT h]hkfree()}(hjV hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjR ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjkfreeuh1hhhhKhj+ ubeh}(h]h ]h"]h$]h&]uh1jIhj( hhhhhMubh)}(h"定义于 ``include/linux/slab.h``h](h 定义于 }(hjw hhhNhNubj)}(h``include/linux/slab.h``h]hinclude/linux/slab.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjw ubeh}(h]h ]h"]h$]h&]uh1hhhhMhj( hhubh)}(h**[可能睡眠:见下]**h]j)}(hj h]h[可能睡眠:见下]}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]uh1hhhhMhj( hhubh)}(h这些函数用于动态请求指针对齐的内存块,类似用户空间中的malloc和free,但 :c:func:`kmalloc()` 需要额外的标志词。重要的值:h](he这些函数用于动态请求指针对齐的内存块,类似用户空间中的malloc和free,但 }(hj hhhNhNubh)}(h:c:func:`kmalloc()`h]j)}(hj h]h kmalloc()}(hj hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjkmallocuh1hhhhMhj ubh+ 需要额外的标志词。重要的值:}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj( hhubj)}(hhh](j)}(h``GFP_KERNEL`` 可以睡眠和交换以释放内存。只允许在用户上下文中使用,但这是分配内存最可靠 的方法。 h](j)}(h``GFP_KERNEL``h]j)}(hj h]h GFP_KERNEL}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]uh1jhhhM hj ubj)}(hhh]h)}(hy可以睡眠和交换以释放内存。只允许在用户上下文中使用,但这是分配内存最可靠 的方法。h]hy可以睡眠和交换以释放内存。只允许在用户上下文中使用,但这是分配内存最可靠 的方法。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM hj ubah}(h]h ]h"]h$]h&]uh1jhj ubeh}(h]h ]h"]h$]h&]uh1jhhhM hj ubj)}(h``GFP_ATOMIC`` 不会睡眠。较 ``GFP_KERNEL`` 更不可靠,但可以从中断上下文调用。你 **应该** 有一个很好的内存不足错误处理策略。 h](j)}(h``GFP_ATOMIC``h]j)}(hj h]h GFP_ATOMIC}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]uh1jhhhMhj ubj)}(hhh]h)}(h不会睡眠。较 ``GFP_KERNEL`` 更不可靠,但可以从中断上下文调用。你 **应该** 有一个很好的内存不足错误处理策略。h](h不会睡眠。较 }(hj4 hhhNhNubj)}(h``GFP_KERNEL``h]h GFP_KERNEL}(hj< hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj4 ubh8 更不可靠,但可以从中断上下文调用。你 }(hj4 hhhNhNubj)}(h **应该**h]h应该}(hjN hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj4 ubh4 有一个很好的内存不足错误处理策略。}(hj4 hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj1 ubah}(h]h ]h"]h$]h&]uh1jhj ubeh}(h]h ]h"]h$]h&]uh1jhhhMhj hhubj)}(hu``GFP_DMA`` 分配低于16MB的ISA DMA。如果你不知道那是什么,那你就不需要了。非常不可靠。 h](j)}(h ``GFP_DMA``h]j)}(hjx h]hGFP_DMA}(hjz hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjv ubah}(h]h ]h"]h$]h&]uh1jhhhMhjr ubj)}(hhh]h)}(hh分配低于16MB的ISA DMA。如果你不知道那是什么,那你就不需要了。非常不可靠。h]hh分配低于16MB的ISA DMA。如果你不知道那是什么,那你就不需要了。非常不可靠。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubah}(h]h ]h"]h$]h&]uh1jhjr ubeh}(h]h ]h"]h$]h&]uh1jhhhMhj hhubeh}(h]h ]h"]h$]h&]uh1jhj( hhhhhNubh)}(h如果您看到一个从无效上下文警告消息调用的睡眠的函数,那么您可能在没有 ``GFP_ATOMIC`` 的情况下从中断上下文调用了一个睡眠的分配函数。你必须立即修复, 快点!h](hg如果您看到一个从无效上下文警告消息调用的睡眠的函数,那么您可能在没有 }(hj hhhNhNubj)}(h``GFP_ATOMIC``h]h GFP_ATOMIC}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhh 的情况下从中断上下文调用了一个睡眠的分配函数。你必须立即修复, 快点!}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj( hhubh)}(hX5如果你要分配至少 ``PAGE_SIZE`` ( ``asm/page.h`` 或 ``asm/page_types.h`` ) 字节,请考虑使用 :c:func:`__get_free_pages()` ( ``include/linux/gfp.h`` )。 它采用顺序参数(0表示页面大小,1表示双页,2表示四页……)和与上述相同的内存 优先级标志字。h](h如果你要分配至少 }(hj hhhNhNubj)}(h ``PAGE_SIZE``h]h PAGE_SIZE}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh ( }(hj hhhNhNubj)}(h``asm/page.h``h]h asm/page.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 或 }(hj hhhNhNubj)}(h``asm/page_types.h``h]hasm/page_types.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh ) 字节,请考虑使用 }(hj hhhNhNubh)}(h:c:func:`__get_free_pages()`h]j)}(hj h]h__get_free_pages()}(hj hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj__get_free_pagesuh1hhhhMhj ubh ( }hj sbj)}(h``include/linux/gfp.h``h]hinclude/linux/gfp.h}(hj1 hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh )。 它采用顺序参数(0表示页面大小,1表示双页,2表示四页……)和与上述相同的内存 优先级标志字。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj( hhubh)}(hX如果分配的字节数超过一页,可以使用 :c:func:`vmalloc()` 。它将在内核映射中分 配虚拟内存。此块在物理内存中不是连续的,但是MMU(内存管理单元)使它看起来像 是为您准备好的连续空间(因此它只是看起来对cpu连续,对外部设备驱动程序则不然)。 如果您真的需要为一些奇怪的设备提供大量物理上连续的内存,那么您就会遇到问题: Linux对此支持很差,因为正在运行的内核中的内存碎片化会使它变得很困难。最好的 方法是在引导过程的早期通过 :c:func:`alloc_bootmem()` 函数分配。h](h4如果分配的字节数超过一页,可以使用 }(hjI hhhNhNubh)}(h:c:func:`vmalloc()`h]j)}(hjS h]h vmalloc()}(hjU hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjQ ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjvmallocuh1hhhhMhjI ubhX 。它将在内核映射中分 配虚拟内存。此块在物理内存中不是连续的,但是MMU(内存管理单元)使它看起来像 是为您准备好的连续空间(因此它只是看起来对cpu连续,对外部设备驱动程序则不然)。 如果您真的需要为一些奇怪的设备提供大量物理上连续的内存,那么您就会遇到问题: Linux对此支持很差,因为正在运行的内核中的内存碎片化会使它变得很困难。最好的 方法是在引导过程的早期通过 }(hjI hhhNhNubh)}(h:c:func:`alloc_bootmem()`h]j)}(hjv h]halloc_bootmem()}(hjx hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjt ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj alloc_bootmemuh1hhhhMhjI ubh 函数分配。}(hjI hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj( hhubh)}(hj在创建自己的常用对象缓存之前,请考虑使用 ``include/linux/slab.h`` 中的slab 缓存。h](h=在创建自己的常用对象缓存之前,请考虑使用 }(hj hhhNhNubj)}(h``include/linux/slab.h``h]hinclude/linux/slab.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 中的slab 缓存。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM$hj( hhubeh}(h] kmalloc-kfreeah ]h"]kmalloc()/kfree()ah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h:c:macro:`current`h]h)}(hj h]j)}(hj h]hcurrent}(hj hhhNhNubah}(h]h ](jjc-macroeh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypemacro refexplicitrefwarnjcurrentuh1hhhhM'hj ubah}(h]h ]h"]h$]h&]uh1jIhj hhhhhM(ubh)}(h#定义于 ``include/asm/current.h``h](h 定义于 }(hj hhhNhNubj)}(h``include/asm/current.h``h]hinclude/asm/current.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubeh}(h]h ]h"]h$]h&]uh1hhhhM*hj hhubh)}(hX此全局变量(其实是宏)包含指向当前任务结构(task structure)的指针,因此仅在 用户上下文中有效。例如,当进程进行系统调用时,这将指向调用进程的任务结构。 在中断上下文中不为空(**not NULL**)。h](h此全局变量(其实是宏)包含指向当前任务结构(task structure)的指针,因此仅在 用户上下文中有效。例如,当进程进行系统调用时,这将指向调用进程的任务结构。 在中断上下文中不为空(}(hj hhhNhNubj)}(h **not NULL**h]hnot NULL}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh)。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM,hj hhubeh}(h]currentah ]h"]currentah$]h&]uh1jDhjhhhhhM(ubjE)}(hhh](jJ)}(h%:c:func:`mdelay()`/:c:func:`udelay()`h](h)}(h:c:func:`mdelay()`h]j)}(hj=h]hmdelay()}(hj?hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj;ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjmdelayuh1hhhhM0hj7ubh/}(hj7hhhNhNubh)}(h:c:func:`udelay()`h]j)}(hj`h]hudelay()}(hjbhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj^ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjudelayuh1hhhhM0hj7ubeh}(h]h ]h"]h$]h&]uh1jIhj4hhhhhM1ubh)}(h=定义于 ``include/asm/delay.h`` / ``include/linux/delay.h``h](h 定义于 }(hjhhhNhNubj)}(h``include/asm/delay.h``h]hinclude/asm/delay.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh / }(hjhhhNhNubj)}(h``include/linux/delay.h``h]hinclude/linux/delay.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhM3hj4hhubh)}(h:c:func:`udelay()` 和 :c:func:`ndelay()` 函数可被用于小暂停。不要对它们使用 大的值,因为这样会导致溢出——帮助函数 :c:func:`mdelay()` 在这里很有用,或者 考虑 :c:func:`msleep()`。h](h)}(h:c:func:`udelay()`h]j)}(hjh]hudelay()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjudelayuh1hhhhM5hjubh 和 }(hjhhhNhNubh)}(h:c:func:`ndelay()`h]j)}(hjh]hndelay()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjndelayuh1hhhhM5hjubho 函数可被用于小暂停。不要对它们使用 大的值,因为这样会导致溢出——帮助函数 }(hjhhhNhNubh)}(h:c:func:`mdelay()`h]j)}(hjh]hmdelay()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjmdelayuh1hhhhM5hjubh$ 在这里很有用,或者 考虑 }(hjhhhNhNubh)}(h:c:func:`msleep()`h]j)}(hj h]hmsleep()}(hj"hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjmsleepuh1hhhhM5hjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM5hj4hhubeh}(h] mdelay-udelayah ]h"]mdelay()/udelay()ah$]h&]uh1jDhjhhhhhM1ubjE)}(hhh](jJ)}(h_:c:func:`cpu_to_be32()`/:c:func:`be32_to_cpu()`/:c:func:`cpu_to_le32()`/:c:func:`le32_to_cpu()`h](h)}(h:c:func:`cpu_to_be32()`h]j)}(hjXh]h cpu_to_be32()}(hjZhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjVubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj cpu_to_be32uh1hhhhM9hjRubh/}(hjRhhhNhNubh)}(h:c:func:`be32_to_cpu()`h]j)}(hj{h]h be32_to_cpu()}(hj}hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjyubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj be32_to_cpuuh1hhhhM9hjRubh/}hjRsbh)}(h:c:func:`cpu_to_le32()`h]j)}(hjh]h cpu_to_le32()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj cpu_to_le32uh1hhhhM9hjRubh/}hjRsbh)}(h:c:func:`le32_to_cpu()`h]j)}(hjh]h le32_to_cpu()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj le32_to_cpuuh1hhhhM9hjRubeh}(h]h ]h"]h$]h&]uh1jIhjOhhhhhM:ubh)}(h%定义于 ``include/asm/byteorder.h``h](h 定义于 }(hjhhhNhNubj)}(h``include/asm/byteorder.h``h]hinclude/asm/byteorder.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhM<hjOhhubh)}(hX:c:func:`cpu_to_be32()` 系列函数(其中“32”可以替换为64或16,“be”可以替换为 “le”)是在内核中进行字节序转换的常用方法:它们返回转换后的值。所有的变体也 提供反向转换函数: :c:func:`be32_to_cpu()` 等。h](h)}(h:c:func:`cpu_to_be32()`h]j)}(hjh]h cpu_to_be32()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj cpu_to_be32uh1hhhhM>hjubh 系列函数(其中“32”可以替换为64或16,“be”可以替换为 “le”)是在内核中进行字节序转换的常用方法:它们返回转换后的值。所有的变体也 提供反向转换函数: }(hjhhhNhNubh)}(h:c:func:`be32_to_cpu()`h]j)}(hj)h]h be32_to_cpu()}(hj+hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj'ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj be32_to_cpuuh1hhhhM>hjubh 等。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM>hjOhhubh)}(hX这些函数有两个主要的变体:指针变体,例如 :c:func:`cpu_to_be32p()` ,它获取 指向给定类型的指针,并返回转换后的值。另一个变体是“in-situ”系列,例如 :c:func:`cpu_to_be32s()` ,它转换指针引用的值,并返回void。h](h=这些函数有两个主要的变体:指针变体,例如 }(hjPhhhNhNubh)}(h:c:func:`cpu_to_be32p()`h]j)}(hjZh]hcpu_to_be32p()}(hj\hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjXubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj cpu_to_be32puh1hhhhMChjPubhv ,它获取 指向给定类型的指针,并返回转换后的值。另一个变体是“in-situ”系列,例如 }(hjPhhhNhNubh)}(h:c:func:`cpu_to_be32s()`h]j)}(hj}h]hcpu_to_be32s()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj{ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj cpu_to_be32suh1hhhhMChjPubh2 ,它转换指针引用的值,并返回void。}(hjPhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMChjOhhubeh}(h]/cpu-to-be32-be32-to-cpu-cpu-to-le32-le32-to-cpuah ]h"]7cpu_to_be32()/be32_to_cpu()/cpu_to_le32()/le32_to_cpu()ah$]h&]uh1jDhjhhhhhM:ubjE)}(hhh](jJ)}(h8:c:func:`local_irq_save()`/:c:func:`local_irq_restore()`h](h)}(h:c:func:`local_irq_save()`h]j)}(hjh]hlocal_irq_save()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjlocal_irq_saveuh1hhhhMGhjubh/}(hjhhhNhNubh)}(h:c:func:`local_irq_restore()`h]j)}(hjh]hlocal_irq_restore()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjlocal_irq_restoreuh1hhhhMGhjubeh}(h]h ]h"]h$]h&]uh1jIhjhhhhhMHubh)}(h&定义于 ``include/linux/irqflags.h``h](h 定义于 }(hjhhhNhNubj)}(h``include/linux/irqflags.h``h]hinclude/linux/irqflags.h}(hjhhhNhNubah}(#h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhMJhjhhubh)}(hX这些程序禁用本地CPU上的硬中断,并还原它们。它们是可重入的;在其一个 ``unsigned long flags`` 参数中保存以前的状态。如果您知道中断已启用,那么可 直接使用 :c:func:`local_irq_disable()` 和 :c:func:`local_irq_enable()`。h](hd这些程序禁用本地CPU上的硬中断,并还原它们。它们是可重入的;在其一个 }(hjhhhNhNubj)}(h``unsigned long flags``h]hunsigned long flags}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhZ 参数中保存以前的状态。如果您知道中断已启用,那么可 直接使用 }(hjhhhNhNubh)}(h:c:func:`local_irq_disable()`h]j)}(hj3h]hlocal_irq_disable()}(hj5hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj1ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjlocal_irq_disableuh1hhhhMMhjubh 和 }(hjhhhNhNubh)}(h:c:func:`local_irq_enable()`h]j)}(hjVh]hlocal_irq_enable()}(hjXhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjTubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjlocal_irq_enableuh1hhhhMMhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMMhjhhubhtarget)}(h.. _local_bh_disable_zh:h]h}(h]h ]h"]h$]h&]refidlocal-bh-disable-zhuh1j}hM\hjhhhhubeh}(h] local-irq-save-local-irq-restoreah ]h"]$local_irq_save()/local_irq_restore()ah$]h&]uh1jDhjhhhhhMHubjE)}(hhh](jJ)}(h8:c:func:`local_bh_disable()`/:c:func:`local_bh_enable()`h](h)}(h:c:func:`local_bh_disable()`h]j)}(hjh]hlocal_bh_disable()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjlocal_bh_disableuh1hhhhMShjubh/}(hjhhhNhNubh)}(h:c:func:`local_bh_enable()`h]j)}(hjh]hlocal_bh_enable()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjlocal_bh_enableuh1hhhhMShjubeh}(h]h ]h"]h$]h&]uh1jIhjhhhhhMTubh)}(h)定义于 ``include/linux/bottom_half.h``h](h 定义于 }(hjhhhNhNubj)}(h``include/linux/bottom_half.h``h]hinclude/linux/bottom_half.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhMVhjhhubh)}(h这些程序禁用本地CPU上的软中断,并还原它们。它们是可重入的;如果之前禁用了 软中断,那么在调用这对函数之后仍然会禁用它们。它们阻止软中断和子任务在当前 CPU上运行。h]h这些程序禁用本地CPU上的软中断,并还原它们。它们是可重入的;如果之前禁用了 软中断,那么在调用这对函数之后仍然会禁用它们。它们阻止软中断和子任务在当前 CPU上运行。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMYhjhhubeh}(h]( local-bh-disable-local-bh-enablejeh ]h"]($local_bh_disable()/local_bh_enable()local_bh_disable_zheh$]h&]uh1jDhjhhhhhMTexpect_referenced_by_name}jjsexpect_referenced_by_id}jjsubjE)}(hhh](jJ)}(h:c:func:`smp_processor_id()`h]h)}(hjh]j)}(hjh]hsmp_processor_id()}(hj#hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjsmp_processor_iduh1hhhhM]hjubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhM^ubh)}(h!定义于 ``include/linux/smp.h``h](h 定义于 }(hjDhhhNhNubj)}(h``include/linux/smp.h``h]hinclude/linux/smp.h}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjDubeh}(h]h ]h"]h$]h&]uh1hhhhM`hjhhubh)}(h:c:func:`get_cpu()` 禁用抢占(这样您就不会突然移动到另一个cpu)并返回当前 处理器号,介于0和 ``NR_CPUS`` 之间。请注意,CPU编号不一定是连续的。完成后, 使用 :c:func:`put_cpu()` 再次返回。h](h)}(h:c:func:`get_cpu()`h]j)}(hjfh]h get_cpu()}(hjhhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjdubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjget_cpuuh1hhhhMbhj`ubhj 禁用抢占(这样您就不会突然移动到另一个cpu)并返回当前 处理器号,介于0和 }(hj`hhhNhNubj)}(h ``NR_CPUS``h]hNR_CPUS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj`ubhK 之间。请注意,CPU编号不一定是连续的。完成后, 使用 }(hj`hhhNhNubh)}(h:c:func:`put_cpu()`h]j)}(hjh]h put_cpu()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjput_cpuuh1hhhhMbhj`ubh 再次返回。}(hj`hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMbhjhhubh)}(h如果您知道您不能被另一个任务抢占(即您处于中断上下文中,或已禁用抢占),您 可以使用 :c:func:`smp_processor_id()`。h](h}如果您知道您不能被另一个任务抢占(即您处于中断上下文中,或已禁用抢占),您 可以使用 }(hjhhhNhNubh)}(h:c:func:`smp_processor_id()`h]j)}(hjh]hsmp_processor_id()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjsmp_processor_iduh1hhhhMfhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMfhjhhubeh}(h]smp-processor-idah ]h"]smp_processor_id()ah$]h&]uh1jDhjhhhhhM^ubjE)}(hhh](jJ)}(h$``__init``/``__exit``/``__initdata``h](j)}(h ``__init``h]h__init}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh/}(hjhhhNhNubj)}(h ``__exit``h]h__exit}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh/}hjsbj)}(h``__initdata``h]h __initdata}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jIhjhhhhhMjubh)}(h#定义于 ``include/linux/init.h``h](h 定义于 }(hj:hhhNhNubj)}(h``include/linux/init.h``h]hinclude/linux/init.h}(hjBhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj:ubeh}(h]h ]h"]h$]h&]uh1hhhhMlhjhhubh)}(hX引导之后,内核释放一个特殊的部分;用 ``__init`` 标记的函数和用 ``__initdata`` 标记的数据结构在引导完成后被丢弃:同样地,模块在初始化后丢弃此内存。 ``__exit`` 用于声明只在退出时需要的函数:如果此文件未编译为模块,则该函数将 被删除。请参阅头文件以使用。请注意,使用 :c:func:`EXPORT_SYMBOL()` 或 :c:func:`EXPORT_SYMBOL_GPL()` 将标记为 ``__init`` 的函数导出到模块是没有意义 的——这将出问题。h](h7引导之后,内核释放一个特殊的部分;用 }(hjVhhhNhNubj)}(h ``__init``h]h__init}(hj^hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjVubh 标记的函数和用 }(hjVhhhNhNubj)}(h``__initdata``h]h __initdata}(hjphhhNhNubah}(h]h ]h"]h$]h&]uh1jhjVubhh 标记的数据结构在引导完成后被丢弃:同样地,模块在初始化后丢弃此内存。 }(hjVhhhNhNubj)}(h ``__exit``h]h__exit}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjVubh 用于声明只在退出时需要的函数:如果此文件未编译为模块,则该函数将 被删除。请参阅头文件以使用。请注意,使用 }(hjVhhhNhNubh)}(h:c:func:`EXPORT_SYMBOL()`h]j)}(hjh]hEXPORT_SYMBOL()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj EXPORT_SYMBOLuh1hhhhMnhjVubh 或 }(hjVhhhNhNubh)}(h:c:func:`EXPORT_SYMBOL_GPL()`h]j)}(hjh]hEXPORT_SYMBOL_GPL()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjEXPORT_SYMBOL_GPLuh1hhhhMnhjVubh 将标记为 }(hjVhhhNhNubj)}(h ``__init``h]h__init}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjVubhD 的函数导出到模块是没有意义 的——这将出问题。}(hjVhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMnhjhhubeh}(h]init-exit-initdataah ]h"]__init/__exit/__initdataah$]h&]uh1jDhjhhhhhMjubjE)}(hhh](jJ)}(h.:c:func:`__initcall()`/:c:func:`module_init()`h](h)}(h:c:func:`__initcall()`h]j)}(hjh]h __initcall()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj __initcalluh1hhhhMvhjubh/}(hjhhhNhNubh)}(h:c:func:`module_init()`h]j)}(hj&h]h module_init()}(hj(hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj$ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj module_inituh1hhhhMvhjubeh}(h]h ]h"]h$]h&]uh1jIhjhhhhhMwubh)}(h@定义于 ``include/linux/init.h`` / ``include/linux/module.h``h](h 定义于 }(hjIhhhNhNubj)}(h``include/linux/init.h``h]hinclude/linux/init.h}(hjQhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjIubh / }(hjIhhhNhNubj)}(h``include/linux/module.h``h]hinclude/linux/module.h}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1jhjIubeh}(h]h ]h"]h$]h&]uh1hhhhMyhjhhubh)}(h内核的许多部分都作为模块(内核的可动态加载部分)良好服务。使用 :c:func:`module_init()` 和 :c:func:`module_exit()` 宏可以简化代码编写,无需 ``#ifdef`` ,即可以作为模块运行或内置在内核中。h](h^内核的许多部分都作为模块(内核的可动态加载部分)良好服务。使用 }(hjwhhhNhNubh)}(h:c:func:`module_init()`h]j)}(hjh]h module_init()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj module_inituh1hhhhM{hjwubh 和 }(hjwhhhNhNubh)}(h:c:func:`module_exit()`h]j)}(hjh]h module_exit()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj module_exituh1hhhhM{hjwubh& 宏可以简化代码编写,无需 }(hjwhhhNhNubj)}(h ``#ifdef``h]h#ifdef}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjwubh7 ,即可以作为模块运行或内置在内核中。}(hjwhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM{hjhhubh)}(hX:c:func:`module_init()` 宏定义在模块插入时(如果文件编译为模块)或在引导时 调用哪个函数:如果文件未编译为模块,:c:func:`module_init()` 宏将等效于 :c:func:`__initcall()` ,它通过链接器的魔力确保在引导时调用该函数。h](h)}(h:c:func:`module_init()`h]j)}(hjh]h module_init()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj module_inituh1hhhhMhjubh 宏定义在模块插入时(如果文件编译为模块)或在引导时 调用哪个函数:如果文件未编译为模块,}(hjhhhNhNubh)}(h:c:func:`module_init()`h]j)}(hjh]h module_init()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj module_inituh1hhhhMhjubh 宏将等效于 }(hjhhhNhNubh)}(h:c:func:`__initcall()`h]j)}(hj)h]h __initcall()}(hj+hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj'ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj __initcalluh1hhhhMhjubhC ,它通过链接器的魔力确保在引导时调用该函数。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h该函数可以返回一个错误值,以导致模块加载失败(不幸的是,如果将模块编译到内核 中,则此操作无效)。此函数在启用中断的用户上下文中调用,因此可以睡眠。h]h该函数可以返回一个错误值,以导致模块加载失败(不幸的是,如果将模块编译到内核 中,则此操作无效)。此函数在启用中断的用户上下文中调用,因此可以睡眠。}(hjPhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]initcall-module-initah ]h"]__initcall()/module_init()ah$]h&]uh1jDhjhhhhhMwubjE)}(hhh](jJ)}(h:c:func:`module_exit()`h]h)}(hjkh]j)}(hjkh]h module_exit()}(hjphhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjmubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj module_exituh1hhhhMhjiubah}(h]h ]h"]h$]h&]uh1jIhjfhhhhhMubh)}(h%定义于 ``include/linux/module.h``h](h 定义于 }(hjhhhNhNubj)}(h``include/linux/module.h``h]hinclude/linux/module.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhMhjfhhubh)}(hX这个宏定义了在模块删除时要调用的函数(如果是编译到内核中的文件,则无用武之地)。 只有在模块使用计数到零时才会调用它。这个函数也可以睡眠,但不能失败:当它返回 时,所有的东西都必须清理干净。h]hX这个宏定义了在模块删除时要调用的函数(如果是编译到内核中的文件,则无用武之地)。 只有在模块使用计数到零时才会调用它。这个函数也可以睡眠,但不能失败:当它返回 时,所有的东西都必须清理干净。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjfhhubh)}(hn注意,这个宏是可选的:如果它不存在,您的模块将不可移除(除非 ``rmmod -f`` )。h](h[注意,这个宏是可选的:如果它不存在,您的模块将不可移除(除非 }(hjhhhNhNubj)}(h ``rmmod -f``h]hrmmod -f}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh )。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjfhhubeh}(h] module-exitah ]h"] module_exit()ah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h1:c:func:`try_module_get()`/:c:func:`module_put()`h](h)}(h:c:func:`try_module_get()`h]j)}(hjh]htry_module_get()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjtry_module_getuh1hhhhMhjubh/}(hjhhhNhNubh)}(h:c:func:`module_put()`h]j)}(hjh]h module_put()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj module_putuh1hhhhMhjubeh}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(h$定义于 ``include/linux/module.h``h](h 定义于 }(hj2hhhNhNubj)}(h``include/linux/module.h``h]hinclude/linux/module.h}(hj:hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj2ubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hX这些函数会操作模块使用计数,以防止删除(如果另一个模块使用其导出的符号之一, 则无法删除模块,参见下文)。在调用模块代码之前,您应该在该模块上调用 :c:func:`try_module_get()` :若失败,那么该模块将被删除,您应该将其视为不存在。 若成功,您就可以安全地进入模块,并在完成后调用模块 :c:func:`module_put()` 。h](h这些函数会操作模块使用计数,以防止删除(如果另一个模块使用其导出的符号之一, 则无法删除模块,参见下文)。在调用模块代码之前,您应该在该模块上调用 }(hjNhhhNhNubh)}(h:c:func:`try_module_get()`h]j)}(hjXh]htry_module_get()}(hjZhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjVubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjtry_module_getuh1hhhhMhjNubh :若失败,那么该模块将被删除,您应该将其视为不存在。 若成功,您就可以安全地进入模块,并在完成后调用模块 }(hjNhhhNhNubh)}(h:c:func:`module_put()`h]j)}(hj{h]h module_put()}(hj}hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjyubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj module_putuh1hhhhMhjNubh 。}(hjNhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h大多数可注册结构体都有所有者字段,例如在 :c:type:`struct file_operations ` 结构体中,此字段应设置为 宏 ``THIS_MODULE`` 。h](h=大多数可注册结构体都有所有者字段,例如在 }(hjhhhNhNubh)}(h2:c:type:`struct file_operations `h]j)}(hjh]hstruct file_operations}(hjhhhNhNubah}(h]h ](jjc-typeeh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypetype refexplicitrefwarnjfile_operationsuh1hhhhMhjubh* 结构体中,此字段应设置为 宏 }(hjhhhNhNubj)}(h``THIS_MODULE``h]h THIS_MODULE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]try-module-get-module-putah ]h"]try_module_get()/module_put()ah$]h&]uh1jDhjhhhhhMubeh}(h]id7ah ]h"]常用函数/程序ah$]h&]uh1jDhjFhhhhhKubjE)}(hhh](jJ)}(h%等待队列 ``include/linux/wait.h``h](h 等待队列 }(hjhhhNhNubj)}(h``include/linux/wait.h``h]hinclude/linux/wait.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(h **[睡眠]**h]j)}(hjh]h[睡眠]}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hX0等待队列用于等待某程序在条件为真时唤醒另一程序。必须小心使用,以确保没有竞争 条件。先声明一个 :c:type:`wait_queue_head_t` ,然后对希望等待该条件的进程声明 一个关于它们自己的 :c:type:`wait_queue_entry_t` ,并将其放入队列中。h](h等待队列用于等待某程序在条件为真时唤醒另一程序。必须小心使用,以确保没有竞争 条件。先声明一个 }(hj+hhhNhNubh)}(h:c:type:`wait_queue_head_t`h]j)}(hj5h]hwait_queue_head_t}(hj7hhhNhNubah}(h]h ](jjc-typeeh"]h$]h&]uh1jhj3ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypetype refexplicitrefwarnjwait_queue_head_tuh1hhhhMhj+ubhN ,然后对希望等待该条件的进程声明 一个关于它们自己的 }(hj+hhhNhNubh)}(h:c:type:`wait_queue_entry_t`h]j)}(hjXh]hwait_queue_entry_t}(hjZhhhNhNubah}(h]h ](jjc-typeeh"]h$]h&]uh1jhjVubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypetype refexplicitrefwarnjwait_queue_entry_tuh1hhhhMhj+ubh ,并将其放入队列中。}(hj+hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubjE)}(hhh](jJ)}(h声明h]h声明}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(h使用 :c:func:`DECLARE_WAIT_QUEUE_HEAD()` 宏声明一个 ``wait_queue_head_t`` , 或者在初始化代码中使用 :c:func:`init_waitqueue_head()` 程序。h](h使用 }(hjhhhNhNubh)}(h#:c:func:`DECLARE_WAIT_QUEUE_HEAD()`h]j)}(hjh]hDECLARE_WAIT_QUEUE_HEAD()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjDECLARE_WAIT_QUEUE_HEADuh1hhhhMhjubh 宏声明一个 }(hjhhhNhNubj)}(h``wait_queue_head_t``h]hwait_queue_head_t}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh' , 或者在初始化代码中使用 }(hjhhhNhNubh)}(h:c:func:`init_waitqueue_head()`h]j)}(hjh]hinit_waitqueue_head()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjinit_waitqueue_headuh1hhhhMhjubh 程序。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id8ah ]h"]声明ah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h排队h]h排队}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(hX将自己放在等待队列中相当复杂,因为你必须在检查条件之前将自己放入队列中。有一 个宏可以来执行此操作: :c:func:`wait_event_interruptible()` ( ``include/linux/wait.h`` )第一个参数是等待队列头,第二个参数是计算的表达 式;当该表达式为true时宏返回0,或者在接收到信号时返回 ``-ERESTARTSYS`` 。 :c:func:`wait_event()` 版本会忽略信号。h](h将自己放在等待队列中相当复杂,因为你必须在检查条件之前将自己放入队列中。有一 个宏可以来执行此操作: }(hjhhhNhNubh)}(h$:c:func:`wait_event_interruptible()`h]j)}(hjh]hwait_event_interruptible()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjwait_event_interruptibleuh1hhhhMhjubh ( }(hjhhhNhNubj)}(h``include/linux/wait.h``h]hinclude/linux/wait.h}(hj:hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh )第一个参数是等待队列头,第二个参数是计算的表达 式;当该表达式为true时宏返回0,或者在接收到信号时返回 }(hjhhhNhNubj)}(h``-ERESTARTSYS``h]h -ERESTARTSYS}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 。 }(hjhhhNhNubh)}(h:c:func:`wait_event()`h]j)}(hj`h]h wait_event()}(hjbhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj^ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj wait_eventuh1hhhhMhjubh 版本会忽略信号。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id9ah ]h"]排队ah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h唤醒排队任务h]h唤醒排队任务}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(hX 调用 :c:func:`wake_up()` ( ``include/linux/wait.h`` ),它将唤醒队列中的所有 进程。例外情况:如果有一个进程设置了 ``TASK_EXCLUSIVE`` ,队列的其余部分将不 会被唤醒。这个基本函数的其他变体也可以在同一个头文件中使用。h](h调用 }(hjhhhNhNubh)}(h:c:func:`wake_up()`h]j)}(hjh]h wake_up()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjwake_upuh1hhhhMhjubh ( }(hjhhhNhNubj)}(h``include/linux/wait.h``h]hinclude/linux/wait.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh] ),它将唤醒队列中的所有 进程。例外情况:如果有一个进程设置了 }(hjhhhNhNubj)}(h``TASK_EXCLUSIVE``h]hTASK_EXCLUSIVE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhz ,队列的其余部分将不 会被唤醒。这个基本函数的其他变体也可以在同一个头文件中使用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id10ah ]h"]唤醒排队任务ah$]h&]uh1jDhjhhhhhMubeh}(h]include-linux-wait-hah ]h"]!等待队列 include/linux/wait.hah$]h&]uh1jDhjFhhhhhMubjE)}(hhh](jJ)}(h 原子操作h]h 原子操作}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(hX 某些操作在所有平台上都有保证。第一类为操作 :c:type:`atomic_t` ( ``include/asm/atomic.h`` )的函数;它包含一个有符号整数(至少32位长), 您必须使用这些函数来操作或读取 :c:type:`atomic_t` 变量。 :c:func:`atomic_read()` 和 :c:func:`atomic_set()` 获取并设置计数器,还有 :c:func:`atomic_add()` ,:c:func:`atomic_sub()` ,:c:func:`atomic_inc()` , :c:func:`atomic_dec()` 和 :c:func:`atomic_dec_and_test()` (如果递减为零, 则返回true)。h](h@某些操作在所有平台上都有保证。第一类为操作 }(hjhhhNhNubh)}(h:c:type:`atomic_t`h]j)}(hj h]hatomic_t}(hj"hhhNhNubah}(h]h ](jjc-typeeh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypetype refexplicitrefwarnjatomic_tuh1hhhhMhjubh ( }(hjhhhNhNubj)}(h``include/asm/atomic.h``h]hinclude/asm/atomic.h}(hjAhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubht )的函数;它包含一个有符号整数(至少32位长), 您必须使用这些函数来操作或读取 }(hjhhhNhNubh)}(h:c:type:`atomic_t`h]j)}(hjUh]hatomic_t}(hjWhhhNhNubah}(h]h ](jjc-typeeh"]h$]h&]uh1jhjSubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypetype refexplicitrefwarnjatomic_tuh1hhhhMhjubh 变量。 }(hjhhhNhNubh)}(h:c:func:`atomic_read()`h]j)}(hjxh]h atomic_read()}(hjzhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjvubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj atomic_readuh1hhhhMhjubh 和 }(hjhhhNhNubh)}(h:c:func:`atomic_set()`h]j)}(hjh]h atomic_set()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj atomic_setuh1hhhhMhjubh# 获取并设置计数器,还有 }(hjhhhNhNubh)}(h:c:func:`atomic_add()`h]j)}(hjh]h atomic_add()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj atomic_adduh1hhhhMhjubh ,}(hjhhhNhNubh)}(h:c:func:`atomic_sub()`h]j)}(hjh]h atomic_sub()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj atomic_subuh1hhhhMhjubh ,}hjsbh)}(h:c:func:`atomic_inc()`h]j)}(hjh]h atomic_inc()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj atomic_incuh1hhhhMhjubh , }(hjhhhNhNubh)}(h:c:func:`atomic_dec()`h]j)}(hj'h]h atomic_dec()}(hj)hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj%ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj atomic_decuh1hhhhMhjubh 和 }hjsbh)}(h:c:func:`atomic_dec_and_test()`h]j)}(hjJh]hatomic_dec_and_test()}(hjLhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjHubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjatomic_dec_and_testuh1hhhhMhjubh- (如果递减为零, 则返回true)。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h=是的。它在原子变量为零时返回true(即!=0)。h]h=是的。它在原子变量为零时返回true(即!=0)。}(hjqhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hW请注意,这些函数比普通的算术运算速度慢,因此不应过度使用。h]hW请注意,这些函数比普通的算术运算速度慢,因此不应过度使用。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hX 第二类原子操作是在 ``unsigned long`` ( ``include/linux/bitops.h`` )上的 原子位操作。这些操作通常采用指向位模式(bit pattern)的指针,第0位是最低有效 位。:c:func:`set_bit()`,:c:func:`clear_bit()` 和 :c:func:`change_bit()` 设置、 清除和更改给定位。:c:func:`test_and_set_bit()` ,:c:func:`test_and_clear_bit()` 和 :c:func:`test_and_change_bit()` 执行相同的操作,但如果之前设置了位,则返回 true;这些对于原子设置标志特别有用。h](h第二类原子操作是在 }(hjhhhNhNubj)}(h``unsigned long``h]h unsigned long}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh ( }(hjhhhNhNubj)}(h``include/linux/bitops.h``h]hinclude/linux/bitops.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh~ )上的 原子位操作。这些操作通常采用指向位模式(bit pattern)的指针,第0位是最低有效 位。}(hjhhhNhNubh)}(h:c:func:`set_bit()`h]j)}(hjh]h set_bit()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjset_bituh1hhhhMhjubh,}(hjhhhNhNubh)}(h:c:func:`clear_bit()`h]j)}(hjh]h clear_bit()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj clear_bituh1hhhhMhjubh 和 }(hjhhhNhNubh)}(h:c:func:`change_bit()`h]j)}(hjh]h change_bit()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj change_bituh1hhhhMhjubh& 设置、 清除和更改给定位。}(hjhhhNhNubh)}(h:c:func:`test_and_set_bit()`h]j)}(hj$h]htest_and_set_bit()}(hj&hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj"ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjtest_and_set_bituh1hhhhMhjubh ,}(hjhhhNhNubh)}(h:c:func:`test_and_clear_bit()`h]j)}(hjGh]htest_and_clear_bit()}(hjIhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjEubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjtest_and_clear_bituh1hhhhMhjubh 和 }(hjhhhNhNubh)}(h:c:func:`test_and_change_bit()`h]j)}(hjjh]htest_and_change_bit()}(hjlhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjhubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjtest_and_change_bituh1hhhhMhjubhu 执行相同的操作,但如果之前设置了位,则返回 true;这些对于原子设置标志特别有用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h可以使用大于 ``BITS_PER_LONG`` 位的位索引调用这些操作。但结果在大端序平台上 不太正常,所以最好不要这样做。h](h可以使用大于 }(hjhhhNhNubj)}(h``BITS_PER_LONG``h]h BITS_PER_LONG}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhq 位的位索引调用这些操作。但结果在大端序平台上 不太正常,所以最好不要这样做。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id11ah ]h"] 原子操作ah$]h&]uh1jDhjFhhhhhMubjE)}(hhh](jJ)}(h符号h]h符号}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(hX1在内核内部,正常的链接规则仍然适用(即除非用static关键字将符号声明为文件范围, 否则它可以在内核中的任何位置使用)。但是对于模块,会保留一个特殊可导出符号表, 该表将入口点限制为内核内部。模块也可以导出符号。h]hX1在内核内部,正常的链接规则仍然适用(即除非用static关键字将符号声明为文件范围, 否则它可以在内核中的任何位置使用)。但是对于模块,会保留一个特殊可导出符号表, 该表将入口点限制为内核内部。模块也可以导出符号。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubjE)}(hhh](jJ)}(h:c:func:`EXPORT_SYMBOL()`h]h)}(hjh]j)}(hjh]hEXPORT_SYMBOL()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj EXPORT_SYMBOLuh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(h$定义于 ``include/linux/export.h``h](h 定义于 }(hjhhhNhNubj)}(h``include/linux/export.h``h]hinclude/linux/export.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hW这是导出符号的经典方法:动态加载的模块将能够正常使用符号。h]hW这是导出符号的经典方法:动态加载的模块将能够正常使用符号。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h] export-symbolah ]h"]export_symbol()ah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h:c:func:`EXPORT_SYMBOL_GPL()`h]h)}(hj:h]j)}(hj:h]hEXPORT_SYMBOL_GPL()}(hj?hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj<ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjEXPORT_SYMBOL_GPLuh1hhhhMhj8ubah}(h]h ]h"]h$]h&]uh1jIhj5hhhhhMubh)}(h$定义于 ``include/linux/export.h``h](h 定义于 }(hj`hhhNhNubj)}(h``include/linux/export.h``h]hinclude/linux/export.h}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj`ubeh}(h]h ]h"]h$]h&]uh1hhhhMhj5hhubh)}(hX类似于 :c:func:`EXPORT_SYMBOL()`,只是 :c:func:`EXPORT_SYMBOL_GPL()` 导出的 符号只能由具有由 :c:func:`MODULE_LICENSE()` 指定GPL兼容许可证的模块看到。这 意味着此函数被认为是一个内部实现问题,而不是一个真正的接口。一些维护人员和 开发人员在添加一些新的API或功能时可能却需要导出 EXPORT_SYMBOL_GPL()。h](h 类似于 }(hj|hhhNhNubh)}(h:c:func:`EXPORT_SYMBOL()`h]j)}(hjh]hEXPORT_SYMBOL()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj EXPORT_SYMBOLuh1hhhhMhj|ubh ,只是 }(hj|hhhNhNubh)}(h:c:func:`EXPORT_SYMBOL_GPL()`h]j)}(hjh]hEXPORT_SYMBOL_GPL()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjEXPORT_SYMBOL_GPLuh1hhhhMhj|ubh$ 导出的 符号只能由具有由 }(hj|hhhNhNubh)}(h:c:func:`MODULE_LICENSE()`h]j)}(hjh]hMODULE_LICENSE()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjMODULE_LICENSEuh1hhhhMhj|ubh 指定GPL兼容许可证的模块看到。这 意味着此函数被认为是一个内部实现问题,而不是一个真正的接口。一些维护人员和 开发人员在添加一些新的API或功能时可能却需要导出 EXPORT_SYMBOL_GPL()。}(hj|hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj5hhubeh}(h]export-symbol-gplah ]h"]export_symbol_gpl()ah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h:c:func:`EXPORT_SYMBOL_NS()`h]h)}(hjh]j)}(hjh]hEXPORT_SYMBOL_NS()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjEXPORT_SYMBOL_NSuh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(h$定义于 ``include/linux/export.h``h](h 定义于 }(hj&hhhNhNubj)}(h``include/linux/export.h``h]hinclude/linux/export.h}(hj.hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj&ubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h这是 ``EXPORT_SYMBOL()`` 的变体,允许指定符号命名空间。符号名称空间记录于 Documentation/core-api/symbol-namespaces.rst 。h](h这是 }(hjBhhhNhNubj)}(h``EXPORT_SYMBOL()``h]hEXPORT_SYMBOL()}(hjJhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjBubhz 的变体,允许指定符号命名空间。符号名称空间记录于 Documentation/core-api/symbol-namespaces.rst 。}(hjBhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]export-symbol-nsah ]h"]export_symbol_ns()ah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h :c:func:`EXPORT_SYMBOL_NS_GPL()`h]h)}(hjoh]j)}(hjoh]hEXPORT_SYMBOL_NS_GPL()}(hjthhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjqubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjEXPORT_SYMBOL_NS_GPLuh1hhhhMhjmubah}(h]h ]h"]h$]h&]uh1jIhjjhhhhhMubh)}(h$定义于 ``include/linux/export.h``h](h 定义于 }(hjhhhNhNubj)}(h``include/linux/export.h``h]hinclude/linux/export.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhMhjjhhubh)}(h这是 ``EXPORT_SYMBOL_GPL()`` 的变体,允许指定符号命名空间。符号名称空间记录于 Documentation/core-api/symbol-namespaces.rst 。h](h这是 }(hjhhhNhNubj)}(h``EXPORT_SYMBOL_GPL()``h]hEXPORT_SYMBOL_GPL()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhz 的变体,允许指定符号命名空间。符号名称空间记录于 Documentation/core-api/symbol-namespaces.rst 。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjjhhubeh}(h]export-symbol-ns-gplah ]h"]export_symbol_ns_gpl()ah$]h&]uh1jDhjhhhhhMubeh}(h]id12ah ]h"]符号ah$]h&]uh1jDhjFhhhhhMubjE)}(hhh](jJ)}(h程序与惯例h]h程序与惯例}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubjE)}(hhh](jJ)}(h%双向链表 ``include/linux/list.h``h](h 双向链表 }(hjhhhNhNubj)}(h``include/linux/list.h``h]hinclude/linux/list.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(h内核头文件中曾经有三组链表程序,但这一组是赢家。如果你对一个单链表没有特别迫切的 需求,那么这是一个不错的选择。h]h内核头文件中曾经有三组链表程序,但这一组是赢家。如果你对一个单链表没有特别迫切的 需求,那么这是一个不错的选择。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h3通常 :c:func:`list_for_each_entry()` 很有用。h](h通常 }(hjhhhNhNubh)}(h:c:func:`list_for_each_entry()`h]j)}(hj)h]hlist_for_each_entry()}(hj+hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhj'ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjlist_for_each_entryuh1hhhhM hjubh 很有用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM hjhhubeh}(h]include-linux-list-hah ]h"]!双向链表 include/linux/list.hah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h返回值惯例h]h返回值惯例}(hj[hhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjXhhhhhM ubh)}(h对于在用户上下文中调用的代码,违背C语言惯例是很常见的,即返回0表示成功,返回 负错误值(例如 ``-EFAULT`` )表示失败。这在一开始可能是不直观的,但在内核中 相当普遍。h](h对于在用户上下文中调用的代码,违背C语言惯例是很常见的,即返回0表示成功,返回 负错误值(例如 }(hjihhhNhNubj)}(h ``-EFAULT``h]h-EFAULT}(hjqhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjiubhY )表示失败。这在一开始可能是不直观的,但在内核中 相当普遍。}(hjihhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjXhhubh)}(hX使用 :c:func:`ERR_PTR()` ( ``include/linux/err.h`` )将负错误值编码到指针中, 然后使用 :c:func:`IS_ERR()` 和 :c:func:`PTR_ERR()` 将其再取出:避免为错误值 使用单独的指针参数。挺讨厌的,但的确是个好方式。h](h使用 }(hjhhhNhNubh)}(h:c:func:`ERR_PTR()`h]j)}(hjh]h ERR_PTR()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjERR_PTRuh1hhhhMhjubh ( }(hjhhhNhNubj)}(h``include/linux/err.h``h]hinclude/linux/err.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh6 )将负错误值编码到指针中, 然后使用 }(hjhhhNhNubh)}(h:c:func:`IS_ERR()`h]j)}(hjh]hIS_ERR()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjIS_ERRuh1hhhhMhjubh 和 }(hjhhhNhNubh)}(h:c:func:`PTR_ERR()`h]j)}(hjh]h PTR_ERR()}(hjhhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnjPTR_ERRuh1hhhhMhjubhn 将其再取出:避免为错误值 使用单独的指针参数。挺讨厌的,但的确是个好方式。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjXhhubeh}(h]id14ah ]h"]返回值惯例ah$]h&]uh1jDhjhhhhhM ubjE)}(hhh](jJ)}(h 破坏编译h]h 破坏编译}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhMubh)}(hXLinus和其他开发人员有时会更改开发内核中的函数或结构体名称;这样做不仅是为了 让每个人都保持警惕,还反映了一个重大的更改(例如,不能再在打开中断的情况下 调用,或者执行额外的检查,或者不执行以前捕获的检查)。通常这会附带发送一个 相当全面的注释到相应的内核邮件列表中;请搜索存档以查看。简单地对文件进行全局 替换通常只会让事情变得 **更糟** 。h](hXLinus和其他开发人员有时会更改开发内核中的函数或结构体名称;这样做不仅是为了 让每个人都保持警惕,还反映了一个重大的更改(例如,不能再在打开中断的情况下 调用,或者执行额外的检查,或者不执行以前捕获的检查)。通常这会附带发送一个 相当全面的注释到相应的内核邮件列表中;请搜索存档以查看。简单地对文件进行全局 替换通常只会让事情变得 }(hj+hhhNhNubj)}(h **更糟**h]h更糟}(hj3hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj+ubh 。}(hj+hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id15ah ]h"] 破坏编译ah$]h&]uh1jDhjhhhhhMubjE)}(hhh](jJ)}(h初始化结构体成员h]h初始化结构体成员}(hjVhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjShhhhhM!ubh)}(h^初始化结构体的首选方法是使用指定的初始化器,如ISO C99所述。 例如::h]h]初始化结构体的首选方法是使用指定的初始化器,如ISO C99所述。 例如:}(hjdhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM#hjShhubj)}(hstatic struct block_device_operations opt_fops = { .open = opt_open, .release = opt_release, .ioctl = opt_ioctl, .check_media_change = opt_media_change, };h]hstatic struct block_device_operations opt_fops = { .open = opt_open, .release = opt_release, .ioctl = opt_ioctl, .check_media_change = opt_media_change, };}hjrsbah}(h]h ]h"]h$]h&]jjuh1jhhhM&hjShhubh)}(h这使得很容易查找(grep),并且可以清楚地看到设置了哪些结构字段。你应该这样做, 因为它看起来很酷。h]h这使得很容易查找(grep),并且可以清楚地看到设置了哪些结构字段。你应该这样做, 因为它看起来很酷。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM.hjShhubeh}(h]id16ah ]h"]初始化结构体成员ah$]h&]uh1jDhjhhhhhM!ubjE)}(hhh](jJ)}(h GNU 扩展h]h GNU 扩展}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjhhhhhM2ubh)}(hXHLinux内核中明确允许GNU扩展。请注意,由于缺乏通用性,一些更复杂的版本并没有 得到很好的支持,但以下内容被认为是标准的(有关更多详细信息,请参阅GCC info页 的“C 扩展”部分——是的,实际上是info页,手册页只是info中内容的简短摘要)。h]hXHLinux内核中明确允许GNU扩展。请注意,由于缺乏通用性,一些更复杂的版本并没有 得到很好的支持,但以下内容被认为是标准的(有关更多详细信息,请参阅GCC info页 的“C 扩展”部分——是的,实际上是info页,手册页只是info中内容的简短摘要)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM4hjhhubj)}(hhh](j)}(h 内联函数 h]h)}(h 内联函数h]h 内联函数}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM8hjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hK语句表达式(Statement expressions)(即({ 和 })结构)。 h]h)}(hI语句表达式(Statement expressions)(即({ 和 })结构)。h]hI语句表达式(Statement expressions)(即({ 和 })结构)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM:hjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h7声明函数/变量/类型的属性(__attribute__) h]h)}(h6声明函数/变量/类型的属性(__attribute__)h]h6声明函数/变量/类型的属性(__attribute__)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM=hjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(htypeof h]h)}(htypeofh]htypeof}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM?hj ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h零长度数组 h]h)}(h零长度数组h]h零长度数组}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMAhj ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h 宏变量 h]h)}(h 宏变量h]h 宏变量}(hj4 hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMChj0 ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h空指针运算 h]h)}(h空指针运算h]h空指针运算}(hjL hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMEhjH ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h+非常量(Non-Constant)初始化程序 h]h)}(h*非常量(Non-Constant)初始化程序h]h*非常量(Non-Constant)初始化程序}(hjd hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMGhj` ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h:汇编程序指令(在 arch/ 和 include/asm/ 之内) h]h)}(h9汇编程序指令(在 arch/ 和 include/asm/ 之内)h]h9汇编程序指令(在 arch/ 和 include/asm/ 之内)}(hj| hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMIhjx ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h$字符串函数名(__func__)。 h]h)}(h#字符串函数名(__func__)。h]h#字符串函数名(__func__)。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMKhj ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h__builtin_constant_p() h]h)}(h__builtin_constant_p()h]h__builtin_constant_p()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMMhj ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]jRjSuh1jhhhM8hjhhubh)}(h在内核中使用long long时要小心,gcc为其生成的代码非常糟糕:除法和乘法在i386上 不能工作,因为内核环境中缺少用于它的gcc运行时函数。h]h在内核中使用long long时要小心,gcc为其生成的代码非常糟糕:除法和乘法在i386上 不能工作,因为内核环境中缺少用于它的gcc运行时函数。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMOhjhhubeh}(h]gnuah ]h"] gnu 扩展ah$]h&]uh1jDhjhhhhhM2ubjE)}(hhh](jJ)}(hC++h]hC++}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jIhj hhhhhMSubh)}(h在内核中使用C++通常是个坏主意,因为内核不提供必要的运行时环境,并且不为其 测试包含文件。不过这仍然是可能的,但不建议。如果你真的想这么做,至少别用 异常处理(exceptions)。h]h在内核中使用C++通常是个坏主意,因为内核不提供必要的运行时环境,并且不为其 测试包含文件。不过这仍然是可能的,但不建议。如果你真的想这么做,至少别用 异常处理(exceptions)。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMUhj hhubeh}(h]cah ]h"]c++ah$]h&]uh1jDhjhhhhhMSubjE)}(hhh](jJ)}(h#ifh]h#if}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1jIhj!hhhhhMZubh)}(h通常认为,在头文件(或.c文件顶部)中使用宏来抽象函数比在源代码中使用“if”预 处理器语句更干净。h]h通常认为,在头文件(或.c文件顶部)中使用宏来抽象函数比在源代码中使用“if”预 处理器语句更干净。}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM\hj!hhubeh}(h]ifah ]h"]#ifah$]h&]uh1jDhjhhhhhMZubeh}(h]id13ah ]h"]程序与惯例ah$]h&]uh1jDhjFhhhhhMubjE)}(hhh](jJ)}(h把你的东西放进内核里h]h把你的东西放进内核里}(hj5!hhhNhNubah}(h]h ]h"]h$]h&]uh1jIhj2!hhhhhM`ubh)}(hN为了让你的东西更正式、补丁更整洁,还有一些工作要做:h]hN为了让你的东西更正式、补丁更整洁,还有一些工作要做:}(hjC!hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMbhj2!hhubj)}(hhh](j)}(hX搞清楚你修改的代码属于谁。查看源文件的根目录、 ``MAINTAINERS`` 文件以及 ``CREDITS`` 文件的最后一部分。你应该和此人协调,确保你没有重新发明轮子, 或者尝试一些已经被拒绝的东西。 确保你把你的名字和电子邮件地址放在你创建或修改的任何文件的顶部。当人们发 现一个缺陷,或者想要做出修改时,这是他们首先会看的地方。 h](h)}(h搞清楚你修改的代码属于谁。查看源文件的根目录、 ``MAINTAINERS`` 文件以及 ``CREDITS`` 文件的最后一部分。你应该和此人协调,确保你没有重新发明轮子, 或者尝试一些已经被拒绝的东西。h](hF搞清楚你修改的代码属于谁。查看源文件的根目录、 }(hjX!hhhNhNubj)}(h``MAINTAINERS``h]h MAINTAINERS}(hj`!hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjX!ubh 文件以及 }(hjX!hhhNhNubj)}(h ``CREDITS``h]hCREDITS}(hjr!hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjX!ubh 文件的最后一部分。你应该和此人协调,确保你没有重新发明轮子, 或者尝试一些已经被拒绝的东西。}(hjX!hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMdhjT!ubh)}(h确保你把你的名字和电子邮件地址放在你创建或修改的任何文件的顶部。当人们发 现一个缺陷,或者想要做出修改时,这是他们首先会看的地方。h]h确保你把你的名字和电子邮件地址放在你创建或修改的任何文件的顶部。当人们发 现一个缺陷,或者想要做出修改时,这是他们首先会看的地方。}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhhjT!ubeh}(h]h ]h"]h$]h&]uh1jhjQ!hhhhhNubj)}(hX通常你需要一个配置选项来支持你的内核编程。在适当的目录中编辑 ``Kconfig`` 。 配置语言很容易通过剪切和粘贴来使用,在 Documentation/kbuild/kconfig-language.rst 中有完整的文档。 在您对选项的描述中,请确保同时照顾到了专家用户和对此功能一无所知的用户。 在此说明任何不兼容和问题。结尾一定要写上“如有疑问,就选N”(或者是“Y”); 这是针对那些看不懂你在说什么的人的。 h](h)}(h通常你需要一个配置选项来支持你的内核编程。在适当的目录中编辑 ``Kconfig`` 。 配置语言很容易通过剪切和粘贴来使用,在 Documentation/kbuild/kconfig-language.rst 中有完整的文档。h](h[通常你需要一个配置选项来支持你的内核编程。在适当的目录中编辑 }(hj!hhhNhNubj)}(h ``Kconfig``h]hKconfig}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj!ubh 。 配置语言很容易通过剪切和粘贴来使用,在 Documentation/kbuild/kconfig-language.rst 中有完整的文档。}(hj!hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMkhj!ubh)}(hX在您对选项的描述中,请确保同时照顾到了专家用户和对此功能一无所知的用户。 在此说明任何不兼容和问题。结尾一定要写上“如有疑问,就选N”(或者是“Y”); 这是针对那些看不懂你在说什么的人的。h]hX在您对选项的描述中,请确保同时照顾到了专家用户和对此功能一无所知的用户。 在此说明任何不兼容和问题。结尾一定要写上“如有疑问,就选N”(或者是“Y”); 这是针对那些看不懂你在说什么的人的。}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMohj!ubeh}(h]h ]h"]h$]h&]uh1jhjQ!hhhhhNubj)}(h编辑 ``Makefile`` :配置变量在这里导出,因此通常你只需添加一行 “obj-$(CONFIG_xxx) += xxx.o”。语法记录在 Documentation/kbuild/makefiles.rst 。 h]h)}(h编辑 ``Makefile`` :配置变量在这里导出,因此通常你只需添加一行 “obj-$(CONFIG_xxx) += xxx.o”。语法记录在 Documentation/kbuild/makefiles.rst 。h](h编辑 }(hj!hhhNhNubj)}(h ``Makefile``h]hMakefile}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj!ubh :配置变量在这里导出,因此通常你只需添加一行 “obj-$(CONFIG_xxx) += xxx.o”。语法记录在 Documentation/kbuild/makefiles.rst 。}(hj!hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMshj!ubah}(h]h ]h"]h$]h&]uh1jhjQ!hhhhhNubj)}(hXK如果你认为自己做了一些有意义的事情,可以把自己放进 ``CREDITS`` ,通常不 止一个文件(无论如何你的名字都应该在源文件的顶部)。 ``MAINTAINERS`` 意味着您希望在对子系统进行更改时得到询问,并了解缺陷;这意味着对某部分 代码做出更多承诺。 h]h)}(hXJ如果你认为自己做了一些有意义的事情,可以把自己放进 ``CREDITS`` ,通常不 止一个文件(无论如何你的名字都应该在源文件的顶部)。 ``MAINTAINERS`` 意味着您希望在对子系统进行更改时得到询问,并了解缺陷;这意味着对某部分 代码做出更多承诺。h](hL如果你认为自己做了一些有意义的事情,可以把自己放进 }(hj"hhhNhNubj)}(h ``CREDITS``h]hCREDITS}(hj "hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj"ubh^ ,通常不 止一个文件(无论如何你的名字都应该在源文件的顶部)。 }(hj"hhhNhNubj)}(h``MAINTAINERS``h]h MAINTAINERS}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj"ubh 意味着您希望在对子系统进行更改时得到询问,并了解缺陷;这意味着对某部分 代码做出更多承诺。}(hj"hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMwhj"ubah}(h]h ]h"]h$]h&]uh1jhjQ!hhhhhNubj)}(hL最后,别忘记去阅读 Documentation/process/submitting-patches.rst。 h]h)}(hK最后,别忘记去阅读 Documentation/process/submitting-patches.rst。h]hK最后,别忘记去阅读 Documentation/process/submitting-patches.rst。}(hj@"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM|hj<"ubah}(h]h ]h"]h$]h&]uh1jhjQ!hhhhhNubeh}(h]h ]h"]h$]h&]jRjSuh1jhhhMdhj2!hhubeh}(h]id17ah ]h"]把你的东西放进内核里ah$]h&]uh1jDhjFhhhhhM`ubjE)}(hhh](jJ)}(hKernel 仙女棒h]hKernel 仙女棒}(hje"hhhNhNubah}(h]h ]h"]h$]h&]uh1jIhjb"hhhhhMubh)}(hB浏览源代码时的一些收藏。请随意添加到此列表。h]hB浏览源代码时的一些收藏。请随意添加到此列表。}(hjs"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjb"hhubh)}(h"``arch/x86/include/asm/delay.h``::h](j)}(h ``arch/x86/include/asm/delay.h``h]harch/x86/include/asm/delay.h}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj"ubh:}(hj"hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjb"hhubj)}(h#define ndelay(n) (__builtin_constant_p(n) ? \ ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ __ndelay(n))h]h#define ndelay(n) (__builtin_constant_p(n) ? \ ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ __ndelay(n))}hj"sbah}(h]h ]h"]h$]h&]jjuh1jhhhMhjb"hhubh)}(h``include/linux/fs.h``::h](j)}(h``include/linux/fs.h``h]hinclude/linux/fs.h}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj"ubh:}(hj"hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjb"hhubj)}(hX/* * Kernel pointers have redundant information, so we can use a * scheme where we can return either an error code or a dentry * pointer with the same return value. * * This should be a per-architecture thing, to allow different * error and pointer decisions. */ #define ERR_PTR(err) ((void *)((long)(err))) #define PTR_ERR(ptr) ((long)(ptr)) #define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))P!h]hX/* * Kernel pointers have redundant information, so we can use a * scheme where we can return either an error code or a dentry * pointer with the same return value. * * This should be a per-architecture thing, to allow different * error and pointer decisions. */ #define ERR_PTR(err) ((void *)((long)(err))) #define PTR_ERR(ptr) ((long)(ptr)) #define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))}hj"sbah}(h]h ]h"]h$]h&]jjuh1jhhhMhjb"hhubh)}(h(``arch/x86/include/asm/uaccess_32.h:``::h](j)}(h&``arch/x86/include/asm/uaccess_32.h:``h]h"arch/x86/include/asm/uaccess_32.h:}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj"ubh:}(hj"hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjb"hhubj)}(h#define copy_to_user(to,from,n) \ (__builtin_constant_p(n) ? \ __constant_copy_to_user((to),(from),(n)) : \ __generic_copy_to_user((to),(from),(n)))h]h#define copy_to_user(to,from,n) \ (__builtin_constant_p(n) ? \ __constant_copy_to_user((to),(from),(n)) : \ __generic_copy_to_user((to),(from),(n)))}hj"sbah}(h]h ]h"]h$]h&]jjuh1jhhhMhjb"hhubh)}(h``arch/sparc/kernel/head.S:``::h](j)}(h``arch/sparc/kernel/head.S:``h]harch/sparc/kernel/head.S:}(hj#hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj"ubh:}(hj"hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjb"hhubj)}(hX/* * Sun people can't spell worth damn. "compatibility" indeed. * At least we *know* we can't spell, and use a spell-checker. */ /* Uh, actually Linus it is I who cannot spell. Too much murky * Sparc assembly will do this to ya. */ C_LABEL(cputypvar): .asciz "compatibility" /* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */ .align 4 C_LABEL(cputypvar_sun4m): .asciz "compatible"h]hX/* * Sun people can't spell worth damn. "compatibility" indeed. * At least we *know* we can't spell, and use a spell-checker. */ /* Uh, actually Linus it is I who cannot spell. Too much murky * Sparc assembly will do this to ya. */ C_LABEL(cputypvar): .asciz "compatibility" /* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */ .align 4 C_LABEL(cputypvar_sun4m): .asciz "compatible"}hj#sbah}(h]h ]h"]h$]h&]jjuh1jhhhMhjb"hhubh)}(h ``arch/sparc/lib/checksum.S:``::h](j)}(h``arch/sparc/lib/checksum.S:``h]harch/sparc/lib/checksum.S:}(hj-#hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj)#ubh:}(hj)#hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjb"hhubj)}(h/* Sun, you just can't beat me, you just can't. Stop trying, * give up. I'm serious, I am going to kick the living shit * out of you, game over, lights out. */h]h/* Sun, you just can't beat me, you just can't. Stop trying, * give up. I'm serious, I am going to kick the living shit * out of you, game over, lights out. */}hjE#sbah}(h]h ]h"]h$]h&]jjuh1jhhhMhjb"hhubeh}(h]kernelah ]h"]kernel 仙女棒ah$]h&]uh1jDhjFhhhhhMubjE)}(hhh](jJ)}(h致谢h]h致谢}(hj^#hhhNhNubah}(h]h ]h"]h$]h&]uh1jIhj[#hhhhhMubh)}(hX感谢Andi Kleen提出点子,回答我的问题,纠正我的错误,充实内容等帮助。 感谢Philipp Rumpf做了许多拼写和清晰度修复,以及一些优秀的不明显的点。 感谢Werner Almesberger对 :c:func:`disable_irq()` 做了一个很好的总结, Jes Sorensen和Andrea Arcangeli补充了一些注意事项。 感谢Michael Elizabeth Chastain检查并补充了配置部分。 感谢Telsa Gwynne教我DocBook。h](h感谢Andi Kleen提出点子,回答我的问题,纠正我的错误,充实内容等帮助。 感谢Philipp Rumpf做了许多拼写和清晰度修复,以及一些优秀的不明显的点。 感谢Werner Almesberger对 }(hjl#hhhNhNubh)}(h:c:func:`disable_irq()`h]j)}(hjv#h]h disable_irq()}(hjx#hhhNhNubah}(h]h ](jjc-funceh"]h$]h&]uh1jhjt#ubah}(h]h ]h"]h$]h&]refdocj refdomainjreftypefunc refexplicitrefwarnj disable_irquh1hhhhMhjl#ubh 做了一个很好的总结, Jes Sorensen和Andrea Arcangeli补充了一些注意事项。 感谢Michael Elizabeth Chastain检查并补充了配置部分。 感谢Telsa Gwynne教我DocBook。}(hjl#hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj[#hhubeh}(h]id18ah ]h"]致谢ah$]h&]uh1jDhjFhhhhhMubeh}(h]id1ah ]h"]内核骇客指北ah$]h&]uh1jDhhhhhhhK ubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(jIN generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerj#error_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh، _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}j]jasnameids}(j#j#jjjjjfjcjjjjjjjjjjjjj j j% j" j j j1j.jLjIjjjjjjjjjjjjjcj`jjjjjjjjjjjjjjjjj2j/jjjgjdjjj/!j,!jUjRjjjPjMjjj j j!j j'!j$!j_"j\"jX#jU#j#j#u nametypes}(j#jjjfjjjjjjj j% j j1jLjjjjjjjcjjjjjjjjj2jjgjj/!jUjjPjj j!j'!j_"jX#j#uh}(j#jFjjjjjcjpjjijjjjjjjjjjj jj" j j j( j.j jIj4jjOjjjjjjjjjjj`jjjfjjjjjjjjjjjjjjj/jjj5jdjjjjj,!jjRjjjXjMjjjSj jj j j$!j!j\"j2!jU#jb"j#j[#u footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}j#KsRparse_messages]transform_messages]hsystem_message)}(hhh]h)}(hhh]h9Hyperlink target "local-bh-disable-zh" is not referenced.}hj:$sbah}(h]h ]h"]h$]h&]uh1hhj7$ubah}(h]h ]h"]h$]h&]levelKtypeINFOsourceh،lineM\uh1j5$uba transformerN include_log];Documentation/translations/zh_CN/kernel-hacking/hacking.rst(NNNNta decorationNhhub.