[toc]

include/linux/once_lite.h: (一次性调用) 提供确保某段代码在多核环境下只被精确执行一次的宏

  • 每个宏仅在第一次使用时,执行函数功能.通过静态变量来实现一次性调用的功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* 调用一次函数。类似于 DO_ONCE(),但不通过jump label使用跳转标签修补。
*/
#define DO_ONCE_LITE(func, ...) \
DO_ONCE_LITE_IF(true, func, ##__VA_ARGS__)

//通过静态变量来实现一次性调用的功能
#define __ONCE_LITE_IF(condition) \
({ \
static bool __section(".data..once") __already_done; \
bool __ret_cond = !!(condition); \
bool __ret_once = false; \
\
if (unlikely(__ret_cond && !__already_done)) { \
__already_done = true; \
__ret_once = true; \
} \
unlikely(__ret_once); \
})

#define DO_ONCE_LITE_IF(condition, func, ...) \
({ \
bool __ret_do_once = !!(condition); \
\
if (__ONCE_LITE_IF(__ret_do_once)) \
func(__VA_ARGS__); \
\
unlikely(__ret_do_once); \
})

include/asm-generic/bug.h: 提供 BUG() 和 WARN_ON() 等用于内核断言和报告错误的宏

BUG

1
2
3
4
5
6
7
8
9
10
/* 除非真的没有出路,否则不要使用 BUG() 或 BUG_ON();一个示例可能是在无法退出的作过程中检测到数据结构损坏。 如果 (sub) 系统可以以某种方式继续运行,也许功能会减少,那么它可能就不值得 BUG。
* 如果你想 BUG(),请再想一想:完全放弃真的是*唯一*的解决方案吗? 通常有更好的选择,用户不需要尽快重启,并且大多数情况下可以干净地关闭。
*/
#ifndef HAVE_ARCH_BUG
#define BUG() do { \
printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
barrier_before_unreachable(); \
panic("BUG!"); \
} while (0)
#endif

BUG_ON

1
2
3
4
5
6
7
8
9
10
11
#ifndef HAVE_ARCH_BUG
#define BUG() do { \
printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
barrier_before_unreachable(); \
panic("BUG!"); \
} while (0)
#endif

#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
#endif

WARN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef WARN
#define WARN(condition, format...) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_printf(TAINT_WARN, format); \
unlikely(__ret_warn_on); \
})
#endif
#ifndef __WARN_FLAGS
#define __WARN() __WARN_printf(TAINT_WARN, NULL)
#define __WARN_printf(taint, arg...) do { \
instrumentation_begin(); \
warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \
instrumentation_end(); \
} while (0)
#else
#define __WARN() __WARN_FLAGS(BUGFLAG_TAINT(TAINT_WARN))
#define __WARN_printf(taint, arg...) do { \
instrumentation_begin(); \
__warn_printk(arg); \
__WARN_FLAGS(BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
instrumentation_end(); \
} while (0)
#define WARN_ON_ONCE(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_FLAGS(BUGFLAG_ONCE | \
BUGFLAG_TAINT(TAINT_WARN)); \
unlikely(__ret_warn_on); \
})
#endif

WARN_ON

WARN_ON_ONCE 打印一次警告

1
2
3
4
#ifndef WARN_ON_ONCE
#define WARN_ON_ONCE(condition) \
DO_ONCE_LITE_IF(condition, WARN_ON, 1)
#endif

