生活随笔
收集整理的這篇文章主要介紹了
I/O多路复用之epoll
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
2019獨角獸企業重金招聘Python工程師標準>>>
在上一章,我們對select進行了大致的描述,知道了它相對傳統的阻塞式服務提高了并發度,但是它也由于輪詢而導致效率底下。本文對epoll進行講解,相比select它的并發度更高,現代高負載服務器很多都采用這種模型。 在講解epoll的具體用法之前,我們先看看采用 epoll模型主要用到的三個函數以及一個數據結構。 epoll中三個主要的函數: (1)int epoll_create(int size); 功能 :生成一個epoll專用的文件描述符。 參數 :size:在該epoll fd上關注的最大socket fd數。 返回值:生成的文件描述符。 (2)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 功能 :控制某個epoll文件描述符上的事件,可以注冊事件,修改事件,刪除事件。 參數 :epfd :由 epoll_create 生成的epoll專用的文件描述符; op :EPOLL_CTL_ADD 注冊、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 刪除; fd :關聯的文件描述符; event:指向epoll_event的指針; 返回值:0:成功; -1:失敗; (3)int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout); 功能 :輪詢I/O事件的發生。 參數 :epfd :由 epoll_create 生成的epoll專用的文件描述符; events :用于回傳待處理事件的數組; maxevents:每次能處理的事件數; timeout :等待I/O事件發生的超時值;-1相當于阻塞,0相當于非阻塞; 返回值:>=0 :返回發生事件數; -1 :錯誤;
epoll中的主要數據結構: view source print ?
| 01 | typedef union epoll_data { |
| 09 | __uint32_t events; /* Epoll events */ |
| 10 | epoll_data_t data; /* User data variable */ |
其中,events的類型有: EPOLLIN :文件描述符可以讀; EPOLLOUT:文件描述符可以寫; EPOLLPRI:文件描述符有緊急的數據可讀; EPOLLERR:文件描述符發生錯誤; EPOLLHUP:文件描述符被掛斷; EPOLLET :文件描述符有事件發生; epoll的使用還是很簡單的,請看下面一個簡單的采用epoll提供并發服務的服務端程序(注:為了簡潔,都沒有進行錯誤處理,實際使用時,一定要記住進行錯誤處理。): view source print ?
| 04 | #include <netinet/in.h> |
| 05 | #include <sys/socket.h> |
| 13 | #define MAX_EPOLL_SIZE 10000 |
| 14 | #define SERVICE_PORT 8888 |
| 17 | int main(int argc, char **argv) |
| 19 | int server_fd, new_fd; |
| 20 | struct sockaddr_in server_addr, client_addr; |
| 22 | struct epoll_event ev; |
| 23 | struct epoll_event events[MAX_EPOLL_SIZE]; |
| 25 | socklen_t len = sizeof(struct sockaddr_in); |
| 26 | server_fd = socket(AF_INET, SOCK_STREAM, 0); |
| 28 | bzero(&server_addr, sizeof(server_addr)); |
| 29 | server_addr.sin_family = AF_INET; |
| 30 | server_addr.sin_port = htons(SERVICE_PORT); |
| 31 | server_addr.sin_addr.s_addr = INADDR_ANY; |
| 33 | bind(server_fd, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)); |
| 34 | listen(server_fd, 1000); |
| 36 | //create epoll fd, and register the server listening fd |
| 37 | int epoll_fd = epoll_create(MAX_EPOLL_SIZE); |
| 38 | ev.events = EPOLLIN | EPOLLET; |
| 39 | ev.data.fd = server_fd; |
| 40 | epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev); |
| 42 | int active_fd_count = 1; |
| 45 | //wait for some events to happen |
| 46 | int event_active_fd_count = epoll_wait(epoll_fd, events, active_fd_count, -1); |
| 49 | for (int i = 0; i < event_active_fd_count; ++i) |
| 51 | if (events[i].data.fd == server_fd) |
| 53 | new_fd = accept(server_fd, (struct sockaddr *) &client_addr,&len); |
| 55 | //register new fd to epoll |
| 56 | ev.events = EPOLLIN | EPOLLET; |
| 58 | epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd, &ev); |
| 63 | handle message on events[i].data.fd |
| 64 | if (client close the connection) |
| 66 | epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd,&ev); |
講完epoll的常規使用方法,這里需要注意的是epoll有兩種工作方式: (1)ET:Edge Triggered,邊緣觸發。僅當狀態發生變化時才會通知,需要細致的處理每個請求,否則容易發生丟失事件的情況。只支持非阻塞的socket。 (2)LT:Level Triggered,水平觸發(默認工作方式)。只要還有沒有處理的事件就會一直通知,因此不用擔心事件丟失的情況。效率會低于ET觸發,尤其在大并發,大流量的情況下。支持阻塞和非阻塞的socket。
最后講講 為什么epoll會比select高效,主要從三方面來進行論述。 (1)elect對描述符狀態的改變是通過輪詢來進行查找的;而epoll是當描述符狀態發生改變時主動進行通知內核,這就是所謂的Reactor事件處理機制。可以用“好萊塢原則”進行描述:不要打電話給我們,我們會打電話通知你。相比之下,select的機制就好比面試結束后不停給面試官打電話詢問面試結果。效率孰高孰低,可見一 斑。 (2)select的文件描述符是使用鏈表進行組織的;而epoll是使用紅黑樹這一高效數據結構組織的。 (3)select從內核到用戶空間傳遞文件描述符上發送的信息是使用內存復制的方式進行的;而epoll是采用共享內存的方式。
轉載于:https://my.oschina.net/u/581475/blog/73195
總結
以上是生活随笔為你收集整理的I/O多路复用之epoll的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。