Kernel之通知链
Kernel之通知链
背景
最近在调触摸屏,看代码时偶然看到了通知链相关的代码,加上之前刚好了解了一点通知链相关的东东,所以特此记录下。
在触摸屏驱动中的主要功能就是在开关屏的时候通知TP进行resume和suspend操作,大致的伪代码如下,以我看的elan的TP驱动为例:
...
/* 通知链的事件回调函数,收到通知TP进行resume和suspend操作 */
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
int *blank;
struct elan_ts_data *ts =
container_of(self, struct elan_ts_data, fb_notif);
if (evdata && evdata->data && event == FB_EVENT_BLANK && ts &&
ts->client) {
blank = evdata->data;
if (*blank == FB_BLANK_UNBLANK)
elan_ts_resume(&ts->client->dev);
else if (*blank == FB_BLANK_POWERDOWN)
elan_ts_suspend(&ts->client->dev);
}
return 0;
}
/*
在probe函数中注册fb client,而 fb_register_client()函数实现的其实就是通知链注册:
int fb_register_client(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&fb_notifier_list, nb);
}
在开关屏的时候会调用 fb_notifier_call_chain(FB_EVENT_BLANK, &event) 来触发通知:
int fb_notifier_call_chain(unsigned long val, void *v)
{
return blocking_notifier_call_chain(&fb_notifier_list, val, v);
}
//drivers/video/fbdev/core/fb_notify.c
*/
static int elan_ts_probe(...)
{
...
ts->fb_notif.notifier_call = fb_notifier_callback;
err = fb_register_client(&ts->fb_notif);
if (err)
dev_err(&client->dev, "[FB]Unable to register fb_notifier: %d",
err);
...
}
/* 在remove函数中注销fb client */
static int elan_ts_remove(struct i2c_client *client)
{
...
fb_unregister_client(&ts->fb_notif);
...
}
...
所以从上面的例子就可以很好的看出什么是通知链,为什么会有通知链?
Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notification chain)。
通知链只能用在各个子系统之间,而不能在内核和用户空间进行事件的通知。主要代码位于kernel/notifier.c
中,对应的头文件为include/linux/notifier.h
。
事件通知链表是一个事件处理函数的列表,每个通知链都与某个或某些事件有关,当特定的事件发生时,就调用相应的事件通知链中的回调函数,进行相应的处理。
类型
针对不同的使用场景,有以下4种通知链:
- 原始通知链
- 原子通知链
- 可阻塞通知链
- SRCU通知链
原始通知链
对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护
对应API:
/* 定义notifier head并静态初始化 */
#define RAW_NOTIFIER_HEAD(name) \
struct raw_notifier_head name = \
RAW_NOTIFIER_INIT(name)
/* 只动态初始化 */
#define RAW_INIT_NOTIFIER_HEAD(name) do { \
(name)->head = NULL; \
} while (0)
/* 注册 */
int raw_notifier_chain_register(struct raw_notifier_head *nh,
struct notifier_block *nb);
/* 注销 */
int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
struct notifier_block *nb);
/* 通知 */
int raw_notifier_call_chain(struct raw_notifier_head *nh,
unsigned long val, void *v);
原子通知链
通知链元素的回调函数(当事件发生时要执行的函数)在中断或原子操作上下文中运行,不允许阻塞
对应API:
/* 定义notifier head并静态初始化 */
#define ATOMIC_NOTIFIER_HEAD(name) \
struct atomic_notifier_head name = \
ATOMIC_NOTIFIER_INIT(name)
/* 只动态初始化 */
#define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \
spin_lock_init(&(name)->lock); \
(name)->head = NULL; \
} while (0)
/* 注册 */
int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
struct notifier_block *nb);
/* 注销 */
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
struct notifier_block *nb);
/* 通知 */
int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
unsigned long val, void *v);
可阻塞通知链
通知链元素的回调函数在进程上下文中运行,允许阻塞
对应API:
/* 定义notifier head并静态初始化 */
#define BLOCKING_NOTIFIER_HEAD(name) \
struct blocking_notifier_head name = \
BLOCKING_NOTIFIER_INIT(name)
/* 只动态初始化 */
#define BLOCKING_INIT_NOTIFIER_HEAD(name) do { \
init_rwsem(&(name)->rwsem); \
(name)->head = NULL; \
} while (0)
/* 注册 */
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *nb);
/* 注销 */
int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
struct notifier_block *nb);
/* 通知 */
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v);
SRCU通知链
可阻塞通知链的一种变体,且初始化与其他的有些差异
对应API:
/* 定义notifier head并静态初始化 */
#define SRCU_NOTIFIER_HEAD(name) \
_SRCU_NOTIFIER_HEAD(name, /* not static */)
#define SRCU_NOTIFIER_HEAD_STATIC(name) \
_SRCU_NOTIFIER_HEAD(name, static)
/* 动态初始化函数 */
/* srcu_notifier_heads must be cleaned up dynamically */
extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
#define srcu_cleanup_notifier_head(name) \
cleanup_srcu_struct(&(name)->srcu);
/* 注册 */
int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
struct notifier_block *nb);
/* 注销 */
int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
struct notifier_block *nb);
/* 通知 */
int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned long val, void *v);
实例
下面是个例子,使用的是原始通知链, 有部分是直接copy参考博文里面的代码,大致功能就是通过proc接口触发不同的通知链事件:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/notifier.h>
#define VERSION "0.1"
#define PROC_FILE_NAME "notifier-test"
#define EVENT_A 0x01
#define EVENT_B 0x02
static char notifier_call;
static RAW_NOTIFIER_HEAD(raw_chain_list); //define notifer chain list
static ssize_t notifier_test_write(struct file *filp, const char *userbuf, size_t count,
loff_t *ppos)
{
char buf[80];
int len = 0;
if(*ppos > 0 /*|| count > 80*/)
return -EFAULT;
count = min_t(size_t, count, (sizeof(buf)-1));
if (copy_from_user(buf, userbuf, count))
return -EFAULT;
if(sscanf(buf, "%hhd", ¬ifier_call) != 1)
return -EFAULT;
len = strlen(buf);
*ppos = len;
printk("notifier_test_write :%d... \n", notifier_call);
switch (notifier_call) {
case EVENT_A:
printk("raw_notifier_call_chain: EVENT_A!\n");
raw_notifier_call_chain(&raw_chain_list, EVENT_A, NULL);
break;
case EVENT_B:
printk("raw_notifier_call_chain: EVENT_B!\n");
raw_notifier_call_chain(&raw_chain_list, EVENT_B, NULL);
break;
default:
printk("notifier_test_write Error!\n");
break;
}
return len;
}
ssize_t notifier_test_read(struct file *filp, char *userbuf, size_t count, loff_t *ppos)
{
int len = 0;
char buf[64];
if(*ppos > 0 /*|| count < BUFSIZE*/)
return 0;
printk("notifier_test_read :%lld... \n", *ppos);
len += sprintf(buf, "%d\n", notifier_call);
if(copy_to_user(userbuf, &buf, len))
return -EFAULT;
*ppos = len;
return len;
}
// define callback function
int raw_notifer_callback(struct notifier_block *nb, unsigned long event, void *v)
{
switch (event) {
case EVENT_A:
printk("raw_notifer_callback running EVENT_A!\n");
break;
case EVENT_B:
printk("raw_notifer_callback running EVENT_B!\n");
break;
default:
break;
}
return NOTIFY_DONE;
}
// define notifier block
static struct notifier_block raw_notif = {
.notifier_call = raw_notifer_callback, //appoint notifier callback function
};
static int __init notifier_test_init(void) /*模块加载函数,通过insmod命令加载模块时,被自动执行*/
{
static struct proc_dir_entry *p;
printk("init notifier_test-module... \n");
p = proc_create(PROC_FILE_NAME, 0664, NULL,
/*(const struct proc_ops *)*/¬ifier_test_fops);
if (p == NULL)
printk("[notifier error] proc_create failed!!\n");
notifier_call = 0;
raw_notifier_chain_register(&raw_chain_list, &raw_notif);
return 0;
}
static void __exit notifier_test_exit(void) /*模块卸载函数,当通过rmmod命令卸载时,会被自动执行*/
{
printk("exit notifier_test-modules... \n");
raw_notifier_chain_unregister(&raw_chain_list, &raw_notif);
remove_proc_entry(PROC_FILE_NAME, NULL);
}
module_init(notifier_test_init);
module_exit(notifier_test_exit);
MODULE_LICENSE("GPL");
参考
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 DD'Notes!
评论