Linux内核通知链 使用和简析

【linux kernel】一文总结linux内核通知链 文本基于内核源码4.19.4描述构成通知链的具体数据结构和API接口,同时描述四种通知链的具体应用场景,并对API接口进行简要分析。 阅读详情

1. 使用简述

通知链表是一个函数链表,链表上的每一个节点都注册了一个处理函数。当该chain对应的事件发生时(call chain),链表上所有节点对应的函数就会被执行。

定义
  • 1 定义notifier head
    通知链是一个链表,需要有一个链表头,后续的元素就可以陆续添加到这个链表中去
static RAW_NOTIFIER_HEAD(hello_chain);
  • 2 定义该通知链的注册方法
    所谓注册就是根据优先级将被通知的元素添加到链表中去
static int register_hello_notifier(struct notifier_block *nb)
{
    int err;
    err = raw_notifier_chain_register(&hello_chain, nb);
    if(err)
        goto out;
out:
    return err;
}

EXPORT_SYMBOL(register_test_notifier);
  • 3 定义该通知链的事件通知方法
static int call_hello_notifiers(unsigned long val, void *v)
{
    return raw_notifier_call_chain(&hello_chain, val, v);
}
EXPORT_SYMBOL(call_hello_notifiers);
通知事件注册
  • 4 定义处理函数
int hello_notifier_event_handler(struct notifier_block *nb, unsigned long event, void *v)
{
    printk("event %lu\n", event);
    return NOTIFY_DONE;
}

/* define a notifier_block */
static struct notifier_block hello_init_notifier = {
    .notifier_call = hello_notifier_event_handler,
}
  • 5 注册
extern int register_hello_notifier(struct notifier_block *nb);
register_test_notifier(&hello_init_notifier);
发出通知事件
  • 6 发出通知事件
extern int call_test_notifiers(unsigned long val, void *v);
#define HEELO_EVENT 0x16
call_test_notifiers(HEELO_EVENT, "no_use");

2. 详述

Linux 内核通知链有四种类型,根据回调函数可运行的上线文限制做出了下面的区分
  • 原子通知链 Atomic notifier chains: Chain callbacks run in interrupt/atomic context. Callouts are not allowed to block.
  • 可阻塞通知链 Blocking notifier chains: Chain callbacks run in process context. Callouts are allowed to block.
  • 原始通知链 Raw notifier chains: There are no restrictions on callbacks, registration, or unregistration. All locking and protection must be provided by the caller.
  • SRCU通知链 SRCU notifier chains: A variant of blocking notifier chains, with the same restrictions.
~/include/linux/notifier.h

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

struct notifier_block {
    notifier_fn_t notifier_call;
    struct notifier_block __rcu *next;
    int priority;
};

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;
};
为了初始化链表头方便,提供了宏定义

需要注意的是SRCU的链表头需要动态初始化

#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);

#define ATOMIC_NOTIFIER_INIT(name) {                \
        .lock = __SPIN_LOCK_UNLOCKED(name.lock),    \
        .head = NULL }
#define BLOCKING_NOTIFIER_INIT(name) {              \
        .rwsem = __RWSEM_INITIALIZER((name).rwsem), \
        .head = NULL }
#define RAW_NOTIFIER_INIT(name) {               \
        .head = NULL }
/* srcu_notifier_heads cannot be initialized statically */

#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)
notifier_chain_register

无论是 ATOMIC BLOCKING RAW SRCU 的注册,都是对notifier_chain_register的封装, 以实现其特性
注册的过程就是讲新的链表元素按优先级添加到链表中去,大部分时候都是用的默认优先级

~/kernel/notifier.c

/*
 *  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;
}
notifier_call_chain

所谓call chain 就是遍历链表,执行所有元素的notifier_call,并传递相关参数


/**
 * notifier_call_chain - Informs the registered notifiers about an event.
 *  @nl:        Pointer to head of the blocking notifier chain
 *  @val:       Value passed unmodified to notifier function
 *  @v:     Pointer passed unmodified to notifier function
 *  @nr_to_call:    Number of notifier functions to be called. Don't care
 *          value of this parameter is -1.
 *  @nr_calls:  Records the number of notifications sent. Don't care
 *          value of this field is NULL.
 *  @returns:   notifier_call_chain returns the value returned by the
 *          last notifier function called.
 */
