2

ユーザー スレッド ライブラリに関するいくつかの質問を見てきましたが、私の質問に答えるようなものはありませんでした。スレッドを作成し、実行し、キャンセルし、終了することができます。何らかの理由でできないのは、データを返すスレッドを取得することです。

スレッド ライブラリを初期化するときに、終了スレッド コンテキストを次のように設定します。

getcontext(&threadEnd);
threadEnd.uc_stack.ss_sp = (char *)malloc(SIGSTKSZ);
threadEnd.uc_stack.ss_size = SIGSTKSZ;
makecontext(&threadEnd, (void *) thread_exit, 1, &ReturnValue);

スレッドを作成し、次のように割り当てます。

thread->myContext.uc_stack.ss_sp = (char *) malloc(SIGSTKSZ);
thread->myContext.uc_stack.ss_size = SIGSTKSZ;
thread->myContext.uc_link = &threadEnd;

関数が戻り、thread_exit()が呼び出されると:

    void thread_exit(void* retval){
    int* test;
    if (retval != NULL)
    {
        test = (int*) retval;
        printf("Returned value: %i\n", *test);
        fflush(stdout);
    }

出力は常に「戻り値: 0」です。

呼び出された関数は、整数へのポインタを返しています。

私は何を間違っていますか?

4

4 に答える 4

1

プログラムを GBD makecontext でステップ実行する場合、コンテキストを作成するために使用される関数の戻り値を保存しないでください。

私の実験の例:(raxレジスタを観察してください):

リターンステートメントで:

thread1 (arg=0x1) at test_create_join.c:14
14      return (void *)11;
Value returned is $1 = 19
(gdb) info registers
rax            0x13 19
---

帰国後:

(gdb) step
15  }
(gdb) info registers
rax            0xb  11

内部コンテキスト スイッチ:

__start_context () at ../sysdeps/unix/sysv/linux/x86_64/__start_context.S:32
32  ../sysdeps/unix/sysv/linux/x86_64/__start_context.S: No such file or directory.
(gdb) info registers
rax            0xb  11

いくつかの命令で戻り値が保持されていることがわかりますが、いくつかのステップの後、0 になります。明らかに x86_64 アーキテクチャに固有のものですが、ほとんどのアーキテクチャ (つまり、makecontext の動作) と同じであると思います。

スレッド関数への戻り値が必要な場合は、別の方法で行うことができます。スレッドを実行するためのスレッド ハンドラを作成し、そのハンドラを使用して新しいコンテキストを作成するだけです。ここで、スレッドとして実行する関数の戻り値を取得し、後で使用するためにスレッド制御ブロック構造に保存できます。

typedef struct {
    thread_start_routine start;
    void *arg;
} entry_point;

static void thread_runner(void *args) {
    exit_critical_section();

    entry_point *entry = (entry_point *) args;

    // run the thread and get the exit code
    current->retcode = entry->start(entry->arg);
}
于 2015-05-28T09:53:55.280 に答える
0

まず第一に、 に引数を与えるときmakecontext、最後の引数は常にNULL:

makecontext(&threadEnd, (void *) thread_exit, 1, &ReturnValue, NULL);

私は同じ問題を抱えていました。つまり、uc_linkI storedを使用する代わりにstart_routine、つまりfunc、構造argの内部でthreadラッパー関数を使用する代わりに、少し異なる方法で取り組みました。thread_runner実際にスレッドの関数を呼び出し、戻り値を格納します。

makecontext(&thread->ctx, (void *) thread_runner, 1, thread, NULL);

スレッドランナーは次のとおりです。

void thread_runner(thread_t *thread){
  void **retval = thread->func(thread->arg);
  if(retval != NULL){
    thread_exit(*retval);
  } else {
    thread_exit(NULL);
  }
}
于 2015-09-10T07:41:58.563 に答える
0

makecontext()のパラメーター リストでポインターを渡すことは想定されていません。マニュアルで指定されているように、整数でなければなりません。

関数 func が呼び出され、argc に続く一連の整数 (int) 引数が渡されます。呼び出し元は、argc でこれらの引数の数を指定する必要があります。

一部のアーキテクチャでは、整数はポインタと同じサイズ (たとえば 32 ビット) ですが、他のアーキテクチャではポインタは 64 ビットで、整数は 32 ビット長です。最近の GLIBC/Linux x86_64 アーキテクチャーでは、パラメーターは「long long」整数としてコンテキストに保管されます。そのため、これはパラメーターを 64 ビット値 (ポインターのサイズと互換性があります) として格納するため機能する可能性がありますが、移植性はありません。したがって、これは、「&ReturnValue」ポインターを渡して正しい値を取得できない理由を説明している可能性があります。

于 2021-01-18T12:49:03.117 に答える