doc-guide/kernel-doc.rst

本文档翻译自sched-eevdf.rst,翻译时文件的最新提交是23a0bc28515934ed081169257f5b76167f07df4a doc-guide: kernel-doc: document Returns: spelling。大部分借助于ChatGPT翻译,仅作为我个人的参考,如果你想查阅,建议看英文文档,因为我不确定我记录的中文翻译是否完整和正确。

Kernel-doc 注释

1 编写 kernel-doc 注释

Linux 内核源文件可能包含 kernel-doc 格式的结构化文档注释,用于描述函数、类型和代码设计。当文档嵌入到源文件中时,更容易保持其最新。

Note

kernel-doc 格式看似与 javadoc、gtk-doc 或 Doxygen 相似,但由于历史原因,实际上有显著不同。内核源代码包含成千上万的 kernel-doc 注释。请坚持遵循此处描述的样式。

Note

kernel-doc 不涵盖 Rust 代码:请参阅 Documentation/rust/general-information.rst。

kernel-doc 结构从注释中提取,并生成带有锚点的 Sphinx C Domain 函数和类型描述。描述会经过过滤,突出显示 kernel-doc 特性并生成交叉引用。详情请参见下文。

每个通过 EXPORT_SYMBOLEXPORT_SYMBOL_GPL 导出的函数都应该有 kernel-doc 注释。头文件中用于模块的函数和数据结构也应该有 kernel-doc 注释。

为其他内核文件可见的函数(未标记为 static)提供 kernel-doc 格式的文档注释也是一种良好的实践。我们还建议为私有的(文件级别 static)例程提供 kernel-doc 格式的文档注释,以保持内核源代码布局的一致性。这是次要优先级,由该内核源文件的维护者自行决定。

1.1 如何格式化 kernel-doc 注释

开头的注释标记 /** 用于 kernel-doc 注释。kernel-doc 工具将提取此类标记的注释。其余部分的注释格式与普通的多行注释相同,左侧带有一列星号,最后以单独一行的 */ 结束。

函数和类型的 kernel-doc 注释应放在被描述的函数或类型之前,以最大化代码更改时也更改文档的机会。概述性的 kernel-doc 注释可以放在顶级缩进的任何位置。

可以通过增加详细程度并不实际生成输出来运行 kernel-doc 工具,以验证文档注释的正确格式。例如:

scripts/kernel-doc -v -none drivers/foo/bar.c

当请求执行额外的 gcc 检查时,内核构建将验证文档格式:

make W=n

1.2 函数文档

函数和类似宏的 kernel-doc 注释的一般格式为:

/**
 * function_name() - 函数的简要描述。
 * @arg1: 描述第一个参数。
 * @arg2: 描述第二个参数。
 *        可以为参数提供多行描述。
 *
 * 对 function_name() 函数进行更长的描述,讨论其使用或修改时的有用信息。以一个空的注释行开头,并可以包括额外的嵌入式空注释行。
 *
 * 更长的描述可以包含多个段落。
 *
 * Context: 上下文:描述函数是否会休眠,它获取、释放或期望持有的锁。这部分可以跨多行。
 * Return: 描述 function_name 的返回值。
 *
 * 返回值的描述也可以包含多个段落,并应放在注释块的末尾。
 */

紧随函数名后的简要描述可以跨多行,结束于参数描述、空的注释行或注释块的结尾。

1.2.1 函数参数

每个函数参数应按顺序描述,紧跟在简短的函数描述之后。不要在函数描述与参数之间,或参数之间留空行。

每个 @argument: 描述可以跨多行。

Note

如果 @argument 的描述有多行,描述的续行应与上一行保持相同的列对齐:

* @argument: 一些长描述
*            继续在接下来的几行中

或者:

* @argument:
*     一些长描述
*     继续在接下来的几行中

如果一个函数有可变数量的参数,应按 kernel-doc 记法书写描述:

* @...: 描述

1.2.2 函数上下文

函数可以被调用的上下文应在名为 Context 的部分中描述。这应包括函数是否休眠或可以从中断上下文中调用,以及它获取、释放和期望由调用者持有的锁。

示例:

