linux notifier的理解和应用
notifier主要用于内核间的各个模块的通信
- (通知源)子系统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;
};
notifer_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);
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)//......#endifstatic 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。
linux notifier的理解和应用相关推荐
- Linux内核深入理解中断和异常(3):异常处理的实现(X86_TRAP_xx)
Linux内核深入理解中断和异常(3):异常处理的实现(X86_TRAP_xx) rtoax 2021年3月 /*** start_kernel()->setup_arch()->idt_ ...
- Linux内核深入理解定时器和时间管理(7):相关的系统调用
Linux内核深入理解定时器和时间管理 相关的系统调用 rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 结构体 ---------------------------- ...
- Linux内核深入理解定时器和时间管理(5):clockevents 框架
Linux内核深入理解定时器和时间管理 clockevents 框架 rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 1. Introduction to the cl ...
- Linux内核深入理解定时器和时间管理(4):定时器 timer
Linux内核深入理解定时器和时间管理 定时器 timer rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 1. Timers This is fourth part ...
- Linux内核深入理解定时器和时间管理(3):tick 广播 框架 和 dyntick
Linux内核深入理解定时器和时间管理 tick 广播 框架 和 dyntick rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 结构体 --------------- ...
- Linux内核深入理解定时器和时间管理(2):clocksource 框架
Linux内核深入理解定时器和时间管理 clocksource 框架 rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 全局部变量 ------------------- ...
- Linux内核深入理解定时器和时间管理(1):硬件时钟和jiffies
Linux内核深入理解定时器和时间管理 硬件时钟和jiffies rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 全局部变量 --------------------- ...
- Linux内核深入理解系统调用(3):open 系统调用实现以及资源限制(setrlimit/getrlimit/prlimit)
Linux内核深入理解系统调用(3) open 系统调用实现以及资源限制(setrlimit/getrlimit/prlimit) rtoax 2021年3月 对原文进行了5.10.13的代码分析. ...
- Linux内核深入理解系统调用(2):vsyscall 和 vDSO 以及程序是如何运行的(execve)
Linux内核深入理解系统调用(2) vsyscall 和 vDSO 以及程序是如何运行的(execve) rtoax 2021年3月 1. vsyscalls 和 vDSO 这是讲解 Linux 内 ...
最新文章
- 时间序列(五)股票分析
- matplotlib subplot画子图
- mac 命令行 解压7z文件_Mac 有哪些好用的压缩软件?
- Python中的[:n],[::n],[:,:,n],[...,n]介绍
- css选择器(css Selectors)的语法分析
- SpringCloud 入门教程(一): 服务注册
- matlab图像分割(肺实质)
- 矩形已知三个点的坐标,求第四个点的坐标
- 论机器学习中数据的重要性
- 电脑崩溃?黑客最爱邮件入侵方式,在双十一也要保护好网络安全!
- Robomaster小陀螺
- 蓝牙 WBS 宽带通话 (wide band speech) 剖析(二) -- BCCMD control chip
- Android实现歌词笔记构思
- 微信支付的回调函数实现验签以及解密
- 使用最小二乘法拟合由分段函数构成的广义贝塔分布的参数
- 《视觉SLAM十四讲 第二版》笔记及课后习题(第三讲)
- 请用python写一段代码:根据excel表中电费缴费时间及购电量计算每月用电量
- FME自动连接转换器
- MOS管栅极驱动电路
- android 点击爱心变色,Android 爱心万花筒简单实现
热门文章
- 牛客月赛14-养鸽场-(二分图+转化二进制01背包)
- Flutter 打包问题 Could not resolve io.flutter:arm64_v8a_release
- Be Better:遇见更好的自己-2016年记
- dasheddotted IE6爷爷居然认不出来的说……
- 2018最受欢迎测试工具
- Codesys配置电子凸轮连接虚轴
- gif一键抠图 在线_一款支持智能抠图、钢笔抠图的在线抠图工具上线了 速抠图 sukoutu.com...
- AD(altium designer)15原理图与PCB设计教程(七)——印制电路板的布局设计
- 中移动Open NFV实验室迎新合作伙伴
- 前端框架light7的使用体验