-1

私はこれを見ました:ユーザーレベルのスレッドパッケージの実装とそれは適用されません。

スレッドを割り当ててスタックを作成するThread_new (int func(void*)) の実装中に、私が正しい場合、プログラム カウンター (%eip) を設定する方法を考えることができないため、スレッドがスケジューラによって開始されると、指定された関数の (func) エントリ ポイントから開始されます。

多くの c のみ (アセンブリなし) の実装を見てきましたが、次のコード (x86) が提供されています。

_thrstart:
    pushl  %edi
    call *%esi
    pushl %eax
    call Thread_exit

%edi をスタックにプッシュする特定の理由はありますか? バイトコピー以外にesi/ediの別の用途が見つからないようです。

*%esi への間接呼び出しは、おそらく新しいスレッドのコンテキストから関数を呼び出すために使用されることを認識していますが、それとは別に、%esi が有効な関数であることをどのように (または何を) 指しているのか理解していないようです。 Thread_new から _thrstart を呼び出したときのアドレス

ノート:

Thread_exit は、c で実装されたクリーンアップ スレッドです。

これは宿題です

4

3 に答える 3

3

一般に; 「スケジューラ」を4つの部分に分解できます。

最初の部分は、あるスレッドから別のスレッドに切り替えるメカニズムです。これには主に、前のスレッドの状態をどこかに保存し、次のスレッドの状態をどこかからロードすることが含まれます。ここで、「どこか」は、ある種のスレッド制御ブロックである可能性もあれば、スレッドのスタックである可能性もあれば、その両方である可能性もあります。スレッドの状態には、汎用レジスタの内容、スタック トップ ( esp)、命令ポインタ ( eip)、その他 (MMX/SSE/AVX レジスタ) が含まれる場合があります。ただし、協調スケジューリングの場合、スレッドの状態ははるかに少なくなる可能性があります (たとえば、スレッドの状態のほとんどはスレッド切り替えによって破棄され、協調スケジューリングが使用されるため、スレッド自体がその状態がいつ破棄されるかを認識し、それに備えることができます)。 )。

2 番目の部分は、いつスレッドを切り替えるか、どのスレッドに切り替えるかを決定することです。これは、スケジューラによって大きく異なります。

3 番目の部分は、スレッドの開始です。これには主に、スレッドの切り替え中にロードされるデータの構築が含まれます。ただし、最初にスレッドを作成するときに最小限の状態のみを作成し、CPU 時間が与えられた後に残りのスレッドの状態の作成を完了する「怠惰な」方法でこれを行うことは可能です。

4 番目の部分は、スレッドの終了です。これには、スレッド切り替え中にロードされるデータの破棄/解放が含まれます。しかし、スレッドが解放できなかったリソース (ファイル ハンドル、ネットワーク接続、スレッド ローカル ストレージなど) をクリーンアップして、「リソース リーク」が発生しないようにすることも意味します。

于 2014-10-19T00:49:43.880 に答える
2

通常、単純な RTOesses では、スレッドは呼び出されたりジャンプしたりして開始されるのではなく、返されるか割り込みで返されることで開始されます。

秘訣は、スレッドが以前に実行されていて、スケジューラを呼び出したか、割り込みを介してスレッドに入ったように見えるように、新しいスタックの一番上にデータを集めることです。この「フレーム」の下部には、スレッド関数のアドレスが表示されます。その後、スタック ポインターにフレームのアドレスをロードし、割り込みを有効にし、RET または IRET を実行してスレッド関数を開始できます。

また、新しいスレッドが取得できるパラメーターと、「TerminateThread」または「Thread_Exit」への呼び出しを最初に押し込むことも便利です。これにより、スレッド関数が返された場合に、スケジューラーがそれを終了できます。

于 2014-10-19T07:00:16.523 に答える
0

問題は以前ほど複雑ではなかったようです。

@Martin James の回答に基づいて、戻りアドレスが _thrstart 関数になるようにスタックが準備されます。コンテキスト スイッチを実行するために使用されるアセンブリに基づいて、レジスタediおよびesiがスタック上の特定の場所に格納されます (スレッドが非アクティブな場合)。ediesi汎用レジスタとして使用することにより、edi にはvoid*引数が含まれ、 esiには新しいスレッドから呼び出される関数のアドレスが含まれます。

_thrstart:
pushl  %edi        #pushes argument for function func to the stack
call *%esi         #indirect call to func
pushl %eax         #Expect return value in eax, push to stack
call Thread_exit   #Call thread cleanup
于 2014-10-19T15:51:22.170 に答える