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()。
參考資料:
- Understanding Linux Network Internals, Ch4 Notification Chains
- Publish/subscribe
沒有留言:
張貼留言