加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 內(nèi)核態(tài)和用戶態(tài)
    • 內(nèi)核態(tài)和用戶態(tài)是怎么控制數(shù)據(jù)傳輸?shù)模?/span>
    • 什么是 DMA ?
    • 那到底什么是 DMA 技術(shù)?
    • 零拷貝技術(shù)實(shí)現(xiàn)的方式
    • 為啥要聊PageCache?
    • 大文件傳輸怎么做?
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

一文讀懂計(jì)算機(jī)內(nèi)核態(tài)、用戶態(tài)和零拷貝技術(shù)

2023/02/12
1445
閱讀需 24 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

存儲(chǔ)介質(zhì)的性能

話不多說(shuō),先看一張圖,下圖左邊是磁盤到內(nèi)存的不同介質(zhì),右邊形象地描述了每種介質(zhì)的讀寫速率。一句話總結(jié)就是越靠近c(diǎn)pu,讀寫性能越快。了解了不同硬件介質(zhì)的讀寫速率后,你會(huì)發(fā)現(xiàn)零拷貝技術(shù)是多么的香,對(duì)于追求極致性能的讀寫系統(tǒng)而言,掌握這個(gè)技術(shù)是多么的優(yōu)秀~

上圖是當(dāng)前主流存儲(chǔ)介質(zhì)的讀寫性能,從磁盤到內(nèi)存、內(nèi)存到緩存、緩存到寄存器,每上一個(gè)臺(tái)階,性能就提升10倍。如果我們打開一個(gè)文件去讀里面的內(nèi)容,你會(huì)發(fā)現(xiàn)時(shí)間讀取的時(shí)間是遠(yuǎn)大于磁盤提供的這個(gè)時(shí)延的,這是為什么呢?問(wèn)題就在內(nèi)核態(tài)和用戶態(tài)這2個(gè)概念后面深藏的I/O邏輯作怪。

內(nèi)核態(tài)和用戶態(tài)

內(nèi)核態(tài):也稱為內(nèi)核空間。cpu可以訪問(wèn)內(nèi)存的所有數(shù)據(jù),還控制著外圍設(shè)備的訪問(wèn),例如硬盤、網(wǎng)卡、鼠標(biāo)、鍵盤等。cpu也可以將自己從一個(gè)程序切換到另一個(gè)程序。

用戶態(tài):也稱為用戶空間。只能受限的訪問(wèn)內(nèi)存地址,cpu資源可以被其他程序獲取。

計(jì)算機(jī)資源的管控范圍

坦白地說(shuō)內(nèi)核態(tài)就是一個(gè)高級(jí)管理員,它可以控制整個(gè)資源的權(quán)限,用戶態(tài)就是一個(gè)業(yè)務(wù),每個(gè)人都可以使用它。那計(jì)算機(jī)為啥要這么分呢?且看下文......

由于需要限制不同的程序之間的訪問(wèn)能力, 防止他們獲取別的程序的內(nèi)存數(shù)據(jù), 或者獲取外圍設(shè)備的數(shù)據(jù), 并發(fā)送到網(wǎng)絡(luò)。CPU劃分出兩個(gè)權(quán)限等級(jí):用戶態(tài)和內(nèi)核態(tài)。

32 位操作系統(tǒng)和 64 位操作系統(tǒng)的虛擬地址空間大小是不同的,在 Linux 操作系統(tǒng)中,虛擬地址空間的內(nèi)部又被分為內(nèi)核空間和用戶空間兩部分,如下所示:

通過(guò)這里可以看出:

32 位系統(tǒng)的內(nèi)核空間占用 1G,位于最高處,剩下的 3G 是用戶空間;

64 位系統(tǒng)的內(nèi)核空間和用戶空間都是 128T,分別占據(jù)整個(gè)內(nèi)存空間的最高和最低處,剩下的中間部分是未定義的。

內(nèi)核態(tài)控制的是內(nèi)核空間的資源管理,用戶態(tài)訪問(wèn)的是用戶空間內(nèi)的資源。

從用戶態(tài)到內(nèi)核態(tài)切換可以通過(guò)三種方式:

