code:
:Original: Documentation/mm/page_owner.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

================================
page owner: 跟踪谁分配的每个页面
================================

概述
====

page owner是用来追踪谁分配的每一个页面。它可以用来调试内存泄漏或找到内存占用者。
当分配发生时，有关分配的信息，如调用堆栈和页面的顺序被存储到每个页面的特定存储中。
当我们需要了解所有页面的状态时，我们可以获得并分析这些信息。

尽管我们已经有了追踪页面分配/释放的tracepoint，但用它来分析谁分配的每个页面是
相当复杂的。我们需要扩大跟踪缓冲区，以防止在用户空间程序启动前出现重叠。而且，启
动的程序会不断地将跟踪缓冲区转出，供以后分析，这将会改变系统的行为，会产生更多的
可能性，而不是仅仅保留在内存中，所以不利于调试。

页面所有者也可以用于各种目的。例如，可以通过每个页面的gfp标志信息获得精确的碎片
统计。如果启用了page owner，它就已经实现并激活了。我们非常欢迎其他用途。

page owner在默认情况下是禁用的。所以，如果你想使用它，你需要在你的启动cmdline
中加入"page_owner=on"。如果内核是用page owner构建的，并且由于没有启用启动
选项而在运行时禁用page owner，那么运行时的开销是很小的。如果在运行时禁用，它不
需要内存来存储所有者信息，所以没有运行时内存开销。而且，页面所有者在页面分配器的
热路径中只插入了两个不可能的分支，如果不启用，那么分配就会像没有页面所有者的内核
一样进行。这两个不可能的分支应该不会影响到分配的性能，特别是在静态键跳转标签修补
功能可用的情况下。以下是由于这个功能而导致的内核代码大小的变化。

- 没有page owner::

   text    data     bss     dec     hex filename
   48392   2333     644   51369    c8a9 mm/page_alloc.o

- 有page owner::

   text    data     bss     dec     hex filename
   48800   2445     644   51889    cab1 mm/page_alloc.o
   6662     108      29    6799    1a8f mm/page_owner.o
   1025       8       8    1041     411 mm/page_ext.o

虽然总共增加了8KB的代码，但page_alloc.o增加了520字节，其中不到一半是在hotpath
中。构建带有page owner的内核，并在需要时打开它，将是调试内核内存问题的最佳选择。

有一个问题是由实现细节引起的。页所有者将信息存储到struct page扩展的内存中。这
个内存的初始化时间比稀疏内存系统中的页面分配器启动的时间要晚一些，所以，在初始化
之前，许多页面可以被分配，但它们没有所有者信息。为了解决这个问题，这些早期分配的
页面在初始化阶段被调查并标记为分配。虽然这并不意味着它们有正确的所有者信息，但至
少，我们可以更准确地判断该页是否被分配。在2GB内存的x86-64虚拟机上，有13343
个早期分配的页面被捕捉和标记，尽管它们大部分是由结构页扩展功能分配的。总之，在这
之后，没有任何页面处于未追踪状态。

使用方法
========

1) 构建用户空间的帮助::

	cd tools/vm
	make page_owner_sort

2) 启用page owner: 添加 "page_owner=on" 到 boot cmdline.

3) 做你想调试的工作。

4) 分析来自页面所有者的信息::

	cat /sys/kernel/debug/page_owner > page_owner_full.txt
	./page_owner_sort page_owner_full.txt sorted_page_owner.txt

   ``page_owner_full.txt`` 的一般输出情况如下::

	Page allocated via order XXX, ...
	PFN XXX ...
	// 栈详情

	Page allocated via order XXX, ...
	PFN XXX ...
	// 栈详情
    默认情况下，它将以一个给定的pfn开始，做完整的pfn转储，且page_owner支持fseek。

    FILE *fp = fopen("/sys/kernel/debug/page_owner", "r");
    fseek(fp, pfn_start, SEEK_SET);

   ``page_owner_sort`` 工具忽略了 ``PFN`` 行，将剩余的行放在buf中，使用regexp提
   取页序值，计算buf的次数和页数，最后根据参数进行排序。

   在 ``sorted_page_owner.txt`` 中可以看到关于谁分配了每个页面的结果。一般输出::

	XXX times, XXX pages:
	Page allocated via order XXX, ...
	// Detailed stack

   默认情况下， ``page_owner_sort`` 是根据buf的时间来排序的。如果你想
   按buf的页数排序，请使用-m参数。详细的参数是:

   基本函数::

	排序:
		-a		按内存分配时间排序
		-m		按总内存排序
		-p		按pid排序。
		-P		按tgid排序。
		-n		按任务命令名称排序。
		-r		按内存释放时间排序。
		-s		按堆栈跟踪排序。
		-t		按时间排序（默认）。
       --sort <order> 指定排序顺序。排序的语法是[+|-]key[,[+|-]key[,...]]。从
       **标准格式指定器**那一节选择一个键。"+"是可选的，因为默认的方向是数字或
       词法的增加。允许混合使用缩写和完整格式的键。

        例子:
				./page_owner_sort <input> <output> --sort=n,+pid,-tgid
				./page_owner_sort <input> <output> --sort=at

    其它函数::

	剔除:
		--cull <rules>
		        指定剔除规则。剔除的语法是key[,key[,...]]。从**标准格式指定器**
				部分选择一个多字母键。
		<rules>是一个以逗号分隔的列表形式的单一参数，它提供了一种指定单个剔除规则的
		方法。 识别的关键字在下面的**标准格式指定器**部分有描述。<规则>可以通过键的
		序列k1,k2,...来指定，在下面的标准排序键部分有描述。允许混合使用简写和完整形
		式的键。

		Examples:
				./page_owner_sort <input> <output> --cull=stacktrace
				./page_owner_sort <input> <output> --cull=st,pid,name
				./page_owner_sort <input> <output> --cull=n,f

	过滤:
		-f		过滤掉内存已被释放的块的信息。

	选择:
		--pid <pidlist>		按pid选择。这将选择进程ID号出现在<pidlist>中的块。
		--tgid <tgidlist>	按tgid选择。这将选择其线程组ID号出现在<tgidlist>
		                    中的块。
		--name <cmdlist>	按任务命令名称选择。这将选择其任务命令名称出现在
		                    <cmdlist>中的区块。

		<pidlist>, <tgidlist>, <cmdlist>是以逗号分隔的列表形式的单个参数，
		它提供了一种指定单个选择规则的方法。

		例子:
				./page_owner_sort <input> <output> --pid=1
				./page_owner_sort <input> <output> --tgid=1,2,3
				./page_owner_sort <input> <output> --name name1,name2

标准格式指定器
==============
::

  --sort的选项:

	短键		长键		描述
	p		pid		进程ID
	tg		tgid		线程组ID
	n		name		任务命令名称
	st		stacktrace	页面分配的堆栈跟踪
	T		txt		块的全文
	ft		free_ts		页面释放时的时间戳
	at		alloc_ts	页面被分配时的时间戳
	ator		allocator	页面的内存分配器

  --curl的选项:

	短键		长键		描述
	p		pid		进程ID
	tg		tgid		线程组ID
	n		name		任务命令名称
	f		free		该页是否已经释放
	st		stacktrace	页面分配的堆栈跟踪
	ator		allocator	页面的内存分配器


code:
:Original: Documentation/mm/remap_file_pages.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

==============================
remap_file_pages()系统调用
==============================

remap_file_pages()系统调用被用来创建一个非线性映射，也就是说，在这个映射中，
文件的页面被无序映射到内存中。使用remap_file_pages()比重复调用mmap(2)的好
处是，前者不需要内核创建额外的VMA（虚拟内存区）数据结构。

支持非线性映射需要在内核虚拟内存子系统中编写大量的non-trivial的代码，包括热
路径。另外，为了使非线性映射工作，内核需要一种方法来区分正常的页表项和带有文件
偏移的项（pte_file）。内核为达到这个目的在PTE中保留了标志。PTE标志是稀缺资
源，特别是在某些CPU架构上。如果能腾出这个标志用于其他用途就更好了。

幸运的是，在生活中并没有很多remap_file_pages()的用户。只知道有一个企业的RDBMS
实现在32位系统上使用这个系统调用来映射比32位虚拟地址空间线性尺寸更大的文件。
由于64位系统的广泛使用，这种使用情况已经不重要了。

syscall被废弃了，现在用一个模拟来代替它。仿真会创建新的VMA，而不是非线性映射。
对于remap_file_pages()的少数用户来说，它的工作速度会变慢，但ABI被保留了。

仿真的一个副作用（除了性能之外）是，由于额外的VMA，用户可以更容易达到
vm.max_map_count的限制。关于限制的更多细节，请参见DEFAULT_MAX_MAP_COUNT
的注释。

repo_name: angery/linux


code:
:Original: Documentation/mm/split_page_table_lock.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

=================================
分页表锁（split page table lock）
=================================

最初，mm->page_table_lock spinlock保护了mm_struct的所有页表。但是这种方
法导致了多线程应用程序的缺页异常可扩展性差，因为对锁的争夺很激烈。为了提高可扩
展性，我们引入了分页表锁。

有了分页表锁，我们就有了单独的每张表锁来顺序化对表的访问。目前，我们对PTE和
PMD表使用分页锁。对高层表的访问由mm->page_table_lock保护。

有一些辅助工具来锁定/解锁一个表和其他访问器函数:

 - pte_offset_map_lock()
	映射pte并获取PTE表锁，返回所取锁的指针；
 - pte_unmap_unlock()
	解锁和解映射PTE表；
 - pte_alloc_map_lock()
	如果需要的话，分配PTE表并获取锁，如果分配失败，返回已获取的锁的指针
	或NULL；
 - pte_lockptr()
	返回指向PTE表锁的指针；
 - pmd_lock()
	取得PMD表锁，返回所取锁的指针。
 - pmd_lockptr()
	返回指向PMD表锁的指针；

如果CONFIG_SPLIT_PTLOCK_CPUS（通常为4）小于或等于NR_CPUS，则在编译
时启用PTE表的分页表锁。如果分页锁被禁用，所有的表都由mm->page_table_lock
来保护。

如果PMD表启用了分页锁，并且架构支持它，那么PMD表的分页锁就会被启用（见
下文）。

Hugetlb 和分页表锁
==================

Hugetlb可以支持多种页面大小。我们只对PMD级别使用分页锁，但不对PUD使用。

Hugetlb特定的辅助函数:

 - huge_pte_lock()
	对PMD_SIZE页面采取pmd分割锁，否则mm->page_table_lock；
 - huge_pte_lockptr()
	返回指向表锁的指针。

架构对分页表锁的支持
====================

没有必要特别启用PTE分页表锁：所有需要的东西都由pgtable_pte_page_ctor()
和pgtable_pte_page_dtor()完成，它们必须在PTE表分配/释放时被调用。

确保架构不使用slab分配器来分配页表：slab使用page->slab_cache来分配其页
面。这个区域与page->ptl共享存储。

PMD分页锁只有在你有两个以上的页表级别时才有意义。

启用PMD分页锁需要在PMD表分配时调用pgtable_pmd_page_ctor()，在释放时调
用pgtable_pmd_page_dtor()。

分配通常发生在pmd_alloc_one()中，释放发生在pmd_free()和pmd_free_tlb()
中，但要确保覆盖所有的PMD表分配/释放路径：即X86_PAE在pgd_alloc()中预先
分配一些PMD。

一切就绪后，你可以设置CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK。

注意：pgtable_pte_page_ctor()和pgtable_pmd_page_ctor()可能失败--必
须正确处理。

page->ptl
=========

page->ptl用于访问分割页表锁，其中'page'是包含该表的页面struct page。它
与page->private（以及union中的其他几个字段）共享存储。

为了避免增加struct page的大小并获得最佳性能，我们使用了一个技巧:

 - 如果spinlock_t适合于long，我们使用page->ptr作为spinlock，这样我们
   就可以避免间接访问并节省一个缓存行。
 - 如果spinlock_t的大小大于long的大小，我们使用page->ptl作为spinlock_t
   的指针并动态分配它。这允许在启用DEBUG_SPINLOCK或DEBUG_LOCK_ALLOC的
   情况下使用分页锁，但由于间接访问而多花了一个缓存行。

PTE表的spinlock_t分配在pgtable_pte_page_ctor()中，PMD表的spinlock_t
分配在pgtable_pmd_page_ctor()中。

请不要直接访问page->ptl - -使用适当的辅助函数。


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/mm/vmalloced-kernel-stacks.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

====================
支持虚拟映射的内核栈
====================

:作者: Shuah Khan <skhan@linuxfoundation.org>

.. contents:: :local:

概览
----

这是介绍 `虚拟映射内核栈功能 <https://lwn.net/Articles/694348/>` 的代码
和原始补丁系列的信息汇总。

简介
----

内核堆栈溢出通常难以调试，并使内核容易被（恶意）利用。问题可能在稍后的时间出现，使其难以
隔离和究其根本原因。

带有保护页的虚拟映射内核堆栈如果溢出，会被立即捕获，而不会放任其导致难以诊断的损
坏。

