99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

Linux學(xué)習(xí)之常見(jiàn)的5種IO 模型

發(fā)布時(shí)間:2017-01-17 19:18  回復(fù):0  查看:2621   最后回復(fù):2017-01-17 19:18  

本文和大家分享的主要是linux中常見(jiàn)的5IO 模型相關(guān)內(nèi)容,一起來(lái)看看吧,希望對(duì)大家學(xué)習(xí)linux有所幫助。

   IO模型
  用一幅圖表示所支持的I/O 模型
Linux學(xué)習(xí)之常見(jiàn)的5種IO 模型
縱向維度是阻塞( Blocking 、 非阻塞( Non-blocking ;橫向維度是 同步 、 異步 ??偨Y(jié)起來(lái)是四種模型  同步阻塞、同步非阻塞;異步阻塞、異步非阻塞  。《Unix 網(wǎng)絡(luò)編程》中劃分出了 第五種 模型 ——“ 信號(hào)驅(qū)動(dòng)式 IO” 其實(shí)屬于異步阻塞類型,這種模型的通知方式有多種多樣后面展開(kāi)說(shuō)明。
   同步/異步、阻塞/非阻塞
  從內(nèi)核角度看I/O 操作分為兩步:用戶層 API 調(diào)用;內(nèi)核層完成系統(tǒng)調(diào)用(發(fā)起 I/O 請(qǐng)求)。所以 異步 / 同步 的是指 API 調(diào)用; 阻塞 / 非阻塞 是指內(nèi)核完成 I/O 調(diào)用的模式。用一幅圖表示更加明顯
Linux學(xué)習(xí)之常見(jiàn)的5種IO 模型
同步是指函數(shù)完成之前會(huì)一直等待;  阻塞  是指系統(tǒng)調(diào)用的時(shí)候進(jìn)程會(huì)被設(shè)置為Sleep 狀態(tài)直到等待的事件發(fā)生(比如有新的數(shù)據(jù))。明白這一點(diǎn)之后再看這五種模型相信就會(huì)清晰很多,我們挨個(gè)分析:
   同步阻塞
  這種模型最為常見(jiàn),用戶空間調(diào)用API (  read    write ) 會(huì)轉(zhuǎn)化成一個(gè) I/O 請(qǐng)求,一直等到 I/O 請(qǐng)求完成 API 調(diào)用才會(huì)完成。這意味著: 在 API 調(diào)用期間用戶程序是同步的的;這個(gè) API 調(diào)用會(huì)導(dǎo)致系統(tǒng)以阻塞的模式執(zhí)行 I/O ,如果此時(shí)沒(méi)有數(shù)據(jù)則一直 等待 (放棄 CPU 主動(dòng)掛起 ——Sleep 狀態(tài)) (注意,對(duì)于硬盤來(lái)說(shuō)是不會(huì)出現(xiàn)阻塞的,無(wú)論是什么時(shí)候讀它總是有數(shù)據(jù)。常見(jiàn)的阻塞設(shè)備是終端、網(wǎng)卡之類的)。
Linux學(xué)習(xí)之常見(jiàn)的5種IO 模型
  read  為例子,它由三個(gè)參數(shù)組成,第一個(gè)函數(shù)是文件描述符;第二個(gè)是  應(yīng)用緩沖  ;第三個(gè)參數(shù)是需要讀取的字節(jié)數(shù)。經(jīng)過(guò)系統(tǒng)調(diào)用會(huì)以阻塞模式執(zhí)行I/O I/O 模塊讀取數(shù)據(jù)后會(huì)放入到 PageCache 中;最后一步是把數(shù)據(jù)從 PageCache 復(fù)制到  應(yīng)用緩沖  。如果I/O 請(qǐng)求無(wú)法得到滿足 —— 沒(méi)有數(shù)據(jù),則主動(dòng)讓出 CPU 直到  有數(shù)據(jù)  (注意,即便系統(tǒng)調(diào)用讓出CPU 也未必真的就讓出。 read 函數(shù)是同步的,所以 CPU 還是會(huì)被用戶空間代碼占用)。
   同步非阻塞
  這種模式通過(guò)調(diào)用  read  、  write  的時(shí)候指定  O_NONBLOCK  參數(shù)。和同步阻塞 模式的區(qū)別在于系統(tǒng)調(diào)用的時(shí)候它是以非阻塞的方式執(zhí)行,無(wú)論是否有數(shù)據(jù)都會(huì)立即返回。 以  read  為例,如果成功讀取到數(shù)據(jù)它返回讀取到的字節(jié)數(shù);如果此時(shí)沒(méi)有數(shù)據(jù)則返回-1 ,同時(shí)設(shè)置 errno EAGAIN (或者 EWOULDBLOCK ,二者相同)。所以這種模式下我們一般會(huì)用一個(gè) 循環(huán) 不停的嘗試讀取數(shù)據(jù),處理數(shù)據(jù)。
   異步阻塞
  同步模型最主要的問(wèn)題是占用CPU , 阻塞 I/O 會(huì)主動(dòng)讓出 CPU 但是用戶空間的系統(tǒng)調(diào)用還是不會(huì)返回依然耗費(fèi) CPU ;非阻塞 I/O 必須不停的 輪詢 不斷嘗試讀取數(shù)據(jù)(會(huì)耗費(fèi)更多 CPU 更加低效)。如果仔細(xì)分析同步模型霸占 CPU 的原因不難得出結(jié)論 —— 都是在等待數(shù)據(jù)到來(lái)。異步模式正是意識(shí)到這一點(diǎn)所以把 I/O 讀取細(xì)化為 訂閱 I/O 事件,實(shí)際 I/O 讀寫,在 訂閱 I/O 事件 事件部分會(huì)主動(dòng)讓出 CPU 直到事件發(fā)生 。異步模式下的 I/O 函數(shù)和同步模式下的 I/O 函數(shù)是一樣的(都是  read  、  write  )唯一的區(qū)別是異步模式  必有數(shù)據(jù)  而同步模式則未必。 常見(jiàn)的異步阻塞函數(shù)包括  select    poll  ,  epoll  ,這些函數(shù)的用法需要花費(fèi)相當(dāng)大的篇幅介紹而這篇文章我想集中精力介紹“I/O 模型 。以  select  為例我們看一下大致原理
Linux學(xué)習(xí)之常見(jiàn)的5種IO 模型
異步模式下我們的API 調(diào)用分為兩步,第一步是通過(guò)  select  訂閱讀寫事件  這個(gè)函數(shù)會(huì)主動(dòng)讓出CPU 直到事件發(fā)生(設(shè)置為 Sleep 狀態(tài),等待事件發(fā)生) ; select 一旦返回就證明可以開(kāi)始讀了所以第二部是通過(guò)  read  讀取數(shù)據(jù)必有數(shù)據(jù) )
   異步阻塞模型之信號(hào)驅(qū)動(dòng)
  完美主義者 看了上面的  select  之后會(huì)有點(diǎn)不爽—— 我還要 等待 讀寫事件(即便  select  會(huì)主動(dòng)讓出CPU ),能不能有讀寫事件的時(shí)候主動(dòng)通知我啊?。借助 信號(hào) 機(jī)制我們可以實(shí)現(xiàn)這個(gè),但是這并不完美而且有點(diǎn)弄巧成拙的意思。 具體用法:通過(guò)  fcntl  函數(shù)設(shè)置一個(gè)  F_GETFL|O_ASYNC    曾經(jīng)信號(hào)驅(qū)動(dòng)I/O 也叫 異步 I/O” 所以才有  O_ASYNC  的說(shuō)法),當(dāng)有I/O 時(shí)間的時(shí)候操作系統(tǒng)會(huì)觸發(fā)  SIGIO  信號(hào)。在程序里只需要綁定  SIGIO  信號(hào)的處理函數(shù)就可以了。但是這里有個(gè)問(wèn)題——  信號(hào)處理函數(shù)由哪個(gè)進(jìn)程執(zhí)行呢?  ,答案是:屬主 進(jìn)程。操作系統(tǒng)只負(fù)責(zé)參數(shù)信號(hào)而實(shí)際的信號(hào)處理函數(shù)必須由用戶空間的進(jìn)程實(shí)現(xiàn)。(這就是設(shè)置  F_SETOWN  為當(dāng)前進(jìn)程PID 的原因) 信號(hào)驅(qū)動(dòng)性能要比  select  、  poll  高(避免文件描述符的復(fù)制)但是缺點(diǎn)是致命的——* Linux 中信號(hào)隊(duì)列是有限制的如果操過(guò)這個(gè)數(shù)字問(wèn)題就完全無(wú)法讀取數(shù)據(jù)。
   異步非阻塞
  這種模型是最省事 的模型,系統(tǒng)調(diào)用完成之后就只要坐等數(shù)據(jù)就可以了。是不是特別爽?其實(shí)不然,問(wèn)題出在實(shí)現(xiàn)上。 Linux 上的 AIO 兩個(gè)實(shí)現(xiàn)版本, POSIX 的實(shí)現(xiàn)最爛(藍(lán)色巨人的鍋)性能很差而且是基于 事件驅(qū)動(dòng) 還會(huì)出現(xiàn) 信號(hào)隊(duì)列不足 的問(wèn)題 ( 所以它就偷偷的創(chuàng)建線程,導(dǎo)致線程也不可控了 ) ;一個(gè)是 Linux 自己實(shí)現(xiàn)的( redhat 貢獻(xiàn)) Native AIO 。 Native AIO 主要涉及到的兩個(gè)函數(shù) io_submit  設(shè)置需要I/O 動(dòng)作(讀、寫,數(shù)據(jù)大小,應(yīng)用緩沖區(qū)等);  io_getevents  等待I/O 動(dòng)作完成。沒(méi)錯(cuò),即便你的整個(gè) I/O 行為是非阻塞的還是需要有一個(gè)辦法知道數(shù)據(jù)是否讀取 / 寫入成功。