系統(tǒng)調(diào)用,其實(shí)系統(tǒng)調(diào)用本身就是中斷,但是軟件中斷,跟硬中斷不同。

異常:如果當(dāng)前進(jìn)程運(yùn)行在用戶態(tài),如果這個(gè)時(shí)候發(fā)生了異常事件,就會(huì)觸發(fā)切換。例如:缺頁(yè)異常。

外設(shè)中斷:當(dāng)外設(shè)完成用戶的請(qǐng)求時(shí),會(huì)向CPU發(fā)送中斷信號(hào)。

內(nèi)核態(tài)和用戶態(tài)是怎么控制數(shù)據(jù)傳輸?shù)模?/h2>

舉個(gè)例子:當(dāng)計(jì)算機(jī)A上a進(jìn)程要把一個(gè)文件傳送到計(jì)算機(jī)B上的b進(jìn)程空間里面去,它是怎么做的呢?在當(dāng)前的計(jì)算機(jī)系統(tǒng)架構(gòu)下,它的I/O路徑如下圖所示:

計(jì)算機(jī)A的進(jìn)程a先要通過(guò)系統(tǒng)調(diào)用Read(內(nèi)核態(tài))打開一個(gè)磁盤上的文件,這個(gè)時(shí)候就要把數(shù)據(jù)copy一次到內(nèi)核態(tài)的PageCache中,進(jìn)入了內(nèi)核態(tài);

進(jìn)程a負(fù)責(zé)將數(shù)據(jù)從內(nèi)核空間的 Page Cache 搬運(yùn)到用戶空間的緩沖區(qū),進(jìn)入用戶態(tài);

進(jìn)程a負(fù)責(zé)將數(shù)據(jù)從用戶空間的緩沖區(qū)搬運(yùn)到內(nèi)核空間的 Socket(資源由內(nèi)核管控) 緩沖區(qū)中,進(jìn)入內(nèi)核態(tài)。

進(jìn)程a負(fù)責(zé)將數(shù)據(jù)從內(nèi)核空間的 Socket 緩沖區(qū)搬運(yùn)到的網(wǎng)絡(luò)中,進(jìn)入用戶態(tài);

從以上4個(gè)步驟我們可以發(fā)現(xiàn),正是因?yàn)橛脩魬B(tài)沒(méi)法控制磁盤和網(wǎng)絡(luò)資源,所以需要來(lái)回的在內(nèi)核態(tài)切換。這樣一個(gè)發(fā)送文件的過(guò)程就產(chǎn)生了4 次上下文切換:

read 系統(tǒng)調(diào)用讀磁盤上的文件時(shí):用戶態(tài)切換到內(nèi)核態(tài);

read 系統(tǒng)調(diào)用完畢:內(nèi)核態(tài)切換回用戶態(tài);

write 系統(tǒng)調(diào)用寫到socket時(shí):用戶態(tài)切換到內(nèi)核態(tài);

write 系統(tǒng)調(diào)用完畢:內(nèi)核態(tài)切換回用戶態(tài)。

如此笨拙的設(shè)計(jì),我們覺(jué)得計(jì)算機(jī)是不是太幼稚了,為啥要來(lái)回切換不能直接在用戶態(tài)做數(shù)據(jù)傳輸嗎?

CPU 全程負(fù)責(zé)內(nèi)存內(nèi)的數(shù)據(jù)拷貝,參考磁盤介質(zhì)的讀寫性能,這個(gè)操作是可以接受的,但是如果要讓內(nèi)存的數(shù)據(jù)和磁盤來(lái)回拷貝,這個(gè)時(shí)間消耗就非常的難看,因?yàn)榇疟P、網(wǎng)卡的速度遠(yuǎn)小于內(nèi)存,內(nèi)存又遠(yuǎn)遠(yuǎn)小于 CPU;

4 次 copy + 4 次上下文切換,代價(jià)太高。

