【问题标题】:Does QEMU user mode emulation exit in a way that would prevent pthread_join from blocking?QEMU 用户模式仿真是否以阻止 pthread_join 阻塞的方式退出?
【发布时间】:2015-09-13 15:02:22
【问题描述】:

我正在尝试将 QEMU 的用户模式模拟器作为我正在编写的更大程序中的线程运行。我已经修改了linux-user/main.c 文件,因此标准的int main(int argc, char **argv, char **envp 函数现在称为void *qemu_user_mode_func(void *arg)。我还在该函数的末尾添加了pthread_exit(NULL),这是 pthreads 的标准做法(或者我被告知)。

但是,当我尝试运行包含我自己的测试函数的第二个线程时(如下图 void *test_func(void *arg) 所示),进程在第二个线程完成之前退出,即使调用了 pthread_join(@987654328 @),我读过它会阻塞调用线程,直到线程 tid 返回。 QEMU 的用户模式仿真是否以阻止pthread_join 退出的方式退出,或者我只是使用了错误的线程?

这是我的代码(不包括大部分qemu_user_mode_func):

void *qemu_user_mode_func(void *arg)
{
    thread_data_t *thread_data;
    int argc;
    char **argv;
    char **envp;

/** QEMU's normal code **/

    //return 0;
    pthread_exit(NULL);
}

void *test_func(void *arg) {
    struct timespec time;
    time.tv_sec = 7;
    time.tv_nsec = 0;

    nanosleep(&time, NULL);

    printf("hello, world - from a thread\n");
    pthread_exit(NULL);
}

int main(int argc, char**argv, char **envp) {
    //Initialize variables to create thread
    int rc;
    pthread_t threads[2];
    thread_data_t main_args;

    main_args.tid = 1;
    main_args.argc = argc;
    main_args.argv = argv;
    main_args.envp = envp;

    //Create thread
    if ((rc = pthread_create(&(threads[0]), NULL, test_func, NULL))) {
        fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
        return EXIT_FAILURE;
    }

    if ((rc = pthread_create(&(threads[1]), NULL, qemu_user_mode_func, (void *)&main_args))) {
        fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
        return EXIT_FAILURE;
    }

    //Wait for thread to finish, then terminate process
    for (rc = 0; rc < 2; rc++) {
        pthread_join(threads[rc], NULL);
    }

    return 0;
}

编辑:我在void cpu_loop(CPUX86State *env) 函数中发现,当模拟程序结束时,QEMU 调用系统调用 231,即sys_exit_group(根据1)。所以我猜这个系统调用正在终止我正在运行的整个过程。我会很感激任何关于如何解决这个问题的提示!

【问题讨论】:

  • 你为什么不直接fork() 并在fork child 中调用qemu main 函数?
  • @caf - 这与我使用 qemu 的方式有关 - 主要是,我正在使用其中的动态二进制翻译系统,称为 Tiny Code Generator。我试图在程序执行的中途开始 DBT - 例如,程序在本机硬件上启动并在执行的中途迁移到虚拟机。由于二进制文件的 text + data 部分在线程之间共享,因此对这些部分的任何内存引用在两个线程中都是有效的;所有改变的是堆栈(这是一个完全不同的野兽)。我同意fork 通常是更好的解决方案!

标签: c multithreading pthreads qemu system-calls


【解决方案1】:

如果你将一个复杂的预先存在的应用程序变成线程,就会出现问题。一是应用程序可以调用exit 或其变体,这将终止您的整个程序。还有许多其他问题可能会导致问题。我建议使用gdb to determine what is making your program exit

【讨论】:

  • 我非常清楚这一点哈哈 - 不幸的是,没有其他虚拟化程序执行 (1) 动态二进制转换,并且 (2) 支持 AARCH64(我知道)。幸运的是,结果证明这是一个简单(尽管丑陋)的修复方法。
【解决方案2】:

问题已通过编辑void cpu_loop(CPUX86State *env) 中的以下部分解决。我在执行之前捕获sys_exit_groupsys_exit 系统调用,然后从函数中返回。

原文:

void cpu_loop(CPUX86State *env)
{
    CPUState *cs = CPU(x86_env_get_cpu(env));
    int trapnr;
    abi_ulong pc;
    target_siginfo_t info;

    for(;;) {
        cpu_exec_start(cs);
        trapnr = cpu_x86_exec(env);
        cpu_exec_end(cs);
        switch(trapnr) {
        case 0x80:
            /* linux syscall from int $0x80 */
            env->regs[R_EAX] = do_syscall(env,
                                          env->regs[R_EAX],
                                          env->regs[R_EBX],
                                          env->regs[R_ECX],
                                          env->regs[R_EDX],
                                          env->regs[R_ESI],
                                          env->regs[R_EDI],
                                          env->regs[R_EBP],
                                          0, 0);
            break;
#ifndef TARGET_ABI32
        case EXCP_SYSCALL:
            /* linux syscall from syscall instruction */
            env->regs[R_EAX] = do_syscall(env,
                                          env->regs[R_EAX],
                                          env->regs[R_EDI],
                                          env->regs[R_ESI],
                                          env->regs[R_EDX],
                                          env->regs[10],
                                          env->regs[8],
                                          env->regs[9],
                                          0, 0);
            break;
#endif

修改:

void cpu_loop(CPUX86State *env)
{
    CPUState *cs = CPU(x86_env_get_cpu(env));
    int trapnr;
    abi_ulong pc;
    target_siginfo_t info;

    for(;;) {
        cpu_exec_start(cs);
        trapnr = cpu_x86_exec(env);
        cpu_exec_end(cs);
        switch(trapnr) {
        case 0x80:
            /* linux syscall from int $0x80 */
            env->regs[R_EAX] = do_syscall(env,
                                          env->regs[R_EAX],
                                          env->regs[R_EBX],
                                          env->regs[R_ECX],
                                          env->regs[R_EDX],
                                          env->regs[R_ESI],
                                          env->regs[R_EDI],
                                          env->regs[R_EBP],
                                          0, 0);
            break;
#ifndef TARGET_ABI32
        case EXCP_SYSCALL:
            /* linux syscall from syscall instruction */
----> if ((env->regs[R_EAX] == __NR_exit_group) || (env->regs[R_EAX] == __NR_exit)) {
                return; 
            }
            env->regs[R_EAX] = do_syscall(env,
                                          env->regs[R_EAX],
                                          env->regs[R_EDI],
                                          env->regs[R_ESI],
                                          env->regs[R_EDX],
                                          env->regs[10],
                                          env->regs[8],
                                          env->regs[9],
                                          0, 0);
            break;
#endif

【讨论】:

  • 您能否解释一下您所做的更改,以便其他人可以在未来受益?
  • @SevenBits 是的 - 刚刚编辑了解决方案。也切换到原来的标题 - 它认为它更好地抓住了问题的本质。感谢您的建议!
猜你喜欢
  • 2010-09-09
  • 2015-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-29
  • 2011-07-14
相关资源
最近更新 更多