给一个atomic_notifier_chain_register的用法示例

下面是一个 Linux 内核模块中使用 atomic_notifier_chain_register 的完整示例。该函数用于向原子通知链注册回调函数,适用于原子上下文(如中断处理程序):

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
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/interrupt.h>
#include <linux/atomic.h>

// 1. 定义原子通知链
static ATOMIC_NOTIFIER_HEAD(test_notifier_chain);

// 2. 定义通知块(notifier_block)结构体
static struct notifier_block test_nb = {
.notifier_call = test_event_handler, // 事件处理回调函数
.priority = 0, // 优先级(数值越大优先级越高)
};

// 3. 事件处理函数实现
static int test_event_handler(struct notifier_block *nb,
unsigned long event, void *data)
{
char *msg = (char *)data;

switch (event) {
case 0x01:
pr_info("处理事件 0x01: %s\n", msg);
break;
case 0x02:
pr_info("处理事件 0x02: %s\n", msg);
break;
default:
pr_info("未知事件: 0x%lx\n", event);
}
return NOTIFY_OK;
}

// 4. 触发通知的示例函数
static void fire_event(void)
{
char *msg = "事件已触发!";

// 发送事件 0x01 到通知链
atomic_notifier_call_chain(&test_notifier_chain, 0x01, msg);

// 发送事件 0x02 到通知链
atomic_notifier_call_chain(&test_notifier_chain, 0x02, msg);
}

// 模块初始化
static int __init notifier_init(void)
{
int ret;

pr_info("注册原子通知链\n");

// 5. 注册通知块
ret = atomic_notifier_chain_register(&test_notifier_chain, &test_nb);
if (ret) {
pr_err("注册失败: %d\n", ret);
return ret;
}

// 测试触发事件
fire_event();
return 0;
}

// 模块退出
static void __exit notifier_exit(void)
{
pr_info("注销通知链\n");

// 6. 注销通知块
atomic_notifier_chain_unregister(&test_notifier_chain, &test_nb);
}

