1. 程式人生 > >轉載:Linux notifier機制

轉載:Linux notifier機制

linux龐大系統中,各個模組是相對獨立的,那麼模組間通訊該如何做呢?當然你也可以使用全域性資源,如果這樣的話系統缺少獨立性,會帶來穩定性問題的。如果你說,使用共享記憶體,程序通訊等,那麼你曲解我的意思了,因為你說的大多是user space的,而我說的是核心模組級別的。notifier_chain,對就是它,實質上這個機制就是一個回撥函式連結串列的操作,回撥函式的註冊,登出,呼叫。源系統處(比如A子系統)進行定義初始化回撥函式呼叫,被通知處(比如B子系統)進行回撥函式的註冊登出,那麼當A系統發生某種事件是,就呼叫通知鏈中所有回撥函式,B系統中註冊的回撥函式就會得到執行。一旦執行回撥函式,它會從連結串列頭依次執行每一個回撥函式,那麼依次執行是一次性全部執行完?執行過程的任意時間都可睡眠?這些需求也就產生了4種類型的notifier_chain。

結構體定義

  1. struct notifier_block { /* chain的基本單位 */
  2. int (*notifier_call)(struct notifier_block *, unsigned long, void *);
  3. struct notifier_block __rcu *next;
  4. int priority;
  5. };
  6. struct atomic_notifier_head {/* atmoic context; 執行(rcu_read_lock);回撥函式執行不能阻塞;實時性高 */
  7. spinlock_t lock;
  8. struct notifier_block
    __rcu *head;
  9. };
  10. struct blocking_notifier_head { /* process context;執行(rw_semaphore) ;回撥函式執行可以阻塞;實時性相對低*/
  11. struct rw_semaphore rwsem;
  12. struct notifier_block __rcu *head;
  13. };
  14. struct raw_notifier_head { /* 原始連結串列操作,註冊,執行過程無任何保護,完全有驅動人員控制 */
  15. struct notifier_block __rcu *head;
  16. };
  17. struct srcu_notifier_head {
    /* process context;可阻塞通知鏈的變體(Sleepable Read-Copy-Update),回撥函式執行可以阻塞 */
  18. struct mutex mutex;
  19. struct srcu_struct srcu;
  20. struct notifier_block __rcu *head;
  21. };
notifier_chain的API使用基本四大步驟:定義初始化、註冊、呼叫和登出,而這些操作的基本單位是notifier_block,這個基本單位中包含了回撥函式notifier_call,後繼notifier_block指標,優先順序priority(預設0,數字越大優先順序越高,越靠近連結串列的頭結點,越優先得到執行)。

初始化

#include <linux/notifier.h>

  1. #define ATOMIC_NOTIFIER_HEAD(name) \
  2. struct atomic_notifier_head name = \
  3. ATOMIC_NOTIFIER_INIT(name)
  4. #define BLOCKING_NOTIFIER_HEAD(name) \
  5. struct blocking_notifier_head name = \
  6. BLOCKING_NOTIFIER_INIT(name)
  7. #define RAW_NOTIFIER_HEAD(name) \
  8. struct raw_notifier_head name = \
  9. RAW_NOTIFIER_INIT(name)
  1. /* srcu_notifier_heads must be initialized and cleaned up dynamically */
  2. extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
  3. #define srcu_cleanup_notifier_head(name) \
  4. cleanup_srcu_struct(&(name)->srcu);

經過定義及初始化後,一類的連結串列的頭就形成了,註冊就是增加節點,執行就是遍歷節點,唯一要說明的就是,srcu鏈需要動態的定義,其他三中不需要。

註冊和登出

  1. int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n);
  2. int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n)
  1. int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n);
  2. /* 註冊的notifier_block不重複*/
  3. int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *n);
  4. int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *n);
  1. int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n);
  2. int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *n);
  1. int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *n);
  2. int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *n);

