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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

大白话五种IO模型

發(fā)布時(shí)間:2025/7/14 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大白话五种IO模型 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • 一、I/O模型介紹
  • 二、阻塞I/O模型
    • 2.1 一個(gè)簡(jiǎn)單的解決方案
    • 2.2 該方案的問(wèn)題
    • 2.3 改進(jìn)方案
    • 2.4 改進(jìn)后方案的問(wèn)題
  • 三、非阻塞I/O模型
    • 3.1 非阻塞I/O實(shí)例
  • 四、多路復(fù)用I/O模型
    • 4.1 select/poll模型
      • 4.1.1 select網(wǎng)絡(luò)I/O模型
      • 4.1.2 select監(jiān)聽(tīng)fd變化的過(guò)程分析
      • 4.1.3 該模型的優(yōu)點(diǎn)
      • 4.1.4 該模型的缺點(diǎn)
    • 4.2 epoll模型(了解)
  • 五、信號(hào)驅(qū)動(dòng)I/O模型(了解)
  • 六、異步I/O模型
  • 七、I/O模型比較分析

一、I/O模型介紹

為了更好地了解I/O模型,我們需要事先回顧下:同步、異步、阻塞、非阻塞

同步(synchronous) I/O和異步(asynchronous) I/O,阻塞(blocking) I/O和非阻塞(non-blocking)I/O分別是什么,到底有什么區(qū)別?這個(gè)問(wèn)題其實(shí)不同的人給出的答案都可能不同,比如wiki,就認(rèn)為asynchronous I/O和non-blocking I/O是一個(gè)東西。這其實(shí)是因?yàn)椴煌娜说闹R(shí)背景不同,并且在討論這個(gè)問(wèn)題的時(shí)候上下文(context)也不相同。所以,為了更好的回答這個(gè)問(wèn)題,我先限定一下本文的上下文。

本文討論的背景是Linux環(huán)境下的network I/O。本文最重要的參考文獻(xiàn)是Richard Stevens的“UNIX? Network Programming Volume 1, Third EditI/On: The Sockets Networking ”,6.2節(jié)“I/O Models ”,Stevens在這節(jié)中詳細(xì)說(shuō)明了各種I/O的特點(diǎn)和區(qū)別,如果英文夠好的話,推薦直接閱讀。Stevens的文風(fēng)是有名的深入淺出,所以不用擔(dān)心看不懂。本文中的流程圖也是截取自參考文獻(xiàn)。

Stevens在文章中一共比較了五種I/O Model

英文中文
blocking I/O阻塞I/O
nonblocking I/O非阻塞I/O
I/O multiplexingI/O多路復(fù)用
signal driven I/O信號(hào)驅(qū)動(dòng)I/O
asynchronous I/O異步I/O

