1. 引言

Linux是单内核架构(monolithic kernel),大多数内核子系统和模块是相互独立的,它们被动态地加载或卸载,以使内核变得小巧和可扩展.然而,子系统或模块之间需要通信,或者说某个特定模块扑捉到的事件可能其它模块对此感兴趣,这就需要一种机制来满足子系统或模块之间交互的需求.

Linux使用通知链表来实现这一需求,它是一个简单的函数链表,当某件事件发生时,链表上的函数就会执行.这是一种发布-订阅(publish-subscribe)模式,当客户(订阅者)需要某个特定事件的通知时,会向主机(发布者)注册自己;接下来,只要感兴趣的事件一发生,主机便会通知客户.

讨论内核通知链之前,有必要注意以下几点:

  • 通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知.
  • 通知链表是一个函数链表,链表上的每一个节点都注册了一个函数.当某个事情发生时,链表上所有节点对应的函数就会被执行.所以对于通知链表来说有一个通知方(主机)与一个接收方(订阅者).
  • 在通知这个事件时所运行的函数由被通知方(订阅者通过回调函数)决定,实际上也即是被通知方注册(订阅者实现)了某个函数,在发生某个事件时这些函数就得到执行.

2. 数据结构

清单1. 原子通知链(Atomic notifier chains):

通知链元素的回调函数(当事件发生时要执行的函数)只能在中断上下文中运行,不允许阻塞

  1. 1 struct atomic_notifier_head {
  2. 2 spinlock_t lock; /*锁*/
  3. 3 struct notifier_block *head;
  4. 4 };

清单2. 可阻塞通知链(Blocking notifier chains):

阻塞通知头结构,增加了一个读写信号灯,通知链元素的回调函数在进程上下文中运行,允许阻塞

  1. 1 struct blocking_notifier_head {
  2. 2 struct rw_semaphore rwsem; /*读写控制信号量*/
  3. 3 struct notifier_block *head;
  4. 4 };

清单3. 原始通知链(Raw notifier chains):

原始通知头结构,就是一个通知块指针,对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护

  1. 1 struct raw_notifier_head {
  2. 2 struct notifier_block *head;
  3. 3 };

清单4. SRCU 通知链(SRCU notifier chains):

可阻塞通知链的一种变体,(SRCU)Sleepable Read Copy Update 的链表通知,与block链表通知类似,不同在处理锁与保护上,SRCU在调用通知时的系统开销小,而从通知链表中去除通知调用的系统开销大,因此适合用在调用通知频繁,而移除调用通知少的情况中

  1. 1 struct srcu_notifier_head {
  2. 2 struct mutex mutex;
  3. 3 struct srcu_struct srcu;
  4. 4 struct notifier_block *head;
  5. 5 };

参看:http://geyingzhen.blog.163.com/blog/static/6558665520091165531675/

清单5. 基本的通知块结构(notifier_block):

通知链的核心结构

  1. 1 struct notifier_block {
  2. 2 int (*notifier_call)(struct notifier_block *, unsigned long, void *); /*回调函数*/
  3. 3 struct notifier_block *next; /*指向链表的下一个元素节点,这是一个单向链表*/
  4. 4 int priority; /*该块的优先级, 在链表中各个块是按此优先级值进
  5. 5 *行排序的, 值大的在链表前, 表明
  6. 6 *相应回调函数执行的顺序*/
  7. 7 };

其中,函数指针notifier_call注册了当某个事件发生时需要调用的函数;next指向下一个链表节点;priority设定链表节点的优先级;数值越大优先级越高,默认为0.因此,所有的通知链表节点组成了一个单链表,并以优先级(priority)排列.

3. 运行机制

通知链的运作机制包括两个角色:

  • 被通知者(订阅者):对某一事件感兴趣一方,定义了当事件发生时,相应的处理函数,即回调函数.但需要事先将其注册到通知链中(被通知者注册的动作就是在通知链中增加一项).
  • 通知者(主机):事件的通知者.当检测到某事件,或者本身产生事件时,通知所有对该事件感兴趣的一方事件发生.他定义了一个通知链,其中保存了每一个被通知者对事件的处理函数(回调函数).通知这个过程实际上就是遍历通知链中的每一项,然后调用相应的事件处理函数.

通知机制实现包括以下三个步骤:

  1. 通知者(主机)定义通知链;
  2. 被通知者(订阅者)向通知链中注册回调函数;
  3. 当事件发生时,通知者(主机)发出通知(执行通知链中所有元素的回调函数).

清单6. notifier_chain_register函数:

