Kernel提供一個notifiers/notifier chains的機制,這是publish-and-subscribe的機制,也就是需要的人自己去訂閱(join到某個notifier chain中),當這個chain的provider有事件要發布,就publish出來(發布給join這個chain的所有人)。
kernel中也提供了一些notifier,如reboot,可以透過register_reboot_notifier()訂閱,用unregister_reboot_notifier()取消訂閱。我們也可以自訂自己的notifier,以下例子就是自訂一個notifier。此一範例為透過寫入/proc/brook_notifier將資料publish給訂閱brook_notifier_list的scbscriber
notifier publisher
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include "notifier.h"
MODULE_LICENSE("GPL");
// 宣告一個新的notifier list – brook_notifier_list
BLOCKING_NOTIFIER_HEAD(brook_notifier_list);
// 訂閱brook_notifier_list事件的wrapper function
int register_brook_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&brook_notifier_list, nb);
}
EXPORT_SYMBOL(register_brook_notifier);
// 取消訂閱brook_notifier_list事件的wrapper function
int unregister_brook_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&brook_notifier_list, nb);
}
EXPORT_SYMBOL(unregister_brook_notifier);
// procfs的write function
static int write_proc(struct file *filp, const char __user *buf,
unsigned long count, void *data)
{
char *p = kzalloc(sizeof(char) * count, GFP_KERNEL);
if (!p) {
printk("no mem\n");
return -ENOMEM;
}
if (copy_from_user(p, buf, count)) {
printk("fault\n");
return -EFAULT;
}
printk("%s(): msg=\"%s\"\n", __FUNCTION__, p);
// 將事件published給brook_notifier_list的subscriber
blocking_notifier_call_chain(&brook_notifier_list, brook_num1, (void*)p);
kfree(p);
return count;
}
static int __init init_modules(void)
{
struct proc_dir_entry *ent;
ent = create_proc_entry("brook_notifier", S_IFREG | S_IWUSR, NULL);
if (!ent) {
printk("create proc child failed\n");
} else {
ent->write_proc = write_proc;
}
return 0;
}
static void __exit exit_modules(void)
{
remove_proc_entry("brook_notifier", NULL);
}
module_init(init_modules);
module_exit(exit_modules);
notifier subscriber
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/notifier.h>
#include "notifier.h"
MODULE_LICENSE("GPL");
// callback function, 當brook_notifier_list有事件發生時, 會呼叫該function
static int brook_notify_sys(struct notifier_block *this,
unsigned long code, void *data)
{
printk("%s(): code=%ld, msg=\"%s\"\n", __FUNCTION__, code, (char*)data);
return 0;
}
// 宣告要註冊到brook_notifier_list的struct
static struct notifier_block brook_notifier = {
.notifier_call = brook_notify_sys,
};
static int __init init_modules(void)
{
// 將brook_notifier註冊到brook_notifier_list
register_brook_notifier(&brook_notifier);
return 0;
}
static void __exit exit_modules(void)
{
// 將brook_notifier自brook_notifier_list移除
unregister_brook_notifier(&brook_notifier);
}
module_init(init_modules);
module_exit(exit_modules);
header file
#ifndef BROOK_NOTIFIER_H
#define BROOK_NOTIFIER_H
#include <linux/notifier.h>
int register_brook_notifier(struct notifier_block *nb);
int unregister_brook_notifier(struct notifier_block *nb);
// event type
enum brook_msg {
brook_num1,
brook_num2,
brook_num3
};
#endif
基本上我們都透過notifier_chain_register()來訂閱某個notifier,透過notifier_chain_unregister()取消某個notifier的訂閱,用notifier_call_chain()來發布event,不過我們常常會用對訂閱與取消訂閱寫一層wrapper,如我們的register_brook_notifier()/unregister_brook_notifier()。
參考資料: