日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

一、通知鏈簡介

舉個(gè)形象的例子:將通知鏈比喻成”訂閱者-發(fā)布者“,訂閱者將感興趣的公眾號關(guān)注并設(shè)置提醒,發(fā)布者一旦發(fā)布某個(gè)文章,訂閱者即可收到通知看到發(fā)布的內(nèi)容。

在linux內(nèi)核中為了及時(shí)響應(yīng)某些到來的事件,采取了通知鏈機(jī)制。該機(jī)制的兩個(gè)角色的任務(wù):

1、通知者定義通知鏈

2、被通知者向通知鏈中注冊回調(diào)函數(shù)

3、當(dāng)事件發(fā)生時(shí),通知者發(fā)送通知 (執(zhí)行通知鏈上每個(gè)調(diào)用塊上的回調(diào)函數(shù))所以通知鏈?zhǔn)且粋€(gè)單鏈表,單鏈表上的節(jié)點(diǎn)是調(diào)用塊,每個(gè)調(diào)用塊上有事件相關(guān)的回調(diào)函數(shù)和調(diào)用塊的優(yōu)先級。當(dāng)事件觸發(fā)時(shí)會按優(yōu)先級順序執(zhí)行該鏈表上的回調(diào)函數(shù)。通知鏈只用于各個(gè)子系統(tǒng)之間,不能用于內(nèi)核和用戶空間進(jìn)行事件的通知。

二、相關(guān)細(xì)節(jié)

1、通知鏈的類型

原子通知鏈( Atomic notifier chains ):

通知鏈元素的回調(diào)函數(shù)(當(dāng)事件發(fā)生時(shí)要執(zhí)行的函數(shù))只能在中斷上下文中運(yùn)行,不允許阻塞。

可阻塞通知鏈( Blocking notifier chains ):

通知鏈元素的回調(diào)函數(shù)在進(jìn)程上下文中運(yùn)行,允許阻塞。

原始通知鏈( Raw notifier chains ):

對通知鏈元素的回調(diào)函數(shù)沒有任何限制,所有鎖和保護(hù)機(jī)制都由調(diào)用者維護(hù)。

SRCU 通知鏈( SRCU notifier chains ):可阻塞通知鏈的一種變體

本文將以原子通知鏈進(jìn)行分析

2、原子通知鏈與通知塊

struct raw_notifier_head {
 struct notifier_block __rcu *head;
};

初始化一個(gè)原子通知鏈?zhǔn)褂靡韵潞甓x

#define RAW_NOTIFIER_HEAD(name)     
 struct raw_notifier_head name =    
  RAW_NOTIFIER_INIT(name)
#define RAW_NOTIFIER_INIT(name) {    
  .head = NULL }

例如創(chuàng)建一個(gè)設(shè)備通知鏈隊(duì)列頭:

RAW_NOTIFIER_HEAD.NETdev_chain)

struct raw_notifier_head就相當(dāng)于存放這條通知鏈單鏈表頭,每一個(gè)通知鏈上的元素也就是通知塊如下定義:

struct notifier_block { 
notifier_fn_t notifier_call; //通知調(diào)用的函數(shù) 
struct notifier_block __rcu *next;//指向下一個(gè)通知節(jié)點(diǎn),從而形成鏈隊(duì) 
int priority;//優(yōu)先級,會根據(jù)優(yōu)先級在單鏈表中排序
};

回調(diào)函數(shù)接口:

typedef int (*notifier_fn_t)(struct notifier_block *nb,
unsigned long action, void *data);

整個(gè)通知鏈的組織如下圖所示:

 

3、向通知鏈中插入通知塊

int raw_notifier_chain_register(struct raw_notifier_head *nh,
  struct notifier_block *n)
{
 return notifier_chain_register(&nh->head, n);
}
static int notifier_chain_register(struct notifier_block **nl,
  struct notifier_block *n)
{
  //循環(huán)遍歷通知鏈
 while ((*nl) != NULL) {
  if (n->priority > (*nl)->priority)//按照優(yōu)先級插入通知鏈表
   break;
  nl = &((*nl)->next);
 }
 n->next = *nl;
 rcu_assign_pointer(*nl, n);
 return 0;
}

4、調(diào)用通知鏈