其中註冊和登出都呼叫了最基本的函式如下:

  1. /*
  2. * Notifier chain core routines. The exported routines below
  3. * are layered on top of these, with appropriate locking added.
  4. */
  5. static int notifier_chain_register(struct notifier_block **nl,
  6. struct notifier_block *n)
  7. {
  8. while ((*nl) != NULL) {
  9. if (n->priority > (*nl)->priority)
  10. break;
  11. nl = &((*nl)->next);
  12. }
  13. n->next = *nl;
  14. rcu_assign_pointer(*nl, n);
  15. return 0;
  16. }
  17. static int notifier_chain_cond_register(struct notifier_block **nl,
  18. struct notifier_block *n)
  19. {
  20. while ((*nl) != NULL) {
  21. if ((*nl) == n)
  22. return 0;
  23. if (n->priority > (*nl)->priority)
  24. break;
  25. nl = &((*nl)->next);
  26. }
  27. n->next = *nl;
  28. rcu_assign_pointer(*nl, n);
  29. return 0;
  30. }
  31. static int notifier_chain_unregister(struct notifier_block **nl,
  32. struct notifier_block *n)
  33. {
  34. while ((*nl) != NULL) {
  35. if ((*nl) == n) {
  36. rcu_assign_pointer(*nl, n->next);
  37. return 0;
  38. }
  39. nl = &((*nl)->next);
  40. }
  41. return -ENOENT;
  42. }


呼叫

當A系統發生事件時,會呼叫下面函式之一的方法來,執行通知鏈中的所有回撥函式,那麼所有註冊的地方都會得到執行,除前一個函式執行返回值含有NOTIFY_STOP_MASK標誌。其中引數val是一個整形,使用者自定義含義的傳入值,引數v是一個void*型別,使用者自定義任何型別含義,一般為平臺結構體指標,使用的時候需要強制轉換。
  1. /*呼叫了__atomic_notifier_call_chain,且nr_to_call=-1表示回撥函式呼叫個數不限,nr_calls=NULL表示不關心已執行的回撥函式個數*/
  2. extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
  3. extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
  4. extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);
  5. <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);
  6. extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);
  7. extern int __raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
  8. extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);
  9. extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);

回撥函式返回值

  1. #define NOTIFY_DONE 0x0000 /* Don't care回撥函式不關心返回值*/
  2. #define NOTIFY_OK 0x0001 /* Suits me 回撥函式呼叫順利完成*/
  3. #define NOTIFY_STOP_MASK 0x8000 /* Don't call further 回撥函式鏈禁止繼續呼叫的掩碼*/
  4. #define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002)
  5. /* Bad/Veto action 回撥函式執行有錯*/
  6. /*
  7. * Clean way to return from the notifier and stop further calls.當前順利呼叫,禁止繼續呼叫
  8. */
  9. #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。
  1. /*
  2. * linux/drivers/video/fb_notify.c
  3. *
  4. * Copyright (C) 2006 Antonino Daplas <[email protected]>
  5. *
  6. * 2001 - Documented with DocBook
  7. * - Brad Douglas <[email protected]>
  8. *
  9. * This file is subject to the terms and conditions of the GNU General Public
  10. * License. See the file COPYING in the main directory of this archive
  11. * for more details.
  12. */
  13. #include <linux/fb.h>
  14. #include <linux/notifier.h>
  15. #include <linux/export.h>
  16. /*靜態定義並初始化通知鏈頭*/
  17. static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
  18. /** 註冊回撥函式,加入一個回撥函式節點
  19. * fb_register_client - register a client notifier
  20. * @nb: notifier block to callback on events
  21. */
  22. int fb_register_client(struct notifier_block *nb)
  23. {
  24. return blocking_notifier_chain_register(&fb_notifier_list, nb);
  25. }
  26. EXPORT_SYMBOL(fb_register_client);
  27. /** 登出回撥函式,刪除一個回撥函式節點
  28. * fb_unregister_client - unregister a client notifier
  29. * @nb: notifier block to callback on events
  30. */
  31. int fb_unregister_client(struct notifier_block *nb)
  32. {
  33. return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
  34. }
  35. EXPORT_SYMBOL(fb_unregister_client);
  36. /** 當源即framebuffer發生某種事件,呼叫該函式執行所有回撥函式,通知其他子系統
  37. * fb_notifier_call_chain - notify clients of fb_events
  38. *
  39. */
  40. int fb_notifier_call_chain(unsigned long val, void *v)
  41. {
  42. return blocking_notifier_call_chain(&fb_notifier_list, val, v);
  43. }
  44. EXPORT_SYMBOL_GPL(fb_notifier_call_chain);

