linux 之内核通知链

1 概念:在内核中,有很多的模块,这些模块之间是相互独立的,也有可能某个模块会对其他模块的某个事件感兴趣,这时候就需要将两个模块进行关联,让这两个模块进行通信。所以在linux内核中提供了通知链机制,通知只能用在内核模块之间,不可用在内核和应用空间进行事件的通信。通知链是一个函数链表,链表上的每一个节点都注册了一个函数,当某个事件发生时,链表上的所有节点都会被通知,所以,在通知事件时,所运行的函数由被通知方决定。

2 数据结构:内核中的通知链有四种类型

①原子通知链:通知链节点的回调函数(当事件发生时要执行的处理事件的函数)只能运行在中断上下文中,不允许阻塞。

struct automic notifier_head

{

    spinlock_tlock;

    struct notifer_block *head;

}

②可阻塞通知链:通知链节点的回掉函数运行在进程上下文中,允许阻塞

struct blocking_notifier_head 
{
    struct rw_semaphore rwsem;
    struct notifier_block *head;
};

③原始通知链( Raw notifier chains ):对通知链节点的回调函数没有任何限制,所有锁和保护机制都由调用者维护。对应的链表头:

struct raw_notifier_head 
{
    struct notifier_block *head;
};

④SRCU 通知链( SRCU notifier chains ):可阻塞通知链的一种变体。对应的链表头:

struct srcu_notifier_head 
{
    struct mutex mutex;
    struct srcu_struct srcu;
    struct notifier_block *head;
};

通知链的核心结构:

struct notifier_block 
{
    int (*notifier_call)(struct notifier_block *, unsigned long, void *);
    struct notifier_block *next;
    int priority;
};

其中notifier_call是通知链要执行的函数指针,它通过next指针形成了通知链节点的“队列”。next用来连接其它的通知结构,priority是这个通知的优先级,同一条链上的notifier_block{}是按优先级排列的。内核代码中一般把通知链命名为xxx_chain, xxx_nofitier_chain这种形的变量名。

3 机制

linux内核的通知链中,主要分为两个角色,通知者和被通知者。

通知者:事件的通知者。当某个是事件产生时,或者通知者自身产生事件时,通知所有对该事件感兴趣的模块。通知者定义了一个通知链,其中保存了每一个被通知者对事件的处理函数的回调(被通知者通过注册函数注册到通知者的通知链上的)

被通知者:对某个事件感兴趣的模块,该模块定义了事件发生时的回掉函数,通过通知者定义的注册函数,将回掉函数注册到通知者的通知链上的。该函数对自己感兴趣的事件进行处理。

通知链的使用主要分为3个步骤,其中通知者有2步,被通知者有1步。

①通知者:定义并初始化通知链,封装通知链的注册函数,注销函数以及通知函数

②被通知者:对事件感兴趣的模块,实现事件的处理函数,该函数中只需对自己感兴趣的事件进行处理,通过通知者提供的注册函数,将该事件处理函数注册到通知者的通知链上

③通知者:事件发生时,使用通知函数来调用被通知者提供的回掉函数,通知被通知者某个事件发生了。

需要注意的是,通知者通知事件时,会通知到注册到该通知链上的所有被通知者,通知时是不区分事件的,所以所有的被通知者都会被通知到,所以被通知者需要对事件进行过滤,只处理自己感兴趣的事件。

4应用举例

使用原始通知链进行举例

①通知者:定义并初始化通知链,提供注册函数,注销函数,通知函数

notifier1.c

#include <linux/kernel.h>

#include <linux/init.h>

#include <asm/uaccess.h>

#include <asm/irq.h>

#include <linux/types.h>

#include <linux/module.h>

#include <linux/notifier.h>

#include <linux/device.h>

/* 使用这个宏可以定义并初始化通知链test_chain,test_chain是一个通知链的头部节点,后续对通知链的操作都是针对该节点进行的。通过这个节点可以找到注册到这个节点中的其他所有的notifier_block(通过注册函数注册的回掉函数) */

static RAW_NOTIFIER_HEAD(test_chain)  ;

/* 自定义注册函数,该函数是封装的原始通知链的注册函数,将前面注册的头节点test_chain作为参数传递进去,这样当其他被通知者调用注册函数进行注册时,被通知者的notifier_block就被注册到这个test_chain通知链上了 */

int register_test_notifier(struct notifier_block *nb)

{

    return raw_notifier_chain_register(&test_chain, nb);

}

EXPORT_SYMBOL(register_test_notifier);

/* 注销函数,该函数也是对通知链test_chain进行操作的 */

int unregister_test_notifier(struct notifier_block *nb)

{

     return raw_notifier_chain_unregister(&test_chain, nb);

}

EXPORT_SYMBOL(unregister_test_chain);

/* 通知函数,该函数就是当通知者的事件发生时,调用的通知函数,该函数会通过通知链找到所有注册到该通知的回掉函数进行回调。其中的参数val,可以定义成事件类型,回掉函数根据该值进行事件的匹配,参数v可以传递缓存的地址 */

int test_notifier_call_chain(unsigned long val, void *v)

{

    return raw_notifier_call_chain(&test_chain, val, v);

}

EXPORT_SYMBOL(test_notifier_call_chain);

②被通知者

notifier2.c

#include <linux/kernel.h>

#include <linux/init.h>

#include <asm/uaccess.h>

#include <asm/irq.h>

#include <linux/types.h>

#include <linux/module.h>

#include <linux/notifier.h>

#include <linux/device.h>

extern int register_test_notifier(struct notifier_block *nb);

extern int unregister_test_notifier(struct notifier_block *nb);

/* 实现自己的回掉函数 */

int test_event1(struct notifier_block *nb, unsigned long event, void *ptr)

{

    switch(event)

      {

            case 1:

                    printk("test event 1\n");

                    break;

             default:

                     break;

       }

      return 0;

}

/* 实现自己的回掉函数 */

int test_event2(struct notifier_block *nb, unsigned long event, void *ptr)

{

    switch(event)

      {

            case 2:

                    printk("test event 2\n");

                    break;

             default:

                     break;

       }

      return 0;

}

定义事件的通知块,注册回调函数:

static struct notifier_block test_notifier1 =

{

    .notifier_call = test_event1,            /* 回调函数 */

}

static struct notifier_block test_notifier2 =

{

      .notifier_call = test_event2,            /* 回调函数 */

}

/* 被通知者的初始化函数,在该函数中注册通知链 */

int test_notifier_init(void)

{

    int iRet;

    iRet = register_test_notifier(&test_notifier1);

    if(0 != iRet)

    {

            printk("failed to register notifier 1\n");

    }

 

    iRet = register_test_notifier(&test_notifier2);

    if(0 != iRet)

    {

            printk("failed to register notifier 2\n");

    }

    return 0;

}

 

int test_notifier_exit(void)

{

    unregister_test_notifier(&test_notifier1);

    unregister_test_notifier(&test_notifier2);

}

③通知者

notifier3.c

#include <linux/kernel.h>

#include <linux/init.h>

#include <asm/uaccess.h>

#include <asm/irq.h>

#include <linux/types.h>

#include <linux/module.h>

#include <linux/notifier.h>

#include <linux/device.h>

extern test_notifier_call_chain(unsgined long val,void *v);

在通知者函数中,当事件发生时,直接调用通知函数就可以了。

test_notifier_call_chain(1, NULL);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值