欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言中零拷貝的原理與實現(xiàn)詳解

 更新時間:2023年08月16日 08:50:51   作者:洋芋編程  
零拷貝是相對于用戶態(tài)來講的,即數(shù)據(jù)在用戶態(tài)不發(fā)生任何拷貝,那么零拷貝的原理是什么,又是如何實現(xiàn)的呢,下面小編就來和大家詳細(xì)聊聊吧

傳統(tǒng)讀寫模式

傳統(tǒng)讀寫模式流程圖

  • 第一次數(shù)據(jù)拷貝: 用戶進(jìn)程發(fā)起 read() 系統(tǒng)調(diào)用,當(dāng)前上下文從用戶態(tài)切換至內(nèi)核態(tài),DMA(Direct Memory Access) 引擎從文件中讀取數(shù)據(jù),并存儲到內(nèi)核態(tài)緩沖區(qū) (DMA 拷貝)
  • 第二次數(shù)據(jù)拷貝: 將數(shù)據(jù)從內(nèi)核態(tài)緩沖區(qū)拷貝到用戶態(tài)緩沖區(qū) (CPU 拷貝),然后返回給用戶進(jìn)程,拷貝數(shù)據(jù)時會發(fā)生一次上下文切換 (從內(nèi)核態(tài)切換到用戶態(tài))
  • 第三次數(shù)據(jù)拷貝: 用戶進(jìn)程發(fā)起 write() 系統(tǒng)調(diào)用,當(dāng)前上下文從用戶態(tài)切換至內(nèi)核態(tài),數(shù)據(jù)從用戶態(tài)緩沖區(qū)被拷貝到 Socket 緩沖區(qū) (CPU 拷貝)
  • 第四次數(shù)據(jù)拷貝: write() 系統(tǒng)調(diào)用結(jié)束返回到用戶進(jìn)程,當(dāng)前上下文從內(nèi)核態(tài)切換至用戶態(tài),第四次數(shù)據(jù)拷貝為異步執(zhí)行,從 Socket 緩沖區(qū)拷貝到網(wǎng)卡 (DMA 拷貝)

transferTo

transferTo() 和 send() 類似,也是一個系統(tǒng)調(diào)用,用于在文件之間高效地傳輸數(shù)據(jù)。

transferTo 在操作系統(tǒng)層面實現(xiàn)了零拷貝技術(shù),允許將數(shù)據(jù)直接從一個文件傳輸?shù)搅硪粋€文件,而無需通過用戶空間進(jìn)行中轉(zhuǎn)。

transferTo 流程圖

  • 第一次數(shù)據(jù)拷貝: 用戶進(jìn)程發(fā)起 transferTo() 調(diào)用,將文件數(shù)據(jù)拷貝到一個 Read buffer(內(nèi)核態(tài))中,當(dāng)前上下文從用戶態(tài)切換至內(nèi)核態(tài)
  • 第二次數(shù)據(jù)拷貝: 內(nèi)核將 Read buffer 中的數(shù)據(jù)拷貝到 Socket 緩沖區(qū)
  • 第三次數(shù)據(jù)拷貝: 數(shù)據(jù)從 Socket 緩沖區(qū)拷貝到網(wǎng)卡,當(dāng)前上下文從內(nèi)核態(tài)切換至用戶態(tài)

相比較于傳統(tǒng)的讀寫模式, transferTo 把上下文的切換次數(shù)從 4 次減少到 2 次,同時把數(shù)據(jù)拷貝的次數(shù)從 4 次降低到了 3 次, 雖然已經(jīng)前進(jìn)了一大步,但是作為過渡階段,transferTo 距離零拷貝還有一些距離。

零拷貝

零拷貝是相對于用戶態(tài)來講的,數(shù)據(jù)在用戶態(tài)不發(fā)生任何拷貝。

sendfile + DMA

sendfile() 是作用于兩個文件描述符之間的數(shù)據(jù)拷貝的系統(tǒng)調(diào)用,這個拷貝操作是直接在內(nèi)核中進(jìn)行的,沒有用戶態(tài)到內(nèi)核態(tài)的數(shù)據(jù)拷貝和上下文切換帶來的開銷,所以稱為零拷貝技術(shù)。

Linux2.4 內(nèi)核對 sendfile 系統(tǒng)調(diào)用做了改進(jìn):

