自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

OS近距離:mmap給你想要的快!

存儲(chǔ) 存儲(chǔ)設(shè)備
今天,我們就來簡單聊一下I/O。當(dāng)然,更主要的還是傾向于和持久化打交道的磁盤I/O。如非特指,我們說的就都是它。

I/O問題一般不會(huì)被大多數(shù)人關(guān)注,因?yàn)榇蠖鄶?shù)開發(fā)都是在做“業(yè)務(wù)”,也就是在搞計(jì)算節(jié)點(diǎn)的事情,通常遇到的I/O問題,也就是日志打的有點(diǎn)多了,磁盤寫起來有點(diǎn)吃力,所以iowait這個(gè)指標(biāo),關(guān)注的人也不多。

可惜的是,工作并不是只考慮怎么折騰CPU,數(shù)據(jù)總歸要落地的。一旦涉及到高性能的磁盤存儲(chǔ),I/O問題就浮上水面。Redis這么流行,就是為了繞開磁盤性能問題而存在的。

換句話說,如果我的磁盤像內(nèi)存一樣快,那還要內(nèi)存干什么~

今天,我們就來簡單聊一下I/O。當(dāng)然,更主要的還是傾向于和持久化打交道的磁盤I/O。如非特指,我們說的就都是它。

I/O都干了些啥?

I/O干的是啥?對我們使用者來說,簡單來講,就兩點(diǎn)。

  • 和操作系統(tǒng)索要數(shù)據(jù),并加載到緩沖區(qū)中。
  • 填滿用戶進(jìn)程緩沖區(qū),并交給操作系統(tǒng)刷盤。

但也不是直接讀寫,因?yàn)椴僮飨到y(tǒng)有內(nèi)核進(jìn)程和用戶進(jìn)程之分。為了保護(hù)內(nèi)核的內(nèi)存,用戶進(jìn)程是不能隨便去讀內(nèi)核所操作的數(shù)據(jù)的。想要數(shù)據(jù),拷貝一份。

這一進(jìn)一出,就涉及到用戶態(tài)和內(nèi)核態(tài)的切換,也就是常說的系統(tǒng)調(diào)用,細(xì)節(jié)上肯定會(huì)比較復(fù)雜。

如上圖,就拿讀數(shù)據(jù)來說,我們可以把讀取過程分為以下幾個(gè)階段。

  • Java進(jìn)程發(fā)起讀取請求,調(diào)用最底層代碼發(fā)起read()系統(tǒng)調(diào)用。
  • 操作系統(tǒng)通過DMA等從磁盤等硬件讀取數(shù)據(jù)。
  • DMA讀取相關(guān)數(shù)據(jù),存入到內(nèi)核的緩沖區(qū)中。這部分操作是不需要CPU參與的。
  • 接下來內(nèi)核將會(huì)把自己緩沖區(qū)的內(nèi)容,拷貝到Java進(jìn)程的緩沖區(qū)中。

可以看到,由于一個(gè)讀取有操作系統(tǒng)的參與,它的交互過程就變的比較復(fù)雜。典型的,當(dāng)Java進(jìn)程讀取數(shù)據(jù)的時(shí)候,內(nèi)核發(fā)現(xiàn)這部分?jǐn)?shù)據(jù)已經(jīng)存在于緩存中了,那么就直接拷貝出來;當(dāng)內(nèi)核緩存不存在這些數(shù)據(jù)的時(shí)候,那么Java進(jìn)程將會(huì)阻塞在那里,直到所需的數(shù)據(jù)拷貝到用戶空間。

總結(jié)一下:內(nèi)核進(jìn)程所持有的內(nèi)存,是不能直接訪問的,我們需要拷貝一份到用戶進(jìn)程。

虛擬地址來幫忙

從上面的描述中可以看出,磁盤文件上的內(nèi)容,要想被用戶進(jìn)程所使用,就不得不經(jīng)過kernel這個(gè)中轉(zhuǎn)站。既然這樣會(huì)影響效率,那么為什么不直接把這些磁盤上的文件直接發(fā)送到用戶進(jìn)程呢?

這不是能不能做的問題,而是應(yīng)不應(yīng)該做的問題。既然用戶進(jìn)程使用了特定的操作系統(tǒng),就要按照操作系統(tǒng)的規(guī)矩辦事。在Linux操作系統(tǒng)上,把這些繁雜的事務(wù)交給操作系統(tǒng),是最安全、最便捷的編程方式。

那么,我現(xiàn)在就是不想按照規(guī)矩來,把效率看的更重一些,怎么辦?

沒別的辦法,只有開啟一條綠色通道。