HAVE_ARCH_VMAP_STACK和VMAP_STACK配置选项能够支持带有保护页的虚拟映射堆栈。
当堆栈溢出时，这个特性会引发可靠的异常。溢出后堆栈跟踪的可用性以及对溢出本身的
响应取决于架构。

.. note::
        截至本文撰写时， arm64, powerpc, riscv, s390, um, 和 x86 支持VMAP_STACK。

HAVE_ARCH_VMAP_STACK
--------------------

能够支持虚拟映射内核栈的架构应该启用这个bool配置选项。要求是:

- vmalloc空间必须大到足以容纳许多内核堆栈。这可能排除了许多32位架构。
- vmalloc空间的堆栈需要可靠地工作。例如，如果vmap页表是按需创建的，当堆栈指向
  具有未填充页表的虚拟地址时，这种机制需要工作，或者架构代码（switch_to()和
  switch_mm()，很可能）需要确保堆栈的页表项在可能未填充的堆栈上运行之前已经填
  充。
- 如果堆栈溢出到一个保护页，就应该发生一些合理的事情。“合理”的定义是灵活的，但
  在没有记录任何东西的情况下立即重启是不友好的。

VMAP_STACK
----------

VMAP_STACK bool配置选项在启用时分配虚拟映射的任务栈。这个选项依赖于
HAVE_ARCH_VMAP_STACK。

- 如果你想使用带有保护页的虚拟映射的内核堆栈，请启用该选项。这将导致内核栈溢出
  被立即捕获，而不是难以诊断的损坏。

.. note::

        使用KASAN的这个功能需要架构支持用真实的影子内存来支持虚拟映射，并且
        必须启用KASAN_VMALLOC。

.. note::

        启用VMAP_STACK时，无法在堆栈分配的数据上运行DMA。

内核配置选项和依赖性不断变化。请参考最新的代码库:

`Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>`

分配方法
--------

当一个新的内核线程被创建时，线程堆栈是由页级分配器分配的虚拟连续的内存页组成。这
些页面被映射到有PAGE_KERNEL保护的连续的内核虚拟空间。

alloc_thread_stack_node()调用__vmalloc_node_range()来分配带有PAGE_KERNEL
保护的栈。

- 分配的堆栈被缓存起来，以后会被新的线程重用，所以在分配/释放堆栈给任务时，要手动
  进行memcg核算。因此，__vmalloc_node_range被调用时没有__GFP_ACCOUNT。
- vm_struct被缓存起来，以便能够找到在中断上下文中启动的空闲线程。 free_thread_stack()
  可以在中断上下文中调用。
- 在arm64上，所有VMAP的堆栈都需要有相同的对齐方式，以确保VMAP的堆栈溢出检测正常
  工作。架构特定的vmap堆栈分配器照顾到了这个细节。
- 这并不涉及中断堆栈--参考原始补丁

线程栈分配是由clone()、fork()、vfork()、kernel_thread()通过kernel_clone()
启动的。留点提示在这，以便搜索代码库，了解线程栈何时以及如何分配。

大量的代码是在:
`kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`.

task_struct中的stack_vm_area指针可以跟踪虚拟分配的堆栈，一个非空的stack_vm_area
指针可以表明虚拟映射的内核堆栈已经启用。

::

        struct vm_struct *stack_vm_area;

堆栈溢出处理
------------

前守护页和后守护页有助于检测堆栈溢出。当堆栈溢出到守护页时，处理程序必须小心不要再
次溢出堆栈。当处理程序被调用时，很可能只留下很少的堆栈空间。

在x86上，这是通过处理表明内核堆栈溢出的双异常堆栈的缺页异常来实现的。

用守护页测试VMAP分配
--------------------

我们如何确保VMAP_STACK在分配时确实有前守护页和后守护页的保护？下面的 lkdtm 测试
可以帮助检测任何回归。

::

        void lkdtm_STACK_GUARD_PAGE_LEADING()
        void lkdtm_STACK_GUARD_PAGE_TRAILING()

结论
----

- vmalloced堆栈的percpu缓存似乎比高阶堆栈分配要快一些，至少在缓存命中时是这样。
- THREAD_INFO_IN_TASK完全摆脱了arch-specific thread_info，并简单地将
  thread_info（仅包含标志）和'int cpu'嵌入task_struct中。
- 一旦任务死亡，线程栈就可以被释放（无需等待RCU），然后，如果使用vmapped栈，就
  可以将整个栈缓存起来，以便在同一cpu上重复使用。


code:
:Original: Documentation/mm/z3fold.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

======
z3fold
======

z3fold是一个专门用于存储压缩页的分配器。它被设计为每个物理页最多可以存储三个压缩页。
它是zbud的衍生物，允许更高的压缩率，保持其前辈的简单性和确定性。

z3fold和zbud的主要区别是:

* 与zbud不同的是，z3fold允许最大的PAGE_SIZE分配。
* z3fold在其页面中最多可以容纳3个压缩页面
* z3fold本身没有输出任何API，因此打算通过zpool的API来使用

为了保持确定性和简单性，z3fold，就像zbud一样，总是在每页存储一个整数的压缩页，但是
它最多可以存储3页，不像zbud最多可以存储2页。因此压缩率达到2.7倍左右，而zbud的压缩
率是1.7倍左右。

不像zbud（但也像zsmalloc），z3fold_alloc()那样不返回一个可重复引用的指针。相反，它
返回一个无符号长句柄，它编码了被分配对象的实际位置。

保持有效的压缩率接近于zsmalloc，z3fold不依赖于MMU的启用，并提供更可预测的回收行
为，这使得它更适合于小型和反应迅速的系统。


code:
:Original: Documentation/mm/zsmalloc.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

========
zsmalloc
========

这个分配器是为与zram一起使用而设计的。因此，该分配器应该在低内存条件下工作良好。特别是，
它从未尝试过higher order页面的分配，这在内存压力下很可能会失败。另一方面，如果我们只
是使用单（0-order）页，它将遭受非常高的碎片化 - 任何大小为PAGE_SIZE/2或更大的对象将
占据整个页面。这是其前身（xvmalloc）的主要问题之一。

为了克服这些问题，zsmalloc分配了一堆0-order页面，并使用各种"struct page"字段将它
们链接起来。这些链接的页面作为一个单一的higher order页面，即一个对象可以跨越0-order
页面的边界。代码将这些链接的页面作为一个实体，称为zspage。

为了简单起见，zsmalloc只能分配大小不超过PAGE_SIZE的对象，因为这满足了所有当前用户的
要求（在最坏的情况下，页面是不可压缩的，因此以"原样"即未压缩的形式存储）。对于大于这
个大小的分配请求，会返回失败（见zs_malloc）。

此外，zs_malloc()并不返回一个可重复引用的指针。相反，它返回一个不透明的句柄（无符号
长），它编码了被分配对象的实际位置。这种间接性的原因是zsmalloc并不保持zspages的永久
映射，因为这在32位系统上会导致问题，因为内核空间映射的VA区域非常小。因此，在使用分配
的内存之前，对象必须使用zs_map_object()进行映射以获得一个可用的指针，随后使用
zs_unmap_object()解除映射。

stat
====

通过CONFIG_ZSMALLOC_STAT，我们可以通过 ``/sys/kernel/debug/zsmalloc/<user name>``
看到zsmalloc内部信息。下面是一个统计输出的例子。::

 # cat /sys/kernel/debug/zsmalloc/zram0/classes

 class  size almost_full almost_empty obj_allocated   obj_used pages_used pages_per_zspage
    ...
    ...
     9   176           0            1           186        129          8                4
    10   192           1            0          2880       2872        135                3
    11   208           0            1           819        795         42                2
    12   224           0            1           219        159         12                4
    ...
    ...

class
	索引
size
	zspage存储对象大小
almost_empty
	ZS_ALMOST_EMPTY zspage的数量（见下文）。
almost_full
	ZS_ALMOST_FULL zspage的数量(见下图)
obj_allocated
	已分配对象的数量
obj_used
	分配给用户的对象的数量
pages_used
	为该类分配的页数
pages_per_zspage
	组成一个zspage的0-order页面的数量

当n <= N / f时，我们将一个zspage分配给ZS_ALMOST_EMPTYfullness组，其中

* n = 已分配对象的数量
* N = zspage可以存储的对象总数
* f = fullness_threshold_frac(即，目前是4个)

同样地，我们将zspage分配给:

* ZS_ALMOST_FULL  when n > N / f
* ZS_EMPTY        when n == 0
* ZS_FULL         when n == N


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/openrisc/index.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

.. _cn_openrisc_index:

=================
OpenRISC 体系架构
=================

.. toctree::
   :maxdepth: 2

   openrisc_port
   todo

Todolist:
    features

.. only::  subproject and html

   Indices
   =======

   * :ref:`genindex`


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/openrisc/openrisc_port.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

.. _cn_openrisc_port:

==============
OpenRISC Linux
==============

这是Linux对OpenRISC类微处理器的移植；具体来说，最早移植目标是32位
OpenRISC 1000系列（或1k）。

关于OpenRISC处理器和正在进行中的开发的信息:

	=======		=============================
	网站		https://openrisc.io
	邮箱		openrisc@lists.librecores.org
	=======		=============================

---------------------------------------------------------------------

OpenRISC工具链和Linux的构建指南
===============================

为了构建和运行Linux for OpenRISC，你至少需要一个基本的工具链，或许
还需要架构模拟器。 这里概述了准备就位这些部分的步骤。

1) 工具链

工具链二进制文件可以从openrisc.io或我们的github发布页面获得。不同
工具链的构建指南可以在openrisc.io或Stafford的工具链构建和发布脚本
中找到。

	======      =================================================
	二进制      https://github.com/openrisc/or1k-gcc/releases
	工具链      https://openrisc.io/software
	构建        https://github.com/stffrdhrn/or1k-toolchain-build
	======      =================================================

2) 构建

像往常一样构建Linux内核::

	make ARCH=openrisc CROSS_COMPILE="or1k-linux-" defconfig
	make ARCH=openrisc CROSS_COMPILE="or1k-linux-"

3) 在FPGA上运行（可选)

OpenRISC社区通常使用FuseSoC来管理构建和编程SoC到FPGA中。 下面是用
OpenRISC SoC对De0 Nano开发板进行编程的一个例子。 在构建过程中，
FPGA RTL是从FuseSoC IP核库中下载的代码，并使用FPGA供应商工具构建。
二进制文件用openocd加载到电路板上。

::

	git clone https://github.com/olofk/fusesoc
	cd fusesoc
	sudo pip install -e .

	fusesoc init
	fusesoc build de0_nano
	fusesoc pgm de0_nano

	openocd -f interface/altera-usb-blaster.cfg \
		-f board/or1k_generic.cfg

	telnet localhost 4444
	> init
	> halt; load_image vmlinux ; reset

4) 在模拟器上运行（可选）

QEMU是一个处理器仿真器，我们推荐它来模拟OpenRISC平台。 请按照QEMU网
站上的OpenRISC说明，让Linux在QEMU上运行。 你可以自己构建QEMU，但你的
Linux发行版可能提供了支持OpenRISC的二进制包。

	=============	======================================================
	qemu openrisc	https://wiki.qemu.org/Documentation/Platforms/OpenRISC
	=============	======================================================

---------------------------------------------------------------------

术语表
======

代码中使用了以下符号约定以将范围限制在几个特定处理器实现上：

========= =======================
openrisc: OpenRISC类型处理器
or1k:     OpenRISC 1000系列处理器
or1200:   OpenRISC 1200处理器
========= =======================

---------------------------------------------------------------------

历史
====

2003-11-18	Matjaz Breskvar (phoenix@bsemi.com)
   将linux初步移植到OpenRISC或32架构。
       所有的核心功能都实现了，并且可以使用。

2003-12-08	Matjaz Breskvar (phoenix@bsemi.com)
   彻底改变TLB失误处理。
   重写异常处理。
   在默认的initrd中实现了sash-3.6的所有功能。
   大幅改进的版本。

2004-04-10	Matjaz Breskvar (phoenix@bsemi.com)
   大量的bug修复。
   支持以太网，http和telnet服务器功能。
   可以运行许多标准的linux应用程序。

2004-06-26	Matjaz Breskvar (phoenix@bsemi.com)
   移植到2.6.x。

2004-11-30	Matjaz Breskvar (phoenix@bsemi.com)
   大量的bug修复和增强功能。
   增加了opencores framebuffer驱动。

2010-10-09    Jonas Bonn (jonas@southpole.se)
   重大重写，使其与上游的Linux 2.6.36看齐。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/openrisc/todo.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

.. _cn_openrisc_todo.rst:

========
待办事项
========

OpenRISC Linux的移植已经完全投入使用，并且从 2.6.35 开始就一直在上游同步。
然而，还有一些剩余的项目需要在未来几个月内完成。 下面是一个即将进行调查的已知
不尽完美的项目列表，即我们的待办事项列表。

-   实现其余的DMA API……dma_map_sg等。

-   完成重命名清理工作……代码中提到了or32，这是架构的一个老名字。 我们
    已经确定的名字是or1k，这个改变正在以缓慢积累的方式进行。 目前，or32相当
    于or1k。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/parisc/debugging.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

.. _cn_parisc_debugging:

