Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1951166
  • 博文数量: 383
  • 博客积分: 10011
  • 博客等级: 上将
  • 技术积分: 4061
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-24 18:53
文章分类

全部博文(383)

文章存档

2011年(1)

2010年(9)

2009年(276)

2008年(97)

我的朋友

分类: LINUX

2009-07-11 21:58:14

通知者链用来发送状态改变信息给请求他们的代码区。不像硬编码机制,通知者提供了多重技巧使得有意思事件产生时得到提醒。通知者原来用来传递网络事件给内核中关注部分,现在用于很多其他目的。内核为重要事件实现了预定义的通知者,这些提醒例子如下:
死亡提醒 当内核函数触发了一个陷阱或违例错误发送,由oops页错误或断点命中引发。例如为一个医学级别卡写设备驱动,你可能想注册自己给死亡提醒者,以在内核崩溃发生时关闭医疗电子信号。
网路设备提醒:网络接口启动或关闭时产生。
CPU频率提醒:当处理器频率跃变时分发出去。
因特网地址提醒:当网络接口的IP地址发生变化被检测时发送。
一个用户提醒的例子就是高级数据链路控制(HDLC)协议驱动drivers/net/wan/hdlc.c,它注册自己给网络设备通知链以察觉载波变化。

要附加你的代码到通知链,你要注册一个时间处理函数给相关的链。当关注的事件产生时,一个事件识别符和一个通知特定的参数作为参数传递给处理函数。要定义一个定制通知链,你要另外实现事件检测到时引发链的基础代码。
下面代码包含了使用预定义和用户定义的通知者例子,下面包含了使用的通知链和产生事件的简短描叙。
Die Notifier Chaindie_chain my_die_event_handler()使用regist_die_notifier()附加给死亡通知链,die_chain。要触发my_die_event_handler(),在你的代码某处引入一个不合理解引用,像下面的:
int *q = 0;
*q = l;
当这个代码片段执行时,my_die_event_handler()得到调用,你可以看到下面这样的消息:
my_die_event_handler: oops! at EIP=f00350e7
死亡事件通知传die_args结构体给已经注册的事件处理函数。这个参数在当错误发生时包含了指向regs结构体的指针,该结构体带有处理器寄存器的快照。my_die_event_handler()打印了指令指针寄存器的内容。

Netdevice Notifier Chain(netdev_chain): my_dev_event_handler()使用register_netdevice_notifer()附加到网络设备通知链netdev_chain。你可以改变网络接口状态如以太网(ethX)和环回(lo)来产生这个事件: bash>ifconfig eth0 up 这样导致my_dev_event_handler()的执行。一个包含网络接口名字的指向struct net_device的指针作为参数传给该处理函数。 my_dev_event_handler()使用这个信息产生下列信息:
my_dev_event_handler: Val=1, Interface=eth0
Val=1
对应于定义在include/linux/notifier.h中的NETDEV_UP事件。

User-Defined Notifier Chain程序也实现了用户定义通知链my_noti_chain。假设你想一个事件在用户读进程文件系统中一个特定文件时产生。添加下列语句到关联的procfs读程序中:
blocking_notifier_call_chain(&my_noti_chain, 100, NULL);
这将导致你读/proc文件时my_event_handler()的执行,并产生下面信息:
my_event_handler: Val=100
Val
包含了产生的事件标识符100,函数参数没有使用到。

#include
#include
#include
#include

/* Die Notifier Definition */
static struct notifier_block my_die_notifier = {
    .notifier_call = my_die_event_handler,
}
;
/* Die notification event handler */
int my_die_event_handler(struct notifier_block *self, unsigned long val, void *data)
{
    struct die_args *args = (struct die_args *)data;

    if (val == 1) { /* '1' corresponds to an "oops" */
        printk("my_die_event: OOPs! at EIP=%lx\n", args->regs->eip);
    }
/* else ignore */
    
return 0;
}

/* Net Device notifier definition */

static struct notifier_block my_dev_notifier = {
   .notifier_call = my_dev_event_handler,
}
;

/* Net Device notification event handler */
int my_dev_event_handler(struct notifier_block *self,  unsigned long val, void *data)
{
    printk("my_dev_event: Val=%ld, Interface=%s\n", val,((struct net_device *) data)->name);
    return 0;
}


/* User-defined notifier chain implementation */
static BLOCKING_NOTIFIER_HEAD(my_noti_chain);

static struct
 notifier_block my_notifier = {
    .notifier_call = my_event_handler,
}
;

/* User-defined notification event handler */
int my_event_handler(struct notifier_block *self, unsigned long val, void *data)
{
     printk("my_event: Val=%ld\n", val);
     return 0;
}

/* Driver Initialization */
static int __init my_init(void)
{
  
/* ... */

  
/* Register Die Notifier */
  register_die_notifier(&my_die_notifier);

  
/* Register Net Device Notifier */
  register_netdevice_notifier(&my_dev_notifier);

  
/* Register a user-defined Notifier */
  blocking_notifier_chain_register(&my_noti_chain, &my_notifier);
  
/* ... */
}
   
当你的模块从内核中释放时,你得在通知链里面反注册事件处理函数。例如,如果你卸载代码后启动和关闭网络接口,就会得到oops,除非在模块的release函数中执行unregister_netdevice_notifier(&my_dev_notifier)。这是因为通知链继续认为处理函数代码是合理的,尽管它就被拉出了内核。
my_noti_chain
通过使用BLOCKING_NOTIFIER_HEAD()声明为阻塞通知并通过blocking_notifier_chain_register()注册。这意味着通知处理函数经常在进程上下文中被调用。所以处理函数my_event_handler()可以进入休眠。如果你的通知处理函数可以在中断上下文中调用,使用ATOMIC_NOTIFIER_HEAD()声明并且用atomic_notifier_chain_register()注册它。
早于2.6.17的内核只支持一个通用通知链。通知注册函数notifier_chain_register()使用自旋锁被内部保护着,但是通知链分发事件到通知处理函数(notifier_call_chain())的代码确实没有加锁。之所以没有锁是基于处理函数进入休眠、运行时反注册自己和在中断上下文中得到调用的可能性。但没有锁的实现引入了竞争条件。新的通知API是基于原有接口构建并意图克服它的限制。

阅读(2365) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~