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

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

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

概述

在linux內(nèi)核中,各個子系統(tǒng)之間有很強的相互關(guān)系,某些子系統(tǒng)可能對其它子系統(tǒng)產(chǎn)生的事件感興趣。為了讓某個子系統(tǒng)在發(fā)生某個事件時通知感興趣的子系統(tǒng),Linux內(nèi)核引入了通知鏈技術(shù)。通知鏈只能夠在內(nèi)核的子系統(tǒng)之間使用,而不能夠在內(nèi)核和用戶空間進行事件的通知。

組成內(nèi)核的核心系統(tǒng)代碼均位于kernel目錄下,通知鏈表就位于其中,它位于 kernel/notifier.c 中,對應(yīng)的頭文件為 include/linux/notifier.h 。

從技術(shù)上來講,這并不是一個多么復(fù)雜、高深、難懂的部分,說白了就是一個單向鏈表的插入、刪除和遍歷等操作。實現(xiàn)她的代碼不超過1000行。

數(shù)據(jù)結(jié)構(gòu)

所有通知鏈的核心數(shù)據(jù)結(jié)構(gòu)都位于 notifier.h 中。通知鏈的核心結(jié)構(gòu)是 notifier_block 。

struct notifier_block {
 notifier_fn_t notifier_call;
 struct notifier_block __rcu *next;
 int priority;
};

其中 notifier_call 是通知鏈要執(zhí)行的函數(shù)指針, next 用來連接其它的通知結(jié)構(gòu), priority 是這個通知的優(yōu)先級,同一條鏈上的 notifier_block 是按優(yōu)先級排列的,數(shù)字越大,優(yōu)先級越高,越會被先執(zhí)行。

內(nèi)核代碼中一般把通知鏈命名為 xxx_chain , xxx_nofitier_chain 這種形式的變量名。圍繞核心數(shù)據(jù)結(jié)構(gòu) notifier_block ,內(nèi)核定義了四種通知鏈類型,它們的主要區(qū)別就是在執(zhí)行通知鏈上的回調(diào)函數(shù)時是否有安全保護措施:

1. 原子通知鏈( Atomic notifier chains ):原子通知鏈采用的自旋鎖,通知鏈元素的回調(diào)函數(shù)(當(dāng)事件發(fā)生時要執(zhí)行的函數(shù))在中斷或原子操作上下文中運行,不允許阻塞。對應(yīng)的鏈表頭結(jié)構(gòu) :

struct atomic_notifier_head {
 spinlock_t lock;
 struct notifier_block __rcu *head;
};

2. 可阻塞通知鏈( Blocking notifier chains ):可阻塞通知鏈?zhǔn)褂眯盘柫繉崿F(xiàn)回調(diào)函數(shù)的加鎖,通知鏈元素的回調(diào)函數(shù)在進程上下文中運行,允許阻塞。對應(yīng)的鏈表頭 :

struct blocking_notifier_head {
 struct rw_semaphore rwsem;
 struct notifier_block __rcu *head;
};

3. 原始通知鏈( Raw notifier chains ):對通知鏈元素的回調(diào)函數(shù)沒有任何限制,所有鎖和保護機制都由調(diào)用者維護。對應(yīng)的鏈表頭 :

struct raw_notifier_head {
 struct notifier_block __rcu *head;
};

4. SRCU 通知鏈( SRCU notifier chains ):可阻塞通知鏈的一種變體,采用互斥鎖和叫做 可睡眠的讀拷貝更新機制 (Sleepable Read-Copy UpdateSleepable Read-Copy Update)。對應(yīng)的鏈表頭 :

struct srcu_notifier_head {
 struct mutex mutex;
 struct srcu_struct srcu;
 struct notifier_block __rcu *head;
};

如何使用通知鏈

這四類通知鏈,我們該怎么用這才是我需要關(guān)心的問題。在定義自己的通知鏈的時候,心里必須明確,自己需要一個什么樣類型的通知鏈,是原子的、可阻塞的還是一個原始通知鏈。內(nèi)核中用于定義并初始化不同類通知鏈的函數(shù)分別是 :