如果我能夠在用戶進(jìn)程里,和操作系統(tǒng)內(nèi)核里,讀到的是同一份數(shù)據(jù),操作的是同一份緩沖區(qū),那么目的就算達(dá)到了。

如果讓用戶進(jìn)程直接去訪問內(nèi)核所擁有的物理內(nèi)存地址,是非常危險(xiǎn)的。如何共享這些物理內(nèi)存,這需要借助 虛擬內(nèi)存。這就是所謂的綠色通道。

虛擬內(nèi)存,肯定是相對于物理內(nèi)存來說的。如果你反編譯一個(gè)二進(jìn)制文件的話,可以看到它的引用地址是固定的。虛擬內(nèi)存區(qū)域是進(jìn)程的虛擬地址空間中的一個(gè)同質(zhì)區(qū)間,即具有同樣特性的連續(xù)地址范圍。如下圖,MMU組件就專門負(fù)責(zé)虛擬內(nèi)存到物理內(nèi)存到翻譯,這都是《計(jì)算機(jī)組成結(jié)構(gòu)》的東西,已經(jīng)是現(xiàn)代操作系統(tǒng)的默認(rèn)操作。

借助于虛擬內(nèi)存,我們就可以使用不同的虛擬內(nèi)存地址指向同一塊物理內(nèi)存地址,變相的實(shí)現(xiàn)了內(nèi)存數(shù)據(jù)的共享,避免了kernel和user進(jìn)程之間的數(shù)據(jù)拷貝。

如果我們同時(shí)mapping一個(gè)內(nèi)核空間的虛擬地址和用戶空間的虛擬地址,到同一塊內(nèi)核實(shí)際的物理內(nèi)存地址上,那么我們就能夠同時(shí)操作這一塊內(nèi)存區(qū)域。

典型應(yīng)用mmap

mmap (Memory Mapped Files) 就是這樣處理映射的一種特殊通道。它可以將一個(gè)文件,或者其他對象,映射到進(jìn)程的虛擬地址空間。

當(dāng)我們操作這一段內(nèi)存的時(shí)候,就可以直接影響到最終操作系統(tǒng)上的文件。雖然文件的讀寫仍然由操作系統(tǒng)去做,但明顯的,我們不必再調(diào)用read、write等函數(shù),從操作系統(tǒng)的內(nèi)存中拷貝數(shù)據(jù),這肯定能夠增加文件讀寫的效率。

MMAP也使得進(jìn)程間共享編程型內(nèi)存,進(jìn)程通信成為了可能,也可以和內(nèi)核進(jìn)程進(jìn)行協(xié)同式交互。當(dāng)我們的物理內(nèi)存空間不足的時(shí)候,甚至可以使用磁盤來模擬內(nèi)存。

這就是抽象的魔力。

mmap的映射區(qū)域必須時(shí)候 物理頁大小(page_size)的整數(shù)倍,這也是操作系統(tǒng)為了增加處理效率所采取的批量處理模式(內(nèi)存管理的最小粒度就是頁)。同時(shí),mmap不能映射超過文件大小的區(qū)域,所以當(dāng)文件大小發(fā)生變化時(shí),就需要重新映射。

Java中有專門處理mmap的類MappedByteBuffer,我們可以通過FileChannel的map函數(shù)來獲取這個(gè)變量。

MappedByteBuffer mb = new RandomAccessFile("test", "rw")
.getChannel()
.map(FileChannel.MapMode.READ_WRITE, 0, 256);

//...
public abstract MappedByteBuffer map(MapMode mode,
long position, long size)

可以看到,通過position和size兩個(gè)參數(shù),就可以直接將文件的內(nèi)容映射到mb變量中。假如我們不同的進(jìn)程做了同樣的映射,內(nèi)存的使用量也不會(huì)翻倍,因?yàn)樗鼈兌际翘摂M的地址。

假如你的內(nèi)存非常大40GB,但操作系統(tǒng)的內(nèi)存只有2GB,通過這種方式,依然能夠快速的讀取和修改文件。

在使用top命令的時(shí)候,我們經(jīng)??吹絪wap區(qū)域,也就是使用文件去模擬內(nèi)存的區(qū)域。當(dāng)你使用2GB內(nèi)存去操作40GB的文件時(shí),通常會(huì)引起swap out,內(nèi)存的數(shù)據(jù)要寫入到磁盤中。在mmap模式下,就不必再使用額外的swap去保證這個(gè)操作。當(dāng)需要swap的時(shí)候,操作系統(tǒng)會(huì)直接使用原始文件,這些映射也會(huì)在要操作的目標(biāo)文件上生效。

