使用sendfile系统调用加速文件传输
今天学到了一个新系统调用:sendfile,它专门用来在两个文件描述符之间传输数据.
#include <sys/sendfile.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
在引入 sendfile
系统调用之前,要传输文件内容(拷贝文件或通过网络传输文件)往往需要经过下面几个步骤:
- 调用
read
系统调用读取文件内容,这会产生一次上下文切换 - 将内核中的数据拷贝到用户空间
- 调用
wirte
函数调用写入数据,这又会产生一次上下文切换
如此算来,至少要经过两次上下文切换和一次数据拷贝的动作,甚至在文件内容巨大的时候这个流程可能需要流转多次。
考虑到拷贝文件这个动作实在太过常见,因此系统引入了 sendtofile
系统调用,这样只需要一个系统调用就行了,在传输巨大文件时能够极大地节省CPU时间。
另外,通过strace跟踪系统调用,居然发现 cp
和 dd
命令还是用的read和write的方式来进行文件之间的数据传输的。
date >d.tmp strace cp d.tmp d1.tmp 2>&1 echo '------------------------------------' strace dd if=d.tmp of=d.tmp bs=512 2>&1
execve("/usr/bin/cp", ["cp", "d.tmp", "d1.tmp"], 0x7ffeb19b6740 /* 55 vars */) = 0 brk(NULL) = 0x560d6be64000 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffff3d40290) = -1 EINVAL (无效的参数) access("/etc/ld.so.preload", R_OK) = -1 ENOENT (没有那个文件或目录) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=328885, ...}) = 0 mmap(NULL, 328885, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1b680aa000 close(3) = 0 openat(AT_FDCWD, "/usr/lib/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\30\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=30504, ...}) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1b680a8000 mmap(NULL, 2125984, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1b67ea0000 mprotect(0x7f1b67ea7000, 2093056, PROT_NONE) = 0 mmap(0x7f1b680a6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f1b680a6000 close(3) = 0 openat(AT_FDCWD, "/usr/lib/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\21\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=18112, ...}) = 0 mmap(NULL, 2113560, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1b67c9b000 mprotect(0x7f1b67c9f000, 2093056, PROT_NONE) = 0 mmap(0x7f1b67e9e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f1b67e9e000 close(3) = 0 openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360o\2\0\0\0\0\0"..., 832) = 832 lseek(3, 64, SEEK_SET) = 64 read(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784) = 784 lseek(3, 848, SEEK_SET) = 848 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 lseek(3, 880, SEEK_SET) = 880 read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\250\257l\201\313(\243{\363\245F\227\v\366B$"..., 68) = 68 fstat(3, {st_mode=S_IFREG|0755, st_size=2133648, ...}) = 0 lseek(3, 64, SEEK_SET) = 64 read(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784) = 784 lseek(3, 848, SEEK_SET) = 848 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 lseek(3, 880, SEEK_SET) = 880 read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\250\257l\201\313(\243{\363\245F\227\v\366B$"..., 68) = 68 mmap(NULL, 1844408, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1b67ad8000 mprotect(0x7f1b67afd000, 1654784, PROT_NONE) = 0 mmap(0x7f1b67afd000, 1351680, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f1b67afd000 mmap(0x7f1b67c47000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16f000) = 0x7f1b67c47000 mmap(0x7f1b67c91000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b8000) = 0x7f1b67c91000 mmap(0x7f1b67c97000, 13496, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1b67c97000 close(3) = 0 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1b67ad5000 arch_prctl(ARCH_SET_FS, 0x7f1b67ad5740) = 0 mprotect(0x7f1b67c91000, 12288, PROT_READ) = 0 mprotect(0x7f1b67e9e000, 4096, PROT_READ) = 0 mprotect(0x7f1b680a6000, 4096, PROT_READ) = 0 mprotect(0x560d69f87000, 4096, PROT_READ) = 0 mprotect(0x7f1b68125000, 4096, PROT_READ) = 0 munmap(0x7f1b680aa000, 328885) = 0 brk(NULL) = 0x560d6be64000 brk(0x560d6be85000) = 0x560d6be85000 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=6180864, ...}) = 0 mmap(NULL, 6180864, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1b674f0000 close(3) = 0 geteuid() = 1000 stat("d1.tmp", {st_mode=S_IFREG|0644, st_size=43, ...}) = 0 stat("d.tmp", {st_mode=S_IFREG|0644, st_size=43, ...}) = 0 newfstatat(AT_FDCWD, "d1.tmp", {st_mode=S_IFREG|0644, st_size=43, ...}, 0) = 0 openat(AT_FDCWD, "d.tmp", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=43, ...}) = 0 openat(AT_FDCWD, "d1.tmp", O_WRONLY|O_TRUNC) = 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) = 0x7f1b680d9000 read(3, "2019\345\271\264 09\346\234\210 16\346\227\245 \346\230\237\346\234\237\344\270\200 17"..., 131072) = 43 write(4, "2019\345\271\264 09\346\234\210 16\346\227\245 \346\230\237\346\234\237\344\270\200 17"..., 43) = 43 read(3, "", 131072) = 0 close(4) = 0 close(3) = 0 munmap(0x7f1b680d9000, 139264) = 0 lseek(0, 0, SEEK_CUR) = 40 close(0) = 0 close(1) = 0 close(2) = 0 exit_group(0) = ? +++ exited with 0 +++ ------------------------------------ execve("/usr/bin/dd", ["dd", "if=d.tmp", "of=d.tmp", "bs=512"], 0x7ffde0cfb688 /* 55 vars */) = 0 brk(NULL) = 0x557745690000 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffed1db1bb0) = -1 EINVAL (无效的参数) access("/etc/ld.so.preload", R_OK) = -1 ENOENT (没有那个文件或目录) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=328885, ...}) = 0 mmap(NULL, 328885, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1fe38dd000 close(3) = 0 openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360o\2\0\0\0\0\0"..., 832) = 832 lseek(3, 64, SEEK_SET) = 64 read(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784) = 784 lseek(3, 848, SEEK_SET) = 848 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 lseek(3, 880, SEEK_SET) = 880 read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\250\257l\201\313(\243{\363\245F\227\v\366B$"..., 68) = 68 fstat(3, {st_mode=S_IFREG|0755, st_size=2133648, ...}) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1fe38db000 lseek(3, 64, SEEK_SET) = 64 read(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784) = 784 lseek(3, 848, SEEK_SET) = 848 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 lseek(3, 880, SEEK_SET) = 880 read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\250\257l\201\313(\243{\363\245F\227\v\366B$"..., 68) = 68 mmap(NULL, 1844408, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1fe3718000 mprotect(0x7f1fe373d000, 1654784, PROT_NONE) = 0 mmap(0x7f1fe373d000, 1351680, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f1fe373d000 mmap(0x7f1fe3887000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16f000) = 0x7f1fe3887000 mmap(0x7f1fe38d1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b8000) = 0x7f1fe38d1000 mmap(0x7f1fe38d7000, 13496, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1fe38d7000 close(3) = 0 arch_prctl(ARCH_SET_FS, 0x7f1fe38dc580) = 0 mprotect(0x7f1fe38d1000, 12288, PROT_READ) = 0 mprotect(0x557743ce2000, 4096, PROT_READ) = 0 mprotect(0x7f1fe3958000, 4096, PROT_READ) = 0 munmap(0x7f1fe38dd000, 328885) = 0 rt_sigaction(SIGINT, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 rt_sigaction(SIGUSR1, {sa_handler=0x557743cd3f60, sa_mask=[INT USR1], sa_flags=SA_RESTORER, sa_restorer=0x7f1fe37527e0}, NULL, 8) = 0 rt_sigaction(SIGINT, {sa_handler=0x557743cd3f50, sa_mask=[INT USR1], sa_flags=SA_RESTORER|SA_NODEFER|SA_RESETHAND, sa_restorer=0x7f1fe37527e0}, NULL, 8) = 0 brk(NULL) = 0x557745690000 brk(0x5577456b1000) = 0x5577456b1000 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=6180864, ...}) = 0 mmap(NULL, 6180864, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1fe3133000 close(3) = 0 openat(AT_FDCWD, "d.tmp", O_RDONLY) = 3 dup2(3, 0) = 0 close(3) = 0 lseek(0, 0, SEEK_CUR) = 0 openat(AT_FDCWD, "d.tmp", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 dup2(3, 1) = 1 close(3) = 0 read(0, "", 512) = 0 close(0) = 0 close(1) = 0 openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 0 fstat(0, {st_mode=S_IFREG|0644, st_size=2997, ...}) = 0 read(0, "# Locale name alias data base.\n#"..., 4096) = 2997 read(0, "", 4096) = 0 close(0) = 0 openat(AT_FDCWD, "/usr/share/locale/zh_CN.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有那个文件或目录) openat(AT_FDCWD, "/usr/share/locale/zh_CN.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有那个文件或目录) openat(AT_FDCWD, "/usr/share/locale/zh_CN/LC_MESSAGES/coreutils.mo", O_RDONLY) = 0 fstat(0, {st_mode=S_IFREG|0644, st_size=219071, ...}) = 0 mmap(NULL, 219071, PROT_READ, MAP_PRIVATE, 0, 0) = 0x7f1fe38f8000 close(0) = 0 openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules.cache", O_RDONLY) = -1 ENOENT (没有那个文件或目录) openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules", O_RDONLY|O_CLOEXEC) = 0 fstat(0, {st_mode=S_IFREG|0644, st_size=56352, ...}) = 0 read(0, "# GNU libc iconv configuration.\n"..., 4096) = 4096 read(0, "1002//\tJUS_I.B1.002//\nmodule\tJUS"..., 4096) = 4096 read(0, "ISO-IR-110//\t\tISO-8859-4//\nalias"..., 4096) = 4096 read(0, "\t\tISO-8859-14//\nalias\tISO_8859-1"..., 4096) = 4096 read(0, "IC-ES//\nalias\tEBCDICES//\t\tEBCDIC"..., 4096) = 4096 read(0, "DIC-CP-ES//\t\tIBM284//\nalias\tCSIB"..., 4096) = 4096 read(0, "\tIBM863//\nalias\tOSF1002035F//\t\tI"..., 4096) = 4096 read(0, "37//\t\tIBM937//\nmodule\tIBM937//\t\t"..., 4096) = 4096 read(0, "JIS//\t\t\tEUC-JP//\nmodule\tEUC-JP//"..., 4096) = 4096 read(0, "ias\tISO2022CN//\t\tISO-2022-CN//\nm"..., 4096) = 4096 read(0, "_5427-EXT//\nalias\tISO_5427EXT//\t"..., 4096) = 4096 brk(0x5577456d2000) = 0x5577456d2000 read(0, "st\nmodule\tMAC-SAMI//\t\tINTERNAL\t\t"..., 4096) = 4096 read(0, "12//\t\tINTERNAL\t\tIBM1112\t\t1\nmodul"..., 4096) = 4096 read(0, "\tCP9448//\t\tIBM9448//\nalias\tCSIBM"..., 4096) = 3104 read(0, "", 4096) = 0 close(0) = 0 write(2, "\350\256\260\345\275\225\344\272\2060+0 \347\232\204\350\257\273\345\205\245\n\350\256\260\345\275\225\344\272\206"..., 46记录了0+0 的读入 记录了0+0 的写出 ) = 46 openat(AT_FDCWD, "/usr/share/locale/zh.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有那个文件或目录) openat(AT_FDCWD, "/usr/share/locale/zh/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有那个文件或目录) write(2, "0 bytes copied, 0.00256732 s, 0."..., 380 bytes copied, 0.00256732 s, 0.0 kB/s) = 38 write(2, "\n", 1 ) = 1 close(2) = 0 exit_group(0) = ? +++ exited with 0 +++