在學習sendfille之前,我們先來了解一下瀏覽器訪問頁面時,后台服務器的大致工作流程。
下圖是從用戶訪問某個頁面到頁面的顯示這幾秒鍾的時間當中,在后台的整個工作過程。
如上圖,黑色箭頭所示的過程,是傳統方式的數據傳輸:
第一步:當用戶請求www.test.com/index.html網頁時,nginx服務器通過網卡接收到請求后,系統調用read導致了從用戶空間到內核空間的上下文切換,內核再向磁盤發送關於Index.html的請求,DMA模塊從磁盤中讀取index.html(以下簡稱為數據)發送到內核緩沖區,完成第一次復制。
第二步:系統調用read返回,導致從內核空間到用戶空間的上下文切換,此時數據已存放在用戶緩沖區中,完成第二次復制。
第三步:系統調用write導致了從用戶空間到內核空間的上下文切換,此時數據存放在了內核空間與socket相關的特定緩沖區中(注意這里要將第一步中的內核緩沖區區分開來),完成第三次復制。
第四步:系統再次調用返回,導致了第四次上下文切換,數據來到了相關協議引擎,完成了第四次復制。再有相關引擎返回給用戶瀏覽器。
這里的復制操作均由DMA模塊來執行完成。
了解了這個過程后,我們來看一下sendfile和mmap到底是什么?
sendfile“零拷貝”
它的目的也是提升數據的傳輸性能
如上圖中的紅色箭頭,sendfile機制實際上就是節省了從內核空間到用戶空間,再從用戶空間到內核空間這個往返的過程,相比於傳統的方式節省了一次數據的復制,提高了訪問效率。它的大致步驟是:
第一步:從磁盤到內核空間,完成第一次復制。
第二步:從內核空間到socket buffer(注意這里的緩沖區也要和傳統方式中第三部的socket區別開來,可以理解為另一塊與socket相關的特定緩沖區),完成第二次復制。
第三步:數據從socket buffer到協議相關引擎,完成第三次復制。
自內核版本號2.1,引進了sendfile2.4之后,sendfile實現了更簡單的方式,不同之處在於,文件到達內核緩沖區后,不必再將數據全部復制到socket buffer緩沖區,而只將記錄數據位置和長度相關的數據保存到socket buffer,而數據實際由DMA模塊直接發送給協議相關引擎,再次降低了復制操作。
mmap內存映射
就是在用戶的虛擬地址空間中尋找空閑的一段地址進行對文件的操作,不必再調用read、write系統調用,它的最終目的是將磁盤中的文件映射到用戶進程的虛擬地址空間,實現用戶進程對文件的直接讀寫,減少了文件復制的開銷,提高了用戶的訪問效率。
總之,sendfile和mmap的目的就是減少了數據從磁盤到用戶過程中的復制操作,使數據傳輸更加高效,提高用戶的訪問速率。
————————————————
原文鏈接:https://blog.csdn.net/keil_wang/article/details/86688271