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

對IO概念模糊:計算機IO過程與零拷貝

開發(fā) 前端
零拷貝需要進(jìn)行系統(tǒng)調(diào)用才能實現(xiàn),很明顯要我們手寫實現(xiàn)零拷貝是很底層、很麻煩的,好在JAVA在NIO中封裝了mmap、SendFile兩種零拷貝的API,當(dāng)我們想在JAVA中使用零拷貝時,直接調(diào)API即可。

一、前置知識

1.1 內(nèi)存分段

現(xiàn)代計算機在加載操作系統(tǒng)、正常啟動后,其內(nèi)存會主要分成兩大段:

  • 內(nèi)核段
  • 用戶段

內(nèi)核段:

操作系統(tǒng)本質(zhì)上是一個計算機的管理程序,該程序相關(guān)的所有資源,被存放在內(nèi)核段中。

用戶段:

用戶段用來存放各個進(jìn)程的數(shù)據(jù)和指令。

根據(jù)所訪問的內(nèi)存段的不同,CPU會處于不同的態(tài),訪問用戶段的時候處于用戶態(tài),訪問內(nèi)核段的時候處于內(nèi)核態(tài)。

1.2 CPU的態(tài)

1.2.1 CPU的工作過程

CPU要執(zhí)行的指令的地址存在寄存器中,指令存放在內(nèi)存中,而CPU本質(zhì)上就是一個去內(nèi)存中根據(jù)地址取指令,然后執(zhí)行指令的硬件。

舉一個例子:

例如PC寄存器中存放50,CPU讀到存放的50,發(fā)出一條取址指令,去取出地址為50的內(nèi)存單元中的指令,再傳回給CPU。

1.2.2 寄存器

眾所周知,為了配平CPU和內(nèi)存之間速率的差距,CPU和內(nèi)存之間存在著一個由寄存器組成的中間層,寄存器種會存放著CPU接下來要執(zhí)行的指令,以及后續(xù)可能要執(zhí)行到的指令以及可能要用到的數(shù)據(jù)。只有預(yù)先裝載進(jìn)去這部分可能要用到的東西才能抹平CPU和內(nèi)存之間的速率差距,不然每次都要去內(nèi)存取內(nèi)容,可能是會拉低CPU的效率的。

但該預(yù)先裝載哪些內(nèi)容進(jìn)寄存器中呢?這里遵循了程序的局部性原理。

程序的局部性原理:

程序在執(zhí)行的時候呈現(xiàn)出局部性規(guī)律,在一段時間內(nèi),整個程序的執(zhí)行僅限于程序中的某一個部分,相應(yīng)的,執(zhí)行所訪問的存儲空間也局限于某個內(nèi)存區(qū)域。局部性又分為時間局部性和空間局部性。時間局部性指的是,如果程序中的某條指令一旦執(zhí)行,則不久后可能會被再次執(zhí)行,執(zhí)行指令時訪問的數(shù)據(jù)單元在不久后會被再次訪問。空間局部性指的是,一旦訪問了某個存儲單元,不久后,其附近的存儲單元也將被訪問。

1.2.3 CPU的上下文

為了抹平內(nèi)存和CPU之間的速率差,給CPU配備了寄存器。寄存器中存儲著當(dāng)前執(zhí)行的指令、數(shù)據(jù)、以及下一條指令在內(nèi)存中的地址等等事關(guān)程序正常運行的關(guān)鍵信息。所以寄存器中存儲的內(nèi)容合稱為CPU的上下文。

1.2.4 系統(tǒng)調(diào)用

系統(tǒng)中將一些對系統(tǒng)級別資源的調(diào)用封裝成了一個個函數(shù),稱為系統(tǒng)調(diào)用,常見的系統(tǒng)調(diào)用有很多,比如IO操作就是個系統(tǒng)調(diào)用。

1.2.5 CPU的態(tài)

操作系統(tǒng)在啟動后,內(nèi)存被分為兩部分(兩段):

  • 內(nèi)核段從0地址開始編址,存放操作系統(tǒng)程序,里面包含系統(tǒng)調(diào)用。
  • 用戶段,從內(nèi)核段之后開始編址,存放用戶程,也就是各個進(jìn)程的數(shù)據(jù)和指令。

由于內(nèi)核段存放的是系統(tǒng)相關(guān)的內(nèi)容,基于安全的考慮,肯定是不允許被CPU隨意訪問的,需要特權(quán)才行。因此將CPU的權(quán)限設(shè)計為了兩種狀態(tài):

  • 用戶態(tài),只能訪問用戶段
  • 內(nèi)核態(tài),能訪問用戶段和內(nèi)核段

