Linux内核中通知块操作

欢迎进入Linux社区论坛,与200万技术人员互动交流 >>进入

Linux内核中通知块操作

严禁用于任何商业用途。

msn:

yfydz_no1@hotmail.com

来源:

http://yfydz.cublog.cn

1. 前言

notify是Linux内核中一种常用的事件回调处理机制,提供了基于优先级的回调链表处理功能。

以下内核代码版本为2.6.1Array.2。

2. 数据结构

/* include/linux/notifier.h */

// 基本的通知块结构

struct notifier_block {

// 回调函数

int (*notifier_call)(struct notifier_block *, unsigned long, void *);

// 链表中的下一个结构, 这个一个单向链表

struct notifier_block *next;

// 该块的优先级, 在链表中各个块是按此优先级值进行排序的, 值大的在链表前, 表明

// 相应回调函数执行的顺序

int priority;

};

出来基本的通知块结构, 还定义了一些扩展的通知块结构:

// 原子通知头结构, 增加了一个锁来保证操作的原子性

struct atomic_notifier_head {

spinlock_t lock;

struct notifier_block *head;

};

// 阻塞通知头结构, 增加了一个读写信号灯

struct blocking_notifier_head {

struct rw_semaphore rwsem;

struct notifier_block *head;

};

// 原始通知头结构, 就是一个通知块指针

struct raw_notifier_head {

struct notifier_block *head;

};

// srcu: Sleepable Read-Copy Update mechanism

// srcu通知头结构, 增加了锁和srcu结构

struct srcu_notifier_head {

struct mutex mutex;

struct srcu_struct srcu;

struct notifier_block *head;

};

以下是一些宏来初始化各种类型的通知头结构, 一般在程序中使用:

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

以下这些宏也是用来初始化各种类型的通知头结构, 但是在参数定义时使用:

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

注意, 没有定义scru通知头结构的初始化, 因为scru是不能静态初始化的。

以下这些宏用来直接定义通知头结构:

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

3. 基本通知块操作函数

关于通知块的基本操作函数都在kernel/sys.c中定义

3.1 登记

该函数将一个通知块结构挂接到指定的通知链表

/*

* Notifier chain core routines. The exported routines below

* are layered on top of these, with appropriate locking added.

*/

// nl是链表头块的地址, n是要添加到该链表的通知块

static int notifier_chain_register(struct notifier_block **nl,

struct notifier_block *n)

{

// 使用的是dummy header算法, 即使刚开始时链表为空也不用显示判断区分

while ((*nl) != NULL) {

// 判断优先权值, 优先权值越大位置越靠前

if (n->priority > (*nl)->priority)

break;

nl = &((*nl)->next);

}

// 将节点n链接到链表nl中的合适位置

n->next = *nl;

// 使用rcu处理函数保证SMP下的安全性, 相当于加上锁再赋值

rcu_assign_pointer(*nl, n);

return 0;

}

3.2 撤销

该函数将一个通知块结构从通知链表中拆除:

// nl是链表头块的地址, n是要删除的通知块

static int notifier_chain_unregister(struct notifier_block **nl,

struct notifier_block *n)

{

while ((*nl) != NULL) {

// 地址匹配, 进行拆除操作

if ((*nl) == n) {

// *nl=n->next的安全赋值操作,相当于将节点从链表断开

rcu_assign_pointer(*nl, n->next);

return 0;

}

nl = &((*nl)->next);

}

return -ENOENT;

}

3.3 回调函数处理

该函数执行链表中各节点的回调函数:

// nl通常是通知块链表头的地址, val和v是传给回调函数的参数

static int __kprobes notifier_call_chain(struct notifier_block **nl,

unsigned long val, void *v)

{

int ret = NOTIFY_DONE;

struct notifier_block *nb, *next_nb;

// 安全地获取通知块指针

nb = rcu_dereference(*nl);

// 链表循环

while (nb) {

// 找下一个块

next_nb = rcu_dereference(nb->next);

// 调用回调函数

ret = nb->notifier_call(nb, val, v);

// 如果返回停止标志, 不执行后续结构

if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)

break;

// 循环到下一节点

nb = next_nb;

}

return ret;

}

[1][2][3]

即使爬到最高的山上,一次也只能脚踏实地地迈一步。

Linux内核中通知块操作

相关文章:

你感兴趣的文章:

标签云: