通知链跟踪方法:找注册通知链调用函数,看注册了哪些调用函数,则发出通知这些函数会被执行
参考文档
[1]内核通知链
[2]背光和fb涉及到的通知链
1 相关数据结构及说明
事件通知链用于内核各个子系统之间进行通信,核心代码位于kernel目录。
相关头文件定义在include/linux/notifier.h
代码实现在kernel/notifier.c
核心数据结构
typedef int (*notifier_fn_t)(struct notifier_block *nb, unsigned long action, void *data);
struct notifier_block {
notifier_fn_t notifier_call; /* 事件发生时要调用的函数 */
struct notifier_block __rcu *next; /* 形成通知链的指针 */
int priority; /* 事件发生时回调函数调用的优先级 */
};
说明:回调函数和优先级由被通知的子系统初始化
struct notifier_block 一般嵌入到子系统的结构中。
Linux内核中一般把通知链命名为 xxx_chain, xxx_nofitier_chain 等形式。
Linux定义了4种常用的通知链:
(1)原子通知链
struct atomic_notifier_head {
spinlock_t lock;
struct notifier_block __rcu *head;
};
通知链元素回调函数在中断或原子上下文中执行,不允许阻塞。
(2)可阻塞通知链
struct blocking_notifier_head {
struct rw_semaphore rwsem;
struct notifier_block __rcu *head;
};
通知链元素回调函数在进程上下文中执行,允许阻塞。
(3)原始通知链
struct raw_notifier_head {
struct notifier_block __rcu *head;
};
没有锁保护机制,需要由调用者维护
(4)SRCU通知链
struct srcu_notifier_head {
struct mutex mutex;
struct srcu_struct srcu;
struct notifier_block __rcu *head;
};
向事件通知链注册的步骤:
(1)申明struct notifier_block结构
(2)编写notifier_call函数
(3)调用特定的事件通知链注册函数,将notifier_block注册到通知链中
核心API
(1) 注册通知链
notifier_chain_register(struct notifier_block **nl, struct notifier_block *n);
(2) 通知事件(该函数中调用通知块中初始化的回调)
notifier_call_chain(struct notifier_block **nl, unsigned long val, void *v, int nr_to_call, int *nr_calls)
其他都是在这两个基础上封装而来
2 代码例子
2.1 reboot通知链例子
(1) 定义reboot通知链(定义在kernel/notifier.c)
BLOCKING_NOTIFIER_HEAD(reboot_notifier_list);
宏定义在include/linux/notifier.h,展开后等同于
struct blocking_notifier_head reboot_notifier_list = {
.rwsem = __RWSEM_INITIALIZER((reboot_notifier_list).rwsem),
.head = NULL
}
(2) 注册通知链(定义在kernel/reboot.c)
封装阻塞通知链注册函数为register_reboot_notifier()
register_reboot_notifier(struct notifier_block *nb)
-->blocking_notifier_chain_register(&reboot_notifier_list, nb);
}
定义通知块和回调。
bootinfo_reboot_notifier(struct notifier_block *nb, unsigned long action, void *p) {}
static struct notifier_block bootinfo_reboot_nb = {
.notifier_call = bootinfo_reboot_notifier,
};
core_initcall(bootinfo_init); // 在内核启动 core initcall 阶段调用
bootinfo_init(void)
-->register_reboot_notifier(&bootinfo_reboot_nb); // 注册通知块到通知链
(3) 通知事件,触发调用已注册的通知回调
kernel/reboot.c 中 reboot系统调用中发送通知事件
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)
-->kernel_restart(buffer);
-->kernel_restart_prepare(buffer);
-->blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer);
-->__blocking_notifier_call_chain(nh, val, v, -1, NULL);
-->notifier_call_chain((&nh->head)nl, val, v, -1, NULL);
-->*nl->notifier_call(*nl, val, v); // 实际这儿就会调用之前定义并注册的 bootinfo_reboot_notifier()
2.2 restart通知链
kernel/linux-4.19.87/kernel/reboot.c
(1) 定义通知链
static ATOMIC_NOTIFIER_HEAD(restart_handler_list);
该宏定义在include/linux/notifier.h
展开后等同于
static struct atomic_notifier_head restart_handler_list =
{
.lock = __SPIN_LOCK_UNLOCKED(restart_handler_list.lock),
.head = NULL
}
(2) 注册通知链
在另外的平台设备pwrseq_emmc的probe()中注册通知链
mmc_pwrseq_emmc_probe(struct platform_device *pdev)
-->(1)pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
-->(2)pwrseq->reset_nb.priority = 255;
-->(3)register_restart_handler(&pwrseq->reset_nb); // 注册通知块到通知链,通知块中有待触发的通知回调, 定义在 kernel/notifier.c +121
-->atomic_notifier_chain_register(&restart_handler_list, nb);
-->notifier_chain_register(&nh->head, n);
n->next = *nl; // 将第二个参数放到第一个参数代表的通知块头
定义待触发的回调函数
mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, unsigned long mode, void *cmd)
-->struct mmc_pwrseq_emmc *pwrseq = container_of(this, struct mmc_pwrseq_emmc, reset_nb);
-->gpiod_set_value(pwrseq->reset_gpio, 1);
(3) 发送事件,触发通知回调
kernel/linux-4.19.87/kernel/reboot.c
kernel_restart("recovery");
-->(3)machine_restart("recovery")
-->do_kernel_restart("recovery");
-->atomic_notifier_call_chain(&restart_handler_list, reboot_mode, "recovery");
-->__atomic_notifier_call_chain(&restart_handler_list, reboot_mode, "recovery", -1, NULL);
-->notifier_call_chain((&&restart_handler_list)->head, reboot_mode, "recovery", -1, NULL);
2.3 背光和fb使用的例子:
drivers/video/backlight/pwm_bl.c
drivers/video/backlight/backlight.c # 背光设备通用文件
定义静态通知链头
static struct blocking_notifier_head backlight_notifier;
注册通知链
backlight_device_register()
(1) 注册通知链
-->backlight_register_fb(struct backlight_device *bd);
-->bd->fb_notif.notifier_call = fb_notifier_callback;
-->fb_register_client(&bd->fb_notif); // 定义在 drivers/video/fbdev/core/fb_notify.c
-->blocking_notifier_chain_register(&fb_notifier_list, nb);
(2) 注册后调用一次通知链
-->blocking_notifier_call_chain(&backlight_notifier, BACKLIGHT_REGISTERED, new_bd);
注册函数的调用位置:
pwm_bl.c中注册平台设备module_platform_driver(pwm_backlight_driver);
调用probe函数
pwm_backlight_probe(struct platform_device *pdev)
-->backlight_device_register()
通知回调函数执行的动作:
fb_notifier_callback(struct notifier_block *self, event, void *data)
-->backlight_update_status(bd);
drivers/video/fb_notify.c 管理fb的通知链,定义了通知数据结构,封装了通知链注册,调用的方法:
通知数据结构定义:
static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
通知链注册:
fb_register_client(struct notifier_block *nb)
-->blocking_notifier_chain_register(&fb_notifier_list, nb);
调用通知链:
fb_notifier_call_chain(unsigned long val, void *v)
-->blocking_notifier_call_chain(&fb_notifier_list, val, v);
理解:背光作为fb的客户端,背光调节事件发生时,通知fb刷新。
使用方法:背光设备文件中调用注册方法注册通知回调,需要时调用通知回调
1、注册通知链 fb_register_client()
2、发起通知调用链 fb_notifier_call_chain()
其他说明:
0、用到通知链的还有fbcon、lcd
1、背光与fb的通知链
drivers/video/backlight/pwm_bl.c文件中的
struct pwm_bl_data {
…
int (*notify)(struct device *, int brightness);
void (*notify_after)(struct device , int brightness);
…
};
两个函数指针并没有使用, 而是使用通用背光设备的通知
struct backlight_device {
/ The framebuffer notifier block */
struct notifier_block fb_notif;
…
};
2、pwm背光与通用背光设备的数据关系
(1)pwm设备通过如下路径获得
bl_get_data(struct backlight_device bl_dev)
dev_get_drvdata(&bl_dev->dev); / dev_get_drvdata(const struct device *dev) drivers/base/dd.c */
dev->p->driver_data;
即 struct pwm_bl_data *pb = (struct backlight_device *)bl_dev->dev->p->driver_data;
即 pwm背光数据结构是通过通用背光设备struct backlight_device的成员结构dev中的设备私有数据(struct device_private *p;)中的驱动数据(void driver_data;)来传递的
(2)相关数据结构如下:
/ include/linux/device.h */
struct device {
struct device_private p;
…
};
/ drivers/base/base.h */
struct device_private {
void *driver_data;
…
};
3、sys文件系统
bl_power_store()函数会在写sys的bl_power文件时调用
本文详细介绍了Linux内核中的事件通知链机制,包括不同类型的链(原子、可阻塞、原始和SRCU),以及如何注册通知链、编写回调函数和触发事件。以reboot和restart通知链为例,展示了如何在内核重启和设备驱动中使用通知链进行通信。
2190

被折叠的 条评论
为什么被折叠?