被通知者(订阅者)调用 notifier_chain_register函数注册回调函数,该函数按照优先级将回调函数加入到通知链中:

  1. 1 /*
  2. 2 * Notifier chain core routines. The exported routines below
  3. 3 * are layered on top of these, with appropriate locking added.
  4. 4 */
  5. 5 /*nl是链表头块的地址, n是要添加到该链表的通知块*/
  6. 6 static int notifier_chain_register(struct notifier_block **nl,
  7. 7 struct notifier_block *n)
  8. 8 {
  9. 9 while ((*nl) != NULL) { /*使用的是dummy header算法, 即使刚开始时链表为空也不用显示判断区分*/
  10. 10 if (n->priority > (*nl)->priority) /*判断优先权值, 优先权值越大位置越靠前*/
  11. 11 break;
  12. 12 nl = &((*nl)->next);
  13. 13 }
  14. 14 n->next = *nl; /*将节点n链接到链表nl中的合适位置*/
  15. 15 rcu_assign_pointer(*nl, n); /*使用rcu处理函数保证SMP下的安全性, 相当于加上锁再赋值*/
  16. 16 return 0;
  17. 17 }

清单7. notifier_chain_unregister函数:

注销回调函数则使用notifier_chain_unregister函数,即将回调函数从通知链中删除:

  1. 1 /*nl是链表头块的地址, n是要删除的通知块*/
  2. 2 static int notifier_chain_unregister(struct notifier_block **nl,
  3. 3 struct notifier_block *n)
  4. 4 {
  5. 5 while ((*nl) != NULL) {
  6. 6 if ((*nl) == n) {
  7. 7 rcu_assign_pointer(*nl, n->next); /* *nl=n->next的安全赋值操作,相当于将节点从链表断开*/
  8. 8 return 0;
  9. 9 }
  10. 10 nl = &((*nl)->next);
  11. 11 }
  12. 12 return -ENOENT;
  13. 13 }

清单8. notifier_call_chain函数:

通知者(主机)调用 notifier_call_chain函数通知事件的到达,这个函数会遍历通知链中所有的元素,然后依次调用每一个的回调函数(即完成通知动作):

  1. 1 /**
  2. 2 * notifier_call_chain - Informs the registered notifiers about an event.
  3. 3 * @nl: Pointer to head of the blocking notifier chain
  4. 4 * @val: Value passed unmodified to notifier function
  5. 5 * @v: Pointer passed unmodified to notifier function
  6. 6 * @nr_to_call: Number of notifier functions to be called. Don't care
  7. 7 * value of this parameter is -1.
  8. 8 * @nr_calls: Records the number of notifications sent. Don't care
  9. 9 * value of this field is NULL.
  10. 10 * @returns: notifier_call_chain returns the value returned by the
  11. 11 * last notifier function called.
  12. 12 */
  13. 13 static int __kprobes notifier_call_chain(struct notifier_block **nl,
  14. 14 unsigned long val, void *v,
  15. 15 int nr_to_call, int *nr_calls)
  16. 16 {
  17. 17 int ret = NOTIFY_DONE;
  18. 18 struct notifier_block *nb, *next_nb;
  19. 19
  20. 20 nb = rcu_dereference(*nl); /*安全地获取通知块指针*/
  21. 21
  22. 22 while (nb && nr_to_call) { /*链表循环*/
  23. 23 next_nb = rcu_dereference(nb->next); /*找下一个块*/
  24. 24
  25. 25 #ifdef CONFIG_DEBUG_NOTIFIERS
  26. 26 if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
  27. 27 WARN(1, "Invalid notifier called!");
  28. 28 nb = next_nb;
  29. 29 continue;
  30. 30 }
  31. 31 #endif
  32. 32 ret = nb->notifier_call(nb, val, v); /*执行订阅者注册的回调函数,对此通知做出响应*/
  33. 33
  34. 34 if (nr_calls)
  35. 35 (*nr_calls)++;
  36. 36
  37. 37 if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) /*如果返回停止标志, 不执行后续结构*/
  38. 38 break;
  39. 39 nb = next_nb; /*循环执行,进入下一个节点*/
  40. 40 nr_to_call--;
  41. 41 }
  42. 42 return ret;
  43. 43 }

参数nl是通知链的头部,val表示事件类型,v用来指向通知链上的函数执行时需要用到的参数,一般不同的通知链,参数类型也不一样,例如当通知一个网卡被注册时,v就指向net_device结构,nr_to_call表示准备最多通知几个,-1表示整条链都通知,nr_calls非空的话,返回通知了多少个.

