Linux的notifier机制在TP中的应用

本文介绍了Linux内核中的通知链机制,详细解释了notifier_block结构体的作用及优先级设定,并通过实例展示了如何利用通知链机制实现触摸屏模块响应帧缓冲区子系统的屏幕开关事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在linux内核系统中,各个模块、子系统之间是相互独立的。Linux内核可以通过通知链机制来获取由其它模块或子系统产生的它感兴趣的某些事件。
notifier_block结构体在include/linux/notifier.h中定义:

struct notifier_block {
	notifier_fn_t notifier_call;
	struct notifier_block __rcu *next;
	int priority;
};
priority用来定义优先级,高优先级的处理例程将被优先执行,数值越大,优先级越高。
回到函数的原型定义:
typedef    int (*notifier_fn_t)(struct notifier_block *nb,
            unsigned long action, void *data);
TP属于输入子系统,可以通过获取framebuffer子系统来实现亮屏和灭屏时触发相应的事件。
fb_register_client和fb_unregister_client函数定义在drivers/video/fb_notify.c:
/**
 *	fb_register_client - register a client notifier
 *	@nb: notifier block to callback on events
 */
int fb_register_client(struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&fb_notifier_list, nb);
}

/**
 *    fb_unregister_client - unregister a client notifier
 *    @nb: notifier block to callback on events
 */
int fb_unregister_client(struct notifier_block *nb)
{
    return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
}
当framebuffer子系统发生事件时,调用notifier_call_chain()来触发相应的处理函数。
/**
 * fb_notifier_call_chain - notify clients of fb_events
 *
 */
int fb_notifier_call_chain(unsigned long val, void *v)
{
	return blocking_notifier_call_chain(&fb_notifier_list, val, v);
}
下面是一个实例:
struct msg21xx_ts_data {
	struct input_dev *input;
	struct hrtimer timer;
	struct work_struct work;
	int irq;
	struct dentry *dir;
	char *ts_info;
	u8 addr;
	int fw_major;
	int fw_minor;
#ifdef CONFIG_FB
	struct notifier_block fb_notif;
#endif
	bool suspended;
	struct i2c_client *client;
	struct regulator *vdd;
	struct regulator *vcc_i2c;
	struct msg21xx_platform_data *pdata;
	struct workqueue_struct *msg21xx_wq;
	struct mutex msg21xx_mutex;
};
probe函数中与notifier相关部分实现:
struct msg21xx_ts_data *data;

data = kzalloc(sizeof(struct msg21xx_ts_data), GFP_KERNEL);
if (!data) {
    dev_err(&client->dev, "%s: Alloc mem fail!", __func__);
    err = -ENOMEM;
    goto exit;
}

#ifdef CONFIG_FB
data->fb_notif.notifier_call = fb_notifier_callback;
err = fb_register_client(&data->fb_notif);
if (err)
    dev_err(&client->dev, "Unable to register fb_notifier: %d\n",
        err);
#endif
fb_notifier_callback实现:
#ifdef CONFIG_FB
static int fb_notifier_callback(struct notifier_block *self,
			unsigned long event, void *data)
{
	struct fb_event *evdata = data;
	int *blank;
	struct msg21xx_ts_data *msg21xx_data =
		container_of(self, struct msg21xx_ts_data, fb_notif);

	if (evdata && evdata->data && event == FB_EVENT_BLANK &&
			msg21xx_data && msg21xx_data->client) {
		blank = evdata->data;
		if (*blank == FB_BLANK_UNBLANK)
			msg21xx_ts_resume(&msg21xx_data->client->dev);
		else if (*blank == FB_BLANK_POWERDOWN)
			msg21xx_ts_suspend(&msg21xx_data->client->dev);
	}

	return 0;
}
#endif
msg21xx_ts_suspend和msg21xx_ts_resume实现如下,主要是操作TP的电源和RST脚,LCD灭屏时,为了降低系统的功耗,需要将TP的power关闭,同时将TP的复位脚拉低,让TP自身进入低功耗模式。
#if defined(CONFIG_PM) || defined(CONFIG_PM_RUNTIME)
static int msg21xx_ts_suspend(struct device *dev)
{
	struct msg21xx_ts_data *data = dev_get_drvdata(dev);
	int err;

	if (data->suspended) {
		dev_info(dev, "Already in suspend state\n");
		return 0;
	}

	disable_irq(data->client->irq);

	err = msg21xx_power_on(data, false);
	if (err)
		dev_err(dev, "power off failed");

	gpio_set_value_cansleep(data->pdata->reset_gpio, 0);

	data->suspended = true;
	return 0;
}

static int msg21xx_ts_resume(struct device *dev)
{
	struct msg21xx_ts_data *data = dev_get_drvdata(dev);
	int err;

	if (!data->suspended) {
		dev_dbg(dev, "Already in awake state\n");
		return 0;
	}

	err = msg21xx_power_on(data, true);
	if (err) {
		dev_err(dev, "power on failed");
		return err;
	}

	enable_irq(data->client->irq);
	gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
	data->suspended = false;
	return 0;
}
#endif