所以計(jì)算機(jī)體系結(jié)構(gòu)的大佬們就想到了能不能單獨(dú)地做一個(gè)模塊來(lái)專職負(fù)責(zé)這個(gè)數(shù)據(jù)的傳輸,不因?yàn)檎加胏pu而降低系統(tǒng)的吞吐呢?方案就是引入了DMA(Direct memory access)

什么是 DMA ?

沒(méi)有 DMA ,計(jì)算機(jī)程序訪問(wèn)磁盤上的數(shù)據(jù)I/O 的過(guò)程是這樣的:

CPU 先發(fā)出讀指令給磁盤控制器(發(fā)出一個(gè)系統(tǒng)調(diào)用),然后返回;

磁盤控制器接受到指令,開始準(zhǔn)備數(shù)據(jù),把數(shù)據(jù)拷貝到磁盤控制器的內(nèi)部緩沖區(qū)中,然后產(chǎn)生一個(gè)中斷;

CPU 收到中斷信號(hào)后,讓出CPU資源,把磁盤控制器的緩沖區(qū)的數(shù)據(jù)一次一個(gè)字節(jié)地拷貝進(jìn)自己的寄存器,然后再把寄存器里的數(shù)據(jù)拷貝到內(nèi)存,而在數(shù)據(jù)傳輸?shù)钠陂g CPU 是無(wú)法執(zhí)行其他任務(wù)的。

可以看到,整個(gè)數(shù)據(jù)的傳輸有幾個(gè)問(wèn)題:一是數(shù)據(jù)在不同的介質(zhì)之間被拷貝了多次;二是每個(gè)過(guò)程都要需要 CPU 親自參與(搬運(yùn)數(shù)據(jù)的過(guò)程),在這個(gè)過(guò)程,在數(shù)據(jù)拷貝沒(méi)有完成前,CPU 是不能做額外事情的,被IO獨(dú)占。

如果I/O操作能比較快的完成,比如簡(jiǎn)單的字符數(shù)據(jù),那沒(méi)問(wèn)題。如果我們用萬(wàn)兆網(wǎng)卡或者硬盤傳輸大量數(shù)據(jù),CPU就會(huì)一直被占用,其他服務(wù)無(wú)法使用,對(duì)單核系統(tǒng)是致命的。

為了解決上面的CPU被持續(xù)占用的問(wèn)題,大佬們就提出了 DMA 技術(shù),即直接內(nèi)存訪問(wèn)(Direct Memory Access) 技術(shù)。

那到底什么是 DMA 技術(shù)?

所謂的 DMA(Direct Memory Access,即直接存儲(chǔ)器訪問(wèn))其實(shí)是一個(gè)硬件技術(shù),其主要目的是減少大數(shù)據(jù)量傳輸時(shí)的 CPU 消耗,從而提高 CPU 利用效率。其本質(zhì)上是一個(gè)主板和 IO 設(shè)備上的 DMAC 芯片。CPU 通過(guò)調(diào)度 DMAC 可以不參與磁盤緩沖區(qū)到內(nèi)核緩沖區(qū)的數(shù)據(jù)傳輸消耗,從而提高效率。

那有了DMA,數(shù)據(jù)讀取過(guò)程是怎么樣的呢?下面我們來(lái)具體看看。

詳細(xì)過(guò)程:

用戶進(jìn)程a調(diào)用系統(tǒng)調(diào)用read 方法,向OS內(nèi)核(資源總管)發(fā)出 I/O 請(qǐng)求,請(qǐng)求讀取數(shù)據(jù)到自己的內(nèi)存緩沖區(qū)中,進(jìn)程進(jìn)入阻塞狀態(tài);

OS內(nèi)核收到請(qǐng)求后,進(jìn)一步將 I/O 請(qǐng)求發(fā)送 DMA,然后讓 CPU 執(zhí)行其他任務(wù);

DMA 再將 I/O 請(qǐng)求發(fā)送給磁盤控制器;

磁盤控制器收到 DMA 的 I/O 請(qǐng)求,把數(shù)據(jù)從磁盤拷貝到磁盤控制器的緩沖區(qū)中,當(dāng)磁盤控制器的緩沖區(qū)被寫滿后,它向 DMA 發(fā)起中斷信號(hào),告知自己緩沖區(qū)已滿;

