diff options
Diffstat (limited to 'Documentation/translations/zh_CN')
80 files changed, 4836 insertions, 1124 deletions
diff --git a/Documentation/translations/zh_CN/PCI/msi-howto.rst b/Documentation/translations/zh_CN/PCI/msi-howto.rst index 1b9b5ea790d8..95baadf767e4 100644 --- a/Documentation/translations/zh_CN/PCI/msi-howto.rst +++ b/Documentation/translations/zh_CN/PCI/msi-howto.rst @@ -88,7 +88,7 @@ MSI功能。 如果设备对最小数量的向量有要求,驱动程序可以传递一个min_vecs参数,设置为这个限制, 如果PCI核不能满足最小数量的向量,将返回-ENOSPC。 -flags参数用来指定设备和驱动程序可以使用哪种类型的中断(PCI_IRQ_LEGACY, PCI_IRQ_MSI, +flags参数用来指定设备和驱动程序可以使用哪种类型的中断(PCI_IRQ_INTX, PCI_IRQ_MSI, PCI_IRQ_MSIX)。一个方便的短语(PCI_IRQ_ALL_TYPES)也可以用来要求任何可能的中断类型。 如果PCI_IRQ_AFFINITY标志被设置,pci_alloc_irq_vectors()将把中断分散到可用的CPU上。 diff --git a/Documentation/translations/zh_CN/PCI/pci.rst b/Documentation/translations/zh_CN/PCI/pci.rst index 83c2a41d38d3..347f5c3f5ce9 100644 --- a/Documentation/translations/zh_CN/PCI/pci.rst +++ b/Documentation/translations/zh_CN/PCI/pci.rst @@ -304,7 +304,7 @@ MSI-X可以分配几个单独的向量。 的PCI_IRQ_MSI和/或PCI_IRQ_MSIX标志来启用MSI功能。这将导致PCI支持将CPU向量数 据编程到PCI设备功能寄存器中。许多架构、芯片组或BIOS不支持MSI或MSI-X,调用 ``pci_alloc_irq_vectors`` 时只使用PCI_IRQ_MSI和PCI_IRQ_MSIX标志会失败, -所以尽量也要指定 ``PCI_IRQ_LEGACY`` 。 +所以尽量也要指定 ``PCI_IRQ_INTX`` 。 对MSI/MSI-X和传统INTx有不同中断处理程序的驱动程序应该在调用 ``pci_alloc_irq_vectors`` 后根据 ``pci_dev``结构体中的 ``msi_enabled`` diff --git a/Documentation/translations/zh_CN/PCI/pciebus-howto.rst b/Documentation/translations/zh_CN/PCI/pciebus-howto.rst index 65c4301f12cd..c6ffda62af21 100644 --- a/Documentation/translations/zh_CN/PCI/pciebus-howto.rst +++ b/Documentation/translations/zh_CN/PCI/pciebus-howto.rst @@ -124,7 +124,7 @@ pcie_port_service_unregister取代了Linux驱动模型的pci_unregister_driver static struct pcie_port_service_driver root_aerdrv = { .name = (char *)device_name, - .id_table = &service_id[0], + .id_table = service_id, .probe = aerdrv_load, .remove = aerdrv_unload, diff --git a/Documentation/translations/zh_CN/admin-guide/bug-hunting.rst b/Documentation/translations/zh_CN/admin-guide/bug-hunting.rst index decb9b26d2f1..c3f6a83294dc 100644 --- a/Documentation/translations/zh_CN/admin-guide/bug-hunting.rst +++ b/Documentation/translations/zh_CN/admin-guide/bug-hunting.rst @@ -329,12 +329,3 @@ sysklogd发行版附带了一个补丁,它修改了 ``modules-2.0.0`` 包, 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 diff --git a/Documentation/translations/zh_CN/admin-guide/index.rst b/Documentation/translations/zh_CN/admin-guide/index.rst index ac2960da33e6..15d9ab5993a7 100644 --- a/Documentation/translations/zh_CN/admin-guide/index.rst +++ b/Documentation/translations/zh_CN/admin-guide/index.rst @@ -37,7 +37,6 @@ Todolist: reporting-issues reporting-regressions - security-bugs bug-hunting bug-bisect tainted-kernels @@ -68,6 +67,7 @@ Todolist: cpu-load cputopology lockup-watchdogs + numastat unicode sysrq mm/index @@ -109,7 +109,6 @@ Todolist: * module-signing * mono * namespaces/index -* numastat * parport * perf-security * pm/index diff --git a/Documentation/translations/zh_CN/admin-guide/mm/damon/start.rst b/Documentation/translations/zh_CN/admin-guide/mm/damon/start.rst index bf21ff84f396..cff7b6f98c59 100644 --- a/Documentation/translations/zh_CN/admin-guide/mm/damon/start.rst +++ b/Documentation/translations/zh_CN/admin-guide/mm/damon/start.rst @@ -15,7 +15,7 @@ 本文通过演示DAMON的默认用户空间工具,简要地介绍了如何使用DAMON。请注意,为了简洁 起见,本文档只描述了它的部分功能。更多细节请参考该工具的使用文档。 -`doc <https://github.com/awslabs/damo/blob/next/USAGE.md>`_ . +`doc <https://github.com/damonitor/damo/blob/next/USAGE.md>`_ . 前提条件 @@ -31,7 +31,7 @@ ------------ 在演示中,我们将使用DAMON的默认用户空间工具,称为DAMON Operator(DAMO)。它可以在 -https://github.com/awslabs/damo找到。下面的例子假设DAMO在你的$PATH上。当然,但 +https://github.com/damonitor/damo找到。下面的例子假设DAMO在你的$PATH上。当然,但 这并不是强制性的。 因为DAMO使用了DAMON的sysfs接口(详情请参考:doc:`usage`),你应该确保 diff --git a/Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst b/Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst index da2745464ece..9d7cb51be493 100644 --- a/Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst +++ b/Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst @@ -16,22 +16,17 @@ DAMON 为不同的用户提供了下面这些接口。 - *DAMON用户空间工具。* - `这 <https://github.com/awslabs/damo>`_ 为有这特权的人, 如系统管理员,希望有一个刚好 + `这 <https://github.com/damonitor/damo>`_ 为有这特权的人, 如系统管理员,希望有一个刚好 可以工作的人性化界面。 使用它,用户可以以人性化的方式使用DAMON的主要功能。不过,它可能不会为特殊情况进行高度调整。 它同时支持虚拟和物理地址空间的监测。更多细节,请参考它的 `使用文档 - <https://github.com/awslabs/damo/blob/next/USAGE.md>`_。 + <https://github.com/damonitor/damo/blob/next/USAGE.md>`_。 - *sysfs接口。* :ref:`这 <sysfs_interface>` 是为那些希望更高级的使用DAMON的特权用户空间程序员准备的。 使用它,用户可以通过读取和写入特殊的sysfs文件来使用DAMON的主要功能。因此,你可以编写和使 用你个性化的DAMON sysfs包装程序,代替你读/写sysfs文件。 `DAMON用户空间工具 - <https://github.com/awslabs/damo>`_ 就是这种程序的一个例子 它同时支持虚拟和物理地址 - 空间的监测。注意,这个界面只提供简单的监测结果 :ref:`统计 <damos_stats>`。对于详细的监测 - 结果,DAMON提供了一个:ref:`跟踪点 <tracepoint>`。 -- *debugfs interface.* - :ref:`这 <debugfs_interface>` 几乎与:ref:`sysfs interface <sysfs_interface>` 接 - 口相同。这将在下一个LTS内核发布后被移除,所以用户应该转移到 - :ref:`sysfs interface <sysfs_interface>`。 + <https://github.com/damonitor/damo>`_ 就是这种程序的一个例子 它同时支持虚拟和物理地址 + 空间的监测。 - *内核空间编程接口。* :doc:`这 </mm/damon/api>` 这是为内核空间程序员准备的。使用它,用户可以通过为你编写内 核空间的DAMON应用程序,最灵活有效地利用DAMON的每一个功能。你甚至可以为各种地址空间扩展DAMON。 @@ -332,250 +327,9 @@ tried_regions/<N>/ # echo 500 > watermarks/mid # echo 300 > watermarks/low -请注意,我们强烈建议使用用户空间的工具,如 `damo <https://github.com/awslabs/damo>`_ , +请注意,我们强烈建议使用用户空间的工具,如 `damo <https://github.com/damonitor/damo>`_ , 而不是像上面那样手动读写文件。以上只是一个例子。 -debugfs接口 -=========== - -.. note:: - - DAMON debugfs接口将在下一个LTS内核发布后被移除,所以用户应该转移到 - :ref:`sysfs接口<sysfs_interface>`。 - -DAMON导出了八个文件, ``attrs``, ``target_ids``, ``init_regions``, -``schemes``, ``monitor_on_DEPRECATED``, ``kdamond_pid``, ``mk_contexts`` 和 -``rm_contexts`` under its debugfs directory, ``<debugfs>/damon/``. - - -属性 ----- - -用户可以通过读取和写入 ``attrs`` 文件获得和设置 ``采样间隔`` 、 ``聚集间隔`` 、 ``更新间隔`` -以及监测目标区域的最小/最大数量。要详细了解监测属性,请参考 `:doc:/mm/damon/design` 。例如, -下面的命令将这些值设置为5ms、100ms、1000ms、10和1000,然后再次检查:: - - # cd <debugfs>/damon - # echo 5000 100000 1000000 10 1000 > attrs - # cat attrs - 5000 100000 1000000 10 1000 - - -目标ID ------- - -一些类型的地址空间支持多个监测目标。例如,虚拟内存地址空间的监测可以有多个进程作为监测目标。用户 -可以通过写入目标的相关id值来设置目标,并通过读取 ``target_ids`` 文件来获得当前目标的id。在监 -测虚拟地址空间的情况下,这些值应该是监测目标进程的pid。例如,下面的命令将pid为42和4242的进程设 -为监测目标,并再次检查:: - - # cd <debugfs>/damon - # echo 42 4242 > target_ids - # cat target_ids - 42 4242 - -用户还可以通过在文件中写入一个特殊的关键字 "paddr\n" 来监测系统的物理内存地址空间。因为物理地 -址空间监测不支持多个目标,读取文件会显示一个假值,即 ``42`` ,如下图所示:: - - # cd <debugfs>/damon - # echo paddr > target_ids - # cat target_ids - 42 - -请注意,设置目标ID并不启动监测。 - - -初始监测目标区域 ----------------- - -在虚拟地址空间监测的情况下,DAMON自动设置和更新监测的目标区域,这样就可以覆盖目标进程的整个 -内存映射。然而,用户可能希望将监测区域限制在特定的地址范围内,如堆、栈或特定的文件映射区域。 -或者,一些用户可以知道他们工作负载的初始访问模式,因此希望为“自适应区域调整”设置最佳初始区域。 - -相比之下,DAMON在物理内存监测的情况下不会自动设置和更新监测目标区域。因此,用户应该自己设置 -监测目标区域。 - -在这种情况下,用户可以通过在 ``init_regions`` 文件中写入适当的值,明确地设置他们想要的初 -始监测目标区域。输入应该是一个由三个整数组成的队列,用空格隔开,代表一个区域的形式如下:: - - <target idx> <start address> <end address> - -目标idx应该是 ``target_ids`` 文件中目标的索引,从 ``0`` 开始,区域应该按照地址顺序传递。 -例如,下面的命令将设置几个地址范围, ``1-100`` 和 ``100-200`` 作为pid 42的初始监测目标 -区域,这是 ``target_ids`` 中的第一个(索引 ``0`` ),另外几个地址范围, ``20-40`` 和 -``50-100`` 作为pid 4242的地址,这是 ``target_ids`` 中的第二个(索引 ``1`` ):: - - # cd <debugfs>/damon - # cat target_ids - 42 4242 - # echo "0 1 100 \ - 0 100 200 \ - 1 20 40 \ - 1 50 100" > init_regions - -请注意,这只是设置了初始的监测目标区域。在虚拟内存监测的情况下,DAMON会在一个 ``更新间隔`` -后自动更新区域的边界。因此,在这种情况下,如果用户不希望更新的话,应该把 ``更新间隔`` 设 -置得足够大。 - - -方案 ----- - -对于通常的基于DAMON的数据访问感知的内存管理优化,用户只是希望系统对特定访问模式的内存区域应用内 -存管理操作。DAMON从用户那里接收这种形式化的操作方案,并将这些方案应用到目标进程中。 - -用户可以通过读取和写入 ``scheme`` debugfs文件来获得和设置这些方案。读取该文件还可以显示每个 -方案的统计数据。在文件中,每一个方案都应该在每一行中以下列形式表示出来:: - - <target access pattern> <action> <quota> <watermarks> - -你可以通过简单地在文件中写入一个空字符串来禁用方案。 - -目标访问模式 -~~~~~~~~~~~~ - -``<目标访问模式>`` 是由三个范围构成的,形式如下:: - - min-size max-size min-acc max-acc min-age max-age - -具体来说,区域大小的字节数( `min-size` 和 `max-size` ),访问频率的每聚合区间的监测访问次 -数( `min-acc` 和 `max-acc` ),区域年龄的聚合区间数( `min-age` 和 `max-age` )都被指定。 -请注意,这些范围是封闭区间。 - -动作 -~~~~ - -``<action>`` 是一个预定义的内存管理动作的整数,DAMON将应用于具有目标访问模式的区域。支持 -的数字和它们的含义如下:: - - - 0: Call ``madvise()`` for the region with ``MADV_WILLNEED`` - - 1: Call ``madvise()`` for the region with ``MADV_COLD`` - - 2: Call ``madvise()`` for the region with ``MADV_PAGEOUT`` - - 3: Call ``madvise()`` for the region with ``MADV_HUGEPAGE`` - - 4: Call ``madvise()`` for the region with ``MADV_NOHUGEPAGE`` - - 5: Do nothing but count the statistics - -配额 -~~~~ - -每个 ``动作`` 的最佳 ``目标访问模式`` 取决于工作负载,所以不容易找到。更糟糕的是,将某个 -动作的方案设置得过于激进会导致严重的开销。为了避免这种开销,用户可以通过下面表格中的 ``<quota>`` -来限制方案的时间和大小配额:: - - <ms> <sz> <reset interval> <priority weights> - -这使得DAMON在 ``<reset interval>`` 毫秒内,尽量只用 ``<ms>`` 毫秒的时间对 ``目标访 -问模式`` 的内存区域应用动作,并在 ``<reset interval>`` 内只对最多<sz>字节的内存区域应 -用动作。将 ``<ms>`` 和 ``<sz>`` 都设置为零,可以禁用配额限制。 - -当预计超过配额限制时,DAMON会根据 ``目标访问模式`` 的大小、访问频率和年龄,对发现的内存 -区域进行优先排序。为了实现个性化的优先级,用户可以在 ``<优先级权重>`` 中设置这三个属性的 -权重,具体形式如下:: - - <size weight> <access frequency weight> <age weight> - -水位 -~~~~ - -有些方案需要根据系统特定指标的当前值来运行,如自由内存比率。对于这种情况,用户可以为该条 -件指定水位。:: - - <metric> <check interval> <high mark> <middle mark> <low mark> - -``<metric>`` 是一个预定义的整数,用于要检查的度量。支持的数字和它们的含义如下。 - - - 0: 忽视水位 - - 1: 系统空闲内存率 (千分比) - -每隔 ``<检查间隔>`` 微秒检查一次公制的值。 - -如果该值高于 ``<高标>`` 或低于 ``<低标>`` ,该方案被停用。如果该值低于 ``<中标>`` , -该方案将被激活。 - -统计数据 -~~~~~~~~ - -它还统计每个方案被尝试应用的区域的总数量和字节数,每个方案被成功应用的区域的两个数量,以 -及超过配额限制的总数量。这些统计数据可用于在线分析或调整方案。 - -统计数据可以通过读取方案文件来显示。读取该文件将显示你在每一行中输入的每个 ``方案`` , -统计的五个数字将被加在每一行的末尾。 - -例子 -~~~~ - -下面的命令应用了一个方案:”如果一个大小为[4KiB, 8KiB]的内存区域在[10, 20]的聚合时间 -间隔内显示出每一个聚合时间间隔[0, 5]的访问量,请分页出该区域。对于分页,每秒最多只能使 -用10ms,而且每秒分页不能超过1GiB。在这一限制下,首先分页出具有较长年龄的内存区域。另外, -每5秒钟检查一次系统的可用内存率,当可用内存率低于50%时开始监测和分页,但如果可用内存率 -大于60%,或低于30%,则停止监测“:: - - # cd <debugfs>/damon - # scheme="4096 8192 0 5 10 20 2" # target access pattern and action - # scheme+=" 10 $((1024*1024*1024)) 1000" # quotas - # scheme+=" 0 0 100" # prioritization weights - # scheme+=" 1 5000000 600 500 300" # watermarks - # echo "$scheme" > schemes - - -开关 ----- - -除非你明确地启动监测,否则如上所述的文件设置不会产生效果。你可以通过写入和读取 ``monitor_on_DEPRECATED`` -文件来启动、停止和检查监测的当前状态。写入 ``on`` 该文件可以启动对有属性的目标的监测。写入 -``off`` 该文件则停止这些目标。如果每个目标进程被终止,DAMON也会停止。下面的示例命令开启、关 -闭和检查DAMON的状态:: - - # cd <debugfs>/damon - # echo on > monitor_on_DEPRECATED - # echo off > monitor_on_DEPRECATED - # cat monitor_on_DEPRECATED - off - -请注意,当监测开启时,你不能写到上述的debugfs文件。如果你在DAMON运行时写到这些文件,将会返 -回一个错误代码,如 ``-EBUSY`` 。 - - -监测线程PID ------------ - -DAMON通过一个叫做kdamond的内核线程来进行请求监测。你可以通过读取 ``kdamond_pid`` 文件获 -得该线程的 ``pid`` 。当监测被 ``关闭`` 时,读取该文件不会返回任何信息:: - - # cd <debugfs>/damon - # cat monitor_on_DEPRECATED - off - # cat kdamond_pid - none - # echo on > monitor_on_DEPRECATED - # cat kdamond_pid - 18594 - - -使用多个监测线程 ----------------- - -每个监测上下文都会创建一个 ``kdamond`` 线程。你可以使用 ``mk_contexts`` 和 ``rm_contexts`` -文件为多个 ``kdamond`` 需要的用例创建和删除监测上下文。 - -将新上下文的名称写入 ``mk_contexts`` 文件,在 ``DAMON debugfs`` 目录上创建一个该名称的目录。 -该目录将有该上下文的 ``DAMON debugfs`` 文件:: - - # cd <debugfs>/damon - # ls foo - # ls: cannot access 'foo': No such file or directory - # echo foo > mk_contexts - # ls foo - # attrs init_regions kdamond_pid schemes target_ids - -如果不再需要上下文,你可以通过把上下文的名字放到 ``rm_contexts`` 文件中来删除它和相应的目录:: - - # echo foo > rm_contexts - # ls foo - # ls: cannot access 'foo': No such file or directory - -注意, ``mk_contexts`` 、 ``rm_contexts`` 和 ``monitor_on_DEPRECATED`` 文件只在根目录下。 - 监测结果的监测点 ================ diff --git a/Documentation/translations/zh_CN/admin-guide/numastat.rst b/Documentation/translations/zh_CN/admin-guide/numastat.rst new file mode 100644 index 000000000000..4d9817b91870 --- /dev/null +++ b/Documentation/translations/zh_CN/admin-guide/numastat.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/admin-guide/numastat.rst +:Translator: Tao Zou <wodemia@linux.alibaba.com> + + +======================= +Numa策略命中/未命中统计 +======================= + +/sys/devices/system/node/node*/numastat + +所有数据的单位都是页面。巨页有独立的计数器。 + +numa_hit、numa_miss和numa_foreign计数器反映了进程是否能够在他们偏好的节点上分配内存。 +如果进程成功在偏好的节点上分配内存则在偏好的节点上增加numa_hit计数,否则在偏好的节点上增 +加numa_foreign计数同时在实际内存分配的节点上增加numa_miss计数。 + +通常,偏好的节点是进程运行所在的CPU的本地节点,但是一些限制可以改变这一行为,比如内存策略, +因此同样有两个基于CPU本地节点的计数器。local_node和numa_hit类似,当在CPU所在的节点上分 +配内存时增加local_node计数,other_node和numa_miss类似,当在CPU所在节点之外的其他节点 +上成功分配内存时增加other_node计数。需要注意,没有和numa_foreign对应的计数器。 + +更多细节内容: + +=============== ============================================================ +numa_hit 一个进程想要从本节点分配内存并且成功。 + +numa_miss 一个进程想要从其他节点分配内存但是最终在本节点完成内存分配。 + +numa_foreign 一个进程想要在本节点分配内存但是最终在其他节点完成内存分配。 + +local_node 一个进程运行在本节点的CPU上并且从本节点上获得了内存。 + +other_node 一个进程运行在其他节点的CPU上但是在本节点上获得了内存。 + +interleave_hit 内存交叉分配策略下想要从本节点分配内存并且成功。 +=============== ============================================================ + +你可以使用numactl软件包(http://oss.sgi.com/projects/libnuma/)中的numastat工具 +来辅助阅读。需要注意,numastat工具目前只在有少量CPU的机器上运行良好。 + +需要注意,在包含无内存节点(一个节点有CPUs但是没有内存)的系统中numa_hit、numa_miss和 +numa_foreign统计数据会被严重曲解。在当前的内核实现中,如果一个进程偏好一个无内存节点(即 +进程正在该节点的一个本地CPU上运行),实际上会从距离最近的有内存节点中挑选一个作为偏好节点。 +结果会导致相应的内存分配不会增加无内存节点上的numa_foreign计数器,并且会扭曲最近节点上的 +numa_hit、numa_miss和numa_foreign统计数据。 diff --git a/Documentation/translations/zh_CN/admin-guide/reporting-issues.rst b/Documentation/translations/zh_CN/admin-guide/reporting-issues.rst index 59e51e3539b4..9ff4ba94391d 100644 --- a/Documentation/translations/zh_CN/admin-guide/reporting-issues.rst +++ b/Documentation/translations/zh_CN/admin-guide/reporting-issues.rst @@ -300,7 +300,7 @@ Documentation/admin-guide/reporting-regressions.rst 对此进行了更详细的 添加到回归跟踪列表中,以确保它不会被忽略。 什么是安全问题留给您自己判断。在继续之前,请考虑阅读 -Documentation/translations/zh_CN/admin-guide/security-bugs.rst , +Documentation/translations/zh_CN/process/security-bugs.rst , 因为它提供了如何最恰当地处理安全问题的额外细节。 当发生了完全无法接受的糟糕事情时,此问题就是一个“非常严重的问题”。例如, @@ -983,7 +983,7 @@ Documentation/admin-guide/reporting-regressions.rst ;它还提供了大量其 报告,请将报告的文本转发到这些地址;但请在报告的顶部加上注释,表明您提交了 报告,并附上工单链接。 -更多信息请参见 Documentation/translations/zh_CN/admin-guide/security-bugs.rst 。 +更多信息请参见 Documentation/translations/zh_CN/process/security-bugs.rst 。 发布报告后的责任 diff --git a/Documentation/translations/zh_CN/admin-guide/sysrq.rst b/Documentation/translations/zh_CN/admin-guide/sysrq.rst index 8276d70f3b40..6b100acc4ed8 100644 --- a/Documentation/translations/zh_CN/admin-guide/sysrq.rst +++ b/Documentation/translations/zh_CN/admin-guide/sysrq.rst @@ -60,24 +60,24 @@ CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE 必须是以 16 进制数写入。 ~~~~~~~~~~~~~~~~~~~~~~~ 在 x86 架构上 - 你可以按下键盘组合键 :kbd:`ALT-SysRq-<command key>`。 + 你可以按下键盘组合键 `ALT-SysRq-<command key>`。 .. note:: 一些键盘可能没有标识 'SySRq' 键。'SySRq' 键也被当做 'Print Screen'键。 - 同时有些键盘无法处理同时按下这么多键,因此你可以先按下键盘 :kbd:`Alt` 键, - 然后按下键盘 :kbd:`SysRq` 键,再释放键盘 :kbd:`SysRq` 键,之后按下键盘上命令键 - :kbd:`<command key>`,最后释放所有键。 + 同时有些键盘无法处理同时按下这么多键,因此你可以先按下键盘 `Alt` 键, + 然后按下键盘 `SysRq` 键,再释放键盘 `SysRq` 键,之后按下键盘上命令键 + `<command key>`,最后释放所有键。 在 SPARC 架构上 - 你可以按下键盘组合键 :kbd:`ALT-STOP-<command key>` 。 + 你可以按下键盘组合键 `ALT-STOP-<command key>` 。 在串行控制台(只针对 PC 类型的标准串口) 你可以发一个 ``BREAK`` ,然后在 5 秒内发送一个命令键, 发送 ``BREAK`` 两次将被翻译为一个正常的 BREAK 操作。 在 PowerPC 架构上 - 按下键盘组合键 :kbd:`ALT - Print Screen` (或者 :kbd:`F13`) - :kbd:`<命令键>` 。 - :kbd:`Print Screen` (或者 :kbd:`F13`) - :kbd:`<命令键>` 或许也能实现。 + 按下键盘组合键 `ALT - Print Screen` (或者 `F13`) - `<命令键>` 。 + `Print Screen` (或者 `F13`) - `<命令键>` 或许也能实现。 在其他架构上 如果你知道其他架构的组合键,请告诉我,我可以把它们添加到这部分。 @@ -87,7 +87,7 @@ CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE 必须是以 16 进制数写入。 echo t > /proc/sysrq-trigger -这个命令键 :kbd:`<command key>` 是区分大小写的。 +这个命令键 `<command key>` 是区分大小写的。 什么是命令键? ~~~~~~~~~~~~~~ @@ -203,8 +203,8 @@ syslogd/klogd 进程是运行的,它们仍将被记录。) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 这也会发生在我这,我发现轻敲键盘两侧的 shift、alt 和 control 键,然后再次敲击 -一个无效的 SysRq 键序列可以解决问题。(比如,像键盘组合键 :kbd:`alt-sysrq-z` ) -切换到另一个虚拟控制台(键盘操作 :kbd:`ALT+Fn` ),然后再切回来应该也有帮助。 +一个无效的 SysRq 键序列可以解决问题。(比如,像键盘组合键 `alt-sysrq-z` ) +切换到另一个虚拟控制台(键盘操作 `ALT+Fn` ),然后再切回来应该也有帮助。 我敲击了 SysRq 键,但像是什么都没发生,发生了什么错误? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -259,7 +259,7 @@ SysRq 键的输出和所有其他控制台输出一样,受制于控制台日 文件的消费访问到。作为一个特例,来自 sysrq 命令的标题行将被传递给所有控制台 使用者,就好像当前日志级别是最大的一样。如果只发出标题头,则几乎可以肯定内核日志 级别太低。如果你需要控制台上的输出,那么你将需要临时提高控制台日志级别,通过使用 -键盘组合键 :kbd:`alt-sysrq-8` 或者:: +键盘组合键 `alt-sysrq-8` 或者:: echo 8 > /proc/sysrq-trigger diff --git a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst index f1e9ab18206c..d4ff80de47b6 100644 --- a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst +++ b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst @@ -87,6 +87,93 @@ PCH-LPC/PCH-MSI,然后被EIOINTC统一收集,再直接到达CPUINTC:: | Devices | +---------+ +虚拟扩展IRQ模型 +=============== + +在这种模型里面, IPI(Inter-Processor Interrupt) 和CPU本地时钟中断直接发送到CPUINTC, +CPU串口 (UARTs) 中断发送到PCH-PIC, 而其他所有设备的中断则分别发送到所连接的PCH_PIC/ +PCH-MSI, 然后V-EIOINTC统一收集,再直接到达CPUINTC:: + + +-----+ +-------------------+ +-------+ + | IPI |--> | CPUINTC(0-255vcpu)| <-- | Timer | + +-----+ +-------------------+ +-------+ + ^ + | + +-----------+ + | V-EIOINTC | + +-----------+ + ^ ^ + | | + +---------+ +---------+ + | PCH-PIC | | PCH-MSI | + +---------+ +---------+ + ^ ^ ^ + | | | + +--------+ +---------+ +---------+ + | UARTs | | Devices | | Devices | + +--------+ +---------+ +---------+ + +V-EIOINTC 是EIOINTC的扩展, 仅工作在虚拟机模式下, 中断经EIOINTC最多可个路由到 +4个虚拟CPU. 但中断经V-EIOINTC最多可个路由到256个虚拟CPU. + +传统的EIOINTC中断控制器,中断路由分为两个部分:8比特用于控制路由到哪个CPU, +4比特用于控制路由到特定CPU的哪个中断管脚。控制CPU路由的8比特前4比特用于控制 +路由到哪个EIOINTC节点,后4比特用于控制此节点哪个CPU。中断路由在选择CPU路由 +和CPU中断管脚路由时,使用bitmap编码方式而不是正常编码方式,所以对于一个 +EIOINTC中断控制器节点,中断只能路由到CPU0 - CPU3,中断管脚IP0-IP3。 + +V-EIOINTC新增了两个寄存器,支持中断路由到更多CPU个和中断管脚。 + +V-EIOINTC功能寄存器 +------------------- +功能寄存器是只读寄存器,用于显示V-EIOINTC支持的特性,目前两个支持两个特性 +EXTIOI_HAS_INT_ENCODE 和 EXTIOI_HAS_CPU_ENCODE。 + +特性EXTIOI_HAS_INT_ENCODE是传统EIOINTC中断控制器的一个特性,如果此比特为1, +显示CPU中断管脚路由方式支持正常编码,而不是bitmap编码,所以中断可以路由到 +管脚IP0 - IP15。 + +特性EXTIOI_HAS_CPU_ENCODE是V-EIOINTC新增特性,如果此比特为1,表示CPU路由 +方式支持正常编码,而不是bitmap编码,所以中断可以路由到CPU0 - CPU255。 + +V-EIOINTC配置寄存器 +------------------- +配置寄存器是可读写寄存器,为了兼容性考虑,如果不写此寄存器,中断路由采用 +和传统EIOINTC相同的路由设置。如果对应比特设置为1,表示采用正常路由方式而 +不是bitmap编码的路由方式。 + +高级扩展IRQ模型 +=============== + +在这种模型里面,IPI(Inter-Processor Interrupt)和CPU本地时钟中断直接发送到CPUINTC, +CPU串口(UARTs)中断发送到LIOINTC,PCH-MSI中断发送到AVECINTC,而后通过AVECINTC直接 +送达CPUINTC,而其他所有设备的中断则分别发送到所连接的PCH-PIC/PCH-LPC,然后由EIOINTC +统一收集,再直接到达CPUINTC:: + + +-----+ +-----------------------+ +-------+ + | IPI | --> | CPUINTC | <-- | Timer | + +-----+ +-----------------------+ +-------+ + ^ ^ ^ + | | | + +---------+ +----------+ +---------+ +-------+ + | EIOINTC | | AVECINTC | | LIOINTC | <-- | UARTs | + +---------+ +----------+ +---------+ +-------+ + ^ ^ + | | + +---------+ +---------+ + | PCH-PIC | | PCH-MSI | + +---------+ +---------+ + ^ ^ ^ + | | | + +---------+ +---------+ +---------+ + | Devices | | PCH-LPC | | Devices | + +---------+ +---------+ +---------+ + ^ + | + +---------+ + | Devices | + +---------+ + ACPI相关的定义 ============== diff --git a/Documentation/translations/zh_CN/core-api/cachetlb.rst b/Documentation/translations/zh_CN/core-api/cachetlb.rst index b4a76ec75daa..64295c61d1c1 100644 --- a/Documentation/translations/zh_CN/core-api/cachetlb.rst +++ b/Documentation/translations/zh_CN/core-api/cachetlb.rst @@ -260,7 +260,7 @@ HyperSparc cpu就是这样一个具有这种属性的cpu。 如果D-cache别名不是一个问题,这个程序可以简单地定义为该架构上 的nop。 - 在page->flags (PG_arch_1)中有一个位是“架构私有”。内核保证, + 在folio->flags (PG_arch_1)中有一个位是“架构私有”。内核保证, 对于分页缓存的页面,当这样的页面第一次进入分页缓存时,它将清除 这个位。 diff --git a/Documentation/translations/zh_CN/core-api/index.rst b/Documentation/translations/zh_CN/core-api/index.rst index 922cabf7b5dd..453a02cd1f44 100644 --- a/Documentation/translations/zh_CN/core-api/index.rst +++ b/Documentation/translations/zh_CN/core-api/index.rst @@ -49,6 +49,7 @@ generic-radix-tree packing this_cpu_ops + union_find ======= diff --git a/Documentation/translations/zh_CN/core-api/symbol-namespaces.rst b/Documentation/translations/zh_CN/core-api/symbol-namespaces.rst index bb16f0611046..b1bec219912d 100644 --- a/Documentation/translations/zh_CN/core-api/symbol-namespaces.rst +++ b/Documentation/translations/zh_CN/core-api/symbol-namespaces.rst @@ -48,7 +48,7 @@ 要是一个预处理器符号。例如,要把符号 ``usb_stor_suspend`` 导出到命名空间 ``USB_STORAGE``, 请使用:: - EXPORT_SYMBOL_NS(usb_stor_suspend, USB_STORAGE); + EXPORT_SYMBOL_NS(usb_stor_suspend, "USB_STORAGE"); 相应的 ksymtab 条目结构体 ``kernel_symbol`` 将有相应的成员 ``命名空间`` 集。 导出时未指明命名空间的符号将指向 ``NULL`` 。如果没有定义命名空间,则默认没有。 @@ -66,7 +66,7 @@ 子系统的 ``Makefile`` 中定义默认命名空间。例如,如果要将usb-common中定义的所有符号导 出到USB_COMMON命名空间,可以在drivers/usb/common/Makefile中添加这样一行:: - ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=USB_COMMON + ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"USB_COMMON"' 这将影响所有 EXPORT_SYMBOL() 和 EXPORT_SYMBOL_GPL() 语句。当这个定义存在时, 用EXPORT_SYMBOL_NS()导出的符号仍然会被导出到作为命名空间参数传递的命名空间中, @@ -76,7 +76,7 @@ 成:: #undef DEFAULT_SYMBOL_NAMESPACE - #define DEFAULT_SYMBOL_NAMESPACE USB_COMMON + #define DEFAULT_SYMBOL_NAMESPACE "USB_COMMON" 应置于相关编译单元中任何 EXPORT_SYMBOL 宏之前 @@ -88,7 +88,7 @@ 表示它所使用的命名空间的符号。例如,一个使用usb_stor_suspend符号的 模块,需要使用如下语句导入命名空间USB_STORAGE:: - MODULE_IMPORT_NS(USB_STORAGE); + MODULE_IMPORT_NS("USB_STORAGE"); 这将在模块中为每个导入的命名空间创建一个 ``modinfo`` 标签。这也顺带 使得可以用modinfo检查模块已导入的命名空间:: diff --git a/Documentation/translations/zh_CN/core-api/unaligned-memory-access.rst b/Documentation/translations/zh_CN/core-api/unaligned-memory-access.rst index 29c33e7e0855..fbe0989a8ce5 100644 --- a/Documentation/translations/zh_CN/core-api/unaligned-memory-access.rst +++ b/Documentation/translations/zh_CN/core-api/unaligned-memory-access.rst @@ -175,7 +175,7 @@ field2会导致非对齐访问,这并不是不合理的。你会期望field2 避免非对齐访问 ============== -避免非对齐访问的最简单方法是使用<asm/unaligned.h>头文件提供的get_unaligned()和 +避免非对齐访问的最简单方法是使用<linux/unaligned.h>头文件提供的get_unaligned()和 put_unaligned()宏。 回到前面的一个可能导致非对齐访问的代码例子:: diff --git a/Documentation/translations/zh_CN/core-api/union_find.rst b/Documentation/translations/zh_CN/core-api/union_find.rst new file mode 100644 index 000000000000..bb93fa8c6533 --- /dev/null +++ b/Documentation/translations/zh_CN/core-api/union_find.rst @@ -0,0 +1,92 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/core-api/union_find.rst + +============================= +Linux中的并查集(Union-Find) +============================= + + +:日期: 2024年6月21日 +:作者: Xavier <xavier_qy@163.com> + +何为并查集,它有什么用? +------------------------ + +并查集是一种数据结构,用于处理一些不交集的合并及查询问题。并查集支持的主要操作: + 初始化:将每个元素初始化为单独的集合,每个集合的初始父节点指向自身。 + + 查询:查询某个元素属于哪个集合,通常是返回集合中的一个“代表元素”。这个操作是为 + 了判断两个元素是否在同一个集合之中。 + + 合并:将两个集合合并为一个。 + +并查集作为一种用于维护集合(组)的数据结构,它通常用于解决一些离线查询、动态连通性和 +图论等相关问题,同时也是用于计算最小生成树的克鲁斯克尔算法中的关键,由于最小生成树在 +网络路由等场景下十分重要,并查集也得到了广泛的引用。此外,并查集在符号计算,寄存器分 +配等方面也有应用。 + +空间复杂度: O(n),n为节点数。 + +时间复杂度:使用路径压缩可以减少查找操作的时间复杂度,使用按秩合并可以减少合并操作的 +时间复杂度,使得并查集每个查询和合并操作的平均时间复杂度仅为O(α(n)),其中α(n)是反阿 +克曼函数,可以粗略地认为并查集的操作有常数的时间复杂度。 + +本文档涵盖了对Linux并查集实现的使用方法。更多关于并查集的性质和实现的信息,参见: + + 维基百科并查集词条 + https://en.wikipedia.org/wiki/Disjoint-set_data_structure + +并查集的Linux实现 +------------------ + +Linux的并查集实现在文件“lib/union_find.c”中。要使用它,需要 +“#include <linux/union_find.h>”。 + +并查集的数据结构定义如下:: + + struct uf_node { + struct uf_node *parent; + unsigned int rank; + }; + +其中parent为当前节点的父节点,rank为当前树的高度,在合并时将rank小的节点接到rank大 +的节点下面以增加平衡性。 + +初始化并查集 +------------- + +可以采用静态或初始化接口完成初始化操作。初始化时,parent 指针指向自身,rank 设置 +为 0。 +示例:: + + struct uf_node my_node = UF_INIT_NODE(my_node); + +或 + + uf_node_init(&my_node); + +查找并查集的根节点 +------------------ + +主要用于判断两个并查集是否属于一个集合,如果根相同,那么他们就是一个集合。在查找过程中 +会对路径进行压缩,提高后续查找效率。 +示例:: + + int connected; + struct uf_node *root1 = uf_find(&node_1); + struct uf_node *root2 = uf_find(&node_2); + if (root1 == root2) + connected = 1; + else + connected = 0; + +合并两个并查集 +-------------- + +对于两个相交的并查集进行合并,会首先查找它们各自的根节点,然后根据根节点秩大小,将小的 +节点连接到大的节点下面。 +示例:: + + uf_union(&node_1, &node_2); diff --git a/Documentation/translations/zh_CN/dev-tools/gcov.rst b/Documentation/translations/zh_CN/dev-tools/gcov.rst index 3158c9da1318..ea8f94852f41 100644 --- a/Documentation/translations/zh_CN/dev-tools/gcov.rst +++ b/Documentation/translations/zh_CN/dev-tools/gcov.rst @@ -120,7 +120,7 @@ gcov的内核分析插桩支持内核的编译和运行是在同一台机器上 如果内核编译和运行是不同的机器,那么需要额外的准备工作,这取决于gcov工具 是在哪里使用的: -.. _gcov-test_zh: +.. _gcov-test_zh_CN: a) 若gcov运行在测试机上 @@ -140,7 +140,7 @@ a) 若gcov运行在测试机上 如果文件是软链接,需要替换成真正的目录文件(这是由make的当前工作 目录变量CURDIR引起的)。 -.. _gcov-build_zh: +.. _gcov-build_zh_CN: b) 若gcov运行在编译机上 @@ -205,7 +205,7 @@ kconfig会根据编译工具链的检查自动选择合适的gcov格式。 -------------------------- 用于在编译机上收集覆盖率元文件的示例脚本 -(见 :ref:`编译机和测试机分离 a. <gcov-test_zh>` ) +(见 :ref:`编译机和测试机分离 a. <gcov-test_zh_CN>` ) .. code-block:: sh @@ -238,7 +238,7 @@ kconfig会根据编译工具链的检查自动选择合适的gcov格式。 ------------------------- 用于在测试机上收集覆盖率数据文件的示例脚本 -(见 :ref:`编译机和测试机分离 b. <gcov-build_zh>` ) +(见 :ref:`编译机和测试机分离 b. <gcov-build_zh_CN>` ) .. code-block:: sh diff --git a/Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst b/Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst index 17b5ce85a90c..3c133a918f30 100644 --- a/Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst +++ b/Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst @@ -34,6 +34,10 @@ Kgdb内核调试器、QEMU等虚拟机管理程序或基于JTAG的硬件接口 但这通常仅在不依赖内核模块时才有效。有关此模式的更多详细信息,请参阅QEMU文档。 在这种情况下,如果架构支持KASLR,应该在禁用CONFIG_RANDOMIZE_BASE的情况下构建内核。 +- 构建gdb脚本(适用于内核v5.1版本及以上) + + make scripts_gdb + - 启用QEMU/KVM的gdb stub,可以通过如下方式实现 - 在VM启动时,通过在QEMU命令行中添加“-s”参数 diff --git a/Documentation/translations/zh_CN/dev-tools/index.rst b/Documentation/translations/zh_CN/dev-tools/index.rst index c2db3e566b1b..7b37194217b0 100644 --- a/Documentation/translations/zh_CN/dev-tools/index.rst +++ b/Documentation/translations/zh_CN/dev-tools/index.rst @@ -20,18 +20,22 @@ Documentation/translations/zh_CN/dev-tools/testing-overview.rst testing-overview sparse + kcov + kcsan + kmsan gcov kasan + ubsan + kmemleak gdb-kernel-debugging Todolist: + - checkpatch - coccinelle - - kcov - - ubsan - - kmemleak - - kcsan - kfence - kgdb - kselftest - kunit/index + - ktap + - checkuapi diff --git a/Documentation/translations/zh_CN/dev-tools/kasan.rst b/Documentation/translations/zh_CN/dev-tools/kasan.rst index 2b1e8f74904b..fd2e3afbdfad 100644 --- a/Documentation/translations/zh_CN/dev-tools/kasan.rst +++ b/Documentation/translations/zh_CN/dev-tools/kasan.rst @@ -235,6 +235,24 @@ slab对象的描述以及关于访问的内存页的信息。 通用KASAN还报告两个辅助调用堆栈跟踪。这些堆栈跟踪指向代码中与对象交互但不直接 出现在错误访问堆栈跟踪中的位置。目前,这包括 call_rcu() 和排队的工作队列。 +CONFIG_KASAN_EXTRA_INFO +~~~~~~~~~~~~~~~~~~~~~~~ + +启用 CONFIG_KASAN_EXTRA_INFO 选项允许 KASAN 记录和报告更多信息。目前支持的 +额外信息包括分配和释放时的 CPU 编号和时间戳。更多的信息可以帮助找到内核错误的原因, +并将错误与其他系统事件关联起来,但代价是用额外的内存来记录更多信息(有关更多 +开销的细节,请参见 CONFIG_KASAN_EXTRA_INFO 选项的帮助文本)。 + +以下为 CONFIG_KASAN_EXTRA_INFO 开启后的报告(仅显示不同部分):: + + ================================================================== + ... + Allocated by task 134 on cpu 5 at 229.133855s: + ... + Freed by task 136 on cpu 3 at 230.199335s: + ... + ================================================================== + 实施细则 -------- @@ -404,16 +422,12 @@ KASAN连接到vmap基础架构以懒清理未使用的影子内存。 ~~~~ 有一些KASAN测试可以验证KASAN是否正常工作并可以检测某些类型的内存损坏。 -测试由两部分组成: - -1. 与KUnit测试框架集成的测试。使用 ``CONFIG_KASAN_KUNIT_TEST`` 启用。 -这些测试可以通过几种不同的方式自动运行和部分验证;请参阅下面的说明。 -2. 与KUnit不兼容的测试。使用 ``CONFIG_KASAN_MODULE_TEST`` 启用并且只能作为模块 -运行。这些测试只能通过加载内核模块并检查内核日志以获取KASAN报告来手动验证。 +所有 KASAN 测试都与 KUnit 测试框架集成,可通过 ``CONFIG_KASAN_KUNIT_TEST`` 启用。 +测试可以通过几种不同的方式自动运行和部分验证;请参阅以下说明。 -如果检测到错误,每个KUnit兼容的KASAN测试都会打印多个KASAN报告之一,然后测试打印 -其编号和状态。 +如果检测到错误,每个 KASAN 测试都会打印多份 KASAN 报告中的一份。 +然后测试会打印其编号和状态。 当测试通过:: @@ -440,16 +454,16 @@ KASAN连接到vmap基础架构以懒清理未使用的影子内存。 not ok 1 - kasan -有几种方法可以运行与KUnit兼容的KASAN测试。 +有几种方法可以运行 KASAN 测试。 1. 可加载模块 - 启用 ``CONFIG_KUNIT`` 后,KASAN-KUnit测试可以构建为可加载模块,并通过使用 - ``insmod`` 或 ``modprobe`` 加载 ``kasan_test.ko`` 来运行。 + 启用 ``CONFIG_KUNIT`` 后,可以将测试构建为可加载模块 + 并通过使用 ``insmod`` 或 ``modprobe`` 加载 ``kasan_test.ko`` 来运行。 2. 内置 - 通过内置 ``CONFIG_KUNIT`` ,也可以内置KASAN-KUnit测试。在这种情况下, + 通过内置 ``CONFIG_KUNIT``,测试也可以内置。 测试将在启动时作为后期初始化调用运行。 3. 使用kunit_tool diff --git a/Documentation/translations/zh_CN/dev-tools/kcov.rst b/Documentation/translations/zh_CN/dev-tools/kcov.rst new file mode 100644 index 000000000000..629154df7121 --- /dev/null +++ b/Documentation/translations/zh_CN/dev-tools/kcov.rst @@ -0,0 +1,359 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/dev-tools/kcov.rst +:Translator: 刘浩阳 Haoyang Liu <tttturtleruss@hust.edu.cn> + +KCOV: 用于模糊测试的代码覆盖率 +============================== + +KCOV 以一种适用于覆盖率引导的模糊测试的形式收集和暴露内核代码覆盖率信息。 +一个正在运行的内核的覆盖率数据可以通过 ``kcov`` 调试文件导出。覆盖率的收集是基 +于任务启用的,因此 KCOV 可以精确捕获单个系统调用的覆盖率。 + +要注意的是 KCOV 不是为了收集尽可能多的覆盖率数据。而是为了收集相对稳定的覆盖率 +,这是系统调用输入的函数。为了完成这个目标,它不收集软硬中断的覆盖率(除非移除 +覆盖率收集被启用,见下文)以及内核中固有的不确定部分的覆盖率(如调度器,锁定) + +除了收集代码覆盖率,KCOV 还收集操作数比较的覆盖率。见 "操作数比较收集" 一节 +查看详细信息。 + +除了从系统调用处理器收集覆盖率数据,KCOV 还从后台内核或软中断任务中执行的内核 +被标注的部分收集覆盖率。见 "远程覆盖率收集" 一节查看详细信息。 + +先决条件 +-------- + +KCOV 依赖编译器插桩,要求 GCC 6.1.0 及更高版本或者内核支持的任意版本的 Clang。 + +收集操作数比较的覆盖率需要 GCC 8+ 或者 Clang。 + +为了启用 KCOV,需要使用如下参数配置内核:: + + CONFIG_KCOV=y + +为了启用操作数比较覆盖率的收集,使用如下参数:: + + CONFIG_KCOV_ENABLE_COMPARISONS=y + +覆盖率数据只会在调试文件系统被挂载后才可以获取:: + + mount -t debugfs none /sys/kernel/debug + +覆盖率收集 +---------- + +下面的程序演示了如何使用 KCOV 在一个测试程序中收集单个系统调用的覆盖率: + +.. code-block:: c + + #include <stdio.h> + #include <stddef.h> + #include <stdint.h> + #include <stdlib.h> + #include <sys/types.h> + #include <sys/stat.h> + #include <sys/ioctl.h> + #include <sys/mman.h> + #include <unistd.h> + #include <fcntl.h> + #include <linux/types.h> + + #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) + #define KCOV_ENABLE _IO('c', 100) + #define KCOV_DISABLE _IO('c', 101) + #define COVER_SIZE (64<<10) + + #define KCOV_TRACE_PC 0 + #define KCOV_TRACE_CMP 1 + + int main(int argc, char **argv) + { + int fd; + unsigned long *cover, n, i; + + /* 单个文件描述符允许 + * 在单线程上收集覆盖率。 + */ + fd = open("/sys/kernel/debug/kcov", O_RDWR); + if (fd == -1) + perror("open"), exit(1); + /* 设置跟踪模式和跟踪大小。 */ + if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) + perror("ioctl"), exit(1); + /* 映射内核空间和用户空间共享的缓冲区。 */ + cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((void*)cover == MAP_FAILED) + perror("mmap"), exit(1); + /* 在当前线程中启用覆盖率收集。 */ + if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC)) + perror("ioctl"), exit(1); + /* 在调用 ioctl() 之后重置覆盖率。 */ + __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); + /* 调用目标系统调用。 */ + read(-1, NULL, 0); + /* 读取收集到的 PC 的数目。 */ + n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); + for (i = 0; i < n; i++) + printf("0x%lx\n", cover[i + 1]); + /* 在当前线程上禁用覆盖率收集。在这之后 + * 可以在其他线程上收集覆盖率 + */ + if (ioctl(fd, KCOV_DISABLE, 0)) + perror("ioctl"), exit(1); + /* 释放资源 */ + if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) + perror("munmap"), exit(1); + if (close(fd)) + perror("close"), exit(1); + return 0; + } + +在使用 ``addr2line`` 传输后,程序输出应该如下所示:: + + SyS_read + fs/read_write.c:562 + __fdget_pos + fs/file.c:774 + __fget_light + fs/file.c:746 + __fget_light + fs/file.c:750 + __fget_light + fs/file.c:760 + __fdget_pos + fs/file.c:784 + SyS_read + fs/read_write.c:562 + +如果一个程序需要从多个线程收集覆盖率(独立地)。那么每个线程都需要单独打开 +``/sys/kernel/debug/kcov``。 + +接口的细粒度允许高效的创建测试进程。即,一个父进程打开了 +``/sys/kernel/debug/kcov``,启用了追踪模式,映射了覆盖率缓冲区,然后在一个循 +环中创建了子进程。这个子进程只需要启用覆盖率收集即可(当一个线程退出时将自动禁 +用覆盖率收集)。 + +操作数比较收集 +-------------- + +操作数比较收集和覆盖率收集类似: + +.. code-block:: c + + /* 包含和上文一样的头文件和宏定义。 */ + + /* 每次记录的 64 位字的数量。 */ + #define KCOV_WORDS_PER_CMP 4 + + /* + * 收集的比较种类的格式。 + * + * 0 比特表示是否是一个编译时常量。 + * 1 & 2 比特包含参数大小的 log2 值,最大 8 字节。 + */ + + #define KCOV_CMP_CONST (1 << 0) + #define KCOV_CMP_SIZE(n) ((n) << 1) + #define KCOV_CMP_MASK KCOV_CMP_SIZE(3) + + int main(int argc, char **argv) + { + int fd; + uint64_t *cover, type, arg1, arg2, is_const, size; + unsigned long n, i; + + fd = open("/sys/kernel/debug/kcov", O_RDWR); + if (fd == -1) + perror("open"), exit(1); + if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) + perror("ioctl"), exit(1); + /* + * 注意缓冲区指针的类型是 uint64_t*,因为所有的 + * 比较操作数都被提升为 uint64_t 类型。 + */ + cover = (uint64_t *)mmap(NULL, COVER_SIZE * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((void*)cover == MAP_FAILED) + perror("mmap"), exit(1); + /* 注意这里是 KCOV_TRACE_CMP 而不是 KCOV_TRACE_PC。 */ + if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_CMP)) + perror("ioctl"), exit(1); + __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); + read(-1, NULL, 0); + /* 读取收集到的比较操作数的数量。 */ + n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); + for (i = 0; i < n; i++) { + uint64_t ip; + + type = cover[i * KCOV_WORDS_PER_CMP + 1]; + /* arg1 和 arg2 - 比较的两个操作数。 */ + arg1 = cover[i * KCOV_WORDS_PER_CMP + 2]; + arg2 = cover[i * KCOV_WORDS_PER_CMP + 3]; + /* ip - 调用者的地址。 */ + ip = cover[i * KCOV_WORDS_PER_CMP + 4]; + /* 操作数的大小。 */ + size = 1 << ((type & KCOV_CMP_MASK) >> 1); + /* is_const - 当操作数是一个编译时常量时为真。*/ + is_const = type & KCOV_CMP_CONST; + printf("ip: 0x%lx type: 0x%lx, arg1: 0x%lx, arg2: 0x%lx, " + "size: %lu, %s\n", + ip, type, arg1, arg2, size, + is_const ? "const" : "non-const"); + } + if (ioctl(fd, KCOV_DISABLE, 0)) + perror("ioctl"), exit(1); + /* 释放资源。 */ + if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) + perror("munmap"), exit(1); + if (close(fd)) + perror("close"), exit(1); + return 0; + } + +注意 KCOV 的模式(代码覆盖率收集或操作数比较收集)是互斥的。 + +远程覆盖率收集 +-------------- + +除了从用户空间进程发布的系统调用句柄收集覆盖率数据以外,KCOV 也可以从部分在其 +他上下文中执行的内核中收集覆盖率 - 称为“远程”覆盖率。 + +使用 KCOV 收集远程覆盖率要求: + +1. 修改内核源码并使用 ``kcov_remote_start`` 和 ``kcov_remote_stop`` 来标注要收集 + 覆盖率的代码片段。 + +2. 在用户空间的收集覆盖率的进程应使用 ``KCOV_REMOTE_ENABLE`` 而不是 ``KCOV_ENABLE``。 + +``kcov_remote_start`` 和 ``kcov_remote_stop`` 的标注以及 ``KCOV_REMOTE_ENABLE`` +ioctl 都接受可以识别特定覆盖率收集片段的句柄。句柄的使用方式取决于匹配代码片段执 +行的上下文。 + +KCOV 支持在如下上下文中收集远程覆盖率: + +1. 全局内核后台任务。这些任务是内核启动时创建的数量有限的实例(如,每一个 + USB HCD 产生一个 USB ``hub_event`` 工作器)。 + +2. 局部内核后台任务。这些任务通常是由于用户空间进程与某些内核接口进行交互时产 + 生的,并且通常在进程退出时会被停止(如,vhost 工作器)。 + +3. 软中断。 + +对于 #1 和 #3,必须选择一个独特的全局句柄并将其传递给对应的 +``kcov_remote_start`` 调用。一个用户空间进程必须将该句柄存储在 +``kcov_remote_arg`` 结构体的 ``handle`` 数组字段中并将其传递给 +``KCOV_REMOTE_ENABLE``。这会将使用的 KCOV 设备附加到由此句柄引用的代码片段。多个全局 +句柄标识的不同代码片段可以一次性传递。 + +对于 #2,用户空间进程必须通过 ``kcov_remote_arg`` 结构体的 ``common_handle`` 字段 +传递一个非零句柄。这个通用句柄将会被保存在当前 ``task_struct`` 结构体的 +``kcov_handle`` 字段中并且需要通过自定义内核代码的修改来传递给新创建的本地任务 +。这些任务需要在 ``kcov_remote_start`` 和 ``kcov_remote_stop`` 标注中依次使用传递过来的 +句柄。 + +KCOV 对全局句柄和通用句柄均遵循一个预定义的格式。每一个句柄都是一个 ``u64`` 整形 +。当前,只有最高位和低四位字节被使用。第 4-7 字节是保留位并且值必须为 0。 + +对于全局句柄,最高位的字节表示该句柄属于的子系统的标识。比如,KCOV 使用 ``1`` +表示 USB 子系统类型。全局句柄的低 4 字节表示子系统中任务实例的标识。比如,每一 +个 ``hub_event`` 工作器使用 USB 总线号作为任务实例的标识。 + +对于通用句柄,使用一个保留值 ``0`` 作为子系统标识,因为这些句柄不属于一个特定 +的子系统。通用句柄的低 4 字节用于识别有用户进程生成的所有本地句柄的集合实例, +该进程将通用句柄传递给 ``KCOV_REMOTE_ENABLE``。 + +实际上,如果只从系统中的单个用户空间进程收集覆盖率,那么可以使用任意值作为通用 +句柄的实例标识。然而,如果通用句柄被多个用户空间进程使用,每个进程必须使用唯一 +的实例标识。一个选择是使用进程标识作为通用句柄实例的标识。 + +下面的程序演示了如何使用 KCOV 从一个由进程产生的本地任务和处理 USB 总线的全局 +任务 #1 收集覆盖率: + +.. code-block:: c + + /* 包含和上文一样的头文件和宏定义。 */ + + struct kcov_remote_arg { + __u32 trace_mode; + __u32 area_size; + __u32 num_handles; + __aligned_u64 common_handle; + __aligned_u64 handles[0]; + }; + + #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) + #define KCOV_DISABLE _IO('c', 101) + #define KCOV_REMOTE_ENABLE _IOW('c', 102, struct kcov_remote_arg) + + #define COVER_SIZE (64 << 10) + + #define KCOV_TRACE_PC 0 + + #define KCOV_SUBSYSTEM_COMMON (0x00ull << 56) + #define KCOV_SUBSYSTEM_USB (0x01ull << 56) + + #define KCOV_SUBSYSTEM_MASK (0xffull << 56) + #define KCOV_INSTANCE_MASK (0xffffffffull) + + static inline __u64 kcov_remote_handle(__u64 subsys, __u64 inst) + { + if (subsys & ~KCOV_SUBSYSTEM_MASK || inst & ~KCOV_INSTANCE_MASK) + return 0; + return subsys | inst; + } + + #define KCOV_COMMON_ID 0x42 + #define KCOV_USB_BUS_NUM 1 + + int main(int argc, char **argv) + { + int fd; + unsigned long *cover, n, i; + struct kcov_remote_arg *arg; + + fd = open("/sys/kernel/debug/kcov", O_RDWR); + if (fd == -1) + perror("open"), exit(1); + if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) + perror("ioctl"), exit(1); + cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((void*)cover == MAP_FAILED) + perror("mmap"), exit(1); + + /* 通过通用句柄和 USB 总线 #1 启用代码覆盖率收集。 */ + arg = calloc(1, sizeof(*arg) + sizeof(uint64_t)); + if (!arg) + perror("calloc"), exit(1); + arg->trace_mode = KCOV_TRACE_PC; + arg->area_size = COVER_SIZE; + arg->num_handles = 1; + arg->common_handle = kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, + KCOV_COMMON_ID); + arg->handles[0] = kcov_remote_handle(KCOV_SUBSYSTEM_USB, + KCOV_USB_BUS_NUM); + if (ioctl(fd, KCOV_REMOTE_ENABLE, arg)) + perror("ioctl"), free(arg), exit(1); + free(arg); + + /* + * 在这里用户需要触发执行一个内核代码段 + * 该代码段要么使用通用句柄标识 + * 要么触发了一些 USB 总线 #1 上的一些活动。 + */ + sleep(2); + + n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); + for (i = 0; i < n; i++) + printf("0x%lx\n", cover[i + 1]); + if (ioctl(fd, KCOV_DISABLE, 0)) + perror("ioctl"), exit(1); + if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) + perror("munmap"), exit(1); + if (close(fd)) + perror("close"), exit(1); + return 0; + } diff --git a/Documentation/translations/zh_CN/dev-tools/kcsan.rst b/Documentation/translations/zh_CN/dev-tools/kcsan.rst new file mode 100644 index 000000000000..8c495c17f109 --- /dev/null +++ b/Documentation/translations/zh_CN/dev-tools/kcsan.rst @@ -0,0 +1,320 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/dev-tools/kcsan.rst +:Translator: 刘浩阳 Haoyang Liu <tttturtleruss@hust.edu.cn> + +内核并发消毒剂(KCSAN) +===================== + +内核并发消毒剂(KCSAN)是一个动态竞争检测器,依赖编译时插桩,并且使用基于观察 +点的采样方法来检测竞争。KCSAN 的主要目的是检测 `数据竞争`_。 + +使用 +---- + +KCSAN 受 GCC 和 Clang 支持。使用 GCC 需要版本 11 或更高,使用 Clang 也需要 +版本 11 或更高。 + +为了启用 KCSAN,用如下参数配置内核:: + + CONFIG_KCSAN = y + +KCSAN 提供了几个其他的配置选项来自定义行为(见 ``lib/Kconfig.kcsan`` 中的各自的 +帮助文档以获取更多信息)。 + +错误报告 +~~~~~~~~ + +一个典型数据竞争的报告如下所示:: + + ================================================================== + BUG: KCSAN: data-race in test_kernel_read / test_kernel_write + + write to 0xffffffffc009a628 of 8 bytes by task 487 on cpu 0: + test_kernel_write+0x1d/0x30 + access_thread+0x89/0xd0 + kthread+0x23e/0x260 + ret_from_fork+0x22/0x30 + + read to 0xffffffffc009a628 of 8 bytes by task 488 on cpu 6: + test_kernel_read+0x10/0x20 + access_thread+0x89/0xd0 + kthread+0x23e/0x260 + ret_from_fork+0x22/0x30 + + value changed: 0x00000000000009a6 -> 0x00000000000009b2 + + Reported by Kernel Concurrency Sanitizer on: + CPU: 6 PID: 488 Comm: access_thread Not tainted 5.12.0-rc2+ #1 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 + ================================================================== + +报告的头部提供了一个关于竞争中涉及到的函数的简短总结。随后是竞争中的两个线程的 +访问类型和堆栈信息。如果 KCSAN 发现了一个值的变化,那么那个值的旧值和新值会在 +“value changed”这一行单独显示。 + +另一个不太常见的数据竞争类型的报告如下所示:: + + ================================================================== + BUG: KCSAN: data-race in test_kernel_rmw_array+0x71/0xd0 + + race at unknown origin, with read to 0xffffffffc009bdb0 of 8 bytes by task 515 on cpu 2: + test_kernel_rmw_array+0x71/0xd0 + access_thread+0x89/0xd0 + kthread+0x23e/0x260 + ret_from_fork+0x22/0x30 + + value changed: 0x0000000000002328 -> 0x0000000000002329 + + Reported by Kernel Concurrency Sanitizer on: + CPU: 2 PID: 515 Comm: access_thread Not tainted 5.12.0-rc2+ #1 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 + ================================================================== + +这个报告是当另一个竞争线程不可能被发现,但是可以从观测的内存地址的值改变而推断 +出来的时候生成的。这类报告总是会带有“value changed”行。这类报告的出现通常是因 +为在竞争线程中缺少插桩,也可能是因为其他原因,比如 DMA 访问。这类报告只会在 +设置了内核参数 ``CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN=y`` 时才会出现,而这 +个参数是默认启用的。 + +选择性分析 +~~~~~~~~~~ + +对于一些特定的访问,函数,编译单元或者整个子系统,可能需要禁用数据竞争检测。 +对于静态黑名单,有如下可用的参数: + +* KCSAN 支持使用 ``data_race(expr)`` 注解,这个注解告诉 KCSAN 任何由访问 + ``expr`` 所引起的数据竞争都应该被忽略,其产生的行为后果被认为是安全的。请查阅 + `在 LKMM 中 "标记共享内存访问"`_ 获得更多信息。 + +* 与 ``data_race(...)`` 相似,可以使用类型限定符 ``__data_racy`` 来标记一个变量 + ,所有访问该变量而导致的数据竞争都是故意为之并且应该被 KCSAN 忽略:: + + struct foo { + ... + int __data_racy stats_counter; + ... + }; + +* 使用函数属性 ``__no_kcsan`` 可以对整个函数禁用数据竞争检测:: + + __no_kcsan + void foo(void) { + ... + + 为了动态限制该为哪些函数生成报告,查阅 `Debug 文件系统接口`_ 黑名单/白名单特性。 + +* 为特定的编译单元禁用数据竞争检测,将下列参数加入到 ``Makefile`` 中:: + + KCSAN_SANITIZE_file.o := n + +* 为 ``Makefile`` 中的所有编译单元禁用数据竞争检测,将下列参数添加到相应的 + ``Makefile`` 中:: + + KCSAN_SANITIZE := n + +.. _在 LKMM 中 "标记共享内存访问": https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/memory-model/Documentation/access-marking.txt + +此外,KCSAN 可以根据偏好设置显示或隐藏整个类别的数据竞争。可以使用如下 +Kconfig 参数进行更改: + +* ``CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY``: 如果启用了该参数并且通过观测点 + (watchpoint) 观测到一个有冲突的写操作,但是对应的内存地址中存储的值没有改变, + 则不会报告这起数据竞争。 + +* ``CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC``: 假设默认情况下,不超过字大小的简 + 单对齐写入操作是原子的。假设这些写入操作不会受到不安全的编译器优化影响,从而导 + 致数据竞争。该选项使 KCSAN 不报告仅由不超过字大小的简单对齐写入操作引起 + 的冲突所导致的数据竞争。 + +* ``CONFIG_KCSAN_PERMISSIVE``: 启用额外的宽松规则来忽略某些常见类型的数据竞争。 + 与上面的规则不同,这条规则更加复杂,涉及到值改变模式,访问类型和地址。这个 + 选项依赖编译选项 ``CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY=y``。请查看 + ``kernel/kcsan/permissive.h`` 获取更多细节。对于只侧重于特定子系统而不是整个 + 内核报告的测试者和维护者,建议禁用该选项。 + +要使用尽可能严格的规则,选择 ``CONFIG_KCSAN_STRICT=y``,这将配置 KCSAN 尽可 +能紧密地遵循 Linux 内核内存一致性模型(LKMM)。 + +Debug 文件系统接口 +~~~~~~~~~~~~~~~~~~ + +文件 ``/sys/kernel/debug/kcsan`` 提供了如下接口: + +* 读 ``/sys/kernel/debug/kcsan`` 返回不同的运行时统计数据。 + +* 将 ``on`` 或 ``off`` 写入 ``/sys/kernel/debug/kcsan`` 允许打开或关闭 KCSAN。 + +* 将 ``!some_func_name`` 写入 ``/sys/kernel/debug/kcsan`` 会将 + ``some_func_name`` 添加到报告过滤列表中,该列表(默认)会将数据竞争报告中的顶 + 层堆栈帧是列表中函数的情况列入黑名单。 + +* 将 ``blacklist`` 或 ``whitelist`` 写入 ``/sys/kernel/debug/kcsan`` 会改变报告 + 过滤行为。例如,黑名单的特性可以用来过滤掉经常发生的数据竞争。白名单特性可以帮 + 助复现和修复测试。 + +性能调优 +~~~~~~~~ + +影响 KCSAN 整体的性能和 bug 检测能力的核心参数是作为内核命令行参数公开的,其默认 +值也可以通过相应的 Kconfig 选项更改。 + +* ``kcsan.skip_watch`` (``CONFIG_KCSAN_SKIP_WATCH``): 在另一个观测点设置之前每 + 个 CPU 要跳过的内存操作次数。更加频繁的设置观测点将增加观察到竞争情况的可能性 + 。这个参数对系统整体的性能和竞争检测能力影响最显著。 + +* ``kcsan.udelay_task`` (``CONFIG_KCSAN_UDELAY_TASK``): 对于任务,观测点设置之 + 后暂停执行的微秒延迟。值越大,检测到竞争情况的可能性越高。 + +* ``kcsan.udelay_interrupt`` (``CONFIG_KCSAN_UDELAY_INTERRUPT``): 对于中断, + 观测点设置之后暂停执行的微秒延迟。中断对于延迟的要求更加严格,其延迟通常应该小 + 于为任务选择的延迟。 + +它们可以通过 ``/sys/module/kcsan/parameters/`` 在运行时进行调整。 + +数据竞争 +-------- + +在一次执行中,如果两个内存访问存在 *冲突*,在不同的线程中并发执行,并且至少 +有一个访问是 *简单访问*,则它们就形成了 *数据竞争*。如果它们访问了同一个内存地址并且 +至少有一个是写操作,则称它们存在 *冲突*。有关更详细的讨论和定义,见 +`LKMM 中的 "简单访问和数据竞争"`_。 + +.. _LKMM 中的 "简单访问和数据竞争": https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/memory-model/Documentation/explanation.txt#n1922 + +与 Linux 内核内存一致性模型(LKMM)的关系 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +LKMM 定义了各种内存操作的传播和排序规则,让开发者可以推理并发代码。最终这允许确 +定并发代码可能的执行情况并判断这些代码是否存在数据竞争。 + +KCSAN 可以识别 *被标记的原子操作* ( ``READ_ONCE``, ``WRITE_ONCE`` , ``atomic_*`` +等),以及内存屏障所隐含的一部分顺序保证。启用 ``CONFIG_KCSAN_WEAK_MEMORY=y`` +配置,KCSAN 会对加载或存储缓冲区进行建模,并可以检测遗漏的 +``smp_mb()``, ``smp_wmb()``, ``smp_rmb()``, ``smp_store_release()``,以及所有的 +具有等效隐含内存屏障的 ``atomic_*`` 操作。 + +请注意,KCSAN 不会报告所有由于缺失内存顺序而导致的数据竞争,特别是在需要内存屏障 +来禁止后续内存操作在屏障之前重新排序的情况下。因此,开发人员应该仔细考虑那些未 +被检查的内存顺序要求。 + +数据竞争以外的竞争检测 +--------------------------- + +对于有着复杂并发设计的代码,竞争状况不总是表现为数据竞争。如果并发操作引起了意 +料之外的系统行为,则认为发生了竞争状况。另一方面,数据竞争是在 C 语言层面定义 +的。内核定义了一些宏定义用来检测非数据竞争的漏洞并发代码的属性。 + +.. note:: + 为了不引入新的文档编译警告,这里不展示宏定义的具体内容,如果想查看具体 + 宏定义可以结合原文(Documentation/dev-tools/kcsan.rst)阅读。 + +实现细节 +-------- + +KCSAN 需要观测两个并发访问。特别重要的是,我们想要(a)增加观测到竞争的机会(尤 +其是很少发生的竞争),以及(b)能够实际观测到这些竞争。我们可以通过(a)注入 +不同的延迟,以及(b)使用地址观测点(或断点)来实现。 + +如果我们在设置了地址观察点的情况下故意延迟一个内存访问,然后观察到观察点被触发 +,那么两个对同一地址的访问就发生了竞争。使用硬件观察点,这是 `DataCollider +<http://usenix.org/legacy/events/osdi10/tech/full_papers/Erickson.pdf>`_ 中采用 +的方法。与 DataCollider 不同,KCSAN 不使用硬件观察点,而是依赖于编译器插桩和“软 +观测点”。 + +在 KCSAN 中,观察点是通过一种高效的编码实现的,该编码将访问类型、大小和地址存储 +在一个长整型变量中;使用“软观察点”的好处是具有可移植性和更大的灵活性。然后, +KCSAN依赖于编译器对普通访问的插桩。对于每个插桩的普通访问: + +1. 检测是否存在一个符合的观测点,如果存在,并且至少有一个操作是写操作,则我们发 + 现了一个竞争访问。 + +2. 如果不存在匹配的观察点,则定期的设置一个观测点并随机延迟一小段时间。 + +3. 在延迟前检查数据值,并在延迟后重新检查数据值;如果值不匹配,我们推测存在一个 + 未知来源的竞争状况。 + +为了检测普通访问和标记访问之间的数据竞争,KCSAN 也对标记访问进行标记,但仅用于 +检查是否存在观察点;即 KCSAN 不会在标记访问上设置观察点。通过不在标记操作上设 +置观察点,如果对一个变量的所有并发访问都被正确标记,KCSAN 将永远不会触发观察点 +,因此也不会报告这些访问。 + +弱内存建模 +~~~~~~~~~~ + +KCSAN 通过建模访问重新排序(使用 ``CONFIG_KCSAN_WEAK_MEMORY=y``)来检测由于缺少 +内存屏障而导致的数据竞争。每个设置了观察点的普通内存访问也会被选择在其函数范围 +内进行模拟重新排序(最多一个正在进行的访问)。 + +一旦某个访问被选择用于重新排序,它将在函数范围内与每个其他访问进行检查。如果遇 +到适当的内存屏障,该访问将不再被考虑进行模拟重新排序。 + +当内存操作的结果应该由屏障排序时,KCSAN 可以检测到仅由于缺失屏障而导致的冲突的 +数据竞争。考虑下面的例子:: + + int x, flag; + void T1(void) + { + x = 1; // data race! + WRITE_ONCE(flag, 1); // correct: smp_store_release(&flag, 1) + } + void T2(void) + { + while (!READ_ONCE(flag)); // correct: smp_load_acquire(&flag) + ... = x; // data race! + } + +当启用了弱内存建模,KCSAN 将考虑对 ``T1`` 中的 ``x`` 进行模拟重新排序。在写入 +``flag`` 之后,x再次被检查是否有并发访问:因为 ``T2`` 可以在写入 +``flag`` 之后继续进行,因此检测到数据竞争。如果遇到了正确的屏障, ``x`` 在正确 +释放 ``flag`` 后将不会被考虑重新排序,因此不会检测到数据竞争。 + +在复杂性上的权衡以及实际的限制意味着只能检测到一部分由于缺失内存屏障而导致的数 +据竞争。由于当前可用的编译器支持,KCSAN 的实现仅限于建模“缓冲”(延迟访问)的 +效果,因为运行时不能“预取”访问。同时要注意,观测点只设置在普通访问上,这是唯 +一一个 KCSAN 会模拟重新排序的访问类型。这意味着标记访问的重新排序不会被建模。 + +上述情况的一个后果是获取 (acquire) 操作不需要屏障插桩(不需要预取)。此外,引 +入地址或控制依赖的标记访问不需要特殊处理(标记访问不能重新排序,后续依赖的访问 +不能被预取)。 + +关键属性 +~~~~~~~~ + +1. **内存开销**:整体的内存开销只有几 MiB,取决于配置。当前的实现是使用一个小长 + 整型数组来编码观测点信息,几乎可以忽略不计。 + +2. **性能开销**:KCSAN 的运行时旨在性能开销最小化,使用一个高效的观测点编码,在 + 快速路径中不需要获取任何锁。在拥有 8 个 CPU 的系统上的内核启动来说: + + - 使用默认 KCSAN 配置时,性能下降 5 倍; + - 仅因运行时快速路径开销导致性能下降 2.8 倍(设置非常大的 + ``KCSAN_SKIP_WATCH`` 并取消设置 ``KCSAN_SKIP_WATCH_RANDOMIZE``)。 + +3. **注解开销**:KCSAN 运行时之外需要的注释很少。因此,随着内核的发展维护的开 + 销也很小。 + +4. **检测设备的竞争写入**:由于设置观测点时会检查数据值,设备的竞争写入也可以 + 被检测到。 + +5. **内存排序**:KCSAN 只了解一部分 LKMM 排序规则;这可能会导致漏报数据竞争( + 假阴性)。 + +6. **分析准确率**: 对于观察到的执行,由于使用采样策略,分析是 *不健全* 的 + (可能有假阴性),但期望得到完整的分析(没有假阳性)。 + +考虑的替代方案 +-------------- + +一个内核数据竞争检测的替代方法是 `Kernel Thread Sanitizer (KTSAN) +<https://github.com/google/kernel-sanitizers/blob/master/KTSAN.md>`_。KTSAN 是一 +个基于先行发生关系(happens-before)的数据竞争检测器,它显式建立内存操作之间的先 +后发生顺序,这可以用来确定 `数据竞争`_ 中定义的数据竞争。 + +为了建立正确的先行发生关系,KTSAN 必须了解 LKMM 的所有排序规则和同步原语。不幸 +的是,任何遗漏都会导致大量的假阳性,这在包含众多自定义同步机制的内核上下文中特 +别有害。为了跟踪前因后果关系,KTSAN 的实现需要为每个内存位置提供元数据(影子内 +存),这意味着每页内存对应 4 页影子内存,在大型系统上可能会带来数十 GiB 的开销 +。 diff --git a/Documentation/translations/zh_CN/dev-tools/kmemleak.rst b/Documentation/translations/zh_CN/dev-tools/kmemleak.rst new file mode 100644 index 000000000000..d248c8428095 --- /dev/null +++ b/Documentation/translations/zh_CN/dev-tools/kmemleak.rst @@ -0,0 +1,229 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/dev-tools/kmemleak.rst +:Translator: 刘浩阳 Haoyang Liu <tttturtleruss@hust.edu.cn> + +内核内存泄露检测器 +================== + +Kmemleak 提供了一个类似 `可追踪的垃圾收集器 <https://en.wikipedia.org/wiki/Tra +cing_garbage_collection>`_ 的方法来检测可能的内核内存泄漏,不同的是孤立对象不会 +被释放,而是仅通过 /sys/kernel/debug/kmemleak 报告。Valgrind 工具 +(``memcheck --leak-check``)使用了一种相似的方法来检测用户空间应用中的内存泄 +露。 + +用法 +---- + +"Kernel hacking" 中的 CONFIG_DEBUG_KMEMLEAK 必须被启用。一个内核线程每10分钟 +(默认情况下)扫描一次内存,并且打印出新发现的未被引用的对象个数。 +如果 ``debugfs`` 没有挂载,则执行:: + + # mount -t debugfs nodev /sys/kernel/debug/ + +显示所有扫描出的可能的内存泄漏的细节信息:: + + # cat /sys/kernel/debug/kmemleak + +启动一次中等程度的内存扫描:: + + # echo scan > /sys/kernel/debug/kmemleak + +清空当前所有可能的内存泄露列表:: + + # echo clear > /sys/kernel/debug/kmemleak + +当再次读取 ``/sys/kernel/debug/kmemleak`` 文件时,将会输出自上次扫描以来检测到的 +新的内存泄露。 + +注意,孤立目标是通过被分配时间来排序的,列表开始的对象可能会导致后续的对象都被 +识别为孤立对象。 + +可以通过写入 ``/sys/kernel/debug/kmemleak`` 文件在运行时修改内存扫描参数。下面是 +支持的参数: + + +* off + 禁用 kmemleak(不可逆) +* stack=on + 开启任务栈扫描(默认) +* stack=off + 禁用任务栈扫描 +* scan=on + 开启自动内存扫描线程(默认) +* scan=off + 关闭自动内存扫描线程 +* scan=<secs>; + 设定自动内存扫描间隔,以秒为单位(默认值为 600,设置为 0 表示停 + 止自动扫描) +* scan + 触发一次内存扫描 +* clear + 通过标记所有当前已报告的未被引用对象为灰,从而清空当前可能的内存泄露列 + 表;如果 kmemleak 被禁用,则释放所有 kmemleak 对象,。 +* dump=<addr> + 输出存储在 <addr> 中的对象信息 + +可以通过在内核命令行中传递 ``kmemleak=off`` 参数从而在启动时禁用 Kmemleak。 + +在 kmemleak 初始化之前就可能会有内存分配或释放,这些操作被存储在一个早期日志缓 +冲区中。缓冲区的大小通过 CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE 选项配置。 + +如果 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF 被启用,则 kmemleak 默认被禁用。在内核命 +令行中传递 ``kmemleak=on`` 参数来开启这个功能。 + +如果出现 "Error while writing to stdout" 或 "write_loop: Invalid argument" 这样 +的错误,请确认 kmemleak 被正确启用。 + +基础算法 +-------- + +通过 :c:func:`kmalloc`, :c:func:`vmalloc`, :c:func:`kmem_cache_alloc` 以及同类 +函数均被跟踪,指针,包括一些额外的信息如大小和栈追踪等,都被存储在红黑树中。 +对应的释放函数调用也被追踪,并从 kmemleak 数据结构中移除相应指针。 + +对于一个已分配的内存块,如果通过扫描内存(包括保存寄存器)没有发现任何指针指向 +它的起始地址或者其中的任何位置,则认为这块内存是孤立的。这意味着内核无法将该内 +存块的地址传递给一个释放内存函数,这块内存便被认为泄露了。 + +扫描算法步骤: + + 1. 标记所有对象为白色(最后剩下的白色对象被认为是孤立的) + 2. 从数据节和栈开始扫描内存,检测每个值是否是红黑树中存储的地址。如果一个指向 + 白色对象的指针被检测到,则将该对象标记为灰色。 + 3. 扫描灰色对象引用的其他对象(有些白色对象可能会变为灰色并被添加到灰名单末尾 + )直到灰名单为空。 + 4. 剩余的白色对象就被认为是孤立的并通过 /sys/kernel/debug/kmemleak 报告。 + +有些指向已分配的内存块的指针存储在内核内部的数据结构中,它们不能被检测为孤立。 +为了避免这种情况,kmemleak 也存储了指向需要被查找的内存块范围内的任意地址的地址 +数量,如此一来这些内存便不会被认为泄露。一个例子是 __vmalloc()。 + +用 kmemleak 测试特定部分 +------------------------ + +在初始化启动阶段 /sys/kernel/debug/kmemleak 的输出可能会很多,这也可能是你在开发 +时编写的漏洞百出的代码导致的。为了解决这种情况你可以使用 'clear' 命令来清除 +/sys/kernel/debug/kmemleak 输出的所有的未引用对象。在执行 'clear' 后执行 'scan' +可以发现新的未引用对象,这将会有利你测试代码的特定部分。 + +为了用一个空的 kmemleak 测试一个特定部分,执行:: + + # echo clear > /sys/kernel/debug/kmemleak + ... 测试你的内核或者模块 ... + # echo scan > /sys/kernel/debug/kmemleak + +然后像平常一样获得报告:: + + # cat /sys/kernel/debug/kmemleak + +释放 kmemleak 内核对象 +---------------------- + +为了允许访问先前发现的内存泄露,当用户禁用或发生致命错误导致 kmemleak +被禁用时,内核中的 kmemleak 对象不会被释放。这些对象可能会占用很大 +一部分物理内存。 + +在这种情况下,你可以用如下命令回收这些内存:: + + # echo clear > /sys/kernel/debug/kmemleak + +Kmemleak API +------------ + +在 include/linux/kmemleak.h 头文件中查看函数原型: + +- ``kmemleak_init`` - 初始化 kmemleak +- ``kmemleak_alloc`` - 通知一个内存块的分配 +- ``kmemleak_alloc_percpu`` - 通知一个 percpu 类型的内存分配 +- ``kmemleak_vmalloc`` - 通知一个使用 vmalloc() 的内存分配 +- ``kmemleak_free`` - 通知一个内存块的释放 +- ``kmemleak_free_part`` - 通知一个部分的内存释放 +- ``kmemleak_free_percpu`` - 通知一个 percpu 类型的内存释放 +- ``kmemleak_update_trace`` - 更新分配对象过程的栈追踪 +- ``kmemleak_not_leak`` - 标记一个对象内存为未泄露的 +- ``kmemleak_ignore`` - 不要扫描或报告某个对象未泄露的 +- ``kmemleak_scan_area`` - 在内存块中添加扫描区域 +- ``kmemleak_no_scan`` - 不扫描某个内存块 +- ``kmemleak_erase`` - 在指针变量中移除某个旧的值 +- ``kmemleak_alloc_recursive`` - 和 kmemleak_alloc 效果相同但会检查是否有递归的 + 内存分配 +- ``kmemleak_free_recursive`` - 和 kmemleak_free 效果相同但会检查是否有递归的 + 内存释放 + +下列函数使用一个物理地址作为对象指针并且只在地址有一个 lowmem 映射时做出相应的 +行为: + +- ``kmemleak_alloc_phys`` +- ``kmemleak_free_part_phys`` +- ``kmemleak_ignore_phys`` + +解决假阳性/假阴性 +----------------- + +假阴性是指由于在内存扫描中有值指向该对象导致 kmemleak 没有报告的实际存在的内存 +泄露(孤立对象)。为了减少假阴性的出现次数,kmemleak 提供了 kmemleak_ignore, +kmemleak_scan_area,kmemleak_no_scan 和 kmemleak_erase 函数(见上)。 +任务栈也会增加假阴性的数量并且默认不开启对它们的扫描。 + +假阳性是对象被误报为内存泄露(孤立对象)。对于已知未泄露的对象,kmemleak +提供了 kmemleak_not_leak 函数。同时 kmemleak_ignore 可以用于标记已知不包含任何 +其他指针的内存块,标记后该内存块不会再被扫描。 + +一些被报告的泄露仅仅是暂时的,尤其是在 SMP(对称多处理)系统中,因为其指针 +暂存在 CPU 寄存器或栈中。Kmemleak 定义了 MSECS_MIN_AGE(默认值为 1000) +来表示一个被报告为内存泄露的对象的最小存活时间。 + +限制和缺点 +---------- + +主要的缺点是内存分配和释放的性能下降。为了避免其他的损失,只有当 +/sys/kernel/debug/kmemleak 文件被读取时才会进行内存扫描。无论如何,这个工具是出于 +调试的目标,性能表现可能不是最重要的。 + +为了保持算法简单,kmemleak 寻找指向某个内存块范围中的任何值。这可能会引发假阴性 +现象的出现。但是,最后一个真正的内存泄露也会变得明显。 + +非指针值的数据是假阴性的另一个来源。在将来的版本中,kmemleak 仅仅会扫 +描已分配结构体中的指针成员。这个特性会解决上述很多的假阴性情况。 + +Kmemleak 会报告假阳性。这可能发生在某些被分配的内存块不需要被释放的情况下 +(某些 init_call 函数中),指针的计算是通过其他方法而不是常规的 container_of 宏 +或是指针被存储在 kmemleak 没有扫描的地方。 + +页分配和 ioremap 不会被追踪。 + +使用 kmemleak-test 测试 +----------------------- + +为了检测是否成功启用了 kmemleak,你可以使用一个故意制造内存泄露的模块 +kmemleak-test。设置 CONFIG_SAMPLE_KMEMLEAK 为模块(不能作为内建模块使用) +并且启动启用了 kmemleak 的内核。加载模块并执行一次扫描:: + + # modprobe kmemleak-test + # echo scan > /sys/kernel/debug/kmemleak + +注意你可能无法立刻或在第一次扫描后得到结果。当 kmemleak 得到结果,将会输出日 +志 ``kmemleak: <count of leaks> new suspected memory leaks`` 。然后通过读取文件 +获取信息:: + + # cat /sys/kernel/debug/kmemleak + unreferenced object 0xffff89862ca702e8 (size 32): + comm "modprobe", pid 2088, jiffies 4294680594 (age 375.486s) + hex dump (first 32 bytes): + 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk + 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5 kkkkkkkkkkkkkkk. + backtrace: + [<00000000e0a73ec7>] 0xffffffffc01d2036 + [<000000000c5d2a46>] do_one_initcall+0x41/0x1df + [<0000000046db7e0a>] do_init_module+0x55/0x200 + [<00000000542b9814>] load_module+0x203c/0x2480 + [<00000000c2850256>] __do_sys_finit_module+0xba/0xe0 + [<000000006564e7ef>] do_syscall_64+0x43/0x110 + [<000000007c873fa6>] entry_SYSCALL_64_after_hwframe+0x44/0xa9 + ... + +用 ``rmmod kmemleak_test`` 移除模块时也会触发 +kmemleak 的结果输出。 diff --git a/Documentation/translations/zh_CN/dev-tools/kmsan.rst b/Documentation/translations/zh_CN/dev-tools/kmsan.rst new file mode 100644 index 000000000000..b1ddb47bd6c4 --- /dev/null +++ b/Documentation/translations/zh_CN/dev-tools/kmsan.rst @@ -0,0 +1,392 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/dev-tools/kmsan.rst +:Translator: 刘浩阳 Haoyang Liu <tttturtleruss@hust.edu.cn> + +======================= +内核内存消毒剂(KMSAN) +======================= + +KMSAN 是一个动态错误检测器,旨在查找未初始化值的使用。它基于编译器插桩,类似于用 +户空间的 `MemorySanitizer tool`_。 + +需要注意的是 KMSAN 并不适合生产环境,因为它会大幅增加内核内存占用并降低系统运行速度。 + +使用方法 +======== + +构建内核 +-------- + +要构建带有 KMSAN 的内核,你需要一个较新的 Clang (14.0.6+)。 +请参阅 `LLVM documentation`_ 了解如何构建 Clang。 + +现在配置并构建一个启用 CONFIG_KMSAN 的内核。 + +示例报告 +-------- + +以下是一个 KMSAN 报告的示例:: + + ===================================================== + BUG: KMSAN: uninit-value in test_uninit_kmsan_check_memory+0x1be/0x380 [kmsan_test] + test_uninit_kmsan_check_memory+0x1be/0x380 mm/kmsan/kmsan_test.c:273 + kunit_run_case_internal lib/kunit/test.c:333 + kunit_try_run_case+0x206/0x420 lib/kunit/test.c:374 + kunit_generic_run_threadfn_adapter+0x6d/0xc0 lib/kunit/try-catch.c:28 + kthread+0x721/0x850 kernel/kthread.c:327 + ret_from_fork+0x1f/0x30 ??:? + + Uninit was stored to memory at: + do_uninit_local_array+0xfa/0x110 mm/kmsan/kmsan_test.c:260 + test_uninit_kmsan_check_memory+0x1a2/0x380 mm/kmsan/kmsan_test.c:271 + kunit_run_case_internal lib/kunit/test.c:333 + kunit_try_run_case+0x206/0x420 lib/kunit/test.c:374 + kunit_generic_run_threadfn_adapter+0x6d/0xc0 lib/kunit/try-catch.c:28 + kthread+0x721/0x850 kernel/kthread.c:327 + ret_from_fork+0x1f/0x30 ??:? + + Local variable uninit created at: + do_uninit_local_array+0x4a/0x110 mm/kmsan/kmsan_test.c:256 + test_uninit_kmsan_check_memory+0x1a2/0x380 mm/kmsan/kmsan_test.c:271 + + Bytes 4-7 of 8 are uninitialized + Memory access of size 8 starts at ffff888083fe3da0 + + CPU: 0 PID: 6731 Comm: kunit_try_catch Tainted: G B E 5.16.0-rc3+ #104 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 + ===================================================== + +报告指出本地变量 ``uninit`` 在 ``do_uninit_local_array()`` 中未初始化。 +第三个堆栈跟踪对应于该变量创建的位置。 + +第一个堆栈跟踪显示了未初始化值的使用位置(在 +``test_uninit_kmsan_check_memory()``)。 +工具显示了局部变量中未初始化的字节及其被复制到其他内存位置前的堆栈。 + +KMSAN 会在以下情况下报告未初始化的值 ``v``: + + - 在条件判断中,例如 ``if (v) { ... }``; + - 在索引或指针解引用中,例如 ``array[v]`` 或 ``*v``; + - 当它被复制到用户空间或硬件时,例如 ``copy_to_user(..., &v, ...)``; + - 当它作为函数参数传递,并且启用 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 时(见下文)。 + +这些情况(除了复制数据到用户空间或硬件外,这是一个安全问题)被视为 C11 标准下的未定义行为。 + +禁用插桩 +-------- + +可以用 ``__no_kmsan_checks`` 标记函数。这样,KMSAN 会忽略该函数中的未初始化值, +并将其输出标记为已初始化。如此,用户不会收到与该函数相关的 KMSAN 报告。 + +KMSAN 还支持 ``__no_sanitize_memory`` 函数属性。KMSAN 不会对拥有该属性的函数进行 +插桩,这在我们不希望编译器干扰某些底层代码(例如标记为 ``noinstr`` 的代码,该 +代码隐式添加了 ``__no_sanitize_memory``)时可能很有用。 + +然而,这会有代价:此类函数的栈分配将具有不正确的影子/初始值,可能导致误报。来 +自非插桩代码的函数也可能接收到不正确的元数据。 + + +作为经验之谈,避免显式使用 ``__no_sanitize_memory``。 + +也可以通过 Makefile 禁用 KMSAN 对某个文件(例如 main.o)的作用:: + + KMSAN_SANITIZE_main.o := n + +或者对整个目录:: + + KMSAN_SANITIZE := n + +将其应用到文件或目录中的每个函数。大多数用户不会需要 KMSAN_SANITIZE, +除非他们的代码被 KMSAN 破坏(例如在早期启动时运行的代码)。 + +还可以通过调用 ``kmsan_disable_current()`` 和 ``kmsan_enable_current()`` +暂时对当前任务禁用 KMSAN 检查。每个 ``kmsan_enable_current()`` 必须在 +``kmsan_disable_current()`` 之后调用;这些调用对可以嵌套。在调用时需要注意保持 +嵌套区域简短,并且尽可能使用其他方法禁用插桩。 + +支持 +==== + +为了使用 KMSAN,内核必须使用 Clang 构建,到目前为止,Clang 是唯一支持 KMSAN +的编译器。内核插桩过程基于用户空间的 `MemorySanitizer tool`_。 + +目前运行时库仅支持 x86_64 架构。 + +KMSAN 的工作原理 +================ + +KMSAN 阴影内存 +-------------- + +KMSAN 将一个元数据字节(也称为阴影字节)与每个内核内存字节关联。仅当内核内存字节 +的相应位未初始化时,阴影字节中的一个比特位才会被设置。将内存标记为未初始化(即 +将其阴影字节设置为 ``0xff``)称为中毒,将其标记为已初始化(将阴影字节设置为 +``0x00``)称为解毒。 + +当在栈上分配新变量时,默认情况下它会中毒,这由编译器插入的插桩代码完成(除非它 +是立即初始化的栈变量)。任何未使用 ``__GFP_ZERO`` 的堆分配也会中毒。 + +编译器插桩还跟踪阴影值在代码中的使用。当需要时,插桩代码会调用 ``mm/kmsan/`` 中 +的运行时库以持久化阴影值。 + +基本或复合类型的阴影值是长度相同的字节数组。当常量值写入内存时,该内存会被解毒 +。当从内存读取值时,其阴影内存也会被获取,并传递到所有使用该值的操作中。对于每 +个需要一个或多个值的指令,编译器会生成代码根据这些值及其阴影来计算结果的阴影。 + + +示例:: + + int a = 0xff; // i.e. 0x000000ff + int b; + int c = a | b; + +在这种情况下, ``a`` 的阴影为 ``0``, ``b`` 的阴影为 ``0xffffffff``, +``c`` 的阴影为 ``0xffffff00``。这意味着 ``c`` 的高三个字节未初始化,而低字节已 +初始化。 + +起源跟踪 +-------- + +每四字节的内核内存都有一个所谓的源点与之映射。这个源点描述了在程序执行中,未初 +始化值的创建点。每个源点都与完整的分配栈(对于堆分配的内存)或包含未初始化变 +量的函数(对于局部变量)相关联。 + +当一个未初始化的变量在栈或堆上分配时,会创建一个新的源点值,并将该变量的初始值 +填充为这个值。当从内存中读取一个值时,其初始值也会被读取并与阴影一起保留。对于 +每个接受一个或多个值的指令,结果的源点是与任何未初始化输入相对应的源点之一。如 +果一个污染值被写入内存,其起源也会被写入相应的存储中。 + +示例 1:: + + int a = 42; + int b; + int c = a + b; + +在这种情况下, ``b`` 的源点是在函数入口时生成的,并在加法结果写入内存之前存储到 +``c`` 的源点中。 + +如果几个变量共享相同的源点地址,则它们被存储在同一个四字节块中。在这种情况下, +对任何变量的每次写入都会更新所有变量的源点。在这种情况下我们必须牺牲精度,因 +为为单独的位(甚至字节)存储源点成本过高。 + +示例 2:: + + int combine(short a, short b) { + union ret_t { + int i; + short s[2]; + } ret; + ret.s[0] = a; + ret.s[1] = b; + return ret.i; + } + +如果 ``a`` 已初始化而 ``b`` 未初始化,则结果的阴影为 0xffff0000,结果的源点为 +``b`` 的源点。 ``ret.s[0]`` 会有相同的起源,但它不会被使用,因为该变量已初始化。 + +如果两个函数参数都未初始化,则只保留第二个参数的源点。 + +源点链 +~~~~~~ + +为了便于调试,KMSAN 在每次将未初始化值存储到内存时都会创建一个新的源点。新的源点 +引用了其创建栈以及值的前一个起源。这可能导致内存消耗增加,因此我们在运行时限制 +了源点链的长度。 + +Clang 插桩 API +-------------- + +Clang 插桩通过在内核代码中插入定义在 ``mm/kmsan/instrumentation.c`` 中的函数调用 +来实现。 + + +阴影操作 +~~~~~~~~ + +对于每次内存访问,编译器都会发出一个函数调用,该函数返回一对指针,指向给定内存 +的阴影和原始地址:: + + typedef struct { + void *shadow, *origin; + } shadow_origin_ptr_t + + shadow_origin_ptr_t __msan_metadata_ptr_for_load_{1,2,4,8}(void *addr) + shadow_origin_ptr_t __msan_metadata_ptr_for_store_{1,2,4,8}(void *addr) + shadow_origin_ptr_t __msan_metadata_ptr_for_load_n(void *addr, uintptr_t size) + shadow_origin_ptr_t __msan_metadata_ptr_for_store_n(void *addr, uintptr_t size) + +函数名依赖于内存访问的大小。 + +编译器确保对于每个加载的值,其阴影和原始值都从内存中读取。当一个值存储到内存时 +,其阴影和原始值也会通过元数据指针进行存储。 + +处理局部变量 +~~~~~~~~~~~~ + +一个特殊的函数用于为局部变量创建一个新的原始值,并将该变量的原始值设置为该值:: + + void __msan_poison_alloca(void *addr, uintptr_t size, char *descr) + +访问每个任务数据 +~~~~~~~~~~~~~~~~ + +在每个插桩函数的开始处,KMSAN 插入一个对 ``__msan_get_context_state()`` 的调用 +:: + + kmsan_context_state *__msan_get_context_state(void) + +``kmsan_context_state`` 在 ``include/linux/kmsan.h`` 中声明:: + + struct kmsan_context_state { + char param_tls[KMSAN_PARAM_SIZE]; + char retval_tls[KMSAN_RETVAL_SIZE]; + char va_arg_tls[KMSAN_PARAM_SIZE]; + char va_arg_origin_tls[KMSAN_PARAM_SIZE]; + u64 va_arg_overflow_size_tls; + char param_origin_tls[KMSAN_PARAM_SIZE]; + depot_stack_handle_t retval_origin_tls; + }; + +KMSAN 使用此结构体在插桩函数之间传递参数阴影和原始值(除非立刻通过 + ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 检查参数)。 + +将未初始化的值传递给函数 +~~~~~~~~~~~~~~~~~~~~~~~~ + +Clang 的 MemorySanitizer 插桩有一个选项 ``-fsanitize-memory-param-retval``,该 +选项使编译器检查按值传递的函数参数,以及函数返回值。 + +该选项由 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 控制,默认启用以便 KMSAN 更早报告 +未初始化的值。有关更多细节,请参考 `LKML discussion`_。 + +由于 LLVM 中的实现检查的方式(它们仅应用于标记为 ``noundef`` 的参数),并不是所 +有参数都能保证被检查,因此我们不能放弃 ``kmsan_context_state`` 中的元数据存储 +。 + +字符串函数 +~~~~~~~~~~~ + +编译器将对 ``memcpy()``/``memmove()``/``memset()`` 的调用替换为以下函数。这些函 +数在数据结构初始化或复制时也会被调用,确保阴影和原始值与数据一起复制:: + + void *__msan_memcpy(void *dst, void *src, uintptr_t n) + void *__msan_memmove(void *dst, void *src, uintptr_t n) + void *__msan_memset(void *dst, int c, uintptr_t n) + +错误报告 +~~~~~~~~ + +对于每个值的使用,编译器发出一个阴影检查,在值中毒的情况下调用 +``__msan_warning()``:: + + void __msan_warning(u32 origin) + +``__msan_warning()`` 使 KMSAN 运行时打印错误报告。 + +内联汇编插桩 +~~~~~~~~~~~~ + +KMSAN 对每个内联汇编输出进行插桩,调用:: + + void __msan_instrument_asm_store(void *addr, uintptr_t size) + +,该函数解除内存区域的污染。 + +这种方法可能会掩盖某些错误,但也有助于避免许多位操作、原子操作等中的假阳性。 + +有时传递给内联汇编的指针不指向有效内存。在这种情况下,它们在运行时被忽略。 + + +运行时库 +-------- + +代码位于 ``mm/kmsan/``。 + +每个任务 KMSAN 状态 +~~~~~~~~~~~~~~~~~~~ + +每个 task_struct 都有一个关联的 KMSAN 任务状态,它保存 KMSAN +上下文(见上文)和一个每个任务计数器以禁止 KMSAN 报告:: + + struct kmsan_context { + ... + unsigned int depth; + struct kmsan_context_state cstate; + ... + } + + struct task_struct { + ... + struct kmsan_context kmsan; + ... + } + +KMSAN 上下文 +~~~~~~~~~~~~ + +在内核任务上下文中运行时,KMSAN 使用 ``current->kmsan.cstate`` 来 +保存函数参数和返回值的元数据。 + +但在内核运行于中断、softirq 或 NMI 上下文中, ``current`` 不可用时, +KMSAN 切换到每 CPU 中断状态:: + + DEFINE_PER_CPU(struct kmsan_ctx, kmsan_percpu_ctx); + +元数据分配 +~~~~~~~~~~ + +内核中有多个地方存储元数据。 + +1. 每个 ``struct page`` 实例包含两个指向其影子和内存页面的指针 +:: + + struct page { + ... + struct page *shadow, *origin; + ... + }; + +在启动时,内核为每个可用的内核页面分配影子和源页面。这是在内核地址空间已经碎片 +化时后完成的,完成的相当晚,因此普通数据页面可能与元数据页面任意交错。 + +这意味着通常两个相邻的内存页面,它们的影子/源页面可能不是连续的。因此,如果内存 +访问跨越内存块的边界,访问影子/源内存可能会破坏其他页面或从中读取错误的值。 + +实际上,由相同 ``alloc_pages()`` 调用返回的连续内存页面将具有连续的元数据,而 +如果这些页面属于两个不同的分配,它们的元数据页面可能会被碎片化。 + +对于内核数据( ``.data``、 ``.bss`` 等)和每 CPU 内存区域,也没有对元数据连续 +性的保证。 + +在 ``__msan_metadata_ptr_for_XXX_YYY()`` 遇到两个页面之间的 +非连续元数据边界时,它返回指向假影子/源区域的指针:: + + char dummy_load_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); + char dummy_store_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); + +``dummy_load_page`` 被初始化为零,因此读取它始终返回零。对 ``dummy_store_page`` 的 +所有写入都被忽略。 + +2. 对于 vmalloc 内存和模块,内存范围、影子和源之间有一个直接映射。KMSAN 将 +vmalloc 区域缩小了 3/4,仅使前四分之一可用于 ``vmalloc()``。vmalloc +区域的第二个四分之一包含第一个四分之一的影子内存,第三个四分之一保存源。第四个 +四分之一的小部分包含内核模块的影子和源。有关更多详细信息,请参阅 +``arch/x86/include/asm/pgtable_64_types.h``。 + +当一系列页面映射到一个连续的虚拟内存空间时,它们的影子和源页面也以连续区域的方 +式映射。 + +参考文献 +======== + +E. Stepanov, K. Serebryany. `MemorySanitizer: fast detector of uninitialized +memory use in C++ +<https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43308.pdf>`_. +In Proceedings of CGO 2015. + +.. _MemorySanitizer tool: https://clang.llvm.org/docs/MemorySanitizer.html +.. _LLVM documentation: https://llvm.org/docs/GettingStarted.html +.. _LKML discussion: https://lore.kernel.org/all/20220614144853.3693273-1-glider@google.com/ diff --git a/Documentation/translations/zh_CN/dev-tools/testing-overview.rst b/Documentation/translations/zh_CN/dev-tools/testing-overview.rst index c91f9b60f9f1..286ed6b01f65 100644 --- a/Documentation/translations/zh_CN/dev-tools/testing-overview.rst +++ b/Documentation/translations/zh_CN/dev-tools/testing-overview.rst @@ -99,6 +99,8 @@ Documentation/dev-tools/kcov.rst 是能够构建在内核之中,用于在每 参阅 Documentation/dev-tools/kfence.rst * lockdep是一个锁定正确性检测器。参阅 Documentation/locking/lockdep-design.rst +* 运行时确认(Runtime Verification)支持检查给定子系统的特定行为。参阅 + Documentation/trace/rv/runtime-verification.rst。 * 除此以外,在内核中还有一些其它的调试工具,大多数能在 lib/Kconfig.debug 中找到。 diff --git a/Documentation/translations/zh_CN/dev-tools/ubsan.rst b/Documentation/translations/zh_CN/dev-tools/ubsan.rst new file mode 100644 index 000000000000..2487696b3772 --- /dev/null +++ b/Documentation/translations/zh_CN/dev-tools/ubsan.rst @@ -0,0 +1,91 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/dev-tools/ubsan.rst +:Translator: Dongliang Mu <dzm91@hust.edu.cn> + +未定义行为消毒剂 - UBSAN +==================================== + +UBSAN是一种动态未定义行为检查工具。 + +UBSAN使用编译时插桩捕捉未定义行为。编译器在可能导致未定义行为的操作前插入特定 +检测代码。如果检查失败,即检测到未定义行为,__ubsan_handle_* 函数将被调用打印 +错误信息。 + +GCC自4.9.x [1_] (详见 ``-fsanitize=undefined`` 选项及其子选项)版本后引入这 +一特性。GCC 5.x 版本实现了更多检查器 [2_]。 + +报告样例 +-------------- + +:: + + ================================================================================ + UBSAN: Undefined behaviour in ../include/linux/bitops.h:110:33 + shift exponent 32 is to large for 32-bit type 'unsigned int' + CPU: 0 PID: 0 Comm: swapper Not tainted 4.4.0-rc1+ #26 + 0000000000000000 ffffffff82403cc8 ffffffff815e6cd6 0000000000000001 + ffffffff82403cf8 ffffffff82403ce0 ffffffff8163a5ed 0000000000000020 + ffffffff82403d78 ffffffff8163ac2b ffffffff815f0001 0000000000000002 + Call Trace: + [<ffffffff815e6cd6>] dump_stack+0x45/0x5f + [<ffffffff8163a5ed>] ubsan_epilogue+0xd/0x40 + [<ffffffff8163ac2b>] __ubsan_handle_shift_out_of_bounds+0xeb/0x130 + [<ffffffff815f0001>] ? radix_tree_gang_lookup_slot+0x51/0x150 + [<ffffffff8173c586>] _mix_pool_bytes+0x1e6/0x480 + [<ffffffff83105653>] ? dmi_walk_early+0x48/0x5c + [<ffffffff8173c881>] add_device_randomness+0x61/0x130 + [<ffffffff83105b35>] ? dmi_save_one_device+0xaa/0xaa + [<ffffffff83105653>] dmi_walk_early+0x48/0x5c + [<ffffffff831066ae>] dmi_scan_machine+0x278/0x4b4 + [<ffffffff8111d58a>] ? vprintk_default+0x1a/0x20 + [<ffffffff830ad120>] ? early_idt_handler_array+0x120/0x120 + [<ffffffff830b2240>] setup_arch+0x405/0xc2c + [<ffffffff830ad120>] ? early_idt_handler_array+0x120/0x120 + [<ffffffff830ae053>] start_kernel+0x83/0x49a + [<ffffffff830ad120>] ? early_idt_handler_array+0x120/0x120 + [<ffffffff830ad386>] x86_64_start_reservations+0x2a/0x2c + [<ffffffff830ad4f3>] x86_64_start_kernel+0x16b/0x17a + ================================================================================ + +用法 +----- + +使用如下内核配置启用UBSAN:: + + CONFIG_UBSAN=y + +使用如下内核配置检查整个内核:: + + CONFIG_UBSAN_SANITIZE_ALL=y + +为了在特定文件或目录启动代码插桩,需要在相应的内核Makefile中添加一行类似内容: + +- 单文件(如main.o):: + + UBSAN_SANITIZE_main.o := y + +- 一个目录中的所有文件:: + + UBSAN_SANITIZE := y + +即使设置了``CONFIG_UBSAN_SANITIZE_ALL=y``,为了避免文件被插桩,可使用:: + + UBSAN_SANITIZE_main.o := n + +与:: + + UBSAN_SANITIZE := n + +未对齐的内存访问检测可通过开启独立选项 - CONFIG_UBSAN_ALIGNMENT 检测。 +该选项在支持未对齐访问的架构上(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y) +默认为关闭。该选项仍可通过内核配置启用,但它将产生大量的UBSAN报告。 + +参考文献 +---------- + +.. _1: https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Debugging-Options.html +.. _2: https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html +.. _3: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html diff --git a/Documentation/translations/zh_CN/devicetree/of_unittest.rst b/Documentation/translations/zh_CN/devicetree/of_unittest.rst index 11eb08ca8866..5c1a8e0cfd16 100644 --- a/Documentation/translations/zh_CN/devicetree/of_unittest.rst +++ b/Documentation/translations/zh_CN/devicetree/of_unittest.rst @@ -40,7 +40,7 @@ OF Selftest被设计用来测试提供给设备驱动开发者的接口(includ drivers/of/unittest-data/tests-phandle.dtsi drivers/of/unittest-data/tests-match.dtsi -当内核在启用OF_SELFTEST的情况下被构建时,那么下面的make规则:: +当内核在启用CONFIG_OF_UNITTEST的情况下被构建时,那么下面的make规则:: $(obj)/%.dtb: $(src)/%.dts FORCE $(call if_changed_dep, dtc) diff --git a/Documentation/translations/zh_CN/doc-guide/checktransupdate.rst b/Documentation/translations/zh_CN/doc-guide/checktransupdate.rst new file mode 100644 index 000000000000..d20b4ce66b9f --- /dev/null +++ b/Documentation/translations/zh_CN/doc-guide/checktransupdate.rst @@ -0,0 +1,55 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/doc-guide/checktransupdate.rst + +:译者: 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +检查翻译更新 + +这个脚本帮助跟踪不同语言的文档翻译状态,即文档是否与对应的英文版本保持更新。 + +工作原理 +------------ + +它使用 ``git log`` 命令来跟踪翻译提交的最新英文提交(按作者日期排序)和英文文档的 +最新提交。如果有任何差异,则该文件被认为是过期的,然后需要更新的提交将被收集并报告。 + +实现的功能 + +- 检查特定语言中的所有文件 +- 检查单个文件或一组文件 +- 提供更改输出格式的选项 +- 跟踪没有翻译过的文件的翻译状态 + +用法 +----- + +:: + + ./scripts/checktransupdate.py --help + +具体用法请参考参数解析器的输出 + +示例 + +- ``./scripts/checktransupdate.py -l zh_CN`` + 这将打印 zh_CN 语言中需要更新的所有文件。 +- ``./scripts/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst`` + 这将只打印指定文件的状态。 + +然后输出类似如下的内容: + +:: + + Documentation/dev-tools/kfence.rst + No translation in the locale of zh_CN + + Documentation/translations/zh_CN/dev-tools/testing-overview.rst + commit 42fb9cfd5b18 ("Documentation: dev-tools: Add link to RV docs") + 1 commits needs resolving in total + +待实现的功能 + +- 文件参数可以是文件夹而不仅仅是单个文件 diff --git a/Documentation/translations/zh_CN/doc-guide/index.rst b/Documentation/translations/zh_CN/doc-guide/index.rst index 78c2e9a1697f..0ac1fc9315ea 100644 --- a/Documentation/translations/zh_CN/doc-guide/index.rst +++ b/Documentation/translations/zh_CN/doc-guide/index.rst @@ -18,6 +18,7 @@ parse-headers contributing maintainer-profile + checktransupdate .. only:: subproject and html diff --git a/Documentation/translations/zh_CN/driver-api/gpio/index.rst b/Documentation/translations/zh_CN/driver-api/gpio/index.rst index 9a6a14162a6c..e4d54724a1b5 100644 --- a/Documentation/translations/zh_CN/driver-api/gpio/index.rst +++ b/Documentation/translations/zh_CN/driver-api/gpio/index.rst @@ -18,8 +18,6 @@ :caption: 目录 :maxdepth: 2 - legacy - Todolist: * intro diff --git a/Documentation/translations/zh_CN/driver-api/gpio/legacy.rst b/Documentation/translations/zh_CN/driver-api/gpio/legacy.rst deleted file mode 100644 index aeccff777170..000000000000 --- a/Documentation/translations/zh_CN/driver-api/gpio/legacy.rst +++ /dev/null @@ -1,634 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -.. include:: ../../disclaimer-zh_CN.rst - -:Original: Documentation/driver-api/gpio/legacy.rst - -:翻译: - - 傅炜 Fu Wei <tekkamanninja@gmail.com> - 司延腾 Yanteng Si <siyanteng@loongson.cn> - -:校译: - - -传统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,使用以下的函数访问:: - - /* 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 输入:返回零或非零 ,可能会休眠 */ - 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 ----------------- - -为了有助于捕获系统配置错误,定义了两个函数:: - - /* 申请 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 使用引脚控制子系统已知的引脚,子系统应该被告知其使用情况; -一个 gpiolib 驱动的 .request()操作应调用 pinctrl_gpio_request(), -而 gpiolib 驱动的 .free()操作应调用 pinctrl_gpio_free()。引脚控制 -子系统允许 pinctrl_gpio_request()在某个引脚或引脚组以复用形式“属于” -一个设备时都成功返回。 - -任何须将 GPIO 信号导向适当引脚的引脚复用硬件的编程应该发生在 GPIO -驱动的 .direction_input()或 .direction_output()函数中,以及 -任何输出 GPIO 值的设置之后。这样可使从引脚特殊功能到 GPIO 的转换 -不会在引脚产生毛刺波形。有时当用一个 GPIO 实现其信号驱动一个非 GPIO -硬件模块的解决方案时,就需要这种机制。 - -某些平台允许部分或所有 GPIO 信号使用不同的引脚。类似的,GPIO 或引脚的 -其他方面也需要配置,如上拉/下拉。平台软件应该在对这些 GPIO 调用 -gpio_request()前将这类细节配置好,例如使用引脚控制子系统的映射表, -使得 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_INIT_* 仅有在配置为输出的时候才存在,所以有效的组合为: - - * GPIOF_IN - 配置为输入 - * GPIOF_OUT_INIT_LOW - 配置为输出,并初始化为低电平 - * GPIOF_OUT_INIT_HIGH - 配置为输出,并初始化为高电平 - -更进一步,为了更简单地声明/释放多个 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); - -它们的返回值为对应命名空间的相关编号,或是负的错误代码(如果无法映射)。 -(例如,某些 GPIO 无法做为 IRQ 使用。)以下的编号错误是未经检测的:使用一个 -未通过 gpio_direction_input()配置为输入的 GPIO 编号,或者使用一个 -并非来源于gpio_to_irq()的 IRQ 编号。 - -这两个映射函数可能会在信号编号的加减计算过程上花些时间。它们不可休眠。 - -gpio_to_irq()返回的非错误值可以传递给 request_irq()或者 free_irq()。 -它们通常通过板级特定的初始化代码存放到平台设备的 IRQ 资源中。注意:IRQ -触发选项是 IRQ 接口的一部分,如 IRQF_TRIGGER_FALLING,系统唤醒能力 -也是如此。 - - -模拟开漏信号 ------------- - -有时在只有低电平信号作为实际驱动结果(译者注:多个输出连接于一点,逻辑电平 -结果为所有输出的逻辑与)的时候,共享的信号线需要使用“开漏”信号。(该术语 -适用于 CMOS 管;而 TTL 用“集电极开路”。)一个上拉电阻使信号为高电平。这 -有时被称为“线与”。实际上,从负逻辑(低电平为真)的角度来看,这是一个“线或”。 - -一个开漏信号的常见例子是共享的低电平使能 IRQ 信号线。此外,有时双向数据总线 -信号也使用漏极开路信号。 - -某些 GPIO 控制器直接支持开漏输出,还有许多不支持。当你需要开漏信号,但 -硬件又不直接支持的时候,一个常用的方法是用任何即可作输入也可作输出的 GPIO -引脚来模拟: - - LOW: gpio_direction_output(gpio, 0) ... 这代码驱动信号并覆盖 - 上拉配置。 - - HIGH: gpio_direction_input(gpio) ... 这代码关闭输出,所以上拉电阻 - (或其他的一些器件)控制了信号。 - -如果你将信号线“驱动”为高电平,但是 gpio_get_value(gpio)报告了一个 -低电平(在适当的上升时间后),你就可以知道是其他的一些组件将共享信号线拉低了。 -这不一定是错误的。一个常见的例子就是 I2C 时钟的延长:一个需要较慢时钟的 -从设备延迟 SCK 的上升沿,而 I2C 主设备相应地调整其信号传输速率。 - -GPIO控制器和引脚控制子系统 --------------------------- - -SOC上的GPIO控制器可能与引脚控制子系统紧密结合,即引脚可以与可选的gpio功 -能一起被其他功能使用。我们已经涵盖了这样的情况,例如一个GPIO控制器需要保 -留一个引脚或通过调用以下任何一个引脚来设置其方向:: - - pinctrl_gpio_request() - pinctrl_gpio_free() - pinctrl_gpio_direction_input() - pinctrl_gpio_direction_output() - -但是,引脚控制子系统是如何将GPIO号码(这是一个全局事项)与某个引脚控制器 -上的某个引脚交叉关联的? - -这是通过注册引脚的“范围”来实现的,这基本上是交叉参考表。这些描述是在 -Documentation/driver-api/pin-control.rst - -虽然引脚分配完全由引脚控制子系统管理,但gpio(在gpiolib下)仍由gpio驱动 -维护。可能发生的情况是,SoC中的不同引脚范围由不同的gpio驱动器管理。 - -这使得在调用 "pinctrl_gpio_request" 之前,让gpio驱动向pin ctrl子系 -统宣布它们的引脚范围是合理的,以便在使用任何gpio之前要求引脚控制子系统准 -备相应的引脚。 - -为此,gpio控制器可以用引脚控制子系统注册其引脚范围。目前有两种方法:有或 -无DT。 - -关于对DT的支持,请参考 Documentation/devicetree/bindings/gpio/gpio.txt. - -对于非DT支持,用户可以用适当的参数调用gpiochip_add_pin_range(),将一 -系列的gpio引脚注册到引脚控制驱动上。为此,必须将引脚控制设备的名称字符串 -作为参数之一传给这个程序。 - - -这些公约忽略了什么? -==================== - -这些公约忽略的最大一件事就是引脚复用,因为这属于高度芯片特定的属性且 -没有可移植性。某个平台可能不需要明确的复用信息;有的对于任意给定的引脚 -可能只有两个功能选项;有的可能每个引脚有八个功能选项;有的可能可以将 -几个引脚中的任何一个作为给定的 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()。 - -它也应提供一个 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 - -这些定义可以用更理想的实现方法替代,那就是使用经过逻辑优化的内联函数来访问 -基于特定片上系统的 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 编号。 - - -API参考 -======= - -本节中列出的函数已被废弃。在新的代码中应该使用基于GPIO描述符的API。 diff --git a/Documentation/translations/zh_CN/driver-api/index.rst b/Documentation/translations/zh_CN/driver-api/index.rst index 92ff1b7fc3d3..4050a2fb51c6 100644 --- a/Documentation/translations/zh_CN/driver-api/index.rst +++ b/Documentation/translations/zh_CN/driver-api/index.rst @@ -23,6 +23,7 @@ Linux驱动实现者的API指南 gpio/index io_ordering + phy/index Todolist: @@ -103,7 +104,6 @@ Todolist: * parport-lowlevel * pps * ptp -* phy/index * pwm * pldmfw/index * rfkill diff --git a/Documentation/translations/zh_CN/driver-api/phy/index.rst b/Documentation/translations/zh_CN/driver-api/phy/index.rst new file mode 100644 index 000000000000..2cdce17b74a9 --- /dev/null +++ b/Documentation/translations/zh_CN/driver-api/phy/index.rst @@ -0,0 +1,20 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============ +PHY 通用框架 +============ + +.. toctree:: + + phy + +Todolist: + +* samsung-usb2 + +.. only:: subproject and html + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/translations/zh_CN/driver-api/phy/phy.rst b/Documentation/translations/zh_CN/driver-api/phy/phy.rst new file mode 100644 index 000000000000..0d7489081b90 --- /dev/null +++ b/Documentation/translations/zh_CN/driver-api/phy/phy.rst @@ -0,0 +1,212 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../../disclaimer-zh_CN.rst + +:Original: Documentation/driver-api/phy/phy.rst + +:翻译: + + 司延腾 Yanteng Si <siyanteng@loongson.cn> + +========= +PHY子系统 +========= + +:作者: Kishon Vijay Abraham I <kishon@ti.com> + +本文档解释了 PHY 的通用框架和提供的API,以及使用方法。 + +简介 +==== + +*PHY* 是物理层的缩写,它被用来把设备连接到一个物理媒介,例如USB控制器 +有一个提供序列化、反序列化、编码、解码和负责获取所需的数据传输速率的 PHY。 +注意,有些USB控制器内嵌了 PHY 的功能,其它的则使用了一个外置的PHY,此外 +使用 PHY 的设备还有无线网、以太网、SATA等(控制器)。 + +创建这个框架的目的是将遍布 Linux 内核的 PHY 驱动程序融入到 drivers/phy, +以增加代码的可复用性,进而提高代码的可维护性。 + +该框架仅适用于使用外部 PHY(PHY 功能未嵌入控制器内)的设备。 + +注册/注销PHY provider +===================== + +PHY provider是指实现一个或多个 PHY 实例的实体。对于 PHY provider 仅 +实现单个 PHY 实例的简单情况,框架在 of_phy_simple_xlate 中提供其自己 +的 of_xlate 实现。如果 PHY provider 实现多个实例,则应提供其自己的 +of_xlate 实现。of_xlate 仅用于 dt 启动情况。 + +:: + + #define of_phy_provider_register(dev, xlate) \ + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate)) + + #define devm_of_phy_provider_register(dev, xlate) \ + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, + (xlate)) + +of_phy_provider_register 和 devm_of_phy_provider_register 宏 +可用于注册 phy_provider,它以 device 和 of_xlate 作为参数。对于 dt +启动情况,所有 PHY provider 都应使用上述两个宏之一来注册 PHY provider。 + +与 PHY provider 关联的设备树节点通常包含一组子节点,每个子节点代表一个 +PHY。某些绑定可能会为了上下文和可扩展性将子节点嵌套在特别的层级中,在这种 +情况下,可以使用低级别的 of_phy_provider_register_full() 和 +devm_of_phy_provider_register_full() 宏来覆盖包含子节点的节点。 + +:: + + #define of_phy_provider_register_full(dev, children, xlate) \ + __of_phy_provider_register(dev, children, THIS_MODULE, xlate) + + #define devm_of_phy_provider_register_full(dev, children, xlate) \ + __devm_of_phy_provider_register_full(dev, children, + THIS_MODULE, xlate) + + void devm_of_phy_provider_unregister(struct device *dev, + struct phy_provider *phy_provider); + void of_phy_provider_unregister(struct phy_provider *phy_provider); + +devm_of_phy_provider_unregister 和 of_phy_provider_unregister +可以被用来注销PHY. + +创建PHY +======= + +PHY 驱动程序应创建 PHY,以便其他外围(芯片)控制器能够使用它。PHY 框架 +提供了 2 个 API 来创建 PHY。 + +:: + + struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops); + struct phy *devm_phy_create(struct device *dev, + struct device_node *node, + const struct phy_ops *ops); + +PHY 驱动程序可以使用上述两个 API 之一,通过传递设备指针和 phy_ops +来创建 PHY。 + +phy_ops 是一组用于执行 PHY 操作(例如 init、exit、power_on 和 +power_off)的函数指针。 + +在 phy_ops 中,PHY provider驱动程序在创建 PHY 后使用 phy_set_drvdata() +设置私有数据,使用 phy_get_drvdata() 获取私有数据。 + +获取对 PHY 的引用 +================= + +控制器必须先获取对 PHY 的引用,然后才能使用 PHY。此框架提供以下 API +来获取对 PHY 的引用。 + +:: + + struct phy *phy_get(struct device *dev, const char *string); + struct phy *devm_phy_get(struct device *dev, const char *string); + struct phy *devm_phy_optional_get(struct device *dev, + const char *string); + struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, + const char *con_id); + struct phy *devm_of_phy_optional_get(struct device *dev, + struct device_node *np, + const char *con_id); + struct phy *devm_of_phy_get_by_index(struct device *dev, + struct device_node *np, + int index); + +phy_get、devm_phy_get 和 devm_phy_optional_get 可用于在 dt +启动的情况下获取 PHY,字符串参数应包含 dt 数据中给出的 phy 名称,在 +非 dt 启动的情况下,它应包含 PHY 的标签。两个 devm_phy_get 在成功 +获取 PHY 后使用 devres 将设备与 PHY 关联。在驱动程序分离时,将在 +devres 数据上调用 release 函数并释放 devres 数据。当 phy 是可选 +的时,应使用 _optional_get 变体。这些函数永远不会返回 -ENODEV,而 +是在找不到 phy 时返回 NULL。一些通用驱动程序(例如 ehci)可能使用 +多个 phy。在这种情况下,devm_of_phy_get 或 devm_of_phy_get_by_index +用于根据名称或索引获取 phy 引用。 + +需要注意的是,NULL 是有效的 phy 引用。NULL phy 上的所有 phy 使用 +者调用都将成为 NOP。也就是说释放调用,当应用于 NULL phy 时,release +调用、phy_init()/phy_exit() 调用、phy_power_on()/phy_power_off() +调用都是 NOP。NULL phy 在处理可选的 phy 设备中很有用。 + +API的调用顺序 +============= + +通常,调用顺序应该是:: + + [devm_][of_]phy_get() + phy_init() + phy_power_on() + [phy_set_mode[_ext]()] + ... + phy_power_off() + phy_exit() + [[of_]phy_put()] + +一些PHY驱动可能没有实现 :c:func:`phy_init` 或 :c:func:`phy_power_on`, +但是控制器应该总是调用这些函数以兼容其它PHY,有些PHY可能要求 +:c:func:`phy_set_mode <phy_set_mode_ext>` 而其他 PHY 可能使用 +默认模式(通常通过设备树或其他固件配置)。出于兼容性考虑,如果您知道将 +使用哪种模式,则应始终调用此函数。通常,应在 :c:func:`phy_power_on` +之后调用此函数,尽管某些 PHY 驱动程序可能随时允许调用它。 + +释放对 PHY 的引用 +================= + +当控制器不再需要 PHY 时,它必须使用上一节中提到的 API 释放对已获得 +的 PHY 的引用。PHY 框架提供了 2 个 API 来释放对 PHY 的引用。 + +:: + + void phy_put(struct phy *phy); + void devm_phy_put(struct device *dev, struct phy *phy); + +这两个 API 都用于释放对 PHY 的引用,并且 devm_phy_put 会销毁与此 +PHY 关联的设备资源。 + +销毁 PHY +======== + +当创建 PHY 的驱动程序被卸载时,它应该使用以下 2 个 API 之一销毁其创 +建的 PHY:: + + void phy_destroy(struct phy *phy); + void devm_phy_destroy(struct device *dev, struct phy *phy); + +这两个 API 都会销毁 PHY,并且 devm_phy_destroy 会销毁与此 PHY 关 +联的 devres。 + +PM Runtime +========== + +这个子系统启用了pm runtime。 所以,在创建PHY 时,将调用此子系统创建的 +phy 设备的 pm_runtime_enable 函数,在销毁 PHY 时,将调用 +pm_runtime_disable。请注意,此子系统创建的 phy 设备将是调用 phy_create +(PHY provider 设备)的设备的子设备。 + +因此,由于父子关系,此子系统创建的 phy_device 的 pm_runtime_get_sync +调用 PHY provider 设备的 pm_runtime_get_sync。还应注意, +phy_power_on 和 phy_power_off 分别执行 phy_pm_runtime_get_sync 和 +phy_pm_runtime_put。有导出的 API,如 phy_pm_runtime_get、 +phy_pm_runtime_get_sync、phy_pm_runtime_put、phy_pm_runtime_put_sync、 +phy_pm_runtime_allow 和 phy_pm_runtime_forbid,用于执行 PM 操作。 + +PHY映射 +======= + +为了在没有 DeviceTree 帮助的情况下获取对 PHY 的引用,框架提供了可与 +clkdev 进行比较的查找,允许将 clk 结构体绑定到设备。当 struct phy 的 +句柄已存在时,可以在运行时进行查找。 + +该框架提供以下 API 用于注册和注销查找:: + + int phy_create_lookup(struct phy *phy, const char *con_id, + const char *dev_id); + void phy_remove_lookup(struct phy *phy, const char *con_id, + const char *dev_id); + +DeviceTree绑定 +============== + +PHY dt 绑定的文档可以在以下位置找到 @ +Documentation/devicetree/bindings/phy/phy-bindings.txt diff --git a/Documentation/translations/zh_CN/glossary.rst b/Documentation/translations/zh_CN/glossary.rst index 24f094df97cd..5975b0426f3d 100644 --- a/Documentation/translations/zh_CN/glossary.rst +++ b/Documentation/translations/zh_CN/glossary.rst @@ -34,3 +34,4 @@ * semaphores: 信号量。 * spinlock: 自旋锁。 * watermark: 水位,一般指页表的消耗水平。 +* PTE: 页表项。(Page Table Entry) diff --git a/Documentation/translations/zh_CN/index.rst b/Documentation/translations/zh_CN/index.rst index 6ccec9657cc6..7574e1673180 100644 --- a/Documentation/translations/zh_CN/index.rst +++ b/Documentation/translations/zh_CN/index.rst @@ -24,8 +24,8 @@ 上的linux-doc邮件列表。 顺便说下,中文文档也需要遵守内核编码风格,风格中中文和英文的主要不同就是中文 -的字符标点占用两个英文字符宽度, 所以,当英文要求不要超过每行100个字符时, -中文就不要超过50个字符。另外,也要注意'-','=' 等符号与相关标题的对齐。在将 +的字符标点占用两个英文字符宽度,所以,当英文要求不要超过每行100个字符时, +中文就不要超过50个字符。另外,也要注意'-','='等符号与相关标题的对齐。在将 补丁提交到社区之前,一定要进行必要的 ``checkpatch.pl`` 检查和编译测试。 与Linux 内核社区一起工作 @@ -89,10 +89,10 @@ TODOList: admin-guide/index admin-guide/reporting-issues.rst userspace-api/index + 内核构建系统 <kbuild/index> TODOList: -* 内核构建系统 <kbuild/index> * 用户空间工具 <tools/index> 也可参考独立于内核文档的 `Linux 手册页 <https://www.kernel.org/doc/man-pages/>`_ 。 diff --git a/Documentation/translations/zh_CN/kbuild/gcc-plugins.rst b/Documentation/translations/zh_CN/kbuild/gcc-plugins.rst new file mode 100644 index 000000000000..67a8abbf5887 --- /dev/null +++ b/Documentation/translations/zh_CN/kbuild/gcc-plugins.rst @@ -0,0 +1,126 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/kbuild/gcc-plugins.rst +:Translator: 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +================ +GCC 插件基础设施 +================ + + +介绍 +==== + +GCC 插件是为编译器提供额外功能的可加载模块 [1]_。它们对于运行时插装和静态分析非常有用。 +我们可以在编译过程中通过回调 [2]_,GIMPLE [3]_,IPA [4]_ 和 RTL Passes [5]_ +(译者注:Pass 是编译器所采用的一种结构化技术,用于完成编译对象的分析、优化或转换等功能) +来分析、修改和添加更多的代码。 + +内核的 GCC 插件基础设施支持构建树外模块、交叉编译和在单独的目录中构建。插件源文件必须由 +C++ 编译器编译。 + +目前 GCC 插件基础设施只支持一些架构。搜索 "select HAVE_GCC_PLUGINS" 来查找支持 +GCC 插件的架构。 + +这个基础设施是从 grsecurity [6]_ 和 PaX [7]_ 移植过来的。 + +-- + +.. [1] https://gcc.gnu.org/onlinedocs/gccint/Plugins.html +.. [2] https://gcc.gnu.org/onlinedocs/gccint/Plugin-API.html#Plugin-API +.. [3] https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html +.. [4] https://gcc.gnu.org/onlinedocs/gccint/IPA.html +.. [5] https://gcc.gnu.org/onlinedocs/gccint/RTL.html +.. [6] https://grsecurity.net/ +.. [7] https://pax.grsecurity.net/ + + +目的 +==== + +GCC 插件的设计目的是提供一个用于试验 GCC 或 Clang 上游没有的潜在编译器功能的场所。 +一旦它们的实用性得到验证,这些功能将被添加到 GCC(和 Clang)的上游。随后,在所有 +支持的 GCC 版本都支持这些功能后,它们会被从内核中移除。 + +具体来说,新插件应该只实现上游编译器(GCC 和 Clang)不支持的功能。 + +当 Clang 中存在 GCC 中不存在的某项功能时,应努力将该功能做到 GCC 上游(而不仅仅 +是作为内核专用的 GCC 插件),以使整个生态都能从中受益。 + +类似的,如果 GCC 插件提供的功能在 Clang 中 **不** 存在,但该功能被证明是有用的,也应 +努力将该功能上传到 GCC(和 Clang)。 + +在上游 GCC 提供了某项功能后,该插件将无法在相应的 GCC 版本(以及更高版本)下编译。 +一旦所有内核支持的 GCC 版本都提供了该功能,该插件将从内核中移除。 + + +文件 +==== + +**$(src)/scripts/gcc-plugins** + + 这是 GCC 插件的目录。 + +**$(src)/scripts/gcc-plugins/gcc-common.h** + + 这是 GCC 插件的兼容性头文件。 + 应始终包含它,而不是单独的 GCC 头文件。 + +**$(src)/scripts/gcc-plugins/gcc-generate-gimple-pass.h, +$(src)/scripts/gcc-plugins/gcc-generate-ipa-pass.h, +$(src)/scripts/gcc-plugins/gcc-generate-simple_ipa-pass.h, +$(src)/scripts/gcc-plugins/gcc-generate-rtl-pass.h** + + 这些头文件可以自动生成 GIMPLE、SIMPLE_IPA、IPA 和 RTL passes 的注册结构。 + 与手动创建结构相比,它们更受欢迎。 + + +用法 +==== + +你必须为你的 GCC 版本安装 GCC 插件头文件,以 Ubuntu 上的 gcc-10 为例:: + + apt-get install gcc-10-plugin-dev + +或者在 Fedora 上:: + + dnf install gcc-plugin-devel libmpc-devel + +或者在 Fedora 上使用包含插件的交叉编译器时:: + + dnf install libmpc-devel + +在内核配置中启用 GCC 插件基础设施与一些你想使用的插件:: + + CONFIG_GCC_PLUGINS=y + CONFIG_GCC_PLUGIN_LATENT_ENTROPY=y + ... + +运行 gcc(本地或交叉编译器),确保能够检测到插件头文件:: + + gcc -print-file-name=plugin + CROSS_COMPILE=arm-linux-gnu- ${CROSS_COMPILE}gcc -print-file-name=plugin + +"plugin" 这个词意味着它们没有被检测到:: + + plugin + +完整的路径则表示插件已经被检测到:: + + /usr/lib/gcc/x86_64-redhat-linux/12/plugin + +编译包括插件在内的最小工具集:: + + make scripts + +或者直接在内核中运行 make,使用循环复杂性 GCC 插件编译整个内核。 + + +4. 如何添加新的 GCC 插件 +======================== + +GCC 插件位于 scripts/gcc-plugins/。你需要将插件源文件放在 scripts/gcc-plugins/ 目录下。 +子目录创建并不支持,你必须添加在 scripts/gcc-plugins/Makefile、scripts/Makefile.gcc-plugins +和相关的 Kconfig 文件中。 diff --git a/Documentation/translations/zh_CN/kbuild/headers_install.rst b/Documentation/translations/zh_CN/kbuild/headers_install.rst new file mode 100644 index 000000000000..02cb8896e555 --- /dev/null +++ b/Documentation/translations/zh_CN/kbuild/headers_install.rst @@ -0,0 +1,39 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/kbuild/headers_install.rst +:Translator: 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +============================ +导出内核头文件供用户空间使用 +============================ + +"make headers_install" 命令以适合于用户空间程序的形式导出内核头文件。 + +Linux 内核导出的头文件描述了用户空间程序尝试使用内核服务的 API。这些内核 +头文件被系统的 C 库(例如 glibc 和 uClibc)用于定义可用的系统调用,以及 +与这些系统调用一起使用的常量和结构。C 库的头文件包括来自 linux 子目录的 +内核头文件。系统的 libc 头文件通常被安装在默认位置 /usr/include,而内核 +头文件在该位置的子目录中(主要是 /usr/include/linux 和 /usr/include/asm)。 + +内核头文件向后兼容,但不向前兼容。这意味着使用旧内核头文件的 C 库构建的程序 +可以在新内核上运行(尽管它可能无法访问新特性),但使用新内核头文件构建的程序 +可能无法在旧内核上运行。 + +"make headers_install" 命令可以在内核源代码的顶层目录中运行(或使用标准 +的树外构建)。它接受两个可选参数:: + + make headers_install ARCH=i386 INSTALL_HDR_PATH=/usr + +ARCH 表明为其生成头文件的架构,默认为当前架构。导出内核头文件的 linux/asm +目录是基于特定平台的,要查看支持架构的完整列表,使用以下命令:: + + ls -d include/asm-* | sed 's/.*-//' + +INSTALL_HDR_PATH 表明头文件的安装位置,默认为 "./usr"。 + +该命令会在 INSTALL_HDR_PATH 中自动创建创建一个 'include' 目录,而头文件 +会被安装在 INSTALL_HDR_PATH/include 中。 + +内核头文件导出的基础设施由 David Woodhouse <dwmw2@infradead.org> 维护。 diff --git a/Documentation/translations/zh_CN/kbuild/index.rst b/Documentation/translations/zh_CN/kbuild/index.rst new file mode 100644 index 000000000000..3f9ab52fa5bb --- /dev/null +++ b/Documentation/translations/zh_CN/kbuild/index.rst @@ -0,0 +1,36 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/kbuild/index.rst +:Translator: 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +============ +内核编译系统 +============ + +.. toctree:: + :maxdepth: 1 + + kconfig + headers_install + gcc-plugins + kbuild + reproducible-builds + llvm + +TODO: + +- kconfig-language +- kconfig-macro-language +- makefiles +- modules +- issues + + +.. only:: subproject and html + + 目录 + ===== + + * :ref:`genindex` diff --git a/Documentation/translations/zh_CN/kbuild/kbuild.rst b/Documentation/translations/zh_CN/kbuild/kbuild.rst new file mode 100644 index 000000000000..e5e2aebe1ebc --- /dev/null +++ b/Documentation/translations/zh_CN/kbuild/kbuild.rst @@ -0,0 +1,304 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/kbuild/kbuild.rst +:Translator: 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +====== +Kbuild +====== + + +输出文件 +======== + +modules.order +------------- +该文件记录模块在 Makefile 中出现的顺序。modprobe 使用该文件来确定性 +解析匹配多个模块的别名。 + +modules.builtin +--------------- +该文件列出了所有内置到内核中的模块。modprobe 使用该文件来避免尝试加载 +内置模块时出错。 + +modules.builtin.modinfo +----------------------- +该文件包含所有内置模块的 modinfo。与单独模块的 modinfo 不同,所有字段 +都带有模块名称前缀。 + +modules.builtin.ranges +---------------------- +该文件包含所有内核内置模块的地址偏移范围(每个 ELF 节)。结合 System.map +文件,它可以用来将模块名称与符号关联起来。 + +环境变量 +======== + +KCPPFLAGS +--------- +在预处理时传递的额外选项。kbuild 进行所有预处理(包括构建 C 文件和汇编文件) +时,都会使用这些预处理选项。 + +KAFLAGS +------- +传递给汇编器的额外选项(适用于内置模块和外部模块)。 + +AFLAGS_MODULE +------------- +外部模块的额外汇编选项。 + +AFLAGS_KERNEL +------------- +内置模块的额外汇编选项。 + +KCFLAGS +------- +传递给 C 编译器的额外选项(适用于内置模块和外部模块)。 + +KRUSTFLAGS +---------- +传递给 Rust 编译器的额外选项(适用于内置模块和外部模块)。 + +CFLAGS_KERNEL +------------- +在编译内置代码时,传递给 $(CC) 的额外选项。 + +CFLAGS_MODULE +------------- +编译外部模块时,传递给 $(CC) 的额外模块特定选项。 + +RUSTFLAGS_KERNEL +---------------- +在编译内置代码时,传递给 $(RUSTC) 的额外选项。 + +RUSTFLAGS_MODULE +---------------- +用于 $(RUSTC) 的额外模块特定选项。 + +LDFLAGS_MODULE +-------------- +用于 $(LD) 链接模块时的额外选项。 + +HOSTCFLAGS +---------- +在构建主机程序时传递给 $(HOSTCC) 的额外标志。 + +HOSTCXXFLAGS +------------ +在构建主机程序时传递给 $(HOSTCXX) 的额外标志。 + +HOSTRUSTFLAGS +------------- +在构建主机程序时传递给 $(HOSTRUSTC) 的额外标志。 + +HOSTLDFLAGS +----------- +链接主机程序时传递的额外选项。 + +HOSTLDLIBS +---------- +在构建主机程序时链接的额外库。 + +.. _zh_cn_userkbuildflags: + +USERCFLAGS +---------- +用于 $(CC) 编译用户程序(userprogs)时的额外选项。 + +USERLDFLAGS +----------- +用于 $(LD) 链接用户程序时的额外选项。用户程序(userprogs)是使用 CC 链接的, +因此 $(USERLDFLAGS) 应该根据需要包含 "-Wl," 前缀。 + +KBUILD_KCONFIG +-------------- +将顶级 Kconfig 文件设置为此环境变量的值。默认名称为 "Kconfig"。 + +KBUILD_VERBOSE +-------------- +设置 kbuild 的详细程度。可以分配与 "V=..." 相同的值。 + +有关完整列表,请参见 `make help`。 + +设置 "V=..." 优先于 KBUILD_VERBOSE。 + +KBUILD_EXTMOD +------------- +在构建外部模块时设置内核源代码的搜索目录。 + +设置 "M=..." 优先于 KBUILD_EXTMOD。 + +KBUILD_OUTPUT +------------- +指定内核构建的输出目录。 + +在单独的构建目录中为预构建内核构建外部模块时,这个变量也可以指向内核输出目录。请注意, +这并不指定外部模块本身的输出目录。 + +输出目录也可以使用 "O=..." 指定。 + +设置 "O=..." 优先于 KBUILD_OUTPUT。 + +KBUILD_EXTRA_WARN +----------------- +指定额外的构建检查。也可以通过在命令行传递 "W=..." 来设置相同的值。 + +请参阅 `make help` 了解支持的值列表。 + +设置 "W=..." 优先于 KBUILD_EXTRA_WARN。 + +KBUILD_DEBARCH +-------------- +对于 deb-pkg 目标,允许覆盖 deb-pkg 部署的正常启发式方法。通常 deb-pkg 尝试根据 +UTS_MACHINE 变量(在某些架构中还包括内核配置)来猜测正确的架构。KBUILD_DEBARCH +的值假定(不检查)为有效的 Debian 架构。 + +KDOCFLAGS +--------- +指定在构建过程中用于 kernel-doc 检查的额外(警告/错误)标志,查看 +scripts/kernel-doc 了解支持的标志。请注意,这目前不适用于文档构建。 + +ARCH +---- +设置 ARCH 为要构建的架构。 + +在大多数情况下,架构的名称与 arch/ 目录中的子目录名称相同。 + +但某些架构(如 x86 和 sparc)有别名。 + +- x86: i386 表示 32 位,x86_64 表示 64 位 +- parisc: parisc64 表示 64 位 +- sparc: sparc32 表示 32 位,sparc64 表示 64 位 + +CROSS_COMPILE +------------- +指定 binutils 文件名的可选固定部分。CROSS_COMPILE 可以是文件名的一部分或完整路径。 + +在某些设置中,CROSS_COMPILE 也用于 ccache。 + +CF +-- +用于 sparse 的额外选项。 + +CF 通常在命令行中如下所示使用:: + + make CF=-Wbitwise C=2 + +INSTALL_PATH +------------ +INSTALL_PATH 指定放置更新后的内核和系统映像的路径。默认值是 /boot,但你可以设置 +为其他值。 + +INSTALLKERNEL +------------- +使用 "make install" 时调用的安装脚本。 +默认名称是 "installkernel"。 + +该脚本将会以以下参数调用: + + - $1 - 内核版本 + - $2 - 内核映像文件 + - $3 - 内核映射文件 + - $4 - 默认安装路径(如果为空,则使用根目录) + +"make install" 的实现是架构特定的,可能与上述有所不同。 + +提供 INSTALLKERNEL 以便在交叉编译内核时可以指定自定义安装程序。 + +MODLIB +------ +指定模块的安装位置。 +默认值为:: + + $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) + +该值可以被覆盖,在这种情况下将忽略默认值。 + +INSTALL_MOD_PATH +---------------- +INSTALL_MOD_PATH 指定了模块目录重定位时 MODLIB 的前缀,通常由构建根 +(build roots)所需。它没有在 makefile 中定义,但如果需要,可以作为 +参数传递给 make。 + +INSTALL_MOD_STRIP +----------------- +如果 INSTALL_MOD_STRIP 被定义,内核模块在安装后会被剥离。如果 +INSTALL_MOD_STRIP 的值为 '1',则会使用默认选项 --strip-debug。否则, +INSTALL_MOD_STRIP 的值将作为 strip 命令的选项。 + +INSTALL_HDR_PATH +---------------- +INSTALL_HDR_PATH 指定了执行 "make headers_*" 时,用户空间头文件的安装位置。 + +默认值为:: + + $(objtree)/usr + +$(objtree) 是保存输出文件的目录。 +输出目录通常使用命令行中的 "O=..." 进行设置。 + +该值可以被覆盖,在这种情况下将忽略默认值。 + +INSTALL_DTBS_PATH +----------------- +INSTALL_DTBS_PATH 指定了设备树二进制文件的安装位置,通常由构建根(build roots)所需。 +它没有在 makefile 中定义,但如果需要,可以作为参数传递给 make。 + +KBUILD_ABS_SRCTREE +-------------------------------------------------- +Kbuild 在可能的情况下使用相对路径指向源代码树。例如,在源代码树中构建时,源代码树路径是 +'.'。 + +设置该标志请求 Kbuild 使用源代码树的绝对路径。 +在某些情况下这是有用的,例如在生成带有绝对路径条目的标签文件时等。 + +KBUILD_SIGN_PIN +--------------- +当签署内核模块时,如果私钥需要密码或 PIN,此变量允许将密码或 PIN 传递给 sign-file 工具。 + +KBUILD_MODPOST_WARN +------------------- +KBUILD_MODPOST_WARN 可以设置为在最终模块链接阶段出现未定义符号时避免错误。它将这些错误 +转为警告。 + +KBUILD_MODPOST_NOFINAL +---------------------- +KBUILD_MODPOST_NOFINAL 可以设置为跳过模块的最终链接。这仅在加速编译测试时有用。 + +KBUILD_EXTRA_SYMBOLS +-------------------- +用于依赖其他模块符号的模块。详见 modules.rst。 + +ALLSOURCE_ARCHS +--------------- +对于 tags/TAGS/cscope 目标,可以指定包含在数据库中的多个架构,用空格分隔。例如:: + + $ make ALLSOURCE_ARCHS="x86 mips arm" tags + +要获取所有可用架构,也可以指定 all。例如:: + + $ make ALLSOURCE_ARCHS=all tags + +IGNORE_DIRS +----------- +对于 tags/TAGS/cscope 目标,可以选择不包含在数据库中的目录,用空格分隔。例如:: + + $ make IGNORE_DIRS="drivers/gpu/drm/radeon tools" cscope + +KBUILD_BUILD_TIMESTAMP +---------------------- +将该环境变量设置为日期字符串,可以覆盖在 UTS_VERSION 定义中使用的时间戳 +(运行内核时的 uname -v)。该值必须是一个可以传递给 date -d 的字符串。默认值是 +内核构建某个时刻的 date 命令输出。 + +KBUILD_BUILD_USER, KBUILD_BUILD_HOST +------------------------------------ +这两个变量允许覆盖启动时显示的 user@host 字符串以及 /proc/version 中的信息。 +默认值分别是 whoami 和 host 命令的输出。 + +LLVM +---- +如果该变量设置为 1,Kbuild 将使用 Clang 和 LLVM 工具,而不是 GCC 和 GNU +binutils 来构建内核。 diff --git a/Documentation/translations/zh_CN/kbuild/kconfig.rst b/Documentation/translations/zh_CN/kbuild/kconfig.rst new file mode 100644 index 000000000000..3b06d8913dbf --- /dev/null +++ b/Documentation/translations/zh_CN/kbuild/kconfig.rst @@ -0,0 +1,259 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/kbuild/kconfig.rst +:Translator: 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +================ +配置目标和编辑器 +================ + +本文件包含使用 ``make *config`` 的一些帮助。 + +使用 ``make help`` 列出所有可能的配置目标。 + +xconfig('qconf')、menuconfig('mconf')和 nconfig('nconf')程序也包含 +内嵌的帮助文本。请务必查看这些帮助文本以获取导航、搜索和其他帮助信息。 + +gconfig('gconf')程序的帮助文本较少。 + + +通用信息 +======== + +新的内核版本通常会引入新的配置符号。更重要的是,新的内核版本可能会重命名配置符号。 +当这种情况发生时,使用之前正常工作的 .config 文件并运行 "make oldconfig" +不一定会生成一个可正常工作的新内核,因此,你可能需要查看哪些新的内核符号被引入。 + +要查看新配置符号的列表,请使用:: + + cp user/some/old.config .config + make listnewconfig + +配置程序将列出所有新配置符号,每行一个。 + +或者,你可以使用暴力破解方法:: + + make oldconfig + scripts/diffconfig .config.old .config | less + + +环境变量 +======== + +``*config`` 的环境变量: + +``KCONFIG_CONFIG`` + 该环境变量可用于指定一个默认的内核配置文件名,以覆盖默认的 ".config"。 + +``KCONFIG_DEFCONFIG_LIST`` + 该环境变量指定了一个配置文件列表,当 .config 不存在时,这些文件可用作基础配置。 + 列表中的条目以空格分隔,只有第一个存在的文件会被使用。 + +``KCONFIG_OVERWRITECONFIG`` + 如果该环境变量被设置,当 .config 是指向其他位置的符号链接时,Kconfig 不会 + 破坏符号链接。 + +``KCONFIG_WARN_UNKNOWN_SYMBOLS`` + 该环境变量使 Kconfig 对配置输入中所有无法识别的符号发出警告。 + +``KCONFIG_WERROR`` + 如果该环境变量被设置,Kconfig 将所有警告视为错误。 + +``CONFIG_`` + 如果该环境变量被设置,Kconfig 将在保存配置时,为所有符号添加其值作为前缀, + 而不是使用默认值。 + +``{allyes/allmod/allno/rand}config`` 的环境变量: + +``KCONFIG_ALLCONFIG`` + allyesconfig/allmodconfig/allnoconfig/randconfig 这些变体也可以使用环境 + 变量 KCONFIG_ALLCONFIG 作为标志或包含用户要求设置为特定值的配置符号的文件名。 + 如果 KCONFIG_ALLCONFIG 未指定文件名,即 KCONFIG_ALLCONFIG == "" 或 + KCONFIG_ALLCONFIG == "1",则 ``make *config`` 将查找名为 + "all{yes/mod/no/def/random}.config" 的文件(对应于所使用的 ``*config`` + 命令)以强制符号值。如果找不到此文件,它会查找名为 "all.config" 的文件以包含 + 强制值。 + + 这可以创建“微型”配置(miniconfig)或自定义配置文件,其中仅包含感兴趣的配置符号。 + 然后,内核配置系统将生成完整的 .config 文件,包括 miniconfig 文件中的符号。 + + ``KCONFIG_ALLCONFIG`` 文件包含许多预设配置符号(通常是所有符号的子集)。 + 这些变量设置仍需遵守正常的依赖性检查。 + + 示例:: + + KCONFIG_ALLCONFIG=custom-notebook.config make allnoconfig + + 或:: + + KCONFIG_ALLCONFIG=mini.config make allnoconfig + + 或:: + + make KCONFIG_ALLCONFIG=mini.config allnoconfig + + 这些示例将禁用大多数配置选项(allnoconfig),但启用或禁用 miniconfig 文件 + 中显式列出的选项。 + +``randconfig`` 的环境变量: + +``KCONFIG_SEED`` + 如果你想调试 kconfig 解析器/前端的行为,你可以将此变量设置整数值,用于初始化 + 随机数生成器。如果未设置,将使用当前时间。 + +``KCONFIG_PROBABILITY`` + 该变量可用于倾斜概率分布。此变量可不设置或设置为空,或设置为以下三种不同格式: + + ======================= ================== ===================== + KCONFIG_PROBABILITY y:n 分配 y:m:n 分配 + ======================= ================== ===================== + 未设置或设置为空 50 : 50 33 : 33 : 34 + N N : 100-N N/2 : N/2 : 100-N + [1] N:M N+M : 100-(N+M) N : M : 100-(N+M) + [2] N:M:L N : 100-N M : L : 100-(M+L) + ======================= ================== ===================== + +其中 N、M 和 L 是范围在 [0,100] 内的整数(以十进制表示),并且需满足: + + [1] N+M 的范围在 [0,100] 之间 + + [2] M+L 的范围在 [0,100] 之间 + +示例:: + + KCONFIG_PROBABILITY=10 + 10% 的布尔值将设置为 'y',90% 设置为 'n' + 5% 的三态值将设置为 'y',5% 设置为 'm',90% 设置为 'n' + KCONFIG_PROBABILITY=15:25 + 40% 的布尔值将设置为 'y',60% 设置为 'n' + 15% 的三态值将设置为 'y',25% 设置为 'm',60% 设置为 'n' + KCONFIG_PROBABILITY=10:15:15 + 10% 的布尔值将设置为 'y',90% 设置为 'n' + 15% 的三态值将设置为 'y',15% 设置为 'm',70% 设置为 'n' + +``syncconfig`` 的环境变量: + +``KCONFIG_NOSILENTUPDATE`` + 如果该变量非空,它将阻止静默的内核配置更新(需要明确更新)。 + +``KCONFIG_AUTOCONFIG`` + 该环境变量可以设置为 "auto.conf" 文件的路径和名称。默认值为 + "include/config/auto.conf"。 + +``KCONFIG_AUTOHEADER`` + 该环境变量可以设置为 "autoconf.h" 头文件的路径和名称。默认值为 + "include/generated/autoconf.h"。 + +menuconfig +========== + +在 menuconfig 中搜索: + + 搜索功能会搜索内核配置符号名称,因此你必须知道欲搜索内容的大致名称。 + + 示例:: + + /hotplug + 这会列出所有包含 "hotplug" 的配置符号,例如,HOTPLUG_CPU, + MEMORY_HOTPLUG。 + + 若需要搜索帮助,输入 / 后跟 TAB-TAB(高亮显示 <Help>)并按回车键。 + 这说明你还可以在搜索字符串中使用正则表达式(regex),所以如果你对 + MEMORY_HOTPLUG 不感兴趣,你可以尝试:: + + /^hotplug + + 在搜索时,符号将按以下顺序排序: + + - 首先,完全匹配的符号,按字母顺序排列(完全匹配是指搜索与符号名称完全匹配); + - 然后是其他匹配项,按字母顺序排列。 + + 例如,^ATH.K 匹配:: + + ATH5K ATH9K ATH5K_AHB ATH5K_DEBUG [...] ATH6KL ATH6KL_DEBUG + [...] ATH9K_AHB ATH9K_BTCOEX_SUPPORT ATH9K_COMMON [...] + + 其中只有 ATH5K 和 ATH9K 完全匹配,因此它们排在前面(按字母顺序), + 接下来是其他符号,同样按字母顺序排列。 + + 在此菜单中,按下以 (#) 为前缀的键将直接跳转到该位置。退出此新菜单后, + 你将返回当前的搜索结果。 + +'menuconfig' 的用户界面选项: + +``MENUCONFIG_COLOR`` + 可以使用变量 MENUCONFIG_COLOR 选择不同的配色主题。使用以下命令选择主题:: + + make MENUCONFIG_COLOR=<theme> menuconfig + + 可用的主题有:: + + - mono => 选择适合单色显示器的颜色 + - blackbg => 选择具有黑色背景的配色方案 + - classic => 经典外观,蓝色背景 + - bluetitle => 经典外观的 LCD 友好版本(默认) + +``MENUCONFIG_MODE`` + 此模式会将所有子菜单显示为一个大树状结构。 + + 示例:: + + make MENUCONFIG_MODE=single_menu menuconfig + +nconfig +======= + +nconfig 是一个替代的基于文本的配置工具。它在终端(窗口)底部列出功能键,用于执行 +命令。除非你在数据输入窗口中,否则你也可以直接使用相应的数字键来执行命令。例如,你 +可以直接按 6,而非 F6 进行保存。 + +使用 F1 获取全局帮助或 F3 打开简短帮助菜单。 + +在 nconfig 中搜索: + + 你可以在菜单项“提示”字符串中或配置符号中进行搜索。 + + 使用 / 开始在菜单项中搜索。这不支持正则表达式。使用 <Down> 或 <Up> + 分别为下一个命中项和上一个命中项。使用 <Esc> 退出搜索模式。 + + F8(SymSearch)在配置符号中搜索给定的字符串或正则表达式(regex)。 + + 在 SymSearch 中,按下 (#) 前缀的键会直接跳转到该位置。退出该新菜单后, + 你将返回到当前的搜索结果。 + +环境变量: + +``NCONFIG_MODE`` + 此模式会将所有子菜单显示为一个大型树结构。 + + 示例:: + + make NCONFIG_MODE=single_menu nconfig + +xconfig +======= + +在 xconfig 中搜索: + + 搜索功能会搜索内核配置符号名称,因此你必须知道欲搜索内容的大致名称。 + + 示例:: + + Ctrl-F hotplug + + 或:: + + 菜单:File, Search, hotplug + + 列出所有符号名称中包含 "hotplug" 的配置符号项。在此搜索对话框中, + 你可以更改任何未灰显条目的配置设置。你还可以输入不同的搜索字符串, + 而无需返回主菜单。 + +gconfig +======= + +在 gconfig 中搜索: + + gconfig 中没有搜索命令。然而,gconfig 具有几种不同的查看选择、模式和选项。 diff --git a/Documentation/translations/zh_CN/kbuild/llvm.rst b/Documentation/translations/zh_CN/kbuild/llvm.rst new file mode 100644 index 000000000000..f87e0181d8e7 --- /dev/null +++ b/Documentation/translations/zh_CN/kbuild/llvm.rst @@ -0,0 +1,203 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/kbuild/llvm.rst +:Translator: 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +========================== +使用 Clang/LLVM 构建 Linux +========================== + +本文档介绍如何使用 Clang 和 LLVM 工具构建 Linux 内核。 + +关于 +---- + +Linux 内核传统上一直使用 GNU 工具链(如 GCC 和 binutils)进行编译。持续的工作使得 +`Clang <https://clang.llvm.org/>`_ 和 `LLVM <https://llvm.org/>`_ 工具可 +作为可行的替代品。一些发行版,如 `Android <https://www.android.com/>`_、 +`ChromeOS <https://www.chromium.org/chromium-os>`_、`OpenMandriva +<https://www.openmandriva.org/>`_ 和 `Chimera Linux +<https://chimera-linux.org/>`_ 使用 Clang 编译的内核。谷歌和 Meta 的数据中心 +集群也运行由 Clang 编译的内核。 + +`LLVM 是由 C++ 对象实现的工具链组件集合 <https://www.aosabook.org/en/llvm.html>`_。 +Clang 是 LLVM 的前端,支持 C 语言和内核所需的 GNU C 扩展,其发音为 "klang",而非 +"see-lang"。 + +使用 LLVM 构建 +-------------- + +通过以下命令调用 ``make``:: + + make LLVM=1 + +为主机目标进行编译。对于交叉编译:: + + make LLVM=1 ARCH=arm64 + +LLVM= 参数 +---------- + +LLVM 有 GNU binutils 工具的替代品。这些工具可以单独启用。以下是支持的 make 变量 +完整列表:: + + make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \ + OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump READELF=llvm-readelf \ + HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld + +``LLVM=1`` 扩展为上述命令。 + +如果你的 LLVM 工具不在 PATH 中,你可以使用以斜杠结尾的 LLVM 变量提供它们的位置:: + + make LLVM=/path/to/llvm/ + +这将使用 ``/path/to/llvm/clang``、``/path/to/llvm/ld.lld`` 等工具。也可以 +使用以下命令:: + + PATH=/path/to/llvm:$PATH make LLVM=1 + +如果你的 LLVM 工具带有版本后缀,并且你希望测试该特定版本而非无后缀的可执行文件, +类似于 ``LLVM=1``,你可以使用 ``LLVM`` 变量传递该后缀:: + + make LLVM=-14 + +这将使用 ``clang-14``、``ld.lld-14`` 等工具。为了支持带有版本后缀的树外路径组合, +我们建议:: + + PATH=/path/to/llvm/:$PATH make LLVM=-14 + +``LLVM=0`` 与省略 ``LLVM`` 完全不同,它将表现得像 ``LLVM=1``。如果你只希望使用 +某些 LLVM 工具,请使用它们各自的 make 变量。 + +在通过不同命令配置和构建时,应为每次调用 ``make`` 设置相同的 ``LLVM=`` 值。如果 +运行的脚本最终会调用 ``make``,则还应将 ``LLVM=`` 设置为环境变量。 + +交叉编译 +-------- + +单个 Clang 编译器二进制文件(及其对应的 LLVM 工具)通常会包含所有支持的后端,这可以 +简化交叉编译,尤其是使用 ``LLVM=1`` 时。如果仅使用 LLVM 工具,``CROSS_COMPILE`` +或目标三元组前缀就变得不必要。示例:: + + make LLVM=1 ARCH=arm64 + +作为混合 LLVM 和 GNU 工具的示例,对于像 ``ARCH=s390`` 这样目前尚不支持 +``ld.lld`` 或 ``llvm-objcopy`` 的目标,你可以通过以下方式调用 ``make``:: + + make LLVM=1 ARCH=s390 LD=s390x-linux-gnu-ld.bfd \ + OBJCOPY=s390x-linux-gnu-objcopy + +此示例将调用 ``s390x-linux-gnu-ld.bfd`` 作为链接器和 +``s390x-linux-gnu-objcopy``,因此请确保它们在你的 ``$PATH`` 中。 + +当 ``LLVM=1`` 未设置时,``CROSS_COMPILE`` 不会用于给 Clang 编译器二进制文件 +(或相应的 LLVM 工具)添加前缀,而 GNU 工具则需要这样做。 + +LLVM_IAS= 参数 +-------------- + +Clang 可以编译汇编代码。你可以传递 ``LLVM_IAS=0`` 禁用此行为,使 Clang 调用 +相应的非集成汇编器。示例:: + + make LLVM=1 LLVM_IAS=0 + +在交叉编译时,你需要使用 ``CROSS_COMPILE`` 与 ``LLVM_IAS=0``,从而设置 +``--prefix=`` 使得编译器可以对应的非集成汇编器(通常,在面向另一种架构时, +你不想使用系统汇编器)。例如:: + + make LLVM=1 ARCH=arm LLVM_IAS=0 CROSS_COMPILE=arm-linux-gnueabi- + +Ccache +------ + +``ccache`` 可以与 ``clang`` 一起使用,以改善后续构建(尽管在不同构建之间 +KBUILD_BUILD_TIMESTAMP_ 应设置为同一确定值,以避免 100% 的缓存未命中, +详见 Reproducible_builds_ 获取更多信息):: + + KBUILD_BUILD_TIMESTAMP='' make LLVM=1 CC="ccache clang" + +.. _KBUILD_BUILD_TIMESTAMP: kbuild.html#kbuild-build-timestamp +.. _Reproducible_builds: reproducible-builds.html#timestamps + +支持的架构 +---------- + +LLVM 并不支持 Linux 内核所有可支持的架构,同样,即使 LLVM 支持某一架构,也并不意味着在 +该架构下内核可以正常构建或工作。以下是当前 ``CC=clang`` 或 ``LLVM=1`` 支持的架构总结。 +支持级别对应于 MAINTAINERS 文件中的 "S" 值。如果某个架构未列出,则表示 LLVM 不支持它 +或存在已知问题。使用最新的稳定版 LLVM 或甚至开发版本通常会得到最佳结果。一个架构的 +``defconfig`` 通常预期能够良好工作,但某些配置可能存在尚未发现的问题。欢迎在以下 +问题跟踪器中提交错误报告! + +.. list-table:: + :widths: 10 10 10 + :header-rows: 1 + + * - 架构 + - 支持级别 + - ``make`` 命令 + * - arm + - 支持 + - ``LLVM=1`` + * - arm64 + - 支持 + - ``LLVM=1`` + * - hexagon + - 维护 + - ``LLVM=1`` + * - loongarch + - 维护 + - ``LLVM=1`` + * - mips + - 维护 + - ``LLVM=1`` + * - powerpc + - 维护 + - ``LLVM=1`` + * - riscv + - 支持 + - ``LLVM=1`` + * - s390 + - 维护 + - ``LLVM=1`` (LLVM >= 18.1.0),``CC=clang`` (LLVM < 18.1.0) + * - um (用户模式) + - 维护 + - ``LLVM=1`` + * - x86 + - 支持 + - ``LLVM=1`` + +获取帮助 +-------- + +- `网站 <https://clangbuiltlinux.github.io/>`_ +- `邮件列表 <https://lore.kernel.org/llvm/>`_: <llvm@lists.linux.dev> +- `旧邮件列表档案 <https://groups.google.com/g/clang-built-linux>`_ +- `问题跟踪器 <https://github.com/ClangBuiltLinux/linux/issues>`_ +- IRC: #clangbuiltlinux 在 irc.libera.chat +- `Telegram <https://t.me/ClangBuiltLinux>`_: @ClangBuiltLinux +- `维基 <https://github.com/ClangBuiltLinux/linux/wiki>`_ +- `初学者问题 <https://github.com/ClangBuiltLinux/linux/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22>`_ + +.. _zh_cn_getting_llvm: + +获取 LLVM +--------- + +我们在 `kernel.org <https://kernel.org/pub/tools/llvm/>`_ 提供预编译的稳定版 LLVM。 +这些版本已经针对 Linux 内核构建,使用配置文件数据进行优化。相较于其他发行版中的 LLVM,它们应该 +能提高内核构建效率。 + +以下是一些有助于从源代码构建 LLVM 或通过发行版的包管理器获取 LLVM 的链接。 + +- https://releases.llvm.org/download.html +- https://github.com/llvm/llvm-project +- https://llvm.org/docs/GettingStarted.html +- https://llvm.org/docs/CMake.html +- https://apt.llvm.org/ +- https://www.archlinux.org/packages/extra/x86_64/llvm/ +- https://github.com/ClangBuiltLinux/tc-build +- https://github.com/ClangBuiltLinux/linux/wiki/Building-Clang-from-source +- https://android.googlesource.com/platform/prebuilts/clang/host/linux-x86/ diff --git a/Documentation/translations/zh_CN/kbuild/reproducible-builds.rst b/Documentation/translations/zh_CN/kbuild/reproducible-builds.rst new file mode 100644 index 000000000000..5f27ebf2fbfc --- /dev/null +++ b/Documentation/translations/zh_CN/kbuild/reproducible-builds.rst @@ -0,0 +1,114 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/kbuild/reproducible-builds.rst + +:Translator: 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +============ +可重现的构建 +============ + +通常希望使用相同工具集构建相同源代码是可重现的,即,输出始终完全相同。这使得能够验证 +二进制分发或嵌入式系统的构建基础设施未被篡改。这样也更容易验证源代码或工具的更改不会 +影响最终生成的二进制文件。 + +`可重现构建项目`_ 提供了有关该主题的更多信息。本文档涵盖了构建内核可能不可重现的 +各种原因,以及如何避免这些问题。 + +时间戳 +------ + +内核在三个地方嵌入时间戳: + +* 通过 ``uname()`` 显示与包含在 ``/proc/version`` 中的版本字符串 + +* initramfs 中的文件时间戳 + +* 如果启动 ``CONFIG_IKHEADERS``,内核或相应模块中嵌入的内核头文件的时间戳, + 通过 ``/sys/kernel/kheaders.tar.xz`` 显示 + +默认情况下,时间戳为当前时间或内核头文件的修改时间。这个内容必须使用 +`KBUILD_BUILD_TIMESTAMP`_ 变量进行覆盖。如果你从某个 git 提交进行构建, +可以使用其提交日期。 + +内核 *不* 使用 ``__DATE__`` 和 ``__TIME__`` 宏,并在使用这些宏时启用警告。 +如果你合并的外部代码使用这些宏,则必须通过设置 `SOURCE_DATE_EPOCH`_ 环境 +变量来覆盖它们对应的时间戳。 + +用户,主机 +---------- + +内核在 ``/proc/version`` 中嵌入构建用户和主机名。必须使用 +`KBUILD_BUILD_USER 和 KBUILD_BUILD_HOST`_ 变量来覆盖这些设置。如果 +您从某个 git 提交进行构建,可以使用其提交者地址。 + +绝对文件名 +---------- + +当内核在树外构建时,调试信息可能包括源文件的绝对文件名。这些信息必须通过在 +`KCFLAGS`_ 变量中包含 ``-fdebug-prefix-map`` 选项来覆盖。 + +根据使用的编译器,``__FILE__`` 宏在树外构建中也可能扩展为绝对文件名。Kbuild +自动使用 ``-fmacro-prefix-map`` 选项来防止这种情况,前提是它被支持。 + +可重现构建网站提供了有关这些 `prefix-map 选项`_ 的更多信息。 + +在源包中的生成文件 +------------------ + +在 ``tools/`` 子目录下,一些程序的构建过程并不完全支持树外构建。这可能导致后续 +使用如 ``make rpm-pkg`` 构建的源码包包含生成的文件。在构建源码包之前,您应该通过 +运行 ``make mrproper`` 或 ``git clean -d -f -x`` 来确保源码树是干净的。 + +模块签名 +-------- + +如果你启用 ``CONFIG_MODULE_SIG_ALL``,默认行为是为每次构建生成不同的临时密钥, +从而导致模块不可重现。然而,将签名密钥包含在源代码中显然会违背签名模块的目的。 + +一种方法是将构建过程分为几个部分,以便不可重现的部分可以作为源处理: + +1. 生成一个持久的签名密钥。将该密钥的证书添加到内核源代码中。 + +2. 将 ``CONFIG_SYSTEM_TRUSTED_KEYS`` 符号设置为包括签名密钥的证书,将 +``CONFIG_MODULE_SIG_KEY`` 设置为空字符串,并禁用 ``CONFIG_MODULE_SIG_ALL``。 +最后,构建内核和模块。 + +3. 为模块创建分离的签名,并将它们作为源发布。 + +4. 附加模块签名并进行第二次构建。这可以重建模块,或使用步骤 2 的输出。 + +结构随机化 +---------- + +如果你启用 ``CONFIG_RANDSTRUCT``,则需要在 ``scripts/basic/randstruct.seed`` +中预生成随机种子,以便每次构建都使用相同的值。有关详细信息,请参见 +``scripts/gen-randstruct-seed.sh``。 + +调试信息冲突 +------------ + +这并非是个不可重现性的问题,而是生成的文件 *过于* 可重现的问题。 + +一旦你设置了所有必要的变量来开展可重现构建,vDSO 的调试信息可能即使对于不同的内核版 +本也是相同的。这会导致不同内核版本的调试信息软件包之间发生文件冲突。 + +为了避免这种情况,你可以通过在 vDSO 中包含一个任意的 salt 字符串,使其对于不同的 +内核版本是不同的。这种机制由 Kconfig 符号 ``CONFIG_BUILD_SALT`` 指定。 + +Git +--- + +未提交的更改或 Git 中的不同提交 ID 也可能导致不同的编译结果。例如,在执行 +``git reset HEAD^`` 后,即使代码相同,编译期间生成的 +``include/config/kernel.release`` 也会不同,导致最终生成的二进制文件也不尽相同。 +有关详细信息,请参见 ``scripts/setlocalversion``。 + +.. _KBUILD_BUILD_TIMESTAMP: kbuild.html#kbuild-build-timestamp +.. _KBUILD_BUILD_USER 和 KBUILD_BUILD_HOST: kbuild.html#kbuild-build-user-kbuild-build-host +.. _KCFLAGS: kbuild.html#kcflags +.. _prefix-map 选项: https://reproducible-builds.org/docs/build-path/ +.. _可重现构建项目: https://reproducible-builds.org/ +.. _SOURCE_DATE_EPOCH: https://reproducible-builds.org/docs/source-date-epoch/ diff --git a/Documentation/translations/zh_CN/mm/active_mm.rst b/Documentation/translations/zh_CN/mm/active_mm.rst index c2816f523bd7..b3352668c4c8 100644 --- a/Documentation/translations/zh_CN/mm/active_mm.rst +++ b/Documentation/translations/zh_CN/mm/active_mm.rst @@ -13,6 +13,11 @@ Active MM ========= +注意,在配置了 CONFIG_MMU_LAZY_TLB_REFCOUNT=n 的内核中,mm_count 引用计数 +可能不再包括“懒惰”用户(运行任务中 ->active_mm == mm && ->mm == NULL)。 +获取和释放这些懒惰引用必须使用 mmgrab_lazy_tlb() 和 mmdrop_lazy_tlb() 这 +两个辅助函数,它们抽象了这个配置选项。 + 这是一封linux之父回复开发者的一封邮件,所以翻译时我尽量保持邮件格式的完整。 :: diff --git a/Documentation/translations/zh_CN/mm/damon/faq.rst b/Documentation/translations/zh_CN/mm/damon/faq.rst index de4be417494a..234d63f4f072 100644 --- a/Documentation/translations/zh_CN/mm/damon/faq.rst +++ b/Documentation/translations/zh_CN/mm/damon/faq.rst @@ -13,23 +13,6 @@ 常见问题 ======== -为什么是一个新的子系统,而不是扩展perf或其他用户空间工具? -========================================================== - -首先,因为它需要尽可能的轻量级,以便可以在线使用,所以应该避免任何不必要的开销,如内核-用户 -空间的上下文切换成本。第二,DAMON的目标是被包括内核在内的其他程序所使用。因此,对特定工具 -(如perf)的依赖性是不可取的。这就是DAMON在内核空间实现的两个最大的原因。 - - -“闲置页面跟踪” 或 “perf mem” 可以替代DAMON吗? -============================================== - -闲置页跟踪是物理地址空间访问检查的一个低层次的原始方法。“perf mem”也是类似的,尽管它可以 -使用采样来减少开销。另一方面,DAMON是一个更高层次的框架,用于监控各种地址空间。它专注于内 -存管理优化,并提供复杂的精度/开销处理机制。因此,“空闲页面跟踪” 和 “perf mem” 可以提供 -DAMON输出的一个子集,但不能替代DAMON。 - - DAMON是否只支持虚拟内存? ========================= diff --git a/Documentation/translations/zh_CN/mm/hmm.rst b/Documentation/translations/zh_CN/mm/hmm.rst index babbbe756c0f..0669f947d0bc 100644 --- a/Documentation/translations/zh_CN/mm/hmm.rst +++ b/Documentation/translations/zh_CN/mm/hmm.rst @@ -129,13 +129,7 @@ struct page可以与现有的 mm 机制进行最简单、最干净的集成。 int hmm_range_fault(struct hmm_range *range); 如果请求写访问,它将在丢失或只读条目上触发缺页异常(见下文)。缺页异常使用通用的 mm 缺 -页异常代码路径,就像 CPU 缺页异常一样。 - -这两个函数都将 CPU 页表条目复制到它们的 pfns 数组参数中。该数组中的每个条目对应于虚拟 -范围中的一个地址。HMM 提供了一组标志来帮助驱动程序识别特殊的 CPU 页表项。 - -在 sync_cpu_device_pagetables() 回调中锁定是驱动程序必须尊重的最重要的方面,以保 -持事物正确同步。使用模式是:: +页异常代码路径,就像 CPU 缺页异常一样。使用模式是:: int driver_populate_range(...) { diff --git a/Documentation/translations/zh_CN/mm/index.rst b/Documentation/translations/zh_CN/mm/index.rst index b950dd118be7..c8726bce8f74 100644 --- a/Documentation/translations/zh_CN/mm/index.rst +++ b/Documentation/translations/zh_CN/mm/index.rst @@ -53,6 +53,8 @@ Linux内存管理文档 page_migration page_owner page_table_check + page_tables + physical_memory remap_file_pages split_page_table_lock vmalloced-kernel-stacks diff --git a/Documentation/translations/zh_CN/mm/overcommit-accounting.rst b/Documentation/translations/zh_CN/mm/overcommit-accounting.rst index d8452d8b7fbb..f136a8b81859 100644 --- a/Documentation/translations/zh_CN/mm/overcommit-accounting.rst +++ b/Documentation/translations/zh_CN/mm/overcommit-accounting.rst @@ -16,8 +16,7 @@ Linux内核支持下列超量使用处理模式 0 启发式超量使用处理。拒绝明显的地址空间超量使用。用于一个典型的系统。 - 它确保严重的疯狂分配失败,同时允许超量使用以减少swap的使用。在这种模式下, - 允许root分配稍多的内存。这是默认的。 + 它确保严重的疯狂分配失败,同时允许超量使用以减少swap的使用。这是默认的。 1 总是超量使用。适用于一些科学应用。经典的例子是使用稀疏数组的代码,只是依赖 几乎完全由零页组成的虚拟内存 diff --git a/Documentation/translations/zh_CN/mm/page_frags.rst b/Documentation/translations/zh_CN/mm/page_frags.rst index 20bd3fafdc8c..a5b22486a913 100644 --- a/Documentation/translations/zh_CN/mm/page_frags.rst +++ b/Documentation/translations/zh_CN/mm/page_frags.rst @@ -25,7 +25,7 @@ sk_buff->head使用,或者用于skb_shared_info的 “frags” 部分。 网络堆栈在每个CPU使用两个独立的缓存来处理碎片分配。netdev_alloc_cache被使用 netdev_alloc_frag和__netdev_alloc_skb调用的调用者使用。napi_alloc_cache -被调用__napi_alloc_frag和__napi_alloc_skb的调用者使用。这两个调用的主要区别是 +被调用__napi_alloc_frag和napi_alloc_skb的调用者使用。这两个调用的主要区别是 它们可能被调用的环境。“netdev” 前缀的函数可以在任何上下文中使用,因为这些函数 将禁用中断,而 ”napi“ 前缀的函数只可以在softirq上下文中使用。 diff --git a/Documentation/translations/zh_CN/mm/page_migration.rst b/Documentation/translations/zh_CN/mm/page_migration.rst index f95063826a15..8c8461c6cb9f 100644 --- a/Documentation/translations/zh_CN/mm/page_migration.rst +++ b/Documentation/translations/zh_CN/mm/page_migration.rst @@ -50,8 +50,8 @@ mbind()设置一个新的内存策略。一个进程的页面也可以通过sys_ 1. 从LRU中移除页面。 - 要迁移的页面列表是通过扫描页面并把它们移到列表中来生成的。这是通过调用 isolate_lru_page() - 来完成的。调用isolate_lru_page()增加了对该页的引用,这样在页面迁移发生时它就不会 + 要迁移的页面列表是通过扫描页面并把它们移到列表中来生成的。这是通过调用 folio_isolate_lru() + 来完成的。调用folio_isolate_lru()增加了对该页的引用,这样在页面迁移发生时它就不会 消失。它还可以防止交换器或其他扫描器遇到该页。 @@ -65,7 +65,7 @@ migrate_pages()如何工作 ======================= migrate_pages()对它的页面列表进行了多次处理。如果当时对一个页面的所有引用都可以被移除, -那么这个页面就会被移动。该页已经通过isolate_lru_page()从LRU中移除,并且refcount被 +那么这个页面就会被移动。该页已经通过folio_isolate_lru()从LRU中移除,并且refcount被 增加,以便在页面迁移发生时不释放该页。 步骤: diff --git a/Documentation/translations/zh_CN/mm/page_owner.rst b/Documentation/translations/zh_CN/mm/page_owner.rst index b72a972271d9..c0d1ca4b9695 100644 --- a/Documentation/translations/zh_CN/mm/page_owner.rst +++ b/Documentation/translations/zh_CN/mm/page_owner.rst @@ -26,6 +26,9 @@ page owner是用来追踪谁分配的每一个页面。它可以用来调试内 页面所有者也可以用于各种目的。例如,可以通过每个页面的gfp标志信息获得精确的碎片 统计。如果启用了page owner,它就已经实现并激活了。我们非常欢迎其他用途。 +它也可以用来显示所有的栈以及它们当前分配的基础页面数,这让我们能够快速了解内存的 +使用情况,而无需浏览所有页面并匹配分配和释放操作。 + page owner在默认情况下是禁用的。所以,如果你想使用它,你需要在你的启动cmdline 中加入"page_owner=on"。如果内核是用page owner构建的,并且由于没有启用启动 选项而在运行时禁用page owner,那么运行时的开销是很小的。如果在运行时禁用,它不 @@ -60,6 +63,49 @@ page owner在默认情况下是禁用的。所以,如果你想使用它,你 4) 分析来自页面所有者的信息:: + cat /sys/kernel/debug/page_owner_stacks/show_stacks > stacks.txt + cat stacks.txt + post_alloc_hook+0x177/0x1a0 + get_page_from_freelist+0xd01/0xd80 + __alloc_pages+0x39e/0x7e0 + allocate_slab+0xbc/0x3f0 + ___slab_alloc+0x528/0x8a0 + kmem_cache_alloc+0x224/0x3b0 + sk_prot_alloc+0x58/0x1a0 + sk_alloc+0x32/0x4f0 + inet_create+0x427/0xb50 + __sock_create+0x2e4/0x650 + inet_ctl_sock_create+0x30/0x180 + igmp_net_init+0xc1/0x130 + ops_init+0x167/0x410 + setup_net+0x304/0xa60 + copy_net_ns+0x29b/0x4a0 + create_new_namespaces+0x4a1/0x820 + nr_base_pages: 16 + ... + ... + echo 7000 > /sys/kernel/debug/page_owner_stacks/count_threshold + cat /sys/kernel/debug/page_owner_stacks/show_stacks> stacks_7000.txt + cat stacks_7000.txt + post_alloc_hook+0x177/0x1a0 + get_page_from_freelist+0xd01/0xd80 + __alloc_pages+0x39e/0x7e0 + alloc_pages_mpol+0x22e/0x490 + folio_alloc+0xd5/0x110 + filemap_alloc_folio+0x78/0x230 + page_cache_ra_order+0x287/0x6f0 + filemap_get_pages+0x517/0x1160 + filemap_read+0x304/0x9f0 + xfs_file_buffered_read+0xe6/0x1d0 [xfs] + xfs_file_read_iter+0x1f0/0x380 [xfs] + __kernel_read+0x3b9/0x730 + kernel_read_file+0x309/0x4d0 + __do_sys_finit_module+0x381/0x730 + do_syscall_64+0x8d/0x150 + entry_SYSCALL_64_after_hwframe+0x62/0x6a + nr_base_pages: 20824 + ... + cat /sys/kernel/debug/page_owner > page_owner_full.txt ./page_owner_sort page_owner_full.txt sorted_page_owner.txt diff --git a/Documentation/translations/zh_CN/mm/page_table_check.rst b/Documentation/translations/zh_CN/mm/page_table_check.rst index e8077310a76c..dc34570dceff 100644 --- a/Documentation/translations/zh_CN/mm/page_table_check.rst +++ b/Documentation/translations/zh_CN/mm/page_table_check.rst @@ -54,3 +54,16 @@ 可以选择用PAGE_TABLE_CHECK_ENFORCED来构建内核,以便在没有额外的内核参数的情况下获得页表 支持。 + +实现注意事项 +============ + +我们特意决定不使用 VMA 信息,以避免依赖于 MM 状态(除了有限的 “struct page” 信息)。页表检查 +独立于 Linux-MM 状态机,它验证用户可访问的页面不会被错误地共享。 + +PAGE_TABLE_CHECK 依赖于 EXCLUSIVE_SYSTEM_RAM。原因在于,若没有 EXCLUSIVE_SYSTEM_RAM, +用户被允许通过 /dev/mem 将任意物理内存区域映射到用户空间。同时,页面可能在映射到用户空间期间 +改变自己的属性(例如,从匿名页面变为命名页面),导致页表检查检测到“损坏”。 + +即使有 EXCLUSIVE_SYSTEM_RAM,I/O 页面可能仍然被允许通过 /dev/mem 映射。然而,这些页面始终 +被视为命名页面,所以它们不会破坏页表检查中使用的逻辑。 diff --git a/Documentation/translations/zh_CN/mm/page_tables.rst b/Documentation/translations/zh_CN/mm/page_tables.rst new file mode 100644 index 000000000000..c9f750fc5298 --- /dev/null +++ b/Documentation/translations/zh_CN/mm/page_tables.rst @@ -0,0 +1,221 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/mm/page_tables.rst + +:翻译: + + 张鹏宇 Pengyu Zhang <zpenya1314@gmail.com> + +:校译: + +==== +页表 +==== + +分页虚拟内存是随虚拟内存的概念一起于 1962 年在 Ferranti Atlas 计算机上被提出的, +这是第一台有分页虚拟内存的计算机。随着时间推移,这个特性被迁移到更新的计算机上, +并且成为所有类 Unix 系统实际的特性。在 1985 年,这个特性被包含在了英特尔 80386 +中,也就是运行 Linux 1.0 的CPU。 + +页表将 CPU 看到的虚拟地址映射到外部内存总线上看到的物理地址。 + +Linux 将页表定义为一个分级结构,目前有五级。对于支持的每种架构,其代码会根据硬件 +限制对这个层级结构进行映射。 + +虚拟地址对应的物理地址通常由底层物理页帧引用。 **页帧号(page frame number,pfn)** +是页的物理地址(在外部内存总线看到的地址)除以 `PAGE_SIZE` 得到的值。 + +物理内存地址 0 对应 *pfn 0*,而最大的 pfn 对应处理器外部地址总线所能寻址物理地址 +的最后一页。 + +在页粒度为 4KB 且地址范围为32位的情况下,pfn 0 对应地址0x00000000,pfn 1 对应 +地址0x00001000,pfn 2 对应地址 0x00002000,以此类推,直到 pfn 0xfffff 对应 +0xfffff000。如果页粒度为 16KB,则 pfn 分别对应地址 0x00004000、0x00008000 +... 0xffffc000,pfn 的范围从 0 到 0x3ffff。 + +如你所见,对于 4KB 页面粒度,页基址使用地址的 12-31 位,这就是为什么在这种情况下 +`PAGE_SHIFT` 被定义为 12,并且 `PAGE_SIZE` 通常由页偏移定义,为 `(1 << PAGE_SHIFT)`。 + +随着内存容量的增加,久而久之层级结构逐渐加深。Linux 最初使用 4KB 页面和一个名为 +`swapper_pg_dir` 的页表,该页表拥有 1024 个表项(entries),覆盖 4MB 的内存, +事实上Torvald 的第一台计算机正好就有 4MB 物理内存。表项在这张表中被称为 *PTE*:s +- 页表项(page table entries)。 + +软件页表层级结构反映了页表硬件已经变得分层化的事实,而这种分层化的目的是为了节省 +页表内存并加快地址映射速度。 + +当然,人们可以想象一张拥有大量表项的单一线性的页表将整个内存分为一个个页。而且, +这样的页表会非常稀疏,因为虚拟内存中大部分位置通常是未使用的。通过页表分层,虚拟 +内存中的大量空洞不会浪费宝贵的页表内存,因为只需要在上层页表中将大块的区域标记为 +未映射即可。 + +另外,在现代处理器中,上层页表项可以直接指向一个物理地址范围,这使得单个上层 +页表项可以连续映射几兆字节甚至几千兆字节的内存范围,从而快捷地实现虚拟地址到 +物理地址的映射:当你找到一个像这样的大型映射范围时,无需在层级结构中进一步遍历。 + +页表的层级结构目前发展为如下所示:: + + +-----+ + | PGD | + +-----+ + | + | +-----+ + +-->| P4D | + +-----+ + | + | +-----+ + +-->| PUD | + +-----+ + | + | +-----+ + +-->| PMD | + +-----+ + | + | +-----+ + +-->| PTE | + +-----+ + + +不同页表层级的符号含义从最底层开始如下: + +- **pte**, `pte_t`, `pteval_t` = **页表项** - 前面提到过。*pte* 是一个由 + `PTRS_PER_PTE` 个 `pteval_t` 类型元素组成的数组,每个元素将一个虚拟内存页 + 映射到一个物理内存页。体系结构定义了 `pteval_t` 的大小和内容。 + + 一个典型的例子是 `pteval_t` 是一个 32 或者 64 位的值,其中高位是 **pfn**, + 而低位则一些特定体系架构相关的位,如内存保护。 + + 这个 **表项(entry)** 有点令人困惑,因为在 Linux 1.0 中它确实指的是单层顶级 + 页表中的单个页表项,但在首次引入二级页表时,它被重新定义为映射元素的数组。 + 因此,*pte* 现在指的是最底层的页 *表*,而不是一个页表 *项*。 + +- **pmd**, `pmd_t`, `pmdval_t` = **页中间目录(Page Middle Directory)**, + 位于 *pte* 之上的层级结构,包含 `PTRS_PER_PMD` 个指向 *pte* 的引用。 + +- **pud**, `pud_t`, `pudval_t` = **页上级目录(Page Upper Directory)** + 是在其他层级之后引入的,用于处理四级页表。它可能未被使用,或者像我们稍后 + 讨论的那样被“折叠”。 + +- **p4d**, `p4d_t`, `p4dval_t` = **页四级目录(Page Level 4 Directory)** + 是在 *pud* 之后用于处理五级页表引入的。至此,显然需要用数字来替代 *pgd*、 + *pmd*、*pud* 等目录层级的名称,不能再继续使用临时的命名方式。这个目录层级 + 只在实际拥有五级页表的系统上使用,否则它会被折叠。 + +- **pgd**, `pgd_t`, `pgdval_t` = **页全局目录(Page Global Directory)** - + Linux 内核用于处理内核内存的 *PGD* 主页表仍然位于 `swapper_pg_dir`。 + 但系统中的每个用户空间进程也有自己的内存上下文,因此也有自己的 *pgd*, + 它位于 `struct mm_struct` 中,而 `struct mm_struct` 又在每个 `struct task_struct` + 中有引用。所以,任务(进程)存在一个形式为 `struct mm_struct` 的内存上下文, + 而这个结构体中有一个指向指向相应的页全局目录 `struct pgt_t *pgd` 指针。 + +重申一下:页表层级结构中的每一层都是一个 *指针数组*,所以 *pgd* 包含 `PTRS_PER_PGD` +个指向下一层的指针,*p4d* 包含 `PTRS_PER_P4D` 个指向 *pud* 项的指针,依此类推。 +每一层的指针数量由体系结构定义。:: + + PMD + --> +-----+ PTE + | ptr |-------> +-----+ + | ptr |- | ptr |-------> PAGE + | ptr | \ | ptr | + | ptr | \ ... + | ... | \ + | ptr | \ PTE + +-----+ +----> +-----+ + | ptr |-------> PAGE + | ptr | + ... + +页表折叠 +======== + +如果架构不使用所有的页表层级,那么这些层级可以被 *折叠*,也就是说被跳过。在 +访问下一层时,所有在页表上执行的操作都会在编译时增强,以跳过这一层。 + +与架构无关的页表处理代码(例如虚拟内存管理器)需要编写得能够遍历当前的所有五个 +层级。对于特定架构的代码,也应优先采用这种风格,以便对未来的变化具有更好的适应性。 + +MMU,TLB 和缺页异常 +=================== + +`内存管理单元(MMU)` 是处理虚拟地址到物理地址转换的硬件组件。它可能会使用相对较小 +的硬件缓存,如 `转换后备缓冲区(TLB)` 和 `页遍历缓存`,以加快这些地址翻译过程。 + +当 CPU 访存时,它会向 MMU 提供一个虚拟地址。MMU 会首先检查 TLB 或者页遍历缓存 +(在支持的架构上)是否存在对应的转换结果。如果没有,MMU 会通过遍历来确定物理地址 +并且建立映射。 + +当页面被写入时,该页的脏位会被设置(即打开)。每个内存页面都有相关的权限位和脏位。 +后者表明这个页自从被加载到内存以来是否被修改。 + +如果没有任何阻碍,物理内存到头来可以被任意访问并且对物理帧进行请求的操作。 + +MMU 无法找到某些转换有多种原因。有可能是 CPU 试图去访问当前进程没有权限访问的 +内存,或者因为访问的数据还不在物理内存中。 + +当这些情况发生时,MMU 会触发缺页异常,这是一种异常类型,用于通知 CPU 暂停当前 +执行并运行一个特殊的函数去处理这些异常。 + +缺页异常有一些常见且预期的原因。这些因素是由称为“懒加载”和“写时复制”的进程管理 +优化技术来触发的。缺页异常也可能发生在当页帧被交换到持久存储(交换分区或者文件) +并从其物理地址移出时。 + +这些技术提高了内存效率,减少了延迟,并且最小化了空间占用。本文档不会深入讨论 +“懒加载”和“写时复制”的细节,因为这些的主题属于进程地址管理范畴,超出了本文范围。 + +交换技术和前面提到的其他技术不同,因为它是在压力过大下情况下减少内存消耗的一种 +迫不得已的手段,因此是不受欢迎的。 + +交换不适用于由内核逻辑地址映射的内存。这些地址是内核虚拟地址空间的子集,直接映射 +一段连续的物理内存。对于提供的任意逻辑地址,它的物理地址可以通过对偏移量进行简单 +的算数运算来确定。对逻辑地址的访问很快,因为这避免了复杂的页表查找,但代价是这些 +内存不能被驱逐或置换。 + +如果内核无法为必须存在于物理帧中的数据腾出空间,那么它会调用内存不足(out-of-memory, +OOM)杀手,通过杀掉低优先级的进程来腾出空间,直到内存压力下降到安全阈值之下。 + +另外,代码漏洞或指示 CPU 访问的精心制作的恶意地址也可能导致缺页异常。一个进程的 +线程可以利用指令来访问不属于其地址空间的(非共享)内存,或者试图执行写入只读位置 +的指令。 + +如果上述情况发生在用户态,内核会向当前线程发送 `段错误` (SIGSEGV)信号。该信号 +通常导致线程及其所属的进程终止。 + +本文将简化并概述 Linux 内核如何处理这些缺页中断、创建表和表项、检查内存是否存在, +以及当内存不存在时,如何请求从持久存储或其他设备加载数据,并更新 MMU 及其缓存。 + +最初的步骤依赖于架构。大多是架构跳转到 `do_page_fault()`,而 x86 中断处理程序是由 +`DEFINE_IDTENTRY_RAW_ERRORCODE()` 宏定义的,该宏调用 `handle_page_fault()`。 + +无论调用路径如何,所有架构最终都会调用 `handle_mm_fault()`,该函数通常会调用 +`__handle_mm_fault()` 来执行实际分配页表的任务。 + +如果不幸无法调用 `__handle_mm_fault()` 则意味着虚拟地址指向了无权访问的物理 +内存区域(至少对于当前上下文如此)。这种情况会导致内核向该进程发送上述的 SIGSEGV +信号,并引发前面提到的后果。 + +这些用于查找偏移量的函数名称通常以 `*_offset()` 结尾,其中“\*”可以是 pgd,p4d, +pud,pmd 或者 pte;而分配相应层级页表的函数名称是 `*_alloc`,它们按照上述命名 +约定以对应页表层级的类型命名。 + +页表遍历可能在中间或者上层结束(PMD,PUD)。 + +Linux 支持比通常 4KB 更大的页面(即所谓的 `巨页`)。当使用这种较大的页面时,没有 +必要使用更低层的页表项(PTE)。巨页通常包含 2MB 到 1GB 的大块连续物理区域,分别由 +PMD 和 PUD 页表项映射。 + +巨页带来许多好处,如减少 TLB 压力,减少页表开销,提高内存分配效率,以及改善 +特定工作负载的性能。然而,这些好处也伴随着权衡,如内存浪费和分配难度增加。 + +在遍历和分配的最后,如果没有返回错误,`__handle_mm_fault()` 最终调用 `handle_pte_fault()` +通过 `do_fault()` 执行 `do_read_fault()`、 `do_cow_fault()` 和 `do_shared_fault()`。 +“read”,“cow”和“shared”分别暗示了它处理错误的类型和原因。 + +实际的工作流程实现是非常复杂的。其设计允许 Linux 根据每种架构的特定特性处理缺页 +异常,同时仍然共享一个通用的整体结构。 + +为了总结 Linux 如何处理缺页中断的概述,需要补充的是,缺页异常处理程序可以通过 +`pagefault_disable()` 和 `pagefault_enable()` 分别禁用和启用。 + +许多代码路径使用了这两个函数,因为它们需要禁止陷入缺页异常处理程序,主要是为了 +防止死锁。 diff --git a/Documentation/translations/zh_CN/mm/physical_memory.rst b/Documentation/translations/zh_CN/mm/physical_memory.rst new file mode 100644 index 000000000000..4594d15cefec --- /dev/null +++ b/Documentation/translations/zh_CN/mm/physical_memory.rst @@ -0,0 +1,356 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/mm/physical_memory.rst + +:翻译: + + 王亚鑫 Yaxin Wang <wang.yaxin@zte.com.cn> + +======== +物理内存 +======== + +Linux可用于多种架构,因此需要一个与架构无关的抽象来表示物理内存。本章描述 +了管理运行系统中物理内存的结构。 + +第一个与内存管理相关的主要概念是 `非一致性内存访问(NUMA) +<https://en.wikipedia.org/wiki/Non-uniform_memory_access>` + +在多核和多插槽机器中,内存可能被组织成不同的存储区,这些存储区根据与处理器 +的距离“不同”而有不同的访问开销。例如,可能为每个CPU分配内存存储区,或者为 +外围设备在附近分配一个非常适合DMA的内存存储区。 + +每个存储区被称为一个节点,节点在Linux中表示为 ``struct pglist_data``, +即使是在UMA架构中也是这样表示。该结构总是通过 ``pg_data_t`` 来引用。特 +定节点的 ``pg_data_t`` 结构体可以通过NODE_DATA(nid)引用,其中nid被称 +为该节点的ID。 + +对于非一致性内存访问(NUMA)架构,节点数据结构在引导时由特定于架构的代码早 +期分配。通常,这些结构在其所在的内存区上本地分配。对于一致性内存访问(UMA) +架构,只使用一个静态的 ``pg_data_t`` 结构体,称为 ``contig_page_data``。 +节点将会在 :ref:`节点 <nodes>` 章节中进一步讨论。 + +整个物理内存被划分为一个或多个被称为区域的块,这些区域表示内存的范围。这 +些范围通常由访问内存的架构限制来决定。在节点内,与特定区域对应的内存范围 +由 ``struct zone`` 结构体描述,该结构被定义为 ``zone_t``,每种区域都 +属于以下描述类型的一种。 + +* ``ZONE_DMA`` 和 ``ZONE_DMA32`` 在历史上代表适用于DMA的内存,这些 + 内存由那些不能访问所有可寻址内存的外设访问。多年来,已经有了更好、更稳 + 固的接口来获取满足特定DMA需求的内存(这些接口由 + Documentation/core-api/dma-api.rst 文档描述),但是 ``ZONE_DMA`` + 和 ``ZONE_DMA32`` 仍然表示访问受限的内存范围。 + +取决于架构的不同,这两种区域可以在构建时通过关闭 ``CONFIG_ZONE_DMA`` 和 +``CONFIG_ZONE_DMA32`` 配置选项来禁用。一些64位的平台可能需要这两种区域, +因为他们支持具有不同DMA寻址限制的外设。 + +* ``ZONE_NORMAL`` 是普通内存的区域,这种内存可以被内核随时访问。如果DMA + 设备支持将数据传输到所有可寻址的内存区域,那么可在该区域的页面上执行DMA + 操作。``ZONE_NORMAL`` 总是开启的。 + +* ``ZONE_HIGHMEM`` 是指那些没有在内核页表中永久映射的物理内存部分。该区 + 域的内存只能通过临时映射被内核访问。该区域只在某些32位架构上可用,并且是 + 通过 ``CONFIG_HIGHMEM`` 选项开启。 + +* ``ZONE_MOVABLE`` 是指可访问的普通内存区域,就像 ``ZONE_NORMAL`` + 一样。不同之处在于 ``ZONE_MOVABLE`` 中的大多数页面内容是可移动的。 + 这意味着这些页面的虚拟地址不会改变,但它们的内容可能会在不同的物理页面 + 之间移动。通常,在内存热插拔期间填充 ``ZONE_MOVABLE``,在启动时也可 + 以使用 ``kernelcore``、``movablecore`` 和 ``movable_node`` + 这些内核命令行参数来填充。更多详细信息,请参阅内核文档 + Documentation/mm/page_migration.rst 和 + Documentation/admin-guide/mm/memory-hotplug.rst。 + +* ``ZONE_DEVICE`` 表示位于持久性内存(PMEM)和图形处理单元(GPU) + 等设备上的内存。它与RAM区域类型有不同的特性,并且它的存在是为了提供 + :ref:`struct page<Pages>` 结构和内存映射服务,以便设备驱动程序能 + 识别物理地址范围。``ZONE_DEVICE`` 通过 ``CONFIG_ZONE_DEVICE`` + 选项开启。 + +需要注意的是,许多内核操作只能使用 ``ZONE_NORMAL`` 来执行,因此它是 +性能最关键区域。区域在 :ref:`区域 <zones>` 章节中有更详细的讨论。 + +节点和区域范围之间的关系由固件报告的物理内存映射决定,另外也由内存寻址 +的架构约束以及内核命令行中的某些参数决定。 + +例如,在具有2GB RAM的x86统一内存架构(UMA)机器上运行32位内核时,整 +个内存将位于节点0,并且将有三个区域: ``ZONE_DMA``、 ``ZONE_NORMAL`` +和 ``ZONE_HIGHMEM``:: + + 0 2G + +-------------------------------------------------------------+ + | node 0 | + +-------------------------------------------------------------+ + + 0 16M 896M 2G + +----------+-----------------------+--------------------------+ + | ZONE_DMA | ZONE_NORMAL | ZONE_HIGHMEM | + +----------+-----------------------+--------------------------+ + + +在内核构建时关闭 ``ZONE_DMA`` 开启 ``ZONE_DMA32``,并且具有16GB +RAM平均分配在两个节点上的arm64机器上,使用 ``movablecore=80%`` 参数 +启动时,``ZONE_DMA32``、``ZONE_NORMAL`` 和 ``ZONE_MOVABLE`` +位于节点0,而 ``ZONE_NORMAL`` 和 ``ZONE_MOVABLE`` 位于节点1:: + + + 1G 9G 17G + +--------------------------------+ +--------------------------+ + | node 0 | | node 1 | + +--------------------------------+ +--------------------------+ + + 1G 4G 4200M 9G 9320M 17G + +---------+----------+-----------+ +------------+-------------+ + | DMA32 | NORMAL | MOVABLE | | NORMAL | MOVABLE | + +---------+----------+-----------+ +------------+-------------+ + + +内存存储区可能位于交错的节点。在下面的例子中,一台x86机器有16GB的RAM分 +布在4个内存存储区上,偶数编号的内存存储区属于节点0,奇数编号的内存条属于 +节点1:: + + 0 4G 8G 12G 16G + +-------------+ +-------------+ +-------------+ +-------------+ + | node 0 | | node 1 | | node 0 | | node 1 | + +-------------+ +-------------+ +-------------+ +-------------+ + + 0 16M 4G + +-----+-------+ +-------------+ +-------------+ +-------------+ + | DMA | DMA32 | | NORMAL | | NORMAL | | NORMAL | + +-----+-------+ +-------------+ +-------------+ +-------------+ + +在这种情况下,节点0将覆盖从0到12GB的内存范围,而节点1将覆盖从4GB到16GB +的内存范围。 + +.. _nodes_zh_CN: + +节点 +==== + +正如我们所提到的,内存中的每个节点由 ``pg_data_t`` 描述,通过 +``struct pglist_data`` 结构体的类型定义。在分配页面时,默认情况下,Linux +使用节点本地分配策略,从离当前运行CPU的最近节点分配内存。由于进程倾向于在同 +一个CPU上运行,很可能会使用当前节点的内存。分配策略可以由用户控制,如内核文 +档 Documentation/admin-guide/mm/numa_memory_policy.rst 中所述。 + +大多数NUMA(非统一内存访问)架构维护了一个指向节点结构的指针数组。这些实际 +的结构在启动过程中的早期被分配,这时特定于架构的代码解析了固件报告的物理内 +存映射。节点初始化的大部分工作是在由free_area_init()实现的启动过程之后 +完成,该函数在后面的小节 :ref:`初始化 <initialization>` 中有详细描述。 + +除了节点结构,内核还维护了一个名为 ``node_states`` 的 ``nodemask_t`` +位掩码数组。这个数组中的每个位掩码代表一组特定属性的节点,这些属性由 +``enum node_states`` 定义,定义如下: + +``N_POSSIBLE`` +节点可能在某个时刻上线。 + +``N_ONLINE`` +节点已经上线。 + +``N_NORMAL_MEMORY`` +节点拥有普通内存。 + +``N_HIGH_MEMORY`` +节点拥有普通或高端内存。当关闭 ``CONFIG_HIGHMEM`` 配置时, +也可以称为 ``N_NORMAL_MEMORY``。 + +``N_MEMORY`` +节点拥有(普通、高端、可移动)内存。 + +``N_CPU`` +节点拥有一个或多个CPU。 + +对于具有上述属性的每个节点,``node_states[<property>]`` +掩码中对应于节点ID的位会被置位。 + +例如,对于具有常规内存和CPU的节点2,第二个bit将被设置:: + + node_states[N_POSSIBLE] + node_states[N_ONLINE] + node_states[N_NORMAL_MEMORY] + node_states[N_HIGH_MEMORY] + node_states[N_MEMORY] + node_states[N_CPU] + +有关使用节点掩码(nodemasks)可能进行的各种操作,请参考 +``include/linux/nodemask.h``。 + +除此之外,节点掩码(nodemasks)提供用于遍历节点的宏,即 +``for_each_node()`` 和 ``for_each_online_node()``。 + +例如,要为每个在线节点调用函数 foo(),可以这样操作:: + + for_each_online_node(nid) { + pg_data_t *pgdat = NODE_DATA(nid); + + foo(pgdat); + } + +节点数据结构 +------------ + +节点结构 ``struct pglist_data`` 在 ``include/linux/mmzone.h`` +中声明。这里我们将简要描述这个结构体的字段: + +通用字段 +~~~~~~~~ + +``node_zones`` +表示该节点的区域列表。并非所有区域都可能被填充,但这是 +完整的列表。它被该节点的node_zonelists以及其它节点的 +node_zonelists引用。 + +``node_zonelists`` +表示所有节点中所有区域的列表。此列表定义了分配内存时首选的区域 +顺序。``node_zonelists`` 在核心内存管理结构初始化期间, +由 ``mm/page_alloc.c`` 中的 ``build_zonelists()`` +函数设置。 + +``nr_zones`` +表示此节点中已填充区域的数量。 + +``node_mem_map`` +对于使用FLATMEM内存模型的UMA系统,0号节点的 ``node_mem_map`` +表示每个物理帧的struct pages数组。 + +``node_page_ext`` +对于使用FLATMEM内存模型的UMA系统,0号节点的 ``node_page_ext`` +是struct pages的扩展数组。只有在构建时开启了 ``CONFIG_PAGE_EXTENSION`` +选项的内核中才可用。 + +``node_start_pfn`` +表示此节点中起始页面帧的页面帧号。 + +``node_present_pages`` +表示此节点中存在的物理页面的总数。 + +``node_spanned_pages`` +表示包括空洞在内的物理页面范围的总大小。 + +``node_size_lock`` +一个保护定义节点范围字段的锁。仅在开启了 ``CONFIG_MEMORY_HOTPLUG`` 或 +``CONFIG_DEFERRED_STRUCT_PAGE_INIT`` 配置选项中的某一个时才定义。提 +供了 ``pgdat_resize_lock()`` 和 ``pgdat_resize_unlock()`` 用来操作 +``node_size_lock``,而无需检查 ``CONFIG_MEMORY_HOTPLUG`` 或 +``CONFIG_DEFERRED_STRUCT_PAGE_INIT`` 选项。 + +``node_id`` +节点的节点ID(NID),从0开始。 + +``totalreserve_pages`` +这是每个节点保留的页面,这些页面不可用于用户空间分配。 + +``first_deferred_pfn`` +如果大型机器上的内存初始化被推迟,那么第一个PFN(页帧号)是需要初始化的。 +在开启了 ``CONFIG_DEFERRED_STRUCT_PAGE_INIT`` 选项时定义。 + +``deferred_split_queue`` +每个节点的大页队列,这些大页的拆分被推迟了。仅在开启了 ``CONFIG_TRANSPARENT_HUGEPAGE`` +配置选项时定义。 + +``__lruvec`` +每个节点的lruvec持有LRU(最近最少使用)列表和相关参数。仅在禁用了内存 +控制组(cgroups)时使用。它不应该直接访问,而应该使用 ``mem_cgroup_lruvec()`` +来查找lruvecs。 + +回收控制 +~~~~~~~~ + +另见内核文档 Documentation/mm/page_reclaim.rst 文件。 + +``kswapd`` +每个节点的kswapd内核线程实例。 + +``kswapd_wait``, ``pfmemalloc_wait``, ``reclaim_wait`` +同步内存回收任务的工作队列。 + +``nr_writeback_throttled`` +等待写回脏页时,被限制的任务数量。 + +``kswapd_order`` +控制kswapd尝试回收的order。 + +``kswapd_highest_zoneidx`` +kswapd线程可以回收的最高区域索引。 + +``kswapd_failures`` +kswapd无法回收任何页面的运行次数。 + +``min_unmapped_pages`` +无法回收的未映射文件支持的最小页面数量。由 ``vm.min_unmapped_ratio`` +系统控制台(sysctl)参数决定。在开启 ``CONFIG_NUMA`` 配置时定义。 + +``min_slab_pages`` +无法回收的SLAB页面的最少数量。由 ``vm.min_slab_ratio`` 系统控制台 +(sysctl)参数决定。在开启 ``CONFIG_NUMA`` 时定义。 + +``flags`` +控制回收行为的标志位。 + +内存压缩控制 +~~~~~~~~~~~~ + +``kcompactd_max_order`` +kcompactd应尝试实现的页面order。 + +``kcompactd_highest_zoneidx`` +kcompactd可以压缩的最高区域索引。 + +``kcompactd_wait`` +同步内存压缩任务的工作队列。 + +``kcompactd`` +每个节点的kcompactd内核线程实例。 + +``proactive_compact_trigger`` +决定是否使用主动压缩。由 ``vm.compaction_proactiveness`` 系统控 +制台(sysctl)参数控制。 + +统计信息 +~~~~~~~~ + +``per_cpu_nodestats`` +表示节点的Per-CPU虚拟内存统计信息。 + +``vm_stat`` +表示节点的虚拟内存统计数据。 + +.. _zones_zh_CN: + +区域 +==== + +.. admonition:: Stub + + 本节内容不完整。请列出并描述相应的字段。 + +.. _pages_zh_CN: + +页 +==== + +.. admonition:: Stub + + 本节内容不完整。请列出并描述相应的字段。 + +.. _folios_zh_CN: + +页码 +==== + +.. admonition:: Stub + + 本节内容不完整。请列出并描述相应的字段。 + +.. _initialization_zh_CN: + +初始化 +====== + +.. admonition:: Stub + + 本节内容不完整。请列出并描述相应的字段。 diff --git a/Documentation/translations/zh_CN/process/4.Coding.rst b/Documentation/translations/zh_CN/process/4.Coding.rst index 7cac9424f5d5..4cc35d410dbc 100644 --- a/Documentation/translations/zh_CN/process/4.Coding.rst +++ b/Documentation/translations/zh_CN/process/4.Coding.rst @@ -54,7 +54,7 @@ 注意您还可以使用 ``clang-format`` 工具来帮助您处理这些规则,快速自动重新格式 化部分代码,和审阅完整的文件以发现代码样式错误、拼写错误和可能的改进。它还 可以方便地排序 ``#includes`` 、对齐变量/宏、重排文本和其他类似任务。有关详细 -信息,请参阅文档 :ref:`Documentation/process/clang-format.rst <clangformat>` +信息,请参阅文档 :ref:`Documentation/dev-tools/clang-format.rst <clangformat>` 抽象层 ****** diff --git a/Documentation/translations/zh_CN/process/5.Posting.rst b/Documentation/translations/zh_CN/process/5.Posting.rst index 6a469e1c7deb..6c83a8f40310 100644 --- a/Documentation/translations/zh_CN/process/5.Posting.rst +++ b/Documentation/translations/zh_CN/process/5.Posting.rst @@ -146,10 +146,6 @@ - 补丁本身,采用统一的(“-u”)补丁格式。使用“-p”选项来diff将使函数名与 更改相关联,从而使结果补丁更容易被其他人读取。 -您应该避免在补丁中包括与更改不相关文件(例如,构建过程生成的文件或编辑器 -备份文件)。文档目录中的“dontdiff”文件在这方面有帮助;使用“-X”选项将 -其传递给diff。 - 上面提到的标签(tag)用于描述各种开发人员如何与这个补丁的开发相关联。 :ref:`Documentation/translations/zh_CN/process/submitting-patches.rst <cn_submittingpatches>` 文档中对它们进行了详细描述;下面是一个简短的总结。每一行的格式如下: diff --git a/Documentation/translations/zh_CN/process/coding-style.rst b/Documentation/translations/zh_CN/process/coding-style.rst index 3bc2810b151d..0484d0c65c25 100644 --- a/Documentation/translations/zh_CN/process/coding-style.rst +++ b/Documentation/translations/zh_CN/process/coding-style.rst @@ -560,17 +560,6 @@ Documentation/translations/zh_CN/doc-guide/index.rst 和 scripts/kernel-doc 。 * 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. - */ - 注释数据也是很重要的,不管是基本类型还是衍生类型。为了方便实现这一点,每一行 应只声明一个数据 (不要使用逗号来一次声明多个数据)。这样你就有空间来为每个数据 写一段小注释来解释它们的用途了。 @@ -654,7 +643,7 @@ Documentation/translations/zh_CN/doc-guide/index.rst 和 scripts/kernel-doc 。 请注意,您还可以使用 ``clang-format`` 工具帮助您处理这些规则,快速自动重新格 式化部分代码,并审阅整个文件以发现代码风格错误、打字错误和可能的改进。它还可 以方便地排序 ``#include`` ,对齐变量/宏,重排文本和其他类似任务。 -详见 Documentation/process/clang-format.rst 。 +详见 Documentation/dev-tools/clang-format.rst 。 10) Kconfig 配置文件 diff --git a/Documentation/translations/zh_CN/process/cve.rst b/Documentation/translations/zh_CN/process/cve.rst new file mode 100644 index 000000000000..e39b796efcec --- /dev/null +++ b/Documentation/translations/zh_CN/process/cve.rst @@ -0,0 +1,89 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/process/cve.rst +:Translator: Dongliang Mu <dzm91@hust.edu.cn> + +==== +CVEs +==== + +Common Vulnerabilities and Exposure (CVE®) 编号是一种明确的方式来 +识别、定义和登记公开披露的安全漏洞。随着时间的推移,它们在内核项目中的实用性 +已经下降,CVE编号经常以不适当的方式和不适当的原因被分配。因此,内核开发社区 +倾向于避免使用它们。然而,分配CVE与其他形式的安全标识符的持续压力,以及内核 +社区之外的个人和公司的持续滥用,已经清楚地表明内核社区应该控制这些CVE分配。 + +Linux内核开发团队确实有能力为潜在的Linux内核安全问题分配CVE。CVE的分配 +独立于 :doc:`安全漏洞报送流程</process/security-bugs>`。 + +所有分配给Linux内核的CVE列表都可以在linux-cve邮件列表的存档中找到,如 +https://lore.kernel.org/linux-cve-announce/ 所示。如果想获得已分配 +CVE的通知,请“订阅”该邮件列表。要获得分配的CVE通知,请订阅该邮件列表: +`订阅 <https://subspace.kernel.org/subscribing.html>`_。 + +过程 +======= + +作为正常稳定发布过程的一部分,可能存在安全问题的内核更改由负责CVE编号分配 +的开发人员识别,并自动为其分配CVE编号。这些CVE分配会作为经常性的通告经常 +发布在linux-cve-announce邮件列表上。 + +注意,由于Linux内核在系统中的特殊地位,几乎任何漏洞都可能被利用来危害内核 +的安全性,但是当漏洞被修复后,利用的可能性通常不明显。因此,CVE分配团队过于 +谨慎,并将CVE编号分配给他们识别的任何漏洞修复。这就解释了为什么Linux内核 +团队会发布大量的CVE。 + +如果CVE分配团队错过了任何用户认为应该分配CVE的特定修复,请发送电子邮件到 +<cve@kernel.org>,那里的团队将与您一起工作。请注意,任何潜在的安全问题 +不应被发送到此邮箱,它仅用于为已发布的内核树中的漏洞修复分配CVE。如果你觉得 +自己发现了一个未修复的安全问题,请按照 :doc:`安全漏洞报送流程 +</process/security-bugs>` 发送到Linux内核社区。 + +Linux内核不会给未修复的安全问题自动分配CVE;只有在安全修复可用且应用于 +稳定内核树后,CVE分配才会自动发生,并且它将通过安全修复的Git提交编号进行 +跟踪。如果有人希望在提交安全修复之前分配CVE,请联系内核CVE分配团队,从 +他们的一批保留编号中获得相应的CVE编号。 + +对于目前没有得到稳定与长期维护内核团队积极支持的内核版本中发现的任何问题, +都不会分配CVEs。当前支持的内核分支列表可以在 https://kernel.org/releases.html +上找到。 + +被分配CVE的争论 +========================= + +对于为特定内核修改分配的CVE,其争论或修改的权限仅属于受影响子系统的维护者。 +这一原则确保了漏洞报告的高度准确性和可问责性。只有那些具有深厚专业知识和 +对子系统深入了解的维护人员,才能有效评估内核漏洞的有效性和范围,并确定其适当的 +CVE指定策略。在此指定权限之外,任何争论或修改CVE的尝试都可能导致混乱、 +不准确的报告,并最终危及系统。 + +无效的CVE +============ + +如果发现的安全问题存在于仅由某Linux发行版支持的Linux内核中,即安全问题是 +由于Linux发行版所做的更改导致,或者Linux的发行版内核版本不再是Linux内核 +社区支持的内核版本,那么Linux内核CVE团队将不能分配CVE,必须从Linux +发行版本身请求。 + +内核CVE分配团队以外的任何团队对Linux内核支持版本分配的CVE都不应被 +视为有效CVE。请通知内核CVE分配团队,以便他们可以通过CNA修复措施使 +这些条目失效。 + +特定CVE的适用性 +============================== + +由于Linux内核可以以许多不同方式使用,外部用户可以通过许多不同方式访问它,或者 +根本没有访问,因此任何特定CVE的适用性取决于Linux用户,而不是内核CVE分配团队。 +请不要与我们联系来尝试确定任何特定CVE的适用性。 + +此外,由于源代码树非常大,而任何一个系统都只使用源代码树的一小部分,因此任何 +Linux用户都应该意识到,大量分配的CVEs与他们的系统无关。 + +简而言之,我们不知道您的用例,也不知道您使用的是内核的哪个部分,因此我们无法 +确定特定的CVE是否与您的系统相关。 + +与往常一样,最好采用所有发布的内核更改,因为它们是由许多社区成员在一个统一的 +整体中一起进行测试的,而不是作为个别的精选更改。还要注意,对于许多安全问题来 +说,整体问题的解决方案并不是在单个更改中找到的,而是在彼此之上的许多修复的总 +和。理想情况下,CVE将被分配给所有问题的所有修复,但有时我们将无法注意到一些 +修复,因此某些修复可能在没有CVE的情况下被采取。 diff --git a/Documentation/translations/zh_CN/process/email-clients.rst b/Documentation/translations/zh_CN/process/email-clients.rst index 34d51cdadc7b..069c0460d20c 100644 --- a/Documentation/translations/zh_CN/process/email-clients.rst +++ b/Documentation/translations/zh_CN/process/email-clients.rst @@ -85,7 +85,7 @@ Alpine (TUI) - :menuselection:`Do Not Send Flowed Text` 必须开启 - :menuselection:`Strip Whitespace Before Sending` 必须关闭 -当写邮件时,光标应该放在补丁会出现的地方,然后按下 :kbd:`CTRL-R` 组合键,使指 +当写邮件时,光标应该放在补丁会出现的地方,然后按下 `CTRL-R` 组合键,使指 定的补丁文件嵌入到邮件中。 Claws Mail (GUI) @@ -93,7 +93,7 @@ Claws Mail (GUI) 可以用,有人用它成功地发过补丁。 -用 :menuselection:`Message-->Insert File` (:kbd:`CTRL-I`) 或外置编辑器插入补丁。 +用 :menuselection:`Message-->Insert File` (`CTRL-I`) 或外置编辑器插入补丁。 若要在Claws编辑窗口重修改插入的补丁,需关闭 :menuselection:`Configuration-->Preferences-->Compose-->Wrapping` @@ -105,11 +105,11 @@ Evolution (GUI) 一些开发者成功的使用它发送补丁。 撰写邮件时: -从 :menuselection:`格式-->段落样式-->预格式化` (:kbd:`CTRL-7`) +从 :menuselection:`格式-->段落样式-->预格式化` (`CTRL-7`) 或工具栏选择 :menuselection:`预格式化` ; 然后使用: -:menuselection:`插入-->文本文件...` (:kbd:`ALT-N x`) 插入补丁文件。 +:menuselection:`插入-->文本文件...` (`ALT-N x`) 插入补丁文件。 你还可以 ``diff -Nru old.c new.c | xclip`` ,选择 :menuselection:`预格式化` , 然后使用鼠标中键进行粘帖。 @@ -197,7 +197,7 @@ Mutt不自带编辑器,所以不管你使用什么编辑器,不自动断行 Mutt 是高度可配置的。 这里是个使用mutt通过 Gmail 发送的补丁的最小配置:: # .muttrc - # ================ IMAP ==================== + # ================ IMAP ==================== set imap_user = 'yourusername@gmail.com' set imap_pass = 'yourpassword' set spoolfile = imaps://imap.gmail.com/INBOX @@ -325,3 +325,10 @@ Gmail网页客户端自动地把制表符转换为空格。 另一个问题是Gmail还会把任何含有非ASCII的字符的消息改用base64编码,如欧洲人的 名字。 +HacKerMaiL (TUI) +**************** + +HacKerMaiL (hkml) 是一个基于公共收件箱的简单邮件管理工具,它不需要订阅邮件列表。 +该工具由 DAMON 维护者开发和维护,旨在支持 DAMON 和通用内核子系统的基本开发工作 +流程。详细信息可参考 HacKerMaiL 的 README 文件 +(https://github.com/sjp38/hackermail/blob/master/README.md)。 diff --git a/Documentation/translations/zh_CN/process/index.rst b/Documentation/translations/zh_CN/process/index.rst index 3ca02d281be0..3bcb3bdaf533 100644 --- a/Documentation/translations/zh_CN/process/index.rst +++ b/Documentation/translations/zh_CN/process/index.rst @@ -48,10 +48,12 @@ TODOLIST: :maxdepth: 1 embargoed-hardware-issues + cve + security-bugs TODOLIST: -* security-bugs +* handling-regressions 其它大多数开发人员感兴趣的社区指南: @@ -63,6 +65,7 @@ TODOLIST: management-style stable-kernel-rules submit-checklist + researcher-guidelines TODOLIST: @@ -70,7 +73,6 @@ TODOLIST: * kernel-docs * deprecated * maintainers -* researcher-guidelines * contribution-maturity-model diff --git a/Documentation/translations/zh_CN/process/magic-number.rst b/Documentation/translations/zh_CN/process/magic-number.rst index 4e4aeaca796c..4ebc84cc0c54 100644 --- a/Documentation/translations/zh_CN/process/magic-number.rst +++ b/Documentation/translations/zh_CN/process/magic-number.rst @@ -1,6 +1,6 @@ .. include:: ../disclaimer-zh_CN.rst -:Original: Documentation/process/magic-number.rst +:Original: Documentation/staging/magic-number.rst :翻译: diff --git a/Documentation/translations/zh_CN/process/programming-language.rst b/Documentation/translations/zh_CN/process/programming-language.rst index fabdc338dbfb..95aa4829d78f 100644 --- a/Documentation/translations/zh_CN/process/programming-language.rst +++ b/Documentation/translations/zh_CN/process/programming-language.rst @@ -3,25 +3,22 @@ :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=gnu11`` :ref:`gcc-c-dialect-options <cn_gcc-c-dialect-options>` 下编译的:ISO C11的 GNU 方言 - -这种方言包含对语言 :ref:`gnu-extensions <cn_gnu-extensions>` 的许多扩展,当然,它们许多都在内核中使用。 +内核是用 C 编程语言编写的 [zh_cn_c-language]_。更准确地说,内核通常使用 ``gcc`` [zh_cn_gcc]_ 编译, +并且使用 ``-std=gnu11`` [zh_cn_gcc-c-dialect-options]_:这是 ISO C11 的 GNU 方言。 +``clang`` [zh_cn_clang]_ 也得到了支持,详见文档: +:ref:`使用 Clang/LLVM 构建 Linux <kbuild_llvm>`。 -对于一些体系结构,有一些使用 :ref:`clang <cn_clang>` 和 :ref:`icc <cn_icc>` 编译内核 -的支持,尽管在编写此文档时还没有完成,仍需要第三方补丁。 +这种方言包含对 C 语言的许多扩展 [zh_cn_gnu-extensions]_,当然,它们许多都在内核中使用。 属性 ---- -在整个内核中使用的一个常见扩展是属性(attributes) :ref:`gcc-attribute-syntax <cn_gcc-attribute-syntax>` +在整个内核中使用的一个常见扩展是属性(attributes) [zh_cn_gcc-attribute-syntax]_。 属性允许将实现定义的语义引入语言实体(如变量、函数或类型),而无需对语言进行 -重大的语法更改(例如添加新关键字) :ref:`n2049 <cn_n2049>` +重大的语法更改(例如添加新关键字) [zh_cn_n2049]_。 在某些情况下,属性是可选的(即不支持这些属性的编译器仍然应该生成正确的代码, 即使其速度较慢或执行的编译时检查/诊断次数不够) @@ -30,42 +27,27 @@ ``__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: +Rust +---- -n2049 - http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2049.pdf +内核对 Rust 编程语言 [zh_cn_rust-language]_ 的支持是实验性的,并且可以通过配置选项 +``CONFIG_RUST`` 来启用。Rust 代码使用 ``rustc`` [zh_cn_rustc]_ 编译器在 +``--edition=2021`` [zh_cn_rust-editions]_ 选项下进行编译。版本(Editions)是一种 +在语言中引入非后向兼容的小型变更的方式。 + +除此之外,内核中还使用了一些不稳定的特性 [zh_cn_rust-unstable-features]_。这些不稳定 +的特性将来可能会发生变化,因此,一个重要的目标是达到仅使用稳定特性的程度。 + +具体请参阅 Documentation/rust/index.rst + +.. [zh_cn_c-language] http://www.open-std.org/jtc1/sc22/wg14/www/standards +.. [zh_cn_gcc] https://gcc.gnu.org +.. [zh_cn_clang] https://clang.llvm.org +.. [zh_cn_gcc-c-dialect-options] https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html +.. [zh_cn_gnu-extensions] https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html +.. [zh_cn_gcc-attribute-syntax] https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html +.. [zh_cn_n2049] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2049.pdf +.. [zh_cn_rust-language] https://www.rust-lang.org +.. [zh_cn_rustc] https://doc.rust-lang.org/rustc/ +.. [zh_cn_rust-editions] https://doc.rust-lang.org/edition-guide/editions/ +.. [zh_cn_rust-unstable-features] https://github.com/Rust-for-Linux/linux/issues/2 diff --git a/Documentation/translations/zh_CN/process/researcher-guidelines.rst b/Documentation/translations/zh_CN/process/researcher-guidelines.rst new file mode 100644 index 000000000000..1f2da68fc4e8 --- /dev/null +++ b/Documentation/translations/zh_CN/process/researcher-guidelines.rst @@ -0,0 +1,129 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. include:: ../disclaimer-zh_CN.rst + +.. _cn_researcherguidelines: + +:Original: Documentation/process/researcher-guidelines.rst + +:译者: + - 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> + +研究人员指南 ++++++++++++++++++++++ + +Linux 内核社区欢迎对 Linux 内核及其开发过程中涉及的活动与任何其他副产品 +进行透明的研究。Linux 从这种研究中受益匪浅,其多方面均由某种形式的研究所推动。 + +社区非常感谢研究人员在公开研究结果之前能分享初步发现,特别是涉及安全的研究。 +早期参与有助于提高研究质量并使 Linux 受益。无论如何,推荐研究人员与社区分享 +已发表研究的开放访问副本。 + +本文旨在澄清研究开展过程中 Linux 内核社区认可与不认可的一些做法。至少,这类 +研究及相关活动应遵循标准的研究伦理规则。有关研究伦理、技术伦理以及开发者社区 +研究的更多背景信息,请查阅: + +* `研究伦理史 <https://www.unlv.edu/research/ORI-HSR/history-ethics>`_ +* `IEEE 伦理 <https://www.ieee.org/about/ethics/index.html>`_ +* `开发者和研究人员对开源项目实验伦理的看法 <https://arxiv.org/pdf/2112.13217.pdf>`_ + +Linux 内核社区期望与项目互动的每个人都是真诚地为了使 Linux 变得更好。 +对 Linux 内核社区产生的任何公开可用的成果(包括但不限于源代码)的研究 +是受欢迎的,对开发者的研究如若要开展,则必须要明确说明,获得(开发者)同意。 + +完全基于公开可用资源(包括公共邮件列表的帖子和公开代码库的提交)的被动研究 +显然是允许的。不过,和任何研究一样,仍需遵循标准伦理。 + +然而,针对开发者行为的主动研究必须在获得相关开发者的明确同意和完全披露的情况下进行。 +未经同意,不得与开发者互动或对其进行实验;这也是标准的研究伦理。 + +调查 +======= + +研究通常采用调查问卷的形式发送给维护者或贡献者。然而,内核社区通常从这些调查问卷中获益 +甚少。内核开发过程之所以有效,是因为每个开发者都从中受益,即使与目标不同的人一起工作。 +而回应调查则是对繁忙开发者的单向需求,对他们自己或整个内核社区没有相应的好处。因此, +这种研究方法不被鼓励。 + +内核社区成员已经收到过多的电子邮件,可能会将调查请求视为对他们时间的又一要求。发送 +此类请求会剥夺社区宝贵的贡献者时间,且不太可能产生有统计意义的回应。 + +作为替代,研究人员应考虑参加开发者活动,举办研讨会来介绍研究项目及其对参与者的益处, +并直接与社区互动。该方式获得的信息将比电子邮件调查问卷丰富得多,且社区也能从中学习 +到您的见解。 + +补丁 +======= + +澄清:向开发者发送补丁**是**与他们互动,但他们已经同意接收**善意贡献**。故意发送有缺陷/ +有漏洞的补丁或在讨论中提供误导信息是不被同意的。这种交流会对开发者造成损害 +(例如,消耗时间、精力和士气),并通过破坏整个开发者社区对贡献者(及其所在组织) +的信任而损害项目,削弱为贡献者提供建设性反馈的努力,并使最终用户面临软件缺陷的风险。 + +研究人员参与 Linux 本身的开发与其他人一样受到欢迎和鼓励。研究 Linux 代码是常见 +做法,尤其是在开发或运行可产生可操作结果的分析工具时。 + +在与开发者社区互动时,发送补丁历来是产生影响的最佳方式。Linux 已经有很多已知的 +漏洞 -- 更有帮助的是经过审核的修复。在贡献之前,请仔细阅读相关文档: + +* Documentation/process/development-process.rst +* Documentation/process/submitting-patches.rst +* Documentation/admin-guide/reporting-issues.rst +* Documentation/process/security-bugs.rst + +然后发送补丁(包括所有如下详细信息的提交日志)并跟进其他开发者的任何反馈。 + +当发送因研究而产生的补丁时,提交日志应至少包含以下详细信息,以便开发者有适当的上下文 +来理解贡献。回答: + +* 找到了什么具体问题? +* 在运行系统上如何触发这个问题? +* 遇到这个问题对系统会有什么影响? +* 如何发现这个问题?具体包括任何测试、静态或动态分析程序及其他用于执行工作的工具或方法的详细信息。 +* 在哪个版本的 Linux 上发现了这个问题?强烈推荐使用最新的发布版本或最近的 linux-next 分支(参见 Documentation/process/howto.rst)。 +* 进行了哪些更改来修复这个问题,为什么认为这些更改是正确的? +* 如何进行构建测试和运行时测试? +* 此更改修复了哪个先前的提交?这应该在 "Fixes:" 标签中,如文档所述。 +* 还有谁审查了这个补丁?这应该在适当的 "Reviewed-by:" 标签中注明;见下文。 + +例如:: + + From: Author <author@email> + Subject: [PATCH] drivers/foo_bar: Add missing kfree() + + The error path in foo_bar driver does not correctly free the allocated + struct foo_bar_info. This can happen if the attached foo_bar device + rejects the initialization packets sent during foo_bar_probe(). This + would result in a 64 byte slab memory leak once per device attach, + wasting memory resources over time. + + This flaw was found using an experimental static analysis tool we are + developing, LeakMagic[1], which reported the following warning when + analyzing the v5.15 kernel release: + + path/to/foo_bar.c:187: missing kfree() call? + + Add the missing kfree() to the error path. No other references to + this memory exist outside the probe function, so this is the only + place it can be freed. + + x86_64 and arm64 defconfig builds with CONFIG_FOO_BAR=y using GCC + 11.2 show no new warnings, and LeakMagic no longer warns about this + code path. As we don't have a FooBar device to test with, no runtime + testing was able to be performed. + + [1] https://url/to/leakmagic/details + + Reported-by: Researcher <researcher@email> + Fixes: aaaabbbbccccdddd ("Introduce support for FooBar") + Signed-off-by: Author <author@email> + Reviewed-by: Reviewer <reviewer@email> + +如果您是第一次参与贡献,建议在补丁在发布到公共列表前请其他人私下进行审核。(如果明确 +告诉您补丁需要更仔细的内部审查,则这是必需的。)这些人预计会在最终的补丁中包含他们的 +"Reviewed-by" 标签。找到熟悉 Linux 贡献的其他开发者,特别是您自己组织内的开发者, +并在将补丁发送到公共邮件列表前请他们帮助审核,往往会显著提高补丁的质量,从而减少 +其他开发者的负担。 + +如果你找不到人内部审核补丁且需要帮助找到这样的人,或者如果您对本文档和开发者社区的期望 +有任何其他问题,请联系技术咨询委员会私有邮件列表:<tech-board@groups.linuxfoundation.org>。 diff --git a/Documentation/translations/zh_CN/admin-guide/security-bugs.rst b/Documentation/translations/zh_CN/process/security-bugs.rst index d6b8f8a4e7f6..a8f5fcbfadc9 100644 --- a/Documentation/translations/zh_CN/admin-guide/security-bugs.rst +++ b/Documentation/translations/zh_CN/process/security-bugs.rst @@ -1,3 +1,5 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + .. include:: ../disclaimer-zh_CN.rst :Original: :doc:`../../../process/security-bugs` @@ -5,6 +7,7 @@ :译者: 吴想成 Wu XiangCheng <bobwxc@email.cn> + 慕冬亮 Dongliang Mu <dzm91@hust.edu.cn> 安全缺陷 ========= @@ -17,13 +20,13 @@ Linux内核开发人员非常重视安全性。因此我们想知道何时发现 可以通过电子邮件<security@kernel.org>联系Linux内核安全团队。这是一个安全人员 的私有列表,他们将帮助验证错误报告并开发和发布修复程序。如果您已经有了一个 -修复,请将其包含在您的报告中,这样可以大大加快进程。安全团队可能会从区域维护 +修复,请将其包含在您的报告中,这样可以大大加快处理进程。安全团队可能会从区域维护 人员那里获得额外的帮助,以理解和修复安全漏洞。 与任何缺陷一样,提供的信息越多,诊断和修复就越容易。如果您不清楚哪些信息有用, 请查看“Documentation/translations/zh_CN/admin-guide/reporting-issues.rst”中 -概述的步骤。任何利用漏洞的攻击代码都非常有用,未经报告者同意不会对外发布,除 -非已经公开。 +概述的步骤。任何利用漏洞的攻击代码都非常有用,未经报告者同意不会对外发布, +除非已经公开。 请尽可能发送无附件的纯文本电子邮件。如果所有的细节都藏在附件里,那么就很难对 一个复杂的问题进行上下文引用的讨论。把它想象成一个 @@ -49,24 +52,31 @@ Linux内核开发人员非常重视安全性。因此我们想知道何时发现 换句话说,我们唯一感兴趣的是修复缺陷。提交给安全列表的所有其他资料以及对报告 的任何后续讨论,即使在解除限制之后,也将永久保密。 -协调 ------- +与其他团队协调 +-------------- + +虽然内核安全团队仅关注修复漏洞,但还有其他组织关注修复发行版上的安全问题以及协调 +操作系统厂商的漏洞披露。协调通常由 "linux-distros" 邮件列表处理,而披露则由 +公共 "oss-security" 邮件列表进行。两者紧密关联且被展示在 linux-distros 维基: +<https://oss-security.openwall.org/wiki/mailing-lists/distros> + +请注意,这三个列表的各自政策和规则是不同的,因为它们追求不同的目标。内核安全团队 +与其他团队之间的协调很困难,因为对于内核安全团队,保密期(即最大允许天数)是从补丁 +可用时开始,而 "linux-distros" 则从首次发布到列表时开始计算,无论是否存在补丁。 -对敏感缺陷(例如那些可能导致权限提升的缺陷)的修复可能需要与私有邮件列表 -<linux-distros@vs.openwall.org>进行协调,以便分发供应商做好准备,在公开披露 -上游补丁时发布一个已修复的内核。发行版将需要一些时间来测试建议的补丁,通常 -会要求至少几天的限制,而供应商更新发布更倾向于周二至周四。若合适,安全团队 -可以协助这种协调,或者报告者可以从一开始就包括linux发行版。在这种情况下,请 -记住在电子邮件主题行前面加上“[vs]”,如linux发行版wiki中所述: -<http://oss-security.openwall.org/wiki/mailing-lists/distros#how-to-use-the-lists>。 +因此,内核安全团队强烈建议,作为一位潜在安全问题的报告者,在受影响代码的维护者 +接受补丁之前,且在您阅读上述发行版维基页面并完全理解联系 "linux-distros" +邮件列表会对您和内核社区施加的要求之前,不要联系 "linux-distros" 邮件列表。 +这也意味着通常情况下不要同时抄送两个邮件列表,除非在协调时有已接受但尚未合并的补丁。 +换句话说,在补丁被接受之前,不要抄送 "linux-distros";在修复程序被合并之后, +不要抄送内核安全团队。 CVE分配 -------- -安全团队通常不分配CVE,我们也不需要它们来进行报告或修复,因为这会使过程不必 -要的复杂化,并可能耽误缺陷处理。如果报告者希望在公开披露之前分配一个CVE编号, -他们需要联系上述的私有linux-distros列表。当在提供补丁之前已有这样的CVE编号时, -如报告者愿意,最好在提交消息中提及它。 +安全团队不分配 CVE,同时我们也不需要 CVE 来报告或修复漏洞,因为这会使过程不必要 +的复杂化,并可能延误漏洞处理。如果报告者希望为确认的问题分配一个 CVE 编号, +可以联系 :doc:`内核 CVE 分配团队 <../process/cve>` 获取。 保密协议 --------- diff --git a/Documentation/translations/zh_CN/process/submitting-patches.rst b/Documentation/translations/zh_CN/process/submitting-patches.rst index f8978f02057c..f7ae584a439e 100644 --- a/Documentation/translations/zh_CN/process/submitting-patches.rst +++ b/Documentation/translations/zh_CN/process/submitting-patches.rst @@ -105,7 +105,7 @@ xyzzy do frotz”或“[I]changed xyzzy to do frotz”,就好像你在命令 当链接到邮件列表存档时,请首选lore.kernel.org邮件存档服务。用邮件中的 ``Message-ID`` 头(去掉尖括号)可以创建链接URL。例如:: - Link: https://lore.kernel.org/r/30th.anniversary.repost@klaava.Helsinki.FI/ + Link: https://lore.kernel.org/30th.anniversary.repost@klaava.Helsinki.FI 请检查该链接以确保可用且指向正确的邮件。 @@ -195,11 +195,8 @@ scripts/get_maintainer.pl在这个步骤中非常有用。如果您找不到正 在MAINTAINERS文件中查找子系统特定的列表;您的补丁可能会在那里得到更多的关注。 不过,请不要发送垃圾邮件到无关的列表。 -许多与内核相关的列表托管在vger.kernel.org上;您可以在 -http://vger.kernel.org/vger-lists.html 上找到它们的列表。不过,也有与内核相关 -的列表托管在其他地方。 - -不要一次发送超过15个补丁到vger邮件列表!!!! +许多与内核相关的列表托管在 kernel.org 上;您可以在 https://subspace.kernel.org +上找到它们的列表。不过,也有与内核相关的列表托管在其他地方。 Linus Torvalds是决定改动能否进入 Linux 内核的最终裁决者。他的邮件地址是 torvalds@linux-foundation.org 。他收到的邮件很多,所以一般来说最好 **别** @@ -208,7 +205,7 @@ torvalds@linux-foundation.org 。他收到的邮件很多,所以一般来说 如果您有修复可利用安全漏洞的补丁,请将该补丁发送到 security@kernel.org 。对于 严重的bug,可以考虑短期禁令以允许分销商(有时间)向用户发布补丁;在这种情况下, 显然不应将补丁发送到任何公共列表。 -参见 Documentation/translations/zh_CN/admin-guide/security-bugs.rst 。 +参见 Documentation/translations/zh_CN/process/security-bugs.rst 。 修复已发布内核中严重错误的补丁程序应该抄送给稳定版维护人员,方法是把以下列行 放进补丁的签准区(注意,不是电子邮件收件人):: @@ -333,10 +330,10 @@ Linus 和其他的内核开发者需要阅读和评论你提交的改动。对 未参与其开发。签署链应当反映补丁传播到维护者并最终传播到Linus所经过的 **真实** 路径,首个签署指明单个作者的主要作者身份。 -何时使用Acked-by:,CC:,和Co-Developed by: +何时使用Acked-by:,Cc:,和Co-developed-by: ------------------------------------------ -Singed-off-by: 标签表示签名者参与了补丁的开发,或者他/她在补丁的传递路径中。 +Signed-off-by: 标签表示签名者参与了补丁的开发,或者他/她在补丁的传递路径中。 如果一个人没有直接参与补丁的准备或处理,但希望表示并记录他们对补丁的批准/赞成, 那么他们可以要求在补丁的变更日志中添加一个Acked-by:。 @@ -358,8 +355,8 @@ Acked-by:不一定表示对整个补丁的确认。例如,如果一个补丁 Co-developed-by: 声明补丁是由多个开发人员共同创建的;当几个人在一个补丁上工 作时,它用于给出共同作者(除了From:所给出的作者之外)。因为Co-developed-by: 表示作者身份,所以每个Co-developed-by:必须紧跟在相关合作作者的签署之后。标准 -签署程序要求Singed-off-by:标签的顺序应尽可能反映补丁的时间历史,无论作者是通 -过From:还是Co-developed-by:表明。值得注意的是,最后一个Singed-off-by:必须是 +签署程序要求Signed-off-by:标签的顺序应尽可能反映补丁的时间历史,无论作者是通 +过From:还是Co-developed-by:表明。值得注意的是,最后一个Signed-off-by:必须是 提交补丁的开发人员。 注意,如果From:作者也是电子邮件标题的From:行中列出的人,则From:标签是可选的。 @@ -621,6 +618,13 @@ Fixes: 指示补丁修复了之前提交的一个问题。它可以便于确定 的工作所基于的树的提交哈希。你应该在封面邮件或系列的第一个补丁中添加它,它应 该放在 ``---`` 行的下面或所有其他内容之后,即只在你的电子邮件签名之前。 +工具 +---- + +这个过程的许多技术方面可以使用 b4 自动完成,其文档可在 +https://b4.docs.kernel.org/en/latest/ 查看。该工具可帮助处理诸如追踪依赖项、运行 +checkpatch 以及格式化和发送邮件等事务。 + 参考文献 -------- @@ -643,9 +647,6 @@ Greg Kroah-Hartman,“如何惹恼内核子系统维护人员” <http://www.kroah.com/log/linux/maintainer-06.html> -不!!!别再发巨型补丁炸弹给linux-kernel@vger.kernel.org的人们了! - <https://lore.kernel.org/r/20050711.125305.08322243.davem@davemloft.net> - 内核 Documentation/translations/zh_CN/process/coding-style.rst Linus Torvalds关于标准补丁格式的邮件 diff --git a/Documentation/translations/zh_CN/rust/arch-support.rst b/Documentation/translations/zh_CN/rust/arch-support.rst index afbd02afec45..abd708d48f82 100644 --- a/Documentation/translations/zh_CN/rust/arch-support.rst +++ b/Documentation/translations/zh_CN/rust/arch-support.rst @@ -16,8 +16,12 @@ 下面是目前可以工作的架构的一般总结。支持程度与 ``MAINTAINERS`` 文件中的``S`` 值相对应: -============ ================ ============================================== -架构 支持水平 限制因素 -============ ================ ============================================== -``x86`` Maintained 只有 ``x86_64`` -============ ================ ============================================== +============= ================ ============================================== +架构 支持水平 限制因素 +============= ================ ============================================== +``arm64`` Maintained 只有小端序 +``loongarch`` Maintained \- +``riscv`` Maintained 只有 ``riscv64`` +``um`` Maintained 只有 ``x86_64`` +``x86`` Maintained 只有 ``x86_64`` +============= ================ ============================================== diff --git a/Documentation/translations/zh_CN/rust/coding-guidelines.rst b/Documentation/translations/zh_CN/rust/coding-guidelines.rst index 6c0bdbbc5a2a..419143b938ed 100644 --- a/Documentation/translations/zh_CN/rust/coding-guidelines.rst +++ b/Documentation/translations/zh_CN/rust/coding-guidelines.rst @@ -157,6 +157,18 @@ https://commonmark.org/help/ https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html +此外,内核支持通过在链接目标前添加 ``srctree/`` 来创建相对于源代码树的链接。例如: + +.. code-block:: rust + + //! C header: [`include/linux/printk.h`](srctree/include/linux/printk.h) + +或者: + +.. code-block:: rust + + /// [`struct mutex`]: srctree/include/linux/mutex.h + 命名 ---- diff --git a/Documentation/translations/zh_CN/rust/general-information.rst b/Documentation/translations/zh_CN/rust/general-information.rst index 6b91dfe1834a..251f6ee2bb44 100644 --- a/Documentation/translations/zh_CN/rust/general-information.rst +++ b/Documentation/translations/zh_CN/rust/general-information.rst @@ -32,7 +32,7 @@ Rust内核代码使用其内置的文档生成器 ``rustdoc`` 进行记录。 要在你的网络浏览器中本地阅读该文档,请运行如:: - xdg-open rust/doc/kernel/index.html + xdg-open Documentation/output/rust/rustdoc/kernel/index.html 要了解如何编写文档,请看 coding-guidelines.rst 。 diff --git a/Documentation/translations/zh_CN/rust/quick-start.rst b/Documentation/translations/zh_CN/rust/quick-start.rst index a4b8e8a50a89..8616556ae4d7 100644 --- a/Documentation/translations/zh_CN/rust/quick-start.rst +++ b/Documentation/translations/zh_CN/rust/quick-start.rst @@ -37,13 +37,18 @@ rustc 需要一个特定版本的Rust编译器。较新的版本可能会也可能不会工作,因为就目前而言,内核依赖 于一些不稳定的Rust特性。 -如果使用的是 ``rustup`` ,请进入检出的源代码目录并运行:: +如果使用的是 ``rustup`` ,请进入内核编译目录(或者用 ``--path=<build-dir>`` 参数 +来 ``设置`` sub-command)并运行:: rustup override set $(scripts/min-tool-version.sh rustc) -或者从以下网址获取一个独立的安装程序或安装 ``rustup`` : ++这将配置你的工作目录使用正确版本的 ``rustc``,而不影响你的默认工具链。 - https://www.rust-lang.org +请注意覆盖应用当前的工作目录(和它的子目录)。 + +如果你使用 ``rustup``, 可以从下面的链接拉取一个单独的安装程序: + + https://forge.rust-lang.org/infra/other-installation-methods.html#standalone Rust标准库源代码 @@ -57,21 +62,23 @@ Rust标准库的源代码是必需的,因为构建系统会交叉编译 ``core 这些组件是按工具链安装的,因此以后升级Rust编译器版本需要重新添加组件。 -否则,如果使用独立的安装程序,可以将Rust仓库克隆到工具链的安装文件夹中:: +否则,如果使用独立的安装程序,可以将Rust源码树下载到安装工具链的文件夹中:: - git clone --recurse-submodules \ - --branch $(scripts/min-tool-version.sh rustc) \ - https://github.com/rust-lang/rust \ - $(rustc --print sysroot)/lib/rustlib/src/rust + curl -L "https://static.rust-lang.org/dist/rust-src-$(scripts/min-tool-version.sh rustc).tar.gz" | + tar -xzf - -C "$(rustc --print sysroot)/lib" \ + "rust-src-$(scripts/min-tool-version.sh rustc)/rust-src/lib/" \ + --strip-components=3 -在这种情况下,以后升级Rust编译器版本需要手动更新这个克隆的仓库。 +在这种情况下,以后升级Rust编译器版本需要手动更新这个源代码树(这可以通过移除 +``$(rustc --print sysroot)/lib/rustlib/src/rust`` ,然后重新执行上 +面的命令做到)。 libclang ******** ``bindgen`` 使用 ``libclang`` (LLVM的一部分)来理解内核中的C代码,这意味着需要安 -装LLVM;同在开启 ``CC=clang`` 或 ``LLVM=1`` 时编译内核一样。 +装LLVM;同在开启``LLVM=1`` 时编译内核一样。 Linux发行版中可能会有合适的包,所以最好先检查一下。 @@ -94,7 +101,20 @@ bindgen 通过以下方式安装它(注意,这将从源码下载并构建该工具):: - cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen + cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli + +``bindgen`` 需要找到合适的 ``libclang`` 才能工作。如果没有找到(或者找到的 +``libclang`` 与应该使用的 ``libclang`` 不同),则可以使用 ``clang-sys`` +理解的环境变量(Rust绑定创建的 ``bindgen`` 用来访问 ``libclang``): + + +* ``LLVM_CONFIG_PATH`` 可以指向一个 ``llvm-config`` 可执行文件。 + +* 或者 ``LIBCLANG_PATH`` 可以指向 ``libclang`` 共享库或包含它的目录。 + +* 或者 ``CLANG_PATH`` 可以指向 ``clang`` 可执行文件。 + +详情请参阅 ``clang-sys`` 的文档: 开发依赖 @@ -163,7 +183,9 @@ rust-analyzer 一起使用,以实现语法高亮、补全、转到定义和其他功能。 ``rust-analyzer`` 需要一个配置文件, ``rust-project.json``, 它可以由 ``rust-analyzer`` -Make 目标生成。 +Make 目标生成:: + + make LLVM=1 rust-analyzer 配置 @@ -189,10 +211,6 @@ Rust支持(CONFIG_RUST)需要在 ``General setup`` 菜单中启用。在其 make LLVM=1 -对于不支持完整LLVM工具链的架构,使用:: - - make CC=clang - 使用GCC对某些配置也是可行的,但目前它是非常试验性的。 diff --git a/Documentation/translations/zh_CN/scheduler/sched-domains.rst b/Documentation/translations/zh_CN/scheduler/sched-domains.rst index e814d4c01141..06363169c56b 100644 --- a/Documentation/translations/zh_CN/scheduler/sched-domains.rst +++ b/Documentation/translations/zh_CN/scheduler/sched-domains.rst @@ -34,17 +34,17 @@ CPU共享。任意两个组的CPU掩码的交集不一定为空,如果是这 调度域中的负载均衡发生在调度组中。也就是说,每个组被视为一个实体。组的负载被定义为它 管辖的每个CPU的负载之和。仅当组的负载不均衡后,任务才在组之间发生迁移。 -在kernel/sched/core.c中,trigger_load_balance()在每个CPU上通过scheduler_tick() +在kernel/sched/core.c中,sched_balance_trigger()在每个CPU上通过sched_tick() 周期执行。在当前运行队列下一个定期调度再平衡事件到达后,它引发一个软中断。负载均衡真正 -的工作由run_rebalance_domains()->rebalance_domains()完成,在软中断上下文中执行 +的工作由sched_balance_softirq()->sched_balance_domains()完成,在软中断上下文中执行 (SCHED_SOFTIRQ)。 -后一个函数有两个入参:当前CPU的运行队列、它在scheduler_tick()调用时是否空闲。函数会从 +后一个函数有两个入参:当前CPU的运行队列、它在sched_tick()调用时是否空闲。函数会从 当前CPU所在的基调度域开始迭代执行,并沿着parent指针链向上进入更高层级的调度域。在迭代 过程中,函数会检查当前调度域是否已经耗尽了再平衡的时间间隔,如果是,它在该调度域运行 -load_balance()。接下来它检查父调度域(如果存在),再后来父调度域的父调度域,以此类推。 +sched_balance_rq()。接下来它检查父调度域(如果存在),再后来父调度域的父调度域,以此类推。 -起初,load_balance()查找当前调度域中最繁忙的调度组。如果成功,在该调度组管辖的全部CPU +起初,sched_balance_rq()查找当前调度域中最繁忙的调度组。如果成功,在该调度组管辖的全部CPU 的运行队列中找出最繁忙的运行队列。如能找到,对当前的CPU运行队列和新找到的最繁忙运行 队列均加锁,并把任务从最繁忙队列中迁移到当前CPU上。被迁移的任务数量等于在先前迭代执行 中计算出的该调度域的调度组的不均衡值。 diff --git a/Documentation/translations/zh_CN/scheduler/sched-stats.rst b/Documentation/translations/zh_CN/scheduler/sched-stats.rst index c5e0be663837..09eee2517610 100644 --- a/Documentation/translations/zh_CN/scheduler/sched-stats.rst +++ b/Documentation/translations/zh_CN/scheduler/sched-stats.rst @@ -75,42 +75,42 @@ domain<N> <cpumask> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 繁忙,新空闲): - 1) 当CPU空闲时,load_balance()在这个调度域中被调用了#次 - 2) 当CPU空闲时,load_balance()在这个调度域中被调用,但是发现负载无需 + 1) 当CPU空闲时,sched_balance_rq()在这个调度域中被调用了#次 + 2) 当CPU空闲时,sched_balance_rq()在这个调度域中被调用,但是发现负载无需 均衡#次 - 3) 当CPU空闲时,load_balance()在这个调度域中被调用,试图迁移1个或更多 + 3) 当CPU空闲时,sched_balance_rq()在这个调度域中被调用,试图迁移1个或更多 任务且失败了#次 - 4) 当CPU空闲时,load_balance()在这个调度域中被调用,发现不均衡(如果有) + 4) 当CPU空闲时,sched_balance_rq()在这个调度域中被调用,发现不均衡(如果有) #次 5) 当CPU空闲时,pull_task()在这个调度域中被调用#次 6) 当CPU空闲时,尽管目标任务是热缓存状态,pull_task()依然被调用#次 - 7) 当CPU空闲时,load_balance()在这个调度域中被调用,未能找到更繁忙的 + 7) 当CPU空闲时,sched_balance_rq()在这个调度域中被调用,未能找到更繁忙的 队列#次 8) 当CPU空闲时,在调度域中找到了更繁忙的队列,但未找到更繁忙的调度组 #次 - 9) 当CPU繁忙时,load_balance()在这个调度域中被调用了#次 - 10) 当CPU繁忙时,load_balance()在这个调度域中被调用,但是发现负载无需 + 9) 当CPU繁忙时,sched_balance_rq()在这个调度域中被调用了#次 + 10) 当CPU繁忙时,sched_balance_rq()在这个调度域中被调用,但是发现负载无需 均衡#次 - 11) 当CPU繁忙时,load_balance()在这个调度域中被调用,试图迁移1个或更多 + 11) 当CPU繁忙时,sched_balance_rq()在这个调度域中被调用,试图迁移1个或更多 任务且失败了#次 - 12) 当CPU繁忙时,load_balance()在这个调度域中被调用,发现不均衡(如果有) + 12) 当CPU繁忙时,sched_balance_rq()在这个调度域中被调用,发现不均衡(如果有) #次 13) 当CPU繁忙时,pull_task()在这个调度域中被调用#次 14) 当CPU繁忙时,尽管目标任务是热缓存状态,pull_task()依然被调用#次 - 15) 当CPU繁忙时,load_balance()在这个调度域中被调用,未能找到更繁忙的 + 15) 当CPU繁忙时,sched_balance_rq()在这个调度域中被调用,未能找到更繁忙的 队列#次 16) 当CPU繁忙时,在调度域中找到了更繁忙的队列,但未找到更繁忙的调度组 #次 - 17) 当CPU新空闲时,load_balance()在这个调度域中被调用了#次 - 18) 当CPU新空闲时,load_balance()在这个调度域中被调用,但是发现负载无需 + 17) 当CPU新空闲时,sched_balance_rq()在这个调度域中被调用了#次 + 18) 当CPU新空闲时,sched_balance_rq()在这个调度域中被调用,但是发现负载无需 均衡#次 - 19) 当CPU新空闲时,load_balance()在这个调度域中被调用,试图迁移1个或更多 + 19) 当CPU新空闲时,sched_balance_rq()在这个调度域中被调用,试图迁移1个或更多 任务且失败了#次 - 20) 当CPU新空闲时,load_balance()在这个调度域中被调用,发现不均衡(如果有) + 20) 当CPU新空闲时,sched_balance_rq()在这个调度域中被调用,发现不均衡(如果有) #次 21) 当CPU新空闲时,pull_task()在这个调度域中被调用#次 22) 当CPU新空闲时,尽管目标任务是热缓存状态,pull_task()依然被调用#次 - 23) 当CPU新空闲时,load_balance()在这个调度域中被调用,未能找到更繁忙的 + 23) 当CPU新空闲时,sched_balance_rq()在这个调度域中被调用,未能找到更繁忙的 队列#次 24) 当CPU新空闲时,在调度域中找到了更繁忙的队列,但未找到更繁忙的调度组 #次 diff --git a/Documentation/translations/zh_CN/security/IMA-templates.rst b/Documentation/translations/zh_CN/security/IMA-templates.rst new file mode 100644 index 000000000000..b391c9d03882 --- /dev/null +++ b/Documentation/translations/zh_CN/security/IMA-templates.rst @@ -0,0 +1,97 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/security/IMA-templates.rst + +:翻译: + 赵硕 Shuo Zhao <zhaoshuo@cqsoftware.com.cn> + +=============== +IMA模板管理机制 +=============== + + +介绍 +==== + +原始的 ``ima`` 模板是固定长度的,包含文件数据的哈希值和路径名。文件数据 +哈希值限制为20字节(md5/sha1)。路径名是一个以空字符终止的字符串,长度限 +制为255个字符内。 +为了克服这些限制并添加额外的文件元数据,通过定义额外的模板来扩展当前版本 +的IMA这是有必要的。例如,可能报告的信息包括索引节点的 UID/GID或索引节点 +及访问它进程的LSM标签。 + +然而,引入这个功能的主要问题是,每次定义一个新模板时,生成和显示度量列表 +的函数都需要包含处理新格式的代码,因此,这些函数的规模随着时间的推移会 +显著增长。 + +提出的解决方案通过将模板管理与其余IMA代码分离来解决这个问题。该解决方案 +的核心是定义两个新的数据结构:一个是模板描述符,用于确定度量列表中应包含 +哪些信息;另一个是模板字段,用于生成和显示给定类型的数据。 + +使用这些结构管理模板非常简单。为了支持一种新的数据类型,开发人员定义字段 +标识符,并实现两个函数,分别为init()和show(),用于生成和显示度量条目。 +定义一个新的模板描述符需要通过 ``ima_template_fmt`` 内核命令行参数指定 +模板格式(一个由 ``|`` 字符分隔的字段标识符字符串)。在启动时,IMA通过将格 +式转换从支持的模板字段集合中选取模板字段数组,来初始化所选的模板描述符。 + +在初始化步骤之后,IMA将调用 ``ima_alloc_init_template()`` (这是为新模板 +管理机制所打补丁中定义的新函数),通过使用在内核配置选择的模板描述符或者新引 +入的 ``ima_template`` 和 ``ima_template_fmt`` 内核命令行参数,生成一个新 +的度量条目。在这一阶段,新架构的优势得以清晰展示:后一个函数将不会包含处理给 +定模板的特定代码,而是简单地调用与所选模板描述符关联的模板字段的 ``init()`` +方法,并将结果(指向已分配数据的指针和数据长度)存储在度量条目结构中。 + +相同的机制也用于显示度量条目。函数 ``ima[_ascii]_measurements_show()`` 会为 +每个条目检索用于生成该条目的模板描述符,并为模板字段结构数组中的每一项调用show() +方法。 + + + +支持的模板字段和描述符 +====================== + +下面是支持的模板字段列表 ``('<identifier>': description)`` ,可以通过将其标识符 +添加到格式字符串中用于定义新的模板描述符(后续将添加对更多数据类型的支持): + + - 'd':事件的摘要(即测量文件的摘要),通过SHA1或MD5哈希算法计算; + - 'n':事件的名称(即文件名),大小至多255字节; + - 'd-ng':事件的摘要,通过任意哈希算法计算(字段格式:<hash algo>:digest); + - 'd-ngv2':与d-ng相同,但以"ima"或"verity"摘要类型为前缀 + (字段格式:<digest type>:<hash algo>:digest); + - 'd-modsig':不含附加modsig的事件摘要; + - 'n-ng':事件的名称,没有大小限制; + - 'sig':文件签名,基于文件的/文件系统验证的摘要[1],或EVM便携式签名, + 如果'security.ima'包含文件哈希; + - 'modsig':附加的文件签名; + - 'buf':用于生成哈希的缓冲区数据,没有大小限制; + - 'evmsig':EVM便携式签名; + - 'iuid':索引节点的UID; + - 'igid':索引节点的GID; + - 'imode':索引节点的模式; + - 'xattrnames':xattr名称的列表(由``|``分隔),仅当xattr存在时; + - 'xattrlengths':xattr长度的列表(u32),仅当xattr存在时; + - 'xattrvalues':xattr值的列表; + + +下面是已定义的模板描述符列表: + + - "ima":其格式是 ``d|n`` ; + - "ima-ng"(默认):其格式是 ``d-ng|n-ng`` ; + - "ima-ngv2":其格式是 ``d-ngv2|n-ng`` ; + - "ima-sig":其格式是 ``d-ng|n-ng|sig`` ; + - "ima-sigv2":其格式是 ``d-ngv2|n-ng|sig`` ; + - "ima-buf":其格式是 ``d-ng|n-ng|buf`` ; + - "ima-modsig":其格式是 ``d-ng|n-ng|sig|d-modsig|modsig`` ; + - "evm-sig":其格式是 ``d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode`` ; + + + +使用 +==== + +要指定用于生成度量条目的模板描述符,目前支持以下方法: + + - 从内核配置所支持模板描述符中选择一个模板描述符( ``ima-ng`` 是默认选择); + - 通过 ``ima_template=`` 参数从内核命令行指定模板描述符名称; + - 通过内核命令行参数 ``ima_template_fmt=`` 注册一个具有自定义格式的新模板描述符。 diff --git a/Documentation/translations/zh_CN/security/digsig.rst b/Documentation/translations/zh_CN/security/digsig.rst new file mode 100644 index 000000000000..3e690b504ec5 --- /dev/null +++ b/Documentation/translations/zh_CN/security/digsig.rst @@ -0,0 +1,103 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/security/digsig.rst + +:翻译: + 赵硕 Shuo Zhao <zhaoshuo@cqsoftware.com.cn> + +=============== +数字签名验证API +=============== + +:作者: Dmitry Kasatkin +:日期: 2011.06.10 + + +.. 内容 + + 1.介绍 + 2.API + 3.用户空间工具 + + +介绍 +==== + +数字签名验证API提供了一种验证数字签名的方法。 +目前,数字签名被IMA/EVM完整性保护子系统使用。 + +数字签名验证是通过精简的GnuPG多精度整数(MPI)库的内核移植来实现的。 +该内核版本提供了内存分配错误处理,已根据内核编码风格进行重构,并修复 +了checkpatch.pl报告的错误和警告。 + +公钥和签名由头部和MPIs组成:: + + struct pubkey_hdr { + uint8_t version; /* 密钥格式版本 */ + time_t timestamp; /* 密钥时间戳,目前为0 */ + uint8_t algo; + uint8_t nmpi; + char mpi[0]; + } __packed; + + struct signature_hdr { + uint8_t version; /* 签名格式版本 */ + time_t timestamp; /* 签名时间戳 */ + uint8_t algo; + uint8_t hash; + uint8_t keyid[8]; + uint8_t nmpi; + char mpi[0]; + } __packed; + +keyid等同对整个密钥的内容进行SHA1哈希运算后的第12到19字节。 +签名头部用于生成签名的输入。这种方法确保了密钥或签名头部无法更改。 +它保护时间戳不被更改,并可以用于回滚保护。 + +API +=== + +目前API仅包含一个函数:: + + digsig_verify() - 使用公钥进行数字签名验证 + + /** + * digsig_verify() - 使用公钥进行数字签名验证 + * @keyring: 查找密钥的密钥环 + * @sig: 数字签名 + * @sigen: 签名的长度 + * @data: 数据 + * @datalen: 数据的长度 + * @return: 成功时返回0,失败时返回 -EINVAL + * + * 验证数据相对于数字签名的完整性。 + * 目前仅支持RSA算法。 + * 通常将内容的哈希值作为此函数的数据。 + * + */ + int digsig_verify(struct key *keyring, const char *sig, int siglen, + const char *data, int datalen); + +用户空间工具 +============ + +签名和密钥管理实用工具evm-utils提供了生成签名、加载密钥到内核密钥环中的功能。 +密钥可以是PEM格式,或转换为内核格式。 +当把密钥添加到内核密钥环时,keyid定义该密钥的名称:下面的示例中为5D2B05FC633EE3E8。 + +以下是keyctl实用工具的示例输出:: + + $ keyctl show + Session Keyring + -3 --alswrv 0 0 keyring: _ses + 603976250 --alswrv 0 -1 \_ keyring: _uid.0 + 817777377 --alswrv 0 0 \_ user: kmk + 891974900 --alswrv 0 0 \_ encrypted: evm-key + 170323636 --alswrv 0 0 \_ keyring: _module + 548221616 --alswrv 0 0 \_ keyring: _ima + 128198054 --alswrv 0 0 \_ keyring: _evm + + $ keyctl list 128198054 + 1 key in keyring: + 620789745: --alswrv 0 0 user: 5D2B05FC633EE3E8 diff --git a/Documentation/translations/zh_CN/security/index.rst b/Documentation/translations/zh_CN/security/index.rst new file mode 100644 index 000000000000..d8aacd1930d9 --- /dev/null +++ b/Documentation/translations/zh_CN/security/index.rst @@ -0,0 +1,34 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/security/index.rst + +:翻译: + + 赵岳辉 Yuehui Zhao <zhaoyuehui@cqsoftware.com.cn> + +======== +安全文档 +======== + +.. toctree:: + :maxdepth: 1 + + lsm + sak + siphash + digsig + landlock + +TODOLIST: +* credentials +* snp-tdx-threat-model +* IMA-templates +* keys/index +* lsm-development +* SCTP +* self-protection +* tpm/index +* secrets/index +* ipe diff --git a/Documentation/translations/zh_CN/security/landlock.rst b/Documentation/translations/zh_CN/security/landlock.rst new file mode 100644 index 000000000000..53b449b637b2 --- /dev/null +++ b/Documentation/translations/zh_CN/security/landlock.rst @@ -0,0 +1,123 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. Copyright ? 2017-2020 Micka?l Salaün <mic@digikod.net> +.. Copyright ? 2019-2020 ANSSI +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/security/landlock.rst + +:翻译: + + 毛玉贤 Yuxian Mao <maoyuxian@cqsoftware.com.cn> + +====================== +Landlock LSM: 内核文档 +====================== + +:作者: Mickaël Salaün +:日期: 2022年12月 + +Landlock的目标是创建有范围的访问控制(即沙箱机制)。为了增强整个 +系统的安全性,此功能应适用于任何进程,包括非特权进程。因为这些进 +程可能被攻击或植入后门(即不可信任的),所以从内核和其他进程的角 +度来看,Landlock的功能必须安全可用。因此,Landlock 的接口设计应当 +尽量减少可能的攻击点。 + +Landlock 旨在为非特权进程使用,同时遵循由其他访问控制机制(例如 DAC、LSM) +强制执行的系统安全策略。Landlock 规则不应与系统上强制执行的其他访问 +控制相冲突,而只能添加额外的限制。 + +任何用户都可以在其进程上强制执行Landlock规则集。这些规则集会以一种 +确保只能添加更多约束的方式与继承的规则集合并和评估。 + + +用户空间文档如下: +Documentation/userspace-api/landlock.rst. + +安全访问控制机制指导原则 +======================== + +* Landlock规则应侧重于对内核对象的访问控制,而非系统调用过滤(即 + 系统调用参数),后者是seccomp-bpf要侧重的。 +* 为了避免多种侧信道攻击(例如安全策略泄露、基于CPU的攻击),Landlock + 规则不应与用户空间进行程序化通信。 +* 内核访问检查不应降低未沙盒化进程的访问请求速度。 +* 与 Landlock 操作相关的计算(例如强制执行规则集)应仅影响请求这些 + 操作的进程。 +* 由沙盒化进程直接从内核中获得的资源(例如文件描述符)在被任何进程 + 使用时,都应保留其获取时的作用域访问权限。 + Cf. `文件描述符访问权限`_. + +设计选择 +======== + +inode访问权限 +------------- + +所有访问权限都与inode以及通过inode所访问到的内容相关。读取目录的 +内容并不意味着有权读取该目录中列出的 inode 所对应文件的内容。实际 +上,文件名是相对于其父目录而言的 ,一个 inode 可以通过多个文件名 +(通过硬链接)来引用。删除文件链接这一操作仅直接影响对应目录,而 +对被移除链接的inode并无影响。这就是“LANDLOCK_ACCESS_FS_REMOVE_FILE” +(文件系统移除文件访问权限)或“LANDLOCK_ACCESS_FS_REFER” (文件系 +统引用访问权限)不能与文件绑定,而只能与目录绑定的原因。 + +文件描述符访问权限 +------------------ + +在打开文件时,会检查访问权限并将其与文件描述符绑定。其基本原则是, +当在相同的 Landlock 域下执行时,等效的操作序列会产生相同的结果。 +以LANDLOCK_ACCESS_FS_TRUNCATE权限为例,如果相关的文件层次结构没有 +授予该访问权限,那么可能允许打开一个文件进行写操作,但不允许使用 +ftruncate调用截断由此产生的文件描述符。 +以下操作序列具有相同的语义,因此会产生相同的结果: + +* ``truncate(path);`` +* ``int fd = open(path, O_WRONLY); ftruncate(fd); close(fd);`` + +与文件访问模式类似(例如O_RDWR),即使文件描述符在进程之间传递 +(例如通过Unix域套接字),文件描述符中的Landlock访问权限也会被保留。 +这些访问权限即使在接收进程未被 Landlock 沙盒化的情况下也会被强制执行。 +事实上,这是为了保持整个系统访问控制的一致性,避免通过文件描述符传递 +而无意中绕过安全限制(即“混淆代理攻击”)。 + +测试 +==== + +用户空间的向后兼容性测试、ptrace 限制测试和文件系统支持测试可以在这里 +找到:tools/testing/selftests/landlock/ + +内核结构 +======== + +对象 +---- + +该API在以下内核代码中: + +security/landlock/object.h + +文件系统 +-------- + +该API在以下内核代码中: + +security/landlock/fs.h + +规则集和域 +---------- + +域是与一组主体(即任务凭证)关联的只读规则集。每次在任务上执行规则集时, +都会复制当前域,并在新域中导入规则集作为新的规则层。 事实上,一旦进入 +一个域,每条规则都与一个层级绑定。要授予对对象的访问权限,每一层中至少 +有一条规则必须允许对该对象执行请求操作。然后,任务只能过渡到一个新的域, +该新域是当前域的约束和任务提供的规则集的约束的交集。任务自行沙盒化时, +主体的定义是隐式的,这使得推理变得更加简单,并有助于避免陷阱。 + +该API在以下内核代码中: + +security/landlock/ruleset.h + +.. Links +.. _tools/testing/selftests/landlock/: + +https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/landlock/ diff --git a/Documentation/translations/zh_CN/security/lsm.rst b/Documentation/translations/zh_CN/security/lsm.rst new file mode 100644 index 000000000000..e0ec4c7831da --- /dev/null +++ b/Documentation/translations/zh_CN/security/lsm.rst @@ -0,0 +1,95 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/security/lsm.rst + +:翻译: + + 赵岳辉 Yuehui Zhao <zhaoyuehui@cqsoftware.com.cn> + +================================ +Linux安全模块:Linux通用安全钩子 +================================ + +:作者: Stephen Smalley +:作者: Timothy Fraser +:作者: Chris Vance + +.. note:: + + 本文中描述的api已经过时了。 + +介绍 +==== + +在2001年3月,美国国家安全局(NSA)在2.5 Linux内核峰会上做了一个关于安全 +增强Linux(SELinux)的报告。SELinux是Linux内核中一种实现灵活且细粒度的非 +自主访问控制,最初作为自己特定的内核补丁实现。其他一些安全项目(例如RSBAC、 +Medusa)也为Linux内核开发了灵活的访问控制架构,并且多个项目为Linux开发了 +特定的访问控制模型(例如LIDS、DTE、SubDomain)。每个项目都开发并维护了自 +己的内核补丁,以支持其安全需求。 + +针对美国国家安全局的报告,Linus Torvalds发表了一系列言论,描述了一个他愿 +意考虑纳入主流Linux内核的安全框架。他描述了一个通用的框架,该框架将提供 +一组安全钩子来控制对内核对象的操作,同时在内核数据结构中提供一组不透明的 +安全域来维护安全属性。这个框架可以被可加载的内核模块用来实现任何所需的安 +全模型。Linus还提出了将Linux权能代码迁移到这样一个模块中的可能性。 + +Linux安全模块(LSM)项目是由WireX发起开发的这样一个框架。LSM是几个安全 +项目共同开发的成果,包括immununix、SELinux、SGI和Janus,以及包括 +Greg Kroah-Hartman和James Morris在内的几个人,来开发一个实现这一框架的 +Linux内核补丁。这项工作在2003年12月被纳入主流内核。此技术报告概述了该框 +架和权能安全模块的内容。 + +LSM框架 +======= + +LSM框架提供了一个通用的内核框架来支持安全模块。特别地,LSM框架主要关注 +支持访问控制模块,尽管未来的开发可能会解决其他安全需求,比如沙箱。就其 +本身而言,框架不提供任何额外的安全;它仅提供了支持安全模块的基础架构。 +LSM框架是可选的,要求开启 `CONFIG_SECURITY` 配置。权能逻辑作为一个安全 +模块被实现。该权能模块将在 `LSM权能模块`_ 一节中进一步讨论。 + +LSM框架在内核数据结构中包含安全域,并在内核代码的关键点调用钩子函数来 +管理这些安全域并执行访问控制。它还增加了注册安全模块的函数。接口 +/sys/kernel/security/lsm记录了一个以逗号分隔的安全模块列表,这些模块在 +系统中是激活的。 + +LSM安全域只是 ``void*`` 指针。数据被称为blob,这些数据可以由框架或使用 +它的各个安全模块进行管理。多个安全模块共同使用的安全blob通常由框架管理。 +对于进程和程序执行的安全信息,安全域包含在 :c:type: +`struct task_struct <task_struct>` 和 :c:type: `struct cred <cred>` 中。 +对于文件系统的安全信息,安全域包含在 :c:type: +`struct super_block <super_block>` 中。对于管道、文件和套接字的安全信息, +安全域包含在 :c:type: `struct inode <inode>` 和 :c:type: `struct file <file>` +中。对于System V IPC的安全信息,安全域被添加到 :c:type: +`struct kern_ipc_perm <kern_ipc_perm>` 和 :c:type: `struct msg_msg <msg_msg>` +中;另外,:c:type: `struct msg_msg <msg_msg>` 、struct msg_queue和 +struct shmid_kernel的定义被移动到头文件中( ``include/linux/msg.h`` 和 +``include/linux/shm.h`` 视情况而定),以允许安全模块使用这些定义。 + +对于数据包和网络设备的安全信息,安全域被添加到 :c:type: +`struct sk_buff <sk_buff>` 和 :c:type: `struct scm_cookie <scm_cookie>` 中。 +与其他安全模块数据不同,这里使用的数据是一个32位整数。安全模块需要将这些值 +进行映射或关联到真正的安全属性。 + +LSM钩子被维护在链表中。每个钩子函数都维护一个链表,这些钩子按照CONFIG_LSM中 +指定的顺序被调用。每个钩子的详细文档都包含在 `security/security.c` 源文件中。 + +LSM框架提供了一种近似通用的安全模块堆栈。它定义了security_add_hooks(),每个安 +全模块向它传递一个 :c:type: `struct security_hooks_list <security_hooks_list>` +,该结构会被添加到链表中。LSM框架没有提供移除已注册钩子的机制。SELinux安全 +模块已经实现了一种移除自身的方法,然而该特性已被弃用。 + +这些钩子可以分为两大类:用于管理安全域的钩子和用于执行访问控制的钩子。 +第一类钩子的示例包括security_inode_alloc()和security_inode_free(),这些 +钩子用于为inode对象分配和释放安全结构。第二类钩子的示例是 +security_inode_permission()钩子,该钩子在访问inode时检查权限。 + +LSM权能模块 +=========== + +POSIX.1e 权能逻辑作为一个安全模块维护,存储在文件 ``security/commoncap.c`` +中。权能模块使用 :c:type: `lsm_info` 描述中的order域来标识它为第一个注册 +的安全模块。与其他模块不同,权能安全模块不使用通用的安全blob。其原因是历史 +性的,主要基于开销、复杂性和性能的考虑。 diff --git a/Documentation/translations/zh_CN/security/sak.rst b/Documentation/translations/zh_CN/security/sak.rst new file mode 100644 index 000000000000..574fe076201b --- /dev/null +++ b/Documentation/translations/zh_CN/security/sak.rst @@ -0,0 +1,86 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/security/sak.rst + +:翻译: + + 张巍 zhangwei <zhangwei@cqsoftware.com.cn> + +=========================== +Linux 安全注意键(SAK)处理 +=========================== + +:日期: 2001年3月18日 +:作者: Andrew Morton + +操作系统的安全注意键是一种安全工具,用于防止系统上存在特洛伊 +木马密码捕获程序。它提供了一种无法规避的方式,用于终止所有可 +能伪装成登录应用程序的程序。用户需要在登录系统之前输入这个安 +全键。 + +从键盘输入的方式生成安全注意键,Linux提供了两种相似但不同的 +方式。一种是按下ALT-SYSRQ-K组合键,但你不应该使用这种方式, +因为它只有在内核启用了SYSRQ支持的情况下才能使用。 + +正确生成SAK的方式是使用``loadkeys``来定义键序列。无论内核是否 +编译了sysrq支持,这种方式都能够正常工作。 + +当键盘处于原始模式时,SAK 能够正常工作。这意味着,一旦定义, +SAK 将终止正在运行的 X 服务器。如果系统处于运行级别 5,X 服 +务器将重新启动,这正是你希望发生的情况。 + +你应该使用什么键序列? CTRL-ALT-DEL用于重启机器,CTRL-ALT- +BACKSPACE对X服务器有特殊作用。我们将选择CTRL-ALT-PAUSE。 + +在你的rc.sysinit(或rc.local)文件中,添加以下命令:: + + echo "Control Alt keycode 101 = SAK" | /bin/loadkeys + +就这样!只有超级用户才能重新编程SAK键。 + +.. note:: + + 1. Linux SAK据说并不是C2级安全性的系统所要求的"真正的SAK"。 + 该原因作者也不知道 + + 2. 在键盘输入的模式下,SAK会终止所有打开了/dev/console的应用 + 程序。 + + 但是不幸的是,这也包括一些你实际上不希望被终止的程序。原因是 + 这些程序错误的保持了/dev/console的打开状态。务必确保向你的 + Linux发行版提供商投诉这个问题。 + + 你可以用以下的命令来识别将被SAK终止的程序:: + + # ls -l /proc/[0-9]*/fd/* | grep console + l-wx------ 1 root root 64 Mar 18 00:46 /proc/579/fd/0 -> /dev/console + + 然后:: + + # ps aux|grep 579 + root 579 0.0 0.1 1088 436 ? S 00:43 0:00 gpm -t ps/2 + + 所以``gpm``会被SAK杀死。这应该gpm中的bug。它应该正在关闭标准输入, + 你可以通过查找initscript来启动gpm并更改它: + + 老的:: + + daemon gpm + + 新的:: + + daemon gpm < /dev/null + + Vixie cron似乎也有这个问题,并且需要采取相同的处理方式。 + + 此外,某个著名的Linux发行版在它的rc.sysinit和rc scripts的脚本中 + 包含了以下三行代码:: + + exec 3<&0 + exec 4>&1 + exec 5>&2 + + 这些代码会导致所有的守护进程将文件描述符3、4和5关联到/dev/console。 + 所以SAK会将他们所有都终止。一个简单的解决办法就是删掉这些代码,但是 + 这样做会导致系统管理应用程序出现异常 - 要对所有的情况进行充分测试。 diff --git a/Documentation/translations/zh_CN/security/siphash.rst b/Documentation/translations/zh_CN/security/siphash.rst new file mode 100644 index 000000000000..26dd8be699b3 --- /dev/null +++ b/Documentation/translations/zh_CN/security/siphash.rst @@ -0,0 +1,195 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst +:Original: Documentation/security/siphash.rst + +:翻译: + + 张巍 zhangwei <zhangwei@cqsoftware.com.cn> + +===================================== +SipHash - 一种短输入伪随机函数(PRF) +===================================== + +:作者: Jason A.Donenfeld <jason@zx2c4.com> + +SipHash是一种加密安全的伪随机函数,即一种用于生成伪随机密钥的哈 +希函数,因为其在处理短输入时表现出色,因此得名。其由密码学家 +Daniel J. Bernstein和Jean-Philippe Aumasson设计。目的主要是替 +代其他哈希函数,例如:jhash,md5_transform,sha1_transform等。 + +SipHash采用一个完全由随机数生成的密钥,以及一个输入缓冲区或者 +多个输入整数,它输出一个与随机数难以区分的整数,你可以将它作 +为安全序列、安全cookies的一部分,或者对其进行掩码处理,以便在 +哈希表中使用。 + +生成密钥 +======== + +密钥应来源于加密安全的随机数生成,要么使用get random bytes +要么使用get random once:: + + siphash_key_t key; + get_random_bytes(&key, sizeof(key)); + +如果你的密钥来源不是这两个,那么你的做法是错的。 + +使用函数 +======== + +这个函数有两个变种,一种是接受整数列表,另一种是接受缓冲区:: + + u64 siphash(const void *data, size_t len, const siphash_key_t *key); + +和:: + + u64 siphash_1u64(u64, const siphash_key_t *key); + u64 siphash_2u64(u64, u64, const siphash_key_t *key); + u64 siphash_3u64(u64, u64, u64, const siphash_key_t *key); + u64 siphash_4u64(u64, u64, u64, u64, const siphash_key_t *key); + u64 siphash_1u32(u32, const siphash_key_t *key); + u64 siphash_2u32(u32, u32, const siphash_key_t *key); + u64 siphash_3u32(u32, u32, u32, const siphash_key_t *key); + u64 siphash_4u32(u32, u32, u32, u32, const siphash_key_t *key); + +如果向一个通用的hsiphash函数传递一个恒定长度的常量,他将 +在编译的时候将常量折叠,并自动选择一个优化后的函数。 + +哈希表键函数的用法:: + + struct some_hashtable { + DECLARE_HASHTABLE(hashtable, 8); + siphash_key_t key; + }; + + void init_hashtable(struct some_hashtable *table) + { + get_random_bytes(&table->key, sizeof(table->key)); + } + + static inline hlist_head *some_hashtable_bucket(struct some_hashtable *table, struct interesting_input *input) + { + return &table->hashtable[siphash(input, sizeof(*input), &table->key) & (HASH_SIZE(table->hashtable) - 1)]; + } + +然后,你可以像往常一样对返回的哈希存储桶进行迭代。 + +安全性 +====== + +SipHash有着非常高的安全性,因为其有128位的密钥。只要密钥是保密的, +即使攻击者看到多个输出,也无法猜测出函数的正确输出,因为2^128次 +方个输出是非常庞大的。 + +Linux实现了SipHash的“2-4”变体 + +Struct-passing陷阱 +================== + +通常情况下,XuY函数的输出长度不够大,因此你可能需要传递一个预填充 +的结构体给SipHash,在这样做时,务必确保结构体没有填充空隙,最简单 +的方法就是将结构体的成员按照大小降序的方式排序,并且使用offsetofend() +函数代替sizeof()来获取结构体大小,出于性能的考虑,如果可以的话,最 +好将结构体按右边界对齐,示例如下:: + + const struct { + struct in6_addr saddr; + u32 counter; + u16 dport; + } __aligned(SIPHASH_ALIGNMENT) combined = { + .saddr = *(struct in6_addr *)saddr, + .counter = counter, + .dport = dport + }; + u64 h = siphash(&combined, offsetofend(typeof(combined), dport), &secret); + +资源 +==== + +如果你有兴趣了解更多信息,请阅读SipHash论文: +https://131002.net/siphash/siphash.pdf + +------------------------------------------------------------------------------- + +=========================================== +HalfSipHash 是 SipHash 的一个较不安全的变种 +=========================================== + +:作者: Jason A.Donenfeld <jason@zx2c4.com> + +如果你认为SipHash的速度不够快,无法满足你的需求,那么你可以 +使用HalfSipHash,这是一种令人担忧但是有用的选择。HalfSipHash +将SipHash的轮数从“2-4”降低到“1-3”,更令人担心的是,它使用一 +个容易被穷举攻击的64位密钥(输出为32位),而不是SipHash的128位 +密钥,不过,这对于要求高性能“jhash”用户来说这是比较好的选择。 + +HalfSipHash是通过 "hsiphash" 系列函数提供的。 + +.. warning:: + 绝对不要在作为哈希表键函数之外使用hsiphash函数,只有在你 + 能完全能确定输出永远不会从内核传输出去的情况下才能使用, + 作为缓解哈希表泛洪拒绝服务攻击的一种手段,它仅在某些情况 + 下比jhash好用。 + +在64位的内核中,hsiphash函数实际上实现的是SipHash-1-3,这是一 +种减少轮数的SipHash变形,而不是HalfSipHash-1-3。这是因为在64位 +代码中SipHash-1-3的性能与HalfSipHash-1-3相当,甚至可能更快,请 +注意,这并不意味这在64位的内核中,hsihpash函数与siphash函数相 +同,也不意味着他们是安全的;hsihash函数仍然使用一种不太安全的 +减少轮数的算法,并将输出截断为32位。 + +生成哈希密钥 +============ + +密钥应始终来源于加密安全的随机数生成,要么使用get random bytes +要么使用get random once:: + + hsiphash_key_t key; + get_random_bytes(&key, sizeof(key)); + +如果你的钥匙来源不是这两个,那么你的做法是错的。 + +使用哈希函数 +============ + +这个函数有两种变体,一个是接受整数列表,另一种是接受缓冲区:: + + u32 hsiphash(const void *data, size_t len, const hsiphash_key_t *key); + +和:: + + u32 hsiphash_1u32(u32, const hsiphash_key_t *key); + u32 hsiphash_2u32(u32, u32, const hsiphash_key_t *key); + u32 hsiphash_3u32(u32, u32, u32, const hsiphash_key_t *key); + u32 hsiphash_4u32(u32, u32, u32, u32, const hsiphash_key_t *key); + +如果向一个通用的hsiphash函数传递一个恒定长度的常量,他将在编译 +的时候将常量折叠,并自动选择一个优化后的函数。 + +哈希表键函数的用法 +================== + +:: + + struct some_hashtable { + DECLARE_HASHTABLE(hashtable, 8); + hsiphash_key_t key; + }; + + void init_hashtable(struct some_hashtable *table) + { + get_random_bytes(&table->key, sizeof(table->key)); + } + + static inline hlist_head *some_hashtable_bucket(struct some_hashtable *table, struct interesting_input *input) + { + return &table->hashtable[hsiphash(input, sizeof(*input), &table->key) & (HASH_SIZE(table->hashtable) - 1)]; + } + +然后,你可以像往常一样对返回的哈希存储桶进行迭代。 + +性能 +==== + +hsiphash()大约比jhash()慢三倍,这是因为有许多替换,不过这些都不是问题, +因为哈希表查找不是瓶颈。而且,这些牺牲是为了hsiphash()的安全性和DoS抗 +性,这是值得的。 diff --git a/Documentation/translations/zh_CN/subsystem-apis.rst b/Documentation/translations/zh_CN/subsystem-apis.rst index 47780bb0772f..8b646c1010be 100644 --- a/Documentation/translations/zh_CN/subsystem-apis.rst +++ b/Documentation/translations/zh_CN/subsystem-apis.rst @@ -88,6 +88,7 @@ TODOList: cpu-freq/index iio/index virt/index + security/index PCI/index peci/index @@ -102,7 +103,6 @@ TODOList: * watchdog/index * hwmon/index * accel/index -* security/index * crypto/index * bpf/index * usb/index diff --git a/Documentation/translations/zh_CN/virt/guest-halt-polling.rst b/Documentation/translations/zh_CN/virt/guest-halt-polling.rst index b798d1cf0b48..463d1d829062 100644 --- a/Documentation/translations/zh_CN/virt/guest-halt-polling.rst +++ b/Documentation/translations/zh_CN/virt/guest-halt-polling.rst @@ -76,7 +76,7 @@ guest_halt_poll_ns将保持高位)。 默认值: Y -模块参数可以从Debugfs文件中设置,在:: +模块参数可以从sysfs文件中设置,在:: /sys/module/haltpoll/parameters/ |