linux系統(tǒng)編程-day08-文件IO(3)

I/O多路復(fù)用

應(yīng)用程序常常需要在多于一個(gè)文件描述符上阻塞寻狂。在不使用線程,尤其是獨(dú)立處理每一個(gè)文件的情況下朋沮,進(jìn)程無(wú)法在多個(gè)文件描述符上同時(shí)阻塞蛇券。尤其是對(duì)于網(wǎng)絡(luò)應(yīng)用程序而言,同時(shí)打開(kāi)的多個(gè)套接字樊拓,會(huì)誘發(fā)潛在的問(wèn)題纠亚。
非阻塞IO可以作為這個(gè)問(wèn)題的一個(gè)解決方案,但這種方法效率較差筋夏。首先菜枷,進(jìn)程要以某種不確定的方式不斷發(fā)起I/O操作,直到某個(gè)打開(kāi)的文件描述符準(zhǔn)備好進(jìn)行I/O叁丧。其次,如果程序可以睡眠的話將更加有效岳瞭,可以讓處理器進(jìn)行其他工作拥娄,直到一個(gè)或更多文件描述符可以進(jìn)行I/O時(shí)再喚醒。

  • I/O多路復(fù)用

I/O多路復(fù)用允許應(yīng)用在多個(gè)文件描述符上同時(shí)阻塞瞳筏,并在其中某個(gè)可以讀寫(xiě)時(shí)收到通知稚瘾。
這時(shí)I/O多路復(fù)用就成了應(yīng)用的關(guān)鍵所在,一般來(lái)講I/O多路復(fù)用的設(shè)計(jì)遵循以下原則:

  1. I/O多路復(fù)用:當(dāng)任何文件描述符準(zhǔn)備好I/O時(shí)告訴我
  2. 在一個(gè)或更多文件描述符就緒前始終處于睡眠狀態(tài)
  3. 喚醒:哪個(gè)準(zhǔn)備好了姚炕?
  4. 在不阻塞的情況下處理所有I/O就緒的文件描述符摊欠。
  5. 返回第一步,重新開(kāi)始柱宦。
    Linux提供了三種I/O多路復(fù)用方案:select, poll和epoll些椒。其中epoll是Linux特有的高級(jí)方法。
  • select( )
    select( )系統(tǒng)調(diào)用提供了一種實(shí)現(xiàn)同步I/O多路復(fù)用的機(jī)制:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

在指定的文件描述符準(zhǔn)備好I/O之前或者超過(guò)一定的時(shí)間限制掸刊,select( )調(diào)用就會(huì)阻塞免糕。
監(jiān)測(cè)的文件描述符可以分為三類,分別等待不同的時(shí)間。

  • 監(jiān)測(cè)readfds集合中的fd石窑,確認(rèn)其中是否有可讀數(shù)據(jù)(也就是說(shuō)牌芋,讀操作可以無(wú)阻塞的完成)
  • 檢測(cè)writefds集合中的fd,確認(rèn)其中是否有一個(gè)寫(xiě)操作可以不阻塞地完成
  • 監(jiān)測(cè)exceptfds集合中的fd松逊,確認(rèn)其中是否有出現(xiàn)異常發(fā)生或者出現(xiàn)帶外(out-of-band)數(shù)據(jù)(這種情況只適用于套接字)躺屁。

如果指定的集合為NULL,則seletc( )不對(duì)此類時(shí)間進(jìn)行監(jiān)視经宏。
成功返回時(shí)犀暑,每個(gè)集合只包含對(duì)應(yīng)類型的I/O就緒的文件描述符。
第一個(gè)參數(shù)n烛恤,等于所有集合中文件描述符的最大值加一母怜。這樣,select( )的調(diào)用者需要找到最大的文件描述符值缚柏,并將其加一后傳給第一個(gè)參數(shù)苹熏。
最后的timeout參數(shù)是一個(gè)指向timeval結(jié)構(gòu)體的指針,定義如下:

#include <sys/time.h>
struct timeval {
    long tv_sec; /* seconds */
    long tv_usec; /*  microseconds */
};