#define ATOMIC_NOTIFIER_HEAD(name) 
 struct atomic_notifier_head name = 
 ATOMIC_NOTIFIER_INIT(name)
#define BLOCKING_NOTIFIER_HEAD(name) 
 struct blocking_notifier_head name = 
 BLOCKING_NOTIFIER_INIT(name)
#define RAW_NOTIFIER_HEAD(name) 
 struct raw_notifier_head name = 
 RAW_NOTIFIER_INIT(name)

其實, ATOMIC_NOTIFIER_HEAD(mynofifierlist) 和下面的代碼是等價的,展開之后如下:

struct atomic_notifier_head mynofifierlist =
{
 .lock = __SPIN_LOCK_UNLOCKED(mynofifierlist.lock),
 .head = NULL
}

另外兩個接口也類似。如果我們已經(jīng)有一個通知鏈的對象,Linux還提供了一組用于初始化一個通知鏈對象的API:

#define ATOMIC_INIT_NOTIFIER_HEAD(name) do { 
 spin_lock_init(&(name)->lock); 
 (name)->head = NULL; 
 } while (0)
#define BLOCKING_INIT_NOTIFIER_HEAD(name) do { 
 init_rwsem(&(name)->rwsem); 
 (name)->head = NULL; 
 } while (0)
#define RAW_INIT_NOTIFIER_HEAD(name) do { 
 (name)->head = NULL; 
 } while (0)

這一組接口一般在下列格式的代碼里見到的會比較多一點:

static struct atomic_notifier_head dock_notifier_list;
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);

有了通知鏈只是第一步,接下來我們還需要提供往通知鏈上注冊通知塊、卸載通知塊、已經(jīng)遍歷執(zhí)行通知鏈上每個通知塊里回調(diào)函數(shù)的基本接口,說白了就是單向鏈表的插入、刪除和遍歷,這樣理解就可以了。

內(nèi)核提供最基本的通知鏈的常用接口為如下:

static int notifier_chain_register(struct notifier_block **nl,
 struct notifier_block *n)
static int notifier_chain_cond_register(struct notifier_block **nl,
 struct notifier_block *n)
static int notifier_chain_unregister(struct notifier_block **nl,
 struct notifier_block *n)
static int notifier_call_chain(struct notifier_block **nl,
 unsigned long val, void *v,
 int nr_to_call, int *nr_calls)

這最基本的三個接口分別實現(xiàn)了對通知鏈上通知塊的注冊、卸載和遍歷操作,可以想象,原子通知鏈、可阻塞通知鏈和原始通知鏈一定會對基本通知鏈的操作函數(shù)再進行一次包裝的,事實也確實如此:

// 注冊函數(shù)
extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
 struct notifier_block *nb);
extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
 struct notifier_block *nb);
extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
 struct notifier_block *nb);
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
 struct notifier_block *nb);
extern int blocking_notifier_chain_cond_register(
 struct blocking_notifier_head *nh,
 struct notifier_block *nb);
// 卸載函數(shù)
extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
 struct notifier_block *nb);
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
 struct notifier_block *nb);
extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
 struct notifier_block *nb);
extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
 struct notifier_block *nb);
// 遍歷操作
extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
 unsigned long val, void *v);
extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
 unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
 unsigned long val, void *v);
extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
 unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int raw_notifier_call_chain(struct raw_notifier_head *nh,
 unsigned long val, void *v);
extern int __raw_notifier_call_chain(struct raw_notifier_head *nh,
 unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
 unsigned long val, void *v);
extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
 unsigned long val, void *v, int nr_to_call, int *nr_calls);

上述這四類通知鏈的基本API又構(gòu)成了內(nèi)核中其他子系統(tǒng)定義、操作自己通知鏈的基礎(chǔ)。例如,Netlink定義了一個原子通知鏈,所以,它對原子通知鏈的基本API又封裝了一層,以形成自己的特色:

