導航:首頁 > 操作系統 > linux內存拷貝

linux內存拷貝

發布時間:2023-07-11 02:53:03

1. linux 用內存隊列,多線程實現文件拷貝的效率問題

首先硬碟I/O是慢速的I/O。你開了4個線程,無非就是全部在等待。原因很簡單,你只有一個硬碟。而這個硬碟同一時間只能被一個線程使用。

2. Linux系統I/O操作與零拷貝

Linux中傳統的I/O操作是一種緩存I/O,I/O過程中產生的數據傳輸通常需要在緩沖區中進行多次拷貝。當應用程序需要訪問某個數據(read()操作)時,操作系統會先判斷這塊數據是否在內核緩沖區中,如果在內核緩沖區中找不到這塊數據,內核會先將這塊數據從磁碟中讀出來放到內核緩沖區中,應用程序再從緩沖區中讀取。當應用程序需要將數據輸出(write())時,同樣需要先將數據拷貝到輸出堆棧相關的內核緩沖區,再從內核緩沖區拷貝到輸出設備中。

以一次網路請求為例,如下圖。對於一次數據讀取,用戶應用程序只需要調用read()及write()兩個系統調用就可以完成一次數據傳輸,但這個過程中數據經過了四次拷貝,且數據拷貝需要由CPU來調控。在某些情況下,這些數據拷貝會極大地降低系統數據傳輸的性能,比如文件伺服器中,一個文件從磁碟讀取後不加修改地回傳給調用方,那麼這佔用CPU時間去處理這四次數據拷貝的性價比是極低的。

一次處理網路調用的系統I/O的流程:

以上可以發現,傳統的Linux系統I/O 操作要進行4次內核空間與應用程序空間的上下文切換,以及4次數據拷貝。

直接內存訪問(Direct Memory Access,DMA)是計算機科學中的一種內存訪問技術,允許某些電腦內部的硬體子系統獨立地讀取系統內存,而不需要中央處理器(CPU)的介入。在同等程度的處理器負擔下,DMA是一種快速的數據傳送方式。這類子系統包括硬碟控制器、顯卡、網卡和音效卡。

在Linux系統中,當應用程序需要讀取文件中的數據時,操作系統先分配一些內存,將數據從存儲設備讀入到這些內存中,然後再將數據傳遞應用進程;當需要往文件中寫數據時,操作系統先分配內存接收用戶數據,然後再將數據從內存寫入磁碟。文件cache管理就是對這些由操作系統分配並用開存儲文件數據的內存的管理。

在Linux系統中,文件cache分為兩個層面,page cache 與 Buffer cache,每個page cache包含若干個buffer cache。操作系統中,磁碟文件都是由一系列的數據塊(Block)組成,buffer cache也叫塊緩存,是對磁碟一個數據塊的緩存,目的是為了在程序多次訪問同一個磁碟塊時減少訪問時間;而文件系統對數據的組織形式為頁,page cache為頁緩存,是由多個塊緩存構成,其對應的緩存數據塊在磁碟上不一定是連續的。也就是說buffer cache緩存文件的具體內容--物理磁碟上的磁碟塊,加速對磁碟的訪問,而page cache緩存文件的邏輯內容,加速對文件內容的訪問。

buffer cache的大小一般為1k,page cache在32位系統上一般為4k,在64位系統上一般為8k。磁碟數據塊、buffer cache、page cache及文件的關系如下圖:

文件cache的目的是加快對數據文件的訪問,同時會有一個預讀過程。對於每個文件的第一次讀請求,系統會讀入所請求的頁面並讀入緊隨其後的幾個頁面;對於第二次讀請求,如果所讀頁面在cache中,則會直接返回,同時又一個非同步預讀的過程(將讀取頁面的下幾頁讀入cache中),如果不在cache中,說明讀請求不是順序讀,則會從磁碟中讀取文件內容並刷新cache。因此在順序讀取情況下,讀取數據的性能近乎內存讀取。

