redis bitmap,slect,poll,epoll區別

 2023-10-18 阅读 23 评论 0

摘要:一、select、poll、epoll三者的特點及區別 (1)select系統調用 下面是select的函數接口: int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); select創建了3個文件描述符集(fd_set)并拷

一、select、poll、epoll三者的特點及區別

(1)select系統調用
下面是select的函數接口:
int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

select創建了3個文件描述符集(fd_set)并拷貝到內核中,分別監聽讀、寫、異常動作。
select可以監聽的文件描述符受到單個進程所能打開的fd的限制,默認為1024.
采用輪詢方式,遍歷所有的fd,最后返回一個文件描述符是否就緒的mask掩碼,并根據mask掩碼給fd_set賦值。將之前的fd_set拷貝傳出到用戶態并返回就緒的文件描述符的總個數。
用戶態并不知道是哪些文件描述符處于就緒態,需要遍歷來判斷。應用程序索引就緒文件描述符的時間復雜度是O(n).
再次調用select時,需要將新的fd_set監聽文件描述符拷貝傳入進內核。
select只能工作在相對較低下的LT模式

(2)poll系統調用

int poll (struct pollfd *fds, unsigned int nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};

將struct pollfd結構體數組拷貝到內核中進行監聽。poll采用鏈表poll_list來進行文件描述符的存儲,因此poll可以監聽的文件描述符數為系統可以打開的最大文件描述符數(65535)。采用輪詢方式,查詢每個fd的狀態,如果就緒,內核就修改fd對應的revents的值,而events成員保持不變,因此下次調用poll時,應用程序無需重置pollfd類型的事件集參數。將之前傳入的struct pollfd結構體數組拷貝傳出到用戶態,并返回就緒文件描述符的總個數。用戶態并不知道是哪些文件描述符處于就緒態,需要遍歷來判讀。應用程序索引就緒文件描述符的時間復雜度是O(n).poll只能工作在相對較低下的LT模式。

(3)epoll系統調用

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);typedef union epoll_data {void *ptr;int fd;__uint32_t u32;__uint64_t u64;} epoll_data_t;struct epoll_event {__uint32_t events;      /* Epoll events */epoll_data_t data;      /* User data variable */};int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

redis bitmap、執行epoll_create()函數會在內核創建一顆紅黑樹rb_node以及就緒鏈表rdllist(存放已經就緒的文件描述符),監聽的文件描述符數為系統可以打開的最大文件描述符數(65535)。接著用戶執行的epoll_ctl()函數將epoll_event結構體拷貝傳入內核,內核會在紅黑樹上添加相應的結點,并注冊回調函數ep_poll_callback(),內核在檢測到某文件描述符可讀/可寫時就調用回調函數callback,該回調函數將文件描述符放入就緒鏈表rdllist中。epoll_wait()函數只需要觀察rdllist中有無就緒的文件描述符即可,內核將就緒的文件描述符事件復制到傳入的poll_event結構體數組中返回給用戶空間,所以用戶只用遍歷依次處理即可,即應用程序索引就緒文件描述符的時間復雜度是O(1)。這里返回的文件描述符是通過mmap讓內核和用戶空間共享同一塊內存傳遞的,減少了不必要的拷貝。再次調用epoll系統調用,不用重建紅黑樹,直接沿用已經存在的即可。epoll支持ET模式,當內核將該事件通知給用戶后,用戶必須立即處理,這樣就減少了可讀、可寫和異常事件被觸發的次數。

二、為什么epoll比select和poll更高效?

(1)減少了用戶態和內核態之間文件描述符的拷貝

select創建了3個文件描述符集(fd_set)并拷貝到內核中,分別監聽讀、寫、異常事件。內核分配相關數據結構(fd_set_bits),內核在檢測到有就緒事件后,就修改用戶傳進來的fd_set的值以告知用戶有就緒的文件描述符。將文件描述符fd_set拷貝傳出到用戶態并返回就緒的文件描述符的總個數。內核刪除和文件描述符相關的數據結構,由于內核修改了用戶傳進來的fd_set文件描述符集,下次調用select前必須要重置fd_set,然后重新傳給內核,內核在重新拷貝一份,重新分配數據結構。

poll系統調用將struct pollfd結構體數組拷貝到內核中進行監聽,內核分配相關數據結構poll_list,用來存儲監聽的文件描述符,然后調用所有fd對應的poll(將current掛到各個fd對應的設備等待隊列上),內核在檢測到有就緒事件后,就修改fd對應的revents的值用來告知用戶有就緒的文件描述符,而events成員保持不變,因此下次調用poll時,應用程序無需重置pollfd類型的事件集參數。將之前傳入的struct pollfd結構體數組拷貝傳出到用戶態,并返回就緒文件描述符的總個數。內核刪除和文件描述符相關的數據結構,下次調用poll需要將struct pollfd重新傳給內核,內核在重新拷貝一份,重新分配數據結構。