DMA 收到磁盤的中斷信號(hào)后,將磁盤控制器緩沖區(qū)中的數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)中,此時(shí)不占用 CPU,CPU 可以執(zhí)行其他任務(wù);

當(dāng) DMA 讀取了一個(gè)固定buffer的數(shù)據(jù),就會(huì)發(fā)送中斷信號(hào)給 CPU;

CPU 收到 DMA 的信號(hào),知道數(shù)據(jù)已經(jīng)Ready,于是將數(shù)據(jù)從內(nèi)核拷貝到用戶空間,結(jié)束系統(tǒng)調(diào)用;

DMA技術(shù)就是釋放了CPU的占用時(shí)間,它只做事件通知,數(shù)據(jù)拷貝完全由DMA完成。雖然DMA優(yōu)化了CPU的利用率,但是并沒(méi)有提高數(shù)據(jù)讀取的性能。為了減少數(shù)據(jù)在2種狀態(tài)之間的切換次數(shù),因?yàn)闋顟B(tài)切換是一個(gè)非常、非常、非常繁重的工作。為此,大佬們就提了零拷貝技術(shù)。

零拷貝技術(shù)實(shí)現(xiàn)的方式

常見的有2種,而今引入持久化內(nèi)存后,還有APP直接訪問(wèn)內(nèi)存數(shù)據(jù)的方式,這里先不展開。下面介紹常用的2種方案,它們的目的減少“上下文切換”和“數(shù)據(jù)拷貝”的次數(shù)。

mmap + write(系統(tǒng)調(diào)用)

sendfile

mmap + write

主要目的,減少數(shù)據(jù)的拷貝

read() 系統(tǒng)調(diào)用:把內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到用戶的緩沖區(qū)里,用 mmap() 替換 read() ,mmap() 直接把內(nèi)核緩沖區(qū)里的數(shù)據(jù)映射到用戶空間,減少這一次拷貝。

buf = mmap(file, len);
write(sockfd, buf, len);

具體過(guò)程如下:

應(yīng)用進(jìn)程調(diào)用了 mmap() 后,DMA 會(huì)把磁盤的數(shù)據(jù)拷貝到內(nèi)核的緩沖區(qū)里。因?yàn)榻⒘诉@個(gè)內(nèi)存的mapping,所以用戶態(tài)的數(shù)據(jù)可以直接訪問(wèn)了;

應(yīng)用進(jìn)程再調(diào)用 write(),CPU將內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到 socket 緩沖區(qū)中,這一切都發(fā)生在內(nèi)核態(tài)

DMA把內(nèi)核的 socket 緩沖區(qū)里的數(shù)據(jù),拷貝到網(wǎng)卡的緩沖區(qū)里

由上可知,系統(tǒng)調(diào)用mmap() 來(lái)代替 read(), 可以減少一次數(shù)據(jù)拷貝。那我們是否還有優(yōu)化的空間呢?畢竟用戶態(tài)和內(nèi)核態(tài)仍然需要 4 次上下文切換,系統(tǒng)調(diào)用還是 2 次。那繼續(xù)研究下是否還能繼續(xù)減少切換和數(shù)據(jù)拷貝呢?答案是確定的:可以

sendfile

Linux 內(nèi)核版本 2.1 提供了一個(gè)專門發(fā)送文件的系統(tǒng)調(diào)用函數(shù) sendfile(),函數(shù)形式如下:

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

參數(shù)說(shuō)明:

前2個(gè)參數(shù)分別是目的端和源端的文件描述符,

后2個(gè)參數(shù)是源端的偏移量和復(fù)制數(shù)據(jù)的長(zhǎng)度,返回值是實(shí)際復(fù)制數(shù)據(jù)的長(zhǎng)度。

首先,使用sendfile()可以替代前面的 read() 和 write() 這兩個(gè)系統(tǒng)調(diào)用,減少一次系統(tǒng)調(diào)用和 2 次上下文切換。

