日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【面试】彻底理解 IO多路复用

發布時間:2025/3/16 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【面试】彻底理解 IO多路复用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

1、什么是IO多路復用?
2、為什么出現IO多路復用機制?
3、IO多路復用的三種實現方式
4、select函數接口
5、select使用示例
6、select缺點
7、poll函數接口
8、poll使用示例
9、poll缺點
10、epoll函數接口
11、epoll使用示例
12、epoll缺點
13、epoll LT 與 ET模式的區別
14、epoll應用
15、select/poll/epoll之間的區別
16、IO多路復用完整代碼實現
17、高頻面試題

1、什么是IO多路復用

「定義」

  • IO多路復用是一種同步IO模型,實現一個線程可以監視多個文件句柄;一旦某個文件句柄就緒,就能夠通知應用程序進行相應的讀寫操作;沒有文件句柄就緒時會阻塞應用程序,交出cpu。多路是指網絡連接,復用指的是同一個線程

2、為什么有IO多路復用機制?

沒有IO多路復用機制時,有BIO、NIO兩種實現方式,但有一些問題

同步阻塞(BIO)

  • 服務端采用單線程,當accept一個請求后,在recv或send調用阻塞時,將無法accept其他請求(必須等上一個請求處recv或send完),無法處理并發

//?偽代碼描述 while(1)?{//?accept阻塞client_fd?=?accept(listen_fd)fds.append(client_fd)for?(fd?in?fds)?{//?recv阻塞(會影響上面的accept)if?(recv(fd))?{//?logic}}?? }
  • 服務器端采用多線程,當accept一個請求后,開啟線程進行recv,可以完成并發處理,但隨著請求數增加需要增加系統線程,大量的線程占用很大的內存空間,并且線程切換會帶來很大的開銷,10000個線程真正發生讀寫事件的線程數不會超過20%,每次accept都開一個線程也是一種資源浪費

//?偽代碼描述 while(1)?{//?accept阻塞client_fd?=?accept(listen_fd)//?開啟線程read數據(fd增多導致線程數增多)new?Thread?func()?{//?recv阻塞(多線程不影響上面的accept)if?(recv(fd))?{//?logic}}?? }

同步非阻塞(NIO)

  • 服務器端當accept一個請求后,加入fds集合,每次輪詢一遍fds集合recv(非阻塞)數據,沒有數據則立即返回錯誤,每次輪詢所有fd(包括沒有發生讀寫事件的fd)會很浪費cpu

setNonblocking(listen_fd) //?偽代碼描述 while(1)?{//?accept非阻塞(cpu一直忙輪詢)client_fd?=?accept(listen_fd)if?(client_fd?!=?null)?{//?有人連接fds.append(client_fd)}?else?{//?無人連接}??for?(fd?in?fds)?{//?recv非阻塞setNonblocking(client_fd)//?recv?為非阻塞命令if?(len?=?recv(fd)?&&?len?>?0)?{//?有讀寫數據//?logic}?else?{無讀寫數據}}?? }

IO多路復用(現在的做法)

  • 服務器端采用單線程通過select/epoll等系統調用獲取fd列表,遍歷有事件的fd進行accept/recv/send,使其能支持更多的并發連接請求

fds?=?[listen_fd] //?偽代碼描述 while(1)?{//?通過內核獲取有讀寫事件發生的fd,只要有一個則返回,無則阻塞//?整個過程只在調用select、poll、epoll這些調用的時候才會阻塞,accept/recv是不會阻塞for?(fd?in?select(fds))?{if?(fd?==?listen_fd)?{client_fd?=?accept(listen_fd)fds.append(client_fd)}?elseif?(len?=?recv(fd)?&&?len?!=?-1)?{?//?logic}}?? }

3、IO多路復用的三種實現方式

  • select

  • poll

  • epoll

4、select函數接口

#include?<sys/select.h> #include?<sys/time.h>#define?FD_SETSIZE?1024 #define?NFDBITS?(8?*?sizeof(unsigned?long)) #define?__FDSET_LONGS?(FD_SETSIZE/NFDBITS)//?數據結構?(bitmap) typedef?struct?{unsigned?long?fds_bits[__FDSET_LONGS]; }?fd_set;//?API int?select(int?max_fd,?fd_set?*readset,?fd_set?*writeset,?fd_set?*exceptset,?struct?timeval?*timeout )??????????????????????????????//?返回值就緒描述符的數目FD_ZERO(int?fd,?fd_set*?fds)???//?清空集合 FD_SET(int?fd,?fd_set*?fds)????//?將給定的描述符加入集合 FD_ISSET(int?fd,?fd_set*?fds)??//?判斷指定描述符是否在集合中? FD_CLR(int?fd,?fd_set*?fds)????//?將給定的描述符從文件中刪除??

5、select使用示例

