Linux内核通知链模块

通知链描述

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

通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。

通知链技术可以概括为:事件的被通知者将事件发生时应该执行的操作通过函数指针方式保存在链表(通知链)中,然后当事件发生时通知者依次执行链表中每一个元素的回调函数完成通知。

通知链实现

以下代码来自与linux-3.6.10

/kernel/notifier.c        通知链相关函数的实现

/include/linux/notifier.h 通知链结构、宏等定义

目前代码实现由四种类型的通知链:

原子通知链(Atomic notifier chains):通知链元素回调函数运行在中断或者原子上下文,不允许阻塞

阻塞通知链(Blocking notifier chains):通知链元素回调函数运行在进程上下文,允许阻塞

原始通知链(Raw notifier chains):通知链元素回调函数运行上下文没有限制,所有锁和保护机制都由调用者维护。

SRCU通知链(SRCU notifier chains):一种阻塞通知链的变种,拥有和阻塞通知链同样的限制,与阻塞通知链的区别在于它使用可睡眠的RCU锁(Sleepable Read-Copy Update)而不是读写信号量来保护通知链。也就意味着它执行回调函数(也即srcu_notifier_call_chain())的开销是非常小的,因为不需要缓存反弹和内存屏障。作为代价,srcu_notifier_chain_unregister()的开销是相当大的。所以SRCU通知链适合用在经常通知notifier_blocks在通知链上很少被去注册的情况。

struct notifier_block {
	int (*notifier_call)(struct notifier_block *, unsigned long, void *);
	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_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()

添加一个元素到通知链,从notifier_chain_register()实现来看,通知链时按优先级从大到小的有序链表。另外notifier_chain_cond_register()与此不同的是查找是否有相同的通知链元素,如果有则直接返回。

/*
 *	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_chain_unregister()

从通知链删除一个元素

static int notifier_chain_unregister(struct notifier_block **nl,
		struct notifier_block *n)
{
	while ((*nl) != NULL) {
		if ((*nl) == n) {
			rcu_assign_pointer(*nl, n->next);
			return 0;
		}
		nl = &((*nl)->next);
	}
	return -ENOENT;
}

notifier_call_chain()

通知已注册模块对应的事件

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

以下函数均是对上面三个函数的封装,只不过同步保护机制不同

atomic_notifier_chain_register()

atomic_notifier_chain_unregister()

atomic_notifier_call_chain()

blocking_notifier_chain_register()

blocking_notifier_chain_cond_register()

blocking_notifier_chain_unregister()

blocking_notifier_call_chain()

raw_notifier_chain_register()

raw_notifier_chain_unregister()

raw_notifier_call_chain()

srcu_notifier_chain_register()

srcu_notifier_chain_unregister()

srcu_notifier_call_chain()

Linux内核通知链模块

时间: 2024-11-05 14:48:37

Linux内核通知链模块的相关文章

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

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

Linux内核调试方法总结之内核通知链

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

内核通知链原理及机制

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

解析 Linux 内核可装载模块的版本检查机制

转自:http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmodules/ 为保持 Linux 内核的稳定与可持续发展,内核在发展过程中引进了可装载模块这一特性.内核可装载模块就是可在内核运行时加载到内核的一组代码.通常 , 我们会在两个版本不同的内核上装载同一模块失败,即使是在两个相邻的补丁级(Patch Level)版本上.这是因为内核在引入可装载模块的同时,对模块采取了版本信息校验.这是一个与模块代码无关,却与内核相连的机制.该校验机

linux内核hello world模块编写

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> int param = 0; /* 设备模块注册时执行的初始化函数 */ static int __init initialization_module(void) { printk("Hello world.\n"); printk("param = %d.\n", param)

Linux内核的主要模块

进程调度SCHED 进程调度指的是系统对进程的多种状态之间转换的策略.分别是:SCHED_OTHER.SCHED_FIFO.SCHED_RR. 内存管理MMU 内存管理是多个进程间的内存共享策略.在Linxu系统中,内存管理的主要概念是虚拟内存. 没个进程的虚拟内存有不同的地址空间,多个进程虚拟内存不会冲突.虚拟内存的大小通常是物理内存的2倍. 虚拟文件系统 Linxu系统下支持多中文件系统,最常用的是ext2和ext3,2中系统之间可以相互转换. 网络接口 网络接口分为网络协议和网络驱动程序,

【Linux操作系统】Linux内核插入卸载模块

打开终端 输入:mkdir moduleTest,创建一个放置文件的文件夹 进入文件夹,cd moduleTest 创建文件:nano hello.c,文件内容如下: #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void){ printk(KERN_ALERT"Hello, xiaoming

Linux内核基础--事件通知链(notifier chain)

转载: http://blog.csdn.net/wuhzossibility/article/details/8079025 http://blog.chinaunix.net/uid-27717694-id-4286337.html 内核通知链 1.1. 概述 Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制(notificationchain). 通知

Linux内核基础--事件通知链(notifier chain)【转】

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