写在前面的话:

刚入手Linux驱动的时候,各种Linux内核的机制是我们需要搬起的一块大砖块。搬砖的过程中,我发现自己不能很好的理解相关的业务驱动的很大的一个原因就是对内核的基本机制掌握不太准确。刚看内核机制代码的时候,需要借助百度上面的各种文档、结合自己的业务代码开展学习。后面有一定基础了,看内核源码、再看业务代码,最后看看网上的博客,效果我自己感觉还可以。通知链(notifier chain)是Display模块中,lcd(显示屏)的背光在lcd的唤醒和挂起是通知tp(触摸屏)的唤醒和挂起功能。是linux内核中比较重要的机制。

通知链(notifier chain)解决了什么问题?
Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。基于这个需求,内核产生了事件通知链机制(notification chain)。

源码解析:

源码目录:linux/drivers/video/fbdev/core/fb_notify.c

源码如下:

/**  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);/**1. fb_notifier_call_chain - notify clients of fb_events2.  */
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);

EXPORT_SYMBOL_GPL 这个宏是表示这个函数是在kernel其他地方使用的

源码总结:就三个函数:

int fb_register_client(struct notifier_block *nb)
int fb_unregister_client(struct notifier_block *nb)
int fb_notifier_call_chain(unsigned long val, void *v)

具体解析需要看个头文件:#include <linux/notifier.h>
相关机制通知链机制都在这个头文件里面,详细的文档在公众号菜单栏:资料下载——>驱动源码下载。
截取部分源码并解析:

typedef  int (*notifier_fn_t)(struct notifier_block *nb,unsigned long action, void *data);

//注释:
//内核使用struct notifier_block结构代表一个notifier

struct notifier_block {notifier_fn_t notifier_call;  //回调函数struct notifier_block __rcu *next;int priority;
};

回调函数的类型定义:
这个函数指针(notifier_fn_t)的返回值为int,
参数为(struct notifier_block *nb, unsigned long action, void *data)
使用typedef就是为了简单,重新定义了一个数据类型。


typedef  int (*notifier_fn_t)(struct notifier_block *nb,unsigned long action, void *data)
;

这是通知链的注册函数:从内核源码中看主要有四种通知链机制。分别为:
atomic_notifier_chain:原子通知链
通知链元素的回调函数(当事件发生时要执行的函数)在中断或原子操作上下文中运行,不允许阻塞。
blocking_notifier_chain:可阻塞通知链(Blocking notifier chains)通知链元素的回调函数在进程上下文中运行,允许阻塞。
raw_notifier_chain:原始通知链(Raw notifierchains)
对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护。
srcu_notifier_chain:SRCU 通知链(SRCU notifier chains)
可阻塞通知链的一种变体。
他们的数据存储的本质是链接,他们分别的链表的头如下:


struct atomic_notifier_head {spinlock_t lock;struct notifier_block __rcu *head;
};struct blocking_notifier_head {struct rw_semaphore rwsem;struct notifier_block __rcu *head;
};struct raw_notifier_head {struct notifier_block __rcu *head;
};struct srcu_notifier_head {struct mutex mutex;struct srcu_struct srcu;struct notifier_block __rcu *head;
};

step1:初始化一个内核通知链


#define ATOMIC_INIT_NOTIFIER_HEAD(name) do {  \spin_lock_init(&(name)->lock);  \(name)->head = NULL;    \} while (0)
#define BLOCKING_INIT_NOTIFIER_HEAD(name) do {  \init_rwsem(&(name)->rwsem);  \(name)->head = NULL;    \} while (0)
#define RAW_INIT_NOTIFIER_HEAD(name) do {  \(name)->head = NULL;    \} while (0)/* 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);

step2:注册一个内核通知链


extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,struct notifier_block *nb);
extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,struct notifier_block *nb);
extern int raw_notifier_chain_register(struct raw_notifier_head *nh,struct notifier_block *nb);
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,struct notifier_block *nb);

step3:发送一个事件到通知链上的notifier block,处理通知链


extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,unsigned long val, void *v);
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,unsigned long val, void *v);
extern int raw_notifier_call_chain(struct raw_notifier_head *nh,unsigned long val, void *v);
extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,unsigned long val, void *v);

step4:注销一个内核通知链:


blocking_notifier_chain_unregister(&fb_notifier_list, nb);
extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,struct notifier_block *nb
);
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,struct notifier_block *nb
);
extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,struct notifier_block *nb
);
extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,struct notifier_block *nb
);

实例:写个例子加深理解
//blocking_notifier_chains.c


//blocking_notifier_chains.c
#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/init.h"
#include "linux/notifier.h"#define EVENT_A 0x01
#define EVENT_B 0x02static BLOCKING_NOTIFIER_HEAD(blocking_notifier_list);int blocking_notifier_call_back(struct notifier_block *nb,unsigned long event, void *v)
{switch(event){case EVENT_A:printk("blocking_notifier_call_back is EVENT_A\n");break;case EVENT_B:printk("blocking_notifier_call_back is EVENT_B\n");break;default:break;}return 0;
}static struct notifier_block  blocking_notifier = {.notifier_call = blocking_notifier_call_back,
};static int __init blocking_notifier_init(void)
{blocking_notifier_chain_register(&blocking_notifier_list,&blocking_notifier);printk("blocking_notifier_chain_register end\n");blocking_notifier_call_chain(&blocking_notifier_list, EVENT_A, NULL);printk("blocking_notifier_call_chain EVENT_A\n");blocking_notifier_call_chain(&blocking_notifier_list, EVENT_B, NULL);printk("blocking_notifier_call_chain EVENT_B\n");return 0;
}
static void __exit blocking_notifier_exit(void)
{blocking_notifier_chain_unregister(&blocking_notifier_list, &blocking_notifier);
}module_init(blocking_notifier_init);
module_exit(blocking_notifier_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiao qiang");

//Makefile 文件:


//Makefile
ifeq ($(KERNELRELEASE),)KERNELDIR ?= /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)
modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules modules_install clean
elseobj-m := blocking_notifier_chains.o
endif

