简述linux通知链机制

notifier chain概述

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

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

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

通知链类型

(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) notifier_block

notifier_call_chain 会按照通知链上各成员的优先级顺序(.priority)来遍历(next)执行回调函数(notifier_call)

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

相应接口

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

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)

示例

下面的示例体现了通知链的<事件,优先级,遍历>的特点

#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\n", __func__);
        break;
    case EVENT_B:
        printk("%s: event=EVENT_B\n", __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\n", __func__);
        break;
    case EVENT_B:
        printk("%s: event=EVENT_B\n", __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\n");
    raw_notifier_chain_register(&test_chain_head, &test_notifier1);
    raw_notifier_chain_register(&test_chain_head, &test_notifier2);

    printk("raw_notifier_call_chain\n");
    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");

//dmesg
raw_notifier_chain_register
raw_notifier_call_chain
test_notifier_event2: event=EVENT_B
test_notifier_event1: event=EVENT_B
test_notifier_event2: event=EVENT_A
test_notifier_event1: event=EVENT_A

注册机制

 notifier_chain_register

static int notifier_chain_register(struct notifier_block **nl, //传递进来nl的是指针的指针的副本
		struct notifier_block *n)
{
	while ((*nl) != NULL) {
		if (unlikely((*nl) == n)) {
			WARN(1, "double register detected");
			return 0;
		}
		if (n->priority > (*nl)->priority)/*step 1*/
			break; 
		nl = &((*nl)->next); /*step 2 nl副本改变不影响链表头或者链表元素*/
	}
	n->next = *nl; /*step 3*/
	rcu_assign_pointer(*nl, n); /*step 4 *nl对地址副本进行*操作就会改变链表头或者链表元素*/
	return 0;
}

1.如果链表头的优先级小于新节点,那么退出循环,让新节点当链表头

2.如果链表头的优先级不小于新节点,那么从链表头循环遍历链表

    2.1找到小于新节点优先级的第一个链表元素tnl,则退出循环

tnl = nl->next;/*step 2*/

    将新节点n插到tnl前面       

n->next = tnl;/*step 3*/
*tnl = nl->next = n;/*step 4*/

    2.2.如果找到链表尾都没找到

tnl = nl->next = null;

    将新节点n插在最后一个元素后面当链表尾     

n->next = tnl = null;/*step 3*/
*tnl = nl->next = n;/*step 4*/

notifier_call_chain

直接遍历且执行通知链里的回调函数notifier_call

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)
			break;
		nb = next_nb;
		nr_to_call--;
	}
	return ret;
}
NOKPROBE_SYMBOL(notifier_call_chain);

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux安全机制是指在Linux操作系统中采取的一系列措施来保护系统和用户数据的安全性。以下是Linux安全机制的简要介绍: 1. 用户和权限管理:Linux通过用户和权限管理来限制对系统资源的访问。每个用户都有一个唯一的用户ID,系统管理员可以为每个用户分配不同的权限,以控制其对文件、目录和其他系统资源的访问权限。 2. 文件权限:Linux使用文件权限来控制对文件和目录的访问。每个文件和目录都有一个所有者和一个所属组,并且可以设置不同的权限,如读、写和执行权限。只有具有足够权限的用户才能对文件进行操作。 3. 防火墙:Linux提供了防火墙功能,用于过滤网络流量并保护系统免受未经授权的访问。防火墙可以配置规则,允许或拒绝特定的网络连接。 4. SELinux:SELinux(Security-Enhanced Linux)是Linux内核的一个安全模块,提供了强制访问控制(MAC)机制。它通过为每个进程和文件分配安全上下文,并定义了一组规则来限制进程对文件和资源的访问,增强了系统的安全性。 5. 安全补丁和更新:Linux社区定期发布安全补丁和更新,以修复已知的漏洞和安全问题。及时应用这些补丁和更新可以提高系统的安全性。 6. 日志和审计:Linux提供了日志和审计功能,记录系统的活动和事件。管理员可以通过分析日志来检测潜在的安全威胁,并采取相应的措施。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值