写入内核文档注释 【ChatGPT】

发布时间 2023-12-09 15:42:34作者: 摩斯电码

写入内核文档注释

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

注意
内核文档格式与javadoc、gtk-doc或Doxygen非常相似,但出于历史原因有着明显的区别。内核源代码包含数以万计的内核文档注释,请遵循此处描述的样式。

注意
内核文档不涵盖Rust代码:请参阅通用信息。

内核文档结构是从注释中提取的,并从中生成适当的Sphinx C域函数和类型描述以及锚点。这些描述经过特殊的内核文档突出显示和交叉引用的过滤。有关详细信息,请参见下文。

使用EXPORT_SYMBOL或EXPORT_SYMBOL_GPL导出到可加载模块的每个函数都应该有内核文档注释。头文件中用于模块使用的函数和数据结构也应该有内核文档注释。

为了保持内核源代码布局的一致性,建议为对其他内核文件可见的函数(未标记为static)提供内核文档格式的文档。我们还建议为私有(文件静态)例程提供内核文档格式的文档,但这是次要的,由该内核源文件的维护者自行决定。

如何格式化内核文档注释

内核文档注释使用开头的注释标记/**。内核文档工具将提取标记为此方式的注释。其余部分的注释格式与普通的多行注释相似,左侧有一列星号,最后一行以*/结束。

函数和类型的内核文档注释应该放在所描述的函数或类型之前,以最大程度地增加代码更改时同时更改文档的机会。概述性的内核文档注释可以放置在任何顶级缩进级别的位置。

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

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

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

make W=n

函数文档

函数和类似函数的宏内核文档注释的一般格式如下:

/**
 * function_name() - 函数的简要描述。
 * @arg1: 描述第一个参数。
 * @arg2: 描述第二个参数。
 *        可以为参数提供多行描述。
 *
 * 更长的描述,对于使用或修改该函数function_name()可能有用的更多讨论。以一个空的注释行开始,可以包括额外的嵌入式空的注释行。
 *
 * 更长的描述可以有多个段落。
 *
 * Context: 描述函数可以休眠的情况,它获取的锁,释放的锁,或者期望其调用者持有的锁。它可以跨多行。
 * Return: 描述function_name的返回值。
 *
 * 返回值描述也可以有多个段落,并且应该放在注释块的末尾。
 */

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

函数参数

每个函数参数应该按顺序描述,紧随简短的函数描述之后。不要在函数描述和参数之间留下空行,也不要在参数之间留下空行。

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

注意

如果@argument描述有多行,续行描述应该从与上一行相同的列开始:
* @argument: 一些很长的描述
*            它在下一行继续

或者:

* @argument:
*         一些很长的描述
*         它在下一行继续

如果一个函数有可变数量的参数,它的描述应该以内核文档符号写成:

* @...: 描述

函数上下文

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

示例:

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

返回值

如果有的话,返回值应该在一个名为返回的专用部分中描述。

注意

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

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

这些将会连在一起,产生:

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

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

* 返回值:
* * %0            - OK,可以运行挂起设备
* * %-EBUSY       - 设备不应该运行挂起

2. 如果您提供的描述文本有以某个短语开头然后跟着冒号的行,每个短语将被视为一个新的部分标题,这可能不会产生所需的效果。

结构、联合和枚举文档

结构、联合和枚举内核文档注释的一般格式如下:

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

您可以将上述示例中的struct替换为union或enum以描述联合或枚举。member用于表示结构和联合的成员名称以及枚举中的枚举值。

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

成员

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

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

private:和public:标记必须紧跟在/注释标记后面。它们可以选择在:和结束/标记之间包括注释。

示例:

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

嵌套结构和联合的文档注释

在内核代码中,可以对嵌套的结构和联合进行文档注释,如下所示:

