前阵子有同事问到KVM中关于磁盘的cache的问题,我一直没有深究,今天好好看了下qemu-kvm的代码。在QEMU/KVM命令行的drive参数中,加上"cache=off"或"cache=none"都是将宿主机中对客户机镜像的读写cache关闭的,因为它open那个image文件时就是用"O_DIRECT"方式打开的,下面进行分析如下。
本次分析基于qemu-kvm.git代码,使用其中的"qemu-kvm-1.1.1"这个tag。
首先,vl.c解析QEMU命令行参数,可以看到对“drive”的解析如下:
1 2 3 4 5 6 7 8 9 |
if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, &machine->use_scsi, 1) != 0) exit(1); static int drive_init_func(QemuOpts *opts, void *opaque) { int *use_scsi = opaque; return drive_init(opts, *use_scsi) == NULL; } |
然后,drive_init函数是在blockdev.c文件中定义的,如下:
1 2 3 4 5 6 7 8 9 10 11 |
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) { /*..................*/ if ((buf = qemu_opt_get(opts, "cache")) != NULL) { if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) { error_report("invalid cache option"); return NULL; } } /*..................*/ } |
而对block devicecache标志的解析在block.c文件的“bdrv_parse_cache_flags”函数,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/** * Set open flags for a given cache mode * * Return 0 on success, -1 if the cache mode was invalid. */ int bdrv_parse_cache_flags(const char *mode, int *flags) { *flags &= ~BDRV_O_CACHE_MASK; if (!strcmp(mode, "off") || !strcmp(mode, "none")) { *flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; } else if (!strcmp(mode, "directsync")) { *flags |= BDRV_O_NOCACHE; } else if (!strcmp(mode, "writeback")) { *flags |= BDRV_O_CACHE_WB; } else if (!strcmp(mode, "unsafe")) { *flags |= BDRV_O_CACHE_WB; *flags |= BDRV_O_NO_FLUSH; } else if (!strcmp(mode, "writethrough")) { /* this is the default */ } else { return -1; } return 0; } |
可见,cache=off/none,将被标志为“BDRV_O_NOCACHE”和“BDRV_O_CACHE_WB”,而将这些标志转换为open系统调用的flag在"block/raw-posix.c"文件中,如下:
1 2 3 4 5 6 |
/* Use O_DSYNC for write-through caching, no flags for write-back caching, * and O_DIRECT for no caching. */ if ((bdrv_flags & BDRV_O_NOCACHE)) s->open_flags |= O_DIRECT; if (!(bdrv_flags & BDRV_O_CACHE_WB)) s->open_flags |= O_DSYNC; |
可见,open syscall的falg被置为"O_DIRECT"了,而最后正式调用open去打开文件的代码在"osdep.c"中,如下:
1 2 3 4 5 6 7 8 9 10 11 |
s->fd = -1; fd = qemu_open(filename, s->open_flags, 0644); #ifdef O_CLOEXEC ret = open(name, flags | O_CLOEXEC, mode); #else ret = open(name, flags, mode); if (ret >= 0) { qemu_set_cloexec(ret); } #endif |
对于open系统调用,可以“man 2 open”来了解;对于前面的strcmp函数,可以“man 3 strcmp”来查看。
很好