diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /Documentation/translations/zh_CN | |
parent | Initial commit. (diff) | |
download | linux-430c2fc249ea5c0536abd21c23382884005c9093.tar.xz linux-430c2fc249ea5c0536abd21c23382884005c9093.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'Documentation/translations/zh_CN')
55 files changed, 10684 insertions, 0 deletions
diff --git a/Documentation/translations/zh_CN/IRQ.txt b/Documentation/translations/zh_CN/IRQ.txt new file mode 100644 index 000000000..9aec8dca4 --- /dev/null +++ b/Documentation/translations/zh_CN/IRQ.txt @@ -0,0 +1,39 @@ +Chinese translated version of Documentation/core-api/irq/index.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Eric W. Biederman <ebiederman@xmission.com> +Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> +--------------------------------------------------------------------- +Documentation/core-api/irq/index.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 +英文版维护者: Eric W. Biederman <ebiederman@xmission.com> +中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- +何为 IRQ? + +一个 IRQ 是来自某个设备的一个中断请求。目前,它们可以来自一个硬件引脚, +或来自一个数据包。多个设备可能连接到同个硬件引脚,从而共享一个 IRQ。 + +一个 IRQ 编号是用于告知硬件中断源的内核标识。通常情况下,这是一个 +全局 irq_desc 数组的索引,但是除了在 linux/interrupt.h 中的实现, +具体的细节是体系结构特定的。 + +一个 IRQ 编号是设备上某个可能的中断源的枚举。通常情况下,枚举的编号是 +该引脚在系统内中断控制器的所有输入引脚中的编号。对于 ISA 总线中的情况, +枚举的是在两个 i8259 中断控制器中 16 个输入引脚。 + +架构可以对 IRQ 编号指定额外的含义,在硬件涉及任何手工配置的情况下, +是被提倡的。ISA 的 IRQ 是一个分配这类额外含义的典型例子。 diff --git a/Documentation/translations/zh_CN/SecurityBugs b/Documentation/translations/zh_CN/SecurityBugs new file mode 100644 index 000000000..2d0fffd12 --- /dev/null +++ b/Documentation/translations/zh_CN/SecurityBugs @@ -0,0 +1,50 @@ +Chinese translated version of Documentation/admin-guide/security-bugs.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Chinese maintainer: Harry Wei <harryxiyou@gmail.com> +--------------------------------------------------------------------- +Documentation/admin-guide/security-bugs.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +中文版维护者: 贾威威 Harry Wei <harryxiyou@gmail.com> +中文版翻译者: 贾威威 Harry Wei <harryxiyou@gmail.com> +中文版校译者: 贾威威 Harry Wei <harryxiyou@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- +Linux内核开发者认为安全非常重要。因此,我们想要知道当一个有关于 +安全的漏洞被发现的时候,并且它可能会被尽快的修复或者公开。请把这个安全 +漏洞报告给Linux内核安全团队。 + +1) 联系 + +linux内核安全团队可以通过email<security@kernel.org>来联系。这是 +一组独立的安全工作人员,可以帮助改善漏洞报告并且公布和取消一个修复。安 +全团队有可能会从部分的维护者那里引进额外的帮助来了解并且修复安全漏洞。 +当遇到任何漏洞,所能提供的信息越多就越能诊断和修复。如果你不清楚什么 +是有帮助的信息,那就请重温一下admin-guide/reporting-bugs.rst文件中的概述过程。任 +何攻击性的代码都是非常有用的,未经报告者的同意不会被取消,除非它已经 +被公布于众。 + +2) 公开 + +Linux内核安全团队的宗旨就是和漏洞提交者一起处理漏洞的解决方案直 +到公开。我们喜欢尽快地完全公开漏洞。当一个漏洞或者修复还没有被完全地理 +解,解决方案没有通过测试或者供应商协调,可以合理地延迟公开。然而,我们 +期望这些延迟尽可能的短些,是可数的几天,而不是几个星期或者几个月。公开 +日期是通过安全团队和漏洞提供者以及供应商洽谈后的结果。公开时间表是从很 +短(特殊的,它已经被公众所知道)到几个星期。作为一个基本的默认政策,我 +们所期望通知公众的日期是7天的安排。 + +3) 保密协议 + +Linux内核安全团队不是一个正式的团体,因此不能加入任何的保密协议。 diff --git a/Documentation/translations/zh_CN/admin-guide/clearing-warn-once.rst b/Documentation/translations/zh_CN/admin-guide/clearing-warn-once.rst new file mode 100644 index 000000000..659264d5f --- /dev/null +++ b/Documentation/translations/zh_CN/admin-guide/clearing-warn-once.rst @@ -0,0 +1,9 @@ +清除 WARN_ONCE +-------------- + +WARN_ONCE / WARN_ON_ONCE / printk_once 仅仅打印一次消息. + +echo 1 > /sys/kernel/debug/clear_warn_once + +可以清除这种状态并且再次允许打印一次告警信息,这对于运行测试集后重现问题 +很有用。 diff --git a/Documentation/translations/zh_CN/admin-guide/cpu-load.rst b/Documentation/translations/zh_CN/admin-guide/cpu-load.rst new file mode 100644 index 000000000..c972731c0 --- /dev/null +++ b/Documentation/translations/zh_CN/admin-guide/cpu-load.rst @@ -0,0 +1,105 @@ +======== +CPU 负载 +======== + +Linux通过``/proc/stat``和``/proc/uptime``导出各种信息,用户空间工具 +如top(1)使用这些信息计算系统花费在某个特定状态的平均时间。 +例如: + + $ iostat + Linux 2.6.18.3-exp (linmac) 02/20/2007 + + avg-cpu: %user %nice %system %iowait %steal %idle + 10.01 0.00 2.92 5.44 0.00 81.63 + + ... + +这里系统认为在默认采样周期內有10.01%的时间工作在用户空间,2.92%的时 +间用在系统空间,总体上有81.63%的时间是空闲的。 + +大多数情况下``/proc/stat``的信息几乎真实反映了系统信息,然而,由于内 +核采集这些数据的方式/时间的特点,有时这些信息根本不可靠。 + +那么这些信息是如何被搜集的呢?每当时间中断触发时,内核查看此刻运行的 +进程类型,并增加与此类型/状态进程对应的计数器的值。这种方法的问题是 +在两次时间中断之间系统(进程)能够在多种状态之间切换多次,而计数器只 +增加最后一种状态下的计数。 + +举例 +--- + +假设系统有一个进程以如下方式周期性地占用cpu:: + + 两个时钟中断之间的时间线 + |-----------------------| + ^ ^ + |_ 开始运行 | + |_ 开始睡眠 + (很快会被唤醒) + +在上面的情况下,根据``/proc/stat``的信息(由于当系统处于空闲状态时, +时间中断经常会发生)系统的负载将会是0 + +大家能够想象内核的这种行为会发生在许多情况下,这将导致``/proc/stat`` +中存在相当古怪的信息:: + + /* gcc -o hog smallhog.c */ + #include <time.h> + #include <limits.h> + #include <signal.h> + #include <sys/time.h> + #define HIST 10 + + static volatile sig_atomic_t stop; + + static void sighandler (int signr) + { + (void) signr; + stop = 1; + } + static unsigned long hog (unsigned long niters) + { + stop = 0; + while (!stop && --niters); + return niters; + } + int main (void) + { + int i; + struct itimerval it = { .it_interval = { .tv_sec = 0, .tv_usec = 1 }, + .it_value = { .tv_sec = 0, .tv_usec = 1 } }; + sigset_t set; + unsigned long v[HIST]; + double tmp = 0.0; + unsigned long n; + signal (SIGALRM, &sighandler); + setitimer (ITIMER_REAL, &it, NULL); + + hog (ULONG_MAX); + for (i = 0; i < HIST; ++i) v[i] = ULONG_MAX - hog (ULONG_MAX); + for (i = 0; i < HIST; ++i) tmp += v[i]; + tmp /= HIST; + n = tmp - (tmp / 3.0); + + sigemptyset (&set); + sigaddset (&set, SIGALRM); + + for (;;) { + hog (n); + sigwait (&set, &i); + } + return 0; + } + + +参考 +--- + +- http://lkml.org/lkml/2007/2/12/6 +- Documentation/filesystems/proc.rst (1.8) + + +谢谢 +--- + +Con Kolivas, Pavel Machek diff --git a/Documentation/translations/zh_CN/admin-guide/index.rst b/Documentation/translations/zh_CN/admin-guide/index.rst new file mode 100644 index 000000000..ed5ab7e37 --- /dev/null +++ b/Documentation/translations/zh_CN/admin-guide/index.rst @@ -0,0 +1,125 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :doc:`../../../admin-guide/index` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + + +Linux 内核用户和管理员指南 +========================== + +下面是一组随时间添加到内核中的面向用户的文档的集合。到目前为止,还没有一个 +整体的顺序或组织 - 这些材料不是一个单一的,连贯的文件!幸运的话,情况会随着 +时间的推移而迅速改善。 + +这个初始部分包含总体信息,包括描述内核的README, 关于内核参数的文档等。 + +Todolist: + + README + kernel-parameters + devices + sysctl/index + +本节介绍CPU漏洞及其缓解措施。 + +Todolist: + + hw-vuln/index + +下面的一组文档,针对的是试图跟踪问题和bug的用户。 + +Todolist: + + reporting-bugs + security-bugs + bug-hunting + bug-bisect + tainted-kernels + ramoops + dynamic-debug-howto + init + kdump/index + perf/index + +这是应用程序开发人员感兴趣的章节的开始。可以在这里找到涵盖内核ABI各个 +方面的文档。 + +Todolist: + + sysfs-rules + +本手册的其余部分包括各种指南,介绍如何根据您的喜好配置内核的特定行为。 + + +.. toctree:: + :maxdepth: 1 + + clearing-warn-once + cpu-load + +Todolist: + + acpi/index + aoe/index + auxdisplay/index + bcache + binderfs + binfmt-misc + blockdev/index + bootconfig + braille-console + btmrvl + cgroup-v1/index + cgroup-v2 + cifs/index + cputopology + dell_rbu + device-mapper/index + edid + efi-stub + ext4 + nfs/index + gpio/index + highuid + hw_random + initrd + iostats + java + jfs + kernel-per-CPU-kthreads + laptops/index + lcd-panel-cgram + ldm + lockup-watchdogs + LSM/index + md + media/index + mm/index + module-signing + mono + namespaces/index + numastat + parport + perf-security + pm/index + pnp + rapidio + ras + rtc + serial-console + svga + sysrq + thunderbolt + ufs + unicode + vga-softcursor + video-output + wimax/index + xfs + +.. only:: subproject and html + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/translations/zh_CN/arm/Booting b/Documentation/translations/zh_CN/arm/Booting new file mode 100644 index 000000000..c3d26ce5f --- /dev/null +++ b/Documentation/translations/zh_CN/arm/Booting @@ -0,0 +1,175 @@ +Chinese translated version of Documentation/arm/booting.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Russell King <linux@arm.linux.org.uk> +Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> +--------------------------------------------------------------------- +Documentation/arm/booting.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +英文版维护者: Russell King <linux@arm.linux.org.uk> +中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> + +以下为正文 +--------------------------------------------------------------------- + + 启动 ARM Linux + ============== + +作者:Russell King +日期:2002年5月18日 + +以下文档适用于 2.4.18-rmk6 及以上版本。 + +为了启动 ARM Linux,你需要一个引导装载程序(boot loader), +它是一个在主内核启动前运行的一个小程序。引导装载程序需要初始化各种 +设备,并最终调用 Linux 内核,将信息传递给内核。 + +从本质上讲,引导装载程序应提供(至少)以下功能: + +1、设置和初始化 RAM。 +2、初始化一个串口。 +3、检测机器的类型(machine type)。 +4、设置内核标签列表(tagged list)。 +5、调用内核映像。 + + +1、设置和初始化 RAM +------------------- + +现有的引导加载程序: 强制 +新开发的引导加载程序: 强制 + +引导装载程序应该找到并初始化系统中所有内核用于保持系统变量数据的 RAM。 +这个操作的执行是设备依赖的。(它可能使用内部算法来自动定位和计算所有 +RAM,或可能使用对这个设备已知的 RAM 信息,还可能使用任何引导装载程序 +设计者想到的匹配方法。) + + +2、初始化一个串口 +----------------------------- + +现有的引导加载程序: 可选、建议 +新开发的引导加载程序: 可选、建议 + +引导加载程序应该初始化并使能一个目标板上的串口。这允许内核串口驱动 +自动检测哪个串口用于内核控制台。(一般用于调试或与目标板通信。) + +作为替代方案,引导加载程序也可以通过标签列表传递相关的'console=' +选项给内核以指定某个串口,而串口数据格式的选项在以下文档中描述: + + Documentation/admin-guide/kernel-parameters.rst。 + + +3、检测机器类型 +-------------------------- + +现有的引导加载程序: 可选 +新开发的引导加载程序: 强制 + +引导加载程序应该通过某些方式检测自身所处的机器类型。这是一个硬件 +代码或通过查看所连接的硬件用某些算法得到,这些超出了本文档的范围。 +引导加载程序最终必须能提供一个 MACH_TYPE_xxx 值给内核。 +(详见 linux/arch/arm/tools/mach-types )。 + +4、设置启动数据 +------------------ + +现有的引导加载程序: 可选、强烈建议 +新开发的引导加载程序: 强制 + +引导加载程序必须提供标签列表或者 dtb 映像以传递配置数据给内核。启动 +数据的物理地址通过寄存器 r2 传递给内核。 + +4a、设置内核标签列表 +-------------------------------- + +bootloader 必须创建和初始化内核标签列表。一个有效的标签列表以 +ATAG_CORE 标签开始,并以 ATAG_NONE 标签结束。ATAG_CORE 标签可以是 +空的,也可以是非空。一个空 ATAG_CORE 标签其 size 域设置为 +‘2’(0x00000002)。ATAG_NONE 标签的 size 域必须设置为零。 + +在列表中可以保存任意数量的标签。对于一个重复的标签是追加到之前标签 +所携带的信息之后,还是会覆盖原来的信息,是未定义的。某些标签的行为 +是前者,其他是后者。 + +bootloader 必须传递一个系统内存的位置和最小值,以及根文件系统位置。 +因此,最小的标签列表如下所示: + + +-----------+ +基地址 -> | ATAG_CORE | | + +-----------+ | + | ATAG_MEM | | 地址增长方向 + +-----------+ | + | ATAG_NONE | | + +-----------+ v + +标签列表应该保存在系统的 RAM 中。 + +标签列表必须置于内核自解压和 initrd'bootp' 程序都不会覆盖的内存区。 +建议放在 RAM 的头 16KiB 中。 + +4b、设置设备树 +------------------------- + +bootloader 必须以 64bit 地址对齐的形式加载一个设备树映像(dtb)到系统 +RAM 中,并用启动数据初始化它。dtb 格式在文档 +Documentation/devicetree/booting-without-of.rst 中。内核将会在 +dtb 物理地址处查找 dtb 魔数值(0xd00dfeed),以确定 dtb 是否已经代替 +标签列表被传递进来。 + +bootloader 必须传递一个系统内存的位置和最小值,以及根文件系统位置。 +dtb 必须置于内核自解压不会覆盖的内存区。建议将其放置于 RAM 的头 16KiB +中。但是不可将其放置于“0”物理地址处,因为内核认为:r2 中为 0,意味着 +没有标签列表和 dtb 传递过来。 + +5、调用内核映像 +--------------------------- + +现有的引导加载程序: 强制 +新开发的引导加载程序: 强制 + +调用内核映像 zImage 有两个选择。如果 zImge 保存在 flash 中,且是为了 +在 flash 中直接运行而被正确链接的。这样引导加载程序就可以在 flash 中 +直接调用 zImage。 + +zImage 也可以被放在系统 RAM(任意位置)中被调用。注意:内核使用映像 +基地址的前 16KB RAM 空间来保存页表。建议将映像置于 RAM 的 32KB 处。 + +对于以上任意一种情况,都必须符合以下启动状态: + +- 停止所有 DMA 设备,这样内存数据就不会因为虚假网络包或磁盘数据而被破坏。 + 这可能可以节省你许多的调试时间。 + +- CPU 寄存器配置 + r0 = 0, + r1 = (在上面 3 中获取的)机器类型码。 + r2 = 标签列表在系统 RAM 中的物理地址,或 + 设备树块(dtb)在系统 RAM 中的物理地址 + +- CPU 模式 + 所有形式的中断必须被禁止 (IRQs 和 FIQs) + CPU 必须处于 SVC 模式。(对于 Angel 调试有特例存在) + +- 缓存,MMUs + MMU 必须关闭。 + 指令缓存开启或关闭都可以。 + 数据缓存必须关闭。 + +- 引导加载程序应该通过直接跳转到内核映像的第一条指令来调用内核映像。 + + 对于支持 ARM 指令集的 CPU,跳入内核入口时必须处在 ARM 状态,即使 + 对于 Thumb-2 内核也是如此。 + + 对于仅支持 Thumb 指令集的 CPU,比如 Cortex-M 系列的 CPU,跳入 + 内核入口时必须处于 Thumb 状态。 diff --git a/Documentation/translations/zh_CN/arm/kernel_user_helpers.txt b/Documentation/translations/zh_CN/arm/kernel_user_helpers.txt new file mode 100644 index 000000000..99af43639 --- /dev/null +++ b/Documentation/translations/zh_CN/arm/kernel_user_helpers.txt @@ -0,0 +1,284 @@ +Chinese translated version of Documentation/arm/kernel_user_helpers.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Nicolas Pitre <nicolas.pitre@linaro.org> + Dave Martin <dave.martin@linaro.org> +Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> +--------------------------------------------------------------------- +Documentation/arm/kernel_user_helpers.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 +英文版维护者: Nicolas Pitre <nicolas.pitre@linaro.org> + Dave Martin <dave.martin@linaro.org> +中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版校译者: 宋冬生 Dongsheng Song <dongshneg.song@gmail.com> + 傅炜 Fu Wei <tekkamanninja@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- +内核提供的用户空间辅助代码 +========================= + +在内核内存空间的固定地址处,有一个由内核提供并可从用户空间访问的代码 +段。它用于向用户空间提供因在许多 ARM CPU 中未实现的特性和/或指令而需 +内核提供帮助的某些操作。这些代码直接在用户模式下执行的想法是为了获得 +最佳效率,但那些与内核计数器联系过于紧密的部分,则被留给了用户库实现。 +事实上,此代码甚至可能因不同的 CPU 而异,这取决于其可用的指令集或它 +是否为 SMP 系统。换句话说,内核保留在不作出警告的情况下根据需要更改 +这些代码的权利。只有本文档描述的入口及其结果是保证稳定的。 + +这与完全成熟的 VDSO 实现不同(但两者并不冲突),尽管如此,VDSO 可阻止 +某些通过常量高效跳转到那些代码段的汇编技巧。且由于那些代码段在返回用户 +代码前仅使用少量的代码周期,则一个 VDSO 间接远程调用将会在这些简单的 +操作上增加一个可测量的开销。 + +在对那些拥有原生支持的新型处理器进行代码优化时,仅在已为其他操作使用 +了类似的新增指令,而导致二进制结果已与早期 ARM 处理器不兼容的情况下, +用户空间才应绕过这些辅助代码,并在内联函数中实现这些操作(无论是通过 +编译器在代码中直接放置,还是作为库函数调用实现的一部分)。也就是说, +如果你编译的代码不会为了其他目的使用新指令,则不要仅为了避免使用这些 +内核辅助代码,导致二进制程序无法在早期处理器上运行。 + +新的辅助代码可能随着时间的推移而增加,所以新内核中的某些辅助代码在旧 +内核中可能不存在。因此,程序必须在对任何辅助代码调用假设是安全之前, +检测 __kuser_helper_version 的值(见下文)。理想情况下,这种检测应该 +只在进程启动时执行一次;如果内核版本不支持所需辅助代码,则该进程可尽早 +中止执行。 + +kuser_helper_version +-------------------- + +位置: 0xffff0ffc + +参考声明: + + extern int32_t __kuser_helper_version; + +定义: + + 这个区域包含了当前运行内核实现的辅助代码版本号。用户空间可以通过读 + 取此版本号以确定特定的辅助代码是否存在。 + +使用范例: + +#define __kuser_helper_version (*(int32_t *)0xffff0ffc) + +void check_kuser_version(void) +{ + if (__kuser_helper_version < 2) { + fprintf(stderr, "can't do atomic operations, kernel too old\n"); + abort(); + } +} + +注意: + + 用户空间可以假设这个域的值不会在任何单个进程的生存期内改变。也就 + 是说,这个域可以仅在库的初始化阶段或进程启动阶段读取一次。 + +kuser_get_tls +------------- + +位置: 0xffff0fe0 + +参考原型: + + void * __kuser_get_tls(void); + +输入: + + lr = 返回地址 + +输出: + + r0 = TLS 值 + +被篡改的寄存器: + + 无 + +定义: + + 获取之前通过 __ARM_NR_set_tls 系统调用设置的 TLS 值。 + +使用范例: + +typedef void * (__kuser_get_tls_t)(void); +#define __kuser_get_tls (*(__kuser_get_tls_t *)0xffff0fe0) + +void foo() +{ + void *tls = __kuser_get_tls(); + printf("TLS = %p\n", tls); +} + +注意: + + - 仅在 __kuser_helper_version >= 1 时,此辅助代码存在 + (从内核版本 2.6.12 开始)。 + +kuser_cmpxchg +------------- + +位置: 0xffff0fc0 + +参考原型: + + int __kuser_cmpxchg(int32_t oldval, int32_t newval, volatile int32_t *ptr); + +输入: + + r0 = oldval + r1 = newval + r2 = ptr + lr = 返回地址 + +输出: + + r0 = 成功代码 (零或非零) + C flag = 如果 r0 == 0 则置 1,如果 r0 != 0 则清零。 + +被篡改的寄存器: + + r3, ip, flags + +定义: + + 仅在 *ptr 为 oldval 时原子保存 newval 于 *ptr 中。 + 如果 *ptr 被改变,则返回值为零,否则为非零值。 + 如果 *ptr 被改变,则 C flag 也会被置 1,以实现调用代码中的汇编 + 优化。 + +使用范例: + +typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr); +#define __kuser_cmpxchg (*(__kuser_cmpxchg_t *)0xffff0fc0) + +int atomic_add(volatile int *ptr, int val) +{ + int old, new; + + do { + old = *ptr; + new = old + val; + } while(__kuser_cmpxchg(old, new, ptr)); + + return new; +} + +注意: + + - 这个例程已根据需要包含了内存屏障。 + + - 仅在 __kuser_helper_version >= 2 时,此辅助代码存在 + (从内核版本 2.6.12 开始)。 + +kuser_memory_barrier +-------------------- + +位置: 0xffff0fa0 + +参考原型: + + void __kuser_memory_barrier(void); + +输入: + + lr = 返回地址 + +输出: + + 无 + +被篡改的寄存器: + + 无 + +定义: + + 应用于任何需要内存屏障以防止手动数据修改带来的一致性问题,以及 + __kuser_cmpxchg 中。 + +使用范例: + +typedef void (__kuser_dmb_t)(void); +#define __kuser_dmb (*(__kuser_dmb_t *)0xffff0fa0) + +注意: + + - 仅在 __kuser_helper_version >= 3 时,此辅助代码存在 + (从内核版本 2.6.15 开始)。 + +kuser_cmpxchg64 +--------------- + +位置: 0xffff0f60 + +参考原型: + + int __kuser_cmpxchg64(const int64_t *oldval, + const int64_t *newval, + volatile int64_t *ptr); + +输入: + + r0 = 指向 oldval + r1 = 指向 newval + r2 = 指向目标值 + lr = 返回地址 + +输出: + + r0 = 成功代码 (零或非零) + C flag = 如果 r0 == 0 则置 1,如果 r0 != 0 则清零。 + +被篡改的寄存器: + + r3, lr, flags + +定义: + + 仅在 *ptr 等于 *oldval 指向的 64 位值时,原子保存 *newval + 指向的 64 位值于 *ptr 中。如果 *ptr 被改变,则返回值为零, + 否则为非零值。 + + 如果 *ptr 被改变,则 C flag 也会被置 1,以实现调用代码中的汇编 + 优化。 + +使用范例: + +typedef int (__kuser_cmpxchg64_t)(const int64_t *oldval, + const int64_t *newval, + volatile int64_t *ptr); +#define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t *)0xffff0f60) + +int64_t atomic_add64(volatile int64_t *ptr, int64_t val) +{ + int64_t old, new; + + do { + old = *ptr; + new = old + val; + } while(__kuser_cmpxchg64(&old, &new, ptr)); + + return new; +} + +注意: + + - 这个例程已根据需要包含了内存屏障。 + + - 由于这个过程的代码长度(此辅助代码跨越 2 个常规的 kuser “槽”), + 因此 0xffff0f80 不被作为有效的入口点。 + + - 仅在 __kuser_helper_version >= 5 时,此辅助代码存在 + (从内核版本 3.1 开始)。 diff --git a/Documentation/translations/zh_CN/arm64/amu.rst b/Documentation/translations/zh_CN/arm64/amu.rst new file mode 100644 index 000000000..ab7180f91 --- /dev/null +++ b/Documentation/translations/zh_CN/arm64/amu.rst @@ -0,0 +1,100 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/arm64/amu.rst <amu_index>` + +Translator: Bailu Lin <bailu.lin@vivo.com> + +================================== +AArch64 Linux 中扩展的活动监控单元 +================================== + +作者: Ionela Voinescu <ionela.voinescu@arm.com> + +日期: 2019-09-10 + +本文档简要描述了 AArch64 Linux 支持的活动监控单元的规范。 + + +架构总述 +-------- + +活动监控是 ARMv8.4 CPU 架构引入的一个可选扩展特性。 + +活动监控单元(在每个 CPU 中实现)为系统管理提供了性能计数器。既可以通 +过系统寄存器的方式访问计数器,同时也支持外部内存映射的方式访问计数器。 + +AMUv1 架构实现了一个由4个固定的64位事件计数器组成的计数器组。 + + - CPU 周期计数器:同 CPU 的频率增长 + - 常量计数器:同固定的系统时钟频率增长 + - 淘汰指令计数器: 同每次架构指令执行增长 + - 内存停顿周期计数器:计算由在时钟域内的最后一级缓存中未命中而引起 + 的指令调度停顿周期数 + +当处于 WFI 或者 WFE 状态时,计数器不会增长。 + +AMU 架构提供了一个高达16位的事件计数器空间,未来新的 AMU 版本中可能 +用它来实现新增的事件计数器。 + +另外,AMUv1 实现了一个多达16个64位辅助事件计数器的计数器组。 + +冷复位时所有的计数器会清零。 + + +基本支持 +-------- + +内核可以安全地运行在支持 AMU 和不支持 AMU 的 CPU 组合中。 +因此,当配置 CONFIG_ARM64_AMU_EXTN 后我们无条件使能后续 +(secondary or hotplugged) CPU 检测和使用这个特性。 + +当在 CPU 上检测到该特性时,我们会标记为特性可用但是不能保证计数器的功能, +仅表明有扩展属性。 + +固件(代码运行在高异常级别,例如 arm-tf )需支持以下功能: + + - 提供低异常级别(EL2 和 EL1)访问 AMU 寄存器的能力。 + - 使能计数器。如果未使能,它的值应为 0。 + - 在从电源关闭状态启动 CPU 前或后保存或者恢复计数器。 + +当使用使能了该特性的内核启动但固件损坏时,访问计数器寄存器可能会遭遇 +panic 或者死锁。即使未发现这些症状,计数器寄存器返回的数据结果并不一 +定能反映真实情况。通常,计数器会返回 0,表明他们未被使能。 + +如果固件没有提供适当的支持最好关闭 CONFIG_ARM64_AMU_EXTN。 +值得注意的是,出于安全原因,不要绕过 AMUSERRENR_EL0 设置而捕获从 +EL0(用户空间) 访问 EL1(内核空间)。 因此,固件应该确保访问 AMU寄存器 +不会困在 EL2或EL3。 + +AMUv1 的固定计数器可以通过如下系统寄存器访问: + + - SYS_AMEVCNTR0_CORE_EL0 + - SYS_AMEVCNTR0_CONST_EL0 + - SYS_AMEVCNTR0_INST_RET_EL0 + - SYS_AMEVCNTR0_MEM_STALL_EL0 + +特定辅助计数器可以通过 SYS_AMEVCNTR1_EL0(n) 访问,其中n介于0到15。 + +详细信息定义在目录:arch/arm64/include/asm/sysreg.h。 + + +用户空间访问 +------------ + +由于以下原因,当前禁止从用户空间访问 AMU 的寄存器: + + - 安全因数:可能会暴露处于安全模式执行的代码信息。 + - 意愿:AMU 是用于系统管理的。 + +同样,该功能对用户空间不可见。 + + +虚拟化 +------ + +由于以下原因,当前禁止从 KVM 客户端的用户空间(EL0)和内核空间(EL1) +访问 AMU 的寄存器: + + - 安全因数:可能会暴露给其他客户端或主机端执行的代码信息。 + +任何试图访问 AMU 寄存器的行为都会触发一个注册在客户端的未定义异常。 diff --git a/Documentation/translations/zh_CN/arm64/booting.txt b/Documentation/translations/zh_CN/arm64/booting.txt new file mode 100644 index 000000000..5b0164132 --- /dev/null +++ b/Documentation/translations/zh_CN/arm64/booting.txt @@ -0,0 +1,246 @@ +Chinese translated version of Documentation/arm64/booting.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +M: Will Deacon <will.deacon@arm.com> +zh_CN: Fu Wei <wefu@redhat.com> +C: 55f058e7574c3615dea4615573a19bdb258696c6 +--------------------------------------------------------------------- +Documentation/arm64/booting.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +英文版维护者: Will Deacon <will.deacon@arm.com> +中文版维护者: 傅炜 Fu Wei <wefu@redhat.com> +中文版翻译者: 傅炜 Fu Wei <wefu@redhat.com> +中文版校译者: 傅炜 Fu Wei <wefu@redhat.com> +本文翻译提交时的 Git 检出点为: 55f058e7574c3615dea4615573a19bdb258696c6 + +以下为正文 +--------------------------------------------------------------------- + 启动 AArch64 Linux + ================== + +作者: Will Deacon <will.deacon@arm.com> +日期: 2012 年 09 月 07 日 + +本文档基于 Russell King 的 ARM 启动文档,且适用于所有公开发布的 +AArch64 Linux 内核代码。 + +AArch64 异常模型由多个异常级(EL0 - EL3)组成,对于 EL0 和 EL1 异常级 +有对应的安全和非安全模式。EL2 是系统管理级,且仅存在于非安全模式下。 +EL3 是最高特权级,且仅存在于安全模式下。 + +基于本文档的目的,我们将简单地使用‘引导装载程序’(‘boot loader’) +这个术语来定义在将控制权交给 Linux 内核前 CPU 上执行的所有软件。 +这可能包含安全监控和系统管理代码,或者它可能只是一些用于准备最小启动 +环境的指令。 + +基本上,引导装载程序(至少)应实现以下操作: + +1、设置和初始化 RAM +2、设置设备树数据 +3、解压内核映像 +4、调用内核映像 + + +1、设置和初始化 RAM +----------------- + +必要性: 强制 + +引导装载程序应该找到并初始化系统中所有内核用于保持系统变量数据的 RAM。 +这个操作的执行方式因设备而异。(它可能使用内部算法来自动定位和计算所有 +RAM,或可能使用对这个设备已知的 RAM 信息,还可能是引导装载程序设计者 +想到的任何合适的方法。) + + +2、设置设备树数据 +--------------- + +必要性: 强制 + +设备树数据块(dtb)必须 8 字节对齐,且大小不能超过 2MB。由于设备树 +数据块将在使能缓存的情况下以 2MB 粒度被映射,故其不能被置于必须以特定 +属性映射的2M区域内。 + +注: v4.2 之前的版本同时要求设备树数据块被置于从内核映像以下 +text_offset 字节处算起第一个 512MB 内。 + +3、解压内核映像 +------------- + +必要性: 可选 + +AArch64 内核当前没有提供自解压代码,因此如果使用了压缩内核映像文件 +(比如 Image.gz),则需要通过引导装载程序(使用 gzip 等)来进行解压。 +若引导装载程序没有实现这个功能,就要使用非压缩内核映像文件。 + + +4、调用内核映像 +------------- + +必要性: 强制 + +已解压的内核映像包含一个 64 字节的头,内容如下: + + u32 code0; /* 可执行代码 */ + u32 code1; /* 可执行代码 */ + u64 text_offset; /* 映像装载偏移,小端模式 */ + u64 image_size; /* 映像实际大小, 小端模式 */ + u64 flags; /* 内核旗标, 小端模式 * + u64 res2 = 0; /* 保留 */ + u64 res3 = 0; /* 保留 */ + u64 res4 = 0; /* 保留 */ + u32 magic = 0x644d5241; /* 魔数, 小端, "ARM\x64" */ + u32 res5; /* 保留 (用于 PE COFF 偏移) */ + + +映像头注释: + +- 自 v3.17 起,除非另有说明,所有域都是小端模式。 + +- code0/code1 负责跳转到 stext. + +- 当通过 EFI 启动时, 最初 code0/code1 被跳过。 + res5 是到 PE 文件头的偏移,而 PE 文件头含有 EFI 的启动入口点 + (efi_stub_entry)。当 stub 代码完成了它的使命,它会跳转到 code0 + 继续正常的启动流程。 + +- v3.17 之前,未明确指定 text_offset 的字节序。此时,image_size 为零, + 且 text_offset 依照内核字节序为 0x80000。 + 当 image_size 非零,text_offset 为小端模式且是有效值,应被引导加载 + 程序使用。当 image_size 为零,text_offset 可假定为 0x80000。 + +- flags 域 (v3.17 引入) 为 64 位小端模式,其编码如下: + 位 0: 内核字节序。 1 表示大端模式,0 表示小端模式。 + 位 1-2: 内核页大小。 + 0 - 未指定。 + 1 - 4K + 2 - 16K + 3 - 64K + 位 3: 内核物理位置 + 0 - 2MB 对齐基址应尽量靠近内存起始处,因为 + 其基址以下的内存无法通过线性映射访问 + 1 - 2MB 对齐基址可以在物理内存的任意位置 + 位 4-63: 保留。 + +- 当 image_size 为零时,引导装载程序应试图在内核映像末尾之后尽可能 + 多地保留空闲内存供内核直接使用。对内存空间的需求量因所选定的内核 + 特性而异, 并无实际限制。 + +内核映像必须被放置在任意一个可用系统内存 2MB 对齐基址的 text_offset +字节处,并从该处被调用。2MB 对齐基址和内核映像起始地址之间的区域对于 +内核来说没有特殊意义,且可能被用于其他目的。 +从映像起始地址算起,最少必须准备 image_size 字节的空闲内存供内核使用。 +注: v4.6 之前的版本无法使用内核映像物理偏移以下的内存,所以当时建议 +将映像尽量放置在靠近系统内存起始的地方。 + +任何提供给内核的内存(甚至在映像起始地址之前),若未从内核中标记为保留 +(如在设备树(dtb)的 memreserve 区域),都将被认为对内核是可用。 + +在跳转入内核前,必须符合以下状态: + +- 停止所有 DMA 设备,这样内存数据就不会因为虚假网络包或磁盘数据而 + 被破坏。这可能可以节省你许多的调试时间。 + +- 主 CPU 通用寄存器设置 + x0 = 系统 RAM 中设备树数据块(dtb)的物理地址。 + x1 = 0 (保留,将来可能使用) + x2 = 0 (保留,将来可能使用) + x3 = 0 (保留,将来可能使用) + +- CPU 模式 + 所有形式的中断必须在 PSTATE.DAIF 中被屏蔽(Debug、SError、IRQ + 和 FIQ)。 + CPU 必须处于 EL2(推荐,可访问虚拟化扩展)或非安全 EL1 模式下。 + +- 高速缓存、MMU + MMU 必须关闭。 + 指令缓存开启或关闭皆可。 + 已载入的内核映像的相应内存区必须被清理,以达到缓存一致性点(PoC)。 + 当存在系统缓存或其他使能缓存的一致性主控器时,通常需使用虚拟地址 + 维护其缓存,而非 set/way 操作。 + 遵从通过虚拟地址操作维护构架缓存的系统缓存必须被配置,并可以被使能。 + 而不通过虚拟地址操作维护构架缓存的系统缓存(不推荐),必须被配置且 + 禁用。 + + *译者注:对于 PoC 以及缓存相关内容,请参考 ARMv8 构架参考手册 + ARM DDI 0487A + +- 架构计时器 + CNTFRQ 必须设定为计时器的频率,且 CNTVOFF 必须设定为对所有 CPU + 都一致的值。如果在 EL1 模式下进入内核,则 CNTHCTL_EL2 中的 + EL1PCTEN (bit 0) 必须置位。 + +- 一致性 + 通过内核启动的所有 CPU 在内核入口地址上必须处于相同的一致性域中。 + 这可能要根据具体实现来定义初始化过程,以使能每个CPU上对维护操作的 + 接收。 + +- 系统寄存器 + 在进入内核映像的异常级中,所有构架中可写的系统寄存器必须通过软件 + 在一个更高的异常级别下初始化,以防止在 未知 状态下运行。 + + 对于拥有 GICv3 中断控制器并以 v3 模式运行的系统: + - 如果 EL3 存在: + ICC_SRE_EL3.Enable (位 3) 必须初始化为 0b1。 + ICC_SRE_EL3.SRE (位 0) 必须初始化为 0b1。 + - 若内核运行在 EL1: + ICC_SRE_EL2.Enable (位 3) 必须初始化为 0b1。 + ICC_SRE_EL2.SRE (位 0) 必须初始化为 0b1。 + - 设备树(DT)或 ACPI 表必须描述一个 GICv3 中断控制器。 + + 对于拥有 GICv3 中断控制器并以兼容(v2)模式运行的系统: + - 如果 EL3 存在: + ICC_SRE_EL3.SRE (位 0) 必须初始化为 0b0。 + - 若内核运行在 EL1: + ICC_SRE_EL2.SRE (位 0) 必须初始化为 0b0。 + - 设备树(DT)或 ACPI 表必须描述一个 GICv2 中断控制器。 + +以上对于 CPU 模式、高速缓存、MMU、架构计时器、一致性、系统寄存器的 +必要条件描述适用于所有 CPU。所有 CPU 必须在同一异常级别跳入内核。 + +引导装载程序必须在每个 CPU 处于以下状态时跳入内核入口: + +- 主 CPU 必须直接跳入内核映像的第一条指令。通过此 CPU 传递的设备树 + 数据块必须在每个 CPU 节点中包含一个 ‘enable-method’ 属性,所 + 支持的 enable-method 请见下文。 + + 引导装载程序必须生成这些设备树属性,并在跳入内核入口之前将其插入 + 数据块。 + +- enable-method 为 “spin-table” 的 CPU 必须在它们的 CPU + 节点中包含一个 ‘cpu-release-addr’ 属性。这个属性标识了一个 + 64 位自然对齐且初始化为零的内存位置。 + + 这些 CPU 必须在内存保留区(通过设备树中的 /memreserve/ 域传递 + 给内核)中自旋于内核之外,轮询它们的 cpu-release-addr 位置(必须 + 包含在保留区中)。可通过插入 wfe 指令来降低忙循环开销,而主 CPU 将 + 发出 sev 指令。当对 cpu-release-addr 所指位置的读取操作返回非零值 + 时,CPU 必须跳入此值所指向的地址。此值为一个单独的 64 位小端值, + 因此 CPU 须在跳转前将所读取的值转换为其本身的端模式。 + +- enable-method 为 “psci” 的 CPU 保持在内核外(比如,在 + memory 节点中描述为内核空间的内存区外,或在通过设备树 /memreserve/ + 域中描述为内核保留区的空间中)。内核将会发起在 ARM 文档(编号 + ARM DEN 0022A:用于 ARM 上的电源状态协调接口系统软件)中描述的 + CPU_ON 调用来将 CPU 带入内核。 + + *译者注: ARM DEN 0022A 已更新到 ARM DEN 0022C。 + + 设备树必须包含一个 ‘psci’ 节点,请参考以下文档: + Documentation/devicetree/bindings/arm/psci.yaml + + +- 辅助 CPU 通用寄存器设置 + x0 = 0 (保留,将来可能使用) + x1 = 0 (保留,将来可能使用) + x2 = 0 (保留,将来可能使用) + x3 = 0 (保留,将来可能使用) diff --git a/Documentation/translations/zh_CN/arm64/hugetlbpage.rst b/Documentation/translations/zh_CN/arm64/hugetlbpage.rst new file mode 100644 index 000000000..13304d269 --- /dev/null +++ b/Documentation/translations/zh_CN/arm64/hugetlbpage.rst @@ -0,0 +1,45 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/arm64/hugetlbpage.rst <hugetlbpage_index>` + +Translator: Bailu Lin <bailu.lin@vivo.com> + +===================== +ARM64中的 HugeTLBpage +===================== + +大页依靠有效利用 TLBs 来提高地址翻译的性能。这取决于以下 +两点 - + + - 大页的大小 + - TLBs 支持的条目大小 + +ARM64 接口支持2种大页方式。 + +1) pud/pmd 级别的块映射 +----------------------- + +这是常规大页,他们的 pmd 或 pud 页面表条目指向一个内存块。 +不管 TLB 中支持的条目大小如何,块映射可以减少翻译大页地址 +所需遍历的页表深度。 + +2) 使用连续位 +------------- + +架构中转换页表条目(D4.5.3, ARM DDI 0487C.a)中提供一个连续 +位告诉 MMU 这个条目是一个连续条目集的一员,它可以被缓存在单 +个 TLB 条目中。 + +在 Linux 中连续位用来增加 pmd 和 pte(最后一级)级别映射的大 +小。受支持的连续页表条目数量因页面大小和页表级别而异。 + + +支持以下大页尺寸配置 - + + ====== ======== ==== ======== === + - CONT PTE PMD CONT PMD PUD + ====== ======== ==== ======== === + 4K: 64K 2M 32M 1G + 16K: 2M 32M 1G + 64K: 2M 512M 16G + ====== ======== ==== ======== === diff --git a/Documentation/translations/zh_CN/arm64/index.rst b/Documentation/translations/zh_CN/arm64/index.rst new file mode 100644 index 000000000..e31a60903 --- /dev/null +++ b/Documentation/translations/zh_CN/arm64/index.rst @@ -0,0 +1,17 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/arm64/index.rst <arm64_index>` +:Translator: Bailu Lin <bailu.lin@vivo.com> + +.. _cn_arm64_index: + + +========== +ARM64 架构 +========== + +.. toctree:: + :maxdepth: 2 + + amu + hugetlbpage diff --git a/Documentation/translations/zh_CN/arm64/legacy_instructions.txt b/Documentation/translations/zh_CN/arm64/legacy_instructions.txt new file mode 100644 index 000000000..e295cf75f --- /dev/null +++ b/Documentation/translations/zh_CN/arm64/legacy_instructions.txt @@ -0,0 +1,72 @@ +Chinese translated version of Documentation/arm64/legacy_instructions.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Punit Agrawal <punit.agrawal@arm.com> + Suzuki K. Poulose <suzuki.poulose@arm.com> +Chinese maintainer: Fu Wei <wefu@redhat.com> +--------------------------------------------------------------------- +Documentation/arm64/legacy_instructions.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +本文翻译提交时的 Git 检出点为: bc465aa9d045feb0e13b4a8f32cc33c1943f62d6 + +英文版维护者: Punit Agrawal <punit.agrawal@arm.com> + Suzuki K. Poulose <suzuki.poulose@arm.com> +中文版维护者: 傅炜 Fu Wei <wefu@redhat.com> +中文版翻译者: 傅炜 Fu Wei <wefu@redhat.com> +中文版校译者: 傅炜 Fu Wei <wefu@redhat.com> + +以下为正文 +--------------------------------------------------------------------- +Linux 内核在 arm64 上的移植提供了一个基础框架,以支持构架中正在被淘汰或已废弃指令的模拟执行。 +这个基础框架的代码使用未定义指令钩子(hooks)来支持模拟。如果指令存在,它也允许在硬件中启用该指令。 + +模拟模式可通过写 sysctl 节点(/proc/sys/abi)来控制。 +不同的执行方式及 sysctl 节点的相应值,解释如下: + +* Undef(未定义) + 值: 0 + 产生未定义指令终止异常。它是那些构架中已废弃的指令,如 SWP,的默认处理方式。 + +* Emulate(模拟) + 值: 1 + 使用软件模拟方式。为解决软件迁移问题,这种模拟指令模式的使用是被跟踪的,并会发出速率限制警告。 + 它是那些构架中正在被淘汰的指令,如 CP15 barriers(隔离指令),的默认处理方式。 + +* Hardware Execution(硬件执行) + 值: 2 + 虽然标记为正在被淘汰,但一些实现可能提供硬件执行这些指令的使能/禁用操作。 + 使用硬件执行一般会有更好的性能,但将无法收集运行时对正被淘汰指令的使用统计数据。 + +默认执行模式依赖于指令在构架中状态。正在被淘汰的指令应该以模拟(Emulate)作为默认模式, +而已废弃的指令必须默认使用未定义(Undef)模式 + +注意:指令模拟可能无法应对所有情况。更多详情请参考单独的指令注释。 + +受支持的遗留指令 +------------- +* SWP{B} +节点: /proc/sys/abi/swp +状态: 已废弃 +默认执行方式: Undef (0) + +* CP15 Barriers +节点: /proc/sys/abi/cp15_barrier +状态: 正被淘汰,不推荐使用 +默认执行方式: Emulate (1) + +* SETEND +节点: /proc/sys/abi/setend +状态: 正被淘汰,不推荐使用 +默认执行方式: Emulate (1)* +注:为了使能这个特性,系统中的所有 CPU 必须在 EL0 支持混合字节序。 +如果一个新的 CPU (不支持混合字节序) 在使能这个特性后被热插入系统, +在应用中可能会出现不可预期的结果。 diff --git a/Documentation/translations/zh_CN/arm64/memory.txt b/Documentation/translations/zh_CN/arm64/memory.txt new file mode 100644 index 000000000..be20f8228 --- /dev/null +++ b/Documentation/translations/zh_CN/arm64/memory.txt @@ -0,0 +1,114 @@ +Chinese translated version of Documentation/arm64/memory.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Catalin Marinas <catalin.marinas@arm.com> +Chinese maintainer: Fu Wei <wefu@redhat.com> +--------------------------------------------------------------------- +Documentation/arm64/memory.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +本文翻译提交时的 Git 检出点为: bc465aa9d045feb0e13b4a8f32cc33c1943f62d6 + +英文版维护者: Catalin Marinas <catalin.marinas@arm.com> +中文版维护者: 傅炜 Fu Wei <wefu@redhat.com> +中文版翻译者: 傅炜 Fu Wei <wefu@redhat.com> +中文版校译者: 傅炜 Fu Wei <wefu@redhat.com> + +以下为正文 +--------------------------------------------------------------------- + Linux 在 AArch64 中的内存布局 + =========================== + +作者: Catalin Marinas <catalin.marinas@arm.com> + +本文档描述 AArch64 Linux 内核所使用的虚拟内存布局。此构架可以实现 +页大小为 4KB 的 4 级转换表和页大小为 64KB 的 3 级转换表。 + +AArch64 Linux 使用 3 级或 4 级转换表,其页大小配置为 4KB,对于用户和内核 +分别都有 39-bit (512GB) 或 48-bit (256TB) 的虚拟地址空间。 +对于页大小为 64KB的配置,仅使用 2 级转换表,有 42-bit (4TB) 的虚拟地址空间,但内存布局相同。 + +用户地址空间的 63:48 位为 0,而内核地址空间的相应位为 1。TTBRx 的 +选择由虚拟地址的 63 位给出。swapper_pg_dir 仅包含内核(全局)映射, +而用户 pgd 仅包含用户(非全局)映射。swapper_pg_dir 地址被写入 +TTBR1 中,且从不写入 TTBR0。 + + +AArch64 Linux 在页大小为 4KB,并使用 3 级转换表时的内存布局: + +起始地址 结束地址 大小 用途 +----------------------------------------------------------------------- +0000000000000000 0000007fffffffff 512GB 用户空间 +ffffff8000000000 ffffffffffffffff 512GB 内核空间 + + +AArch64 Linux 在页大小为 4KB,并使用 4 级转换表时的内存布局: + +起始地址 结束地址 大小 用途 +----------------------------------------------------------------------- +0000000000000000 0000ffffffffffff 256TB 用户空间 +ffff000000000000 ffffffffffffffff 256TB 内核空间 + + +AArch64 Linux 在页大小为 64KB,并使用 2 级转换表时的内存布局: + +起始地址 结束地址 大小 用途 +----------------------------------------------------------------------- +0000000000000000 000003ffffffffff 4TB 用户空间 +fffffc0000000000 ffffffffffffffff 4TB 内核空间 + + +AArch64 Linux 在页大小为 64KB,并使用 3 级转换表时的内存布局: + +起始地址 结束地址 大小 用途 +----------------------------------------------------------------------- +0000000000000000 0000ffffffffffff 256TB 用户空间 +ffff000000000000 ffffffffffffffff 256TB 内核空间 + + +更详细的内核虚拟内存布局,请参阅内核启动信息。 + + +4KB 页大小的转换表查找: + ++--------+--------+--------+--------+--------+--------+--------+--------+ +|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0| ++--------+--------+--------+--------+--------+--------+--------+--------+ + | | | | | | + | | | | | v + | | | | | [11:0] 页内偏移 + | | | | +-> [20:12] L3 索引 + | | | +-----------> [29:21] L2 索引 + | | +---------------------> [38:30] L1 索引 + | +-------------------------------> [47:39] L0 索引 + +-------------------------------------------------> [63] TTBR0/1 + + +64KB 页大小的转换表查找: + ++--------+--------+--------+--------+--------+--------+--------+--------+ +|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0| ++--------+--------+--------+--------+--------+--------+--------+--------+ + | | | | | + | | | | v + | | | | [15:0] 页内偏移 + | | | +----------> [28:16] L3 索引 + | | +--------------------------> [41:29] L2 索引 + | +-------------------------------> [47:42] L1 索引 + +-------------------------------------------------> [63] TTBR0/1 + + +当使用 KVM 时, 管理程序(hypervisor)在 EL2 中通过相对内核虚拟地址的 +一个固定偏移来映射内核页(内核虚拟地址的高 24 位设为零): + +起始地址 结束地址 大小 用途 +----------------------------------------------------------------------- +0000004000000000 0000007fffffffff 256GB 在 HYP 中映射的内核对象 diff --git a/Documentation/translations/zh_CN/arm64/silicon-errata.txt b/Documentation/translations/zh_CN/arm64/silicon-errata.txt new file mode 100644 index 000000000..440c59ac7 --- /dev/null +++ b/Documentation/translations/zh_CN/arm64/silicon-errata.txt @@ -0,0 +1,74 @@ +Chinese translated version of Documentation/arm64/silicon-errata.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +M: Will Deacon <will.deacon@arm.com> +zh_CN: Fu Wei <wefu@redhat.com> +C: 1926e54f115725a9248d0c4c65c22acaf94de4c4 +--------------------------------------------------------------------- +Documentation/arm64/silicon-errata.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +英文版维护者: Will Deacon <will.deacon@arm.com> +中文版维护者: 傅炜 Fu Wei <wefu@redhat.com> +中文版翻译者: 傅炜 Fu Wei <wefu@redhat.com> +中文版校译者: 傅炜 Fu Wei <wefu@redhat.com> +本文翻译提交时的 Git 检出点为: 1926e54f115725a9248d0c4c65c22acaf94de4c4 + +以下为正文 +--------------------------------------------------------------------- + 芯片勘误和软件补救措施 + ================== + +作者: Will Deacon <will.deacon@arm.com> +日期: 2015年11月27日 + +一个不幸的现实:硬件经常带有一些所谓的“瑕疵(errata)”,导致其在 +某些特定情况下会违背构架定义的行为。就基于 ARM 的硬件而言,这些瑕疵 +大体可分为以下几类: + + A 类:无可行补救措施的严重缺陷。 + B 类:有可接受的补救措施的重大或严重缺陷。 + C 类:在正常操作中不会显现的小瑕疵。 + +更多资讯,请在 infocenter.arm.com (需注册)中查阅“软件开发者勘误 +笔记”(“Software Developers Errata Notice”)文档。 + +对于 Linux 而言,B 类缺陷可能需要操作系统的某些特别处理。例如,避免 +一个特殊的代码序列,或是以一种特定的方式配置处理器。在某种不太常见的 +情况下,为将 A 类缺陷当作 C 类处理,可能需要用类似的手段。这些手段被 +统称为“软件补救措施”,且仅在少数情况需要(例如,那些需要一个运行在 +非安全异常级的补救措施 *并且* 能被 Linux 触发的情况)。 + +对于尚在讨论中的可能对未受瑕疵影响的系统产生干扰的软件补救措施,有一个 +相应的内核配置(Kconfig)选项被加在 “内核特性(Kernel Features)”-> +“基于可选方法框架的 ARM 瑕疵补救措施(ARM errata workarounds via +the alternatives framework)"。这些选项被默认开启,若探测到受影响的CPU, +补丁将在运行时被使用。至于对系统运行影响较小的补救措施,内核配置选项 +并不存在,且代码以某种规避瑕疵的方式被构造(带注释为宜)。 + +这种做法对于在任意内核源代码树中准确地判断出哪个瑕疵已被软件方法所补救 +稍微有点麻烦,所以在 Linux 内核中此文件作为软件补救措施的注册表, +并将在新的软件补救措施被提交和向后移植(backported)到稳定内核时被更新。 + +| 实现者 | 受影响的组件 | 勘误编号 | 内核配置 | ++----------------+-----------------+-----------------+-------------------------+ +| ARM | Cortex-A53 | #826319 | ARM64_ERRATUM_826319 | +| ARM | Cortex-A53 | #827319 | ARM64_ERRATUM_827319 | +| ARM | Cortex-A53 | #824069 | ARM64_ERRATUM_824069 | +| ARM | Cortex-A53 | #819472 | ARM64_ERRATUM_819472 | +| ARM | Cortex-A53 | #845719 | ARM64_ERRATUM_845719 | +| ARM | Cortex-A53 | #843419 | ARM64_ERRATUM_843419 | +| ARM | Cortex-A57 | #832075 | ARM64_ERRATUM_832075 | +| ARM | Cortex-A57 | #852523 | N/A | +| ARM | Cortex-A57 | #834220 | ARM64_ERRATUM_834220 | +| | | | | +| Cavium | ThunderX ITS | #22375, #24313 | CAVIUM_ERRATUM_22375 | +| Cavium | ThunderX GICv3 | #23154 | CAVIUM_ERRATUM_23154 | diff --git a/Documentation/translations/zh_CN/arm64/tagged-pointers.txt b/Documentation/translations/zh_CN/arm64/tagged-pointers.txt new file mode 100644 index 000000000..77ac3548a --- /dev/null +++ b/Documentation/translations/zh_CN/arm64/tagged-pointers.txt @@ -0,0 +1,52 @@ +Chinese translated version of Documentation/arm64/tagged-pointers.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Will Deacon <will.deacon@arm.com> +Chinese maintainer: Fu Wei <wefu@redhat.com> +--------------------------------------------------------------------- +Documentation/arm64/tagged-pointers.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +英文版维护者: Will Deacon <will.deacon@arm.com> +中文版维护者: 傅炜 Fu Wei <wefu@redhat.com> +中文版翻译者: 傅炜 Fu Wei <wefu@redhat.com> +中文版校译者: 傅炜 Fu Wei <wefu@redhat.com> + +以下为正文 +--------------------------------------------------------------------- + Linux 在 AArch64 中带标记的虚拟地址 + ================================= + +作者: Will Deacon <will.deacon@arm.com> +日期: 2013 年 06 月 12 日 + +本文档简述了在 AArch64 地址转换系统中提供的带标记的虚拟地址及其在 +AArch64 Linux 中的潜在用途。 + +内核提供的地址转换表配置使通过 TTBR0 完成的虚拟地址转换(即用户空间 +映射),其虚拟地址的最高 8 位(63:56)会被转换硬件所忽略。这种机制 +让这些位可供应用程序自由使用,其注意事项如下: + + (1) 内核要求所有传递到 EL1 的用户空间地址带有 0x00 标记。 + 这意味着任何携带用户空间虚拟地址的系统调用(syscall) + 参数 *必须* 在陷入内核前使它们的最高字节被清零。 + + (2) 非零标记在传递信号时不被保存。这意味着在应用程序中利用了 + 标记的信号处理函数无法依赖 siginfo_t 的用户空间虚拟 + 地址所携带的包含其内部域信息的标记。此规则的一个例外是 + 当信号是在调试观察点的异常处理程序中产生的,此时标记的 + 信息将被保存。 + + (3) 当使用带标记的指针时需特别留心,因为仅对两个虚拟地址 + 的高字节,C 编译器很可能无法判断它们是不同的。 + +此构架会阻止对带标记的 PC 指针的利用,因此在异常返回时,其高字节 +将被设置成一个为 “55” 的扩展符。 diff --git a/Documentation/translations/zh_CN/disclaimer-zh_CN.rst b/Documentation/translations/zh_CN/disclaimer-zh_CN.rst new file mode 100644 index 000000000..dcf803ede --- /dev/null +++ b/Documentation/translations/zh_CN/disclaimer-zh_CN.rst @@ -0,0 +1,9 @@ +:orphan: + +.. warning:: + 此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, + 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 + +.. note:: + 如果您发现本文档与原始文件有任何不同或者有翻译问题,请联系该文件的译者, + 或者请求时奎亮的帮助:<alex.shi@linux.alibaba.com>。 diff --git a/Documentation/translations/zh_CN/filesystems/debugfs.rst b/Documentation/translations/zh_CN/filesystems/debugfs.rst new file mode 100644 index 000000000..822c4d42f --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/debugfs.rst @@ -0,0 +1,221 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :doc:`../../../filesystems/debugfs` + +======= +Debugfs +======= + +译者 +:: + + 中文版维护者: 罗楚成 Chucheng Luo <luochucheng@vivo.com> + 中文版翻译者: 罗楚成 Chucheng Luo <luochucheng@vivo.com> + 中文版校译者: 罗楚成 Chucheng Luo <luochucheng@vivo.com> + + + +版权所有2020 罗楚成 <luochucheng@vivo.com> + + +Debugfs是内核开发人员在用户空间获取信息的简单方法。与/proc不同,proc只提供进程 +信息。也不像sysfs,具有严格的“每个文件一个值“的规则。debugfs根本没有规则,开发 +人员可以在这里放置他们想要的任何信息。debugfs文件系统也不能用作稳定的ABI接口。 +从理论上讲,debugfs导出文件的时候没有任何约束。但是[1]实际情况并不总是那么 +简单。即使是debugfs接口,也最好根据需要进行设计,并尽量保持接口不变。 + + +Debugfs通常使用以下命令安装:: + + mount -t debugfs none /sys/kernel/debug + +(或等效的/etc/fstab行)。 +debugfs根目录默认仅可由root用户访问。要更改对文件树的访问,请使用“ uid”,“ gid” +和“ mode”挂载选项。请注意,debugfs API仅按照GPL协议导出到模块。 + +使用debugfs的代码应包含<linux/debugfs.h>。然后,首先是创建至少一个目录来保存 +一组debugfs文件:: + + struct dentry *debugfs_create_dir(const char *name, struct dentry *parent); + +如果成功,此调用将在指定的父目录下创建一个名为name的目录。如果parent参数为空, +则会在debugfs根目录中创建。创建目录成功时,返回值是一个指向dentry结构体的指针。 +该dentry结构体的指针可用于在目录中创建文件(以及最后将其清理干净)。ERR_PTR +(-ERROR)返回值表明出错。如果返回ERR_PTR(-ENODEV),则表明内核是在没有debugfs +支持的情况下构建的,并且下述函数都不会起作用。 + +在debugfs目录中创建文件的最通用方法是:: + + struct dentry *debugfs_create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); + +在这里,name是要创建的文件的名称,mode描述了访问文件应具有的权限,parent指向 +应该保存文件的目录,data将存储在产生的inode结构体的i_private字段中,而fops是 +一组文件操作函数,这些函数中实现文件操作的具体行为。至少,read()和/或 +write()操作应提供;其他可以根据需要包括在内。同样的,返回值将是指向创建文件 +的dentry指针,错误时返回ERR_PTR(-ERROR),系统不支持debugfs时返回值为ERR_PTR +(-ENODEV)。创建一个初始大小的文件,可以使用以下函数代替:: + + struct dentry *debugfs_create_file_size(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops, + loff_t file_size); + +file_size是初始文件大小。其他参数跟函数debugfs_create_file的相同。 + +在许多情况下,没必要自己去创建一组文件操作;对于一些简单的情况,debugfs代码提供 +了许多帮助函数。包含单个整数值的文件可以使用以下任何一项创建:: + + void debugfs_create_u8(const char *name, umode_t mode, + struct dentry *parent, u8 *value); + void debugfs_create_u16(const char *name, umode_t mode, + struct dentry *parent, u16 *value); + struct dentry *debugfs_create_u32(const char *name, umode_t mode, + struct dentry *parent, u32 *value); + void debugfs_create_u64(const char *name, umode_t mode, + struct dentry *parent, u64 *value); + +这些文件支持读取和写入给定值。如果某个文件不支持写入,只需根据需要设置mode +参数位。这些文件中的值以十进制表示;如果需要使用十六进制,可以使用以下函数 +替代:: + + void debugfs_create_x8(const char *name, umode_t mode, + struct dentry *parent, u8 *value); + void debugfs_create_x16(const char *name, umode_t mode, + struct dentry *parent, u16 *value); + void debugfs_create_x32(const char *name, umode_t mode, + struct dentry *parent, u32 *value); + void debugfs_create_x64(const char *name, umode_t mode, + struct dentry *parent, u64 *value); + +这些功能只有在开发人员知道导出值的大小的时候才有用。某些数据类型在不同的架构上 +有不同的宽度,这样会使情况变得有些复杂。在这种特殊情况下可以使用以下函数:: + + void debugfs_create_size_t(const char *name, umode_t mode, + struct dentry *parent, size_t *value); + +不出所料,此函数将创建一个debugfs文件来表示类型为size_t的变量。 + +同样地,也有导出无符号长整型变量的函数,分别以十进制和十六进制表示如下:: + + struct dentry *debugfs_create_ulong(const char *name, umode_t mode, + struct dentry *parent, + unsigned long *value); + void debugfs_create_xul(const char *name, umode_t mode, + struct dentry *parent, unsigned long *value); + +布尔值可以通过以下方式放置在debugfs中:: + + struct dentry *debugfs_create_bool(const char *name, umode_t mode, + struct dentry *parent, bool *value); + + +读取结果文件将产生Y(对于非零值)或N,后跟换行符写入的时候,它只接受大写或小写 +值或1或0。任何其他输入将被忽略。 + +同样,atomic_t类型的值也可以放置在debugfs中:: + + void debugfs_create_atomic_t(const char *name, umode_t mode, + struct dentry *parent, atomic_t *value) + +读取此文件将获得atomic_t值,写入此文件将设置atomic_t值。 + +另一个选择是通过以下结构体和函数导出一个任意二进制数据块:: + + struct debugfs_blob_wrapper { + void *data; + unsigned long size; + }; + + struct dentry *debugfs_create_blob(const char *name, umode_t mode, + struct dentry *parent, + struct debugfs_blob_wrapper *blob); + +读取此文件将返回由指针指向debugfs_blob_wrapper结构体的数据。一些驱动使用“blobs” +作为一种返回几行(静态)格式化文本的简单方法。这个函数可用于导出二进制信息,但 +似乎在主线中没有任何代码这样做。请注意,使用debugfs_create_blob()命令创建的 +所有文件是只读的。 + +如果您要转储一个寄存器块(在开发过程中经常会这么做,但是这样的调试代码很少上传 +到主线中。Debugfs提供两个函数:一个用于创建仅寄存器文件,另一个把一个寄存器块 +插入一个顺序文件中:: + + struct debugfs_reg32 { + char *name; + unsigned long offset; + }; + + struct debugfs_regset32 { + struct debugfs_reg32 *regs; + int nregs; + void __iomem *base; + }; + + struct dentry *debugfs_create_regset32(const char *name, umode_t mode, + struct dentry *parent, + struct debugfs_regset32 *regset); + + void debugfs_print_regs32(struct seq_file *s, struct debugfs_reg32 *regs, + int nregs, void __iomem *base, char *prefix); + +“base”参数可能为0,但您可能需要使用__stringify构建reg32数组,实际上有许多寄存器 +名称(宏)是寄存器块在基址上的字节偏移量。 + +如果要在debugfs中转储u32数组,可以使用以下函数创建文件:: + + void debugfs_create_u32_array(const char *name, umode_t mode, + struct dentry *parent, + u32 *array, u32 elements); + +“array”参数提供数据,而“elements”参数为数组中元素的数量。注意:数组创建后,数组 +大小无法更改。 + +有一个函数来创建与设备相关的seq_file:: + + struct dentry *debugfs_create_devm_seqfile(struct device *dev, + const char *name, + struct dentry *parent, + int (*read_fn)(struct seq_file *s, + void *data)); + +“dev”参数是与此debugfs文件相关的设备,并且“read_fn”是一个函数指针,这个函数在 +打印seq_file内容的时候被回调。 + +还有一些其他的面向目录的函数:: + + struct dentry *debugfs_rename(struct dentry *old_dir, + struct dentry *old_dentry, + struct dentry *new_dir, + const char *new_name); + + struct dentry *debugfs_create_symlink(const char *name, + struct dentry *parent, + const char *target); + +调用debugfs_rename()将为现有的debugfs文件重命名,可能同时切换目录。 new_name +函数调用之前不能存在;返回值为old_dentry,其中包含更新的信息。可以使用 +debugfs_create_symlink()创建符号链接。 + +所有debugfs用户必须考虑的一件事是: + +debugfs不会自动清除在其中创建的任何目录。如果一个模块在不显式删除debugfs目录的 +情况下卸载模块,结果将会遗留很多野指针,从而导致系统不稳定。因此,所有debugfs +用户-至少是那些可以作为模块构建的用户-必须做模块卸载的时候准备删除在此创建的 +所有文件和目录。一份文件可以通过以下方式删除:: + + void debugfs_remove(struct dentry *dentry); + +dentry值可以为NULL或错误值,在这种情况下,不会有任何文件被删除。 + +很久以前,内核开发者使用debugfs时需要记录他们创建的每个dentry指针,以便最后所有 +文件都可以被清理掉。但是,现在debugfs用户能调用以下函数递归清除之前创建的文件:: + + void debugfs_remove_recursive(struct dentry *dentry); + +如果将对应顶层目录的dentry传递给以上函数,则该目录下的整个层次结构将会被删除。 + +注释: +[1] http://lwn.net/Articles/309298/ diff --git a/Documentation/translations/zh_CN/filesystems/index.rst b/Documentation/translations/zh_CN/filesystems/index.rst new file mode 100644 index 000000000..186501d13 --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/index.rst @@ -0,0 +1,28 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/filesystems/index.rst <filesystems_index>` +:Translator: Wang Wenhu <wenhu.wang@vivo.com> + +.. _cn_filesystems_index: + +======================== +Linux Kernel中的文件系统 +======================== + +这份正在开发的手册或许在未来某个辉煌的日子里以易懂的形式将Linux虚拟\ +文件系统(VFS)层以及基于其上的各种文件系统如何工作呈现给大家。当前\ +可以看到下面的内容。 + +文件系统 +======== + +文件系统实现文档。 + +.. toctree:: + :maxdepth: 2 + + virtiofs + debugfs + diff --git a/Documentation/translations/zh_CN/filesystems/sysfs.txt b/Documentation/translations/zh_CN/filesystems/sysfs.txt new file mode 100644 index 000000000..046cc1d52 --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/sysfs.txt @@ -0,0 +1,373 @@ +Chinese translated version of Documentation/filesystems/sysfs.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Patrick Mochel <mochel@osdl.org> + Mike Murphy <mamurph@cs.clemson.edu> +Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> +--------------------------------------------------------------------- +Documentation/filesystems/sysfs.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 +英文版维护者: Patrick Mochel <mochel@osdl.org> + Mike Murphy <mamurph@cs.clemson.edu> +中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- +sysfs - 用于导出内核对象(kobject)的文件系统 + +Patrick Mochel <mochel@osdl.org> +Mike Murphy <mamurph@cs.clemson.edu> + +修订: 16 August 2011 +原始版本: 10 January 2003 + + +sysfs 简介: +~~~~~~~~~~ + +sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核 +数据结构及其属性,以及它们之间的关联到用户空间的方法。 + +sysfs 始终与 kobject 的底层结构紧密相关。请阅读 +Documentation/core-api/kobject.rst 文档以获得更多关于 kobject 接口的 +信息。 + + +使用 sysfs +~~~~~~~~~~~ + +只要内核配置中定义了 CONFIG_SYSFS ,sysfs 总是被编译进内核。你可 +通过以下命令挂载它: + + mount -t sysfs sysfs /sys + + +创建目录 +~~~~~~~~ + +任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个 +目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递 +内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的 +共同祖先;例如:某些对象属于某个子系统。 + +Sysfs 在与其目录关联的 kernfs_node 对象中内部保存一个指向实现 +目录的 kobject 的指针。以前,这个 kobject 指针被 sysfs 直接用于 +kobject 文件打开和关闭的引用计数。而现在的 sysfs 实现中,kobject +引用计数只能通过 sysfs_schedule_callback() 函数直接修改。 + + +属性 +~~~~ + +kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义 +了面向文件 I/O 操作的方法,以提供对内核属性的读写。 + + +属性应为 ASCII 码文本文件。以一个文件只存储一个属性值为宜。但一个 +文件只包含一个属性值可能影响效率,所以一个包含相同数据类型的属性值 +数组也被广泛地接受。 + +混合类型、表达多行数据以及一些怪异的数据格式会遭到强烈反对。这样做是 +很丢脸的,而且其代码会在未通知作者的情况下被重写。 + + +一个简单的属性结构定义如下: + +struct attribute { + char * name; + struct module *owner; + umode_t mode; +}; + + +int sysfs_create_file(struct kobject * kobj, const struct attribute * attr); +void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr); + + +一个单独的属性结构并不包含读写其属性值的方法。子系统最好为增删特定 +对象类型的属性定义自己的属性结构体和封装函数。 + +例如:驱动程序模型定义的 device_attribute 结构体如下: + +struct device_attribute { + struct attribute attr; + ssize_t (*show)(struct device *dev, struct device_attribute *attr, + char *buf); + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +}; + +int device_create_file(struct device *, const struct device_attribute *); +void device_remove_file(struct device *, const struct device_attribute *); + +为了定义设备属性,同时定义了一下辅助宏: + +#define DEVICE_ATTR(_name, _mode, _show, _store) \ +struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) + +例如:声明 + +static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo); + +等同于如下代码: + +static struct device_attribute dev_attr_foo = { + .attr = { + .name = "foo", + .mode = S_IWUSR | S_IRUGO, + .show = show_foo, + .store = store_foo, + }, +}; + + +子系统特有的回调函数 +~~~~~~~~~~~~~~~~~~~ + +当一个子系统定义一个新的属性类型时,必须实现一系列的 sysfs 操作, +以帮助读写调用实现属性所有者的显示和储存方法。 + +struct sysfs_ops { + ssize_t (*show)(struct kobject *, struct attribute *, char *); + ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); +}; + +[子系统应已经定义了一个 struct kobj_type 结构体作为这个类型的 +描述符,并在此保存 sysfs_ops 的指针。更多的信息参见 kobject 的 +文档] + +sysfs 会为这个类型调用适当的方法。当一个文件被读写时,这个方法会 +将一般的kobject 和 attribute 结构体指针转换为适当的指针类型后 +调用相关联的函数。 + + +示例: + +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) + +static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct device_attribute *dev_attr = to_dev_attr(attr); + struct device *dev = kobj_to_dev(kobj); + ssize_t ret = -EIO; + + if (dev_attr->show) + ret = dev_attr->show(dev, dev_attr, buf); + if (ret >= (ssize_t)PAGE_SIZE) { + printk("dev_attr_show: %pS returned bad count\n", + dev_attr->show); + } + return ret; +} + + + +读写属性数据 +~~~~~~~~~~~~ + +在声明属性时,必须指定 show() 或 store() 方法,以实现属性的 +读或写。这些方法的类型应该和以下的设备属性定义一样简单。 + +ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); + +也就是说,他们应只以一个处理对象、一个属性和一个缓冲指针作为参数。 + +sysfs 会分配一个大小为 (PAGE_SIZE) 的缓冲区并传递给这个方法。 +Sysfs 将会为每次读写操作调用一次这个方法。这使得这些方法在执行时 +会出现以下的行为: + +- 在读方面(read(2)),show() 方法应该填充整个缓冲区。回想属性 + 应只导出了一个属性值或是一个同类型属性值的数组,所以这个代价将 + 不会不太高。 + + 这使得用户空间可以局部地读和任意的向前搜索整个文件。如果用户空间 + 向后搜索到零或使用‘0’偏移执行一个pread(2)操作,show()方法将 + 再次被调用,以重新填充缓存。 + +- 在写方面(write(2)),sysfs 希望在第一次写操作时得到整个缓冲区。 + 之后 Sysfs 传递整个缓冲区给 store() 方法。 + + 当要写 sysfs 文件时,用户空间进程应首先读取整个文件,修该想要 + 改变的值,然后回写整个缓冲区。 + + 在读写属性值时,属性方法的执行应操作相同的缓冲区。 + +注记: + +- 写操作导致的 show() 方法重载,会忽略当前文件位置。 + +- 缓冲区应总是 PAGE_SIZE 大小。对于i386,这个值为4096。 + +- show() 方法应该返回写入缓冲区的字节数,也就是 scnprintf()的 + 返回值。 + +- show() 方法在将格式化返回值返回用户空间的时候,禁止使用snprintf()。 + 如果可以保证不会发生缓冲区溢出,可以使用sprintf(),否则必须使用 + scnprintf()。 + +- store() 应返回缓冲区的已用字节数。如果整个缓存都已填满,只需返回 + count 参数。 + +- show() 或 store() 可以返回错误值。当得到一个非法值,必须返回一个 + 错误值。 + +- 一个传递给方法的对象将会通过 sysfs 调用对象内嵌的引用计数固定在 + 内存中。尽管如此,对象代表的物理实体(如设备)可能已不存在。如有必要, + 应该实现一个检测机制。 + +一个简单的(未经实验证实的)设备属性实现如下: + +static ssize_t show_name(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name); +} + +static ssize_t store_name(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + snprintf(dev->name, sizeof(dev->name), "%.*s", + (int)min(count, sizeof(dev->name) - 1), buf); + return count; +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, store_name); + + +(注意:真正的实现不允许用户空间设置设备名。) + +顶层目录布局 +~~~~~~~~~~~~ + +sysfs 目录的安排显示了内核数据结构之间的关系。 + +顶层 sysfs 目录如下: + +block/ +bus/ +class/ +dev/ +devices/ +firmware/ +net/ +fs/ + +devices/ 包含了一个设备树的文件系统表示。他直接映射了内部的内核 +设备树,反映了设备的层次结构。 + +bus/ 包含了内核中各种总线类型的平面目录布局。每个总线目录包含两个 +子目录: + + devices/ + drivers/ + +devices/ 包含了系统中出现的每个设备的符号链接,他们指向 root/ 下的 +设备目录。 + +drivers/ 包含了每个已为特定总线上的设备而挂载的驱动程序的目录(这里 +假定驱动没有跨越多个总线类型)。 + +fs/ 包含了一个为文件系统设立的目录。现在每个想要导出属性的文件系统必须 +在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.rst)。 + +dev/ 包含两个子目录: char/ 和 block/。在这两个子目录中,有以 +<major>:<minor> 格式命名的符号链接。这些符号链接指向 sysfs 目录 +中相应的设备。/sys/dev 提供一个通过一个 stat(2) 操作结果,查找 +设备 sysfs 接口快捷的方法。 + +更多有关 driver-model 的特性信息可以在 Documentation/driver-api/driver-model/ +中找到。 + + +TODO: 完成这一节。 + + +当前接口 +~~~~~~~~ + +以下的接口层普遍存在于当前的sysfs中: + +- 设备 (include/linux/device.h) +---------------------------------- +结构体: + +struct device_attribute { + struct attribute attr; + ssize_t (*show)(struct device *dev, struct device_attribute *attr, + char *buf); + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +}; + +声明: + +DEVICE_ATTR(_name, _mode, _show, _store); + +增/删属性: + +int device_create_file(struct device *dev, const struct device_attribute * attr); +void device_remove_file(struct device *dev, const struct device_attribute * attr); + + +- 总线驱动程序 (include/linux/device.h) +-------------------------------------- +结构体: + +struct bus_attribute { + struct attribute attr; + ssize_t (*show)(struct bus_type *, char * buf); + ssize_t (*store)(struct bus_type *, const char * buf, size_t count); +}; + +声明: + +BUS_ATTR(_name, _mode, _show, _store) + +增/删属性: + +int bus_create_file(struct bus_type *, struct bus_attribute *); +void bus_remove_file(struct bus_type *, struct bus_attribute *); + + +- 设备驱动程序 (include/linux/device.h) +----------------------------------------- + +结构体: + +struct driver_attribute { + struct attribute attr; + ssize_t (*show)(struct device_driver *, char * buf); + ssize_t (*store)(struct device_driver *, const char * buf, + size_t count); +}; + +声明: + +DRIVER_ATTR(_name, _mode, _show, _store) + +增/删属性: + +int driver_create_file(struct device_driver *, const struct driver_attribute *); +void driver_remove_file(struct device_driver *, const struct driver_attribute *); + + +文档 +~~~~ + +sysfs 目录结构以及其中包含的属性定义了一个内核与用户空间之间的 ABI。 +对于任何 ABI,其自身的稳定和适当的文档是非常重要的。所有新的 sysfs +属性必须在 Documentation/ABI 中有文档。详见 Documentation/ABI/README。 diff --git a/Documentation/translations/zh_CN/filesystems/virtiofs.rst b/Documentation/translations/zh_CN/filesystems/virtiofs.rst new file mode 100644 index 000000000..09bc9e012 --- /dev/null +++ b/Documentation/translations/zh_CN/filesystems/virtiofs.rst @@ -0,0 +1,58 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/filesystems/virtiofs.rst <virtiofs_index>` + +译者 +:: + + 中文版维护者: 王文虎 Wang Wenhu <wenhu.wang@vivo.com> + 中文版翻译者: 王文虎 Wang Wenhu <wenhu.wang@vivo.com> + 中文版校译者: 王文虎 Wang Wenhu <wenhu.wang@vivo.com> + +=========================================== +virtiofs: virtio-fs 主机<->客机共享文件系统 +=========================================== + +- Copyright (C) 2020 Vivo Communication Technology Co. Ltd. + +介绍 +==== +Linux的virtiofs文件系统实现了一个半虚拟化VIRTIO类型“virtio-fs”设备的驱动,通过该\ +类型设备实现客机<->主机文件系统共享。它允许客机挂载一个已经导出到主机的目录。 + +客机通常需要访问主机或者远程系统上的文件。使用场景包括:在新客机安装时让文件对其\ +可见;从主机上的根文件系统启动;对无状态或临时客机提供持久存储和在客机之间共享目录。 + +尽管在某些任务可能通过使用已有的网络文件系统完成,但是却需要非常难以自动化的配置\ +步骤,且将存储网络暴露给客机。而virtio-fs设备通过提供不经过网络的文件系统访问文件\ +的设计方式解决了这些问题。 + +另外,virto-fs设备发挥了主客机共存的优点提高了性能,并且提供了网络文件系统所不具备 +的一些语义功能。 + +用法 +==== +以``myfs``标签将文件系统挂载到``/mnt``: + +.. code-block:: sh + + guest# mount -t virtiofs myfs /mnt + +请查阅 https://virtio-fs.gitlab.io/ 了解配置QEMU和virtiofsd守护程序的详细信息。 + +内幕 +==== +由于virtio-fs设备将FUSE协议用于文件系统请求,因此Linux的virtiofs文件系统与FUSE文\ +件系统客户端紧密集成在一起。客机充当FUSE客户端而主机充当FUSE服务器,内核与用户空\ +间之间的/dev/fuse接口由virtio-fs设备接口代替。 + +FUSE请求被置于虚拟队列中由主机处理。主机填充缓冲区中的响应部分,而客机处理请求的完成部分。 + +将/dev/fuse映射到虚拟队列需要解决/dev/fuse和虚拟队列之间语义上的差异。每次读取\ +/dev/fuse设备时,FUSE客户端都可以选择要传输的请求,从而可以使某些请求优先于其他\ +请求。虚拟队列有其队列语义,无法更改已入队请求的顺序。在虚拟队列已满的情况下尤 +其关键,因为此时不可能加入高优先级的请求。为了解决此差异,virtio-fs设备采用“hiprio”\ +(高优先级)虚拟队列,专门用于有别于普通请求的高优先级请求。 + diff --git a/Documentation/translations/zh_CN/gpio.txt b/Documentation/translations/zh_CN/gpio.txt new file mode 100644 index 000000000..a23ee14fc --- /dev/null +++ b/Documentation/translations/zh_CN/gpio.txt @@ -0,0 +1,650 @@ +Chinese translated version of Documentation/admin-guide/gpio + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Grant Likely <grant.likely@secretlab.ca> + Linus Walleij <linus.walleij@linaro.org> +Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> +--------------------------------------------------------------------- +Documentation/admin-guide/gpio 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 +英文版维护者: Grant Likely <grant.likely@secretlab.ca> + Linus Walleij <linus.walleij@linaro.org> +中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- +GPIO 接口 + +本文档提供了一个在Linux下访问GPIO的公约概述。 + +这些函数以 gpio_* 作为前缀。其他的函数不允许使用这样的前缀或相关的 +__gpio_* 前缀。 + + +什么是GPIO? +========== +"通用输入/输出口"(GPIO)是一个灵活的由软件控制的数字信号。他们可 +由多种芯片提供,且对于从事嵌入式和定制硬件的 Linux 开发者来说是 +比较熟悉。每个GPIO 都代表一个连接到特定引脚或球栅阵列(BGA)封装中 +“球珠”的一个位。电路板原理图显示了 GPIO 与外部硬件的连接关系。 +驱动可以编写成通用代码,以使板级启动代码可传递引脚配置数据给驱动。 + +片上系统 (SOC) 处理器对 GPIO 有很大的依赖。在某些情况下,每个 +非专用引脚都可配置为 GPIO,且大多数芯片都最少有一些 GPIO。 +可编程逻辑器件(类似 FPGA) 可以方便地提供 GPIO。像电源管理和 +音频编解码器这样的多功能芯片经常留有一些这样的引脚来帮助那些引脚 +匮乏的 SOC。同时还有通过 I2C 或 SPI 串行总线连接的“GPIO扩展器” +芯片。大多数 PC 的南桥有一些拥有 GPIO 能力的引脚 (只有BIOS +固件才知道如何使用他们)。 + +GPIO 的实际功能因系统而异。通常用法有: + + - 输出值可写 (高电平=1,低电平=0)。一些芯片也有如何驱动这些值的选项, + 例如只允许输出一个值、支持“线与”及其他取值类似的模式(值得注意的是 + “开漏”信号) + + - 输入值可读(1、0)。一些芯片支持引脚在配置为“输出”时回读,这对于类似 + “线与”的情况(以支持双向信号)是非常有用的。GPIO 控制器可能有输入 + 去毛刺/消抖逻辑,这有时需要软件控制。 + + - 输入通常可作为 IRQ 信号,一般是沿触发,但有时是电平触发。这样的 IRQ + 可能配置为系统唤醒事件,以将系统从低功耗状态下唤醒。 + + - 通常一个 GPIO 根据不同产品电路板的需求,可以配置为输入或输出,也有仅 + 支持单向的。 + + - 大部分 GPIO 可以在持有自旋锁时访问,但是通常由串行总线扩展的 GPIO + 不允许持有自旋锁。但某些系统也支持这种类型。 + +对于给定的电路板,每个 GPIO 都用于某个特定的目的,如监控 MMC/SD 卡的 +插入/移除、检测卡的写保护状态、驱动 LED、配置收发器、模拟串行总线、 +复位硬件看门狗、感知开关状态等等。 + + +GPIO 公约 +========= +注意,这个叫做“公约”,因为这不是强制性的,不遵循这个公约是无伤大雅的, +因为此时可移植性并不重要。GPIO 常用于板级特定的电路逻辑,甚至可能 +随着电路板的版本而改变,且不可能在不同走线的电路板上使用。仅有在少数 +功能上才具有可移植性,其他功能是平台特定。这也是由于“胶合”的逻辑造成的。 + +此外,这不需要任何的执行框架,只是一个接口。某个平台可能通过一个简单地 +访问芯片寄存器的内联函数来实现它,其他平台可能通过委托一系列不同的GPIO +控制器的抽象函数来实现它。(有一些可选的代码能支持这种策略的实现,本文档 +后面会介绍,但作为 GPIO 接口的客户端驱动程序必须与它的实现无关。) + +也就是说,如果在他们的平台上支持这个公约,驱动应尽可能的使用它。同时,平台 +必须在 Kconfig 中选择 ARCH_REQUIRE_GPIOLIB 或者 ARCH_WANT_OPTIONAL_GPIOLIB +选项。那些调用标准 GPIO 函数的驱动应该在 Kconfig 入口中声明依赖GENERIC_GPIO。 +当驱动包含文件: + + #include <linux/gpio.h> + +则 GPIO 函数是可用,无论是“真实代码”还是经优化过的语句。如果你遵守 +这个公约,当你的代码完成后,对其他的开发者来说会更容易看懂和维护。 + +注意,这些操作包含所用平台的 I/O 屏障代码,驱动无须显式地调用他们。 + + +标识 GPIO +--------- +GPIO 是通过无符号整型来标识的,范围是 0 到 MAX_INT。保留“负”数 +用于其他目的,例如标识信号“在这个板子上不可用”或指示错误。未接触底层 +硬件的代码会忽略这些整数。 + +平台会定义这些整数的用法,且通常使用 #define 来定义 GPIO,这样 +板级特定的启动代码可以直接关联相应的原理图。相对来说,驱动应该仅使用 +启动代码传递过来的 GPIO 编号,使用 platform_data 保存板级特定 +引脚配置数据 (同时还有其他须要的板级特定数据),避免可能出现的问题。 + +例如一个平台使用编号 32-159 来标识 GPIO,而在另一个平台使用编号0-63 +标识一组 GPIO 控制器,64-79标识另一类 GPIO 控制器,且在一个含有 +FPGA 的特定板子上使用 80-95。编号不一定要连续,那些平台中,也可以 +使用编号2000-2063来标识一个 I2C 接口的 GPIO 扩展器中的 GPIO。 + +如果你要初始化一个带有无效 GPIO 编号的结构体,可以使用一些负编码 +(如"-EINVAL"),那将使其永远不会是有效。来测试这样一个结构体中的编号 +是否关联一个 GPIO,你可使用以下断言: + + int gpio_is_valid(int number); + +如果编号不存在,则请求和释放 GPIO 的函数将拒绝执行相关操作(见下文)。 +其他编号也可能被拒绝,比如一个编号可能存在,但暂时在给定的电路上不可用。 + +一个平台是否支持多个 GPIO 控制器为平台特定的实现问题,就像是否可以 +在 GPIO 编号空间中有“空洞”和是否可以在运行时添加新的控制器一样。 +这些问题会影响其他事情,包括相邻的 GPIO 编号是否存在等。 + +使用 GPIO +--------- +对于一个 GPIO,系统应该做的第一件事情就是通过 gpio_request() +函数分配它,见下文。 + +接下来是设置I/O方向,这通常是在板级启动代码中为所使用的 GPIO 设置 +platform_device 时完成。 + + /* 设置为输入或输出, 返回 0 或负的错误代码 */ + int gpio_direction_input(unsigned gpio); + int gpio_direction_output(unsigned gpio, int value); + +返回值为零代表成功,否则返回一个负的错误代码。这个返回值需要检查,因为 +get/set(获取/设置)函数调用没法返回错误,且有可能是配置错误。通常, +你应该在进程上下文中调用这些函数。然而,对于自旋锁安全的 GPIO,在板子 +启动的早期、进程启动前使用他们也是可以的。 + +对于作为输出的 GPIO,为其提供初始输出值,对于避免在系统启动期间出现 +信号毛刺是很有帮助的。 + +为了与传统的 GPIO 接口兼容, 在设置一个 GPIO 方向时,如果它还未被申请, +则隐含了申请那个 GPIO 的操作(见下文)。这种兼容性正在从可选的 gpiolib +框架中移除。 + +如果这个 GPIO 编码不存在,或者特定的 GPIO 不能用于那种模式,则方向 +设置可能失败。依赖启动固件来正确地设置方向通常是一个坏主意,因为它可能 +除了启动Linux,并没有做更多的验证工作。(同理, 板子的启动代码可能需要 +将这个复用的引脚设置为 GPIO,并正确地配置上拉/下拉电阻。) + + +访问自旋锁安全的 GPIO +------------------- +大多数 GPIO 控制器可以通过内存读/写指令来访问。这些指令不会休眠,可以 +安全地在硬(非线程)中断例程和类似的上下文中完成。 + +对于那些用 gpio_cansleep()测试总是返回失败的 GPIO(见下文),使用 +以下的函数访问: + + /* GPIO 输入:返回零或非零 */ + int gpio_get_value(unsigned gpio); + + /* GPIO 输出 */ + void gpio_set_value(unsigned gpio, int value); + +GPIO值是布尔值,零表示低电平,非零表示高电平。当读取一个输出引脚的值时, +返回值应该是引脚上的值。这个值不总是和输出值相符,因为存在开漏输出信号和 +输出延迟问题。 + +以上的 get/set 函数无错误返回值,因为之前 gpio_direction_*()应已检查过 +其是否为“无效GPIO”。此外,还需要注意的是并不是所有平台都可以从输出引脚 +中读取数据,对于不能读取的引脚应总返回零。另外,对那些在原子上下文中无法 +安全访问的 GPIO (译者注:因为访问可能导致休眠)使用这些函数是不合适的 +(见下文)。 + +在 GPIO 编号(还有输出、值)为常数的情况下,鼓励通过平台特定的实现来优化 +这两个函数来访问 GPIO 值。这种情况(读写一个硬件寄存器)下只需要几条指令 +是很正常的,且无须自旋锁。这种优化函数比起那些在子程序上花费许多指令的 +函数可以使得模拟接口(译者注:例如 GPIO 模拟 I2C、1-wire 或 SPI)的 +应用(在空间和时间上都)更具效率。 + + +访问可能休眠的 GPIO +----------------- +某些 GPIO 控制器必须通过基于总线(如 I2C 或 SPI)的消息访问。读或写这些 +GPIO 值的命令需要等待其信息排到队首才发送命令,再获得其反馈。期间需要 +休眠,这不能在 IRQ 例程(中断上下文)中执行。 + +支持此类 GPIO 的平台通过以下函数返回非零值来区分出这种 GPIO。(此函数需要 +一个之前通过 gpio_request 分配到的有效 GPIO 编号): + + int gpio_cansleep(unsigned gpio); + +为了访问这种 GPIO,内核定义了一套不同的函数: + + /* GPIO 输入:返回零或非零 ,可能会休眠 */ + int gpio_get_value_cansleep(unsigned gpio); + + /* GPIO 输出,可能会休眠 */ + void gpio_set_value_cansleep(unsigned gpio, int value); + + +访问这样的 GPIO 需要一个允许休眠的上下文,例如线程 IRQ 处理例程,并用以上的 +访问函数替换那些没有 cansleep()后缀的自旋锁安全访问函数。 + +除了这些访问函数可能休眠,且它们操作的 GPIO 不能在硬件 IRQ 处理例程中访问的 +事实,这些处理例程实际上和自旋锁安全的函数是一样的。 + +** 除此之外 ** 调用设置和配置此类 GPIO 的函数也必须在允许休眠的上下文中, +因为它们可能也需要访问 GPIO 控制器芯片: (这些设置函数通常在板级启动代码或者 +驱动探测/断开代码中,所以这是一个容易满足的约束条件。) + + gpio_direction_input() + gpio_direction_output() + gpio_request() + +## gpio_request_one() +## gpio_request_array() +## gpio_free_array() + + gpio_free() + gpio_set_debounce() + + + +声明和释放 GPIO +---------------------------- +为了有助于捕获系统配置错误,定义了两个函数。 + + /* 申请 GPIO, 返回 0 或负的错误代码. + * 非空标签可能有助于诊断. + */ + int gpio_request(unsigned gpio, const char *label); + + /* 释放之前声明的 GPIO */ + void gpio_free(unsigned gpio); + +将无效的 GPIO 编码传递给 gpio_request()会导致失败,申请一个已使用这个 +函数声明过的 GPIO 也会失败。gpio_request()的返回值必须检查。你应该在 +进程上下文中调用这些函数。然而,对于自旋锁安全的 GPIO,在板子启动的早期、 +进入进程之前是可以申请的。 + +这个函数完成两个基本的目标。一是标识那些实际上已作为 GPIO 使用的信号线, +这样便于更好地诊断;系统可能需要服务几百个可用的 GPIO,但是对于任何一个 +给定的电路板通常只有一些被使用。另一个目的是捕获冲突,查明错误:如两个或 +更多驱动错误地认为他们已经独占了某个信号线,或是错误地认为移除一个管理着 +某个已激活信号的驱动是安全的。也就是说,申请 GPIO 的作用类似一种锁机制。 + +某些平台可能也使用 GPIO 作为电源管理激活信号(例如通过关闭未使用芯片区和 +简单地关闭未使用时钟)。 + +对于 GPIO 使用 pinctrl 子系统已知的引脚,子系统应该被告知其使用情况; +一个 gpiolib 驱动的 .request()操作应调用 pinctrl_gpio_request(), +而 gpiolib 驱动的 .free()操作应调用 pinctrl_gpio_free()。pinctrl +子系统允许 pinctrl_gpio_request()在某个引脚或引脚组以复用形式“属于” +一个设备时都成功返回。 + +任何须将 GPIO 信号导向适当引脚的引脚复用硬件的编程应该发生在 GPIO +驱动的 .direction_input()或 .direction_output()函数中,以及 +任何输出 GPIO 值的设置之后。这样可使从引脚特殊功能到 GPIO 的转换 +不会在引脚产生毛刺波形。有时当用一个 GPIO 实现其信号驱动一个非 GPIO +硬件模块的解决方案时,就需要这种机制。 + +某些平台允许部分或所有 GPIO 信号使用不同的引脚。类似的,GPIO 或引脚的 +其他方面也需要配置,如上拉/下拉。平台软件应该在对这些 GPIO 调用 +gpio_request()前将这类细节配置好,例如使用 pinctrl 子系统的映射表, +使得 GPIO 的用户无须关注这些细节。 + +还有一个值得注意的是在释放 GPIO 前,你必须停止使用它。 + + +注意:申请一个 GPIO 并没有以任何方式配置它,只不过标识那个 GPIO 处于使用 +状态。必须有另外的代码来处理引脚配置(如控制 GPIO 使用的引脚、上拉/下拉)。 +考虑到大多数情况下声明 GPIO 之后就会立即配置它们,所以定义了以下三个辅助函数: + + /* 申请一个 GPIO 信号, 同时通过特定的'flags'初始化配置, + * 其他和 gpio_request()的参数和返回值相同 + * + */ + int gpio_request_one(unsigned gpio, unsigned long flags, const char *label); + + /* 在单个函数中申请多个 GPIO + */ + int gpio_request_array(struct gpio *array, size_t num); + + /* 在单个函数中释放多个 GPIO + */ + void gpio_free_array(struct gpio *array, size_t num); + +这里 'flags' 当前定义可指定以下属性: + + * GPIOF_DIR_IN - 配置方向为输入 + * GPIOF_DIR_OUT - 配置方向为输出 + + * GPIOF_INIT_LOW - 在作为输出时,初始值为低电平 + * GPIOF_INIT_HIGH - 在作为输出时,初始值为高电平 + * GPIOF_OPEN_DRAIN - gpio引脚为开漏信号 + * GPIOF_OPEN_SOURCE - gpio引脚为源极开路信号 + + * GPIOF_EXPORT_DIR_FIXED - 将 gpio 导出到 sysfs,并保持方向 + * GPIOF_EXPORT_DIR_CHANGEABLE - 同样是导出, 但允许改变方向 + +因为 GPIOF_INIT_* 仅有在配置为输出的时候才存在,所以有效的组合为: + + * GPIOF_IN - 配置为输入 + * GPIOF_OUT_INIT_LOW - 配置为输出,并初始化为低电平 + * GPIOF_OUT_INIT_HIGH - 配置为输出,并初始化为高电平 + +当设置 flag 为 GPIOF_OPEN_DRAIN 时,则假设引脚是开漏信号。这样的引脚 +将不会在输出模式下置1。这样的引脚需要连接上拉电阻。通过使能这个标志,gpio库 +将会在被要求输出模式下置1时将引脚变为输入状态来使引脚置高。引脚在输出模式下 +通过置0使其输出低电平。 + +当设置 flag 为 GPIOF_OPEN_SOURCE 时,则假设引脚为源极开路信号。这样的引脚 +将不会在输出模式下置0。这样的引脚需要连接下拉电阻。通过使能这个标志,gpio库 +将会在被要求输出模式下置0时将引脚变为输入状态来使引脚置低。引脚在输出模式下 +通过置1使其输出高电平。 + +将来这些标志可能扩展到支持更多的属性。 + +更进一步,为了更简单地声明/释放多个 GPIO,'struct gpio'被引进来封装所有 +这三个领域: + + struct gpio { + unsigned gpio; + unsigned long flags; + const char *label; + }; + +一个典型的用例: + + static struct gpio leds_gpios[] = { + { 32, GPIOF_OUT_INIT_HIGH, "Power LED" }, /* 默认开启 */ + { 33, GPIOF_OUT_INIT_LOW, "Green LED" }, /* 默认关闭 */ + { 34, GPIOF_OUT_INIT_LOW, "Red LED" }, /* 默认关闭 */ + { 35, GPIOF_OUT_INIT_LOW, "Blue LED" }, /* 默认关闭 */ + { ... }, + }; + + err = gpio_request_one(31, GPIOF_IN, "Reset Button"); + if (err) + ... + + err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios)); + if (err) + ... + + gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios)); + + +GPIO 映射到 IRQ +-------------------- +GPIO 编号是无符号整数;IRQ 编号也是。这些构成了两个逻辑上不同的命名空间 +(GPIO 0 不一定使用 IRQ 0)。你可以通过以下函数在它们之间实现映射: + + /* 映射 GPIO 编号到 IRQ 编号 */ + int gpio_to_irq(unsigned gpio); + + /* 映射 IRQ 编号到 GPIO 编号 (尽量避免使用) */ + int irq_to_gpio(unsigned irq); + +它们的返回值为对应命名空间的相关编号,或是负的错误代码(如果无法映射)。 +(例如,某些 GPIO 无法做为 IRQ 使用。)以下的编号错误是未经检测的:使用一个 +未通过 gpio_direction_input()配置为输入的 GPIO 编号,或者使用一个 +并非来源于gpio_to_irq()的 IRQ 编号。 + +这两个映射函数可能会在信号编号的加减计算过程上花些时间。它们不可休眠。 + +gpio_to_irq()返回的非错误值可以传递给 request_irq()或者 free_irq()。 +它们通常通过板级特定的初始化代码存放到平台设备的 IRQ 资源中。注意:IRQ +触发选项是 IRQ 接口的一部分,如 IRQF_TRIGGER_FALLING,系统唤醒能力 +也是如此。 + +irq_to_gpio()返回的非错误值大多数通常可以被 gpio_get_value()所使用, +比如在 IRQ 是沿触发时初始化或更新驱动状态。注意某些平台不支持反映射,所以 +你应该尽量避免使用它。 + + +模拟开漏信号 +---------------------------- +有时在只有低电平信号作为实际驱动结果(译者注:多个输出连接于一点,逻辑电平 +结果为所有输出的逻辑与)的时候,共享的信号线需要使用“开漏”信号。(该术语 +适用于 CMOS 管;而 TTL 用“集电极开路”。)一个上拉电阻使信号为高电平。这 +有时被称为“线与”。实际上,从负逻辑(低电平为真)的角度来看,这是一个“线或”。 + +一个开漏信号的常见例子是共享的低电平使能 IRQ 信号线。此外,有时双向数据总线 +信号也使用漏极开路信号。 + +某些 GPIO 控制器直接支持开漏输出,还有许多不支持。当你需要开漏信号,但 +硬件又不直接支持的时候,一个常用的方法是用任何即可作输入也可作输出的 GPIO +引脚来模拟: + + LOW: gpio_direction_output(gpio, 0) ... 这代码驱动信号并覆盖 + 上拉配置。 + + HIGH: gpio_direction_input(gpio) ... 这代码关闭输出,所以上拉电阻 + (或其他的一些器件)控制了信号。 + +如果你将信号线“驱动”为高电平,但是 gpio_get_value(gpio)报告了一个 +低电平(在适当的上升时间后),你就可以知道是其他的一些组件将共享信号线拉低了。 +这不一定是错误的。一个常见的例子就是 I2C 时钟的延长:一个需要较慢时钟的 +从设备延迟 SCK 的上升沿,而 I2C 主设备相应地调整其信号传输速率。 + + +这些公约忽略了什么? +================ +这些公约忽略的最大一件事就是引脚复用,因为这属于高度芯片特定的属性且 +没有可移植性。某个平台可能不需要明确的复用信息;有的对于任意给定的引脚 +可能只有两个功能选项;有的可能每个引脚有八个功能选项;有的可能可以将 +几个引脚中的任何一个作为给定的 GPIO。(是的,这些例子都来自于当前运行 +Linux 的系统。) + +在某些系统中,与引脚复用相关的是配置和使能集成的上、下拉模式。并不是所有 +平台都支持这种模式,或者不会以相同的方式来支持这种模式;且任何给定的电路板 +可能使用外置的上拉(或下拉)电阻,这时芯片上的就不应该使用。(当一个电路需要 +5kOhm 的拉动电阻,芯片上的 100 kOhm 电阻就不能做到。)同样的,驱动能力 +(2 mA vs 20 mA)和电压(1.8V vs 3.3V)是平台特定问题,就像模型一样在 +可配置引脚和 GPIO 之间(没)有一一对应的关系。 + +还有其他一些系统特定的机制没有在这里指出,例如上述的输入去毛刺和线与输出 +选项。硬件可能支持批量读或写 GPIO,但是那一般是配置相关的:对于处于同一 +块区(bank)的GPIO。(GPIO 通常以 16 或 32 个组成一个区块,一个给定的 +片上系统一般有几个这样的区块。)某些系统可以通过输出 GPIO 触发 IRQ, +或者从并非以 GPIO 管理的引脚取值。这些机制的相关代码没有必要具有可移植性。 + +当前,动态定义 GPIO 并不是标准的,例如作为配置一个带有某些 GPIO 扩展器的 +附加电路板的副作用。 + +GPIO 实现者的框架 (可选) +===================== +前面提到了,有一个可选的实现框架,让平台使用相同的编程接口,更加简单地支持 +不同种类的 GPIO 控制器。这个框架称为"gpiolib"。 + +作为一个辅助调试功能,如果 debugfs 可用,就会有一个 /sys/kernel/debug/gpio +文件。通过这个框架,它可以列出所有注册的控制器,以及当前正在使用中的 GPIO +的状态。 + + +控制器驱动: gpio_chip +------------------- +在框架中每个 GPIO 控制器都包装为一个 "struct gpio_chip",他包含了 +该类型的每个控制器的常用信息: + + - 设置 GPIO 方向的方法 + - 用于访问 GPIO 值的方法 + - 告知调用其方法是否可能休眠的标志 + - 可选的 debugfs 信息导出方法 (显示类似上拉配置一样的额外状态) + - 诊断标签 + +也包含了来自 device.platform_data 的每个实例的数据:它第一个 GPIO 的 +编号和它可用的 GPIO 的数量。 + +实现 gpio_chip 的代码应支持多控制器实例,这可能使用驱动模型。那些代码要 +配置每个 gpio_chip,并发起gpiochip_add()。卸载一个 GPIO 控制器很少见, +但在必要的时候可以使用 gpiochip_remove()。 + +大部分 gpio_chip 是一个实例特定结构体的一部分,而并不将 GPIO 接口单独 +暴露出来,比如编址、电源管理等。类似编解码器这样的芯片会有复杂的非 GPIO +状态。 + +任何一个 debugfs 信息导出方法通常应该忽略还未申请作为 GPIO 的信号线。 +他们可以使用 gpiochip_is_requested()测试,当这个 GPIO 已经申请过了 +就返回相关的标签,否则返回 NULL。 + + +平台支持 +------- +为了支持这个框架,一个平台的 Kconfig 文件将会 "select"(选择) +ARCH_REQUIRE_GPIOLIB 或 ARCH_WANT_OPTIONAL_GPIOLIB,并让它的 +<asm/gpio.h> 包含 <asm-generic/gpio.h>,同时定义三个方法: +gpio_get_value()、gpio_set_value()和 gpio_cansleep()。 + +它也应提供一个 ARCH_NR_GPIOS 的定义值,这样可以更好地反映该平台 GPIO +的实际数量,节省静态表的空间。(这个定义值应该包含片上系统内建 GPIO 和 +GPIO 扩展器中的数据。) + +ARCH_REQUIRE_GPIOLIB 意味着 gpiolib 核心在这个构架中将总是编译进内核。 + +ARCH_WANT_OPTIONAL_GPIOLIB 意味着 gpiolib 核心默认关闭,且用户可以 +使能它,并将其编译进内核(可选)。 + +如果这些选项都没被选择,该平台就不通过 GPIO-lib 支持 GPIO,且代码不可以 +被用户使能。 + +以下这些方法的实现可以直接使用框架代码,并总是通过 gpio_chip 调度: + + #define gpio_get_value __gpio_get_value + #define gpio_set_value __gpio_set_value + #define gpio_cansleep __gpio_cansleep + +这些定义可以用更理想的实现方法替代,那就是使用经过逻辑优化的内联函数来访问 +基于特定片上系统的 GPIO。例如,若引用的 GPIO (寄存器位偏移)是常量“12”, +读取或设置它可能只需少则两或三个指令,且不会休眠。当这样的优化无法实现时, +那些函数必须使用框架提供的代码,那就至少要几十条指令才可以实现。对于用 GPIO +模拟的 I/O 接口, 如此精简指令是很有意义的。 + +对于片上系统,平台特定代码为片上 GPIO 每个区(bank)定义并注册 gpio_chip +实例。那些 GPIO 应该根据芯片厂商的文档进行编码/标签,并直接和电路板原理图 +对应。他们应该开始于零并终止于平台特定的限制。这些 GPIO(代码)通常从 +arch_initcall()或者更早的地方集成进平台初始化代码,使这些 GPIO 总是可用, +且他们通常可以作为 IRQ 使用。 + +板级支持 +------- +对于外部 GPIO 控制器(例如 I2C 或 SPI 扩展器、专用芯片、多功能器件、FPGA +或 CPLD),大多数常用板级特定代码都可以注册控制器设备,并保证他们的驱动知道 +gpiochip_add()所使用的 GPIO 编号。他们的起始编号通常跟在平台特定的 GPIO +编号之后。 + +例如板级启动代码应该创建结构体指明芯片公开的 GPIO 范围,并使用 platform_data +将其传递给每个 GPIO 扩展器芯片。然后芯片驱动中的 probe()例程可以将这个 +数据传递给 gpiochip_add()。 + +初始化顺序很重要。例如,如果一个设备依赖基于 I2C 的(扩展)GPIO,那么它的 +probe()例程就应该在那个 GPIO 有效以后才可以被调用。这意味着设备应该在 +GPIO 可以工作之后才可被注册。解决这类依赖的的一种方法是让这种 gpio_chip +控制器向板级特定代码提供 setup()和 teardown()回调函数。一旦所有必须的 +资源可用之后,这些板级特定的回调函数将会注册设备,并可以在这些 GPIO 控制器 +设备变成无效时移除它们。 + + +用户空间的 Sysfs 接口(可选) +======================== +使用“gpiolib”实现框架的平台可以选择配置一个 GPIO 的 sysfs 用户接口。 +这不同于 debugfs 接口,因为它提供的是对 GPIO方向和值的控制,而不只显示 +一个GPIO 的状态摘要。此外,它可以出现在没有调试支持的产品级系统中。 + +例如,通过适当的系统硬件文档,用户空间可以知道 GIOP #23 控制 Flash +存储器的写保护(用于保护其中 Bootloader 分区)。产品的系统升级可能需要 +临时解除这个保护:首先导入一个 GPIO,改变其输出状态,然后在重新使能写保护 +前升级代码。通常情况下,GPIO #23 是不会被触及的,并且内核也不需要知道他。 + +根据适当的硬件文档,某些系统的用户空间 GPIO 可以用于确定系统配置数据, +这些数据是标准内核不知道的。在某些任务中,简单的用户空间 GPIO 驱动可能是 +系统真正需要的。 + +注意:标准内核驱动中已经存在通用的“LED 和按键”GPIO 任务,分别是: +"leds-gpio" 和 "gpio_keys"。请使用这些来替代直接访问 GPIO,因为集成在 +内核框架中的这类驱动比你在用户空间的代码更好。 + + +Sysfs 中的路径 +-------------- +在/sys/class/gpio 中有 3 类入口: + + - 用于在用户空间控制 GPIO 的控制接口; + + - GPIOs 本身;以及 + + - GPIO 控制器 ("gpio_chip" 实例)。 + +除了这些标准的文件,还包含“device”符号链接。 + +控制接口是只写的: + + /sys/class/gpio/ + + "export" ... 用户空间可以通过写其编号到这个文件,要求内核导出 + 一个 GPIO 的控制到用户空间。 + + 例如: 如果内核代码没有申请 GPIO #19,"echo 19 > export" + 将会为 GPIO #19 创建一个 "gpio19" 节点。 + + "unexport" ... 导出到用户空间的逆操作。 + + 例如: "echo 19 > unexport" 将会移除使用"export"文件导出的 + "gpio19" 节点。 + +GPIO 信号的路径类似 /sys/class/gpio/gpio42/ (对于 GPIO #42 来说), +并有如下的读/写属性: + + /sys/class/gpio/gpioN/ + + "direction" ... 读取得到 "in" 或 "out"。这个值通常运行写入。 + 写入"out" 时,其引脚的默认输出为低电平。为了确保无故障运行, + "low" 或 "high" 的电平值应该写入 GPIO 的配置,作为初始输出值。 + + 注意:如果内核不支持改变 GPIO 的方向,或者在导出时内核代码没有 + 明确允许用户空间可以重新配置 GPIO 方向,那么这个属性将不存在。 + + "value" ... 读取得到 0 (低电平) 或 1 (高电平)。如果 GPIO 配置为 + 输出,这个值允许写操作。任何非零值都以高电平看待。 + + 如果引脚可以配置为中断信号,且如果已经配置了产生中断的模式 + (见"edge"的描述),你可以对这个文件使用轮询操作(poll(2)), + 且轮询操作会在任何中断触发时返回。如果你使用轮询操作(poll(2)), + 请在 events 中设置 POLLPRI 和 POLLERR。如果你使用轮询操作 + (select(2)),请在 exceptfds 设置你期望的文件描述符。在 + 轮询操作(poll(2))返回之后,既可以通过 lseek(2)操作读取 + sysfs 文件的开始部分,也可以关闭这个文件并重新打开它来读取数据。 + + "edge" ... 读取得到“none”、“rising”、“falling”或者“both”。 + 将这些字符串写入这个文件可以选择沿触发模式,会使得轮询操作 + (select(2))在"value"文件中返回。 + + 这个文件仅有在这个引脚可以配置为可产生中断输入引脚时,才存在。 + + "active_low" ... 读取得到 0 (假) 或 1 (真)。写入任何非零值可以 + 翻转这个属性的(读写)值。已存在或之后通过"edge"属性设置了"rising" + 和 "falling" 沿触发模式的轮询操作(poll(2))将会遵循这个设置。 + +GPIO 控制器的路径类似 /sys/class/gpio/gpiochip42/ (对于从#42 GPIO +开始实现控制的控制器),并有着以下只读属性: + + /sys/class/gpio/gpiochipN/ + + "base" ... 与以上的 N 相同,代表此芯片管理的第一个 GPIO 的编号 + + "label" ... 用于诊断 (并不总是只有唯一值) + + "ngpio" ... 此控制器所管理的 GPIO 数量(而 GPIO 编号从 N 到 + N + ngpio - 1) + +大多数情况下,电路板的文档应当标明每个 GPIO 的使用目的。但是那些编号并不总是 +固定的,例如在扩展卡上的 GPIO会根据所使用的主板或所在堆叠架构中其他的板子而 +有所不同。在这种情况下,你可能需要使用 gpiochip 节点(尽可能地结合电路图)来 +确定给定信号所用的 GPIO 编号。 + + +从内核代码中导出 +------------- +内核代码可以明确地管理那些已通过 gpio_request()申请的 GPIO 的导出: + + /* 导出 GPIO 到用户空间 */ + int gpio_export(unsigned gpio, bool direction_may_change); + + /* gpio_export()的逆操作 */ + void gpio_unexport(); + + /* 创建一个 sysfs 连接到已导出的 GPIO 节点 */ + int gpio_export_link(struct device *dev, const char *name, + unsigned gpio) + +在一个内核驱动申请一个 GPIO 之后,它可以通过 gpio_export()使其在 sysfs +接口中可见。该驱动可以控制信号方向是否可修改。这有助于防止用户空间代码无意间 +破坏重要的系统状态。 + +这个明确的导出有助于(通过使某些实验更容易来)调试,也可以提供一个始终存在的接口, +与文档配合作为板级支持包的一部分。 + +在 GPIO 被导出之后,gpio_export_link()允许在 sysfs 文件系统的任何地方 +创建一个到这个 GPIO sysfs 节点的符号链接。这样驱动就可以通过一个描述性的 +名字,在 sysfs 中他们所拥有的设备下提供一个(到这个 GPIO sysfs 节点的)接口。 diff --git a/Documentation/translations/zh_CN/index.rst b/Documentation/translations/zh_CN/index.rst new file mode 100644 index 000000000..be6f11176 --- /dev/null +++ b/Documentation/translations/zh_CN/index.rst @@ -0,0 +1,27 @@ +.. raw:: latex + + \renewcommand\thesection* + \renewcommand\thesubsection* + +中文翻译 +======== + +这些手册包含有关如何开发内核的整体信息。内核社区非常庞大,一年下来有数千名开发 +人员做出贡献。 与任何大型社区一样,知道如何完成任务将使得更改合并的过程变得更 +加容易。 + +翻译计划: +内核中文文档欢迎任何翻译投稿,特别是关于内核用户和管理员指南部分。 + +.. toctree:: + :maxdepth: 2 + + admin-guide/index + process/index + filesystems/index + arm64/index + +目录和表格 +---------- + +* :ref:`genindex` diff --git a/Documentation/translations/zh_CN/io_ordering.txt b/Documentation/translations/zh_CN/io_ordering.txt new file mode 100644 index 000000000..7bb308622 --- /dev/null +++ b/Documentation/translations/zh_CN/io_ordering.txt @@ -0,0 +1,67 @@ +Chinese translated version of Documentation/driver-api/io_ordering.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Chinese maintainer: Lin Yongting <linyongting@gmail.com> +--------------------------------------------------------------------- +Documentation/driver-api/io_ordering.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +中文版维护者: 林永听 Lin Yongting <linyongting@gmail.com> +中文版翻译者: 林永听 Lin Yongting <linyongting@gmail.com> +中文版校译者: 林永听 Lin Yongting <linyongting@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- + +在某些平台上,所谓的内存映射I/O是弱顺序。在这些平台上,驱动开发者有责任 +保证I/O内存映射地址的写操作按程序图意的顺序达到设备。通常读取一个“安全” +设备寄存器或桥寄存器,触发IO芯片清刷未处理的写操作到达设备后才处理读操作, +而达到保证目的。驱动程序通常在spinlock保护的临界区退出之前使用这种技术。 +这也可以保证后面的写操作只在前面的写操作之后到达设备(这非常类似于内存 +屏障操作,mb(),不过仅适用于I/O)。 + +假设一个设备驱动程的具体例子: + + ... +CPU A: spin_lock_irqsave(&dev_lock, flags) +CPU A: val = readl(my_status); +CPU A: ... +CPU A: writel(newval, ring_ptr); +CPU A: spin_unlock_irqrestore(&dev_lock, flags) + ... +CPU B: spin_lock_irqsave(&dev_lock, flags) +CPU B: val = readl(my_status); +CPU B: ... +CPU B: writel(newval2, ring_ptr); +CPU B: spin_unlock_irqrestore(&dev_lock, flags) + ... + +上述例子中,设备可能会先接收到newval2的值,然后接收到newval的值,问题就 +发生了。不过很容易通过下面方法来修复: + + ... +CPU A: spin_lock_irqsave(&dev_lock, flags) +CPU A: val = readl(my_status); +CPU A: ... +CPU A: writel(newval, ring_ptr); +CPU A: (void)readl(safe_register); /* 配置寄存器?*/ +CPU A: spin_unlock_irqrestore(&dev_lock, flags) + ... +CPU B: spin_lock_irqsave(&dev_lock, flags) +CPU B: val = readl(my_status); +CPU B: ... +CPU B: writel(newval2, ring_ptr); +CPU B: (void)readl(safe_register); /* 配置寄存器?*/ +CPU B: spin_unlock_irqrestore(&dev_lock, flags) + +在解决方案中,读取safe_register寄存器,触发IO芯片清刷未处理的写操作, +再处理后面的读操作,防止引发数据不一致问题。 diff --git a/Documentation/translations/zh_CN/oops-tracing.txt b/Documentation/translations/zh_CN/oops-tracing.txt new file mode 100644 index 000000000..c5f3bda7a --- /dev/null +++ b/Documentation/translations/zh_CN/oops-tracing.txt @@ -0,0 +1,212 @@ +Chinese translated version of Documentation/admin-guide/bug-hunting.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Chinese maintainer: Dave Young <hidave.darkstar@gmail.com> +--------------------------------------------------------------------- +Documentation/admin-guide/bug-hunting.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +中文版维护者: 杨瑞 Dave Young <hidave.darkstar@gmail.com> +中文版翻译者: 杨瑞 Dave Young <hidave.darkstar@gmail.com> +中文版校译者: 李阳 Li Yang <leoyang.li@nxp.com> + 王聪 Wang Cong <xiyou.wangcong@gmail.com> + +以下为正文 +--------------------------------------------------------------------- + +注意: ksymoops 在2.6中是没有用的。 请以原有格式使用Oops(来自dmesg,等等)。 +忽略任何这样那样关于“解码Oops”或者“通过ksymoops运行”的文档。 如果你贴出运行过 +ksymoops的来自2.6的Oops,人们只会让你重贴一次。 + +快速总结 +------------- + +发现Oops并发送给看似相关的内核领域的维护者。别太担心对不上号。如果你不确定就发给 +和你所做的事情相关的代码的负责人。 如果可重现试着描述怎样重构。 那甚至比oops更有 +价值。 + +如果你对于发送给谁一无所知, 发给linux-kernel@vger.kernel.org。感谢你帮助Linux +尽可能地稳定。 + +Oops在哪里? +---------------------- + +通常Oops文本由klogd从内核缓冲区里读取并传给syslogd,由syslogd写到syslog文件中, +典型地是/var/log/messages(依赖于/etc/syslog.conf)。有时klogd崩溃了,这种情况下你 +能够运行dmesg > file来从内核缓冲区中读取数据并保存下来。 否则你可以 +cat /proc/kmsg > file, 然而你必须介入中止传输, kmsg是一个“永不结束的文件”。如 +果机器崩溃坏到你不能输入命令或者磁盘不可用那么你有三种选择:- + +(1) 手抄屏幕上的文本待机器重启后再输入计算机。 麻烦但如果没有针对崩溃的准备, +这是仅有的选择。 另外,你可以用数码相机把屏幕拍下来-不太好,但比没有强。 如果信 +息滚动到了终端的上面,你会发现以高分辩率启动(比如,vga=791)会让你读到更多的文 +本。(注意:这需要vesafb,所以对‘早期’的oops没有帮助) + +(2)用串口终端启动(请参看Documentation/admin-guide/serial-console.rst),运行一个null +modem到另一台机器并用你喜欢的通讯工具获取输出。Minicom工作地很好。 + +(3)使用Kdump(请参看Documentation/admin-guide/kdump/kdump.rst), +使用在Documentation/admin-guide/kdump/gdbmacros.txt中定义的dmesg gdb宏,从旧的内存中提取内核 +环形缓冲区。 + +完整信息 +---------------- + +注意:以下来自于Linus的邮件适用于2.4内核。 我因为历史原因保留了它,并且因为其中 +一些信息仍然适用。 特别注意的是,请忽略任何ksymoops的引用。 + +From: Linus Torvalds <torvalds@osdl.org> + +怎样跟踪Oops.. [原发到linux-kernel的一封邮件] + +主要的窍门是有五年和这些烦人的oops消息打交道的经验;-) + +实际上,你有办法使它更简单。我有两个不同的方法: + + gdb /usr/src/linux/vmlinux + gdb> disassemble <offending_function> + +那是发现问题的简单办法,至少如果bug报告做的好的情况下(象这个一样-运行ksymoops +得到oops发生的函数及函数内的偏移)。 + +哦,如果报告发生的内核以相同的编译器和相似的配置编译它会有帮助的。 + +另一件要做的事是反汇编bug报告的“Code”部分:ksymoops也会用正确的工具来做这件事, +但如果没有那些工具你可以写一个傻程序: + + char str[] = "\xXX\xXX\xXX..."; + main(){} + +并用gcc -g编译它然后执行“disassemble str”(XX部分是由Oops报告的值-你可以仅剪切 +粘贴并用“\x”替换空格-我就是这么做的,因为我懒得写程序自动做这一切)。 + +另外,你可以用scripts/decodecode这个shell脚本。它的使用方法是: +decodecode < oops.txt + +“Code”之后的十六进制字节可能(在某些架构上)有一些当前指令之前的指令字节以及 +当前和之后的指令字节 + +Code: f9 0f 8d f9 00 00 00 8d 42 0c e8 dd 26 11 c7 a1 60 ea 2b f9 8b 50 08 a1 +64 ea 2b f9 8d 34 82 8b 1e 85 db 74 6d 8b 15 60 ea 2b f9 <8b> 43 04 39 42 54 +7e 04 40 89 42 54 8b 43 04 3b 05 00 f6 52 c0 + +最后,如果你想知道代码来自哪里,你可以: + + cd /usr/src/linux + make fs/buffer.s # 或任何产生BUG的文件 + +然后你会比gdb反汇编更清楚的知道发生了什么。 + +现在,问题是把你所拥有的所有数据结合起来:C源码(关于它应该怎样的一般知识), +汇编代码及其反汇编得到的代码(另外还有从“oops”消息得到的寄存器状态-对了解毁坏的 +指针有用,而且当你有了汇编代码你也能拿其它的寄存器和任何它们对应的C表达式做匹配 +)。 + +实际上,你仅需看看哪里不匹配(这个例子是“Code”反汇编和编译器生成的代码不匹配)。 +然后你须要找出为什么不匹配。通常很简单-你看到代码使用了空指针然后你看代码想知道 +空指针是怎么出现的,还有检查它是否合法.. + +现在,如果明白这是一项耗时的工作而且需要一丁点儿的专心,没错。这就是我为什么大多 +只是忽略那些没有符号表信息的崩溃报告的原因:简单的说太难查找了(我有一些 +程序用于在内核代码段中搜索特定的模式,而且有时我也已经能找出那些崩溃的地方,但是 +仅仅是找出正确的序列也确实需要相当扎实的内核知识) + +_有时_会发生这种情况,我仅看到崩溃中的反汇编代码序列, 然后我马上就明白问题出在 +哪里。这时我才意识到自己干这个工作已经太长时间了;-) + + Linus + + +--------------------------------------------------------------------------- +关于Oops跟踪的注解: + +为了帮助Linus和其它内核开发者,klogd纳入了大量的支持来处理保护错误。为了拥有对 +地址解析的完整支持至少应该使用1.3-pl3的sysklogd包。 + +当保护错误发生时,klogd守护进程自动把内核日志信息中的重要地址翻译成它们相应的符 +号。 + +klogd执行两种类型的地址解析。首先是静态翻译其次是动态翻译。静态翻译和ksymoops +一样使用System.map文件。为了做静态翻译klogd守护进程必须在初始化时能找到system +map文件。关于klogd怎样搜索map文件请参看klogd手册页。 + +动态地址翻译在使用内核可装载模块时很重要。 因为内核模块的内存是从内核动态内存池 +里分配的,所以不管是模块开始位置还是模块中函数和符号的位置都不是固定的。 + +内核支持允许程序决定装载哪些模块和它们在内存中位置的系统调用。使用这些系统调用 +klogd守护进程生成一张符号表用于调试发生在可装载模块中的保护错误。 + +至少klogd会提供产生保护错误的模块名。还可有额外的符号信息供可装载模块开发者选择 +以从模块中输出符号信息。 + +因为内核模块环境可能是动态的,所以必须有一种机制当模块环境发生改变时来通知klogd +守护进程。 有一些可用的命令行选项允许klogd向当前执行中的守护进程发送信号,告知符 +号信息应该被刷新了。 更多信息请参看klogd手册页。 + +sysklogd发布时包含一个补丁修改了modules-2.0.0包,无论何时一个模块装载或者卸载都 +会自动向klogd发送信号。打上这个补丁提供了必要的对调试发生于内核可装载模块的保护 +错误的无缝支持。 + +以下是被klogd处理过的发生在可装载模块中的一个保护错误例子: +--------------------------------------------------------------------------- +Aug 29 09:51:01 blizard kernel: Unable to handle kernel paging request at virtual address f15e97cc +Aug 29 09:51:01 blizard kernel: current->tss.cr3 = 0062d000, %cr3 = 0062d000 +Aug 29 09:51:01 blizard kernel: *pde = 00000000 +Aug 29 09:51:01 blizard kernel: Oops: 0002 +Aug 29 09:51:01 blizard kernel: CPU: 0 +Aug 29 09:51:01 blizard kernel: EIP: 0010:[oops:_oops+16/3868] +Aug 29 09:51:01 blizard kernel: EFLAGS: 00010212 +Aug 29 09:51:01 blizard kernel: eax: 315e97cc ebx: 003a6f80 ecx: 001be77b edx: 00237c0c +Aug 29 09:51:01 blizard kernel: esi: 00000000 edi: bffffdb3 ebp: 00589f90 esp: 00589f8c +Aug 29 09:51:01 blizard kernel: ds: 0018 es: 0018 fs: 002b gs: 002b ss: 0018 +Aug 29 09:51:01 blizard kernel: Process oops_test (pid: 3374, process nr: 21, stackpage=00589000) +Aug 29 09:51:01 blizard kernel: Stack: 315e97cc 00589f98 0100b0b4 bffffed4 0012e38e 00240c64 003a6f80 00000001 +Aug 29 09:51:01 blizard kernel: 00000000 00237810 bfffff00 0010a7fa 00000003 00000001 00000000 bfffff00 +Aug 29 09:51:01 blizard kernel: bffffdb3 bffffed4 ffffffda 0000002b 0007002b 0000002b 0000002b 00000036 +Aug 29 09:51:01 blizard kernel: Call Trace: [oops:_oops_ioctl+48/80] [_sys_ioctl+254/272] [_system_call+82/128] +Aug 29 09:51:01 blizard kernel: Code: c7 00 05 00 00 00 eb 08 90 90 90 90 90 90 90 90 89 ec 5d c3 +--------------------------------------------------------------------------- + +Dr. G.W. Wettstein Oncology Research Div. Computing Facility +Roger Maris Cancer Center INTERNET: greg@wind.rmcc.com +820 4th St. N. +Fargo, ND 58122 +Phone: 701-234-7556 + + +--------------------------------------------------------------------------- +受污染的内核 + +一些oops报告在程序记数器之后包含字符串'Tainted: '。这表明内核已经被一些东西给污 +染了。 该字符串之后紧跟着一系列的位置敏感的字符,每个代表一个特定的污染值。 + + 1:'G'如果所有装载的模块都有GPL或相容的许可证,'P'如果装载了任何的专有模块。 +没有模块MODULE_LICENSE或者带有insmod认为是与GPL不相容的的MODULE_LICENSE的模块被 +认定是专有的。 + + 2:'F'如果有任何通过“insmod -f”被强制装载的模块,' '如果所有模块都被正常装载。 + + 3:'S'如果oops发生在SMP内核中,运行于没有证明安全运行多处理器的硬件。 当前这种 +情况仅限于几种不支持SMP的速龙处理器。 + + 4:'R'如果模块通过“insmod -f”被强制装载,' '如果所有模块都被正常装载。 + + 5:'M'如果任何处理器报告了机器检查异常,' '如果没有发生机器检查异常。 + + 6:'B'如果页释放函数发现了一个错误的页引用或者一些非预期的页标志。 + + 7:'U'如果用户或者用户应用程序特别请求设置污染标志,否则' '。 + + 8:'D'如果内核刚刚死掉,比如有OOPS或者BUG。 + +使用'Tainted: '字符串的主要原因是要告诉内核调试者,这是否是一个干净的内核亦或发 +生了任何的不正常的事。污染是永久的:即使出错的模块已经被卸载了,污染值仍然存在, +以表明内核不再值得信任。 diff --git a/Documentation/translations/zh_CN/process/1.Intro.rst b/Documentation/translations/zh_CN/process/1.Intro.rst new file mode 100644 index 000000000..10a15f3dc --- /dev/null +++ b/Documentation/translations/zh_CN/process/1.Intro.rst @@ -0,0 +1,186 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/1.Intro.rst <development_process_intro>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_process_intro: + +介绍 +==== + +执行摘要 +-------- + +本节的其余部分涵盖了内核开发过程的范围,以及开发人员及其雇主在这方面可能遇 +到的各种挫折。内核代码应该合并到正式的(“主线”)内核中有很多原因,包括对用 +户的自动可用性、多种形式的社区支持以及影响内核开发方向的能力。提供给Linux +内核的代码必须在与GPL兼容的许可证下可用。 + +:ref:`cn_development_process` 介绍了开发过程、内核发布周期和合并窗口的机制。 +涵盖了补丁开发、审查和合并周期中的各个阶段。有一些关于工具和邮件列表的讨论。 +鼓励希望开始内核开发的开发人员作为初始练习跟踪并修复bug。 + + +:ref:`cn_development_early_stage` 包括早期项目规划,重点是尽快让开发社区参与 + +:ref:`cn_development_coding` 是关于编码过程的;讨论了其他开发人员遇到的几个 +陷阱。对补丁的一些要求已经涵盖,并且介绍了一些工具,这些工具有助于确保内核 +补丁是正确的。 + +:ref:`cn_development_posting` 讨论发布补丁以供评审的过程。为了让开发社区 +认真对待,补丁必须正确格式化和描述,并且必须发送到正确的地方。遵循本节中的 +建议有助于确保为您的工作提供最好的接纳。 + +:ref:`cn_development_followthrough` 介绍了发布补丁之后发生的事情;该工作 +在这一点上还远远没有完成。与审阅者一起工作是开发过程中的一个重要部分;本节 +提供了一些关于如何在这个重要阶段避免问题的提示。当补丁被合并到主线中时, +开发人员要注意不要假定任务已经完成。 + +:ref:`cn_development_advancedtopics` 介绍了两个“高级”主题: +使用Git管理补丁和查看其他人发布的补丁。 + +:ref:`cn_development_conclusion` 总结了有关内核开发的更多信息,附带有带有 +指向资源的链接. + +这个文件是关于什么的 +-------------------- + +Linux内核有超过800万行代码,每个版本的贡献者超过1000人,是现存最大、最活跃 +的免费软件项目之一。从1991年开始,这个内核已经发展成为一个最好的操作系统 +组件,运行在袖珍数字音乐播放器、台式PC、现存最大的超级计算机以及所有类型的 +系统上。它是一种适用于几乎任何情况的健壮、高效和可扩展的解决方案。 + +随着Linux的发展,希望参与其开发的开发人员(和公司)的数量也在增加。硬件供应商 +希望确保Linux能够很好地支持他们的产品,使这些产品对Linux用户具有吸引力。嵌入 +式系统供应商使用Linux作为集成产品的组件,希望Linux能够尽可能地胜任手头的任务。 +分销商和其他基于Linux的软件供应商对Linux内核的功能、性能和可靠性有着明确的 +兴趣。最终用户也常常希望修改Linux,使之更好地满足他们的需求。 + +Linux最引人注目的特性之一是这些开发人员可以访问它;任何具备必要技能的人都可以 +改进Linux并影响其开发方向。专有产品不能提供这种开放性,这是自由软件的一个特点。 +但是,如果有什么不同的话,内核比大多数其他自由软件项目更开放。一个典型的三个月 +内核开发周期可以涉及1000多个开发人员,他们为100多个不同的公司 +(或者根本没有公司)工作。 + +与内核开发社区合作并不是特别困难。但是,尽管如此,许多潜在的贡献者在尝试做 +内核工作时遇到了困难。内核社区已经发展了自己独特的操作方式,使其能够在每天 +都要更改数千行代码的环境中顺利运行(并生成高质量的产品)。因此,Linux内核开发 +过程与专有的开发方法有很大的不同也就不足为奇了。 + +对于新开发人员来说,内核的开发过程可能会让人感到奇怪和恐惧,但这个背后有充分的 +理由和坚实的经验。一个不了解内核社区的方式的开发人员(或者更糟的是,他们试图 +抛弃或规避内核社区的方式)会有一个令人沮丧的体验。开发社区, 在帮助那些试图学习 +的人的同时,没有时间帮助那些不愿意倾听或不关心开发过程的人。 + +希望阅读本文的人能够避免这种令人沮丧的经历。这里有很多材料,但阅读时所做的 +努力会在短时间内得到回报。开发社区总是需要能让内核变更好的开发人员;下面的 +文本应该帮助您或为您工作的人员加入我们的社区。 + +致谢 +---- + +本文件由Jonathan Corbet撰写,corbet@lwn.net。以下人员的建议使之更为完善: +Johannes Berg, James Berry, Alex Chiang, Roland Dreier, Randy Dunlap, +Jake Edge, Jiri Kosina, Matt Mackall, Arthur Marsh, Amanda McPherson, +Andrew Morton, Andrew Price, Tsugikazu Shibata, 和 Jochen Voß. + +这项工作得到了Linux基金会的支持,特别感谢Amanda McPherson,他看到了这项工作 +的价值并把它变成现实。 + +代码进入主线的重要性 +-------------------- + +有些公司和开发人员偶尔会想,为什么他们要费心学习如何与内核社区合作,并将代码 +放入主线内核(“主线”是由Linus Torvalds维护的内核,Linux发行商将其用作基础)。 +在短期内,贡献代码看起来像是一种可以避免的开销;仅仅将代码分开并直接支持用户 +似乎更容易。事实上,保持代码独立(“树外”)是在经济上是错误的。 + +作为说明树外代码成本的一种方法,下面是内核开发过程的一些相关方面;本文稍后将 +更详细地讨论其中的大部分内容。考虑: + +- 所有Linux用户都可以使用合并到主线内核中的代码。它将自动出现在所有启用它的 + 发行版上。不需要驱动程序磁盘、下载,也不需要为多个发行版的多个版本提供支持; + 对于开发人员和用户来说,这一切都是可行的。并入主线解决了大量的分布和支持问题 + +- 当内核开发人员努力维护一个稳定的用户空间接口时,内部内核API处于不断变化之中. + 缺乏一个稳定的内部接口是一个深思熟虑的设计决策;它允许在任何时候进行基本的改 + 进,并产生更高质量的代码。但该策略的一个结果是,如果要使用新的内核,任何树外 + 代码都需要持续的维护。维护树外代码需要大量的工作才能使代码保持工作状态。 + + 相反,位于主线中的代码不需要这样做,因为一个简单的规则要求进行API更改的任何 + 开发人员也必须修复由于该更改而破坏的任何代码。因此,合并到主线中的代码大大 + 降低了维护成本。 + +- 除此之外,内核中的代码通常会被其他开发人员改进。令人惊讶的结果可能来自授权 + 您的用户社区和客户改进您的产品。 + +- 内核代码在合并到主线之前和之后都要经过审查。不管原始开发人员的技能有多强, + 这个审查过程总是能找到改进代码的方法。审查经常发现严重的错误和安全问题。 + 这对于在封闭环境中开发的代码尤其如此;这种代码从外部开发人员的审查中获益 + 匪浅。树外代码是低质量代码。 + +- 参与开发过程是您影响内核开发方向的方式。旁观者的抱怨会被听到,但是活跃的 + 开发人员有更强的声音——并且能够实现使内核更好地满足其需求的更改。 + +- 当单独维护代码时,总是存在第三方为类似功能提供不同实现的可能性。如果发生 + 这种情况,合并代码将变得更加困难——甚至到了不可能的地步。然后,您将面临以下 + 令人不快的选择:(1)无限期地维护树外的非标准特性,或(2)放弃代码并将用户 + 迁移到树内版本。 + +- 代码的贡献是使整个过程工作的根本。通过贡献代码,您可以向内核添加新功能,并 + 提供其他内核开发人员使用的功能和示例。如果您已经为Linux开发了代码(或者 + 正在考虑这样做),那么您显然对这个平台的持续成功感兴趣;贡献代码是确保成功 + 的最好方法之一。 + +上述所有理由都适用于任何树外内核代码,包括以专有的、仅二进制形式分发的代码。 +然而,在考虑任何类型的纯二进制内核代码分布之前,还需要考虑其他因素。这些包括: + +- 围绕专有内核模块分发的法律问题充其量是模糊的;相当多的内核版权所有者认为, + 大多数仅限二进制的模块是内核的派生产品,因此,它们的分发违反了GNU通用公共 + 许可证(下面将详细介绍)。您的作者不是律师,本文档中的任何内容都不可能被 + 视为法律建议。封闭源代码模块的真实法律地位只能由法院决定。但不管怎样,困扰 + 这些模块的不确定性仍然存在。 + +- 二进制模块大大增加了调试内核问题的难度,以至于大多数内核开发人员甚至都不会 + 尝试。因此,只分发二进制模块将使您的用户更难从社区获得支持。 + +- 对于只支持二进制的模块的发行者来说,支持也更加困难,他们必须为他们希望支持 + 的每个发行版和每个内核版本提供一个版本的模块。为了提供相当全面的覆盖范围, + 可能需要一个模块的几十个构建,并且每次升级内核时,您的用户都必须单独升级 + 您的模块。 + +- 上面提到的关于代码评审的所有问题都更加存在于封闭源代码。由于该代码根本不可 + 用,因此社区无法对其进行审查,毫无疑问,它将存在严重问题。 + +尤其是嵌入式系统的制造商,可能会倾向于忽视本节中所说的大部分内容,因为他们 +相信自己正在商用一种使用冻结内核版本的独立产品,在发布后不需要再进行开发。 +这个论点忽略了广泛的代码审查的价值以及允许用户向产品添加功能的价值。但这些 +产品也有有限的商业寿命,之后必须发布新版本的产品。在这一点上,代码在主线上 +并得到良好维护的供应商将能够更好地占位,以使新产品快速上市。 + +许可 +---- + +代码是根据一些许可证提供给Linux内核的,但是所有代码都必须与GNU通用公共许可 +证(GPLV2)的版本2兼容,该版本是覆盖整个内核分发的许可证。在实践中,这意味 +着所有代码贡献都由GPLv2(可选地,语言允许在更高版本的GPL下分发)或3子句BSD +许可(New BSD License, 译者注)覆盖。任何不包含在兼容许可证中的贡献都不会 +被接受到内核中。 + +贡献给内核的代码不需要(或请求)版权分配。合并到主线内核中的所有代码都保留 +其原始所有权;因此,内核现在拥有数千个所有者。 + +这种所有权结构的一个暗示是,任何改变内核许可的尝试都注定会失败。很少有实际 +的场景可以获得所有版权所有者的同意(或者从内核中删除他们的代码)。因此,特 +别是,在可预见的将来,不可能迁移到GPL的版本3。 + +所有贡献给内核的代码都必须是合法的免费软件。因此,不接受匿名(或匿名)贡献 +者的代码。所有贡献者都需要在他们的代码上“sign off”,声明代码可以在GPL下与内 +核一起分发。无法提供未被其所有者许可为免费软件的代码,或可能为内核造成版权 +相关问题的代码(例如,由缺乏适当保护的反向工程工作派生的代码)不能被接受。 + +有关版权相关问题的问题在Linux开发邮件列表中很常见。这样的问题通常会得到不少 +答案,但要记住,回答这些问题的人不是律师,不能提供法律咨询。如果您有关于 +Linux源代码的法律问题,那么与了解该领域的律师交流是无法替代的。依靠从技术 +邮件列表中获得的答案是一件冒险的事情。 + diff --git a/Documentation/translations/zh_CN/process/2.Process.rst b/Documentation/translations/zh_CN/process/2.Process.rst new file mode 100644 index 000000000..ebe2e0254 --- /dev/null +++ b/Documentation/translations/zh_CN/process/2.Process.rst @@ -0,0 +1,360 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/2.Process.rst <development_process>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_process: + +开发流程如何工作 +================ + +90年代早期的Linux内核开发是一件相当松散的事情,涉及的用户和开发人员相对较 +少。由于拥有数以百万计的用户群,并且在一年的时间里有大约2000名开发人员参与 +进来,内核因此必须发展许多流程来保持开发的顺利进行。要成为流程的有效组成 +部分,需要对流程的工作方式有一个扎实的理解。 + +总览 +---- + +内核开发人员使用一个松散的基于时间的发布过程,每两到三个月发布一次新的主要 +内核版本。最近的发布历史记录如下: + + ====== ================= + 4.11 四月 30, 2017 + 4.12 七月 2, 2017 + 4.13 九月 3, 2017 + 4.14 十一月 12, 2017 + 4.15 一月 28, 2018 + 4.16 四月 1, 2018 + ====== ================= + +每4.x版本都是一个主要的内核版本,具有新特性、内部API更改等等。一个典型的4.x +版本包含大约13000个变更集,变更了几十万行代码。因此,4.x是Linux内核开发的前 +沿;内核使用滚动开发模型,不断集成重大变化。 + +对于每个版本的补丁合并,遵循一个相对简单的规则。在每个开发周期的开始,“合并 +窗口”被打开。当时,被认为足够稳定(并且被开发社区接受)的代码被合并到主线内 +核中。在这段时间内,新开发周期的大部分变更(以及所有主要变更)将以接近每天 +1000次变更(“补丁”或“变更集”)的速度合并。 + +(顺便说一句,值得注意的是,合并窗口期间集成的更改并不是凭空产生的;它们是 +提前收集、测试和分级的。稍后将详细描述该过程的工作方式)。 + +合并窗口持续大约两周。在这段时间结束时,LinusTorvalds将声明窗口已关闭,并 +释放第一个“rc”内核。例如,对于目标为4.14的内核,在合并窗口结束时发生的释放 +将被称为4.14-rc1。RC1版本是一个信号,表示合并新特性的时间已经过去,稳定下一 +个内核的时间已经开始。 + +在接下来的6到10周内,只有修复问题的补丁才应该提交给主线。有时会允许更大的 +更改,但这种情况很少发生;试图在合并窗口外合并新功能的开发人员往往会受到不 +友好的接待。一般来说,如果您错过了给定特性的合并窗口,最好的做法是等待下一 +个开发周期。(对于以前不支持的硬件,偶尔会对驱动程序进行例外;如果它们不 +改变已有代码,则不会导致回归,并且应该可以随时安全地添加)。 + +随着修复程序进入主线,补丁速度将随着时间的推移而变慢。Linus大约每周发布一次 +新的-rc内核;一个正常的系列将在-rc6和-rc9之间,内核被认为足够稳定并最终发布。 +然后,整个过程又重新开始了。 + +例如,这里是4.16的开发周期进行情况(2018年的所有日期): + + ============== ============================== + 一月 28 4.15 稳定版发布 + 二月 11 4.16-rc1, 合并窗口关闭 + 二月 18 4.16-rc2 + 二月 25 4.16-rc3 + 三月 4 4.16-rc4 + 三月 11 4.16-rc5 + 三月 18 4.16-rc6 + 三月 25 4.16-rc7 + 四月 1 4.16 稳定版发布 + ============== ============================== + +开发人员如何决定何时结束开发周期并创建稳定的版本?使用的最重要的指标是以前 +版本的回归列表。不欢迎出现任何错误,但是那些破坏了以前能工作的系统的错误被 +认为是特别严重的。因此,导致回归的补丁是不受欢迎的,很可能在稳定期内删除。 + +开发人员的目标是在稳定发布之前修复所有已知的回归。在现实世界中,这种完美是 +很难实现的;在这种规模的项目中,变量太多了。有一点,延迟最终版本只会使问题 +变得更糟;等待下一个合并窗口的一堆更改将变大,从而在下次创建更多的回归错误。 +因此,大多数4.x内核都有一些已知的回归错误,不过,希望没有一个是严重的。 + +一旦一个稳定的版本发布,它正在进行的维护工作就被移交给“稳定团队”,目前由 +Greg Kroah-Hartman组成。稳定团队将使用4.x.y编号方案不定期的发布稳定版本的更 +新。要加入更新版本,补丁程序必须(1)修复一个重要的bug,(2)已经合并到 +下一个开发主线中。内核通常会在超过其初始版本的一个以上的开发周期内接收稳定 +的更新。例如,4.13内核的历史如下 + + ============== =============================== + 九月 3 4.13 稳定版发布 + 九月 13 4.13.1 + 九月 20 4.13.2 + 九月 27 4.13.3 + 十月 5 4.13.4 + 十月 12 4.13.5 + ... ... + 十一月 24 4.13.16 + ============== =============================== + +4.13.16是4.13版本的最终稳定更新。 + +有些内核被指定为“长期”内核;它们将得到更长时间的支持。在本文中,当前的长期 +内核及其维护者是: + + ====== ====================== ============================== + 3.16 Ben Hutchings (长期稳定内核) + 4.1 Sasha Levin + 4.4 Greg Kroah-Hartman (长期稳定内核) + 4.9 Greg Kroah-Hartman + 4.14 Greg Kroah-Hartman + ====== ====================== ============================== + +为长期支持选择内核纯粹是维护人员有必要和时间来维护该版本的问题。目前还没有 +为即将发布的任何特定版本提供长期支持的已知计划。 + +补丁的生命周期 +-------------- + +补丁不会直接从开发人员的键盘进入主线内核。相反,有一个稍微复杂(如果有些非 +正式)的过程,旨在确保对每个补丁进行质量审查,并确保每个补丁实现了一个在主线 +中需要的更改。对于小的修复,这个过程可能会很快发生,或者,在大的和有争议的 +变更的情况下,会持续数年。许多开发人员的挫折来自于对这个过程缺乏理解或者 +试图绕过它。 + +为了减少这种挫折感,本文将描述补丁如何进入内核。下面是一个介绍,它以某种 +理想化的方式描述了这个过程。更详细的过程将在后面的章节中介绍。 + +补丁程序经历的阶段通常是: + +- 设计。这就是补丁的真正需求——以及满足这些需求的方式——的所在。设计工作通常 + 是在不涉及社区的情况下完成的,但是如果可能的话,最好是在公开的情况下完成 + 这项工作;这样可以节省很多稍后再重新设计的时间。 + +- 早期评审。补丁被发布到相关的邮件列表中,列表中的开发人员会回复他们可能有 + 的任何评论。如果一切顺利的话,这个过程应该会发现补丁的任何主要问题。 + +- 更广泛的评审。当补丁接近准备好纳入主线时,它应该被相关的子系统维护人员 + 接受——尽管这种接受并不能保证补丁会一直延伸到主线。补丁将出现在维护人员的 + 子系统树中,并进入 -next 树(如下所述)。当流程工作时,此步骤将导致对补丁 + 进行更广泛的审查,并发现由于将此补丁与其他人所做的工作集成而导致的任何 + 问题。 + +- 请注意,大多数维护人员也有日常工作,因此合并补丁可能不是他们的最高优先级。 + 如果您的补丁程序得到了关于所需更改的反馈,那么您应该进行这些更改,或者为 + 不应该进行这些更改的原因辩护。如果您的补丁没有评审意见,但没有被其相应的 + 子系统或驱动程序维护者接受,那么您应该坚持不懈地将补丁更新到当前内核,使 + 其干净地应用,并不断地将其发送以供审查和合并。 + +- 合并到主线。最终,一个成功的补丁将被合并到由LinusTorvalds管理的主线存储库 + 中。此时可能会出现更多的评论和/或问题;开发人员应对这些问题并解决出现的 + 任何问题很重要。 + +- 稳定版发布。可能受补丁影响的用户数量现在很大,因此可能再次出现新的问题。 + +- 长期维护。虽然开发人员在合并代码后可能会忘记代码,但这种行为往往会给开发 + 社区留下不良印象。合并代码消除了一些维护负担,因为其他代码将修复由API + 更改引起的问题。但是,如果代码要长期保持有用,原始开发人员应该继续为 + 代码负责。 + +内核开发人员(或他们的雇主)犯的最大错误之一是试图将流程简化为一个 +“合并到主线”步骤。这种方法总是会让所有相关人员感到沮丧。 + +补丁如何进入内核 +---------------- + +只有一个人可以将补丁合并到主线内核存储库中:LinusTorvalds。但是,在进入 +2.6.38内核的9500多个补丁中,只有112个(大约1.3%)是由Linus自己直接选择的。 +内核项目已经发展到一个规模,没有一个开发人员可以在没有支持的情况下检查和 +选择每个补丁。内核开发人员处理这种增长的方式是通过使用围绕信任链构建的 +助理系统。 + +内核代码库在逻辑上被分解为一组子系统:网络、特定的体系结构支持、内存管理、 +视频设备等。大多数子系统都有一个指定的维护人员,开发人员对该子系统中的代码 +负有全部责任。这些子系统维护者(松散地)是他们所管理的内核部分的守护者; +他们(通常)会接受一个补丁以包含到主线内核中。 + +子系统维护人员每个人都使用git源代码管理工具管理自己版本的内核源代码树。Git +等工具(以及Quilt或Mercurial等相关工具)允许维护人员跟踪补丁列表,包括作者 +信息和其他元数据。在任何给定的时间,维护人员都可以确定他或她的存储库中的哪 +些补丁在主线中找不到。 + +当合并窗口打开时,顶级维护人员将要求Linus从其存储库中“拉出”他们为合并选择 +的补丁。如果Linus同意,补丁流将流向他的存储库,成为主线内核的一部分。 +Linus对拉操作中接收到的特定补丁的关注程度各不相同。很明显,有时他看起来很 +关注。但是,作为一般规则,Linus相信子系统维护人员不会向上游发送坏补丁。 + +子系统维护人员反过来也可以从其他维护人员那里获取补丁。例如,网络树是由首先 +在专用于网络设备驱动程序、无线网络等的树中积累的补丁构建的。此存储链可以 +任意长,但很少超过两个或三个链接。由于链中的每个维护者都信任那些管理较低 +级别树的维护者,所以这个过程称为“信任链”。 + +显然,在这样的系统中,获取内核补丁取决于找到正确的维护者。直接向Linus发送 +补丁通常不是正确的方法。 + +Next 树 +------- + +子系统树链引导补丁流到内核,但它也提出了一个有趣的问题:如果有人想查看为 +下一个合并窗口准备的所有补丁怎么办?开发人员将感兴趣的是,还有什么其他的 +更改有待解决,以查看是否存在需要担心的冲突;例如,更改核心内核函数原型的 +修补程序将与使用该函数旧形式的任何其他修补程序冲突。审查人员和测试人员希望 +在所有这些变更到达主线内核之前,能够访问它们的集成形式中的变更。您可以从所有 +有趣的子系统树中提取更改,但这将是一项大型且容易出错的工作。 + +答案以-next树的形式出现,在这里子系统树被收集以供测试和审查。Andrew Morton +维护的这些旧树被称为“-mm”(用于内存管理,这就是它的启动名字)。-mm 树集成了 +一长串子系统树中的补丁;它还包含一些旨在帮助调试的补丁。 + +除此之外,-mm 还包含大量由Andrew直接选择的补丁。这些补丁可能已经发布在邮件 +列表上,或者它们可能应用于内核中没有指定子系统树的部分。结果,-mm 作为一种 +最后手段的子系统树运行;如果没有其他明显的路径可以让补丁进入主线,那么它很 +可能以-mm 结束。累积在-mm 中的各种补丁最终将被转发到适当的子系统树,或者直接 +发送到Linus。在典型的开发周期中,大约5-10%的补丁通过-mm 进入主线。 + +当前-mm 补丁可在“mmotm”(-mm of the moment)目录中找到,地址: + + https://www.ozlabs.org/~akpm/mmotm/ + +然而,使用mmotm树可能是一种令人沮丧的体验;它甚至可能无法编译。 + +下一个周期补丁合并的主要树是linux-next,由Stephen Rothwell 维护。根据设计 +linux-next 是下一个合并窗口关闭后主线的快照。linux-next树在Linux-kernel 和 +Linux-next 邮件列表中发布,可从以下位置下载: + + https://www.kernel.org/pub/linux/kernel/next/ + +Linux-next 已经成为内核开发过程中不可或缺的一部分;在一个给定的合并窗口中合并 +的所有补丁都应该在合并窗口打开之前的一段时间内找到进入Linux-next 的方法。 + +Staging 树 +---------- + +内核源代码树包含drivers/staging/directory,其中有许多驱动程序或文件系统的 +子目录正在被添加到内核树中。它们然需要更多的工作的时候可以保留在 +driver/staging目录中;一旦完成,就可以将它们移到内核中。这是一种跟踪不符合 +Linux内核编码或质量标准的驱动程序的方法,但人们可能希望使用它们并跟踪开发。 + +Greg Kroah Hartman 目前负责维护staging 树。仍需要工作的驱动程序将发送给他, +每个驱动程序在drivers/staging/中都有自己的子目录。除了驱动程序源文件之外, +目录中还应该有一个TODO文件。todo文件列出了驱动程序需要接受的挂起的工作, +以及驱动程序的任何补丁都应该抄送的人员列表。当前的规则要求,staging的驱动 +程序必须至少正确编译。 + +Staging 是一种相对容易的方法,可以让新的驱动程序进入主线,幸运的是,他们会 +引起其他开发人员的注意,并迅速改进。然而,进入staging并不是故事的结尾; +staging中没有看到常规进展的代码最终将被删除。经销商也倾向于相对不愿意使用 +staging驱动程序。因此,在成为一名合适的主线驱动的路上,staging 充其量只是 +一个停留。 + +工具 +---- + +从上面的文本可以看出,内核开发过程在很大程度上依赖于在不同方向上聚集补丁的 +能力。如果没有适当强大的工具,整个系统将无法在任何地方正常工作。关于如何使用 +这些工具的教程远远超出了本文档的范围,但是还是有一些指南的空间。 + +到目前为止,内核社区使用的主要源代码管理系统是git。Git是在自由软件社区中开发 +的许多分布式版本控制系统之一。它非常适合内核开发,因为它在处理大型存储库和 +大量补丁时性能非常好。它还有一个难以学习和使用的名声,尽管随着时间的推移它 +变得更好了。对于内核开发人员来说,对Git的某种熟悉几乎是一种要求;即使他们不 +将它用于自己的工作,他们也需要Git来跟上其他开发人员(以及主线)正在做的事情。 + +现在几乎所有的Linux发行版都打包了Git。主页位于: + + https://git-scm.com/ + +那个页面有指向文档和教程的指针。 + +在不使用git的内核开发人员中,最流行的选择几乎肯定是mercurial: + + http://www.seleric.com/mercurial/ + +Mercurial与Git共享许多特性,但它提供了一个界面,许多人觉得它更易于使用。 + +另一个值得了解的工具是quilt: + + https://savannah.nongnu.org/projects/quilt + +Quilt 是一个补丁管理系统,而不是源代码管理系统。它不会随着时间的推移跟踪历史; +相反,它面向根据不断发展的代码库跟踪一组特定的更改。一些主要的子系统维护人员 +使用Quilt来管理打算向上游移动的补丁。对于某些树的管理(例如-mm),quilt 是 +最好的工具。 + +邮件列表 +-------- + +大量的Linux内核开发工作是通过邮件列表完成的。如果不在某个地方加入至少一个列表, +就很难成为社区中一个功能完备的成员。但是,Linux邮件列表对开发人员来说也是一个 +潜在的危险,他们可能会被一堆电子邮件淹没,违反Linux列表上使用的约定,或者 +两者兼而有之。 + +大多数内核邮件列表都在vger.kernel.org上运行;主列表位于: + + http://vger.kernel.org/vger-lists.html + +不过,也有一些列表托管在别处;其中一些列表位于lists.redhat.com。 + +当然,内核开发的核心邮件列表是linux-kernel。这个名单是一个令人生畏的地方; +每天的信息量可以达到500条,噪音很高,谈话技术性很强,参与者并不总是表现出 +高度的礼貌。但是,没有其他地方可以让内核开发社区作为一个整体聚集在一起; +避免使用此列表的开发人员将错过重要信息。 + +有一些提示可以帮助在linux-kernel生存: + +- 将邮件转移到单独的文件夹,而不是主邮箱。我们必须能够持续地忽略洪流。 + +- 不要试图跟踪每一次谈话-其他人都不会。重要的是要对感兴趣的主题(尽管请 + 注意,长时间的对话可以在不更改电子邮件主题行的情况下偏离原始主题)和参与 + 的人进行筛选。 + +- 不要挑事。如果有人试图激起愤怒的反应,忽略他们。 + +- 当响应Linux内核电子邮件(或其他列表上的电子邮件)时,请为所有相关人员保留 + cc:header。如果没有强有力的理由(如明确的请求),则不应删除收件人。一定要 + 确保你要回复的人在cc:list中。这个惯例也使你不必在回复邮件时明确要求被抄送。 + +- 在提出问题之前,搜索列表档案(和整个网络)。有些开发人员可能会对那些显然 + 没有完成家庭作业的人感到不耐烦。 + +- 避免贴顶帖(把你的答案放在你要回复的引文上面的做法)。这会让你的回答更难 + 理解,印象也很差。 + +- 询问正确的邮件列表。linux-kernel 可能是通用的讨论点,但它不是从所有子系统 + 中寻找开发人员的最佳场所。 + +最后一点——找到正确的邮件列表——是开发人员出错的常见地方。在Linux内核上提出与 +网络相关的问题的人几乎肯定会收到一个礼貌的建议,转而在netdev列表上提出, +因为这是大多数网络开发人员经常出现的列表。还有其他列表可用于scsi、 +video4linux、ide、filesystem等子系统。查找邮件列表的最佳位置是与内核源代码 +一起打包的MAINTAINERS文件。 + +开始内核开发 +------------ + +关于如何开始内核开发过程的问题很常见——来自个人和公司。同样常见的是错误,这 +使得关系的开始比必须的更困难。 + +公司通常希望聘请知名的开发人员来启动开发团队。实际上,这是一种有效的技术。 +但它也往往是昂贵的,而且没有增长经验丰富的内核开发人员储备。考虑到时间的 +投入,可以让内部开发人员加快Linux内核的开发速度。花这个时间可以让雇主拥有 +一批了解内核和公司的开发人员,他们也可以帮助培训其他人。从中期来看,这往往 +是更有利可图的方法。 + +可以理解的是,单个开发人员往往对起步感到茫然。从一个大型项目开始可能会很 +吓人;人们往往想先用一些较小的东西来测试水域。这是一些开发人员开始创建修补 +拼写错误或轻微编码风格问题的补丁的地方。不幸的是,这样的补丁会产生一定程度 +的噪音,这会分散整个开发社区的注意力,因此,越来越多的人看不起它们。希望向 +社区介绍自己的新开发人员将无法通过这些方式获得他们想要的那种接待。 + +Andrew Morton 为有抱负的内核开发人员提供了这个建议 + +:: + + 所有内核初学者的No.1项目肯定是“确保内核在所有的机器上,你可以触摸 + 到的,始终运行良好" 通常这样做的方法是与其他人一起解决问题(这 + 可能需要坚持!)但这很好——这是内核开发的一部分 + +(http://lwn.net/articles/283982/) + +在没有明显问题需要解决的情况下,建议开发人员查看当前的回归和开放式错误列表. +解决需要修复的问题没有任何缺点;通过解决这些问题,开发人员将获得处理过程的 +经验,同时与开发社区的其他人建立尊重。 diff --git a/Documentation/translations/zh_CN/process/3.Early-stage.rst b/Documentation/translations/zh_CN/process/3.Early-stage.rst new file mode 100644 index 000000000..b8676aec6 --- /dev/null +++ b/Documentation/translations/zh_CN/process/3.Early-stage.rst @@ -0,0 +1,161 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/3.Early-stage.rst <development_early_stage>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_early_stage: + +早期规划 +======== + +当考虑一个Linux内核开发项目时,很可能会直接跳进去开始编码。然而,与任何重要 +的项目一样,成功的许多基础最好是在第一行代码编写之前就做好了。在早期计划和 +沟通中花费一些时间可以节省更多的时间。 + +详述问题 +-------- + +与任何工程项目一样,成功的内核增强从要解决的问题的清晰描述开始。在某些情况 +下,这个步骤很容易:例如,当某个特定硬件需要驱动程序时。不过,在其他方面, +将实际问题与建议的解决方案混淆是很有诱惑力的,这可能会导致困难。 + +举个例子:几年前,使用Linux音频的开发人员寻求一种方法来运行应用程序,而不因 +系统延迟过大而导致退出或其他工件。他们得到的解决方案是一个内核模块,旨在连 +接到Linux安全模块(LSM)框架中;这个模块可以配置为允许特定的应用程序访问 +实时调度程序。这个模块被实现并发送到Linux内核邮件列表,在那里它立即遇到问题。 + +对于音频开发人员来说,这个安全模块足以解决他们当前的问题。但是,对于更广泛的 +内核社区来说,这被视为对LSM框架的滥用(LSM框架并不打算授予他们原本不具备的 +进程特权),并对系统稳定性造成风险。他们首选的解决方案包括短期的通过rlimit +机制进行实时调度访问,以及长期的减少延迟的工作。 + +然而,音频社区看不到他们实施的特定解决方案的过去;他们不愿意接受替代方案。 +由此产生的分歧使这些开发人员对整个内核开发过程感到失望;其中一个开发人员返回 +到音频列表并发布了以下内容: + + 有很多非常好的Linux内核开发人员,但他们往往会被一群傲慢的傻瓜所压倒。 + 试图向这些人传达用户需求是浪费时间。他们太“聪明”了,根本听不到少数人 + 的话。 + +(http://lwn.net/articles/131776/) + +实际情况不同;与特定模块相比,内核开发人员更关心系统稳定性、长期维护以及找到 +正确的问题解决方案。这个故事的寓意是把重点放在问题上——而不是具体的解决方案 +上——并在投入创建代码之前与开发社区讨论这个问题。 + +因此,在考虑一个内核开发项目时,我们应该得到一组简短问题的答案: + + - 究竟需要解决的问题是什么? + + - 受此问题影响的用户是谁?解决方案应该解决哪些用例? + + - 内核现在为何没能解决这个问题? + +只有这样,才能开始考虑可能的解决方案。 + + +早期讨论 +-------- + +在计划内核开发项目时,在开始实施之前与社区进行讨论是很有意义的。早期沟通可以 +通过多种方式节省时间和麻烦: + + - 很可能问题是由内核以您不理解的方式解决的。Linux内核很大,具有许多不明显 + 的特性和功能。并不是所有的内核功能都像人们所希望的那样有文档记录,而且很 + 容易遗漏一些东西。你的作者发出了一个完整的驱动程序,复制了一个新作者不 + 知道的现有驱动程序。重新设计现有轮子的代码不仅浪费,而且不会被接受到主线 + 内核中。 + + - 建议的解决方案中可能有一些元素不适用于主线合并。在编写代码之前,最好先 + 了解这样的问题。 + + - 其他开发人员完全有可能考虑过这个问题;他们可能有更好的解决方案的想法,并且 + 可能愿意帮助创建这个解决方案。 + +在内核开发社区的多年经验给了我们一个明确的教训:闭门设计和开发的内核代码总是 +有一些问题,这些问题只有在代码发布到社区中时才会被发现。有时这些问题很严重, +需要数月或数年的努力才能使代码达到内核社区的标准。一些例子包括: + + - 设计并实现了单处理器系统的DeviceScape网络栈。只有使其适合于多处理器系统, + 才能将其合并到主线中。在代码中改装锁等等是一项困难的任务;因此,这段代码 + (现在称为mac80211)的合并被推迟了一年多。 + + - Reiser4文件系统包含许多功能,核心内核开发人员认为这些功能应该在虚拟文件 + 系统层中实现。它还包括一些特性,这些特性在不将系统暴露于用户引起的死锁的 + 情况下是不容易实现的。这些问题的最新发现——以及对其中一些问题的拒绝——已经 + 导致Reiser4远离了主线内核。 + + - Apparmor安全模块以被认为不安全和不可靠的方式使用内部虚拟文件系统数据结构。 + 这种担心(包括其他)使Apparmor多年不在主线上。 + +在每一种情况下,通过与内核开发人员的早期讨论,可以避免大量的痛苦和额外的工作。 + +找谁交流 +-------- + +当开发人员决定公开他们的计划时,下一个问题是:我们从哪里开始?答案是找到正确 +的邮件列表和正确的维护者。对于邮件列表,最好的方法是在维护者(MAINTAINERS)文件 +中查找要发布的相关位置。如果有一个合适的子系统列表,那么发布它通常比在Linux +内核上发布更可取;您更有可能接触到在相关子系统中具有专业知识的开发人员,并且 +环境可能具支持性。 + +找到维护人员可能会有点困难。同样,维护者文件是开始的地方。但是,该文件往往不总 +是最新的,并且并非所有子系统都在那里表示。实际上,维护者文件中列出的人员可能 +不是当前实际担任该角色的人员。因此,当对联系谁有疑问时,一个有用的技巧是使用 +git(尤其是“git-log”)查看感兴趣的子系统中当前活动的用户。看看谁在写补丁, +如果有人的话,谁会在这些补丁上加上用线签名的。这些人将是帮助新开发项目的最佳 +人选。 + +找到合适的维护者的任务有时是非常具有挑战性的,以至于内核开发人员添加了一个 +脚本来简化过程: + +:: + + .../scripts/get_maintainer.pl + +当给定“-f”选项时,此脚本将返回给定文件或目录的当前维护者。如果在命令行上传递 +了一个补丁,它将列出可能接收补丁副本的维护人员。有许多选项可以调节 +get_maintainer.pl搜索维护者的难易程度;请小心使用更具攻击性的选项,因为最终 +可能会包括对您正在修改的代码没有真正兴趣的开发人员。 + +如果所有其他方法都失败了,那么与Andrew Morton交谈可以成为一种有效的方法来跟踪 +特定代码段的维护人员。 + +何时邮寄? +---------- + +如果可能的话,在早期阶段发布你的计划只会有帮助。描述正在解决的问题以及已经 +制定的关于如何实施的任何计划。您可以提供的任何信息都可以帮助开发社区为项目 +提供有用的输入。 + +在这个阶段可能发生的一件令人沮丧的事情不是敌对的反应,而是很少或根本没有 +反应。可悲的事实是:(1)内核开发人员往往很忙;(2)不缺少有宏伟计划和很少 +代码(甚至代码前景)支持他们的人;(3)没有人有义务审查或评论别人发表的 +想法。除此之外,高级设计常常隐藏一些问题,这些问题只有在有人真正尝试实现 +这些设计时才会被发现;因此,内核开发人员宁愿看到代码。 + +如果发表评论的请求在评论的方式上没有什么效果,不要假设这意味着对项目没有 +兴趣。不幸的是,你也不能假设你的想法没有问题。在这种情况下,最好的做法是 +继续进行,把你的进展随时通知社区。 + +获得官方认可 +----------------------- + +如果您的工作是在公司环境中完成的,就像大多数Linux内核工作一样,显然,在您将 +公司的计划或代码发布到公共邮件列表之前,必须获得适当授权的经理的许可。发布 +不确定是否兼容GPL的代码可能是有特别问题的;公司的管理层和法律人员越早能够就 +发布内核开发项目达成一致,对参与的每个人都越好。 + +一些读者可能会认为他们的核心工作是为了支持还没有正式承认存在的产品。将雇主 +的计划公布在公共邮件列表上可能不是一个可行的选择。在这种情况下,有必要考虑 +保密是否真的是必要的;通常不需要把开发计划关在门内。 + +也就是说,有些情况下,一家公司在开发过程的早期就不能合法地披露其计划。拥有 +经验丰富的内核开发人员的公司可以选择以开环的方式进行,前提是他们以后能够避免 +严重的集成问题。对于没有这种内部专业知识的公司,最好的选择往往是聘请外部 +开发商根据保密协议审查计划。Linux基金会运行了一个NDA程序,旨在帮助解决这种 +情况; + + http://www.linuxfoundation.org/en/NDA_program + +这种审查通常足以避免以后出现严重问题,而无需公开披露项目。 diff --git a/Documentation/translations/zh_CN/process/4.Coding.rst b/Documentation/translations/zh_CN/process/4.Coding.rst new file mode 100644 index 000000000..959a06ba0 --- /dev/null +++ b/Documentation/translations/zh_CN/process/4.Coding.rst @@ -0,0 +1,290 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/4.Coding.rst <development_coding>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_coding: + +使代码正确 +====================== + +虽然对于一个坚实的、面向社区的设计过程有很多话要说,但是任何内核开发项目的 +证明都在生成的代码中。它是将由其他开发人员检查并合并(或不合并)到主线树中 +的代码。所以这段代码的质量决定了项目的最终成功。 + +本节将检查编码过程。我们将从内核开发人员出错的几种方式开始。然后重点将转移 +到正确的事情和可以帮助这个任务的工具上。 + +陷阱 +---- + +编码风格 +******** + +内核长期以来都有一种标准的编码风格,如 +:ref:`Documentation/translations/zh_CN/process/coding-style.rst <cn_codingstyle>` +中所述。在大部分时间里,该文件中描述的政策被认为至多是建议性的。因此,内核 +中存在大量不符合编码风格准则的代码。代码的存在会给内核开发人员带来两个独立 +的危害。 + +首先,要相信内核编码标准并不重要,也不强制执行。事实上,如果没有按照标准对代 +码进行编码,那么向内核添加新代码是非常困难的;许多开发人员甚至会在审查代码之 +前要求对代码进行重新格式化。一个与内核一样大的代码库需要一些统一的代码,以使 +开发人员能够快速理解其中的任何部分。所以已经没有空间来存放奇怪的格式化代码了。 + +偶尔,内核的编码风格会与雇主的强制风格发生冲突。在这种情况下,内核的风格必须 +在代码合并之前获胜。将代码放入内核意味着以多种方式放弃一定程度的控制权——包括 +控制代码的格式化方式。 + +另一个陷阱是假定已经在内核中的代码迫切需要编码样式的修复。开发人员可能会开始 +生成重新格式化补丁,作为熟悉过程的一种方式,或者作为将其名称写入内核变更日志 +的一种方式,或者两者兼而有之。但是纯编码风格的修复被开发社区视为噪音;它们往 +往受到冷遇。因此,最好避免使用这种类型的补丁。由于其他原因,在处理一段代码的 +同时修复它的样式是很自然的,但是编码样式的更改不应该仅为了更改而进行。 + +编码风格的文档也不应该被视为绝对的法律,这是永远不会被违反的。如果有一个很好 +的理由反对这种样式(例如,如果拆分为适合80列限制的行,那么它的可读性就会大大 +降低),那么就这样做。 + +请注意,您还可以使用 ``clang-format`` 工具来帮助您处理这些规则,自动重新格式 +化部分代码,并查看完整的文件,以发现编码样式错误、拼写错误和可能的改进。它还 +可以方便地进行排序,包括对齐变量/宏、回流文本和其他类似任务。有关详细信息,请 +参阅文件 :ref:`Documentation/process/clang-format.rst <clangformat>` + +抽象层 +****** + +计算机科学教授教学生以灵活性和信息隐藏的名义广泛使用抽象层。当然,内核广泛 +地使用了抽象;任何涉及数百万行代码的项目都不能做到这一点并存活下来。但经验 +表明,过度或过早的抽象可能和过早的优化一样有害。抽象应用于所需的级别, +不要过度。 + +在一个简单的级别上,考虑一个函数的参数,该参数总是由所有调用方作为零传递。 +我们可以保留这个论点: 以防有人最终需要使用它提供的额外灵活性。不过,到那时, +实现这个额外参数的代码很有可能以某种从未被注意到的微妙方式被破坏——因为它从 +未被使用过。或者,当需要额外的灵活性时,它不会以符合程序员早期期望的方式来 +这样做。内核开发人员通常会提交补丁来删除未使用的参数;一般来说,首先不应该 +添加这些参数。 + +隐藏硬件访问的抽象层——通常允许大量的驱动程序在多个操作系统中使用——尤其不受 +欢迎。这样的层使代码变得模糊,可能会造成性能损失;它们不属于Linux内核。 + +另一方面,如果您发现自己从另一个内核子系统复制了大量的代码,那么现在是时候 +问一下,事实上,将这些代码中的一些提取到单独的库中,或者在更高的层次上实现 +这些功能是否有意义。在整个内核中复制相同的代码没有价值。 + +#ifdef 和预处理 +*************** + +C预处理器似乎给一些C程序员带来了强大的诱惑,他们认为它是一种有效地将大量灵 +活性编码到源文件中的方法。但是预处理器不是C,大量使用它会导致代码对其他人来 +说更难读取,对编译器来说更难检查正确性。大量的预处理器几乎总是代码需要一些 +清理工作的标志。 + +使用ifdef的条件编译实际上是一个强大的功能,它在内核中使用。但是很少有人希望 +看到代码被大量地撒上ifdef块。作为一般规则,ifdef的使用应尽可能限制在头文件 +中。有条件编译的代码可以限制函数,如果代码不存在,这些函数就会变成空的。然后 +编译器将悄悄地优化对空函数的调用。结果是代码更加清晰,更容易理解。 + +C预处理器宏存在许多危险,包括可能对具有副作用且没有类型安全性的表达式进行多 +重评估。如果您试图定义宏,请考虑创建一个内联函数。结果相同的代码,但是内联 +函数更容易读取,不会多次计算其参数,并且允许编译器对参数和返回值执行类型检查。 + +内联函数 +******** + +不过,内联函数本身也存在风险。程序员可以倾心于避免函数调用和用内联函数填充源 +文件所固有的效率。然而,这些功能实际上会降低性能。因为它们的代码在每个调用站 +点都被复制,所以它们最终会增加编译内核的大小。反过来,这会对处理器的内存缓存 +造成压力,从而大大降低执行速度。通常,内联函数应该非常小,而且相对较少。毕竟, +函数调用的成本并不高;大量内联函数的创建是过早优化的典型例子。 + +一般来说,内核程序员会忽略缓存效果,这会带来危险。在开始的数据结构课程中,经 +典的时间/空间权衡通常不适用于当代硬件。空间就是时间,因为一个大的程序比一个 +更紧凑的程序运行得慢。 + +最近的编译器在决定一个给定函数是否应该被内联方面扮演着越来越积极的角色。 +因此,“inline”关键字的自由放置可能不仅仅是过度的,它也可能是无关的。 + +锁 +** + +2006年5月,“deviceescape”网络堆栈在GPL下发布,并被纳入主线内核。这是一个受 +欢迎的消息;对Linux中无线网络的支持充其量被认为是不合格的,而deviceescape +堆栈提供了修复这种情况的承诺。然而,直到2007年6月(2.6.22),这段代码才真 +正进入主线。发生了什么? + +这段代码显示了许多闭门造车的迹象。但一个特别大的问题是,它并不是设计用于多 +处理器系统。在合并这个网络堆栈(现在称为mac80211)之前,需要对其进行一个锁 +方案的改造。 + +曾经,Linux内核代码可以在不考虑多处理器系统所带来的并发性问题的情况下进行 +开发。然而,现在,这个文件是写在双核笔记本电脑上的。即使在单处理器系统上, +为提高响应能力所做的工作也会提高内核内的并发性水平。编写内核代码而不考虑锁 +的日子已经过去很长了。 + +可以由多个线程并发访问的任何资源(数据结构、硬件寄存器等)必须由锁保护。新 +的代码应该记住这一要求;事后改装锁是一项相当困难的任务。内核开发人员应该花 +时间充分了解可用的锁原语,以便为作业选择正确的工具。显示对并发性缺乏关注的 +代码进入主线将很困难。 + +回归 +**** + +最后一个值得一提的危险是:它可能会引起改变(这可能会带来很大的改进),从而 +导致现有用户的某些东西中断。这种变化被称为“回归”,回归已经成为主线内核最不 +受欢迎的。除少数例外情况外,如果回归不能及时修正,会导致回归的变化将被取消。 +最好首先避免回归。 + +人们常常争论,如果回归让更多人可以工作,远超过产生问题,那么回归是合理的。 +如果它破坏的一个系统却为十个系统带来新的功能,为什么不进行更改呢?2007年7月, +Linus对这个问题给出了最佳答案: + +:: + 所以我们不会通过引入新问题来修复错误。那样的谎言很疯狂,没有人知道 + 你是否真的有进展。是前进两步,后退一步,还是向前一步,向后两步? + +(http://lwn.net/articles/243460/) + +一种特别不受欢迎的回归类型是用户空间ABI的任何变化。一旦接口被导出到用户空间, +就必须无限期地支持它。这一事实使得用户空间接口的创建特别具有挑战性:因为它们 +不能以不兼容的方式进行更改,所以必须第一次正确地进行更改。因此,用户空间界面 +总是需要大量的思考、清晰的文档和广泛的审查。 + + +代码检查工具 +------------ + +至少目前,编写无错误代码仍然是我们中很少人能达到的理想状态。不过,我们希望做 +的是,在代码进入主线内核之前,尽可能多地捕获并修复这些错误。为此,内核开发人 +员已经组装了一系列令人印象深刻的工具,可以自动捕获各种各样的模糊问题。计算机 +发现的任何问题都是一个以后不会困扰用户的问题,因此,只要有可能,就应该使用 +自动化工具。 + +第一步只是注意编译器产生的警告。当代版本的GCC可以检测(并警告)大量潜在错误。 +通常,这些警告都指向真正的问题。提交以供审阅的代码通常不会产生任何编译器警告。 +在消除警告时,注意了解真正的原因,并尽量避免“修复”,使警告消失而不解决其原因。 + +请注意,并非所有编译器警告都默认启用。使用“make EXTRA_CFLAGS=-W”构建内核以 +获得完整集合。 + +内核提供了几个配置选项,可以打开调试功能;大多数配置选项位于“kernel hacking” +子菜单中。对于任何用于开发或测试目的的内核,都应该启用其中几个选项。特别是, +您应该打开: + + - 启用 ENABLE_MUST_CHECK and FRAME_WARN 以获得一组额外的警告,以解决使用不 + 推荐使用的接口或忽略函数的重要返回值等问题。这些警告生成的输出可能是冗长 + 的,但您不必担心来自内核其他部分的警告。 + + - DEBUG_OBJECTS 将添加代码,以跟踪内核创建的各种对象的生存期,并在出现问题时 + 发出警告。如果要添加创建(和导出)自己的复杂对象的子系统,请考虑添加对对象 + 调试基础结构的支持。 + + - DEBUG_SLAB 可以发现各种内存分配和使用错误;它应该用于大多数开发内核。 + + - DEBUG_SPINLOCK, DEBUG_ATOMIC_SLEEP and DEBUG_MUTEXES 会发现许多常见的 + 锁定错误. + +还有很多其他调试选项,其中一些将在下面讨论。其中一些具有显著的性能影响,不应 +一直使用。但是,在学习可用选项上花费的一些时间可能会在短期内得到多次回报。 + +其中一个较重的调试工具是锁定检查器或“lockdep”。该工具将跟踪系统中每个锁 +(spinlock或mutex)的获取和释放、获取锁的相对顺序、当前中断环境等等。然后, +它可以确保总是以相同的顺序获取锁,相同的中断假设适用于所有情况,等等。换句话 +说,lockdep可以找到许多场景,在这些场景中,系统很少会死锁。在部署的系统中, +这种问题可能会很痛苦(对于开发人员和用户而言);LockDep允许提前以自动方式 +发现问题。具有任何类型的非普通锁定的代码在提交包含前应在启用lockdep的情况 +下运行。 + +作为一个勤奋的内核程序员,毫无疑问,您将检查任何可能失败的操作(如内存分配) +的返回状态。然而,事实上,最终的故障恢复路径可能完全没有经过测试。未测试的 +代码往往会被破坏;如果所有这些错误处理路径都被执行了几次,那么您可能对代码 +更有信心。 + +内核提供了一个可以做到这一点的错误注入框架,特别是在涉及内存分配的情况下。 +启用故障注入后,内存分配的可配置百分比将失败;这些失败可以限制在特定的代码 +范围内。在启用了故障注入的情况下运行,程序员可以看到当情况恶化时代码如何响 +应。有关如何使用此工具的详细信息,请参阅 +Documentation/fault-injection/fault-injection.rst。 + +使用“sparse”静态分析工具可以发现其他类型的错误。对于sparse,可以警告程序员 +用户空间和内核空间地址之间的混淆、big endian和small endian数量的混合、在需 +要一组位标志的地方传递整数值等等。sparse必须单独安装(如果您的分发服务器没 +有将其打包,可以在 https://sparse.wiki.kernel.org/index.php/Main_page)找到, +然后可以通过在make命令中添加“C=1”在代码上运行它。 + +“Coccinelle”工具 :ref:`http://coccinelle.lip6.fr/ <devtools_coccinelle>` +能够发现各种潜在的编码问题;它还可以为这些问题提出修复方案。在 +scripts/coccinelle目录下已经打包了相当多的内核“语义补丁”;运行 +“make coccicheck”将运行这些语义补丁并报告发现的任何问题。有关详细信息,请参阅 +:ref:`Documentation/dev-tools/coccinelle.rst <devtools_coccinelle>` + + +其他类型的可移植性错误最好通过为其他体系结构编译代码来发现。如果没有S/390系统 +或Blackfin开发板,您仍然可以执行编译步骤。可以在以下位置找到一组用于x86系统的 +大型交叉编译器: + + https://www.kernel.org/pub/tools/crosstool/ + +花一些时间安装和使用这些编译器将有助于避免以后的尴尬。 + +文档 +---- + +文档通常比内核开发规则更为例外。即便如此,足够的文档将有助于简化将新代码合并 +到内核中的过程,使其他开发人员的生活更轻松,并对您的用户有所帮助。在许多情况 +下,文件的添加已基本上成为强制性的。 + +任何补丁的第一个文档是其关联的变更日志。日志条目应该描述正在解决的问题、解决 +方案的形式、处理补丁的人员、对性能的任何相关影响,以及理解补丁可能需要的任何 +其他内容。确保changelog说明了为什么补丁值得应用;大量开发人员未能提供这些信息。 + +任何添加新用户空间界面的代码(包括新的sysfs或/proc文件)都应该包含该界面的 +文档,该文档使用户空间开发人员能够知道他们在使用什么。请参阅 +Documentation/ABI/README,了解如何格式化此文档以及需要提供哪些信息。 + +文件 :ref:`Documentation/admin-guide/kernel-parameters.rst <kernelparameters>` +描述了内核的所有引导时间参数。任何添加新参数的补丁都应该向该文件添加适当的 +条目。 + +任何新的配置选项都必须附有帮助文本,帮助文本清楚地解释了这些选项以及用户可能 +希望何时选择它们。 + +许多子系统的内部API信息通过专门格式化的注释进行记录;这些注释可以通过 +“kernel-doc”脚本以多种方式提取和格式化。如果您在具有kerneldoc注释的子系统中 +工作,则应该维护它们,并根据需要为外部可用的功能添加它们。即使在没有如此记录 +的领域中,为将来添加kerneldoc注释也没有坏处;实际上,这对于刚开始开发内核的人 +来说是一个有用的活动。这些注释的格式以及如何创建kerneldoc模板的一些信息可以在 +:ref:`Documentation/doc-guide/ <doc_guide>` 上找到。 + +任何阅读大量现有内核代码的人都会注意到,注释的缺失往往是最值得注意的。再一次, +对新代码的期望比过去更高;合并未注释的代码将更加困难。这就是说,人们几乎不希望 +用语言注释代码。代码本身应该是可读的,注释解释了更微妙的方面。 + +某些事情应该总是被注释。使用内存屏障时,应附上一行文字,解释为什么需要设置内存 +屏障。数据结构的锁定规则通常需要在某个地方解释。一般来说,主要数据结构需要全面 +的文档。应该指出单独代码位之间不明显的依赖性。任何可能诱使代码看门人进行错误的 +“清理”的事情都需要一个注释来说明为什么要这样做。等等。 + + +内部API更改 +----------- + +内核提供给用户空间的二进制接口不能被破坏,除非在最严重的情况下。相反,内核的 +内部编程接口是高度流动的,当需要时可以更改。如果你发现自己不得不处理一个内核 +API,或者仅仅因为它不满足你的需求而不使用特定的功能,这可能是API需要改变的一 +个标志。作为内核开发人员,您有权进行此类更改。 + +当然, 可以进行API更改,但它们必须是合理的。因此,任何进行内部API更改的补丁都 +应该附带一个关于更改内容和必要原因的描述。这种变化也应该分解成一个单独的补丁, +而不是埋在一个更大的补丁中。 + +另一个要点是,更改内部API的开发人员通常要负责修复内核树中被更改破坏的任何代码。 +对于一个广泛使用的函数,这个职责可以导致成百上千的变化,其中许多变化可能与其他 +开发人员正在做的工作相冲突。不用说,这可能是一项大工作,所以最好确保理由是 +可靠的。请注意,coccinelle工具可以帮助进行广泛的API更改。 + +在进行不兼容的API更改时,应尽可能确保编译器捕获未更新的代码。这将帮助您确保找 +到该接口的树内用处。它还将警告开发人员树外代码存在他们需要响应的更改。支持树外 +代码不是内核开发人员需要担心的事情,但是我们也不必使树外开发人员的生活有不必要 +的困难。 diff --git a/Documentation/translations/zh_CN/process/5.Posting.rst b/Documentation/translations/zh_CN/process/5.Posting.rst new file mode 100644 index 000000000..9ff9945f9 --- /dev/null +++ b/Documentation/translations/zh_CN/process/5.Posting.rst @@ -0,0 +1,240 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/5.Posting.rst <development_posting>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_posting: + +发布补丁 +======== + +迟早,当您的工作准备好提交给社区进行审查,并最终包含到主线内核中时。不出所料, +内核开发社区已经发展出一套用于发布补丁的约定和过程;遵循这些约定和过程将使 +参与其中的每个人的生活更加轻松。本文件将试图合理详细地涵盖这些期望;更多信息 +也可在以下文件中找到 +:ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>`, +:ref:`Documentation/process/submitting-drivers.rst <submittingdrivers>` +和 :ref:`Documentation/translations/zh_CN/process/submit-checklist.rst <cn_submitchecklist>`. + +何时邮寄 +-------- + +在补丁完全“准备好”之前,有一个不断的诱惑来避免发布补丁。对于简单的补丁, +这不是问题。但是,如果正在完成的工作很复杂,那么在工作完成之前从社区获得 +反馈就可以获得很多好处。因此,您应该考虑发布正在进行的工作,甚至使Git树 +可用,以便感兴趣的开发人员可以随时赶上您的工作。 + +当发布还没有准备好包含的代码时,最好在发布本身中这样说。还应提及任何有待完成 +的主要工作和任何已知问题。很少有人会看到那些被认为是半生不熟的补丁,但是那些 +人会想到他们可以帮助你把工作推向正确的方向。 + +创建补丁之前 +------------ + +在考虑将补丁发送到开发社区之前,有许多事情应该做。这些包括: + + - 尽可能地测试代码。利用内核的调试工具,确保内核使用所有合理的配置选项组合 + 进行构建,使用跨编译器为不同的体系结构进行构建等。 + + - 确保您的代码符合内核编码风格指南。 + + - 您的更改是否具有性能影响?如果是这样,您应该运行基准测试来显示您的变更的 + 影响(或好处);结果的摘要应该包含在补丁中。 + + - 确保您有权发布代码。如果这项工作是为雇主完成的,雇主对这项工作具有所有权, + 并且必须同意根据GPL对其进行放行。 + +一般来说,在发布代码之前进行一些额外的思考,几乎总是能在短时间内得到回报。 + +补丁准备 +-------- + +准备发布补丁可能是一个惊人的工作量,但再次尝试节省时间在这里通常是不明智的, +即使在短期内。 + +必须针对内核的特定版本准备补丁。作为一般规则,补丁程序应该基于Linus的Git树中 +的当前主线。当以主线为基础时,从一个众所周知的发布点开始——一个稳定的或RC的 +发布——而不是在一个主线分支任意点。 + +但是,可能需要针对-mm、linux-next或子系统树生成版本,以便于更广泛的测试和审查。 +根据补丁的区域以及其他地方的情况,针对这些其他树建立补丁可能需要大量的工作来 +解决冲突和处理API更改。 + +只有最简单的更改才应格式化为单个补丁;其他所有更改都应作为一系列逻辑更改进行。 +分割补丁是一门艺术;一些开发人员花了很长时间来弄清楚如何按照社区期望的方式来 +做。然而,有一些经验法则可以大大帮助: + + - 您发布的补丁程序系列几乎肯定不会是工作系统中的一系列更改。相反,您所做的 + 更改需要在最终形式中加以考虑,然后以有意义的方式进行拆分。开发人员对离散的、 + 自包含的更改感兴趣,而不是您获取这些更改的路径。 + + - 每个逻辑上独立的变更都应该格式化为单独的补丁。这些更改可以是小的(“向此 + 结构添加字段”)或大的(例如,添加一个重要的新驱动程序),但它们在概念上 + 应该是小的,并且可以接受一行描述。每个补丁都应该做一个特定的更改,可以单独 + 检查并验证它所做的事情。 + + - 作为重申上述准则的一种方法:不要在同一补丁中混合不同类型的更改。如果一个 + 补丁修复了一个关键的安全漏洞,重新排列了一些结构,并重新格式化了代码,那么 + 很有可能它会被忽略,而重要的修复将丢失。 + + - 每个补丁都应该产生一个内核,它可以正确地构建和运行;如果补丁系列在中间被 + 中断,那么结果应该仍然是一个工作的内核。补丁系列的部分应用是使用 + “git bisct”工具查找回归的一个常见场景;如果结果是一个损坏的内核,那么对于 + 那些从事追踪问题的高尚工作的开发人员和用户来说,将使他们的生活更加艰难。 + + - 不过,不要过分。一位开发人员曾经将一组编辑内容作为500个单独的补丁发布到一个 + 文件中,这并没有使他成为内核邮件列表中最受欢迎的人。一个补丁可以相当大, + 只要它仍然包含一个单一的逻辑变更。 + + - 用一系列补丁添加一个全新的基础设施是很有诱惑力的,但是在系列中的最后一个 + 补丁启用整个补丁之前,该基础设施是不使用的。如果可能的话,应该避免这种 + 诱惑;如果这个系列增加了回归,那么二分法将指出最后一个补丁是导致问题的 + 补丁,即使真正的bug在其他地方。只要有可能,添加新代码的补丁程序应该立即 + 激活该代码。 + +创建完美补丁系列的工作可能是一个令人沮丧的过程,在完成“真正的工作”之后需要花费 +大量的时间和思考。但是,如果做得好,这是一段很好的时间。 + +补丁格式和更改日志 +------------------ + +所以现在你有了一系列完美的补丁可以发布,但是这项工作还没有完成。每个补丁都 +需要被格式化成一条消息,它可以快速而清晰地将其目的传达给世界其他地方。为此, +每个补丁将由以下部分组成: + + - 命名补丁作者的可选“from”行。只有当你通过电子邮件传递别人的补丁时,这一行 + 才是必要的,但是如果有疑问,添加它不会有任何伤害。 + + - 一行描述补丁的作用。对于没有其他上下文的读者来说,此消息应该足够了解补丁 + 的范围;这是将在“短格式”变更日志中显示的行。此消息通常首先用相关的子系统 + 名称格式化,然后是补丁的目的。例如: + + :: + + gpio: fix build on CONFIG_GPIO_SYSFS=n + + - 一个空白行,后面是补丁内容的详细描述。这个描述可以是必需的;它应该说明补丁 + 的作用以及为什么它应该应用于内核。 + + - 一个或多个标记行,至少有一个由补丁作者的:signed-off-by 签名。签名将在下面 + 更详细地描述。 + +上面的项目一起构成补丁的变更日志。写一篇好的变更日志是一门至关重要但常常被 +忽视的艺术;值得花一点时间来讨论这个问题。当你写一个变更日志时,你应该记住 +有很多不同的人会读你的话。其中包括子系统维护人员和审查人员,他们需要决定是否 +应该包括补丁,分销商和其他维护人员试图决定是否应该将补丁反向移植到其他内核, +bug搜寻人员想知道补丁是否负责他们正在追查的问题,想知道内核如何变化的用户。 +等等。一个好的变更日志以最直接和最简洁的方式向所有这些人传达所需的信息。 + +为此,总结行应该描述变更的影响和动机,以及在一行约束条件下可能发生的变化。 +然后,详细的描述可以详述这些主题,并提供任何需要的附加信息。如果补丁修复了 +一个bug,请引用引入该bug的commit(如果可能,请在引用commits时同时提供commit id +和标题)。如果某个问题与特定的日志或编译器输出相关联,请包含该输出以帮助其他 +人搜索同一问题的解决方案。如果更改是为了支持以后补丁中的其他更改,那么就这么 +说。如果更改了内部API,请详细说明这些更改以及其他开发人员应该如何响应。一般 +来说,你越能把自己放在每个阅读你的changelog的人的位置上,changelog(和内核 +作为一个整体)就越好。 + +不用说,变更日志应该是将变更提交到修订控制系统时使用的文本。接下来是: + + - 补丁本身,采用统一的(“-u”)补丁格式。将“-p”选项用于diff将使函数名与更改 + 相关联,从而使结果补丁更容易被其他人读取。 + +您应该避免在补丁中包括对不相关文件(例如,由构建过程生成的文件或编辑器 +备份文件)的更改。文档目录中的文件“dontdiff”在这方面有帮助;使用“-X”选项将 +其传递给diff。 + +上面提到的标签用于描述各种开发人员如何与这个补丁的开发相关联。 +:ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` +文档中对它们进行了详细描述;下面是一个简短的总结。每一行的格式如下: + +:: + + tag: Full Name <email address> optional-other-stuff + +常用的标签有: + + - Signed-off-by: 这是一个开发人员的证明,他或她有权提交补丁以包含到内核中。 + 这是开发来源认证协议,其全文可在 + :ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` + 中找到,如果没有适当的签字,则不能合并到主线中。 + + - Co-developed-by: 声明补丁是由多个开发人员共同创建的;当几个人在一个补丁上 + 工作时,它用于将属性赋予共同作者(除了 From: 所赋予的作者之外)。因为 + Co-developed-by: 表示作者身份,所以每个共同开发人, 必须紧跟在相关合作作者 + 的签名之后。具体内容和示例可以在以下文件中找到 + :ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` + + - Acked-by: 表示另一个开发人员(通常是相关代码的维护人员)同意补丁适合包含 + 在内核中。 + + - Tested-by: 声明指定的人已经测试了补丁并发现它可以工作。 + + - Reviewed-by: 指定的开发人员已经审查了补丁的正确性;有关详细信息,请参阅 + :ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` + + - Reported-by: 指定报告此补丁修复的问题的用户;此标记用于提供感谢。 + + - Cc:指定的人收到了补丁的副本,并有机会对此发表评论。 + +在补丁中添加标签时要小心:只有cc:才适合在没有指定人员明确许可的情况下添加。 + +发送补丁 +-------- + +在邮寄补丁之前,您还需要注意以下几点: + + - 您确定您的邮件发送程序不会损坏补丁吗?有免费的空白更改或由邮件客户端 + 执行的行包装的补丁不会在另一端复原,并且通常不会进行任何详细检查。如果有 + 任何疑问,把补丁寄给你自己,让你自己相信它是完好无损的。 + + :ref:`Documentation/translations/zh_CN/process/email-clients.rst <cn_email_clients>` + 提供了一些有用的提示,可以让特定的邮件客户机工作以发送补丁。 + + - 你确定你的补丁没有愚蠢的错误吗?您应该始终通过scripts/checkpatch.pl运行 + 补丁程序,并解决它提出的投诉。请记住,checkpatch.pl虽然是大量思考内核 + 补丁应该是什么样子的体现,但它并不比您聪明。如果修复checkpatch.pl投诉会 + 使代码变得更糟,请不要这样做。 + +补丁应始终以纯文本形式发送。请不要将它们作为附件发送;这使得审阅者在答复中更难 +引用补丁的部分。相反,只需将补丁直接放到您的消息中。 + +邮寄补丁时,重要的是将副本发送给任何可能感兴趣的人。与其他一些项目不同,内核 +鼓励人们错误地发送过多的副本;不要假定相关人员会看到您在邮件列表中的发布。 +尤其是,副本应发送至: + + - 受影响子系统的维护人员。如前所述,维护人员文件是查找这些人员的第一个地方。 + + - 其他在同一领域工作的开发人员,尤其是那些现在可能在那里工作的开发人员。使用 + git查看还有谁修改了您正在处理的文件,这很有帮助。 + + - 如果您对错误报告或功能请求做出响应,也可以抄送原始发送人。 + + - 将副本发送到相关邮件列表,或者,如果没有其他应用,则发送到Linux内核列表。 + + - 如果您正在修复一个bug,请考虑该修复是否应进入下一个稳定更新。如果是这样, + stable@vger.kernel.org 应该得到补丁的副本。另外,在补丁本身的标签中添加 + 一个“cc:stable@vger.kernel.org”;这将使稳定团队在修复进入主线时收到通知。 + +当为一个补丁选择接收者时,最好知道你认为谁最终会接受这个补丁并将其合并。虽然 +可以将补丁直接发送给LinusTorvalds并让他合并,但通常情况下不会这样做。Linus +很忙,并且有子系统维护人员负责监视内核的特定部分。通常您会希望维护人员合并您 +的补丁。如果没有明显的维护人员,Andrew Morton通常是最后的补丁目标。 + +补丁需要好的主题行。补丁程序行的规范格式如下: + +:: + + [PATCH nn/mm] subsys: one-line description of the patch + +其中“nn”是补丁的序号,“mm”是系列中补丁的总数,“subsys”是受影响子系统的名称。 +显然,一个单独的补丁可以省略nn/mm。 + +如果您有一系列重要的补丁,那么通常将介绍性描述作为零部分发送。不过,这种约定 +并没有得到普遍遵循;如果您使用它,请记住简介中的信息不会使它进入内核变更日志。 +因此,请确保补丁本身具有完整的变更日志信息。 + +一般来说,多部分补丁的第二部分和后续部分应作为对第一部分的回复发送,以便它们 +在接收端都连接在一起。像git和coilt这样的工具有命令,可以通过适当的线程发送 +一组补丁。但是,如果您有一个长系列,并且正在使用git,请远离–chain reply-to +选项,以避免创建异常深的嵌套。 diff --git a/Documentation/translations/zh_CN/process/6.Followthrough.rst b/Documentation/translations/zh_CN/process/6.Followthrough.rst new file mode 100644 index 000000000..f509e077e --- /dev/null +++ b/Documentation/translations/zh_CN/process/6.Followthrough.rst @@ -0,0 +1,145 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/6.Followthrough.rst <development_followthrough>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_followthrough: + +跟进 +==== + +在这一点上,您已经遵循了到目前为止给出的指导方针,并且,随着您自己的工程技能 +的增加,已经发布了一系列完美的补丁。即使是经验丰富的内核开发人员也能犯的最大 +错误之一是,认为他们的工作现在已经完成了。事实上,发布补丁意味着进入流程的下 +一个阶段,可能还需要做很多工作。 + +一个补丁在第一次发布时就非常出色,没有改进的余地,这是很罕见的。内核开发流程 +认识到这一事实,因此,它非常注重对已发布代码的改进。作为代码的作者,您应该与 +内核社区合作,以确保您的代码符合内核的质量标准。如果不参与这个过程,很可能会 +阻止将补丁包含到主线中。 + +与审阅者合作 +------------ + +任何意义上的补丁都会导致其他开发人员在审查代码时发表大量评论。对于许多开发 +人员来说,与审查人员合作可能是内核开发过程中最令人生畏的部分。但是,如果你 +记住一些事情,生活会变得容易得多: + + - 如果你已经很好地解释了你的补丁,评论人员会理解它的价值,以及为什么你会 + 费尽心思去写它。但是这个并不能阻止他们提出一个基本的问题:五年或十年后 + 用这个代码维护一个内核会是什么感觉?你可能被要求做出的许多改变——从编码风格 + 的调整到大量的重写——都来自于对Linux的理解,即从现在起十年后,Linux仍将在 + 开发中。 + + - 代码审查是一项艰苦的工作,这是一项相对吃力不讨好的工作;人们记得谁编写了 + 内核代码,但对于那些审查它的人来说,几乎没有什么持久的名声。因此,评论 + 人员可能会变得暴躁,尤其是当他们看到同样的错误被一遍又一遍地犯下时。如果 + 你得到了一个看起来愤怒、侮辱或完全冒犯你的评论,抵制以同样方式回应的冲动。 + 代码审查是关于代码的,而不是关于人的,代码审查人员不会亲自攻击您。 + + - 同样,代码审查人员也不想以牺牲你雇主的利益为代价来宣传他们雇主的议程。 + 内核开发人员通常希望今后几年能在内核上工作,但他们明白他们的雇主可能会改 + 变。他们真的,几乎毫无例外地,致力于创造他们所能做到的最好的内核;他们并 + 没有试图给雇主的竞争对手造成不适。 + +所有这些归根结底都是,当审阅者向您发送评论时,您需要注意他们正在进行的技术 +观察。不要让他们的表达方式或你自己的骄傲阻止这种事情的发生。当你在一个补丁 +上得到评论时,花点时间去理解评论人想说什么。如果可能的话,请修复审阅者要求 +您修复的内容。然后回复审稿人:谢谢他们,并描述你将如何回答他们的问题。 + +请注意,您不必同意审阅者建议的每个更改。如果您认为审阅者误解了您的代码,请 +解释到底发生了什么。如果您对建议的更改有技术上的异议,请描述它并证明您对该 +问题的解决方案是正确的。如果你的解释有道理,审稿人会接受的。不过,如果你的 +解释不能证明是有说服力的,尤其是当其他人开始同意审稿人的观点时,请花些时间 +重新考虑一下。你很容易对自己解决问题的方法视而不见,以至于你没有意识到某个 +问题根本是错误的,或者你甚至没有解决正确的问题。 + +Andrew Morton建议,每一条不会导致代码更改的评论都应该导致额外的代码注释; +这可以帮助未来的评论人员避免出现第一次出现的问题。 + +一个致命的错误是忽视评论,希望它们会消失。他们不会走的。如果您在没有对之前 +收到的注释做出响应的情况下重新发布代码,那么很可能会发现补丁毫无用处。 + +说到重新发布代码:请记住,审阅者不会记住您上次发布的代码的所有细节。因此, +提醒审查人员以前提出的问题以及您如何处理这些问题总是一个好主意;补丁变更 +日志是提供此类信息的好地方。审阅者不必搜索列表档案来熟悉上次所说的内容; +如果您帮助他们开始运行,当他们重新访问您的代码时,他们的心情会更好。 + +如果你已经试着做正确的事情,但事情仍然没有进展呢?大多数技术上的分歧都可以 +通过讨论来解决,但有时人们只需要做出决定。如果你真的认为这个决定对你不利, +你可以试着向更高的权力上诉。在这篇文章中,更高的权力倾向于Andrew Morton。 +Andrew在内核开发社区中受i很大的尊重;他经常为似乎被绝望地阻塞事情清障。 +尽管如此,对Andrew的呼吁不应轻而易举,也不应在所有其他替代方案都被探索之前 +使用。当然,记住,他也可能不同意你的意见。 + +接下来会发生什么 +---------------- + +如果一个补丁被认为是添加到内核中的一件好事,并且一旦大多数审查问题得到解决, +下一步通常是进入子系统维护人员的树中。工作方式因子系统而异;每个维护人员都 +有自己的工作方式。特别是,可能有不止一棵树——一棵树,也许,专门用于计划下一 +个合并窗口的补丁,另一棵树用于长期工作。 + +对于应用于没有明显子系统树(例如内存管理修补程序)的区域的修补程序,默认树 +通常以-mm结尾。影响多个子系统的补丁也可以最终通过-mm树。 + +包含在子系统树中可以提高补丁的可见性。现在,使用该树的其他开发人员将默认获 +得补丁。子系统树通常也为Linux提供支持,使其内容对整个开发社区可见。在这一点 +上,您很可能会从一组新的审阅者那里得到更多的评论;这些评论需要像上一轮那样 +得到回答。 + +在这一点上也会发生什么,这取决于你的补丁的性质,是与其他人正在做的工作发生 +冲突。在最坏的情况下,严重的补丁冲突可能会导致一些工作被搁置,以便剩余的补丁 +可以成形并合并。另一些时候,冲突解决将涉及到与其他开发人员合作,可能还会 +在树之间移动一些补丁,以确保所有的应用都是干净的。这项工作可能是一件痛苦的 +事情,但要计算您的福祉:在Linux下一棵树出现之前,这些冲突通常只在合并窗口 +中出现,必须迅速解决。现在可以在合并窗口打开之前,在空闲时解决这些问题。 + +有朝一日,如果一切顺利,您将登录并看到您的补丁已经合并到主线内核中。祝贺你! +然而,一旦庆祝活动完成(并且您已经将自己添加到维护人员文件中),就值得记住 +一个重要的小事实:工作仍然没有完成。并入主线带来了自身的挑战。 + +首先,补丁的可见性再次提高。可能会有新一轮的开发者评论,他们以前不知道这 +个补丁。忽略它们可能很有诱惑力,因为您的代码不再存在任何被合并的问题。但是, +要抵制这种诱惑,您仍然需要对有问题或建议的开发人员作出响应。 + +不过,更重要的是:将代码包含在主线中会将代码交给更大的一组测试人员。即使您 +为尚未提供的硬件提供了驱动程序,您也会惊讶于有多少人会将您的代码构建到内核 +中。当然,如果有测试人员,也会有错误报告。 + +最糟糕的错误报告是回归。如果你的补丁导致回归,你会发现很多不舒服的眼睛盯着 +你;回归需要尽快修复。如果您不愿意或无法修复回归(其他人都不会为您修复), +那么在稳定期内,您的补丁几乎肯定会被移除。除了否定您为使补丁进入主线所做的 +所有工作之外,如果由于未能修复回归而取消补丁,很可能会使将来的工作更难合并。 + +在处理完任何回归之后,可能还有其他普通的bug需要处理。稳定期是修复这些错误并 +确保代码在主线内核版本中的首次发布尽可能可靠的最好机会。所以,请回答错误 +报告,并尽可能解决问题。这就是稳定期的目的;一旦解决了旧补丁的任何问题,就 +可以开始创建酷的新补丁。 + +别忘了,还有其他里程碑也可能会创建bug报告:下一个主线稳定版本,当著名的发行 +商选择包含补丁的内核版本时,等等。继续响应这些报告是您工作的基本骄傲。但是, +如果这不是足够的动机,那么也值得考虑的是,开发社区会记住那些在合并后对代码 +失去兴趣的开发人员。下一次你发布补丁时,他们会以你以后不会在身边维护它为假 +设来评估它。 + +其他可能发生的事情 +------------------ + +有一天,你可以打开你的邮件客户端,看到有人给你寄了一个代码补丁。毕竟,这是 +让您的代码公开存在的好处之一。如果您同意这个补丁,您可以将它转发给子系统 +维护人员(确保包含一个正确的From:行,这样属性是正确的,并添加一个您自己 +的签准),或者回复一个Acked-by,让原始发送者向上发送它。 + +如果您不同意补丁,请发送一个礼貌的回复,解释原因。如果可能的话,告诉作者需要 +做哪些更改才能让您接受补丁。对于代码的编写者和维护者所反对的合并补丁,存在着 +一定的阻力,但仅此而已。如果你被认为不必要的阻碍了好的工作,那么这些补丁最 +终会经过你身边并进入主线。在Linux内核中,没有人对任何代码拥有绝对的否决权。 +除了Linus。 + +在非常罕见的情况下,您可能会看到完全不同的东西:另一个开发人员发布了针对您 +的问题的不同解决方案。在这一点上,两个补丁中的一个可能不会合并,“我的在这里 +是第一个”不被认为是一个令人信服的技术论据。如果有人的补丁取代了你的补丁而进 +入了主线,那么只有一种方法可以回应你:高兴你的问题得到解决,继续你的工作。 +以这种方式把一个人的工作推到一边可能会伤害和气馁,但是在他们忘记了谁的补丁 +真正被合并很久之后,社区会记住你的反应。 diff --git a/Documentation/translations/zh_CN/process/7.AdvancedTopics.rst b/Documentation/translations/zh_CN/process/7.AdvancedTopics.rst new file mode 100644 index 000000000..2f0ef7507 --- /dev/null +++ b/Documentation/translations/zh_CN/process/7.AdvancedTopics.rst @@ -0,0 +1,124 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/7.AdvancedTopics.rst <development_advancedtopics>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_advancedtopics: + +高级主题 +======== + +现在,希望您能够掌握开发流程的工作方式。然而,还有更多的东西要学!本节将介绍 +一些主题,这些主题对希望成为Linux内核开发过程常规部分的开发人员有帮助。 + +使用Git管理补丁 +--------------- + +内核使用分布式版本控制始于2002年初,当时Linus首次开始使用专有的Bitkeeper应用 +程序。虽然bitkeeper存在争议,但它所体现的软件版本管理方法却肯定不是。分布式 +版本控制可以立即加速内核开发项目。在当前的时代,有几种免费的比特保持器替代品。 +无论好坏,内核项目都将Git作为其选择的工具。 + +使用Git管理补丁可以使开发人员的生活更加轻松,尤其是随着补丁数量的增加。Git +也有其粗糙的边缘和一定的危险,它是一个年轻和强大的工具,仍然在其开发人员完善 +中。本文档不会试图教会读者如何使用git;这会是个巨长的文档。相反,这里的重点 +将是Git如何特别适合内核开发过程。想要加快Git的开发人员可以在以下网站上找到 +更多信息: + + https://git-scm.com/ + + https://www.kernel.org/pub/software/scm/git/docs/user-manual.html + +在尝试使用它使补丁可供其他人使用之前,第一要务是阅读上述站点,对Git的工作 +方式有一个扎实的了解。使用Git的开发人员应该能够获得主线存储库的副本,探索 +修订历史,提交对树的更改,使用分支等。了解Git用于重写历史的工具(如Rebase) +也很有用。Git有自己的术语和概念;Git的新用户应该了解refs、远程分支、索引、 +快进合并、推拉、分离头等。一开始可能有点吓人,但这些概念不难通过一点学习来 +理解。 + +使用git生成通过电子邮件提交的补丁是提高速度的一个很好的练习。 + +当您准备好开始安装Git树供其他人查看时,您当然需要一个可以从中提取的服务器。 +如果您有一个可以访问Internet的系统,那么使用git守护进程设置这样的服务器相 +对简单。否则,免费的公共托管网站(例如github)开始出现在网络上。成熟的开发 +人员可以在kernel.org上获得一个帐户,但这些帐户并不容易找到;有关更多信息, +请参阅 https://kernel.org/faq/ + +正常的Git工作流程涉及到许多分支的使用。每一条开发线都可以分为单独的“主题 +分支”,并独立维护。Git的分支机构很便宜,没有理由不免费使用它们。而且,在 +任何情况下,您都不应该在任何您打算让其他人从中受益的分支中进行开发。应该 +小心地创建公开可用的分支;当它们处于完整的形式并准备好运行时(而不是之前), +合并开发分支的补丁。 + +Git提供了一些强大的工具,可以让您重写开发历史。一个不方便的补丁(比如说, +一个打破二分法的补丁,或者有其他一些明显的缺陷)可以在适当的位置修复,或者 +完全从历史中消失。一个补丁系列可以被重写,就好像它是在今天的主线之上写的 +一样,即使你已经花了几个月的时间在写它。可以透明地将更改从一个分支转移到另 +一个分支。等等。明智地使用git修改历史的能力可以帮助创建问题更少的干净补丁集。 + +然而,过度使用这种能力可能会导致其他问题,而不仅仅是对创建完美项目历史的 +简单痴迷。重写历史将重写该历史中包含的更改,将经过测试(希望)的内核树变 +为未经测试的内核树。但是,除此之外,如果开发人员没有对项目历史的共享视图, +他们就无法轻松地协作;如果您重写了其他开发人员拉入他们存储库的历史,您将 +使这些开发人员的生活更加困难。因此,这里有一个简单的经验法则:被导出到其他 +人的历史在此后通常被认为是不可变的。 + +因此,一旦将一组更改推送到公开可用的服务器上,就不应该重写这些更改。如果您 +尝试强制进行不会导致快进合并(即不共享同一历史记录的更改)的更改,Git将尝 +试强制执行此规则。可以重写此检查,有时可能需要重写导出的树。在树之间移动变 +更集以避免Linux-next中的冲突就是一个例子。但这种行为应该是罕见的。这就是为 +什么开发应该在私有分支中进行(必要时可以重写)并且只有在公共分支处于合理的 +高级状态时才转移到公共分支中的原因之一。 + +当主线(或其他一组变更所基于的树)前进时,很容易与该树合并以保持领先地位。 +对于一个私有的分支,rebasing 可能是一个很容易跟上另一棵树的方法,但是一旦 +一棵树被导出到全世界,rebasing就不是一个选项。一旦发生这种情况,就必须进行 +完全合并(merge)。合并有时是很有意义的,但是过于频繁的合并会不必要地扰乱 +历史。在这种情况下,建议的技术是不经常合并,通常只在特定的发布点(如主线-rc +发布)合并。如果您对特定的更改感到紧张,则可以始终在私有分支中执行测试合并。 +在这种情况下,git rerere 工具很有用;它记住合并冲突是如何解决的,这样您就 +不必重复相同的工作。 + +关于Git这样的工具的一个最大的反复抱怨是:补丁从一个存储库到另一个存储库的 +大量移动使得很容易陷入错误建议的变更中,这些变更避开审查雷达进入主线。当内 +核开发人员看到这种情况发生时,他们往往会感到不高兴;在Git树上放置未查看或 +主题外的补丁可能会影响您将来获取树的能力。引用Linus: + +:: + + 你可以给我发补丁,但要我从你哪里取一个Git补丁,我需要知道你知道 + 你在做什么,我需要能够相信事情而不去检查每个个人改变。 + +(http://lwn.net/articles/224135/)。 + +为了避免这种情况,请确保给定分支中的所有补丁都与相关主题紧密相关;“驱动程序 +修复”分支不应更改核心内存管理代码。而且,最重要的是,不要使用Git树来绕过 +审查过程。不时的将树的摘要发布到相关的列表中,当时间合适时,请求 +Linux-next 中包含该树。 + +如果其他人开始发送补丁以包含到您的树中,不要忘记查看它们。还要确保您维护正确 +的作者信息; ``git am`` 工具在这方面做得最好,但是如果它通过第三方转发给您, +您可能需要在补丁中添加“From:”行。 + +请求pull操作时,请务必提供所有相关信息:树的位置、要拉的分支以及拉操作将导致 +的更改。在这方面,git request pull 命令非常有用;它将按照其他开发人员的预期 +格式化请求,并检查以确保您记住了将这些更改推送到公共服务器。 + +审查补丁 +-------- + +一些读者当然会反对将本节与“高级主题”放在一起,因为即使是刚开始的内核开发人员 +也应该检查补丁。当然,学习如何在内核环境中编程没有比查看其他人发布的代码更好 +的方法了。此外,审阅者永远供不应求;通过查看代码,您可以对整个流程做出重大贡献。 + +审查代码可能是一个令人生畏的前景,特别是对于一个新的内核开发人员来说,他们 +可能会对公开询问代码感到紧张,而这些代码是由那些有更多经验的人发布的。不过, +即使是最有经验的开发人员编写的代码也可以得到改进。也许对评审员(所有评审员) +最好的建议是:把评审评论当成问题而不是批评。询问“在这条路径中如何释放锁?” +总是比说“这里的锁是错误的”更好。 + +不同的开发人员将从不同的角度审查代码。一些主要关注的是编码样式以及代码行是 +否有尾随空格。其他人将主要关注补丁作为一个整体实现的变更是否对内核有好处。 +然而,其他人会检查是否存在锁定问题、堆栈使用过度、可能的安全问题、在其他 +地方发现的代码重复、足够的文档、对性能的不利影响、用户空间ABI更改等。所有 +类型的检查,如果它们导致更好的代码进入内核,都是受欢迎和值得的。 diff --git a/Documentation/translations/zh_CN/process/8.Conclusion.rst b/Documentation/translations/zh_CN/process/8.Conclusion.rst new file mode 100644 index 000000000..90cec3de6 --- /dev/null +++ b/Documentation/translations/zh_CN/process/8.Conclusion.rst @@ -0,0 +1,64 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/8.Conclusion.rst <development_conclusion>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_conclusion: + +更多信息 +======== + +关于Linux内核开发和相关主题的信息来源很多。首先是在内核源代码分发中找到的 +文档目录。顶级 :ref:`Documentation/translations/zh_CN/process/howto.rst <cn_process_howto>` +文件是一个重要的起点 +:ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` +和 :ref:`process/submitting-drivers.rst <submittingdrivers>` +也是所有内核开发人员都应该阅读的内容。许多内部内核API都是使用kerneldoc机制 +记录的;“make htmldocs”或“make pdfdocs”可用于以HTML或PDF格式生成这些文档( +尽管某些发行版提供的tex版本会遇到内部限制,无法正确处理文档)。 + +不同的网站在各个细节层次上讨论内核开发。您的作者想谦虚地建议用 https://lwn.net/ +作为来源;有关许多特定内核主题的信息可以通过以下网址的lwn内核索引找到: + + http://lwn.net/kernel/index/ + +除此之外,内核开发人员的一个宝贵资源是: + + https://kernelnewbies.org/ + +当然,我们不应该忘记 https://kernel.org/ 这是内核发布信息的最终位置。 + +关于内核开发有很多书: + + Linux设备驱动程序,第三版(Jonathan Corbet、Alessandro Rubini和Greg Kroah Hartman)。 + 在线:http://lwn.net/kernel/ldd3/ + + Linux内核开发(Robert Love)。 + + 了解Linux内核(Daniel Bovet和Marco Cesati)。 + +然而,所有这些书都有一个共同的缺点:当它们上架时,它们往往有些过时,而且它们 +已经上架一段时间了。不过,在那里还可以找到相当多的好信息。 + +有关git的文档,请访问: + + https://www.kernel.org/pub/software/scm/git/docs/ + + https://www.kernel.org/pub/software/scm/git/docs/user-manual.html + +结论 +==== + +祝贺所有通过这篇冗长的文件的人。希望它能够帮助您理解Linux内核是如何开发的, +以及您如何参与这个过程。 + +最后,重要的是参与。任何开源软件项目都不超过其贡献者投入其中的总和。Linux内核 +的发展速度和以前一样快,因为它得到了大量开发人员的帮助,他们都在努力使它变得 +更好。内核是一个主要的例子,说明当成千上万的人为了一个共同的目标一起工作时, +可以做些什么。 + +不过,内核总是可以从更大的开发人员基础中获益。总有更多的工作要做。但是,同样 +重要的是,Linux生态系统中的大多数其他参与者可以通过为内核做出贡献而受益。使 +代码进入主线是提高代码质量、降低维护和分发成本、提高对内核开发方向的影响程度 +等的关键。这是一种人人都赢的局面。踢开你的编辑,来加入我们吧,你会非常受 +欢迎的。 diff --git a/Documentation/translations/zh_CN/process/code-of-conduct-interpretation.rst b/Documentation/translations/zh_CN/process/code-of-conduct-interpretation.rst new file mode 100644 index 000000000..c323ce76e --- /dev/null +++ b/Documentation/translations/zh_CN/process/code-of-conduct-interpretation.rst @@ -0,0 +1,108 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/code-of-conduct-interpretation.rst <code_of_conduct_interpretation>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_code_of_conduct_interpretation: + +Linux内核贡献者契约行为准则解释 +=============================== + +:ref:`cn_code_of_conduct` 准则是一个通用文档,旨在为几乎所有开源社区提供一套规则。 +每个开源社区都是独一无二的,Linux内核也不例外。因此,本文描述了Linux内核社区中 +如何解释它。我们也不希望这种解释随着时间的推移是静态的,并将根据需要进行调整。 + +与开发软件的“传统”方法相比,Linux内核开发工作是一个非常个人化的过程。你的贡献 +和背后的想法将被仔细审查,往往导致批判和批评。审查将几乎总是需要改进,材料才 +能包括在内核中。要知道这是因为所有相关人员都希望看到Linux整体成功的最佳解决方 +案。这个开发过程已经被证明可以创建有史以来最健壮的操作系统内核,我们不想做任何 +事情来导致提交质量和最终结果的下降。 + +维护者 +------ + +行为准则多次使用“维护者”一词。在内核社区中,“维护者”是负责子系统、驱动程序或 +文件的任何人,并在内核源代码树的维护者文件中列出。 + +责任 +---- + +《行为准则》提到了维护人员的权利和责任,这需要进一步澄清。 + +首先,最重要的是,有一个合理的期望是由维护人员通过实例来领导。 + +也就是说,我们的社区是广阔的,对维护者没有新的要求,他们单方面处理其他人在 +他们活跃的社区的行为。这一责任由我们所有人承担,最终《行为准则》记录了最终的 +上诉路径,以防有关行为问题的问题悬而未决。 + +维护人员应该愿意在出现问题时提供帮助,并在需要时与社区中的其他人合作。如果您 +不确定如何处理出现的情况,请不要害怕联系技术咨询委员会(TAB)或其他维护人员。 +除非您愿意,否则不会将其视为违规报告。如果您不确定是否该联系TAB 或任何其他维 +护人员,请联系我们的冲突调解人 Mishi Choudhary <mishi@linux.com>。 + +最后,“善待对方”才是每个人的最终目标。我们知道每个人都是人,有时我们都会失败, +但我们所有人的首要目标应该是努力友好地解决问题。执行行为准则将是最后的选择。 + +我们的目标是创建一个强大的、技术先进的操作系统,以及所涉及的技术复杂性,这自 +然需要专业知识和决策。 + +所需的专业知识因贡献领域而异。它主要由上下文和技术复杂性决定,其次由贡献者和 +维护者的期望决定。 + +专家的期望和决策都要经过讨论,但在最后,为了取得进展,必须能够做出决策。这一 +特权掌握在维护人员和项目领导的手中,预计将善意使用。 + +因此,设定专业知识期望、作出决定和拒绝不适当的贡献不被视为违反行为准则。 + +虽然维护人员一般都欢迎新来者,但他们帮助(新)贡献者克服障碍的能力有限,因此 +他们必须确定优先事项。这也不应被视为违反了行为准则。内核社区意识到这一点,并 +以各种形式提供入门级节目,如 kernelnewbies.org 。 + +范围 +---- + +Linux内核社区主要在一组公共电子邮件列表上进行交互,这些列表分布在由多个不同 +公司或个人控制的多个不同服务器上。所有这些列表都在内核源代码树中的 +MAINTAINERS 文件中定义。发送到这些邮件列表的任何电子邮件都被视为包含在行为 +准则中。 + +使用 kernel.org bugzilla和其他子系统bugzilla 或bug跟踪工具的开发人员应该遵循 +行为准则的指导原则。Linux内核社区没有“官方”项目电子邮件地址或“官方”社交媒体 +地址。使用kernel.org电子邮件帐户执行的任何活动必须遵循为kernel.org发布的行为 +准则,就像任何使用公司电子邮件帐户的个人必须遵循该公司的特定规则一样。 + +行为准则并不禁止在邮件列表消息、内核更改日志消息或代码注释中继续包含名称、 +电子邮件地址和相关注释。 + +其他论坛中的互动包括在适用于上述论坛的任何规则中,通常不包括在行为准则中。 +除了在极端情况下可考虑的例外情况。 + +提交给内核的贡献应该使用适当的语言。在行为准则之前已经存在的内容现在不会被 +视为违反。然而,不适当的语言可以被视为一个bug;如果任何相关方提交补丁, +这样的bug将被更快地修复。当前属于用户/内核API的一部分的表达式,或者反映已 +发布标准或规范中使用的术语的表达式,不被视为bug。 + +执行 +---- + +行为准则中列出的地址属于行为准则委员会。https://kernel.org/code-of-conduct.html +列出了在任何给定时间接收这些电子邮件的确切成员。成员不能访问在加入委员会之前 +或离开委员会之后所做的报告。 + +最初的行为准则委员会由TAB的志愿者以及作为中立第三方的专业调解人组成。委员会 +的首要任务是建立文件化的流程,并将其公开。 + +如果报告人不希望将整个委员会纳入投诉或关切,可直接联系委员会的任何成员,包括 +调解人。 + +行为准则委员会根据流程审查案例(见上文),并根据需要和适当与TAB协商,例如请求 +和接收有关内核社区的信息。 + +委员会做出的任何决定都将提交到表中,以便在必要时与相关维护人员一起执行。行为 +准则委员会的决定可以通过三分之二的投票推翻。 + +每季度,行为准则委员会和标签将提供一份报告,概述行为准则委员会收到的匿名报告 +及其状态,以及任何否决决定的细节,包括完整和可识别的投票细节。 + +我们希望在启动期之后为行为准则委员会人员配备建立一个不同的流程。发生此情况时, +将使用该信息更新此文档。 diff --git a/Documentation/translations/zh_CN/process/code-of-conduct.rst b/Documentation/translations/zh_CN/process/code-of-conduct.rst new file mode 100644 index 000000000..99024df05 --- /dev/null +++ b/Documentation/translations/zh_CN/process/code-of-conduct.rst @@ -0,0 +1,72 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/code-of-conduct.rst <code_of_conduct>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_code_of_conduct: + +贡献者契约行为准则 +++++++++++++++++++ + +我们的誓言 +========== + +为了营造一个开放、友好的环境,我们作为贡献者和维护人承诺,让我们的社区和参 +与者,拥有一个无骚扰的体验,无论年龄、体型、残疾、种族、性别特征、性别认同 +和表达、经验水平、教育程度、社会状况,经济地位、国籍、个人外貌、种族、宗教 +或性身份和取向。 + +我们的标准 +========== + +有助于创造积极环境的行为包括: + +* 使用欢迎和包容的语言 +* 尊重不同的观点和经验 +* 优雅地接受建设性的批评 +* 关注什么对社区最有利 +* 对其他社区成员表示同情 + +参与者的不可接受行为包括: + +* 使用性意味的语言或意象以及不受欢迎的性注意或者更过分的行为 +* 煽动、侮辱/贬损评论以及个人或政治攻击 +* 公开或私下骚扰 +* 未经明确许可,发布他人的私人信息,如物理或电子地址。 +* 在专业场合被合理认为不适当的其他行为 + +我们的责任 +========== + +维护人员负责澄清可接受行为的标准,并应针对任何不可接受行为采取适当和公平的 +纠正措施。 + +维护人员有权和责任删除、编辑或拒绝与本行为准则不一致的评论、承诺、代码、 +wiki编辑、问题和其他贡献,或暂时或永久禁止任何贡献者从事他们认为不适当、 +威胁、冒犯或有害的其他行为。 + +范围 +==== + +当个人代表项目或其社区时,本行为准则既适用于项目空间,也适用于公共空间。 +代表一个项目或社区的例子包括使用一个正式的项目电子邮件地址,通过一个正式 +的社交媒体帐户发布,或者在在线或离线事件中担任指定的代表。项目维护人员可以 +进一步定义和澄清项目的表示。 + +执行 +==== + +如有滥用、骚扰或其他不可接受的行为,可联系行为准则委员会<conduct@kernel.org>。 +所有投诉都将接受审查和调查,并将得到必要和适当的答复。行为准则委员会有义务 +对事件报告人保密。具体执行政策的进一步细节可单独公布。 + +归属 +==== + +本行为准则改编自《贡献者契约》,版本1.4,可从 +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 获取。 + +解释 +==== + +有关Linux内核社区如何解释此文档,请参阅 :ref:`cn_code_of_conduct_interpretation` diff --git a/Documentation/translations/zh_CN/process/coding-style.rst b/Documentation/translations/zh_CN/process/coding-style.rst new file mode 100644 index 000000000..406d43a02 --- /dev/null +++ b/Documentation/translations/zh_CN/process/coding-style.rst @@ -0,0 +1,954 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/coding-style.rst <codingstyle>` + +.. _cn_codingstyle: + +译者:: + + 中文版维护者: 张乐 Zhang Le <r0bertz@gentoo.org> + 中文版翻译者: 张乐 Zhang Le <r0bertz@gentoo.org> + 中文版校译者: 王聪 Wang Cong <xiyou.wangcong@gmail.com> + wheelz <kernel.zeng@gmail.com> + 管旭东 Xudong Guan <xudong.guan@gmail.com> + Li Zefan <lizf@cn.fujitsu.com> + Wang Chen <wangchen@cn.fujitsu.com> + +Linux 内核代码风格 +========================= + +这是一个简短的文档,描述了 linux 内核的首选代码风格。代码风格是因人而异的, +而且我不愿意把自己的观点强加给任何人,但这就像我去做任何事情都必须遵循的原则 +那样,我也希望在绝大多数事上保持这种的态度。请 (在写代码时) 至少考虑一下这里 +的代码风格。 + +首先,我建议你打印一份 GNU 代码规范,然后不要读。烧了它,这是一个具有重大象征 +性意义的动作。 + +不管怎样,现在我们开始: + + +1) 缩进 +-------------- + +制表符是 8 个字符,所以缩进也是 8 个字符。有些异端运动试图将缩进变为 4 (甚至 +2!) 字符深,这几乎相当于尝试将圆周率的值定义为 3。 + +理由:缩进的全部意义就在于清楚的定义一个控制块起止于何处。尤其是当你盯着你的 +屏幕连续看了 20 小时之后,你将会发现大一点的缩进会使你更容易分辨缩进。 + +现在,有些人会抱怨 8 个字符的缩进会使代码向右边移动的太远,在 80 个字符的终端 +屏幕上就很难读这样的代码。这个问题的答案是,如果你需要 3 级以上的缩进,不管用 +何种方式你的代码已经有问题了,应该修正你的程序。 + +简而言之,8 个字符的缩进可以让代码更容易阅读,还有一个好处是当你的函数嵌套太 +深的时候可以给你警告。留心这个警告。 + +在 switch 语句中消除多级缩进的首选的方式是让 ``switch`` 和从属于它的 ``case`` +标签对齐于同一列,而不要 ``两次缩进`` ``case`` 标签。比如: + +.. code-block:: c + + switch (suffix) { + case 'G': + case 'g': + mem <<= 30; + break; + case 'M': + case 'm': + mem <<= 20; + break; + case 'K': + case 'k': + mem <<= 10; + /* fall through */ + default: + break; + } + +不要把多个语句放在一行里,除非你有什么东西要隐藏: + +.. code-block:: c + + if (condition) do_this; + do_something_everytime; + +也不要在一行里放多个赋值语句。内核代码风格超级简单。就是避免可能导致别人误读 +的表达式。 + +除了注释、文档和 Kconfig 之外,不要使用空格来缩进,前面的例子是例外,是有意为 +之。 + +选用一个好的编辑器,不要在行尾留空格。 + + +2) 把长的行和字符串打散 +------------------------------ + +代码风格的意义就在于使用平常使用的工具来维持代码的可读性和可维护性。 + +每一行的长度的限制是 80 列,我们强烈建议您遵守这个惯例。 + +长于 80 列的语句要打散成有意义的片段。除非超过 80 列能显著增加可读性,并且不 +会隐藏信息。子片段要明显短于母片段,并明显靠右。这同样适用于有着很长参数列表 +的函数头。然而,绝对不要打散对用户可见的字符串,例如 printk 信息,因为这样就 +很难对它们 grep。 + + +3) 大括号和空格的放置 +------------------------------ + +C 语言风格中另外一个常见问题是大括号的放置。和缩进大小不同,选择或弃用某种放 +置策略并没有多少技术上的原因,不过首选的方式,就像 Kernighan 和 Ritchie 展示 +给我们的,是把起始大括号放在行尾,而把结束大括号放在行首,所以: + +.. code-block:: c + + if (x is true) { + we do y + } + +这适用于所有的非函数语句块 (if, switch, for, while, do)。比如: + +.. code-block:: c + + switch (action) { + case KOBJ_ADD: + return "add"; + case KOBJ_REMOVE: + return "remove"; + case KOBJ_CHANGE: + return "change"; + default: + return NULL; + } + +不过,有一个例外,那就是函数:函数的起始大括号放置于下一行的开头,所以: + +.. code-block:: c + + int function(int x) + { + body of function + } + +全世界的异端可能会抱怨这个不一致性是... 呃... 不一致的,不过所有思维健全的人 +都知道 (a) K&R 是 **正确的** 并且 (b) K&R 是正确的。此外,不管怎样函数都是特 +殊的 (C 函数是不能嵌套的)。 + +注意结束大括号独自占据一行,除非它后面跟着同一个语句的剩余部分,也就是 do 语 +句中的 "while" 或者 if 语句中的 "else",像这样: + +.. code-block:: c + + do { + body of do-loop + } while (condition); + +和 + +.. code-block:: c + + if (x == y) { + .. + } else if (x > y) { + ... + } else { + .... + } + +理由:K&R。 + +也请注意这种大括号的放置方式也能使空 (或者差不多空的) 行的数量最小化,同时不 +失可读性。因此,由于你的屏幕上的新行是不可再生资源 (想想 25 行的终端屏幕),你 +将会有更多的空行来放置注释。 + +当只有一个单独的语句的时候,不用加不必要的大括号。 + +.. code-block:: c + + if (condition) + action(); + +和 + +.. code-block:: c + + if (condition) + do_this(); + else + do_that(); + +这并不适用于只有一个条件分支是单语句的情况;这时所有分支都要使用大括号: + +.. code-block:: c + + if (condition) { + do_this(); + do_that(); + } else { + otherwise(); + } + +3.1) 空格 +******************** + +Linux 内核的空格使用方式 (主要) 取决于它是用于函数还是关键字。(大多数) 关键字 +后要加一个空格。值得注意的例外是 sizeof, typeof, alignof 和 __attribute__,这 +些关键字某些程度上看起来更像函数 (它们在 Linux 里也常常伴随小括号而使用,尽管 +在 C 里这样的小括号不是必需的,就像 ``struct fileinfo info;`` 声明过后的 +``sizeof info``)。 + +所以在这些关键字之后放一个空格:: + + if, switch, case, for, do, while + +但是不要在 sizeof, typeof, alignof 或者 __attribute__ 这些关键字之后放空格。 +例如, + +.. code-block:: c + + s = sizeof(struct file); + +不要在小括号里的表达式两侧加空格。这是一个 **反例** : + +.. code-block:: c + + s = sizeof( struct file ); + +当声明指针类型或者返回指针类型的函数时, ``*`` 的首选使用方式是使之靠近变量名 +或者函数名,而不是靠近类型名。例子: + +.. code-block:: c + + char *linux_banner; + unsigned long long memparse(char *ptr, char **retptr); + char *match_strdup(substring_t *s); + +在大多数二元和三元操作符两侧使用一个空格,例如下面所有这些操作符:: + + = + - < > * / % | & ^ <= >= == != ? : + +但是一元操作符后不要加空格:: + + & * + - ~ ! sizeof typeof alignof __attribute__ defined + +后缀自加和自减一元操作符前不加空格:: + + ++ -- + +前缀自加和自减一元操作符后不加空格:: + + ++ -- + +``.`` 和 ``->`` 结构体成员操作符前后不加空格。 + +不要在行尾留空白。有些可以自动缩进的编辑器会在新行的行首加入适量的空白,然后 +你就可以直接在那一行输入代码。不过假如你最后没有在那一行输入代码,有些编辑器 +就不会移除已经加入的空白,就像你故意留下一个只有空白的行。包含行尾空白的行就 +这样产生了。 + +当 git 发现补丁包含了行尾空白的时候会警告你,并且可以应你的要求去掉行尾空白; +不过如果你是正在打一系列补丁,这样做会导致后面的补丁失败,因为你改变了补丁的 +上下文。 + + +4) 命名 +------------------------------ + +C 是一个简朴的语言,你的命名也应该这样。和 Modula-2 和 Pascal 程序员不同, +C 程序员不使用类似 ThisVariableIsATemporaryCounter 这样华丽的名字。C 程序员会 +称那个变量为 ``tmp`` ,这样写起来会更容易,而且至少不会令其难于理解。 + +不过,虽然混用大小写的名字是不提倡使用的,但是全局变量还是需要一个具描述性的 +名字。称一个全局函数为 ``foo`` 是一个难以饶恕的错误。 + +全局变量 (只有当你 **真正** 需要它们的时候再用它) 需要有一个具描述性的名字,就 +像全局函数。如果你有一个可以计算活动用户数量的函数,你应该叫它 +``count_active_users()`` 或者类似的名字,你不应该叫它 ``cntuser()`` 。 + +在函数名中包含函数类型 (所谓的匈牙利命名法) 是脑子出了问题——编译器知道那些类 +型而且能够检查那些类型,这样做只能把程序员弄糊涂了。难怪微软总是制造出有问题 +的程序。 + +本地变量名应该简短,而且能够表达相关的含义。如果你有一些随机的整数型的循环计 +数器,它应该被称为 ``i`` 。叫它 ``loop_counter`` 并无益处,如果它没有被误解的 +可能的话。类似的, ``tmp`` 可以用来称呼任意类型的临时变量。 + +如果你怕混淆了你的本地变量名,你就遇到另一个问题了,叫做函数增长荷尔蒙失衡综 +合症。请看第六章 (函数)。 + + +5) Typedef +----------- + +不要使用类似 ``vps_t`` 之类的东西。 + +对结构体和指针使用 typedef 是一个 **错误** 。当你在代码里看到: + +.. code-block:: c + + vps_t a; + +这代表什么意思呢? + +相反,如果是这样 + +.. code-block:: c + + struct virtual_container *a; + +你就知道 ``a`` 是什么了。 + +很多人认为 typedef ``能提高可读性`` 。实际不是这样的。它们只在下列情况下有用: + + (a) 完全不透明的对象 (这种情况下要主动使用 typedef 来 **隐藏** 这个对象实际上 + 是什么)。 + + 例如: ``pte_t`` 等不透明对象,你只能用合适的访问函数来访问它们。 + + .. note:: + + 不透明性和 "访问函数" 本身是不好的。我们使用 pte_t 等类型的原因在于真 + 的是完全没有任何共用的可访问信息。 + + (b) 清楚的整数类型,如此,这层抽象就可以 **帮助** 消除到底是 ``int`` 还是 + ``long`` 的混淆。 + + u8/u16/u32 是完全没有问题的 typedef,不过它们更符合类别 (d) 而不是这里。 + + .. note:: + + 要这样做,必须事出有因。如果某个变量是 ``unsigned long`` ,那么没有必要 + + typedef unsigned long myflags_t; + + 不过如果有一个明确的原因,比如它在某种情况下可能会是一个 ``unsigned int`` + 而在其他情况下可能为 ``unsigned long`` ,那么就不要犹豫,请务必使用 + typedef。 + + (c) 当你使用 sparse 按字面的创建一个 **新** 类型来做类型检查的时候。 + + (d) 和标准 C99 类型相同的类型,在某些例外的情况下。 + + 虽然让眼睛和脑筋来适应新的标准类型比如 ``uint32_t`` 不需要花很多时间,可 + 是有些人仍然拒绝使用它们。 + + 因此,Linux 特有的等同于标准类型的 ``u8/u16/u32/u64`` 类型和它们的有符号 + 类型是被允许的——尽管在你自己的新代码中,它们不是强制要求要使用的。 + + 当编辑已经使用了某个类型集的已有代码时,你应该遵循那些代码中已经做出的选 + 择。 + + (e) 可以在用户空间安全使用的类型。 + + 在某些用户空间可见的结构体里,我们不能要求 C99 类型而且不能用上面提到的 + ``u32`` 类型。因此,我们在与用户空间共享的所有结构体中使用 __u32 和类似 + 的类型。 + +可能还有其他的情况,不过基本的规则是 **永远不要** 使用 typedef,除非你可以明 +确的应用上述某个规则中的一个。 + +总的来说,如果一个指针或者一个结构体里的元素可以合理的被直接访问到,那么它们 +就不应该是一个 typedef。 + + +6) 函数 +------------------------------ + +函数应该简短而漂亮,并且只完成一件事情。函数应该可以一屏或者两屏显示完 (我们 +都知道 ISO/ANSI 屏幕大小是 80x24),只做一件事情,而且把它做好。 + +一个函数的最大长度是和该函数的复杂度和缩进级数成反比的。所以,如果你有一个理 +论上很简单的只有一个很长 (但是简单) 的 case 语句的函数,而且你需要在每个 case +里做很多很小的事情,这样的函数尽管很长,但也是可以的。 + +不过,如果你有一个复杂的函数,而且你怀疑一个天分不是很高的高中一年级学生可能 +甚至搞不清楚这个函数的目的,你应该严格遵守前面提到的长度限制。使用辅助函数, +并为之取个具描述性的名字 (如果你觉得它们的性能很重要的话,可以让编译器内联它 +们,这样的效果往往会比你写一个复杂函数的效果要好。) + +函数的另外一个衡量标准是本地变量的数量。此数量不应超过 5-10 个,否则你的函数 +就有问题了。重新考虑一下你的函数,把它分拆成更小的函数。人的大脑一般可以轻松 +的同时跟踪 7 个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可 +能会记不清你 2 个星期前做过的事情。 + +在源文件里,使用空行隔开不同的函数。如果该函数需要被导出,它的 **EXPORT** 宏 +应该紧贴在它的结束大括号之下。比如: + +.. code-block:: c + + int system_is_up(void) + { + return system_state == SYSTEM_RUNNING; + } + EXPORT_SYMBOL(system_is_up); + +在函数原型中,包含函数名和它们的数据类型。虽然 C 语言里没有这样的要求,在 +Linux 里这是提倡的做法,因为这样可以很简单的给读者提供更多的有价值的信息。 + + +7) 集中的函数退出途径 +------------------------------ + +虽然被某些人声称已经过时,但是 goto 语句的等价物还是经常被编译器所使用,具体 +形式是无条件跳转指令。 + +当一个函数从多个位置退出,并且需要做一些类似清理的常见操作时,goto 语句就很方 +便了。如果并不需要清理操作,那么直接 return 即可。 + +选择一个能够说明 goto 行为或它为何存在的标签名。如果 goto 要释放 ``buffer``, +一个不错的名字可以是 ``out_free_buffer:`` 。别去使用像 ``err1:`` 和 ``err2:`` +这样的GW_BASIC 名称,因为一旦你添加或删除了 (函数的) 退出路径,你就必须对它们 +重新编号,这样会难以去检验正确性。 + +使用 goto 的理由是: + +- 无条件语句容易理解和跟踪 +- 嵌套程度减小 +- 可以避免由于修改时忘记更新个别的退出点而导致错误 +- 让编译器省去删除冗余代码的工作 ;) + +.. code-block:: c + + int fun(int a) + { + int result = 0; + char *buffer; + + buffer = kmalloc(SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + if (condition1) { + while (loop1) { + ... + } + result = 1; + goto out_free_buffer; + } + ... + out_free_buffer: + kfree(buffer); + return result; + } + +一个需要注意的常见错误是 ``一个 err 错误`` ,就像这样: + +.. code-block:: c + + err: + kfree(foo->bar); + kfree(foo); + return ret; + +这段代码的错误是,在某些退出路径上 ``foo`` 是 NULL。通常情况下,通过把它分离 +成两个错误标签 ``err_free_bar:`` 和 ``err_free_foo:`` 来修复这个错误: + +.. code-block:: c + + err_free_bar: + kfree(foo->bar); + err_free_foo: + kfree(foo); + return ret; + +理想情况下,你应该模拟错误来测试所有退出路径。 + + +8) 注释 +------------------------------ + +注释是好的,不过有过度注释的危险。永远不要在注释里解释你的代码是如何运作的: +更好的做法是让别人一看你的代码就可以明白,解释写的很差的代码是浪费时间。 + +一般的,你想要你的注释告诉别人你的代码做了什么,而不是怎么做的。也请你不要把 +注释放在一个函数体内部:如果函数复杂到你需要独立的注释其中的一部分,你很可能 +需要回到第六章看一看。你可以做一些小注释来注明或警告某些很聪明 (或者槽糕) 的 +做法,但不要加太多。你应该做的,是把注释放在函数的头部,告诉人们它做了什么, +也可以加上它做这些事情的原因。 + +当注释内核 API 函数时,请使用 kernel-doc 格式。请看 +Documentation/doc-guide/ 和 scripts/kernel-doc 以获得详细信息。 + +长 (多行) 注释的首选风格是: + +.. code-block:: c + + /* + * This is the preferred style for multi-line + * comments in the Linux kernel source code. + * Please use it consistently. + * + * Description: A column of asterisks on the left side, + * with beginning and ending almost-blank lines. + */ + +对于在 net/ 和 drivers/net/ 的文件,首选的长 (多行) 注释风格有些不同。 + +.. code-block:: c + + /* The preferred comment style for files in net/ and drivers/net + * looks like this. + * + * It is nearly the same as the generally preferred comment style, + * but there is no initial almost-blank line. + */ + +注释数据也是很重要的,不管是基本类型还是衍生类型。为了方便实现这一点,每一行 +应只声明一个数据 (不要使用逗号来一次声明多个数据)。这样你就有空间来为每个数据 +写一段小注释来解释它们的用途了。 + + +9) 你已经把事情弄糟了 +------------------------------ + +这没什么,我们都是这样。可能你的使用了很长时间 Unix 的朋友已经告诉你 +``GNU emacs`` 能自动帮你格式化 C 源代码,而且你也注意到了,确实是这样,不过它 +所使用的默认值和我们想要的相去甚远 (实际上,甚至比随机打的还要差——无数个猴子 +在 GNU emacs 里打字永远不会创造出一个好程序) (译注:Infinite Monkey Theorem) + +所以你要么放弃 GNU emacs,要么改变它让它使用更合理的设定。要采用后一个方案, +你可以把下面这段粘贴到你的 .emacs 文件里。 + +.. code-block:: none + + (defun c-lineup-arglist-tabs-only (ignored) + "Line up argument lists by tabs, not spaces" + (let* ((anchor (c-langelem-pos c-syntactic-element)) + (column (c-langelem-2nd-pos c-syntactic-element)) + (offset (- (1+ column) anchor)) + (steps (floor offset c-basic-offset))) + (* (max steps 1) + c-basic-offset))) + + (dir-locals-set-class-variables + 'linux-kernel + '((c-mode . ( + (c-basic-offset . 8) + (c-label-minimum-indentation . 0) + (c-offsets-alist . ( + (arglist-close . c-lineup-arglist-tabs-only) + (arglist-cont-nonempty . + (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) + (arglist-intro . +) + (brace-list-intro . +) + (c . c-lineup-C-comments) + (case-label . 0) + (comment-intro . c-lineup-comment) + (cpp-define-intro . +) + (cpp-macro . -1000) + (cpp-macro-cont . +) + (defun-block-intro . +) + (else-clause . 0) + (func-decl-cont . +) + (inclass . +) + (inher-cont . c-lineup-multi-inher) + (knr-argdecl-intro . 0) + (label . -1000) + (statement . 0) + (statement-block-intro . +) + (statement-case-intro . +) + (statement-cont . +) + (substatement . +) + )) + (indent-tabs-mode . t) + (show-trailing-whitespace . t) + )))) + + (dir-locals-set-directory-class + (expand-file-name "~/src/linux-trees") + 'linux-kernel) + +这会让 emacs 在 ``~/src/linux-trees`` 下的 C 源文件获得更好的内核代码风格。 + +不过就算你尝试让 emacs 正确的格式化代码失败了,也并不意味着你失去了一切:还可 +以用 ``indent`` 。 + +不过,GNU indent 也有和 GNU emacs 一样有问题的设定,所以你需要给它一些命令选 +项。不过,这还不算太糟糕,因为就算是 GNU indent 的作者也认同 K&R 的权威性 +(GNU 的人并不是坏人,他们只是在这个问题上被严重的误导了),所以你只要给 indent +指定选项 ``-kr -i8`` (代表 ``K&R,8 字符缩进``),或使用 ``scripts/Lindent`` +这样就可以以最时髦的方式缩进源代码。 + +``indent`` 有很多选项,特别是重新格式化注释的时候,你可能需要看一下它的手册。 +不过记住: ``indent`` 不能修正坏的编程习惯。 + + +10) Kconfig 配置文件 +------------------------------ + +对于遍布源码树的所有 Kconfig* 配置文件来说,它们缩进方式有所不同。紧挨着 +``config`` 定义的行,用一个制表符缩进,然而 help 信息的缩进则额外增加 2 个空 +格。举个例子:: + + config AUDIT + bool "Auditing support" + depends on NET + help + Enable auditing infrastructure that can be used with another + kernel subsystem, such as SELinux (which requires this for + logging of avc messages output). Does not do system-call + auditing without CONFIG_AUDITSYSCALL. + +而那些危险的功能 (比如某些文件系统的写支持) 应该在它们的提示字符串里显著的声 +明这一点:: + + config ADFS_FS_RW + bool "ADFS write support (DANGEROUS)" + depends on ADFS_FS + ... + +要查看配置文件的完整文档,请看 Documentation/kbuild/kconfig-language.rst。 + + +11) 数据结构 +------------------------------ + +如果一个数据结构,在创建和销毁它的单线执行环境之外可见,那么它必须要有一个引 +用计数器。内核里没有垃圾收集 (并且内核之外的垃圾收集慢且效率低下),这意味着你 +绝对需要记录你对这种数据结构的使用情况。 + +引用计数意味着你能够避免上锁,并且允许多个用户并行访问这个数据结构——而不需要 +担心这个数据结构仅仅因为暂时不被使用就消失了,那些用户可能不过是沉睡了一阵或 +者做了一些其他事情而已。 + +注意上锁 **不能** 取代引用计数。上锁是为了保持数据结构的一致性,而引用计数是一 +个内存管理技巧。通常二者都需要,不要把两个搞混了。 + +很多数据结构实际上有 2 级引用计数,它们通常有不同 ``类`` 的用户。子类计数器统 +计子类用户的数量,每当子类计数器减至零时,全局计数器减一。 + +这种 ``多级引用计数`` 的例子可以在内存管理 (``struct mm_struct``: mm_users 和 +mm_count),和文件系统 (``struct super_block``: s_count 和 s_active) 中找到。 + +记住:如果另一个执行线索可以找到你的数据结构,但这个数据结构没有引用计数器, +这里几乎肯定是一个 bug。 + + +12) 宏,枚举和RTL +------------------------------ + +用于定义常量的宏的名字及枚举里的标签需要大写。 + +.. code-block:: c + + #define CONSTANT 0x12345 + +在定义几个相关的常量时,最好用枚举。 + +宏的名字请用大写字母,不过形如函数的宏的名字可以用小写字母。 + +一般的,如果能写成内联函数就不要写成像函数的宏。 + +含有多个语句的宏应该被包含在一个 do-while 代码块里: + +.. code-block:: c + + #define macrofun(a, b, c) \ + do { \ + if (a == 5) \ + do_this(b, c); \ + } while (0) + +使用宏的时候应避免的事情: + +1) 影响控制流程的宏: + +.. code-block:: c + + #define FOO(x) \ + do { \ + if (blah(x) < 0) \ + return -EBUGGERED; \ + } while (0) + +**非常** 不好。它看起来像一个函数,不过却能导致 ``调用`` 它的函数退出;不要打 +乱读者大脑里的语法分析器。 + +2) 依赖于一个固定名字的本地变量的宏: + +.. code-block:: c + + #define FOO(val) bar(index, val) + +可能看起来像是个不错的东西,不过它非常容易把读代码的人搞糊涂,而且容易导致看起 +来不相关的改动带来错误。 + +3) 作为左值的带参数的宏: FOO(x) = y;如果有人把 FOO 变成一个内联函数的话,这 + 种用法就会出错了。 + +4) 忘记了优先级:使用表达式定义常量的宏必须将表达式置于一对小括号之内。带参数 + 的宏也要注意此类问题。 + +.. code-block:: c + + #define CONSTANT 0x4000 + #define CONSTEXP (CONSTANT | 3) + +5) 在宏里定义类似函数的本地变量时命名冲突: + +.. code-block:: c + + #define FOO(x) \ + ({ \ + typeof(x) ret; \ + ret = calc_ret(x); \ + (ret); \ + }) + +ret 是本地变量的通用名字 - __foo_ret 更不容易与一个已存在的变量冲突。 + +cpp 手册对宏的讲解很详细。gcc internals 手册也详细讲解了 RTL,内核里的汇编语 +言经常用到它。 + + +13) 打印内核消息 +------------------------------ + +内核开发者应该是受过良好教育的。请一定注意内核信息的拼写,以给人以好的印象。 +不要用不规范的单词比如 ``dont``,而要用 ``do not`` 或者 ``don't`` 。保证这些信 +息简单明了,无歧义。 + +内核信息不必以英文句号结束。 + +在小括号里打印数字 (%d) 没有任何价值,应该避免这样做。 + +<linux/device.h> 里有一些驱动模型诊断宏,你应该使用它们,以确保信息对应于正确 +的设备和驱动,并且被标记了正确的消息级别。这些宏有:dev_err(), dev_warn(), +dev_info() 等等。对于那些不和某个特定设备相关连的信息,<linux/printk.h> 定义 +了 pr_notice(), pr_info(), pr_warn(), pr_err() 和其他。 + +写出好的调试信息可以是一个很大的挑战;一旦你写出后,这些信息在远程除错时能提 +供极大的帮助。然而打印调试信息的处理方式同打印非调试信息不同。其他 pr_XXX() +函数能无条件地打印,pr_debug() 却不;默认情况下它不会被编译,除非定义了 DEBUG +或设定了 CONFIG_DYNAMIC_DEBUG。实际这同样是为了 dev_dbg(),一个相关约定是在一 +个已经开启了 DEBUG 时,使用 VERBOSE_DEBUG 来添加 dev_vdbg()。 + +许多子系统拥有 Kconfig 调试选项来开启 -DDEBUG 在对应的 Makefile 里面;在其他 +情况下,特殊文件使用 #define DEBUG。当一条调试信息需要被无条件打印时,例如, +如果已经包含一个调试相关的 #ifdef 条件,printk(KERN_DEBUG ...) 就可被使用。 + + +14) 分配内存 +------------------------------ + +内核提供了下面的一般用途的内存分配函数: +kmalloc(), kzalloc(), kmalloc_array(), kcalloc(), vmalloc() 和 vzalloc()。 +请参考 API 文档以获取有关它们的详细信息。 + +传递结构体大小的首选形式是这样的: + +.. code-block:: c + + p = kmalloc(sizeof(*p), ...); + +另外一种传递方式中,sizeof 的操作数是结构体的名字,这样会降低可读性,并且可能 +会引入 bug。有可能指针变量类型被改变时,而对应的传递给内存分配函数的 sizeof +的结果不变。 + +强制转换一个 void 指针返回值是多余的。C 语言本身保证了从 void 指针到其他任何 +指针类型的转换是没有问题的。 + +分配一个数组的首选形式是这样的: + +.. code-block:: c + + p = kmalloc_array(n, sizeof(...), ...); + +分配一个零长数组的首选形式是这样的: + +.. code-block:: c + + p = kcalloc(n, sizeof(...), ...); + +两种形式检查分配大小 n * sizeof(...) 的溢出,如果溢出返回 NULL。 + + +15) 内联弊病 +------------------------------ + +有一个常见的误解是 ``内联`` 是 gcc 提供的可以让代码运行更快的一个选项。虽然使 +用内联函数有时候是恰当的 (比如作为一种替代宏的方式,请看第十二章),不过很多情 +况下不是这样。inline 的过度使用会使内核变大,从而使整个系统运行速度变慢。 +因为体积大内核会占用更多的指令高速缓存,而且会导致 pagecache 的可用内存减少。 +想象一下,一次 pagecache 未命中就会导致一次磁盘寻址,将耗时 5 毫秒。5 毫秒的 +时间内 CPU 能执行很多很多指令。 + +一个基本的原则是如果一个函数有 3 行以上,就不要把它变成内联函数。这个原则的一 +个例外是,如果你知道某个参数是一个编译时常量,而且因为这个常量你确定编译器在 +编译时能优化掉你的函数的大部分代码,那仍然可以给它加上 inline 关键字。 +kmalloc() 内联函数就是一个很好的例子。 + +人们经常主张给 static 的而且只用了一次的函数加上 inline,如此不会有任何损失, +因为没有什么好权衡的。虽然从技术上说这是正确的,但是实际上这种情况下即使不加 +inline gcc 也可以自动使其内联。而且其他用户可能会要求移除 inline,由此而来的 +争论会抵消 inline 自身的潜在价值,得不偿失。 + + +16) 函数返回值及命名 +------------------------------ + +函数可以返回多种不同类型的值,最常见的一种是表明函数执行成功或者失败的值。这样 +的一个值可以表示为一个错误代码整数 (-Exxx=失败,0=成功) 或者一个 ``成功`` +布尔值 (0=失败,非0=成功)。 + +混合使用这两种表达方式是难于发现的 bug 的来源。如果 C 语言本身严格区分整形和 +布尔型变量,那么编译器就能够帮我们发现这些错误... 不过 C 语言不区分。为了避免 +产生这种 bug,请遵循下面的惯例:: + + 如果函数的名字是一个动作或者强制性的命令,那么这个函数应该返回错误代 + 码整数。如果是一个判断,那么函数应该返回一个 "成功" 布尔值。 + +比如, ``add work`` 是一个命令,所以 add_work() 在成功时返回 0,在失败时返回 +-EBUSY。类似的,因为 ``PCI device present`` 是一个判断,所以 pci_dev_present() +在成功找到一个匹配的设备时应该返回 1,如果找不到时应该返回 0。 + +所有 EXPORTed 函数都必须遵守这个惯例,所有的公共函数也都应该如此。私有 +(static) 函数不需要如此,但是我们也推荐这样做。 + +返回值是实际计算结果而不是计算是否成功的标志的函数不受此惯例的限制。一般的, +他们通过返回一些正常值范围之外的结果来表示出错。典型的例子是返回指针的函数, +他们使用 NULL 或者 ERR_PTR 机制来报告错误。 + + +17) 不要重新发明内核宏 +------------------------------ + +头文件 include/linux/kernel.h 包含了一些宏,你应该使用它们,而不要自己写一些 +它们的变种。比如,如果你需要计算一个数组的长度,使用这个宏 + +.. code-block:: c + + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +类似的,如果你要计算某结构体成员的大小,使用 + +.. code-block:: c + + #define sizeof_field(t, f) (sizeof(((t*)0)->f)) + +还有可以做严格的类型检查的 min() 和 max() 宏,如果你需要可以使用它们。你可以 +自己看看那个头文件里还定义了什么你可以拿来用的东西,如果有定义的话,你就不应 +在你的代码里自己重新定义。 + + +18) 编辑器模式行和其他需要罗嗦的事情 +-------------------------------------------------- + +有一些编辑器可以解释嵌入在源文件里的由一些特殊标记标明的配置信息。比如,emacs +能够解释被标记成这样的行: + +.. code-block:: c + + -*- mode: c -*- + +或者这样的: + +.. code-block:: c + + /* + Local Variables: + compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c" + End: + */ + +Vim 能够解释这样的标记: + +.. code-block:: c + + /* vim:set sw=8 noet */ + +不要在源代码中包含任何这样的内容。每个人都有他自己的编辑器配置,你的源文件不 +应该覆盖别人的配置。这包括有关缩进和模式配置的标记。人们可以使用他们自己定制 +的模式,或者使用其他可以产生正确的缩进的巧妙方法。 + + +19) 内联汇编 +------------------------------ + +在特定架构的代码中,你可能需要内联汇编与 CPU 和平台相关功能连接。需要这么做时 +就不要犹豫。然而,当 C 可以完成工作时,不要平白无故地使用内联汇编。在可能的情 +况下,你可以并且应该用 C 和硬件沟通。 + +请考虑去写捆绑通用位元 (wrap common bits) 的内联汇编的简单辅助函数,别去重复 +地写下只有细微差异内联汇编。记住内联汇编可以使用 C 参数。 + +大型,有一定复杂度的汇编函数应该放在 .S 文件内,用相应的 C 原型定义在 C 头文 +件中。汇编函数的 C 原型应该使用 ``asmlinkage`` 。 + +你可能需要把汇编语句标记为 volatile,用来阻止 GCC 在没发现任何副作用后就把它 +移除了。你不必总是这样做,尽管,这不必要的举动会限制优化。 + +在写一个包含多条指令的单个内联汇编语句时,把每条指令用引号分割而且各占一行, +除了最后一条指令外,在每个指令结尾加上 \n\t,让汇编输出时可以正确地缩进下一条 +指令: + +.. code-block:: c + + asm ("magic %reg1, #42\n\t" + "more_magic %reg2, %reg3" + : /* outputs */ : /* inputs */ : /* clobbers */); + + +20) 条件编译 +------------------------------ + +只要可能,就不要在 .c 文件里面使用预处理条件 (#if, #ifdef);这样做让代码更难 +阅读并且更难去跟踪逻辑。替代方案是,在头文件中用预处理条件提供给那些 .c 文件 +使用,再给 #else 提供一个空桩 (no-op stub) 版本,然后在 .c 文件内无条件地调用 +那些 (定义在头文件内的) 函数。这样做,编译器会避免为桩函数 (stub) 的调用生成 +任何代码,产生的结果是相同的,但逻辑将更加清晰。 + +最好倾向于编译整个函数,而不是函数的一部分或表达式的一部分。与其放一个 ifdef +在表达式内,不如分解出部分或全部表达式,放进一个单独的辅助函数,并应用预处理 +条件到这个辅助函数内。 + +如果你有一个在特定配置中,可能变成未使用的函数或变量,编译器会警告它定义了但 +未使用,把它标记为 __maybe_unused 而不是将它包含在一个预处理条件中。(然而,如 +果一个函数或变量总是未使用,就直接删除它。) + +在代码中,尽可能地使用 IS_ENABLED 宏来转化某个 Kconfig 标记为 C 的布尔 +表达式,并在一般的 C 条件中使用它: + +.. code-block:: c + + if (IS_ENABLED(CONFIG_SOMETHING)) { + ... + } + +编译器会做常量折叠,然后就像使用 #ifdef 那样去包含或排除代码块,所以这不会带 +来任何运行时开销。然而,这种方法依旧允许 C 编译器查看块内的代码,并检查它的正 +确性 (语法,类型,符号引用,等等)。因此,如果条件不满足,代码块内的引用符号就 +不存在时,你还是必须去用 #ifdef。 + +在任何有意义的 #if 或 #ifdef 块的末尾 (超过几行的),在 #endif 同一行的后面写下 +注解,注释这个条件表达式。例如: + +.. code-block:: c + + #ifdef CONFIG_SOMETHING + ... + #endif /* CONFIG_SOMETHING */ + + +附录 I) 参考 +------------------- + +The C Programming Language, 第二版 +作者:Brian W. Kernighan 和 Denni M. Ritchie. +Prentice Hall, Inc., 1988. +ISBN 0-13-110362-8 (软皮), 0-13-110370-9 (硬皮). + +The Practice of Programming +作者:Brian W. Kernighan 和 Rob Pike. +Addison-Wesley, Inc., 1999. +ISBN 0-201-61586-X. + +GNU 手册 - 遵循 K&R 标准和此文本 - cpp, gcc, gcc internals and indent, +都可以从 https://www.gnu.org/manual/ 找到 + +WG14 是 C 语言的国际标准化工作组,URL: http://www.open-std.org/JTC1/SC22/WG14/ + +Kernel process/coding-style.rst,作者 greg@kroah.com 发表于 OLS 2002: +http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/ diff --git a/Documentation/translations/zh_CN/process/development-process.rst b/Documentation/translations/zh_CN/process/development-process.rst new file mode 100644 index 000000000..30cffe66c --- /dev/null +++ b/Documentation/translations/zh_CN/process/development-process.rst @@ -0,0 +1,26 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/development-process.rst <development_process_main>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_development_process_main: + +内核开发过程指南 +================ + +内容: + +.. toctree:: + :numbered: + :maxdepth: 2 + + 1.Intro + 2.Process + 3.Early-stage + 4.Coding + 5.Posting + 6.Followthrough + 7.AdvancedTopics + 8.Conclusion + +本文档的目的是帮助开发人员(及其经理)以最小的挫折感与开发社区合作。它试图记录这个社区如何以一种不熟悉Linux内核开发(或者实际上是自由软件开发)的人可以访问的方式工作。虽然这里有一些技术资料,但这是一个面向过程的讨论,不需要深入了解内核编程就可以理解。 diff --git a/Documentation/translations/zh_CN/process/email-clients.rst b/Documentation/translations/zh_CN/process/email-clients.rst new file mode 100644 index 000000000..102023651 --- /dev/null +++ b/Documentation/translations/zh_CN/process/email-clients.rst @@ -0,0 +1,248 @@ +.. _cn_email_clients: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/email-clients.rst <email_clients>` + +译者:: + + 中文版维护者: 贾威威 Harry Wei <harryxiyou@gmail.com> + 中文版翻译者: 贾威威 Harry Wei <harryxiyou@gmail.com> + 时奎亮 Alex Shi <alex.shi@linux.alibaba.com> + 中文版校译者: Yinglin Luan <synmyth@gmail.com> + Xiaochen Wang <wangxiaochen0@gmail.com> + yaxinsn <yaxinsn@163.com> + +Linux邮件客户端配置信息 +======================= + +Git +--- + +现在大多数开发人员使用 ``git send-email`` 而不是常规的电子邮件客户端。这方面 +的手册非常好。在接收端,维护人员使用 ``git am`` 加载补丁。 + +如果你是 ``git`` 新手,那么把你的第一个补丁发送给你自己。将其保存为包含所有 +标题的原始文本。运行 ``git am raw_email.txt`` ,然后使用 ``git log`` 查看更 +改日志。如果工作正常,再将补丁发送到相应的邮件列表。 + + +普通配置 +-------- +Linux内核补丁是通过邮件被提交的,最好把补丁作为邮件体的内嵌文本。有些维护者 +接收附件,但是附件的内容格式应该是"text/plain"。然而,附件一般是不赞成的, +因为这会使补丁的引用部分在评论过程中变的很困难。 + +用来发送Linux内核补丁的邮件客户端在发送补丁时应该处于文本的原始状态。例如, +他们不能改变或者删除制表符或者空格,甚至是在每一行的开头或者结尾。 + +不要通过"format=flowed"模式发送补丁。这样会引起不可预期以及有害的断行。 + +不要让你的邮件客户端进行自动换行。这样也会破坏你的补丁。 + +邮件客户端不能改变文本的字符集编码方式。要发送的补丁只能是ASCII或者UTF-8编码方式, +如果你使用UTF-8编码方式发送邮件,那么你将会避免一些可能发生的字符集问题。 + +邮件客户端应该形成并且保持 References: 或者 In-Reply-To: 标题,那么 +邮件话题就不会中断。 + +复制粘帖(或者剪贴粘帖)通常不能用于补丁,因为制表符会转换为空格。使用xclipboard, xclip +或者xcutsel也许可以,但是最好测试一下或者避免使用复制粘帖。 + +不要在使用PGP/GPG署名的邮件中包含补丁。这样会使得很多脚本不能读取和适用于你的补丁。 +(这个问题应该是可以修复的) + +在给内核邮件列表发送补丁之前,给自己发送一个补丁是个不错的主意,保存接收到的 +邮件,将补丁用'patch'命令打上,如果成功了,再给内核邮件列表发送。 + + +一些邮件客户端提示 +------------------ +这里给出一些详细的MUA配置提示,可以用于给Linux内核发送补丁。这些并不意味是 +所有的软件包配置总结。 + +说明: +TUI = 以文本为基础的用户接口 +GUI = 图形界面用户接口 + +Alpine (TUI) +~~~~~~~~~~~~ + +配置选项: +在"Sending Preferences"部分: + +- "Do Not Send Flowed Text"必须开启 +- "Strip Whitespace Before Sending"必须关闭 + +当写邮件时,光标应该放在补丁会出现的地方,然后按下CTRL-R组合键,使指定的 +补丁文件嵌入到邮件中。 + +Evolution (GUI) +~~~~~~~~~~~~~~~ + +一些开发者成功的使用它发送补丁 + +当选择邮件选项:Preformat + 从Format->Heading->Preformatted (Ctrl-7)或者工具栏 + +然后使用: + Insert->Text File... (Alt-n x)插入补丁文件。 + +你还可以"diff -Nru old.c new.c | xclip",选择Preformat,然后使用中间键进行粘帖。 + +Kmail (GUI) +~~~~~~~~~~~ + +一些开发者成功的使用它发送补丁。 + +默认设置不为HTML格式是合适的;不要启用它。 + +当书写一封邮件的时候,在选项下面不要选择自动换行。唯一的缺点就是你在邮件中输入的任何文本 +都不会被自动换行,因此你必须在发送补丁之前手动换行。最简单的方法就是启用自动换行来书写邮件, +然后把它保存为草稿。一旦你在草稿中再次打开它,它已经全部自动换行了,那么你的邮件虽然没有 +选择自动换行,但是还不会失去已有的自动换行。 + +在邮件的底部,插入补丁之前,放上常用的补丁定界符:三个连字号(---)。 + +然后在"Message"菜单条目,选择插入文件,接着选取你的补丁文件。还有一个额外的选项,你可以 +通过它配置你的邮件建立工具栏菜单,还可以带上"insert file"图标。 + +你可以安全地通过GPG标记附件,但是内嵌补丁最好不要使用GPG标记它们。作为内嵌文本的签发补丁, +当从GPG中提取7位编码时会使他们变的更加复杂。 + +如果你非要以附件的形式发送补丁,那么就右键点击附件,然后选中属性,突出"Suggest automatic +display",这样内嵌附件更容易让读者看到。 + +当你要保存将要发送的内嵌文本补丁,你可以从消息列表窗格选择包含补丁的邮件,然后右击选择 +"save as"。你可以使用一个没有更改的包含补丁的邮件,如果它是以正确的形式组成。当你正真在它 +自己的窗口之下察看,那时没有选项可以保存邮件--已经有一个这样的bug被汇报到了kmail的bugzilla +并且希望这将会被处理。邮件是以只针对某个用户可读写的权限被保存的,所以如果你想把邮件复制到其他地方, +你不得不把他们的权限改为组或者整体可读。 + +Lotus Notes (GUI) +~~~~~~~~~~~~~~~~~ + +不要使用它。 + +Mutt (TUI) +~~~~~~~~~~ + +很多Linux开发人员使用mutt客户端,所以证明它肯定工作的非常漂亮。 + +Mutt不自带编辑器,所以不管你使用什么编辑器都不应该带有自动断行。大多数编辑器都带有 +一个"insert file"选项,它可以通过不改变文件内容的方式插入文件。 + +'vim'作为mutt的编辑器: + set editor="vi" + + 如果使用xclip,敲入以下命令 + :set paste + 按中键之前或者shift-insert或者使用 + :r filename + +如果想要把补丁作为内嵌文本。 +(a)ttach工作的很好,不带有"set paste"。 + +你可以通过 ``git format-patch`` 生成补丁,然后用 Mutt发送它们:: + + $ mutt -H 0001-some-bug-fix.patch + +配置选项: +它应该以默认设置的形式工作。 +然而,把"send_charset"设置为"us-ascii::utf-8"也是一个不错的主意。 + +Mutt 是高度可配置的。 这里是个使用mutt通过 Gmail 发送的补丁的最小配置:: + + # .muttrc + # ================ IMAP ==================== + set imap_user = 'yourusername@gmail.com' + set imap_pass = 'yourpassword' + set spoolfile = imaps://imap.gmail.com/INBOX + set folder = imaps://imap.gmail.com/ + set record="imaps://imap.gmail.com/[Gmail]/Sent Mail" + set postponed="imaps://imap.gmail.com/[Gmail]/Drafts" + set mbox="imaps://imap.gmail.com/[Gmail]/All Mail" + + # ================ SMTP ==================== + set smtp_url = "smtp://username@smtp.gmail.com:587/" + set smtp_pass = $imap_pass + set ssl_force_tls = yes # Require encrypted connection + + # ================ Composition ==================== + set editor = `echo \$EDITOR` + set edit_headers = yes # See the headers when editing + set charset = UTF-8 # value of $LANG; also fallback for send_charset + # Sender, email address, and sign-off line must match + unset use_domain # because joe@localhost is just embarrassing + set realname = "YOUR NAME" + set from = "username@gmail.com" + set use_from = yes + +Mutt文档含有更多信息: + + http://dev.mutt.org/trac/wiki/UseCases/Gmail + + http://dev.mutt.org/doc/manual.html + +Pine (TUI) +~~~~~~~~~~ + +Pine过去有一些空格删减问题,但是这些现在应该都被修复了。 + +如果可以,请使用alpine(pine的继承者) + +配置选项: +- 最近的版本需要消除流程文本 +- "no-strip-whitespace-before-send"选项也是需要的。 + + +Sylpheed (GUI) +~~~~~~~~~~~~~~ + +- 内嵌文本可以很好的工作(或者使用附件)。 +- 允许使用外部的编辑器。 +- 对于目录较多时非常慢。 +- 如果通过non-SSL连接,无法使用TLS SMTP授权。 +- 在组成窗口中有一个很有用的ruler bar。 +- 给地址本中添加地址就不会正确的了解显示名。 + +Thunderbird (GUI) +~~~~~~~~~~~~~~~~~ + +默认情况下,thunderbird很容易损坏文本,但是还有一些方法可以强制它变得更好。 + +- 在用户帐号设置里,组成和寻址,不要选择"Compose messages in HTML format"。 + +- 编辑你的Thunderbird配置设置来使它不要拆行使用:user_pref("mailnews.wraplength", 0); + +- 编辑你的Thunderbird配置设置,使它不要使用"format=flowed"格式:user_pref("mailnews. + send_plaintext_flowed", false); + +- 你需要使Thunderbird变为预先格式方式: + 如果默认情况下你书写的是HTML格式,那不是很难。仅仅从标题栏的下拉框中选择"Preformat"格式。 + 如果默认情况下你书写的是文本格式,你不得把它改为HTML格式(仅仅作为一次性的)来书写新的消息, + 然后强制使它回到文本格式,否则它就会拆行。要实现它,在写信的图标上使用shift键来使它变为HTML + 格式,然后标题栏的下拉框中选择"Preformat"格式。 + +- 允许使用外部的编辑器: + 针对Thunderbird打补丁最简单的方法就是使用一个"external editor"扩展,然后使用你最喜欢的 + $EDITOR来读取或者合并补丁到文本中。要实现它,可以下载并且安装这个扩展,然后添加一个使用它的 + 按键View->Toolbars->Customize...最后当你书写信息的时候仅仅点击它就可以了。 + +TkRat (GUI) +~~~~~~~~~~~ + +可以使用它。使用"Insert file..."或者外部的编辑器。 + +Gmail (Web GUI) +~~~~~~~~~~~~~~~ + +不要使用它发送补丁。 + +Gmail网页客户端自动地把制表符转换为空格。 + +虽然制表符转换为空格问题可以被外部编辑器解决,同时它还会使用回车换行把每行拆分为78个字符。 + +另一个问题是Gmail还会把任何不是ASCII的字符的信息改为base64编码。它把东西变的像欧洲人的名字。 + + ### diff --git a/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst b/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst new file mode 100644 index 000000000..88273ebe7 --- /dev/null +++ b/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst @@ -0,0 +1,228 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/embargoed-hardware-issues.rst <embargoed_hardware_issues>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +被限制的硬件问题 +================ + +范围 +---- + +导致安全问题的硬件问题与只影响Linux内核的纯软件错误是不同的安全错误类别。 + +必须区别对待诸如熔毁(Meltdown)、Spectre、L1TF等硬件问题,因为它们通常会影响 +所有操作系统(“OS”),因此需要在不同的OS供应商、发行版、硬件供应商和其他各方 +之间进行协调。对于某些问题,软件缓解可能依赖于微码或固件更新,这需要进一步的 +协调。 + +.. _zh_Contact: + +接触 +---- + +Linux内核硬件安全小组独立于普通的Linux内核安全小组。 + +该小组只负责协调被限制的硬件安全问题。Linux内核中纯软件安全漏洞的报告不由该 +小组处理,报告者将被引导至常规Linux内核安全小组(:ref:`Documentation/admin-guide/ +<securitybugs>`)联系。 + +可以通过电子邮件 <hardware-security@kernel.org> 与小组联系。这是一份私密的安全 +官名单,他们将帮助您根据我们的文档化流程协调问题。 + +邮件列表是加密的,发送到列表的电子邮件可以通过PGP或S/MIME加密,并且必须使用报告 +者的PGP密钥或S/MIME证书签名。该列表的PGP密钥和S/MIME证书可从 +https://www.kernel.org/.... 获得。 + +虽然硬件安全问题通常由受影响的硬件供应商处理,但我们欢迎发现潜在硬件缺陷的研究 +人员或个人与我们联系。 + +硬件安全官 +^^^^^^^^^^ + +目前的硬件安全官小组: + + - Linus Torvalds(Linux基金会院士) + - Greg Kroah Hartman(Linux基金会院士) + - Thomas Gleixner(Linux基金会院士) + +邮件列表的操作 +^^^^^^^^^^^^^^ + +处理流程中使用的加密邮件列表托管在Linux Foundation的IT基础设施上。通过提供这项 +服务,Linux基金会的IT基础设施安全总监在技术上有能力访问被限制的信息,但根据他 +的雇佣合同,他必须保密。Linux基金会的IT基础设施安全总监还负责 kernel.org 基础 +设施。 + +Linux基金会目前的IT基础设施安全总监是 Konstantin Ryabitsev。 + +保密协议 +-------- + +Linux内核硬件安全小组不是正式的机构,因此无法签订任何保密协议。核心社区意识到 +这些问题的敏感性,并提供了一份谅解备忘录。 + +谅解备忘录 +---------- + +Linux内核社区深刻理解在不同操作系统供应商、发行商、硬件供应商和其他各方之间 +进行协调时,保持硬件安全问题处于限制状态的要求。 + +Linux内核社区在过去已经成功地处理了硬件安全问题,并且有必要的机制允许在限制 +限制下进行符合社区的开发。 + +Linux内核社区有一个专门的硬件安全小组负责初始联系,并监督在限制规则下处理 +此类问题的过程。 + +硬件安全小组确定开发人员(领域专家),他们将组成特定问题的初始响应小组。最初 +的响应小组可以引入更多的开发人员(领域专家)以最佳的技术方式解决这个问题。 + +所有相关开发商承诺遵守限制规定,并对收到的信息保密。违反承诺将导致立即从当前 +问题中排除,并从所有相关邮件列表中删除。此外,硬件安全小组还将把违反者排除在 +未来的问题之外。这一后果的影响在我们社区是一种非常有效的威慑。如果发生违规 +情况,硬件安全小组将立即通知相关方。如果您或任何人发现潜在的违规行为,请立即 +向硬件安全人员报告。 + +流程 +^^^^ + +由于Linux内核开发的全球分布式特性,面对面的会议几乎不可能解决硬件安全问题。 +由于时区和其他因素,电话会议很难协调,只能在绝对必要时使用。加密电子邮件已被 +证明是解决此类问题的最有效和最安全的通信方法。 + +开始披露 +"""""""" + +披露内容首先通过电子邮件联系Linux内核硬件安全小组。此初始联系人应包含问题的 +描述和任何已知受影响硬件的列表。如果您的组织制造或分发受影响的硬件,我们建议 +您也考虑哪些其他硬件可能会受到影响。 + +硬件安全小组将提供一个特定于事件的加密邮件列表,用于与报告者进行初步讨论、 +进一步披露和协调。 + +硬件安全小组将向披露方提供一份开发人员(领域专家)名单,在与开发人员确认他们 +将遵守本谅解备忘录和文件化流程后,应首先告知开发人员有关该问题的信息。这些开发 +人员组成初始响应小组,并在初始接触后负责处理问题。硬件安全小组支持响应小组, +但不一定参与缓解开发过程。 + +虽然个别开发人员可能通过其雇主受到保密协议的保护,但他们不能以Linux内核开发 +人员的身份签订个别保密协议。但是,他们将同意遵守这一书面程序和谅解备忘录。 + +披露方应提供已经或应该被告知该问题的所有其他实体的联系人名单。这有几个目的: + + - 披露的实体列表允许跨行业通信,例如其他操作系统供应商、硬件供应商等。 + + - 可联系已披露的实体,指定应参与缓解措施开发的专家。 + + - 如果需要处理某一问题的专家受雇于某一上市实体或某一上市实体的成员,则响应 + 小组可要求该实体披露该专家。这确保专家也是实体反应小组的一部分。 + +披露 +"""" + +披露方通过特定的加密邮件列表向初始响应小组提供详细信息。 + +根据我们的经验,这些问题的技术文档通常是一个足够的起点,最好通过电子邮件进行 +进一步的技术澄清。 + +缓解开发 +"""""""" + +初始响应小组设置加密邮件列表,或在适当的情况下重新修改现有邮件列表。 + +使用邮件列表接近于正常的Linux开发过程,并且在过去已经成功地用于为各种硬件安全 +问题开发缓解措施。 + +邮件列表的操作方式与正常的Linux开发相同。发布、讨论和审查修补程序,如果同意, +则应用于非公共git存储库,参与开发人员只能通过安全连接访问该存储库。存储库包含 +针对主线内核的主开发分支,并根据需要为稳定的内核版本提供向后移植分支。 + +最初的响应小组将根据需要从Linux内核开发人员社区中确定更多的专家。引进专家可以 +在开发过程中的任何时候发生,需要及时处理。 + +如果专家受雇于披露方提供的披露清单上的实体或其成员,则相关实体将要求其参与。 + +否则,披露方将被告知专家参与的情况。谅解备忘录涵盖了专家,要求披露方确认参与。 +如果披露方有令人信服的理由提出异议,则必须在五个工作日内提出异议,并立即与事件 +小组解决。如果披露方在五个工作日内未作出回应,则视为默许。 + +在确认或解决异议后,专家由事件小组披露,并进入开发过程。 + +协调发布 +"""""""" + +有关各方将协商限制结束的日期和时间。此时,准备好的缓解措施集成到相关的内核树中 +并发布。 + +虽然我们理解硬件安全问题需要协调限制时间,但限制时间应限制在所有有关各方制定、 +测试和准备缓解措施所需的最短时间内。人为地延长限制时间以满足会议讨论日期或其他 +非技术原因,会给相关的开发人员和响应小组带来了更多的工作和负担,因为补丁需要 +保持最新,以便跟踪正在进行的上游内核开发,这可能会造成冲突的更改。 + +CVE分配 +""""""" + +硬件安全小组和初始响应小组都不分配CVE,开发过程也不需要CVE。如果CVE是由披露方 +提供的,则可用于文档中。 + +流程专使 +-------- + +为了协助这一进程,我们在各组织设立了专使,他们可以回答有关报告流程和进一步处理 +的问题或提供指导。专使不参与特定问题的披露,除非响应小组或相关披露方提出要求。 +现任专使名单: + + ============= ======================================================== + ARM + AMD Tom Lendacky <tom.lendacky@amd.com> + IBM + Intel Tony Luck <tony.luck@intel.com> + Qualcomm Trilok Soni <tsoni@codeaurora.org> + + Microsoft Sasha Levin <sashal@kernel.org> + VMware + Xen Andrew Cooper <andrew.cooper3@citrix.com> + + Canonical John Johansen <john.johansen@canonical.com> + Debian Ben Hutchings <ben@decadent.org.uk> + Oracle Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> + Red Hat Josh Poimboeuf <jpoimboe@redhat.com> + SUSE Jiri Kosina <jkosina@suse.cz> + + Amazon + Google Kees Cook <keescook@chromium.org> + ============= ======================================================== + +如果要将您的组织添加到专使名单中,请与硬件安全小组联系。被提名的专使必须完全 +理解和支持我们的过程,并且在Linux内核社区中很容易联系。 + +加密邮件列表 +------------ + +我们使用加密邮件列表进行通信。这些列表的工作原理是,发送到列表的电子邮件使用 +列表的PGP密钥或列表的/MIME证书进行加密。邮件列表软件对电子邮件进行解密,并 +使用订阅者的PGP密钥或S/MIME证书为每个订阅者分别对其进行重新加密。有关邮件列表 +软件和用于确保列表安全和数据保护的设置的详细信息,请访问: +https://www.kernel.org/.... + +关键点 +^^^^^^ + +初次接触见 :ref:`zh_Contact`. 对于特定于事件的邮件列表,密钥和S/MIME证书通过 +特定列表发送的电子邮件传递给订阅者。 + +订阅事件特定列表 +^^^^^^^^^^^^^^^^ + +订阅由响应小组处理。希望参与通信的披露方将潜在订户的列表发送给响应组,以便 +响应组可以验证订阅请求。 + +每个订户都需要通过电子邮件向响应小组发送订阅请求。电子邮件必须使用订阅服务器 +的PGP密钥或S/MIME证书签名。如果使用PGP密钥,则必须从公钥服务器获得该密钥, +并且理想情况下该密钥连接到Linux内核的PGP信任网。另请参见: +https://www.kernel.org/signature.html. + +响应小组验证订阅者,并将订阅者添加到列表中。订阅后,订阅者将收到来自邮件列表 +的电子邮件,该邮件列表使用列表的PGP密钥或列表的/MIME证书签名。订阅者的电子邮件 +客户端可以从签名中提取PGP密钥或S/MIME证书,以便订阅者可以向列表发送加密电子 +邮件。 diff --git a/Documentation/translations/zh_CN/process/howto.rst b/Documentation/translations/zh_CN/process/howto.rst new file mode 100644 index 000000000..ee3dee476 --- /dev/null +++ b/Documentation/translations/zh_CN/process/howto.rst @@ -0,0 +1,496 @@ +.. _cn_process_howto: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/howto.rst <process_howto>` + +译者:: + + 英文版维护者: Greg Kroah-Hartman <greg@kroah.com> + 中文版维护者: 李阳 Li Yang <leoyang.li@nxp.com> + 中文版翻译者: 李阳 Li Yang <leoyang.li@nxp.com> + 时奎亮 Alex Shi <alex.shi@linux.alibaba.com> + 中文版校译者: + 钟宇 TripleX Chung <xxx.phy@gmail.com> + 陈琦 Maggie Chen <chenqi@beyondsoft.com> + 王聪 Wang Cong <xiyou.wangcong@gmail.com> + +如何参与Linux内核开发 +===================== + +这是一篇将如何参与Linux内核开发的相关问题一网打尽的终极秘笈。它将指导你 +成为一名Linux内核开发者,并且学会如何同Linux内核开发社区合作。它尽可能不 +包括任何关于内核编程的技术细节,但会给你指引一条获得这些知识的正确途径。 + +如果这篇文章中的任何内容不再适用,请给文末列出的文件维护者发送补丁。 + + +入门 +---- + +你想了解如何成为一名Linux内核开发者?或者老板吩咐你“给这个设备写个Linux +驱动程序”?这篇文章的目的就是教会你达成这些目标的全部诀窍,它将描述你需 +要经过的流程以及给出如何同内核社区合作的一些提示。它还将试图解释内核社区 +为何这样运作。 + +Linux内核大部分是由C语言写成的,一些体系结构相关的代码用到了汇编语言。要 +参与内核开发,你必须精通C语言。除非你想为某个架构开发底层代码,否则你并 +不需要了解(任何体系结构的)汇编语言。下面列举的书籍虽然不能替代扎实的C +语言教育和多年的开发经验,但如果需要的话,做为参考还是不错的: + + - "The C Programming Language" by Kernighan and Ritchie [Prentice Hall] + 《C程序设计语言(第2版·新版)》(徐宝文 李志 译)[机械工业出版社] + - "Practical C Programming" by Steve Oualline [O'Reilly] + 《实用C语言编程(第三版)》(郭大海 译)[中国电力出版社] + - "C: A Reference Manual" by Harbison and Steele [Prentice Hall] + 《C语言参考手册(原书第5版)》(邱仲潘 等译)[机械工业出版社] + +Linux内核使用GNU C和GNU工具链开发。虽然它遵循ISO C89标准,但也用到了一些 +标准中没有定义的扩展。内核是自给自足的C环境,不依赖于标准C库的支持,所以 +并不支持C标准中的部分定义。比如long long类型的大数除法和浮点运算就不允许 +使用。有时候确实很难弄清楚内核对工具链的要求和它所使用的扩展,不幸的是目 +前还没有明确的参考资料可以解释它们。请查阅gcc信息页(使用“info gcc”命令 +显示)获得一些这方面信息。 + +请记住你是在学习怎么和已经存在的开发社区打交道。它由一群形形色色的人组成, +他们对代码、风格和过程有着很高的标准。这些标准是在长期实践中总结出来的, +适应于地理上分散的大型开发团队。它们已经被很好得整理成档,建议你在开发 +之前尽可能多的学习这些标准,而不要期望别人来适应你或者你公司的行为方式。 + + +法律问题 +-------- + +Linux内核源代码都是在GPL(通用公共许可证)的保护下发布的。要了解这种许可 +的细节请查看源代码主目录下的COPYING文件。Linux内核许可准则和如何使用 +`SPDX <https://spdx.org/>` 标志符说明在这个文件中 +:ref:`Documentation/translations/zh_CN/process/license-rules.rst <cn_kernel_licensing>` +如果你对它还有更深入问题请联系律师,而不要在Linux内核邮件组上提问。因为 +邮件组里的人并不是律师,不要期望他们的话有法律效力。 + +对于GPL的常见问题和解答,请访问以下链接: + https://www.gnu.org/licenses/gpl-faq.html + + +文档 +---- + +Linux内核代码中包含有大量的文档。这些文档对于学习如何与内核社区互动有着 +不可估量的价值。当一个新的功能被加入内核,最好把解释如何使用这个功能的文 +档也放进内核。当内核的改动导致面向用户空间的接口发生变化时,最好将相关信 +息或手册页(manpages)的补丁发到mtk.manpages@gmail.com,以向手册页(manpages) +的维护者解释这些变化。 + +以下是内核代码中需要阅读的文档: + :ref:`Documentation/admin-guide/README.rst <readme>` + 文件简要介绍了Linux内核的背景,并且描述了如何配置和编译内核。内核的 + 新用户应该从这里开始。 + + + :ref:`Documentation/process/changes.rst <changes>` + 文件给出了用来编译和使用内核所需要的最小软件包列表。 + + :ref:`Documentation/translations/zh_CN/process/coding-style.rst <cn_codingstyle>` + 描述Linux内核的代码风格和理由。所有新代码需要遵守这篇文档中定义的规 + 范。大多数维护者只会接收符合规定的补丁,很多人也只会帮忙检查符合风格 + 的代码。 + + :ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` + :ref:`Documentation/process/submitting-drivers.rst <submittingdrivers>` + + 这两份文档明确描述如何创建和发送补丁,其中包括(但不仅限于): + - 邮件内容 + - 邮件格式 + - 选择收件人 + + 遵守这些规定并不能保证提交成功(因为所有补丁需要通过严格的内容和风格 + 审查),但是忽视他们几乎就意味着失败。 + + 其他关于如何正确地生成补丁的优秀文档包括: + "The Perfect Patch" + + https://www.ozlabs.org/~akpm/stuff/tpp.txt + + "Linux kernel patch submission format" + + https://web.archive.org/web/20180829112450/http://linux.yyz.us/patch-format.html + + :ref:`Documentation/translations/zh_CN/process/stable-api-nonsense.rst <cn_stable_api_nonsense>` + 论证内核为什么特意不包括稳定的内核内部API,也就是说不包括像这样的特 + 性: + + - 子系统中间层(为了兼容性?) + - 在不同操作系统间易于移植的驱动程序 + - 减缓(甚至阻止)内核代码的快速变化 + + 这篇文档对于理解Linux的开发哲学至关重要。对于将开发平台从其他操作系 + 统转移到Linux的人来说也很重要。 + + :ref:`Documentation/admin-guide/security-bugs.rst <securitybugs>` + 如果你认为自己发现了Linux内核的安全性问题,请根据这篇文档中的步骤来 + 提醒其他内核开发者并帮助解决这个问题。 + + :ref:`Documentation/translations/zh_CN/process/management-style.rst <cn_managementstyle>` + + 描述内核维护者的工作方法及其共有特点。这对于刚刚接触内核开发(或者对 + 它感到好奇)的人来说很重要,因为它解释了很多对于内核维护者独特行为的 + 普遍误解与迷惑。 + + :ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>` + 解释了稳定版内核发布的规则,以及如何将改动放入这些版本的步骤。 + + :ref:`Documentation/process/kernel-docs.rst <kernel_docs>` + 有助于内核开发的外部文档列表。如果你在内核自带的文档中没有找到你想找 + 的内容,可以查看这些文档。 + + :ref:`Documentation/process/applying-patches.rst <applying_patches>` + 关于补丁是什么以及如何将它打在不同内核开发分支上的好介绍 + +内核还拥有大量从代码自动生成或者从 ReStructuredText(ReST) 标记生成的文档, +比如这个文档,它包含内核内部API的全面介绍以及如何妥善处理加锁的规则。所有 +这些文档都可以通过运行以下命令从内核代码中生成为PDF或HTML文档:: + + make pdfdocs + make htmldocs + +ReST格式的文档会生成在 Documentation/output. 目录中。 +它们也可以用下列命令生成 LaTeX 和 ePub 格式文档:: + + make latexdocs + make epubdocs + +如何成为内核开发者 +------------------ +如果你对Linux内核开发一无所知,你应该访问“Linux内核新手”计划: + + https://kernelnewbies.org + +它拥有一个可以问各种最基本的内核开发问题的邮件列表(在提问之前一定要记得 +查找已往的邮件,确认是否有人已经回答过相同的问题)。它还拥有一个可以获得 +实时反馈的IRC聊天频道,以及大量对于学习Linux内核开发相当有帮助的文档。 + +网站简要介绍了源代码组织结构、子系统划分以及目前正在进行的项目(包括内核 +中的和单独维护的)。它还提供了一些基本的帮助信息,比如如何编译内核和打补 +丁。 + +如果你想加入内核开发社区并协助完成一些任务,却找不到从哪里开始,可以访问 +“Linux内核房管员”计划: + + https://kernelnewbies.org/KernelJanitors + +这是极佳的起点。它提供一个相对简单的任务列表,列出内核代码中需要被重新 +整理或者改正的地方。通过和负责这个计划的开发者们一同工作,你会学到将补丁 +集成进内核的基本原理。如果还没有决定下一步要做什么的话,你还可能会得到方 +向性的指点。 + +在真正动手修改内核代码之前,理解要修改的代码如何运作是必需的。要达到这个 +目的,没什么办法比直接读代码更有效了(大多数花招都会有相应的注释),而且 +一些特制的工具还可以提供帮助。例如,“Linux代码交叉引用”项目就是一个值得 +特别推荐的帮助工具,它将源代码显示在有编目和索引的网页上。其中一个更新及 +时的内核源码库,可以通过以下地址访问: + + https://elixir.bootlin.com/ + + +开发流程 +-------- + +目前Linux内核开发流程包括几个“主内核分支”和很多子系统相关的内核分支。这 +些分支包括: + + - Linus 的内核源码树 + - 多个主要版本的稳定版内核树 + - 子系统相关的内核树 + - linux-next 集成测试树 + + +主线树 +------ +主线树是由Linus Torvalds 维护的。你可以在https://kernel.org 网站或者代码 +库中下找到它。它的开发遵循以下步骤: + + - 每当一个新版本的内核被发布,为期两周的集成窗口将被打开。在这段时间里 + 维护者可以向Linus提交大段的修改,通常这些修改已经被放到-mm内核中几个 + 星期了。提交大量修改的首选方式是使用git工具(内核的代码版本管理工具 + ,更多的信息可以在 https://git-scm.com/ 获取),不过使用普通补丁也是 + 可以的。 + - 两个星期以后-rc1版本内核发布。之后只有不包含可能影响整个内核稳定性的 + 新功能的补丁才可能被接受。请注意一个全新的驱动程序(或者文件系统)有 + 可能在-rc1后被接受是因为这样的修改完全独立,不会影响其他的代码,所以 + 没有造成内核退步的风险。在-rc1以后也可以用git向Linus提交补丁,不过所 + 有的补丁需要同时被发送到相应的公众邮件列表以征询意见。 + - 当Linus认为当前的git源码树已经达到一个合理健全的状态足以发布供人测试 + 时,一个新的-rc版本就会被发布。计划是每周都发布新的-rc版本。 + - 这个过程一直持续下去直到内核被认为达到足够稳定的状态,持续时间大概是 + 6个星期。 + +关于内核发布,值得一提的是Andrew Morton在linux-kernel邮件列表中如是说: + “没有人知道新内核何时会被发布,因为发布是根据已知bug的情况来决定 + 的,而不是根据一个事先制定好的时间表。” + +子系统特定树 +------------ + +各种内核子系统的维护者——以及许多内核子系统开发人员——在源代码库中公开了他们 +当前的开发状态。这样,其他人就可以看到内核的不同区域发生了什么。在开发速度 +很快的领域,可能会要求开发人员将提交的内容建立在这样的子系统内核树上,这样 +就避免了提交与其他已经进行的工作之间的冲突。 + +这些存储库中的大多数都是Git树,但是也有其他的scm在使用,或者补丁队列被发布 +为Quilt系列。这些子系统存储库的地址列在MAINTAINERS文件中。其中许多可以在 +https://git.kernel.org/上浏览。 + +在将一个建议的补丁提交到这样的子系统树之前,需要对它进行审查,审查主要发生 +在邮件列表上(请参见下面相应的部分)。对于几个内核子系统,这个审查过程是通 +过工具补丁跟踪的。Patchwork提供了一个Web界面,显示补丁发布、对补丁的任何评 +论或修订,维护人员可以将补丁标记为正在审查、接受或拒绝。大多数补丁网站都列 +在 https://patchwork.kernel.org/ + +Linux-next 集成测试树 +--------------------- + +在将子系统树的更新合并到主线树之前,需要对它们进行集成测试。为此,存在一个 +特殊的测试存储库,其中几乎每天都会提取所有子系统树: + + https://git.kernel.org/?p=linux/kernel/git/next/linux-next.git + +通过这种方式,Linux-next 对下一个合并阶段将进入主线内核的内容给出了一个概要 +展望。非常欢冒险的测试者运行测试Linux-next。 + +多个主要版本的稳定版内核树 +----------------------------------- +由3个数字组成的内核版本号说明此内核是-stable版本。它们包含内核的相对较小且 +至关重要的修补,这些修补针对安全性问题或者严重的内核退步。 + +这种版本的内核适用于那些期望获得最新的稳定版内核并且不想参与测试开发版或 +者实验版的用户。 + +稳定版内核树版本由“稳定版”小组(邮件地址<stable@vger.kernel.org>)维护,一般 +隔周发布新版本。 + +内核源码中的 :ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>` +文件具体描述了可被稳定版内核接受的修改类型以及发布的流程。 + + +报告bug +------- + +bugzilla.kernel.org是Linux内核开发者们用来跟踪内核Bug的网站。我们鼓励用 +户在这个工具中报告找到的所有bug。如何使用内核bugzilla的细节请访问: + + http://test.kernel.org/bugzilla/faq.html + +内核源码主目录中的:ref:`admin-guide/reporting-bugs.rst <reportingbugs>` +文件里有一个很好的模板。它指导用户如何报告可能的内核bug以及需要提供哪些信息 +来帮助内核开发者们找到问题的根源。 + + +利用bug报告 +----------- + +练习内核开发技能的最好办法就是修改其他人报告的bug。你不光可以帮助内核变 +得更加稳定,还可以学会如何解决实际问题从而提高自己的技能,并且让其他开发 +者感受到你的存在。修改bug是赢得其他开发者赞誉的最好办法,因为并不是很多 +人都喜欢浪费时间去修改别人报告的bug。 + +要尝试修改已知的bug,请访问 http://bugzilla.kernel.org 网址。 + + +邮件列表 +-------- + +正如上面的文档所描述,大多数的骨干内核开发者都加入了Linux Kernel邮件列 +表。如何订阅和退订列表的细节可以在这里找到: + + http://vger.kernel.org/vger-lists.html#linux-kernel + +网上很多地方都有这个邮件列表的存档(archive)。可以使用搜索引擎来找到这些 +存档。比如: + + http://dir.gmane.org/gmane.linux.kernel + +在发信之前,我们强烈建议你先在存档中搜索你想要讨论的问题。很多已经被详细 +讨论过的问题只在邮件列表的存档中可以找到。 + +大多数内核子系统也有自己独立的邮件列表来协调各自的开发工作。从 +MAINTAINERS文件中可以找到不同话题对应的邮件列表。 + +很多邮件列表架设在kernel.org服务器上。这些列表的信息可以在这里找到: + + http://vger.kernel.org/vger-lists.html + +在使用这些邮件列表时,请记住保持良好的行为习惯。下面的链接提供了与这些列 +表(或任何其它邮件列表)交流的一些简单规则,虽然内容有点滥竽充数。 + + http://www.albion.com/netiquette/ + +当有很多人回复你的邮件时,邮件的抄送列表会变得很长。请不要将任何人从抄送 +列表中删除,除非你有足够的理由这么做。也不要只回复到邮件列表。请习惯于同 +一封邮件接收两次(一封来自发送者一封来自邮件列表),而不要试图通过添加一 +些奇特的邮件头来解决这个问题,人们不会喜欢的。 + +记住保留你所回复内容的上下文和源头。在你回复邮件的顶部保留“某某某说到……” +这几行。将你的评论加在被引用的段落之间而不要放在邮件的顶部。 + +如果你在邮件中附带补丁,请确认它们是可以直接阅读的纯文本(如 +:ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` +文档中所述)。内核开发者们不希望遇到附件或者被压缩了的补丁。只有这样才能 +保证他们可以直接评论你的每行代码。请确保你使用的邮件发送程序不会修改空格 +和制表符。一个防范性的测试方法是先将邮件发送给自己,然后自己尝试是否可以 +顺利地打上收到的补丁。如果测试不成功,请调整或者更换你的邮件发送程序直到 +它正确工作为止。 + +总而言之,请尊重其他的邮件列表订阅者。 + + +同内核社区合作 +---------------- + +内核社区的目标就是提供尽善尽美的内核。所以当你提交补丁期望被接受进内核的 +时候,它的技术价值以及其他方面都将被评审。那么你可能会得到什么呢? + + - 批评 + - 评论 + - 要求修改 + - 要求证明修改的必要性 + - 沉默 + +要记住,这些是把补丁放进内核的正常情况。你必须学会听取对补丁的批评和评论, +从技术层面评估它们,然后要么重写你的补丁要么简明扼要地论证修改是不必要 +的。如果你发的邮件没有得到任何回应,请过几天后再试一次,因为有时信件会湮 +没在茫茫信海中。 + +你不应该做的事情: + + - 期望自己的补丁不受任何质疑就直接被接受 + - 翻脸 + - 忽略别人的评论 + - 没有按照别人的要求做任何修改就重新提交 + +在一个努力追寻最好技术方案的社区里,对于一个补丁有多少好处总会有不同的见 +解。你必须要抱着合作的态度,愿意改变自己的观点来适应内核的风格。或者至少 +愿意去证明你的想法是有价值的。记住,犯错误是允许的,只要你愿意朝着正确的 +方案去努力。 + +如果你的第一个补丁换来的是一堆修改建议,这是很正常的。这并不代表你的补丁 +不会被接受,也不意味着有人和你作对。你只需要改正所有提出的问题然后重新发 +送你的补丁。 + +内核社区和公司文化的差异 +------------------------ + +内核社区的工作模式同大多数传统公司开发队伍的工作模式并不相同。下面这些例 +子,可以帮助你避免某些可能发生问题: +用这些话介绍你的修改提案会有好处: + + - 它同时解决了多个问题 + - 它删除了2000行代码 + - 这是补丁,它已经解释了我想要说明的 + - 我在5种不同的体系结构上测试过它…… + - 这是一系列小补丁用来…… + - 这个修改提高了普通机器的性能…… + +应该避免如下的说法: + + - 我们在AIX/ptx/Solaris就是这么做的,所以这么做肯定是好的…… + - 我做这行已经20年了,所以…… + - 为了我们公司赚钱考虑必须这么做 + - 这是我们的企业产品线所需要的 + - 这里是描述我观点的1000页设计文档 + - 这是一个5000行的补丁用来…… + - 我重写了现在乱七八糟的代码,这就是…… + - 我被规定了最后期限,所以这个补丁需要立刻被接受 + +另外一个内核社区与大部分传统公司的软件开发队伍不同的地方是无法面对面地交 +流。使用电子邮件和IRC聊天工具做为主要沟通工具的一个好处是性别和种族歧视 +将会更少。Linux内核的工作环境更能接受妇女和少数族群,因为每个人在别人眼 +里只是一个邮件地址。国际化也帮助了公平的实现,因为你无法通过姓名来判断人 +的性别。男人有可能叫李丽,女人也有可能叫王刚。大多数在Linux内核上工作过 +并表达过看法的女性对在linux上工作的经历都给出了正面的评价。 + +对于一些不习惯使用英语的人来说,语言可能是一个引起问题的障碍。在邮件列表 +中要正确地表达想法必需良好地掌握语言,所以建议你在发送邮件之前最好检查一 +下英文写得是否正确。 + + +拆分修改 +-------- + +Linux内核社区并不喜欢一下接收大段的代码。修改需要被恰当地介绍、讨论并且 +拆分成独立的小段。这几乎完全和公司中的习惯背道而驰。你的想法应该在开发最 +开始的阶段就让大家知道,这样你就可以及时获得对你正在进行的开发的反馈。这 +样也会让社区觉得你是在和他们协作,而不是仅仅把他们当作倾销新功能的对象。 +无论如何,你不要一次性地向邮件列表发送50封信,你的补丁序列应该永远用不到 +这么多。 + +将补丁拆开的原因如下: + +1) 小的补丁更有可能被接受,因为它们不需要太多的时间和精力去验证其正确性。 + 一个5行的补丁,可能在维护者看了一眼以后就会被接受。而500行的补丁则 + 需要数个小时来审查其正确性(所需时间随补丁大小增加大约呈指数级增长)。 + + 当出了问题的时候,小的补丁也会让调试变得非常容易。一个一个补丁地回溯 + 将会比仔细剖析一个被打上的大补丁(这个补丁破坏了其他东西)容易得多。 + +2)不光发送小的补丁很重要,在提交之前重新编排、化简(或者仅仅重新排列) + 补丁也是很重要的。 + +这里有内核开发者Al Viro打的一个比方: + “想象一个老师正在给学生批改数学作业。老师并不希望看到学生为了得 + 到正确解法所进行的尝试和产生的错误。他希望看到的是最干净最优雅的 + 解答。好学生了解这点,绝不会把最终解决之前的中间方案提交上去。” + + 内核开发也是这样。维护者和评审者不希望看到一个人在解决问题时的思 + 考过程。他们只希望看到简单和优雅的解决方案。 + +直接给出一流的解决方案,和社区一起协作讨论尚未完成的工作,这两者之间似乎 +很难找到一个平衡点。所以最好尽早开始收集有利于你进行改进的反馈;同时也要 +保证修改分成很多小块,这样在整个项目都准备好被包含进内核之前,其中的一部 +分可能会先被接收。 + +必须了解这样做是不可接受的:试图将未完成的工作提交进内核,然后再找时间修 +复。 + + +证明修改的必要性 +---------------- +除了将补丁拆成小块,很重要的一点是让Linux社区了解他们为什么需要这样修改。 +你必须证明新功能是有人需要的并且是有用的。 + + +记录修改 +-------- + +当你发送补丁的时候,需要特别留意邮件正文的内容。因为这里的信息将会做为补 +丁的修改记录(ChangeLog),会被一直保留以备大家查阅。它需要完全地描述补丁, +包括: + + - 为什么需要这个修改 + - 补丁的总体设计 + - 实现细节 + - 测试结果 + +想了解它具体应该看起来像什么,请查阅以下文档中的“ChangeLog”章节: + “The Perfect Patch” + https://www.ozlabs.org/~akpm/stuff/tpp.txt + + +这些事情有时候做起来很难。要在任何方面都做到完美可能需要好几年时间。这是 +一个持续提高的过程,它需要大量的耐心和决心。只要不放弃,你一定可以做到。 +很多人已经做到了,而他们都曾经和现在的你站在同样的起点上。 + + +感谢 +---- +感谢Paolo Ciarrocchi允许“开发流程”部分基于他所写的文章 +(http://www.kerneltravel.net/newbie/2.6-development_process),感谢Randy +Dunlap和Gerrit Huizenga完善了应该说和不该说的列表。感谢Pat Mochel, Hanna +Linder, Randy Dunlap, Kay Sievers, Vojtech Pavlik, Jan Kara, Josh Boyer, +Kees Cook, Andrew Morton, Andi Kleen, Vadim Lobanov, Jesper Juhl, Adrian +Bunk, Keri Harris, Frans Pop, David A. Wheeler, Junio Hamano, Michael +Kerrisk和Alex Shepard的评审、建议和贡献。没有他们的帮助,这篇文档是不可 +能完成的。 + + + +英文版维护者: Greg Kroah-Hartman <greg@kroah.com> diff --git a/Documentation/translations/zh_CN/process/index.rst b/Documentation/translations/zh_CN/process/index.rst new file mode 100644 index 000000000..8051a7b32 --- /dev/null +++ b/Documentation/translations/zh_CN/process/index.rst @@ -0,0 +1,63 @@ +.. raw:: latex + + \renewcommand\thesection* + \renewcommand\thesubsection* + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/index.rst <process_index>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_process_index: + +与Linux 内核社区一起工作 +======================== + +那么你想成为Linux内核开发人员? 欢迎! 不但从技术意义上讲有很多关于内核的知识 +需要学,而且了解我们社区的工作方式也很重要。 阅读这些文章可以让您以更轻松地, +麻烦最少的方式将更改合并到内核。 + +以下是每位开发人员应阅读的基本指南。 + +.. toctree:: + :maxdepth: 1 + + howto + code-of-conduct + code-of-conduct-interpretation + submitting-patches + programming-language + coding-style + development-process + email-clients + license-rules + kernel-enforcement-statement + kernel-driver-statement + +其它大多数开发人员感兴趣的社区指南: + + +.. toctree:: + :maxdepth: 1 + + submitting-drivers + submit-checklist + stable-api-nonsense + stable-kernel-rules + management-style + embargoed-hardware-issues + +这些是一些总体技术指南,由于缺乏更好的地方,现在已经放在这里 + +.. toctree:: + :maxdepth: 1 + + magic-number + volatile-considered-harmful + +.. only:: subproject and html + + 目录 + ==== + + * :ref:`genindex` diff --git a/Documentation/translations/zh_CN/process/kernel-driver-statement.rst b/Documentation/translations/zh_CN/process/kernel-driver-statement.rst new file mode 100644 index 000000000..2b3375bcc --- /dev/null +++ b/Documentation/translations/zh_CN/process/kernel-driver-statement.rst @@ -0,0 +1,199 @@ +.. _cn_process_statement_driver: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/kernel-driver-statement.rst <process_statement_driver>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +内核驱动声明 +------------ + +关于Linux内核模块的立场声明 +=========================== + +我们,以下署名的Linux内核开发人员,认为任何封闭源Linux内核模块或驱动程序都是 +有害的和不可取的。我们已经一再发现它们对Linux用户,企业和更大的Linux生态系统 +有害。这样的模块否定了Linux开发模型的开放性,稳定性,灵活性和可维护性,并使 +他们的用户无法使用Linux社区的专业知识。提供闭源内核模块的供应商迫使其客户 +放弃Linux的主要优势或选择新的供应商。因此,为了充分利用开源所提供的成本节省和 +共享支持优势,我们敦促供应商采取措施,以开源内核代码在Linux上为其客户提供支持。 + +我们只为自己说话,而不是我们今天可能会为之工作,过去或将来会为之工作的任何公司。 + + - Dave Airlie + - Nick Andrew + - Jens Axboe + - Ralf Baechle + - Felipe Balbi + - Ohad Ben-Cohen + - Muli Ben-Yehuda + - Jiri Benc + - Arnd Bergmann + - Thomas Bogendoerfer + - Vitaly Bordug + - James Bottomley + - Josh Boyer + - Neil Brown + - Mark Brown + - David Brownell + - Michael Buesch + - Franck Bui-Huu + - Adrian Bunk + - François Cami + - Ralph Campbell + - Luiz Fernando N. Capitulino + - Mauro Carvalho Chehab + - Denis Cheng + - Jonathan Corbet + - Glauber Costa + - Alan Cox + - Magnus Damm + - Ahmed S. Darwish + - Robert P. J. Day + - Hans de Goede + - Arnaldo Carvalho de Melo + - Helge Deller + - Jean Delvare + - Mathieu Desnoyers + - Sven-Thorsten Dietrich + - Alexey Dobriyan + - Daniel Drake + - Alex Dubov + - Randy Dunlap + - Michael Ellerman + - Pekka Enberg + - Jan Engelhardt + - Mark Fasheh + - J. Bruce Fields + - Larry Finger + - Jeremy Fitzhardinge + - Mike Frysinger + - Kumar Gala + - Robin Getz + - Liam Girdwood + - Jan-Benedict Glaw + - Thomas Gleixner + - Brice Goglin + - Cyrill Gorcunov + - Andy Gospodarek + - Thomas Graf + - Krzysztof Halasa + - Harvey Harrison + - Stephen Hemminger + - Michael Hennerich + - Tejun Heo + - Benjamin Herrenschmidt + - Kristian Høgsberg + - Henrique de Moraes Holschuh + - Marcel Holtmann + - Mike Isely + - Takashi Iwai + - Olof Johansson + - Dave Jones + - Jesper Juhl + - Matthias Kaehlcke + - Kenji Kaneshige + - Jan Kara + - Jeremy Kerr + - Russell King + - Olaf Kirch + - Roel Kluin + - Hans-Jürgen Koch + - Auke Kok + - Peter Korsgaard + - Jiri Kosina + - Aaro Koskinen + - Mariusz Kozlowski + - Greg Kroah-Hartman + - Michael Krufky + - Aneesh Kumar + - Clemens Ladisch + - Christoph Lameter + - Gunnar Larisch + - Anders Larsen + - Grant Likely + - John W. Linville + - Yinghai Lu + - Tony Luck + - Pavel Machek + - Matt Mackall + - Paul Mackerras + - Roland McGrath + - Patrick McHardy + - Kyle McMartin + - Paul Menage + - Thierry Merle + - Eric Miao + - Akinobu Mita + - Ingo Molnar + - James Morris + - Andrew Morton + - Paul Mundt + - Oleg Nesterov + - Luca Olivetti + - S.Çağlar Onur + - Pierre Ossman + - Keith Owens + - Venkatesh Pallipadi + - Nick Piggin + - Nicolas Pitre + - Evgeniy Polyakov + - Richard Purdie + - Mike Rapoport + - Sam Ravnborg + - Gerrit Renker + - Stefan Richter + - David Rientjes + - Luis R. Rodriguez + - Stefan Roese + - Francois Romieu + - Rami Rosen + - Stephen Rothwell + - Maciej W. Rozycki + - Mark Salyzyn + - Yoshinori Sato + - Deepak Saxena + - Holger Schurig + - Amit Shah + - Yoshihiro Shimoda + - Sergei Shtylyov + - Kay Sievers + - Sebastian Siewior + - Rik Snel + - Jes Sorensen + - Alexey Starikovskiy + - Alan Stern + - Timur Tabi + - Hirokazu Takata + - Eliezer Tamir + - Eugene Teo + - Doug Thompson + - FUJITA Tomonori + - Dmitry Torokhov + - Marcelo Tosatti + - Steven Toth + - Theodore Tso + - Matthias Urlichs + - Geert Uytterhoeven + - Arjan van de Ven + - Ivo van Doorn + - Rik van Riel + - Wim Van Sebroeck + - Hans Verkuil + - Horst H. von Brand + - Dmitri Vorobiev + - Anton Vorontsov + - Daniel Walker + - Johannes Weiner + - Harald Welte + - Matthew Wilcox + - Dan J. Williams + - Darrick J. Wong + - David Woodhouse + - Chris Wright + - Bryan Wu + - Rafael J. Wysocki + - Herbert Xu + - Vlad Yasevich + - Peter Zijlstra + - Bartlomiej Zolnierkiewicz diff --git a/Documentation/translations/zh_CN/process/kernel-enforcement-statement.rst b/Documentation/translations/zh_CN/process/kernel-enforcement-statement.rst new file mode 100644 index 000000000..75f7b7b91 --- /dev/null +++ b/Documentation/translations/zh_CN/process/kernel-enforcement-statement.rst @@ -0,0 +1,151 @@ +.. _cn_process_statement_kernel: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/kernel-enforcement-statement.rst <process_statement_kernel>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +Linux 内核执行声明 +------------------ + +作为Linux内核的开发人员,我们对如何使用我们的软件以及如何实施软件许可证有着 +浓厚的兴趣。遵守GPL-2.0的互惠共享义务对我们软件和社区的长期可持续性至关重要。 + +虽然有权强制执行对我们社区的贡献中的单独版权权益,但我们有共同的利益,即确保 +个人强制执行行动的方式有利于我们的社区,不会对我们软件生态系统的健康和增长 +产生意外的负面影响。为了阻止无益的执法行动,我们同意代表我们自己和我们版权 +利益的任何继承人对Linux内核用户作出以下符合我们开发社区最大利益的承诺: + + 尽管有GPL-2.0的终止条款,我们同意,采用以下GPL-3.0条款作为我们许可证下的 + 附加许可,作为任何对许可证下权利的非防御性主张,这符合我们开发社区的最佳 + 利益。 + + 但是,如果您停止所有违反本许可证的行为,则您从特定版权持有人处获得的 + 许可证将被恢复:(a)暂时恢复,除非版权持有人明确并最终终止您的许可证; + 以及(b)永久恢复, 如果版权持有人未能在你终止违反后60天内以合理方式 + 通知您违反本许可证的行为,则永久恢复您的许可证。 + + 此外,如果版权所有者以某种合理的方式通知您违反了本许可,这是您第一次 + 从该版权所有者处收到违反本许可的通知(对于任何作品),并且您在收到通知 + 后的30天内纠正违规行为。则您从特定版权所有者处获得的许可将永久恢复. + +我们提供这些保证的目的是鼓励更多地使用该软件。我们希望公司和个人使用、修改和 +分发此软件。我们希望以公开和透明的方式与用户合作,以消除我们对法规遵从性或强制 +执行的任何不确定性,这些不确定性可能会限制我们软件的采用。我们将法律行动视为 +最后手段,只有在其他社区努力未能解决这一问题时才采取行动。 + +最后,一旦一个不合规问题得到解决,我们希望用户会感到欢迎,加入我们为之努力的 +这个项目。共同努力,我们会更强大。 + +除了下面提到的以外,我们只为自己说话,而不是为今天、过去或将来可能为之工作的 +任何公司说话。 + + - Laura Abbott + - Bjorn Andersson (Linaro) + - Andrea Arcangeli + - Neil Armstrong + - Jens Axboe + - Pablo Neira Ayuso + - Khalid Aziz + - Ralf Baechle + - Felipe Balbi + - Arnd Bergmann + - Ard Biesheuvel + - Tim Bird + - Paolo Bonzini + - Christian Borntraeger + - Mark Brown (Linaro) + - Paul Burton + - Javier Martinez Canillas + - Rob Clark + - Kees Cook (Google) + - Jonathan Corbet + - Dennis Dalessandro + - Vivien Didelot (Savoir-faire Linux) + - Hans de Goede + - Mel Gorman (SUSE) + - Sven Eckelmann + - Alex Elder (Linaro) + - Fabio Estevam + - Larry Finger + - Bhumika Goyal + - Andy Gross + - Juergen Gross + - Shawn Guo + - Ulf Hansson + - Stephen Hemminger (Microsoft) + - Tejun Heo + - Rob Herring + - Masami Hiramatsu + - Michal Hocko + - Simon Horman + - Johan Hovold (Hovold Consulting AB) + - Christophe JAILLET + - Olof Johansson + - Lee Jones (Linaro) + - Heiner Kallweit + - Srinivas Kandagatla + - Jan Kara + - Shuah Khan (Samsung) + - David Kershner + - Jaegeuk Kim + - Namhyung Kim + - Colin Ian King + - Jeff Kirsher + - Greg Kroah-Hartman (Linux Foundation) + - Christian König + - Vinod Koul + - Krzysztof Kozlowski + - Viresh Kumar + - Aneesh Kumar K.V + - Julia Lawall + - Doug Ledford + - Chuck Lever (Oracle) + - Daniel Lezcano + - Shaohua Li + - Xin Long + - Tony Luck + - Catalin Marinas (Arm Ltd) + - Mike Marshall + - Chris Mason + - Paul E. McKenney + - Arnaldo Carvalho de Melo + - David S. Miller + - Ingo Molnar + - Kuninori Morimoto + - Trond Myklebust + - Martin K. Petersen (Oracle) + - Borislav Petkov + - Jiri Pirko + - Josh Poimboeuf + - Sebastian Reichel (Collabora) + - Guenter Roeck + - Joerg Roedel + - Leon Romanovsky + - Steven Rostedt (VMware) + - Frank Rowand + - Ivan Safonov + - Anna Schumaker + - Jes Sorensen + - K.Y. Srinivasan + - David Sterba (SUSE) + - Heiko Stuebner + - Jiri Kosina (SUSE) + - Willy Tarreau + - Dmitry Torokhov + - Linus Torvalds + - Thierry Reding + - Rik van Riel + - Luis R. Rodriguez + - Geert Uytterhoeven (Glider bvba) + - Eduardo Valentin (Amazon.com) + - Daniel Vetter + - Linus Walleij + - Richard Weinberger + - Dan Williams + - Rafael J. Wysocki + - Arvind Yadav + - Masahiro Yamada + - Wei Yongjun + - Lv Zheng + - Marc Zyngier (Arm Ltd) diff --git a/Documentation/translations/zh_CN/process/license-rules.rst b/Documentation/translations/zh_CN/process/license-rules.rst new file mode 100644 index 000000000..30c272b2a --- /dev/null +++ b/Documentation/translations/zh_CN/process/license-rules.rst @@ -0,0 +1,370 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/license-rules.rst <kernel_licensing>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_kernel_licensing: + +Linux内核许可规则 +================= + +Linux内核根据LICENSES/preferred/GPL-2.0中提供的GNU通用公共许可证版本2 +(GPL-2.0)的条款提供,并在LICENSES/exceptions/Linux-syscall-note中显式 +描述了例外的系统调用,如COPYING文件中所述。 + +此文档文件提供了如何对每个源文件进行注释以使其许可证清晰明确的说明。 +它不会取代内核的许可证。 + +内核源代码作为一个整体适用于COPYING文件中描述的许可证,但是单个源文件可以 +具有不同的与GPL-20兼容的许可证:: + + GPL-1.0+ : GNU通用公共许可证v1.0或更高版本 + GPL-2.0+ : GNU通用公共许可证v2.0或更高版本 + LGPL-2.0 : 仅限GNU库通用公共许可证v2 + LGPL-2.0+: GNU 库通用公共许可证v2或更高版本 + LGPL-2.1 : 仅限GNU宽通用公共许可证v2.1 + LGPL-2.1+: GNU宽通用公共许可证v2.1或更高版本 + +除此之外,个人文件可以在双重许可下提供,例如一个兼容的GPL变体,或者BSD, +MIT等许可。 + +用户空间API(UAPI)头文件描述了用户空间程序与内核的接口,这是一种特殊情况。 +根据内核COPYING文件中的注释,syscall接口是一个明确的边界,它不会将GPL要求 +扩展到任何使用它与内核通信的软件。由于UAPI头文件必须包含在创建在Linux内核 +上运行的可执行文件的任何源文件中,因此此例外必须记录在特别的许可证表述中。 + +表达源文件许可证的常用方法是将匹配的样板文本添加到文件的顶部注释中。由于 +格式,拼写错误等,这些“样板”很难通过那些在上下文中使用的验证许可证合规性 +的工具。 + +样板文本的替代方法是在每个源文件中使用软件包数据交换(SPDX)许可证标识符。 +SPDX许可证标识符是机器可解析的,并且是用于提供文件内容的许可证的精确缩写。 +SPDX许可证标识符由Linux 基金会的SPDX 工作组管理,并得到了整个行业,工具 +供应商和法律团队的合作伙伴的一致同意。有关详细信息,请参阅 +https://spdx.org/ + +Linux内核需要所有源文件中的精确SPDX标识符。内核中使用的有效标识符在 +`许可标识符`_ 一节中进行了解释,并且已可以在 +https://spdx.org/licenses/ 上的官方SPDX许可证列表中检索,并附带许可证 +文本。 + +许可标识符语法 +-------------- + +1.安置: + + 内核文件中的SPDX许可证标识符应添加到可包含注释的文件中的第一行。对于大多 + 数文件,这是第一行,除了那些在第一行中需要'#!PATH_TO_INTERPRETER'的脚本。 + 对于这些脚本,SPDX标识符进入第二行。 + +| + +2. 风格: + + SPDX许可证标识符以注释的形式添加。注释样式取决于文件类型:: + + C source: // SPDX-License-Identifier: <SPDX License Expression> + C header: /* SPDX-License-Identifier: <SPDX License Expression> */ + ASM: /* SPDX-License-Identifier: <SPDX License Expression> */ + scripts: # SPDX-License-Identifier: <SPDX License Expression> + .rst: .. SPDX-License-Identifier: <SPDX License Expression> + .dts{i}: // SPDX-License-Identifier: <SPDX License Expression> + + 如果特定工具无法处理标准注释样式,则应使用工具接受的相应注释机制。这是在 + C 头文件中使用“/\*\*/”样式注释的原因。过去在使用生成的.lds文件中观察到 + 构建被破坏,其中'ld'无法解析C++注释。现在已经解决了这个问题,但仍然有较 + 旧的汇编程序工具无法处理C++样式的注释。 + +| + +3. 句法: + + <SPDX许可证表达式>是SPDX许可证列表中的SPDX短格式许可证标识符,或者在许可 + 证例外适用时由“WITH”分隔的两个SPDX短格式许可证标识符的组合。当应用多个许 + 可证时,表达式由分隔子表达式的关键字“AND”,“OR”组成,并由“(”,“)”包围。 + + 带有“或更高”选项的[L]GPL等许可证的许可证标识符通过使用“+”来表示“或更高” + 选项来构建。:: + + // SPDX-License-Identifier: GPL-2.0+ + // SPDX-License-Identifier: LGPL-2.1+ + + 当需要修正的许可证时,应使用WITH。 例如,linux内核UAPI文件使用表达式:: + + // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note + // SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note + + 其它在内核中使用WITH例外的事例如下:: + + // SPDX-License-Identifier: GPL-2.0 WITH mif-exception + // SPDX-License-Identifier: GPL-2.0+ WITH GCC-exception-2.0 + + 例外只能与特定的许可证标识符一起使用。有效的许可证标识符列在异常文本文件 + 的标记中。有关详细信息,请参阅 `许可标识符`_ 一章中的 `例外`_ 。 + + 如果文件是双重许可且只选择一个许可证,则应使用OR。例如,一些dtsi文件在双 + 许可下可用:: + + // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + + 内核中双许可文件中许可表达式的示例:: + + // SPDX-License-Identifier: GPL-2.0 OR MIT + // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + // SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 + // SPDX-License-Identifier: GPL-2.0 OR MPL-1.1 + // SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT + // SPDX-License-Identifier: GPL-1.0+ OR BSD-3-Clause OR OpenSSL + + 如果文件具有多个许可证,其条款全部适用于使用该文件,则应使用AND。例如, + 如果代码是从另一个项目继承的,并且已经授予了将其放入内核的权限,但原始 + 许可条款需要保持有效:: + + // SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT + + 另一个需要遵守两套许可条款的例子是:: + + // SPDX-License-Identifier: GPL-1.0+ AND LGPL-2.1+ + +许可标识符 +---------- + +当前使用的许可证以及添加到内核的代码许可证可以分解为: + +1. _`优先许可`: + + 应尽可能使用这些许可证,因为它们已知完全兼容并广泛使用。这些许可证在内核 + 目录:: + + LICENSES/preferred/ + + 此目录中的文件包含完整的许可证文本和 `元标记`_ 。文件名与SPDX许可证标识 + 符相同,后者应用于源文件中的许可证。 + + 例如:: + + LICENSES/preferred/GPL-2.0 + + 包含GPLv2许可证文本和所需的元标签:: + + LICENSES/preferred/MIT + + 包含MIT许可证文本和所需的元标记 + + _`元标记`: + + 许可证文件中必须包含以下元标记: + + - Valid-License-Identifier: + + 一行或多行, 声明那些许可标识符在项目内有效, 以引用此特定许可的文本。通 + 常这是一个有效的标识符,但是例如对于带有'或更高'选项的许可证,两个标识 + 符都有效。 + + - SPDX-URL: + + SPDX页面的URL,其中包含与许可证相关的其他信息. + + - Usage-Guidance: + + 使用建议的自由格式文本。该文本必须包含SPDX许可证标识符的正确示例,因为 + 它们应根据 `许可标识符语法`_ 指南放入源文件中。 + + - License-Text: + + 此标记之后的所有文本都被视为原始许可文本 + + 文件格式示例:: + + Valid-License-Identifier: GPL-2.0 + Valid-License-Identifier: GPL-2.0+ + SPDX-URL: https://spdx.org/licenses/GPL-2.0.html + Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU General Public License (GPL) version 2 only' use: + SPDX-License-Identifier: GPL-2.0 + For 'GNU General Public License (GPL) version 2 or any later version' use: + SPDX-License-Identifier: GPL-2.0+ + License-Text: + Full license text + + :: + + SPDX-License-Identifier: MIT + SPDX-URL: https://spdx.org/licenses/MIT.html + Usage-Guide: + To use this license in source code, put the following SPDX + tag/value pair into a comment according to the placement + guidelines in the licensing rules documentation. + SPDX-License-Identifier: MIT + License-Text: + Full license text + +| + +2. 不推荐的许可证: + + 这些许可证只应用于现有代码或从其他项目导入代码。这些许可证在内核目录:: + + LICENSES/other/ + + 此目录中的文件包含完整的许可证文本和 `元标记`_ 。文件名与SPDX许可证标识 + 符相同,后者应用于源文件中的许可证。 + + 例如:: + + LICENSES/other/ISC + + 包含国际系统联合许可文本和所需的元标签:: + + LICENSES/other/ZLib + + 包含ZLIB许可文本和所需的元标签. + + 元标签: + + “其他”许可证的元标签要求与 `优先许可`_ 的要求相同。 + + 文件格式示例:: + + Valid-License-Identifier: ISC + SPDX-URL: https://spdx.org/licenses/ISC.html + Usage-Guide: + Usage of this license in the kernel for new code is discouraged + and it should solely be used for importing code from an already + existing project. + To use this license in source code, put the following SPDX + tag/value pair into a comment according to the placement + guidelines in the licensing rules documentation. + SPDX-License-Identifier: ISC + License-Text: + Full license text + +| + +3. _`例外`: + + 某些许可证可以修改,并允许原始许可证不具有的某些例外权利。这些例外在 + 内核目录:: + + LICENSES/exceptions/ + + 此目录中的文件包含完整的例外文本和所需的 `例外元标记`_ 。 + + 例如:: + + LICENSES/exceptions/Linux-syscall-note + + 包含Linux内核的COPYING文件中记录的Linux系统调用例外,该文件用于UAPI + 头文件。例如:: + + LICENSES/exceptions/GCC-exception-2.0 + + 包含GCC'链接例外',它允许独立于其许可证的任何二进制文件与标记有此例外的 + 文件的编译版本链接。这是从GPL不兼容源代码创建可运行的可执行文件所必需的。 + + _`例外元标记`: + + 以下元标记必须在例外文件中可用: + + - SPDX-Exception-Identifier: + + 一个可与SPDX许可证标识符一起使用的例外标识符。 + + - SPDX-URL: + + SPDX页面的URL,其中包含与例外相关的其他信息。 + + - SPDX-Licenses: + + 以逗号分隔的例外可用的SPDX许可证标识符列表。 + + - Usage-Guidance: + + 使用建议的自由格式文本。必须在文本后面加上SPDX许可证标识符的正确示例, + 因为它们应根据 `许可标识符语法`_ 指南放入源文件中。 + + - Exception-Text: + + 此标记之后的所有文本都被视为原始异常文本 + + 文件格式示例:: + + SPDX-Exception-Identifier: Linux-syscall-note + SPDX-URL: https://spdx.org/licenses/Linux-syscall-note.html + SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+ + Usage-Guidance: + This exception is used together with one of the above SPDX-Licenses + to mark user-space API (uapi) header files so they can be included + into non GPL compliant user-space application code. + To use this exception add it with the keyword WITH to one of the + identifiers in the SPDX-Licenses tag: + SPDX-License-Identifier: <SPDX-License> WITH Linux-syscall-note + Exception-Text: + Full exception text + + :: + + SPDX-Exception-Identifier: GCC-exception-2.0 + SPDX-URL: https://spdx.org/licenses/GCC-exception-2.0.html + SPDX-Licenses: GPL-2.0, GPL-2.0+ + Usage-Guidance: + The "GCC Runtime Library exception 2.0" is used together with one + of the above SPDX-Licenses for code imported from the GCC runtime + library. + To use this exception add it with the keyword WITH to one of the + identifiers in the SPDX-Licenses tag: + SPDX-License-Identifier: <SPDX-License> WITH GCC-exception-2.0 + Exception-Text: + Full exception text + + +所有SPDX许可证标识符和例外都必须在LICENSES子目录中具有相应的文件。这是允许 +工具验证(例如checkpatch.pl)以及准备好从源读取和提取许可证所必需的, 这是 +各种FOSS组织推荐的,例如 `FSFE REUSE initiative <https://reuse.software/>`_. + +_`模块许可` +----------------- + + 可加载内核模块还需要MODULE_LICENSE()标记。此标记既不替代正确的源代码 + 许可证信息(SPDX-License-Identifier),也不以任何方式表示或确定提供模块 + 源代码的确切许可证。 + + 此标记的唯一目的是提供足够的信息,该模块是否是自由软件或者是内核模块加 + 载器和用户空间工具的专有模块。 + + MODULE_LICENSE()的有效许可证字符串是: + + ============================= ============================================= + "GPL" 模块是根据GPL版本2许可的。这并不表示仅限于 + GPL-2.0或GPL-2.0或更高版本之间的任何区别。 + 最正确许可证信息只能通过相应源文件中的许可证 + 信息来确定 + + "GPL v2" 和"GPL"相同,它的存在是因为历史原因。 + + "GPL and additional rights" 表示模块源在GPL v2变体和MIT许可下双重许可的 + 历史变体。请不要在新代码中使用。 + + "Dual MIT/GPL" 表达该模块在GPL v2变体或MIT许可证选择下双重 + 许可的正确方式。 + + "Dual BSD/GPL" 该模块根据GPL v2变体或BSD许可证选择进行双重 + 许可。 BSD许可证的确切变体只能通过相应源文件 + 中的许可证信息来确定。 + + "Dual MPL/GPL" 该模块根据GPL v2变体或Mozilla Public License + (MPL)选项进行双重许可。 MPL许可证的确切变体 + 只能通过相应的源文件中的许可证信息来确定。 + + "Proprietary" 该模块属于专有许可。此字符串仅用于专有的第三 + 方模块,不能用于在内核树中具有源代码的模块。 + 以这种方式标记的模块在加载时会使用'P'标记污 + 染内核,并且内核模块加载器拒绝将这些模块链接 + 到使用EXPORT_SYMBOL_GPL()导出的符号。 + ============================= ============================================= + diff --git a/Documentation/translations/zh_CN/process/magic-number.rst b/Documentation/translations/zh_CN/process/magic-number.rst new file mode 100644 index 000000000..e4c225996 --- /dev/null +++ b/Documentation/translations/zh_CN/process/magic-number.rst @@ -0,0 +1,150 @@ +.. _cn_magicnumbers: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/magic-number.rst <magicnumbers>` + +如果想评论或更新本文的内容,请直接发信到LKML。如果你使用英文交流有困难的话,也可 +以向中文版维护者求助。如果本翻译更新不及时或者翻译存在问题,请联系中文版维护者:: + + 中文版维护者: 贾威威 Jia Wei Wei <harryxiyou@gmail.com> + 中文版翻译者: 贾威威 Jia Wei Wei <harryxiyou@gmail.com> + 中文版校译者: 贾威威 Jia Wei Wei <harryxiyou@gmail.com> + +Linux 魔术数 +============ + +这个文件是有关当前使用的魔术值注册表。当你给一个结构添加了一个魔术值,你也应该把这个魔术值添加到这个文件,因为我们最好把用于各种结构的魔术值统一起来。 + +使用魔术值来保护内核数据结构是一个非常好的主意。这就允许你在运行期检查(a)一个结构是否已经被攻击,或者(b)你已经给一个例行程序通过了一个错误的结构。后一种情况特别地有用---特别是当你通过一个空指针指向结构体的时候。tty源码,例如,经常通过特定驱动使用这种方法并且反复地排列特定方面的结构。 + +使用魔术值的方法是在结构的开始处声明的,如下:: + + struct tty_ldisc { + int magic; + ... + }; + +当你以后给内核添加增强功能的时候,请遵守这条规则!这样就会节省数不清的调试时间,特别是一些古怪的情况,例如,数组超出范围并且重新写了超出部分。遵守这个规则,这些情况可以被快速地,安全地避免。 + + Theodore Ts'o + 31 Mar 94 + +给当前的Linux 2.1.55添加魔术表。 + + Michael Chastain + <mailto:mec@shout.net> + 22 Sep 1997 + +现在应该最新的Linux 2.1.112.因为在特性冻结期间,不能在2.2.x前改变任何东西。这些条目被数域所排序。 + + Krzysztof G.Baranowski + <mailto: kgb@knm.org.pl> + 29 Jul 1998 + +更新魔术表到Linux 2.5.45。刚好越过特性冻结,但是有可能还会有一些新的魔术值在2.6.x之前融入到内核中。 + + Petr Baudis + <pasky@ucw.cz> + 03 Nov 2002 + +更新魔术表到Linux 2.5.74。 + + Fabian Frederick + <ffrederick@users.sourceforge.net> + 09 Jul 2003 + +===================== ================ ======================== ========================================== +魔术数名 数字 结构 文件 +===================== ================ ======================== ========================================== +PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/pg.h`` +CMAGIC 0x0111 user ``include/linux/a.out.h`` +MKISS_DRIVER_MAGIC 0x04bf mkiss_channel ``drivers/net/mkiss.h`` +HDLC_MAGIC 0x239e n_hdlc ``drivers/char/n_hdlc.c`` +APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` +CYCLADES_MAGIC 0x4359 cyclades_port ``include/linux/cyclades.h`` +DB_MAGIC 0x4442 fc_info ``drivers/net/iph5526_novram.c`` +DL_MAGIC 0x444d fc_info ``drivers/net/iph5526_novram.c`` +FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` +FF_MAGIC 0x4646 fc_info ``drivers/net/iph5526_novram.c`` +ISICOM_MAGIC 0x4d54 isi_port ``include/linux/isicom.h`` +PTY_MAGIC 0x5001 ``drivers/char/pty.c`` +PPP_MAGIC 0x5002 ppp ``include/linux/if_pppvar.h`` +SSTATE_MAGIC 0x5302 serial_state ``include/linux/serial.h`` +SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` +STRIP_MAGIC 0x5303 strip ``drivers/net/strip.c`` +X25_ASY_MAGIC 0x5303 x25_asy ``drivers/net/x25_asy.h`` +SIXPACK_MAGIC 0x5304 sixpack ``drivers/net/hamradio/6pack.h`` +AX25_MAGIC 0x5316 ax_disp ``drivers/net/mkiss.h`` +TTY_MAGIC 0x5401 tty_struct ``include/linux/tty.h`` +MGSL_MAGIC 0x5401 mgsl_info ``drivers/char/synclink.c`` +TTY_DRIVER_MAGIC 0x5402 tty_driver ``include/linux/tty_driver.h`` +MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c`` +TTY_LDISC_MAGIC 0x5403 tty_ldisc ``include/linux/tty_ldisc.h`` +USB_SERIAL_MAGIC 0x6702 usb_serial ``drivers/usb/serial/usb-serial.h`` +FULL_DUPLEX_MAGIC 0x6969 ``drivers/net/ethernet/dec/tulip/de2104x.c`` +USB_BLUETOOTH_MAGIC 0x6d02 usb_bluetooth ``drivers/usb/class/bluetty.c`` +RFCOMM_TTY_MAGIC 0x6d02 ``net/bluetooth/rfcomm/tty.c`` +USB_SERIAL_PORT_MAGIC 0x7301 usb_serial_port ``drivers/usb/serial/usb-serial.h`` +CG_MAGIC 0x00090255 ufs_cylinder_group ``include/linux/ufs_fs.h`` +RPORT_MAGIC 0x00525001 r_port ``drivers/char/rocket_int.h`` +LSEMAGIC 0x05091998 lse ``drivers/fc4/fc.c`` +GDTIOCTL_MAGIC 0x06030f07 gdth_iowr_str ``drivers/scsi/gdth_ioctl.h`` +RIEBL_MAGIC 0x09051990 ``drivers/net/atarilance.c`` +NBD_REQUEST_MAGIC 0x12560953 nbd_request ``include/linux/nbd.h`` +RED_MAGIC2 0x170fc2a5 (any) ``mm/slab.c`` +BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` +ISDN_X25IFACE_MAGIC 0x1e75a2b9 isdn_x25iface_proto_data ``drivers/isdn/isdn_x25iface.h`` +ECP_MAGIC 0x21504345 cdkecpsig ``include/linux/cdk.h`` +LSOMAGIC 0x27091997 lso ``drivers/fc4/fc.c`` +LSMAGIC 0x2a3b4d2a ls ``drivers/fc4/fc.c`` +WANPIPE_MAGIC 0x414C4453 sdla_{dump,exec} ``include/linux/wanpipe.h`` +CS_CARD_MAGIC 0x43525553 cs_card ``sound/oss/cs46xx.c`` +LABELCL_MAGIC 0x4857434c labelcl_info_s ``include/asm/ia64/sn/labelcl.h`` +ISDN_ASYNC_MAGIC 0x49344C01 modem_info ``include/linux/isdn.h`` +CTC_ASYNC_MAGIC 0x49344C01 ctc_tty_info ``drivers/s390/net/ctctty.c`` +ISDN_NET_MAGIC 0x49344C02 isdn_net_local_s ``drivers/isdn/i4l/isdn_net_lib.h`` +SAVEKMSG_MAGIC2 0x4B4D5347 savekmsg ``arch/*/amiga/config.c`` +CS_STATE_MAGIC 0x4c4f4749 cs_state ``sound/oss/cs46xx.c`` +SLAB_C_MAGIC 0x4f17a36d kmem_cache ``mm/slab.c`` +COW_MAGIC 0x4f4f4f4d cow_header_v1 ``arch/um/drivers/ubd_user.c`` +I810_CARD_MAGIC 0x5072696E i810_card ``sound/oss/i810_audio.c`` +TRIDENT_CARD_MAGIC 0x5072696E trident_card ``sound/oss/trident.c`` +ROUTER_MAGIC 0x524d4157 wan_device [in ``wanrouter.h`` pre 3.9] +SAVEKMSG_MAGIC1 0x53415645 savekmsg ``arch/*/amiga/config.c`` +GDA_MAGIC 0x58464552 gda ``arch/mips/include/asm/sn/gda.h`` +RED_MAGIC1 0x5a2cf071 (any) ``mm/slab.c`` +EEPROM_MAGIC_VALUE 0x5ab478d2 lanai_dev ``drivers/atm/lanai.c`` +HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` +PCXX_MAGIC 0x5c6df104 channel ``drivers/char/pcxx.h`` +KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` +I810_STATE_MAGIC 0x63657373 i810_state ``sound/oss/i810_audio.c`` +TRIDENT_STATE_MAGIC 0x63657373 trient_state ``sound/oss/trident.c`` +M3_CARD_MAGIC 0x646e6f50 m3_card ``sound/oss/maestro3.c`` +FW_HEADER_MAGIC 0x65726F66 fw_header ``drivers/atm/fore200e.h`` +SLOT_MAGIC 0x67267321 slot ``drivers/hotplug/cpqphp.h`` +SLOT_MAGIC 0x67267322 slot ``drivers/hotplug/acpiphp.h`` +LO_MAGIC 0x68797548 nbd_device ``include/linux/nbd.h`` +OPROFILE_MAGIC 0x6f70726f super_block ``drivers/oprofile/oprofilefs.h`` +M3_STATE_MAGIC 0x734d724d m3_state ``sound/oss/maestro3.c`` +VMALLOC_MAGIC 0x87654320 snd_alloc_track ``sound/core/memory.c`` +KMALLOC_MAGIC 0x87654321 snd_alloc_track ``sound/core/memory.c`` +PWC_MAGIC 0x89DC10AB pwc_device ``drivers/usb/media/pwc.h`` +NBD_REPLY_MAGIC 0x96744668 nbd_reply ``include/linux/nbd.h`` +ENI155_MAGIC 0xa54b872d midway_eprom ``drivers/atm/eni.h`` +CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` +DPMEM_MAGIC 0xc0ffee11 gdt_pci_sram ``drivers/scsi/gdth.h`` +YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` +CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` +QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` +QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` +HTB_CMAGIC 0xFEFAFEF1 htb_class ``net/sched/sch_htb.c`` +NMI_MAGIC 0x48414d4d455201 nmi_s ``arch/mips/include/asm/sn/nmi.h`` +===================== ================ ======================== ========================================== + + +请注意,在声音记忆管理中仍然有一些特殊的为每个驱动定义的魔术值。查看include/sound/sndmagic.h来获取他们完整的列表信息。很多OSS声音驱动拥有自己从声卡PCI ID构建的魔术值-他们也没有被列在这里。 + +IrDA子系统也使用了大量的自己的魔术值,查看include/net/irda/irda.h来获取他们完整的信息。 + +HFS是另外一个比较大的使用魔术值的文件系统-你可以在fs/hfs/hfs.h中找到他们。 diff --git a/Documentation/translations/zh_CN/process/management-style.rst b/Documentation/translations/zh_CN/process/management-style.rst new file mode 100644 index 000000000..c6a5bb285 --- /dev/null +++ b/Documentation/translations/zh_CN/process/management-style.rst @@ -0,0 +1,207 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/management-style.rst <managementstyle>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_managementstyle: + +Linux内核管理风格 +================= + +这是一个简短的文档,描述了Linux内核首选的(或胡编的,取决于您问谁)管理风格。 +它的目的是在某种程度上参照 :ref:`process/coding-style.rst <codingstyle>` +主要是为了避免反复回答 [#cnf1]_ 相同(或类似)的问题。 + +管理风格是非常个人化的,比简单的编码风格规则更难以量化,因此本文档可能与实 +际情况有关,也可能与实际情况无关。起初它是一个玩笑,但这并不意味着它可能不 +是真的。你得自己决定。 + +顺便说一句,在谈到“核心管理者”时,主要是技术负责人,而不是在公司内部进行传 +统管理的人。如果你签署了采购订单或者对你的团队的预算有任何了解,你几乎肯定 +不是一个核心管理者。这些建议可能适用于您,也可能不适用于您。 + +首先,我建议你购买“高效人的七个习惯”,而不是阅读它。烧了它,这是一个伟大的 +象征性姿态。 + +.. [#cnf1] 本文件并不是通过回答问题,而是通过让提问者痛苦地明白,我们不知道 + 答案是什么。 + +不管怎样,这里是: + +.. _cn_decisions: + +1)决策 +------- + +每个人都认为管理者做决定,而且决策很重要。决定越大越痛苦,管理者就必须越高级。 +这很明显,但事实并非如此。 + +游戏的名字是 **避免** 做出决定。尤其是,如果有人告诉你“选择(a)或(b), +我们真的需要你来做决定”,你就是陷入麻烦的管理者。你管理的人比你更了解细节, +所以如果他们来找你做技术决策,你完蛋了。你显然没有能力为他们做这个决定。 + +(推论:如果你管理的人不比你更了解细节,你也会被搞砸,尽管原因完全不同。 +也就是说,你的工作是错的,他们应该管理你的才智) + +所以游戏的名字是 **避免** 做出决定,至少是那些大而痛苦的决定。做一些小的 +和非结果性的决定是很好的,并且使您看起来好像知道自己在做什么,所以内核管理者 +需要做的是将那些大的和痛苦的决定变成那些没有人真正关心的小事情。 + +这有助于认识到一个大的决定和一个小的决定之间的关键区别是你是否可以在事后修正 +你的决定。任何决定都可以通过始终确保如果你错了(而且你一定会错),你以后总是 +可以通过回溯来弥补损失。突然间,你就要做两个无关紧要的决定,一个是错误的,另 +一个是正确的。 + +人们甚至会认为这是真正的领导能力(咳,胡说,咳)。 + +因此,避免重大决策的关键在于避免做那些无法挽回的事情。不要被引导到一个你无法 +逃离的角落。走投无路的老鼠可能很危险——走投无路的管理者真可怜。 + +事实证明,由于没有人会愚蠢到让内核管理者承担巨大的财政责任,所以通常很容易 +回溯。既然你不可能浪费掉你无法偿还的巨额资金,你唯一可以回溯的就是技术决策, +而回溯很容易:只要告诉大家你是个不称职的傻瓜,说对不起,然后撤销你去年让别 +人所做的毫无价值的工作。突然间,你一年前做的决定不在是一个重大的决定,因为 +它很容易被推翻。 + +事实证明,有些人对接受这种方法有困难,原因有两个: + + - 承认你是个白痴比看起来更难。我们都喜欢保持形象,在公共场合说你错了有时 + 确实很难。 + - 如果有人告诉你,你去年所做的工作终究是不值得的,那么对那些可怜的低级工 + 程师来说也是很困难的,虽然实际的 **工作** 很容易删除,但你可能已经不可 + 挽回地失去了工程师的信任。记住:“不可撤销”是我们一开始就试图避免的, + 而你的决定终究是一个重大的决定。 + +令人欣慰的是,这两个原因都可以通过预先承认你没有任何线索,提前告诉人们你的 +决定完全是初步的,而且可能是错误的事情来有效地缓解。你应该始终保留改变主意 +的权利,并让人们 **意识** 到这一点。当你 **还没有** 做过真正愚蠢的事情的时 +候,承认自己是愚蠢的要容易得多。 + +然后,当它真的被证明是愚蠢的时候,人们就转动他们的眼珠说“哎呀,下次不要了”。 + +这种对不称职的先发制人的承认,也可能使真正做这项工作的人也会三思是否值得做。 +毕竟,如果他们不确定这是否是一个好主意,你肯定不应该通过向他们保证他们所做 +的工作将会进入(内核)鼓励他们。在他们开始一项巨大的努力之前,至少让他们三 +思而后行。 + +记住:他们最好比你更了解细节,而且他们通常认为他们对每件事都有答案。作为一 +个管理者,你能做的最好的事情不是灌输自信,而是对他们所做的事情进行健康的批 +判性思考。 + +顺便说一句,另一种避免做出决定的方法是看起来很可怜的抱怨 “我们不能两者兼 +得吗?” 相信我,它是有效的。如果不清楚哪种方法更好,他们最终会弄清楚的。 +最终的答案可能是两个团队都会因为这种情况而感到沮丧,以至于他们放弃了。 + +这听起来像是一个失败,但这通常是一个迹象,表明两个项目都有问题,而参与其中 +的人不能做决定的原因是他们都是错误的。你最终会闻到玫瑰的味道,你避免了另一 +个你本可以搞砸的决定。 + +2)人 +----- + +大多数人都是白痴,做一名管理者意味着你必须处理好这件事,也许更重要的是, +**他们** 必须处理好你。 + +事实证明,虽然很容易纠正技术错误,但不容易纠正人格障碍。你只能和他们的和 +你的(人格障碍)共处。 + +但是,为了做好作为内核管理者的准备,最好记住不要烧掉任何桥梁,不要轰炸任何 +无辜的村民,也不要疏远太多的内核开发人员。事实证明,疏远人是相当容易的,而 +亲近一个疏远的人是很难的。因此,“疏远”立即属于“不可逆”的范畴,并根据 +:ref:`cn_decisions` 成为绝不可以做的事情。 + +这里只有几个简单的规则: + + (1) 不要叫人笨蛋(至少不要在公共场合) + (2) 学习如何在忘记规则(1)时道歉 + +问题在于 #1 很容易去做,因为你可以用数百万种不同的方式说“你是一个笨蛋” [#cnf2]_ +有时甚至没有意识到,而且几乎总是带着一种白热化的信念,认为你是对的。 + +你越确信自己是对的(让我们面对现实吧,你可以把几乎所有人都称为坏人,而且你 +经常是对的),事后道歉就越难。 + +要解决此问题,您实际上只有两个选项: + + - 非常擅长道歉 + - 把“爱”均匀地散开,没有人会真正感觉到自己被不公平地瞄准了。让它有足够的 + 创造性,他们甚至可能会觉得好笑。 + +选择永远保持礼貌是不存在的。没有人会相信一个如此明显地隐藏了他们真实性格的人。 + +.. [#cnf2] 保罗·西蒙演唱了“离开爱人的50种方法”,因为坦率地说,“告诉开发者 + 他们是D*CKHEAD" 的100万种方法都无法确认。但我确信他已经这么想了。 + +3)人2 - 好人 +------------- + +虽然大多数人都是白痴,但不幸的是,据此推论你也是白痴,尽管我们都自我感觉良 +好,我们比普通人更好(让我们面对现实吧,没有人相信他们是普通人或低于普通人), +我们也应该承认我们不是最锋利的刀,而且会有其他人比你更不像白痴。 + +有些人对聪明人反应不好。其他人利用它们。 + +作为内核维护人员,确保您在第二组中。接受他们,因为他们会让你的工作更容易。 +特别是,他们能够为你做决定,这就是游戏的全部内容。 + +所以当你发现一个比你聪明的人时,就顺其自然吧。你的管理职责在很大程度上变成 +了“听起来像是个好主意——去尝试吧”,或者“听起来不错,但是XXX呢?”“。第二个版 +本尤其是一个很好的方法,要么学习一些关于“XXX”的新东西,要么通过指出一些聪明 +人没有想到的东西来显得更具管理性。无论哪种情况,你都会赢。 + +要注意的一件事是认识到一个领域的伟大不一定会转化为其他领域。所以你可能会向 +特定的方向刺激人们,但让我们面对现实吧,他们可能擅长他们所做的事情,而且对 +其他事情都很差劲。好消息是,人们往往会自然而然地重拾他们擅长的东西,所以当 +你向某个方向刺激他们时,你并不是在做不可逆转的事情,只是不要用力推。 + +4)责备 +------- + +事情会出问题的,人们希望去责备人。贴标签,你就是受责备的人。 + +事实上,接受责备并不难,尤其是当人们意识到这不 **全是** 你的过错时。这让我 +们找到了承担责任的最佳方式:为别人承担这件事。你会感觉很好,他们会感觉很好, +没有受到指责. 那谁,失去了他们的全部36GB色情收藏的人,因为你的无能将勉强承 +认,你至少没有试图逃避责任。 + +然后让真正搞砸了的开发人员(如果你能找到他们)私下知道他们搞砸了。不仅是为 +了将来可以避免,而且为了让他们知道他们欠你一个人情。而且,也许更重要的是, +他们也可能是能够解决问题的人。因为,让我们面对现实吧,肯定不是你。 + +承担责任也是你首先成为管理者的原因。这是让人们信任你,让你获得潜在的荣耀的 +一部分,因为你就是那个会说“我搞砸了”的人。如果你已经遵循了以前的规则,你现 +在已经很擅长说了。 + +5)应避免的事情 +--------------- + +有一件事人们甚至比被称为“笨蛋”更讨厌,那就是在一个神圣的声音中被称为“笨蛋”。 +第一个你可以道歉,第二个你不会真正得到机会。即使你做得很好,他们也可能不再 +倾听。 + +我们都认为自己比别人强,这意味着当别人装腔作势时,这会让我们很恼火。你也许 +在道德和智力上比你周围的每个人都优越,但不要试图太明显,除非你真的打算激怒 +某人 [#cnf3]_ + +同样,不要对事情太客气或太微妙。礼貌很容易落得落花流水,把问题隐藏起来, +正如他们所说,“在互联网上,没人能听到你的含蓄。”用一个钝器把这一点锤进去, +因为你不能真的依靠别人来获得你的观点。 + +一些幽默可以帮助缓和直率和道德化。过度到荒谬的地步,可以灌输一个观点,而不 +会让接受者感到痛苦,他们只是认为你是愚蠢的。因此,它可以帮助我们摆脱对批评 +的个人心理障碍。 + +.. [#cnf3] 提示:与你的工作没有直接关系的网络新闻组是消除你对他人不满的好 + 方法。偶尔写些侮辱性的帖子,打个喷嚏,让你的情绪得到净化。别把牢骚带回家 + +6)为什么是我? +--------------- + +既然你的主要责任似乎是为别人的错误承担责任,并且让别人痛苦地明白你是不称职 +的,那么显而易见的问题之一就变成了为什么首先要这样做。 + +首先,虽然你可能会或可能不会听到十几岁女孩(或男孩,让我们不要在这里评判或 +性别歧视)敲你的更衣室门,你会得到一个巨大的个人成就感为“负责”。别介意你真 +的在领导别人,你要跟上别人,尽可能快地追赶他们。每个人都会认为你是负责人。 + +如果你可以做到这个, 这是个伟大的工作! diff --git a/Documentation/translations/zh_CN/process/programming-language.rst b/Documentation/translations/zh_CN/process/programming-language.rst new file mode 100644 index 000000000..2a47a1d2e --- /dev/null +++ b/Documentation/translations/zh_CN/process/programming-language.rst @@ -0,0 +1,72 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/programming-language.rst <programming_language>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_programming_language: + +程序设计语言 +============ + +内核是用C语言 :ref:`c-language <cn_c-language>` 编写的。更准确地说,内核通常是用 :ref:`gcc <cn_gcc>` +在 ``-std=gnu89`` :ref:`gcc-c-dialect-options <cn_gcc-c-dialect-options>` 下编译的:ISO C90的 GNU 方言( +包括一些C99特性) + +这种方言包含对语言 :ref:`gnu-extensions <cn_gnu-extensions>` 的许多扩展,当然,它们许多都在内核中使用。 + +对于一些体系结构,有一些使用 :ref:`clang <cn_clang>` 和 :ref:`icc <cn_icc>` 编译内核 +的支持,尽管在编写此文档时还没有完成,仍需要第三方补丁。 + +属性 +---- + +在整个内核中使用的一个常见扩展是属性(attributes) :ref:`gcc-attribute-syntax <cn_gcc-attribute-syntax>` +属性允许将实现定义的语义引入语言实体(如变量、函数或类型),而无需对语言进行 +重大的语法更改(例如添加新关键字) :ref:`n2049 <cn_n2049>` + +在某些情况下,属性是可选的(即不支持这些属性的编译器仍然应该生成正确的代码, +即使其速度较慢或执行的编译时检查/诊断次数不够) + +内核定义了伪关键字(例如, ``pure`` ),而不是直接使用GNU属性语法(例如, +``__attribute__((__pure__))`` ),以检测可以使用哪些关键字和/或缩短代码, 具体 +请参阅 ``include/linux/compiler_attributes.h`` + +.. _cn_c-language: + +c-language + http://www.open-std.org/jtc1/sc22/wg14/www/standards + +.. _cn_gcc: + +gcc + https://gcc.gnu.org + +.. _cn_clang: + +clang + https://clang.llvm.org + +.. _cn_icc: + +icc + https://software.intel.com/en-us/c-compilers + +.. _cn_gcc-c-dialect-options: + +c-dialect-options + https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html + +.. _cn_gnu-extensions: + +gnu-extensions + https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html + +.. _cn_gcc-attribute-syntax: + +gcc-attribute-syntax + https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html + +.. _cn_n2049: + +n2049 + http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2049.pdf diff --git a/Documentation/translations/zh_CN/process/stable-api-nonsense.rst b/Documentation/translations/zh_CN/process/stable-api-nonsense.rst new file mode 100644 index 000000000..b4ddb6e88 --- /dev/null +++ b/Documentation/translations/zh_CN/process/stable-api-nonsense.rst @@ -0,0 +1,155 @@ +.. _cn_stable_api_nonsense: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/stable-api-nonsense.rst + <stable_api_nonsense>` + +译者:: + + 中文版维护者: 钟宇 TripleX Chung <xxx.phy@gmail.com> + 中文版翻译者: 钟宇 TripleX Chung <xxx.phy@gmail.com> + 中文版校译者: 李阳 Li Yang <leoyang.li@nxp.com> + +Linux 内核驱动接口 +================== + +写作本文档的目的,是为了解释为什么Linux既没有二进制内核接口,也没有稳定 +的内核接口。这里所说的内核接口,是指内核里的接口,而不是内核和用户空间 +的接口。内核到用户空间的接口,是提供给应用程序使用的系统调用,系统调用 +在历史上几乎没有过变化,将来也不会有变化。我有一些老应用程序是在0.9版本 +或者更早版本的内核上编译的,在使用2.6版本内核的Linux发布上依然用得很好 +。用户和应用程序作者可以将这个接口看成是稳定的。 + + +执行纲要 +-------- + +你也许以为自己想要稳定的内核接口,但是你不清楚你要的实际上不是它。你需 +要的其实是稳定的驱动程序,而你只有将驱动程序放到公版内核的源代码树里, +才有可能达到这个目的。而且这样做还有很多其它好处,正是因为这些好处使得 +Linux能成为强壮,稳定,成熟的操作系统,这也是你最开始选择Linux的原因。 + + +入门 +----- + +只有那些写驱动程序的“怪人”才会担心内核接口的改变,对广大用户来说,既 +看不到内核接口,也不需要去关心它。 + +首先,我不打算讨论关于任何非GPL许可的内核驱动的法律问题,这些非GPL许可 +的驱动程序包括不公开源代码,隐藏源代码,二进制或者是用源代码包装,或者 +是其它任何形式的不能以GPL许可公开源代码的驱动程序。如果有法律问题,请咨 +询律师,我只是一个程序员,所以我只打算探讨技术问题(不是小看法律问题, +法律问题很实际,并且需要一直关注)。 + +既然只谈技术问题,我们就有了下面两个主题:二进制内核接口和稳定的内核源 +代码接口。这两个问题是互相关联的,让我们先解决掉二进制接口的问题。 + + +二进制内核接口 +-------------- +假如我们有一个稳定的内核源代码接口,那么自然而然的,我们就拥有了稳定的 +二进制接口,是这样的吗?错。让我们看看关于Linux内核的几点事实: + + - 取决于所用的C编译器的版本,不同的内核数据结构里的结构体的对齐方 + 式会有差别,代码中不同函数的表现形式也不一样(函数是不是被inline + 编译取决于编译器行为)。不同的函数的表现形式并不重要,但是数据 + 结构内部的对齐方式很关键。 + + - 取决于内核的配置选项,不同的选项会让内核的很多东西发生改变: + + - 同一个结构体可能包含不同的成员变量 + - 有的函数可能根本不会被实现(比如编译的时候没有选择SMP支持 + 一些锁函数就会被定义成空函数)。 + - 内核使用的内存会以不同的方式对齐,这取决于不同的内核配置选 + 项。 + + - Linux可以在很多的不同体系结构的处理器上运行。在某个体系结构上编 + 译好的二进制驱动程序,不可能在另外一个体系结构上正确的运行。 + +对于一个特定的内核,满足这些条件并不难,使用同一个C编译器和同样的内核配 +置选项来编译驱动程序模块就可以了。这对于给一个特定Linux发布的特定版本提 +供驱动程序,是完全可以满足需求的。但是如果你要给不同发布的不同版本都发 +布一个驱动程序,就需要在每个发布上用不同的内核设置参数都编译一次内核, +这简直跟噩梦一样。而且还要注意到,每个Linux发布还提供不同的Linux内核, +这些内核都针对不同的硬件类型进行了优化(有很多种不同的处理器,还有不同 +的内核设置选项)。所以每发布一次驱动程序,都需要提供很多不同版本的内核 +模块。 + +相信我,如果你真的要采取这种发布方式,一定会慢慢疯掉,我很久以前就有过 +深刻的教训... + + +稳定的内核源代码接口 +-------------------- + +如果有人不将他的内核驱动程序,放入公版内核的源代码树,而又想让驱动程序 +一直保持在最新的内核中可用,那么这个话题将会变得没完没了。 +内核开发是持续而且快节奏的,从来都不会慢下来。内核开发人员在当前接口中 +找到bug,或者找到更好的实现方式。一旦发现这些,他们就很快会去修改当前的 +接口。修改接口意味着,函数名可能会改变,结构体可能被扩充或者删减,函数 +的参数也可能发生改变。一旦接口被修改,内核中使用这些接口的地方需要同时 +修正,这样才能保证所有的东西继续工作。 + +举一个例子,内核的USB驱动程序接口在USB子系统的整个生命周期中,至少经历 +了三次重写。这些重写解决以下问题: + + - 把数据流从同步模式改成非同步模式,这个改动减少了一些驱动程序的 + 复杂度,提高了所有USB驱动程序的吞吐率,这样几乎所有的USB设备都 + 能以最大速率工作了。 + - 修改了USB核心代码中为USB驱动分配数据包内存的方式,所有的驱动都 + 需要提供更多的参数给USB核心,以修正了很多已经被记录在案的死锁。 + +这和一些封闭源代码的操作系统形成鲜明的对比,在那些操作系统上,不得不额 +外的维护旧的USB接口。这导致了一个可能性,新的开发者依然会不小心使用旧的 +接口,以不恰当的方式编写代码,进而影响到操作系统的稳定性。 +在上面的例子中,所有的开发者都同意这些重要的改动,在这样的情况下修改代 +价很低。如果Linux保持一个稳定的内核源代码接口,那么就得创建一个新的接口 +;旧的,有问题的接口必须一直维护,给Linux USB开发者带来额外的工作。既然 +所有的Linux USB驱动的作者都是利用自己的时间工作,那么要求他们去做毫无意 +义的免费额外工作,是不可能的。 +安全问题对Linux来说十分重要。一个安全问题被发现,就会在短时间内得到修 +正。在很多情况下,这将导致Linux内核中的一些接口被重写,以从根本上避免安 +全问题。一旦接口被重写,所有使用这些接口的驱动程序,必须同时得到修正, +以确定安全问题已经得到修复并且不可能在未来还有同样的安全问题。如果内核 +内部接口不允许改变,那么就不可能修复这样的安全问题,也不可能确认这样的 +安全问题以后不会发生。 +开发者一直在清理内核接口。如果一个接口没有人在使用了,它就会被删除。这 +样可以确保内核尽可能的小,而且所有潜在的接口都会得到尽可能完整的测试 +(没有人使用的接口是不可能得到良好的测试的)。 + + +要做什么 +-------- + +如果你写了一个Linux内核驱动,但是它还不在Linux源代码树里,作为一个开发 +者,你应该怎么做?为每个发布的每个版本提供一个二进制驱动,那简直是一个 +噩梦,要跟上永远处于变化之中的内核接口,也是一件辛苦活。 +很简单,让你的驱动进入内核源代码树(要记得我们在谈论的是以GPL许可发行 +的驱动,如果你的代码不符合GPL,那么祝你好运,你只能自己解决这个问题了, +你这个吸血鬼<把Andrew和Linus对吸血鬼的定义链接到这里>)。当你的代码加入 +公版内核源代码树之后,如果一个内核接口改变,你的驱动会直接被修改接口的 +那个人修改。保证你的驱动永远都可以编译通过,并且一直工作,你几乎不需要 +做什么事情。 + +把驱动放到内核源代码树里会有很多的好处: + + - 驱动的质量会提升,而维护成本(对原始作者来说)会下降。 + - 其他人会给驱动添加新特性。 + - 其他人会找到驱动中的bug并修复。 + - 其他人会在驱动中找到性能优化的机会。 + - 当外部的接口的改变需要修改驱动程序的时候,其他人会修改驱动程序 + - 不需要联系任何发行商,这个驱动会自动的随着所有的Linux发布一起发 + 布。 + +和别的操作系统相比,Linux为更多不同的设备提供现成的驱动,而且能在更多不 +同体系结构的处理器上支持这些设备。这个经过考验的开发模式,必然是错不了 +的 :) + +感谢 +---- +感谢 Randy Dunlap, Andrew Morton, David Brownell, Hanna Linder, +Robert Love, and Nishanth Aravamudan 对于本文档早期版本的评审和建议。 + +英文版维护者: Greg Kroah-Hartman <greg@kroah.com> diff --git a/Documentation/translations/zh_CN/process/stable-kernel-rules.rst b/Documentation/translations/zh_CN/process/stable-kernel-rules.rst new file mode 100644 index 000000000..fba361f2d --- /dev/null +++ b/Documentation/translations/zh_CN/process/stable-kernel-rules.rst @@ -0,0 +1,64 @@ +.. _cn_stable_kernel_rules: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>` + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者:: + + 中文版维护者: 钟宇 TripleX Chung <xxx.phy@gmail.com> + 中文版翻译者: 钟宇 TripleX Chung <xxx.phy@gmail.com> + 中文版校译者: + - 李阳 Li Yang <leoyang.li@nxp.com> + - Kangkai Yin <e12051@motorola.com> + +所有你想知道的事情 - 关于linux稳定版发布 +======================================== + +关于Linux 2.6稳定版发布,所有你想知道的事情。 + +关于哪些类型的补丁可以被接收进入稳定版代码树,哪些不可以的规则: +---------------------------------------------------------------- + + - 必须是显而易见的正确,并且经过测试的。 + - 连同上下文,不能大于100行。 + - 必须只修正一件事情。 + - 必须修正了一个给大家带来麻烦的真正的bug(不是“这也许是一个问题...” + 那样的东西)。 + - 必须修正带来如下后果的问题:编译错误(对被标记为CONFIG_BROKEN的例外), + 内核崩溃,挂起,数据损坏,真正的安全问题,或者一些类似“哦,这不 + 好”的问题。简短的说,就是一些致命的问题。 + - 没有“理论上的竞争条件”,除非能给出竞争条件如何被利用的解释。 + - 不能存在任何的“琐碎的”修正(拼写修正,去掉多余空格之类的)。 + - 必须被相关子系统的维护者接受。 + - 必须遵循Documentation/translations/zh_CN/process/submitting-patches.rst里的规则。 + +向稳定版代码树提交补丁的过程: +------------------------------ + + - 在确认了补丁符合以上的规则后,将补丁发送到stable@vger.kernel.org。 + - 如果补丁被接受到队列里,发送者会收到一个ACK回复,如果没有被接受,收 + 到的是NAK回复。回复需要几天的时间,这取决于开发者的时间安排。 + - 被接受的补丁会被加到稳定版本队列里,等待其他开发者的审查。 + - 安全方面的补丁不要发到这个列表,应该发送到security@kernel.org。 + +审查周期: +---------- + + - 当稳定版的维护者决定开始一个审查周期,补丁将被发送到审查委员会,以 + 及被补丁影响的领域的维护者(除非提交者就是该领域的维护者)并且抄送 + 到linux-kernel邮件列表。 + - 审查委员会有48小时的时间,用来决定给该补丁回复ACK还是NAK。 + - 如果委员会中有成员拒绝这个补丁,或者linux-kernel列表上有人反对这个 + 补丁,并提出维护者和审查委员会之前没有意识到的问题,补丁会从队列中 + 丢弃。 + - 在审查周期结束的时候,那些得到ACK回应的补丁将会被加入到最新的稳定版 + 发布中,一个新的稳定版发布就此产生。 + - 安全性补丁将从内核安全小组那里直接接收到稳定版代码树中,而不是通过 + 通常的审查周期。请联系内核安全小组以获得关于这个过程的更多细节。 + +审查委员会: +------------ + - 由一些自愿承担这项任务的内核开发者,和几个非志愿的组成。 diff --git a/Documentation/translations/zh_CN/process/submit-checklist.rst b/Documentation/translations/zh_CN/process/submit-checklist.rst new file mode 100644 index 000000000..50386e0e4 --- /dev/null +++ b/Documentation/translations/zh_CN/process/submit-checklist.rst @@ -0,0 +1,107 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/submit-checklist.rst <submitchecklist>` +:Translator: Alex Shi <alex.shi@linux.alibaba.com> + +.. _cn_submitchecklist: + +Linux内核补丁提交清单 +~~~~~~~~~~~~~~~~~~~~~ + +如果开发人员希望看到他们的内核补丁提交更快地被接受,那么他们应该做一些基本 +的事情。 + +这些都是在 +:ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` +和其他有关提交Linux内核补丁的文档中提供的。 + +1) 如果使用工具,则包括定义/声明该工具的文件。不要依赖于其他头文件拉入您使用 + 的头文件。 + +2) 干净的编译: + + a) 使用适用或修改的 ``CONFIG`` 选项 ``=y``、``=m`` 和 ``=n`` 。没有GCC + 警告/错误,没有链接器警告/错误。 + + b) 通过allnoconfig、allmodconfig + + c) 使用 ``O=builddir`` 时可以成功编译 + +3) 通过使用本地交叉编译工具或其他一些构建场在多个CPU体系结构上构建。 + +4) PPC64是一种很好的交叉编译检查体系结构,因为它倾向于对64位的数使用无符号 + 长整型。 + +5) 如下所述 :ref:`Documentation/translations/zh_CN/process/coding-style.rst <cn_codingstyle>`. + 检查您的补丁是否为常规样式。在提交( ``scripts/check patch.pl`` )之前, + 使用补丁样式检查器检查是否有轻微的冲突。您应该能够处理您的补丁中存在的所有 + 违规行为。 + +6) 任何新的或修改过的 ``CONFIG`` 选项都不会弄脏配置菜单,并默认为关闭,除非 + 它们符合 ``Documentation/kbuild/kconfig-language.rst`` 中记录的异常条件, + 菜单属性:默认值. + +7) 所有新的 ``kconfig`` 选项都有帮助文本。 + +8) 已仔细审查了相关的 ``Kconfig`` 组合。这很难用测试来纠正——脑力在这里是有 + 回报的。 + +9) 用 sparse 检查干净。 + +10) 使用 ``make checkstack`` 和 ``make namespacecheck`` 并修复他们发现的任何 + 问题。 + + .. note:: + + ``checkstack`` 并没有明确指出问题,但是任何一个在堆栈上使用超过512 + 字节的函数都可以进行更改。 + +11) 包括 :ref:`kernel-doc <kernel_doc>` 内核文档以记录全局内核API。(静态函数 + 不需要,但也可以。)使用 ``make htmldocs`` 或 ``make pdfdocs`` 检查 + :ref:`kernel-doc <kernel_doc>` 并修复任何问题。 + +12) 通过以下选项同时启用的测试 ``CONFIG_PREEMPT``, ``CONFIG_DEBUG_PREEMPT``, + ``CONFIG_DEBUG_SLAB``, ``CONFIG_DEBUG_PAGEALLOC``, ``CONFIG_DEBUG_MUTEXES``, + ``CONFIG_DEBUG_SPINLOCK``, ``CONFIG_DEBUG_ATOMIC_SLEEP``, + ``CONFIG_PROVE_RCU`` and ``CONFIG_DEBUG_OBJECTS_RCU_HEAD`` + +13) 已经过构建和运行时测试,包括有或没有 ``CONFIG_SMP``, ``CONFIG_PREEMPT``. + +14) 如果补丁程序影响IO/磁盘等:使用或不使用 ``CONFIG_LBDAF`` 进行测试。 + +15) 所有代码路径都已在启用所有lockdep功能的情况下运行。 + +16) 所有新的/proc条目都记录在 ``Documentation/`` + +17) 所有新的内核引导参数都记录在 + Documentation/admin-guide/kernel-parameters.rst 中。 + +18) 所有新的模块参数都记录在 ``MODULE_PARM_DESC()`` + +19) 所有新的用户空间接口都记录在 ``Documentation/ABI/`` 中。有关详细信息, + 请参阅 ``Documentation/ABI/README`` 。更改用户空间接口的补丁应该抄送 + linux-api@vger.kernel.org。 + +20) 检查是否全部通过 ``make headers_check`` 。 + +21) 已通过至少注入slab和page分配失败进行检查。请参阅 ``Documentation/fault-injection/`` + 如果新代码是实质性的,那么添加子系统特定的故障注入可能是合适的。 + +22) 新添加的代码已经用 ``gcc -W`` 编译(使用 ``make EXTRA-CFLAGS=-W`` )。这 + 将产生大量噪声,但对于查找诸如“警告:有符号和无符号之间的比较”之类的错误 + 很有用。 + +23) 在它被合并到-mm补丁集中之后进行测试,以确保它仍然与所有其他排队的补丁以 + 及VM、VFS和其他子系统中的各种更改一起工作。 + +24) 所有内存屏障例如 ``barrier()``, ``rmb()``, ``wmb()`` 都需要源代码中的注 + 释来解释它们正在执行的操作及其原因的逻辑。 + +25) 如果补丁添加了任何ioctl,那么也要更新 ``Documentation/userspace-api/ioctl/ioctl-number.rst`` + +26) 如果修改后的源代码依赖或使用与以下 ``Kconfig`` 符号相关的任何内核API或 + 功能,则在禁用相关 ``Kconfig`` 符号和/或 ``=m`` (如果该选项可用)的情况 + 下测试以下多个构建[并非所有这些都同时存在,只是它们的各种/随机组合]: + + ``CONFIG_SMP``, ``CONFIG_SYSFS``, ``CONFIG_PROC_FS``, ``CONFIG_INPUT``, ``CONFIG_PCI``, ``CONFIG_BLOCK``, ``CONFIG_PM``, ``CONFIG_MAGIC_SYSRQ``, + ``CONFIG_NET``, ``CONFIG_INET=n`` (但是后者伴随 ``CONFIG_NET=y``). diff --git a/Documentation/translations/zh_CN/process/submitting-drivers.rst b/Documentation/translations/zh_CN/process/submitting-drivers.rst new file mode 100644 index 000000000..98341e7cd --- /dev/null +++ b/Documentation/translations/zh_CN/process/submitting-drivers.rst @@ -0,0 +1,160 @@ +.. _cn_submittingdrivers: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/submitting-drivers.rst + <submittingdrivers>` + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者:: + + 中文版维护者: 李阳 Li Yang <leoyang.li@nxp.com> + 中文版翻译者: 李阳 Li Yang <leoyang.li@nxp.com> + 中文版校译者: 陈琦 Maggie Chen <chenqi@beyondsoft.com> + 王聪 Wang Cong <xiyou.wangcong@gmail.com> + 张巍 Zhang Wei <wezhang@outlook.com> + +如何向 Linux 内核提交驱动程序 +============================= + +这篇文档将会解释如何向不同的内核源码树提交设备驱动程序。请注意,如果你感 +兴趣的是显卡驱动程序,你也许应该访问 XFree86 项目(https://www.xfree86.org/) +和/或 X.org 项目 (https://x.org)。 + +另请参阅 Documentation/translations/zh_CN/process/submitting-patches.rst 文档。 + + +分配设备号 +---------- + +块设备和字符设备的主设备号与从设备号是由 Linux 命名编号分配权威 LANANA( +现在是 Torben Mathiasen)负责分配。申请的网址是 https://www.lanana.org/。 +即使不准备提交到主流内核的设备驱动也需要在这里分配设备号。有关详细信息, +请参阅 Documentation/admin-guide/devices.rst。 + +如果你使用的不是已经分配的设备号,那么当你提交设备驱动的时候,它将会被强 +制分配一个新的设备号,即便这个设备号和你之前发给客户的截然不同。 + +设备驱动的提交对象 +------------------ + +Linux 2.0: + 此内核源码树不接受新的驱动程序。 + +Linux 2.2: + 此内核源码树不接受新的驱动程序。 + +Linux 2.4: + 如果所属的代码领域在内核的 MAINTAINERS 文件中列有一个总维护者, + 那么请将驱动程序提交给他。如果此维护者没有回应或者你找不到恰当的 + 维护者,那么请联系 Willy Tarreau <w@1wt.eu>。 + +Linux 2.6: + 除了遵循和 2.4 版内核同样的规则外,你还需要在 linux-kernel 邮件 + 列表上跟踪最新的 API 变化。向 Linux 2.6 内核提交驱动的顶级联系人 + 是 Andrew Morton <akpm@linux-foundation.org>。 + +决定设备驱动能否被接受的条件 +---------------------------- + +许可: 代码必须使用 GNU 通用公开许可证 (GPL) 提交给 Linux,但是 + 我们并不要求 GPL 是唯一的许可。你或许会希望同时使用多种 + 许可证发布,如果希望驱动程序可以被其他开源社区(比如BSD) + 使用。请参考 include/linux/module.h 文件中所列出的可被 + 接受共存的许可。 + +版权: 版权所有者必须同意使用 GPL 许可。最好提交者和版权所有者 + 是相同个人或实体。否则,必需列出授权使用 GPL 的版权所有 + 人或实体,以备验证之需。 + +接口: 如果你的驱动程序使用现成的接口并且和其他同类的驱动程序行 + 为相似,而不是去发明无谓的新接口,那么它将会更容易被接受。 + 如果你需要一个 Linux 和 NT 的通用驱动接口,那么请在用 + 户空间实现它。 + +代码: 请使用 Documentation/process/coding-style.rst 中所描述的 Linux 代码风 + 格。如果你的某些代码段(例如那些与 Windows 驱动程序包共 + 享的代码段)需要使用其他格式,而你却只希望维护一份代码, + 那么请将它们很好地区分出来,并且注明原因。 + +可移植性: 请注意,指针并不永远是 32 位的,不是所有的计算机都使用小 + 尾模式 (little endian) 存储数据,不是所有的人都拥有浮点 + 单元,不要随便在你的驱动程序里嵌入 x86 汇编指令。只能在 + x86 上运行的驱动程序一般是不受欢迎的。虽然你可能只有 x86 + 硬件,很难测试驱动程序在其他平台上是否可用,但是确保代码 + 可以被轻松地移植却是很简单的。 + +清晰度: 做到所有人都能修补这个驱动程序将会很有好处,因为这样你将 + 会直接收到修复的补丁而不是 bug 报告。如果你提交一个试图 + 隐藏硬件工作机理的驱动程序,那么它将会被扔进废纸篓。 + +电源管理: 因为 Linux 正在被很多移动设备和桌面系统使用,所以你的驱 + 动程序也很有可能被使用在这些设备上。它应该支持最基本的电 + 源管理,即在需要的情况下实现系统级休眠和唤醒要用到的 + .suspend 和 .resume 函数。你应该检查你的驱动程序是否能正 + 确地处理休眠与唤醒,如果实在无法确认,请至少把 .suspend + 函数定义成返回 -ENOSYS(功能未实现)错误。你还应该尝试确 + 保你的驱动在什么都不干的情况下将耗电降到最低。要获得驱动 + 程序测试的指导,请参阅 + Documentation/power/drivers-testing.rst。有关驱动程序电 + 源管理问题相对全面的概述,请参阅 + Documentation/driver-api/pm/devices.rst。 + +管理: 如果一个驱动程序的作者还在进行有效的维护,那么通常除了那 + 些明显正确且不需要任何检查的补丁以外,其他所有的补丁都会 + 被转发给作者。如果你希望成为驱动程序的联系人和更新者,最 + 好在代码注释中写明并且在 MAINTAINERS 文件中加入这个驱动 + 程序的条目。 + +不影响设备驱动能否被接受的条件 +------------------------------ + +供应商: 由硬件供应商来维护驱动程序通常是一件好事。不过,如果源码 + 树里已经有其他人提供了可稳定工作的驱动程序,那么请不要期 + 望“我是供应商”会成为内核改用你的驱动程序的理由。理想的情 + 况是:供应商与现有驱动程序的作者合作,构建一个统一完美的 + 驱动程序。 + +作者: 驱动程序是由大的 Linux 公司研发还是由你个人编写,并不影 + 响其是否能被内核接受。没有人对内核源码树享有特权。只要你 + 充分了解内核社区,你就会发现这一点。 + + +资源列表 +-------- + +Linux 内核主源码树: + ftp.??.kernel.org:/pub/linux/kernel/... + ?? == 你的国家代码,例如 "cn"、"us"、"uk"、"fr" 等等 + +Linux 内核邮件列表: + linux-kernel@vger.kernel.org + [可通过向majordomo@vger.kernel.org发邮件来订阅] + +Linux 设备驱动程序,第三版(探讨 2.6.10 版内核): + https://lwn.net/Kernel/LDD3/ (免费版) + +LWN.net: + 每周内核开发活动摘要 - https://lwn.net/ + + 2.6 版中 API 的变更: + + https://lwn.net/Articles/2.6-kernel-api/ + + 将旧版内核的驱动程序移植到 2.6 版: + + https://lwn.net/Articles/driver-porting/ + +内核新手(KernelNewbies): + 为新的内核开发者提供文档和帮助 + https://kernelnewbies.org/ + +Linux USB项目: + http://www.linux-usb.org/ + +写内核驱动的“不要”(Arjan van de Ven著): + http://www.fenrus.org/how-to-not-write-a-device-driver-paper.pdf + +内核清洁工 (Kernel Janitor): + https://kernelnewbies.org/KernelJanitors diff --git a/Documentation/translations/zh_CN/process/submitting-patches.rst b/Documentation/translations/zh_CN/process/submitting-patches.rst new file mode 100644 index 000000000..2e7dbaad4 --- /dev/null +++ b/Documentation/translations/zh_CN/process/submitting-patches.rst @@ -0,0 +1,682 @@ +.. _cn_submittingpatches: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/submitting-patches.rst <submittingpatches>` + +译者:: + + 中文版维护者: 钟宇 TripleX Chung <xxx.phy@gmail.com> + 中文版翻译者: 钟宇 TripleX Chung <xxx.phy@gmail.com> + 时奎亮 Alex Shi <alex.shi@linux.alibaba.com> + 中文版校译者: 李阳 Li Yang <leoyang.li@nxp.com> + 王聪 Wang Cong <xiyou.wangcong@gmail.com> + + +如何让你的改动进入内核 +====================== + +对于想要将改动提交到 Linux 内核的个人或者公司来说,如果不熟悉“规矩”, +提交的流程会让人畏惧。本文档收集了一系列建议,这些建议可以大大的提高你 +的改动被接受的机会. + +以下文档含有大量简洁的建议, 具体请见: +:ref:`Documentation/process <development_process_main>` +同样,:ref:`Documentation/translations/zh_CN/process/submit-checklist.rst <cn_submitchecklist>` +给出在提交代码前需要检查的项目的列表。如果你在提交一个驱动程序,那么 +同时阅读一下: +:ref:`Documentation/process/submitting-drivers.rst <submittingdrivers>` + +其中许多步骤描述了Git版本控制系统的默认行为;如果您使用Git来准备补丁, +您将发现它为您完成的大部分机械工作,尽管您仍然需要准备和记录一组合理的 +补丁。一般来说,使用git将使您作为内核开发人员的生活更轻松。 + + +0) 获取当前源码树 +----------------- + +如果您没有一个可以使用当前内核源代码的存储库,请使用git获取一个。您将要 +从主线存储库开始,它可以通过以下方式获取:: + + git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + +但是,请注意,您可能不希望直接针对主线树进行开发。大多数子系统维护人员运 +行自己的树,并希望看到针对这些树准备的补丁。请参见MAINTAINERS文件中子系 +统的 **T:** 项以查找该树,或者简单地询问维护者该树是否未在其中列出。 + +仍然可以通过tarballs下载内核版本(如下一节所述),但这是进行内核开发的 +一种困难的方式。 + +1) "diff -up" +------------- + +使用 "diff -up" 或者 "diff -uprN" 来创建补丁。 + +所有内核的改动,都是以补丁的形式呈现的,补丁由 diff(1) 生成。创建补丁的 +时候,要确认它是以 "unified diff" 格式创建的,这种格式由 diff(1) 的 '-u' +参数生成。而且,请使用 '-p' 参数,那样会显示每个改动所在的C函数,使得 +产生的补丁容易读得多。补丁应该基于内核源代码树的根目录,而不是里边的任 +何子目录。 + +为一个单独的文件创建补丁,一般来说这样做就够了:: + + SRCTREE=linux + MYFILE=drivers/net/mydriver.c + + cd $SRCTREE + cp $MYFILE $MYFILE.orig + vi $MYFILE # make your change + cd .. + diff -up $SRCTREE/$MYFILE{.orig,} > /tmp/patch + +为多个文件创建补丁,你可以解开一个没有修改过的内核源代码树,然后和你自 +己的代码树之间做 diff 。例如:: + + MYSRC=/devel/linux + + tar xvfz linux-3.19.tar.gz + mv linux-3.19 linux-3.19-vanilla + diff -uprN -X linux-3.19-vanilla/Documentation/dontdiff \ + linux-3.19-vanilla $MYSRC > /tmp/patch + +"dontdiff" 是内核在编译的时候产生的文件的列表,列表中的文件在 diff(1) +产生的补丁里会被跳过。 + +确定你的补丁里没有包含任何不属于这次补丁提交的额外文件。记得在用diff(1) +生成补丁之后,审阅一次补丁,以确保准确。 + +如果你的改动很散乱,你应该研究一下如何将补丁分割成独立的部分,将改动分 +割成一系列合乎逻辑的步骤。这样更容易让其他内核开发者审核,如果你想你的 +补丁被接受,这是很重要的。请参阅: +:ref:`cn_split_changes` + +如果你用 ``git`` , ``git rebase -i`` 可以帮助你这一点。如果你不用 ``git``, +``quilt`` <https://savannah.nongnu.org/projects/quilt> 另外一个流行的选择。 + +.. _cn_describe_changes: + +2) 描述你的改动 +--------------- + +描述你的问题。无论您的补丁是一行错误修复还是5000行新功能,都必须有一个潜在 +的问题激励您完成这项工作。让审稿人相信有一个问题值得解决,让他们读完第一段 +是有意义的。 + +描述用户可见的影响。直接崩溃和锁定是相当有说服力的,但并不是所有的错误都那么 +明目张胆。即使在代码审查期间发现了这个问题,也要描述一下您认为它可能对用户产 +生的影响。请记住,大多数Linux安装运行的内核来自二级稳定树或特定于供应商/产品 +的树,只从上游精选特定的补丁,因此请包含任何可以帮助您将更改定位到下游的内容: +触发的场景、DMESG的摘录、崩溃描述、性能回归、延迟尖峰、锁定等。 + +量化优化和权衡。如果您声称在性能、内存消耗、堆栈占用空间或二进制大小方面有所 +改进,请包括支持它们的数字。但也要描述不明显的成本。优化通常不是免费的,而是 +在CPU、内存和可读性之间进行权衡;或者,探索性的工作,在不同的工作负载之间进 +行权衡。请描述优化的预期缺点,以便审阅者可以权衡成本和收益。 + +一旦问题建立起来,就要详细地描述一下您实际在做什么。对于审阅者来说,用简单的 +英语描述代码的变化是很重要的,以验证代码的行为是否符合您的意愿。 + +如果您将补丁描述写在一个表单中,这个表单可以很容易地作为“提交日志”放入Linux +的源代码管理系统git中,那么维护人员将非常感谢您。见 :ref:`cn_explicit_in_reply_to`. + +每个补丁只解决一个问题。如果你的描述开始变长,这就表明你可能需要拆分你的补丁。 +请见 :ref:`cn_split_changes` + +提交或重新提交修补程序或修补程序系列时,请包括完整的修补程序说明和理由。不要 +只说这是补丁(系列)的第几版。不要期望子系统维护人员引用更早的补丁版本或引用 +URL来查找补丁描述并将其放入补丁中。也就是说,补丁(系列)及其描述应该是独立的。 +这对维护人员和审查人员都有好处。一些评审者可能甚至没有收到补丁的早期版本。 + +描述你在命令语气中的变化,例如“make xyzzy do frotz”而不是“[这个补丁]make +xyzzy do frotz”或“[我]changed xyzzy to do frotz”,就好像你在命令代码库改变 +它的行为一样。 + +如果修补程序修复了一个记录的bug条目,请按编号和URL引用该bug条目。如果补丁来 +自邮件列表讨论,请给出邮件列表存档的URL;使用带有 ``Message-ID`` 的 +https://lkml.kernel.org/ 重定向,以确保链接不会过时。 + +但是,在没有外部资源的情况下,尽量让你的解释可理解。除了提供邮件列表存档或 +bug的URL之外,还要总结需要提交补丁的相关讨论要点。 + +如果您想要引用一个特定的提交,不要只引用提交的 SHA-1 ID。还请包括提交的一行 +摘要,以便于审阅者了解它是关于什么的。例如:: + + Commit e21d2170f36602ae2708 ("video: remove unnecessary + platform_set_drvdata()") removed the unnecessary + platform_set_drvdata(), but left the variable "dev" unused, + delete it. + +您还应该确保至少使用前12位 SHA-1 ID. 内核存储库包含*许多*对象,使与较短的ID +发生冲突的可能性很大。记住,即使现在不会与您的六个字符ID发生冲突,这种情况 +可能五年后改变。 + +如果修补程序修复了特定提交中的错误,例如,使用 ``git bisct`` ,请使用带有前 +12个字符SHA-1 ID 的"Fixes:"标记和单行摘要。为了简化不要将标记拆分为多个, +行、标记不受分析脚本“75列换行”规则的限制。例如:: + + Fixes: 54a4f0239f2e ("KVM: MMU: make kvm_mmu_zap_page() return the number of pages it actually freed") + +下列 ``git config`` 设置可以添加让 ``git log``, ``git show`` 漂亮的显示格式:: + + [core] + abbrev = 12 + [pretty] + fixes = Fixes: %h (\"%s\") + +.. _cn_split_changes: + +3) 拆分你的改动 +--------------- + +将每个逻辑更改分隔成一个单独的补丁。 + +例如,如果你的改动里同时有bug修正和性能优化,那么把这些改动拆分到两个或 +者更多的补丁文件中。如果你的改动包含对API的修改,并且修改了驱动程序来适 +应这些新的API,那么把这些修改分成两个补丁。 + +另一方面,如果你将一个单独的改动做成多个补丁文件,那么将它们合并成一个 +单独的补丁文件。这样一个逻辑上单独的改动只被包含在一个补丁文件里。 + +如果有一个补丁依赖另外一个补丁来完成它的改动,那没问题。简单的在你的补 +丁描述里指出“这个补丁依赖某补丁”就好了。 + +在将您的更改划分为一系列补丁时,要特别注意确保内核在系列中的每个补丁之后 +都能正常构建和运行。使用 ``git bisect`` 来追踪问题的开发者可能会在任何时 +候分割你的补丁系列;如果你在中间引入错误,他们不会感谢你。 + +如果你不能将补丁浓缩成更少的文件,那么每次大约发送出15个,然后等待审查 +和集成。 + +4) 检查你的更改风格 +------------------- + +检查您的补丁是否存在基本样式冲突,详细信息可在 +:ref:`Documentation/translations/zh_CN/process/coding-style.rst <cn_codingstyle>` +中找到。如果不这样做,只会浪费审稿人的时间,并且会导致你的补丁被拒绝,甚至 +可能没有被阅读。 + +一个重要的例外是在将代码从一个文件移动到另一个文件时——在这种情况下,您不应 +该在移动代码的同一个补丁中修改移动的代码。这清楚地描述了移动代码和您的更改 +的行为。这大大有助于审查实际差异,并允许工具更好地跟踪代码本身的历史。 + +在提交之前,使用补丁样式检查程序检查补丁(scripts/check patch.pl)。不过, +请注意,样式检查程序应该被视为一个指南,而不是作为人类判断的替代品。如果您 +的代码看起来更好,但有违规行为,那么最好不要使用它。 + +检查者报告三个级别: + + - ERROR:很可能出错的事情 + - WARNING:需要仔细审查的事项 + - CHECK:需要思考的事情 + +您应该能够判断您的补丁中存在的所有违规行为。 + +5) 选择补丁收件人 +----------------- + +您应该总是在任何补丁上复制相应的子系统维护人员,以获得他们维护的代码;查看 +维护人员文件和源代码修订历史记录,以了解这些维护人员是谁。脚本 +scripts/get_Maintainer.pl在这个步骤中非常有用。如果您找不到正在工作的子系统 +的维护人员,那么Andrew Morton(akpm@linux-foundation.org)将充当最后的维护 +人员。 + +您通常还应该选择至少一个邮件列表来接收补丁集的。linux-kernel@vger.kernel.org +作为最后一个解决办法的列表,但是这个列表上的体积已经引起了许多开发人员的拒绝。 +在MAINTAINERS文件中查找子系统特定的列表;您的补丁可能会在那里得到更多的关注。 +不过,请不要发送垃圾邮件到无关的列表。 + +许多与内核相关的列表托管在vger.kernel.org上;您可以在 +http://vger.kernel.org/vger-lists.html 上找到它们的列表。不过,也有与内核相关 +的列表托管在其他地方。 + +不要一次发送超过15个补丁到vger邮件列表!!!! + +Linus Torvalds 是决定改动能否进入 Linux 内核的最终裁决者。他的 e-mail +地址是 <torvalds@linux-foundation.org> 。他收到的 e-mail 很多,所以一般 +的说,最好别给他发 e-mail。 + +如果您有修复可利用安全漏洞的补丁,请将该补丁发送到 security@kernel.org。对于 +严重的bug,可以考虑短期暂停以允许分销商向用户发布补丁;在这种情况下,显然不应 +将补丁发送到任何公共列表。 + +修复已发布内核中严重错误的补丁程序应该指向稳定版维护人员,方法是放这样的一行:: + + Cc: stable@vger.kernel.org + +进入补丁的签准区(注意,不是电子邮件收件人)。除了这个文件之外,您还应该阅读 +:ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>` + +但是,请注意,一些子系统维护人员希望得出他们自己的结论,即哪些补丁应该被放到 +稳定的树上。尤其是网络维护人员,不希望看到单个开发人员在补丁中添加像上面这样 +的行。 + +如果更改影响到用户和内核接口,请向手册页维护人员(如维护人员文件中所列)发送 +手册页补丁,或至少发送更改通知,以便一些信息进入手册页。还应将用户空间API +更改复制到 linux-api@vger.kernel.org。 + +对于小的补丁,你也许会CC到搜集琐碎补丁的邮件列表(Trivial Patch Monkey) +trivial@kernel.org,那里专门收集琐碎的补丁。下面这样的补丁会被看作“琐碎的” +补丁: + + - 文档的拼写修正。 + - 修正会影响到 grep(1) 的拼写。 + - 警告信息修正(频繁的打印无用的警告是不好的。) + - 编译错误修正(代码逻辑的确是对的,只是编译有问题。) + - 运行时修正(只要真的修正了错误。) + - 移除使用了被废弃的函数/宏的代码(例如 check_region。) + - 联系方式和文档修正。 + - 用可移植的代码替换不可移植的代码(即使在体系结构相关的代码中,既然有 + - 人拷贝,只要它是琐碎的) + - 任何文件的作者/维护者对该文件的改动(例如 patch monkey 在重传模式下) + +(译注,关于“琐碎补丁”的一些说明:因为原文的这一部分写得比较简单,所以不得不 +违例写一下译注。"trivial"这个英文单词的本意是“琐碎的,不重要的。”但是在这里 +有稍微有一些变化,例如对一些明显的NULL指针的修正,属于运行时修正,会被归类 +到琐碎补丁里。虽然NULL指针的修正很重要,但是这样的修正往往很小而且很容易得到 +检验,所以也被归入琐碎补丁。琐碎补丁更精确的归类应该是 +“simple, localized & easy to verify”,也就是说简单的,局部的和易于检验的。 +trivial@kernel.org邮件列表的目的是针对这样的补丁,为提交者提供一个中心,来 +降低提交的门槛。) + +6) 没有 MIME 编码,没有链接,没有压缩,没有附件,只有纯文本 +----------------------------------------------------------- + +Linus 和其他的内核开发者需要阅读和评论你提交的改动。对于内核开发者来说 +,可以“引用”你的改动很重要,使用一般的 e-mail 工具,他们就可以在你的 +代码的任何位置添加评论。 + +因为这个原因,所有的提交的补丁都是 e-mail 中“内嵌”的。 + +.. warning:: + 如果你使用剪切-粘贴你的补丁,小心你的编辑器的自动换行功能破坏你的补丁 + +不要将补丁作为 MIME 编码的附件,不管是否压缩。很多流行的 e-mail 软件不 +是任何时候都将 MIME 编码的附件当作纯文本发送的,这会使得别人无法在你的 +代码中加评论。另外,MIME 编码的附件会让 Linus 多花一点时间来处理,这就 +降低了你的改动被接受的可能性。 + +例外:如果你的邮递员弄坏了补丁,那么有人可能会要求你使用mime重新发送补丁 + +请参阅 :ref:`Documentation/translations/zh_CN/process/email-clients.rst <cn_email_clients>` +以获取有关配置电子邮件客户端以使其不受影响地发送修补程序的提示。 + +7) e-mail 的大小 +---------------- + +大的改动对邮件列表不合适,对某些维护者也不合适。如果你的补丁,在不压缩 +的情况下,超过了300kB,那么你最好将补丁放在一个能通过 internet 访问的服 +务器上,然后用指向你的补丁的 URL 替代。但是请注意,如果您的补丁超过了 +300kb,那么它几乎肯定需要被破坏。 + +8)回复评审意见 +--------------- + +你的补丁几乎肯定会得到评审者对补丁改进方法的评论。您必须对这些评论作出 +回应;让补丁被忽略的一个好办法就是忽略审阅者的意见。不会导致代码更改的 +意见或问题几乎肯定会带来注释或变更日志的改变,以便下一个评审者更好地了解 +正在发生的事情。 + +一定要告诉审稿人你在做什么改变,并感谢他们的时间。代码审查是一个累人且 +耗时的过程,审查人员有时会变得暴躁。即使在这种情况下,也要礼貌地回应并 +解决他们指出的问题。 + +9)不要泄气或不耐烦 +------------------- + +提交更改后,请耐心等待。审阅者是忙碌的人,可能无法立即访问您的修补程序。 + +曾几何时,补丁曾在没有评论的情况下消失在空白中,但开发过程比现在更加顺利。 +您应该在一周左右的时间内收到评论;如果没有收到评论,请确保您已将补丁发送 +到正确的位置。在重新提交或联系审阅者之前至少等待一周-在诸如合并窗口之类的 +繁忙时间可能更长。 + +10)主题中包含 PATCH +-------------------- + +由于到linus和linux内核的电子邮件流量很高,通常会在主题行前面加上[PATCH] +前缀. 这使Linus和其他内核开发人员更容易将补丁与其他电子邮件讨论区分开。 + +11)签署你的作品-开发者原始认证 +------------------------------- + +为了加强对谁做了何事的追踪,尤其是对那些透过好几层的维护者的补丁,我们 +建议在发送出去的补丁上加一个 “sign-off” 的过程。 + +"sign-off" 是在补丁的注释的最后的简单的一行文字,认证你编写了它或者其他 +人有权力将它作为开放源代码的补丁传递。规则很简单:如果你能认证如下信息: + +开发者来源证书 1.1 +^^^^^^^^^^^^^^^^^^ + +对于本项目的贡献,我认证如下信息: + + (a)这些贡献是完全或者部分的由我创建,我有权利以文件中指出 + 的开放源代码许可证提交它;或者 + (b)这些贡献基于以前的工作,据我所知,这些以前的工作受恰当的开放 + 源代码许可证保护,而且,根据许可证,我有权提交修改后的贡献, + 无论是完全还是部分由我创造,这些贡献都使用同一个开放源代码许可证 + (除非我被允许用其它的许可证),正如文件中指出的;或者 + (c)这些贡献由认证(a),(b)或者(c)的人直接提供给我,而 + 且我没有修改它。 + (d)我理解并同意这个项目和贡献是公开的,贡献的记录(包括我 + 一起提交的个人记录,包括 sign-off )被永久维护并且可以和这个项目 + 或者开放源代码的许可证同步地再发行。 + +那么加入这样一行:: + + Signed-off-by: Random J Developer <random@developer.example.org> + +使用你的真名(抱歉,不能使用假名或者匿名。) + +有人在最后加上标签。现在这些东西会被忽略,但是你可以这样做,来标记公司 +内部的过程,或者只是指出关于 sign-off 的一些特殊细节。 + +如果您是子系统或分支维护人员,有时需要稍微修改收到的补丁,以便合并它们, +因为树和提交者中的代码不完全相同。如果你严格遵守规则(c),你应该要求提交者 +重新发布,但这完全是在浪费时间和精力。规则(b)允许您调整代码,但是更改一个 +提交者的代码并让他认可您的错误是非常不礼貌的。要解决此问题,建议在最后一个 +由签名行和您的行之间添加一行,指示更改的性质。虽然这并不是强制性的,但似乎 +在描述前加上您的邮件和/或姓名(全部用方括号括起来),这足以让人注意到您对最 +后一分钟的更改负有责任。例如:: + + Signed-off-by: Random J Developer <random@developer.example.org> + [lucky@maintainer.example.org: struct foo moved from foo.c to foo.h] + Signed-off-by: Lucky K Maintainer <lucky@maintainer.example.org> + +如果您维护一个稳定的分支机构,同时希望对作者进行致谢、跟踪更改、合并修复并 +保护提交者不受投诉,那么这种做法尤其有用。请注意,在任何情况下都不能更改作者 +的ID(From 头),因为它是出现在更改日志中的标识。 + +对回合(back-porters)的特别说明:在提交消息的顶部(主题行之后)插入一个补丁 +的起源指示似乎是一种常见且有用的实践,以便于跟踪。例如,下面是我们在3.x稳定 +版本中看到的内容:: + + Date: Tue Oct 7 07:26:38 2014 -0400 + + libata: Un-break ATA blacklist + + commit 1c40279960bcd7d52dbdf1d466b20d24b99176c8 upstream. + +还有, 这里是一个旧版内核中的一个回合补丁:: + + Date: Tue May 13 22:12:27 2008 +0200 + + wireless, airo: waitbusy() won't delay + + [backport of 2.6 commit b7acbdfbd1f277c1eb23f344f899cfa4cd0bf36a] + +12)何时使用Acked-by:,CC:,和Co-Developed by: +---------------------------------------------- + +Singed-off-by: 标记表示签名者参与了补丁的开发,或者他/她在补丁的传递路径中。 + +如果一个人没有直接参与补丁的准备或处理,但希望表示并记录他们对补丁的批准, +那么他们可以要求在补丁的变更日志中添加一个 Acked-by: + +Acked-by:通常由受影响代码的维护者使用,当该维护者既没有贡献也没有转发补丁时。 + +Acked-by: 不像签字人那样正式。这是一个记录,确认人至少审查了补丁,并表示接受。 +因此,补丁合并有时会手动将Acker的“Yep,looks good to me”转换为 Acked-By:(但 +请注意,通常最好要求一个明确的Ack)。 + +Acked-by:不一定表示对整个补丁的确认。例如,如果一个补丁影响多个子系统,并且 +有一个:来自一个子系统维护者,那么这通常表示只确认影响维护者代码的部分。这里 +应该仔细判断。如有疑问,应参考邮件列表档案中的原始讨论。 + +如果某人有机会对补丁进行评论,但没有提供此类评论,您可以选择在补丁中添加 ``Cc:`` +这是唯一一个标签,它可以在没有被它命名的人显式操作的情况下添加,但它应该表明 +这个人是在补丁上抄送的。讨论中包含了潜在利益相关方。 + +Co-developed-by: 声明补丁是由多个开发人员共同创建的;当几个人在一个补丁上工 +作时,它用于将属性赋予共同作者(除了 From: 所赋予的作者之外)。因为 +Co-developed-by: 表示作者身份,所以每个共同开发人:必须紧跟在相关合作作者的 +签名之后。标准的签核程序要求:标记的签核顺序应尽可能反映补丁的时间历史,而不 +管作者是通过 From :还是由 Co-developed-by: 共同开发的。值得注意的是,最后一 +个签字人:必须始终是提交补丁的开发人员。 + +注意,当作者也是电子邮件标题“发件人:”行中列出的人时,“From: ” 标记是可选的。 + +作者提交的补丁程序示例:: + + <changelog> + + Co-developed-by: First Co-Author <first@coauthor.example.org> + Signed-off-by: First Co-Author <first@coauthor.example.org> + Co-developed-by: Second Co-Author <second@coauthor.example.org> + Signed-off-by: Second Co-Author <second@coauthor.example.org> + Signed-off-by: From Author <from@author.example.org> + +合作开发者提交的补丁示例:: + + From: From Author <from@author.example.org> + + <changelog> + + Co-developed-by: Random Co-Author <random@coauthor.example.org> + Signed-off-by: Random Co-Author <random@coauthor.example.org> + Signed-off-by: From Author <from@author.example.org> + Co-developed-by: Submitting Co-Author <sub@coauthor.example.org> + Signed-off-by: Submitting Co-Author <sub@coauthor.example.org> + + +13)使用报告人:、测试人:、审核人:、建议人:、修复人: +-------------------------------------------------------- + +Reported-by: 给那些发现错误并报告错误的人致谢,它希望激励他们在将来再次帮助 +我们。请注意,如果bug是以私有方式报告的,那么在使用Reported-by标记之前,请 +先请求权限。 + +Tested-by: 标记表示补丁已由指定的人(在某些环境中)成功测试。这个标签通知 +维护人员已经执行了一些测试,为将来的补丁提供了一种定位测试人员的方法,并确 +保测试人员的信誉。 + +Reviewed-by:相反,根据审查人的声明,表明该补丁已被审查并被认为是可接受的: + + +审查人的监督声明 +^^^^^^^^^^^^^^^^ + +通过提供我的 Reviewed-by,我声明: + + (a) 我已经对这个补丁进行了一次技术审查,以评估它是否适合被包含到 + 主线内核中。 + + (b) 与补丁相关的任何问题、顾虑或问题都已反馈给提交者。我对提交者对 + 我的评论的回应感到满意。 + + (c) 虽然这一提交可能会改进一些东西,但我相信,此时,(1)对内核 + 进行了有价值的修改,(2)没有包含争论中涉及的已知问题。 + + (d) 虽然我已经审查了补丁并认为它是健全的,但我不会(除非另有明确 + 说明)作出任何保证或保证它将在任何给定情况下实现其规定的目的 + 或正常运行。 + +Reviewed-by 是一种观点声明,即补丁是对内核的适当修改,没有任何遗留的严重技术 +问题。任何感兴趣的审阅者(完成工作的人)都可以为一个补丁提供一个 Review-by +标签。此标签用于向审阅者提供致谢,并通知维护者已在修补程序上完成的审阅程度。 +Reviewed-by: 当由已知了解主题区域并执行彻底检查的审阅者提供时,通常会增加 +补丁进入内核的可能性。 + +Suggested-by: 表示补丁的想法是由指定的人提出的,并确保将此想法归功于指定的 +人。请注意,未经许可,不得添加此标签,特别是如果该想法未在公共论坛上发布。 +这就是说,如果我们勤快地致谢我们的创意者,他们很有希望在未来得到鼓舞,再次 +帮助我们。 + +Fixes: 指示补丁在以前的提交中修复了一个问题。它可以很容易地确定错误的来源, +这有助于检查错误修复。这个标记还帮助稳定内核团队确定应该接收修复的稳定内核 +版本。这是指示补丁修复的错误的首选方法。请参阅 :ref:`cn_describe_changes` +描述您的更改以了解更多详细信息。 + +.. _cn_the_canonical_patch_format: + +12)标准补丁格式 +---------------- + +本节描述如何格式化补丁本身。请注意,如果您的补丁存储在 ``Git`` 存储库中,则 +可以使用 ``git format-patch`` 进行正确的补丁格式设置。但是,这些工具无法创建 +必要的文本,因此请务必阅读下面的说明。 + +标准的补丁,标题行是:: + + Subject: [PATCH 001/123] 子系统:一句话概述 + +标准补丁的信体存在如下部分: + + - 一个 "from" 行指出补丁作者。后跟空行(仅当发送修补程序的人不是作者时才需要)。 + + - 解释的正文,行以75列包装,这将被复制到永久变更日志来描述这个补丁。 + + - 一个空行 + + - 上面描述的“Signed-off-by” 行,也将出现在更改日志中。 + + - 只包含 ``---`` 的标记线。 + + - 任何其他不适合放在变更日志的注释。 + + - 实际补丁( ``diff`` 输出)。 + +标题行的格式,使得对标题行按字母序排序非常的容易 - 很多 e-mail 客户端都 +可以支持 - 因为序列号是用零填充的,所以按数字排序和按字母排序是一样的。 + +e-mail 标题中的“子系统”标识哪个内核子系统将被打补丁。 + +e-mail 标题中的“一句话概述”扼要的描述 e-mail 中的补丁。“一句话概述” +不应该是一个文件名。对于一个补丁系列(“补丁系列”指一系列的多个相关补 +丁),不要对每个补丁都使用同样的“一句话概述”。 + +记住 e-mail 的“一句话概述”会成为该补丁的全局唯一标识。它会蔓延到 git +的改动记录里。然后“一句话概述”会被用在开发者的讨论里,用来指代这个补 +丁。用户将希望通过 google 来搜索"一句话概述"来找到那些讨论这个补丁的文 +章。当人们在两三个月后使用诸如 ``gitk`` 或 ``git log --oneline`` 之类 +的工具查看数千个补丁时,也会很快看到它。 + +出于这些原因,概述必须不超过70-75个字符,并且必须描述补丁的更改以及为 +什么需要补丁。既要简洁又要描述性很有挑战性,但写得好的概述应该这样做。 + +概述的前缀可以用方括号括起来:“Subject: [PATCH <tag>...] <概述>”。标记 +不被视为概述的一部分,而是描述应该如何处理补丁。如果补丁的多个版本已发 +送出来以响应评审(即“v1,v2,v3”)或“rfc”,以指示评审请求,那么通用标记 +可能包括版本描述符。如果一个补丁系列中有四个补丁,那么各个补丁可以这样 +编号:1/4、2/4、3/4、4/4。这可以确保开发人员了解补丁应用的顺序,并且他们 +已经查看或应用了补丁系列中的所有补丁。 + +一些标题的例子:: + + Subject: [patch 2/5] ext2: improve scalability of bitmap searching + Subject: [PATCHv2 001/207] x86: fix eflags tracking + +"From" 行是信体里的最上面一行,具有如下格式: + From: Patch Author <author@example.com> + +"From" 行指明在永久改动日志里,谁会被确认为作者。如果没有 "From" 行,那 +么邮件头里的 "From: " 行会被用来决定改动日志中的作者。 + +说明的主题将会被提交到永久的源代码改动日志里,因此对那些早已经不记得和 +这个补丁相关的讨论细节的有能力的读者来说,是有意义的。包括补丁程序定位 +错误的(内核日志消息、OOPS消息等)症状,对于搜索提交日志以寻找适用补丁的人 +尤其有用。如果一个补丁修复了一个编译失败,那么可能不需要包含所有编译失败; +只要足够让搜索补丁的人能够找到它就行了。与概述一样,既要简洁又要描述性。 + +"---" 标记行对于补丁处理工具要找到哪里是改动日志信息的结束,是不可缺少 +的。 + +对于 "---" 标记之后的额外注解,一个好的用途就是用来写 diffstat,用来显 +示修改了什么文件和每个文件都增加和删除了多少行。diffstat 对于比较大的补 +丁特别有用。其余那些只是和时刻或者开发者相关的注解,不合适放到永久的改 +动日志里的,也应该放这里。 +使用 diffstat的选项 "-p 1 -w 70" 这样文件名就会从内核源代码树的目录开始 +,不会占用太宽的空间(很容易适合80列的宽度,也许会有一些缩进。) + +在后面的参考资料中能看到适当的补丁格式的更多细节。 + +.. _cn_explicit_in_reply_to: + +15) 明确回复邮件头(In-Reply-To) +------------------------------- + +手动添加回复补丁的的标题头(In-Reply_To:) 是有帮助的(例如,使用 ``git send-email`` ) +将补丁与以前的相关讨论关联起来,例如,将bug修复程序链接到电子邮件和bug报告。 +但是,对于多补丁系列,最好避免在回复时使用链接到该系列的旧版本。这样, +补丁的多个版本就不会成为电子邮件客户端中无法管理的引用序列。如果链接有用, +可以使用 https://lkml.kernel.org/ 重定向器(例如,在封面电子邮件文本中) +链接到补丁系列的早期版本。 + +16) 发送git pull请求 +-------------------- + +如果您有一系列补丁,那么让维护人员通过git pull操作将它们直接拉入子系统存储 +库可能是最方便的。但是,请注意,从开发人员那里获取补丁比从邮件列表中获取补 +丁需要更高的信任度。因此,许多子系统维护人员不愿意接受请求,特别是来自新的 +未知开发人员的请求。如果有疑问,您可以在封面邮件中使用pull 请求作为补丁系列 +正常发布的一个选项,让维护人员可以选择使用其中之一。 + +pull 请求的主题行中应该有[Git Pull]。请求本身应该在一行中包含存储库名称和 +感兴趣的分支;它应该看起来像:: + + Please pull from + + git://jdelvare.pck.nerim.net/jdelvare-2.6 i2c-for-linus + + to get these changes: + + +pull 请求还应该包含一条整体消息,说明请求中将包含什么,一个补丁本身的 ``Git shortlog`` +以及一个显示补丁系列整体效果的 ``diffstat`` 。当然,将所有这些信息收集在一起 +的最简单方法是让 ``git`` 使用 ``git request-pull`` 命令为您完成这些工作。 + +一些维护人员(包括Linus)希望看到来自已签名提交的请求;这增加了他们对你的 +请求信心。特别是,在没有签名标签的情况下,Linus 不会从像 Github 这样的公共 +托管站点拉请求。 + +创建此类签名的第一步是生成一个 GNRPG 密钥,并由一个或多个核心内核开发人员对 +其进行签名。这一步对新开发人员来说可能很困难,但没有办法绕过它。参加会议是 +找到可以签署您的密钥的开发人员的好方法。 + +一旦您在Git 中准备了一个您希望有人拉的补丁系列,就用 ``git tag -s`` 创建一 +个签名标记。这将创建一个新标记,标识该系列中的最后一次提交,并包含用您的私 +钥创建的签名。您还可以将changelog样式的消息添加到标记中;这是一个描述拉请求 +整体效果的理想位置。 + +如果维护人员将要从中提取的树不是您正在使用的存储库,请不要忘记将已签名的标记 +显式推送到公共树。 + +生成拉请求时,请使用已签名的标记作为目标。这样的命令可以实现:: + + git request-pull master git://my.public.tree/linux.git my-signed-tag + +参考文献 +-------- + +Andrew Morton, "The perfect patch" (tpp). + <https://www.ozlabs.org/~akpm/stuff/tpp.txt> + +Jeff Garzik, "Linux kernel patch submission format". + <https://web.archive.org/web/20180829112450/http://linux.yyz.us/patch-format.html> + +Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer". + <http://www.kroah.com/log/linux/maintainer.html> + + <http://www.kroah.com/log/linux/maintainer-02.html> + + <http://www.kroah.com/log/linux/maintainer-03.html> + + <http://www.kroah.com/log/linux/maintainer-04.html> + + <http://www.kroah.com/log/linux/maintainer-05.html> + + <http://www.kroah.com/log/linux/maintainer-06.html> + +NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people! + <https://lkml.org/lkml/2005/7/11/336> + +Kernel Documentation/process/coding-style.rst: + :ref:`Documentation/translations/zh_CN/process/coding-style.rst <cn_codingstyle>` + +Linus Torvalds's mail on the canonical patch format: + <http://lkml.org/lkml/2005/4/7/183> + +Andi Kleen, "On submitting kernel patches" + Some strategies to get difficult or controversial changes in. + + http://halobates.de/on-submitting-patches.pdf diff --git a/Documentation/translations/zh_CN/process/volatile-considered-harmful.rst b/Documentation/translations/zh_CN/process/volatile-considered-harmful.rst new file mode 100644 index 000000000..ded3b5d0c --- /dev/null +++ b/Documentation/translations/zh_CN/process/volatile-considered-harmful.rst @@ -0,0 +1,106 @@ +.. _cn_volatile_considered_harmful: + +.. include:: ../disclaimer-zh_CN.rst + +:Original: :ref:`Documentation/process/volatile-considered-harmful.rst + <volatile_considered_harmful>` + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者:: + + 英文版维护者: Jonathan Corbet <corbet@lwn.net> + 中文版维护者: 伍鹏 Bryan Wu <bryan.wu@analog.com> + 中文版翻译者: 伍鹏 Bryan Wu <bryan.wu@analog.com> + 中文版校译者: 张汉辉 Eugene Teo <eugeneteo@kernel.sg> + 杨瑞 Dave Young <hidave.darkstar@gmail.com> + 时奎亮 Alex Shi <alex.shi@linux.alibaba.com> + +为什么不应该使用“volatile”类型 +============================== + +C程序员通常认为volatile表示某个变量可以在当前执行的线程之外被改变;因此,在内核 +中用到共享数据结构时,常常会有C程序员喜欢使用volatile这类变量。换句话说,他们经 +常会把volatile类型看成某种简易的原子变量,当然它们不是。在内核中使用volatile几 +乎总是错误的;本文档将解释为什么这样。 + +理解volatile的关键是知道它的目的是用来消除优化,实际上很少有人真正需要这样的应 +用。在内核中,程序员必须防止意外的并发访问破坏共享的数据结构,这其实是一个完全 +不同的任务。用来防止意外并发访问的保护措施,可以更加高效的避免大多数优化相关的 +问题。 + +像volatile一样,内核提供了很多原语来保证并发访问时的数据安全(自旋锁, 互斥量,内 +存屏障等等),同样可以防止意外的优化。如果可以正确使用这些内核原语,那么就没有 +必要再使用volatile。如果仍然必须使用volatile,那么几乎可以肯定在代码的某处有一 +个bug。在正确设计的内核代码中,volatile能带来的仅仅是使事情变慢。 + +思考一下这段典型的内核代码:: + + spin_lock(&the_lock); + do_something_on(&shared_data); + do_something_else_with(&shared_data); + spin_unlock(&the_lock); + +如果所有的代码都遵循加锁规则,当持有the_lock的时候,不可能意外的改变shared_data的 +值。任何可能访问该数据的其他代码都会在这个锁上等待。自旋锁原语跟内存屏障一样—— 它 +们显式的用来书写成这样 —— 意味着数据访问不会跨越它们而被优化。所以本来编译器认为 +它知道在shared_data里面将有什么,但是因为spin_lock()调用跟内存屏障一样,会强制编 +译器忘记它所知道的一切。那么在访问这些数据时不会有优化的问题。 + +如果shared_data被声名为volatile,锁操作将仍然是必须的。就算我们知道没有其他人正在 +使用它,编译器也将被阻止优化对临界区内shared_data的访问。在锁有效的同时, +shared_data不是volatile的。在处理共享数据的时候,适当的锁操作可以不再需要 +volatile —— 并且是有潜在危害的。 + +volatile的存储类型最初是为那些内存映射的I/O寄存器而定义。在内核里,寄存器访问也应 +该被锁保护,但是人们也不希望编译器“优化”临界区内的寄存器访问。内核里I/O的内存访问 +是通过访问函数完成的;不赞成通过指针对I/O内存的直接访问,并且不是在所有体系架构上 +都能工作。那些访问函数正是为了防止意外优化而写的,因此,再说一次,volatile类型不 +是必需的。 + +另一种引起用户可能使用volatile的情况是当处理器正忙着等待一个变量的值。正确执行一 +个忙等待的方法是:: + + while (my_variable != what_i_want) + cpu_relax(); + +cpu_relax()调用会降低CPU的能量消耗或者让位于超线程双处理器;它也作为内存屏障一样出 +现,所以,再一次,volatile不是必需的。当然,忙等待一开始就是一种反常规的做法。 + +在内核中,一些稀少的情况下volatile仍然是有意义的: + + - 在一些体系架构的系统上,允许直接的I/0内存访问,那么前面提到的访问函数可以使用 + volatile。基本上,每一个访问函数调用它自己都是一个小的临界区域并且保证了按照 + 程序员期望的那样发生访问操作。 + + - 某些会改变内存的内联汇编代码虽然没有什么其他明显的附作用,但是有被GCC删除的可 + 能性。在汇编声明中加上volatile关键字可以防止这种删除操作。 + + - Jiffies变量是一种特殊情况,虽然每次引用它的时候都可以有不同的值,但读jiffies + 变量时不需要任何特殊的加锁保护。所以jiffies变量可以使用volatile,但是不赞成 + 其他跟jiffies相同类型变量使用volatile。Jiffies被认为是一种“愚蠢的遗留物" + (Linus的话)因为解决这个问题比保持现状要麻烦的多。 + + - 由于某些I/0设备可能会修改连续一致的内存,所以有时,指向连续一致内存的数据结构 + 的指针需要正确的使用volatile。网络适配器使用的环状缓存区正是这类情形的一个例 + 子,其中适配器用改变指针来表示哪些描述符已经处理过了。 + +对于大多代码,上述几种可以使用volatile的情况都不适用。所以,使用volatile是一种 +bug并且需要对这样的代码额外仔细检查。那些试图使用volatile的开发人员需要退一步想想 +他们真正想实现的是什么。 + +非常欢迎删除volatile变量的补丁 - 只要证明这些补丁完整的考虑了并发问题。 + +注释 +---- + +[1] https://lwn.net/Articles/233481/ +[2] https://lwn.net/Articles/233482/ + +致谢 +---- + +最初由Randy Dunlap推动并作初步研究 +由Jonathan Corbet撰写 +参考Satyam Sharma,Johannes Stezenbach,Jesper Juhl,Heikki Orsila, +H. Peter Anvin,Philipp Hahn和Stefan Richter的意见改善了本档。 diff --git a/Documentation/translations/zh_CN/sparse.txt b/Documentation/translations/zh_CN/sparse.txt new file mode 100644 index 000000000..0f444b83d --- /dev/null +++ b/Documentation/translations/zh_CN/sparse.txt @@ -0,0 +1,91 @@ +Chinese translated version of Documentation/dev-tools/sparse.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Chinese maintainer: Li Yang <leoyang.li@nxp.com> +--------------------------------------------------------------------- +Documentation/dev-tools/sparse.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +中文版维护者: 李阳 Li Yang <leoyang.li@nxp.com> +中文版翻译者: 李阳 Li Yang <leoyang.li@nxp.com> + + +以下为正文 +--------------------------------------------------------------------- + +Copyright 2004 Linus Torvalds +Copyright 2004 Pavel Machek <pavel@ucw.cz> +Copyright 2006 Bob Copeland <me@bobcopeland.com> + +使用 sparse 工具做类型检查 +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +"__bitwise" 是一种类型属性,所以你应该这样使用它: + + typedef int __bitwise pm_request_t; + + enum pm_request { + PM_SUSPEND = (__force pm_request_t) 1, + PM_RESUME = (__force pm_request_t) 2 + }; + +这样会使 PM_SUSPEND 和 PM_RESUME 成为位方式(bitwise)整数(使用"__force" +是因为 sparse 会抱怨改变位方式的类型转换,但是这里我们确实需要强制进行转 +换)。而且因为所有枚举值都使用了相同的类型,这里的"enum pm_request"也将 +会使用那个类型做为底层实现。 + +而且使用 gcc 编译的时候,所有的 __bitwise/__force 都会消失,最后在 gcc +看来它们只不过是普通的整数。 + +坦白来说,你并不需要使用枚举类型。上面那些实际都可以浓缩成一个特殊的"int +__bitwise"类型。 + +所以更简单的办法只要这样做: + + typedef int __bitwise pm_request_t; + + #define PM_SUSPEND ((__force pm_request_t) 1) + #define PM_RESUME ((__force pm_request_t) 2) + +现在你就有了严格的类型检查所需要的所有基础架构。 + +一个小提醒:常数整数"0"是特殊的。你可以直接把常数零当作位方式整数使用而 +不用担心 sparse 会抱怨。这是因为"bitwise"(恰如其名)是用来确保不同位方 +式类型不会被弄混(小尾模式,大尾模式,cpu尾模式,或者其他),对他们来说 +常数"0"确实是特殊的。 + +获取 sparse 工具 +~~~~~~~~~~~~~~~~ + +你可以从 Sparse 的主页获取最新的发布版本: + + http://www.kernel.org/pub/linux/kernel/people/josh/sparse/ + +或者,你也可以使用 git 克隆最新的 sparse 开发版本: + + git://git.kernel.org/pub/scm/linux/kernel/git/josh/sparse.git + +一旦你下载了源码,只要以普通用户身份运行: + + make + make install + +它将会被自动安装到你的 ~/bin 目录下。 + +使用 sparse 工具 +~~~~~~~~~~~~~~~~ + +用"make C=1"命令来编译内核,会对所有重新编译的 C 文件使用 sparse 工具。 +或者使用"make C=2"命令,无论文件是否被重新编译都会对其使用 sparse 工具。 +如果你已经编译了内核,用后一种方式可以很快地检查整个源码树。 + +make 的可选变量 CHECKFLAGS 可以用来向 sparse 工具传递参数。编译系统会自 +动向 sparse 工具传递 -Wbitwise 参数。 diff --git a/Documentation/translations/zh_CN/video4linux/omap3isp.txt b/Documentation/translations/zh_CN/video4linux/omap3isp.txt new file mode 100644 index 000000000..75e481985 --- /dev/null +++ b/Documentation/translations/zh_CN/video4linux/omap3isp.txt @@ -0,0 +1,277 @@ +Chinese translated version of Documentation/admin-guide/media/omap3isp.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + Sakari Ailus <sakari.ailus@iki.fi> + David Cohen <dacohen@gmail.com> +Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> +--------------------------------------------------------------------- +Documentation/admin-guide/media/omap3isp.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 +英文版维护者: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + Sakari Ailus <sakari.ailus@iki.fi> + David Cohen <dacohen@gmail.com> +中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- +OMAP 3 图像信号处理器 (ISP) 驱动 + +Copyright (C) 2010 Nokia Corporation +Copyright (C) 2009 Texas Instruments, Inc. + +联系人: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + Sakari Ailus <sakari.ailus@iki.fi> + David Cohen <dacohen@gmail.com> + + +介绍 +=== + +本文档介绍了由 drivers/media/video/omap3isp 加载的德州仪器 +(TI)OMAP 3 图像信号处理器 (ISP) 驱动。原始驱动由德州仪器(TI) +编写,但此后由诺基亚重写了两次。 + +驱动已在以下 OMAP 3 系列的芯片中成功使用: + + 3430 + 3530 + 3630 + +驱动实现了 V4L2、媒体控制器和 v4l2_subdev 接口。支持内核中使用 +v4l2_subdev 接口的传感器、镜头和闪光灯驱动。 + + +拆分为子设备 +========== + +OMAP 3 ISP 被拆分为 V4L2 子设备,ISP中的每个模块都由一个子设备 +来表示。每个子设备向用户空间提供一个 V4L2 子设备接口。 + + OMAP3 ISP CCP2 + OMAP3 ISP CSI2a + OMAP3 ISP CCDC + OMAP3 ISP preview + OMAP3 ISP resizer + OMAP3 ISP AEWB + OMAP3 ISP AF + OMAP3 ISP histogram + +ISP 中每个可能的连接都通过一个链接嵌入到媒体控制器接口中。详见例程 [2]。 + + +控制 OMAP 3 ISP +============== + +通常,对 OMAP 3 ISP 的配置会在下一帧起始时生效。在传感器垂直消隐期间, +模块变为空闲时完成配置。在内存到内存的操作中,视频管道一次处理一帧。 +应用配置应在帧间完成。 + +ISP 中的所有模块,除 CSI-2 和 (可能存在的)CCP2 接收器外,都必须 +接收完整的帧数据。因此,传感器必须保证从不发送部分帧数据给ISP。 + +Autoidle(自动空闲)功能至少在 3430 的 ISP 模块中确实存在一些问题。 +当 omap3isp 模块参数 autoidle 非零时,autoidle(自动空闲)功能 +仅在 3630 中启用了。 + + +事件机制 +====== + +OMAP 3 ISP 驱动在 CCDC 和统计(AEWB、AF 和 直方图)子设备中支持 +V4L2 事件机制接口。 + +CCDC 子设备通过 HS_VS 中断,处理 V4L2_EVENT_FRAME_SYNC 类型 +事件,用于告知帧起始。早期版本的驱动则使用 V4L2_EVENT_OMAP3ISP_HS_VS。 +当在 CCDC 模块中接收到起始帧的第一行时,会准确地触发事件。这个事件 +可以在 CCDC 子设备中“订阅”。 + +(当使用并行接口时,必须注意正确地配置 VS 信号极性。而当使用串行接收时 +这个会自动校正。) + +每个统计子设备都可以产生事件。每当一个统计缓冲区可由用户空间应用程序 +通过 VIDIOC_OMAP3ISP_STAT_REQ IOCTL 操作获取时,就会产生一个 +事件。当前存在以下事件: + + V4L2_EVENT_OMAP3ISP_AEWB + V4L2_EVENT_OMAP3ISP_AF + V4L2_EVENT_OMAP3ISP_HIST + +这些 ioctl 的事件数据类型为 struct omap3isp_stat_event_status +结构体。如果出现计算错误的统计,也同样会产生一个事件,但没有相关的统计 +数据缓冲区。这种情况下 omap3isp_stat_event_status.buf_err 会被 +设置为非零值。 + + +私有 IOCTL +========== + +OMAP 3 ISP 驱动支持标准的 V4L2 IOCTL 以及可能存在且实用的控制。但 +ISP 提供的许多功能都不在标准 IOCTL 之列,例如 gamma(伽马)表和统计 +数据采集配置等。 + +通常,会有一个私有 ioctl 用于配置每个包含硬件依赖功能的模块。 + +支持以下私有 IOCTL: + + VIDIOC_OMAP3ISP_CCDC_CFG + VIDIOC_OMAP3ISP_PRV_CFG + VIDIOC_OMAP3ISP_AEWB_CFG + VIDIOC_OMAP3ISP_HIST_CFG + VIDIOC_OMAP3ISP_AF_CFG + VIDIOC_OMAP3ISP_STAT_REQ + VIDIOC_OMAP3ISP_STAT_EN + +在 include/linux/omap3isp.h 中描述了这些 ioctl 使用的参数结构体。 +与特定 ISP 模块相关的 ISP 自身的详细功能在技术参考手册 (TRMs)中有 +描述,详见文档结尾。 + +虽然在不使用任何私有 IOCTL 的情况下使用 ISP 驱动是可能的,但这样无法 +获得最佳的图像质量。AEWB、AF 和 直方图(译者注:一般用于自动曝光和增益 +控制,以及图像均衡等)模块无法在未使用适当的私有 IOCTL 配置的情况下使用。 + + +CCDC 和 preview(预览)模块 IOCTL +=============================== + +VIDIOC_OMAP3ISP_CCDC_CFG 和 VIDIOC_OMAP3ISP_PRV_CFG IOCTL +被分别用于配置、启用和禁用 CCDC 和 preview(预览)模块的功能。在它们 +所控制的模块中,两个 IOCTL 控制多种功能。VIDIOC_OMAP3ISP_CCDC_CFG IOCTL +接受一个指向 omap3isp_ccdc_update_config 结构体的指针作为它的参数。 +同样的,VIDIOC_OMAP3ISP_PRV_CFG 接受一个指向 omap3isp_prev_update_config +结构体的指针。以上两个结构体定义位于 [1]。 + +这些结构体中的 update 域标识是否针对指定的功能更新配置,而 flag 域 +则标识是启用还是禁用此功能。 + +update 和 flag 位接受以下掩码值。CCDC 和 preview(预览)模块的 +每个单独功能都与一个 flag 关联(禁用或启用;在结构体中 flag 域的 +一部分)和一个指向功能配置数据的指针。 + +对于 VIDIOC_OMAP3ISP_CCDC_CFG,下面列出了 update 和 flag 域 +中的有效值。 这些值可能会在同一个 IOCTL 调用中配置多个功能。 + + OMAP3ISP_CCDC_ALAW + OMAP3ISP_CCDC_LPF + OMAP3ISP_CCDC_BLCLAMP + OMAP3ISP_CCDC_BCOMP + OMAP3ISP_CCDC_FPC + OMAP3ISP_CCDC_CULL + OMAP3ISP_CCDC_CONFIG_LSC + OMAP3ISP_CCDC_TBL_LSC + +针对 VIDIOC_OMAP3ISP_PRV_CFG 的相应值如下: + + OMAP3ISP_PREV_LUMAENH + OMAP3ISP_PREV_INVALAW + OMAP3ISP_PREV_HRZ_MED + OMAP3ISP_PREV_CFA + OMAP3ISP_PREV_CHROMA_SUPP + OMAP3ISP_PREV_WB + OMAP3ISP_PREV_BLKADJ + OMAP3ISP_PREV_RGB2RGB + OMAP3ISP_PREV_COLOR_CONV + OMAP3ISP_PREV_YC_LIMIT + OMAP3ISP_PREV_DEFECT_COR + OMAP3ISP_PREV_GAMMABYPASS + OMAP3ISP_PREV_DRK_FRM_CAPTURE + OMAP3ISP_PREV_DRK_FRM_SUBTRACT + OMAP3ISP_PREV_LENS_SHADING + OMAP3ISP_PREV_NF + OMAP3ISP_PREV_GAMMA + +在启用某个功能的时候,相关的配置数据指针不可为 NULL。在禁用某个功能时, +配置数据指针会被忽略。 + + +统计模块 IOCTL +============= + +统计子设备相较于其他子设备提供了更多动态配置选项。在图像处理流水线处于 +工作状态时,它们可以被启用、禁用和重配。 + +统计模块总是从 CCDC 中获取输入的图像数据(由于直方图内存读取未实现)。 +统计数据可由用户通过统计子设备节点使用私有 IOCTL 获取。 + +AEWB、AF 和 直方图子设备提供的私有 IOCTL 极大程度上反应了 ISP 硬件 +提供的寄存器级接口。有些方面纯粹和驱动程序的实现相关,这些将在下面讨论。 + +VIDIOC_OMAP3ISP_STAT_EN +----------------------- + +这个私有 IOCTL 启用/禁用 一个统计模块。如果这个申请在视频流启动前完成, +它将在视频流水线开始工作时生效。如果视频流水线已经处于工作状态了,它将在 +CCDC 变为空闲时生效。 + +VIDIOC_OMAP3ISP_AEWB_CFG, VIDIOC_OMAP3ISP_HIST_CFG and VIDIOC_OMAP3ISP_AF_CFG +----------------------------------------------------------------------------- + +这些 IOCTL 用于配置模块。它们要求用户应用程序对硬件有深入的认识。对 +大多数域的解释可以在 OMAP 的 TRM 中找到。以下两个域对于以上所有的 +私有 IOCTL 配置都很常见,由于他们没有在 TRM 中提及,故需要对其有 +更好的认识。 + +omap3isp_[h3a_af/h3a_aewb/hist]_config.buf_size: + +模块在内部处理自身缓冲。对模块数据输出所必需的缓存大小依赖于已申请的配置。 +虽然驱动支持在视频流工作时重新配置,但对于所需缓存量大于模块启用时内部 +所分配数量的情况,则不支持重新配置。在这种情况下将返回 -EBUSY。为了避免 +此类状况,无论是禁用/重配/启用模块,还是第一次配置时申请必须的缓存大小, +都应在模块禁用的情况下进行。 + +内部缓冲分配的大小需综合考虑所申请配置的最小缓存量以及 buf_size 域中 +所设的值。如果 buf_size 域在[minimum(最小值), maximum(最大值)] +缓冲大小范围之外,则应该将其调整到其范围中。驱动则会选择最大值。正确的 +buf_size 值将回写到用户应用程序中。 + +omap3isp_[h3a_af/h3a_aewb/hist]_config.config_counter: + +由于配置并未在申请之后同步生效,驱动必须提供一个跟踪这类信息的方法, +以提供更准确的数据。在一个配置被申请之后,返回到用户空间应用程序的 +config_counter 是一个与其配置相关的唯一值。当用户应用程序接收到 +一个缓冲可用或一个新的缓冲申请事件时,这个 config_counter 用于 +一个缓冲数据和一个配置的匹配。 + +VIDIOC_OMAP3ISP_STAT_REQ +------------------------ + +将内部缓冲队列中最早的数据发送到用户空间,然后丢弃此缓冲区。 +omap3isp_stat_data.frame_number 域与视频缓冲的 field_count +域相匹配。 + + +技术参考手册 (TRMs) 和其他文档 +========================== + +OMAP 3430 TRM: +<URL:http://focus.ti.com/pdfs/wtbu/OMAP34xx_ES3.1.x_PUBLIC_TRM_vZM.zip> +参考于 2011-03-05. + +OMAP 35xx TRM: +<URL:http://www.ti.com/litv/pdf/spruf98o> 参考于 2011-03-05. + +OMAP 3630 TRM: +<URL:http://focus.ti.com/pdfs/wtbu/OMAP36xx_ES1.x_PUBLIC_TRM_vQ.zip> +参考于 2011-03-05. + +DM 3730 TRM: +<URL:http://www.ti.com/litv/pdf/sprugn4h> 参考于 2011-03-06. + + +参考资料 +======= + +[1] include/linux/omap3isp.h + +[2] http://git.ideasonboard.org/?p=media-ctl.git;a=summary diff --git a/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt b/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt new file mode 100644 index 000000000..a88fcbc11 --- /dev/null +++ b/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt @@ -0,0 +1,976 @@ +Chinese translated version of Documentation/driver-api/media/index.rst + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Mauro Carvalho Chehab <mchehab@kernel.org> +Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> +--------------------------------------------------------------------- +Documentation/driver-api/media/index.rst 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 +英文版维护者: Mauro Carvalho Chehab <mchehab@kernel.org> +中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> +中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> + + +以下为正文 +--------------------------------------------------------------------- +V4L2 驱动框架概览 +============== + +本文档描述 V4L2 框架所提供的各种结构和它们之间的关系。 + + +介绍 +---- + +大部分现代 V4L2 设备由多个 IC 组成,在 /dev 下导出多个设备节点, +并同时创建非 V4L2 设备(如 DVB、ALSA、FB、I2C 和红外输入设备)。 +由于这种硬件的复杂性,V4L2 驱动也变得非常复杂。 + +尤其是 V4L2 必须支持 IC 实现音视频的多路复用和编解码,这就更增加了其 +复杂性。通常这些 IC 通过一个或多个 I2C 总线连接到主桥驱动器,但也可 +使用其他总线。这些设备称为“子设备”。 + +长期以来,这个框架仅限于通过 video_device 结构体创建 V4L 设备节点, +并使用 video_buf 处理视频缓冲(注:本文不讨论 video_buf 框架)。 + +这意味着所有驱动必须自己设置设备实例并连接到子设备。其中一部分要正确地 +完成是比较复杂的,使得许多驱动都没有正确地实现。 + +由于框架的缺失,有很多通用代码都不可重复利用。 + +因此,这个框架构建所有驱动都需要的基本结构块,而统一的框架将使通用代码 +创建成实用函数并在所有驱动中共享变得更加容易。 + + +驱动结构 +------- + +所有 V4L2 驱动都有如下结构: + +1) 每个设备实例的结构体--包含其设备状态。 + +2) 初始化和控制子设备的方法(如果有)。 + +3) 创建 V4L2 设备节点 (/dev/videoX、/dev/vbiX 和 /dev/radioX) + 并跟踪设备节点的特定数据。 + +4) 特定文件句柄结构体--包含每个文件句柄的数据。 + +5) 视频缓冲处理。 + +以下是它们的初略关系图: + + device instances(设备实例) + | + +-sub-device instances(子设备实例) + | + \-V4L2 device nodes(V4L2 设备节点) + | + \-filehandle instances(文件句柄实例) + + +框架结构 +------- + +该框架非常类似驱动结构:它有一个 v4l2_device 结构用于保存设备 +实例的数据;一个 v4l2_subdev 结构体代表子设备实例;video_device +结构体保存 V4L2 设备节点的数据;将来 v4l2_fh 结构体将跟踪文件句柄 +实例(暂未尚未实现)。 + +V4L2 框架也可与媒体框架整合(可选的)。如果驱动设置了 v4l2_device +结构体的 mdev 域,子设备和视频节点的入口将自动出现在媒体框架中。 + + +v4l2_device 结构体 +---------------- + +每个设备实例都通过 v4l2_device (v4l2-device.h)结构体来表示。 +简单设备可以仅分配这个结构体,但在大多数情况下,都会将这个结构体 +嵌入到一个更大的结构体中。 + +你必须注册这个设备实例: + + v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); + +注册操作将会初始化 v4l2_device 结构体。如果 dev->driver_data 域 +为 NULL,就将其指向 v4l2_dev。 + +需要与媒体框架整合的驱动必须手动设置 dev->driver_data,指向包含 +v4l2_device 结构体实例的驱动特定设备结构体。这可以在注册 V4L2 设备 +实例前通过 dev_set_drvdata() 函数完成。同时必须设置 v4l2_device +结构体的 mdev 域,指向适当的初始化并注册过的 media_device 实例。 + +如果 v4l2_dev->name 为空,则它将被设置为从 dev 中衍生出的值(为了 +更加精确,形式为驱动名后跟 bus_id)。如果你在调用 v4l2_device_register +前已经设置好了,则不会被修改。如果 dev 为 NULL,则你*必须*在调用 +v4l2_device_register 前设置 v4l2_dev->name。 + +你可以基于驱动名和驱动的全局 atomic_t 类型的实例编号,通过 +v4l2_device_set_name() 设置 name。这样会生成类似 ivtv0、ivtv1 等 +名字。若驱动名以数字结尾,则会在编号和驱动名间插入一个破折号,如: +cx18-0、cx18-1 等。此函数返回实例编号。 + +第一个 “dev” 参数通常是一个指向 pci_dev、usb_interface 或 +platform_device 的指针。很少使其为 NULL,除非是一个ISA设备或者 +当一个设备创建了多个 PCI 设备,使得 v4l2_dev 无法与一个特定的父设备 +关联。 + +你也可以提供一个 notify() 回调,使子设备可以调用它实现事件通知。 +但这个设置与子设备相关。子设备支持的任何通知必须在 +include/media/<subdevice>.h 中定义一个消息头。 + +注销 v4l2_device 使用如下函数: + + v4l2_device_unregister(struct v4l2_device *v4l2_dev); + +如果 dev->driver_data 域指向 v4l2_dev,将会被重置为 NULL。注销同时 +会自动从设备中注销所有子设备。 + +如果你有一个热插拔设备(如USB设备),则当断开发生时,父设备将无效。 +由于 v4l2_device 有一个指向父设备的指针必须被清除,同时标志父设备 +已消失,所以必须调用以下函数: + + v4l2_device_disconnect(struct v4l2_device *v4l2_dev); + +这个函数并*不*注销子设备,因此你依然要调用 v4l2_device_unregister() +函数。如果你的驱动器并非热插拔的,就没有必要调用 v4l2_device_disconnect()。 + +有时你需要遍历所有被特定驱动注册的设备。这通常发生在多个设备驱动使用 +同一个硬件的情况下。如:ivtvfb 驱动是一个使用 ivtv 硬件的帧缓冲驱动, +同时 alsa 驱动也使用此硬件。 + +你可以使用如下例程遍历所有注册的设备: + +static int callback(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + + /* 测试这个设备是否已经初始化 */ + if (v4l2_dev == NULL) + return 0; + ... + return 0; +} + +int iterate(void *p) +{ + struct device_driver *drv; + int err; + + /* 在PCI 总线上查找ivtv驱动。 + pci_bus_type是全局的. 对于USB总线使用usb_bus_type。 */ + drv = driver_find("ivtv", &pci_bus_type); + /* 遍历所有的ivtv设备实例 */ + err = driver_for_each_device(drv, NULL, p, callback); + put_driver(drv); + return err; +} + +有时你需要一个设备实例的运行计数。这个通常用于映射一个设备实例到一个 +模块选择数组的索引。 + +推荐方法如下: + +static atomic_t drv_instance = ATOMIC_INIT(0); + +static int drv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +{ + ... + state->instance = atomic_inc_return(&drv_instance) - 1; +} + +如果你有多个设备节点,对于热插拔设备,知道何时注销 v4l2_device 结构体 +就比较困难。为此 v4l2_device 有引用计数支持。当调用 video_register_device +时增加引用计数,而设备节点释放时减小引用计数。当引用计数为零,则 +v4l2_device 的release() 回调将被执行。你就可以在此时做最后的清理工作。 + +如果创建了其他设备节点(比如 ALSA),则你可以通过以下函数手动增减 +引用计数: + +void v4l2_device_get(struct v4l2_device *v4l2_dev); + +或: + +int v4l2_device_put(struct v4l2_device *v4l2_dev); + +由于引用技术初始化为 1 ,你也需要在 disconnect() 回调(对于 USB 设备)中 +调用 v4l2_device_put,或者 remove() 回调(例如对于 PCI 设备),否则 +引用计数将永远不会为 0 。 + +v4l2_subdev结构体 +------------------ + +许多驱动需要与子设备通信。这些设备可以完成各种任务,但通常他们负责 +音视频复用和编解码。如网络摄像头的子设备通常是传感器和摄像头控制器。 + +这些一般为 I2C 接口设备,但并不一定都是。为了给驱动提供调用子设备的 +统一接口,v4l2_subdev 结构体(v4l2-subdev.h)产生了。 + +每个子设备驱动都必须有一个 v4l2_subdev 结构体。这个结构体可以单独 +代表一个简单的子设备,也可以嵌入到一个更大的结构体中,与更多设备状态 +信息保存在一起。通常有一个下级设备结构体(比如:i2c_client)包含了 +内核创建的设备数据。建议使用 v4l2_set_subdevdata() 将这个结构体的 +指针保存在 v4l2_subdev 的私有数据域(dev_priv)中。这使得通过 v4l2_subdev +找到实际的低层总线特定设备数据变得容易。 + +你同时需要一个从低层结构体获取 v4l2_subdev 指针的方法。对于常用的 +i2c_client 结构体,i2c_set_clientdata() 函数可用于保存一个 v4l2_subdev +指针;对于其他总线你可能需要使用其他相关函数。 + +桥驱动中也应保存每个子设备的私有数据,比如一个指向特定桥的各设备私有 +数据的指针。为此 v4l2_subdev 结构体提供主机私有数据域(host_priv), +并可通过 v4l2_get_subdev_hostdata() 和 v4l2_set_subdev_hostdata() +访问。 + +从总线桥驱动的视角,驱动加载子设备模块并以某种方式获得 v4l2_subdev +结构体指针。对于 i2c 总线设备相对简单:调用 i2c_get_clientdata()。 +对于其他总线也需要做类似的操作。针对 I2C 总线上的子设备辅助函数帮你 +完成了大部分复杂的工作。 + +每个 v4l2_subdev 都包含子设备驱动需要实现的函数指针(如果对此设备 +不适用,可为NULL)。由于子设备可完成许多不同的工作,而在一个庞大的 +函数指针结构体中通常仅有少数有用的函数实现其功能肯定不合适。所以, +函数指针根据其实现的功能被分类,每一类都有自己的函数指针结构体。 + +顶层函数指针结构体包含了指向各类函数指针结构体的指针,如果子设备驱动 +不支持该类函数中的任何一个功能,则指向该类结构体的指针为NULL。 + +这些结构体定义如下: + +struct v4l2_subdev_core_ops { + int (*log_status)(struct v4l2_subdev *sd); + int (*init)(struct v4l2_subdev *sd, u32 val); + ... +}; + +struct v4l2_subdev_tuner_ops { + ... +}; + +struct v4l2_subdev_audio_ops { + ... +}; + +struct v4l2_subdev_video_ops { + ... +}; + +struct v4l2_subdev_pad_ops { + ... +}; + +struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; + const struct v4l2_subdev_tuner_ops *tuner; + const struct v4l2_subdev_audio_ops *audio; + const struct v4l2_subdev_video_ops *video; + const struct v4l2_subdev_pad_ops *video; +}; + +其中 core(核心)函数集通常可用于所有子设备,其他类别的实现依赖于 +子设备。如视频设备可能不支持音频操作函数,反之亦然。 + +这样的设置在限制了函数指针数量的同时,还使增加新的操作函数和分类 +变得较为容易。 + +子设备驱动可使用如下函数初始化 v4l2_subdev 结构体: + + v4l2_subdev_init(sd, &ops); + +然后,你必须用一个唯一的名字初始化 subdev->name,并初始化模块的 +owner 域。若使用 i2c 辅助函数,这些都会帮你处理好。 + +若需同媒体框架整合,你必须调用 media_entity_pads_init() 初始化 v4l2_subdev +结构体中的 media_entity 结构体(entity 域): + + struct media_pad *pads = &my_sd->pads; + int err; + + err = media_entity_pads_init(&sd->entity, npads, pads); + +pads 数组必须预先初始化。无须手动设置 media_entity 的 type 和 +name 域,但如有必要,revision 域必须初始化。 + +当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。 + +在子设备被注销之后,不要忘记清理 media_entity 结构体: + + media_entity_cleanup(&sd->entity); + +如果子设备驱动趋向于处理视频并整合进了媒体框架,必须使用 v4l2_subdev_pad_ops +替代 v4l2_subdev_video_ops 实现格式相关的功能。 + +这种情况下,子设备驱动应该设置 link_validate 域,以提供它自身的链接 +验证函数。链接验证函数应对管道(两端链接的都是 V4L2 子设备)中的每个 +链接调用。驱动还要负责验证子设备和视频节点间格式配置的正确性。 + +如果 link_validate 操作没有设置,默认的 v4l2_subdev_link_validate_default() +函数将会被调用。这个函数保证宽、高和媒体总线像素格式在链接的收发两端 +都一致。子设备驱动除了它们自己的检测外,也可以自由使用这个函数以执行 +上面提到的检查。 + +设备(桥)驱动程序必须向 v4l2_device 注册 v4l2_subdev: + + int err = v4l2_device_register_subdev(v4l2_dev, sd); + +如果子设备模块在它注册前消失,这个操作可能失败。在这个函数成功返回后, +subdev->dev 域就指向了 v4l2_device。 + +如果 v4l2_device 父设备的 mdev 域为非 NULL 值,则子设备实体将被自动 +注册为媒体设备。 + +注销子设备则可用如下函数: + + v4l2_device_unregister_subdev(sd); + +此后,子设备模块就可卸载,且 sd->dev == NULL。 + +注册之设备后,可通过以下方式直接调用其操作函数: + + err = sd->ops->core->g_std(sd, &norm); + +但使用如下宏会比较容易且合适: + + err = v4l2_subdev_call(sd, core, g_std, &norm); + +这个宏将会做 NULL 指针检查,如果 subdev 为 NULL,则返回-ENODEV;如果 +subdev->core 或 subdev->core->g_std 为 NULL,则返回 -ENOIOCTLCMD; +否则将返回 subdev->ops->core->g_std ops 调用的实际结果。 + +有时也可能同时调用所有或一系列子设备的某个操作函数: + + v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm); + +任何不支持此操作的子设备都会被跳过,并忽略错误返回值。但如果你需要 +检查出错码,则可使用如下函数: + + err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm); + +除 -ENOIOCTLCMD 外的任何错误都会跳出循环并返回错误值。如果(除 -ENOIOCTLCMD +外)没有错误发生,则返回 0。 + +对于以上两个函数的第二个参数为组 ID。如果为 0,则所有子设备都会执行 +这个操作。如果为非 0 值,则只有那些组 ID 匹配的子设备才会执行此操作。 +在桥驱动注册一个子设备前,可以设置 sd->grp_id 为任何期望值(默认值为 +0)。这个值属于桥驱动,且子设备驱动将不会修改和使用它。 + +组 ID 赋予了桥驱动更多对于如何调用回调的控制。例如,电路板上有多个 +音频芯片,每个都有改变音量的能力。但当用户想要改变音量的时候,通常 +只有一个会被实际使用。你可以对这样的子设备设置组 ID 为(例如 AUDIO_CONTROLLER) +并在调用 v4l2_device_call_all() 时指定它为组 ID 值。这就保证了只有 +需要的子设备才会执行这个回调。 + +如果子设备需要通知它的 v4l2_device 父设备一个事件,可以调用 +v4l2_subdev_notify(sd, notification, arg)。这个宏检查是否有一个 +notify() 回调被注册,如果没有,返回 -ENODEV。否则返回 notify() 调用 +结果。 + +使用 v4l2_subdev 的好处在于它是一个通用结构体,且不包含任何底层硬件 +信息。所有驱动可以包含多个 I2C 总线的子设备,但也有子设备是通过 GPIO +控制。这个区别仅在配置设备时有关系,一旦子设备注册完成,对于 v4l2 +子系统来说就完全透明了。 + + +V4L2 子设备用户空间API +-------------------- + +除了通过 v4l2_subdev_ops 结构导出的内核 API,V4L2 子设备也可以直接 +通过用户空间应用程序来控制。 + +可以在 /dev 中创建名为 v4l-subdevX 设备节点,以通过其直接访问子设备。 +如果子设备支持用户空间直接配置,必须在注册前设置 V4L2_SUBDEV_FL_HAS_DEVNODE +标志。 + +注册子设备之后, v4l2_device 驱动会通过调用 v4l2_device_register_subdev_nodes() +函数为所有已注册并设置了 V4L2_SUBDEV_FL_HAS_DEVNODE 的子设备创建 +设备节点。这些设备节点会在子设备注销时自动删除。 + +这些设备节点处理 V4L2 API 的一个子集。 + +VIDIOC_QUERYCTRL +VIDIOC_QUERYMENU +VIDIOC_G_CTRL +VIDIOC_S_CTRL +VIDIOC_G_EXT_CTRLS +VIDIOC_S_EXT_CTRLS +VIDIOC_TRY_EXT_CTRLS + + 这些 ioctls 控制与 V4L2 中定义的一致。他们行为相同,唯一的 + 不同是他们只处理子设备的控制实现。根据驱动程序,这些控制也 + 可以通过一个(或多个) V4L2 设备节点访问。 + +VIDIOC_DQEVENT +VIDIOC_SUBSCRIBE_EVENT +VIDIOC_UNSUBSCRIBE_EVENT + + 这些 ioctls 事件与 V4L2 中定义的一致。他们行为相同,唯一的 + 不同是他们只处理子设备产生的事件。根据驱动程序,这些事件也 + 可以通过一个(或多个) V4L2 设备节点上报。 + + 要使用事件通知的子设备驱动,在注册子设备前必须在 v4l2_subdev::flags + 中设置 V4L2_SUBDEV_USES_EVENTS 并在 v4l2_subdev::nevents + 中初始化事件队列深度。注册完成后,事件会在 v4l2_subdev::devnode + 设备节点中像通常一样被排队。 + + 为正确支持事件机制,poll() 文件操作也应被实现。 + +私有 ioctls + + 不在以上列表中的所有 ioctls 会通过 core::ioctl 操作直接传递 + 给子设备驱动。 + + +I2C 子设备驱动 +------------- + +由于这些驱动很常见,所以内特提供了特定的辅助函数(v4l2-common.h)让这些 +设备的使用更加容易。 + +添加 v4l2_subdev 支持的推荐方法是让 I2C 驱动将 v4l2_subdev 结构体 +嵌入到为每个 I2C 设备实例创建的状态结构体中。而最简单的设备没有状态 +结构体,此时可以直接创建一个 v4l2_subdev 结构体。 + +一个典型的状态结构体如下所示(‘chipname’用芯片名代替): + +struct chipname_state { + struct v4l2_subdev sd; + ... /* 附加的状态域*/ +}; + +初始化 v4l2_subdev 结构体的方法如下: + + v4l2_i2c_subdev_init(&state->sd, client, subdev_ops); + +这个函数将填充 v4l2_subdev 结构体中的所有域,并保证 v4l2_subdev 和 +i2c_client 都指向彼此。 + +同时,你也应该为从 v4l2_subdev 指针找到 chipname_state 结构体指针 +添加一个辅助内联函数。 + +static inline struct chipname_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct chipname_state, sd); +} + +使用以下函数可以通过 v4l2_subdev 结构体指针获得 i2c_client 结构体 +指针: + + struct i2c_client *client = v4l2_get_subdevdata(sd); + +而以下函数则相反,通过 i2c_client 结构体指针获得 v4l2_subdev 结构体 +指针: + + struct v4l2_subdev *sd = i2c_get_clientdata(client); + +当 remove()函数被调用前,必须保证先调用 v4l2_device_unregister_subdev(sd)。 +此操作将会从桥驱动中注销子设备。即使子设备没有注册,调用此函数也是 +安全的。 + +必须这样做的原因是:当桥驱动注销 i2c 适配器时,remove()回调函数 +会被那个适配器上的 i2c 设备调用。此后,相应的 v4l2_subdev 结构体 +就不存在了,所有它们必须先被注销。在 remove()回调函数中调用 +v4l2_device_unregister_subdev(sd),可以保证执行总是正确的。 + + +桥驱动也有一些辅组函数可用: + +struct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter, + "module_foo", "chipid", 0x36, NULL); + +这个函数会加载给定的模块(如果没有模块需要加载,可以为 NULL), +并用给定的 i2c 适配器结构体指针(i2c_adapter)和 器件地址(chip/address) +作为参数调用 i2c_new_client_device()。如果一切顺利,则就在 v4l2_device +中注册了子设备。 + +你也可以利用 v4l2_i2c_new_subdev()的最后一个参数,传递一个可能的 +I2C 地址数组,让函数自动探测。这些探测地址只有在前一个参数为 0 的 +情况下使用。非零参数意味着你知道准确的 i2c 地址,所以此时无须进行 +探测。 + +如果出错,两个函数都返回 NULL。 + +注意:传递给 v4l2_i2c_new_subdev()的 chipid 通常与模块名一致。 +它允许你指定一个芯片的变体,比如“saa7114”或“saa7115”。一般通过 +i2c 驱动自动探测。chipid 的使用是在今后需要深入了解的事情。这个与 +i2c 驱动不同,较容易混淆。要知道支持哪些芯片变体,你可以查阅 i2c +驱动代码的 i2c_device_id 表,上面列出了所有可能支持的芯片。 + +还有两个辅助函数: + +v4l2_i2c_new_subdev_cfg:这个函数添加新的 irq 和 platform_data +参数,并有‘addr’和‘probed_addrs’参数:如果 addr 非零,则被使用 +(不探测变体),否则 probed_addrs 中的地址将用于自动探测。 + +例如:以下代码将会探测地址(0x10): + +struct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter, + "module_foo", "chipid", 0, NULL, 0, I2C_ADDRS(0x10)); + +v4l2_i2c_new_subdev_board 使用一个 i2c_board_info 结构体,将其 +替代 irq、platform_data 和 add r参数传递给 i2c 驱动。 + +如果子设备支持 s_config 核心操作,这个操作会在子设备配置好之后以 irq 和 +platform_data 为参数调用。早期的 v4l2_i2c_new_(probed_)subdev 函数 +同样也会调用 s_config,但仅在 irq 为 0 且 platform_data 为 NULL 时。 + +video_device结构体 +----------------- + +在 /dev 目录下的实际设备节点根据 video_device 结构体(v4l2-dev.h) +创建。此结构体既可以动态分配也可以嵌入到一个更大的结构体中。 + +动态分配方法如下: + + struct video_device *vdev = video_device_alloc(); + + if (vdev == NULL) + return -ENOMEM; + + vdev->release = video_device_release; + +如果将其嵌入到一个大结构体中,则必须自己实现 release()回调。 + + struct video_device *vdev = &my_vdev->vdev; + + vdev->release = my_vdev_release; + +release()回调必须被设置,且在最后一个 video_device 用户退出之后 +被调用。 + +默认的 video_device_release()回调只是调用 kfree 来释放之前分配的 +内存。 + +你应该设置这些域: + +- v4l2_dev: 设置为 v4l2_device 父设备。 + +- name: 设置为唯一的描述性设备名。 + +- fops: 设置为已有的 v4l2_file_operations 结构体。 + +- ioctl_ops: 如果你使用v4l2_ioctl_ops 来简化 ioctl 的维护 + (强烈建议使用,且将来可能变为强制性的!),然后设置你自己的 + v4l2_ioctl_ops 结构体. + +- lock: 如果你要在驱动中实现所有的锁操作,则设为 NULL 。否则 + 就要设置一个指向 struct mutex_lock 结构体的指针,这个锁将 + 在 unlocked_ioctl 文件操作被调用前由内核获得,并在调用返回后 + 释放。详见下一节。 + +- prio: 保持对优先级的跟踪。用于实现 VIDIOC_G/S_PRIORITY。如果 + 设置为 NULL,则会使用 v4l2_device 中的 v4l2_prio_state 结构体。 + 如果要对每个设备节点(组)实现独立的优先级,可以将其指向自己 + 实现的 v4l2_prio_state 结构体。 + +- parent: 仅在使用 NULL 作为父设备结构体参数注册 v4l2_device 时 + 设置此参数。只有在一个硬件设备包含多一个 PCI 设备,共享同一个 + v4l2_device 核心时才会发生。 + + cx88 驱动就是一个例子:一个 v4l2_device 结构体核心,被一个裸的 + 视频 PCI 设备(cx8800)和一个 MPEG PCI 设备(cx8802)共用。由于 + v4l2_device 无法与特定的 PCI 设备关联,所有没有设置父设备。但当 + video_device 配置后,就知道使用哪个父 PCI 设备了。 + +如果你使用 v4l2_ioctl_ops,则应该在 v4l2_file_operations 结构体中 +设置 .unlocked_ioctl 指向 video_ioctl2。 + +请勿使用 .ioctl!它已被废弃,今后将消失。 + +某些情况下你要告诉核心:你在 v4l2_ioctl_ops 指定的某个函数应被忽略。 +你可以在 video_device_register 被调用前通过以下函数标记这个 ioctls。 + +void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd); + +基于外部因素(例如某个板卡已被使用),在不创建新结构体的情况下,你想 +要关闭 v4l2_ioctl_ops 中某个特性往往需要这个机制。 + +v4l2_file_operations 结构体是 file_operations 的一个子集。其主要 +区别在于:因 inode 参数从未被使用,它将被忽略。 + +如果需要与媒体框架整合,你必须通过调用 media_entity_pads_init() 初始化 +嵌入在 video_device 结构体中的 media_entity(entity 域)结构体: + + struct media_pad *pad = &my_vdev->pad; + int err; + + err = media_entity_pads_init(&vdev->entity, 1, pad); + +pads 数组必须预先初始化。没有必要手动设置 media_entity 的 type 和 +name 域。 + +当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。 + +v4l2_file_operations 与锁 +-------------------------- + +你可以在 video_device 结构体中设置一个指向 mutex_lock 的指针。通常 +这既可是一个顶层互斥锁也可为设备节点自身的互斥锁。默认情况下,此锁 +用于 unlocked_ioctl,但为了使用 ioctls 你通过以下函数可禁用锁定: + + void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd); + +例如: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF); + +你必须在注册 video_device 前调用这个函数。 + +特别是对于 USB 驱动程序,某些命令(如设置控制)需要很长的时间,可能 +需要自行为缓冲区队列的 ioctls 实现锁定。 + +如果你需要更细粒度的锁,你必须设置 mutex_lock 为 NULL,并完全自己实现 +锁机制。 + +这完全由驱动开发者决定使用何种方法。然而,如果你的驱动存在长延时操作 +(例如,改变 USB 摄像头的曝光时间可能需要较长时间),而你又想让用户 +在等待长延时操作完成期间做其他的事,则你最好自己实现锁机制。 + +如果指定一个锁,则所有 ioctl 操作将在这个锁的作用下串行执行。如果你 +使用 videobuf,则必须将同一个锁传递给 videobuf 队列初始化函数;如 +videobuf 必须等待一帧的到达,则可临时解锁并在这之后重新上锁。如果驱动 +也在代码执行期间等待,则可做同样的工作(临时解锁,再上锁)让其他进程 +可以在第一个进程阻塞时访问设备节点。 + +在使用 videobuf2 的情况下,必须实现 wait_prepare 和 wait_finish 回调 +在适当的时候解锁/加锁。进一步来说,如果你在 video_device 结构体中使用 +锁,则必须在 wait_prepare 和 wait_finish 中对这个互斥锁进行解锁/加锁。 + +热插拔的断开实现也必须在调用 v4l2_device_disconnect 前获得锁。 + +video_device注册 +--------------- + +接下来你需要注册视频设备:这会为你创建一个字符设备。 + + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (err) { + video_device_release(vdev); /* or kfree(my_vdev); */ + return err; + } + +如果 v4l2_device 父设备的 mdev 域为非 NULL 值,视频设备实体将自动 +注册为媒体设备。 + +注册哪种设备是根据类型(type)参数。存在以下类型: + +VFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX +VFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视) +VFL_TYPE_RADIO: 用于广播调谐器的 radioX + +最后一个参数让你确定一个所控制设备的设备节点号数量(例如 videoX 中的 X)。 +通常你可以传入-1,让 v4l2 框架自己选择第一个空闲的编号。但是有时用户 +需要选择一个特定的节点号。驱动允许用户通过驱动模块参数选择一个特定的 +设备节点号是很普遍的。这个编号将会传递给这个函数,且 video_register_device +将会试图选择这个设备节点号。如果这个编号被占用,下一个空闲的设备节点 +编号将被选中,并向内核日志中发送一个警告信息。 + +另一个使用场景是当驱动创建多个设备时。这种情况下,对不同的视频设备在 +编号上使用不同的范围是很有用的。例如,视频捕获设备从 0 开始,视频 +输出设备从 16 开始。所以你可以使用最后一个参数来指定设备节点号最小值, +而 v4l2 框架会试图选择第一个的空闲编号(等于或大于你提供的编号)。 +如果失败,则它会就选择第一个空闲的编号。 + +由于这种情况下,你会忽略无法选择特定设备节点号的警告,则可调用 +video_register_device_no_warn() 函数避免警告信息的产生。 + +只要设备节点被创建,一些属性也会同时创建。在 /sys/class/video4linux +目录中你会找到这些设备。例如进入其中的 video0 目录,你会看到‘name’和 +‘index’属性。‘name’属性值就是 video_device 结构体中的‘name’域。 + +‘index’属性值就是设备节点的索引值:每次调用 video_register_device(), +索引值都递增 1 。第一个视频设备节点总是从索引值 0 开始。 + +用户可以设置 udev 规则,利用索引属性生成花哨的设备名(例如:用‘mpegX’ +代表 MPEG 视频捕获设备节点)。 + +在设备成功注册后,就可以使用这些域: + +- vfl_type: 传递给 video_register_device 的设备类型。 +- minor: 已指派的次设备号。 +- num: 设备节点编号 (例如 videoX 中的 X)。 +- index: 设备索引号。 + +如果注册失败,你必须调用 video_device_release() 来释放已分配的 +video_device 结构体;如果 video_device 是嵌入在自己创建的结构体中, +你也必须释放它。vdev->release() 回调不会在注册失败之后被调用, +你也不应试图在注册失败后注销设备。 + + +video_device 注销 +---------------- + +当视频设备节点已被移除,不论是卸载驱动还是USB设备断开,你都应注销 +它们: + + video_unregister_device(vdev); + +这个操作将从 sysfs 中移除设备节点(导致 udev 将其从 /dev 中移除)。 + +video_unregister_device() 返回之后,就无法完成打开操作。尽管如此, +USB 设备的情况则不同,某些应用程序可能依然打开着其中一个已注销设备 +节点。所以在注销之后,所有文件操作(当然除了 release )也应返回错误值。 + +当最后一个视频设备节点的用户退出,则 vdev->release() 回调会被调用, +并且你可以做最后的清理操作。 + +不要忘记清理与视频设备相关的媒体入口(如果被初始化过): + + media_entity_cleanup(&vdev->entity); + +这可以在 release 回调中完成。 + + +video_device 辅助函数 +--------------------- + +一些有用的辅助函数如下: + +- file/video_device 私有数据 + +你可以用以下函数在 video_device 结构体中设置/获取驱动私有数据: + +void *video_get_drvdata(struct video_device *vdev); +void video_set_drvdata(struct video_device *vdev, void *data); + +注意:在调用 video_register_device() 前执行 video_set_drvdata() +是安全的。 + +而以下函数: + +struct video_device *video_devdata(struct file *file); + +返回 file 结构体中拥有的的 video_device 指针。 + +video_drvdata 辅助函数结合了 video_get_drvdata 和 video_devdata +的功能: + +void *video_drvdata(struct file *file); + +你可以使用如下代码从 video_device 结构体中获取 v4l2_device 结构体 +指针: + +struct v4l2_device *v4l2_dev = vdev->v4l2_dev; + +- 设备节点名 + +video_device 设备节点在内核中的名称可以通过以下函数获得 + +const char *video_device_node_name(struct video_device *vdev); + +这个名字被用户空间工具(例如 udev)作为提示信息使用。应尽可能使用 +此功能,而非访问 video_device::num 和 video_device::minor 域。 + + +视频缓冲辅助函数 +--------------- + +v4l2 核心 API 提供了一个处理视频缓冲的标准方法(称为“videobuf”)。 +这些方法使驱动可以通过统一的方式实现 read()、mmap() 和 overlay()。 +目前在设备上支持视频缓冲的方法有分散/聚集 DMA(videobuf-dma-sg)、 +线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc +分配的缓冲(videobuf-vmalloc)。 + +请参阅 Documentation/driver-api/media/v4l2-videobuf.rst,以获得更多关于 videobuf +层的使用信息。 + +v4l2_fh 结构体 +------------- + +v4l2_fh 结构体提供一个保存用于 V4L2 框架的文件句柄特定数据的简单方法。 +如果 video_device 标志,新驱动 +必须使用 v4l2_fh 结构体,因为它也用于实现优先级处理(VIDIOC_G/S_PRIORITY)。 + +v4l2_fh 的用户(位于 V4l2 框架中,并非驱动)可通过测试 +video_device->flags 中的 V4L2_FL_USES_V4L2_FH 位得知驱动是否使用 +v4l2_fh 作为他的 file->private_data 指针。这个位会在调用 v4l2_fh_init() +时被设置。 + +v4l2_fh 结构体作为驱动自身文件句柄结构体的一部分被分配,且驱动在 +其打开函数中将 file->private_data 指向它。 + +在许多情况下,v4l2_fh 结构体会嵌入到一个更大的结构体中。这钟情况下, +应该在 open() 中调用 v4l2_fh_init+v4l2_fh_add,并在 release() 中 +调用 v4l2_fh_del+v4l2_fh_exit。 + +驱动可以通过使用 container_of 宏提取他们自己的文件句柄结构体。例如: + +struct my_fh { + int blah; + struct v4l2_fh fh; +}; + +... + +int my_open(struct file *file) +{ + struct my_fh *my_fh; + struct video_device *vfd; + int ret; + + ... + + my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL); + + ... + + v4l2_fh_init(&my_fh->fh, vfd); + + ... + + file->private_data = &my_fh->fh; + v4l2_fh_add(&my_fh->fh); + return 0; +} + +int my_release(struct file *file) +{ + struct v4l2_fh *fh = file->private_data; + struct my_fh *my_fh = container_of(fh, struct my_fh, fh); + + ... + v4l2_fh_del(&my_fh->fh); + v4l2_fh_exit(&my_fh->fh); + kfree(my_fh); + return 0; +} + +以下是 v4l2_fh 函数使用的简介: + +void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) + + 初始化文件句柄。这*必须*在驱动的 v4l2_file_operations->open() + 函数中执行。 + +void v4l2_fh_add(struct v4l2_fh *fh) + + 添加一个 v4l2_fh 到 video_device 文件句柄列表。一旦文件句柄 + 初始化完成就必须调用。 + +void v4l2_fh_del(struct v4l2_fh *fh) + + 从 video_device() 中解除文件句柄的关联。文件句柄的退出函数也 + 将被调用。 + +void v4l2_fh_exit(struct v4l2_fh *fh) + + 清理文件句柄。在清理完 v4l2_fh 后,相关内存会被释放。 + + +如果 v4l2_fh 不是嵌入在其他结构体中的,则可以用这些辅助函数: + +int v4l2_fh_open(struct file *filp) + + 分配一个 v4l2_fh 结构体空间,初始化并将其添加到 file 结构体相关的 + video_device 结构体中。 + +int v4l2_fh_release(struct file *filp) + + 从 file 结构体相关的 video_device 结构体中删除 v4l2_fh ,清理 + v4l2_fh 并释放空间。 + +这两个函数可以插入到 v4l2_file_operation 的 open() 和 release() +操作中。 + + +某些驱动需要在第一个文件句柄打开和最后一个文件句柄关闭的时候做些 +工作。所以加入了两个辅助函数以检查 v4l2_fh 结构体是否是相关设备 +节点打开的唯一文件句柄。 + +int v4l2_fh_is_singular(struct v4l2_fh *fh) + + 如果此文件句柄是唯一打开的文件句柄,则返回 1 ,否则返回 0 。 + +int v4l2_fh_is_singular_file(struct file *filp) + + 功能相同,但通过 filp->private_data 调用 v4l2_fh_is_singular。 + + +V4L2 事件机制 +----------- + +V4L2 事件机制提供了一个通用的方法将事件传递到用户空间。驱动必须使用 +v4l2_fh 才能支持 V4L2 事件机制。 + + +事件通过一个类型和选择 ID 来定义。ID 对应一个 V4L2 对象,例如 +一个控制 ID。如果未使用,则 ID 为 0。 + +当用户订阅一个事件,驱动会为此分配一些 kevent 结构体。所以每个 +事件组(类型、ID)都会有自己的一套 kevent 结构体。这保证了如果 +一个驱动短时间内产生了许多同类事件,不会覆盖其他类型的事件。 + +但如果你收到的事件数量大于同类事件 kevent 的保存数量,则最早的 +事件将被丢弃,并加入新事件。 + +此外,v4l2_subscribed_event 结构体内部有可供驱动设置的 merge() 和 +replace() 回调,这些回调会在新事件产生且没有多余空间的时候被调用。 +replace() 回调让你可以将早期事件的净荷替换为新事件的净荷,将早期 +净荷的相关数据合并到替换进来的新净荷中。当该类型的事件仅分配了一个 +kevent 结构体时,它将被调用。merge() 回调让你可以合并最早的事件净荷 +到在它之后的那个事件净荷中。当该类型的事件分配了两个或更多 kevent +结构体时,它将被调用。 + +这种方法不会有状态信息丢失,只会导致中间步骤信息丢失。 + + +关于 replace/merge 回调的一个不错的例子在 v4l2-event.c 中:用于 +控制事件的 ctrls_replace() 和 ctrls_merge() 回调。 + +注意:这些回调可以在中断上下文中调用,所以它们必须尽快完成并退出。 + +有用的函数: + +void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) + + 将事件加入视频设备的队列。驱动仅负责填充 type 和 data 域。 + 其他域由 V4L2 填充。 + +int v4l2_event_subscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub, unsigned elems, + const struct v4l2_subscribed_event_ops *ops) + + video_device->ioctl_ops->vidioc_subscribe_event 必须检测驱动能 + 产生特定 id 的事件。然后调用 v4l2_event_subscribe() 来订阅该事件。 + + elems 参数是该事件的队列大小。若为 0,V4L2 框架将会(根据事件类型) + 填充默认值。 + + ops 参数允许驱动指定一系列回调: + * add: 当添加一个新监听者时调用(重复订阅同一个事件,此回调 + 仅被执行一次)。 + * del: 当一个监听者停止监听时调用。 + * replace: 用‘新’事件替换‘早期‘事件。 + * merge: 将‘早期‘事件合并到‘新’事件中。 + 这四个调用都是可选的,如果不想指定任何回调,则 ops 可为 NULL。 + +int v4l2_event_unsubscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) + + v4l2_ioctl_ops 结构体中的 vidioc_unsubscribe_event 回调函数。 + 驱动程序可以直接使用 v4l2_event_unsubscribe() 实现退订事件过程。 + + 特殊的 V4L2_EVENT_ALL 类型,可用于退订所有事件。驱动可能在特殊 + 情况下需要做此操作。 + +int v4l2_event_pending(struct v4l2_fh *fh) + + 返回未决事件的数量。有助于实现轮询(poll)操作。 + +事件通过 poll 系统调用传递到用户空间。驱动可用 +v4l2_fh->wait (wait_queue_head_t 类型)作为参数调用 poll_wait()。 + +事件分为标准事件和私有事件。新的标准事件必须使用可用的最小事件类型 +编号。驱动必须从他们本类型的编号起始处分配事件。类型的编号起始为 +V4L2_EVENT_PRIVATE_START + n * 1000 ,其中 n 为可用最小编号。每个 +类型中的第一个事件类型编号是为以后的使用保留的,所以第一个可用事件 +类型编号是‘class base + 1’。 + +V4L2 事件机制的使用实例可以在 OMAP3 ISP 的驱动 +(drivers/media/video/omap3isp)中找到。 |