其次,sendfile可以直接把內(nèi)核緩沖區(qū)里的數(shù)據(jù)拷貝到 socket 緩沖區(qū)里,不再拷貝到用戶態(tài),優(yōu)化后只有 2 次上下文切換,和 3 次數(shù)據(jù)拷貝。如下圖:

盡管如此,我們還是又?jǐn)?shù)據(jù)拷貝,這不符合我們的標(biāo)題目標(biāo)。如果網(wǎng)卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技術(shù),我們就可以進(jìn)一步減少通過(guò) CPU 把內(nèi)核緩沖區(qū)里的數(shù)據(jù)拷貝到 socket 緩沖區(qū)的過(guò)程。

我們可以在 Linux 系統(tǒng)下通過(guò)下面的命令,查看網(wǎng)卡是否支持 scatter-gather 特性:

$ ethtool -k eth0 | grep scatter-gather
scatter-gather: on

于是,從 Linux 內(nèi)核 2.4 版本開始起,對(duì)于支持網(wǎng)卡支持 SG-DMA 技術(shù)的情況下, sendfile() 系統(tǒng)調(diào)用的過(guò)程發(fā)生了點(diǎn)變化,具體過(guò)程如下:

通過(guò) DMA 將磁盤上的數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)里;

緩沖區(qū)描述符和數(shù)據(jù)長(zhǎng)度傳到 socket 緩沖區(qū),這樣網(wǎng)卡的 SG-DMA 控制器就可以直接將內(nèi)核緩存中的數(shù)據(jù)拷貝到網(wǎng)卡的緩沖區(qū)里;

在這個(gè)過(guò)程之中,實(shí)際上只進(jìn)行了 2 次數(shù)據(jù)拷貝,如下圖:

這就是零拷貝(Zero-copy)技術(shù),因?yàn)槲覀儧](méi)有在內(nèi)存層面去拷貝數(shù)據(jù),也就是說(shuō)全程沒(méi)有通過(guò) CPU 來(lái)搬運(yùn)數(shù)據(jù),所有的數(shù)據(jù)都是通過(guò) DMA 來(lái)進(jìn)行傳輸?shù)摹?/p>

零拷貝技術(shù)的文件傳輸方式相比傳統(tǒng)文件傳輸?shù)姆绞?,只需?2 次上下文切換和數(shù)據(jù)拷貝次數(shù),就可以完成文件的傳輸,而且 2 次的數(shù)據(jù)拷貝過(guò)程,都不需要通過(guò) CPU,2 次都是由 DMA 來(lái)搬運(yùn)。

所以,零拷貝技術(shù)可以把文件傳輸?shù)男阅芴岣咧辽僖槐丁?/p>

為啥要聊PageCache?

回顧第一節(jié)的存儲(chǔ)介質(zhì)的性能,如果我們總是在磁盤和內(nèi)存間傳輸數(shù)據(jù),一個(gè)大文件的跨機(jī)器傳輸肯定會(huì)讓你抓狂。那有什么方法加速呢?直觀的想法就是建立一個(gè)離CPU近的一個(gè)臨時(shí)通道,這樣就可以加速文件的傳輸。這個(gè)通道就是我們前文提到的「內(nèi)核緩沖區(qū)」,這個(gè)「內(nèi)核緩沖區(qū)」實(shí)際上是磁盤高速緩存(PageCache)。

零拷貝就是使用了DMA + PageCache 技術(shù)提升了性能,我們來(lái)看看 PageCache 是如何做到的。

從開篇的介質(zhì)性能看,磁盤相比內(nèi)存讀寫的速度要慢很多,所以優(yōu)化的思路就是盡量的把「讀寫磁盤」替換成「讀寫內(nèi)存」。因此通過(guò) DMA 把磁盤里的數(shù)據(jù)搬運(yùn)到內(nèi)存里,轉(zhuǎn)為直接讀內(nèi)存,這樣就快多了。但是內(nèi)存的空間是有限的,成本也比磁盤貴,它只能拷貝磁盤里的一小部分?jǐn)?shù)據(jù)。

