qemu启动流程

来源:互联网 发布:java.net.socket 编辑:程序博客网 时间:2024/05/11 17:13

1.主线程代码分析


主线程主要是监控各种需要处理的请求的句柄,然后等相关事件触发后,进行相关的处理。

下面是典型的主线程的调用栈,通过ppoll来监控事件。


(gdb) bt
#0  0x00007ffff618dd51 in ppoll () from /lib64/libc.so.6
#1  0x000055555580fdbc in ppoll (__ss=0x0, __timeout=0x7fffffffdb90, 
    __nfds=<optimized out>, __fds=<optimized out>)
    at /usr/include/bits/poll2.h:77
#2  qemu_poll_ns (fds=<optimized out>, nfds=<optimized out>, 
    timeout=timeout@entry=46251116) at qemu-timer.c:322
#3  0x000055555580f594 in os_host_main_loop_wait (timeout=46251116)
    at main-loop.c:238
#4  main_loop_wait (nonblocking=<optimized out>) at main-loop.c:493
#5  0x00005555555ea10e in main_loop () at vl.c:1808
#6  main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>)
    at vl.c:4470


代码分析如下:

main -> main_loop() 

main_loop判断是否有关闭,暂停的操作,如果没有,循环调用main_loop_wait进行监控。

static void main_loop(void)

{
    bool nonblocking;
    int last_io = 0;
    do {
        nonblocking = !kvm_enabled() && !xen_enabled() && last_io > 0;
        last_io = main_loop_wait(nonblocking);
    } while (!main_loop_should_exit());
}


main_loop_should_exit函数分析,判断是否有关闭,暂停的请求。

static bool main_loop_should_exit(void)
{
    RunState r;
    if (qemu_debug_requested()) {
        vm_stop(RUN_STATE_DEBUG);
    }
    if (qemu_suspend_requested()) {
        qemu_system_suspend();
    }
    if (qemu_shutdown_requested()) {
        qemu_kill_report();
        qapi_event_send_shutdown(&error_abort);
        if (no_shutdown) {
            vm_stop(RUN_STATE_SHUTDOWN);
        } else {
            return true;
        }
    }
    if (qemu_reset_requested()) {
        pause_all_vcpus();


主线程的poll是在监控哪些?

main_loop_wait中处理:

g_array_set_size(gpollfds, 0);


./iohandler.c中进行的调试。

qemu_iohandler_fill(gpollfds); 


根据全局变量io_handlers进行填充。
void qemu_iohandler_fill(GArray *pollfds)


根据句柄轮询调用之间的处理函数。

qemu_iohandler_poll(gpollfds, ret);


GArray *pollfds


void qemu_iohandler_fill(GArray *pollfds)
{
    IOHandlerRecord *ioh;


    QLIST_FOREACH(ioh, &io_handlers, next) {
        int events = 0;


        if (ioh->deleted)
            continue;
        if (ioh->fd_read) {
            events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
        }
        if (ioh->fd_write) {
            events |= G_IO_OUT | G_IO_ERR;
        }
        if (events) {
            GPollFD pfd = {
                .fd = ioh->fd,
                .events = events,
            };
            ioh->pollfds_idx = pollfds->len;
            g_array_append_val(pollfds, pfd);
        } else {
            ioh->pollfds_idx = -1;
        }
    }
}


typedef struct IOHandlerRecord {
    IOHandler *fd_read;
    IOHandler *fd_write;
    void *opaque;
    QLIST_ENTRY(IOHandlerRecord) next;
    int fd;
    int pollfds_idx;
    bool deleted;
} IOHandlerRecord;


注册监控句柄的接口函数。

void qemu_set_fd_handler(int fd,
                         IOHandler *fd_read,
                         IOHandler *fd_write,
                         void *opaque)



2. VCPU线程分析


qemu针对每一个vcpu单独创建一个线程,循环调用ioctl。

调用栈:

(gdb) bt

#0  0x00007f1e257b7407 in ioctl () from /lib64/libc.so.6
#1  0x00007f1e276f94f4 in kvm_vcpu_ioctl (cpu=cpu@entry=0x7f1e29f8bb30, 
    type=type@entry=44672) at /home/jemmy/develop/qemu/kvm-all.c:1916
#2  0x00007f1e276f95ae in kvm_cpu_exec (cpu=cpu@entry=0x7f1e29f8bb30)
    at /home/jemmy/develop/qemu/kvm-all.c:1775
#3  0x00007f1e276e732a in qemu_kvm_cpu_thread_fn (arg=0x7f1e29f8bb30)
    at /home/jemmy/develop/qemu/cpus.c:976
#4  0x00007f1e25a8552a in start_thread () from /lib64/libpthread.so.0
#5  0x00007f1e257c122d in clone () from /lib64/libc.so.6


代码分析如下:

int kvm_cpu_exec(CPUState *cpu)

    do {
        MemTxAttrs attrs;

        if (cpu->kvm_vcpu_dirty) {
            kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE);
            cpu->kvm_vcpu_dirty = false;
        }

        kvm_arch_pre_run(cpu, run);
        if (cpu->exit_request) {
            DPRINTF("interrupt exit requested\n");
            /*
             * KVM requires us to reenter the kernel after IO exits to complete
             * instruction emulation. This self-signal will ensure that we
             * leave ASAP again.
             */
            qemu_cpu_kick_self();
        }
        qemu_mutex_unlock_iothread();

        run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);

        qemu_mutex_lock_iothread();
        attrs = kvm_arch_post_run(cpu, run);

        trace_kvm_run_exit(cpu->cpu_index, run->exit_reason);


        switch (run->exit_reason) {                            //根据推出原因,进行相关的处理。
        case KVM_EXIT_IO:
            DPRINTF("handle_io\n");
            kvm_handle_io(run->io.port, attrs,
                          (uint8_t *)run + run->io.data_offset,
                          run->io.direction,
                          run->io.size,
                          run->io.count);
            ret = 0;
            break;
        case KVM_EXIT_MMIO:
            DPRINTF("handle_mmio\n");
            address_space_rw(&address_space_memory,
                             run->mmio.phys_addr, attrs,
                             run->mmio.data,
                             run->mmio.len,
                             run->mmio.is_write);
            ret = 0;
            break;


简单以address_space_rw为例,进行调试,调用栈如下:


(gdb) b address_space_rw
Breakpoint 1 at 0x7f1e276c5dd0: file /home/jemmy/develop/qemu/exec.c, line 2317.
(gdb) c
Continuing.
[Switching to Thread 0x7f1e24ad9700 (LWP 25581)]


Breakpoint 1, address_space_rw (as=0x7f1e27daef40 <address_space_io>, 
    addr=addr@entry=112, attrs=attrs@entry=..., 
    buf=0x7f1e2761c000 "\217\200\377\377", len=len@entry=1, 
    is_write=is_write@entry=true) at /home/jemmy/develop/qemu/exec.c:2317
2317 {
(gdb) bt
#0  address_space_rw (as=0x7f1e27daef40 <address_space_io>, 
    addr=addr@entry=112, attrs=attrs@entry=..., 
    buf=0x7f1e2761c000 "\217\200\377\377", len=len@entry=1, 
    is_write=is_write@entry=true) at /home/jemmy/develop/qemu/exec.c:2317
#1  0x00007f1e276f9805 in kvm_handle_io (count=1, size=1, 
    direction=<optimized out>, data=<optimized out>, attrs=..., port=112)
    at /home/jemmy/develop/qemu/kvm-all.c:1636
