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

詳解Golang中零拷貝的原理以及實踐

 更新時間:2023年07月02日 14:15:06   作者:藍胖子的編程夢  
零拷貝技術相信大家都有所耳聞,但是本文不僅會講述零拷貝技術的原理,并將從實際代碼出發(fā),看看零拷貝技術在golang中的應用,現(xiàn)在讓我們開始吧

零拷貝原理

零拷貝技術的原理本質(zhì)上就是減少數(shù)據(jù)的拷貝次數(shù),因為當調(diào)用傳統(tǒng)read write方法讀取文件內(nèi)容并返回給客戶端的時候,會經(jīng)過四次拷貝。我用golang代碼舉例如下

func?main()?{??
???http.HandleFunc("/tradition",?func(writer?http.ResponseWriter,?request?*http.Request)?{??
??????f,?_?:=?os.Open("./testmmap.txt")??
??????buf?:=?make([]byte,?1024)??
??????//?內(nèi)核拷貝到buf
??????n,?_?:=?f.Read(buf)??
??????//?buf拷貝到內(nèi)核
??????writer.Write(buf[:n])??
???})??
???http.ListenAndServe(":8080",?http.DefaultServeMux)??
}

如上面代碼所示,如果我們需要將本地testmmap.txt文件的內(nèi)容讀出來返回給客戶端。

testmmap.txt里只有一個hello的單詞,當服務啟動以后訪問接口便會返回hello。

(base) ?  codelearning git:(master) ? cat testmmap.txt
hello
(base) ?  codelearning git:(master) ? curl localhost:8080/tradition
hello

整個過程需要經(jīng)過read和write兩次系統(tǒng)調(diào)用,而每次read和write的調(diào)用將面臨用戶態(tài)和內(nèi)核態(tài)緩沖區(qū)之間數(shù)據(jù)的拷貝。

整個拷貝過程如上圖所示,磁盤和內(nèi)核間的數(shù)據(jù)傳遞可以通過DMA技術讓cpu不參與其中,但是內(nèi)核態(tài)和用戶態(tài)間的數(shù)據(jù)拷貝則需要經(jīng)過cpu參與,涉及到了兩次系統(tǒng)調(diào)用,和4次數(shù)據(jù)拷貝。

mmap+write

基于上述傳統(tǒng)文件的訪問方式,我們可以用mmap技術進行優(yōu)化,mmap可以讓用戶緩沖區(qū)buf的地址和文件磁盤地址建立映射,這樣訪問用戶緩沖區(qū)buf的數(shù)據(jù)就等效于訪問磁盤文件上的數(shù)據(jù)。

用mmap優(yōu)化后的文件訪問代碼如下:

??
http.HandleFunc("/mmap",?func(writer?http.ResponseWriter,?request?*http.Request)?{??
???f,?_?:=?os.Open("./testmmap.txt")??
???data,?err?:=?syscall.Mmap(int(f.Fd()),?0,?5,?syscall.PROT_READ,?syscall.MAP_SHARED)??
???if?err?!=?nil?{??
??????panic(err)??
???}??
???writer.Write(data)??
})

可以看到mmap返回了一個data的字節(jié)數(shù)組,這個字節(jié)數(shù)組的內(nèi)容就是映射了文件內(nèi)容,之后將字節(jié)數(shù)組寫入到響應體里。

syscall.Mmap(int(f.Fd()), 0, 5, syscall.PROT_READ, syscall.MAP_SHARED)

這里再解釋下mmap涉及的參數(shù)含義:

其中第一個參數(shù)代表要映射的文件描述符。

接著是映射的范圍是從0個字節(jié)到第5個字節(jié)。

第四個參數(shù) 代表映射的后的內(nèi)存區(qū)域是只讀的,類似的參數(shù)還有 syscall.PROT_WRITE表示內(nèi)存區(qū)域可以被寫入,syscall.PROT_NONE表示內(nèi)存區(qū)域不可訪問。

第五個參數(shù)表示 映射的內(nèi)存區(qū)域可以被多個進程共享,這樣一個進程修改了這個內(nèi)存區(qū)域的數(shù)據(jù),對其他進程是可見的,并且修改后的內(nèi)容會自動被操作系統(tǒng)同步到磁盤文件里。

類似的參數(shù)還有syscall.MAP_PRIVATE表示內(nèi)存區(qū)域是私有的,不可被其他進程訪問,聲明為私有后,每個進程擁有單獨的一份內(nèi)存映射拷貝,并且對此內(nèi)存區(qū)域進行修改不會被同步到磁盤文件。