* Context: 任何上下文。
* Context: 任何上下文。获取并释放 RCU 锁。
* Context: 任何上下文。期望 <lock> 由调用者持有。
* Context: 进程上下文。如果 @gfp 标志允许,可能会休眠。
* Context: 进程上下文。获取并释放 <mutex>。
* Context: 软中断或进程上下文。获取并释放 <lock>,BH 安全。
* Context: 中断上下文。

1.2.3 返回值

如果有返回值,应在专门的部分中描述,部分名称为 Return(或 Returns)。

Note

  1. 你提供的多行描述文本不识别换行符,因此如果你尝试像这样格式化一些文本:

这些文本将会连在一起,产生如下结果:

返回: 0 - OK -EINVAL - 无效参数 -ENOMEM - 内存不足

因此,为了产生所需的换行,你需要使用 ReST 列表,例如:

* Return:
* * %0        - 设备可以进行运行时挂起
* * %-EBUSY   - 设备不应进行运行时挂起
  1. 如果你提供的描述文本有一些行以某个短语开头,并以冒号结尾,每个短语将被视为新节标题,可能不会产生所需的效果。

1.3 结构、联合和枚举文档

结构、联合和枚举 kernel-doc 注释的一般格式为:

/**
 * struct struct_name - 简短描述。
 * @member1: 对 member1 的描述。
 * @member2: 对 member2 的描述。
 *           可以为成员提供多行描述。
 *
 * 对该结构的描述。
 */

在上面的示例中,您可以用 unionenum 替换 struct 来描述联合或枚举。member 用来表示结构体和联合体成员名以及枚举中的枚举项。

结构名称后的简短描述可以跨多行,并以成员描述、空的注释行或注释块的结束为止。

1.3.1 成员

结构体、联合体和枚举的成员应与函数参数的文档相同;它们紧随简短描述之后,并且可以是多行的。

在结构体或联合体描述中,您可以使用 private:public: 注释标签。位于 private: 区域内的结构字段不会在生成的输出文档中列出。

private:public: 标签必须紧随 /* 注释标记之后开始。它们可以选择性地在 : 和结束的 */ 标记之间包含注释。

示例:

/**
 * struct my_struct - 简短描述
 * @a: 第一个成员
 * @b: 第二个成员
 * @d: 第四个成员
 *
 * 更长的描述
 */
struct my_struct {
    int a;
    int b;
/* private: 仅供内部使用 */
    int c;
/* public: 下一个是公有的 */
    int d;
};

1.3.2 嵌套结构体/联合体

可以为嵌套的结构体和联合体编写文档,例如:

/**
 * struct nested_foobar - 一个带有嵌套联合体和结构体的结构
 * @memb1: 匿名联合体/结构体的第一个成员
 * @memb2: 匿名联合体/结构体的第二个成员
 * @memb3: 匿名联合体/结构体的第三个成员
 * @memb4: 匿名联合体/结构体的第四个成员
 * @bar: 非匿名联合体
 * @bar.st1: 联合体 bar 中的结构体 st1
 * @bar.st2: 联合体 bar 中的结构体 st2
 * @bar.st1.memb1: 联合体 bar 中结构体 st1 的第一个成员
 * @bar.st1.memb2: 联合体 bar 中结构体 st1 的第二个成员
 * @bar.st2.memb1: 联合体 bar 中结构体 st2 的第一个成员
 * @bar.st2.memb2: 联合体 bar 中结构体 st2 的第二个成员
 */
struct nested_foobar {
  /* 匿名联合体/结构体 */
  union {
    struct {
      int memb1;
      int memb2;
    };
    struct {
      void *memb3;
      int memb4;
    };
  };
  union {
    struct {
      int memb1;
      int memb2;
    } st1;
    struct {
      void *memb1;
      int memb2;
    } st2;
  } bar;
};

Note

  1. 在为嵌套结构体或联合体编写文档时,如果结构体/联合体 foo 有名字,里面的成员 bar 应当写为 @foo.bar:
  2. 当嵌套的结构体/联合体是匿名时,里面的成员 bar 应当写为 @bar:

1.3.3 行内成员文档注释

结构体成员也可以在定义中进行行内文档编写。有两种风格,一种是单行注释,开头的 /** 和结尾的 */ 在同一行,另一种是多行注释,它们分别位于各自的行上,像其他所有的 kernel-doc 注释一样:

