前阵子有同事问到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”的解析如下:
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文件中定义的,如下:
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”函数,如下:
/**
* 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”文件中,如下:
/* 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”中,如下:
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”来查看。
很好