=================
调试PA-RISC
=================

好吧，这里有一些关于调试linux/parisc的较底层部分的信息。

1. 绝对地址
=====================

很多汇编代码目前运行在实模式下，这意味着会使用绝对地址，而不是像内核其他
部分那样使用虚拟地址。要将绝对地址转换为虚拟地址，你可以在System.map中查
找，添加__PAGE_OFFSET（目前是0x10000000）。

2. HPMCs
========

当实模式的代码试图访问不存在的内存时，会出现HPMC（high priority machine
check）而不是内核oops。若要调试HPMC，请尝试找到系统响应程序/请求程序地址。
系统请求程序地址应该与（某）处理器的HPA（I/O范围内的高地址）相匹配；系统响应程
序地址是实模式代码试图访问的地址。

系统响应程序地址的典型值是大于__PAGE_OFFSET （0x10000000）的地址，这意味着
在实模式试图访问它之前，虚拟地址没有被翻译成物理地址。

3. 有趣的Q位
============

某些非常关键的代码必须清除PSW中的Q位。当Q位被清除时，CPU不会更新中断处理
程序所读取的寄存器，以找出机器被中断的位置——所以如果你在清除Q位的指令和再
次设置Q位的RFI之间遇到中断，你不知道它到底发生在哪里。如果你幸运的话，IAOQ
会指向清除Q位的指令，如果你不幸运的话，它会指向任何地方。通常Q位的问题会
表现为无法解释的系统挂起或物理内存越界。


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/parisc/index.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

.. _cn_parisc_index:

====================
PA-RISC体系架构
====================

.. toctree::
   :maxdepth: 2

   debugging
   registers

Todolist:

   features

.. only::  subproject and html

   Indices
   =======

   * :ref:`genindex`


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/parisc/registers.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

.. _cn_parisc_registers:

=========================
Linux/PA-RISC的寄存器用法
=========================

[ 用星号表示目前尚未实现的计划用途。 ]

ABI约定的通用寄存器
===================

控制寄存器
----------

============================    =================================
CR 0 (恢复计数器)               用于ptrace
CR 1-CR 7(无定义)               未使用
CR 8 (Protection ID)            每进程值*
CR 9, 12, 13 (PIDS)             未使用
CR10 (CCR)                      FPU延迟保存*
CR11                            按照ABI的规定（SAR）
CR14 (中断向量)                 初始化为 fault_vector
CR15 (EIEM)                     所有位初始化为1*
CR16 (间隔计时器)               读取周期数/写入开始时间间隔计时器
CR17-CR22                       中断参数
CR19                            中断指令寄存器
CR20                            中断空间寄存器
CR21                            中断偏移量寄存器
CR22                            中断 PSW
CR23 (EIRR)                     读取未决中断/写入清除位
CR24 (TR 0)                     内核空间页目录指针
CR25 (TR 1)                     用户空间页目录指针
CR26 (TR 2)                     不使用
CR27 (TR 3)                     线程描述符指针
CR28 (TR 4)                     不使用
CR29 (TR 5)                     不使用
CR30 (TR 6)                     当前 / 0
CR31 (TR 7)                     临时寄存器，在不同地方使用
============================    =================================

空间寄存器（内核模式）
----------------------

========                        ==============================
SR0                             临时空间寄存器
SR4-SR7                         设置为0
SR1                             临时空间寄存器
SR2                             内核不应该破坏它
SR3                             用于用户空间访问（当前进程）
========                        ==============================

空间寄存器（用户模式）
----------------------

========                        ============================
SR0                             临时空间寄存器
SR1                             临时空间寄存器
SR2                             保存Linux gateway page的空间
SR3                             在内核中保存用户地址空间的值
SR4-SR7                         定义了用户/内核的短地址空间
========                        ============================

处理器状态字
------------

======================          ================================================
W （64位地址）                  0
E （小尾端）                    0
S （安全间隔计时器）            0
T （产生分支陷阱）              0
H （高特权级陷阱）              0
L （低特权级陷阱）              0
N （撤销下一条指令）            被C代码使用
X （数据存储中断禁用）          0
B （产生分支）                  被C代码使用
C （代码地址转译）              1, 在执行实模式代码时为0
V （除法步长校正）              被C代码使用
M （HPMC 掩码）                 0, 在执行HPMC操作*时为1
C/B （进/借 位）                被C代码使用
O （有序引用）                  1*
F （性能监视器）                0
R （回收计数器陷阱）            0
Q （收集中断状态）              1 （在rfi之前的代码中为0）
P （保护标识符）                1*
D （数据地址转译）              1, 在执行实模式代码时为0
I （外部中断掩码）              由cli()/sti()宏使用。
======================          ================================================

“隐形”寄存器（影子寄存器）
---------------------------

=============                   ===================
PSW W 默认值                    0
PSW E 默认值                    0
影子寄存器                      被中断处理代码使用
TOC启用位                       1
=============                   ===================

----------------------------------------------------------

PA-RISC架构定义了7个寄存器作为“影子寄存器”。这些寄存器在
RETURN FROM INTERRUPTION AND RESTORE指令中使用，通过消
除中断处理程序中对一般寄存器（GR）的保存和恢复的需要来减
少状态保存和恢复时间。影子寄存器是GRs 1, 8, 9, 16, 17,
24和25。

-------------------------------------------------------------------------

寄存器使用说明，最初由John Marvin提供，并由Randolph Chung提供一些补充说明。

对于通用寄存器:

r1,r2,r19-r26,r28,r29 & r31可以在不保存它们的情况下被使用。当然，如果你
关心它们，在调用另一个程序之前，你也需要保存它们。上面的一些寄存器确实
有特殊的含义，你应该注意一下:

    r1:
       addil指令是硬性规定将其结果放在r1中，所以如果你使用这条指令要
       注意这点。

    r2:
       这就是返回指针。一般来说，你不想使用它，因为你需要这个指针来返
       回给你的调用者。然而，它与这组寄存器组合在一起，因为调用者不能
       依赖你返回时的值是相同的，也就是说，你可以将r2复制到另一个寄存
       器，并在作废r2后通过该寄存器返回，这应该不会给调用程序带来问题。

    r19-r22:
       这些通常被认为是临时寄存器。
       请注意，在64位中它们是arg7-arg4。

    r23-r26:
       这些是arg3-arg0，也就是说，如果你不再关心传入的值，你可以使用
       它们。

    r28,r29:
       这俩是ret0和ret1。它们是你传入返回值的地方。r28是主返回值。当返回
       小结构体时，r29也可以用来将数据传回给调用程序。

    r30:
       栈指针

    r31:
       ble指令将返回指针放在这里。

    r3-r18,r27,r30需要被保存和恢复。r3-r18只是一般用途的寄存器。
    r27是数据指针，用来使对全局变量的引用更容易。r30是栈指针。


code:
.. SPDX-License-Identifier: GPL-2.0-only
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/peci/index.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

=================
Linux PECI 子系统
=================

.. toctree::

   peci

.. only::  subproject and html

   Indices
   =======

   * :ref:`genindex`


code:
.. SPDX-License-Identifier: GPL-2.0-only
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/peci/peci.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

====
概述
====

平台环境控制接口（PECI）是英特尔处理器和管理控制器（如底板管理控制器，BMC）
之间的一个通信接口。PECI提供的服务允许管理控制器通过访问各种寄存器来配置、监
控和调试平台。它定义了一个专门的命令协议，管理控制器作为PECI的发起者，处理器
作为PECI的响应者。PECI可以用于基于单处理器和多处理器的系统中。

注意：英特尔PECI规范没有作为专门的文件发布，而是作为英特尔CPU的外部设计规范
（EDS）的一部分。外部设计规范通常是不公开的。

PECI 线
---------

PECI线接口使用单线进行自锁和数据传输。它不需要任何额外的控制线--物理层是一个
自锁的单线总线信号，每一个比特都从接近零伏的空闲状态开始驱动、上升边缘。驱动高
电平信号的持续时间可以确定位值是逻辑 “0” 还是逻辑 “1”。PECI线还包括与每个信
息建立的可变数据速率。

对于PECI线，每个处理器包将在一个定义的范围内利用唯一的、固定的地址，该地址应
该与处理器插座ID有固定的关系--如果其中一个处理器被移除，它不会影响其余处理器
的地址。

PECI子系统代码内嵌文档
------------------------

该API在以下内核代码中:

include/linux/peci.h

drivers/peci/internal.h

drivers/peci/core.c

drivers/peci/request.c

PECI CPU 驱动 API
-------------------

该API在以下内核代码中:

drivers/peci/cpu.c


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/power/energy-model.rst

:翻译:

  唐艺舟 Tang Yizhou <tangyeechou@gmail.com>

============
设备能量模型
============

1. 概述
-------

能量模型（EM）框架是一种驱动程序与内核子系统之间的接口。其中驱动程序了解不同
性能层级的设备所消耗的功率，而内核子系统愿意使用该信息做出能量感知决策。

设备所消耗的功率的信息来源在不同的平台上可能有很大的不同。这些功率成本在某些
情况下可以使用设备树数据来估算。在其它情况下，固件会更清楚。或者，用户空间可能
是最清楚的。以此类推。为了避免每一个客户端子系统对每一种可能的信息源自己重新
实现支持，EM框架作为一个抽象层介入，它在内核中对功率成本表的格式进行标准化，
因此能够避免多余的工作。

功率值可以用毫瓦或“抽象刻度”表示。多个子系统可能使用EM，由系统集成商来检查
功率值刻度类型的要求是否满足。可以在能量感知调度器的文档中找到一个例子
Documentation/scheduler/sched-energy.rst。对于一些子系统，比如热能或
powercap，用“抽象刻度”描述功率值可能会导致问题。这些子系统对过去使用的功率的
估算值更感兴趣，因此可能需要真实的毫瓦。这些要求的一个例子可以在智能功率分配
Documentation/driver-api/thermal/power_allocator.rst文档中找到。

内核子系统可能（基于EM内部标志位）实现了对EM注册设备是否具有不一致刻度的自动
检查。要记住的重要事情是，当功率值以“抽象刻度”表示时，从中推导以毫焦耳为单位
的真实能量消耗是不可能的。

下图描述了一个驱动的例子（这里是针对Arm的，但该方法适用于任何体系结构），它
向EM框架提供了功率成本，感兴趣的客户端可从中读取数据::

       +---------------+  +-----------------+  +---------------+
       | Thermal (IPA) |  | Scheduler (EAS) |  |     Other     |
       +---------------+  +-----------------+  +---------------+
               |                   | em_cpu_energy()   |
               |                   | em_cpu_get()      |
               +---------+         |         +---------+
                         |         |         |
                         v         v         v
                        +---------------------+
                        |    Energy Model     |
                        |     Framework       |
                        +---------------------+
                           ^       ^       ^
                           |       |       | em_dev_register_perf_domain()
                +----------+       |       +---------+
                |                  |                 |
        +---------------+  +---------------+  +--------------+
        |  cpufreq-dt   |  |   arm_scmi    |  |    Other     |
        +---------------+  +---------------+  +--------------+
                ^                  ^                 ^
                |                  |                 |
        +--------------+   +---------------+  +--------------+
        | Device Tree  |   |   Firmware    |  |      ?       |
        +--------------+   +---------------+  +--------------+

对于CPU设备，EM框架管理着系统中每个“性能域”的功率成本表。一个性能域是一组
性能一起伸缩的CPU。性能域通常与CPUFreq策略具有1对1映射。一个性能域中的
所有CPU要求具有相同的微架构。不同性能域中的CPU可以有不同的微架构。

2. 核心API
----------

2.1 配置选项
^^^^^^^^^^^^

必须使能CONFIG_ENERGY_MODEL才能使用EM框架。

2.2 性能域的注册
^^^^^^^^^^^^^^^^

“高级”EM的注册
~~~~~~~~~~~~~~~~

“高级”EM因它允许驱动提供更精确的功率模型而得名。它并不受限于框架中的一些已
实现的数学公式（就像“简单”EM那样）。它可以更好地反映每个性能状态的实际功率
测量。因此，在EM静态功率（漏电流功率）是重要的情况下，应该首选这种注册方式。

驱动程序应通过以下API将性能域注册到EM框架中::

  int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
		struct em_data_callback *cb, cpumask_t *cpus, bool milliwatts);

驱动程序必须提供一个回调函数，为每个性能状态返回<频率,功率>元组。驱动程序
提供的回调函数可以自由地从任何相关位置（DT、固件......）以及以任何被认为是
必要的方式获取数据。只有对于CPU设备，驱动程序必须使用cpumask指定性能域的CPU。
对于CPU以外的其他设备，最后一个参数必须被设置为NULL。

最后一个参数“milliwatts”（毫瓦）设置成正确的值是很重要的，使用EM的内核
子系统可能会依赖这个标志来检查所有的EM设备是否使用相同的刻度。如果有不同的
刻度，这些子系统可能决定：返回警告/错误，停止工作或崩溃（panic）。

关于实现这个回调函数的驱动程序的例子，参见第3节。或者在第2.4节阅读这个API
的更多文档。

“简单”EM的注册
~~~~~~~~~~~~~~~~

