Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1377311
  • 博文数量: 488
  • 博客积分: 161
  • 博客等级: 入伍新兵
  • 技术积分: 5064
  • 用 户 组: 普通用户
  • 注册时间: 2011-07-01 07:37
个人简介

只有偏执狂才能生存

文章分类

全部博文(488)

文章存档

2016年(10)

2015年(112)

2014年(66)

2013年(272)

2012年(28)

分类: LINUX

2013-06-20 16:31:24

qemu-kvm 线程事件模型
1.主(父)线程。
主线程执行循环,主要做三件事情
1).执行select操作,查询文件描述符有无读写操作
2).执行定时器回调函数
3).执行下半部(BH)回调函数。为什么要采用BH,资料说主要避免可重入性和调用栈溢出。

2.执行客户机代码的线程
只讨论kvm执行客户机代码情况(不考虑TCG,TCG采用动态翻译技术),如果有多个vcpu,就意味着存在多个线程。

3.异步io文件操作线程
提交i/o操作请求到队列中, 该线程从队列取请求,并进行处理。

4.主线程与执行客户机代码线程同步
主线程与执行客户机代码线程不能同时运行,主要通过一个全局互斥锁实现。

代码分析

1.主(父)线程。
下面函数是主线程主要执行函数:当文件描述符,定时器,下半部分触发相应事件后,将执行相应回调函数。
void main_loop_wait(int timeout){
  ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
 if (ret > 0) {
        IOHandlerRecord *pioh;
        QLIST_FOREACH(ioh, &io_handlers, next) {
            if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) {
                ioh->fd_read(ioh->opaque);
                if (!(ioh->fd_read_poll && ioh->fd_read_poll(ioh->opaque)))
                    FD_CLR(ioh->fd, &rfds);
            }
            if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) {
                ioh->fd_write(ioh->opaque);
            }
        }
    }

  qemu_run_timers(&active_timers[QEMU_CLOCK_HOST],
                    qemu_get_clock(host_clock));

   /* Check bottom-halves last in case any of the earlier events triggered
       them.  */
    qemu_bh_poll();
}

对于select函数轮循文件描述符,以及对于该描述执行操作函数,主要通过qemu_set_fd_handler()和qemu_set_fd_handler2函数添加完成的。
int qemu_set_fd_handler(int fd,
                        IOHandler *fd_read,
                        IOHandler *fd_write,
                        void *opaque);
int qemu_set_fd_handler2(int fd,
                         IOCanRWHandler *fd_read_poll,
                         IOHandler *fd_read,
                         IOHandler *fd_write,
                         void *opaque)

对于到期执行的定时器函数,回调函数由qemu_new_time函数添加的,触发时间qemu_mod_timer函数修改的
EMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque)
void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)

下半部要添加调度函数由qemu_bh_new 和qemu_bh_schedule完成的。
EMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
void qemu_bh_schedule(QEMUBH *bh)


2.执行客户机代码的线程
当初始化客户机硬件时,对于每个cpu创建一个线程,每个线程执行ap_main_loop函数,该函数运行kvm_run函数,运行客户机代码。
/* PC hardware initialisation */
static void pc_init1(ram_addr_t ram_size,
                     const char *boot_device,
                     const char *kernel_filename,
                     const char *kernel_cmdline,
                     const char *initrd_filename,
                     const char *cpu_model,
                     int pci_enabled)
{
 for (i = 0; i < smp_cpus; i++) {
        env = pc_new_cpu(cpu_model);
    }

}

