這個 syscall 能更有效的利用底層 file system 提供的機制加速 file 的 copy, 或更確切的說, 減少不必要的資源開銷.
File copy 說起來簡單, 舉 cp
命令而言, 一般的檔案內容的 copy 實現如下
$ strace cp src dest
...
open("src", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1048576, ...}) = 0
open("dest", O_WRONLY|O_CREAT|O_EXCL, 0644) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc7977c3000
ioctl(3, FS_IOC_FIEMAP, 0x7ffd6f7c7370) = -1 EOPNOTSUPP (Operation not supported)
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 131072
lseek(4, 131072, SEEK_CUR) = 131072
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 131072
lseek(4, 131072, SEEK_CUR) = 262144
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 131072
lseek(4, 131072, SEEK_CUR) = 393216
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 131072
lseek(4, 131072, SEEK_CUR) = 524288
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 131072
lseek(4, 131072, SEEK_CUR) = 655360
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 131072
lseek(4, 131072, SEEK_CUR) = 786432
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 131072
lseek(4, 131072, SEEK_CUR) = 917504
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 131072
lseek(4, 131072, SEEK_CUR) = 1048576
read(3, "", 131072) = 0
ftruncate(4, 1048576) = 0
close(4) = 0
close(3) = 0
...
基本上就是從來源 read()
出資料後 write()
到目標. 這中間的問題有
- Buffer 的管理. 上面
cp
用的 buffer size 是最佳的嗎? - 資料於 kernel <-> user space 間來回 copy 浪費 CPU 時間及 memory bandwidth
- 大量 context switch 帶來的額外負擔 - 尤其是 SSD 及 NVM 這樣的高速存儲
- 如何更高效的處理檔案上的洞 (hole)?
- 就算來源及目標檔案都在同一個 file system 中, 也不好實現 COW copy
- Buffer 經過了 user space, checksum 就得要重新計算
- 在 block level 是否有機會更高效的 dedup?
(https://lwn.net/Articles/260795/) - 在 NFS 上無法達成 server side copy, 造成資料在 client & server 間來回傳輸
基於以上需求及問題, kernel 社區經過多次討論及嘗試, 例如 reflink(), 最後成為即將進到 4.5 中的 copy_file_range(), 基本解決了上面提到的大部份問題, 當然, 這也需要 file system 內部實現的支持. 以後 cp
命令應該會比現在高效不少.