通知链表是一个函数链表,链表上的每一个节点都注册了一个处理函数。当该chain对应的事件发生时(call chain),链表上所有节点对应的函数就会被执行。
定义
1、定义notifier head
通知链是一个链表,需要有一个链表头,后续的元素就可以陆续添加到这个链表中去
static RAW_NOTIFIER_HEAD(hello_chain);1
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 0x1612
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 */