static ATOMIC_NOTIFIER_HEAD(netlink_chain);
int netlink_register_notifier(struct notifier_block *nb)
{
 return atomic_notifier_chain_register(&netlink_chain, nb);
}
EXPORT_SYMBOL(netlink_register_notifier);
int netlink_unregister_notifier(struct notifier_block *nb)
{
 return atomic_notifier_chain_unregister(&netlink_chain, nb);
}
EXPORT_SYMBOL(netlink_unregister_notifier);

網(wǎng)絡(luò)事件也有一個原子通知鏈(net/core/netevent.c):

/*
 * Network event notifiers
 *
 * Authors:
 * Tom Tucker <[email protected]>
 * Steve Wise <[email protected]>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * Fixes:
 */
#include <linux/rtnetlink.h>
#include <linux/notifier.h>
#include <linux/export.h>
#include <net/netevent.h>
static ATOMIC_NOTIFIER_HEAD(netevent_notif_chain);
/**
 * register_netevent_notifier - register a netevent notifier block
 * @nb: notifier
 *
 * Register a notifier to be called when a netevent occurs.
 * The notifier passed is linked into the kernel structures and must
 * not be reused until it has been unregistered. A negative errno code
 * is returned on a failure.
 */
int register_netevent_notifier(struct notifier_block *nb)
{
 int err;
 err = atomic_notifier_chain_register(&netevent_notif_chain, nb);
 return err;
}
EXPORT_SYMBOL_GPL(register_netevent_notifier);
/**
 * netevent_unregister_notifier - unregister a netevent notifier block
 * @nb: notifier
 *
 * Unregister a notifier previously registered by
 * register_neigh_notifier(). The notifier is unlinked into the
 * kernel structures and may then be reused. A negative errno code
 * is returned on a failure.
 */
int unregister_netevent_notifier(struct notifier_block *nb)
{
 return atomic_notifier_chain_unregister(&netevent_notif_chain, nb);
}
EXPORT_SYMBOL_GPL(unregister_netevent_notifier);
/**
 * call_netevent_notifiers - call all netevent notifier blocks
 * @val: value passed unmodified to notifier function
 * @v: pointer passed unmodified to notifier function
 *
 * Call all neighbour notifier blocks. Parameters and return value
 * are as for notifier_call_chain().
 */
int call_netevent_notifiers(unsigned long val, void *v)
{
 return atomic_notifier_call_chain(&netevent_notif_chain, val, v);
}
EXPORT_SYMBOL_GPL(call_netevent_notifiers);

運作機制

通知鏈的運作機制包括兩個角色:

  • 被通知者:對某一事件感興趣一方。定義了當(dāng)事件發(fā)生時,相應(yīng)的處理函數(shù),即回調(diào)函數(shù),被通知者將其注冊到通知鏈中(被通知者注冊的動作就是在通知鏈中增加一項)。
  • 通知者:事件的通知者。當(dāng)檢測到某事件,或者本身產(chǎn)生事件時,通知所有對該事件感興趣的一方事件發(fā)生。它定義了一個通知鏈,其中保存了每一個被通知者對事件的回調(diào)函數(shù)。通知這個過程實際上就是遍歷通知鏈中的每一項,然后調(diào)用相應(yīng)的回調(diào)函數(shù)。

包括以下過程:

  • 通知者定義通知鏈。
  • 被通知者向通知鏈中注冊回調(diào)函數(shù)。
  • 當(dāng)事件發(fā)生時,通知者發(fā)出通知(執(zhí)行通知鏈中所有元素的回調(diào)函數(shù))。

其他注意事項

1. 如果一個子系統(tǒng)A在運行過程中會產(chǎn)生一個實時事件,而這些事件對其他子系統(tǒng)來說非常重要,那么子系統(tǒng)A可以定義一個自己的通知鏈對象,根據(jù)需求可以選擇原子通知鏈、非阻塞通知鏈和原始通知鏈,并向外提供向這個通知鏈里注冊、卸載、執(zhí)行事件的回調(diào)函數(shù)的接口。