void kvm_init_vcpu(CPUState *env)
{
    pthread_create(&env->kvm_cpu_state.thread, NULL, ap_main_loop, env);

    while (env->created == 0)
        qemu_cond_wait(&qemu_vcpu_cond);
}
执行客户机线程调用函数ap_main_loop,该函数最终调用函数kvm_main_loop_cpu, 该函数工作过程如下:
1.注入中断,执行客户机代码,解决客户机退出原因,例如 KVM_EXIT_MMIO, KVM_EXIT_IO。如果解决成功,继续运行。失败话,进入步骤2 
2.该步骤如果vcpu存在着,已传递但是还没有处理里信号SIG_IPI,SIGBUS,该线程阻塞,也就意味着暂停处理器客户机代码,直到处理相应信号。
3.如果上述过程完成后,继续允许执行客户机代码。
static int kvm_main_loop_cpu(CPUState *env)
{
    while (1) {
        int run_cpu = !is_cpu_stopped(env);
        if (run_cpu && !kvm_irqchip_in_kernel()) {
            process_irqchip_events(env);
            run_cpu = !env->halted;
        }
        if (run_cpu) {
            kvm_cpu_exec(env);
            kvm_main_loop_wait(env, 0);
        } else {
            kvm_main_loop_wait(env, 1000);
        }
    }
    pthread_mutex_unlock(&qemu_mutex);
    return 0;
}
static void kvm_main_loop_wait(CPUState *env, int timeout)
{
    struct timespec ts;
    int r, e;
    siginfo_t siginfo;
    sigset_t waitset;
    sigset_t chkset;

    ts.tv_sec = timeout / 1000;
    ts.tv_nsec = (timeout % 1000) * 1000000;
    sigemptyset(&waitset);
    sigaddset(&waitset, SIG_IPI);
    sigaddset(&waitset, SIGBUS);

    do {
        pthread_mutex_unlock(&qemu_mutex);

        r = sigtimedwait(&waitset, &siginfo, &ts);
        e = errno;

        pthread_mutex_lock(&qemu_mutex);

        if (r == -1 && !(e == EAGAIN || e == EINTR)) {
            printf("sigtimedwait: %s\n", strerror(e));
            exit(1);
        }

        switch (r) {
        case SIGBUS:
            kvm_on_sigbus(env, &siginfo);
            break;
        default:
            break;
        }
        r = sigpending(&chkset);
        if (r == -1) {
            printf("sigpending: %s\n", strerror(e));
            exit(1);
        }
    } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));

    cpu_single_env = env;
    flush_queued_work(env);

    if (env->stop) {
        env->stop = 0;
        env->stopped = 1;
        pthread_cond_signal(&qemu_pause_cond);
    }

    env->kvm_cpu_state.signalled = 0;
}
3.异步io文件操作线程
创建io操作线程,进行读写操作。可以通过gdb跟踪,验证查看io线程 
static void spawn_thread(void)
{
    sigset_t set, oldset;

    cur_threads++;
    idle_threads++;

    /* block all signals */
    if (sigfillset(&set)) die("sigfillset");
    if (sigprocmask(SIG_SETMASK, &set, &oldset)) die("sigprocmask");

    thread_create(&thread_id, &attr, aio_thread, NULL);

    if (sigprocmask(SIG_SETMASK, &oldset, NULL)) die("sigprocmask restore");
}

static void qemu_paio_submit(struct qemu_paiocb *aiocb)
{
    aiocb->ret = -EINPROGRESS;
    aiocb->active = 0;
    mutex_lock(&lock);
    if (idle_threads == 0 && cur_threads < max_threads)
        spawn_thread();
    QTAILQ_INSERT_TAIL(&request_list, aiocb, node);
    mutex_unlock(&lock);
    cond_signal(&cond);
}
可见启动一次bdrv_aio_readv或者raw_aio_writev操作,创建一个aio_thread线程。
static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
        BlockDriverCompletionFunc *cb, void *opaque)
{
    return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
                          cb, opaque, QEMU_AIO_READ);
}

static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
        BlockDriverCompletionFunc *cb, void *opaque)
{
    return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
                          cb, opaque, QEMU_AIO_WRITE);
}

4.主线程与执行客户机代码线程,主线程与异步io文件操作线程同步----qemu_global_mutex
select阻塞(主线程)和执行客户机代码(客户机线程)不需要同步锁,这在qemu运行过程占的时间比例较大。
但是执行异步io文件操作时,占用qemu_global_mutex锁的。
select阻塞这里,实际不需要锁定。
void main_loop_wait(int timeout)
{
   qemu_mutex_unlock_iothread();//开锁
    ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
    qemu_mutex_lock_iothread(); //锁住
}
执行客户机代码是不要锁定
int kvm_cpu_exec(CPUState *env)
{
       qemu_mutex_unlock_iothread();//开锁
        ret = kvm_vcpu_ioctl(env, KVM_RUN, 0);
        qemu_mutex_lock_iothread(); //锁住
}

阅读(2886) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册