1. 程式人生 > >sendfile原理,實現檔案傳輸效能的提升

sendfile原理,實現檔案傳輸效能的提升

在apache,nginx,lighttpd等web伺服器當中,都有一項sendfile相關的配置,在一些網上的資料都有談到sendfile會提升檔案傳輸效能,那sendfile到底是什麼呢?它的原理又是如何呢? 

在傳統的檔案傳輸裡面(read/write方式),在實現上其實是比較複雜的,需要經過多次上下文的切換,我們看一下如下兩行程式碼: 
Java程式碼  收藏程式碼
  1. read(file, tmp_buf, len);      
  2.        write(socket, tmp_buf, len);      

        以上兩行程式碼是傳統的read/write方式進行檔案到socket的傳輸。 

當需要對一個檔案進行傳輸的時候,其具體流程細節如下: 
1、呼叫read函式,檔案資料被copy到核心緩衝區 
2、read函式返回,檔案資料從核心緩衝區copy到使用者緩衝區 
3、write函式呼叫,將檔案資料從使用者緩衝區copy到核心與socket相關的緩衝區。 
4、資料從socket緩衝區copy到相關協議引擎。 

以上細節是傳統read/write方式進行網路檔案傳輸的方式,我們可以看到,在這個過程當中,檔案資料實際上是經過了四次copy操作: 

硬碟—>核心buf—>使用者buf—>socket相關緩衝區—>協議引擎 

而sendfile系統呼叫則提供了一種減少以上多次copy,提升檔案傳輸效能的方法。Sendfile系統呼叫是在2.1版本核心時引進的: 

Java程式碼  收藏程式碼
  1. sendfile(socket, file, len);  

執行流程如下: 
1、sendfile系統呼叫,檔案資料被copy至核心緩衝區 
2、再從核心緩衝區copy至核心中socket相關的緩衝區 
3、最後再socket相關的緩衝區copy到協議引擎 

相較傳統read/write方式,2.1版本核心引進的sendfile已經減少了核心緩衝區到user緩衝區,再由user緩衝區到socket相關緩衝區的檔案copy,而在核心版本2.4之後,檔案描述符結果被改變,sendfile實現了更簡單的方式,系統呼叫方式仍然一樣,細節與2.1版本的不同之處在於,當檔案資料被複制到核心緩衝區時,不再將所有資料copy到socket相關的緩衝區,而是僅僅將記錄資料位置和長度相關的資料儲存到socket相關的快取,而實際資料將由DMA模組直接傳送到協議引擎,再次減少了一次copy操作。 


以上描述雖然簡單,但實際情況會更加複雜,本文只希望以簡單的方式大致上講解sendfile的原理,並不奢望通過本文能夠闡述出所有的細節,我也沒這水平,希望此文對各位朋友有所幫助。 

參考自:http://www.linuxjournal.com/article/6345

-----------------------------------------------------------------------------------------------------------------------------------------------------------

Linux kernel 2.2之前,(如圖)讀寫資料基本都是使用read系統呼叫和write系呼叫,以nginx來說如果一個請求建立,從磁碟的檔案到網路連線之間會通過硬體(DMA)---核心層---使用者層多次讀寫系統來完成檔案資料的複製傳輸:從核心層用read系統呼叫讀到使用者層,再從使用者層用write系統呼叫寫到核心層,每一次使用者層到核心層的進行一次上下文轉換,這種代價是非常昂貴的。甚至在沒有資料變化時這種複製尤其顯得多餘。如果nginx接受大量併發請求,這種系統呼叫就會非常頻繁,伺服器的效能就會下降。 

wKiom1OC_0Oih4A7AADfiB_7GRE907.jpg 

在Linux kernel2.2版本之後出現了一種叫做“零拷貝(zero-copy)”系統呼叫機制,目前很多應用伺服器如apache、samba、nginx都支援sendfile。注意:sendfile系統呼叫是一種檔案傳輸的系統呼叫和kernel系統呼叫關係不大。 

wKiom1ODB92wC3m3AADAG83M0Mg738.jpg 

如圖所示,nginx在支援了sendfile系統呼叫後,避免了核心層與使用者層的上線文切換(content swith)工作,大大減少了系統性能的開銷。

可以使用man 8 sendfile 進一步瞭解sendfile系統呼叫。

wKioL1ODENTx2Hf5AADvMSIdktA859.jpg

以下是對引數解釋

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 out_fd a file descriptor, open for writing, for the data to be written in_fd a file descriptor, open for reading, for the data to be read offset the offset in the input file to start transfer (e.g. a value of 0 indicates the beginning of the file). This is passed into the function and updated when the function returns. count the number of bytes to be transferred

正常情況下函式會返回被寫入的位元組數,如果出錯就返回-1

我們都知道在linux系統裡檔案描述符fd,可以是一個真實的檔案或者是一個裝置,例如一個網路socket,(當然linux世界裡一切皆檔案,這裡只是具體區別一下。)senfile需要輸入的檔案描述符是一個支援mmap的真實檔案或者裝置,那麼socket就不能作為輸入的fd,而輸出的fd是可以的。

具體在nginx的使用中可以通過在配置檔案nginx.conf中增加引數使用sendfile

http {

        sendfile on;

        tcp_nopush on;

        tcp_nodelay on;        

        ...........................

}

本文出自 “老徐的私房菜” 部落格,謝絕轉載!