如果這個(gè)參數(shù)不是NULL币喧,即時(shí)此時(shí)沒(méi)有文件描述符處于I/O就緒狀態(tài)轨域,select( )調(diào)用也將在tv_sec秒tv_usec微秒后返回。返回時(shí)杀餐,這個(gè)結(jié)構(gòu)圖的狀態(tài)是未定義的干发。

  • 較新版本的Linux會(huì)自動(dòng)將該值改為剩余的時(shí)間。
  • 如果時(shí)限中的兩個(gè)值都是零史翘,調(diào)用會(huì)立即返回枉长,并報(bào)告調(diào)用時(shí)所有事件對(duì)應(yīng)的文件描述符均不可用,且不等待任何后續(xù)事件琼讽。

三個(gè)集合中的fds并不直接操作必峰,而是通過(guò)以下輔助來(lái)進(jìn)行管理:

  • FD_ZERO從指定的集合中移除所有文件描述符:

fd_set writefds;
FD_ZERO(&writefds);

  • FD_SET想指定集合中添加一個(gè)文件描述符

FD_SET(fd, &writefds);

  • FD_CLR從指定集合中移除一個(gè)文件描述符,設(shè)計(jì)良好的代碼應(yīng)該從不使用FD_CLR钻蹬,一般來(lái)講吼蚁,很少使用該宏

FD_CLR(fd, &writefds);

  • FD_ISSET測(cè)試一個(gè)文件描述符在不在給定集合中。如果在问欠,返回一個(gè)非0值肝匆,不在,返回0顺献。一般在select( )調(diào)用返回后使用FD_ISSET來(lái)檢查一個(gè)文件描述符是否就緒旗国。

if (FD_ISSET(fd, &readfds))
/* 'fd' is readable without blocking! */

  • 由于文件描述符集合是靜態(tài)建立的,所以對(duì)于文件描述符數(shù)量的上限和文件描述符的最大值均有限制注整。二者都由FD_SETSIZE設(shè)定粗仓。在Linux上嫁怀,這個(gè)值是1024。
用select( )實(shí)現(xiàn)可移植的sleep( )

由于select( )在各種Unix系統(tǒng)中都很容易實(shí)現(xiàn)借浊,相對(duì)于微妙級(jí)精度的睡眠機(jī)制來(lái)講塘淑,經(jīng)常將select( )做為一種可移植的微秒級(jí)的睡眠機(jī)制。

  • 該方法通過(guò)將三個(gè)集合值設(shè)為空NULL蚂斤,將超時(shí)值設(shè)置為non-NULL來(lái)實(shí)現(xiàn)存捺。
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500;
/* sleep for 500 microseconds */
 select(0, NULL, NULL, NULL, &tv);
pselect( )

POSIX定義了自己的方法--pselect( ):

#define _XOPEN_SOURCE 600
#include <sys/select.h>
int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);

四個(gè)宏定義與select的相同。
pselect( )和select( )有三點(diǎn)不同:

  1. pselect( )的timeout參數(shù)使用了timespec結(jié)構(gòu)曙蒸。timespec使用秒和納秒捌治,而select的timeval使用秒和豪秒。理論上timespec更精準(zhǔn)一些纽窟。
  2. pselect( )調(diào)用并不修改timeout參數(shù)肖油。
  3. pselect( )比select( )多一個(gè)sigmask參數(shù)。當(dāng)這個(gè)參數(shù)被設(shè)置為零時(shí)臂港,pselect( )的行為等同于select( ).
  • 添加pselect( )到Unix工具箱的主要原因是為了增加sigmask參數(shù)森枪,以此來(lái)解決信號(hào)和等待文件描述符之間的競(jìng)爭(zhēng)關(guān)系。
  • pselect( )提供了一組可阻塞的信號(hào)审孽,阻塞的信號(hào)直到解除阻塞才會(huì)被處理县袱。
  • 如果不考慮pselect( )中的改進(jìn),大多數(shù)應(yīng)用會(huì)繼續(xù)使用select( )佑力,部分是出于習(xí)慣式散,其他則是考慮可移植性。

