您的位置:首页 > 编程语言

qemu-kvm 代码分析

2015-07-05 10:19 417 查看
物理计算机由处理器,存储器,输入与设备三大部件组成,虚拟机实际也是一台计算机,必然包括这三大组件,所以虚拟技术包括处理机虚拟化,存储器虚拟化,输入与输出设备虚拟化,这些虚拟化是通过软件虚拟还是硬件虚拟呢?

接下来看看具体虚拟化软件qemu-kvm是如何处理的?qemu是采用纯软件模拟的技术,kvm暂且认为采用硬件模拟技术, 可见qemu-kvm的虚拟机是硬件辅助虚拟机,其中处理机虚拟化和存储器虚拟化采用硬件模拟技术,输入与设备采用软件模拟的。

虚拟机组成

实际上面所说计算机组成比较笼统,实际处理器,存储器,输入与设备种类繁多,

- i440FX host PCI bridge and PIIX3 PCI to ISA bridge

总线桥: i440FX pci桥,PIIX3 ISA桥

- Cirrus CLGD 5446 PCI VGA card or dummy VGA card with Bochs VESA

extensions (hardware level, including all non standard modes).

显卡:

- PS/2 mouse and keyboard

鼠标和键盘

- 2 PCI IDE interfaces with hard disk and CD-ROM support

IDE的硬盘和光驱

- Floppy disk

软盘

- PCI and ISA network adapters

PCI ISA 总线

- Serial ports

串口

- Creative SoundBlaster 16 sound card

声卡 SoundBlaster 1370 AC97等

- ENSONIQ AudioPCI ES1370 sound card

- Intel 82801AA AC97 Audio compatible sound card

- Intel HD Audio Controller and HDA codec

- Adlib(OPL2) - Yamaha YM3812 compatible chip

- Gravis Ultrasound GF1 sound card

- CS4231A compatible sound card

UHCI USB总线控制器

- PCI UHCI USB controller and a virtual USB hub.

最大支持 255个cpu

SMP is supported with up to 255 CPUs.

qemu-kvm参数说明:

-S -M rhel6.3.0 -enable-kvm //启用硬件辅助模拟,模拟的机器类型为rhel6.3.0,虚拟机默认不启动。(后面解释类型为rhel6.3.0大概配置)

//机器配置

-m 1111 //内存

-smp 1,sockets=1,cores=1,threads=1 //处理器

-rtc base=localtime,clock=vm,driftfix=slew //时钟

-device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 //usb控制器usb-uhci

-device usb-ehci,id=usb1,bus=pci.0,addr=0x5 //usb控制器usb-ehci

-device virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x6 //pci

-drive file=/mnt/local/4d4fdbb5-216a-44a4-afae-ea6e2a80b481,if=none,id=drive-ide0-0-0,format=qcow2,cache=writeback //ide硬盘

-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=2 //主ide

-drive if=none,media=cdrom,id=drive-ide0-1-0,readonly=on,format=raw,cache=writeback //ide光驱

-device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0,bootindex=1 //从ide

-netdev tap,fd=23,id=hostnet0 -device e1000,netdev=hostnet0,id=net0,mac=02:00:77:a2:00:0d,bus=pci.0,addr=0x3 //网卡

-chardev pty,id=charserial0 -device isa-serial,chardev=charserial0,id=serial0 //伪终端

-chardev spicevmc,id=charchannel0,name=vdagent -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=com.redhat.spice.0 //串口

-device usb-tablet,id=input0,bus=usb.0,port=1 //触摸盘

-spice port=5902,addr=0.0.0.0,disable-ticketing //远程显示

-vga qxl -global qxl-vga.vram_size=67108864 -device AC97,id=sound0,bus=pci.0,addr=0x4 //显卡

-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x7 -readconfig /etc/qemu/ich9-ehci-uhci.cfg -chardev spicevmc,name=usbredir,id=usbredirchardev1

-device usb-redir,chardev=usbredirchardev1,id=usbredirdev1,bus=ehci.0,debug=3 -chardev spicevmc,name=usbredir,id=usbredirchardev2 //usb设备重定向

-device usb-redir,chardev=usbredirchardev2,id=usbredirdev2,bus=ehci.0,debug=3 -chardev spicevmc,name=usbredir,id=usbredirchardev3

-device usb-redir,chardev=usbredirchardev3,id=usbredirdev3,bus=ehci.0,debug=3

虚拟机流程

1.硬件虚拟设备初始化