每个被执行的notifier_block回调函数的返回值可能取值为以下几个:

  • NOTIFY_DONE:表示对相关的事件类型不关心;
  • NOTIFY_OK:顺利执行;
  • NOTIFY_BAD:执行有错;
  • NOTIFY_STOP:停止执行后面的回调函数;
  • NOTIFY_STOP_MASK:停止执行的掩码;
  • Notifier_call_chain()把最后一个被调用的回调函数的返回值作为它的返回值.

4. 应用实例

在这里,写了一个简单的通知链表的代码.实际上,整个通知链的编写也就两个过程:

  1. 首先是定义自己的通知链的头节点,并将要执行的函数注册到自己的通知链中;
  2. 其次则是由另外的子系统来通知这个链,让其上面注册的函数运行.

这里将第一个过程分成了两步来写,第一步是定义了头节点和一些自定义的注册函数(针对该头节点的),第二步则是使用自定义的注册函数注册了一些通知链节点.分别在代码buildchain.c与regchain.c中.发送通知信息的代码为notify.c.

清单9. buildchain.c

它的作用是自定义一个通知链表test_chain,然后再自定义两个函数分别向这个通知链中加入或删除节点,最后再定义一个函数通知这个test_chain链

buildchain.c

清单10. regchain.c

该代码的作用是将test_notifier1,test_notifier2,test_notifier3这三个节点加到之前定义的test_chain这个通知链表上,同时每个节点都注册了一个函数

regchain.c

清单11. notify.c

该代码的作用就是向test_chain通知链中发送消息,让链中的函数运行

notify.c

清单12. Makefile

  1. 1 obj-m:=buildchain.o regchain.o notify.o
  2. 2 CURRENT_PATH := $(shell pwd)
  3. 3 LINUX_KERNEL := $(shell uname -r)
  4. 4 KERNELDIR := /usr/src/linux-headers-$(LINUX_KERNEL)
  5. 5
  6. 6 all:
  7. 7 make -C $(KERNELDIR) M=$(CURRENT_PATH) modules
  8. 8
  9. 9 clean:
  10. 10
  11. 11 make -C $(KERNELDIR) M=$(CURRENT_PATH) clean

清单13. compile&load

  1. 1 #make
  2. 2
  3. 3 #insmod buildchain.ko
  4. 4 #insmod regchain.ko
  5. 5 #insmod notify.ko

清单14. result

  1. 1 init_notifier
  2. 2 Begin to register:
  3. 3 register test_notifier1 completed
  4. 4 register test_notifier2 completed
  5. 5 register test_notifier3 completed
  6. 6 Begin to notify:
  7. 7 ==============================
  8. 8 In Event 1: Event Number is 1
  9. 9 In Event 2: Event Number is 1
  10. 10 In Event 3: Event Number is 1
  11. 11 ==============================

5. 通知头结构的有关宏

清单15. Head-macro

  1. 1 /*以下是一些宏来初始化各种类型的通知头结构, 一般在程序中使用*/
  2. 2 #define ATOMIC_INIT_NOTIFIER_HEAD(name) do{ \
  3. 3 spin_lock_init(&(name)->lock); \
  4. 4 (name)->head =NULL; \
  5. 5 } while (0)
  6. 6
  7. 7 #define BLOCKING_INIT_NOTIFIER_HEAD(name) do{ \
  8. 8 init_rwsem(&(name)->rwsem); \
  9. 9 (name)->head =NULL; \
  10. 10 } while (0)
  11. 11
  12. 12 #define RAW_INIT_NOTIFIER_HEAD(name) do{ \
  13. 13 (name)->head =NULL; \
  14. 14 } while (0)
  15. 15
  16. 16
  17. 17 /*以下这些宏也是用来初始化各种类型的通知头结构,但是在参数定义时使用(即作为赋值的右半部分,作为等号右边的部分)*/
  18. 18 #define ATOMIC_NOTIFIER_INIT(name){ \
  19. 19 .lock =__SPIN_LOCK_UNLOCKED(name.lock), \
  20. 20 .head = NULL }
  21. 21 #define BLOCKING_NOTIFIER_INIT(name){ \
  22. 22 .rwsem =__RWSEM_INITIALIZER((name).rwsem), \
  23. 23 .head = NULL }
  24. 24 #defineRAW_NOTIFIER_INIT(name) { \
  25. 25 .head = NULL }
  26. 26
  27. 27 /*注意, 没有定义scru通知头结构的初始化, 因为scru是不能静态初始化的.*/
  28. 28
  29. 29
  30. 30 /*以下这些宏用来直接定义通知头结构*/
  31. 31 #defineATOMIC_NOTIFIER_HEAD(name) \
  32. 32 struct atomic_notifier_head name= \
  33. 33 ATOMIC_NOTIFIER_INIT(name)
  34. 34 #defineBLOCKING_NOTIFIER_HEAD(name) \
  35. 35 struct blocking_notifier_head name= \
  36. 36 BLOCKING_NOTIFIER_INIT(name)
  37. 37 #defineRAW_NOTIFIER_HEAD(name) \
  38. 38 struct raw_notifier_head name= \
  39. 39 RAW_NOTIFIER_INIT(name)