DMA允許硬體子系統直接將數據從磁碟讀取到內核緩沖區,那麼在一次數據傳輸中,磁碟與內核緩沖區,輸出設備與內核緩沖區之間的兩次數據拷貝就不需要CPU進行調度,CPU只需要進行緩沖區管理、以及創建和處理DMA。而Page Cache/Buffer Cache的預讀取機制則加快了數據的訪問效率。如下圖所示,還是以文件伺服器請求為例,此時CPU負責的數據拷貝次數減少了兩次,數據傳輸性能有了較大的提高。

使用DMA的系統I/O操作要進行4次內核空間與應用程序空間的上下文切換,2次CPU數據拷貝及2次DMA數據拷貝。

Mmap內存映射與標准I/O操作的區別在於當應用程序需要訪問數據時,不需要進行內核緩沖區到應用程序緩沖區之間的數據拷貝。Mmap使得應用程序和操作系統共享內核緩沖區,應用程序直接對內核緩沖區進行讀寫操作,不需要進行數據拷貝。Linux系統中通過調用mmap()替代read()操作。

同樣以文件伺服器獲取文件(不加修改)為例,通過mmap操作的一次系統I/O過程如下:

通過以上流程可以看到,數據拷貝從原來的4次變為3次,2次DMA拷貝1次內核空間數據拷貝,CPU只需要調控1次內核空間之間的數據拷貝,CPU花費在數據拷貝上的時間進一步減少(4次上下文切換沒有改變)。對於大容量文件讀寫,採用mmap的方式其讀寫效率和性能都比較高。(數據頁較多,需要多次拷貝)

註:mmap()是讓應用程序空間與內核空間共享DMA從磁碟中讀取的文件緩沖,也就是應用程序能直接讀寫這部分PageCache,至於上圖中從頁緩存到socket緩沖區的數據拷貝只是文件伺服器的處理,根據應用程序的不同會有不同的處理,應用程序也可以讀取數據後進行修改。重點是虛擬內存映射,內核緩存共享。

djk中nio包下的MappedByteBuffer,官方注釋為 A direct byte buffer whose content is a memory-mapped region of a file,即直接位元組緩沖區,其內容是文件的內存映射區域。 FileChannel是是nio操作文件的類,其map()方法在在實現類中調用native map0()本地方法,該方法通過mmap()實現,因此是將文件從磁碟讀取到內核緩沖區,用戶應用程序空間直接操作內核空間共享的緩沖區,Java程序通過MappedByteBuffer的get()方法獲取內存數據。

MappedByteBuffer允許Java程序直接從內存訪問文件,可以將整個文件或文件的一部分映射到內存中,由操作系統進行相關的請求並將內存中的修改寫入到磁碟中。

FileChannel map有三種模式

MappedByteBuffer的應用,以rocketMQ為例(簡單介紹)。

procer端發送消息最終會被寫入到commitLog文件中,consumer端消費時先從訂閱的consumeQueue中讀取持久化消息的commitLogOffset、size等內容,隨後再根據offset、size從commitLog中讀取消息的真正實體內容。其中,commitLog是混合部署的,所有topic下的消息隊列共用一個commitLog日誌數據文件,consumeQueue類似於索引,同時區分開不同topic下不同MessageQueue的消息。

rocketMQ利用MappedByteBuffer及PageCache加速對持久化文件的讀寫操作。rocketMQ通過MappedByteBuffer將日誌數據文件映射到OS的虛擬內存中(PageCache),寫消息時首先寫入PageCache,通過刷盤方式(非同步或同步)將消息批量持久化到磁碟;consumer消費消息時,讀取consumeQueue是順序讀取的,雖然有多個消費者操作不同的consumeQueue,對混合部署的commitLog的訪問時隨機的,但整體上是從舊到新的有序讀,加上PageCache的預讀機制,大部分情況下消息還是從PageCache中讀取,不會產生太多的缺頁中斷(要讀取的消息不在pageCache中)而從磁碟中讀取。

