一、为什么需要通知链:
If (subsystem_X_enabled) {
do_something_1
}
if (subsystem_Y_enabled) {
do_something_2
}
If (subsystem_Z_enabled) {
do_something_3
}
... ... ...
二、什么是通知链机制
三、怎样定义一个链
通知链列表元素的类型是
struct notifier_block
{
int (*notifier_call)(struct notifier_block *self, unsigned long,void *);
struct notifier_block *next;
int priority;
};
notifier_call就是回调函数,priority是函数的优先级,但实际实现过程中,这个值通常不设置,即让它取默认值0,因此回调函数的执行顺序就决定于注册的顺序(半随机顺序)。定义某个notifier_block实例时通常使用的名字如xxx_chain,xxx_notifier_chain,
四、注册一个链(将某个链元素插入list)
定义好一个链,还需要对它进行注册。当某个子系统对定义的某个通知链感兴趣时,它就会使用notifier_chain_register函数来注册这个链。这个函数还有一些包裹函数,如对于inetaddr_chain,
inet6addr_chain , netdev_chain这三条链,对应的注册,删除和通知函数(及其包裹函数)如下:
int notifier_chain_register(struct notifier_block **list, structnotifier_block *n)
将新的节点n插入list指向的链表中
inetaddr_chain: register_inetaddr_notifier
inet6addr_chain: register_inet6addr_notifier
netdev_chain: register_netdevice_notifier
int notifier_chain_unregister(struct notifier_block **nl, structnotifier_block *n)
从链表nl中删除节点n
inetaddr_chain: unregister_inetaddr_notifier
inet6addr_chain: unregister_inet6addr_notifier
netdev_chain: unregister_netdevice_notifier
int notifier_call_chain(struct notifier_block **n, unsigned longval, void *v)
int notifier_chain_register(struct notifier_block **list, structnotifier_block *n)
{
}
五、向通知链发送通知消息
int notifier_call_chain(struct notifier_block **n, unsigned longval, void *v)
{
int ret = NOTIFY_DONE;
struct notifier_block *nb = *n;
while (nb)
{
ret = nb->notifier_call(nb, val, v);
if (ret & NOTIFY_STOP_MASK)
{
return ret;
}
nb = nb->next;
}
return ret;
}
其中对通知链调用的回调函数(notifier_call)的返回值可以是如下数值,它们被定义于/include/linux/norifier.h中:
Notification was processed correctly.
Not interested in the notification. 不要用来替代NOTIFY_OK.
Something went wrong. Stop calling the callback routines for thisevent.
Routine invoked correctly. However, no further callbacks need to becalled for this event.
This flag is checked by notifier_call_chain to see whether to stopinvoking the callback
routines, or keep going. Both NOTIFY_BAD and NOTIFY_STOP includethis flag in their
definitions.
注意,对于同一个通知链,在同一时间,有可能不同的CPU会同时调用notofier_call_chain,而负责互斥访问和串行化的是回调函数
inetaddr_chain:有关本地接口的ipv4地址的插入删除变更等的通知信息,ipv6使用类型的链inet6addr_chain
netdev_chain:有关网络设备注册状态的通知信息
通知链注册通常发生在感兴趣的内核组建初始化的时候,例如路由系统初始化时调用的函数ip_fib_init会注册两条相关的链:(code in /net/ipv4/fib_frontend.c)
static struct notifier_block fib_inetaddr_notifier = {
.notifier_call = fib_inetaddr_event,
};
static struct notifier_block fib_netdev_notifier = {
.notifier_call = fib_netdev_event,
};
void _ _init ip_fib_init(void)
{
... ... ...
register_netdevice_notifier(&fib_netdev_notifier);
register_inetaddr_notifier(&fib_inetaddr_notifier);
}
七、定义自己的通知链
1.定义通知链的头节点
2.把某个事件发生时需要执行的回调函数以节点的形式插入链表
3.发生了感兴趣的时间,notifier_call_chain执行,链上注册的回调函数得到调用