6. 扩展的通知块操作

扩展的通知块操作功能和基本通知块类似,但使用了扩展的结构中的参数保证操作的安全

6.1 原子通知块

6.1.1 登记
  1. 1 /**
  2. 2 * atomic_notifier_chain_register - Add notifier to an atomic notifier chain
  3. 3 * @nh: Pointer to head of the atomic notifier chain
  4. 4 * @n: New entry in notifier chain
  5. 5 *
  6. 6 * Adds a notifier to an atomic notifier chain.
  7. 7 *
  8. 8 * Currently always returns zero.
  9. 9 */
  10. 10 /*只在基本通知登记操作前后加锁进行保护*/
  11. 11 int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
  12. 12 struct notifier_block *n)
  13. 13 {
  14. 14 unsigned long flags;
  15. 15 int ret;
  16. 16
  17. 17 spin_lock_irqsave(&nh->lock, flags); /*加锁*/
  18. 18 ret = notifier_chain_register(&nh->head, n);
  19. 19 spin_unlock_irqrestore(&nh->lock, flags); /*解锁*/
  20. 20 return ret;
  21. 21 }
  22. 22 EXPORT_SYMBOL_GPL(atomic_notifier_chain_register);
6.1.2 撤销
  1. 1 /**
  2. 2 * atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain
  3. 3 * @nh: Pointer to head of the atomic notifier chain
  4. 4 * @n: Entry to remove from notifier chain
  5. 5 *
  6. 6 * Removes a notifier from an atomic notifier chain.
  7. 7 *
  8. 8 * Returns zero on success or %-ENOENT on failure.
  9. 9 */
  10. 10 /*只是在基本通知块撤销操作前后加锁解锁进行保护*/
  11. 11 int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
  12. 12 struct notifier_block *n)
  13. 13 {
  14. 14 unsigned long flags;
  15. 15 int ret;
  16. 16
  17. 17 spin_lock_irqsave(&nh->lock, flags); /*加锁*/
  18. 18 ret = notifier_chain_unregister(&nh->head, n);
  19. 19 spin_unlock_irqrestore(&nh->lock, flags); /*解锁*/
  20. 20 synchronize_rcu(); /*同步rcu, 等待一个grace period*/
  21. 21 return ret;
  22. 22 }
  23. 23 EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
6.1.3 原子回调
  1. 1 /**
  2. 2 * __atomic_notifier_call_chain - Call functions in an atomic notifier chain
  3. 3 * @nh: Pointer to head of the atomic notifier chain
  4. 4 * @val: Value passed unmodified to notifier function
  5. 5 * @v: Pointer passed unmodified to notifier function
  6. 6 * @nr_to_call: See the comment for notifier_call_chain.
  7. 7 * @nr_calls: See the comment for notifier_call_chain.
  8. 8 *
  9. 9 * Calls each function in a notifier chain in turn. The functions
  10. 10 * run in an atomic context, so they must not block.
  11. 11 * This routine uses RCU to synchronize with changes to the chain.
  12. 12 *
  13. 13 * If the return value of the notifier can be and'ed
  14. 14 * with %NOTIFY_STOP_MASK then atomic_notifier_call_chain()
  15. 15 * will return immediately, with the return value of
  16. 16 * the notifier function which halted execution.
  17. 17 * Otherwise the return value is the return value
  18. 18 * of the last notifier function called.
  19. 19 */
  20. 20 int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
  21. 21 unsigned long val, void *v,
  22. 22 int nr_to_call, int *nr_calls)
  23. 23 {
  24. 24 int ret;
  25. 25
  26. 26 rcu_read_lock(); /*禁止抢占*/
  27. 27 ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); /*使用基本通知块回调*/
  28. 28 rcu_read_unlock(); /*使能抢占*/
  29. 29 return ret;
  30. 30 }
  31. 31 EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain);
  32. 32
  33. 33 /*这个函数是在原子操作上下文中调用, 是不能阻塞的*/
  34. 34 int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh,
  35. 35 unsigned long val, void *v)
  36. 36 {
  37. 37 return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
  38. 38 }
  39. 39 EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);

6.2 可阻塞通知块

