Chinaunix首页 | 论坛 | 博客
  • 博客访问: 437993
  • 博文数量: 75
  • 博客积分: 556
  • 博客等级: 中士
  • 技术积分: 712
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-12 10:10
文章分类
文章存档

2015年(4)

2014年(4)

2013年(31)

2012年(8)

2011年(8)

2010年(20)

分类:

2012-07-21 19:09:04

原文地址:Linux内核通知链notifier 作者:stanleymiao

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

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

通知链技术可以概括为:事件的被通知者将事件发生时应该执行的操作通过函数指针方式保存在链表(通知链)中,然后当事件发生时通知者依次执行链表中每一个元素的回调函数完成通知。
The notifier chain facility is a general mechanism provided by the kernel. It is designed to provide a way for kernel elements to express interest in being informed about the occurrence of general asynchronous events.

The basic building block of the mechanism is the struct notifier_block which is defined in include/linux/notifier.h.

struct notifier_block {
        int (*notifier_call)(struct notifier_block *, unsigned long, void *);
        struct notifier_block __rcu *next;
        int priority; /*用于对注册者进行优先级排队,高优先级的处理例程将被优先执行,由注册者自己指定 */
};


The block contains a pointer to the function to be called when the event occurs. The parameters passed to the notifier function include:

• a pointer to the notifier block itself,
• an event code such as NETDEV_REGISTER or NETDEV_UNREGISTER,
• and a pointer to an unspecified private data type which in the case of the network chain points to the associated struct netdevice.

The kernel function notifier_chain_register() assembles related notifier blocks into notifier chains. Modules within the networking subsystem use the register_netdevice_notifier() function defined in net/core/dev.c to add their own notifier blocks to the netdev_chain which is statically initialized as NULL in dev.c.

int register_netdevice_notifier(struct notifier_block *nb)
{
    return notifier_chain_register(&netdev_chain, nb);
}


Adding the notifier_block to the chain.

The kernel routine notifier_chain_register() links the notifier block into the specified chain in priority order.

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


卸载函数是:

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 函数通知事件的到达,这个函数会遍历n1指向的通知链中所有的元素,然后依次调用每一个的回调函数,完成通知动作。

/**
 * 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);


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

参数nl是通知链的头部,val表示事件类型,v用来指向通知链上的函数执行时需要用到的参数,一般不同的通知链,参数类型也不一样,例如当通知一个网卡被注册时,v就指向net_device结构,nr_to_call表示准备最多通知几个,-1表示整条链都通知,nr_calls非空的话,返回通知了多少个。

    每个被执行的notifier_block回调函数的返回值可能取值为以下几个:

  1. NOTIFY_DONE:表示对相关的事件类型不关心。
  2. NOTIFY_OK:顺利执行。
  3. NOTIFY_BAD:执行有错。
  4. NOTIFY_STOP:停止执行后面的回调函数。
  5. NOTIFY_STOP_MASK:停止执行的掩码。

Notifier_call_chain()把最后一个被调用的回调函数的返回值作为它的返回值。


内核预定义了四种类型的notifier chain.

/*
 * Notifier chains are of four types:
 *
 
* 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 notifier chains: A variant of blocking notifier chains, with the same restrictions.

 *
 * atomic_notifier_chain_register() may be called from an atomic context, but blocking_notifier_chain_register() and srcu_notifier_chain_register() must be called from a process context. Ditto for the corresponding _unregister() routines.
 *
 * atomic_notifier_chain_unregister(), blocking_notifier_chain_unregister(),and srcu_notifier_chain_unregister() _must not_ be called from within the call chain.
 *
 * SRCU notifier chains are an alternative form of blocking notifier chains. They use SRCU (Sleepable Read-Copy Update) instead of rw-semaphores for protection of the chain links. This means there is _very_ low overheadin srcu_notifier_call_chain(): no cache bounces and no memory barriers. As compensation, srcu_notifier_chain_unregister() is rather expensive. SRCU notifier chains should be used when the chain will be called very often but notifier_blocks will seldom be removed. Also, SRCU notifier chains are slightly more difficult to use because they require special runtime initialization.

 */

/* 原子通知链( Atomic notifier chains ):通知链元素的回调函数(当事件发生时要执行的函数)只能在中断上下文中运行,不允许阻塞 */
struct atomic_notifier_head {
        spinlock_t lock;
        struct notifier_block __rcu *head;
};

/* 可阻塞通知链( Blocking notifier chains ):通知链元素的回调函数在进程上下文中运行,允许阻塞 */
struct blocking_notifier_head {
        struct rw_semaphore rwsem;
        struct notifier_block __rcu *head;
};

/* 原始通知链( Raw notifier chains ):对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护 */
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;
};


对应四中通知链的注册函数分别是:

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


通知函数分别是:

extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);
extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, v);

extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);oid *v);


阅读(678) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~