九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
零拷貝(zero copy)技術你真的懂嗎?什么時候需要用到內存映射?

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

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

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

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

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

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

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

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

例如:缺頁異常。

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

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

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

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

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

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

3. 用戶程序調用write,再次進入內核態(tài),CPU把數據從用戶空間復制到socket關聯的內核空間。

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

sendfile零拷貝(<Linux 2.4)

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

2. 在內核態(tài)下,CPU把數據從內核空間復制到socket關聯的內核空間。

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

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

**從內核空間角度來看,其實已經是“ZERO COPY”了,因為沒有往用戶空間復制的操作。**

sendfile零拷貝(>=Linux 2.4)

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

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

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

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

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

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

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

Java調用零拷貝

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

上面這種方式其實調用的是Linux系統(tǒng)的sendfile系統(tǒng)指令,無論什么語言代碼實現的零拷貝其實調用的都是操作系統(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());

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

//mmap寫數據
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寫數據
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)勢,數量級的優(yōu)勢。但是把上例的循環(huán)次數改為50的話,mmap就不占優(yōu)勢了,因為在映射的時候需要新開辟內存空間,這個耗時相對于極少量的寫操作而言顯得占比重就大了。

來源:

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

本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
阿里二面:什么是mmap?
Android Binder機制の設計與實現6
超硬核,基于mmap和零拷貝實現高效的內存共享
用戶空間和內核空間
mmap()
內核如何阻塞與喚醒進程?
更多類似文章 >>
生活服務
熱點新聞
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯系客服!

聯系客服