Linux驅動之多路監聽

Select/poll:IO多路監聽

案例:一個應用程序如何去處理多個設備,例如網口,串口,按鍵數據

明確:對設備訪問永遠先open

方法一:串行+阻塞的方式:缺點:每當阻塞讀取標準輸入時如果用戶不進行標準輸入的操作,而此時客戶端給服務器發送數據,導致服務器無法讀取客戶端發來的數據!

方法二、採用多線程或者多進程機制來實現讀取:開闢多個線程,每一個線程處理一個設備,不會導致數據無法讀取,但是系統的開銷會增加。

方法三:採用linux系統提供的高級IO的處理機制

Select/poll:兩者一樣,主進程能夠利用select或者polll能夠對多個設備進行監聽。

Select 函數原型:int  select(int nfds, fd_set * readfds, fd_set *writefds, fd_set * exceptfds,

Struct timeval * timeout);

函數功能:主進程利用此函數能夠對多個設備進行監聽,一旦發現監聽的設備都不可用(不可讀也不可寫也沒有異常),那麼主進程進入休眠狀態,一旦監聽的設備中,只要有一個設備可以(可讀或可寫或沒有異常)都會喚醒休眠的主進程,select也就會返回。注意這個函數僅僅起到一個監聽的功能,數據的後續處理,例如讀和寫都是通過read、write、ioctl來。

分析以上的總結內容:1.多個設備就代表着有多個驅動程序;每一個設備都對應一個驅動程序;2.設備不可用代表着應用程序會到內核空間操作設備,只要在內核空間纔有權限訪問硬件設備。3.主進程休眠代表主進程進入內核空間進行休眠,在內核空間將利用等待隊列機制讓主進程休眠; 4.等待隊列需要有一個等待隊列頭,喚醒休眠的進程,使用wake_up,這個函數只需傳遞一個等待隊列頭即可喚醒休眠進程;只要有一個設備可用都會喚醒休眠的進程,這句話的潛臺詞就是一個主進程分別被添加到被監聽的設備對應的驅動程序定義的等待隊列頭中。

5.select函數引起的主進程休眠,假如底層驅動也有對應的select函數,那隻需利用等待隊列機制讓主進程在底層驅動的select函數進行休眠操作即可。

參數說明:nfds:對設備的訪問永遠都是先open獲取fd;監聽的設備中,最大的文件描述符集合,用來保存描述監聽的設備,裏面存放時被監聽設備的文件描述符;如果select要監聽某一個設備,必須把這個設備的fd添加到對應的文件描述符集合中!readfds:讀文件描述符集合指針,如果select要監聽設備是否可讀,需將設備的fd添加到這個集合中! Writefds:寫文件描述符集合指針,如果select要監聽設備是否可寫,需將設備的fd添加到這個集合中! Exceptfds:異常文件描述符集合指針,如果select要監聽設備是否有異常,需將設備的fd添加到這個集合中!

注意:一個設備的fd可以同時添加到三個集合中!

Timeout:指定監聽的超時時間,如果此參數指定一個時間,例如5秒鐘,select發現設備不可用,主進程進入休眠狀態,如果5秒之內設備還不可用,5秒到期,主進程主動喚醒;如果此參數指定爲NULL,休眠爲永久休眠!

返回值有三種:-1表示出錯 0表示沒有文件描述符準備好正數表示已經準備好的描述符的數 FD_ISSET測試一指定位是否被設置,若fd在描述符中,則返回非0,否則返回0.

Int FD_ISSET(INT FD, fd_set *set);

Void FD_SET(int fd, fd_set * set); //添加一個新的被監聽的設備

Void FD_ZERO(fd_set * set); //清空文件描述符集合; 注意:如果要重複監聽,需要再次清空集合和添加監聽設備!


Select系統調用過程:1.應用程序調用select,首先調用C庫的select函數實現;2.C庫的select保存select系統調用號到R7,調用SVC或者SWI觸發軟中斷,至此由用戶空間寫入內核空間,ARM的工作模式由用戶模式轉變爲SVC管理模式;3.調轉到內核準備好的異常向量表的入口地址,根據R7保存的系統調用,以它爲索引在系統調用表中找到對應的實現函數sys_selcect 4.select要完成:(1)把被監聽的設備對應的驅動程序的poll函數挨個調用一遍,被監聽的設備都不可用,他們的驅動的poll函數都返回0; (2)判斷是否是驅動主動喚醒,還是超時喚醒,還是接收到信號喚醒;(3)如果既沒有驅動主動喚醒,也沒有超時喚醒,沒有接收到信號,sys_select調用poll_schedule_timeput主動讓進程進入休眠。 (4)假設被監聽的設備中,有一個設備可用(可讀可寫或異常,硬件中斷通過中斷來判斷),都會喚醒休眠的主進程;(5)sys_select的poll_schedule_timeout函數返回,不在休眠。(6)再次把被監聽的設備驅動的poll函數挨個調用一遍,此時可用的設備對應的驅動Poll函數會返回非0 (7)if(ret || time_out || …) //ret = 1,立即返回到用戶空間。

總結:1.明確本來應該底層驅動的poll函數利用等待隊列機制讓進程進行休眠,但是等待隊列休眠9步驟並不是都是驅動的poll來編寫,有一部分是內核sys_select來實現。

2.驅動Poll函數完成如下內容即可:(1)調用poll_wait,將當前進程添加到驅動定義的等待隊列中(2)根據設備是否可用,決定返回0還是非03.明確:監聽機制,底層poll函數不是必須的,如果要監聽設備還是可以使用多線程機制也能完成監聽;但是使用select/poll監聽設備,驅動必須有poll實現!

案例:給之前的按鍵驅動添加被監聽的機制,並且採用平臺總線的軟件實現。

發佈了31 篇原創文章 · 獲贊 19 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章