隨筆-86  評論-33  文章-0  trackbacks-0

          我們平時通過網絡發送文件時會用到的兩個系統調用:
          read(file, tmp_buf, len);
          write(socket, tmp_buf, len);

          調用過程示意圖如下:

          在用戶空間調用 read() 讀取文件時發生兩次內存拷貝:

          1. DMA引擎將文件讀取到內核的文件緩沖區
          2. 調用返回用戶空間時將內核的文件緩沖區的數據復制到用戶空間的緩沖區

          接著調用 write() 把數據寫入 socket 時,又發生了兩次內存拷貝:

          1. 將用戶空間的緩沖區的數據復制到內核的 socket 緩沖區
          2. 將內核 socket 緩沖區的數據復制到網絡協議引擎

          也就是說,在整個文件發送的過程中,發生了四次內存拷貝。
          然后,數據讀取到用戶空間后并沒有做過任何加工處理,因此通過網絡發送文件時,根本沒有必要把文件內容復制到用戶空間。

          于是引入了 mmap():
          tmp_buf = mmap(file, len);
          write(socket, tmp_buf, len);

          調用過程示意圖:

          1. 調用 mmap() 時會將文件直接讀取到內核緩沖區,并把內核緩沖區直接共享到用戶空間
          2. 調用 write() 時,直接將內核緩沖區的數據復制到網絡協議引擎

          這樣一來,就少了用戶空間和內核空間之間的內存復制了。
          這種方式會有個問題,當前進程在調用 write() 時,另一個進程把文件清空了,程序就會報出 SIGBUS 類型錯誤。

          Linux Kernel 2.1 引進了 sendfile(),只需要一個系統調用來實現文件發送。
          sendfile(socket, file, len);

          調用過程示意圖:

          1. 調用 sendfile() 時會直接在內核空間把文件讀取到內核的文件緩沖區
          2. 將內核的文件緩沖區的數據復制到內核的 socket 緩沖區中
          3. 將內核的 socket 緩沖區的數據復制到網絡協議引擎

          從性能上看,這種方式只是少了一個系統調用而已,還是做了3次拷貝操作。

          Linux Kernel 2.4 改進了 sendfile(),調用接口沒有變化:
          sendfile(socket, file, len);

          調用過程示意圖:

          1. 調用 sendfile() 時會直接在內核空間把文件讀取到內核的文件緩沖區
          2. 內核的 socket 緩沖區中保存的是當前要發送的數據在內核的文件緩沖區中的位置和偏移量
          3. DMA gather copy 將內核的文件緩沖區的數據復制到網絡協議引擎

          這樣就只剩下2次拷貝啦。

          在許多 http server 中,都引入了 sendfile 的機制,如 nginx、lighttpd 等,它們正是利用 sendfile() 這個特性來實現高性能的文件發送的。

          posted on 2011-12-29 21:32 Derek.Guo 閱讀(706) 評論(0)  編輯  收藏 所屬分類: Linux/Unix
          MSN:envoydada@hotmail.com QQ:34935442
          主站蜘蛛池模板: 林芝县| 裕民县| 乳源| 武强县| 卢氏县| 兴海县| 台东市| 陕西省| 延安市| 岐山县| 伊川县| 汶上县| 保定市| 化州市| 巴里| 同心县| 湖南省| 博客| 尤溪县| 阿勒泰市| 海安县| 大足县| 永寿县| 永登县| 九台市| 利津县| 南充市| 简阳市| 吴堡县| 富源县| 天柱县| 仁怀市| 江孜县| 九寨沟县| 黔江区| 鄱阳县| 河北区| 元谋县| 柘荣县| 黔东| 梧州市|