include/linux/cleanup.h: 提供基于GCC attribute((cleanup)) 的自动资源清理机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* DOC:基于范围的清理辅助程序
*
* “goto error” 模式因引入细微的资源泄漏而臭名昭著。将新的资源获取约束添加到已经具有多个展开条件的代码路径中是乏味且容易出错的。“cleanup” 帮助程序使编译器能够帮助解决这种乏味问题,并可以帮助维护 LIFO (后进先出) 展开顺序,以避免意外泄漏。
*
* 由于驱动程序构成了内核代码库的大部分,下面是使用这些帮助程序清理 PCI 驱动程序的示例。清理的目标是在返回之前使用 goto 展开设备引用 (pci_dev_put()) 或解锁设备 (pci_dev_unlock()) 的情况。
*
* DEFINE_FREE() 宏可以安排在关联变量超出范围时删除 PCI 设备引用:
*
* DEFINE_FREE(pci_dev_put, struct pci_dev *, if (_T) pci_dev_put(_T))
* ...
* struct pci_dev *dev __free(pci_dev_put) =
* pci_get_slot(parent, PCI_DEVFN(0, 0));
*
* 如果 @dev 超出范围(自动变量范围)时@dev非 NULL,则上述代码将自动调用 pci_dev_put()。如果一个函数想在出错时调用 pci_dev_put(),但在成功时返回 @dev(即不释放它),它可以执行以下作:
*
* return no_free_ptr(dev);
*
* ...or::
*
* return_ptr(dev);
*
* DEFINE_GUARD() 宏可以安排在调用 guard() 的作用域结束时删除 PCI 设备锁:
*
* DEFINE_GUARD(pci_dev, struct pci_dev *, pci_dev_lock(_T), pci_dev_unlock(_T))
* ...
* guard(pci_dev)(dev);
*
* guard() 帮助程序获取的锁的生命周期遵循自动变量声明的范围。以以下示例为例:
*
* func(...)
* {
* if (...) {
* ...
* guard(pci_dev)(dev); // pci_dev_lock() invoked here
* ...
* } // <- implied pci_dev_unlock() triggered here
* }
*
* 观察锁是 “if ()” 块的剩余部分保持的,而不是 “func() ”的剩余部分。
*
* 现在,当函数同时使用 __free() 和 guard() 或 __free() 的多个实例时,变量定义顺序的 LIFO 顺序很重要。GCC 文档说:
*
* “当同一范围内的多个变量具有清理属性时,在退出范围时,其关联的清理函数将按定义(最后定义、第一次清理)的相反顺序运行。”
*
* 当展开顺序很重要时,它要求在函数范围中定义变量,而不是在文件顶部定义变量。 以以下示例为例,请注意 “!!”:突出显示的 bug:
*
* LIST_HEAD(list);
* DEFINE_MUTEX(lock);
*
* struct object {
* struct list_head node;
* };
*
* static struct object *alloc_add(void)
* {
* struct object *obj;
*
* lockdep_assert_held(&lock);
* obj = kzalloc(sizeof(*obj), GFP_KERNEL);
* if (obj) {
* LIST_HEAD_INIT(&obj->node);
* list_add(obj->node, &list):
* }
* return obj;
* }
*
* static void remove_free(struct object *obj)
* {
* lockdep_assert_held(&lock);
* list_del(&obj->node);
* kfree(obj);
* }
*
* DEFINE_FREE(remove_free, struct object *, if (_T) remove_free(_T))
* static int init(void)
* {
* struct object *obj __free(remove_free) = NULL;
* int err;
*
* guard(mutex)(&lock);
* obj = alloc_add();
*
* if (!obj)
* return -ENOMEM;
*
* err = other_init(obj);
* if (err)
* return err; // remove_free() called without the lock!!
*
* no_free_ptr(obj);
* return 0;
* }
*
* 通过将 init() 更改为调用 guard() 并按以下顺序定义 initialize @obj 来修复该错误:
*
* 守卫(互斥锁)(&lock);
* 结构体对象 *obj __free(remove_free) = alloc_add();
*
* 鉴于“__free(...)= NULL“模式会带来这种潜在的相互依赖关系问题,建议始终在一个语句中定义和分配变量,而不是在使用 __free() 时在函数顶部对变量定义进行分组。
*
* 最后,鉴于清理帮助程序的好处是删除了 “goto”,并且 “goto” 语句可以在范围之间跳转,因此预期 “goto” 和清理帮助程序的使用永远不会在同一函数中混合使用。即,对于给定例程,将所有需要 “goto” 清理的资源转换为基于范围的清理,或者不转换任何资源。
*/

__cleanup

__cleanup 是一个 GCC 提供的属性(attribute),用于在变量的作用域结束时自动调用指定的清理函数。它是一种基于作用域的资源管理机制,能够有效减少手动管理资源释放的复杂性,避免资源泄漏。


