動機
setjmp
後でジャンプして関数を呼び出し、スタックフレームが互いに実行されるようにするために、さまざまな環境を保存しようとしています。例えば:
env[0] スタック:
----------
| text |
----------
| data |
----------
| |
| |
| |
| |
| |
| |
| |
| ~~~~ |
----------
ここで、チルダはメインなどのスタック フレームを表します。
env[1] スタック:
----------
| text |
----------
| data |
----------
| |
| |
| |
| **** |
| **** |
| **** |
| **** |
| ~~~~ |
----------
星は、私が割り当てた大きな配列を表しています。
アイデアは、いくつかのメソッドの実行を開始してからいくつlongjmp
かのメソッドの実行を開始すると、 のスタック フレームがその配列の空きスペースを埋め始め、 のスタック フレームがその配列の上にあるということです。のスタック フレームを上書きしません。基本的な糸通し。env[0]
longjmp
env[1]
env[0]
env[1]
env[0]
また、2 つ以上のスレッドが必要です。実際、MAXTHREADS が必要です。直感的には、次のように機能します。
for(int i = 0; i < MAXTHREADS; i++) {
char c[STACKSIZE];
if( setjmp(env[i]) != 0 ) {
/* Stuff that thread i will do goes here */
}
}
テスト 1
ただし、次のテスト プログラムで明確にわかるように、各 c 配列はスタックの同じ場所から開始され、目的に反しています。
for(int i = 0; i < 10; i++) {
char c[10];
printf("On Iteration %d array starts at %x and goes to %x\n",i,c,c+10);
}
出力:
On Iteration 0 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 1 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 2 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 3 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 4 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 5 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 6 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 7 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 8 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 9 array starts at ffbfef02 and goes to ffbfef0c
これは多くの理由で理にかなっています。スコープは、c 配列がループの最後に存在しなくなることを指示します。さらに、サイズが静的であるため、コンパイラはおそらくそれをループの外に移動します。
テスト 1 で必要なサイズが得られましたが、必要な割り当てではありませんでした
テスト 2
次に考えたのは、STACKSIZE * i の配列を割り当てることでした。これにより、配列がそれぞれ同じ場所から開始されたとしても、そのたびに大きくなり、その環境に切り替えるときにスタック ポインターを十分に押し上げることができます。 . テストは次のとおりです。
for(int i = 0; i < 10; i++) {
char c[10 * i];
printf("On Iteration %d array starts at %u and goes to %u\n",i,c,c+(10*i));
//note: switched from %x to %u to make comparison mentally simpler
}
ただし、これは出力します
On Iteration 0 array starts at 4290768760 and goes to 4290768760
On Iteration 1 array starts at 4290768744 and goes to 4290768754
On Iteration 2 array starts at 4290768720 and goes to 4290768740
On Iteration 3 array starts at 4290768688 and goes to 4290768718
On Iteration 4 array starts at 4290768648 and goes to 4290768688
On Iteration 5 array starts at 4290768592 and goes to 4290768642
On Iteration 6 array starts at 4290768528 and goes to 4290768588
On Iteration 7 array starts at 4290768456 and goes to 4290768526
On Iteration 8 array starts at 4290768376 and goes to 4290768456
On Iteration 9 array starts at 4290768280 and goes to 4290768370
最初は少し見づらいですが、数字が大きいため、下 3 桁だけに集中できます。
最初の配列は760から760になります -- これは i = 0 の場合です
2 番目の配列は744から754になります -- 少し移動しましたが、必要な一般的な位置にあるサイズ 10 の配列です。
3 番目の配列は720から740になります-- うーん、配列は他の配列と同じ場所から始まっていませんでした。サイズが 2 倍になっています。
スタックのパターンを説明するために、各 * は配列内の 10 文字を表し、星のチャンク間のギャップは配列間のギャップです。
*
*
*
*
*
*
~
チルダがメインなどはこれまで通り。
パターンは続きます。これはおそらくマシンに依存しますが、一定サイズの配列の場合のように毎回下に戻るのではなく、私のマシンは配列を順番に割り当てています。
テスト 2 では、必要な割り当てが得られましたが、サイズが希望よりもはるかに大きくなっています。
テスト 3
私のマシンは、同じサイズの場合は互いに上書きする配列を割り当てますが、サイズが異なる場合は互いに積み重ねることを知ったので、それぞれのSTACKSIZE + i
代わりに割り当てることができるのではないかと考えましたSTACKSIZE * i
。この実験を実行しました:
for(int i = 0; i < 10; i++) {
char c[10+i];
printf("On Iteration %d array starts at %u and goes to %u\n",i,c,c+10+i);
}
どの出力:
On Iteration 0 array starts at 4290768808 and goes to 4290768818
On Iteration 1 array starts at 4290768792 and goes to 4290768803
On Iteration 2 array starts at 4290768776 and goes to 4290768788
On Iteration 3 array starts at 4290768760 and goes to 4290768773
On Iteration 4 array starts at 4290768744 and goes to 4290768758
On Iteration 5 array starts at 4290768728 and goes to 4290768743
On Iteration 6 array starts at 4290768712 and goes to 4290768728
On Iteration 7 array starts at 4290768688 and goes to 4290768705
On Iteration 8 array starts at 4290768664 and goes to 4290768682
On Iteration 9 array starts at 4290768640 and goes to 4290768659
分析後、期待したのと同じ割り当て動作を使用しています。
テスト 3 では、必要な割り当てと、希望に近いサイズが得られましたが、まだ完全ではありません
質問
したがって、テスト 3 の戦略は元のコードで機能し、STACKSIZE + i の MAXTHREADS 配列を割り当て、それぞれの間に環境を保存しますが、最適ではないようです。コンパイラをだますためだけに、各配列に余分なスペースを割り当てています。
これを行うより良い方法はありますか?コンパイラに STACKSIZE の新しい配列を毎回割り当てるようにする他のトリックはありますか?
テスト 1 のサイズで、テスト 2 と 3 の割り当てを取得するにはどうすればよいですか?