运行结果:

命令:make //如下图为编译成功


查看文件:ls

动态加载驱动:sudo insmod blocking_notifier_chains.ko

查看内核打印信息:dmesg

我的个人微信公众号为: Linux驱动,欢迎关注。

Linux内核源码——通知链(notifier chain)相关推荐

  1. Linux内核基础--事件通知链(notifier chain)【转】

    转自:http://blog.csdn.net/wuhzossibility/article/details/8079025 内核通知链 1.1. 概述 Linux内核中各个子系统相互依赖,当其中某个 ...

  2. iostat IO统计原理linux内核源码分析----基于单通道SATA盘

    iostat IO统计原理linux内核源码分析----基于单通道SATA盘 先上一个IO发送submit_bio流程图,本文基本就是围绕该流程讲解. 内核版本 3.10.96 详细的源码注释:htt ...

  3. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】...

    原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinauni ...

  4. ARM树莓派高级开发——linux内核源码、树莓派源码编译、SD卡挂载

    文章目录 linux内核开发基础(linux内核源码.树莓派源码编译.SD卡挂载) 树莓派等芯片带操作系统的启动过程 linux内核源码树 Linux内核源代码目录树结构: 树莓派Linux源码配置 ...

  5. 详细讲解Linux内核源码内存管理(值得收藏)

    Linux的内存管理是一个非常复杂的过程,主要分成两个大的部分:内核的内存管理和进程虚拟内存.内核的内存管理是Linux内存管理的核心,所以我们先对内核的内存管理进行简介. 一.物理内存模型 物理内存 ...

  6. Linux内核源码分析《进程管理》

    Linux内核源码分析<进程管理> 前言 1. Linux 内核源码分析架构 2. 进程原理分析 2.1 进程基础知识 2.2 Linux进程四要素 2.3 进程描述符 task_stru ...

  7. Linux内核源码中使用宏定义的若干技巧

    在C中,宏定义的概念虽然简单,但是真要用好却并不那么容易,下面从Linux源码中抽取一些宏定义的使用方法,希望能从中得到点启发: 1. 类型检查 比如module_init的宏定义: 点击(此处)折叠 ...

  8. 深入分析Linux内核源码oss.org.cn/kernel-book/

    本html页面地址:http://oss.org.cn/kernel-book/ 深入分析Linux内核源码 前言         第一章 走进linux 1.1 GNU与Linux的成长 1.2 L ...

  9. Redhat7.2上编译Linux内核源码

    下载linux源码包:https://git.kernel.org/pub/scm/virt/kvm/kvm.git/snapshot/kvm-4.17-1.tar.gz (这是包含kvm开发版本的l ...

最新文章

  1. 【JVM】肝了一周,吐血整理出这份超硬核的JVM笔记(升级版)!!
  2. Windows系统下MySQL安装详细教程(解决MySQL服务无法启动)
  3. python批量读取根目录下文件内容并保存
  4. android异步任务详解 AsynTask[转 杨瓦瓦]
  5. HDU5977 Garden of Eden 【FMT】【树形DP】
  6. C# MemoryStream先写后读的奇怪现象
  7. Spring表达式语言使用
  8. linux xampp图形界面,linux下安装xampp,XAMPP目录结构
  9. 2Y叔的clusterProfiler-book阅读Chapter 2 Functional Enrichment Analysis Methods
  10. 【转】Sobel 算子
  11. matlab fft没有误差,matlab仿真FFT结果幅值比实际的略有降低?
  12. 千月影视选集前端,这个可以H5多端
  13. 7.去空格函数-strip
  14. java不完全教程附编码示例
  15. python打包whl文件
  16. 网站空间和独享主机的区别是什么
  17. 实战案例 | Scrapy 集成Selenium爬取智联招聘数据
  18. An adaptive seismic signal denoising method based on variational mode decomposition 阅读笔记
  19. dw如何把html转换成网址,我用flash做的网页,怎么把它在dw里变成html网页?
  20. signature=5cb1209670cd93d97d200f9cc5f1d775,Tunable THz generalized Weyl points

热门文章

  1. 大三学生关于实习和考研的安排
  2. 读书笔记 摘自:《为什么精英都是时间控》
  3. r730xd外置光驱安装linux,PowerEdge r730xd 安装centos 6.7
  4. 用matlab求摆线,摆线-最速降曲线Matlab示例
  5. Jay的小迷弟-字符串溢出处理取模例题
  6. 使用阿里云对象存储oos遇到跨域访问的问题
  7. 上java培训班有用吗?Java有必要上培训班吗?
  8. 以太网交换机MAC地址表格式 IVL和SVL
  9. html dashed 属性,css虚线样式dotted和dashed
  10. 设备备件管理怎么做?