Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 506, comments - 13, trackbacks - 0, articles - 1

          1、系列文章引言

          1.1 文章目的

          作為即時通訊技術(shù)的開發(fā)者來說,高性能、高并發(fā)相關(guān)的技術(shù)概念早就了然與胸,什么線程池、零拷貝、多路復(fù)用、事件驅(qū)動、epoll等等名詞信手拈來,又或許你對具有這些技術(shù)特征的技術(shù)框架比如:Java的Netty、Php的workman、Go的nget等熟練掌握。但真正到了面視或者技術(shù)實踐過程中遇到無法釋懷的疑惑時,方知自已所掌握的不過是皮毛。

          返璞歸真、回歸本質(zhì),這些技術(shù)特征背后的底層原理到底是什么?如何能通俗易懂、毫不費力真正透徹理解這些技術(shù)背后的原理,正是《從根上理解高性能、高并發(fā)》系列文章所要分享的。

          1.2 文章源起

          我整理了相當多有關(guān)IM、消息推送等即時通訊技術(shù)相關(guān)的資源和文章,從最開始的開源IM框架MobileIMSDK,到網(wǎng)絡(luò)編程經(jīng)典巨著《TCP/IP詳解》的在線版本,再到IM開發(fā)綱領(lǐng)性文章《新手入門一篇就夠:從零開發(fā)移動端IM》,以及網(wǎng)絡(luò)編程由淺到深的《網(wǎng)絡(luò)編程懶人入門》、《腦殘式網(wǎng)絡(luò)編程入門》、《高性能網(wǎng)絡(luò)編程》、《不為人知的網(wǎng)絡(luò)編程》系列文章。

          越往知識的深處走,越覺得對即時通訊技術(shù)了解的太少。于是后來,為了讓開發(fā)者門更好地從基礎(chǔ)電信技術(shù)的角度理解網(wǎng)絡(luò)(尤其移動網(wǎng)絡(luò))特性,我跨專業(yè)收集整理了《IM開發(fā)者的零基礎(chǔ)通信技術(shù)入門》系列高階文章。這系列文章已然是普通即時通訊開發(fā)者的網(wǎng)絡(luò)通信技術(shù)知識邊界,加上之前這些網(wǎng)絡(luò)編程資料,解決網(wǎng)絡(luò)通信方面的知識盲點基本夠用了。

          對于即時通訊IM這種系統(tǒng)的開發(fā)來說,網(wǎng)絡(luò)通信知識確實非常重要,但回歸到技術(shù)本質(zhì),實現(xiàn)網(wǎng)絡(luò)通信本身的這些技術(shù)特征:包括上面提到的線程池、零拷貝、多路復(fù)用、事件驅(qū)動等等,它們的本質(zhì)是什么?底層原理又是怎樣?這就是整理本系列文章的目的,希望對你有用。

          1.3 文章目錄

          1.4 本篇概述

          接上篇《深入計算機底層,理解線程與線程池》,本篇是高性能、高并發(fā)系列的第2篇文章,在這里我們來到了I/O這一話題。你有沒有想過,當我們執(zhí)行文件I/O、網(wǎng)絡(luò)I/O操作時計算機底層到底發(fā)生了些什么?對于計算機來說I/O是極其重要的,本篇將帶給你這個問的答案。

          2、本文作者

          應(yīng)作者要求,不提供真名,也不提供個人照片。

          本文作者主要技術(shù)方向為互聯(lián)網(wǎng)后端、高并發(fā)高性能服務(wù)器、檢索引擎技術(shù),網(wǎng)名是“碼農(nóng)的荒島求生”,公眾號“碼農(nóng)的荒島求生”。感謝作者的無私分享。

          3、不能執(zhí)行I/O的計算機是什么?

          相信對于程序員來說I/O操作是最為熟悉不過的了,比如:

          • 1)當我們使用C語言中的printf、C++中的"<<",Python中的print,Java中的System.out.println等時;
          • 2)當我們使用各種語言讀寫文件時;
          • 3)當我們通過TCP/IP進行網(wǎng)絡(luò)通信時;
          • 4)當我們使用鼠標龍飛鳳舞時;
          • 5)當我們拿起鍵盤在評論區(qū)指點江山亦或是埋頭苦干努力制造bug時;
          • 6)當我們能看到屏幕上的漂亮的圖形界面時等等。

          以上這一切,都是I/O!

          想一想:如果沒有I/O計算機該是一種多么枯燥的設(shè)備,不能看電影、不能玩游戲,也不能上網(wǎng),這樣的計算機最多就是一個大號的計算器。

          既然I/O這么重要,那么到底什么才是I/O呢?

          4、什么是I/O?

          I/O就是簡單的數(shù)據(jù)Copy,僅此而已!

          這一點很重要!

          既然是copy數(shù)據(jù),那么又是從哪里copy到哪里呢?

          如果數(shù)據(jù)是從外部設(shè)備copy到內(nèi)存中,這就是Input。

          如果數(shù)據(jù)是從內(nèi)存copy到外部設(shè)備,這就是Output。

          內(nèi)存與外部設(shè)備之間不嫌麻煩的來回copy數(shù)據(jù)就是Input and Output,簡稱I/O(Input/Output),僅此而已。

          5、I/O與CPU

          現(xiàn)在我們知道了什么是I/O,接下來就是重點部分了,大家注意,坐穩(wěn)了。

          我們知道現(xiàn)在的CPU其主頻都是數(shù)GHz起步,這是什么意思呢?

          簡單說就是:CPU執(zhí)行機器指令的速度是納秒級別的,而通常的I/O比如磁盤操作,一次磁盤seek大概在毫秒級別,因此如果我們把CPU的速度比作戰(zhàn)斗機的話,那么I/O操作的速度就是肯德雞。

          也就是說當我們的程序跑起來時(CPU執(zhí)行機器指令),其速度是要遠遠快于I/O速度的。那么接下來的問題就是二者速度相差這么大,那么我們該如何設(shè)計、該如何更加合理的高效利用系統(tǒng)資源呢?

          既然有速度差異,而且進程在執(zhí)行完I/O操作前不能繼續(xù)向前推進,那么顯然只有一個辦法,那就是等待(wait)。

          同樣是等待,有聰明的等待,也有傻傻的等待,簡稱傻等,那么是選擇聰明的等待呢還是選擇傻等呢?

          假設(shè)你是一個急性子(CPU),需要等待一個重要的文件,不巧的是這個文件只能快遞過來(I/O),那么這時你是選擇什么事情都不干了,深情的注視著門口就像盼望著你的哈尼一樣專心等待這個快遞呢?還是暫時先不要管快遞了,玩?zhèn)€游戲看個電影刷會兒短視頻等快遞來了再說呢?

          很顯然,更好的方法就是先去干其它事情,快遞來了再說。

          因此:這里的關(guān)鍵點就是快遞沒到前手頭上的事情可以先暫停,切換到其它任務(wù),等快遞過來了再切換回來。

          理解了這一點你就能明白執(zhí)行I/O操作時底層都發(fā)生了什么。

          接下來讓我們以讀取磁盤文件為例來講解這一過程。

          6、執(zhí)行I/O時底層都發(fā)生了什么

          在上一篇《深入計算機底層,理解線程與線程池》中,我們引入了進程和線程的概念。

          在支持線程的操作系統(tǒng)中,實際上被調(diào)度的是線程而不是進程,為了更加清晰的理解I/O過程,我們暫時假設(shè)操作系統(tǒng)只有進程這樣的概念,先不去考慮線程,這并不會影響我們的討論。

          現(xiàn)在內(nèi)存中有兩個進程,進程A和進程B,當前進程A正在運行。

          如下圖所示:

          進程A中有一段讀取文件的代碼,不管在什么語言中通常我們定義一個用來裝數(shù)據(jù)的buff,然后調(diào)用read之類的函數(shù)。

          就像這樣:

          read(buff);

          這就是一種典型的I/O操作,當CPU執(zhí)行到這段代碼的時候會向磁盤發(fā)送讀取請求。

          注意:與CPU執(zhí)行指令的速度相比,I/O操作操作是非常慢的,因此操作系統(tǒng)是不可能把寶貴的CPU計算資源浪費在無謂的等待上的,這時重點來了,注意接下來是重點哦。

          由于外部設(shè)備執(zhí)行I/O操作是相當慢的,因此在I/O操作完成之前進程是無法繼續(xù)向前推進的,這就是所謂的阻塞,即通常所說的block。

          操作系統(tǒng)檢測到進程向I/O設(shè)備發(fā)起請求后就暫停進程的運行,怎么暫停運行呢?很簡單:只需要記錄下當前進程的運行狀態(tài)并把CPU的PC寄存器指向其它進程的指令就可以了。

          進程有暫停就會有繼續(xù)執(zhí)行,因此操作系統(tǒng)必須保存被暫停的進程以備后續(xù)繼續(xù)執(zhí)行,顯然我們可以用隊列來保存被暫停執(zhí)行的進程。

          如下圖所示,進程A被暫停執(zhí)行并被放到阻塞隊列中(注意:不同的操作系統(tǒng)會有不同的實現(xiàn),可能每個I/O設(shè)備都有一個對應(yīng)的阻塞隊列,但這種實現(xiàn)細節(jié)上的差異不影響我們的討論)。

          這時操作系統(tǒng)已經(jīng)向磁盤發(fā)送了I/O請求,因此磁盤driver開始將磁盤中的數(shù)據(jù)copy到進程A的buff中。雖然這時進程A已經(jīng)被暫停執(zhí)行了,但這并不妨礙磁盤向內(nèi)存中copy數(shù)據(jù)。

          注意:現(xiàn)代磁盤向內(nèi)存copy數(shù)據(jù)時無需借助CPU的幫助,這就是所謂的DMA(Direct Memory Access)。

          這個過程如下圖所示:

          讓磁盤先copy著數(shù)據(jù),我們接著聊。

          實際上:操作系統(tǒng)中除了有阻塞隊列之外也有就緒隊列,所謂就緒隊列是指隊列里的進程準備就緒可以被CPU執(zhí)行了。

          你可能會問為什么不直接執(zhí)行非要有個就緒隊列呢?答案很簡單:那就是僧多粥少,在即使只有1個核的機器上也可以創(chuàng)建出成千上萬個進程,CPU不可能同時執(zhí)行這么多的進程,因此必然存在這樣的進程,即使其一切準備就緒也不能被分配到計算資源,這樣的進程就被放到了就緒隊列。

          現(xiàn)在進程B就位于就緒隊列,萬事俱備只欠CPU。

          如下圖所示:

          當進程A被暫停執(zhí)行后CPU是不可以閑下來的,因為就緒隊列中還有嗷嗷待哺的進程B,這時操作系統(tǒng)開始在就緒隊列中找下一個可以執(zhí)行的進程,也就是這里的進程B。

          此時操作系統(tǒng)將進程B從就緒隊列中取出,找出進程B被暫停時執(zhí)行到的機器指令的位置,然后將CPU的PC寄存器指向該位置,這樣進程B就開始運行啦。

          如下圖所示:

          注意:接下來的這段是重點中的重點!

          注意觀察上圖:此時進程B在被CPU執(zhí)行,磁盤在向進程A的內(nèi)存空間中copy數(shù)據(jù),看出來了嗎——大家都在忙,誰都沒有閑著,數(shù)據(jù)copy和指令執(zhí)行在同時進行,在操作系統(tǒng)的調(diào)度下,CPU、磁盤都得到了充分的利用,這就是程序員的智慧所在。

          現(xiàn)在你應(yīng)該理解為什么操作系統(tǒng)這么重要了吧。

          此后磁盤終于將全部數(shù)據(jù)都copy到了進程A的內(nèi)存中,這時磁盤通知操作系統(tǒng)任務(wù)完成啦,你可能會問怎么通知呢?這就是中斷。

          操作系統(tǒng)接收到磁盤中斷后發(fā)現(xiàn)數(shù)據(jù)copy完畢,進程A重新獲得繼續(xù)運行的資格,這時操作系統(tǒng)小心翼翼的把進程A從阻塞隊列放到了就緒隊列當中。

          如下圖所示:

          注意:從前面關(guān)于就緒狀態(tài)的討論中我們知道,操作系統(tǒng)是不會直接運行進程A的,進程A必須被放到就緒隊列中等待,這樣對大家都公平。

          此后進程B繼續(xù)執(zhí)行,進程A繼續(xù)等待,進程B執(zhí)行了一會兒后操作系統(tǒng)認為進程B執(zhí)行的時間夠長了,因此把進程B放到就緒隊列,把進程A取出并繼續(xù)執(zhí)行。

          注意:操作系統(tǒng)把進程B放到的是就緒隊列,因此進程B被暫停運行僅僅是因為時間片到了而不是因為發(fā)起I/O請求被阻塞。

          如下圖所示:

          進程A繼續(xù)執(zhí)行,此時buff中已經(jīng)裝滿了想要的數(shù)據(jù),進程A就這樣愉快的運行下去了,就好像從來沒有被暫停過一樣,進程對于自己被暫停一事一無所知,這就是操作系統(tǒng)的魔法。

          現(xiàn)在你應(yīng)該明白了I/O是一個怎樣的過程了吧。

          這種進程執(zhí)行I/O操作被阻塞暫停執(zhí)行的方式被稱為阻塞式I/O,blocking I/O,這也是最常見最容易理解的I/O方式,有阻塞式I/O就有非阻塞式I/O,在這里我們暫時先不考慮這種方式。

          在本節(jié)開頭我們說過暫時只考慮進程而不考慮線程,現(xiàn)在我們放寬這個條件,實際上也非常簡單,只需要把前圖中調(diào)度的進程改為線程就可以了,這里的討論對于線程一樣成立。

          7、零拷貝(Zero-copy)

          最后需要注意的一點就是:上面的講解中我們直接把磁盤數(shù)據(jù)copy到了進程空間中,但實際上一般情況下I/O數(shù)據(jù)是要首先copy到操作系統(tǒng)內(nèi)部,然后操作系統(tǒng)再copy到進程空間中。

          因此我們可以看到這里其實還有一層經(jīng)過操作系統(tǒng)的copy,對于性能要求很高的場景其實也是可以繞過操作系統(tǒng)直接進行數(shù)據(jù)copy的,這也是本文描述的場景,這種繞過操作系統(tǒng)直接進行數(shù)據(jù)copy的技術(shù)被稱為Zero-copy,也就零拷貝,高并發(fā)、高性能場景下常用的一種技術(shù),原理上很簡單吧。

          PS:對于搞即時通訊開發(fā)的Java程序員來說,著名的高性能網(wǎng)絡(luò)框架Netty就使用了零拷貝技術(shù),具體可以讀《NIO框架詳解:Netty的高性能之道》一文的第12節(jié)。如果對于Netty框架很好奇但不了解的話,可以因著這兩篇文章入門:《新手入門:目前為止最透徹的的Netty高性能原理和框架架構(gòu)解析》、《史上最通俗Netty入門長文:基本介紹、環(huán)境搭建、動手實戰(zhàn)》。

          8、本文小結(jié)

          本文講解的是程序員常用的I/O(包括所謂的網(wǎng)絡(luò)I/O),一般來說作為程序員我們無需關(guān)心,但是理解I/O背后的底層原理對于設(shè)計比如IM這種高性能、高并發(fā)系統(tǒng)是極為有益的,希望這篇能對大家加深對I/O的認識有所幫助。

          接下來的一篇《從根上理解高性能、高并發(fā)(三):深入操作系統(tǒng),徹底理解I/O多路復(fù)用》將要分享的是I/O技術(shù)的一大突破,正是因為它,才徹底解決了高并發(fā)網(wǎng)絡(luò)通信中的C10K問題(見《高性能網(wǎng)絡(luò)編程(二):上一個10年,著名的C10K并發(fā)連接問題),敬請期待!

          附錄:更多高性能、高并發(fā)文章精選

          高性能網(wǎng)絡(luò)編程(一):單臺服務(wù)器并發(fā)TCP連接數(shù)到底可以有多少

          高性能網(wǎng)絡(luò)編程(二):上一個10年,著名的C10K并發(fā)連接問題

          高性能網(wǎng)絡(luò)編程(三):下一個10年,是時候考慮C10M并發(fā)問題了

          高性能網(wǎng)絡(luò)編程(四):從C10K到C10M高性能網(wǎng)絡(luò)應(yīng)用的理論探索

          高性能網(wǎng)絡(luò)編程(五):一文讀懂高性能網(wǎng)絡(luò)編程中的I/O模型

          高性能網(wǎng)絡(luò)編程(六):一文讀懂高性能網(wǎng)絡(luò)編程中的線程模型

          高性能網(wǎng)絡(luò)編程(七):到底什么是高并發(fā)?一文即懂!

          以網(wǎng)游服務(wù)端的網(wǎng)絡(luò)接入層設(shè)計為例,理解實時通信的技術(shù)挑戰(zhàn)

          知乎技術(shù)分享:知乎千萬級并發(fā)的高性能長連接網(wǎng)關(guān)技術(shù)實踐

          淘寶技術(shù)分享:手淘億級移動端接入層網(wǎng)關(guān)的技術(shù)演進之路

          一套海量在線用戶的移動端IM架構(gòu)設(shè)計實踐分享(含詳細圖文)

          一套原創(chuàng)分布式即時通訊(IM)系統(tǒng)理論架構(gòu)方案

          微信后臺基于時間序的海量數(shù)據(jù)冷熱分級架構(gòu)設(shè)計實踐

          微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡(演講全文)

          如何解讀《微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡》

          快速裂變:見證微信強大后臺架構(gòu)從0到1的演進歷程(一)

          17年的實踐:騰訊海量產(chǎn)品的技術(shù)方法論

          騰訊資深架構(gòu)師干貨總結(jié):一文讀懂大型分布式系統(tǒng)設(shè)計的方方面面

          以微博類應(yīng)用場景為例,總結(jié)海量社交系統(tǒng)的架構(gòu)設(shè)計步驟

          新手入門:零基礎(chǔ)理解大型分布式架構(gòu)的演進歷史、技術(shù)原理、最佳實踐

          從新手到架構(gòu)師,一篇就夠:從100到1000萬高并發(fā)的架構(gòu)演進之路

          本文已同步發(fā)布于“即時通訊技術(shù)圈”公眾號。

          ▲ 本文在公眾號上的鏈接是:點此進入。同步發(fā)布鏈接是:http://www.52im.net/thread-3280-1-1.html



          作者:Jack Jiang (點擊作者姓名進入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時通訊開發(fā)交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時是【原創(chuàng)Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處(也可前往 我的52im.net 找到我)。


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 阿拉尔市| 南靖县| 阿克苏市| 临江市| 文安县| 宝山区| 荔波县| 开原市| 泌阳县| 扶风县| 南宫市| 鹤壁市| 雅安市| 青川县| 重庆市| 隆德县| 喀喇| 墨竹工卡县| 杭锦旗| 克拉玛依市| 北宁市| 宜兴市| 娱乐| 甘德县| 璧山县| 新密市| 伊春市| 盐津县| 云霄县| 中西区| 吴川市| 孟连| 南乐县| 新和县| 青州市| 都江堰市| 房产| 屏边| 长丰县| 安徽省| 新龙县|