展示自己、证明自己
分类: C/C++
2014-07-28 18:03:21
同步读写
前提
1)注释raw-posix.c 中bdrv_raw结构体中的bdrv_aio_readv、bdrv_aio_writev和bdrv_aio_flush变量,其目的是为了防止对文件读写时走aio分支。
2)建立原始文件,qemu-img create os.img 10G, 再安装启动虚拟机
io流走向
vm-guset-os ---> hw(ide/virtio) ---> block.c --->raw-posix.c--->img镜像文件
具体流程
在虚拟机系统中进行复制、新建文件等操作
虚拟机系统中该卷所在的磁盘驱动捕获读写操作,并记录操作所在的文件和内容,向qemu程序发送中断
qemu子线程(ap_main_loop)捕获到该操作,并退出(vm-exit), 然后进行端口IO读写(kvm_handle_io)
使用vm-exit的端口参数和qemu初始化的地址进行对比操作,找到合适的驱动(ide or virtio)将虚拟机系统的读写操作解析
调用block.c文件中bdrv_aio_writev函数,选择原始写函数。详见文章末尾附录1(block函数初始化过程)
调用raw-posix.c中的原始读写函数将内容写入文件中。
gdb调试结果
Breakpoint 1, raw_write (bs=0xc8b3b0, sector_num=5485936, buf=0x7ffff001e200 "\002", nb_sectors=8)
at block/raw-posix.c:515
515 ret = raw_pwrite(bs, sector_num * 512, buf, nb_sectors * 512);
(gdb) bt
#0 raw_write (bs=0xc8b3b0, sector_num=5485936, buf=0x7ffff001e200 "\002", nb_sectors=8) at block/raw-posix.c:515
#1 0x0000000000499e6d in bdrv_write (bs=0xc8b3b0, sector_num=5485936, buf=0x7ffff001e200 "\002", nb_sectors=8)
at block.c:685
#2 0x000000000049c6ef in bdrv_aio_rw_vector (bs=0xc8b3b0, sector_num=5485936, qiov=0x7ffff00009a8,
nb_sectors=8,
cb=0x58c0bd
#3 0x000000000049c7dd in bdrv_aio_writev_em (bs=0xc8b3b0, sector_num=5485936, qiov=0x7ffff00009a8,
nb_sectors=8,
cb=0x58c0bd
#4 0x000000000049bcf5 in bdrv_aio_writev (bs=0xc8b3b0, sector_num=5485936, qiov=0x7ffff00009a8, nb_sectors=8,
cb=0x58c0bd
#5 0x000000000058c31c in dma_bdrv_cb (opaque=0x7ffff0000950, ret=0) at /root/qemu-kvm12/dma-helpers.c:120
#6 0x000000000058c4a4 in dma_bdrv_io (bs=0xc8b3b0, sg=0x1212568, sector_num=5485936,
cb=0x44023c
#7 0x000000000058c55b in dma_bdrv_write (bs=0xc8b3b0, sg=0x1212568, sector=5485936,
cb=0x44023c
#8 0x00000000004403d9 in ide_write_dma_cb (opaque=0x1212f70, ret=0) at /root/qemu-kvm12/hw/ide/core.c:743
#9 0x0000000000445765 in bmdma_cmd_writeb (opaque=0x1212f70, addr=49152, val=1)
at /root/qemu-kvm12/hw/ide/pci.c:51
#10 0x00000000004ca131 in ioport_write (index=0, address=49152, data=1) at ioport.c:80
#11 0x00000000004ca4c5 in cpu_outb (addr=49152, val=1 '\001') at ioport.c:198
#12 0x0000000000429327 in kvm_handle_io (port=49152, data=0x7ffff7ff7000, direction=1, size=1, count=1)
at /root/qemu-kvm12/kvm-all.c:535
#13 0x000000000042b79b in kvm_run (env=0xc8e2a0) at /root/qemu-kvm12/qemu-kvm.c:964
#14 0x000000000042cabd in kvm_cpu_exec (env=0xc8e2a0) at /root/qemu-kvm12/qemu-kvm.c:1646
#15 0x000000000042d28d in kvm_main_loop_cpu (env=0xc8e2a0) at /root/qemu-kvm12/qemu-kvm.c:1888
#16 0x000000000042d3ee in ap_main_loop (_env=0xc8e2a0) at /root/qemu-kvm12/qemu-kvm.c:1938
#17 0x0000003337c079d1 in start_thread () from /lib64/libpthread.so.0
#18 0x00000033378e8b6d in clone () from /lib64/libc.so.6
异步读写
读写函数和读写操作异步进行。
前面步骤与原始读写步骤一样,在最后读写是调用raw_aio_readv或raw_aio_writev函数
调用读写函数
以raw_aio_writev为例,
->bdrv_raw
->raw_aio_writev
->raw_aio_submit
->paio_submit
->qemu_paio_submit
->spawn_thread
->thread_create(&thread_id, &attr, aio_thread, NULL);
实际读写操作
aio_thread
->handle_aiocb_rw
->handle_aiocb_rw_linear(aiocb, buf);
->len = pwrite(aiocb->aio_fildes,
(const char *)buf + offset,
aiocb->aio_nbytes - offset,
aiocb->aio_offset + offset);
附录1(block函数初始化过程)
1) 调用bdrv_register,将各个bdrv结构体函数加载到firts_drv链表中。
2) 如果结构体中不存在变量bdrv_aio_readv,则将bdrv_aio_readv_em函数赋值给bdrv->bdrv_aio_readv(写函数类似)
3) 如果结构体中不存在变量bdrv_read,则将bdrv_read_em函数赋值给bdrv->bdrv_read(写函数类似)
./block/raw-posix.c:1345: bdrv_register(&bdrv_raw);
./block/raw-posix.c:1346: bdrv_register(&bdrv_host_device);
注册函数的实现void bdrv_register(BlockDriver *bdrv)
{
if (!bdrv->bdrv_aio_readv) { //判断bdrv_aio是否存在
/* add AIO emulation layer */
bdrv->bdrv_aio_readv = bdrv_aio_readv_em;
bdrv->bdrv_aio_writev = bdrv_aio_writev_em;
} else if (!bdrv->bdrv_read) { //判断bdrv是否存在
/* add synchronous IO emulation layer */
bdrv->bdrv_read = bdrv_read_em;
bdrv->bdrv_write = bdrv_write_em;
} // 如果两个函数均在块结构体中没有重定义,则读写函数将陷入死循环中
if (!bdrv->bdrv_aio_flush)
bdrv->bdrv_aio_flush = bdrv_aio_flush_em;
bdrv->next = first_drv;
first_drv = bdrv;
}
块读写过程,判断bdrv_aio_readv或bdrv_read结构体变量是否被重定义,如果bdrv_aio_readv没有重定义,则接着调用bdrv_read。同理如果bdrv_read没有重定义,则接着调用bdrv_aio_readv,这就不难理解上面的调用过程了。
以bdrv_aio_readv_em为例:接着调用bdrv_aio_rw_vector函数
static
BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs, int64_t
sector_num, QEMUIOVector *qiov,
int nb_sectors, BlockDriverCompletionFunc
*cb, void *opaque)
{
return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
}
static BlockDriverAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs,int64_t sector_num,QEMUIOVector *qiov,
int nb_sectors,BlockDriverCompletionFunc *cb,void *opaque,int is_write)
{
if (is_write) {
qemu_iovec_to_buffer(acb->qiov, acb->bounce);
acb->ret = bdrv_write(bs, sector_num, acb->bounce, nb_sectors);
} else {
acb->ret = bdrv_read(bs, sector_num, acb->bounce, nb_sectors);
}
}
int bdrv_read(BlockDriverState *bs, int64_t sector_num,uint8_t *buf, int nb_sectors)
{
return drv->bdrv_read(bs, sector_num, buf, nb_sectors);
}
以bdrv_read_em为例,
static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,uint8_t *buf, int nb_sectors)
{
acb = bdrv_aio_readv(bs, sector_num, &qiov, nb_sectors,bdrv_rw_em_cb, &async_ret);
}
BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
ret = drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors,cb, opaque);
}
以后需阅读并编写的:
1)端口IO处理过程
2)virtio,ide驱动原理
3)qemu各个线程的响应
4)qemu-1.3 块设备的读写(使用协程(coroutine)进行函数调用管理bdrv_co_do_rw,然后调用对应读写函数)