那就不可避免的產(chǎn)生一個(gè)問(wèn)題,到底選擇哪些磁盤數(shù)據(jù)拷貝到內(nèi)存呢?

從業(yè)務(wù)的視角來(lái)看,業(yè)務(wù)的數(shù)據(jù)有冷熱之分,我們通過(guò)一些的淘汰算法可以知道哪些是熱數(shù)據(jù),因?yàn)閿?shù)據(jù)訪問(wèn)的時(shí)序性,被訪問(wèn)過(guò)的數(shù)據(jù)可能被再次訪問(wèn)的概率很高,于是我們可以用 PageCache 來(lái)緩存最近被訪問(wèn)的數(shù)據(jù),當(dāng)空間不足時(shí)淘汰最久未被訪問(wèn)的數(shù)據(jù)。

讀Cache

當(dāng)內(nèi)核發(fā)起一個(gè)讀請(qǐng)求時(shí)(例如進(jìn)程發(fā)起read()請(qǐng)求),首先會(huì)檢查請(qǐng)求的數(shù)據(jù)是否緩存到了Page Cache中。如果有,那么直接從內(nèi)存中讀取,不需要訪問(wèn)磁盤,這被稱為cache命中(cache hit);如果cache中沒(méi)有請(qǐng)求的數(shù)據(jù),即cache未命中(cache miss),就必須從磁盤中讀取數(shù)據(jù)。然后內(nèi)核將讀取的數(shù)據(jù)緩存到cache中,這樣后續(xù)的讀請(qǐng)求就可以命中cache了。

page可以只緩存一個(gè)文件部分的內(nèi)容,不需要把整個(gè)文件都緩存進(jìn)來(lái)。

寫Cache

當(dāng)內(nèi)核發(fā)起一個(gè)寫請(qǐng)求時(shí)(例如進(jìn)程發(fā)起write()請(qǐng)求),同樣是直接往cache中寫入,后備存儲(chǔ)中的內(nèi)容不會(huì)直接更新(當(dāng)服務(wù)器出現(xiàn)斷電關(guān)機(jī)時(shí),存在數(shù)據(jù)丟失風(fēng)險(xiǎn))。

內(nèi)核會(huì)將被寫入的page標(biāo)記為dirty,并將其加入dirty list中。內(nèi)核會(huì)周期性地將dirty list中的page寫回到磁盤上,從而使磁盤上的數(shù)據(jù)和內(nèi)存中緩存的數(shù)據(jù)一致。

當(dāng)滿足以下兩個(gè)條件之一將觸發(fā)臟數(shù)據(jù)刷新到磁盤操作:

數(shù)據(jù)存在的時(shí)間超過(guò)了dirty_expire_centisecs(默認(rèn)300厘秒,即30秒)時(shí)間;

臟數(shù)據(jù)所占內(nèi)存 > dirty_background_ratio,也就是說(shuō)當(dāng)臟數(shù)據(jù)所占用的內(nèi)存占總內(nèi)存的比例超過(guò)dirty_background_ratio(默認(rèn)10,即系統(tǒng)內(nèi)存的10%)的時(shí)候會(huì)觸發(fā)pdflush刷新臟數(shù)據(jù)。

還有一點(diǎn),現(xiàn)在的磁盤是擦除式讀寫,每次需要讀一個(gè)固定的大小,隨機(jī)讀取帶來(lái)的磁頭尋址會(huì)增加時(shí)延,為了降低它的影響,PageCache 使用了「預(yù)讀功能」。

在某些應(yīng)用場(chǎng)景下,比如我們每次打開文件只需要讀取或者寫入幾個(gè)字節(jié)的情況,會(huì)比Direct I/O多一些磁盤的讀取于寫入。

舉個(gè)例子,假設(shè)每次我們要讀 32 KB 的字節(jié),read填充到用戶buffer的大小是0~32KB,但內(nèi)核會(huì)把其后面的 32~64 KB 也讀取到 PageCache,這樣后面讀取 32~64 KB 的成本就很低,如果在 32~64 KB 淘汰出 PageCache 前,進(jìn)程需要讀這些數(shù)據(jù),對(duì)比分塊讀取的方式,這個(gè)策略收益就非常大。