這整個(gè)過程中,除了操作系統(tǒng)缺頁引起文件讀寫,沒有其他任何的緩沖區(qū)參與,所以是非常高效的。

怎么用?

如果你仔細(xì)翻一下mmap相關(guān)的代碼,可以發(fā)現(xiàn)默認(rèn)提供的函數(shù)非常非常非常的稀少,要拿它來搞事情的話,使用起來各種限制。

所以我們需要配合索引文件,來配合mmap完成高效的操作。

在一些數(shù)據(jù)庫和中間件中,我們經(jīng)常看到mmap的身影,尤其是那些涉及到大文件讀寫的場景。

在kafka和rocketmq中,commitlog需要根據(jù)偏移量讀取數(shù)據(jù),mmap無疑是非常好的加速方式。就拿kafka的索引文件來說,就大量使用了mmap;消費(fèi)時(shí)Kafka 直接把文件發(fā)送給消費(fèi)者,配合mmap 作為文件讀寫方式,直接把它傳給 sendfile。

包括主流的ES,也大量使用了mmap。這是一種作弊的行為。

不要高興的太早。

經(jīng)過很多benchmark的測試,mmap在不同的Linux平臺(tái)上,并不總是有這么好的表現(xiàn)。當(dāng)文件大小不被內(nèi)存所容下的時(shí)候,頻繁的文件交換和缺頁依然會(huì)發(fā)生,這需要經(jīng)過實(shí)際驗(yàn)證才能確認(rèn)服務(wù)真正的表現(xiàn)。

所以,在內(nèi)嵌數(shù)據(jù)庫rocksdb中,mmap相關(guān)的優(yōu)化參數(shù)是默認(rèn)關(guān)閉的。mmap應(yīng)該作為一種魔法存在,而不能作為一種通用的優(yōu)化方法。

allow_mmap_reads=false
allow_mmap_writes=false

另外,mmap在作為寫入時(shí),也并不是十分可靠。因?yàn)閷懭氲絤map中的數(shù)據(jù),并沒有被真正的寫到硬盤,它需要操作系統(tǒng)在調(diào)用flush函數(shù)的時(shí)候才真正的刷到硬盤上。所以,作為數(shù)據(jù)恢復(fù)用的wal日志或者translog、redolog等,并沒有采用mmap這種方式。

mmap另外一個(gè)比較嚴(yán)重的問題,就是不可預(yù)料的I/O停頓。有了操作系統(tǒng)這一環(huán),加上應(yīng)用中的各種Buffer的參與,再加上預(yù)讀這種操作,應(yīng)用在操作文件的時(shí)候,會(huì)比較平滑。但一旦使用了mmap,你可能在不可預(yù)料的情況下被阻塞,或者被不合理的預(yù)讀干擾,發(fā)生頻繁的I/O。

End

性能優(yōu)化從來都是一把雙刃劍。這把劍,到底是能殺掉敵人,還是手殘傷了隊(duì)友,那就要看掌劍人的水平了。mmap也不例外,它有好處,也有缺點(diǎn),多一點(diǎn)敬畏,結(jié)論從實(shí)踐中來,才是正確的態(tài)度。

責(zé)任編輯:趙寧寧 來源: 小姐姐味道
相關(guān)推薦

2022-03-21 18:27:38

Linux計(jì)算機(jī)函數(shù)

2023-11-27 17:03:45

syncGo

2015-01-19 09:13:39

CloudStack云計(jì)算架構(gòu)虛擬機(jī)管理

2011-03-02 15:10:43

國產(chǎn)數(shù)據(jù)庫

2013-04-23 09:15:59

Windows 8.1

2010-10-07 20:57:37

2015-06-04 16:35:00

2011-05-06 15:28:00

SMB數(shù)據(jù)中心

2009-09-29 11:23:53

互聯(lián)網(wǎng)

2021-08-15 19:00:14

算法floydDijkstra

2013-07-16 14:36:43

天河2號(hào)超級(jí)計(jì)算機(jī)國防科技大學(xué)

2018-03-05 08:56:10

物聯(lián)網(wǎng)無線通信終端設(shè)備

2019-04-30 10:08:22

Windows 功能系統(tǒng)

2023-11-29 11:28:21

智能視覺

2017-09-19 12:08:13

Google

2015-07-03 10:04:03

2019-10-29 09:18:19

量子芯片網(wǎng)絡(luò)

2021-12-12 09:20:43

Windows 11任務(wù)欄微軟

2019-01-16 10:55:08

Python 開發(fā)編程語言

2016-09-29 10:27:42

天云軟件Docker容器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)