QEMU 用户模式仿真是否以防止 pthread_join 阻塞的方式退出?

Does QEMU user mode emulation exit in a way that would prevent pthread_join from blocking?

我正在尝试 运行 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(tid),我读过它会阻塞调用线程,直到线程 tid returns。 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)。所以我猜这个系统调用正在终止我正在 运行ning 的整个过程。如果有任何关于如何解决该问题的提示,我将不胜感激!

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

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

原文:

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 [=10=]x80 */
            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 [=11=]x80 */
            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