2015-06-26 2 views
0

Я пытаюсь запустить эмулятор режима пользователя QEMU в виде потока в более крупной программе, которую я пишу. Я изменил файл linux-user/main.c, так что теперь стандартная функция int main(int argc, char **argv, char **envp называется void *qemu_user_mode_func(void *arg). Я также добавил pthread_exit(NULL) в конце этой функции, как это стандартная практика для pthreads (или так мне сказали).Выходит ли эмуляция режима пользователя QEMU таким образом, чтобы предотвратить блокирование pthread_join?

Однако, когда я пытаюсь запустить второй поток, который содержит свою собственную функцию тестирования (как показано ниже в void *test_func(void *arg)), процесс выходит перед вторым потоком завершается, даже с вызовом pthread_join(tid), который я ve читает блокирует вызывающую нить до тех пор, пока нить 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; 
} 

EDIT: Я обнаружил в функции void cpu_loop(CPUX86State *env), что когда эмулировать программа достигает свое заключение, QEMU вызывает системный вызов 231, который sys_exit_group (согласно 1). Поэтому я предполагаю, что этот syscall завершает весь процесс, который я запускаю. Я был бы признателен за любые советы о том, как обойти это!

+0

Почему вы не просто 'вилка()' и называют основной функцией QEMU в раздвоенной ребенка? – caf

+0

@caf - Это связано с тем, как я использую qemu - в основном, я работаю над использованием в нем динамической бинарной системы перевода, известной как генератор крошечного кода. Я пытаюсь начать DBT частично через выполнение программы - как и в, программа запускается на собственном оборудовании и переносится на виртуальную машину частично через выполнение. Поскольку секции text + data двоичного файла разделяются между потоками, любые ссылки на память этих разделов действительны в обоих потоках; все, что меняется, это стек (который является целым другим зверьком). Я согласен, что 'fork' - лучшее решение, как правило,! – tonysdg

ответ

0

Проблема была решена путем редактирования следующего раздела в void cpu_loop(CPUX86State *env). Я фиксирую либо sys_exit_group, либо sys_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 

Modified:

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 
+0

Не могли бы вы объяснить изменения, которые вы сделали, чтобы другие могли извлечь выгоду в будущем? – SevenBits

+0

@SevenBits Yup - только что отредактировал решение. Также переключился на исходное название - он считает, что он лучше отражает суть проблемы. Спасибо за предложение! – tonysdg

0

Если вы превратите сложное существовавшее ранее приложение в поток, будут проблемы. Во-первых, приложение может вызывать exit или его варианты, которые завершат всю вашу программу. Существует множество других проблем, которые могут вызвать проблемы. Я бы предложил использовать gdb to determine what is making your program exit.

+0

Я в курсе этого хаха - к сожалению, ни одна другая программа виртуализации не выполняет (1) динамический двоичный перевод, а (2) поддерживает AARCH64 (что я знаю). К счастью, это оказалось простым (хотя и уродливым) решением. – tonysdg

Смежные вопросы