zoukankan      html  css  js  c++  java
  • Linux内核通知链(notifier chain)

    1. notifier chain概述

      Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,有时需要使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notification chain)。

      通知链只能用在各个子系统之间(当然一个子系统内部也可以使用),而不能在内核和用户空间进行事件的通知。

      事件通知链表是一个事件处理函数的列表,每个通知链都与某个或某些事件有关,当特定的事件发生时,就调用相应的事件通知链中的回调函数,进行相应的处理。


    2. 实现代码位置

    组成内核的核心系统代码均位于kernel目录下,通知链相关函数位于kernel/notifier.c中,对应的头文件为include/linux/notifier.h。


    3. 通知链类型

    (1) 原子通知链

    通知链元素的回调函数必须能在中断或原子操作上下文中运行,不允许阻塞。对应的链表头结构:

    struct atomic_notifier_head {  
        spinlock_t  lock;  
        struct  notifier_block *head;  
    };

    (2) 可阻塞通知链

    通知链元素的回调函数在进程上下文中运行,允许阻塞。对应的链表头:

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

    (3) 原始通知链

    对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护。对应的链表头:

    struct  raw_notifier_head {  
        struct  notifier_block  *head;  
    };

    (4) SRCU通知链

    可阻塞通知链的一种变体。对应的链表头:

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

    4. 相应接口

    注册通知链,在通知链表注册时,需要有一个链表头,他指向这个通知链表的第一个元素,这样就可以根据这个链表头找到这个链表中的所有数据。

    static int notifier_chain_register(struct notifier_block **nl, struct notifier_block *n);
    static int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n);

    通知链表,当有事件发生时,就使用notifier_call_chain向某个通知链表发送消息。这个函数会遍历通知链中所有的元素,然后依次调用每一个的回调函数。

    static int __kprobes notifier_call_chain(struct notifier_block **nl, unsigned long val, void *v, int nr_to_call, int *nr_calls)

    5. 一个简单的Demo

    #include <linux/module.h>
    #include <linux/notifier.h>
    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    
    //static RAW_NOTIFIER_HEAD(test_chain_head);
    
    struct raw_notifier_head test_chain_head = {
        .head = NULL
    };
    
    #define EVENT_A 0x01
    #define EVENT_B 0x02
    
    int test_notifier_event1(struct notifier_block *nb, unsigned long event, void *v)
    {
        switch (event) {
        case EVENT_A:
            printk("%s: event=EVENT_A
    ", __func__);
            break;
        case EVENT_B:
            printk("%s: event=EVENT_B
    ", __func__);
            break;
        default:
            break;
        }
    
        return NOTIFY_DONE;
    }
    
    int test_notifier_event2(struct notifier_block *nb, unsigned long event, void *v)
    {
        switch (event) {
        case EVENT_A:
            printk("%s: event=EVENT_A
    ", __func__);
            break;
        case EVENT_B:
            printk("%s: event=EVENT_B
    ", __func__);
            break;
        default:
            break;
        }
    
        return NOTIFY_DONE;
    }
    
    
    static struct notifier_block test_notifier1 = {
        .notifier_call = test_notifier_event1,
        .priority = 1,
    };
    
    static struct notifier_block test_notifier2 = {
        .notifier_call = test_notifier_event2,
        .priority = 2,
    };
    
    
    static int __init mynotify_init(void)
    {
        printk("raw_notifier_chain_register
    ");
        raw_notifier_chain_register(&test_chain_head, &test_notifier1);
        raw_notifier_chain_register(&test_chain_head, &test_notifier2);
    
        printk("raw_notifier_call_chain
    ");
        raw_notifier_call_chain(&test_chain_head, EVENT_B, NULL);
        raw_notifier_call_chain(&test_chain_head, EVENT_A, NULL);
    
        return 0;
    }
    
    static void __exit mynotify_exit(void)
    {
        raw_notifier_chain_unregister(&test_chain_head, &test_notifier1);
        raw_notifier_chain_unregister(&test_chain_head, &test_notifier2);
    }
    
    module_init(mynotify_init);
    module_exit(mynotify_exit);
    
    MODULE_LICENSE("GPL");
    
    
    /*
    root@ubuntu:/home/ubuntu/mytest/notifer# dmesg
    [19507.972012] raw_notifier_chain_register
    [19507.972015] raw_notifier_call_chain
    [19507.972016] test_notifier_event2: event=EVENT_B
    [19507.972017] test_notifier_event1: event=EVENT_B
    [19507.972018] test_notifier_event2: event=EVENT_A
    [19507.972018] test_notifier_event1: event=EVENT_A
    */

    主机上测试的Makefile

    obj-m += notifer_demo.o
    
    all:
        make -C /lib/modules/`uname -r`/build M=$(PWD)
    
    clean:
        make -C /lib/modules/`uname -r`/build M=$(PWD) modules clean

      首先要将notifier_block通过raw_notifier_chain_register注册到notifier_block_head链表上,notifier_block中必须要实现回调函数,然后在需要的时候通过raw_notifier_call_chain去调用notifier_block中的回调函数。

    6.补充:
    (1) notifier_call_chain 会按照通知链上各成员的优先级顺序执行回调函数(notifier_block.notifier_call),优先级的处理提现在注册时,在 notifier_chain_register() 中;回调函数的执行现场在 notifier_call_chain() 进程的地址空间;
    其返回值是NOTIFY_XXX的形式,在include/linux/notifier.h中。

    (2) priority的值越大,越先被通知。 

    7. 内核中的使用实例

    // net/bridge/br.c
    br_init(void)
        register_netdevice_notifier(&br_device_notifier);
        register_switchdev_notifier(&br_switchdev_notifier);
    
    static struct notifier_block br_device_notifier = {
        .notifier_call = br_device_event
    };
    static struct notifier_block br_switchdev_notifier = {
        .notifier_call = br_switchdev_event,
    };

    8. 相关资源

    (1) 此博文完全参考:https://www.linuxidc.com/Linux/2016-12/137886.htm

    (2) 以网络设备为例来讲解notifier_call_chain, 更加详细: https://www.cnblogs.com/pengdonglin137/p/4075148.html

  • 相关阅读:
    Atom使用教程
    4-[函数]-参数
    4-[函数]- 独立功能的代码块
    3 [文件]-修改文件
    2 [文件]-文件操作
    1. [文件]- 文件类型,文件open模式
    2-16 阶段考核
    react native 网络get请求方式参数不可为undefined或null
    [网络]远程访问局域网svn服务器[转]
    react native listview 一个有用的属性,用作两列布局
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/11073008.html
Copyright © 2011-2022 走看看