sendfile 改進(jìn)

  • 用戶進(jìn)程發(fā)起 sendfile() 系統(tǒng)調(diào)用,當(dāng)前上下文從用戶態(tài)切換至內(nèi)核態(tài),DMA 將數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)
  • 向 Socket 緩沖區(qū)中發(fā)送當(dāng)前數(shù)據(jù)在內(nèi)核緩沖區(qū)的地址和偏移量兩個值
  • 根據(jù) Socket 緩沖區(qū)的地址和偏移量,直接將內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到網(wǎng)卡,當(dāng)前上下文從內(nèi)核態(tài)切換至用戶態(tài)

零拷貝流程圖

相比較于傳統(tǒng)的讀寫模式, sendfile + DMA 把上下文的切換次數(shù)從 4 次減少到 2 次,同時把數(shù)據(jù)拷貝的次數(shù)從 4 次降低到了 2 次 (2 次均為 DMA 拷貝),完全消除了數(shù)據(jù)從用戶態(tài)和內(nèi)核態(tài)之間拷貝數(shù)據(jù)帶來的開銷。

sendfile + DMA 雖然已經(jīng)足夠高效,但是依然存在兩個不足之處:

  • 方案本身需要引入新的硬件支持
  • 輸入文件描述符僅支持文件類型

splice

針對 sendfile + DMA 方案存在的不足,Linux 引入了 splice() 系統(tǒng)調(diào)用, splice() 不需要硬件支持,能夠?qū)崿F(xiàn)在任意的兩個文件描述符時之間傳輸數(shù)據(jù)。

splice() 是基于管道緩沖區(qū)機(jī)制實現(xiàn)的,所以兩個參數(shù)文件描述符必須有一個是管道設(shè)備。在實際開發(fā)中,splice() 作為實現(xiàn)零拷貝的首選,因此 sendfile() 的內(nèi)部實現(xiàn)也替換為了 splice()。

Go 語言中的零拷貝

現(xiàn)在有了前文的理論基礎(chǔ)后,我們來看下在 Go 語言中標(biāo)準(zhǔn)庫的零拷貝方法原型和應(yīng)用方法,筆者的 Go 版本為 go1.19 linux/amd64。

sendfile

sendfile 的方法原型為 syscall.Sendfile,文件路徑為 syscall/syscall_unix.go。

func?Sendfile(outfd?int,?infd?int,?offset?*int64,?count?int)?(written?int,?err?error)

一個簡單的使用示例:

package?main
import?(
?"fmt"
?"os"
?"syscall"
)
func?main()?{
?//?設(shè)置源文件
?src,?err?:=?os.Open("/tmp/source.txt")
?if?err?!=?nil?{
??panic(err)
?}
?defer?src.Close()
?//?設(shè)置目標(biāo)文件
?target,?err?:=?os.Create("/tmp/target.txt")
?if?err?!=?nil?{
??panic(err)
?}
?defer?target.Close()
?//?獲取源文件的文件描述符
?srcFd?:=?int(src.Fd())
?//?獲取目標(biāo)文件的文件描述符
?targetFd?:=?int(target.Fd())
?//?使用?Sendfile?實現(xiàn)零拷貝?(拷貝?10?個字節(jié))
?//?如果因為字符編碼導(dǎo)致的字符截斷問題?(如中文亂碼問題),?結(jié)果自動保留到截斷前的最后完整字節(jié)
?//?例如文件內(nèi)容為?“星期三四五六七”,count?參數(shù)為?4,?那么只會拷貝第一個字?(一個漢字?3?個字節(jié))
?//?但是需要注意的是,方法的返回值?written?不受影響?(和?count?參數(shù)保持一致)
?//?所以實際開發(fā)中,第三個參數(shù)?offset?必須設(shè)置正確,否則就可能引起亂碼或數(shù)據(jù)丟失問題
?n,?err?:=?syscall.Sendfile(targetFd,?srcFd,?nil,?4)
?if?err?!=?nil?{
??fmt.Println(err)
??return
?}
?fmt.Printf("寫入字節(jié)數(shù):?%d",?n)
}

splice

splice 的方法原型為 syscall.Splice,文件路徑為 syscall/zsyscall_linux_amd64.go。

func?Splice(rfd?int,?roff?*int64,?wfd?int,?woff?*int64,?len?int,?flags?int)?(n?int64,?err?error)

一個簡單的使用示例:

