通知链

    内核的很多子系统之间有很强的相互依赖性,因此,其中一个子系统侦测到的或者产生的事件,其他的子系统可能很感兴趣。为了实现这种交互需求,Linux使用了所谓的通知链。通知链上的是一个个的notifiter_block结构:

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函数:

int register_inetaddr_notifier(struct notifier_block *nb){
	return blocking_notifier_chain_register(&inetaddr_chain, nb);
}
一般的这些函数都是包裹函数,通过blocking_notifier_chain_register函数将notifier_block添加到不同的链表上:

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的问题应该是很多其他函数的情况下是不会出现的,不过在这里需要分开考虑。下面是将通知插入到对应链的尾部:

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机制就是为了让读写并行,下面将节点插入:

#define rcu_assign_pointer(p, v) \
	({ \
		if (!__builtin_constant_p(v) || \
			((v) != NULL)) \
			smp_wmb(); \
		(p) = (v); \
	})
其中__builtin_constant_p是GCC的内联函数,用于判断v是不是编译时常数。而smp_wmb则保证了前面的代码执行完了才执行后面的代码。优化屏障的代码如下:

#define barrier() __asm__ __volatile__("": : :"memory")
__volatile__表示阻止编译器对该值进行优化,确保变量使用了用户定义的精确地址,而不是装有同一信息的一些别名。memory表示指令修改了内存单元。对不同的体系结构应该使用不同的内存屏障:

内存屏障的宏定义功能说明
mb()多(单)处理器的内存屏障
rmb()多(单)处理器的读内存屏障
wmb()多(单)处理器的写内存屏障
smp_mb()多处理器的内存屏障
smp_rmb()多处理器的读内存屏障
smp_wmb()多处理器的写内存屏障

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

inetaddr_chain本地接口上的IPv4地址的插入、删除、变更的通知信息
netdev_chain有关网络设备注册状态的通知信息
关于通知链的使用下面是一个例子,路由子系统用到的初始化函数:

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);
	// ...
}
---------------------------------

个人理解,欢迎拍砖。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值