内核通知链 linux内核提供通知链机制,用来提供一种异步通知机制。实现原理是将一些异步事件的回调函数链接到一个链表头上,在某种特定情况下,通过遍历该链表以调用所有注册到该链表上的事件回调函数。
通知链类型 linux内核定义了以下四种通知链:
Atomic通知链:在中断/原子操作上下文中调用通知回调。通知块回调函数不可以阻塞。
1 2 3 4 struct atomic_notifier_head { spinlock_t lock; struct notifier_block __rcu *head ; };
blocking通知链:在进程上下文中的通知,允许通知块阻塞。
1 2 3 4 struct blocking_notifier_head { struct rw_semaphore rwsem ; struct notifier_block __rcu *head ; };
raw通知:通知块回调不做任何限制,所有的锁和保护由调用者提供。
1 2 3 struct raw_notifier_head { struct notifier_block __rcu *head ; };
SRCU(Sleepable Read-Copy Update)通知:是一种Blocking通知链的变体,其限制和blocking通知链相同。用Sleepable Read-Copy Update代替了阻塞通知链中的读写信号量。具有开销小的优势,常用于通知块需要频繁调用,并且通知块基本不会删除的场景。
struct srcu_notifier_head { struct mutex mutex; struct srcu_struct srcu; struct notifier_block __rcu *head; };
通知链上的实例 Linux内核以一个notifier_block结构定义一个通知块:
1 2 3 4 5 struct notifier_block { int (*notifier_call)(struct notifier_block *, unsigned long , void *); struct notifier_block __rcu *next ; int priority; };
其中:
1 2 3 notifier_call是该通知实例的回调函数。 next指向该通知块所属通知链的下一个通知块。 priority表示该通知实例的优先级。
通知链定义和初始化接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 extern void srcu_init_notifier_head (struct srcu_notifier_head *nh) ;#define srcu_cleanup_notifier_head(name) \ cleanup_srcu_struct(&(name)->srcu); #define ATOMIC_NOTIFIER_INIT(name) { \ .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .head = NULL } #define BLOCKING_NOTIFIER_INIT(name) { \ .rwsem = __RWSEM_INITIALIZER((name).rwsem), \ .head = NULL } #define RAW_NOTIFIER_INIT(name) { \ .head = NULL } #define ATOMIC_NOTIFIER_HEAD(name) \ struct atomic_notifier_head name = \ ATOMIC_NOTIFIER_INIT(name) #define BLOCKING_NOTIFIER_HEAD(name) \ struct blocking_notifier_head name = \ BLOCKING_NOTIFIER_INIT(name) #define RAW_NOTIFIER_HEAD(name) \ struct raw_notifier_head name = \ RAW_NOTIFIER_INIT(name)
通知链注册和取消注册接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 extern int atomic_notifier_chain_register (struct atomic_notifier_head *nh, struct notifier_block *nb) ;extern int blocking_notifier_chain_register (struct blocking_notifier_head *nh, struct notifier_block *nb) ;extern int raw_notifier_chain_register (struct raw_notifier_head *nh, struct notifier_block *nb) ;extern int srcu_notifier_chain_register (struct srcu_notifier_head *nh, struct notifier_block *nb) ;extern int blocking_notifier_chain_cond_register ( struct blocking_notifier_head *nh, struct notifier_block *nb) ;extern int atomic_notifier_chain_unregister (struct atomic_notifier_head *nh, struct notifier_block *nb) ;extern int blocking_notifier_chain_unregister (struct blocking_notifier_head *nh, struct notifier_block *nb) ;extern int raw_notifier_chain_unregister (struct raw_notifier_head *nh, struct notifier_block *nb) ;extern int srcu_notifier_chain_unregister (struct srcu_notifier_head *nh, struct notifier_block *nb) ;
通知链执行实例接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 extern int atomic_notifier_call_chain (struct atomic_notifier_head *nh, unsigned long val, void *v) ;extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls); extern int blocking_notifier_call_chain (struct blocking_notifier_head *nh, unsigned long val, void *v) ;extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls); extern int raw_notifier_call_chain (struct raw_notifier_head *nh, unsigned long val, void *v) ;extern int __raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls); extern int srcu_notifier_call_chain (struct srcu_notifier_head *nh, unsigned long val, void *v) ;extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
通知链的应用 以usb总线为例说明,usb子系统初始化的时候注册了usb总线,在总线注册的时候初始化了总线的通知链。
1 2 3 4 5 6 7 8 9 10 int __bus_register(struct bus_type *bus, struct lock_class_key *key){ BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); }
接着向usb总线通知链上注册了通知实例usb_bus_nb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static struct notifier_block usb_bus_nb = { .notifier_call = usb_bus_notify, }; ... static int __init usb_init (void ) { retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb); if (retval) goto bus_notifier_failed; }
由其回调函数实现可知该通知实例的目的是在usb设备热插拔的时候在sysfs下创建和删除设备或接口文件。
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 static int usb_bus_notify (struct notifier_block *nb, unsigned long action, void *data) { struct device *dev = data ; switch (action) { case BUS_NOTIFY_ADD_DEVICE: if (dev->type == &usb_device_type) (void ) usb_create_sysfs_dev_files(to_usb_device(dev)); else if (dev->type == &usb_if_device_type) usb_create_sysfs_intf_files(to_usb_interface(dev)); break ; case BUS_NOTIFY_DEL_DEVICE: if (dev->type == &usb_device_type) usb_remove_sysfs_dev_files(to_usb_device(dev)); else if (dev->type == &usb_if_device_type) usb_remove_sysfs_intf_files(to_usb_interface(dev)); break ; } return 0 ; }
当插入或拔出usb设备时,会通过usb_notify_add_device或者usb_notify_remove_device调用usb总线通知链上的回调函数。
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 static int generic_probe (struct usb_device *udev) { int err, c; if (usb_device_is_owned(udev)) ; else if (udev->authorized == 0 ) dev_err(&udev->dev, "Device is not authorized for usage\n" ); else { c = usb_choose_configuration(udev); if (c >= 0 ) { err = usb_set_configuration(udev, c); if (err) { dev_err(&udev->dev, "can't set config #%d, error %d\n" , c, err); } } } usb_notify_add_device(udev); return 0 ; }
上面代码中usb_notify_add_device函数实现如下:
1 2 3 4 void usb_notify_add_device (struct usb_device *udev) { blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev); }