2. 如果子系統(tǒng)B對子系統(tǒng)A中的某些事件感興趣,或者說強依賴,就是說子系統(tǒng)B需要根據(jù)子系統(tǒng)A中某些事件來執(zhí)行自己特定的操作,那么此時系統(tǒng)B需要實例化一個通知塊 struct notifier_block xxx{} ,然后編寫通知塊里的回調(diào)處理函數(shù)來相應(yīng)系統(tǒng)A中的事件就可以了。

3. 通知塊 struct notifier_block xxx{} 里有一個優(yōu)先級的特性,起始在標(biāo)準(zhǔn)內(nèi)核里每個實例化的通知塊都沒有使用優(yōu)先級。不用優(yōu)先級字段的結(jié)果就是:先注冊的通知塊里的回調(diào)函數(shù)在事件發(fā)生時會先執(zhí)行。注意這里說的后注冊指的是模塊被動態(tài)加載到內(nèi)核的先后順序,和哪個模塊代碼先寫沒有關(guān)系。

注意區(qū)分。意思就是說,如果子系統(tǒng)B和C都對子系統(tǒng)A的up事件感興趣,B和C在向A注冊up事件的回調(diào)函數(shù)時并沒有指定函數(shù)的優(yōu)先級。無論是通過`insmod`手動加載模塊B和C,還是系統(tǒng) boot 時自動加載B和C,哪個模塊先被加載,它的回調(diào)函數(shù)在A系統(tǒng)的up事件發(fā)生時會先被執(zhí)行

4. 關(guān)于通知鏈的回調(diào)函數(shù),正常情況下都需要返回 NOTIFY_OK 或者 NOTIFY_DONE ,這樣通知鏈上后面掛載的其他函數(shù)可以繼續(xù)執(zhí)行。如果返回 NOTIFY_STOP ,則會使得通知鏈上后續(xù)掛載的函數(shù)無法得到執(zhí)行,除非特別想這么做,否則編寫通知鏈回調(diào)函數(shù)時,最好不要返回這個值。

5. 通知鏈上的回調(diào)函數(shù)的原型為:

typedef int (*notifier_fn_t)(struct notifier_block *nb,
 unsigned long action, void *data);
struct notifier_block {
 notifier_fn_t notifier_call;
 struct notifier_block __rcu *next;
 int priority;
};

其中第二個參數(shù)一般用于指明事件的類型。通知都是一個整數(shù);而第三個參數(shù)是一個 void 類型的內(nèi)存地址,在不同的子系統(tǒng)中表示不同的信息。我們在設(shè)計自己的通知鏈系統(tǒng)可以用第三個入?yún)崿F(xiàn)在通知系統(tǒng)和被通知系統(tǒng)之間數(shù)據(jù)的傳遞,以便被通知系統(tǒng)的工作可以更加緊湊、高效。

6. 如果以后在看到內(nèi)核代碼中某個子系統(tǒng)在調(diào)用通知鏈注冊函數(shù)時,做到以下幾點就沒事了:

  • 心里首先要明確,這個注冊通知鏈回調(diào)函數(shù)的系統(tǒng)一定和提供通知鏈的系統(tǒng)有某種聯(lián)系,且本系統(tǒng)需要那個系統(tǒng)對某些重要事件進行響應(yīng)。
  • 看本系統(tǒng)注冊的通知鏈回調(diào)函數(shù)的實現(xiàn),具體看它對哪些事件感興趣,并且是怎么處理的。
  • 看看提供通知鏈對象的系統(tǒng)有哪些事件;

最后,也就明白了這個子系統(tǒng)為什么要用通知鏈來感知別的系統(tǒng)的變化了,這樣一來,對這兩個子系統(tǒng)從宏觀到微觀的層面上都有一個總體的認識和把握,后續(xù)研究起來就順風(fēng)順?biāo)恕?/p>

了解更多,Linux相關(guān)知識,可以關(guān)注我。

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

網(wǎng)友整理

注冊時間:

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

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

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

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

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

答題星2018-06-03

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

全階人生考試2018-06-03

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

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

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

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

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

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

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