<think>好的,我现在需要回答用户关于Linux内核中通知链(Notifier Chain机制的问题,并且要举例说明应用场景。首先,我需要确认自己对通知链的理解是否正确。 通知链是Linux内核中用于实现事件通知的一种机制,允许不同模块在特定事件发生时进行通信。这应该涉及到观察者模式,即当某个事件发生时,所有注册的观察者都会收到通知。接下来,我需要明确几个关键点:通知链的基本结构、注册和注销过程、事件触发的流程,以及具体的应用例子。 首先,基本结构。通知链可能由结构体表示,比如struct notifier_block,每个通知块包含处理事件的回调函数。通知链本身可能是一个链表,维护所有注册的通知块。当事件发生时,遍历链表调用每个回调函数。 然后是注册和注销函数。内核应该提供接口,比如notifier_chain_register和notifier_chain_unregister,或者类似的函数,让其他模块可以将自己的通知块添加到链中或移除。 事件触发的流程可能涉及到调用notifier_call_chain函数,传递事件类型和相关参数,每个通知块根据优先级被调用,可能支持阻塞或非阻塞处理。 应用场景方面,需要举出几个具体的例子。比如网络子系统中的设备状态变化,例如网卡插拔;CPU热插拔事件;电源管理中的系统挂起或唤醒;文件系统挂载/卸载等。 现在需要检查是否遗漏了任何关键点。比如,优先级处理,通知链的类型(原子、可阻塞等),以及回调函数的返回值如何处理。此外,可能需要提到通知链的不同类型,如原子通知链、阻塞通知链、原始通知链等,这些在内核中可能有不同的实现和使用场景。 在举例时,要具体说明哪个子系统使用哪种通知链,比如网络设备使用notifier_chain来通知设备注册或注销,电源管理使用PM_NOTIFIER等。可能还需要提到具体的函数,如register_netdevice_notifier。 用户可能希望了解为什么需要这样的机制,它的优势是什么,比如解耦事件发布者和订阅者,提高模块化,降低耦合度等。 需要确保回答结构清晰,分点说明机制,然后举例应用场景。使用中文,避免技术术语的直译,保持口语化但准确。 最后,检查是否有错误,比如回调函数的返回值是否影响链的执行,比如NOTIFY_STOP或NOTIFY_OK的处理方式,以及通知链的初始化过程等。确保例子正确,比如网络设备通知链的具体应用情况。</think>Linux内核中的**通知链(Notifier Chain)**是一种典型的事件通知机制,实现了发布-订阅模式。以下是详细解析: --- ### **一、机制原理** #### 1. 核心结构 ```c struct notifier_block { int (*notifier_call)(struct notifier_block *, unsigned long, void *); struct notifier_block __rcu *next; int priority; // 优先级(数值越小优先级越高) }; ``` - **回调函数**:`notifier_call`是事件处理函数 - **优先级**:决定回调执行顺序(例如:`INT_MAX`表示最低优先级) #### 2. 关键操作 ```c // 注册通知块 int notifier_chain_register(struct notifier_block **nl, struct notifier_block *n); // 注销通知块 int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n); // 触发通知链 int __kprobes notifier_call_chain(struct notifier_block **nl, unsigned long val, void *v); ``` #### 3. 执行流程 ```text 事件触发 → 遍历通知链 → 按优先级调用回调 → 处理返回值(如NOTIFY_DONE) ``` --- ### **二、应用场景示例** #### 1. 网络设备状态变化 **场景**:网卡插拔、链路状态变化 **代码**: ```c // 注册网络设备通知链 register_netdevice_notifier(&my_netdev_notifier); // 通知块定义 static struct notifier_block my_netdev_notifier = { .notifier_call = netdev_event_handler, .priority = 0, }; // 回调实现 int netdev_event_handler(struct notifier_block *nb, unsigned long event, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); switch (event) { case NETDEV_REGISTER: printk("Device %s registered\n", dev->name); break; case NETDEV_UNREGISTER: printk("Device %s unregistered\n", dev->name); break; } return NOTIFY_DONE; } ``` #### 2. CPU热插拔 **场景**:CPU核心上线/下线 **实现**: ```c // 注册CPU通知链 cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE, "my_module:online", my_cpu_online, my_cpu_offline); // 回调示例 int my_cpu_online(unsigned int cpu) { printk("CPU %d is online\n", cpu); return 0; } ``` #### 3. 电源管理事件 **场景**:系统休眠/唤醒 **代码**: ```c // 注册PM通知链 pm_notifier(my_pm_notifier, 0); // 回调处理 static int my_pm_notifier(struct notifier_block *nb, unsigned long event, void *dummy) { switch (event) { case PM_SUSPEND_PREPARE: printk("System preparing to suspend\n"); break; case PM_POST_SUSPEND: printk("System resumed\n"); break; } return NOTIFY_OK; } ``` --- ### **三、设计特点** | 特性 | 说明 | |---------------------|----------------------------------------------------------------------| | **松耦合** | 事件发布者无需知道订阅者的存在 | | **多级优先级** | 通过`priority`字段控制回调执行顺序(例如:硬件相关处理优先) | | **返回值控制** | `NOTIFY_STOP`终止链传播,`NOTIFY_OK`继续执行后续回调 | | **类型安全** | 通过`void *`参数传递上下文数据(需类型转换) | --- ### **四、性能注意事项** 1. **原子性要求**:使用`ATOMIC_NOTIFIER_HEAD`定义的通知链需在原子上下文执行 2. **回调耗时**:避免在回调中进行耗时操作(可能阻塞事件传播) 3. **死锁风险**:注意锁的获取顺序,尤其是在嵌套通知场景中 --- ### **五、扩展应用** - **自定义事件**:通过`include/linux/notifier.h`定义新的事件类型 ```c #define MY_EVENT_FOO 0x0001 #define MY_EVENT_BAR 0x0002 ``` - **链嵌套**:允许在回调中触发其他通知链(需谨慎处理递归) 通过这种机制,内核实现了高效、灵活的事件驱动架构,是模块间通信的重要基础设施。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值