POSIX
同步IO收恢、異步IO、阻塞IO祭往、非阻塞IO伦意,這幾個(gè)詞常見于各種各樣的與網(wǎng)絡(luò)相關(guān)的文章之中,往往不同上下文中它們的意思是不一樣的硼补,以致于我在很長(zhǎng)一段時(shí)間對(duì)此感到困惑驮肉,所以想寫一篇文章整理一下。
POSIX(可移植操作系統(tǒng)接口)把同步IO操作定義為導(dǎo)致進(jìn)程阻塞直到IO完成的操作已骇,反之則是異步IO
按POSIX的描述似乎把同步和阻塞劃等號(hào)离钝,異步和非阻塞劃等號(hào)票编,但是為什么有的人說同步IO不等于阻塞IO呢?先來說說幾種常見的IO模型吧卵渴。
IO模型
這里統(tǒng)一使用Linux下的系統(tǒng)調(diào)用recv作為例子慧域,它用于從套接字上接收一個(gè)消息,因?yàn)槭且粋€(gè)系統(tǒng)調(diào)用浪读,所以調(diào)用時(shí)會(huì)從用戶進(jìn)程空間切換到內(nèi)核空間運(yùn)行一段時(shí)間再切換回來昔榴。默認(rèn)情況下recv會(huì)等到網(wǎng)絡(luò)數(shù)據(jù)到達(dá)并且復(fù)制到用戶進(jìn)程空間或者發(fā)生錯(cuò)誤時(shí)返回,而第4個(gè)參數(shù)flags可以讓它馬上返回碘橘。
阻塞IO模型
使用recv的默認(rèn)參數(shù)一直等數(shù)據(jù)直到拷貝到用戶空間互订,這段時(shí)間內(nèi)進(jìn)程始終阻塞。A同學(xué)用杯子裝水痘拆,打開水龍頭裝滿水然后離開仰禽。這一過程就可以看成是使用了阻塞IO模型,因?yàn)槿绻堫^沒有水纺蛆,他也要等到有水并裝滿杯子才能離開去做別的事情吐葵。很顯然,這種IO模型是同步的犹撒。
非阻塞IO模型
改變flags折联,讓recv不管有沒有獲取到數(shù)據(jù)都返回,如果沒有數(shù)據(jù)那么一段時(shí)間后再調(diào)用recv看看识颊,如此循環(huán)诚镰。B同學(xué)也用杯子裝水,打開水龍頭后發(fā)現(xiàn)沒有水祥款,它離開了清笨,過一會(huì)他又拿著杯子來看看……在中間離開的這些時(shí)間里,B同學(xué)離開了裝水現(xiàn)場(chǎng)(回到用戶進(jìn)程空間)刃跛,可以做他自己的事情抠艾。這就是非阻塞IO模型。但是它只有是檢查無數(shù)據(jù)的時(shí)候是非阻塞的桨昙,在數(shù)據(jù)到達(dá)的時(shí)候依然要等待復(fù)制數(shù)據(jù)到用戶空間(等著水將水杯裝滿)检号,因此它還是同步IO。
IO復(fù)用模型
這里在調(diào)用recv前先調(diào)用select或者poll蛙酪,這2個(gè)系統(tǒng)調(diào)用都可以在內(nèi)核準(zhǔn)備好數(shù)據(jù)(網(wǎng)絡(luò)數(shù)據(jù)到達(dá)內(nèi)核)時(shí)告知用戶進(jìn)程齐苛,這個(gè)時(shí)候再調(diào)用recv一定是有數(shù)據(jù)的。因此這一過程中它是阻塞于select或poll桂塞,而沒有阻塞于recv凹蜂,有人將非阻塞IO定義成在讀寫操作時(shí)沒有阻塞于系統(tǒng)調(diào)用的IO操作(不包括數(shù)據(jù)從內(nèi)核復(fù)制到用戶空間時(shí)的阻塞,因?yàn)檫@相對(duì)于網(wǎng)絡(luò)IO來說確實(shí)很短暫),如果按這樣理解玛痊,這種IO模型也能稱之為非阻塞IO模型汰瘫,但是按POSIX來看,它也是同步IO擂煞,那么也和樓上一樣稱之為同步非阻塞IO吧混弥。
這種IO模型比較特別,分個(gè)段颈娜。因?yàn)樗芡瑫r(shí)監(jiān)聽多個(gè)文件描述符(fd)剑逃。這個(gè)時(shí)候C同學(xué)來裝水浙宜,發(fā)現(xiàn)有一排水龍頭官辽,舍管阿姨告訴他這些水龍頭都還沒有水,等有水了告訴他粟瞬。于是等啊等(select調(diào)用中)同仆,過了一會(huì)阿姨告訴他有水了,但不知道是哪個(gè)水龍頭有水裙品,自己看吧俗批。于是C同學(xué)一個(gè)個(gè)打開,往杯子里裝水(recv)市怎。這里再順便說說鼎鼎大名的epoll(高性能的代名詞啊)岁忘,epoll也屬于IO復(fù)用模型,主要區(qū)別在于舍管阿姨會(huì)告訴C同學(xué)哪幾個(gè)水龍頭有水了区匠,不需要一個(gè)個(gè)打開看(當(dāng)然還有其它區(qū)別)干像。
信號(hào)驅(qū)動(dòng)IO模型
通過調(diào)用sigaction注冊(cè)信號(hào)函數(shù),等內(nèi)核數(shù)據(jù)準(zhǔn)備好的時(shí)候系統(tǒng)中斷當(dāng)前程序驰弄,執(zhí)行信號(hào)函數(shù)(在這里面調(diào)用recv)麻汰。D同學(xué)讓舍管阿姨等有水的時(shí)候通知他(注冊(cè)信號(hào)函數(shù)),沒多久D同學(xué)得知有水了戚篙,跑去裝水五鲫。是不是很像異步IO?很遺憾岔擂,它還是同步IO(省不了裝水的時(shí)間啊)位喂。
異步IO模型
調(diào)用aio_read,讓內(nèi)核等數(shù)據(jù)準(zhǔn)備好乱灵,并且復(fù)制到用戶進(jìn)程空間后執(zhí)行事先指定好的函數(shù)塑崖。E同學(xué)讓舍管阿姨將杯子裝滿水后通知他。整個(gè)過程E同學(xué)都可以做別的事情(沒有recv)阔蛉,這才是真正的異步IO弃舒。
最后,總結(jié)比較下五種IO模型:
總結(jié)
IO分兩階段:
1.數(shù)據(jù)準(zhǔn)備階段
2.內(nèi)核空間復(fù)制回用戶進(jìn)程緩沖區(qū)階段
一般來講:阻塞IO模型、非阻塞IO模型聋呢、IO復(fù)用模型(select/poll/epoll)苗踪、信號(hào)驅(qū)動(dòng)IO模型都屬于同步IO,因?yàn)殡A段2是阻塞的(盡管時(shí)間很短)削锰。只有異步IO模型是符合POSIX異步IO操作含義的通铲,不管在階段1還是階段2都可以干別的事。