分类: LINUX
2009-07-11 21:58:14
通知者链用来发送状态改变信息给请求他们的代码区。不像硬编码机制,通知者提供了多重技巧使得有意思事件产生时得到提醒。通知者原来用来传递网络事件给内核中关注部分,现在用于很多其他目的。内核为重要事件实现了预定义的通知者,这些提醒例子如下:
死亡提醒, 当内核函数触发了一个陷阱或违例错误发送,由oops页错误或断点命中引发。例如为一个医学级别卡写设备驱动,你可能想注册自己给死亡提醒者,以在内核崩溃发生时关闭医疗电子信号。
网路设备提醒:网络接口启动或关闭时产生。
CPU频率提醒:当处理器频率跃变时分发出去。
因特网地址提醒:当网络接口的IP地址发生变化被检测时发送。
一个用户提醒的例子就是高级数据链路控制(HDLC)协议驱动drivers/net/wan/hdlc.c,它注册自己给网络设备通知链以察觉载波变化。
要附加你的代码到通知链,你要注册一个时间处理函数给相关的链。当关注的事件产生时,一个事件识别符和一个通知特定的参数作为参数传递给处理函数。要定义一个定制通知链,你要另外实现事件检测到时引发链的基础代码。
下面代码包含了使用预定义和用户定义的通知者例子,下面包含了使用的通知链和产生事件的简短描叙。
Die Notifier Chain(die_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);
}
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()注册它。
早于