亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談Linux的零拷貝技術

 更新時間:2023年04月28日 10:14:24   作者:魏天樂大帥哥  
零拷貝主要的任務就是避免CPU將數據從一塊存儲拷貝到另外一塊存儲,主要就是利用各種零拷貝技術,避免讓CPU做大量的數據拷貝任務,減少不必要的拷貝,需要的朋友可以參考下

前言

在Linux系統(tǒng)內部緩存和內存容量都是有限的,更多的數據都是存儲在磁盤中。對于Web服務器來說,經常需要從磁盤中讀取數據到內存,然后再通過網卡傳輸給用戶

img

那么這也算一次I O的過程,都知道IO過程中需要狀態(tài)的切換還有一系列拷貝過程,都是要時間開銷的,那么怎么優(yōu)化用戶態(tài)和內核態(tài)的狀態(tài)的切換次數和各種緩沖區(qū)之間的拷貝次數,也是linux的服務器實現高并發(fā)的重要技術了!

傳統(tǒng)數據交互

傳統(tǒng) io 的執(zhí)行流程: 下面將圖左半部分read過程的硬件抽象為磁盤; 圖右半部分write過程的硬件設為網卡,模擬webserver進行一次IO的過程; 方便理解;

  • read:將數據從 IO 設備讀取到內核緩存區(qū)中,再將數據從內核緩沖區(qū)拷貝到用戶緩沖區(qū)
  • write:將數據從用戶緩沖區(qū)寫入到內核緩沖區(qū)中,再將數據從內核緩沖區(qū)拷貝到 IO 設備

read/write 屬于系統(tǒng)調用 syscall,每一次系統(tǒng)調用 ,發(fā)生兩次上下文切換

  • 調用 syscall 從用戶態(tài)切換到內核態(tài)
  • syscall 返回從內核態(tài)切換到用戶態(tài)

如圖所示,傳統(tǒng) io 的過程中,發(fā)生了4次空間切換 + 4次拷貝

在這里插入圖片描述

不難看出,傳統(tǒng)模式下的IO,涉及多次空間切換和數據冗余拷貝,效率并不高。而零拷貝 Zero-Copy 目的就是降低冗余數據拷貝,解放 CPU

  • 減少數據在內核緩沖區(qū)和用戶緩沖區(qū)之間的冗余拷貝(CPU拷貝)
  • 減少系統(tǒng)調用導致的空間切換

目前來看,零拷貝技術的實現手段主要包括:mmap+write、sendfile、sendfile+DMA、splice

零拷貝

首先解釋一下,零拷貝中的0,指的是CPU級別的數據拷貝(比如內核緩沖區(qū)到用戶緩沖區(qū)的拷貝,用戶緩沖區(qū)再到socket緩沖區(qū); 或者內核緩沖區(qū)直接到socket緩沖區(qū)的拷貝!),并不是DMA硬件的拷貝,否則數據不靠DMA怎么轉移呢?

mmap+write

  • 內存映射 memory mapping,mmap 是一種內存映射文件的方法,即將一個文件或者其他對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一映射關系。

mmap可以充當read的功能,將內核讀緩沖區(qū)地址與用戶緩沖區(qū)地址進行映射,實現內核緩沖區(qū)與用戶緩沖區(qū)的共享。這樣就減少了一次用戶態(tài)和內核態(tài)的CPU拷貝。

mmap + write 流程如圖所示,發(fā)生了4次切換 + 2次DMA拷貝 + 1次CPU拷貝

在這里插入圖片描述

函數原型

#include <sys/mman.h>
// 內存映射
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);


/*
參數
start:指定映射的虛擬內存地址,通常定義為 NULL,由內核選定地址

length:映射的長度

prot:描述映射內存的訪問權限

PROT_EXEC頁面可以被 cpu 執(zhí)行指令組成,PROT_NONE 頁面不能訪問

PROT_READ 頁面可讀,PROT_WRITE 頁面可寫,

flags:指定映射的類型,MAP_SHARED共享對象,MAP_PRIVATE私有的,寫時復制對象

fd:要進行映射的文件句柄

offset:文件偏移量
*/