module_init(notifier_init);
module_exit(notifier_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Atomic Notifier Chain 使用示例");

关键点解释:

  1. 定义原子通知链

    1
    static ATOMIC_NOTIFIER_HEAD(test_notifier_chain);

    使用内核宏声明并初始化一个原子通知链。

  2. 创建通知块

    1
    2
    3
    4
    static struct notifier_block test_nb = {
    .notifier_call = test_event_handler,
    .priority = 0,
    };
    • notifier_call: 事件处理函数指针
    • priority: 回调优先级(数值越大越先执行)
  3. 事件处理函数

    1
    static int test_event_handler(...)

    接收三个参数:

    • event: 事件标识(自定义数值)
    • data: 传递的数据指针(可强转为任意类型)
  4. 触发通知

    1
    atomic_notifier_call_chain(&test_notifier_chain, event_id, data_ptr);

    调用后所有注册的处理函数都会被执行。

  5. 注册/注销

    • 注册:atomic_notifier_chain_register()
    • 注销:atomic_notifier_chain_unregister()

使用场景:

  1. 中断处理:在原子上下文(如中断上半部)发送通知
  2. 硬件事件:处理器热事件、电源状态变化等
  3. 跨模块通信:解耦模块间的通知机制

实际输出示例(dmesg):

1
2
3
4
[  ] 注册原子通知链
[ ] 处理事件 0x01: 事件已触发!
[ ] 处理事件 0x02: 事件已触发!
[ ] 注销通知链

注意

  1. 原子通知链的回调函数不能睡眠(禁止阻塞操作)
  2. 优先级相同的回调按注册顺序执行
  3. 适用于需要高性能和原子安全性的场景

给一个atomic_notifier_chain_register的用法示例,回调函数支持带多个参数

带多参数的 atomic_notifier_chain_register 示例

下面是一个完整的内核模块示例,展示了如何在 atomic_notifier_chain_register 的回调函数中传递多个参数:

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/interrupt.h>
#include <linux/atomic.h>
#include <linux/slab.h> // 用于 kzalloc/kfree

// 1. 定义参数结构体(用于传递多个参数)
struct event_params {
int cpu_id; // CPU ID
unsigned long flags; // 标志位
char *message; // 消息字符串
unsigned int count; // 计数器
};

// 2. 定义原子通知链
static ATOMIC_NOTIFIER_HEAD(cpu_event_chain);

// 3. 定义通知块结构体
static struct notifier_block cpu_nb;

// 4. 事件处理函数实现(带多参数解析)
static int cpu_event_handler(struct notifier_block *nb,
unsigned long event, void *data)
{
// 将void指针转换为我们的参数结构体
struct event_params *params = (struct event_params *)data;

// 安全检查
if (!params) {
pr_warn("事件 %lu 没有参数数据!\n", event);
return NOTIFY_DONE;
}

switch (event) {
case 0x01: // CPU 上线事件
pr_info("CPU %d 上线: %s (标志: 0x%lx, 计数: %u)\n",
params->cpu_id, params->message, params->flags, params->count);
break;

case 0x02: // CPU 下线事件
pr_info("CPU %d 下线: %s (标志: 0x%lx, 计数: %u)\n",
params->cpu_id, params->message, params->flags, params->count);
break;

case 0x03: // CPU 错误事件
pr_err("CPU %d 错误: %s (标志: 0x%lx, 计数: %u)\n",
params->cpu_id, params->message, params->flags, params->count);
break;

default:
pr_info("未知事件 %lu: CPU=%d, 消息='%s'\n",
event, params->cpu_id, params->message);
}

return NOTIFY_OK;
}

// 5. 触发通知的函数(带多参数)
static void trigger_cpu_event(unsigned long event, int cpu_id,
unsigned long flags, const char *msg, unsigned int count)
{
// 动态分配参数结构体(在原子上下文中使用 GFP_ATOMIC)
struct event_params *params = kzalloc(sizeof(struct event_params), GFP_ATOMIC);

if (!params) {
pr_err("无法分配事件参数内存!\n");
return;
}

// 填充参数
params->cpu_id = cpu_id;
params->flags = flags;
params->message = (char *)msg; // 注意:字符串必须是常量或在其他地方管理生命周期
params->count = count;

// 发送事件到通知链
atomic_notifier_call_chain(&cpu_event_chain, event, params);

// 释放参数内存
kfree(params);
}

// 模块初始化
static int __init cpu_notifier_init(void)
{
int ret;

pr_info("CPU 通知模块初始化\n");

// 初始化通知块
cpu_nb.notifier_call = cpu_event_handler;
cpu_nb.priority = 0;

// 注册到通知链
ret = atomic_notifier_chain_register(&cpu_event_chain, &cpu_nb);
if (ret) {
pr_err("注册CPU通知链失败: %d\n", ret);
return ret;
}

// 测试触发多个事件
pr_info("\n触发测试事件:\n");

// CPU 0 上线
trigger_cpu_event(0x01, 0, 0xF0, "主处理器启动", 1);

// CPU 1 上线
trigger_cpu_event(0x01, 1, 0xE1, "辅助处理器启动", 2);

// CPU 2 错误
trigger_cpu_event(0x03, 2, 0xDEAD, "温度过高", 3);

// CPU 1 下线
trigger_cpu_event(0x02, 1, 0x0F, "正常关闭", 4);

return 0;
}

// 模块退出
static void __exit cpu_notifier_exit(void)
{
pr_info("CPU 通知模块退出\n");

// 注销通知块
atomic_notifier_chain_unregister(&cpu_event_chain, &cpu_nb);

pr_info("通知块已注销\n");
}

module_init(cpu_notifier_init);
module_exit(cpu_notifier_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux 内核开发者");
MODULE_DESCRIPTION("带多参数的原子通知链示例");
MODULE_VERSION("1.0");

关键点说明

1. 多参数传递机制

  • 创建参数结构体 event_params 封装所有需要传递的参数
  • 在触发事件时动态分配结构体并填充数据
  • 将结构体指针作为 void *data 传递给回调函数
  • 在回调函数中将 void * 转换回结构体指针

2. 内存管理

  • 使用 kzalloc(sizeof(struct event_params), GFP_ATOMIC) 动态分配内存
    • GFP_ATOMIC 确保在原子上下文中安全分配
  • 在通知处理完成后使用 kfree(params) 释放内存

3. 参数结构体设计

1
2
3
4
5
6
struct event_params {
int cpu_id; // 整数参数
unsigned long flags; // 标志位参数
char *message; // 字符串参数
unsigned int count; // 计数器参数
};
  • 支持多种类型参数组合
  • 可根据实际需求扩展

4. 安全注意事项

  • 检查 data 是否为 NULL 避免空指针解引用
  • 字符串使用常量或确保生命周期足够长
  • 在原子上下文中避免复杂操作

编译和测试方法

  1. 创建 Makefile:

    1
    2
    3
    4
    5
    6
    7
    obj-m += atomic_notifier_example.o

    all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

    clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  2. 编译模块:

    1
    make
  3. 加载模块:

    1
    sudo insmod atomic_notifier_example.ko
  4. 查看输出:

    1
    dmesg | tail -n 20

预期输出

1
2
3
4
5
6
[  ] CPU 通知模块初始化
[ ] 触发测试事件:
[ ] CPU 0 上线: 主处理器启动 (标志: 0xf0, 计数: 1)
[ ] CPU 1 上线: 辅助处理器启动 (标志: 0xe1, 计数: 2)
[ ] CPU 2 错误: 温度过高 (标志: 0xdead, 计数: 3)
[ ] CPU 1 下线: 正常关闭 (标志: 0xf, 计数: 4)

当卸载模块时:

1
2
[  ] CPU 通知模块退出
[ ] 通知块已注销

这个示例展示了如何在原子通知链中传递复杂的多参数数据,同时确保了原子上下文下的内存安全和操作安全。