rocketMQ利用mmap()使程序與內核空間共享內核緩沖區,直接對PageCache中的文件進行讀寫操作,加速對消息的讀寫請求,這是其高吞吐量的重要手段。

使用mmap能減少CPU數據拷貝的次數,但也存在一些問題。

從Linux2.1開始,Linux引入sendfile()簡化操作。取消read()/write(),mmap()/write()。

調用sendfile的流程如下:

通過sendfile()的I/O進行了2次應用程序空間與內核空間的上下文切換,以及3次數據拷貝,其中2次是DMA拷貝,1次是CPU拷貝。sendfile相比起mmap,數據信息沒有進入到應用程序空間,所以能減少2次上下文切換的開銷,而數據拷貝次數是一樣的。

上述流程也可以看出,sendfile()適合對文件不加修改的I/O操作。

sendfile()只是減少應用程序空間與內核空間的上下文切換,並沒有減少CPU數據拷貝的次數,還存在一次內核空間的兩個緩沖區的數據拷貝。要實現CPU零數據拷貝,需要引入一些硬體上的支持。在上一小節的sendfile流程中,數據需要從內核緩沖區拷貝到內核空間socket緩沖區,數據都是在內核空間,如果socket緩沖區到網卡的這次DMA數據傳輸操作能直接讀取到內核緩沖區中的數據,那麼這一次的CPU數據拷貝也就能避免。要達到這個目的,DMA需要知道存有文件位置和長度信息的緩沖區描述符,即socket緩沖區需要從內核緩沖區接收這部分信息,DMA需要支持數據收集功能。

sendfile()調用後,數據從磁碟文件拷貝到內核緩沖區中,然後將文件位置和長度信息的緩沖區描述符傳遞到socket緩沖區,此時數據並沒有被拷貝。之後網卡子系統根據socket緩沖區中的文件信息利用DMA技術收集拷貝數據。整個過程進行了2次內核空間和應用程序空間的上下文切換,及2次DMA數據拷貝,CPU不需要參與數據拷貝工作,從而實現零拷貝。當然DMA收集拷貝功能需要硬體和驅動程序的支持。

在操作系統中,硬體和軟體之間的數據傳輸可以通過DMA來進行,DMA進行數據傳輸的過程幾乎不需要CPU參與,但是在內核緩沖區(頁緩存)與應用程序緩沖區之間的數據拷貝並沒有類似於DMA之類的工具可以使用,mmap、sendfile都是為了減少數據在內核空間與應用程序空間傳輸時的數據拷貝和上下文切換次數,有效地改善數據在兩者之間傳遞的效率。

linux操作系統的零拷貝技術並不單指某一種方式,現有的零拷貝技術種類非常多,在不同的Linux內核版本上有不同的支持。常見的,如果應用程序需要修改數據,則使用mmap(),如果只進行文件數據傳輸,則可選擇sendfile()。

另外,關於零拷貝技術適用於什麼場景?在上述的描述中,數據在傳遞過程中,除了mmap外,應用程序和操作系統幾乎是沒有改變數據的,mmap的內存映射也是沒有改變數據的,也就是說在靜態資源的讀取場景下,零拷貝更能發揮作用。正如其名,拷貝是在不改變數據的情況下,零是利用手段去減少CPU參與數據拷貝的次數,以釋放CPU去進行其他系統調用與計算。

3. 探討一下 Linux 共享內存的 N 種方式

關於 Linux 共享內存,寫得最好的應該是宋寶華的 《世上最好的共享內存》 一文。

本文可以說是對這篇文章的學習筆記,順手練習了一下 rust libc —— shichaoyuan/learn_rust/linux-shmipc-demo

按照宋寶華的總結,當前有四種主流的共享內存方式:

