Linux 內核通知鏈隨筆【中】
關於內核通知鏈不像Netlink那樣,既能夠用於內核與用戶空間的通訊,還能用於內核不一樣子系統之間的通訊,通知鏈只能用於內核不一樣子系統之間的通訊。那麼內核通知鏈究竟是怎麼工做的?咱們如何才能用好通知鏈?內核源代碼裏隨處可見的通知鏈身影,咱們到底該如何理解呢?本片博文事後,您的這些疑問和顧慮將通通消除。
之前有個女神,超凡脫俗、出水芙蓉,不過在怎麼滴也是人,是人就會有各類各樣的需求,女神的全部需求都放在她的需求鏈表裏requirment_chain,好比物質需求,精神需求等等。而後女神首先須要作的事情就是將本身的需求鏈給實例化了:
- /* Godness.c */
- /* 咱們假設女神需求鏈的類型是原始通知鏈(PS:不要和原始需求掛鉤理解 -_-||)*/
- static RAW_NOTIFIER_HEAD(requirment_chain);
-
當需求被定義出來後,還須要向外提供兩個接口:一個是別人用於知足她需求的接口,另外一個是別人須要和她break out的接口(雖然在現實生活中這種狀況比較使人sadness,但女神所在的虛擬世界裏這個是必須的)。因而女神提供了別人往其需求鏈註冊響應函數的接口和卸載響應函數的接口:
- /* Godness.c*/
-
- int register_godness_notifier(struct notifier_block *nb)
- {
- return raw_notifier_chain_register(&requirment_chain, nb);
- }
- EXPORT_SYMBOL(register_godness_notifier); //註冊函數實現了以後必須將其公佈出去,否則別人怎麼看獲得呢
-
- int unregister_godness_notifier(struct notifier_block *nb)
- {
- return raw_notifier_chain_unregister(&requirment_chain, nb);
- }
- EXPORT_SYMBOL(unregister_godness_notifier); //同上
而後,女神要作的就是提需求,並看看哪些屌絲、土豪或高富帥來追求本身:
- int call_godness_notifier_chain(unsigned long val, void *v)
- {
- return raw_notifier_call_chain(&requirment_chain, val, v);
- }
- EXPORT_SYMBOL(call_godness_notifier_chain);
爲了模擬測試過程,咱們須要一個內核線程,模擬女神提需求的過程,而後不斷調用上面的需求響應的檢測函數。咱們姑且認爲認爲女神的需求有兩種:物質需求就是對menoy的需求,精神需求就是音樂的需求。女神每3秒鐘提一個需求,一共提10個需求:
- #define PHY_REQ 0 //物質需求
- #define SPR_REQ 1 //精神需求
-
- #define REQ_MAX SPR_REQ+1
-
- static int make_requirment_thread(void *data)
- {
- int i = 10;
- struct completion cmpl;
- unsigned int requirment_type = 0;
- printk("[Godness]requirements thread starting...\n");
- while((i--) > 0){
- init_completion(&cmpl);
- wait_for_completion_timeout(&cmpl, 3 * HZ);
-
- get_random_bytes(&requirment_type,sizeof(requirment_type)); //生成一個內核隨機數
- requirment_type %= REQ_MAX; //需求類型之多是0或者1
-
- printk("[Godness]requirment type: %d \n",requirment_type);
- call_godness_notifier_chain(requirment_type,NULL);
- }
- printk("[Godness]requirements thread ended!\n");
- return 0;
- }
女神的最終模型以下:
- #include <asm/uaccess.h>
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/notifier.h>
- #include <linux/init.h>
- #include <linux/types.h>
- #include <linux/module.h>
- #include <linux/kthread.h>
- MODULE_LICENSE("GPL");
-
- #define PHY_REQ 0 //物質需求
- #define SPR_REQ 1 //精神需求
- #define REQ_MAX SPR_REQ+1
-
- extern void get_random_bytes(void* buf,int nbytes);
- static struct task_struct *requirments_thread = NULL;
- /*
- * 女神全部的需求都會列在她的需求鏈裏。這裏咱們定義了一個原始通知鏈,暫時沒考慮鎖的問題。
- */
- static RAW_NOTIFIER_HEAD(requirment_chain);
-
- /*
- * 若是誰想追求本女王,就來獻殷勤吧
- */
- int register_godness_notifier(struct notifier_block *nb)
- {
- return raw_notifier_chain_register(&requirment_chain, nb);
- }
- EXPORT_SYMBOL(register_godness_notifier);
-
- /*
- * 伺候不起的,趕忙Get out as soon as
- */
- int unregister_godness_notifier(struct notifier_block *nb)
- {
- return raw_notifier_chain_unregister(&requirment_chain, nb);
- }
- EXPORT_SYMBOL(unregister_godness_notifier);
-
- /*
- * 本女王開始提需求了,看看誰能纔是真心的。
- */
- int call_godness_notifier_chain(unsigned long val, void *v)
- {
- return raw_notifier_call_chain(&requirment_chain, val, v);
- }
- EXPORT_SYMBOL(call_godness_notifier_chain);
-
- static int make_requirment_thread(void *data)
- {
- int i = 10;
- struct completion cmpl;
- unsigned int requirment_type = 0;
- printk("[Godness]requirements thread starting...\n");
- while((i--) > 0){
- init_completion(&cmpl);
- wait_for_completion_timeout(&cmpl, 3 * HZ);
- get_random_bytes(&requirment_type,sizeof(requirment_type)); //生成一個內核隨機數
- requirment_type %= REQ_MAX; //需求類型之多是0或者1
- printk("[Godness]requirment type: %d \n",requirment_type);
- call_godness_notifier_chain(requirment_type,NULL);
- }
- printk("[Godness]requirements thread ended!\n");
- return 0;
- }
-
- static int __init godness_init_notifier(void)
- {
- printk("[Attention]The Godness coming into the world!\n");
- requirments_thread = kthread_run(make_requirment_thread,NULL,"Godness_requirments_thread");
- return 0;
- }
-
- static void __exit godness_exit_notifier(void)
- {
- printk("[Attention]The Godness leaving out!\n");
- }
- module_init(godness_init_notifier);
- module_exit(godness_exit_notifier);
這個時候有個叫土豪的傢伙,忽然於茫茫人海中發現了女神,而且知道了女神有金錢需求的慾望,因而土豪向女神的需求鏈裏註冊了一個金錢的響應函數,這樣一旦女神須要用錢的時候他第一時間就能收到通知,而後以迅雷下載不及掩耳盜鈴之勢加以知足:
- /*Tuhao.c*/
-
- extern int register_godness_notifier(struct notifier_block*);
- extern int unregister_godness_notifier(struct notifier_block*);
-
- static int baby_need_money(struct notifier_block *this, unsigned long event, void *ptr)
- {
- if(event != 0) //不是金錢需求關我鳥事
- {
- return NOTIFY_DONE; //Don't care
- }
- printk("[Tuhao]Hi Baby,$$$$$$$$ 麼麼噠 \n");
- return NOTIFY_OK;
- }
-
- static struct notifier_block cash_notifier =
- {
- .notifier_call = baby_need_money,
- .priority = 2,
- };
-
- static int __init tuhao_register(void)
- {
- int err;
- printk("[Tuhao]Tuhao register cash_requirment response to Godness...");
-
- err = register_godness_notifier(&cash_notifier);
- if (err)
- {
- printk("Refused!\n");
- return -1;
- }
- printk("Accepted!\n");
-
- return err;
- }
-
- static void __exit tuhao_unregister(void)
- {
- unregister_godness_notifier(&cash_notifier);
- printk("[Tuhao]Tuhao is giving up Godness!(Son of bitch)\n");
- }
-
- module_init(tuhao_register);
- module_exit(tuhao_unregister);
這時,有一個屌絲,也於茫茫人海中發現了女神,他發現女神喜歡音樂,因而他開始響應女神的精神需求:
- /*Diors.c*/
-
- extern int register_godness_notifier(struct notifier_block*);
- extern int unregister_godness_notifier(struct notifier_block*);
-
- static int godness_need_music(struct notifier_block *this, unsigned long event, void *ptr)
- {
- if(event != 1) //我又沒錢,給不了你大房子、氣派的車子...
- {
- return NOTIFY_DONE; //Don't care
- }
- printk("[Diors]Hi girl,This is a classic Music disk,take it. \n");
- return NOTIFY_OK;
- }
-
- static struct notifier_block music_notifier =
- {
- .notifier_call = godness_need_music,
- .priority = 2,
- };
-
- static int __init diors_register(void)
- {
- int err;
- printk("[Diors]Diors register music_requirment response to Godness...");
-
- err = register_godness_notifier(&music_notifier);
- if (err)
- {
- printk("Refused!\n");
- return -1;
- }
- printk("Accepted!\n");
-
- return err;
- }
-
- static void __exit diors_unregister(void)
- {
- unregister_godness_notifier(&music_notifier);
- printk("[Diors]Tuhao is giving up Godness!(What a pity)\n");
- }
-
- module_init(diors_register);
- module_exit(diors_unregister);
-
好的,到此爲止,一切就緒,好戲正式開始:
- #Makefile for fun
-
- obj-m:=Goddess.o Tuhao.o Diors.o
-
- CURRENT_PATH := $(shell pwd)
- KERNEL_VERSION := $(shell uname -r)
- KERNEL_HEADER_DIR := /usr/src/kernels/$(LINUX_KERNELKERNEL_VERSION)
- all:
- make -C $(KERNEL_HEADER_DIR) M=$(CURRENT_PATH) modules
- clean:
- make -C $(KERNEL_HEADER_DIR) M=$(CURRENT_PATH) clean
主角們閃亮登場:
It's time time to show :)
咱們能夠看到,女神初到人間時須要用錢,結果沒人搭理,去了趟韓國回來以後,被土豪給瞄到了,因而土豪開始大把大把地視金錢如糞土。過了8秒鐘,屌絲男也發現了女神,當女神想聽歌時屌絲男就屁顛屁顛地把本身珍藏了多年的古典樂光盤送給了女神。最後劇中,謝幕。
OK,讓咱們總結一下Linux內核通知鏈的應用場景。若是一個子系統須要向外通告事件時,它須要首先定義本身的通知鏈對象,而後向內核裏其餘子系統
提供一個向本身的通知鏈註冊消息響應函數的接口,固然也必須
提供一個用於從本身從本身的通知鏈上卸載響應函數的接口。接下來,咱們這個子系統要作的事情就是根據本身的實際運行狀況,按期地產生一些消息,並調用本身通知鏈裏別的系統已經註冊好了消息響應函數,這樣別的子系統就能夠根據咱們這個系統的的消息類型進行一些處理動做。
那麼多個子系統對咱們的同一種消息都掛有響應函數時該怎麼處理?鑑於時間關係,下次再敘。
未完,待續...
歡迎關注本站公眾號,獲取更多信息