6.2.1 登记
  1. 1 /**
  2. 2 * blocking_notifier_chain_register - Add notifier to a blocking notifier chain
  3. 3 * @nh: Pointer to head of the blocking notifier chain
  4. 4 * @n: New entry in notifier chain
  5. 5 *
  6. 6 * Adds a notifier to a blocking notifier chain.
  7. 7 * Must be called in process context.
  8. 8 *
  9. 9 * Currently always returns zero.
  10. 10 */
  11. 11 int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
  12. 12 struct notifier_block *n)
  13. 13 {
  14. 14 int ret;
  15. 15
  16. 16 /*
  17. 17 * This code gets used during boot-up, when task switching is
  18. 18 * not yet working and interrupts must remain disabled. At
  19. 19 * such times we must not call down_write().
  20. 20 */
  21. 21 if (unlikely(system_state == SYSTEM_BOOTING)) /*此时是不能阻塞*/
  22. 22 return notifier_chain_register(&nh->head, n);
  23. 23
  24. 24 down_write(&nh->rwsem); /*使用信号灯进行同步, 可能阻塞*/
  25. 25 ret = notifier_chain_register(&nh->head, n); /*基本登记函数*/
  26. 26 up_write(&nh->rwsem); /*释放信号量*/
  27. 27 return ret;
  28. 28 }
  29. 29 EXPORT_SYMBOL_GPL(blocking_notifier_chain_register);
6.2.2 撤销
  1. 1 /**
  2. 2 * blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain
  3. 3 * @nh: Pointer to head of the blocking notifier chain
  4. 4 * @n: Entry to remove from notifier chain
  5. 5 *
  6. 6 * Removes a notifier from a blocking notifier chain.
  7. 7 * Must be called from process context.
  8. 8 *
  9. 9 * Returns zero on success or %-ENOENT on failure.
  10. 10 */
  11. 11 /*该函数是在进程处理过程中调用,可阻塞*/
  12. 12 int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
  13. 13 struct notifier_block *n)
  14. 14 {
  15. 15 int ret;
  16. 16
  17. 17 /*
  18. 18 * This code gets used during boot-up, when task switching is
  19. 19 * not yet working and interrupts must remain disabled. At
  20. 20 * such times we must not call down_write().
  21. 21 */
  22. 22 if (unlikely(system_state == SYSTEM_BOOTING))
  23. 23 return notifier_chain_unregister(&nh->head, n);
  24. 24
  25. 25 down_write(&nh->rwsem);
  26. 26 ret = notifier_chain_unregister(&nh->head, n);
  27. 27 up_write(&nh->rwsem);
  28. 28 return ret;
  29. 29 }
  30. 30 EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
6.2.3 回调
  1. 1 /**
  2. 2 * __blocking_notifier_call_chain - Call functions in a blocking notifier chain
  3. 3 * @nh: Pointer to head of the blocking notifier chain
  4. 4 * @val: Value passed unmodified to notifier function
  5. 5 * @v: Pointer passed unmodified to notifier function
  6. 6 * @nr_to_call: See comment for notifier_call_chain.
  7. 7 * @nr_calls: See comment for notifier_call_chain.
  8. 8 *
  9. 9 * Calls each function in a notifier chain in turn. The functions
  10. 10 * run in a process context, so they are allowed to block.
  11. 11 *
  12. 12 * If the return value of the notifier can be and'ed
  13. 13 * with %NOTIFY_STOP_MASK then blocking_notifier_call_chain()
  14. 14 * will return immediately, with the return value of
  15. 15 * the notifier function which halted execution.
  16. 16 * Otherwise the return value is the return value
  17. 17 * of the last notifier function called.
  18. 18 */
  19. 19 int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
  20. 20 unsigned long val, void *v,
  21. 21 int nr_to_call, int *nr_calls)
  22. 22 {
  23. 23 int ret = NOTIFY_DONE;
  24. 24
  25. 25 /*
  26. 26 * We check the head outside the lock, but if this access is
  27. 27 * racy then it does not matter what the result of the test
  28. 28 * is, we re-check the list after having taken the lock anyway:
  29. 29 */
  30. 30 if (rcu_dereference(nh->head)) {
  31. 31 down_read(&nh->rwsem);
  32. 32 ret = notifier_call_chain(&nh->head, val, v, nr_to_call,
  33. 33 nr_calls);
  34. 34 up_read(&nh->rwsem);
  35. 35 }
  36. 36 return ret;
  37. 37 }
  38. 38 EXPORT_SYMBOL_GPL(__blocking_notifier_call_chain);
  39. 39 /*在进行上下文中调用, 可以阻塞*/
  40. 40 int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
  41. 41 unsigned long val, void *v)
  42. 42 {
  43. 43 return __blocking_notifier_call_chain(nh, val, v, -1, NULL);
  44. 44 }
  45. 45 EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);