Page Cache的優(yōu)勢(shì)與劣勢(shì)

優(yōu)勢(shì)

加快對(duì)數(shù)據(jù)的訪問(wèn)

減少磁盤I/O的訪問(wèn)次數(shù),提高系統(tǒng)磁盤壽命

減少對(duì)磁盤I/O的訪問(wèn),提高系統(tǒng)磁盤I/O吞吐量(Page Cache的預(yù)讀機(jī)制)

劣勢(shì)

使用額外的物理內(nèi)存空間,當(dāng)物理內(nèi)存比較緊俏的時(shí)候,可能會(huì)導(dǎo)致頻繁的swap操作,最終會(huì)導(dǎo)致系統(tǒng)的磁盤I/O負(fù)載上升。

Page Cache沒(méi)有給應(yīng)用層提供一個(gè)很好的API。導(dǎo)致應(yīng)用層想要優(yōu)化Page Cache的使用策略很難。因此一些應(yīng)用實(shí)現(xiàn)了自己的Page管理,比如MySQL的InnoDB存儲(chǔ)引擎以16KB的頁(yè)進(jìn)行管理。

另外,由于文件太大,可能某些部分的文件數(shù)據(jù)已經(jīng)被淘汰出去了,這樣就會(huì)帶來(lái) 2 個(gè)問(wèn)題:

PageCache 由于長(zhǎng)時(shí)間被大文件的部分塊占據(jù),而導(dǎo)致一些「熱點(diǎn)」的小文件可能就無(wú)法常駐 PageCache,導(dǎo)致頻繁讀寫磁盤而引起性能下降;

PageCache 中的大文件數(shù)據(jù),由于沒(méi)有全部常駐內(nèi)存,只有部分無(wú)法享受到緩存帶來(lái)的好處,同時(shí)過(guò)多的DMA 拷貝動(dòng)作,增加了時(shí)延;

因此針對(duì)大文件的傳輸,不應(yīng)該使用 PageCache。

Page Cache緩存查看工具:cachestat

PageCache的參數(shù)調(diào)優(yōu)

備注:不同硬件配置的服務(wù)器可能效果不同,所以,具體的參數(shù)值設(shè)置需要考慮自己集群硬件配置。

考慮的因素主要包括:CPU核數(shù)、內(nèi)存大小、硬盤類型、網(wǎng)絡(luò)帶寬等。

查看Page Cache參數(shù):sysctl -a|grep dirty

調(diào)整內(nèi)核參數(shù)來(lái)優(yōu)化IO性能?

vm.dirty_background_ratio參數(shù)優(yōu)化:當(dāng)cached中緩存當(dāng)數(shù)據(jù)占總內(nèi)存的比例達(dá)到這個(gè)參數(shù)設(shè)定的值時(shí)將觸發(fā)刷磁盤操作。把這個(gè)參數(shù)適當(dāng)調(diào)小,這樣可以把原來(lái)一個(gè)大的IO刷盤操作變?yōu)槎鄠€(gè)小的IO刷盤操作,從而把IO寫峰值削平。對(duì)于內(nèi)存很大和磁盤性能比較差的服務(wù)器,應(yīng)該把這個(gè)值設(shè)置的小一點(diǎn)。

vm.dirty_ratio參數(shù)優(yōu)化:對(duì)于寫壓力特別大的,建議把這個(gè)參數(shù)適當(dāng)調(diào)大;對(duì)于寫壓力小的可以適當(dāng)調(diào)小;如果cached的數(shù)據(jù)所占比例(這里是占總內(nèi)存的比例)超過(guò)這個(gè)設(shè)置,系統(tǒng)會(huì)停止所有的應(yīng)用層的IO寫操作,等待刷完數(shù)據(jù)后恢復(fù)IO。所以萬(wàn)一觸發(fā)了系統(tǒng)的這個(gè)操作,對(duì)于用戶來(lái)說(shuō)影響非常大的。

