linux庞大系统中,各个模块是相对独立的,那么模块间通信该如何做呢?当然你也可以使用全局资源,如果这样的话系统缺少独立性,会带来稳定性问题的。如果你说,使用共享内存,进程通信等,那么你曲解我的意思了,因为你说的大多是user space的,而我说的是内核模块级别的。notifier_chain,对就是它,实质上这个机制就是一个回调函数链表的操作,回调函数的注册,注销,调用。源系统处(比如A子系统)进行定义初始化和回调函数调用,被通知处(比如B子系统)进行回调函数的注册和注销,那么当A系统发生某种事件是,就调用通知链中所有回调函数,B系统中注册的回调函数就会得到执行。一旦执行回调函数,它会从链表头依次执行每一个回调函数,那么依次执行是一次性全部执行完?执行过程的任意时间都可睡眠?这些需求也就产生了4种类型的notifier_chain。
结构体定义:
-
struct notifier_block {
/* chain的基本单位 */
-
int (*notifier_call)(struct notifier_block *,
unsigned
long,
void *);
-
struct notifier_block __rcu *next;
-
int priority;
-
};
-
-
struct atomic_notifier_head {
/* atmoic context; 执行(rcu_read_lock);回调函数执行不能阻塞;实时性高 */
-
spinlock_t lock;
-
struct notifier_block __rcu *head;
-
};
-
-
struct blocking_notifier_head {
/* process context;执行(rw_semaphore) ;回调函数执行可以阻塞;实时性相对低*/
-
struct rw_semaphore rwsem;
-
struct notifier_block __rcu *head;
-
};
-
-
struct raw_notifier_head {
/* 原始链表操作,注册,执行过程无任何保护,完全有驱动人员控制 */
-
struct notifier_block __rcu *head;
-
};
-
-
struct srcu_notifier_head {
/* process context;可阻塞通知链的变体(Sleepable Read-Copy-Update),回调函数执行可以阻塞 */
-
struct mutex mutex;
-
struct srcu_struct srcu;
-
struct notifier_block __rcu *head;
-
};
notifier_chain的API使用基本四大步骤:定义初始化、注册、调用和注销,而这些操作的基本单位是notifier_block,这个基本单位中包含了回调函数notifier_call,后继notifier_block指针,优先级priority(默认0,数字越大优先级越高,越靠近链表的头结点,越优先得到执行)。
初始化:
#include <linux/notifier.h>
-
#define ATOMIC_NOTIFIER_HEAD(name) \
-
struct atomic_notifier_head name = \
-
ATOMIC_NOTIFIER_INIT(name)
-
#define BLOCKING_NOTIFIER_HEAD(name) \
-
struct blocking_notifier_head name = \
-
BLOCKING_NOTIFIER_INIT(name)
-
#define RAW_NOTIFIER_HEAD(name) \
-
struct raw_notifier_head name = \
-
RAW_NOTIFIER_INIT(name)
-
/* srcu_notifier_heads must be initialized and 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);
经过定义及初始化后,一类的链表的头就形成了,注册就是增加节点,执行就是遍历节点,唯一要说明的就是,srcu链需要动态的定义,其他三中不需要。
注册和注销:
-
int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n);
-
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n)
-
int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n);
-
/* 注册的notifier_block不重复*/
-
int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *n);
-
int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *n);
-
int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n);
-
int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *n);
-
int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *n);
-
int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *n);
其中注册和注销都调用了最基本的函数如下:
-
/*
-
* Notifier chain core routines. The exported routines below
-
* are layered on top of these, with appropriate locking added.
-
*/
-
-
static int notifier_chain_register(struct notifier_block **nl,
-
struct notifier_block *n)
-
{
-
while ((*nl) !=
NULL) {
-
if (n->priority > (*nl)->priority)
-
break;
-
nl = &((*nl)->next);
-
}
-
n->next = *nl;
-
rcu_assign_pointer(*nl, n);
-
return
0;
-
}
-
-
static int notifier_chain_cond_register(struct notifier_block **nl,
-
struct notifier_block *n)
-
{
-
while ((*nl) !=
NULL) {
-
if ((*nl) == n)
-
return
0;
-
if (n->priority > (*nl)->priority)
-
break;
-
nl = &((*nl)->next);
-
}
-
n->next = *nl;
-
rcu_assign_pointer(*nl, n);
-
return
0;
-
}
-
-
static int notifier_chain_unregister(struct notifier_block **nl,
-
struct notifier_block *n)
-
{
-
while ((*nl) !=
NULL) {
-
if ((*nl) == n) {
-
rcu_assign_pointer(*nl, n->next);
-
return
0;
-
}
-
nl = &((*nl)->next);
-
}
-
return -ENOENT;
-
}
-
调用:
当A系统发生事件时,会调用下面函数之一的方法来,执行通知链中的所有回调函数,那么所有注册的地方都会得到执行,除前一个函数执行返回值含有NOTIFY_STOP_MASK标志。其中参数val是一个整形,用户自定义含义的传入值,参数v是一个void*类型,用户自定义任何类型含义,一般为平台结构体指针,使用的时候需要强制转换。
-
/*调用了__atomic_notifier_call_chain,且nr_to_call=-1表示回调函数调用个数不限,nr_calls=NULL表示不关心已执行的回调函数个数*/
-
extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
-
extern
int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
unsigned
long val,
void *v,
int nr_to_call,
int *nr_calls);
-
-
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);
-
<span style=
"white-space:pre"> </span>
extern
int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned
long val,
void *v,
int nr_to_call,
int *nr_calls);
-
-
extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);
-
extern
int __raw_notifier_call_chain(struct raw_notifier_head *nh,
unsigned
long val,
void *v,
int nr_to_call,
int *nr_calls);
-
-
extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);
-
extern
int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned
long val,
void *v,
int nr_to_call,
int *nr_calls);
回调函数返回值:
-
#define NOTIFY_DONE 0x0000 /* Don't care回调函数不关心返回值*/
-
#define NOTIFY_OK 0x0001 /* Suits me 回调函数调用顺利完成*/
-
#define NOTIFY_STOP_MASK 0x8000 /* Don't call further 回调函数链禁止继续调用的掩码*/
-
#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002)
-
/* Bad/Veto action 回调函数执行有错*/
-
/*
-
* Clean way to return from the notifier and stop further calls.当前顺利调用,禁止继续调用
-
*/
-
#define NOTIFY_STOP (NOTIFY_OK|NOTIFY_STOP_MASK)
在notifier_call_chain函数中去依次执行每一个注册的回调函数,并以前一个回调函数的返回值为判断依据,是否继续调用,最后一个执行函数的返回值作为notifier_call_chain的返回值。当前标准API中,只关注了NOTIFY_STOP_MASK,其他的在notifier_call_chain中未做处理。
典型用例:
在linux中,液晶显示器会提供一个fb_notify,当显示器发生某种事件时,会调用notifier_call_chain,这样注册的回调函数得到执行,回调函数根据显示的framebuffer状态执行你预想的动作。
它选用通知链类型的是blocking_notifier_chain。
-
/*
-
* linux/drivers/video/fb_notify.c
-
*
-
* Copyright (C) 2006 Antonino Daplas <adaplas@pol.net>
-
*
-
* 2001 - Documented with DocBook
-
* - Brad Douglas <brad@neruo.com>
-
*
-
* This file is subject to the terms and conditions of the GNU General Public
-
* License. See the file COPYING in the main directory of this archive
-
* for more details.
-
*/
-
#include <linux/fb.h>
-
#include <linux/notifier.h>
-
#include <linux/export.h>
-
-
/*静态定义并初始化通知链头*/
-
static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
-
-
/** 注册回调函数,加入一个回调函数节点
-
* 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);
-
}
-
EXPORT_SYMBOL(fb_register_client);
-
-
/** 注销回调函数,删除一个回调函数节点
-
* 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);
-
}
-
EXPORT_SYMBOL(fb_unregister_client);
-
-
/** 当源即framebuffer发生某种事件,调用该函数执行所有回调函数,通知其他子系统
-
* 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);
-
}
-
EXPORT_SYMBOL_GPL(fb_notifier_call_chain);
-
假如framebuffer为A子系统,触屏ft5x06为B子系统,现想要做到触屏伴随显示屏息屏而休眠,亮屏而唤醒。
B子系统(触屏)/被通知者,代码如下:
-
kernel\drivers\input\touchscreen\ft5x06_ts.c
-
-
#if defined(CONFIG_FB)
-
static int fb_notifier_callback(struct notifier_block *self,
-
unsigned
long event,
void *data)
-
{
-
struct fb_event *evdata = data;
-
int *blank;
-
struct ft5x06_ts_data *ft5x06_data =
-
container_of(
self,
struct
ft5x06_ts_data,
fb_notif);
-
/* 检测是否是显示器BLANK改变事件 */
-
if (evdata && evdata->data && event == FB_EVENT_BLANK &&
-
ft5x06_data && ft5x06_data->client) {
-
blank = evdata->data;
-
if (*blank == FB_BLANK_UNBLANK)
/*是BLANK事件中的LCD亮屏事件,唤醒触屏*/
-
ft5x06_ts_resume(&ft5x06_data->client->dev);
-
else
if (*blank == FB_BLANK_POWERDOWN)
/*是BLANK事件中的LCD灭屏事件,让触屏休眠 */
-
ft5x06_ts_suspend(&ft5x06_data->client->dev);
-
}
-
-
return
0;
-
}
-
#elif defined(CONFIG_HAS_EARLYSUSPEND)
-
-
//……
-
-
#endif
-
-
static int ft5x06_ts_probe(struct i2c_client *client,
-
const struct i2c_device_id *id)
-
{
-
-
//……
-
#if defined(CONFIG_FB)
-
data->fb_notif.notifier_call = fb_notifier_callback;
-
/*注册fb回调函数*/
-
err = fb_register_client(&data->fb_notif);
-
-
if (err)
-
dev_err(&client->dev,
“Unable to register fb_notifier: %d\n”,
-
err);
-
#endif
-
//……
-
-
}
-
static
int __
devexit ft5x06_ts_remove(struct i2c_client *client)
-
{
-
-
//……
-
#if defined(CONFIG_FB)
-
/*注销fb回调函数*/
-
if (fb_unregister_client(&data->fb_notif))
-
dev_err(&client->dev,
“Error occurred while unregistering fb_notifier.\n”);
-
#elif defined(CONFIG_HAS_EARLYSUSPEND)
-
unregister_early_suspend(&data->early_suspend);
-
#endif
-
-
//……
-
}
A子系统(framebuffer)/通知者,代码如下:
-
int fb_blank(struct fb_info *info, int blank)
-
{
-
int ret = -EINVAL;
-
-
if (blank > FB_BLANK_POWERDOWN)
-
blank = FB_BLANK_POWERDOWN;
-
-
if (info->fbops->fb_blank)
/*硬件执行亮屏还是灭屏的操作*/
-
ret = info->fbops->fb_blank(blank, info);
-
-
if (!ret) {
-
struct fb_event event;
-
-
event.info = info;
-
event.data = ␣
-
/*硬件BLANK操作成功后,调用所有的注册回调函数,比如通知给触屏*/
-
fb_notifier_call_chain(FB_EVENT_BLANK, &event);
-
}
-
-
return ret;
-
}
-
-
/*fbmem中的ioctl*/
-
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
-
unsigned
long arg)
-
{
-
-
//…….
-
case FBIOBLANK:
//由上层发下来的亮屏还是息屏的IO命令
-
if (!lock_fb_info(info))
-
return -ENODEV;
-
console_lock();
-
info->flags |= FBINFO_MISC_USEREVENT;
-
ret = fb_blank(info, arg);
-
info->flags &= ~FBINFO_MISC_USEREVENT;
-
console_unlock();
-
unlock_fb_info(info);
-
break;
-
//……
-
}
至于framebuffer中的notifier_call_chain的第二、三个参数,详见linux\fb.h,fbmem.c,fbcon.c。有时间也写个关于fb相关的文章。
</div>
</div>