參考
https://zhuanlan.zhihu.com/p/20768200?refer=auxten
而成本很多時候的體現就是對計算資源的消耗,其中最重要的一個資源就是CPU資源。
Sendfile(2)在這個時代背景下於2003年前后被加入Linux Kernel,陸續在各大UNIX、Linux、Solaris平台上獲得了支持。這個系統內核調用本身被設計出來是用來從磁盤到TCP協議棧拷貝數據用的,但也我們也是可以把它用來做兩個文件之間的數據拷貝。
在Linux Kernel 2.6版本中,這個系統調用的原型是這樣的:
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
- in_fd 被打開是等待讀數據的fd.
- out_fd 被打開是等待寫數據的fd.
- Offset 是在正式開始讀取數據之前應該向前偏移的byte數.
- count 是需要在兩個fd之間“搬移”的數據的byte數.
參數特別注意的是:in_fd必須是一個支持mmap函數的文件描述符,也就是說必須指向真實文件,不能使socket描述符和管道。
out_fd必須是一個socket描述符。
由此可見sendfile幾乎是專門為在網絡上傳輸文件而設計的。
在sendfile(2)出現之前,我們想要把一個文件發送到socket上需要進行如下幾個步驟:
- 調用read(2)函數,文件數據被copy到內核緩沖區
- read(2)函數返回,文件數據從內核緩沖區copy到用戶緩沖區
- write(2)函數調用,將文件數據從用戶緩沖區copy到內核與socket相關的緩沖區。
- 數據從socket緩沖區copy到相關協議引擎。

相比sendfile(2),“Read & Write”方式帶來的性能損耗主要有兩點:
- 不必要的內存拷貝。
- 系統調用帶來的額外的用戶態/內核態上下文切換(Context Switch)。
而我們知道,上下文切換涉及到非常多的CPU、內存堆棧的操作,會讓分支預測失敗率大增,所以頻繁的上線文切換是高性能編程的大忌。類UNIX操作系統里都有一個系統命令vmstat可以展示當前系統的“Context Switch”的量(--system--下的cs列):
