Linux内核源码——通知链(notifier chain)

写在前面的话:

刚入手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_events
 2.  */
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 0x02

static 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)/build
  PWD := $(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
else
        obj-m := blocking_notifier_chains.o
endif

运行结果:

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

在这里插入图片描述
查看文件:ls
在这里插入图片描述
动态加载驱动:sudo insmod blocking_notifier_chains.ko
在这里插入图片描述
查看内核打印信息:dmesg
在这里插入图片描述
我的个人微信公众号为: Linux驱动,欢迎关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值