main-->kvm_init-->kvm_create_context-->kvm_create

r = kvm_create_vm(kvm);

r = kvm_arch_create(kvm, phys_mem_bytes, vm_mem);

r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem);

kvm_create_irqchip(kvm);

由上面代码可见,硬件虚拟技术为处理器,内存,中断

2.启动虚拟机

1>初始化所有设备,

module_call_init(MODULE_INIT_DEVICE);

块驱动,外围设备,机器初始化...

module.h:30:#define block_init(function) module_init(function, MODULE_INIT_BLOCK)

module.h:31:#define device_init(function) module_init(function, MODULE_INIT_DEVICE)

module.h:32:#define machine_init(function) module_init(function, MODULE_INIT_MACHINE)

hw/sb16.c:1418:device_init (sb16_register)

module.h:31:#define device_init(function) module_init(function, MODULE_INIT_DEVICE)

block/qcow2.c:1525:block_init(bdrv_qcow2_init);

module.h:30:#define block_init(function) module_init(function, MODULE_INIT_BLOCK)

hw/pc.c:1787:machine_init(rhel_machine_init);

module.h:32:#define machine_init(function) module_init(function, MODULE_INIT_MACHINE)

2>根据-M rhel6.3.0类型启动虚拟机。

main--> machine->init(ram_size, boot_devices,kernel_filename, kernel_cmdline, initrd_filename, cpu_model);

1)创建vcpu

kvm_vm_ioctl(kvm_state, KVM_CREATE_VCPU, id);

初始化vcpu

kvm_arch_init_vcpu

2) 创建内存

mmap_size = kvm_ioctl(kvm_state, KVM_GET_VCPU_MMAP_SIZE, 0);

env->kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, env->kvm_fd,

3) 启动虚拟机,运行系统

kvm_run(env);

3 处理i/o

当虚拟机运行过程,有输入输出操作,进行i/o设备模拟。i/o设备分为端口和内存映射两种

case KVM_EXIT_IO:

r = kvm_handle_io(run->io.port,

case KVM_EXIT_MMIO:

r = handle_mmio(env);

qemu-kvm虚拟机线程

1. qemu-kvm线程工作过程:

1)启动一个子线程,创建初始化vcpu,主线程等待

2)子线程创建初始化vcpu完毕,子线程等待,并等候通知主线程运行

3)主线程继续初始化虚拟机工作,初始化完成,通知子线程继续运行

4)子线程继续启动虚拟机kvm_run,主线程执行select交互处理

2.一个虚拟机进程包含着几个线程

1)启动虚拟机

2)ps -eLf | grep qemu-kvm

3.每个线程作用是什么呢?

gdb -p 28678

(gdb) info thread

3 Thread 0x7f0972706700 (LWP 28697) 0x00007f0977426257 in ioctl () from /lib64/libc.so.6

2 Thread 0x7f092b5fe700 (LWP 28698) 0x00007f09790697bb in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0

* 1 Thread 0x7f0979674940 (LWP 28678) 0x00007f0977426d03 in select () from /lib64/libc.so.6

(gdb) thread 1

[Switching to thread 1 (Thread 0x7f0979674940 (LWP 28678))]#0 0x00007f0977426d03 in select () from /lib64/libc.so.6

(gdb) bt

#0 0x00007f0977426d03 in select () from /lib64/libc.so.6

#1 0x000000000040c3b0 in main_loop_wait (timeout=1000) at /usr/src/debug/qemu-kvm-0.12.1.2/vl.c:4017

#2 0x000000000042aefa in kvm_main_loop () at /usr/src/debug/qemu-kvm-0.12.1.2/qemu-kvm.c:2225

#3 0x000000000040de85 in main_loop (argc=<value optimized out>, argv=<value optimized out>, envp=<value optimized out>)

at /usr/src/debug/qemu-kvm-0.12.1.2/vl.c:4234

#4 main (argc=<value optimized out>, argv=<value optimized out>, envp=<value optimized out>)

at /usr/src/debug/qemu-kvm-0.12.1.2/vl.c:6470

这个线程是主线程,这个线程loop循环,循环操作select.实际就是查看有无读写文件描述符,有的话进行读写操作呗。

(gdb) thread 3

[Switching to thread 3 (Thread 0x7f0972706700 (LWP 28697))]#0 0x00007f0977426257 in ioctl () from /lib64/libc.so.6

(gdb) bt

#0 0x00007f0977426257 in ioctl () from /lib64/libc.so.6