/**
 * struct foo - 简短描述。
 * @foo: Foo 成员。
 */
struct foo {
      int foo;
      /**
       * @bar: Bar 成员。
       */
      int bar;
      /**
       * @baz: Baz 成员。
       *
       * 此处,成员描述可以包含多个段落。
       */
      int baz;
      union {
              /** @foobar: 单行描述。 */
              int foobar;
      };
      /** @bar2: @foo 内部结构体 @bar2 的描述 */
      struct {
              /**
               * @bar2.barbar: @foo.bar2 内部 @barbar 的描述
               */
              int barbar;
      } bar2;
};

1.4 类型定义文档

类型定义 kernel-doc 注释的一般格式为:

/**
 * typedef type_name - 简短描述。
 *
 * 类型的描述。
 */

带有函数原型的类型定义也可以进行文档编写:

/**
 * typedef type_name - 简短描述。
 * @arg1: 对 arg1 的描述
 * @arg2: 对 arg2 的描述
 *
 * 类型的描述。
 *
 * 上下文: 锁上下文。
 * 返回: 返回值的含义。
 */
 typedef void (*type_name)(struct v4l2_ctrl *arg1, void *arg2);

1.5 类似对象的宏文档

类似对象的宏与类似函数的宏不同。它们的区别在于宏名后是否紧跟一个左括号 ('(')。如果是类似函数的宏,会紧跟左括号,否则就是类似对象的宏。

类似函数的宏由 scripts/kernel-doc 处理。它们可能有一个参数列表。而类似对象的宏没有参数列表。

类似对象的宏 kernel-doc 注释的一般格式为:

/**
 * define object_name - 简短描述。
 *
 * 对该对象的描述。
 */

示例:

/**
 * define MAX_ERRNO - 支持的最大 errno 值
 *
 * 内核指针有冗余信息,因此我们可以使用一种方案,其中返回值可以是错误码或正常指针。
 */
#define MAX_ERRNO 4095

示例:

/**
 * define DRM_GEM_VRAM_PLANE_HELPER_FUNCS - \
 *    初始化用于 VRAM 处理的 struct drm_plane_helper_funcs
 *
 * 此宏初始化 struct drm_plane_helper_funcs 以使用相应的辅助函数。
 */
#define DRM_GEM_VRAM_PLANE_HELPER_FUNCS \
  .prepare_fb = drm_gem_vram_plane_helper_prepare_fb, \
  .cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb

1.6 重点和交叉引用

在 kernel-doc 注释描述文本中识别以下特殊模式,并将其转换为正确的 reStructuredText 标记和 Sphinx C Domain 引用。

下面的内容*仅在 kernel-doc 注释中识别,不*在普通的 reStructuredText 文档中识别。

funcname()

函数引用。

@parameter

函数参数名称。(无交叉引用,仅格式化。)

%CONST

常量名称。(无交叉引用,仅格式化。)

literal

应按原样处理的字面块。输出将使用等宽字体

如果需要使用特殊字符而这些字符在 kernel-doc 脚本或 reStructuredText 中具有某种含义时,这将非常有用。

特别是在函数描述中需要使用 %ph 之类的内容时,这非常有用。

$ENVVAR

环境变量名称。(无交叉引用,仅格式化。)

&struct name

结构体引用。

&enum name

枚举引用。

&typedef name

类型定义引用。

&struct_name->member&struct_name.member

结构体或联合体成员引用。交叉引用将指向结构体或联合体定义,而不是直接指向成员。

&name

泛型类型引用。建议优先使用上面描述的完整引用。这主要用于遗留注释。

1.6.1 从 reStructuredText 进行交叉引用

无需额外语法即可从 reStructuredText 文档中交叉引用 kernel-doc 注释中定义的函数和类型。 只需以 () 结尾函数名,并在类型前写上 structunionenumtypedef。 例如:

参见 foo()。
参见 struct foo。
参见 union bar。
参见 enum baz。
参见 typedef meh。

但是,如果需要在交叉引用链接中使用自定义文本,可以通过以下语法完成:

参见 :c:func:`函数 foo 的自定义链接文本 <foo>`。
参见 :c:type:`结构体 bar 的自定义链接文本 <bar>`。

