非同步io是對阻塞和輪詢io的機制補充,所謂非同步io就是在裝置資料就緒時主動通知所屬程序進行處理的機制。之所以說是非同步是相對與被通知程序的,因為程序不知道也無法知道什麼時候會被通知;這一機制非常類似於硬體上的中斷。非同步io的實現也依賴於linux核心程序的訊號機制,因為非同步io就是通過sigio訊號通知的程序,而程序在收到訊號後就會像中斷一樣直接跳轉去執行之前就註冊好的訊號處理介面。
使用者空間
註冊訊號介面函式有兩個版本的介面signal(int signal,sighandler_t handler)其中signal為核心定義的訊號型別目前支援30種訊號,每種訊號也有其預設的處理方式如果當前程序未指定某一訊號的處理方式,系統就會按系統預設的方式處理。還有乙個比較新介面他功能也更加強大是 int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact))他們的詳細使用和定義內容如下:
//第乙個引數指定訊號的值,第二個引數指定針對前面訊號的處理方式或介面,可以設定為如下值:介面typedef void (*sighandler_t)(int
);//
原型sighandler_t signal(int
signum, sighandler_t handler));
//定義
void (*signal(int signum, void (*handler))(int)))(int);
忽略該訊號(引數設為sig_ign)
採用系統預設方式處理訊號(引數設為sig_dfl)
可以自己實現處理方式(引數指定乙個函式位址)signal()
呼叫成功,返回上一次為成功安裝訊號signum而呼叫signal()時的handler值,失敗則返回sig_err。
sigaction:
int sigaction(int signum,constsigaction的第乙個引數和signal相同,第二和第三個引數都是 struct sigaction型別的指標,第乙個為給這個訊號新安插的處理介面,而第三個用來接收之前給這個訊號安插的處理介面的返回。這個結構體中的union 是訊號處理介面函式,使用的是哪一種取決與flags變數的bit標誌,而sa_mask則負責在訊號處理過程中對指定訊號的掩蔽類似避免中斷巢狀。最後乙個sa_restorer現在已經廢棄不建議再使用最後就是siginfo_t 結構體他是sigaction介面高階於signal介面的另乙個力證。其中主要的成員是意義使用起來比較複雜在專門的地方學習,今天主要學習驅動部分的非同步io實現機制。struct sigaction *act,struct sigaction *oldact));
//struct sigaction 結構體
struct
sigaction _u;
sigset_t sa_mask;
unsigned
long
sa_flags;
void (*sa_restorer)(void
);};
//__sighandler_t
void (*__sighandler_t)(int );
核心空間
前面說了應用層的訊號使用和非同步io的工作原理,接下來就是說明驅動也就是核心空間的非同步io實現機制了。在使用者空間是負責接收訊號那麼核心空間肯定就是負責訊號的釋放,但是要能釋放訊號給指定的程序還要滿足幾個條件:
檔案是非同步方式開啟的。
當前程序是檔案的屬主。
其中第乙個條件可以同通過開啟檔案時指定fasync標誌或通過fcntl(fd,f_setfl,...)介面修改檔案標誌以支援非同步通知。第二個則是通過使用介面fcntl(fd,f_setown,getpid());進行設定。
而在核心部分的實現主要依賴乙個資料結構和兩個介面
struct fasync_struct ;檔案操作介面中有乙個fasync介面函式在檔案async狀態變更時會被呼叫所以需要實現它,的實現模版大致如下://釋放訊號
void kill_fasync(struct fasync_struct **, int, int);
//處理檔案fasync 狀態變更的介面
int fasync_helper(int, struct file *, int, struct fasync_struct **);
ststic int ***_async(int fd,struct file* filp,int mode)然後就是釋放訊號的的操作,這一部分有可能發生在中斷也有可能發生的其他的介面函式中,比如寫介面中可以釋放io可以讀的就緒訊號給程序,而讀介面可以釋放可以寫的介面給程序等,具體的使用就是:
ststic int ***(int fd,struct file* filp,...)kill_fasync 第乙個介面引數為 非同步結構體指標而 第二個引數為訊號值一般為sigio,第三個為io事件可為如下值或如下的值的或組合
//events可以是以下幾個巨集的:
epollin :表示對應的檔案描述符可以讀(包括對端socket正常關閉);最後需要注意的是,在通過***_async介面把檔案加入到非同步通知列表後如果關閉檔案或取消非同步時需要通過這個介面把fd檔案描述符設定為-1,並把async_queue設為none將檔案從非同步通知列表中移除。epollout:表示對應的檔案描述符可以寫;
epollpri:表示對應的檔案描述符有緊急的資料可讀(這裡應該表示有帶外資料到來);
epollerr:表示對應的檔案描述符發生錯誤;
epollhup:表示對應的檔案描述符被結束通話;
epollet: 將epoll設為邊緣觸發(edge triggered)模式,這是相對於水平觸發(level triggered)來說的。
epolloneshot:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到epoll佇列裡
//如參考:static int ***_release(struct inode* inode,struct file *filp)
{ ...
***_fasync(-1,filp,0);
...return 0;
}
linux中驅動非同步通知
驅動程式執行在核心空間中,應用程式執行在使用者空間中,兩者是不能直接通訊的。但在實際應用中,在裝置已經準備好的時候,我們希望通知使用者程式裝置已經ok,使用者程式可以讀取了,這樣應用程式就不需要一直查詢該裝置的狀態,從而節約了資源,這就是非同步通知。好,那下乙個問題就來了,這個過程如何實現呢?簡單,...
Linux驅動中的非同步通知
在很多應用中都需要應用程式主動去查詢驅動中是否有資料可讀或者是否可以向驅動寫入資料,對於單執行緒的應用,這可能會導致程序阻塞。當然,可以使用select來不斷輪詢驅動是否可讀或可寫,但是這並不是很好的解決方法,更好的解決方式是由驅動主動通知應用程式其狀態,而不是應用程式主動去查詢驅動的狀態。非同步通...
linux驅動(一) linux驅動框架
編寫linux驅動先看一下驅動框架是什麼樣子的。驅動編寫和應用層編寫有什麼區別呢?一 首先 入口函式的問題。應用層編寫我們的入口就是main函式,但在驅動編寫時不是這樣的,有兩種情況,1 預設情況下 int init init module void 載入模組時的初始化函式,也就是驅動模組的入口函式...