#1 0x000000000042c7cf in kvm_run (env=0x1a6fde0) at /usr/src/debug/qemu-kvm-0.12.1.2/qemu-kvm.c:989

#2 0x000000000042cc59 in kvm_cpu_exec (env=<value optimized out>) at /usr/src/debug/qemu-kvm-0.12.1.2/qemu-kvm.c:1730

#3 0x000000000042da9e in kvm_main_loop_cpu (_env=0x1a6fde0) at /usr/src/debug/qemu-kvm-0.12.1.2/qemu-kvm.c:1991

#4 ap_main_loop (_env=0x1a6fde0) at /usr/src/debug/qemu-kvm-0.12.1.2/qemu-kvm.c:2041

#5 0x00007f0979065851 in start_thread () from /lib64/libpthread.so.0

#6 0x00007f097742e11d in clone () from /lib64/libc.so.6

这个子线程,kvm_run启动和运行虚拟机

(gdb) thread 2

[Switching to thread 2 (Thread 0x7f092b5fe700 (LWP 28698))]#0 0x00007f09790697bb in pthread_cond_timedwait@@GLIBC_2.3.2 ()

from /lib64/libpthread.so.0

(gdb) bt

#0 0x00007f09790697bb in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0

#1 0x00000000004856a3 in cond_timedwait (unused=<value optimized out>) at posix-aio-compat.c:104

#2 aio_thread (unused=<value optimized out>) at posix-aio-compat.c:325

#3 0x00007f0979065851 in start_thread () from /lib64/libpthread.so.0

#4 0x00007f097742e11d in clone () from /lib64/libc.so.6

(gdb)

这个子线程,异步进行i/o操作,主要针对磁盘映像操作(block drive)

qemu-kvm虚拟机进程的字符设备处理(char drive)

1.查看采用哪些字符设备

ps aux | grep qemu-kvm | grep “、-chardev” --color=auto

-chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/ha1.monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=control

-chardev pty,id=charserial0 -device isa-serial,chardev=charserial0,id=serial0

第一个unix套接字,做为虚拟机进程与libvirt(或者进程)通信使用,例如输入命令等

第二个pty伪终端, 模拟串口使用

2.代码查处字符设备

1)解析命令行参数,查找所有的 -chardev

for(;;) {

case QEMU_OPTION_chardev:

opts = qemu_opts_parse(&qemu_chardev_opts, optarg, 1);

}

2.初始化相应字符设备驱动

1)解析

if (qemu_opts_foreach(&qemu_chardev_opts, chardev_init_func, NULL, 1) != 0)

exit(1);

2)初始化

{ .name = "socket", .open = qemu_chr_open_socket },

{ .name = "pty", .open = qemu_chr_open_pty },

3)打开字符设备

chr = backend_table[i].open(opts);

static CharDriverState *qemu_chr_open_socket(QemuOpts *opts){

is_listen = qemu_opt_get_bool(opts, "server", 0);

is_waitconnect = qemu_opt_get_bool(opts, "wait", 1);

is_telnet = qemu_opt_get_bool(opts, "telnet", 0);

do_nodelay = !qemu_opt_get_bool(opts, "delay", 1);

is_unix = qemu_opt_get(opts, "path") != NULL;

fd = unix_listen_opts(opts); //创建unix套接字

socket_set_nonblock(fd); //设置描述符为非阻塞

qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);//为主线程select函数,读描述进行设置

}

4.qemu-kvm的monitor如何使用chardev?

if (qemu_opts_foreach(&qemu_mon_opts, mon_init_func, NULL, 1) != 0)

exit(1);

解析字符设备,为监控使用

static int mon_init_func(QemuOpts *opts, void *opaque)

chardev = qemu_opt_get(opts, "chardev");

chr = qemu_chr_find(chardev);

if (chr == NULL) {

fprintf(stderr, "chardev \"%s\" not found\n", chardev);

exit(1);

}

monitor_init(chr, flags);

}

5 系统关机,关闭电源等操作事件的通知

void qemu_system_shutdown_request(void)

{

shutdown_requested = 1;

qemu_notify_event();

}

void qemu_system_powerdown_request(void)

{

powerdown_requested = 1;

qemu_notify_event();

}

当系统关机,关闭电源等操作,以通过事件通知方式,通知主线程。 由于主线程进行select可能短时间阻塞,如果进行关机,关闭电源操作,可以解除select阻塞,进行处理这些操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: