linux notifier的理解和应用

notifier主要用于内核间的各个模块的通信

  1. (通知源)子系统A进行定义初始化和回调函数的调用
  2. (被通知)子系统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;
};

notifer_chain的api使用的四大基本步骤,定义初始化,注册,调用和注销,而这些操作的基本单位是notifier_block,这个基本单位中包含了回调函数的notifier_call,后继notifier_block指针,优先级priority(默认0,数字越大优先级越大,越靠近链表的头节点,越优先得到执行)。

  1. 初始化
#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);
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);
 

假如framenbuffer为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中的notifie_call_chain的第二,第三个参数,详见linux\fb.h,fbmem.c,fbcon.c。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值