6.3 原始通知块操作

  1. 1 /*
  2. 2 * Raw notifier chain routines. There is no protection;
  3. 3 * the caller must provide it. Use at your own risk!
  4. 4 */
  5. 5
  6. 6 /**
  7. 7 * raw_notifier_chain_register - Add notifier to a raw notifier chain
  8. 8 * @nh: Pointer to head of the raw notifier chain
  9. 9 * @n: New entry in notifier chain
  10. 10 *
  11. 11 * Adds a notifier to a raw notifier chain.
  12. 12 * All locking must be provided by the caller.
  13. 13 *
  14. 14 * Currently always returns zero.
  15. 15 */
  16. 16 /*
  17. 17 和基本原始块操作完全相同*/
  18. 18 int raw_notifier_chain_register(struct raw_notifier_head *nh,
  19. 19 struct notifier_block *n)
  20. 20 {
  21. 21 return notifier_chain_register(&nh->head, n);
  22. 22 }
  23. 23 EXPORT_SYMBOL_GPL(raw_notifier_chain_register);
  24. 24
  25. 25 /**
  26. 26 * raw_notifier_chain_unregister - Remove notifier from a raw notifier chain
  27. 27 * @nh: Pointer to head of the raw notifier chain
  28. 28 * @n: Entry to remove from notifier chain
  29. 29 *
  30. 30 * Removes a notifier from a raw notifier chain.
  31. 31 * All locking must be provided by the caller.
  32. 32 *
  33. 33 * Returns zero on success or %-ENOENT on failure.
  34. 34 */
  35. 35 int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
  36. 36 struct notifier_block *n)
  37. 37 {
  38. 38 return notifier_chain_unregister(&nh->head, n);
  39. 39 }
  40. 40 EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
  41. 41
  42. 42 /**
  43. 43 * __raw_notifier_call_chain - Call functions in a raw notifier chain
  44. 44 * @nh: Pointer to head of the raw notifier chain
  45. 45 * @val: Value passed unmodified to notifier function
  46. 46 * @v: Pointer passed unmodified to notifier function
  47. 47 * @nr_to_call: See comment for notifier_call_chain.
  48. 48 * @nr_calls: See comment for notifier_call_chain
  49. 49 *
  50. 50 * Calls each function in a notifier chain in turn. The functions
  51. 51 * run in an undefined context.
  52. 52 * All locking must be provided by the caller.
  53. 53 *
  54. 54 * If the return value of the notifier can be and'ed
  55. 55 * with %NOTIFY_STOP_MASK then raw_notifier_call_chain()
  56. 56 * will return immediately, with the return value of
  57. 57 * the notifier function which halted execution.
  58. 58 * Otherwise the return value is the return value
  59. 59 * of the last notifier function called.
  60. 60 */
  61. 61 int __raw_notifier_call_chain(struct raw_notifier_head *nh,
  62. 62 unsigned long val, void *v,
  63. 63 int nr_to_call, int *nr_calls)
  64. 64 {
  65. 65 return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
  66. 66 }
  67. 67 EXPORT_SYMBOL_GPL(__raw_notifier_call_chain);
  68. 68
  69. 69 int raw_notifier_call_chain(struct raw_notifier_head *nh,
  70. 70 unsigned long val, void *v)
  71. 71 {
  72. 72 return __raw_notifier_call_chain(nh, val, v, -1, NULL);
  73. 73 }
  74. 74 EXPORT_SYMBOL_GPL(raw_notifier_call_chain);

6.4 SRCU通知块

6.4.1 登记
  1. 1 /**
  2. 2 * srcu_notifier_chain_register - Add notifier to an SRCU notifier chain
  3. 3 * @nh: Pointer to head of the SRCU notifier chain
  4. 4 * @n: New entry in notifier chain
  5. 5 *
  6. 6 * Adds a notifier to an SRCU notifier chain.
  7. 7 * Must be called in process context.
  8. 8 *
  9. 9 * Currently always returns zero.
  10. 10 */
  11. 11 /*必须在进程的上下文中调用, 和blocking通知类似*/
  12. 12 int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
  13. 13 struct notifier_block *n)
  14. 14 {
  15. 15 int ret;
  16. 16
  17. 17 /*
  18. 18 * This code gets used during boot-up, when task switching is
  19. 19 * not yet working and interrupts must remain disabled. At
  20. 20 * such times we must not call mutex_lock().
  21. 21 */
  22. 22 if (unlikely(system_state == SYSTEM_BOOTING))
  23. 23 return notifier_chain_register(&nh->head, n);
  24. 24
  25. 25 mutex_lock(&nh->mutex);
  26. 26 ret = notifier_chain_register(&nh->head, n);
  27. 27 mutex_unlock(&nh->mutex);
  28. 28 return ret;
  29. 29 }
  30. 30 EXPORT_SYMBOL_GPL(srcu_notifier_chain_register);