工作原理

  1. 定义清理函数:

    • 清理函数是一个普通的 C 函数,用于释放资源或执行其他清理操作。
    • 该函数的参数通常是一个指向需要清理的变量的指针。
  2. 声明变量时使用 __cleanup:

    • 在声明变量时,通过 __cleanup 属性指定清理函数。
    • 当变量超出其作用域时,编译器会自动调用指定的清理函数。
  3. 作用域结束时自动调用:

    • 当变量的作用域结束(如函数返回或代码块结束)时,清理函数会被自动调用,无需显式调用。

DEFINE_FREE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
* DEFINE_FREE(name, type, free):
* 简单的辅助宏,用于定义基于 __free() 的清理函数所需的包装器。@free 是使用 '_T' 访问变量的表达式。@free通常应在调用函数之前包含 NULL 测试,请参阅以下示例。
*
* __free(name):
* variable 属性向变量添加基于范围的清理。
*
* no_free_ptr(var):
* 就像非原子的 xchg(var, NULL) 一样,这样清理函数就会被禁止 —— 只要它理智地处理 NULL 值。
*
- 注意:这具有 __must_check 语义,因此更难意外泄漏资源。
*
* return_ptr(p):
* 返回 p,同时抑制 __free()。
*
* Ex.
*
* DEFINE_FREE(kfree, void *, if (_T) kfree(_T))
*
* void *alloc_obj(...)
* {
* struct obj *p __free(kfree) = kmalloc(...);
* if (!p)
* return NULL;
*
* if (!init_obj(p))
* return NULL;
*
* return_ptr(p);
* }
*
*注意:DEFINE_FREE() 的 @free 表达式包括 NULL 测试,即使 kfree() 可以使用 NULL 值调用。这是故意的。这样,编译器会将 alloc_obj() 函数的结尾视为:
*
* tmp = p;
* p = NULL;
* if (p)
* kfree(p);
* return tmp;
*
* 通过值传播和死代码消除的魔力,它消除了实际的清理调用并编译为:
*
* return p;
*
* 如果没有 NULL 测试,它会变得一团糟,编译器无法帮助我们。
*/

#define DEFINE_FREE(_name, _type, _free) \
static inline void __free_##_name(void *p) { _type _T = *(_type *)p; _free; }

#define __free(_name) __cleanup(__free_##_name)

no_free_ptr return_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* 获取指针 p 的当前值。
将指针 p 置空,防止后续误用。
强制调用者检查返回值,确保指针的正确使用。
返回值的类型与输入指针 p 的类型一致 */
#define __get_and_null(p, nullvalue) \
({ \
__auto_type __ptr = &(p); \
__auto_type __val = *__ptr; \
*__ptr = nullvalue; \
__val; \
})

static inline __must_check
const volatile void * __must_check_fn(const volatile void *val)
{ return val; }

#define no_free_ptr(p) \
((typeof(p)) __must_check_fn((__force const volatile void *)__get_and_null(p, NULL)))

#define return_ptr(p) return no_free_ptr(p)

CLASS

这段代码定义了一个宏 CLASS,用于简化特定类型对象的声明和管理,特别是在对象的构造和析构过程中。它结合了 GCC 的 __cleanup 属性和自定义的构造函数与析构函数,为对象的生命周期管理提供了一种自动化的机制。

宏的功能

  1. 参数说明:

    • _name: 表示对象的类型名称的一部分,用于生成具体的类型和函数名称。
    • var: 表示声明的变量名称。
  2. 生成的代码:

    • 宏会将 _name 拼接成完整的类型名称 class_##_name##_t,并将其作为变量 var 的类型。
    • 宏会将变量 var 与一个自定义的析构函数 class_##_name##_destructor 关联起来,通过 GCC 的 __cleanup 属性实现。当变量超出作用域时,class_##_name##_destructor 会被自动调用。
    • 宏会将变量 var 初始化为 class_##_name##_constructor 的返回值,表示在声明时自动调用构造函数。

工作原理

  • __cleanup 属性:
    GCC 的 __cleanup 属性允许在变量超出作用域时自动调用指定的函数。这种机制类似于 C++ 的析构函数,但可以在 C 语言中使用。

  • 构造函数和析构函数:
    宏假定存在两个函数:

    • class_##_name##_constructor: 用于初始化变量,返回类型为 class_##_name##_t
    • class_##_name##_destructor: 用于清理变量,参数为 class_##_name##_t 类型的指针。
  • 自动化管理:
    使用该宏声明的变量会在创建时自动调用构造函数,在超出作用域时自动调用析构函数,从而实现资源的自动管理。

