摘要:?Linux I/O多路復用 select poll epoll
Linux中一切皆文件,不論是我們存儲在磁盤上的字符文件,可執行文件還是我們的接入電腦的I/O設備等都被VFS抽象成了文件,比如標準輸入設備默認是鍵盤,我們在操作標準輸入設備的時候,其實操作的是默認打開的一個文件描述符是0的文件,而一切軟件操作硬件都需要通過OS,而OS操作一切硬件都需要相應的驅動程序,這個驅動程序里配置了這個硬件的相應配置和使用方法。Linux的I/O分為阻塞I/O,非阻塞I/O,I/O多路復用,信號驅動I/O四種。對于I/O設備的驅動,一般都會提供關于阻塞和非阻塞兩種配置。我們最常見的I/O設備之一--鍵盤(標準輸入設備)的驅動程序默認是阻塞的。
多路復用就是為了使進程能夠從多個阻塞I/O中獲得自己想要的數據并繼續執行接下來的任務。其主要的思路就是同時監視多個文件描述符,如果有文件描述符的設定狀態的被觸發,就繼續執行進程,如果沒有任何一個文件描述符的設定狀態被觸發,進程進入sleep
多路復用的一個主要用途就是實現"I/O多路復用并發服務器",和多線程并發或者多進程并發相比,這種服務器的系統開銷更低,更適合做web服務器,但是由于其并沒有實現真正的多任務,所以當壓力大的時候,部分用戶的請求響應會較慢
阻塞I/O
阻塞I/O,就是當進程試圖訪問這個I/O設備而這個設備并沒有準備好的時候,設備的驅動程序會通過內核讓這個試圖訪問的進程進入sleep狀態。阻塞I/O的一個好處就是可以大大的節約CPU時間,因為一旦一個進程試圖訪問一個沒有準備好的阻塞I/O,就會進入sleep狀態,而進入sleep狀態的進程是不在內核的進程調度鏈表中,直到目標I/O準備好了將其喚醒并加入調度鏈表,這樣就可以節約CPU時間。當然阻塞I/O也有其固有的缺點,如果進程試圖訪問一個阻塞I/O,但是否訪問成功并不對接下來的任務有決定性影響,那么直接使其進入sleep狀態顯然會延誤其任務的完成。
典型的默認阻塞IO有標準輸入設備,socket設備,管道設備等,當我們使用gets(),scanf(),read()等操作請求這些IO時而IO并沒有數據流入,就會造成進程的sleep。 進程會一直阻塞下去直到接收緩沖區中有數據可讀,此時內核再去喚醒該進程,通過相應的函數從中獲取數據。如果阻塞過程中對方發生故障,那么這個進程將會永遠阻塞下去。
寫操作時發生阻塞的情況要比讀操作少,主要發生在要寫入的緩沖區的大小小于要寫入的數據量的情況下,這時寫操作將不進行任何任何拷貝工作,將發生阻塞。一旦發送緩沖區內有足夠的空間,內核將喚醒進程,將數據從用戶緩沖區中拷貝到相應的發送數據緩沖區。udp不用等待確認,沒有實際的發送緩沖區,所以udp協議中不存在發送緩沖區滿的情況,在udp套接字上執行的寫操作永遠都不會阻塞
現假設一個進程希望通過三個管道中任意一個中讀取數據并顯示,偽代碼如下
read(pipe_0,buf,sizeof(buf));? ? ? //sleepprint buf;read(pipe_1,buf,sizeof(buf));print buf;read(pipe_2,buf,sizeof(buf));print buf;
由于管道是阻塞I/O,所以如果pipe_0沒有數據流入,進程就是在第一個read()處進入sleep狀態而即使pipe_1和pipe_2有數據流入也不會被讀取。
如果我們使用下述代碼重新設置管道的阻塞屬性,顯然,如果三個管道都沒有數據流入,那么進程就無法獲得請求的數據而繼續執行,倘若這些數據很重要(所以我們才要用阻塞I/O),那結果就會十分的糟糕,改為輪詢卻又大量的占據CPU時間。
int fl = fcntl(pipe_fd, F_GETFL);fcntl(pipe_fd, F_SETFL, fl | O_NONBLOCK);
如何讓進程同時監視三個管道,其中一個有數據就繼續執行而不會sleep,如果全部沒有數據流入再sleep,就是多路復用技術需要解決的問題。
非阻塞I/O
非阻塞I/O就是當一個進程試圖訪問一個I/O設備的時候,無論是否從中獲取了請求的數據都會返回并繼續執行接下來的任務。,但非常適合請求是否成功對接下來的任務影響不大的I/O請求。但如果訪問一個非阻塞I/O,但這個請求如果失敗對進程接下來的任務有致命影響,最粗暴的就是使用while(1){read()}輪詢。顯然,這種方式會占用大量的CPU時間。對于非阻塞IO,除了直接返回,一個更重要的應用就是利用IO多路復用機制同時監視多個非阻塞IO。
select機制
select是一種非常"古老"的同步I/O接口,但是提供了一種很好的I/O多路復用的思路
模型
fd_set//創建fd_set對象,將來從中增減需要監視的fdFD_ZERO()//清空fd_set對象FD_SET()//將一個fd加入fd_set對象中? select()//監視fd_set對象中的文件描述符pselect()//先設定信號屏蔽,再監視FD_ISSET()//測試fd是否屬于fd_set對象FD_CLR()//從fd_set對象中刪除fd
Note:
select的第一個參數nfds是指集合中的最大的文件描述符+1,因為select會無差別遍歷整個文件描述符表直到找到目標,而文件描述符是從0開始的,所以一共是集合中的最大的文件描述符+1次。
上一條導致了這種機制的低效,如果需要監視的文件描述符是0和100那么每一次都會遍歷101次
select()每次返回都會修改fd_set,如果要循環select(),需要先對初始的fd_set進行備
例子_I/O多路復用并發服務器
關于server本身的編程模型,參見tcp/ip協議服務器模型和udp/ip協議服務器模型這里僅是使用select實現偽并行的部分模型
#defineBUFSIZE 100#defineMAXNFD? 1024intmain(){/***********服務器的listenfd已經準本好了**************/fd_set readfds;? ? fd_set writefds;? ? FD_ZERO(&readfds);? ? FD_ZERO(&writefds);? ? FD_SET(listenfd, &readfds);? ? fd_set temprfds = readfds;? ? fd_set tempwfds = writefds;intmaxfd = listenfd;intnready;charbuf[MAXNFD][BUFSIZE] = {0};while(1){? ? ? ? temprfds = readfds;? ? ? ? tempwfds = writefds;? ? ? ? nready = select(maxfd+1, &temprfds, &tempwfds,NULL,NULL)if(FD_ISSET(listenfd, &temprfds)){//如果監聽到的是listenfd就進行acceptintsockfd = accept(listenfd, (structsockaddr*)&clientaddr, &len);//將新accept的scokfd加入監聽集合,并保持maxfd為最大fdFD_SET(sockfd, &readfds);? ? ? ? ? ? maxfd = maxfd>sockfd?maxfd:sockfd;//如果意見檢查了nready個fd,就沒有必要再等了,直接下一個循環if(--nready==0)continue;? ? ? ? }intfd =0;//遍歷文件描述符表,處理接收到的消息for(;fd<=maxfd; fd++){if(fd == listenfd)continue;if(FD_ISSET(fd, &temprfds)){intret = read(fd, buf[fd],sizeofbuf[0]);if(0== ret){//客戶端鏈接已經斷開close(fd);? ? ? ? ? ? ? ? ? ? FD_CLR(fd, &readfds);if(maxfd==fd)? ? ? ? ? ? ? ? ? ? ? ? --maxfd;continue;? ? ? ? ? ? ? ? }//將fd加入監聽可寫的集合FD_SET(fd, &writefds);? ? ? ? ? ? ? }//找到了接收消息的socket的fd,接下來將其加入到監視寫的fd_set中//將在下一次while()循環開始監視if(FD_ISSET(fd, &tempwfds)){intret = write(fd, buf[fd],sizeofbuf[0]);printf("ret %d: %d\n", fd, ret);? ? ? ? ? ? ? ? FD_CLR(fd, &writefds);? ? ? ? ? ? }? ? ? ? }? ? }? ? close(listenfd);}
poll機制
poll是一種基于select的改良機制,其針對select的一些缺陷進行了重新設計,包括不需要備份fd_set等等,但是依然是遍歷整個文件描述符表,效率較低
模型
structpollfd? fds//創建一個pollfd類型的數組fds[0].fd//向fds[0]中放入需要監視的fdfds[0].events//向fds[0]中放入需要監視的fd的觸發事件POLLIN//I/O有輸入POLLPRI//有緊急數據需要讀取POLLOUT//I/O可寫POLLRDHUP//流式套接字連接斷開或套接字處于半關閉狀態POLLERR//錯誤條件(僅針對輸出)POLLHUP//掛起(僅針對輸出)POLLNVAL//無效的請求:fd沒有被打開(僅針對輸出)
例子_I/O多路復用并發服務器
/* ... */intmain(){/* ... */structpollfd myfds[MAXNFD] = {0};? ? myfds[0].fd = listenfd;? ? myfds[0].events = POLLIN;intmaxnum =1;intnready;//準備二維數組buf,每個fd使用buf的一行,數據干擾charbuf[MAXNFD][BUFSIZE] = {0};while(1){//poll直接返回event被觸發的fd的個數nready = poll(myfds, maxnum,-1)inti =0;for(;i
epoll
epoll在poll基礎上實現的更為健壯的接口,它每次只會遍歷我們關心的文件描述符,也是現在主流的web服務器使用的多路復用技術,epoll一大特色就是支持EPOLLET(邊沿觸發)和EPOLLLT (水平觸發),前者表示如果讀取之后緩沖區還有數據,那么只要讀取結束,剩余的數據也會丟棄,而后者表示里面的數據不會丟棄,下次讀的時候還在,默認是EPOLLLT
模型
epoll_create()//創建epoll對象structepoll_event//準備事件結構體和事件結構體數組event.eventsevent.data.fd ...epoll_ctl()//配置epoll對象epoll_wait()//監控epoll對象中的fd及其相應的event
例子_I/O多路復用并發服務器
/* ... */intmain(){/* ... *//* 創建epoll對象 */intepoll_fd = epoll_create(1024);//準備一個事件結構體structepoll_eventevent= {0};event.events = EPOLLIN;event.data.fd = listenfd;//data是一個共用體,除了fd還可以返回其他數據//ctl是監控listenfd是否有event被觸發//如果發生了就把event通過wait帶出。//所以,如果event里不標明fd,我們將來獲取就不知道哪個fdepoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenfd, &event);structepoll_event revents[MAXNFD] = {0};intnready;charbuf[MAXNFD][BUFSIZE] = {0};while(1){//wait返回等待的event發生的數目//并把相應的event放到event類型的數組中nready = epoll_wait(epoll_fd, revents, MAXNFD,-1)inti =0;for(;i
版權聲明:本文內容由互聯網用戶自發貢獻,版權歸作者所有,本社區不擁有所有權,也不承擔相關法律責任。如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至:yqgroup@service.aliyun.com?進行舉報,并提供相關證據,一經查實,本社區將立刻刪除涉嫌侵權內容。
用云棲社區APP,舒服~
【云棲快訊】中辦國辦印發《推進互聯網協議第六版(IPv6)規模部署行動計劃》加快推進基于 IPv6 的下一代互聯網規模部署,計劃指出2025年末中國 IPv6 規模要達到世界第一,阿里云也第一時間宣布了將全面提供IPv6服務,那么在全面部署 IPV6 前,你需要了解都在這兒詳情請點擊
評論文章 (0)?(0)?(0)
分享到:
上一篇:兩種高性能I/O設計模式(Reactor/Proactor)的比較
下一篇:Windbg+Procdump解決w3wp.exe CPU過百問題
相關文章
網友評論
軟件開發,安全加密
文中提到的云產品
?推薦引擎
用于實時預測用戶對物品偏好,支持企業定制推薦算法,支持A/B Test效果對比?更多>
?共享帶寬
共享帶寬提供地域級帶寬共享和復用功能,支持同地域下所有彈性公網IP共享帶寬,進而讓綁定彈性公網IP的云服務器EC...?更多>
深度挖掘企業與企業、企業與人物的關系,通過多位交叉分析及智能算法,構建基于企業全息畫像和企業關系網絡的風險洞察、...?更多>
為您提供簡單高效、處理能力可彈性伸縮的計算服務,幫助您快速構建更穩定、安全的應用,提升運維效率,降低 IT 成本...更多>
目錄
博主其他文章
Expert 診斷優化系列------------------語句調優三板斧
Expert 診斷優化系列------------------冤枉磁盤了
Expert 診斷優化系列------------------內存不夠用么?
Expert 診斷優化系列------------------你的CPU高么?
相關話題
禁止數據中心使用GeForce顯卡跑深度學習,英偉達在打什么盤算?
原文連接