關於eventhub的學習有一點前置知識很重要,就是epoll和inotify機制,可以參考這篇博文:www.cheelok.com/_inx/88。
在前面input系列的博文中已經學習,來自底層的input事件經過事件樞紐eventhub處理後,讓inputreader通過epoll和inotify從eventhub中讀取。那麼現在就來學習eventhub是如何讀取底層事件的吧。
eventhub::eventhub(void) :
mbuiltinkeyboardid(no_built_in_keyboard), mnextdeviceid(1), mcontrollernumbers(),
mopeningdevices(0), mclosingdevices(0),
mneedtosendfinisheddevicescan(false),
mneedtoreopendevices(false), mneedtoscandevices(true),
mpendingeventcount(0), mpendingeventindex(0), mpendinginotify(false) 複製**
總結下來就是這張圖:
按照上面的學習,我們知道:eventhub在建立時,建立了兩個fd,mepollfd和minotifyfd。其中minotifyfd用於監聽裝置節點是否有裝置檔案的增刪,將minotifyfd註冊到mepollfd中,當發生新的裝置增刪,裝置節點下的裝置檔案也會隨之增刪,就會通知mepollfd有新的可讀的裝置增刪事件,通知eventhub對裝置進行處理。
換言之,剛建立eventhub時,mepollfd只監聽了minotifyfd。
getevents整個函式比較長,我就不貼所有**了,這個函式主要做了以下事情:
建立乙個大小為buffersize的input_event的緩衝區,用於儲存讀取的input事件
判斷是否需要重新開啟input裝置
處理最後被新增/刪除的input裝置,其中會為了處理新增的裝置而進行裝置掃瞄
判斷是否需要掃瞄裝置
獲取input事件
開啟/關閉在步驟3中新增/刪除的input裝置
如果裝置資訊發生變化,則通知
喚醒epoll_wait,通知inputreader讀取事件,需要注意的是,整個喚醒過程都是加鎖的
對於我們來說,學習重點自然是步驟4了,但是在看它做了什麼之前不妨先想想,建立eventhub之後,第一次getevents時,mepollfd只監聽了minotifyfd,那mepollfd要怎麼獲取裝置上的發生的input事件呢?
我們在getevents裡面可以看到,在獲取input事件之前,會判斷是否需要掃瞄裝置:
if (mneedtoscandevices) 複製**
mneedtoscandevices在建立eventhub時是預設賦值為true的,那麼第一次走getevents肯定會走進來,scandeviceslocked最終會呼叫scandirlocked掃瞄/dev/input。在這個函式裡面就是迴圈掃瞄/dev/input下的裝置檔案了:
status_t eventhub::scandirlocked(const
char *dirname)
……}複製**
看起來掃瞄到裝置檔案後就會opendevicelocked,在這個函式裡其實沒做什麼特別的事情,就是新增裝置以及各種處理,有乙個地方需要關注下:
// register with epoll.
struct epoll_event eventitem;
memset(&eventitem, 0, sizeof(eventitem));
eventitem.events = epollin;
if (musingepollwakeup)
eventitem.data.u32 = deviceid;
if (epoll_ctl(mepollfd, epoll_ctl_add, fd, &eventitem))
string8 wakemechanism("epollwakeup");
if (!musingepollwakeup) else
}複製**
這裡我們將新裝置的fd註冊到mepollfd中進行監聽,並且寫入e喚醒epoll。於是前面的問題就解決了,由於每次獲取input事件前都會更新裝置資訊,因此mepollfd能監聽到最新的裝置fd。
獲取input事件的整個流程**很多,我就不貼了,主要做了以下事情:
迴圈讀取mpendingeventitems中的eventitem
判斷eventitem是否為合法的inotify型別(eventitem.data.u32為epoll_id_inotify,且eventitem.events & epollin為true),如果是,說明有新的裝置增刪事件,則需要更新裝置列表資訊
判斷eventitem是否為合法的喚醒管道讀端的事件(eventitem.data.u32為epoll_id_wake),若合法,則喚醒管道的讀端,也就是inputreader
判斷eventitem的接收裝置是否合法
如果eventitem不屬於epoll_id_inotify、epoll_id_wake型別,且是epoll的輸入事件(epollin),則開始事件讀取的邏輯
事件讀取的邏輯裡做了以下事情:
首先通過裝置的readsize判斷裝置是否被移除、裝置讀取的事件總大小是否為input_event倍數(是否符合讀取格式)、readsize是否合法,通過以上檢查則獲取裝置id
迴圈讀取readbuffer中的input_event
對input_event的發生時間進行處理,儲存到rawevent的when中
將deviceid、type、code、value封裝到rawevent中
於是eventhub就成功地得到了來自裝置的事件,並成功將它們轉化為rawevent,交給inputreader。
至此,本文結束。
如果你覺得我的分享有幫助到你的話,請我吃個零食/喝杯咖啡唄~
字元裝置與塊裝置
系統中能夠隨機 不需要按順序 訪問固定大小資料片 chunks 的裝置被稱作塊裝置,這些資料片就稱作塊。最常見的塊裝置是硬碟,除此以外,還有軟盤驅動器 cd rom驅動器和快閃儲存器等等許多其他塊裝置。注意,它們都是以安裝檔案系統的方式使用的 這也是塊裝置一般的訪問方式。一般訪問單位為512k的整數...
字元裝置與塊裝置的區別
1 在linux裡面,裝置型別分為 字元裝置 塊裝置以及網路裝置。字元裝置 塊裝置主要區別是 在對字元裝置發出讀 寫請求時,實際的硬體i o一般就緊接著發生了,而塊裝置則不然,它利用一塊系統記憶體作為緩衝區,當使用者程序對裝置請求能滿足使用者的要求時,就返回請求的資料,如果不能就呼叫請求函式來進行實...
字元裝置與塊裝置的區別
塊裝置 系統中可以隨機訪問 不按順序訪問 資料,這種裝置稱為塊裝置。比如我們常用的磁碟就是一種典型的塊裝置。字元裝置 系統中按字元流的方式有序的訪問資料,這種裝置稱為字元裝置。比如我們常用的鍵盤。為什麼說磁碟是一種典型的塊裝置呢?假如磁碟是按字元裝置的方式訪問資料,那就是說磁碟是按順序訪問資料的。如...