“简单”EM是用框架的辅助函数cpufreq_register_em_with_opp()注册的。它实现了
一个和以下数学公式紧密相关的功率模型::

	Power = C * V^2 * f

使用这种方法注册的EM可能无法正确反映真实设备的物理特性，例如当静态功率
（漏电流功率）很重要时。

2.3 访问性能域
^^^^^^^^^^^^^^

有两个API函数提供对能量模型的访问。em_cpu_get()以CPU id为参数，em_pd_get()
以设备指针为参数。使用哪个接口取决于子系统，但对于CPU设备来说，这两个函数都返
回相同的性能域。

对CPU的能量模型感兴趣的子系统可以通过em_cpu_get() API检索它。在创建性能域时
分配一次能量模型表，它保存在内存中不被修改。

一个性能域所消耗的能量可以使用em_cpu_energy() API来估算。该估算假定CPU设备
使用的CPUfreq监管器是schedutil。当前该计算不能提供给其它类型的设备。

关于上述API的更多细节可以在 ``<linux/energy_model.h>`` 或第2.4节中找到。

2.4 API的细节描述
^^^^^^^^^^^^^^^^^
参见 include/linux/energy_model.h 和 kernel/power/energy_model.c 的kernel doc。

3. 驱动示例
-----------

CPUFreq框架支持专用的回调函数，用于为指定的CPU（们）注册EM：
cpufreq_driver::register_em()。这个回调必须为每个特定的驱动程序正确实现，
因为框架会在设置过程中适时地调用它。本节提供了一个简单的例子，展示CPUFreq驱动
在能量模型框架中使用（假的）“foo”协议注册性能域。该驱动实现了一个est_power()
函数提供给EM框架::

  -> drivers/cpufreq/foo_cpufreq.c

  01	static int est_power(unsigned long *mW, unsigned long *KHz,
  02			struct device *dev)
  03	{
  04		long freq, power;
  05
  06		/* 使用“foo”协议设置频率上限 */
  07		freq = foo_get_freq_ceil(dev, *KHz);
  08		if (freq < 0);
  09			return freq;
  10
  11		/* 估算相关频率下设备的功率成本 */
  12		power = foo_estimate_power(dev, freq);
  13		if (power < 0);
  14			return power;
  15
  16		/* 将这些值返回给EM框架 */
  17		*mW = power;
  18		*KHz = freq;
  19
  20		return 0;
  21	}
  22
  23	static void foo_cpufreq_register_em(struct cpufreq_policy *policy)
  24	{
  25		struct em_data_callback em_cb = EM_DATA_CB(est_power);
  26		struct device *cpu_dev;
  27		int nr_opp;
  28
  29		cpu_dev = get_cpu_device(cpumask_first(policy->cpus));
  30
  31     	/* 查找该策略支持的OPP数量 */
  32     	nr_opp = foo_get_nr_opp(policy);
  33
  34     	/* 并注册新的性能域 */
  35     	em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus,
  36					    true);
  37	}
  38
  39	static struct cpufreq_driver foo_cpufreq_driver = {
  40		.register_em = foo_cpufreq_register_em,
  41	};


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/power/index.rst

:翻译:

  唐艺舟 Tang Yizhou <tangyeechou@gmail.com>

========
电源管理
========

.. toctree::
    :maxdepth: 1

    energy-model
    opp

TODOList:

    * apm-acpi
    * basic-pm-debugging
    * charger-manager
    * drivers-testing
    * freezing-of-tasks
    * pci
    * pm_qos_interface
    * power_supply_class
    * runtime_pm
    * s2ram
    * suspend-and-cpuhotplug
    * suspend-and-interrupts
    * swsusp-and-swap-files
    * swsusp-dmcrypt
    * swsusp
    * video
    * tricks

    * userland-swsusp

    * powercap/powercap
    * powercap/dtpm

    * regulator/consumer
    * regulator/design
    * regulator/machine
    * regulator/overview
    * regulator/regulator

.. only::  subproject and html

   Indices
   =======

   * :ref:`genindex`


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/power/opp.rst

:翻译:

  唐艺舟 Tang Yizhou <tangyeechou@gmail.com>

======================
操作性能值（OPP）库
======================

(C) 2009-2010 Nishanth Menon <nm@ti.com>, 德州仪器公司

.. 目录

  1. 简介
  2. OPP链表初始注册
  3. OPP搜索函数
  4. OPP可用性控制函数
  5. OPP数据检索函数
  6. 数据结构

1. 简介
=======

1.1 何为操作性能值（OPP）？
------------------------------

当今复杂的单片系统（SoC）由多个子模块组成，这些子模块会联合工作。在一个执行不同用例
的操作系统中，并不是SoC中的所有模块都需要一直以最高频率工作。为了促成这一点，SoC中
的子模块被分组为不同域，允许一些域以较低的电压和频率运行，而其它域则以较高的“电压/
频率对”运行。

设备按域支持的由频率电压对组成的离散的元组的集合，被称为操作性能值（组），或OPPs。

举例来说：

让我们考虑一个支持下述频率、电压值的内存保护单元（MPU）设备：
{300MHz，最低电压为1V}, {800MHz，最低电压为1.2V}, {1GHz，最低电压为1.3V}

我们能将它们表示为3个OPP，如下述{Hz, uV}元组（译注：频率的单位是赫兹，电压的单位是
微伏）。

- {300000000, 1000000}
- {800000000, 1200000}
- {1000000000, 1300000}

1.2 操作性能值库
----------------

OPP库提供了一组辅助函数来组织和查询OPP信息。该库位于drivers/opp/目录下，其头文件
位于include/linux/pm_opp.h中。OPP库可以通过开启CONFIG_PM_OPP来启用。某些SoC，
如德州仪器的OMAP框架允许在不需要cpufreq的情况下可选地在某一OPP下启动。

OPP库的典型用法如下::

 （用户）        -> 注册一个默认的OPP集合               -> （库）
 （SoC框架）     -> 在必要的情况下，对某些OPP进行修改     -> OPP layer
                -> 搜索/检索信息的查询                 ->

OPP层期望每个域由一个唯一的设备指针来表示。SoC框架在OPP层为每个设备注册了一组初始
OPP。这个链表的长度被期望是一个最优化的小数字，通常每个设备大约5个。初始链表包含了
一个OPP集合，这个集合被期望能在系统中安全使能。

关于OPP可用性的说明
^^^^^^^^^^^^^^^^^^^

随着系统的运行，SoC框架可能会基于各种外部因素选择让某些OPP在每个设备上可用或不可用，
示例：温度管理或其它异常场景中，SoC框架可能会选择禁用一个较高频率的OPP以安全地继续
运行，直到该OPP被重新启用（如果可能）。

OPP库在它的实现中达成了这个概念。以下操作函数只能对可用的OPP使用：
dev_pm_opp_find_freq_{ceil, floor}, dev_pm_opp_get_voltage,
dev_pm_opp_get_freq, dev_pm_opp_get_opp_count。

dev_pm_opp_find_freq_exact是用来查找OPP指针的，该指针可被用在dev_pm_opp_enable/
disable函数，使一个OPP在被需要时变为可用。

警告：如果对一个设备调用dev_pm_opp_enable/disable函数，OPP库的用户应该使用
dev_pm_opp_get_opp_count来刷新OPP的可用性计数。触发这些的具体机制，或者对有依赖的
子系统（比如cpufreq）的通知机制，都是由使用OPP库的SoC特定框架酌情处理的。在这些操作
中，同样需要注意刷新cpufreq表。

2. OPP链表初始注册
==================
SoC的实现会迭代调用dev_pm_opp_add函数来增加每个设备的OPP。预期SoC框架将以最优的
方式注册OPP条目 - 典型的数字范围小于5。通过注册OPP生成的OPP链表，在整个设备运行过程
中由OPP库维护。SoC框架随后可以使用dev_pm_opp_enable / disable函数动态地
控制OPP的可用性。

dev_pm_opp_add
	为设备指针所指向的特定域添加一个新的OPP。OPP是用频率和电压定义的。一旦完成
	添加，OPP被认为是可用的，可以用dev_pm_opp_enable/disable函数来控制其可用性。
	OPP库内部用dev_pm_opp结构体存储并管理这些信息。这个函数可以被SoC框架根据SoC
	的使用环境的需求来定义一个最优链表。

	警告：
		不要在中断上下文使用这个函数。

	示例::

	 soc_pm_init()
	 {
		/* 做一些事情 */
		r = dev_pm_opp_add(mpu_dev, 1000000, 900000);
		if (!r) {
			pr_err("%s: unable to register mpu opp(%d)\n", r);
			goto no_cpufreq;
		}
		/* 做一些和cpufreq相关的事情 */
	 no_cpufreq:
		/* 做剩余的事情 */
	 }

3. OPP搜索函数
==============
cpufreq等高层框架对频率进行操作，为了将频率映射到相应的OPP，OPP库提供了便利的函数
来搜索OPP库内部管理的OPP链表。这些搜索函数如果找到匹配的OPP，将返回指向该OPP的指针，
否则返回错误。这些错误预计由标准的错误检查，如IS_ERR()来处理，并由调用者采取适当的
行动。

这些函数的调用者应在使用完OPP后调用dev_pm_opp_put()。否则，OPP的内存将永远不会
被释放，并导致内存泄露。

dev_pm_opp_find_freq_exact
	根据 *精确的* 频率和可用性来搜索OPP。这个函数对默认不可用的OPP特别有用。
	例子：在SoC框架检测到更高频率可用的情况下，它可以使用这个函数在调用
	dev_pm_opp_enable之前找到OPP::

	 opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false);
	 dev_pm_opp_put(opp);
	 /* 不要操作指针.. 只是做有效性检查.. */
	 if (IS_ERR(opp)) {
		pr_err("frequency not disabled!\n");
		/* 触发合适的操作.. */
	 } else {
		dev_pm_opp_enable(dev,1000000000);
	 }

	注意：
	  这是唯一一个可以搜索不可用OPP的函数。

dev_pm_opp_find_freq_floor
	搜索一个 *最多* 提供指定频率的可用OPP。这个函数在搜索较小的匹配或按频率
	递减的顺序操作OPP信息时很有用。
	例子：要找的一个设备的最高OPP::

	 freq = ULONG_MAX;
	 opp = dev_pm_opp_find_freq_floor(dev, &freq);
	 dev_pm_opp_put(opp);

dev_pm_opp_find_freq_ceil
	搜索一个 *最少* 提供指定频率的可用OPP。这个函数在搜索较大的匹配或按频率
	递增的顺序操作OPP信息时很有用。
	例1：找到一个设备最小的OPP::

	 freq = 0;
	 opp = dev_pm_opp_find_freq_ceil(dev, &freq);
	 dev_pm_opp_put(opp);

	例: 一个SoC的cpufreq_driver->target的简易实现::

	 soc_cpufreq_target(..)
	 {
		/* 做策略检查等操作 */
		/* 找到和请求最接近的频率 */
		opp = dev_pm_opp_find_freq_ceil(dev, &freq);
		dev_pm_opp_put(opp);
		if (!IS_ERR(opp))
			soc_switch_to_freq_voltage(freq);
		else
			/* 当不能满足请求时，要做的事 */
		/* 做其它事 */
	 }

4. OPP可用性控制函数
====================
在OPP库中注册的默认OPP链表也许无法满足所有可能的场景。OPP库提供了一套函数来修改
OPP链表中的某个OPP的可用性。这使得SoC框架能够精细地动态控制哪一组OPP是可用于操作
的。设计这些函数的目的是在诸如考虑温度时 *暂时地* 删除某个OPP（例如，在温度下降
之前不要使用某OPP）。

警告：
	不要在中断上下文使用这些函数。

dev_pm_opp_enable
	使一个OPP可用于操作。
	例子：假设1GHz的OPP只有在SoC温度低于某个阈值时才可用。SoC框架的实现可能
	会选择做以下事情::

	 if (cur_temp < temp_low_thresh) {
		/* 若1GHz未使能，则使能 */
		opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false);
		dev_pm_opp_put(opp);
		/* 仅仅是错误检查 */
		if (!IS_ERR(opp))
			ret = dev_pm_opp_enable(dev, 1000000000);
		else
			goto try_something_else;
	 }

dev_pm_opp_disable
	使一个OPP不可用于操作。
	例子：假设1GHz的OPP只有在SoC温度高于某个阈值时才可用。SoC框架的实现可能
	会选择做以下事情::

	 if (cur_temp > temp_high_thresh) {
		/* 若1GHz已使能，则关闭 */
		opp = dev_pm_opp_find_freq_exact(dev, 1000000000, true);
		dev_pm_opp_put(opp);
		/* 仅仅是错误检查 */
		if (!IS_ERR(opp))
			ret = dev_pm_opp_disable(dev, 1000000000);
		else
			goto try_something_else;
	 }

5. OPP数据检索函数
==================
由于OPP库对OPP信息进行了抽象化处理，因此需要一组函数来从dev_pm_opp结构体中提取
信息。一旦使用搜索函数检索到一个OPP指针，以下函数就可以被SoC框架用来检索OPP层
内部描述的信息。

