最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時間:2023年08月16日 08:50:51   作者:洋芋編程  
零拷貝是相對于用戶態(tài)來講的,即數(shù)據(jù)在用戶態(tài)不發(fā)生任何拷貝,那么零拷貝的原理是什么,又是如何實(shí)現(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)層面實(shí)現(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ī)制實(shí)現(xiàn)的,所以兩個參數(shù)文件描述符必須有一個是管道設(shè)備。在實(shí)際開發(fā)中,splice() 作為實(shí)現(xiàn)零拷貝的首選,因此 sendfile() 的內(nèi)部實(shí)現(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?實(shí)現(xiàn)零拷貝?(拷貝?10?個字節(jié))
?//?如果因?yàn)樽址幋a導(dǎo)致的字符截斷問題?(如中文亂碼問題),?結(jié)果自動保留到截斷前的最后完整字節(jié)
?//?例如文件內(nèi)容為?“星期三四五六七”,count?參數(shù)為?4,?那么只會拷貝第一個字?(一個漢字?3?個字節(jié))
?//?但是需要注意的是,方法的返回值?written?不受影響?(和?count?參數(shù)保持一致)
?//?所以實(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語言中零拷貝的原理與實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Go零拷貝的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang中iota的用法小結(jié)

    golang中iota的用法小結(jié)

    iota是Go語言中的一個預(yù)定義標(biāo)識符,用于表示const聲明塊中每個常量的偏移值,本文主要介紹了golang中iota的用法小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • Go語言func匿名函數(shù)閉包示例詳解

    Go語言func匿名函數(shù)閉包示例詳解

    這篇文章主要為大家介紹了Go語言func匿名函數(shù)閉包示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • GIN的路由以及傳參問題

    GIN的路由以及傳參問題

    本文主要介紹了GIN的路由以及傳參問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Gin框架使用panic處理中間件問題詳解

    Gin框架使用panic處理中間件問題詳解

    這篇文章主要介紹了Gin框架使用panic處理中間件問題,在 Gin 框架中,錯誤處理和 panic 處理是非常重要的功能。當(dāng)處理 HTTP 請求時,可能會出現(xiàn)各種各樣的錯誤,例如數(shù)據(jù)庫連接錯誤、網(wǎng)絡(luò)錯誤、權(quán)限問題等等
    2023-04-04
  • 淺談Go中數(shù)字轉(zhuǎn)換字符串的正確姿勢

    淺談Go中數(shù)字轉(zhuǎn)換字符串的正確姿勢

    這篇文章主要介紹了淺談Go中數(shù)字轉(zhuǎn)換字符串的正確姿勢,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • Go語言字符串及strings和strconv包使用實(shí)例

    Go語言字符串及strings和strconv包使用實(shí)例

    字符串是工作中最常用的,值得我們專門的練習(xí)一下,下面這篇文章主要給大家介紹了關(guān)于Go語言字符串及strings和strconv包使用的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • GO語言實(shí)現(xiàn)串口簡單通訊

    GO語言實(shí)現(xiàn)串口簡單通訊

    本文分享了使用Go語言進(jìn)行串口通訊的實(shí)踐過程,詳細(xì)介紹了串口配置、數(shù)據(jù)發(fā)送與接收的代碼實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11
  • 如何判斷Golang接口是否實(shí)現(xiàn)的操作

    如何判斷Golang接口是否實(shí)現(xiàn)的操作

    這篇文章主要介紹了如何判斷Golang接口是否實(shí)現(xiàn)的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 詳解Golang中channel的實(shí)現(xiàn)

    詳解Golang中channel的實(shí)現(xiàn)

    channel俗稱管道,用于數(shù)據(jù)傳遞或數(shù)據(jù)共享,其本質(zhì)是一個先進(jìn)先出的隊(duì)列,使用goroutine+channel進(jìn)行數(shù)據(jù)通訊簡單高效,同時也線程安全,本文就給大家講講Golang中channel的實(shí)現(xiàn),需要的朋友可以參考下
    2023-09-09
  • 讓Go反射變快的方法實(shí)例探究

    讓Go反射變快的方法實(shí)例探究

    反射允許你在運(yùn)行時獲得有關(guān) Go 類型的信息,如果你曾經(jīng)愚蠢地嘗試編寫 json.Unmarshal 之類的新版本,本文將探討的就是如何使用反射來填充結(jié)構(gòu)體值
    2024-01-01

最新評論

德化县| 菏泽市| 祥云县| 习水县| 关岭| 石楼县| 元江| 凤山市| 大庆市| 德钦县| 尚义县| 扎兰屯市| 筠连县| 内江市| 丰镇市| 紫金县| 澎湖县| 台中县| 濮阳县| 攀枝花市| 西乌珠穆沁旗| 寿宁县| 长宁区| 出国| 大埔区| 张家口市| 台安县| 宾川县| 宣恩县| 从化市| 云霄县| 夏邑县| 福州市| 霍山县| 出国| 钦州市| 崇州市| 绵阳市| 青龙| 东乡县| 班戈县|