Linux學(xué)習(xí)之常見(jiàn)的5種IO 模型
注意圖中,內(nèi)核不再為I/O 分配 PageCache ,所有的數(shù)據(jù)必須有用戶自己讀取到應(yīng)用緩沖中維護(hù)。所以 AIO 一定是和 直接 I/O” 配合使用。 AIO 針對(duì)網(wǎng)卡設(shè)備的意義不大,首先它的實(shí)現(xiàn)本質(zhì)上和 epoll 差不多;其次它在 Linux 中的作用更多的是用于磁盤 I/O (異步非阻塞可以不用多線程就造成大量的 I/O 請(qǐng)求便于 I/O 模塊 合并 優(yōu)化會(huì)提高整體 I/O 的吞吐率 —— 而且對(duì) CPU 開(kāi)銷比較少)。 在 Nginx 中用了一個(gè)技巧,可以實(shí)現(xiàn) AIO epoll 聯(lián)動(dòng), AIO 讀取到數(shù)據(jù)后觸發(fā) epoll 發(fā)送數(shù)據(jù)。(這個(gè)特性是非常尷尬的,如果是磁盤文件完全可以用 sendfile 搞定)。
   Direct I/OBuffered I/O
  Linux 在進(jìn)行 I/O 操作的時(shí)候會(huì)先把數(shù)據(jù)放到 PageCache 中然后通過(guò) 內(nèi)存映射 的方式返回給應(yīng)用程序,這樣做的好處是可以預(yù)讀數(shù)據(jù)也能在多個(gè)進(jìn)程讀取相同數(shù)據(jù)的時(shí)候起到 Cache 的作用。應(yīng)用程序不能直接使用 PageCache 中的數(shù)據(jù),通常是復(fù)制到一塊 用戶空間 的內(nèi)存中再使用。
  ·Direct I/O 是指數(shù)據(jù)不落在 PageCache ,直接從設(shè)備讀取到數(shù)據(jù)后放到用戶空間中
  ·Buffered I/O 是指數(shù)據(jù)競(jìng)購(gòu) PageCache
  同步I/O 只能使用 Buffered I/O ;異步阻塞 I/O 可以 Buffered I/O 也可以使用 Direct I/O ;異步非阻塞 I/O 只能使用 Direct I/O
   Zero Copy
  考慮從磁盤讀取文件經(jīng)過(guò)網(wǎng)卡發(fā)送出去,會(huì)有  四次內(nèi)存復(fù)制  1. DMA 會(huì)復(fù)制磁盤數(shù)據(jù)到內(nèi)核空間, 2.  應(yīng)用程序復(fù)制內(nèi)核空間的數(shù)據(jù)到用戶空間; 3.  應(yīng)用程序用戶空間的數(shù)據(jù)復(fù)制到 Socket 緩沖(內(nèi)核空間); 4.  協(xié)議棧把數(shù)據(jù)復(fù)制到網(wǎng)卡的中發(fā)送。 簡(jiǎn)單來(lái)說(shuō) Zero Copy 就是 節(jié)省這個(gè)過(guò)程中的內(nèi)存復(fù)制次數(shù)  。有幾種做法:
  ·Direct I/O 直接把磁盤數(shù)據(jù)復(fù)制到內(nèi)核空間; 但是 Direct I/O 沒(méi)有辦法直接把數(shù)據(jù)放到網(wǎng)卡中 —— 必須要經(jīng)過(guò)協(xié)議棧 。所以可以節(jié)省一次內(nèi)存復(fù)制;
  ·sendfile ,磁盤數(shù)據(jù)通過(guò) DMA 讀取到內(nèi)核空間后直接交給 TCP/IP 協(xié)議棧;真正的不需要內(nèi)存復(fù)制;
  除此之外還可以利用  splice  、  mmap  做一些優(yōu)化,根據(jù)不同的設(shè)備需要采用不同的方式此處不再展開(kāi)。
   最后的禮物
  我是一個(gè) Java 程序員,出于職業(yè)的良知(公眾賬號(hào)里遍地的 Python 、 C Erlang 都讓我遺忘了自己的本性)最后我決定回歸本性看一下 Java AIO 。  JDK7.0 引入了 AIO ,但是  JavaAIO其實(shí)內(nèi)部是用Epoll實(shí)現(xiàn)  (自己去看LinuxAsynchronousChannelProvider.java 文件吧)。所以不要以為 Java AIO 會(huì)比 NIO 快多少多多,壓根沒(méi)有什么性能變化。
來(lái)源:公眾號(hào)
您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?