int raw_notifier_call_chain(struct raw_notifier_head *nh,
  unsigned long val, void *v)
{
 return __raw_notifier_call_chain(nh, val, v, -1, NULL);
}
int __raw_notifier_call_chain(struct raw_notifier_head *nh,
         unsigned long val, void *v,
         int nr_to_call, int *nr_calls)
{
 return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
}
static int notifier_call_chain(struct notifier_block **nl,
          unsigned long val, void *v,
          int nr_to_call, int *nr_calls)
{
 int ret = NOTIFY_DONE;
 struct notifier_block *nb, *next_nb;

 nb = rcu_dereference_raw(*nl);
  //循環(huán)遍歷調(diào)用鏈上的調(diào)用塊
 while (nb && nr_to_call) {
  next_nb = rcu_dereference_raw(nb->next);

#ifdef CONFIG_DEBUG_NOTIFIERS
  if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
   WARN(1, "Invalid notifier called!");
   nb = next_nb;
   continue;
  }
#endif
//執(zhí)行該調(diào)用塊的回調(diào)函數(shù)
  ret = nb->notifier_call(nb, val, v);

  if (nr_calls)
   (*nr_calls)++;
    //如果該調(diào)用塊的回調(diào)函數(shù)返回值為NOTIFY_STOP_MASK則跳出調(diào)用鏈的遍歷,也就不執(zhí)行后面的調(diào)用塊的回調(diào)函數(shù)了
  if (ret & NOTIFY_STOP_MASK)
   break;
  nb = next_nb;
  nr_to_call--;
 }
 return ret;
}

三、編寫內(nèi)核模塊進(jìn)行實(shí)驗(yàn)

1、案例1

編寫內(nèi)核模塊作為被通知者,向內(nèi)核netdev_chain通知鏈中插入自定義通知塊(在通知塊中自定義事件觸發(fā)的回調(diào)函數(shù)),源碼如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
 
//處理網(wǎng)絡(luò)設(shè)備的啟動與禁用等事件
int test_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
   struct net_device *dev = (struct net_device *)ptr;
 
 
    switch(event)
    {
        case NETDEV_UP:
             if(dev && dev->name)
                 printk("dev[%s] is upn",dev->name);
             break;
        case NETDEV_DOWN:
             if(dev && dev->name)
                 printk("dev[%s] is downn",dev->name);
                break;
        default:
             break;
    }
 
    return NOTIFY_DONE;
}          
       
                       
struct notifier_block devhandle={
    .notifier_call = test_netdev_event
};
 
static int __init  test_init(void)
{   
    /*
    在netdev_chain通知鏈上注冊消息塊 
    netdev_chain通知鏈?zhǔn)莾?nèi)核中用于傳遞有關(guān)網(wǎng)絡(luò)設(shè)備注冊狀態(tài)的通知信息
 */
    register_netdevice_notifier(&devhandle);
 
    return 0; 
}   
 
static void __exit test_exit(void)
{
    unregister_netdevice_notifier(&devhandle);

    return;
}
 
 
module_init(test_init);
module_exit(test_exit);
 
MODULE_LICENSE("GPL");

Makefile:

obj-m:=Demo.o
                                    
CURRENT_PATH:=$(shell pwd)    
LINUX_KERNEL:=$(shell uname -r)  
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)  
                                    
all:
 make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules  
clean:
 make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean   

將模塊插入內(nèi)核后,將網(wǎng)卡關(guān)閉再重啟一次,查看日志信息:

dx@ubuntu:~/Linux_Sys_code/Notice/Module3$sudo insmod Demo.ko
 dx@ubuntu:~/Linux_Sys_code/Notice/Module3$ dmesg
[24309.137937] inet[00000000baf272e6] is down
[24313.046209] inet[00000000baf272e6] is up

2、案例2

通過寫兩個(gè)內(nèi)核模塊,其中一個(gè)作為通知者一個(gè)作為被通知者

module_1.c:

  • 初始化一個(gè)通知鏈
  • 定義事件的回調(diào)函數(shù)并向通知鏈中插入三個(gè)通知塊(與之前定義的回調(diào)函數(shù)相對應(yīng))
  • 測試通知鏈:循環(huán)遍歷通知鏈的通知塊,并同時(shí)調(diào)用對應(yīng)的回調(diào)函數(shù)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/notifier.h>

/*
模塊功能:1、初始化一個(gè)通知鏈
         2、定義事件的回調(diào)函數(shù)并向通知鏈中插入三個(gè)通知塊(與之前定義的回調(diào)函數(shù)相對應(yīng))
         3、測試通知鏈:循環(huán)遍歷通知鏈的通知塊,并同時(shí)調(diào)用對應(yīng)的回調(diào)函數(shù)
*/
static RAW_NOTIFIER_HEAD(test_chain_head);
EXPORT_SYMBOL_GPL(test_chain_head);

//通知塊1的執(zhí)行函數(shù)
static int A_call(struct notifier_block *nb, unsigned long event, void *v)
{

    printk("AAAAAAAAAA---------event_A occur!---------AAAAAAAAAAn");
    printk("my priority:%dn",nb->priority);
    return NOTIFY_DONE;
} 
//通知塊1:testA
static struct notifier_block testA = {
    .notifier_call = A_call,
    .priority = 7,
};

