I/O復用:即使得程序可以同時監聽多個文件描述符。
1、select系統調用
(1)系統調用原型:
(2)參數介紹:其中nfds指定被監聽的文件描述符的總數,通常被設置為select監聽的所有文件描述符中的最大值加1;readfds、writefds、exceptfds分別指向可讀、可寫和異常等對應的文件描述符集合,select調用返回時,內核將修改它們來通知應用程序哪些文件描述符已經就緒,它們都是fd_set結構指針類型,fd_set結構體定義如下:
由以上定義可知,fd_set結構體僅包含一個整形數組,該數組的每個元素的每一位標記一個文件描述符。_FD_SETSIZE限制了select能同時處理的文件描述符的總量。由于位操作過于繁瑣,采用下面的函數來訪問fd_set結構體中的位:
timeout用來設置select函數的超時時間,它是一個timeval結構類型的指針,該結構體定義如下:
若給timeout的兩個參數都傳0,則select立即返回;若給timeout傳遞NULL,則select一直阻塞,直到某個文件描述符就緒。
(3)系統調用介紹:select成功時返回就緒(可讀、可寫和異常)文件描述符的總數。
2、poll系統調用
(1)系統調用原型:
(2)參數介紹:fds是一個pollfd結構類型的數組,它指定所有我們感興趣的文件描述符上發生的可讀、可寫和異常的事件,pollfd結構體定義如下:
nfds指定被監聽事件集合fds的大小,其定義如下:
timeout指定poll的超時值,單位是ms;當timeout為-1時,poll調用將永遠阻塞,直到某個事件發生,當timeout值為0時,poll調用立即返回。
3、epoll系統調用
聲明:epoll與select、poll不同,它將用戶關心的文件描述符放在內核里的一個事件表中,不像select、poll,每次調用都要重復傳入文件描述符集或事件集。
(1)系統調用相關函數介紹:
一、epoll需要使用一個額外的文件描述符來唯一標識內核中的這個事件表,通過以下函數創建:
其中size只是告訴內核事件表需要多大的提示。
二、操作內核事件表的函數:
epfd是上述函數創建得到的文件描述符,通過它來訪問內核事件表,op指定操作的類型,有如下3種:
event指定事件,它是epoll_event結構指針類型,有關定義如下:
其中events事件常用的有EPOLLIN(讀事件)和EPOLLOUT(寫事件)。epoll_data_t是一個聯合體,它的4個成員中使用最多的是fd,它指定事件所從屬的目標文件描述符。
函數成功返回0,失敗返回-1。
三、在一段時間內等待一組文件描述符上的事件的函數:
函數成功返回就緒的文件描述符的個數,失敗返回-1。
maxevents指定最多監聽多少個事件,必須大于0,該函數若檢測到事件,就將所有就緒事件從內核事件表中復制到它的第二個參數events指向的數組中。
四、epoll的LT(水平觸發)和ET(邊沿觸發)模式。
LT:系統默認的工作模式,即阻塞,也就是會再次通知應用程序響應事件;
ET:通過注冊EPOLLET事件設置,它不會再次通知。
五、epoll的EPOLLONESHOT事件
一個socket上的某個事件很可能被觸發多次,在并發程序中,一個線程或者進程在讀取完某個socket上的數據后開始處理這些數據,而在數據處理過程中該socket上又有新數據可讀(即EPOLLIN再次觸發),這是就會喚醒另外一個線程或者進程來讀取這些數據,這種局面就是兩個線程(進程)同時操作一個socket,為了避免這種情況,注冊EPOLLONESHOT事件可以使一個socket連接在任一時刻僅被一個線程(進程)處理。
4、三組I/O復用函數的比較
epoll適用于連接數量多,但活動連接少(因為若活動連接數多,會頻繁調用回調函數)。
5、基于Reactor模式的I/O框架庫包括以下組件:
(1)句柄-----------即I/O框架庫要處理的對象(I/O事件、信號、定時事件,三者合稱為統一事件源)。一個事件源通常和一個句柄綁定在一起。當內核檢測到就緒事件時,它將通過句柄來通知應用程序這一事件。在LINUX環境下,I/O事件對應的句柄是文件描述符,信號事件對應的句柄是信號值。
(2)事件多路分發器-----------因為程序需要循環地等待并處理隨機或異步到來的事件,而等待事件一般使用I/O復用技術實現。將系統支持的各種I/O復用系統調用封裝成統一接口,稱為事件多路分發器(方法是等待事件的核心函數,內部調用的是select、poll、epoll_wait),此外,它還要提供register_event(向事件多路分發器中添加事件,被register_handler調用)和remove_event(刪除事件多路分發器中的事件,被remove_handler調用)的方法。
(3)事件處理器和具體事件處理器-----------事件處理器執行事件對應的業務邏輯。它通常包含一個或多個handle_event回調函數(等待事件-->依次處理所有就緒事件對應的事件處理器),這些回調函數在事件循環中被執行。I/O框架庫提供的事件處理器通常就是個接口(通常斜寫為虛函數),用戶需要繼承它來實現自己的事件處理器,即具體事件處理器。此外,它還提供一個get_handle方法,返回與該事件處理器關聯的句柄(解釋:當事件多路分發器檢測到有事件發生時,它是通過句柄來通知應用程序的,而只有事件處理器和句柄綁定,才能在事件發生時獲取到正確的事件處理器)。
2、使用Libevent的案例:高性能的分布式內存對象緩存軟件memcached
3、高性能I/O框架庫Libevent的優點:(1)跨平臺支持(2)統一事件源(3)線程安全