linux 内核通知链

</pre>概述:<p></p><p>内核的各子系统之间具有较强的相互依赖性,因此一个子系统中侦测或者发生的事件,其他子系统可能感兴趣,所以<span style="font-family:Calibri">linux</span><span style="font-family:宋体">使用通知链实现这种交互。</span></p><p>通知链仅存在于各内核子系统中,内核与用户层的交互使用其他机制。</p><p></p><p></p><p>1、 主要数据结构:</p><p>struct notifier_block {</p><p>int (*notifier_call)(struct notifier_block *, unsigned long, void *);</p><p>struct notifier_block *next;</p><p>int priority;</p><p>};</p><p>其中<span style="font-family:Calibri">notifier_call</span><span style="font-family:宋体">是回调函数,当某个通知链发生时,会遍历该链表上所有的成员,执行其</span><span style="font-family:Calibri">notifier_call</span><span style="font-family:宋体">函数;</span><span style="font-family:Calibri">next</span><span style="font-family:宋体">用于通知链表,指向下一个链成员;</span><span style="font-family:Calibri">priority</span><span style="font-family:宋体">表示优先级,不过该成员值很少被使用,所以一般按链成员的加入顺序决定执行顺序。</span></p><p>2、 链类型</p><p>内核代码中一般把通知链命名为<span style="font-family:Calibri">xxx_chain, xxx_nofitier_chain</span><span style="font-family:宋体">这种形式的变量名。围绕核心数据结构</span><span style="font-family:Calibri">notifier_block</span><span style="font-family:宋体">,内核定义了四种通知链类型:</span></p><p>1) 原子通知链</p><p>通知链元素的回调函数(当事件发生时要执行的函数)在中断或原子操作上下文中运行,不允许阻塞。</p><p>struct atomic_notifier_head {</p><p>spinlock_t lock;</p><p>struct notifier_block *head;</p><p>};</p><p>2) 可阻塞通知链</p><p>通知链元素的回调函数在进程上下文执行,允许阻塞</p><p>struct blocking_notifier_head {</p><p>struct rw_semaphore rwsem;</p><p>struct notifier_block *head;</p><p>};</p><p>3) 原始通知链</p><p>对通知链元素的回调函数的执行没有要求,由相应的内核子系统来保证通知链的回调函数的执行环境</p><p>struct raw_notifier_head {</p><p>struct notifier_block *head;</p><p>};</p><p>4) SRCU<span style="font-family:宋体">通知链</span></p><p>对阻塞通知链的一种改进</p><p>struct srcu_notifier_head {</p><p>struct mutex mutex;</p><p>struct srcu_struct srcu;</p><p>struct notifier_block *head;</p><p>};</p><p></p><p>3、 链注册</p><p>当某个内核子系统对于一个通知链感兴趣时,就会调用链注册函数,注册到该链表上。</p><p>通用链注册函数:</p><p>上面介绍的四种通知链类型,其注册链函数最后都是调用的通用链注册函数,通用链的注册函数如下:</p><p>static int notifier_chain_register(struct notifier_block **nl,</p><p>struct notifier_block *n)</p><p>{</p><p>while ((*nl) != NULL) {</p><p>if (n->priority > (*nl)->priority)</p><p>break;</p><p>nl = &((*nl)->next);</p><p>}</p><p>n->next = *nl;</p><p>rcu_assign_pointer(*nl, n);</p><p>return 0;</p><p>}</p><p>该函数实现将新的链成员添加到通知链的链尾</p><p></p><p>4、 通知链上的消息</p><p>我们主要介绍通用的通知事件触发函数notifier_call_chain,上述四类型的通知链的通知事件都会调用该通用函数。</p><p></p><p></p><p></p><p>/**</p><p> * notifier_call_chain - Informs the registered notifiers about an event.</p><p> * @nl: Pointer to head of the blocking notifier chain</p><p> * @val: Value passed unmodified to notifier function</p><p> * @v: Pointer passed unmodified to notifier function</p><p> * @nr_to_call: Number of notifier functions to be called. Don't care</p><p> * value of this parameter is -1.</p><p> * @nr_calls: Records the number of notifications sent. Don't care</p><p> * value of this field is NULL.</p><p> * @returns: notifier_call_chain returns the value returned by the</p><p> * last notifier function called.</p><p> */</p><p>static int __kprobes notifier_call_chain(struct notifier_block **nl,</p><p>unsigned long val, void *v,</p><p>int nr_to_call, int *nr_calls)</p><p>{</p><p>int ret = NOTIFY_DONE;</p><p>struct notifier_block *nb, *next_nb;</p><p></p><p>nb = rcu_dereference(*nl);</p><p></p><p>while (nb && nr_to_call) {</p><p>next_nb = rcu_dereference(nb->next);</p><p></p><p>#ifdef CONFIG_DEBUG_NOTIFIERS</p><p>if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {</p><p>WARN(1, "Invalid notifier called!");</p><p>nb = next_nb;</p><p>continue;</p><p>}</p><p>#endif</p><p>ret = nb->notifier_call<span style="color:rgb(0,112,192)">(nb, val, v);</span></p><p></p><p>if (nr_calls)</p><p>(*nr_calls)++;</p><p></p><p>if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)</p><p>break;</p><p>nb = next_nb;</p><p>nr_to_call--;</p><p>}</p><p>return ret;</p><p>}</p><p>该函数的作用是遍历通知链表nl,调用该通知链表上每一个链元素的回调函数,让各回调函数对该通知做相应的处理。</p><p>其中<span style="font-family:Calibri">nb</span><span style="font-family:宋体">代表链元素,</span><span style="font-family:Calibri">val</span><span style="font-family:宋体">代表通知事件类型,</span><span style="font-family:Calibri">v</span><span style="font-family:宋体">的值则在不同情况下有不同用处,根据通知事件类型的不同,其传递的值也不尽相同。</span></p><p><span style="font-family:宋体"></span></p><p><span style="font-family:宋体"></span></p><p><span style="font-family:宋体"></span></p><p><span style="font-family:宋体"></span></p><p>实现一个简单的链注册</p><p>主要使用原始通知链的相关函数,主要代码如下:</p><p>chain_server.c</p><p>在该文件中,主要实现<span style="font-family:Calibri">subsystem</span><span style="font-family:宋体">的注册链函数、链事件通知函数的定义,以及链事件类型的定义,并注册一个</span><span style="font-family:Calibri">subsystem</span><span style="font-family:宋体">链元素</span></p><p><span style="font-family:宋体"></span></p><pre name="code" class="cpp">#include <linux/init.h>
#include <linux/kernel.h>       
#include <linux/fs.h>   
#include <linux/module.h>
#include <linux/notifier.h>
static RAW_NOTIFIER_HEAD(subsystem_chain);
#define SUBSYSTEM_CHAIN_INIT 0x90U
int register_subsystem_notifier(struct notifier_block *nb)
{
  return raw_notifier_chain_register(&subsystem_chain, nb);
}
EXPORT_SYMBOL(register_subsystem_notifier);
int unregister_subsystem_notifier(struct notifier_block *nb)
{
  return raw_notifier_chain_unregister(&subsystem_chain, nb);
}
EXPORT_SYMBOL(unregister_subsystem_notifier);
int call_subsystem_notifiers(unsigned long val, void *v)
{
  return raw_notifier_call_chain(&subsystem_chain, val, v);
}
EXPORT_SYMBOL(call_subsystem_notifiers);

static int subsystem_event(struct notifier_block *nb, unsigned long event,void *v)
{
  switch(event)
  {
        case SUBSYSTEM_CHAIN_INIT:
                printk(KERN_INFO"=====>Get subsystem chain event SUBSYSTEM_CHAIN_INIT\n");
                break;
        default:
                printk(KERN_ERR"Unknown event num %ld\n", event);
  }
  return NOTIFY_DONE;
}

static  struct notifier_block subsystem_notifier ={
        .notifier_call=subsystem_event,
};

static int __init chain_server_register(void)
{
  printk("chain server init (subsystem server init)\n");
  register_subsystem_notifier(&subsystem_notifier);
  return 0;
}
static void __exit chain_server_unregister(void)
{
  printk("chain server exit\n");
  unregister_subsystem_notifier(&subsystem_notifier);
}
module_init(chain_server_register);
module_exit(chain_server_unregister);
MODULE_LICENSE("GPL");
chain_client.c
该文件主要是在初始化时,产生一个链事件通知,然后由感兴趣的链元素调用相关的call函数执行(此时chain_server.c中的subsystem_event则会执行)。
#include <linux/init.h>
#include <linux/kernel.h>      
#include <linux/fs.h>   
#include <linux/module.h>
#include <linux/notifier.h>
#define SUBSYSTEM_CHAIN_INIT 0x90U
extern int call_subsystem_notifiers(unsigned long val, void *v);
static int __init chain_client_register(void)
{
  printk(" chain client init (subsystem client)\n");
  call_subsystem_notifiers(SUBSYSTEM_CHAIN_INIT, NULL);
  return 0;
}
static void __exit chain_client_unregister(void)
{
  printk("chain client exit\n");

}
module_init(chain_client_register);
module_exit(chain_client_unregister);
MODULE_LICENSE("GPL");

输出结果如下:

至此完成通知链的学习。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值