Chinaunix首页 | 论坛 | 博客
  • 博客访问: 56848
  • 博文数量: 19
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 177
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-25 21:11
文章分类

全部博文(19)

文章存档

2011年(1)

2009年(18)

我的朋友

分类: LINUX

2009-03-01 20:55:37

Linux内核中有许多子系统,他们之间有着非常多的依赖与交互关系,当某一个子系统中有事件发生时,就会影响到其他子系统的工作,比如说网卡ip地址的改变,设备的热插拔等等。Linux内核中使用了Notification Chains的方法来处理这类事件,顾名思义,它是一个链表的数据结构,其中链表的表元由回调函数,priority和next指针组成。
 35 struct notifier_block {
36 int (*notifier_call)(struct notifier_block *, unsigned long, void *);
37 struct notifier_block *next;
38 int priority;
39 };
      当我们初始化子系统的时候,应该知道自己到底对哪些事件比较敏感,也就是说知道哪些事件的发生会对自己的功能发生影响,如果有的话,就注册自己到这个特定的notification chain里面去,我们举一个例子来看看:
1242 void __init arp_init(void)
1243 {
1244 neigh_table_init(&arp_tbl);
1245
1246 dev_add_pack(&arp_packet_type);
1247 arp_proc_init();
1248 #ifdef CONFIG_SYSCTL
1249 neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4,
1250 NET_IPV4_NEIGH, "ipv4", NULL, NULL);
1251 #endif
1252 register_netdevice_notifier(&arp_netdev_notifier);
1253 }
      这个取自arp的初始化,我们注意最后一行的函数register_netdevice_notifier,这个就是注册函数,当然它还会有多层的调用,而arp_netdev_notifier就是一个notifier_block的结构,其中回调函数初始化为arp_netdev_event。我们先来看一下这个回调函数的实现
1201 static int arp_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1202 {
1203 struct net_device *dev = ptr;
1204
1205 switch (event) {
1206 case NETDEV_CHANGEADDR:
1207 neigh_changeaddr(&arp_tbl, dev);
1208 rt_cache_flush(0);
1209 break;
1210 default:
1211 break;
1212 }
1213
1214 return NOTIFY_DONE;
1215 }
      我们看到了,它只关心一种event,那就是NETDEV_CHANGEADDR,其他的事件发生它就并不在意了,当网络设备发生任何已经定义好的事件时,会遍历notification chain来进行遍历通知,如果是我们关心的事件发生,我们就作出相应的反应进行处理,如果不是,那么就当什么都没有发生。
      注册函数有一个通用的类型函数,上文所见到的register_netdevice_notifier经过二层调用之后就会遇到,这个函数就是
105 static int notifier_chain_register(struct notifier_block **nl,
106 struct notifier_block *n)
107 {
108 while ((*nl) != NULL) {
109 if (n->priority > (*nl)->priority)
110 break;
111 nl = &((*nl)->next);
112 }
113 n->next = *nl;
114 rcu_assign_pointer(*nl, n);
115 return 0;
116 }
      这个函数的作用就是把一个notifier_block加入到chain中去,不过这个链表的插入方法有点诡异,因为它直接利用了二级指针的一个巧妙应用,一般我们插入链表都需要有一个指针来保存前一个位置,然后再插入表元,而在这里,它只利用了一个指针就完成了这些工作,也就是把这个二级指针保存了前一个表元的next指针。
      最后我们来看一下那个事件发生时通知在通知链上的表元的函数
131 static int __kprobes notifier_call_chain(struct notifier_block **nl,
132 unsigned long val, void *v)
133 {
134 int ret = NOTIFY_DONE;
135 struct notifier_block *nb;
136
137 nb = rcu_dereference(*nl);
138 while (nb) {
139 ret = nb->notifier_call(nb, val, v);
140 if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
141 break;
142 nb = rcu_dereference(nb->next);
143 }
144 return ret;
145 }
      这个函数应该很容易理解,就是调用回调函数进行处理。
      Notification Chains最重要的好处是每个子系统自己来注册到底需要听哪些事件,而不是事件的发生方来遍历各个子系统来问我的改变你们是不是需要进行相应的对策处理,这样效率就提高了,也就是所谓的publish-and-subscribe model。
阅读(865) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~