假如framebuffer為A子系統,觸屏ft5x06為B子系統,現想要做到觸屏伴隨顯示屏息屏而休眠,亮屏而喚醒。

B子系統(觸屏)/被通知者,程式碼如下:
  1. kernel\drivers\input\touchscreen\ft5x06_ts.c
  2. #if defined(CONFIG_FB)
  3. static int fb_notifier_callback(struct notifier_block *self,
  4. unsigned long event, void *data)
  5. {
  6. struct fb_event *evdata = data;
  7. int *blank;
  8. struct ft5x06_ts_data *ft5x06_data =
  9. container_of(self, struct ft5x06_ts_data, fb_notif);
  10. /* 檢測是否是顯示器BLANK改變事件 */
  11. if (evdata && evdata->data && event == FB_EVENT_BLANK &&
  12. ft5x06_data && ft5x06_data->client) {
  13. blank = evdata->data;
  14. if (*blank == FB_BLANK_UNBLANK) /*是BLANK事件中的LCD亮屏事件,喚醒觸屏*/
  15. ft5x06_ts_resume(&ft5x06_data->client->dev);
  16. else if (*blank == FB_BLANK_POWERDOWN) /*是BLANK事件中的LCD滅屏事件,讓觸屏休眠 */
  17. ft5x06_ts_suspend(&ft5x06_data->client->dev);
  18. }
  19. return 0;
  20. }
  21. #elif defined(CONFIG_HAS_EARLYSUSPEND)
  22. //……
  23. #endif
  24. static int ft5x06_ts_probe(struct i2c_client *client,
  25. const struct i2c_device_id *id)
  26. {
  27. //……
  28. #if defined(CONFIG_FB)
  29. data->fb_notif.notifier_call = fb_notifier_callback;
  30. /*註冊fb回撥函式*/
  31. err = fb_register_client(&data->fb_notif);
  32. if (err)
  33. dev_err(&client->dev, “Unable to register fb_notifier: %d\n”,
  34. err);
  35. #endif
  36. //……
  37. }
  38. static int __devexit ft5x06_ts_remove(struct i2c_client *client)
  39. {
  40. //……
  41. #if defined(CONFIG_FB)
  42. /*登出fb回撥函式*/
  43. if (fb_unregister_client(&data->fb_notif))
  44. dev_err(&client->dev, “Error occurred while unregistering fb_notifier.\n”);
  45. #elif defined(CONFIG_HAS_EARLYSUSPEND)
  46. unregister_early_suspend(&data->early_suspend);
  47. #endif
  48. //……
  49. }

A子系統(framebuffer)/通知者,程式碼如下:
  1. int fb_blank(struct fb_info *info, int blank)
  2. {
  3. int ret = -EINVAL;
  4. if (blank > FB_BLANK_POWERDOWN)
  5. blank = FB_BLANK_POWERDOWN;
  6. if (info->fbops->fb_blank) /*硬體執行亮屏還是滅屏的操作*/
  7. ret = info->fbops->fb_blank(blank, info);
  8. if (!ret) {
  9. struct fb_event event;
  10. event.info = info;
  11. event.data = ␣
  12. /*硬體BLANK操作成功後,呼叫所有的註冊回撥函式,比如通知給觸屏*/
  13. fb_notifier_call_chain(FB_EVENT_BLANK, &event);
  14. }
  15. return ret;
  16. }
  17. /*fbmem中的ioctl*/
  18. static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
  19. unsigned long arg)
  20. {
  21. //…….
  22. case FBIOBLANK: //由上層發下來的亮屏還是息屏的IO命令
  23. if (!lock_fb_info(info))
  24. return -ENODEV;
  25. console_lock();
  26. info->flags |= FBINFO_MISC_USEREVENT;
  27. ret = fb_blank(info, arg);
  28. info->flags &= ~FBINFO_MISC_USEREVENT;
  29. console_unlock();
  30. unlock_fb_info(info);
  31. break;
  32. //……
  33. }

至於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遇到的零拷貝,並不是不復制資料,而是減少不必要的資料拷貝次數,從而提升程式碼效能 - 零拷貝的好處 - 核心空間和使用者空間 - 緩衝