dev_pm_opp_get_voltage
	检索OPP指针描述的电压。
	例子: 当cpufreq切换到到不同频率时，SoC框架需要用稳压器框架将OPP描述
	的电压设置到提供电压的电源管理芯片中::

	 soc_switch_to_freq_voltage(freq)
	 {
		/* 做一些事情 */
		opp = dev_pm_opp_find_freq_ceil(dev, &freq);
		v = dev_pm_opp_get_voltage(opp);
		dev_pm_opp_put(opp);
		if (v)
			regulator_set_voltage(.., v);
		/* 做其它事 */
	 }

dev_pm_opp_get_freq
	检索OPP指针描述的频率。
	例子：比方说，SoC框架使用了几个辅助函数，通过这些函数，我们可以将OPP
	指针传入，而不是传入额外的参数，用来处理一系列数据参数::

	 soc_cpufreq_target(..)
	 {
		/* 做一些事情.. */
		 max_freq = ULONG_MAX;
		 max_opp = dev_pm_opp_find_freq_floor(dev,&max_freq);
		 requested_opp = dev_pm_opp_find_freq_ceil(dev,&freq);
		 if (!IS_ERR(max_opp) && !IS_ERR(requested_opp))
			r = soc_test_validity(max_opp, requested_opp);
		 dev_pm_opp_put(max_opp);
		 dev_pm_opp_put(requested_opp);
		/* 做其它事 */
	 }
	 soc_test_validity(..)
	 {
		 if(dev_pm_opp_get_voltage(max_opp) < dev_pm_opp_get_voltage(requested_opp))
			 return -EINVAL;
		 if(dev_pm_opp_get_freq(max_opp) < dev_pm_opp_get_freq(requested_opp))
			 return -EINVAL;
		/* 做一些事情.. */
	 }

dev_pm_opp_get_opp_count
	检索某个设备可用的OPP数量。
	例子：假设SoC中的一个协处理器需要知道某个表中的可用频率，主处理器可以
	按如下方式发出通知::

	 soc_notify_coproc_available_frequencies()
	 {
		/* 做一些事情 */
		num_available = dev_pm_opp_get_opp_count(dev);
		speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL);
		/* 按升序填充表 */
		freq = 0;
		while (!IS_ERR(opp = dev_pm_opp_find_freq_ceil(dev, &freq))) {
			speeds[i] = freq;
			freq++;
			i++;
			dev_pm_opp_put(opp);
		}

		soc_notify_coproc(AVAILABLE_FREQs, speeds, num_available);
		/* 做其它事 */
	 }

6. 数据结构
===========
通常，一个SoC包含多个可变电压域。每个域由一个设备指针描述。和OPP之间的关系可以
按以下方式描述::

  SoC
   |- device 1
   |	|- opp 1 (availability, freq, voltage)
   |	|- opp 2 ..
   ...	...
   |	`- opp n ..
   |- device 2
   ...
   `- device m

OPP库维护着一个内部链表，SoC框架使用上文描述的各个函数来填充和访问。然而，描述
真实OPP和域的结构体是OPP库自身的内部组成，以允许合适的抽象在不同系统中得到复用。

struct dev_pm_opp
	OPP库的内部数据结构，用于表示一个OPP。除了频率、电压、可用性信息外，
	它还包含OPP库运行所需的内部统计信息。指向这个结构体的指针被提供给
	用户（比如SoC框架）使用，在与OPP层的交互中作为OPP的标识符。

	警告：
	  结构体dev_pm_opp的指针不应该由用户解析或修改。一个实例的默认值由
	  dev_pm_opp_add填充，但OPP的可用性由dev_pm_opp_enable/disable函数
	  修改。

struct device
	这用于向OPP层标识一个域。设备的性质和它的实现是由OPP库的用户决定的，
	如SoC框架。

总体来说，以一个简化的视角看，对数据结构的操作可以描述为下面各图::

  初始化 / 修改:
              +-----+        /- dev_pm_opp_enable
  dev_pm_opp_add --> | opp | <-------
    |         +-----+        \- dev_pm_opp_disable
    \-------> domain_info(device)

  搜索函数:
               /-- dev_pm_opp_find_freq_ceil  ---\   +-----+
  domain_info<---- dev_pm_opp_find_freq_exact -----> | opp |
               \-- dev_pm_opp_find_freq_floor ---/   +-----+

  检索函数:
  +-----+     /- dev_pm_opp_get_voltage
  | opp | <---
  +-----+     \- dev_pm_opp_get_freq

  domain_info <- dev_pm_opp_get_opp_count


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: :ref:`Documentation/process/1.Intro.rst <development_process_intro>`

:Translator:

 时奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校译:

 吴想成 Wu XiangCheng <bobwxc@email.cn>

.. _cn_development_process_intro:

引言
====

内容提要
--------

本节的其余部分涵盖了内核开发的过程，以及开发人员及其雇主在这方面可能遇到的
各种问题。有很多原因使内核代码应被合并到正式的（“主线”）内核中，包括对用户
的自动可用性、多种形式的社区支持以及影响内核开发方向的能力。提供给Linux内核
的代码必须在与GPL兼容的许可证下可用。

:ref:`cn_development_process` 介绍了开发过程、内核发布周期和合并窗口的机制。
涵盖了补丁开发、审查和合并周期中的各个阶段。还有一些关于工具和邮件列表的讨论？
鼓励希望开始内核开发的开发人员跟踪并修复缺陷以作为初步练习。

: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年开始，这个内核已经发展成为一个最好的操作系统组件，
运行在袖珍数字音乐播放器、台式电脑、现存最大的超级计算机以及所有类型的系统上。
它是一种适用于几乎任何情况的健壮、高效和可扩展的解决方案。

随着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源代码
的法律问题，没有什么可以代替咨询了解这一领域的律师。依赖从技术邮件列表中获得
的答案是一件冒险的事情。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: :ref:`Documentation/process/2.Process.rst <development_process>`

:Translator:

 时奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校译:

 吴想成 Wu XiangCheng <bobwxc@email.cn>

.. _cn_development_process:

开发流程如何进行
================

90年代早期的Linux内核开发是一件相当松散的事情，涉及的用户和开发人员相对较少。
由于拥有数以百万计的用户群，且每年有大约2000名开发人员参与进来，内核因此必须
发展出许多既定流程来保证开发的顺利进行。要参与到流程中来，需要对此流程的进行
方式有一个扎实的理解。

总览
----

内核开发人员使用一个松散的基于时间的发布过程，每两到三个月发布一次新的主要
内核版本。最近的发布历史记录如下：

	======  =================
	5.0	2019年3月3日
	5.1	2019年5月5日
	5.2	2019年7月7日
	5.3	2019年9月15日
	5.4	2019年11月24日
	5.5	2020年1月6日
	======  =================

每个5.x版本都是一个主要的内核版本，具有新特性、内部API更改等等。一个典型的5.x
版本包含大约13000个变更集，变更了几十万行代码。因此，5.x是Linux内核开发的前
沿；内核使用滚动开发模型，不断集成重大变化。

对于每个版本的补丁合并，遵循一个相对简单的规则。在每个开发周期的开头，“合并
窗口”被打开。这时，被认为足够稳定（并且被开发社区接受）的代码被合并到主线内
核中。在这段时间内，新开发周期的大部分变更（以及所有主要变更）将以接近每天
1000次变更（“补丁”或“变更集”）的速度合并。

（顺便说一句，值得注意的是，合并窗口期间集成的更改并不是凭空产生的；它们是经
提前收集、测试和分级的。稍后将详细描述该过程的工作方式。）

合并窗口持续大约两周。在这段时间结束时，Linus Torvalds将声明窗口已关闭，并
释放第一个“rc”内核。例如，对于目标为5.6的内核，在合并窗口结束时发生的释放
将被称为5.6-rc1。-rc1 版本是一个信号，表示合并新特性的时间已经过去，稳定下一
个内核的时间已经到来。

在接下来的6到10周内，只有修复问题的补丁才应该提交给主线。有时会允许更大的
更改，但这种情况很少发生；试图在合并窗口外合并新功能的开发人员往往受不到
友好的接待。一般来说，如果您错过了给定特性的合并窗口，最好的做法是等待下一
个开发周期。（偶尔会对未支持硬件的驱动程序进行例外；如果它们不改变已有代码，
则不会导致回归，应该可以随时被安全地加入）。

随着修复程序进入主线，补丁速度将随着时间的推移而变慢。Linus大约每周发布一次
新的-rc内核；在内核被认为足够稳定并最终发布前，一般会达到-rc6到-rc9之间。
然后，整个过程又重新开始了。

例如，这里是5.4的开发周期进行情况（2019年）:

	==============  ==============================
	九月 15         5.3 稳定版发布
	九月 30         5.4-rc1 合并窗口关闭
	十月 6          5.4-rc2
	十月 13         5.4-rc3
	十月 20         5.4-rc4
	十月 27         5.4-rc5
	十一月 3        5.4-rc6
	十一月 10       5.4-rc7
	十一月 17       5.4-rc8
	十一月 24       5.4 稳定版发布
	==============  ==============================

开发人员如何决定何时结束开发周期并创建稳定版本？最重要的指标是以前版本的
回归列表。不欢迎出现任何错误，但是那些破坏了以前能工作的系统的错误被认为是
特别严重的。因此，导致回归的补丁是不受欢迎的，很可能在稳定期内删除。

开发人员的目标是在稳定发布之前修复所有已知的回归。在现实世界中，这种完美是
很难实现的；在这种规模的项目中，变数太多了。需要说明的是，延迟最终版本只会
使问题变得更糟；等待下一个合并窗口的更改将变多，导致下次出现更多的回归错误。
因此，大多数5.x内核都有一些已知的回归错误，不过，希望没有一个是严重的。

一旦一个稳定的版本发布，它的持续维护工作就被移交给“稳定团队”，目前由
Greg Kroah-Hartman领导。稳定团队将使用5.x.y编号方案不定期地发布稳定版本的
更新。要合入更新版本，补丁必须（1）修复一个重要的缺陷，且（2）已经合并到
下一个开发版本主线中。内核通常会在其初始版本后的一个以上的开发周期内收到
稳定版更新。例如，5.2内核的历史如下（2019年）：

	==============  ===============================
        七月 7 	        5.2 稳定版发布
	七月 13	        5.2.1
	七月 21	        5.2.2
	七月 26	        5.2.3
	七月 28	        5.2.4
	七月 31	        5.2.5
	...	        ...
	十月 11         5.2.21
	==============  ===============================

5.2.21是5.2版本的最终稳定更新。

有些内核被指定为“长期”内核；它们将得到更长时间的支持。在本文中，当前的长期
内核及其维护者是：

	======  ================================	================
	3.16	Ben Hutchings				（长期稳定内核）
	4.4	Greg Kroah-Hartman & Sasha Levin	（长期稳定内核）
	4.9	Greg Kroah-Hartman & Sasha Levin
	4.14	Greg Kroah-Hartman & Sasha Levin
	4.19	Greg Kroah-Hartman & Sasha Levin
	5.4	Greg Kroah-Hartman & Sasha Levin
	======  ================================	================

长期支持内核的选择纯粹是维护人员是否有需求和时间来维护该版本的问题。
目前还没有为即将发布的任何特定版本提供长期支持的已知计划。

补丁的生命周期
--------------

补丁不会直接从开发人员的键盘进入主线内核。相反，有一个稍微复杂（如果有些非
正式）的过程，旨在确保对每个补丁进行质量审查，并确保每个补丁实现了一个在主线
中需要的更改。对于小的修复，这个过程可能会很快完成，，而对于较大或有争议的
变更，可能会持续数年。许多开发人员的沮丧来自于对这个过程缺乏理解或者试图绕过它。

为了减少这种挫败，本文将描述补丁如何进入内核。下面的介绍以一种较为理想化的
方式描述了这个过程。更详细的过程将在后面的章节中介绍。

补丁通常要经历以下阶段：

- 设计。这就是补丁的真正需求——以及满足这些需求的方式——所在。设计工作通常
  是在不涉及社区的情况下完成的，但是如果可能的话，最好是在公开的情况下完成
  这项工作；这样可以节省很多稍后再重新设计的时间。

- 早期评审。补丁被发布到相关的邮件列表中，列表中的开发人员会回复他们可能有
  的任何评论。如果一切顺利的话，这个过程应该会发现补丁的任何主要问题。

- 更广泛的评审。当补丁接近准备好纳入主线时，它应该被相关的子系统维护人员
  接受——尽管这种接受并不能保证补丁会一直延伸到主线。补丁将出现在维护人员的
  子系统树中，并进入 -next 树（如下所述）。当流程进行时，此步骤将会对补丁
  进行更广泛的审查，并发现由于将此补丁与其他人所做的工作合并而导致的任何
  问题。

- 请注意，大多数维护人员也有日常工作，因此合并补丁可能不是他们的最优先工作。
  如果您的补丁得到了需要更改的反馈，那么您应该进行这些更改，或者解释为何
  不应该进行这些更改。如果您的补丁没有评审意见，也没有被其相应的子系统或
  驱动程序维护者接受，那么您应该坚持不懈地将补丁更新到当前内核使其可被正常
  应用，并不断地发送它以供审查和合并。

