Linux内核通知链 使用和简析

通知链表是一个函数链表,链表上的每一个节点都注册了一个处理函数。当该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 */

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/11928.html