再說(shuō)一下I/O發(fā)生時(shí)涉及的對(duì)象和步驟。對(duì)于一個(gè)network I/O (這里我們以read舉例),它會(huì)涉及到兩個(gè)系統(tǒng)對(duì)象,一個(gè)是調(diào)用這個(gè)I/O的process (or thread),另一個(gè)就是系統(tǒng)內(nèi)核(kernel)。當(dāng)一個(gè)read操作發(fā)生時(shí),該操作會(huì)經(jīng)歷兩個(gè)階段:

  • 等待數(shù)據(jù)準(zhǔn)備 (Waiting for the data to be ready)
  • 將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程中(Copying the data from the kernel to the process)
  • 記住這兩點(diǎn)很重要,因?yàn)檫@些I/O模型的區(qū)別就是在兩個(gè)階段上各有不同的情況。

    在網(wǎng)絡(luò)環(huán)境下,再通俗的講,將I/O分為兩步:

  • 等;
  • 數(shù)據(jù)搬遷。
  • 如果要想提高I/O效率,需要將等的時(shí)間降低。

    五種I/O模型包括:阻塞I/O、非阻塞I/O、信號(hào)驅(qū)動(dòng)I/O、I/O多路轉(zhuǎn)接、異步I/O。其中,前四個(gè)被稱(chēng)為同步I/O。

    在介紹五種I/O模型時(shí),我會(huì)舉生活中老王買(mǎi)車(chē)票的例子,加深理解。

    二、阻塞I/O模型

    以買(mǎi)票的例子舉例,該模型小結(jié)為:

    # 老王去火車(chē)站買(mǎi)票,排隊(duì)三天買(mǎi)到一張退票。# 耗費(fèi):在車(chē)站吃喝拉撒睡 3天,其他事一件沒(méi)干。

    在linux中,默認(rèn)情況下所有的socket都是blocking,一個(gè)典型的讀操作流程大概是這樣:

    當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個(gè)系統(tǒng)調(diào)用,kernel就開(kāi)始了I/O的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)。對(duì)于network I/O來(lái)說(shuō),很多時(shí)候數(shù)據(jù)在一開(kāi)始還沒(méi)有到達(dá)(比如,還沒(méi)有收到一個(gè)完整的UDP包),這個(gè)時(shí)候kernel就要等待足夠的數(shù)據(jù)到來(lái)。

    而在用戶進(jìn)程這邊,整個(gè)進(jìn)程會(huì)被阻塞。當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從kernel中拷貝到用戶內(nèi)存,然后kernel返回結(jié)果,用戶進(jìn)程才解除block的狀態(tài),重新運(yùn)行起來(lái)。所以,blocking I/O的特點(diǎn)就是在I/O執(zhí)行的兩個(gè)階段(等待數(shù)據(jù)和拷貝數(shù)據(jù)兩個(gè)階段)都被block了。

    幾乎所有的程序員第一次接觸到的網(wǎng)絡(luò)編程都是從listen()、send()、recv() 等接口開(kāi)始的,使用這些接口可以很方便的構(gòu)建服務(wù)器/客戶機(jī)的模型。然而大部分的socket接口都是阻塞型的。如下圖

    ps:所謂阻塞型接口是指系統(tǒng)調(diào)用(一般是I/O接口)不返回調(diào)用結(jié)果并讓當(dāng)前線程一直阻塞,只有當(dāng)該系統(tǒng)調(diào)用獲得結(jié)果或者超時(shí)出錯(cuò)時(shí)才返回。

    實(shí)際上,除非特別指定,幾乎所有的I/O接口 ( 包括socket接口 ) 都是阻塞型的。這給網(wǎng)絡(luò)編程帶來(lái)了一個(gè)很大的問(wèn)題,如在調(diào)用recv(1024)的同時(shí),線程將被阻塞,在此期間,線程將無(wú)法執(zhí)行任何運(yùn)算或響應(yīng)任何的網(wǎng)絡(luò)請(qǐng)求。

    2.1 一個(gè)簡(jiǎn)單的解決方案

    在服務(wù)器端使用多線程(或多進(jìn)程)。多線程(或多進(jìn)程)的目的是讓每個(gè)連接都擁有獨(dú)立的線程(或進(jìn)程),這樣任何一個(gè)連接的阻塞都不會(huì)影響其他的連接。

    2.2 該方案的問(wèn)題

    開(kāi)啟多進(jìn)程或都線程的方式,在遇到要同時(shí)響應(yīng)成百上千路的連接請(qǐng)求,則無(wú)論多線程還是多進(jìn)程都會(huì)嚴(yán)重占據(jù)系統(tǒng)資源,降低系統(tǒng)對(duì)外界響應(yīng)效率,而且線程與進(jìn)程本身也更容易進(jìn)入假死狀態(tài)。

    2.3 改進(jìn)方案

    很多程序員可能會(huì)考慮使用“線程池”或“連接池”。“線程池”旨在減少創(chuàng)建和銷(xiāo)毀線程的頻率,其維持一定合理數(shù)量的線程,并讓空閑的線程重新承擔(dān)新的執(zhí)行任務(wù)。“連接池”維持連接的緩存池,盡量重用已有的連接、減少創(chuàng)建和關(guān)閉連接的頻率。這兩種技術(shù)都可以很好的降低系統(tǒng)開(kāi)銷(xiāo),都被廣泛應(yīng)用很多大型系統(tǒng),如websphere、tomcat和各種數(shù)據(jù)庫(kù)等。

    2.4 改進(jìn)后方案的問(wèn)題

    “線程池”和“連接池”技術(shù)也只是在一定程度上緩解了頻繁調(diào)用I/O接口帶來(lái)的資源占用。而且,所謂“池”始終有其上限,當(dāng)請(qǐng)求大大超過(guò)上限時(shí),“池”構(gòu)成的系統(tǒng)對(duì)外界的響應(yīng)并不比沒(méi)有池的時(shí)候效果好多少。所以使用“池”必須考慮其面臨的響應(yīng)規(guī)模,并根據(jù)響應(yīng)規(guī)模調(diào)整“池”的大小。

    對(duì)應(yīng)上例中的所面臨的可能同時(shí)出現(xiàn)的上千甚至上萬(wàn)次的客戶端請(qǐng)求,“線程池”或“連接池”或許可以緩解部分壓力,但是不能解決所有問(wèn)題。總之,多線程模型可以方便高效的解決小規(guī)模的服務(wù)請(qǐng)求,但面對(duì)大規(guī)模的服務(wù)請(qǐng)求,多線程模型也會(huì)遇到瓶頸,可以用非阻塞接口來(lái)嘗試解決這個(gè)問(wèn)題。

    三、非阻塞I/O模型

    以買(mǎi)票的例子舉例,該模型小結(jié)為

    # 老王去火車(chē)站買(mǎi)票,隔12小時(shí)去火車(chē)站問(wèn)有沒(méi)有退票,三天后買(mǎi)到一張票。# 耗費(fèi):往返車(chē)站6次,路上6小時(shí),其他時(shí)間做了好多事。

    Linux下,可以通過(guò)設(shè)置socket使其變?yōu)閚on-blocking。當(dāng)對(duì)一個(gè)non-blocking socket執(zhí)行讀操作時(shí),流程是這個(gè)樣子:

    從圖中可以看出,當(dāng)用戶進(jìn)程發(fā)出read操作時(shí),如果kernel中的數(shù)據(jù)還沒(méi)有準(zhǔn)備好,那么它并不會(huì)block用戶進(jìn)程,而是立刻返回一個(gè)error。從用戶進(jìn)程角度講 ,它發(fā)起一個(gè)read操作后,并不需要等待,而是馬上就得到了一個(gè)結(jié)果。用戶進(jìn)程判斷結(jié)果是一個(gè)error時(shí),它就知道數(shù)據(jù)還沒(méi)有準(zhǔn)備好,于是用戶就可以在本次到下次再發(fā)起read詢(xún)問(wèn)的時(shí)間間隔內(nèi)做其他事情,或者直接再次發(fā)送read操作。一旦kernel中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶進(jìn)程的system call,那么它馬上就將數(shù)據(jù)拷貝到了用戶內(nèi)存(這一階段仍然是阻塞的),然后返回。

    也就是說(shuō)非阻塞的recvform系統(tǒng)調(diào)用調(diào)用之后,進(jìn)程并沒(méi)有被阻塞,內(nèi)核馬上返回給進(jìn)程,如果數(shù)據(jù)還沒(méi)準(zhǔn)備好,此時(shí)會(huì)返回一個(gè)error。進(jìn)程在返回之后,可以干點(diǎn)別的事情,然后再發(fā)起recvform系統(tǒng)調(diào)用。重復(fù)上面的過(guò)程,循環(huán)往復(fù)的進(jìn)行recvform系統(tǒng)調(diào)用。這個(gè)過(guò)程通常被稱(chēng)之為輪詢(xún)。輪詢(xún)檢查內(nèi)核數(shù)據(jù),直到數(shù)據(jù)準(zhǔn)備好,再拷貝數(shù)據(jù)到進(jìn)程,進(jìn)行數(shù)據(jù)處理。需要注意,拷貝數(shù)據(jù)整個(gè)過(guò)程,進(jìn)程仍然是屬于阻塞的狀態(tài)。所以,在非阻塞式I/O中,用戶進(jìn)程其實(shí)是需要不斷的主動(dòng)詢(xún)問(wèn)kernel數(shù)據(jù)準(zhǔn)備好了沒(méi)有。

    3.1 非阻塞I/O實(shí)例

    #服務(wù)端 from socket import * import time s=socket(AF_INET,SOCK_STREAM) s.bind(('127.0.0.1',8080)) s.listen(5) s.setblocking(False) #設(shè)置socket的接口為非阻塞 conn_l=[] del_l=[] while True:try:conn,addr=s.accept()conn_l.append(conn)except BlockingI/OError:print(conn_l)for conn in conn_l:try:data=conn.recv(1024)if not data:del_l.append(conn)continueconn.send(data.upper())except BlockingI/OError:passexcept ConnectI/OnResetError:del_l.append(conn)for conn in del_l:conn_l.remove(conn)conn.close()del_l=[]#客戶端 from socket import * c=socket(AF_INET,SOCK_STREAM) c.connect(('127.0.0.1',8080))while True:msg=input('>>: ')if not msg:continuec.send(msg.encode('utf-8'))data=c.recv(1024)print(data.decode('utf-8'))

    但是非阻塞I/O模型絕不被推薦。

    我們不能否則其優(yōu)點(diǎn):能夠在等待任務(wù)完成的時(shí)間里干其他活了(包括提交其他任務(wù),也就是 “后臺(tái)” 可以有多個(gè)任務(wù)在“”同時(shí)“”執(zhí)行)。

    但是也難掩其缺點(diǎn):

  • 循環(huán)調(diào)用recv()將大幅度推高CPU占用率;這也是我們?cè)诖a中留一句time.sleep(2)的原因,否則在低配主機(jī)下極容易出現(xiàn)卡機(jī)情況
  • 任務(wù)完成的響應(yīng)延遲增大了,因?yàn)槊窟^(guò)一段時(shí)間才去輪詢(xún)一次read操作,而任務(wù)可能在兩次輪詢(xún)之間的任意時(shí)間完成。這會(huì)導(dǎo)致整體數(shù)據(jù)吞吐量的降低。
  • 此外,在這個(gè)方案中recv()更多的是起到檢測(cè)“操作是否完成”的作用,實(shí)際操作系統(tǒng)提供了更為高效的檢測(cè)“操作是否完成“作用的接口,例如select()多路復(fù)用模式,可以一次檢測(cè)多個(gè)連接是否活躍。

    四、多路復(fù)用I/O模型

    4.1 select/poll模型

    以買(mǎi)票的例子舉例,select/poll模型小結(jié)為:

    1. # 老王去火車(chē)站買(mǎi)票,委托黃牛,然后每隔6小時(shí)電話黃牛詢(xún)問(wèn),黃牛三天內(nèi)買(mǎi)到票,然后老王去火車(chē)站交錢(qián)領(lǐng)票。# 耗費(fèi):往返車(chē)站2次,路上2小時(shí),黃牛手續(xù)費(fèi)100元,打電話17次

    I/O multiplexing這個(gè)詞可能有點(diǎn)陌生,但是如果我說(shuō)select/poll,大概就都能明白了。有些地方也稱(chēng)這種I/O方式為事件驅(qū)動(dòng)I/O(event driven I/O)。我們都知道,select/poll的好處就在于單個(gè)process就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)連接的I/O。它的基本原理就是select/poll這個(gè)functI/On會(huì)不斷的輪詢(xún)所負(fù)責(zé)的所有socket,當(dāng)某個(gè)socket有數(shù)據(jù)到達(dá)了,就通知用戶進(jìn)程。它的流程如圖:

    當(dāng)用戶進(jìn)程調(diào)用了select,那么整個(gè)進(jìn)程會(huì)被block,而同時(shí),kernel會(huì)“監(jiān)視”所有select負(fù)責(zé)的socket,當(dāng)任何一個(gè)socket中的數(shù)據(jù)準(zhǔn)備好了,select就會(huì)返回。這個(gè)時(shí)候用戶進(jìn)程再調(diào)用read操作,將數(shù)據(jù)從kernel拷貝到用戶進(jìn)程。

    這個(gè)圖和blocking I/O的圖其實(shí)并沒(méi)有太大的不同,事實(shí)上還更差一些。因?yàn)檫@里需要使用兩個(gè)系統(tǒng)調(diào)用(select和recvfrom),而blocking I/O只調(diào)用了一個(gè)系統(tǒng)調(diào)用(recvfrom)。但是,用select的優(yōu)勢(shì)在于它可以同時(shí)處理多個(gè)connectI/On。

    強(qiáng)調(diào):

  • 如果處理的連接數(shù)不是很高的話,使用select/poll的web server不一定比使用multi-threading + blocking I/O的web server性能更好,可能延遲還更大。select/poll的優(yōu)勢(shì)并不是對(duì)于單個(gè)連接能處理得更快,而是在于能處理更多的連接。
  • 在多路復(fù)用模型中,對(duì)于每一個(gè)socket,一般都設(shè)置成為non-blocking,但是,如上圖所示,整個(gè)用戶的process其實(shí)是一直被block的。只不過(guò)process是被select這個(gè)函數(shù)block,而不是被socket I/O給block。

    結(jié)論: select的優(yōu)勢(shì)在于可以處理多個(gè)連接,不適用于單個(gè)連接

  • 4.1.1 select網(wǎng)絡(luò)I/O模型

    #服務(wù)端 from socket import * import selects=socket(AF_INET,SOCK_STREAM) s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(('127.0.0.1',8081)) s.listen(5) s.setblocking(False) #設(shè)置socket的接口為非阻塞 read_l=[s,] while True:r_l,w_l,x_l=select.select(read_l,[],[])print(r_l)for ready_obj in r_l:if ready_obj == s:conn,addr=ready_obj.accept() #此時(shí)的ready_obj等于sread_l.append(conn)else:try:data=ready_obj.recv(1024) #此時(shí)的ready_obj等于connif not data:ready_obj.close()read_l.remove(ready_obj)continueready_obj.send(data.upper())except ConnectI/OnResetError:ready_obj.close()read_l.remove(ready_obj)#客戶端 from socket import * c=socket(AF_INET,SOCK_STREAM) c.connect(('127.0.0.1',8081))while True:msg=input('>>: ')if not msg:continuec.send(msg.encode('utf-8'))data=c.recv(1024)print(data.decode('utf-8'))

    4.1.2 select監(jiān)聽(tīng)fd變化的過(guò)程分析

  • 用戶進(jìn)程創(chuàng)建socket對(duì)象,拷貝監(jiān)聽(tīng)的fd到內(nèi)核空間,每一個(gè)fd會(huì)對(duì)應(yīng)一張系統(tǒng)文件表,內(nèi)核空間的fd響應(yīng)到數(shù)據(jù)后,就會(huì)發(fā)送信號(hào)給用戶進(jìn)程數(shù)據(jù)已到;

  • 用戶進(jìn)程再發(fā)送系統(tǒng)調(diào)用,比如(accept)將內(nèi)核空間的數(shù)據(jù)copy到用戶空間,同時(shí)作為接受數(shù)據(jù)端內(nèi)核空間的數(shù)據(jù)清除,這樣重新監(jiān)聽(tīng)時(shí)fd再有新的數(shù)據(jù)又可以響應(yīng)到了(發(fā)送端因?yàn)榛赥CP協(xié)議所以需要收到應(yīng)答后才會(huì)清除)。

  • 4.1.3 該模型的優(yōu)點(diǎn)

    相比其他模型,使用select() 的事件驅(qū)動(dòng)模型只用單線程(進(jìn)程)執(zhí)行,占用資源少,不消耗太多 CPU,同時(shí)能夠?yàn)槎嗫蛻舳颂峁┓?wù)。如果試圖建立一個(gè)簡(jiǎn)單的事件驅(qū)動(dòng)的服務(wù)器程序,這個(gè)模型有一定的參考價(jià)值。

    4.1.4 該模型的缺點(diǎn)

  • 首先select()接口并不是實(shí)現(xiàn)“事件驅(qū)動(dòng)”的最好選擇。因?yàn)楫?dāng)需要探測(cè)的句柄值較大時(shí),select()接口本身需要消耗大量時(shí)間去輪詢(xún)各個(gè)句柄。
  • 很多操作系統(tǒng)提供了更為高效的接口,如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll,…。
  • 如果需要實(shí)現(xiàn)更高效的服務(wù)器程序,類(lèi)似epoll這樣的接口更被推薦。遺憾的是不同的操作系統(tǒng)特供的epoll接口有很大差異,
  • 所以使用類(lèi)似于epoll的接口實(shí)現(xiàn)具有較好跨平臺(tái)能力的服務(wù)器會(huì)比較困難。
  • 其次,該模型將事件探測(cè)和事件響應(yīng)夾雜在一起,一旦事件響應(yīng)的執(zhí)行體龐大,則對(duì)整個(gè)模型是災(zāi)難性的。
  • 4.2 epoll模型(了解)

    以買(mǎi)票的例子舉例,epoll模型小結(jié)為:

    # 老王去火車(chē)站買(mǎi)票,委托黃牛,黃牛買(mǎi)到后即通知老王去領(lǐng),然后老王去火車(chē)站交錢(qián)領(lǐng)票。# 耗費(fèi):往返車(chē)站2次,路上2小時(shí),黃牛手續(xù)費(fèi)100元,無(wú)需打電話

    I/O多路復(fù)用這個(gè)概念被提出來(lái)以后, select是第一個(gè)實(shí)現(xiàn) (1983 左右在BSD里面實(shí)現(xiàn))。但是,select模型有著一個(gè)很大的問(wèn)題,那就是它所支持的fd的數(shù)量是有限制的,從linux源碼中所看:

    它所需要的fd_set類(lèi)型其實(shí)是一個(gè)__FD_SETSIZE長(zhǎng)度bit的數(shù)組。也就是說(shuō)通過(guò)FD_SET宏來(lái)對(duì)它進(jìn)行操作的時(shí)候,需要注意socket不能過(guò)大,否則可能出現(xiàn)數(shù)組寫(xiě)越界的錯(cuò)誤。

    而linux在2.6 內(nèi)核中引入的epoll模型,就徹底解決了這個(gè)問(wèn)題,由于目前epoll模型只能在linux中使用,因此我們開(kāi)發(fā)僅做了解即可。

    epoll模型提供了三個(gè)接口:

    其使用流程基本與select一致,大致如下:

    epoll_create函數(shù)會(huì)為要監(jiān)聽(tīng)的fd分配內(nèi)存。

    epoll_ctl函數(shù)將要監(jiān)聽(tīng)的fd拷貝到內(nèi)核空間,從而避免每次等待事件都要進(jìn)行內(nèi)存拷貝。同時(shí),注冊(cè)一個(gè)回調(diào)函數(shù)到 fd的設(shè)備等待隊(duì)列中,這樣,當(dāng)設(shè)備就緒的時(shí)候,驅(qū)動(dòng)程序可以直接調(diào)用回調(diào)函數(shù)進(jìn)行處理,從而避免了對(duì)所有監(jiān)聽(tīng)fd的輪循。

    epoll_wait函數(shù)會(huì)檢查是否已經(jīng)有fd就緒了,如果有則直接返回,如果沒(méi)有,則進(jìn)入休眠狀態(tài),直到被上述的回調(diào)函數(shù)喚醒或者超時(shí)時(shí)間到達(dá)。

    五、信號(hào)驅(qū)動(dòng)I/O模型(了解)

    以買(mǎi)票的例子舉例,該模型小結(jié)為:

    # 老王去火車(chē)站買(mǎi)票,給售票員留下電話,有票后,售票員電話通知老王,然后老王去火車(chē)站交錢(qián)領(lǐng)票。# 耗費(fèi):往返車(chē)站2次,路上2小時(shí),免黃牛費(fèi)100元,無(wú)需打電話

    由于信號(hào)驅(qū)動(dòng)I/O在實(shí)際中并不常用,所以我們只做簡(jiǎn)單了解。

    信號(hào)驅(qū)動(dòng)I/O模型,應(yīng)用進(jìn)程告訴內(nèi)核:當(dāng)數(shù)據(jù)報(bào)準(zhǔn)備好的時(shí)候,給我發(fā)送一個(gè)信號(hào),對(duì)SIGI/O信號(hào)進(jìn)行捕捉,并且調(diào)用我的信號(hào)處理函數(shù)來(lái)獲取數(shù)據(jù)報(bào)。

    六、異步I/O模型

    以買(mǎi)票的例子舉例,該模型小結(jié)為:

    # 老王去火車(chē)站買(mǎi)票,給售票員留下電話,有票后,售票員電話通知老王并快遞送票上門(mén)。# 耗費(fèi):往返車(chē)站1次,路上1小時(shí),免黃牛費(fèi)100元,無(wú)需打電話

    Linux下的asynchronous I/O其實(shí)用得不多,從內(nèi)核2.6版本才開(kāi)始引入。先看一下它的流程:

    用戶進(jìn)程發(fā)起read操作之后,立刻就可以開(kāi)始去做其它的事。而另一方面,從kernel的角度,當(dāng)它受到一個(gè)asynchronous read之后,首先它會(huì)立刻返回,所以不會(huì)對(duì)用戶進(jìn)程產(chǎn)生任何block。然后,kernel會(huì)等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶內(nèi)存,當(dāng)這一切都完成之后,kernel會(huì)給用戶進(jìn)程發(fā)送一個(gè)signal,告訴它read操作完成了。

    七、I/O模型比較分析

    到目前為止,已經(jīng)將四個(gè) I/O Model都介紹完了。現(xiàn)在回過(guò)頭來(lái)回答最初的那幾個(gè)問(wèn)題:阻塞和非阻塞的區(qū)別在哪,同步I/O 和 異步I/O 的區(qū)別在哪。

    先回答最簡(jiǎn)單的這個(gè):阻塞 vs 非阻塞。前面的介紹中其實(shí)已經(jīng)很明確的說(shuō)明了這兩者的區(qū)別。調(diào)用 阻塞I/O 會(huì)一直block住對(duì)應(yīng)的進(jìn)程直到操作完成,而 非阻塞I/O 在kernel還準(zhǔn)備數(shù)據(jù)的情況下會(huì)立刻返回。

    再說(shuō)明同步I/O 和 異步I/O 的區(qū)別之前,需要先給出兩者的定義。Stevens給出的定義(其實(shí)是POSIX的定義)是這樣子的:

    A synchronous I/O operatI/O n causes the requesting process to be blocked until that I/O operatI/O ncompletes;

    An asynchronous I/O operatI/O n does not cause the requesting process to be blocked;

    兩者的區(qū)別就在于 同步I/O 做”I/O operatI/O n”的時(shí)候會(huì)將process阻塞。按照這個(gè)定義,四個(gè)I/O模型可以分為兩大類(lèi),之前所述的 阻塞I/O , 非阻塞I/O ,I/O多路復(fù)用 都屬于 同步I/O 這一類(lèi),而 異步I/O 屬于后一類(lèi)。

    有人可能會(huì)說(shuō), 非阻塞I/O 并沒(méi)有被block啊。這里有個(gè)非常“狡猾”的地方,定義中所指的”I/O operatI/O n”是指真實(shí)的I/O操作,就是例子中的recvfrom這個(gè)system call。 非阻塞I/O 在執(zhí)行recvfrom這個(gè)system call的時(shí)候,如果kernel的數(shù)據(jù)沒(méi)有準(zhǔn)備好,這時(shí)候不會(huì)block進(jìn)程。但是,當(dāng)kernel中數(shù)據(jù)準(zhǔn)備好的時(shí)候,recvfrom會(huì)將數(shù)據(jù)從kernel拷貝到用戶內(nèi)存中,這個(gè)時(shí)候進(jìn)程是被block了,在這段時(shí)間內(nèi),進(jìn)程是被block的。而 異步I/O 則不一樣,當(dāng)進(jìn)程發(fā)起I/O操作之后,就直接返回再也不理睬了,直到kernel發(fā)送一個(gè)信號(hào),告訴進(jìn)程說(shuō)I/O完成。在這整個(gè)過(guò)程中,進(jìn)程完全沒(méi)有被block。

    各個(gè)I/O Model的比較如圖所示:

    經(jīng)過(guò)上面的介紹,會(huì)發(fā)現(xiàn) 非阻塞I/O 和 異步I/O 的區(qū)別還是很明顯的。在非阻塞I/O 中,雖然進(jìn)程大部分時(shí)間都不會(huì)被block,但是它仍然要求進(jìn)程去主動(dòng)的check,并且當(dāng)數(shù)據(jù)準(zhǔn)備完成以后,也需要進(jìn)程主動(dòng)的再次調(diào)用recvfrom來(lái)將數(shù)據(jù)拷貝到用戶內(nèi)存。而 異步I/O 則完全不同。它就像是用戶進(jìn)程將整個(gè)I/O操作交給了他人(kernel)完成,然后他人做完后發(fā)信號(hào)通知。在此期間,用戶進(jìn)程不需要去檢查I/O操作的狀態(tài),也不需要主動(dòng)的去拷貝數(shù)據(jù)。

    可以看出,以上五個(gè)模型的阻塞程度由低到高為:阻塞I/O>非阻塞I/O>多路轉(zhuǎn)接I/O>信號(hào)驅(qū)動(dòng)I/O>異步I/O,因此他們的效率是由低到高的。

    轉(zhuǎn)載于:https://www.cnblogs.com/nickchen121/p/11145098.html

    總結(jié)

    以上是生活随笔為你收集整理的大白话五种IO模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。