现在的位置: 首页 > 综合 > 正文

深入理解linux网络技术内幕–通知链

2014年05月16日 ⁄ 综合 ⁄ 共 6811字 ⁄ 字号 评论关闭

一 通知链概述:

  大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,
Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。其实和系统调用signal的思想差不多。

注意:通知链只在内核子系统之间使用。

二 定义:

    为了方便相互联系紧密的子系统之间的互交,内核中应用了通知链。通知链表的节点类型为notifier_block,其定义如下:

struct notifier_block {
	int (*notifier_call)(struct notifier_block *, unsigned long, void *);
	struct notifier_block __rcu *next;
	int priority;
};

其中最重要的就是notifier_call这个函数指针,表示了这个节点所对应的要运行的那个函数。next指向下一个节点,即当前事件发生时还要继续执行的那些节点。该函数在: include/linux/notifier.h

三 通知链的注册与解注册:

     通知链的注册与解注册分别由函数:int
notifier_chain_register
(struct
notifier_block **nl,
struct notifier_block
*n) 和函数:int
notifier_chain_unregister(strut
notifier_block **nl,
struct notifier_block
*n)
来实现的,由于上面的两个函数用法极其简单,就不在详细介绍了。

四 通知链上的事件:

  当有事件发生时,就使用notifier_call_chain向某个通知链表发送消息。该函数的定义和解释如下所示:

/**
 * notifier_call_chain - Informs the registered notifiers about an event.
 *	@nl:		Pointer to head of the blocking notifier chain
 *	@val:		Value passed unmodified to notifier function
 *	@v:		Pointer passed unmodified to notifier function
 *	@nr_to_call:	Number of notifier functions to be called. Don't care
 *			value of this parameter is -1.
 *	@nr_calls:	Records the number of notifications sent. Don't care
 *			value of this field is NULL.
 *	@returns:	notifier_call_chain returns the value returned by the
 *			last notifier function called.
 */
static int __kprobes notifier_call_chain(struct notifier_block **nl,
					unsigned long val, void *v,
					int nr_to_call,	int *nr_calls)
{
	int ret = NOTIFY_DONE;
	struct notifier_block *nb, *next_nb;

	nb = rcu_dereference_raw(*nl);

	while (nb && nr_to_call) {
		next_nb = rcu_dereference_raw(nb->next);

#ifdef CONFIG_DEBUG_NOTIFIERS
		if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
			WARN(1, "Invalid notifier called!");
			nb = next_nb;
			continue;
		}
#endif
		ret = nb->notifier_call(nb, val, v);

		if (nr_calls)
			(*nr_calls)++;

		if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
			break;
		nb = next_nb;
		nr_to_call--;
	}
	return ret;
}

函数路径:kernel/kernel/notifier.c

五 内核实际例子:

   代码路径:kernel/net/ipv4/fib_frontend.c

六 测试实例:

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

 #include <asm/uaccess.h>
 2:  #include <linux/types.h>
 3:  #include <linux/kernel.h>
 4:  #include <linux/sched.h>
 5:  #include <linux/notifier.h>
 6:  #include <linux/init.h>
 7:  #include <linux/types.h>
 8:  #include <linux/module.h>
 9:  MODULE_LICENSE("GPL");