// 解除映射
int munmap(void *addr, size_t length);

例: 發(fā)送方:

// 建立內存映射
char *pMap = (char*) mmap (NULL, fileInfo.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 

send(clientFd, pMap, fileInfo.st_size, 0);

// 解除映射
munmap(pMap, fileInfo.st_size); 

接收方:

// 使用 mmap 前用使用 ftruncate 來擴大文件大小
ftruncate(fd, fileSize);
char *pMap = (char*) mmap (NULL, fileSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

recvCycle(sfd, pMap, fileSize);
munmap(pMap, fileSize);

小結

mmap充當read的功能,進行一次完整的IO,減少了傳統(tǒng)方式read數據的時候,從內核態(tài)CPU拷貝到用戶態(tài)的這次拷貝; (發(fā)生了4次切換 + 2次DMA拷貝 + 1次CPU拷貝;)

mmap 存在的問題:mmap 對大文件傳輸有一定優(yōu)勢,但是小文件可能出現碎片,并且在多個進程同時操作文件時可能產生引發(fā) coredump 的 signal。

sendfile

mmap+write 方式有一定改進,但是由系統(tǒng)調用引起的狀態(tài)切換并沒有減少,因此在 Linux 內核2.1版本中引入了 sendfile 系統(tǒng)調用。

sendfile 在兩個文件之間通過內核直接傳輸數據,避免了內核緩沖區(qū)和用戶緩沖區(qū)之間的數據拷貝操作。sendfile 只能用于發(fā)送數據,不能用于接收數據。

sendfile 方式只使用一個函數就可以完成之前的 read+write 和 mmap+write 的功能,這樣減少一個系統(tǒng)調用(2次狀態(tài)切換),由于數據不經過用戶緩沖區(qū),因此該數據無法被修改。

sendfile 的流程如圖所示, 發(fā)生了2次切換 + 2次DMA拷貝+1次CPU拷貝

在這里插入圖片描述

sendfile + DMA

linux2.4版本后,對 sendfile 系統(tǒng)調用進行優(yōu)化,配合硬件 DMA,可以直接從內核空間緩沖區(qū)中將數據拷貝到網卡,徹底省去了CPU拷貝。

如圖所示,sendfile + DMA 的過程中發(fā)生了2次切換 + 2次DMA拷貝 + 0次CPU拷貝

在這里插入圖片描述

sendfile 函數原型

#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

/*
參數
- out_fd:待寫入內容的文件描述符
- in_fd:待讀出內容的文件描述符
- offset:文件偏移量
- count:傳輸的字節(jié)數
*/

例:

發(fā)送方

sendfile(clientFd, fd, 0, fileInfo.st_size);

小結

早期sendfile : 2次切換 (sendfile后,數據不用過用戶層了,導致不能修改了,不過也少了兩次狀態(tài)切換!)+ 2次DMA拷貝(磁盤到內核,socket緩沖區(qū)到網卡)+ 1次CPU拷貝(內核到socket緩沖區(qū))

改良的sendfile + DMA : 發(fā)生了2次切換 + 2次DMA拷貝(磁盤到內核,內核直接到網卡) + 0次CPU拷貝

sendfile 存在的問題:無法對數據進行修改(數據沒上到用戶層,也沒必要,webserver一般都不需要修改,返回的本地的資源!),并且需要硬件層面DMA的支持,并且 sendfile 只能將文件數據拷貝到 socketfd,有一定的局限性。

splice

splice 系統(tǒng)調用在 Linux 2.6 版本引入,不需要硬件支持,并且不再限定于 socket 上,實現了兩個普通文件之間的零拷貝。

可以在內核緩沖區(qū)和 socket 緩沖區(qū)間建立管道來傳輸數據,避免了兩者之間的 CPU 拷貝操作。

在這里插入圖片描述

函數原型

#define _GNU_SOURCE 
#include <fcntl.h>

ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);

/*
返回值;成功返回接收到的字節(jié)數,失敗-1

參數
- fd_in:待輸入數據的文件描述符。
- off_in: 輸入流偏移量。若 fd_in 是管道文件描述符,則設置為 NULL,表示從當前偏移讀入。   否則,off_in 表示從輸入數據流的某處開始讀取。
- fd_out:待輸出數據的文件描述符。
- off_out:輸出流偏移量,同上。
- len:單次寫入的數據長度,最多65536
- flags:0
*/

例:web服務器端代碼: transFile.c:

int fds[2];
pipe(fds);

int recvLen = 0;
//當讀到的數據量超過文件大小時,即已經讀取數據完成
while(recvLen < fileInfo.st_size){
    //將數據從服務器端本地讀到管道
    ret = splice(fd, 0, fds[1], 0, 65536, 0);
    //將數據從管道讀到客戶端
    ret = splice(fds[0], 0, clientFd, 0, ret, 0);
    //計算已經讀到的數據量
    recvLen += ret;
}

小結

splice 引入管道機制,實現了普通文件之間的0拷貝,突破了僅限于socket的sendfile0拷貝;

splice 存在的問題:它的兩個文件描述符中有一個必須是管道設備

到此這篇關于淺談Linux的零拷貝技術的文章就介紹到這了,更多相關Linux零拷貝技術內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • ubuntu20.04設置靜態(tài)ip地址(包括不同版本)

    ubuntu20.04設置靜態(tài)ip地址(包括不同版本)

    這篇文章主要介紹了ubuntu20.04設置靜態(tài)ip地址,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • ubuntu14.04安裝jdk1.8的教程

    ubuntu14.04安裝jdk1.8的教程

    這篇文章主要介紹了ubuntu14.04安裝jdk1.8的教程,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • 高性能HTTP加速器Varnish-3.0.3搭建、配置及優(yōu)化步驟

    高性能HTTP加速器Varnish-3.0.3搭建、配置及優(yōu)化步驟

    這篇文章主要介紹了高性能HTTP加速器Varnish-3.0.3搭建、配置及優(yōu)化步驟,需要的朋友可以參考下
    2016-02-02
  • 解決linux下大量TIME WAIT的方法詳解

    解決linux下大量TIME WAIT的方法詳解

    本篇文章是對linux下大量TIME WAIT的解決方法進行了詳細的分析介紹,需要的朋友參考下
    2013-06-06
  • node+Express測試服務器性能

    node+Express測試服務器性能

    這篇文章介紹了使用node+Express測試服務器性能,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-12-12
  • linux下多線程中的fork介紹

    linux下多線程中的fork介紹

    大家好,本篇文章主要講的是linux下多線程中的fork介紹,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下哦,方便下次瀏覽
    2021-12-12
  • Vim命令高級用法

    Vim命令高級用法

    一些高級的Vim命令用法:同時編輯多個文件、文本重排、域操作、緩沖區(qū)的使用等命令。
    2013-11-11
  • Linux下的多線程編程(三)

    Linux下的多線程編程(三)

    學東西,往往實例才是最讓人感興趣的,老是學基礎理論,不動手,感覺沒有成就感,呵呵。
    2014-08-08
  • Linux上下行網速測試工具_speedtest-cli安裝使用方法

    Linux上下行網速測試工具_speedtest-cli安裝使用方法

    speedtest-cli是Linux下的一個上下行網速測試工具,是一個用Python寫的命令行腳本,需要的朋友可以參考下
    2017-03-03
  • 在CentOS 8上安裝htop的教程

    在CentOS 8上安裝htop的教程

    這篇文章主要介紹如何在CentOS 8上安裝htop以及如何使用該命令檢索系統(tǒng)統(tǒng)計信息。本文通過圖文實例詳解的形式給大家展示的非常詳細,需要的朋友參考下
    2019-11-11

最新評論