int?main()?{/**?這里進行一些初始化的設置,*?包括socket建立,地址的設置等,*/fd_set?read_fs,?write_fs;struct?timeval?timeout;int?max?=?0;??//?用于記錄最大的fd,在輪詢中時刻更新即可//?初始化比特位FD_ZERO(&read_fs);FD_ZERO(&write_fs);int?nfds?=?0;?//?記錄就緒的事件,可以減少遍歷的次數while?(1)?{//?阻塞獲取//?每次需要把fd從用戶態拷貝到內核態nfds?=?select(max?+?1,?&read_fd,?&write_fd,?NULL,?&timeout);//?每次需要遍歷所有fd,判斷有無讀寫事件發生for?(int?i?=?0;?i?<=?max?&&?nfds;?++i)?{if?(i?==?listenfd)?{--nfds;//?這里處理accept事件FD_SET(i,?&read_fd);//將客戶端socket加入到集合中}if?(FD_ISSET(i,?&read_fd))?{--nfds;//?這里處理read事件}if?(FD_ISSET(i,?&write_fd))?{--nfds;//?這里處理write事件}}}

6、select缺點

  • 單個進程所打開的FD是有限制的,通過FD_SETSIZE設置,默認1024

  • 每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大

  • 對socket掃描時是線性掃描,采用輪詢的方法,效率較低(高并發時)

7、poll函數接口

poll與select相比,只是沒有fd的限制,其它基本一樣

#include?<poll.h> //?數據結構 struct?pollfd?{int?fd;?????????????????????????//?需要監視的文件描述符short?events;???????????????????//?需要內核監視的事件short?revents;??????????????????//?實際發生的事件 };//?API int?poll(struct?pollfd?fds[],?nfds_t?nfds,?int?timeout);

8、poll使用示例

//?先宏定義長度 #define?MAX_POLLFD_LEN?4096??int?main()?{/**?在這里進行一些初始化的操作,*?比如初始化數據和socket等。*/int?nfds?=?0;pollfd?fds[MAX_POLLFD_LEN];memset(fds,?0,?sizeof(fds));fds[0].fd?=?listenfd;fds[0].events?=?POLLRDNORM;int?max??=?0;??//?隊列的實際長度,是一個隨時更新的,也可以自定義其他的int?timeout?=?0;int?current_size?=?max;while?(1)?{//?阻塞獲取//?每次需要把fd從用戶態拷貝到內核態nfds?=?poll(fds,?max+1,?timeout);if?(fds[0].revents?&?POLLRDNORM)?{//?這里處理accept事件connfd?=?accept(listenfd);//將新的描述符添加到讀描述符集合中}//?每次需要遍歷所有fd,判斷有無讀寫事件發生for?(int?i?=?1;?i?<?max;?++i)?{?????if?(fds[i].revents?&?POLLRDNORM)?{?sockfd?=?fds[i].fdif?((n?=?read(sockfd,?buf,?MAXLINE))?<=?0)?{//?這里處理read事件if?(n?==?0)?{close(sockfd);fds[i].fd?=?-1;}}?else?{//?這里處理write事件?????}if?(--nfds?<=?0)?{break;???????}???}}}

9、poll缺點

  • 每次調用poll,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大

  • 對socket掃描時是線性掃描,采用輪詢的方法,效率較低(高并發時)

10、epoll函數接口

#include?<sys/epoll.h>//?數據結構 //?每一個epoll對象都有一個獨立的eventpoll結構體 //?用于存放通過epoll_ctl方法向epoll對象中添加進來的事件 //?epoll_wait檢查是否有事件發生時,只需要檢查eventpoll對象中的rdlist雙鏈表中是否有epitem元素即可 struct?eventpoll?{/*紅黑樹的根節點,這顆樹中存儲著所有添加到epoll中的需要監控的事件*/struct?rb_root??rbr;/*雙鏈表中則存放著將要通過epoll_wait返回給用戶的滿足條件的事件*/struct?list_head?rdlist; };//?APIint?epoll_create(int?size);?//?內核中間加一個?ep?對象,把所有需要監聽的?socket?都放到?ep?對象中 int?epoll_ctl(int?epfd,?int?op,?int?fd,?struct?epoll_event?*event);?//?epoll_ctl?負責把?socket?增加、刪除到內核紅黑樹 int?epoll_wait(int?epfd,?struct?epoll_event?*?events,?int?maxevents,?int?timeout);//?epoll_wait?負責檢測可讀隊列,沒有可讀?socket?則阻塞進程

11、epoll使用示例

int?main(int?argc,?char*?argv[]) {/**?在這里進行一些初始化的操作,*?比如初始化數據和socket等。*///?內核中創建ep對象epfd=epoll_create(256);//?需要監聽的socket放到ep中epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);while(1)?{//?阻塞獲取nfds?=?epoll_wait(epfd,events,20,0);for(i=0;i<nfds;++i)?{if(events[i].data.fd==listenfd)?{//?這里處理accept事件connfd?=?accept(listenfd);//?接收新連接寫到內核對象中epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);}?else?if?(events[i].events&EPOLLIN)?{//?這里處理read事件read(sockfd,?BUF,?MAXLINE);//讀完后準備寫epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);}?else?if(events[i].events&EPOLLOUT)?{//?這里處理write事件write(sockfd,?BUF,?n);//寫完后準備讀epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);}}}return?0; }

12、epoll缺點

  • epoll只能工作在linux下

13、epoll LT 與 ET模式的區別

  • epoll有EPOLLLT和EPOLLET兩種觸發模式,LT是默認的模式,ET是“高速”模式。

  • LT模式下,只要這個fd還有數據可讀,每次 epoll_wait都會返回它的事件,提醒用戶程序去操作

  • ET模式下,它只會提示一次,直到下次再有數據流入之前都不會再提示了,無論fd中是否還有數據可讀。所以在ET模式下,read一個fd的時候一定要把它的buffer讀完,或者遇到EAGAIN錯誤

14、epoll應用

  • redis

  • nginx

15、select/poll/epoll之間的區別

16、完整代碼示例

https://github.com/caijinlin/learning-pratice/tree/master/linux/io

17、高頻面試題

  • 什么是IO多路復用?

  • nginx/redis 所使用的IO模型是什么?

  • select、poll、epoll之間的區別

  • epoll 水平觸發(LT)與 邊緣觸發(ET)的區別?

有道無術,術可成;有術無道,止于術

歡迎大家關注Java之道公眾號

好文章,我在看??

新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!

總結

以上是生活随笔為你收集整理的【面试】彻底理解 IO多路复用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 草草在线影院 | 91久久久久久久久久久 | 456av| 特大黑人巨交性xxxx | 欧美福利一区二区 | 香蕉网在线 | 91视频看片 | 日韩有码av | 久久大陆 | www.猫咪av.com| 成人免费a视频 | 一区二区三区四区不卡 | 成人在线免费 | 久久综合伊人77777蜜臀 | 日本精品黄色 | 污污的视频网站在线观看 | 欧美日韩亚洲一区 | 免费视频二区 | 国内自拍视频在线观看 | 欧美在线播放视频 | 精品一区精品二区 | 亚洲欧美高清视频 | 亚洲国产精品欧美久久 | 国产精品激情偷乱一区二区∴ | 国产成人激情视频 | 色哟哟av| 日本免费一区二区视频 | 久久福利在线 | 精品国产三级a∨在线 | 小柔的裸露日记h | 亚洲一区二区在线观看视频 | 日韩av大全 | 国产网站91 | 色婷婷小说 | 四虎精品永久在线 | 91在线免费视频 | 国产精品视频一二三 | 日皮视频免费看 | 久久精品视 | 久久免费视频网站 | 高清乱码毛片 | www.狠狠撸.com | 亚洲高清视频一区二区 | 黄网在线免费观看 | 中文字幕在线观看高清 | 中文字幕网伦射乱中文 | 在线观看日韩视频 | 日本亚洲国产 | 日本一区二区网站 | 美女又大又黄 | 骚虎av在线 | 一区二区三区视频免费看 | 国产精品99久 | 伊人焦久影院 | 黄色片视频在线观看 | 高h捆绑拘束调教小说 | 欧美91成人网 | 男人的天堂狠狠干 | 激情图片网站 | 欧美日本一区二区 | 人妻无码一区二区三区久久 | 伊人中文字幕在线观看 | 少妇久久久久久被弄高潮 | www.欧美视频 | 五月天在线播放 | 中国白嫩丰满人妻videos | 国产高潮失禁喷水爽到抽搐 | 中日精品一色哟哟 | 亚欧在线播放 | 2020国产精品视频 | 丰满熟女人妻一区二区三区 | 热玖玖| 日本黄色录象 | 永久免费未满视频 | 无码精品久久久久久久 | 日韩欧美成人一区 | 色欲一区二区三区精品a片 在线观看黄网站 | 男女做事网站 | 久久精品中文闷骚内射 | 国产成人欧美一区二区三区的 | 不卡av在线免费观看 | 一区二区三区久久久 | 理论片中文字幕 | 国产精品三级av | 337p日本大胆噜噜噜噜 | 在线免费观看视频你懂的 | 亚洲一级片av | 天天干 夜夜操 | 黄色在线视频播放 | 蝌蚪久久 | 欧美sm凌虐视频网站 | 中文字幕第31页 | 国产成人无遮挡在线视频 | aa视频在线观看 | www香蕉视频 | 久久福利国产 | 欧美黄色大片在线观看 | 日本不卡视频在线播放 | 亚洲第一页在线 |