轉載:Linux notifier機制
linux龐大系統中,各個模組是相對獨立的,那麼模組間通訊該如何做呢?當然你也可以使用全域性資源,如果這樣的話系統缺少獨立性,會帶來穩定性問題的。如果你說,使用共享記憶體,程序通訊等,那麼你曲解我的意思了,因為你說的大多是user space的,而我說的是核心模組級別的。notifier_chain,對就是它,實質上這個機制就是一個回撥函式連結串列的操作,回撥函式的註冊,登出,呼叫。源系統處(比如A子系統)進行定義初始化和回撥函式呼叫,被通知處(比如B子系統)進行回撥函式的註冊和登出,那麼當A系統發生某種事件是,就呼叫通知鏈中所有回撥函式,B系統中註冊的回撥函式就會得到執行。一旦執行回撥函式,它會從連結串列頭依次執行每一個回撥函式,那麼依次執行是一次性全部執行完?執行過程的任意時間都可睡眠?這些需求也就產生了4種類型的notifier_chain。
結構體定義:
- struct notifier_block { /* chain的基本單位 */
- int (*notifier_call)(struct notifier_block *, unsigned long, void *);
- struct notifier_block __rcu *next;
- int priority;
- };
- struct atomic_notifier_head {/* atmoic context; 執行(rcu_read_lock);回撥函式執行不能阻塞;實時性高 */
- spinlock_t lock;
- struct notifier_block
__rcu *head; - };
- struct blocking_notifier_head { /* process context;執行(rw_semaphore) ;回撥函式執行可以阻塞;實時性相對低*/
- struct rw_semaphore rwsem;
- struct notifier_block __rcu *head;
- };
- struct raw_notifier_head { /* 原始連結串列操作,註冊,執行過程無任何保護,完全有驅動人員控制 */
- struct notifier_block __rcu *head;
- };
- struct srcu_notifier_head {
/* process context;可阻塞通知鏈的變體(Sleepable Read-Copy-Update),回撥函式執行可以阻塞 */ - struct mutex mutex;
- struct srcu_struct srcu;
- struct notifier_block __rcu *head;
- };
notifier_chain的API使用基本四大步驟:定義初始化、註冊、呼叫和登出,而這些操作的基本單位是notifier_block,這個基本單位中包含了回撥函式notifier_call,後繼notifier_block指標,優先順序priority(預設0,數字越大優先順序越高,越靠近連結串列的頭結點,越優先得到執行)。初始化:
#include <linux/notifier.h>
- #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)
- /* srcu_notifier_heads must be initialized and cleaned up dynamically */
- extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
- #define srcu_cleanup_notifier_head(name) \
- cleanup_srcu_struct(&(name)->srcu);
經過定義及初始化後,一類的連結串列的頭就形成了,註冊就是增加節點,執行就是遍歷節點,唯一要說明的就是,srcu鏈需要動態的定義,其他三中不需要。
註冊和登出:
- int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n);
- int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n)
- int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n);
- /* 註冊的notifier_block不重複*/
- int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *n);
- int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *n);
- int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n);
- int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *n);
- int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *n);
- int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *n);
其中註冊和登出都呼叫了最基本的函式如下:
- /*
- * Notifier chain core routines. The exported routines below
- * are layered on top of these, with appropriate locking added.
- */
- static int notifier_chain_register(struct notifier_block **nl,
- struct notifier_block *n)
- {
- while ((*nl) != NULL) {
- if (n->priority > (*nl)->priority)
- break;
- nl = &((*nl)->next);
- }
- n->next = *nl;
- rcu_assign_pointer(*nl, n);
- return 0;
- }
- static int notifier_chain_cond_register(struct notifier_block **nl,
- struct notifier_block *n)
- {
- while ((*nl) != NULL) {
- if ((*nl) == n)
- return 0;
- if (n->priority > (*nl)->priority)
- break;
- nl = &((*nl)->next);
- }
- n->next = *nl;
- rcu_assign_pointer(*nl, n);
- return 0;
- }
- static int notifier_chain_unregister(struct notifier_block **nl,
- struct notifier_block *n)
- {
- while ((*nl) != NULL) {
- if ((*nl) == n) {
- rcu_assign_pointer(*nl, n->next);
- return 0;
- }
- nl = &((*nl)->next);
- }
- return -ENOENT;
- }
呼叫:
當A系統發生事件時,會呼叫下面函式之一的方法來,執行通知鏈中的所有回撥函式,那麼所有註冊的地方都會得到執行,除前一個函式執行返回值含有NOTIFY_STOP_MASK標誌。其中引數val是一個整形,使用者自定義含義的傳入值,引數v是一個void*型別,使用者自定義任何型別含義,一般為平臺結構體指標,使用的時候需要強制轉換。- /*呼叫了__atomic_notifier_call_chain,且nr_to_call=-1表示回撥函式呼叫個數不限,nr_calls=NULL表示不關心已執行的回撥函式個數*/
- 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);
- <span style="white-space:pre"> </span>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);
回撥函式返回值:
- #define NOTIFY_DONE 0x0000 /* Don't care回撥函式不關心返回值*/
- #define NOTIFY_OK 0x0001 /* Suits me 回撥函式呼叫順利完成*/
- #define NOTIFY_STOP_MASK 0x8000 /* Don't call further 回撥函式鏈禁止繼續呼叫的掩碼*/
- #define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002)
- /* Bad/Veto action 回撥函式執行有錯*/
- /*
- * Clean way to return from the notifier and stop further calls.當前順利呼叫,禁止繼續呼叫
- */
- #define NOTIFY_STOP (NOTIFY_OK|NOTIFY_STOP_MASK)
在notifier_call_chain函式中去依次執行每一個註冊的回撥函式,並以前一個回撥函式的返回值為判斷依據,是否繼續呼叫,最後一個執行函式的返回值作為notifier_call_chain的返回值。當前標準API中,只關注了NOTIFY_STOP_MASK,其他的在notifier_call_chain中未做處理。
典型用例:
在linux中,液晶顯示器會提供一個fb_notify,當顯示器發生某種事件時,會呼叫notifier_call_chain,這樣註冊的回撥函式得到執行,回撥函式根據顯示的framebuffer狀態執行你預想的動作。 它選用通知鏈型別的是blocking_notifier_chain。- /*
- * linux/drivers/video/fb_notify.c
- *
- * Copyright (C) 2006 Antonino Daplas <[email protected]>
- *
- * 2001 - Documented with DocBook
- * - Brad Douglas <[email protected]>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- */
- #include <linux/fb.h>
- #include <linux/notifier.h>
- #include <linux/export.h>
- /*靜態定義並初始化通知鏈頭*/
- static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
- /** 註冊回撥函式,加入一個回撥函式節點
- * fb_register_client - register a client notifier
- * @nb: notifier block to callback on events
- */
- int fb_register_client(struct notifier_block *nb)
- {
- return blocking_notifier_chain_register(&fb_notifier_list, nb);
- }
- EXPORT_SYMBOL(fb_register_client);
- /** 登出回撥函式,刪除一個回撥函式節點
- * fb_unregister_client - unregister a client notifier
- * @nb: notifier block to callback on events
- */
- int fb_unregister_client(struct notifier_block *nb)
- {
- return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
- }
- EXPORT_SYMBOL(fb_unregister_client);
- /** 當源即framebuffer發生某種事件,呼叫該函式執行所有回撥函式,通知其他子系統
- * fb_notifier_call_chain - notify clients of fb_events
- *
- */
- int fb_notifier_call_chain(unsigned long val, void *v)
- {
- return blocking_notifier_call_chain(&fb_notifier_list, val, v);
- }
- EXPORT_SYMBOL_GPL(fb_notifier_call_chain);
假如framebuffer為A子系統,觸屏ft5x06為B子系統,現想要做到觸屏伴隨顯示屏息屏而休眠,亮屏而喚醒。
B子系統(觸屏)/被通知者,程式碼如下:- kernel\drivers\input\touchscreen\ft5x06_ts.c
- #if defined(CONFIG_FB)
- static int fb_notifier_callback(struct notifier_block *self,
- unsigned long event, void *data)
- {
- struct fb_event *evdata = data;
- int *blank;
- struct ft5x06_ts_data *ft5x06_data =
- container_of(self, struct ft5x06_ts_data, fb_notif);
- /* 檢測是否是顯示器BLANK改變事件 */
- if (evdata && evdata->data && event == FB_EVENT_BLANK &&
- ft5x06_data && ft5x06_data->client) {
- blank = evdata->data;
- if (*blank == FB_BLANK_UNBLANK) /*是BLANK事件中的LCD亮屏事件,喚醒觸屏*/
- ft5x06_ts_resume(&ft5x06_data->client->dev);
- else if (*blank == FB_BLANK_POWERDOWN) /*是BLANK事件中的LCD滅屏事件,讓觸屏休眠 */
- ft5x06_ts_suspend(&ft5x06_data->client->dev);
- }
- return 0;
- }
- #elif defined(CONFIG_HAS_EARLYSUSPEND)
- //……
- #endif
- static int ft5x06_ts_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- //……
- #if defined(CONFIG_FB)
- data->fb_notif.notifier_call = fb_notifier_callback;
- /*註冊fb回撥函式*/
- err = fb_register_client(&data->fb_notif);
- if (err)
- dev_err(&client->dev, “Unable to register fb_notifier: %d\n”,
- err);
- #endif
- //……
- }
- static int __devexit ft5x06_ts_remove(struct i2c_client *client)
- {
- //……
- #if defined(CONFIG_FB)
- /*登出fb回撥函式*/
- if (fb_unregister_client(&data->fb_notif))
- dev_err(&client->dev, “Error occurred while unregistering fb_notifier.\n”);
- #elif defined(CONFIG_HAS_EARLYSUSPEND)
- unregister_early_suspend(&data->early_suspend);
- #endif
- //……
- }
A子系統(framebuffer)/通知者,程式碼如下:
- int fb_blank(struct fb_info *info, int blank)
- {
- int ret = -EINVAL;
- if (blank > FB_BLANK_POWERDOWN)
- blank = FB_BLANK_POWERDOWN;
- if (info->fbops->fb_blank) /*硬體執行亮屏還是滅屏的操作*/
- ret = info->fbops->fb_blank(blank, info);
- if (!ret) {
- struct fb_event event;
- event.info = info;
- event.data = ␣
- /*硬體BLANK操作成功後,呼叫所有的註冊回撥函式,比如通知給觸屏*/
- fb_notifier_call_chain(FB_EVENT_BLANK, &event);
- }
- return ret;
- }
- /*fbmem中的ioctl*/
- static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
- {
- //…….
- case FBIOBLANK: //由上層發下來的亮屏還是息屏的IO命令
- if (!lock_fb_info(info))
- return -ENODEV;
- console_lock();
- info->flags |= FBINFO_MISC_USEREVENT;
- ret = fb_blank(info, arg);
- info->flags &= ~FBINFO_MISC_USEREVENT;
- console_unlock();
- unlock_fb_info(info);
- break;
- //……
- }
至於framebuffer中的notifier_call_chain的第二、三個引數,詳見linux\fb.h,fbmem.c,fbcon.c。有時間也寫個關於fb相關的文章。
</div>
</div>
相關推薦
轉載:Linux notifier機制
linux龐大系統中,各個模組是相對獨立的,那麼模組間通訊該如何做呢?當然你也可以使用全域性資源,如果這樣的話系統缺少獨立性,會帶來穩定性問題的。如果你說,使用共享記憶體,程序通訊等,那麼你曲解我的意思了,因為你說的大多是user space的,而我說的是核心模組級別的。notifier_chain,對就
轉載:Linux下查看/修改系統時區、時間
div 系統 啟動 localtime ive hctosys red 亞洲 命令 一、查看和修改Linux的時區 1. 查看當前時區 命令 : "date -R" 2. 修改設置Linux服務器時區 方法 A 命令 : "tzselect" 方法 B 僅限於RedHat
轉載:linux tar 解壓命令總結
linu 追加 tar.gz 備忘 標準輸出 需要 中一 意思 檔案 把常用的tar解壓命令總結下,當作備忘: tar -c: 建立壓縮檔案 -x:解壓 -t:查看內容 -r:向壓縮歸檔文件末尾追加文件 -u:更新原壓縮包中的文件 這五個是獨立的命令,壓縮解壓都要用到其中
轉載:Linux音頻驅動-OSS和ALSA聲音系統簡介及其比較
sdn stat 邏輯 音量 技術 hone ltr close clear Linux音頻驅動-OSS和ALSA聲音系統簡介及其比較 概述 昨天想在Ubuntu上用一下HTK工具包來繪制語音信號的頻譜圖和提取MFCC的結果,但由於前段時間把Ubuntu升級到13.0
轉載:linux /proc接口
似的 edit dstat 清單 lean string 返回 lock author linux proc接口的建立與使用 /proc 文件系統是一個虛擬文件系統,通過它可以使用一種新的方法在 Linux? 內核空間和用戶空間之間進行通信。在 /proc 文件系統中
轉載:linux概念之/proc與/sys
其中 結構體 分層 scsi 內聯 聯系 tom 基於 是否 linux概念之/proc與/sys http://blog.chinaunix.net/uid-1835494-id-3070465.html proc/x:1/sched http://bbs.chin
轉載:Linux下解壓zip亂碼問題的解決(unzip)
方式 -h linu 文件名 inf etc java env 系統默認 https://blog.csdn.net/abyjun/article/details/48344379 在windows上壓縮的文件,是以系統默認編碼中文來壓縮文件。由於zip文件中沒有聲明其編碼
轉載:Linux核心的ioctl函式
關於ioctl的網上資源 一、 什麼是ioctl。 ioctl是裝置驅動程式中對裝置的I/O通道進行管理的函式。所謂對I/O通道進行管理,就是對裝置的一些特性進行控制,例如串列埠的傳輸波特率、馬達的轉速等等。它的呼叫個數如下: int ioctl(int
轉載:Linux核心 裝置樹操作常用API
Linux裝置樹語法詳解一文中介紹了裝置樹的語法,這裡主要介紹核心中提供的操作裝置樹的API,這些API通常都在"include/of.h"中宣告。 device_node,核心中用下面的這個結構描述裝置樹中的一個節點,後面的API都需要一個device_nod
Linux程序:Linux切換機制主流程
Linux切換並沒有使用X86CPU的切換方法,Linux切換的實質就是cr3切換(記憶體空間切換,在switch_mm函式中)+ 暫存器切換(包括EIP,ESP等,均在switch_to函式中)。這裡我們講述下switch_to主流程: 在switch_mm函式中將new_task-&
轉載:linux 下CPU數量、核心數量、是否支援超執行緒的判斷
英文版:1.Physical id and core id are not necessarily consecutive but they are unique. Any cpu with the same core id are hyperthreads in the same core.2.Any c
轉載:linux平臺裝置驅動架構詳解 Linux Platform Device and Driver
從Linux 2.6起引入了一套新的驅動管理和註冊機制:Platform_device和Platform_driver。Linux中大部分的裝置驅動,都可以使用這套機制, 裝置用Platform_device表示,驅動用Platform_driver進行註冊。Linux platform driver機制和傳
轉載:ASP.NET MVC擴展自定義視圖引擎支持多模板&動態換膚skins機制
生效 amp 文件名 attr 情況 etc lis new out ASP.NET mvc的razor視圖引擎是一個非常好的.NET MVC框架內置的視圖引擎。一般情況我們使用.NET MVC框架為我們提供的這個Razor視圖引擎就足夠了。但是有時我們想在我們的項目支持多
轉:Linux設備樹(Device Tree)機制
十六進制 交互 mic 映射 0.0.0.0 內容 14. 字節 讀取 目錄 1. 設備樹(Device Tree)基本概念及作用 2. 設備樹的組成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DTB 2.4. Bootloader 3
轉載:什麼是 .bashrc,為什麼要編輯 .bashrc? linux學習之路 1
如果你執行一個基於 Unix 或者類 Unix 的作業系統,bash 很有可能是作為預設終端被安裝的。雖然存在很多不同的 shell,bash 卻是最常見或許也是最主流的。如果你不明白那意味著什麼,bash 是一個能解釋你輸入進終端程式的東西,並且基於你的輸入來執行命令。它在一定程度上支援使用指令碼
轉載:深入淺出Zookeeper(一) Zookeeper架構及FastLeaderElection機制
轉載至 http://www.jasongj.com/zookeeper/fastleaderelection/: 原創文章,轉載請務必將下面這段話置於文章開頭處。本文轉發自技術世界,原文連結 http://www.jasongj.com/zookeeper/fastlead
Linux 同步機制:條件變數
條件變數的優勢 條件變數提供了一種執行緒間的通知機制,達到條件喚醒對應執行緒,配合互斥量,可以解決多執行緒中大多數的同步問題。需要訊號量的解決問題的基本都可以用條件變數加互斥量解決。由於訊號量使用起來容易出錯,實際工程中用互斥量和條件變數的更多。 互斥量可以
Linux中斷機制之三:中斷的執行
在核心程式碼中,對X86平臺中斷執行的基本過程是: 1、 通過IDT中的中斷描述符,呼叫common_interrupt; 2、 通過common_interrupt,呼叫do_IRQ,完成vector到irq_desc的轉換,進入Generic int
轉載: ssh連接上華為雲Linux服務器,一會就自動斷開
etc 配置 ive 服務端 shel 依賴 Language 斷開連接 prism 依賴 ssh 客戶端定時發送心跳,putty、SecureCRT、XShell 都有這個功能。 Linux / Unix 下,編輯 ssh 配置文件: # vim /etc/ssh/s
框架篇:Linux零拷貝機制和FileChannel
# 前言 大白話解釋,零拷貝就是沒有把資料從一個儲存區域拷貝到另一個儲存區域。但是沒有資料的複製,怎麼可能實現資料的傳輸呢?其實我們在java NIO、netty、kafka遇到的零拷貝,並不是不復制資料,而是減少不必要的資料拷貝次數,從而提升程式碼效能 - 零拷貝的好處 - 核心空間和使用者空間 - 緩衝