- 合并到主线。最终，一个成功的补丁将被合并到由LinusTorvalds管理的主线存储库
  中。此时可能会出现更多的评论和/或问题；对开发人员来说应对这些问题并解决
  出现的任何问题仍很重要。

- 稳定版发布。大量用户可能受此补丁影响，因此可能再次出现新的问题。

- 长期维护。虽然开发人员在合并代码后可能会忘记代码，但这种行为往往会给开发
  社区留下不良印象。合并代码消除了一些维护负担，因为其他人将修复由API更改
  引起的问题。但是，如果代码要长期保持可用，原始开发人员应该继续为代码负责。

内核开发人员（或他们的雇主）犯的最大错误之一是试图将流程简化为一个“合并到
主线”步骤。这种方法总是会让所有相关人员感到沮丧。

补丁如何进入内核
----------------

只有一个人可以将补丁合并到主线内核存储库中：Linus Torvalds。但是，在进入
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/目录，其中有许多驱动程序或文件系统的子目录
正在被添加到内核树中。它们在仍然需要更多的修正的时候可以保留在driver/staging/
目录中；一旦完成，就可以将它们移到内核中。这是一种跟踪不符合Linux内核编码或
质量标准的驱动程序的方法，人们可能希望使用它们并跟踪开发。

Greg Kroah Hartman 目前负责维护staging 树。仍需要修正的驱动程序将发送给他，
每个驱动程序在drivers/staging/中都有自己的子目录。除了驱动程序源文件之外，
目录中还应该有一个TODO文件。TODO文件列出了驱动程序需要接受的暂停的工作，
以及驱动程序的任何补丁都应该抄送的人员列表。当前的规则要求，staging的驱动
程序必须至少正确编译。

Staging 是一种让新的驱动程序进入主线的相对容易的方法，它们会幸运地引起其他
开发人员的注意，并迅速改进。然而，进入staging并不是故事的结尾；staging中
没有看到常规进展的代码最终将被删除。经销商也倾向于相对不愿意使用staging驱动
程序。因此，在成为一个合适的主线驱动的路上，staging 仅是一个中转站。

工具
----

从上面的文本可以看出，内核开发过程在很大程度上依赖于在不同方向上聚集补丁的
能力。如果没有适当强大的工具，整个系统将无法在任何地方正常工作。关于如何使用
这些工具的教程远远超出了本文档的范围，但还是用一点篇幅介绍一些关键点。

到目前为止，内核社区使用的主要源代码管理系统是git。Git是在自由软件社区中开发
的许多分布式版本控制系统之一。它非常适合内核开发，因为它在处理大型存储库和
大量补丁时性能非常好。它也以难以学习和使用而著称，尽管随着时间的推移它变得
更好了。对于内核开发人员来说，对Git的某种熟悉几乎是一种要求；即使他们不将它
用于自己的工作，他们也需要Git来跟上其他开发人员（以及主线）正在做的事情。

现在几乎所有的Linux发行版都打包了Git。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

不过，也有一些列表托管在别处；其中一些列表位于
redhat.com/mailman/listinfo。

当然，内核开发的核心邮件列表是linux-kernel。这个列表是一个令人生畏的地方：
每天的信息量可以达到500条，噪音很高，谈话技术性很强，且参与者并不总是表现出
高度的礼貌。但是，没有其他地方可以让内核开发社区作为一个整体聚集在一起；
不使用此列表的开发人员将错过重要信息。

以下一些提示可以帮助在linux-kernel生存：

- 将邮件转移到单独的文件夹，而不是主邮箱文件夹。我们必须能够持续地忽略洪流。

- 不要试图跟上每一次谈话——没人会这样。重要的是要筛选感兴趣的主题（但请注意
  长时间的对话可能会偏离原来的主题，尽管未改变电子邮件的主题）和参与的人。

- 不要回复挑事的人。如果有人试图激起愤怒，请忽略他们。

- 当回复Linux内核电子邮件（或其他列表上的电子邮件）时，请为所有相关人员保留
  Cc: 抄送头。如果没有确实的理由（如明确的请求），则不应删除收件人。一定要
  确保你要回复的人在抄送列表中。这个惯例也使你不必在回复邮件时明确要求被抄送。

- 在提出问题之前，搜索列表存档（和整个网络）。有些开发人员可能会对那些显然
  没有完成家庭作业的人感到不耐烦。

- 避免顶部回复（把你的答案放在你要回复的引文上面的做法）。这会让你的回答更难
  理解，印象也很差。

- 在正确的邮件列表发问。linux-kernel 可能是通用的讨论场所，但它不是寻找所有
  子系统开发人员的最佳场所。

最后一点——找到正确的邮件列表——是开发人员常出错的地方。在linux-kernel上
提出与网络相关的问题的人几乎肯定会收到一个礼貌的建议，转到netdev列表上提出，
因为这是大多数网络开发人员经常出现的列表。还有其他列表可用于scsi、video4linux、
ide、filesystem等子系统。查找邮件列表的最佳位置是与内核源代码一起打包的
MAINTAINERS文件。

开始内核开发
------------

关于如何开始内核开发过程的问题很常见——个人和公司皆然。同样常见的是失误，这
使得关系的开始比本应的更困难。

公司通常希望聘请知名的开发人员来启动开发团队。实际上，这是一种有效的技术。
但它也往往是昂贵的，而且对增加有经验的内核开发人员的数量没有多大帮助。考
虑到时间投入，可以让内部开发人员加快Linux内核的开发速度。利用这段时间可以
让雇主拥有一批既了解内核又了解公司的开发人员，还可以帮助培训其他人。从中期
来看，这通常是更有利可图的方法。

可以理解的是，单个开发人员往往对起步感到茫然。从一个大型项目开始可能会很
吓人；人们往往想先用一些较小的东西来试试水。由此，一些开发人员开始创建修补
拼写错误或轻微编码风格问题的补丁。不幸的是，这样的补丁会产生一定程度的噪音，
这会分散整个开发社区的注意力，因此，它们越来越被人不看重。希望向社区介绍
自己的新开发人员将无法通过这些方式获得他们期待的反响。

Andrew Morton 为有抱负的内核开发人员提供了如下建议

::

	所有内核开发者的第一个项目肯定应该是“确保内核在您可以操作的所有
	机器上始终完美运行”。通常的方法是和其他人一起解决问题（这可能需
	要坚持！），但就是如此——这是内核开发的一部分。