注意整個過程,我們是沒有將文件內(nèi)容讀取到用戶空間的任何緩沖區(qū)的。我們僅僅是在write系統(tǒng)調(diào)用時,告訴了內(nèi)核一個地址(即字節(jié)數(shù)組的地址),而這個地址被mmap映射成了文件的地址。示意圖如下:

整個過程是用戶進程告訴內(nèi)核需要拷貝的數(shù)據(jù)數(shù)據(jù)的地址,然后內(nèi)核拷貝數(shù)據(jù)。

sendfile

基于上述mmap+write方式進行優(yōu)化后的文件內(nèi)容訪問減少了一次拷貝過程,不過系統(tǒng)調(diào)用還是兩次。如果用sendfile的話可以將系統(tǒng)調(diào)用減少到一次。

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

Sendfile的系統(tǒng)調(diào)用可以將目的文件描述符和源文件描述符傳遞進去,剩下的拷貝過程就交給內(nèi)核了。示意圖如下:

但是sendfile對源文件描述符有要求,普通的文件可以,如果源文件描述符是socket則不能用sendfile了。

splice

splice系統(tǒng)調(diào)用則是為了解決源文件描述符和目的文件描述符都是socket的情況而產(chǎn)生的。splice系統(tǒng)調(diào)用的原理是通過管道讓數(shù)據(jù)在源socket和目的socket之間進行傳輸。示意圖如下:

splice的系統(tǒng)調(diào)用方法如下:

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

注意splice系統(tǒng)調(diào)用需要保證傳入的文件描述符,rfd或者wfd至少一個是管道的文件描述符。創(chuàng)建管道也是一個系統(tǒng)調(diào)用,如下:

func?Pipe2(p?[]int,?flags?int)?error?

再回到通過splice系統(tǒng)調(diào)用的情況,可以看到要調(diào)用兩次splice系統(tǒng)調(diào)用,才能完成socket間的數(shù)據(jù)傳遞,因為splice系統(tǒng)調(diào)用會根據(jù)源文件描述符或目的文件描述符是管道的情況做不同的動作。

第一次系統(tǒng)調(diào)用,目的文件描述符是管道,那么內(nèi)核則會將管道和源文件描述符綁定在一起,注意此時是不會進行數(shù)據(jù)拷貝的。

第二次splice系統(tǒng)調(diào)用,源文件描述符是管道,那么內(nèi)核才會將管道內(nèi)的數(shù)據(jù)拷貝到目的文件描述符,由于在前一次,管道已經(jīng)和源文件描述符進行了綁定,所以這次的splice系統(tǒng)調(diào)用,實際上會將源文件描述符的數(shù)據(jù)拷貝到目的文件描述符。

整個過程,拋開DMA技術拷貝的次數(shù),一共只有一次數(shù)據(jù)拷貝的過程。

零拷貝在golang中的實踐

講完了零拷貝涉及的技術,我們來看看golang是如何運用這些技術的。拿一個比較常用的方法舉例,io.Copy, 其底層調(diào)用了copyBuffer方法,copyBuffer會判斷copy的目的接口Writer是否實現(xiàn)了ReaderFrom 接口,如果實現(xiàn)了則直接調(diào)用ReaderFrom  從src讀取數(shù)據(jù)。

func?Copy(dst?Writer,?src?Reader)?(written?int64,?err?error)?{??
???return?copyBuffer(dst,?src,?nil)??
}
func?copyBuffer(dst?Writer,?src?Reader,?buf?[]byte)?(written?int64,?err?error)?{??
???//?If?the?reader?has?a?WriteTo?method,?use?it?to?do?the?copy.??
???//?Avoids?an?allocation?and?a?copy.???if?wt,?ok?:=?src.(WriterTo);?ok?{??
??????return?wt.WriteTo(dst)??
???}??
???//?Similarly,?if?the?writer?has?a?ReadFrom?method,?use?it?to?do?the?copy.??
???if?rt,?ok?:=?dst.(ReaderFrom);?ok?{??
??????return?rt.ReadFrom(src)??
???}??
???//?進行傳統(tǒng)的文件讀取,代碼較長,暫時省略了。
???.......
???return?written,?err??
}

net.TcpConn實現(xiàn)了ReadFrom 接口,拿net.TcpConn舉例,看看它的實現(xiàn)。

func?(c?*TCPConn)?readFrom(r?io.Reader)?(int64,?error)?{??
???if?n,?err,?handled?:=?splice(c.fd,?r);?handled?{??
??????return?n,?err??
???}??
???if?n,?err,?handled?:=?sendFile(c.fd,?r);?handled?{??
??????return?n,?err??
???}??
???return?genericReadFrom(c,?r)??
}