static int notifier_call_chain(struct notifier_block **nl,
                   unsigned long val, void *v,
                   int nr_to_call, int *nr_calls)
{
    int ret = NOTIFY_DONE;
    struct notifier_block *nb, *next_nb;

    nb = rcu_dereference_raw(*nl);

    while (nb && nr_to_call) {
        next_nb = rcu_dereference_raw(nb->next);

#ifdef CONFIG_DEBUG_NOTIFIERS
        if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
            WARN(1, "Invalid notifier called!");
            nb = next_nb;
            continue;
        }
#endif
        ret = nb->notifier_call(nb, val, v);

        if (nr_calls)
            (*nr_calls)++;

        if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
            break;
        nb = next_nb;
        nr_to_call--;
    }
    return ret;
}
【Linux 内核源码分析】Linux内核通知链机制 Linux内核通知链(notifier chain)是一种机制,用于实现内核中的事件通知和处理。它提供了一种灵活的方式,让不同的模块可以注册自己感兴趣的事件,并在事件发生时接收到通知。通知链由一个或多个注册在其中的回调函数组成,每个回调函数都有一个优先级。当事件发生时,内核会按照优先级顺序调用相应的回调函数进行处理。开发者可以使用notifier_chain_register()函数向通知链中注册回调函数,使用notifier_call_chain()函数触发对通知链中所有回调函数的调用。 阅读详情

相关推荐

notifier chain — 内核通知链

大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说

嵌入式技术博客 1万+

内核通知链

目标 Ø内核通知链引入 Ø内核通知链原理 Ø内核通知链使用 Ø内核通知链实现 Ø内核中使用实例分析 内核通知链引入 概念 子系统之间产生关联(耦合) .只能在内核子系统之间使用,不能内核与用户空间 函数注册到一个链表,事件产生后调用链表上的函数 通知链实现很简单 不到600行 代码位于 kernel/notifier.c 头文件位于 include/linux/n...

神圣雅诗人 640

BLE天线设计避坑:NRF52832官方50欧姆阻抗匹配的3个常见误区

本文深入解析NRF52832蓝牙SoC天线设计中的常见误区,重点探讨50欧姆阻抗匹配的3个关键问题:PCB板材介电常数差异、匹配元件寄生参数及VSWR测试。通过实测数据和优化方案,帮助工程师规避设计陷阱,提升BLE通信性能。

beer8的博客 214

内核通知链(网络子系统为例)

1.Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就。为满足这样的需求,内核实现了2.通知链只能用在各个子系统之间,而。组成内核的核心系统代码均位于kernel目录下,通知链表位于kernel/notifier.c中,对应的头文件为include/linux/notifier.h。3.事件通知链表是,,进行相应的处理。(类似于设计模式中的观察者模式,解耦)...

开源&&分享 1517

青龙面板实战:用快手极速版脚本日赚XX元的核心参数解析

本文深入解析了青龙面板运行快手极速版脚本的核心参数配置,旨在帮助进阶用户提升收益稳定性。重点剖析了设备标识符(did)的获取原理、多账号Cookie的精准拼接规则,以及如何通过科学设置定时任务频率来量化并优化收益,实现细水长流的稳定收益。

weixin_29310605的博客 1075

手动以及使用torch.nn实现前馈神经网络实验

手动以及使用torch.nn实现前馈神经网络 手动实现前馈神经网络解决上述回归、二分类、多分类任务 利用torch.nn解决上述回归、二分类、多分类任务 在多分类任务中使用至少三种不同的激活函数 对多分类任务中的模型评估隐藏层层数和隐藏单元个数对实验结果的影响

m0_52910424的博客 9373

Linux 内核通知链(notifier chain)原理和使用方法

目录 通知链的引入 通知链类型 一个简单的测试实例 通知链的引入 Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notification chain)。 通知链只能用在各个子系统之间,而不能在内核和用户空间进行事件的通知。组成内核的核心系统代码...

yetaibing1990的博客 5807

ENVI SARscape安装全攻略:从下载到汉化一步到位(附最新试用申请链接)

本文提供ENVI SARscape从下载安装到汉化的完整指南,特别针对InSAR(合成孔径雷达干涉测量)分析环境搭建。详细介绍了系统要求、分步安装流程、汉化优化方法及硬件加速配置,并分享数据处理验证和常见问题解决方案,帮助用户高效搭建遥感数据分析平台。

rnn9storyteller的博客 1413

Linux内核事件通知链

Linux内核事件通知链 一、概念: 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对...

团长的专栏 1854

FPGA实现高效数字下变频:CORDIC与CIC滤波器的Verilog优化设计

本文深入探讨了FPGA实现高效数字下变频的技术方案,重点介绍了CORDIC算法与CIC滤波器的Verilog优化设计。通过详细的算法原理分析、FPGA实现技巧和实战经验分享,展示了如何利用CORDIC的旋转运算替代乘法操作,以及CIC滤波器在降采样中的高效性能。文章还提供了完整的系统集成方案和资源优化策略,为无线通信和雷达信号处理领域的工程师提供了实用参考。

redis7keeper的博客 724

从USB 2.0到USB 3.x:Synopsys SVT USB VIP配置避坑与接口选择指南

本文详细解析了从USB 2.0到USB 3.x的Synopsys SVT USB VIP配置要点,涵盖协议版本选择、物理接口匹配及UVM层次化架构实现。重点介绍了layering sequence的配置技巧和常见问题解决方案,帮助验证工程师高效避坑并优化验证流程。

weixin_42653691的博客 238