poll( )

poll( )系統(tǒng)調(diào)用是system V的I/O多路復(fù)用解決方案打颤。

#include <sys/poll.h>
int poll(struct pollfd *fds, unsigned int nfds, int timeout);

poll( )使用一個(gè)簡(jiǎn)單的nfds個(gè)pollfd結(jié)構(gòu)體構(gòu)成的數(shù)組暴拄,fds指向該數(shù)組

#include <sys/poll.h>
struct pollfd {
  int fd; /* file descriptor */
  short events; /* requested events to watch */
  short revents;  /* returned events witnessed */
};

每個(gè)pollfd結(jié)構(gòu)體指定監(jiān)視單一的文件描述符”嘟龋可以傳遞多個(gè)結(jié)構(gòu)體乖篷,使得poll( )監(jiān)視多個(gè)文件描述符。

  • events是要監(jiān)視的fd事件的一組位掩碼反肋,用戶設(shè)置這個(gè)字段
  • revents是發(fā)生在該fd上的事件的位掩碼,內(nèi)核在返回時(shí)設(shè)置這個(gè)字段

下面是合法的事件:

  • POLLIN:沒(méi)有數(shù)據(jù)可讀
  • POLLRDNORM:有正常數(shù)據(jù)可讀
  • POLLRDBAND:有優(yōu)先數(shù)據(jù)可讀
  • POLLPRI:有高優(yōu)先級(jí)數(shù)據(jù)可讀
  • POLLOUT:寫(xiě)操作不會(huì)阻塞
  • POLLWRNORM:寫(xiě)正常數(shù)據(jù)不會(huì)阻塞
  • POLLBAND:寫(xiě)優(yōu)先數(shù)據(jù)不會(huì)阻塞
  • POLLMSG:有一個(gè)SIGPOLL消息可用

另外踏施,如下事件可能在revents中返回:

  • POLLER:給出文件描述符上有錯(cuò)誤
  • POLLHUP:文件描述符上有掛起事件
  • POLLNVAL:給出的文件描述符非法

POLLIN|POLLPRI等價(jià)于select( )的讀事件石蔗,而POLLOUT|POLLWRBAND等價(jià)于select( )的寫(xiě)事件。POLLIN等價(jià)于POLLRDNORM|POLLRDBAND畅形, 而POLLOUT等價(jià)于POLLWRNORM养距。

  • 舉例來(lái)說(shuō),監(jiān)視一個(gè)文件描述符是否可讀寫(xiě)日熬,需設(shè)置events為POLLIN|POLLOUT棍厌。返回時(shí),將在revents中檢查是否有相應(yīng)的標(biāo)志。
  • timeout參數(shù)指定在任何I/O就緒前需要等待時(shí)間的長(zhǎng)度耘纱,以毫秒計(jì)敬肚。負(fù)值表示永遠(yuǎn)等待。一個(gè)零值表示調(diào)用立即返回束析,列出所有未準(zhǔn)備好的I/O艳馒,但不等待任何其他事件。這種情況下员寇,poll( )就如圖其名弄慰,輪詢一次后立即返回。
ppoll( ):

Linux提供了一個(gè)poll( )的近似調(diào)用——ppoll( )蝶锋。ppoll( )和pselect( )同源陆爽,然而和pselect( )不同的是,ppoll( )是Linux的專有調(diào)用:

#define _GNU_SOURCE
#include <sys/poll.h>
int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask);

像pselect( )那樣扳缕,timeout參數(shù)以秒和納秒計(jì)指定了時(shí)限慌闭,而sigmask參數(shù)提供了一組等待處理的信號(hào)。

對(duì)比poll( )與select( ):

