环境准备
预先安装 docker、cachestat 、cachetop 等工具,如 apt install docker.io dstat sysstat
操作和分析
dd 作为一个磁盘和文件的拷贝工具,经常被拿来测试磁盘或者文件系统的读写性能。不过,既然缓存会影响到性能,用 dd 对同一个文件进行多次读取文件
用 dd 命令生成一个临时文件,用于后面的文件读取测试:
# 生成一个512MB的临时文件
dd if=/dev/sda1 of=file bs=1M count=512
# 清理缓存
echo 3 > /proc/sys/vm/drop_caches
继续在第一个终端,运行 pcstat 命令,确认刚刚生成的文件不在缓存中。如果一切正常,你会看到 Cached 和 Percent 都是 0:
还是在第一个终端中,现在运行 cachetop 命令:
# 每隔5秒刷新一次数据
cachetop 5
第二个终端,运行 dd 命令测试文件的读取速度:
dd if=file of=/dev/null bs=1M
dd 命令运行前我们已经清理了缓存,所以 dd 命令读取数据时,肯定要通过文件系统从磁盘中读取。
第二个终端,再次执行刚才的 dd 命令:
第二个终端,再次执行 pcstat 查看文件 file 的缓存情况:
file 已经被全部缓存了起来
看一个文件读写的案例
第一个终端中运行 cachetop 命令:
# 每隔5秒刷新一次数据
cachetop 5
第二个终端,执行下面的命令运行案例:
docker run --privileged --name=app -itd feisky/app:io-direct /app -d /dev/vda1 -s 33554432
运行下面这个命令,来确认案例已经正常启动。如果一切正常,你应该可以看到类似下面的输出:
docker logs app
Reading data from disk /dev/vda1 with buffer size 33554432
Time used: 0.300478 s to read 33554432 bytes
Time used: 0.299448 s to read 33554432 bytes
每读取 32 MB 的数据,就需要花 0.3 秒,太慢了吧,是不是没用系统缓存。
应用程序是否用了直接 I/O,最简单的方法当然是观察它的系统调用,查找应用程序在调用它们时的选项。使用什么工具来观察系统调用呢?strace。
运行下面的 strace 命令,观察案例应用的系统调用情况。注意,这里使用了 pgrep 命令来查找案例进程的 PID 号:
# strace -p $(pgrep app)
strace: Process 25192 attached
restart_syscall(<... resuming interrupted read ...>) = 0
openat(AT_FDCWD, "/dev/vda1", O_RDONLY|O_DIRECT) = 3
mmap(NULL, 33558528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f548ef0f000
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"..., 33554432) = 33554432
write(1, "Time used: 0.299328 s to read 33"..., 45) = 45
close(3) = 0
munmap(0x7f548ef0f000, 33558528) = 0
= 0
trace 的结果可以看到,案例应用调用了 openat 来打开磁盘分区 /dev/vda1,并且传入的参数为 O_RDONLY|O_DIRECT(中间的竖线表示或)。
O_RDONLY 表示以只读方式打开,而 O_DIRECT 则表示以直接读取的方式打开,这会绕过系统的缓存。