select * 映射错误_高性能IO模型分析-浅析Select、Poll、Epoll机制(三)
本章(第三章)內容其實和第二章內容,都是第一章內容的延伸。第二章內容是第一章內容的延伸,本章內容則是第一章內容再往底層方面的延伸,也是面試中考察網絡方面知識時,可能會問到的幾個點。
select、poll、epoll都是I/O多路復用的機制。I/O多路復用就是通過一種機制,一個進程可以監視多個文件描述符,一旦某個描述符就緒(讀就緒或寫就緒),能夠通知程序進行相應的讀寫操作 。
但是,select,poll,epoll本質還是同步I/O(I/O多路復用本身就是同步IO)的范疇,因為它們都需要在讀寫事件就緒后線程自己進行讀寫,讀寫的過程阻塞的。而異步I/O的實現是系統會把負責把數據從內核空間拷貝到用戶空間,無需線程自己再進行阻塞的讀寫,內核已經準備完成。
一、Select機制
API簡介
linux系統中/usr/include/sys/select.h文件中對select方法的定義如下:
/* fd_set for select and pselect. */typedef struct { /* XPG4.2 requires this member name. Otherwise avoid the name from the global namespace. */ #ifdef __USE_XOPEN __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; # define __FDS_BITS(set) ((set)->fds_bits) #else __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS]; # define __FDS_BITS(set) ((set)->__fds_bits) #endif } fd_set;/* Check the first NFDS descriptors each in READFDS (if not NULL) for read readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS (if not NULL) for exceptional conditions. If TIMEOUT is not NULL, time out after waiting the interval specified therein. Returns the number of ready descriptors, or -1 for errors. This function is a cancellation point and therefore not marked with __THROW. */extern int select (int __nfds, fd_set *__restrict __readfds, fd_set *__restrict __writefds, fd_set *__restrict __exceptfds, struct timeval *__restrict __timeout);int __nfds是fd_set中最大的描述符+1,當調用select時,內核態會判斷fd_set中描述符是否就緒,__nfds告訴內核最多判斷到哪一個描述符。
__readfds、__writefds、__exceptfds都是結構體fd_set,fd_set可以看作是一個描述符的集合。 select函數中存在三個fd_set集合,分別代表三種事件,readfds表示讀描述符集合,writefds表示讀描述符集合,exceptfds表示異常描述符集合。當對應的fd_set = NULL時,表示不監聽該類描述符。
timeval __timeout用來指定select的工作方式,即當文件描述符尚未就緒時,select是永遠等下去,還是等待一定的時間,或者是直接返回
函數返回值int表示: 就緒描述符的數量,如果為-1表示產生錯誤 。
運行機制
Select會將全量fd_set從用戶空間拷貝到內核空間,并注冊回調函數, 在內核態空間來判斷每個請求是否準備好數據 。select在沒有查詢到有文件描述符就緒的情況下,將一直阻塞(I/O多路服用中提過:select是一個阻塞函數)。如果有一個或者多個描述符就緒,那么select將就緒的文件描述符置位,然后select返回。返回后,由程序遍歷查看哪個請求有數據。
Select的缺陷
- 每次調用select,都需要把fd集合從用戶態拷貝到內核態,fd越多開銷則越大;
- 每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大
- select支持的文件描述符數量有限,默認是1024。參見/usr/include/linux/posix_types.h中的定義:
# define __FD_SETSIZE 1024
二、Poll機制
API簡介
linux系統中/usr/include/sys/poll.h文件中對poll方法的定義如下:
/* Data structure describing a polling request. */struct pollfd { int fd; /* File descriptor to poll. */ short int events; /* Types of events poller cares about. */ short int revents; /* Types of events that actually occurred. */ };/* Poll the file descriptors described by the NFDS structures starting at FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for an event to occur; if TIMEOUT is -1, block until an event occurs. Returns the number of file descriptors with events, zero if timed out, or -1 for errors. This function is a cancellation point and therefore not marked with __THROW. */extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);__fds參數時Poll機制中定義的結構體pollfd,用來指定一個需要監聽的描述符。結構體中fd為需要監聽的文件描述符,events為需要監聽的事件類型,而revents為經過poll調用之后返回的事件類型,在調用poll的時候,一般會傳入一個pollfd的結構體數組,數組的元素個數表示監控的描述符個數。
__nfds和__timeout參數都和Select機制中的同名參數含義類似
運行機制
poll的實現和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結構代替select的fd_set(網上講:類似于位圖)結構,其他的本質上都差不多。所以Poll機制突破了Select機制中的文件描述符數量最大為1024的限制。
Poll的缺陷
Poll機制相較于Select機制中,解決了文件描述符數量上限為1024的缺陷。但另外兩點缺陷依然存在:
- 每次調用poll,都需要把fd集合從用戶態拷貝到內核態,fd越多開銷則越大;
- 每次調用poll,都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大
三、Epoll機制
Epoll在Linux2.6內核正式提出,是基于事件驅動的I/O方式。相對于select來說,epoll沒有描述符個數限制;使用一個文件描述符管理多個描述符,將用戶關心的文件描述符的事件存放到內核的一個事件表中,通過內存映射,使其在用戶空間也可直接訪問,省去了拷貝帶來的資源消耗。
API簡介
linux系統中/usr/include/sys/epoll.h文件中有如下方法:
/* Creates an epoll instance. Returns an fd for the new instance. The "size" parameter is a hint specifying the number of file descriptors to be associated with the new instance. The fd returned by epoll_create() should be closed with close(). */extern int epoll_create (int __size) __THROW;/* Manipulate an epoll instance "epfd". Returns 0 in case of success, -1 in case of error ( the "errno" variable will contain the specific error code ) The "op" parameter is one of the EPOLL_CTL_* constants defined above. The "fd" parameter is the target of the operation. The "event" parameter describes which events the caller is interested in and any associated user data. */extern int epoll_ctl (int __epfd, int __op, int __fd, struct epoll_event *__event) __THROW;/* Wait for events on an epoll instance "epfd". Returns the number of triggered events returned in "events" buffer. Or -1 in case of error with the "errno" variable set to the specific error code. The "events" parameter is a buffer that will contain triggered events. The "maxevents" is the maximum number of events to be returned ( usually size of "events" ). The "timeout" parameter specifies the maximum wait time in milliseconds (-1 == infinite). This function is a cancellation point and therefore not marked with __THROW. */extern int epoll_wait (int __epfd, struct epoll_event *__events, int __maxevents, int __timeout);epoll_create函數:創建一個epoll實例并返回,該實例可以用于監控__size個文件描述符
epoll_ctl函數:向epoll中注冊事件,該函數如果調用成功返回0,否則返回-1。
- __epfd為epoll_create返回的epoll實例
- __op表示要進行的操作
- __fd為要進行監控的文件描述符
- __event要監控的事件
epoll_wait函數:類似與select機制中的select函數、poll機制中的poll函數,等待內核返回監聽描述符的事件產生。該函數返回已經就緒的事件的數量,如果為-1表示出錯。
- __epfd為epoll_create返回的epoll實例
- __events數組為 epoll_wait要返回的已經產生的事件集合
- __maxevents為希望返回的最大的事件數量(通常為__events的大小)
- __timeout和select、poll機制中的同名參數含義相同
運行機制
epoll操作過程需要上述三個函數,也正是通過三個函數完成Select機制中一個函數完成的事情,解決了Select機制的三大缺陷。epoll的工作機制更為復雜,我們就解釋一下,它是如何解決Select機制的三大缺陷的。
工作模式
相較于Select和Poll,Epoll內部還分為兩種工作模式: LT水平觸發(level trigger)和ET邊緣觸發(edge trigger)。
- LT模式: 默認的工作模式,即當epoll_wait檢測到某描述符事件就緒并通知應用程序時,應用程序可以不立即處理該事件;事件會被放回到就緒鏈表中,下次調用epoll_wait時,會再次通知此事件。
- ET模式: 當epoll_wait檢測到某描述符事件就緒并通知應用程序時,應用程序必須立即處理該事件。如果不處理,下次調用epoll_wait時,不會再次響應并通知此事件。
由于上述兩種工作模式的區別,LT模式同時支持block和no-block socket兩種,而ET模式下僅支持no-block socket。即epoll工作在ET模式的時候,必須使用非阻塞套接口,以避免由于一個fd的阻塞I/O操作把多個處理其他文件描述符的任務餓死。ET模式在很大程度上減少了epoll事件被重復觸發的次數,因此效率要比LT模式高。
Epoll的優點
- 使用內存映射技術,節省了用戶態和內核態間數據拷貝的資源消耗;
- 通過每個fd定義的回調函數來實現的,只有就緒的fd才會執行回調函數。I/O的效率不會隨著監視fd的數量的增長而下降;
- 文件描述符數量不再受限;
四、Select、Poll、Epoll機制的對比
下圖主流I/O多路復用機制的benchmark:
當并發fd較小時,Select、Poll、Epoll的響應效率想差無幾,甚至Select和Poll更勝一籌。但是當并發連接(fd)較多時,Epoll的優勢便真正展現出來。
下面一張表格總結三種模式的區別:
通過上述的一些總結,希望我們對I/O多路復用的Select、Poll、Epoll機制有一個更深刻的認識。也要明白為什么epoll會成為Linux平臺下實現高性能網絡服務器的首選I/O多路復用機制。
五、Epoll的使用場景
上面的文章中已經不斷介紹了Epoll機制的優勢,又提到它是Linux平臺下實現高性能網絡服務器的首選I/O復用機制。實際工作中,我們在哪里會用到它?怎么用呢?
比如下面代碼,就是我們使用高性能網絡框架Netty實現IM項目中對于netty的bossGroup和workerGroup以及serverChannel的配置
String os = System.getProperty("os.name");if(os.toLowerCase().startsWith("win") || os.toLowerCase().startsWith("mac")){ // 點開NioEventLoopGroup的源碼,對于這個類是這么注釋的 // MultithreadEventLoopGroup implementations which is used for NIO Selector based Channel bossGroup = new NioEventLoopGroup(1); workerGroup = new NioEventLoopGroup(4);}else{ // 點開EpollEventLoopGroup的源碼,對于這個類是這么注釋的 // EventLoopGroup which uses epoll under the covers. Because of this it only works on linux. bossGroup = new EpollEventLoopGroup(1); workerGroup = new EpollEventLoopGroup(4);}bootStrap = new ServerBootstrap();bootStrap.group(bossGroup,workerGroup);if(os.toLowerCase().startsWith("win") || os.toLowerCase().startsWith("mac")) { // NioServerSocketChannel implementation which uses NIO selector based implementation to accept new connections. bootStrap.channel(NioServerSocketChannel.class);}else{ // ServerSocketChannel implementation that uses linux EPOLL Edge-Triggered Mode for maximal performance. // 注意看注釋中的“linux EPOLL Edge-Triggered Mode”,linux下ET模式的Epoll機制 bootStrap.channel(EpollServerSocketChannel.class);}看完這些,我們對Select、Poll、Epoll的了解是不是更多了一點。
至此,我們高性能IO模型分析的三篇文章已完結。如果能幫助到你,點個贊再走唄!
總結
以上是生活随笔為你收集整理的select * 映射错误_高性能IO模型分析-浅析Select、Poll、Epoll机制(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 查看git安装目录_一、Linux和Wi
- 下一篇: mysql弱口令加强_MySQL弱口令利