10:  
11:  /*
12:  * 定义自己的通知链头结点以及注册和卸载通知链的外包函数
13:  */
14:  
15:  /*
16:  * RAW_NOTIFIER_HEAD是定义一个通知链的头部结点,
17:  * 通过这个头部结点可以找到这个链中的其它所有的notifier_block
18:  */
19:  
20:  static RAW_NOTIFIER_HEAD(test_chain);
21:  
22:  /*
23:  * 自定义的注册函数,将notifier_block节点加到刚刚定义的test_chain这个链表中来
24:  * raw_notifier_chain_register会调用notifier_chain_register
25:  */
26:  
27:  int register_test_notifier(struct notifier_block *nb)
28:  {
29:          return raw_notifier_chain_register(&test_chain, nb);
30:  }
31:  EXPORT_SYMBOL(register_test_notifier);
32:  
33:  int unregister_test_notifier(struct notifier_block *nb)
34:  {
35:          return raw_notifier_chain_unregister(&test_chain, nb);
36:  }
37:  EXPORT_SYMBOL(unregister_test_notifier);
38:  
39:  /*
40:  * 自定义的通知链表的函数,即通知test_chain指向的链表中的所有节点执行相应的函数
41:  */
42:  
43:  int test_notifier_call_chain(unsigned long val, void *v)
44:  {
45:          return raw_notifier_call_chain(&test_chain, val, v);
46:  }
47:  EXPORT_SYMBOL(test_notifier_call_chain);
48:  
49:  /*
50:  * init and exit
51:  */
52:  
53:  static int __init init_notifier(void)
54:  {
55:          printk("init_notifier\n");
56:          return 0;
57:  }
58:  
59:  static void __exit exit_notifier(void)
60:  {
61:          printk("exit_notifier\n");
62:  }
63:  module_init(init_notifier);
64:  module_exit(exit_notifier);

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

 #include <asm/uaccess.h>
  2:  #include <linux/types.h>
  3:  #include <linux/kernel.h>
  4:  #include <linux/sched.h>
  5:  #include <linux/notifier.h>
  6:  #include <linux/init.h>
  7:  #include <linux/types.h>
  8:  #include <linux/module.h>
  9:  MODULE_LICENSE("GPL");
 10:  
 11:  /*
 12:  * 注册通知链
 13:  */
 14:  
 15:  extern int register_test_notifier(struct notifier_block*);
 16:  
 17:  extern int unregister_test_notifier(struct notifier_block*);
 18:  
 19:  static int test_event1(struct notifier_block *this, unsigned long event, void *ptr)
 20:  {
 21:          printk("In Event 1: Event Number is %d\n", event);
 22:          return 0;
 23:  }
 24:  
 25:  static int test_event2(struct notifier_block *this, unsigned long event, void *ptr)
 26:  {
 27:          printk("In Event 2: Event Number is %d\n", event);
 28:          return 0;
 29:  }
 30:  
 31:  static int test_event3(struct notifier_block *this, unsigned long event, void *ptr)
 32:  {
 33:          printk("In Event 3: Event Number is %d\n", event);
 34:          return 0;
 35:  }
 36:  
 37:  /*
 38:  * 事件1,该节点执行的函数为test_event1
 39:  */
 40:  
 41:  static struct notifier_block test_notifier1 =
 42:  {
 43:          .notifier_call = test_event1,
 44:  };
 45:  
 46:  /*
 47:  * 事件2,该节点执行的函数为test_event1
 48:  */
 49:  
 50:  static struct notifier_block test_notifier2 =
 51:  {
 52:          .notifier_call = test_event2,
 53:  };
 54:  
 55:  /*
 56:  * 事件3,该节点执行的函数为test_event1
 57:  */
 58:  
 59:  static struct notifier_block test_notifier3 =
 60:  {
 61:          .notifier_call = test_event3,
 62:  };
 63:  
 64:  /*
 65:  * 对这些事件进行注册
 66:  */
 67:  
 68:  static int __init reg_notifier(void)
 69:  {
 70:          int err;
 71:          printk("Begin to register:\n");
 72:  
 73:          err = register_test_notifier(&test_notifier1);
 74:          if (err)
 75:          {
 76:                  printk("register test_notifier1 error\n");
 77:                  return -1;
 78:          }
 79:          printk("register test_notifier1 completed\n");
 80:  
 81:          err = register_test_notifier(&test_notifier2);
 82:          if (err)
 83:          {
 84:                  printk("register test_notifier2 error\n");
 85:                  return -1;
 86:          }
 87:          printk("register test_notifier2 completed\n");
 88:  
 89:          err = register_test_notifier(&test_notifier3);
 90:          if (err)
 91:          {
 92:                  printk("register test_notifier3 error\n");
 93:                  return -1;
 94:          }
 95:          printk("register test_notifier3 completed\n");
 96:          return err;
 97:  }
 98:  
 99:  /*
100:  * 卸载刚刚注册了的通知链
101:  */
102:  
103:  static void __exit unreg_notifier(void)
104:  {
105:          printk("Begin to unregister\n");
106:          unregister_test_notifier(&test_notifier1);
107:          unregister_test_notifier(&test_notifier2);
108:          unregister_test_notifier(&test_notifier3);
109:          printk("Unregister finished\n");
110:  }
111:  module_init(reg_notifier);
112:  module_exit(unreg_notifier);

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

 1:  #include <asm/uaccess.h>
 2:  #include <linux/types.h>
 3:  #include <linux/kernel.h>
 4:  #include <linux/sched.h>
 5:  #include <linux/notifier.h>
 6:  #include <linux/init.h>
 7:  #include <linux/types.h>
 8:  #include <linux/module.h>
 9:  MODULE_LICENSE("GPL");
10:  
11:  extern int test_notifier_call_chain(unsigned long val, void *v);
12:  
13:  /*
14:  * 向通知链发送消息以触发注册了的函数
15:  */
16:  
17:  static int __init call_notifier(void)
18:  {
19:          int err;
20:          printk("Begin to notify:\n");
21:  
22:  /*
23:  * 调用自定义的函数,向test_chain链发送消息
24:  */
25:  
26:          printk("==============================\n");
27:          err = test_notifier_call_chain(1, NULL);
28:          printk("==============================\n");
29:          if (err)
30:                  printk("notifier_call_chain error\n");
31:          return err;
32:  }
33:  
34:  
35:  static void __exit uncall_notifier(void)
36:  {
37:          printk("End notify\n");
38:  }
39:  module_init(call_notifier);
40:  module_exit(uncall_notifier);

分别注册三个驱动,查看打印,可以看到如下打印:

1: init_notifier

2: Begin to register:

3: register test_notifier1 completed

4: register test_notifier2 completed

5: register test_notifier3 completed

6: Begin to notify:

 7: ==============================

8: In Event 1: Event Number is 1

 9: In Event 2: Event Number is 1

10: In Event 3: Event Number is 1

11: ==============================

 

抱歉!评论已关闭.