<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux 百萬并發(fā)「零拷貝」實(shí)現(xiàn)原理

          共 2965字,需瀏覽 6分鐘

           ·

          2021-02-13 10:57




          傳統(tǒng)的I/O操作讀取文件并通過Socket發(fā)送,需要經(jīng)過4次上下文切換、2次CPU數(shù)據(jù)拷貝和2次DMA控制器數(shù)據(jù)拷貝,如下圖:


          從中也可以看得出提高性能可以從減少數(shù)據(jù)拷貝和上下文切換的次數(shù)著手,在Linux操作系統(tǒng)層面上有4種實(shí)現(xiàn)方案:內(nèi)存映射mmap、sendfile、splice、tee,這些實(shí)現(xiàn)中或多多少的減少數(shù)據(jù)拷貝次數(shù)或減少上下文切換次數(shù)。

          操作系統(tǒng)層面的減少數(shù)據(jù)拷貝次數(shù)主要是指用戶空間和內(nèi)核空間的數(shù)據(jù)拷貝,因?yàn)橹挥兴麄兊目截愂谴罅肯腃PU時(shí)間片的,而DMA控制器拷貝數(shù)據(jù)CPU參與的工作較少,只是輔助作用。


          現(xiàn)實(shí)中對(duì)零拷貝的概念有廣義和狹義之分,廣義上是指只要減少了數(shù)據(jù)拷貝的次數(shù)就稱之為零拷貝;狹義上是指真正的零拷貝,比如上例中避免2和3的CPU拷貝。


          下面我們逐一看看他們的設(shè)計(jì)思想和實(shí)現(xiàn)方案


          mmap內(nèi)存映射
          既然是內(nèi)存映射,首先來了解解下虛擬內(nèi)存和物理內(nèi)存的映射關(guān)系,虛擬內(nèi)存是操作系統(tǒng)為了方便操作而對(duì)物理內(nèi)存做的抽象,他們之間是靠頁(yè)表(Page Table)進(jìn)行關(guān)聯(lián)的,關(guān)系如下

          每個(gè)進(jìn)程都有自己的PageTable,進(jìn)程的虛擬內(nèi)存地址通過PageTable對(duì)應(yīng)于物理內(nèi)存,內(nèi)存分配具有惰性,它的過程一般是這樣的:進(jìn)程創(chuàng)建后新建與進(jìn)程對(duì)應(yīng)的PageTable,當(dāng)進(jìn)程需要內(nèi)存時(shí)會(huì)通過PageTable尋找物理內(nèi)存,如果沒有找到對(duì)應(yīng)的頁(yè)幀就會(huì)發(fā)生缺頁(yè)中斷,從而創(chuàng)建PageTable與物理內(nèi)存的對(duì)應(yīng)關(guān)系。虛擬內(nèi)存不僅可以對(duì)物理內(nèi)存進(jìn)行擴(kuò)展,還可以更方便地靈活分配,并對(duì)編程提供更友好的操作。


          內(nèi)存映射(mmap)是指用戶空間和內(nèi)核空間的虛擬內(nèi)存地址同時(shí)映射到同一塊物理內(nèi)存,用戶態(tài)進(jìn)程可以直接操作物理內(nèi)存,避免用戶空間和內(nèi)核空間之間的數(shù)據(jù)拷貝。

          它的具體執(zhí)行流程是這樣的

          1. 用戶進(jìn)程通過系統(tǒng)調(diào)用mmap函數(shù)進(jìn)入內(nèi)核態(tài),發(fā)生第1次上下文切換,并建立內(nèi)核緩沖區(qū);

          2. 發(fā)生缺頁(yè)中斷,CPU通知DMA讀取數(shù)據(jù);

          3. DMA拷貝數(shù)據(jù)到物理內(nèi)存,并建立內(nèi)核緩沖區(qū)和物理內(nèi)存的映射關(guān)系;

          4. 建立用戶空間的進(jìn)程緩沖區(qū)和同一塊物理內(nèi)存的映射關(guān)系,由內(nèi)核態(tài)轉(zhuǎn)變?yōu)橛脩魬B(tài),發(fā)生第2次上下文切換;

          5. 用戶進(jìn)程進(jìn)行邏輯處理后,通過系統(tǒng)調(diào)用Socket send,用戶態(tài)進(jìn)入內(nèi)核態(tài),發(fā)生第3次上下文切換;

          6. 系統(tǒng)調(diào)用Send創(chuàng)建網(wǎng)絡(luò)緩沖區(qū),并拷貝內(nèi)核讀緩沖區(qū)數(shù)據(jù);

          7. DMA控制器將網(wǎng)絡(luò)緩沖區(qū)的數(shù)據(jù)發(fā)送網(wǎng)卡,并返回,由內(nèi)核態(tài)進(jìn)入用戶態(tài),發(fā)生第4次上下文切換;

          總結(jié)
          1. 避免了內(nèi)核空間和用戶空間的2次CPU拷貝,但增加了1次內(nèi)核空間的CPU拷貝,整體上相當(dāng)于只減少了1次CPU拷貝;

          2. 針對(duì)大文件比較適合mmap,小文件則會(huì)造成較多的內(nèi)存碎片,得不償失;

          3. 當(dāng)mmap一個(gè)文件時(shí),如果文件被另一個(gè)進(jìn)程截獲可能會(huì)因?yàn)榉欠ㄔL問導(dǎo)致進(jìn)程被SIGBUS 信號(hào)終止;


          sendfile
          sendfile是在linux2.1引入的,它只需要2次上下文切換和1次內(nèi)核CPU拷貝、2次DMA拷貝,函數(shù)原型
          ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

          out_fd為文件描述符,in_fd為網(wǎng)絡(luò)緩沖區(qū)描述符,offset偏移量(默認(rèn)NULL),count文件大小。

          它的內(nèi)部執(zhí)行流程是這樣的

          1. 用戶進(jìn)程系統(tǒng)調(diào)用senfile,由用戶態(tài)進(jìn)入內(nèi)核態(tài),發(fā)生第1次上下文切換;

          2. CPU通知DMA控制器把文件數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū);

          3. 內(nèi)核空間自動(dòng)調(diào)用網(wǎng)絡(luò)發(fā)送功能并拷貝數(shù)據(jù)到網(wǎng)絡(luò)緩沖區(qū);

          4. CPU通知DMA控制器發(fā)送數(shù)據(jù);

          5. sendfile系統(tǒng)調(diào)用結(jié)束并返回,進(jìn)程由內(nèi)核態(tài)進(jìn)入用戶態(tài),發(fā)生第2次上下文切換;

          總結(jié)


          1. 數(shù)據(jù)處理完全是由內(nèi)核操作,減少了2次上下文切換,整個(gè)過程2次上下文切換、1次CPU拷貝,2次DMA拷貝;

          2. 雖然可以設(shè)置偏移量,但不能對(duì)數(shù)據(jù)進(jìn)行任何的修改;


          sendfile+DMA gather
          Linux2.4對(duì)sendfile進(jìn)行了優(yōu)化,為DMA控制器引入了gather功能,就是在不拷貝數(shù)據(jù)到網(wǎng)絡(luò)緩沖區(qū),而是將待發(fā)送數(shù)據(jù)的內(nèi)存地址和偏移量等描述信息存在網(wǎng)絡(luò)緩沖區(qū),DMA根據(jù)描述信息從內(nèi)核的讀緩沖區(qū)截取數(shù)據(jù)并發(fā)送。它的流程是如下

          1. 用戶進(jìn)程系統(tǒng)調(diào)用senfile,由用戶態(tài)進(jìn)入內(nèi)核態(tài),發(fā)生第1次上下文切換;

          2. CPU通知DMA控制器把文件數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū);

          3. 把內(nèi)核緩沖區(qū)地址和sendfile的相關(guān)參數(shù)作為數(shù)據(jù)描述信息存在網(wǎng)絡(luò)緩沖區(qū)中;

          4. CPU通知DMA控制器,DMA根據(jù)網(wǎng)絡(luò)緩沖區(qū)中的數(shù)據(jù)描述截取數(shù)據(jù)并發(fā)送;

          5. sendfile系統(tǒng)調(diào)用結(jié)束并返回,進(jìn)程由內(nèi)核態(tài)進(jìn)入用戶態(tài),發(fā)生第2次上下文切換;

          總結(jié)
          1. 需要硬件支持,如DMA;

          2. 整個(gè)過程2次上下文切換,0次CPU拷貝,2次DMA拷貝,實(shí)現(xiàn)真正意義上的零拷貝;

          3. 依然不能修改數(shù)據(jù);

          但那時(shí)的sendfile有個(gè)致命的缺陷,如果你查看Sendfild手冊(cè),你會(huì)發(fā)現(xiàn)如下描述

          in_fd不僅僅不能是socket,而且在2.6.33之前Sendfile的out_fd必須是socket,因此sendfile幾乎成了專為網(wǎng)絡(luò)傳輸而設(shè)計(jì)的,限制了其使用范圍比較狹窄。2.6.33之后out_fd才可以是任何file,于是乎出現(xiàn)了splice。



          splice
          鑒于Sendfile的缺點(diǎn),在Linux2.6.17中引入了Splice,它在讀緩沖區(qū)和網(wǎng)絡(luò)操作緩沖區(qū)之間建立管道避免CPU拷貝:先將文件讀入到內(nèi)核緩沖區(qū),然后再與內(nèi)核網(wǎng)絡(luò)緩沖區(qū)建立管道。它的函數(shù)原型
          ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);

          它的執(zhí)行流程如下

          1. 用戶進(jìn)程系統(tǒng)調(diào)用splice,由用戶態(tài)進(jìn)入內(nèi)核態(tài),發(fā)生第1次上下文切換;

          2. CPU通知DMA控制器把文件數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū);

          3. 建立內(nèi)核緩沖區(qū)和網(wǎng)絡(luò)緩沖區(qū)的管道;

          4. CPU通知DMA控制器,DMA從管道讀取數(shù)據(jù)并發(fā)送;

          5. splice系統(tǒng)調(diào)用結(jié)束并返回,進(jìn)程由內(nèi)核態(tài)進(jìn)入用戶態(tài),發(fā)生第2次上下文切換;

          總結(jié)
          1. 整個(gè)過程2次上下文切換,0次CPU拷貝,2次DMA拷貝,實(shí)現(xiàn)真正意義上的零拷貝;

          2. 依然不能修改數(shù)據(jù);

          3. fd_in和fd_out必須有一個(gè)是管道;


          tee
          tee與splice類同,但fd_in和fd_out都必須是管道。

          寫在最后


          各種I/O方案總結(jié)對(duì)比如上。

          良許個(gè)人微信


          添加良許個(gè)人微信即送3套程序員必讀資料


          → 精選技術(shù)資料共享

          → 高手如云交流社群





          本公眾號(hào)全部博文已整理成一個(gè)目錄,請(qǐng)?jiān)诠娞?hào)里回復(fù)「m」獲??!

          推薦閱讀:

          VS Code 真的會(huì)一統(tǒng)江湖嗎?

          從 "?" 到 "錕斤拷",這都是些啥玩意?

          推薦一款,比 Navicat 還要好用,功能還很強(qiáng)大的 工具!


          5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機(jī),樹莓派,等等。在公眾號(hào)內(nèi)回復(fù)「1024」,即可免費(fèi)獲?。?!


          瀏覽 76
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品久久久久久久久久妞妞 | 亚洲免费看片 | 国产乱╳╳╳AⅤ | 五月天中文字幕 | 日韩视频一二三区 |