linux內核系統調用--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到相關協議引擎。

一般來說一個網絡應用是通過讀硬盤數據,然后寫數據到 socket 來完成網絡傳輸的。上面2行用代碼解釋了這一點,不過上面2行簡單的代碼掩蓋了底層的很多操作。來看看底層是怎么執行上面2行代碼的:

1、系統調用 read() 產生一個上下文切換:從 user mode 切換到 kernel mode,然后 DMA 執行拷貝,把文件數據從硬盤讀到一個 kernel buffer 里。
2、數據從 kernel buffer 拷貝到 user buffer,然后系統調用 read() 返回,這時又產生一個上下文切換:從kernel mode 切換到 user mode。
3、系統調用 write() 產生一個上下文切換:從 user mode 切換到 kernel mode,然后把步驟2讀到 user buffer 的數據拷貝到 kernel buffer(數據第2次拷貝到 kernel buffer),不過這次是個不同的 kernel buffer,這個 buffer 和 socket 相關聯。
4、系統調用 write() 返回,產生一個上下文切換:從 kernel mode 切換到 user mode(第4次切換了),然后 DMA 從 kernel buffer 拷貝數據到協議棧(第4次拷貝了)。

上面4個步驟有4次上下文切換,有4次拷貝,我們發現如果能減少切換次數和拷貝次數將會有效提升性能。在kernel 2.0+ 版本中,系統調用 sendfile() 就是用來簡化上面步驟提升性能的。sendfile() 不但能減少切換次數而且還能減少拷貝次數。

 

以上細節是傳統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操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM