小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

零拷貝(zero copy)技術(shù)你真的懂嗎?什么時候需要用到內(nèi)存映射?

 西北望msm66g9f 2020-04-13

Linux系統(tǒng)內(nèi)存管理知識補充

Linux系統(tǒng)是虛擬內(nèi)存系統(tǒng),虛擬內(nèi)存并不是真正的物理內(nèi)存,而是虛擬的連續(xù)內(nèi)存地址空間。虛擬內(nèi)存又分為內(nèi)核空間和用戶空間,內(nèi)核空間是內(nèi)核程序運行的地方,用戶空間是用戶進程代碼運行的地方,只有內(nèi)核才能直接訪問物理內(nèi)存并為用戶空間映射物理內(nèi)存(MMU)。內(nèi)核會為每個進程分配獨立的連續(xù)的虛擬內(nèi)存空間,并且在需要的時候映射物理內(nèi)存,為了完成內(nèi)存映射,內(nèi)核為每個進程都維護了一張頁表,記錄虛擬地址與物理地址的映射關(guān)系,這個頁表就是存在于MMU中;用戶進程訪問內(nèi)存的時候,通過頁表把虛擬內(nèi)存地址轉(zhuǎn)換為物理內(nèi)存地址進而訪問數(shù)據(jù);其實對于用戶進程而言,虛擬內(nèi)存就是內(nèi)存一般的存在(當(dāng)作內(nèi)存看待就好)。這樣的設(shè)計可以把用戶程序和系統(tǒng)程序分開,互不影響;內(nèi)核可以對所有的用戶程序進行管理,比如限制內(nèi)存濫用等。

虛擬內(nèi)存的最小單位是頁,通常是4KB大小,所以虛擬內(nèi)存會有很多很多的頁組成,當(dāng)然也有大頁,顧名思義就是大的虛擬內(nèi)存空間,比如12KB,2MB。虛擬內(nèi)存和物理內(nèi)存的映射都是等空間的,映射的物理內(nèi)存是多大的,那么占用的虛擬內(nèi)存差不多也是多大,都是4KB的整數(shù)倍。比如映射了一個1KB的內(nèi)存空間,那么也是占用一頁4KB虛擬內(nèi)存。

用戶進程在處于用戶態(tài)時,只能訪問用戶空間;只有進入內(nèi)核態(tài)后,才可以訪問內(nèi)核空間。雖然每個進程的地址空間都包含了內(nèi)核空間,但這些內(nèi)核空間映射的物理內(nèi)存都是相同的,所以當(dāng)進程切換到內(nèi)核態(tài)后可以快速的訪問內(nèi)核空間數(shù)據(jù)。

內(nèi)核其實就是一段特殊的代碼程序,運行于內(nèi)核空間,控制著計算機的CPU、IO、內(nèi)存等,提供了一系列的系統(tǒng)接口供外部調(diào)用,通常叫做系統(tǒng)調(diào)用。只有線程或者進程處于內(nèi)核態(tài)的時候才能進行系統(tǒng)調(diào)用,如果處于用戶態(tài)的話,是需要轉(zhuǎn)換為內(nèi)核態(tài)才能訪問。其實就是權(quán)限不同,內(nèi)核態(tài)(Ring0)擁有比用戶態(tài)(Ring3)更高的權(quán)限,擁有著訪問系統(tǒng)硬件資源的權(quán)限。

一般用戶線程或者進程是不需要切換到內(nèi)核態(tài)運行的,除非:

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

2. 異常:如果當(dāng)前進程運行在用戶態(tài),如果這個時候發(fā)生了異常事件,就會觸發(fā)切換。

例如:缺頁異常。

3. 外設(shè)中斷:當(dāng)外設(shè)完成用戶的請求時,會向CPU發(fā)送中斷信號。

比如讀取硬盤數(shù)據(jù),除了IO屬于系統(tǒng)操作需要切換為內(nèi)核態(tài)來獲取權(quán)限的原因外還要一原因是:

為了減少磁盤的IO操作,為了提高性能而考慮的,因為我們的程序訪問一般都帶有局部性,也就是所謂的局部性原理,即我們訪問了文件的某一段數(shù)據(jù),那么接下去很可能還會訪問接下去的一段數(shù)據(jù),由于磁盤IO操作的速度比直接訪問內(nèi)存慢了好幾個數(shù)量級,所以O(shè)S根據(jù)局部性原理會在一次 read()系統(tǒng)調(diào)用過程中預(yù)讀更多的文件數(shù)據(jù)緩存在內(nèi)核IO緩沖區(qū)中,當(dāng)繼續(xù)訪問的文件數(shù)據(jù)在緩沖區(qū)中時便直接拷貝數(shù)據(jù)到進程私有空間,避免了再次的低效率磁盤IO操作。

傳統(tǒng)IO發(fā)送文件

1. 用戶程序調(diào)用read,進入內(nèi)核態(tài),上下文切換由用戶空間切換為內(nèi)核空間,由DMA(Direct Memory Access)加載文件數(shù)據(jù)到內(nèi)核空間。

2. CPU把數(shù)據(jù)從內(nèi)核空間復(fù)制到用戶空間,轉(zhuǎn)換為用戶態(tài),上下文由內(nèi)核空間切換為用戶空間。

3. 用戶程序調(diào)用write,再次進入內(nèi)核態(tài),CPU把數(shù)據(jù)從用戶空間復(fù)制到socket關(guān)聯(lián)的內(nèi)核空間。

4. 最后通過DMA 將內(nèi)核模式下的socket緩沖區(qū)中的數(shù)據(jù)復(fù)制到網(wǎng)卡設(shè)備中傳送,進而返回用戶空間進入用戶態(tài)。

sendfile零拷貝(<Linux 2.4)

1. 用戶程序調(diào)用read,進入內(nèi)核態(tài),上下文切換由用戶空間切換為內(nèi)核空間,由DMA(Direct Memory Access)加載文件數(shù)據(jù)到內(nèi)核空間,第一步和傳統(tǒng)IO相同。

2. 在內(nèi)核態(tài)下,CPU把數(shù)據(jù)從內(nèi)核空間復(fù)制到socket關(guān)聯(lián)的內(nèi)核空間。

3. 最后通過DMA 將內(nèi)核模式下的socket緩沖區(qū)中的數(shù)據(jù)復(fù)制到網(wǎng)卡設(shè)備中傳送,進而返回用戶空間進入用戶態(tài),最后一步也是和傳統(tǒng)IO相同。

與傳統(tǒng)IO相比,缺少了把數(shù)據(jù)從內(nèi)核空間復(fù)制到用戶空間,再由用戶空間復(fù)制到內(nèi)核空間,比原來缺少了一次CPU復(fù)制(復(fù)制3次,CPU參與復(fù)制一次),少了兩次上下文切換(兩次)。

**從內(nèi)核空間角度來看,其實已經(jīng)是“ZERO COPY”了,因為沒有往用戶空間復(fù)制的操作。**

sendfile零拷貝(>=Linux 2.4)

1. 用戶程序調(diào)用read,進入內(nèi)核態(tài),上下文切換由用戶空間切換為內(nèi)核空間,由DMA(Direct Memory Access)加載文件數(shù)據(jù)到內(nèi)核空間,第一步和傳統(tǒng)IO相同。

2. 在內(nèi)核態(tài)下,描述符(包含了數(shù)據(jù)的位置和長度等信息)追加到socket關(guān)聯(lián)的緩沖區(qū)中,并沒有進行數(shù)據(jù)的拷貝。

3. 最后DMA根據(jù)提供的位置和偏移量信息直接將內(nèi)核空間緩沖區(qū)中的數(shù)據(jù)拷貝到協(xié)議引擎上進而返回用戶空間進入用戶態(tài)。

**這次優(yōu)化點在于沒有CPU參與復(fù)制,兩次DMA數(shù)據(jù)復(fù)制,不過還是兩次上下文切換。**

# 通過mmap實現(xiàn)的零拷貝(常用來處理大文件)

當(dāng)進行mmap系統(tǒng)調(diào)用的時候,將文件的內(nèi)容的全部或一部分直接映射到進程的地址空間(虛擬內(nèi)存),映射完成后,進程可以像訪問普通內(nèi)存一樣做其他的操作,mmap并不分配物理地址空間,它只是占有進程的虛擬地址空間。

當(dāng)進程訪問內(nèi)核中的緩沖區(qū)時候,并沒有實際拷貝數(shù)據(jù),這時MMU在地址映射表中是無法找到與ptr相對應(yīng)的物理地址的,也就是MMU失敗,就會觸發(fā)缺頁中斷。內(nèi)核將文件的這一頁數(shù)據(jù)讀入到內(nèi)核高速緩沖區(qū)中,并更新用戶進程的頁表,使頁表指向內(nèi)核緩沖中的這一頁,實現(xiàn)了用戶空間和內(nèi)核空間數(shù)據(jù)的直接交換,可以看待為內(nèi)核空間和用戶空間共享的一段物理內(nèi)存。

Java調(diào)用零拷貝

FileInputStream input = new FileInputStream('1.txt');
FileChannel channel = input.getChannel();
FileOutputStream out = new FileOutputStream('2.txt');
channel.transferTo(0, channel.size(), out.getChannel());

上面這種方式其實調(diào)用的是Linux系統(tǒng)的sendfile系統(tǒng)指令,無論什么語言代碼實現(xiàn)的零拷貝其實調(diào)用的都是操作系統(tǒng)本身提供的系統(tǒng)指令,只是做了封裝而已。

		FileInputStream input = new FileInputStream('1.txt');
FileChannel channel = input.getChannel();
MappedByteBuffer mappedBuffer = channel.map(MapMode.READ_ONLY, 0, channel.size());
System.out.println(Charset.forName('utf-8').decode(mappedBuffer).toString());

上面這種方式其實調(diào)用的是Linux系統(tǒng)的mmap系統(tǒng)指令;在讀取大文件的時候用這種方法映射大文件的一部分到內(nèi)存空間,比較方便快捷。

//mmap寫數(shù)據(jù)
Instant now = Instant.now();
RandomAccessFile outFile = new RandomAccessFile('1.txt','rw');
FileChannel channel = outFile.getChannel();
long size = 1024*1024*60;
MappedByteBuffer mappedBuffer = channel.map(MapMode.READ_WRITE, 0, size);
for(int i=0;i<1000000;i++) {
mappedBuffer.put('11111111111111111111111111111111111111111111111111111111111\n'.getBytes());
}
System.out.println(ChronoUnit.MILLIS.between(now, Instant.now()));
//fileOutputStream寫數(shù)據(jù)
Instant nowStream = Instant.now();
FileOutputStream outStream = new FileOutputStream('2.txt');
for(int i=0;i<1000000;i++) {
outStream.write('11111111111111111111111111111111111111111111111111111111111\n'.getBytes());
}
System.out.println(ChronoUnit.MILLIS.between(nowStream, Instant.now()));
118
9130

通過上面的測試可以看出在頻繁的寫入文件操作上mmap占有很多大的優(yōu)勢,數(shù)量級的優(yōu)勢。但是把上例的循環(huán)次數(shù)改為50的話,mmap就不占優(yōu)勢了,因為在映射的時候需要新開辟內(nèi)存空間,這個耗時相對于極少量的寫操作而言顯得占比重就大了。

來源:

https://www.toutiao.com/i6810663802636337677/

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多