6.4.2 撤销
  1. 1 /**
  2. 2 * srcu_notifier_chain_unregister - Remove notifier from an SRCU notifier chain
  3. 3 * @nh: Pointer to head of the SRCU notifier chain
  4. 4 * @n: Entry to remove from notifier chain
  5. 5 *
  6. 6 * Removes a notifier from an SRCU notifier chain.
  7. 7 * Must be called from process context.
  8. 8 *
  9. 9 * Returns zero on success or %-ENOENT on failure.
  10. 10 */
  11. 11 /*必须在进程的上下文中调用, 和blocking通知类似*/
  12. 12 int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
  13. 13 struct notifier_block *n)
  14. 14 {
  15. 15 int ret;
  16. 16
  17. 17 /*
  18. 18 * This code gets used during boot-up, when task switching is
  19. 19 * not yet working and interrupts must remain disabled. At
  20. 20 * such times we must not call mutex_lock().
  21. 21 */
  22. 22 if (unlikely(system_state == SYSTEM_BOOTING))
  23. 23 return notifier_chain_unregister(&nh->head, n);
  24. 24
  25. 25 mutex_lock(&nh->mutex);
  26. 26 ret = notifier_chain_unregister(&nh->head, n);
  27. 27 mutex_unlock(&nh->mutex);
  28. 28 synchronize_srcu(&nh->srcu);
  29. 29 return ret;
  30. 30 }
  31. 31 EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
6.4.3 回调
  1. 1 /**
  2. 2 * __srcu_notifier_call_chain - Call functions in an SRCU notifier chain
  3. 3 * @nh: Pointer to head of the SRCU notifier chain
  4. 4 * @val: Value passed unmodified to notifier function
  5. 5 * @v: Pointer passed unmodified to notifier function
  6. 6 * @nr_to_call: See comment for notifier_call_chain.
  7. 7 * @nr_calls: See comment for notifier_call_chain
  8. 8 *
  9. 9 * Calls each function in a notifier chain in turn. The functions
  10. 10 * run in a process context, so they are allowed to block.
  11. 11 *
  12. 12 * If the return value of the notifier can be and'ed
  13. 13 * with %NOTIFY_STOP_MASK then srcu_notifier_call_chain()
  14. 14 * will return immediately, with the return value of
  15. 15 * the notifier function which halted execution.
  16. 16 * Otherwise the return value is the return value
  17. 17 * of the last notifier function called.
  18. 18 */
  19. 19 int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
  20. 20 unsigned long val, void *v,
  21. 21 int nr_to_call, int *nr_calls)
  22. 22 {
  23. 23 int ret;
  24. 24 int idx;
  25. 25
  26. 26 idx = srcu_read_lock(&nh->srcu); /*使用srcu来加锁*/
  27. 27 ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
  28. 28 srcu_read_unlock(&nh->srcu, idx);
  29. 29 return ret;
  30. 30 }
  31. 31 EXPORT_SYMBOL_GPL(__srcu_notifier_call_chain);
  32. 32 /*在进程的上下文中调用, 可以阻塞*/
  33. 33 int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
  34. 34 unsigned long val, void *v)
  35. 35 {
  36. 36 return __srcu_notifier_call_chain(nh, val, v, -1, NULL);
  37. 37 }
  38. 38 EXPORT_SYMBOL_GPL(srcu_notifier_call_chain);
6.4.4 初始化
  1. 1 /**
  2. 2 * srcu_init_notifier_head - Initialize an SRCU notifier head
  3. 3 * @nh: Pointer to head of the srcu notifier chain
  4. 4 *
  5. 5 * Unlike other sorts of notifier heads, SRCU notifier heads require
  6. 6 * dynamic initialization. Be sure to call this routine before
  7. 7 * calling any of the other SRCU notifier routines for this head.
  8. 8 *
  9. 9 * If an SRCU notifier head is deallocated, it must first be cleaned
  10. 10 * up by calling srcu_cleanup_notifier_head(). Otherwise the head's
  11. 11 * per-cpu data (used by the SRCU mechanism) will leak.
  12. 12 */
  13. 13 /*因为SRCU通知不能通过宏来初始化,必须要专门定义一个初始化函数来初始化srcu的通知块参数*/
  14. 14 void srcu_init_notifier_head(struct srcu_notifier_head *nh)
  15. 15 {
  16. 16 mutex_init(&nh->mutex);
  17. 17 if (init_srcu_struct(&nh->srcu) < 0)
  18. 18 BUG();
  19. 19 nh->head = NULL;
  20. 20 }
  21. 21 EXPORT_SYMBOL_GPL(srcu_init_notifier_head);