最終net.TcpConn 會調(diào)用readFrom方法從來源io.Reader讀取數(shù)據(jù),而readFrom讀取數(shù)據(jù)用到的技術則是剛剛所講的零拷貝技術,這里用到了splice和sendFile系統(tǒng)調(diào)用,如果來源io.Reader是一個tcp連接或者時unix 連接則會調(diào)用splice進行數(shù)據(jù)拷貝,否則就會調(diào)用sendFile進行數(shù)據(jù)拷貝,具體細節(jié)我就不在這里展開了。

總之,你可以看到,其實我們平時用到的方法就用到了零拷貝技術,這些經(jīng)常說的底層原理離我們并不遙遠,學習,永遠懷著一顆謙卑的心。

到此這篇關于詳解Golang中零拷貝的原理以及實踐的文章就介紹到這了,更多相關Golang零拷貝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Golang內(nèi)存管理之內(nèi)存分配器詳解

    Golang內(nèi)存管理之內(nèi)存分配器詳解

    Go內(nèi)存分配器的設計思想來源于TCMalloc,全稱是Thread-Caching?Malloc,核心思想是把內(nèi)存分為多級管理,下面就來和大家深入聊聊Go語言內(nèi)存分配器的使用吧
    2023-06-06
  • Go?語言簡單實現(xiàn)Vigenere加密算法

    Go?語言簡單實現(xiàn)Vigenere加密算法

    這篇文章主要介紹了Go語言簡單實現(xiàn)Vigenere加密算法,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下
    2022-09-09
  • Golang Defer關鍵字特定操作詳解

    Golang Defer關鍵字特定操作詳解

    defer是Go語言中的延遲執(zhí)行語句,用來添加函數(shù)結(jié)束時執(zhí)行的代碼,常用于釋放某些已分配的資源、關閉數(shù)據(jù)庫連接、斷開socket連接、解鎖一個加鎖的資源,這篇文章主要介紹了golang中的defer函數(shù)理解,需要的朋友可以參考下
    2023-03-03
  • go語言標準庫fmt包的一鍵入門

    go語言標準庫fmt包的一鍵入門

    這篇文章主要為大家介紹了go語言標準庫fmt包的一鍵入門使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • goland Duration 和time的區(qū)別說明

    goland Duration 和time的區(qū)別說明

    這篇文章主要介紹了goland Duration 和time的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go?1.21.0?新增結(jié)構化日志記錄標準庫log/slog使用詳解

    Go?1.21.0?新增結(jié)構化日志記錄標準庫log/slog使用詳解

    這篇文章主要為大家介紹了Go?1.21.0?新增結(jié)構化日志記錄標準庫log/slog使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • Golang開發(fā)使用gorm時打印SQL語句方式

    Golang開發(fā)使用gorm時打印SQL語句方式

    這篇文章主要介紹了Golang開發(fā)使用gorm時打印SQL語句方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • Go?不支持?[]T轉(zhuǎn)換為[]interface類型詳解

    Go?不支持?[]T轉(zhuǎn)換為[]interface類型詳解

    這篇文章主要為大家介紹了Go不支持[]T轉(zhuǎn)換為[]interface類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Golang標準庫之errors包應用方式

    Golang標準庫之errors包應用方式

    Go語言的errors包提供了基礎的錯誤處理能力,允許通過errors.New創(chuàng)建自定義error對象,error在Go中是一個接口,通過實現(xiàn)Error方法來定義錯誤文本,對錯誤的比較通?;趯ο蟮刂?而非文本內(nèi)容,因此即使兩個錯誤文本相同
    2024-10-10
  • Go與Rust高性能解析JSON實現(xiàn)方法示例

    Go與Rust高性能解析JSON實現(xiàn)方法示例

    這篇文章主要為大家介紹了Go與Rust高性能的解析JSON實現(xiàn)方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12

最新評論

鲜城| 赤峰市| 山丹县| 义乌市| 温泉县| 外汇| 绵阳市| 平顶山市| 汉寿县| 曲阳县| 纳雍县| 安多县| 灌南县| 瑞安市| 上饶市| 远安县| 鄂温| 汝城县| 尚志市| 文水县| 微博| 和静县| 丰顺县| 临洮县| 祁连县| 宁远县| 怀仁县| 仁怀市| 光泽县| 新绛县| 拉孜县| 萝北县| 安化县| 南郑县| 敖汉旗| 马尔康县| 大关县| 徐汇区| 东源县| 威信县| 香格里拉县|