Linux内核基础--事件通知链

通知链跟踪方法:找注册通知链调用函数,看注册了哪些调用函数,则发出通知这些函数会被执行
参考文档
[1]内核通知链
[2]背光和fb涉及到的通知链

1 相关数据结构及说明

事件通知链用于内核各个子系统之间进行通信,核心代码位于kernel目录。
相关头文件定义在include/linux/notifier.h
代码实现在kernel/notifier.c
核心数据结构
struct notifier_block {
notifier_fn_t notifier_call; /* 事件发生时要调用的函数 */
struct notifier_block __rcu next; / 形成通知链的指针 /
int priority; /
事件发生时回调函数调用的优先级 */
};
typedef int (*notifier_fn_t)(struct notifier_block *nb, unsigned long action, void *data);
说明:回调函数和优先级由被通知的子系统初始化

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
#define BLOCKING_NOTIFIER_HEAD(name)    struct blocking_notifier_head name = BLOCKING_NOTIFIER_INIT(name)
(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);
(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); // 注册通知块到通知链,通知块中有待触发的通知回调
    -->atomic_notifier_chain_register(&restart_handler_list, nb);
定义待触发的回调函数
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()
–>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()
backlight_register_fb(struct backlight_device *bd)
bd->fb_notif.notifier_call = fb_notifier_callback;
fb_register_client(&bd->fb_notif);

fb_notifier_callback(struct notifier_block *self, event, void *data)
backlight_update_status(bd);
drivers/video/fb_notify.c管理fb的通知链,提供了注册方法 fb_register_client

用到该方法的有fbcon、lcd和backlight
发起通知:
fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);

使用方法:
1、注册通知链 fb_register_client
2、发起通知调用链 fb_notifier_call_chain

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;

};

另外
sys文件系统
bl_power_store()函数会在写sys的bl_power文件时调用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值