執行epoll_create()函數會在內核創建一顆紅黑樹rb_node以及就緒鏈表rdllist(存放已經就緒的文件描述符),接著用戶執行的epoll_ctl()函數將epoll_event結構體拷貝傳入內核,內核會在紅黑樹上添加相應的結點,內核將就緒的文件描述符事件復制到傳入的poll_event結構體數組中返回給用戶空間,系統調用在返回時采用mmap共享存儲區,需要拷貝的次數大大減少。由于epoll創建的有關文件描述符的數據結構本身就存在于內核態中。下一次調用epoll系統調用時,不需要再次拷貝用戶空間所要監聽的文件描述符,也不需要重新構建紅黑樹和就緒鏈表等相關數據結構,直接沿用已經存在的數據結構。

(2)減少了對就緒文件描述符的遍歷

select數據結構。select和poll采用輪詢的方式來檢查文件描述符是否處于就緒狀態。并且內核修改用戶傳進來的fd_set和pollfd結構體的成員的revents值以告知用戶有文件描述符就緒,但是用戶并不知道有哪些文件描述符處于就緒態,需要遍歷查找就緒文件描述符,因此,應用程序索引就緒文件描述符的時間復雜度為O(n).

而epoll采用回調機制。在調用epoll_ctl時,已經將用戶感興趣的事件傳給了內核,內核會維持一個內核事件表,記錄用戶感興趣的事件,就緒事件發生時,驅動設備調用回調函數ep_poll_callback()將就緒的fd掛到rdllist上。用戶調用epoll_wait時,將rdllist上就緒的文件描述符發送給用戶。此時發送給用戶的都是就緒的fd。因此,應用程序索引就緒文件描述符的時間復雜度為O(1)。

(3)select和poll只支持LT模式,而epoll支持高效的ET模式,并且epoll還支持EPOLLONESHOT事件。

LT模式(電平觸發):LT模式是默認的工作模式,當檢測到文件描述符上有事件發生并將此事件通知給應用程序,應用程序可以不立即處理該事件,下次調用會再次響應應用程序并通知此事件。

ET模式(邊沿觸發):當檢測到文件描述符上有事件發生并將此事件通知給應用程序,應用程序必須立即處理該事件,如果沒處理或者沒處理完,下次調用不會再響應應用程序并通知此事件。

ET模式很大程度上減少了epoll事件被重復觸發的次數,因此效率要比LT模式高,epoll工作在ET模式的時候,必須使用非阻塞的套接字,以避免由于一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。

select函數。即使使用ET模式,一個socket上的某個事件還是可能被觸發多次,這在并發程序中就會引發一個問題。比如一個線程在讀取完某個socket上的數據開始處理這些數據的時候,而在數據的處理過程中這個socket上又有新數據可讀,這時另一個線程被喚醒來處理新數據,于是就出現了兩個線程同時操作一個socket的局面。因此需要使用epoll的EPOLLONESHOT事件實現。對于注冊了EPOLLONESHOT事件的文件描述符,操作系統最多觸發其上的一個讀、寫或異常事件,且只觸發一次。當一個線程在處理socket時,其它線程是不可能有機會操作該socket的。注冊了EPOLLONESHOT事件的socket一旦被某個線程處理完,該線程就應該立即重置這個socket上的EPOLLONESHOT事件,以確保這個socket下次可讀時,其EPOLLIN事件可被觸發,進而讓其它線程有機會處理這個socket。使用EPOLLONESHOT事件能進一步減少可讀、可寫和異常事件的被觸發的次數。

三、無論哪種情況下,epoll都比select和poll高效嗎?

epoll適用于連接較多,活動數量較少的情況。
(1)epoll為了實現返回就緒的文件描述符,維護了一個紅黑樹和好多個等待隊列,內核開銷很大。如果此時監聽了很少的文件描述符,底層的開銷會得不償失;

(2)epoll中注冊了回調函數,當有事件發生時,服務器設備驅動調用回調函數將就緒的fd掛在rdllist上,如果有很多的活動,同一時間需要調用的回調函數數量太多,服務器壓力太大。

select和poll適用于連接較少的情況。
當select和poll上監聽的fd數量較少,內核通知用戶現在有就緒事件發生,應用程序判斷當前是哪個fd就緒所消耗的時間復雜度就會大大減小。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/148929.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息