使用场景

该宏适用于需要自动管理资源的场景,例如文件句柄、内存分配、锁等。通过这种方式,可以减少手动调用构造和析构函数的代码量,同时降低资源泄漏的风险。

示例

假设有以下构造函数和析构函数:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct {
int value;
} class_example_t;

class_example_t class_example_constructor() {
class_example_t obj = { .value = 42 };
return obj;
}

void class_example_destructor(class_example_t *obj) {
printf("Cleaning up: %d\n", obj->value);
}

可以使用 CLASS 宏如下:

1
2
3
CLASS(example, my_obj);
printf("Value: %d\n", my_obj.value);
// 当 `my_obj` 超出作用域时,会自动调用 `class_example_destructor`。

总结

CLASS 宏通过结合 GCC 的 __cleanup 属性和自定义的构造/析构函数,为 C 语言提供了一种类似于 C++ RAII(资源获取即初始化)的机制。它简化了资源管理的代码,同时提高了代码的安全性和可维护性,非常适合需要自动管理资源的场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
* DEFINE_CLASS(name, type, exit, init, init_args...):
* helper to define the destructor and constructor for a type.
* @exit is an expression using '_T' -- similar to FREE above.
* @init is an expression in @init_args resulting in @type
*
* EXTEND_CLASS(name, ext, init, init_args...):
* extends class @name to @name@ext with the new constructor
*
* CLASS(name, var)(args...):
* declare the variable @var as an instance of the named class
*
* Ex.
*
* DEFINE_CLASS(fdget, struct fd, fdput(_T), fdget(fd), int fd)
*
* CLASS(fdget, f)(fd);
* if (fd_empty(f))
* return -EBADF;
*
* // use 'f' without concern
*/

#define DEFINE_CLASS(_name, _type, _exit, _init, _init_args...) \
typedef _type class_##_name##_t; \
static inline void class_##_name##_destructor(_type *p) \
{ _type _T = *p; _exit; } \
static inline _type class_##_name##_constructor(_init_args) \
{ _type t = _init; return t; }