/**
 * 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;
};

注意:

  • 当文档化嵌套的结构或联合时,如果结构/联合 foo 被命名,其中的成员 bar 应该被文档化为 @foo.bar。
  • 当嵌套的结构/联合是匿名的时,其中的成员 bar 应该被文档化为 @bar。

内联成员文档注释

结构体成员也可以在定义内联文档化。有两种风格,一种是单行注释,另一种是多行注释,就像其他内核文档注释一样。

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

Typedef 文档注释

typedef 的一般格式如下:

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

带有函数原型的 typedef 也可以进行文档化:

/**
 * typedef type_name - 简要描述
 * @arg1: 参数1的描述
 * @arg2: 参数2的描述
 *
 * 类型的描述。
 *
 * Context: 锁定上下文
 * Return: 返回值的含义
 */
typedef void (*type_name)(struct v4l2_ctrl *arg1, void *arg2);

突出显示和交叉引用

在内核文档注释的描述文本中,以下特殊模式会被识别并转换为适当的 reStructuredText 标记和 Sphinx C Domain 引用。

  • funcname():函数引用
  • @parameter:函数参数的名称(无交叉引用,只是格式化)
  • %CONST:常量的名称(无交叉引用,只是格式化)
  • literal:应当按原样处理的文字块,输出将使用等宽字体
  • $ENVVAR:环境变量的名称(无交叉引用,只是格式化)
  • &struct name:结构体引用
  • &enum name:枚举引用
  • &typedef name:typedef 引用
  • &struct_name->member 或 &struct_name.member:结构体或联合成员引用,交叉引用将指向结构体或联合定义,而不是直接的成员
  • &name:通用类型引用

从 reStructuredText 进行交叉引用

在 reStructuredText 文档中,要进行交叉引用内核文档注释中定义的函数和类型,只需在函数名后加上 (),并在类型前写上 struct、union、enum 或 typedef。例如:

See foo().
See struct foo.
See union bar.
See enum baz.
See typedef meh.

如果要在交叉引用链接中使用自定义文本,可以通过以下语法实现:

See :c:func:`my custom link text for function foo <foo>`.
See :c:type:`my custom link text for struct bar <bar>`.

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

概述文档注释

为了便于将源代码和注释放在一起,可以包含自由格式的内核文档文档块,而不是作为函数、结构、联合、枚举或 typedef 的内核文档。例如,可以用于驱动程序或库代码的操作理论。

使用 DOC: 关键字和部分标题的概述或高级文档注释的一般格式如下:

/**
 * DOC: Theory of Operation
 *
 * The whizbang foobar is a dilly of a gizmo. It can do whatever you
 * want it to do, at any time. It reads your mind. Here's how it works.
 *
 * foo bar splat
 *
 * The only drawback to this gizmo is that is can sometimes damage
 * hardware, software, or its subject(s).
 */

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

包含内核文档注释

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

kernel-doc 指令的格式如下:

.. kernel-doc:: source
   :option:

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

  • export: [source-pattern ...]:包含 source 中使用 EXPORT_SYMBOL 或 EXPORT_SYMBOL_GPL 导出的所有函数的文档。source-pattern 在内核源代码树中的头文件中放置了内核文档注释,而 EXPORT_SYMBOL 和 EXPORT_SYMBOL_GPL 则在函数定义旁边。
  • internal: [source-pattern ...]:包含 source 中未使用 EXPORT_SYMBOL 或 EXPORT_SYMBOL_GPL 导出的所有函数和类型的文档。
  • identifiers: [ function/type ...]:包含 source 中每个函数和类型的文档。如果未指定函数,则将包含 source 中所有函数和类型的文档。
  • no-identifiers: [ function/type ...]:排除 source 中每个函数和类型的文档。
  • functions: [ function/type ...]:这是 'identifiers' 指令的别名,已被弃用。
  • doc: title:包含 source 中由 title 标识的 DOC: 段落的文档。title 中允许包含空格,不需要引号。title 仅用作段落的标识符,不会包含在输出中。请确保在包含的 reStructuredText 文档中有适当的标题。

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

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

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

如果只想使用 kernel-doc 生成 man 手册页,可以在内核 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