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;
}
調用:
/*調用了__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/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);
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相關的文章。