本文参考:

http://blog.csdn.net/tommy_wxie/article/details/7963926

http://blog.sina.com.cn/s/blog_5426448c0100ntqb.html

http://hi.baidu.com/yskcg/item/8947658d7cbdd8c0b07154a5

http://geyingzhen.blog.163.com/blog/static/6558665520091165531675/

Linux内核通知链分析【转】的更多相关文章

  1. Linux 内核通知链随笔【中】

    关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不同子系统之间的通信,通知链只能用于内核不同子系统之间的通信.那么内核通知链到底是怎么工作的?我们如何才能用好通知链? ...

  2. Linux 内核通知链随笔【中】【转】

    转自:http://blog.chinaunix.net/uid-23069658-id-4364171.html 关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不 ...

  3. Linux 内核通知链机制的原理及实现

    一.概念: 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子 系统,Linux内核提供了通知链的机制.通 ...

  4. Linux内核通知链模块

    通知链描写叙述 大多数内核子系统都是相互独立的,因此某个子系统可能对其他子系统产生的事件感兴趣. 为了满足这个需求,也即是让某个子系统在发生某个事件时通知其他的子系统.Linux内核提供了通知链的机制 ...

  5. Linux内核通知链机制的原理及实现【转】

    转自:http://www.cnblogs.com/armlinux/archive/2011/11/11/2396781.html 一.概念: 大多数内核子系统都是相互独立的,因此某个子系统可能对其 ...

  6. [Linux] 内核通知链 notifier

    Linux 内核中每个模块之间都是独立的,如果模块需要感知其他模块的事件,就需要用到内核通知链. 最典型的通知链应用就是 LCD 和 TP 之间,TP 需要根据 LCD 的亮灭来控制是否打开关闭触摸功 ...

  7. Linux内核调试方法总结之内核通知链

    Linux内核通知链notifier 1.内核通知链表简介(引用网络资料)    大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在 ...

  8. Linux内核基础--事件通知链(notifier chain)

    转载: http://blog.csdn.net/wuhzossibility/article/details/8079025 http://blog.chinaunix.net/uid-277176 ...

  9. 深入理解Linux网络技术内幕——Notification内核通知表链

    为什么要有内核通知表链:     Linux由多个相互依赖的子系统组成.其中一些子系统可能需要对其他子系统的一些事件感兴趣.这样子系统之间需要一些通信机制来实现这一功能.     在接触Notific ...

随机推荐

  1. iOS-开发中的时间处理

    做App避免不了要和时间打交道,关于时间的处理,里面有不少门道,远不是一行API调用,获取当前系统时间这么简单.我们需要了解与时间相关的各种API之间的差别,再因场景而异去设计相应的机制. 时间的形式 ...

  2. bzoj4031-小Z的房间

    题目 给一个\(n\*m\)的矩阵,每个点可能为"."或"*",有多少种方法把矩阵中的点全部连接起来,并且每两个点之间只有一条路径. 分析 题目所求的是一个矩阵 ...

  3. AOP拦截+权限验证+返回默认接口对象

    接口如:public IList<string> TestAOP(string token); public IMethodReturn Invoke(IMethodInvocation ...

  4. BZOJ1013:[JSOI2008]球形空间产生器——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=1013 Description 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在 ...

  5. Django Model 数据表

    Django Model 定义语法 版本:1.7主要来源:https://docs.djangoproject.com/en/1.7/topics/db/models/ 简单用法 from djang ...

  6. git使用笔记(十一)rebase

    By francis_hao    Oct 22,2017   git-rebase,改变commit的基础参照 概要 git rebase [-i | --interactive] [options ...

  7. js正则表达式,判断字符串是否以数字组结尾,并取出结尾的数字

    js正则表达式,判断字符串是否以数字组结尾,并取出结尾的数字 <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  8. maven的tomcat插件如何进行debug调试

    利用maven来部署工程时,一般采用的是tomcat插件,使项目在tomcat上面运行,那么这个debug调试是如何进行呢? 我们在调试的时候问题: 会提示找不到资源,那么如何进行修改呢,方法两个: ...

  9. jq 正则

    if(_each_this_type_name == 'post_num'){ var patrn = /^[a-zA-Z0-9]{3,12}$/; if(!patrn.test(_each_this ...

  10. 自定义函数,根据p个数,自适应剧中效果

    //最后投保进程line-height 自适应居中; function self_adaption(){ $('.last_place').each(function(){ var _this = $ ...