这也是Bridge的副产品,真是好就没有没弄内核了,忘光了,还好Notification chain是个轻量级的东西。仍是联系Bridge来看,Brdige运用网络子体系的netdev_chain的有关操作也同时学习了。写这篇博客消耗2个小时。
Notification chain,翻译成告诉链,用于内核不一样的子体系之间彼此告诉一些事情,并主动触发告诉链上的函数对其进行处置。内核中注册了数以十计的告诉链。例如,激活网卡的事情,会使得sysfs在/sys下的显现发作改动,那么sysfs就会调用有关的notification chain上的函数去完结这个操作。而这些处置函数,就叫做Notifier。这个当地就有一个谁主谁次的联系,假如sysfs想在网卡em1接受到28326412个packets的时分,让网络子体系告诉它,进行一些列的操作,是不是可行?答案是不是定的,由于要从头编译内核,无法动态注册。
内核在文件kernel/notifier.c中界说了notification chain的有关操作,这些操作包含声明一个“告诉链”,向告诉链中注册“告诉块”,将“告诉块”从“告诉链”中刊出,调用告诉链中的告诉块等等。本篇博客将呈现如下词汇或相似说法,基本上即是字面意思,与内核中的布局或函数相对应:
- 告诉链
- 告诉块
- 告诉函数
- 注册告诉块
- 刊出告诉块
- 调用告诉函数
先看下数据布局:
1、Notification chain的首要布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | typedefint(*notifier_fn_t)(structnotifier_block *nb, unsignedlongaction,void*data); structnotifier_block { notifier_fn_t notifier_call; structnotifier_block __rcu *next; intpriority; }; structatomic_notifier_head { spinlock_t lock; structnotifier_block __rcu *head; }; structblocking_notifier_head { structrw_semaphore rwsem; structnotifier_block __rcu *head; }; structraw_notifier_head { structnotifier_block __rcu *head; }; structsrcu_notifier_head { structmutex mutex; structsrcu_struct srcu; structnotifier_block __rcu *head; }; |
“告诉链”以单向链表的办法保护,内核供给了几种类型的链表头,他们之间的差异在于同步机制,即在向链表中添加、删去元素时运用的同步办法,有atomic_notifier_head、blocking_notifier_head、srcu_notifier_head,当然,假如你自个有同步办法,也能够运用raw_notifier_head(NETLINK)。内核供给了几种声明并初始化“告诉链头”的宏界说,如ATOMIC_NOTIFIER_HEAD(name)、BLOCKING_NOTIFIER_HEAD(name)、RAW_NOTIFIER_HEAD(name)。
“告诉链”上的每个元素,即“告诉块”,即是布局体notifier_block,其首要效果的成员时“告诉函数”notifier_call,priority为“告诉链”上的告诉块的优先级,优先级高的“告诉函数”先履行:
关于其他操作,如向告诉链中注册“告诉块”,将“告诉块”从“告诉链”中刊出,调用告诉链中的告诉块等,内核有相似如下的界说(以atomic系列为例,blocking,raw,srcu相似):
1 2 3 4 5 6 | intatomic_notifier_chain_register(structatomic_notifier_head *nh, structnotifier_block *nb); intatomic_notifier_chain_unregister(structatomic_notifier_head *nh, structnotifier_block *nb); intatomic_notifier_call_chain(structatomic_notifier_head *nh, unsignedlongval,void*v); |
“告诉链”一般依照“声明告诉链头”,“注册告诉块”,“调用告诉链”的进程运用,调用xxx_notifier_call_chain()时,会依照告诉链上的告诉块的优先级由高到低的次第进行调用。
2、网络子体系的netdev_chain
网络子体系实际上不止一个“告诉链”,而netdev_chain是用来进行设备音讯告诉的,比方网卡激活、网卡地址修正等,netdev_chain声明在net/core/dev.c:
1 | staticRAW_NOTIFIER_HEAD(netdev_chain); |
netdev_chain撑持的事情有(include/linux/netdevice.h):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #define NETDEV_UP 0x0001 #define NETDEV_DOWN 0x0002 #define NETDEV_REBOOT 0x0003 #define NETDEV_CHANGE 0x0004 #define NETDEV_REGISTER 0x0005 #define NETDEV_UNREGISTER 0x0006 #define NETDEV_CHANGEMTU 0x0007 #define NETDEV_CHANGEADDR 0x0008 #define NETDEV_GOING_DOWN 0x0009 #define NETDEV_CHANGENAME 0x000A #define NETDEV_FEAT_CHANGE 0x000B #define NETDEV_BONDING_FAILOVER 0x000C #define NETDEV_PRE_UP 0x000D #define NETDEV_PRE_TYPE_CHANGE 0x000E #define NETDEV_POST_TYPE_CHANGE 0x000F #define NETDEV_POST_INIT 0x0010 #define NETDEV_UNREGISTER_FINAL 0x0011 #define NETDEV_RELEASE 0x0012 #define NETDEV_NOTIFY_PEERS 0x0013 #define NETDEV_JOIN 0x0014 #define NETDEV_CHANGEUPPER 0x0015 #define NETDEV_RESEND_IGMP 0x0016 |
为了便利netdev_chain愈加贴近于net_device操作,网络子体系在“告诉链”的基础上封装了几个netdev_chain的操作接口,基本上函数名即是字面意思,联系网桥的notifier的完成详细去剖析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | externintregister_netdevice_notifier(structnotifier_block *nb); externintunregister_netdevice_notifier(structnotifier_block *nb); structnetdev_notifier_info { structnet_device *dev; }; structnetdev_notifier_change_info { structnetdev_notifier_info info;/* must be first */ unsignedintflags_changed; }; staticinlinevoidnetdev_notifier_info_init(structnetdev_notifier_info *info, structnet_device *dev) { info->dev = dev; } staticinlinestructnet_device * netdev_notifier_info_to_dev(conststructnetdev_notifier_info *info) { returninfo->dev; } externintcall_netdevice_notifiers_info(unsignedlongval,structnet_device *dev, structnetdev_notifier_info *info); externintcall_netdevice_notifiers(unsignedlongval,structnet_device *dev); |
call_netdevice_notifiers的第二个参数相当于
3、Bridge里的notifier
bridge在模块初始化的时分就经过register_netdevice_notifier向netdev_chain注册了自个“告诉块”,姓名是br_device_notifier,告诉处置函数是br_device_event。br_device_notifier完成了netdev_chain撑持的一切的事情。bridge注册br_device_notifier的意图在于,假如体系内任何网络设备的装备、状况等信息发作了改动,将能够影响到网桥本身的装备、状况的改动,因而需求搜集与网桥有关的设备的装备、状况改动事情,并做有关的处置。
register_netdevice_notifier接口对比有意思(其实register_netdevice_notifier并没有调用atomic_notifier_call_chain()之类的函数,而是直接调用了告诉块的notifier_call函数),假如某模块经过register_netdevice_notifier注册自个的告诉块,则register_netdevice_notifier将网络子体系保护的一切网络的一切net_device,均运用新注册的告诉块的告诉函数+NETDEV_REGISTER参数,处置一遍。假如设备是IFF_UP状况,则再运用NETDEV_UP参数处置一遍。
应该是这样了解,netdev_chain用于保护一切网络的一切net_device的告诉事情,关于netdev_chain新注册的告诉块,对告诉块本身而言,刚刚获悉其他设备的存在,或许以为其刚刚注册,假如其他设备处于激活状况,那么新注册的告诉块以为刚刚激活,则调用一遍告诉处置函数。
不过这样以来,将发作一个风趣的表象,假如用户经过ioctl()为网桥添加一个NIC,由于网桥添加NIC的进程将致使bridge的ID和MTU从头核算,而bridge从头核算自个的ID和MTU后,假如发现ID改动了,将会发作一个告诉NETDEV_CHANGEADDR事件,netdev_chain将播送该告诉,这样bridge又会收到由于自个装备、状况发作改动而触发的自个的notifier;假如MTU的值发作了改动,相同由dev_set_mtu()接口自生就会触发一个NETDEV_CHANGEMTU事情,bridge又收到一个自个触发的事情,而notifier原则上是要接纳其他子体系的告诉。
为了防止这种循环欠套,br_device_notifier()在处置时,一方面只处置网桥设备和网桥的port设备,另一方面关于改动ID、MTU等接口,需求添加逻辑判别,假如真改动,则触发notifier,假如仅仅需求改动得与旧值共同,则不触发notifier。