所謂的態(tài)就是能訪問用戶段的上下文以及能訪問內(nèi)核段的上下文。當(dāng)我們調(diào)用系統(tǒng)調(diào)用的時候會引起上下文的切換,也就是CPU態(tài)的切換。上下文切換的意思是,先把前一個任務(wù)的 CPU 上下文保存起來,然后加載新任務(wù)的上下文到這些寄存器和程序計數(shù)器,最后再跳轉(zhuǎn)到程序計數(shù)器所指的新位置,運行新任務(wù)。之所以會切換上下文,這是因為寄存器加載數(shù)據(jù)和指令的時候遵循了程序的局部性原理。CPU訪問用戶段時,寄存器里預(yù)加載的是用戶段的資源;CPU訪問內(nèi)核段時,寄存器里預(yù)加載的是內(nèi)核段的資源。

所以CPU進(jìn)行態(tài)切換的時候,上下文一定會完全換一套的??偟膩碚f為了保證多數(shù)情況下程序執(zhí)行的效率,“局部性原理”是必須存在的,為了內(nèi)核的安全,CPU態(tài)的劃分是必須存在的。所以,CPU上下文切換是不得不接受的一種代價。

CPU的上下文切換是種耗時的操作:

寄存器保存和恢復(fù):在上下文切換過程中,需要保存當(dāng)前任務(wù)的寄存器狀態(tài),并恢復(fù)下一個任務(wù)的寄存器狀態(tài)。寄存器保存和恢復(fù)涉及將寄存器的值從CPU保存到內(nèi)存(或者棧)中,以及從內(nèi)存中恢復(fù)到CPU中。這涉及到數(shù)據(jù)的讀寫和復(fù)制操作,會引入一定的延遲和開銷。

內(nèi)存刷新和緩存失效:上下文切換可能涉及刷新CPU緩存和內(nèi)存管理單元(MMU)的操作。當(dāng)切換到一個新的任務(wù)時,之前的任務(wù)的緩存內(nèi)容可能需要刷新,新任務(wù)的頁表和內(nèi)存映射需要加載和設(shè)置,這些操作可能導(dǎo)致緩存失效和內(nèi)存訪問延遲。

上下文數(shù)據(jù)復(fù)制:在上下文切換過程中,需要將當(dāng)前任務(wù)的上下文數(shù)據(jù)保存到內(nèi)存中,同時從內(nèi)存中加載下一個任務(wù)的上下文數(shù)據(jù)。這包括寄存器狀態(tài)、程序計數(shù)器、標(biāo)志位和其他與任務(wù)執(zhí)行相關(guān)的數(shù)據(jù)。數(shù)據(jù)的復(fù)制和加載需要占用CPU和內(nèi)存帶寬,并引入一定的延遲。

任務(wù)切換開銷:上下文切換不僅僅涉及寄存器和內(nèi)存的操作,還包括任務(wù)切換本身的開銷。這包括切換內(nèi)核棧、更新任務(wù)控制塊(TCB)、更新調(diào)度器數(shù)據(jù)結(jié)構(gòu)等操作。這些操作可能需要修改內(nèi)核數(shù)據(jù)結(jié)構(gòu),增加了上下文切換的開銷。

總的來說CPU上下文切換很耗時,我們常見的就是IO操作、進(jìn)程切換這些都會引起CPU上下文切換。

1.3 計算機IO的過程

在程序執(zhí)行時有很多高耗時操作,比如IO操作就是。當(dāng)計算機執(zhí)行IO操作的時候,IO設(shè)備的速度肯定是遠(yuǎn)遠(yuǎn)落后于CPU的速度的,IO沒有完成,后續(xù)依賴的數(shù)據(jù)沒到位,程序也沒辦法繼續(xù)向下執(zhí)行,于是CPU就只好賦閑,傻傻的等IO執(zhí)行完成,再繼續(xù)向下運行程序,無疑這會造成CPU資源的浪費,使得計算機的工作效率變得很低。

于是現(xiàn)代操作系統(tǒng)中將CPU劃分成了很多時間片,不同時間片可以去運行不同的程序,比如:

  1. 這一秒運行的A程序,
  2. 下一秒運行的B程序,
  3. 再下一秒再運行A程序。

這樣間插執(zhí)行就會避免傻等帶來的CPU資源的浪費,如果IO耗時2秒,那么CPU至少還有1秒被其它程序使用到了。