#2  kvm_cpu_exec (cpu=cpu@entry=0x7f1e29f8bb30)
    at /home/jemmy/develop/qemu/kvm-all.c:1804
#3  0x00007f1e276e732a in qemu_kvm_cpu_thread_fn (arg=0x7f1e29f8bb30)
    at /home/jemmy/develop/qemu/cpus.c:976
#4  0x00007f1e25a8552a in start_thread () from /lib64/libpthread.so.0
#5  0x00007f1e257c122d in clone () from /lib64/libc.so.6


3.VNC线程分析:

(gdb) bt
#0  0x00007f1e25a8a590 in pthread_cond_wait@@GLIBC_2.3.2 ()
   from /lib64/libpthread.so.0
#1  0x00007f1e2794af19 in qemu_cond_wait (cond=cond@entry=0x7f1e2ad81150, 
    mutex=mutex@entry=0x7f1e2ad81180) at util/qemu-thread-posix.c:132
#2  0x00007f1e278d7823 in vnc_worker_thread_loop (
    queue=queue@entry=0x7f1e2ad81150) at ui/vnc-jobs.c:222
#3  0x00007f1e278d7c08 in vnc_worker_thread (arg=0x7f1e2ad81150)
    at ui/vnc-jobs.c:323
#4  0x00007f1e25a8552a in start_thread () from /lib64/libpthread.so.0
#5  0x00007f1e257c122d in clone () from /lib64/libc.so.6


疑问:其余的线程是什么作用?在哪里进行的创建?

(gdb) bt
#0  0x00007f1e257bb939 in syscall () from /lib64/libc.so.6
#1  0x00007f1e2794b211 in futex_wait (val=4294967295, 
    ev=0x7f1e2823ccc4 <rcu_call_ready_event>) at util/qemu-thread-posix.c:301
#2  qemu_event_wait (ev=ev@entry=0x7f1e2823ccc4 <rcu_call_ready_event>)
    at util/qemu-thread-posix.c:399
#3  0x00007f1e279598f6 in call_rcu_thread (opaque=<optimized out>)
    at util/rcu.c:233
#4  0x00007f1e25a8552a in start_thread () from /lib64/libpthread.so.0
#5  0x00007f1e257c122d in clone () from /lib64/libc.so.6


0 0