#define EXTEND_CLASS(_name, ext, _init, _init_args...) \
typedef class_##_name##_t class_##_name##ext##_t; \
static inline void class_##_name##ext##_destructor(class_##_name##_t *p)\
{ class_##_name##_destructor(p); } \
static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
{ class_##_name##_t t = _init; return t; }

#define CLASS(_name, var) \
class_##_name##_t var __cleanup(class_##_name##_destructor) = \
class_##_name##_constructor

#define DEFINE_LOCK_GUARD_1(_name, _type, _lock, _unlock, ...) \
__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \
__DEFINE_UNLOCK_GUARD(_name, _type, _unlock, __VA_ARGS__) \
__DEFINE_LOCK_GUARD_1(_name, _type, _lock)

scoped_guard

这段代码定义了两个宏 __scoped_guardscoped_guard,它们是用于实现一种基于作用域的资源管理机制(类似于 C++ 中的 RAII)。这些宏的主要目的是在特定作用域内自动管理资源(如锁的获取和释放),从而减少手动管理资源的复杂性和潜在的错误。

__scoped_guard 的功能

__scoped_guard 是一个底层的辅助宏,用于实现基于作用域的资源管理。它的核心逻辑是通过一个 for 循环和条件判断来确保资源在作用域结束时被正确释放。以下是它的关键部分:

  1. CLASS(_name, scope)(args)

    • 这是一个构造函数调用,创建了一个临时对象 scope,该对象的类型由 CLASS(_name, scope) 决定。
    • 这个对象通常会在构造函数中获取资源(如加锁),并在析构函数中释放资源(如解锁)。
  2. 循环条件

    • __guard_ptr(_name)(&scope):检查资源是否需要被管理(例如,锁是否已经被获取)。
    • !__is_cond_ptr(_name):确保对于无条件的资源管理(如无条件加锁),编译器不会跳过循环体。
    • 这两个条件的组合确保了资源管理逻辑的正确性。
  3. goto_label

    • goto _label 是一种跳转机制,用于在作用域结束时退出循环并触发资源释放。
    • _label 是一个唯一的标签,由调用者提供,用于标记循环的退出点。
  4. if (0)else

    • if (0) 的作用是确保循环体的代码不会被执行。
    • else 子句中包含调用者提供的代码,这些代码会在资源被正确管理的情况下执行。

scoped_guard 的功能

scoped_guard 是对 __scoped_guard 的封装,简化了调用方式。它通过 __UNIQUE_ID(label) 自动生成唯一的标签 _label,避免了手动指定标签的麻烦。

使用场景

这些宏通常用于以下场景:

  • 锁管理:在进入某个作用域时自动加锁,在离开作用域时自动解锁。
  • 资源清理:在作用域结束时自动释放资源(如文件句柄、内存等)。
  • 异常安全:确保即使在异常或错误路径中,资源也能被正确释放。

设计细节

  • 作用域管理:通过 for 循环和临时对象的生命周期,确保资源的获取和释放与作用域绑定。
  • 条件判断:通过 __guard_ptr__is_cond_ptr 的组合,支持条件和无条件的资源管理。
  • 灵活性:调用者可以通过 args 传递参数,灵活地配置资源管理的行为。

总结

这段代码实现了一种高效且灵活的基于作用域的资源管理机制,类似于 C++ 的 RAII 模式。它通过宏和临时对象的结合,简化了资源管理的代码,同时减少了手动管理资源可能引入的错误。这种设计在需要严格控制资源生命周期的场景(如多线程编程中的锁管理)中非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define __guard_ptr(_name) class_##_name##_lock_ptr
#define __is_cond_ptr(_name) class_##_name##_is_conditional

/*
* Helper macro for scoped_guard().
*
* Note that the "!__is_cond_ptr(_name)" part of the condition ensures that
* compiler would be sure that for the unconditional locks the body of the
* loop (caller-provided code glued to the else clause) could not be skipped.
* It is needed because the other part - "__guard_ptr(_name)(&scope)" - is too
* hard to deduce (even if could be proven true for unconditional locks).
*/
#define __scoped_guard(_name, _label, args...) \
for (CLASS(_name, scope)(args); \
__guard_ptr(_name)(&scope) || !__is_cond_ptr(_name); \
({ goto _label; })) \
if (0) { \
_label: \
break; \
} else

#define scoped_guard(_name, args...) \
__scoped_guard(_name, __UNIQUE_ID(label), args)

include/linux/err.h: 提供 IS_ERR()、PTR_ERR() 等用于处理和判断指针中编码的错误码

IS_ERR_VALUE 判断指针是否是错误指针

1
2
3
4
5
6
7
/**
* IS_ERR_VALUE - 检测错误指针。
* @x:要检查的指针。
*
* 与 IS_ERR() 类似,但如果 result 未使用,则不会生成编译器警告。
*/
#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)

ERR_PTR 将错误码转换为指针

1
2
3
4
5
6
7
8
9
10
11
12
/**
* ERR_PTR - 创建错误指针。
* @error:负错误代码。
*
* 将@error编码为指针值。用户应将结果视为不透明,并且不要对错误的编码方式做出任何假设。
*
* Return:在其值中编码了 @error 的指针。
*/
static inline void * __must_check ERR_PTR(long error)
{
return (void *) error;
}

include/linux/args.h: 提供用于处理可变参数的宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* 这些宏是如何工作的?
*
* 在 __COUNT_ARGS() 中,_0 到 _12 只是从头开始的占位符,
* 目的是确保 _n 正确地定位到从 12 到 0 的数字
* (取决于 X,这是一个可变参数列表)。
* 它们除了占据一个位置外没有其他用途。由于每个宏参数必须有一个唯一的标识符,
* 这些标识符就像其他标识符一样有效。
*
* 在 COUNT_ARGS() 中,我们使用实际的整数,因此 __COUNT_ARGS() 将其返回为 _n。
*/
/* This counts to 15. Any more, it will return 16th argument. */
#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _n, X...) _n
#define COUNT_ARGS(X...) __COUNT_ARGS(, ##X, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

/* 连接两个参数,但允许它们在之前进行扩展。 */
#define __CONCAT(a, b) a ## b
#define CONCATENATE(a, b) __CONCAT(a, b)