<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                到目前為止,幾乎所有人都聽說過Linux下所謂的零拷貝功能,但我經常遇到對這個主題沒有充分了解的人。因此,我決定撰寫一些文章,深入研究這個問題,希望能夠揭開這個有用的特征。在本文中,我們從用戶模式應用程序的角度來看零拷貝,因此故意省略了內核級別的詳細信息。 什么是零拷貝? 為了更好地理解問題的解決方案,我們首先需要了解問題本身。讓我們來看一下網絡服務器的簡單過程所涉及的內容,該過程通過網絡將存儲在文件中的數據提供給客戶端。這是一些示例代碼: ~~~ read(file,tmp_buf,len); write(socket,tmp_buf,len); ~~~ 看起來很簡單;你會認為只有那兩個系統調用沒有太多的開銷。實際上,這不可能是事實。在這兩個調用之后,數據已被復制至少四次,并且幾乎已經執行了多個用戶/內核上下文切換。(實際上這個過程要復雜得多,但我想保持簡單)。為了更好地了解所涉及的過程,請查看圖1.頂部顯示上下文切換,底部顯示復制操作。 ![](https://box.kancloud.cn/1dd3632e6962e7a51918691f59ea8ade_427x316.png) 圖1.兩個示例系統調用中的復制 第一步:讀取系統調用導致從用戶模式到內核模式的上下文切換。第一個副本由DMA引擎執行,DMA引擎從磁盤讀取文件內容并將它們存儲到內核地址空間緩沖區中。 第二步:將數據從內核緩沖區復制到用戶緩沖區,并返回讀取系統調用。從調用返回導致從內核切換到用戶模式的上下文。現在數據存儲在用戶地址空間緩沖區中,它可以再次開始。 第三步:寫系統調用導致從用戶模式到內核模式的上下文切換。執行第三個副本以再次將數據放入內核地址空間緩沖區。但是這一次,數據被放入一個不同的緩沖區,一個與套接字相關的緩沖區。 第四步:寫入系統調用返回,創建第四個上下文切換。獨立和異步地,當DMA引擎將數據從內核緩沖區傳遞到協議引擎時,會發生第四個副本。你可能會問自己,“你是什么意思獨立和異步?在呼叫返回之前是不是傳輸了數據?“呼叫返回,實際上并不保證傳輸;它甚至不能保證傳輸的開始。它只是意味著以太網驅動程序在其隊列中有自由描述符并已接受我們的數據進行傳輸。在我們之前可能有許多數據包排隊。除非驅動程序/硬件實現優先級環或隊列,否則數據以先進先出的方式傳輸。(圖1中的分叉DMA副本說明了最后一個副本可以延遲的事實)。 正如您所看到的,實際上并不需要進行大量的數據復制。可以消除一些重復,以減少開銷并提高性能。作為驅動程序開發人員,我使用具有一些非常高級功能的硬件。某些硬件可以完全繞過主存儲器并將數據直接傳輸到另一個設備。此功能消除了系統內存中的副本,并且是一件好事,但并非所有硬件都支持它。還存在必須為網絡重新打包來自磁盤的數據的問題,這引入了一些復雜性。為了消除開銷,我們可以從消除內核和用戶緩沖區之間的一些復制開始。 消除副本的一種方法是跳過調用read而不是調用mmap。例如: ~~~ tmp_buf = mmap(file,len); write(socket,tmp_buf,len); ~~~ 為了更好地了解所涉及的過程,請參見圖2.上下文切換保持不變。 ![](https://box.kancloud.cn/97e8c95002f98066dfa7fb63408292b3_420x314.png) 圖2.調用mmap 第一步:mmap系統調用導致文件內容被DMA引擎復制到內核緩沖區。然后與用戶進程共享緩沖區,而不在內核和用戶存儲空間之間執行任何復制。 第二步:寫入系統調用使內核將數據從原始內核緩沖區復制到與套接字關聯的內核緩沖區中。 第三步:第三個副本發生在DMA引擎將數據從內核套接字緩沖區傳遞到協議引擎時。 通過使用mmap而不是read,我們減少了內核復制數據量的一半。當傳輸大量數據時,這會產生相當好的結果。然而,這種改進并非沒有代價;使用mmap + write方法時存在隱藏的陷阱。當內存映射文件然后調用write而另一個進程截斷同一文件時,您將陷入其中一個。您的寫入系統調用將被總線錯誤信號SIGBUS中斷,因為您執行了錯誤的內存訪問。該信號的默認行為是終止進程并轉儲核心 - 而不是網絡服務器最理想的操作。有兩種方法可以解決這個問題。 第一種方法是為SIGBUS信號安裝信號處理程序,然后在處理程序中簡單地調用return。通過這樣做,write系統調用返回它在被中斷之前寫入的字節數并且errno設置為成功。讓我指出,這將是一個糟糕的解決方案,一個治療癥狀,而不是問題的原因。因為SIGBUS發出信號表明該過程嚴重錯誤,我不鼓勵將其作為解決方案。 第二個解決方案涉及內核中的文件租用(在Microsoft Windows中稱為“機會鎖定”)。這是解決此問題的正確方法。通過在文件描述符上使用租用,您可以在特定文件上使用內核。然后,您可以從內核請求讀/寫租約。當另一個進程試圖截斷您正在傳輸的文件時,內核會向您發送一個實時信號RT\_SIGNAL\_LEASE信號。它告訴您內核正在破壞該文件的寫入或讀取租約。您的寫入調用在程序訪問無效地址之前被中斷,并被SIGBUS信號殺死。寫調用的返回值是中斷前寫入的字節數,errno將設置為成功。下面是一些示例代碼,展示了如何從內核獲得租約: ~~~ if(fcntl(fd,F_SETSIG,RT_SIGNAL_LEASE)== -1){ perror(“內核租約設置信號”); 返回-1; } / * l_type可以是F_RDLCK F_WRLCK * / if(fcntl(fd,F_SETLEASE,l_type)){ perror(“內核租約集類型”); 返回-1; } ~~~ 您應該在獲取文件之前獲得租約,并在完成后中斷租約。這是通過使用租約類型F\_UNLCK調用fcntl F\_SETLEASE來實現的。 發送文件 在內核版本2.1中,引入了sendfile系統調用以簡化通過網絡和兩個本地文件之間的數據傳輸。sendfile的引入不僅減少了數據復制,還減少了上下文切換。像這樣使用它: ~~~ sendfile(socket,file,len); ~~~ 為了更好地了解所涉及的過程,請查看圖3 ![](https://box.kancloud.cn/5eddd594c8ecb22eeba8994ff13bb58a_419x314.png) 圖3.用Sendfile替換讀寫 第一步:sendfile系統調用導致文件內容被DMA引擎復制到內核緩沖區。然后,內核將數據復制到與套接字關聯的內核緩沖區中。 第二步:第三個副本發生在DMA引擎將數據從內核套接字緩沖區傳遞到協議引擎時。 您可能想知道如果另一個進程截斷我們使用sendfile系統調用傳輸的文件會發生什么。如果我們沒有注冊任何信號處理程序,sendfile調用只會返回它在被中斷之前傳輸的字節數,并且errno將被設置為成功。 但是,如果我們在調用sendfile之前從文件內核獲得租約,則行為和返回狀態完全相同。我們還在sendfile調用返回之前獲得RT\_SIGNAL\_LEASE信號。 到目前為止,我們已經能夠避免讓內核生成多個副本,但我們仍然只留下一個副本。這可以避免嗎?當然,在硬件的幫助下。為了消除內核完成的所有數據復制,我們需要一個支持收集操作的網絡接口。這僅僅意味著等待傳輸的數據不需要在連續的存儲器中;它可以分散在各種存儲位置。在內核版本2.4中,修改了套接字緩沖區描述符以適應這些要求 - 在Linux下稱為零拷貝。這種方法不僅減少了多個上下文切換,還消除了處理器完成的數據復制。對于用戶級應用程序,沒有任何更改,因此代碼仍然如下所示: ~~~ sendfile(socket,file,len); ~~~ 為了更好地了解所涉及的過程,請查看圖4。 ![](https://box.kancloud.cn/39feb0ed0883de89f1ece57b9e3b3e37_429x312.png) 圖4.支持收集的硬件可以從多個內存位置組裝數據,從而消除了另一個副本。 第一步:sendfile系統調用導致文件內容被DMA引擎復制到內核緩沖區。 第二步:沒有數據被復制到套接字緩沖區。相反,只有具有關于數據的下落和長度信息的描述符被附加到套接字緩沖區。DMA引擎將數據直接從內核緩沖區傳遞到協議引擎,從而消除了剩余的最終副本。 因為數據實際上仍然是從磁盤復制到內存,從內存復制到線路,所以有些人可能會認為這不是真正的零拷貝。但是,從操作系統的角度來看,這是零拷貝,因為內核緩沖區之間的數據不會重復。使用零拷貝時,除了復制避免之外,還可以獲得其他性能優勢,例如更少的上下文切換,更少的CPU數據高速緩存污染以及無CPU校驗和計算。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看