关闭

Linux内核的通知链机制

标签: linux内核structnullsemaphore
5450人阅读 评论(0) 收藏 举报
分类:

    内核源码:linux-2.6.38.8.tar.bz2

 

    在Linux内核中,通知链是一种非常好的异步通信机制,它的实现也非常简单,就是通过某个单循环链表来实现。

    1、通知链实例都使用notifier_block结构体来表示 

/* linux-2.6.38.8/include/linux/notifier.h */
struct notifier_block {
	int (*notifier_call)(struct notifier_block *, unsigned long, void *);//处理函数
	struct notifier_block __rcu *next; //用于构建单循环链表
	int priority; //为本链表中的通知链实例设定优先级,高优先级的处理函数将优先被执行
};

    2、在当前的Linux内核中有四种类型的通知链链表表头(增加了某种锁机制而已) 

/* linux-2.6.38.8/include/linux/notifier.h */
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_head类型的表头不支持静态地创建。 

/* linux-2.6.38.8/include/linux/notifier.h */
#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)


#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 }

    3、注册和注销(实际上就是向相应的链表中插入或删除通知链实例) 

/* linux-2.6.38.8/include/linux/notifier.h */
extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
		struct notifier_block *nb);
extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
		struct notifier_block *nb);
extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
		struct notifier_block *nb);
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
		struct notifier_block *nb);

extern int blocking_notifier_chain_cond_register(
		struct blocking_notifier_head *nh,
		struct notifier_block *nb);

extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
		struct notifier_block *nb);
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
		struct notifier_block *nb);
extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
		struct notifier_block *nb);
extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
		struct notifier_block *nb);

    它们最后都会调用notifier_chain_register、notifier_chain_cond_register(只被blocking_notifier_chain_cond_register函数所使用)或notifier_chain_unregister函数。 

/* linux-2.6.38.8/kernel/notifier.c */
static int notifier_chain_register(struct notifier_block **nl,
		struct notifier_block *n)
{
	while ((*nl) != NULL) { //检查链表是否不为空。
		if (n->priority > (*nl)->priority) //检查将要插入的通知链实例的priority是否大于找到的链表元素的priority值,priority值最大的被插入到表头后面
			break;
		nl = &((*nl)->next); //指向链表的下一个元素
	}
	n->next = *nl;
	rcu_assign_pointer(*nl, n); //*nl指向n
	return 0;
}

static int notifier_chain_cond_register(struct notifier_block **nl,
		struct notifier_block *n)
{
	while ((*nl) != NULL) {
		if ((*nl) == n)  //检查被插入的通知链实例是否已经存在,避免重复插入
			return 0;
		if (n->priority > (*nl)->priority)
			break;
		nl = &((*nl)->next);
	}
	n->next = *nl;
	rcu_assign_pointer(*nl, n);
	return 0;
}

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;
}

/* linux-2.6.38.8/include/linux/rcupdate.h */
#define rcu_assign_pointer(p, v) \
	__rcu_assign_pointer((p), (v), __rcu)

#define __rcu_assign_pointer(p, v, space) \
	({ \
		if (!__builtin_constant_p(v) || \
		    ((v) != NULL)) \
			smp_wmb(); \
		(p) = (typeof(*v) __force space *)(v); \
	})

    4、触发相应的处理函数 (即使用链表中的通知链实例)

/* linux-2.6.38.8/include/linux/notifier.h */
extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
		unsigned long val, void *v);
extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
	unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
		unsigned long val, void *v);
extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
	unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int raw_notifier_call_chain(struct raw_notifier_head *nh,
		unsigned long val, void *v);
extern int __raw_notifier_call_chain(struct raw_notifier_head *nh,
	unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
		unsigned long val, void *v);
extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
	unsigned long val, void *v, int nr_to_call, int *nr_calls);

    它们最后都会调用notifier_call_chain函数。 

/* linux-2.6.38.8/kernel/notifier.c */
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) {  //根据需要,会调用链表中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;
}

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:649278次
    • 积分:4864
    • 等级:
    • 排名:第6124名
    • 原创:70篇
    • 转载:0篇
    • 译文:0篇
    • 评论:104条
    版权声明
    本博客所有文章均为原创,每篇尽心尽力,力求精益求精,转载请注明出处http://blog.csdn.net/npy_lp和署名tanglinux,商业用途请联系tanglinux@gmail.com。
    最新评论