亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

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

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

概述

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

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

從技術(shù)上來(lái)講,這并不是一個(gè)多么復(fù)雜、高深、難懂的部分,說(shuō)白了就是一個(gè)單向鏈表的插入、刪除和遍歷等操作。實(shí)現(xiàn)她的代碼不超過(guò)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 用來(lái)連接其它的通知結(jié)構(gòu), priority 是這個(gè)通知的優(yōu)先級(jí),同一條鏈上的 notifier_block 是按優(yōu)先級(jí)排列的,數(shù)字越大,優(yōu)先級(jí)越高,越會(huì)被先執(zhí)行。

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

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

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

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

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

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

struct raw_notifier_head {
 struct notifier_block __rcu *head;
};

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

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

如何使用通知鏈

這四類通知鏈,我們?cè)撛趺从眠@才是我需要關(guān)心的問(wèn)題。在定義自己的通知鏈的時(shí)候,心里必須明確,自己需要一個(gè)什么樣類型的通知鏈,是原子的、可阻塞的還是一個(gè)原始通知鏈。內(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)

其實(shí), ATOMIC_NOTIFIER_HEAD(mynofifierlist) 和下面的代碼是等價(jià)的,展開(kāi)之后如下:

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

另外兩個(gè)接口也類似。如果我們已經(jīng)有一個(gè)通知鏈的對(duì)象,Linux還提供了一組用于初始化一個(gè)通知鏈對(duì)象的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)

這一組接口一般在下列格式的代碼里見(jiàn)到的會(huì)比較多一點(diǎn):

static struct atomic_notifier_head dock_notifier_list;
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);

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

內(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)

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

// 注冊(cè)函數(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定義了一個(gè)原子通知鏈,所以,它對(duì)原子通知鏈的基本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ò)事件也有一個(gè)原子通知鏈(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);

運(yùn)作機(jī)制

通知鏈的運(yùn)作機(jī)制包括兩個(gè)角色:

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

包括以下過(guò)程:

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

其他注意事項(xiàng)

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

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

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

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

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

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;
};

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

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

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

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

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

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

網(wǎng)友整理

注冊(cè)時(shí)間:

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

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

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

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

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

答題星2018-06-03

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

全階人生考試2018-06-03

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

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

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

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

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

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定