有关更多详细信息,请参阅 Sphinx C Domain 文档。

1.7 概述文档注释

为了便于源代码和注释靠得更近,你可以包含 kernel-doc 文档块,这些块是自由格式的注释,而不是用于函数、结构体、联合体、枚举或类型定义的 kernel-doc。这可以用于例如驱动程序或库代码的操作理论等内容。

这是通过使用 DOC: 部分关键字和部分标题来实现的。

概述或高层文档注释的一般格式是:

/**
 * DOC: 操作理论
 *
 * whizbang foobar 是一个神奇的小玩意。它可以随时做你想做的任何事情。它能读取你的思想。以下是它的工作原理。
 *
 * foo bar splat
 *
 * 这个小玩意唯一的缺点是,它有时会损坏硬件、软件或其目标。
 */

DOC: 后面的标题在源文件中充当标题,同时也是提取文档注释的标识符。因此,标题在文件中必须唯一。

2 包含 kernel-doc 注释

可以使用专用的 kernel-doc Sphinx 指令扩展在任何 reStructuredText 文档中包含文档注释。

kernel-doc 指令的格式为:

.. kernel-doc:: source
   :option:

source 是源文件的路径,相对于内核源代码树。支持以下指令选项:

export: [source-pattern ...]

包含 source 中使用 EXPORT_SYMBOLEXPORT_SYMBOL_GPL 导出的所有函数的文档, 无论是在 source 还是 source-pattern 指定的文件中。

source-pattern 在内核文档注释放在头文件中,而 EXPORT_SYMBOLEXPORT_SYMBOL_GPL 位于函数定义旁时很有用。

示例:

.. kernel-doc:: lib/bitmap.c
   :export:

.. kernel-doc:: include/net/mac80211.h
   :export: net/mac80211/*.c
internal: [source-pattern ...]

包含 source 中*未使用 ``EXPORT_SYMBOL`` 或 ``EXPORT_SYMBOL_GPL`` 导出的所有函数和类型的文档, 无论是在source* 还是 source-pattern 指定的文件中。

示例:

.. kernel-doc:: drivers/gpu/drm/i915/intel_audio.c
   :internal:
identifiers: [ function/type ...]

包含 source 中每个 functiontype 的文档。 如果未指定 function,则包含 source 中所有函数和类型的文档。

示例:

.. kernel-doc:: lib/bitmap.c
   :identifiers: bitmap_parselist bitmap_parselist_user

.. kernel-doc:: lib/idr.c
   :identifiers:
no-identifiers: [ function/type ...]

排除 source 中每个 functiontype 的文档。

示例:

.. kernel-doc:: lib/bitmap.c
   :no-identifiers: bitmap_parselist
functions: [ function/type ...]

这是 'identifiers' 指令的别名,已被弃用。

doc: title

包含 source 中由 title 标识的 DOC: 段落的文档。 title 中允许有空格;不要引用 title。*title* 仅用作段落的标识符,不会包含在输出中。 请确保在封闭的 reStructuredText 文档中有适当的标题。

示例:

.. kernel-doc:: drivers/gpu/drm/i915/intel_audio.c
   :doc: HDMI 和 Display Port 上的高清音频

如果没有选项,kernel-doc 指令将包含源文件中的所有文档注释。

kernel-doc 扩展包含在内核源代码树中,位于 Documentation/sphinx/kerneldoc.py。 内部使用 scripts/kernel-doc 脚本从源文件中提取文档注释。

2.1 如何使用 kernel-doc 生成手册页

如果你只想使用 kernel-doc 生成手册页,可以从内核 git 树中执行以下操作:

$ scripts/kernel-doc -man \
  $(git grep -l '/\*\*' -- :^Documentation :^tools) \
  | scripts/split-man.pl /tmp/man

某些较旧版本的 git 不支持某些语法变体的路径排除。以下命令之一可能适用于这些版本:

$ scripts/kernel-doc -man \
  $(git grep -l '/\*\*' -- . ':!Documentation' ':!tools') \
  | scripts/split-man.pl /tmp/man

$ scripts/kernel-doc -man \
  $(git grep -l '/\*\*' -- . ":(exclude)Documentation" ":(exclude)tools") \
  | scripts/split-man.pl /tmp/man