通知链

转自:http://blog.csdn.net/onlyg/article/details/6945652
 内核的很多子系统之间有很强的相互依赖性,因此,其中一个子系统侦测到的或者产生的事件,其他的子系统可能很感兴趣。为了实现这种交互需求,Linux使用了所谓的通知链。通知链上的是一个个的notifiter_block结构:
  1. struct notifier_block  
  2.     int (*notifier_call)(struct notifier_block *, unsigned long, void *);  
  3.     struct notifier_block *next;  
  4.     int priority;  
  5. };  
struct notifier_block {
        int (*notifier_call)(struct notifier_block *, unsigned long, void *);
        struct notifier_block *next;
        int priority;
};
priority 代表的是该函数的优先级,较高的优先级的函数会先被执行,但是实际中几乎都不会理会该值。因此,执行的次序仅依赖于注册的顺序。回调函数 notifier_call是在调用notifier_call_chain的进程上下文中执行,然而,回调函数也可以实现把通知信息安排在某处的队列, 然后唤醒查看此通知信息的进程。

    在内存中不同的通知被放在不同的链上,在需要将通知加入的时候只要调用相应的注册函数就可以了,比如register_inetaddr_notifier函数:

  1. int register_inetaddr_notifier(struct notifier_block *nb){  
  2.     return blocking_notifier_chain_register(&inetaddr_chain, nb);  
  3.  
int register_inetaddr_notifier(struct notifier_block *nb){
        return blocking_notifier_chain_register(&inetaddr_chain, nb);
}
一般的这些函数都是包裹函数,通过blocking_notifier_chain_register函数将notifier_block添加到不同的链表上:
  1. int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n){  
  2.     int ret;  
  3.     // 如果是在开机的时候还不具备down_write的条件  
  4.     if (unlikely(system_state SYSTEM_RUNNING))  
  5.         return notifier_chain_register(&nh->head, n);  
  6.     down_write(&nh->rwsem);  
  7.     ret notifier_chain_register(&nh->head, n);  
  8.     up_write(&nh->rwsem);  
  9.     return ret;  
  10.  
int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n){
        int ret;
        // 如果是在开机的时候还不具备down_write的条件
        if (unlikely(system_state < SYSTEM_RUNNING))
                return notifier_chain_register(&nh->head, n);
        down_write(&nh->rwsem);
        ret = notifier_chain_register(&nh->head, n);
        up_write(&nh->rwsem);
        return ret;
}
down_write的问题应该是很多其他函数的情况下是不会出现的,不过在这里需要分开考虑。下面是将通知插入到对应链的尾部:
  1. static int notifier_chain_register(struct notifier_block **nl,struct notifier_block *n){  
  2.     // 找到链表的尾部  
  3.     while ((*nl) != NULL)  
  4.         if (n->priority (*nl)->priority)  
  5.             break;  
  6.         nl &((*nl)->next);  
  7.      
  8.     n->next *nl;  
  9.     // rcu机制  
  10.     rcu_assign_pointer(*nl, n);  
  11.     return 0;  
  12.  
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机制
        rcu_assign_pointer(*nl, n);
        return 0;
}
RCU机制就是为了让读写并行,下面将节点插入:
  1. #define rcu_assign_pointer(p, v)  
  2.     ({  
  3.         if (!__builtin_constant_p(v) ||  
  4.             ((v) != NULL))  
  5.             smp_wmb();  
  6.         (p) (v);  
  7.     })  
#define rcu_assign_pointer(p, v) \
        ({ \
                if (!__builtin_constant_p(v) || \
                        ((v) != NULL)) \
                        smp_wmb(); \
                (p) = (v); \
        })
其中__builtin_constant_p是GCC的内联函数,用于判断v是不是编译时常数。而smp_wmb则保证了前面的代码执行完了才执行后面的代码。优化屏障的代码如下:
  1. #define barrier() __asm__ __volatile__("": :"memory")  
#define barrier() __asm__ __volatile__("": : :"memory")
__volatile__表示阻止编译器对该值进行优化,确保变量使用了用户定义的精确地址,而不是装有同一信息的一些别名。memory表示指令修改了内存单元。对不同的体系结构应该使用不同的内存屏障:
内存屏障的宏定义功能说明
mb()多(单)处理器的内存屏障
rmb()多(单)处理器的读内存屏障
wmb()多(单)处理器的写内存屏障
smp_mb()多处理器的内存屏障
smp_rmb()多处理器的读内存屏障
smp_wmb()多处理器的写内存屏障

对网络层代码表征特别重要事件的通知链有如下两个:

inetaddr_chain本地接口上的IPv4地址的插入、删除、变更的通知信息
netdev_chain有关网络设备注册状态的通知信息
关于通知链的使用下面是一个例子,路由子系统用到的初始化函数:
  1. static struct notifier_block fib_inetaddr_notifier  
  2.     .notifier_call =fib_inetaddr_event,  
  3. };  
  4. static struct notifier_block fib_netdev_notifier  
  5.     .notifier_call =fib_netdev_event,  
  6. };  
  7. void __init ip_fib_init(void){  
  8.     // ...  
  9.     register_netdevice_notifier(&fib_netdev_notifier);  
  10.     register_inetaddr_notifier(&fib_inetaddr_notifier);  
  11.     // ...  
  12.  
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);
        // ...
}
---------------------------------

int register_netdevice_notifier(struct notifier_block *nb)

这个函数主要完成两件事情:

1)把参数struct notifier_block *nb注册到netdev_chain通知链上;

2)系统中所有已经被注册过的或者激活的网络设备的事件都要被新增的这个通知的回调函数重新调用一遍,使设备更新到一个完整的状态。

inetaddr_chain 网络设备的IP地址结构

1) 在TCPIP协议环境下,
网络设备结构(net_device)具有一个ip_ptr指针指向IP协议的设备参数块(in_device),
它包含设备IP地址结构(in_ifaddr)的链表指针(ifa_list).
IP地址结构链可以为一个网络设备配置多个IP地址,
使得局域网中的单台主机能模拟多台主机的作用.

(2) 设备IP地址的配置由应用程序通过ioctl()系统调用使用ifreq参数结构来完成.
同一设备的不同IP地址用不同的设备别名来标识,
例如"eth0:1"和"eth0:2"分别代表设备eht0的两个地址. 当增加一个别名设备时,
如果它的地址与已有地址属于同一子网, 则它的地址被标记为"从属"(IFA_F_SECONDARY).
当设备最后一个别名被删除时, 设备的IP参数块将被释放.设备地址参数发生改变时,
将通过地址消息链(inetaddr_chain)向有关子系统发送通知消息,

例如路由子系统用来刷新转发表和路由缓冲表.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值