vm.dirty_expire_centisecs參數(shù)優(yōu)化:這個(gè)參數(shù)會(huì)和參數(shù)vm.dirty_background_ratio一起來(lái)作用,一個(gè)表示大小比例,一個(gè)表示時(shí)間;即滿足其中任何一個(gè)的條件都達(dá)到刷盤的條件。

vm.dirty_writeback_centisecs參數(shù)優(yōu)化:理論上調(diào)小這個(gè)參數(shù),可以提高刷磁盤的頻率,從而盡快把臟數(shù)據(jù)刷新到磁盤上。但一定要保證間隔時(shí)間內(nèi)一定可以讓數(shù)據(jù)刷盤完成。

vm.swappiness參數(shù)優(yōu)化:禁用swap空間,設(shè)置vm.swappiness=0

大文件傳輸怎么做?

我們先來(lái)回顧下前文的讀流程,當(dāng)調(diào)用 read 方法讀取文件時(shí),如果數(shù)據(jù)沒(méi)有準(zhǔn)備好,進(jìn)程會(huì)阻塞在 read 方法調(diào)用,要等待磁盤數(shù)據(jù)的返回,如下圖:

具體過(guò)程:

當(dāng)調(diào)用 read 方法時(shí),切到內(nèi)核態(tài)訪問(wèn)磁盤資源。此時(shí)內(nèi)核會(huì)向磁盤發(fā)起 I/O 請(qǐng)求,磁盤收到請(qǐng)求后,準(zhǔn)備數(shù)據(jù)。數(shù)據(jù)讀取到控制器緩沖區(qū)完成后,就會(huì)向內(nèi)核發(fā)起 I/O 中斷,通知內(nèi)核磁盤數(shù)據(jù)已經(jīng)準(zhǔn)備好;

內(nèi)核收到 I/O 中斷后,將數(shù)據(jù)從磁盤控制器緩沖區(qū)拷貝到 PageCache 里;

內(nèi)核把 PageCache 中的數(shù)據(jù)拷貝到用戶緩沖區(qū),read 調(diào)用返回成功。

對(duì)于大塊數(shù)傳輸導(dǎo)致的阻塞,可以用異步 I/O 來(lái)解決,如下圖:

分為兩步執(zhí)行:

內(nèi)核向磁盤發(fā)起讀請(qǐng)求,因?yàn)槭钱惒秸?qǐng)求可以不等待數(shù)據(jù)就位就可以返回,于是CPU釋放出來(lái)可以處理其他任務(wù);

當(dāng)內(nèi)核將磁盤中的數(shù)據(jù)拷貝到進(jìn)程緩沖區(qū)后,進(jìn)程將接收到內(nèi)核的通知,再去處理數(shù)據(jù);

從上面流程來(lái)看,異步 I/O 并沒(méi)有讀寫 PageCache,繞開 PageCache 的 I/O 叫直接 I/O,使用 PageCache 的 I/O 則叫緩存 I/O。通常,對(duì)于磁盤異步 I/O 只支持直接 I/O。

因此,在高并發(fā)的場(chǎng)景下,針對(duì)大文件的傳輸?shù)姆绞?,?yīng)該使用「異步 I/O + 直接 I/O」來(lái)替代零拷貝技術(shù)。

直接 I/O 的兩種場(chǎng)景:

應(yīng)用程序已經(jīng)實(shí)現(xiàn)了磁盤數(shù)據(jù)的緩存

大文件傳輸

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號(hào)『一口Linux』號(hào)主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗(yàn)和培訓(xùn)經(jīng)驗(yàn)。曾任職ZTE,某研究所,華清遠(yuǎn)見教學(xué)總監(jiān)。擁有多篇網(wǎng)絡(luò)協(xié)議相關(guān)專利和軟件著作。精通計(jì)算機(jī)網(wǎng)絡(luò)、Linux系統(tǒng)編程、ARM、Linux驅(qū)動(dòng)、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實(shí)際項(xiàng)目出發(fā),保持原理+實(shí)踐風(fēng)格,適合Linux驅(qū)動(dòng)新手入門和技術(shù)進(jìn)階。