(http://lwn.net/articles/283982/)

在没有明显问题需要解决的情况下，通常建议开发人员查看当前的回归和开放缺陷
列表。从来都不缺少需要解决的问题；通过解决这些问题，开发人员将从该过程获得
经验，同时与开发社区的其他成员建立相互尊重。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: :ref:`Documentation/process/3.Early-stage.rst <development_early_stage>`

:Translator:

 时奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校译:

 吴想成 Wu XiangCheng <bobwxc@email.cn>

.. _cn_development_early_stage:

早期规划
========

当考虑一个Linux内核开发项目时，很可能会直接跳进去开始编码。然而，与任何重要
的项目一样，许多成功的基础最好是在第一行代码编写之前就打下。在早期计划和
沟通中花费一些时间可以在之后节省更多的时间。

搞清问题
--------

与任何工程项目一样，成功的内核改善从清晰描述要解决的问题开始。在某些情况
下，这个步骤很容易：例如当某个特定硬件需要驱动程序时。不过，在其他情况下，
很容易将实际问题与建议的解决方案混在一起，这可能会导致麻烦。

举个例子：几年前，Linux音频的开发人员寻求一种方法来运行应用程序，而不会因
系统延迟过大而导致退出或其他问题。他们得到的解决方案是一个连接到Linux安全
模块（LSM）框架中的内核模块；这个模块可以配置为允许特定的应用程序访问实时
调度程序。这个模块被实现并发到linux-kernel邮件列表，在那里它立即遇到了麻烦。

对于音频开发人员来说，这个安全模块足以解决他们当前的问题。但是，对于更广泛的
内核社区来说，这被视为对LSM框架的滥用（LSM框架并不打算授予他们原本不具备的
进程特权），并对系统稳定性造成风险。他们首选的解决方案包括短期的通过rlimit
机制进行实时调度访问，以及长期的减少延迟的工作。

然而，音频社区无法超越他们实施的特定解决方案来看问题；他们不愿意接受替代方案。
由此产生的分歧使这些开发人员对整个内核开发过程感到失望；其中一个开发人员返回
到audio列表并发布了以下内容：

	有很多非常好的Linux内核开发人员，但他们往往会被一群傲慢的傻瓜所压倒。
	试图向这些人传达用户需求是浪费时间。他们太“聪明”了，根本听不到少数
	人的话。

（http://lwn.net/articles/131776/）

实际情况却是不同的；与特定模块相比，内核开发人员更关心系统稳定性、长期维护
以及找到问题的正确解决方案。这个故事的寓意是把重点放在问题上——而不是具体的
解决方案上——并在开始编写代码之前与开发社区讨论这个问题。

因此，在考虑一个内核开发项目时，我们应该得到一组简短问题的答案：

 - 需要解决的问题究竟是什么？

 - 受此问题影响的用户有哪些？解决方案应该解决哪些使用案例？

 - 内核现在为何没能解决这个问题？

只有这样，才能开始考虑可能的解决方案。

早期讨论
--------

在计划内核开发项目时，在开始实施之前与社区进行讨论是很有意义的。早期沟通可以
通过多种方式节省时间和麻烦：

 - 很可能问题是由内核以您不理解的方式解决的。Linux内核很大，具有许多不明显
   的特性和功能。并不是所有的内核功能都像人们所希望的那样有文档记录，而且很
   容易遗漏一些东西。某作者发布了一个完整的驱动程序，重复了一个其不
   知道的现有驱动程序。重新发明现有轮子的代码不仅浪费，而且不会被接受到主线
   内核中。

 - 建议的解决方案中可能有一些要素不适合并入主线。在编写代码之前，最好先了解
   这样的问题。

 - 其他开发人员完全有可能考虑过这个问题；他们可能有更好的解决方案的想法，并且
   可能愿意帮助创建这个解决方案。

在内核开发社区的多年经验给了我们一个明确的教训：闭门设计和开发的内核代码总是
有一些问题，这些问题只有在代码发布到社区中时才会被发现。有时这些问题很严重，
需要数月或数年的努力才能使代码达到内核社区的标准。例如：

 - 设计并实现了单处理器系统的DeviceScape网络栈。只有使其适合于多处理器系统，
   才能将其合并到主线中。在代码中修改锁等等是一项困难的任务；因此，这段代码
   （现在称为mac80211）的合并被推迟了一年多。

 - Reiser4文件系统包含许多功能，核心内核开发人员认为这些功能应该在虚拟文件
   系统层中实现。它还包括一些特性，这些特性在不将系统暴露于用户引起的死锁的
   情况下是不容易实现的。这些问题过迟发现——以及拒绝处理其中一些问题——已经
   导致Reiser4置身主线内核之外。

 - Apparmor安全模块以被认为不安全和不可靠的方式使用内部虚拟文件系统数据结构。
   这种担心（包括其他）使Apparmor多年来无法进入主线。

在这些情况下，与内核开发人员的早期讨论，可以避免大量的痛苦和额外的工作。

找谁交流？
----------

当开发人员决定公开他们的计划时，下一个问题是：我们从哪里开始？答案是找到正确
的邮件列表和正确的维护者。对于邮件列表，最好的方法是在维护者(MAINTAINERS)文件
中查找要发布的相关位置。如果有一个合适的子系统列表，那么其上发布通常比在
linux-kernel上发布更可取；您更有可能接触到在相关子系统中具有专业知识的开发
人员，并且环境可能具支持性。

找到维护人员可能会有点困难。同样，维护者文件是开始的地方。但是，该文件往往不
是最新的，并且并非所有子系统都在那里显示。实际上，维护者文件中列出的人员可能
不是当前实际担任该角色的人员。因此，当对联系谁有疑问时，一个有用的技巧是使用
git（尤其是“git-log”）查看感兴趣的子系统中当前活动的用户。看看谁在写补丁、
谁会在这些补丁上加上Signed-off-by行签名（如有）。这些人将是帮助新开发项目的
最佳人选。

找到合适的维护者有时是非常具有挑战性的，以至于内核开发人员添加了一个脚本来
简化这个过程：

::

	.../scripts/get_maintainer.pl

当给定“-f”选项时，此脚本将返回指定文件或目录的当前维护者。如果在命令行上
给出了一个补丁，它将列出可能接收补丁副本的维护人员。有许多选项可以调节
get_maintainer.pl搜索维护者的严格程度；请小心使用更激进的选项，因为最终结果
可能会包括对您正在修改的代码没有真正兴趣的开发人员。

如果所有其他方法都失败了，那么与Andrew Morton交流是跟踪特定代码段维护人员
的一种有效方法。

何时邮寄？
----------

如果可能的话，在早期阶段发布你的计划只会更有帮助。描述正在解决的问题以及已经
制定的关于如何实施的任何计划。您可以提供的任何信息都可以帮助开发社区为项目
提供有用的输入。

在这个阶段可能发生的一件令人沮丧的事情不是得到反对意见，而是很少或根本没有
反馈。令人伤心的事实是：（1）内核开发人员往往很忙；（2）不缺少有宏伟计划但
代码（甚至代码设想）很少的人去支持他们；（3）没有人有义务审查或评论别人发表
的想法。除此之外，高层级的设计常常隐藏着一些问题，这些问题只有在有人真正尝试
实现这些设计时才会被发现；因此，内核开发人员宁愿看到代码。

如果发布请求评论（RFC）并没得到什么有用的评论，不要以为这意味着无人对此项目
有兴趣，同时你也不能假设你的想法没有问题。在这种情况下，最好的做法是继续进
行，把你的进展随时通知社区。

获得官方认可
-----------------------

如果您的工作是在公司环境中完成的，就像大多数Linux内核工作一样；显然，在您将
公司的计划或代码发布到公共邮件列表之前，必须获得有适当权利经理的许可。发布
不确定是否兼容GPL的代码尤其会带来问题；公司的管理层和法律人员越早能够就发布
内核开发项目达成一致，对参与的每个人都越好。

一些读者可能会认为他们的核心工作是为了支持还没有正式承认存在的产品。将雇主
的计划公布在公共邮件列表上可能不是一个可行的选择。在这种情况下，有必要考虑
保密是否真的是必要的；通常不需要把开发计划关在门内。

的确，有些情况下一家公司在开发过程的早期无法合法地披露其计划。拥有经验丰富
的内核开发人员的公司可能选择以开环的方式进行开发，前提是他们以后能够避免
严重的集成问题。对于没有这种内部专业知识的公司，最好的选择往往是聘请外部
开发者根据保密协议审查计划。Linux基金会运行了一个NDA程序，旨在帮助解决这种
情况；更多信息参见：

    http://www.linuxfoundation.org/nda/

这种审查通常足以避免以后出现严重问题，而无需公开披露项目。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: :ref:`Documentation/process/4.Coding.rst <development_coding>`

:Translator:

 时奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校译:

 吴想成 Wu XiangCheng <bobwxc@email.cn>

.. _cn_development_coding:

使代码正确
======================

虽然一个坚实的、面向社区的设计过程有很多值得说道的，但是任何内核开发项目工作
的证明都反映在代码中。它是将由其他开发人员检查并合并（或不合并）到主线树中
的代码。所以这段代码的质量决定了项目的最终成功。

本节将检查编码过程。我们将从内核开发人员常犯的几种错误开始。然后重点将转移
到正确的做法和相关有用的工具上。

陷阱
----

代码风格
********

内核长期以来都有其标准的代码风格，如
:ref:`Documentation/translations/zh_CN/process/coding-style.rst <cn_codingstyle>`
中所述。在多数时候，该文档中描述的准则至多被认为是建议性的。因此，内核中存在
大量不符合代码风格准则的代码。这种代码的存在会给内核开发人员带来两方面的危害。

首先，相信内核代码标准并不重要，也不强制执行。但事实上，如果没有按照标准
编写代码，那么新代码将很难加入到内核中；许多开发人员甚至会在审查代码之前要求
对代码进行重新格式化。一个像内核这么大的代码库需要一些统一格式的代码，以使
开发人员能够快速理解其中的任何部分。所以再也经不起奇怪格式的代码的折腾了。

内核的代码风格偶尔会与雇主的强制风格发生冲突。在这种情况下，必须在代码合并
之前遵从内核代码风格。将代码放入内核意味着以多种方式放弃一定程度的控制权——
包括控制代码样式。

另一个危害是认为已经在内核中的代码迫切需要修复代码样式。开发者可能会开始编写
重新格式化补丁，作为熟悉开发过程的一种方式，或者作为将其名字写入内核变更日志
的一种方式，或者两者兼而有之。但是纯代码风格的修复被开发社区视为噪音，它们往
往受到冷遇。因此，最好避免编写这种类型的补丁。在由于其他原因处理一段代码的
同时顺带修复其样式是很自然的，但是不应该仅为了更改代码样式而更改之。

代码风格文档也不应该被视为绝对不可违反的规则。如果有一个足够的理由反对这种
样式（例如为了80列限制拆分行会导致可读性大大降低），那么就这样做吧。

注意您还可以使用 ``clang-format`` 工具来帮助您处理这些规则，快速自动重新格式
化部分代码，和审阅完整的文件以发现代码样式错误、拼写错误和可能的改进。它还
可以方便地排序 ``#includes`` 、对齐变量/宏、重排文本和其他类似任务。有关详细
信息，请参阅文档 :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 KCFLAGS=-W”构建内核以
获得完整集合。

内核提供了几个配置选项，可以打开调试功能；大多数配置选项位于“kernel hacking”
子菜单中。对于任何用于开发或测试目的的内核，都应该启用其中几个选项。特别是，
您应该打开：

 - FRAME_WARN 获取大于给定数量的堆栈帧的警告。
   这些警告生成的输出可能比较冗长，但您不必担心来自内核其他部分的警告。

 - DEBUG_OBJECTS 将添加代码以跟踪内核创建的各种对象的生命周期，并在出现问题
   时发出警告。如果你要添加创建（和导出）关于其自己的复杂对象的子系统，请
   考虑打开对象调试基础结构的支持。

 - DEBUG_SLAB 可以发现各种内存分配和使用错误；它应该用于大多数开发内核。

 - DEBUG_SPINLOCK, DEBUG_ATOMIC_SLEEP 和 DEBUG_MUTEXES 会发现许多常见的
   锁错误。

还有很多其他调试选项，其中一些将在下面讨论。其中一些有显著的性能影响，不应
一直使用。在学习可用选项上花费一些时间，可能会在短期内得到许多回报。

其中一个较重的调试工具是锁检查器或“lockdep”。该工具将跟踪系统中每个锁
（spinlock或mutex）的获取和释放、获取锁的相对顺序、当前中断环境等等。然后，
它可以确保总是以相同的顺序获取锁，相同的中断假设适用于所有情况等等。换句话
说，lockdep可以找到许多导致系统死锁的场景。在部署的系统中，这种问题可能会
很痛苦（对于开发人员和用户而言）；LockDep允许提前以自动方式发现问题。具有
任何类型的非普通锁的代码在提交合并前应在启用lockdep的情况下运行测试。

作为一个勤奋的内核程序员，毫无疑问，您将检查任何可能失败的操作（如内存分配）
的返回状态。然而，事实上，最终的故障复现路径可能完全没有经过测试。未测试的
代码往往会出问题；如果所有这些错误处理路径都被执行了几次，那么您可能对代码
更有信心。

内核提供了一个可以做到这一点的错误注入框架，特别是在涉及内存分配的情况下。
启用故障注入后，内存分配的可配置失败的百分比；这些失败可以限定在特定的代码
范围内。在启用了故障注入的情况下运行，程序员可以看到当情况恶化时代码如何响
应。有关如何使用此工具的详细信息，请参阅
Documentation/fault-injection/fault-injection.rst。

“sparse”静态分析工具可以发现其他类型的错误。sparse可以警告程序员用户空间
和内核空间地址之间的混淆、大端序与小端序的混淆、在需要一组位标志的地方传递
整数值等等。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/

花一些时间安装和使用这些编译器将有助于避免以后的尴尬。

文档
----

文档通常比内核开发规则更为例外。即便如此，足够的文档将有助于简化将新代码合并
到内核中的过程，使其他开发人员的生活更轻松，并对您的用户有所帮助。在许多情况
下，添加文档已基本上是强制性的。

任何补丁的第一个文档是其关联的变更日志。日志条目应该描述正在解决的问题、解决
方案的形式、处理补丁的人员、对性能的任何相关影响，以及理解补丁可能需要的任何
其他内容。确保变更日志说明了*为什么*补丁值得应用；大量开发者未能提供这些信息。

任何添加新用户空间接口的代码——包括新的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更改时，应尽可能确保编译器捕获未更新的代码。这将帮助您确保找
到该接口的树内用处。它还将警告开发人员树外代码存在他们需要响应的更改。支持树外
代码不是内核开发人员需要担心的事情，但是我们也不必使树外开发人员的生活有不必要
的困难。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: :ref:`Documentation/process/5.Posting.rst <development_posting>`

:Translator:

 时奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校译:

 吴想成 Wu XiangCheng <bobwxc@email.cn>

.. _cn_development_posting:

发布补丁
========

您的工作迟早会准备好提交给社区进行审查，并最终包含到主线内核中。毫不稀奇，
内核开发社区已经发展出一套用于发布补丁的约定和过程；遵循这些约定和过程将使
参与其中的每个人的生活更加轻松。本文档试图描述这些约定的部分细节；更多信息
也可在以下文档中找到
:ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>`
和 :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”行，表明补丁作者。只有当你通过电子邮件发送别人的补丁时，这一行
   才是必须的，但是为防止疑问加上它也不会有什么坏处。

 - 一行描述，说明补丁的作用。对于在没有其他上下文的情况下看到该消息的读者来说，
   该消息应足以确定修补程序的范围；此行将显示在“short form（简短格式）”变更
   日志中。此消息通常需要先加上子系统名称前缀，然后是补丁的目的。例如：

   ::

        gpio: fix build on CONFIG_GPIO_SYSFS=n

 - 一行空白，后接补丁内容的详细描述。此描述可以是任意需要的长度；它应该说明补丁
   的作用以及为什么它应该应用于内核。

 - 一个或多个标记行，至少有一个由补丁作者的 Signed-off-by 签名。标记将在下面
   详细描述。

上面的项目一起构成补丁的变更日志。写一则好的变更日志是一门至关重要但常常被
忽视的艺术；值得花一点时间来讨论这个问题。当你编写变更日志时，你应该记住有
很多不同的人会读你的话。其中包括子系统维护人员和审查人员，他们需要决定是否
应该合并补丁，分销商和其他维护人员试图决定是否应该将补丁反向移植到其他内核，
缺陷搜寻人员想知道补丁是否导致他们正在追查的问题，以及想知道内核如何变化的
用户等等。一个好的变更日志以最直接和最简洁的方式向所有这些人传达所需的信息。

在结尾，总结行应该描述变更的影响和动机，以及在一行约束条件下可能发生的变化。
然后，详细的描述可以详述这些主题，并提供任何需要的附加信息。如果补丁修复了
一个缺陷，请引用引入该缺陷的提交（如果可能，请在引用提交时同时提供其 id 和
标题）。如果某个问题与特定的日志或编译器输出相关联，请包含该输出以帮助其他
人搜索同一问题的解决方案。如果更改是为了支持以后补丁中的其他更改，那么应当
说明。如果更改了内部API，请详细说明这些更改以及其他开发人员应该如何响应。
一般来说，你越把自己放在每个阅读你变更日志的人的位置上，变更日志（和内核
作为一个整体）就越好。

不需要说，变更日志是将变更提交到版本控制系统时使用的文本。接下来将是：

 - 补丁本身，采用统一的（“-u”）补丁格式。使用“-p”选项来diff将使函数名与
   更改相关联，从而使结果补丁更容易被其他人读取。

您应该避免在补丁中包括与更改不相关文件（例如，构建过程生成的文件或编辑器
备份文件）。文档目录中的“dontdiff”文件在这方面有帮助；使用“-X”选项将
其传递给diff。

上面提到的标签（tag）用于描述各种开发人员如何与这个补丁的开发相关联。
: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: 表示作者身份，所以每个共同开发人，必须紧跟在相关合作作者
   的Signed-off-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-kernel列表。

 - 如果您正在修复一个缺陷，请考虑该修复是否应进入下一个稳定更新。如果是这样，
   补丁副本也应发到stable@vger.kernel.org 。另外，在补丁本身的标签中添加一个
   “Cc: stable@vger.kernel.org”；这将使稳定版团队在修复进入主线时收到通知。

当为一个补丁选择接收者时，最好清楚你认为谁最终会接受这个补丁并将其合并。虽然
可以将补丁直接发给Linus Torvalds并让他合并，但通常情况下不会这样做。Linus很
忙，并且有子系统维护人员负责监视内核的特定部分。通常您会希望维护人员合并您的
补丁。如果没有明显的维护人员，Andrew Morton通常是最后的补丁接收者。

补丁需要好的主题行。补丁主题行的规范格式如下：

::

	[PATCH nn/mm] subsys: one-line description of the patch

其中“nn”是补丁的序号，“mm”是系列中补丁的总数，“subsys”是受影响子系统的
名称。当然，一个单独的补丁可以省略nn/mm。

如果您有一系列重要的补丁，那么通常发送一个简介作为第〇部分。不过，这个约定
并没有得到普遍遵循；如果您使用它，请记住简介中的信息不会进入内核变更日志。
因此，请确保补丁本身具有完整的变更日志信息。

一般来说，多部分补丁的第二部分和后续部分应作为对第一部分的回复发送，以便它们
在接收端都连接在一起。像git和coilt这样的工具有命令，可以通过适当的线程发送
一组补丁。但是，如果您有一长串补丁，并正使用git，请不要使用–-chain-reply-to
选项，以避免创建过深的嵌套。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: :ref:`Documentation/process/6.Followthrough.rst <development_followthrough>`

:Translator:

 时奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校译:

 吴想成 Wu XiangCheng <bobwxc@email.cn>

.. _cn_development_followthrough:

跟进
====

此时，您已经遵循了到目前为止给出的指导方针，并且，随着您自己的工程技能的增加，
已经发布了一系列完美的补丁。即使是经验丰富的内核开发人员也能犯的最大错误之一
是，认为他们的工作现在已经完成了。事实上，发布补丁意味着进入流程的下一个阶段，
可能还需要做很多工作。

一个补丁在首次发布时就非常出色、没有改进的余地，这是很罕见的。内核开发流程已
认识到这一事实，因此它非常注重对已发布代码的改进。作为代码的作者，您应该与
内核社区合作，以确保您的代码符合内核的质量标准。如果不参与这个过程，很可能会
无法将补丁合并到主线中。

与审阅者合作
------------

任何意义上的补丁都会导致其他开发人员在审查代码时发表大量评论。对于许多开发
人员来说，与审阅人员合作可能是内核开发过程中最令人生畏的部分。但是如果你
记住一些事情，生活会变得容易得多：

 - 如果你已经很好地解释了你的补丁，审阅人员会理解它的价值，以及为什么你会
   费尽心思去写它。但是这个并不能阻止他们提出一个基本的问题：在五年或十年后
   维护含有此代码的内核会怎么样？你可能被要求做出的许多改变——从编码风格的
   调整到大量的重写——都来自于对Linux的理解，即从现在起十年后，Linux仍将
   在开发中。

 - 代码审查是一项艰苦的工作，这是一项相对吃力不讨好的工作；人们记得谁编写了
   内核代码，但对于那些审查它的人来说，几乎没有什么长久的名声。因此，审阅
   人员可能会变得暴躁，尤其是当他们看到同样的错误被一遍又一遍地犯下时。如果
   你得到了一个看起来愤怒、侮辱或完全冒犯你的评论，请抑制以同样方式回应的冲动。
   代码审查是关于代码的，而不是关于人的，代码审阅人员不会亲自攻击您。

 - 同样，代码审阅人员也不想以牺牲你雇主的利益为代价来宣传他们雇主的议程。
   内核开发人员通常希望今后几年能在内核上工作，但他们明白他们的雇主可能会改
   变。他们真的，几乎毫无例外地，致力于创造他们所能做到的最好的内核；他们并
   没有试图给雇主的竞争对手造成不适。

所有这些归根结底就是，当审阅者向您发送评论时，您需要注意他们正在进行的技术
评论。不要让他们的表达方式或你自己的骄傲阻止此事。当你在一个补丁上得到评论
时，花点时间去理解评论人想说什么。如果可能的话，请修复审阅者要求您修复的内
容。然后回复审阅者：谢谢他们，并描述你将如何回答他们的问题。

请注意，您不必同意审阅者建议的每个更改。如果您认为审阅者误解了您的代码，请
解释到底发生了什么。如果您对建议的更改有技术上的异议，请描述它并证明您对该
问题的解决方案是正确的。如果你的解释有道理，审阅者会接受的。不过，如果你的
解释证明缺乏说服力，尤其是当其他人开始同意审稿人的观点时，请花些时间重新考虑
一下。你很容易对自己解决问题的方法视而不见，以至于你没有意识到某些东西完全
是错误的，或者你甚至没有解决正确的问题。

Andrew Morton建议，每一个不会导致代码更改的审阅评论都应该产生一个额外的代码
注释；这可以帮助未来的审阅人员避免第一次出现的问题。

一个致命的错误是忽视评论，希望它们会消失。它们不会走的。如果您在没有对之前
收到的评论做出响应的情况下重新发布代码，那么很可能会发现补丁毫无用处。

说到重新发布代码：请记住，审阅者不会记住您上次发布的代码的所有细节。因此，
提醒审阅人员以前提出的问题以及您如何处理这些问题总是一个好主意；补丁变更
日志是提供此类信息的好地方。审阅者不必搜索列表档案来熟悉上次所说的内容；
如果您帮助他们直接开始，当他们重新查看您的代码时，心情会更好。

如果你已经试着做正确的事情，但事情仍然没有进展呢？大多数技术上的分歧都可以
通过讨论来解决，但有时人们仍需要做出决定。如果你真的认为这个决定对你不利，
你可以试着向有更高权力的人上诉。对于本文，更高权力的人是 Andrew Morton 。
Andrew 在内核开发社区中非常受尊敬；他经常为似乎被绝望阻塞的事情清障。尽管
如此，不应轻易就直接找 Andrew ，也不应在所有其他替代方案都被尝试之前找他。
当然，记住，他也可能不同意你的意见。

接下来会发生什么
----------------

如果一个补丁被认为适合添加到内核中，并且大多数审查问题得到解决，下一步通常
是进入子系统维护人员的树中。工作方式因子系统而异；每个维护人员都有自己的
工作方式。特别是可能有不止一棵树——也许一棵树专门用于计划下一个合并窗口的
补丁，另一棵树用于长期工作。

对于应用到不属于明显子系统树（例如内存管理修补程序）的区域的修补程序，默认树
通常上溯到-mm。影响多个子系统的补丁也可以最终进入-mm树。

包含在子系统树中可以提高补丁的可见性。现在，使用该树的其他开发人员将默认获
得补丁。子系统树通常也为Linux提供支持，使其内容对整个开发社区可见。在这一点
上，您很可能会从一组新的审阅者那里得到更多的评论；这些评论需要像上一轮那样
得到回应。

在这时也会发生点什么，这取决于你的补丁的性质，是否与其他人正在做的工作发生
冲突。在最坏的情况下，严重的补丁冲突可能会导致一些工作被搁置，以便剩余的补丁
可以成形并合并。另一些时候，冲突解决将涉及到与其他开发人员合作，可能还会
在树之间移动一些补丁，以确保所有的应用都是干净的。这项工作可能是一件痛苦的
事情，但也需庆幸现在的幸福：在linux-next树出现之前，这些冲突通常只在合并窗口
中出现，必须迅速解决。现在可以在合并窗口打开之前的空闲时间解决这些问题。

有朝一日，如果一切顺利，您将登录并看到您的补丁已经合并到主线内核中。祝贺你！
然而，一旦庆祝完了（并且您已经将自己添加到维护人员文件中），就一定要记住
一个重要的小事实：工作仍然没有完成。并入主线也带来了它的挑战。

首先，补丁的可见性再次提高。可能会有以前不知道这个补丁的开发者的新一轮评论。
忽略它们可能很有诱惑力，因为您的代码不再存在任何被合并的问题。但是，要抵制
这种诱惑，您仍然需要对有问题或建议的开发人员作出响应。

不过，更重要的是：将代码包含在主线中会将代码交给更多的一些测试人员。即使您
为尚未可用的硬件提供了驱动程序，您也会惊讶于有多少人会将您的代码构建到内核
中。当然，如果有测试人员，也可能会有错误报告。

最糟糕的错误报告是回归。如果你的补丁导致回归，你会发现多到让你不舒服的眼睛盯
着你；回归需要尽快修复。如果您不愿意或无法修复回归（其他人都不会为您修复），
那么在稳定期内，您的补丁几乎肯定会被移除。除了否定您为使补丁进入主线所做的
所有工作之外，如果由于未能修复回归而取消补丁，很可能会使将来的工作更难被合并。

在处理完任何回归之后，可能还有其他普通缺陷需要处理。稳定期是修复这些错误并
确保代码在主线内核版本中的首次发布尽可能可靠的最好机会。所以，请回应错误
报告，并尽可能解决问题。这就是稳定期的目的；一旦解决了旧补丁的任何问题，就
可以开始尽情创建新补丁。

别忘了，还有其他节点也可能会创建缺陷报告：下一个主线稳定版本，当著名的发行
商选择包含您补丁的内核版本时等等。继续响应这些报告是您工作的基本素养。但是
如果这不能提供足够的动机，那么也需要考虑：开发社区会记住那些在合并后对代码
失去兴趣的开发人员。下一次你发布补丁时，他们会以你以后不会持续维护它为前提
来评估它。

其他可能发生的事情
------------------

某天，当你打开你的邮件客户端时，看到有人给你寄了一个代码补丁。毕竟，这是
让您的代码公开存在的好处之一。如果您同意这个补丁，您可以将它转发给子系统
维护人员（确保包含一个正确的From:行，这样属性是正确的，并添加一个您自己的
signoff ），或者回复一个 Acked-by: 让原始发送者向上发送它。

如果您不同意补丁，请礼貌地回复，解释原因。如果可能的话，告诉作者需要做哪些
更改才能让您接受补丁。合并代码的编写者和维护者所反对的补丁的确存在着一定的
阻力，但仅此而已。如果你被认为不必要的阻碍了好的工作，那么这些补丁最终会
绕过你并进入主线。在Linux内核中，没有人对任何代码拥有绝对的否决权。可能除
了Linus。

在非常罕见的情况下，您可能会看到完全不同的东西：另一个开发人员发布了针对您
的问题的不同解决方案。在这时，两个补丁之一可能不会被合并，“我的补丁首先
发布”不被认为是一个令人信服的技术论据。如果有别人的补丁取代了你的补丁而进
入了主线，那么只有一种方法可以回应你：很高兴你的问题解决了，请继续工作吧。
以这种方式把某人的工作推到一边可能导致伤心和气馁，但是社区会记住你的反应，
即使很久以后他们已经忘记了谁的补丁真正被合并。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: :ref:`Documentation/process/7.AdvancedTopics.rst <development_advancedtopics>`

:Translator:

 时奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校译:

 吴想成 Wu XiangCheng <bobwxc@email.cn>

.. _cn_development_advancedtopics:

高级主题
========

现在，希望您能够掌握开发流程的工作方式。然而，还有更多的东西要学！本节将介绍
一些主题，这些主题对希望成为Linux内核开发过程常规部分的开发人员有帮助。

使用Git管理补丁
---------------

内核使用分布式版本控制始于2002年初，当时Linus首次开始使用专有的Bitkeeper应用
程序。虽然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的新用户应该了解引用、远程分支、索引、
快进合并、推拉、游离头等。一开始可能有点吓人，但这些概念不难通过一点学习来
理解。

使用git生成通过电子邮件提交的补丁是提高速度的一个很好的练习。

当您准备好开始建立Git树供其他人查看时，无疑需要一个可以从中拉取的服务器。
如果您有一个可以访问因特网的系统，那么使用git-daemon设置这样的服务器相对
简单。同时，免费的公共托管网站（例如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:”行。

请求拉取时，请务必提供所有相关信息：树的位置、要拉取的分支以及拉取将导致的
更改。在这方面 git request-pull 命令非常有用；它将按照其他开发人员所期望的
格式化请求，并检查以确保您已记得将这些更改推送到公共服务器。

审阅补丁
--------

一些读者显然会反对将本节与“高级主题”放在一起，因为即使是刚开始的内核开发人员
也应该审阅补丁。当然，没有比查看其他人发布的代码更好的方法来学习如何在内核环境
中编程了。此外，审阅者永远供不应求；通过审阅代码，您可以对整个流程做出重大贡献。

审查代码可能是一副令人生畏的图景，特别是对一个新的内核开发人员来说，他们
可能会对公开询问代码感到紧张，而这些代码是由那些有更多经验的人发布的。不过，
即使是最有经验的开发人员编写的代码也可以得到改进。也许对（所有）审阅者最好
的建议是：把审阅评论当成问题而不是批评。询问“在这条路径中如何释放锁？”
总是比说“这里的锁是错误的”更好。

不同的开发人员将从不同的角度审查代码。部分人会主要关注代码风格以及代码行是
否有尾随空格。其他人会主要关注补丁作为一个整体实现的变更是否对内核有好处。
同时也有人会检查是否存在锁问题、堆栈使用过度、可能的安全问题、在其他地方
发现的代码重复、足够的文档、对性能的不利影响、用户空间ABI更改等。所有类型
的检查，只要它们能引导更好的代码进入内核，都是受欢迎和值得的。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: :ref:`Documentation/process/8.Conclusion.rst <development_conclusion>`
:Translator:

 时奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校译:

 吴想成 Wu XiangCheng <bobwxc@email.cn>

.. _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>`
也是所有内核开发人员都应该阅读的内容。许多内部内核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生态系统中的大多数其他参与者可以通过为内核做出贡献而受益。使
代码进入主线是提高代码质量、降低维护和分发成本、提高对内核开发方向的影响程度
等的关键。这是一种共赢的局面。启动你的编辑器，来加入我们吧；你会非常受欢迎的。


code:
.. 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协商，例如请求
和接收有关内核社区的信息。

委员会做出的任何决定都将提交到表中，以便在必要时与相关维护人员一起执行。行为
准则委员会的决定可以通过三分之二的投票推翻。

每季度，行为准则委员会和标签将提供一份报告，概述行为准则委员会收到的匿名报告
及其状态，以及任何否决决定的细节，包括完整和可识别的投票细节。

我们希望在启动期之后为行为准则委员会人员配备建立一个不同的流程。发生此情况时，
将使用该信息更新此文档。


code:
.. 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`