后來操作系統(tǒng)用了更激進(jìn)的方式來處理IO指令,讓CPU的時間一絲一毫都不被浪費,這種處理方式就是遇見IO指令,直接啟動IO后,CPU直接轉(zhuǎn)去執(zhí)行其它任務(wù),當(dāng)IO完成后發(fā)送一個中斷信號給CPU,讓CPU中斷當(dāng)前的任務(wù),轉(zhuǎn)過來繼續(xù)執(zhí)行IO后的程序

1.4 IO與內(nèi)存

計算機進(jìn)行IO的時候,本質(zhì)上會為每一個IO設(shè)備在內(nèi)存中分配一塊空間,向這塊空間里進(jìn)行讀寫,即可完成IO。為什么給IO設(shè)備分配的內(nèi)存會是在內(nèi)核段里喃?主要是基于兩點進(jìn)行考慮的:

  • 安全性
  • 特權(quán)操作

1.4.1 安全性:

I/O 操作通常需要與計算機的外部設(shè)備(如磁盤、網(wǎng)絡(luò)設(shè)備等)進(jìn)行交互,如果允許各個進(jìn)程自己私自與外部設(shè)備進(jìn)行交互,IO的內(nèi)存放在各個進(jìn)程內(nèi)部,太散了,不是很好進(jìn)行安全控制,相反,如果將IO的內(nèi)存放在內(nèi)核段,就很便于集中管理,可以附加一些安全機制上去。

1.4.2 特權(quán)操作:

首先IO指令本身就是特權(quán)指令,會讓CPU進(jìn)入內(nèi)核態(tài),其次進(jìn)行IO的時候會用到中斷信號,也涉及到特權(quán)指令,也要求CPU處于內(nèi)核態(tài),所以如果IO內(nèi)存是在內(nèi)核段中,讓CPU提前進(jìn)入內(nèi)核狀態(tài),也避免了后面來回切狀態(tài)造成的時間浪費。

整個IO在內(nèi)存中的流轉(zhuǎn)過程如下:

讀的時候磁盤拷貝到內(nèi)核段、內(nèi)核段拷貝到用戶段,

寫的時候用戶段拷貝到內(nèi)核段、內(nèi)核段拷貝到磁盤。

一共四次復(fù)制。

特別說明:

我知道其它很多地方這里將圖畫成了這個樣子:

這是因為他描繪的這次IO是從磁盤上讀出來然后寫到網(wǎng)絡(luò)上去,網(wǎng)卡和磁盤可以理解為兩個不同的IO設(shè)備,所以他們在內(nèi)核段中的IO內(nèi)存,地址是不同的。但是如果僅僅是對磁盤的一次本地IO,那么進(jìn)行IO的內(nèi)核段地址會是同一個,在同一個地址內(nèi)進(jìn)行讀寫。這里為了涵蓋多種情況,所以博主沒有將它分開,讀者悉知。

二、零拷貝

零拷貝(Zero-copy)是一種優(yōu)化技術(shù),并不是一次拷貝都不做,而是旨在減少數(shù)據(jù)在系統(tǒng)內(nèi)部的復(fù)制操作,從而提高數(shù)據(jù)傳輸?shù)男?。它的主要目?biāo)是減少內(nèi)存到內(nèi)存之間的數(shù)據(jù)拷貝。零拷貝有兩種實現(xiàn)方式:

  1. MMap
  2. SendFile

2.1.MMap

通過上文我們知道一次IO,數(shù)據(jù)會進(jìn)行四次拷貝,MMap這種方式在將內(nèi)核段中的數(shù)據(jù)拷貝到用戶段的這次拷貝中,拷貝的不是數(shù)據(jù),而是數(shù)據(jù)的映射,這樣在用戶段中進(jìn)行數(shù)據(jù)處理完后,就不必再從用戶段拷貝回內(nèi)核段,從而減少了一次拷貝。

之所以能實現(xiàn)這樣的效果是得益于操作系統(tǒng)底層有兩種讀操作:

讀取數(shù)據(jù):常見的系統(tǒng)調(diào)用如 read()(用于文件描述符)或 recv()(用于套接字)用于從文件或套接字中讀取數(shù)據(jù)。這些系統(tǒng)調(diào)用從相應(yīng)的輸入源(如磁盤、網(wǎng)絡(luò)等)讀取數(shù)據(jù),并將其復(fù)制到應(yīng)用程序提供的緩沖區(qū)中。這種方式涉及了數(shù)據(jù)的復(fù)制,因為數(shù)據(jù)需要從內(nèi)核態(tài)復(fù)制到用戶態(tài)緩沖區(qū)中。

