Linux I/O多路復用

摘要:?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過百問題

相關文章

《Linux系統編程(第2版)》——2.10 I/O多路…

不可不知 Linux操作系統網絡服務器模型

關于網絡IO中的同步、異步、阻塞、非阻塞

關于網絡IO中的同步、異步、阻塞、非阻塞

Linux下套接字詳解(十)---epoll模式下的IO…

開始學習ACE

Java NIO類庫Selector機制解析(上)

Linux下套接字詳解(九)---poll模式下的IO多…

Reactor模式 NIO epoll

select、poll、epoll之間的區別總結[整理]…

網友評論

登錄后可評論,請?登錄?或?注冊

評論

關注

nothingfinal

軟件開發,安全加密

505篇文章|0關注

文中提到的云產品

?推薦引擎

用于實時預測用戶對物品偏好,支持企業定制推薦算法,支持A/B Test效果對比?更多>

?共享帶寬

共享帶寬提供地域級帶寬共享和復用功能,支持同地域下所有彈性公網IP共享帶寬,進而讓綁定彈性公網IP的云服務器EC...?更多>

?企業級數據服務解決方案

深度挖掘企業與企業、企業與人物的關系,通過多位交叉分析及智能算法,構建基于企業全息畫像和企業關系網絡的風險洞察、...?更多>

?云服務器ECS

為您提供簡單高效、處理能力可彈性伸縮的計算服務,幫助您快速構建更穩定、安全的應用,提升運維效率,降低 IT 成本...更多>

目錄

收起

1阻塞I/O

2非阻塞I/O

3select機制

4模型

5例子_I/O多路復用并發服務器

6poll機制

7模型

8例子_I/O多路復用并發服務器

9epoll

10模型

11例子_I/O多路復用并發服務器

博主其他文章

更多>

Expert 診斷優化系列------------------語句調優三板斧

Expert 診斷優化系列------------------冤枉磁盤了

Expert 診斷優化系列------------------內存不夠用么?

Expert 診斷優化系列------------------你的CPU高么?

redis源碼筆記 - redis-cli.c

談談如何使用Netty開發實現高性能的RPC服務器

Java如何調用dll

Java調用C/C++實現的DLL動態庫——JNI

java調用C++ DLL庫方法

在libevent中使用線程池

相關話題

更多>

新的一年,新的精彩,2018你有啥計劃?

禁止數據中心使用GeForce顯卡跑深度學習,英偉達在打什么盤算?

年度編程語言之王,你最看好誰

深度學習應用,不但要考慮準確性,更要考慮安全

TensorFlow你了解多少,一起來聊聊吧

原文連接

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內容

  • 本文摘抄自linux基礎編程 IO概念 Linux的內核將所有外部設備都可以看做一個文件來操作。那么我們對與外部設...
    VD2012閱讀 1,024評論 0 2
  • 本文摘抄自linux基礎編程 IO概念 Linux的內核將所有外部設備都可以看做一個文件來操作。那么我們對與外部設...
    lintong閱讀 1,595評論 0 4
  • I/O復用基本概念 I/O多路復用技術通過把多個I/O的阻塞復用到同一個select的阻塞上,從而使得系統在單線程...
    Ycres閱讀 980評論 0 0
  • 最近在看 UNIX 網絡編程并研究了一下 Redis 的實現,感覺 Redis 的源代碼十分適合閱讀和分析,其中 ...
    Draveness閱讀 10,769評論 8 56
  • 許多時候, 只有去經歷,你才知道自己到底適合什么。 昨晚和一位箏友約定今晚去某個藝術中心聽音樂會,...
    書言菡語閱讀 257評論 0 0