package?main
import?(
?"fmt"
?"os"
?"syscall"
)
func?main()?{
?//?設(shè)置源文件
?src,?err?:=?os.Open("/tmp/source.txt")
?if?err?!=?nil?{
??panic(err)
?}
?defer?src.Close()
?//?設(shè)置目標(biāo)文件
?target,?err?:=?os.Create("/tmp/target.txt")
?if?err?!=?nil?{
??panic(err)
?}
?defer?target.Close()
?//?創(chuàng)建管道文件
?//?作為兩個文件傳輸數(shù)據(jù)的中介
?pipeReader,?pipeWriter,?err?:=?os.Pipe()
?if?err?!=?nil?{
??panic(err)
?}
?defer?pipeReader.Close()
?defer?pipeWriter.Close()
?//?設(shè)置文件讀寫模式
?//?筆者在標(biāo)準(zhǔn)庫中沒有找到對應(yīng)的常量說明
?//?讀者可以參考這個文檔:
?//???https://pkg.go.dev/golang.org/x/sys/unix#pkg-constants
?//???SPLICE_F_NONBLOCK?=?0x2
?spliceNonBlock?:=?0x02
?//?使用?Splice?將數(shù)據(jù)從源文件描述符移動到管道?writer
?_,?err?=?syscall.Splice(int(src.Fd()),?nil,?int(pipeWriter.Fd()),?nil,?1024,?spliceNonBlock)
?if?err?!=?nil?{
??panic(err)
?}
?//?使用?Splice?將數(shù)據(jù)從管道?reader?移動到目標(biāo)文件描述符
?n,?err?:=?syscall.Splice(int(pipeReader.Fd()),?nil,?int(target.Fd()),?nil,?1024,?spliceNonBlock)
?if?err?!=?nil?{
??panic(err)
?}
?fmt.Printf("寫入字節(jié)數(shù):?%d",?n)
}

以上就是Go語言中零拷貝的原理與實現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Go零拷貝的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • gin?session中間件使用及源碼流程分析

    gin?session中間件使用及源碼流程分析

    這篇文章主要為大家介紹了gin?session中間件使用及源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • Go語言中三種不同md5計算方式的性能比較

    Go語言中三種不同md5計算方式的性能比較

    md5計算在我們?nèi)粘9ぷ鞯臅r候經(jīng)常能遇到,下面這篇文章主要介紹了Go語言中三種不同md5計算方式的性能比較,需要的朋友可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-01-01
  • go語言實現(xiàn)順序存儲的棧

    go語言實現(xiàn)順序存儲的棧

    這篇文章主要介紹了go語言實現(xiàn)順序存儲的棧,實例分析了Go語言實現(xiàn)順序存儲的棧的原理與各種常見的操作技巧,需要的朋友可以參考下
    2015-03-03
  • Golang 實現(xiàn)分片讀取http超大文件流和并發(fā)控制

    Golang 實現(xiàn)分片讀取http超大文件流和并發(fā)控制

    這篇文章主要介紹了Golang 實現(xiàn)分片讀取http超大文件流和并發(fā)控制,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言使用select{}阻塞main函數(shù)介紹

    Go語言使用select{}阻塞main函數(shù)介紹

    這篇文章主要介紹了Go語言使用select{}阻塞main函數(shù)介紹,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang連接池的幾種實現(xiàn)案例小結(jié)

    Golang連接池的幾種實現(xiàn)案例小結(jié)

    這篇文章主要介紹了Golang連接池的幾種實現(xiàn)案例小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Golang實現(xiàn)Md5校驗的示例代碼

    Golang實現(xiàn)Md5校驗的示例代碼

    本文主要介紹了Golang實現(xiàn)Md5校驗的示例代碼,要求接收方需要文件的md5值,和接收到的文件做比對,以免文件不完整,但引起bug,下面就一起來解決一下
    2024-08-08
  • Go語言七篇入門教程三函數(shù)方法及接口

    Go語言七篇入門教程三函數(shù)方法及接口

    這篇文章主要為大家介紹了Go語言的函數(shù)方法及接口的示例詳解,本文是Go語言七篇入門系列文章,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2021-11-11
  • Golang應(yīng)用執(zhí)行Shell命令實戰(zhàn)

    Golang應(yīng)用執(zhí)行Shell命令實戰(zhàn)

    本文主要介紹了Golang應(yīng)用執(zhí)行Shell命令實戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 使用Go module和GoLand初始化一個Go項目的方法

    使用Go module和GoLand初始化一個Go項目的方法

    這篇文章主要介紹了使用Go module和GoLand初始化一個Go項目,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12

最新評論