//通知塊2的執(zhí)行函數(shù)
static int B_call(struct notifier_block *nb, unsigned long event, void *v)
{

    printk("BBBBBBBBBB---------event_B occur!---------BBBBBBBBBn");
    printk("my priority:%dn",nb->priority);
    return NOTIFY_STOP_MASK;
} 
//通知塊2:testB
static struct notifier_block testB = {
    .notifier_call = B_call,
    .priority = 9,
};

//通知塊1的執(zhí)行函數(shù)
static int C_call(struct notifier_block *nb, unsigned long event, void *v)
{
    printk("CCCCCCCCCC---------event_c occur!---------CCCCCCCCCCn");
    printk("my priority:%dn",nb->priority);
    return NOTIFY_DONE;

}
static struct notifier_block testC = {
    .notifier_call = C_call,
    .priority = 6,
};


static int __init my_register(void)
{
    printk("----------register notice chain---------n");
    raw_notifier_chain_register(&test_chain_head,&testA);
    raw_notifier_chain_register(&test_chain_head,&testB);
    raw_notifier_chain_register(&test_chain_head,&testC);
    printk("----------register notice chain done---------n");
    //遍歷已經(jīng)注冊的調(diào)用鏈
 struct notifier_block *nb, *next_nb;
    struct raw_notifier_head *tmp = &test_chain_head;
    struct notifier_block *head = tmp->head;
 nb = rcu_dereference_raw(head);
    printk("----Test registed notice call----n");
//循環(huán)遍歷調(diào)用鏈,測試一下所插入的通知塊
 while (nb) {
        int ret = NOTIFY_DONE;
        int index=0;
        next_nb = rcu_dereference_raw(nb->next);
        printk("notice%d fun:%p,priority:%d",++index,nb->notifier_call,nb->priority);
        ret = nb->notifier_call(nb, 1, NULL);          //調(diào)用注冊的回調(diào)函數(shù)
        nb = next_nb;
 }
   printk("--------------Module_1 test end-------------n");
    return 0;
}
static void __exit my_unregister(void)
{
    raw_notifier_chain_unregister(&test_chain_head,&testA);
    raw_notifier_chain_unregister(&test_chain_head,&testB);
    raw_notifier_chain_unregister(&test_chain_head,&testC);
}


module_init(my_register);
module_exit(my_unregister);

MODULE_AUTHOR("Dong Xu");
MODULE_LICENSE("GPL");

module_2.c:模擬某事件發(fā)生,并調(diào)用通知鏈

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/notifier.h>

/*
模塊功能:模擬某事件發(fā)生,并調(diào)用通知鏈.

*/

extern struct raw_notifier_head test_chain_head;

//某事件
static int event(unsigned long val)
{
    int ret = raw_notifier_call_chain(&test_chain_head,val,NULL);
    return notifier_to_errno(ret);
}

static int __init my_entry(void)
{
    event(666);//模擬某事件發(fā)生
    return 0;
}
static void __exit my_exit(void)
{
    printk("test endn");
}
module_init(my_entry);
module_exit(my_exit);

MODULE_AUTHOR("Dong Xu");
MODULE_LICENSE("GPL");

(module_1與module_2的Makefile可參考上面的Demo1)

運(yùn)行時(shí)先插入module_1再插入module_2結(jié)果如下,紅框內(nèi)是module_1中的測試輸出日志,綠框內(nèi)為世界調(diào)用通知鏈時(shí)的執(zhí)行結(jié)果日志。

 

從上面可以看到通知鏈的執(zhí)行順序是按照優(yōu)先級進(jìn)行的,那么當(dāng)調(diào)用通知鏈時(shí)是否每個(gè)通知塊上的回調(diào)函數(shù)都會執(zhí)行呢?

答案:不是,每個(gè)被執(zhí)行的notifier_block回調(diào)函數(shù)的返回值可能取值以下幾個(gè):

  1. NOTIFY_DONE:表示對相關(guān)的事件類型不關(guān)心。
  2. NOTIFY_OK:順利執(zhí)行。
  3. NOTIFY_BAD:執(zhí)行有錯(cuò)。
  4. NOTIFY_STOP:停止執(zhí)行后面的回調(diào)函數(shù)。
  5. NOTIFY_STOP_MASK:停止執(zhí)行的掩碼

如當(dāng)返回值NOTIFY_STOP_MASK會停止執(zhí)行后面優(yōu)先級低的調(diào)用塊的函數(shù)。

例如把module_1中通知塊的回調(diào)函數(shù)B_call的返回值修改為NOTIFY_STOP_MASK后,重新編譯,運(yùn)行結(jié)果如下,只執(zhí)行了調(diào)用鏈中調(diào)用塊2的回調(diào)函數(shù)。

 

分享到:
標(biāo)簽:內(nèi)核 Linux
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定