一般來(lái)說(shuō)第献,poll( )系統(tǒng)調(diào)用優(yōu)于select( ):

  • poll( )無(wú)需使用者計(jì)算最大的文件描述符值加一和傳遞該參數(shù)
  • poll( )應(yīng)對(duì)較大值的fd時(shí)更具效率贡必。
  • 使用poll( )可以創(chuàng)建合適大小的數(shù)組,只需要監(jiān)視一項(xiàng)或僅僅傳遞一個(gè)結(jié)構(gòu)體庸毫。
  • poll( )系統(tǒng)調(diào)用分離了輸入events字段和輸出revents字段仔拟,數(shù)組無(wú)需改變即而重用。
  • select的timeout參數(shù)在返回時(shí)是未定義的飒赃±ǎ可移植的代碼需要重新初始化它。然而pselect沒(méi)有這個(gè)問(wèn)題载佳。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末炒事,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔫慧,更是在濱河造成了極大的恐慌挠乳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姑躲,死亡現(xiàn)場(chǎng)離奇詭異睡扬,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)黍析,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)卖怜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人阐枣,你說(shuō)我怎么就攤上這事马靠⊙俪椋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵甩鳄,是天一觀的道長(zhǎng)逞度。 經(jīng)常有香客問(wèn)我,道長(zhǎng)娩贷,這世上最難降的妖魔是什么第晰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮彬祖,結(jié)果婚禮上茁瘦,老公的妹妹穿的比我還像新娘。我一直安慰自己储笑,他們只是感情好甜熔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著突倍,像睡著了一般腔稀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羽历,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天焊虏,我揣著相機(jī)與錄音,去河邊找鬼秕磷。 笑死诵闭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的澎嚣。 我是一名探鬼主播疏尿,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼易桃!你這毒婦竟也來(lái)了褥琐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晤郑,失蹤者是張志新(化名)和其女友劉穎敌呈,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體造寝,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡磕洪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了匹舞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褐鸥。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡线脚,死狀恐怖赐稽,靈堂內(nèi)的尸體忽然破棺而出叫榕,到底是詐尸還是另有隱情,我是刑警寧澤姊舵,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布晰绎,位于F島的核電站,受9級(jí)特大地震影響括丁,放射性物質(zhì)發(fā)生泄漏荞下。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一史飞、第九天 我趴在偏房一處隱蔽的房頂上張望尖昏。 院中可真熱鬧,春花似錦构资、人聲如沸抽诉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)迹淌。三九已至,卻和暖如春己单,著一層夾襖步出監(jiān)牢的瞬間唉窃,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工纹笼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纹份,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓允乐,卻偏偏與公主長(zhǎng)得像矮嫉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牍疏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文摘抄自linux基礎(chǔ)編程 IO概念 Linux的內(nèi)核將所有外部設(shè)備都可以看做一個(gè)文件來(lái)操作蠢笋。那么我們對(duì)與外部設(shè)...
    VD2012閱讀 1,021評(píng)論 0 2
  • 本文討論的背景是Linux環(huán)境下的network IO厦滤。 一援岩、 概念說(shuō)明 在進(jìn)行解釋之前,首先要說(shuō)明幾個(gè)概念: 用...
    faunjoe閱讀 4,374評(píng)論 1 15
  • 本文摘抄自linux基礎(chǔ)編程 IO概念 Linux的內(nèi)核將所有外部設(shè)備都可以看做一個(gè)文件來(lái)操作掏导。那么我們對(duì)與外部設(shè)...
    lintong閱讀 1,582評(píng)論 0 4
  • 必備的理論基礎(chǔ) 1.操作系統(tǒng)作用: 隱藏丑陋復(fù)雜的硬件接口享怀,提供良好的抽象接口。 管理調(diào)度進(jìn)程趟咆,并將多個(gè)進(jìn)程對(duì)硬件...
    drfung閱讀 3,541評(píng)論 0 5
  • 趴著 軟綿綿的一個(gè)地方 黑熊的肚腩 走路搖搖晃晃 醉了才知命短夢(mèng)長(zhǎng) 酒能讓一個(gè)慫人清醒 也能讓一個(gè)英雄惆悵 總之 ...
    小小仲馬閱讀 212評(píng)論 2 5