讀取映射:另一種方式是通過內(nèi)存映射(Memory Mapping)來實現(xiàn)讀取操作。通過將文件或設(shè)備的數(shù)據(jù)映射到進(jìn)程的內(nèi)存區(qū)域中,應(yīng)用程序可以直接訪問內(nèi)存映射區(qū)域中的數(shù)據(jù),而無需使用傳統(tǒng)的 read() 系統(tǒng)調(diào)用。在這種情況下,應(yīng)用程序可以通過直接讀取內(nèi)存映射區(qū)域中的數(shù)據(jù)來獲取文件或設(shè)備的內(nèi)容,避免了中間的數(shù)據(jù)復(fù)制。

特別說明:

還是和上文類似,畫圖的問題。這里為了涵蓋,本地IO和網(wǎng)絡(luò)IO兩種情況,內(nèi)核段沒拆成幾個設(shè)備的不同地址空間,但是如果是從磁盤中讀,然后向網(wǎng)絡(luò)中寫,是跨了IO設(shè)備的,所以中間有個內(nèi)核段地址間的復(fù)制過程,如下圖:

2.2.SendFile

SendFile更狠,直接就不走用戶段,直接就是從內(nèi)核段的一個內(nèi)存地址復(fù)制到另一個內(nèi)存地址,主要是拿來進(jìn)行網(wǎng)絡(luò)傳輸?shù)模瑥谋镜卮疟P讀數(shù)據(jù),讀到一個地址里,然后將這個地址里的數(shù)據(jù)復(fù)制給另一個IO設(shè)備的地址,這個地址就可以是網(wǎng)絡(luò)IO的地址。很明顯sendFile有一個弊病,就是沒走用戶段的話,數(shù)據(jù)沒辦法處理,所以其只是一種用于實現(xiàn)數(shù)據(jù)傳輸?shù)?"零拷貝" 技術(shù),而不能直接進(jìn)行數(shù)據(jù)處理。并且SendFile還存在大小限制。

三、JAVA中的零拷貝

零拷貝需要進(jìn)行系統(tǒng)調(diào)用才能實現(xiàn),很明顯要我們手寫實現(xiàn)零拷貝是很底層、很麻煩的,好在JAVA在NIO中封裝了mmap、SendFile兩種零拷貝的API,當(dāng)我們想在JAVA中使用零拷貝時,直接調(diào)API即可。

很多同學(xué)在NIO中老是搞不明白channel和buffer的關(guān),容易暈,這里博主一句話總結(jié)一下:

JavaNlO中 的Channel就相當(dāng)于操作系統(tǒng)中的內(nèi)核緩沖區(qū),而Buffer就相當(dāng)于操作系統(tǒng)中的用戶緩沖區(qū)。

mmap:

MappedByteBuffer mappedByteBuffer = new RandomAccessFile(file, "r") 
                                 .getChannel() 
                                .map(FileChannel.MapMode.READ_ONLY, 0, len);

SendFile:

sendFile進(jìn)行網(wǎng)絡(luò)傳輸:

FileChannel sourceChannel = new RandomAccessFile(sourceFile, "rw").getChannel();
SocketChannel socketChannel = SocketChannel.open(sa);
sourceChannel.transferTo(0, sourceChannel.size(), socketChannel);

sendFile進(jìn)行文件拷貝:

try (FileChannel srcChannel = new FileInputStream(src).getChannel();
     FileChannel targetChannel = new FileInputStream(target).getChannel()) {
     srcChannel.transferTo(0, srcChannel.size(), targetChannel );
 } catch (IOException e) {
     e.printStackTrace();
 }


責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2024-01-09 18:09:43

模型方式DMA

2024-09-26 08:53:43

2021-06-16 11:13:49

曙光

2010-09-13 10:19:15

2017-07-14 15:40:28

2019-09-10 12:58:03

電腦編程語言硬件

2023-12-17 14:24:46

計算機進(jìn)程線程

2023-12-06 07:28:47

阻塞IO異步IO

2024-03-26 10:38:16

計算機視覺人工智能

2015-05-28 11:09:00

2011-07-27 21:28:53

計算機網(wǎng)絡(luò)服務(wù)

2021-01-09 13:59:49

異步IO驅(qū)動式

2023-03-06 10:30:27

零售業(yè)人工智能

2021-09-30 07:26:15

磁盤IO網(wǎng)絡(luò)

2010-06-09 11:00:56

2011-06-21 08:56:19

IOjdk6

2021-06-21 11:11:29

LinuxIO磁盤IO

2012-06-20 10:40:36

量子計算機

2011-05-19 09:39:51

數(shù)據(jù)庫計算機

2023-08-07 11:36:52

計算機視覺零售行業(yè)
點贊
收藏

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