Chinaunix首页 | 论坛 | 博客
  • 博客访问: 313558
  • 博文数量: 49
  • 博客积分: 4045
  • 博客等级: 上校
  • 技术积分: 1195
  • 用 户 组: 普通用户
  • 注册时间: 2007-06-15 12:56
文章分类

全部博文(49)

文章存档

2013年(29)

2010年(6)

2009年(6)

2008年(8)

我的朋友

分类: LINUX

2013-03-13 14:34:18

内核中的很多子系统都是联系很紧密的,因此有可能某个子系统的某些事件,其他多个子系统都很感兴趣,此时就需要用到notification chain. 

举个具体的例子,比如说一台主机由于某个网卡的损坏或其他原因不能使用,从而导致连接此网卡的网络不能使用,这个时侯就是notification chain.来通知路由表去除这个网络的路由表项。 

notification chain就是一个链表,包括了所有当某个事件发生时当前子系统所需要执行的函数,而这些函数是被其他的子系统注册到nc(下面notification chain就简称nc了)里。 


下面来看源码. 

首先来看nc的数据结构(include/linux/notifier.h): 

notifier_call也就是将会执行的回调函数,next指向下一个nc节点,priority是优先级,优先级高的节点会先执行。 

Java代码  收藏代码
  1. struct notifier_block {  
  2.     int (*notifier_call)(struct notifier_block *, unsigned longvoid *);  
  3.     struct notifier_block *next;  
  4.     int priority;  
  5. };  


注册到一个nc上: 
这里我们使用notifier_chain_register来实现注册。 

这里遍历nc然后按照优先级来插入相应的位置(也就是插入到优先级比自己小的之前) 
Java代码  收藏代码
  1. static int notifier_chain_register(struct notifier_block **nl,  
  2.         struct notifier_block *n)  
  3. {  
  4. ///开始遍历  
  5.   
  6.     while ((*nl) != NULL) {  
  7. ///比较优先级  
  8.         if (n->priority > (*nl)->priority)  
  9.             break;  
  10.         nl = &((*nl)->next);  
  11.     }  
  12. ///链表插入的操作  
  13.     n->next = *nl;  
  14.     rcu_assign_pointer(*nl, n);  
  15.     return 0;  
  16. }  


在nc上唤醒一个事件,也就是执行相对应的函数: 

notifier_call_chain方法. 

第一个参数为nc,第二个参数为事件类型,第三个参数为传给回调函数的参数,第三个和第四个参数分别为nc已经遍历的数目。 

事件类型值可以看notifier.h中定义的。 
流程很简单就是遍历此nc,然后调用相应的回调函数。 

Java代码  收藏代码
  1. static int __kprobes notifier_call_chain(struct notifier_block **nl,  
  2.                     unsigned long val, void *v,  
  3.                     int nr_to_call, int *nr_calls)  
  4. {  
  5.     int ret = NOTIFY_DONE;  
  6.     struct notifier_block *nb, *next_nb;  
  7.   
  8.     nb = rcu_dereference(*nl);  
  9.   
  10.     while (nb && nr_to_call) {  
  11.         next_nb = rcu_dereference(nb->next);  
  12. ///基本上所有的回调函数里面都会使用switch语句,通过判断不同的event值来执行不同的操作。  
  13.         ret = nb->notifier_call(nb, val, v);  
  14.   
  15.         if (nr_calls)  
  16.             (*nr_calls)++;  
  17.   
  18.         if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)  
  19.             break;  
  20.         nb = next_nb;  
  21.         nr_to_call--;  
  22.     }  
  23. ///返回值为最后一个回调函数执行的返回值  
  24.     return ret;  
  25. }  



这里还要注意下,一般上来说内核都会对上面的注册和执行函数进行包装。 

内核中至少有10种不同的nc,比如inetaddr_chain就是本地网络地址的改变等等事件所触发。 
阅读(574) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~