前兩種方式比較符合傳統的用法,共享內存做為進程間通信的媒介。
第三種方式更像是通過傳遞內存「句柄」進行數據傳輸。
第四種方式是為設備間傳遞數據設計,避免內存拷貝,直接傳遞內存「句柄」。

這里嘗試了一下第二種和第三種方式。

這套 API 應該是最普遍的 —— shm_open + mmap,本質上來說 Aeron 也是用的這種方式(關於 Aeron 可以參考 我之前的文章 )。

看一下 glibc 中 shm_open 函數的實現就一清二楚了:

shm_open 函數就是在 /dev/shm 目錄下建文件,該目錄掛載為 tmpfs,至於 tmpfs 可以簡單理解為存儲介質是內存的一種文件系統,更准確的理解可以參考官方文檔 tmpfs.txt 。

然後通過 mmap 函數將 tmpfs 文件映射到用戶空間就可以隨意操作了。

優點:
這種方式最大的優勢在於共享的內存是有「實體」(也就是 tmpfs 中的文件)的,所以多個進程可以很容易通過文件名這個信息構建共享內存結構,特別適合把共享內存做為通信媒介的場景(例如 Aeron )。

缺點:
如果非要找一個缺點的話,可能是,文件本身獨立於進程的生命周期,在使用完畢後需要注意刪除文件(僅僅 close 是不行的),否則會一直佔用內存資源。

memfd_create 函數的作用是創建一個匿名的文件,返回對應的 fd,這個文件當然不普通,它存活在內存中。更准確的理解可以參考官方文檔 memfd_create(2) 。

直觀理解,memfd_create 與 shm_open 的作用是一樣的,都是創建共享內存實體,只是 memfd_create 創建的實體是匿名的,這就帶了一個問題:如何讓其它進程獲取到匿名的實體?shm_open 方式有具體的文件名,所以可以通過打開文件的方式獲取,那麼對於匿名的文件怎麼處理呢?

答案是:通過 Unix Domain Socket 傳遞 fd。

rust 的 UDS 實現:
rust 在 std 中已經提供了 UDS 的實現,但是關於傳遞 fd 的 send_vectored_with_ancillary 函數還屬於 nightly-only experimental API 階段。所以這里使用了一個三方 crate —— sendfd ,坦白說可以自己實現一下,使用 libc 構建好 SCM_RIGHTS 數據,sendmsg 出去即可,不過細節還是挺多,我這里就放棄了。

這套 API 設計更靈活,直接拓展了我的思路,本來還是受限於 Aeron 的用法,如果在這套 API 的加持下,是否可以通過傳遞數據包內存塊(fd)真正實現零拷貝呢?

優點:
靈活。

缺點:

4. Linux內核中如何申請和釋放內存

1、首先打開Linux命令窗口,可使用快捷鍵Ctrl+Alt+T打開。

閱讀全文

與linux內存拷貝相關的資料

熱點內容
單片機中三位數碼管原件 瀏覽:138
pdf可以刪除其中一頁 瀏覽:214
清dns緩存的命令 瀏覽:103
免費pdf在線轉換 瀏覽:768
堆貨演算法 瀏覽:878
vsc編譯vc程序 瀏覽:197
centos55命令 瀏覽:709
美國干編程有什麼條件 瀏覽:505
阿里雲伺服器遠程鏈接 瀏覽:249
墨鏡慧眼怎麼下載廠商的app 瀏覽:61
iphone加密專線 瀏覽:491
aes產生加密文件 瀏覽:415
編程實現藍牙通信 瀏覽:769
怎麼恢復掉簽的app 瀏覽:848
伺服器部署ip地址 瀏覽:323
涉密場所周邊安全防護距離演算法 瀏覽:674
安卓fpse模擬器怎麼設置加速 瀏覽:948
建行app怎麼生成電子簽章 瀏覽:510
獲取當前時間javadate 瀏覽:75
帶密碼的wifi如何加密 瀏覽:239