Linux内核通知链

Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notificationchain)。通知链只能用在各个子系统之间,而不能在内核和用户空间进行事件的通知。组成内核的核心系统代码均位于kernel目录下,通知链表位于kernel/notifier.c中,对应的...

哈哈一笑 1062

【Segment Anything Model】三:SAM模型微调自定义数据集,更改混合提示方式:点,框,点框混合

1.微调SAM预备知识。

cvxiayixiao的博客 1万+

Linux内核中的通知链机制解析

Linux内核中的通知链机制解析 【免费下载链接】linux-insides 项目地址: https://gitcode.com/gh_mirrors/li/linux-insides ...

gitblog_01002的博客 609

YOLOv11【第三章:高阶应用与落地篇·第7节】多任务联合训练(Multi-Task Learning):检测+分割+关键点三头并进!

🏆 本文收录于专栏 《YOLOv11实战:从入门到深度优化》。本专栏围绕 YOLOv11 的改进、训练、部署与工程优化 展开,系统梳理并复现当前主流的 YOLOv11 实战案例与优化方案,内容目前已覆盖 分类、检测、分割、追踪、关键点、OBB 检测 等多个方向。

**My Coding Family** 730

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Linux 内核通知链和例程代码

概念大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提...

嵌入式Linux 669

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

linux内核的通知链机制

一、为什么需要通知链:    linux内核的各个子系统之间往往互相关联,一个子系统产生或者侦测到的事件,其它的子系统往往也很感兴趣,因此linux内核采用了通知链机制实现内核的子系统之间的通信需求。值的注意的是,通知链机制仅用于内核内部的子系统之间的通信,内核与用户空间的通信依赖于其它机制,如系统调用、procfs、sysctl、ioctl等。    以下图为例,路由器RT直接连接网络A、B

pirloofmilan的专栏 1073

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

linux内核通知链原理及应用

一、linux内核通知链介绍          内核的很多子系统(例如:进程调度、内存管理、虚拟文件系统、路由子系统等)之间具有很强的相互依赖性,因此,其中一个子系统侦测到的或者产生的事件,其他子系统可能都有兴趣,为了完成这种交互需求,linux使用了所谓的通知链(notification chain)     注意:通知链只在内核子系统之间使用,内核和用户空间之间的通知信息则是依赖其他机

JIANGXIN04211的专栏 1188

Linux 内核通知链随笔【中】

关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不同子系统之间的通信,通知链只能用于内核不同子系统之间的通信。那么内核通知链到底是怎么工作的?我们如何才能用好通知链?内核源代码里随处可见的通知链身影,我们到底该如何理解呢?本片博文过后,您的这些疑问和顾虑将统统消除。    以前有个女神,超凡脱俗、出水芙蓉,不过在怎么滴也是人,是人就会有各种各样的需求,女神

wjlkoorey的专栏 1146

Linux内核notifier机制通知链

内核使用通知链的机制在内核各子系统之间进行事件通知(注:无法用于内核态和用户态之间的事件通知)。 一、通知链介绍 在文件include/linux/notifier.h中,可以查看Linux内核定义的四种通知链类型: 通知链类型 初始化 注册函数 通知函数 Atomic notifier chains(原子通知链) ATOMIC_NOTIFIER_HEAD() atomic_notifier_chain_register()/ atomic_notifier_c

蓝精灵的专栏 1850

linux 通知链,Linux内核通知链notifier

当有事件发生时,通知者调用 notifier_call_chain函数通知事件的到达,这个函数会遍历n1指向的通知链中所有的元素,然后依次调用每一个的回调函数,完成通知动作。static int __kprobes notifier_call_chain(struct notifier_block**nl,unsigned long val, void *v,int nr_to_call,int ...

weixin_39524425的博客 776

内核通知链机制的原理及实现

这篇文章是对以下两位博主的文章进行了简单整理转载引用,备忘。非常感谢两位的分享。 http://my.chinaunix.net/space.php?uid=25445243&do=blog&id=212959 http://yfydz.cublog.cn   一、概念:     大多数内核子系统都是相互独立的,因此...

RopenYuan的专栏 952

linux 内核通知,[Linux] 内核通知链 notifier

Linux 内核中每个模块之间都是独立的,如果模块需要感知其他模块的事件,就需要用到内核通知链。最典型的通知链应用就是 LCD 和 TP 之间,TP 需要根据 LCD 的亮灭来控制是否打开关闭触摸功能。通俗的讲,LCD 会创建一个函数链表,TP 会将suspend 和 resume函数添加到链表中,当 LCD 发生亮灭变化时,会根据情况执行链表上所有对应的函数,函数会根据不同的动作执行 TP ...

weixin_35057850的博客 600
上一篇: Linux DEVFREQ - 通用DVFS Framework
下一篇: Android系统功耗优化之CPU - CPU功耗和频率的关系
DecJ
博客等级 码龄18年 537粉丝 25原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值