web-dev-qa-db-ja.com

スタックアドレスがメモリアドレスの減少に向かって増加するのはなぜですか?

メモリアドレスを減らすとスタックが大きくなることを教科書で読みました。つまり、上位アドレスから下位アドレスへ。悪い質問かもしれませんが、コンセプトが正しくありませんでした。説明できますか?

32
Jestin Joy

まず、プラットフォームに依存します。一部のアーキテクチャでは、スタックはアドレス空間の下部から割り当てられ、上に向かって成長します。

スタックがアドレス空間の上から下に向かって成長するx86のようなアーキテクチャを想定すると、考え方は非常に簡単です。

===============     Highest Address (e.g. 0xFFFF)
|             |
|    STACK    |
|             |
|-------------|  <- Stack Pointer   (e.g. 0xEEEE)
|             |
.     ...     .
|             |
|-------------|  <- Heap Pointer    (e.g. 0x2222)
|             |
|    HEAP     |
|             |
===============     Lowest Address  (e.g. 0x0000)

スタックを拡大するには、スタックポインタを減らします。

===============     Highest Address (e.g. 0xFFFF)
|             |
|    STACK    |
|             |
|.............|  <- Old Stack Pointer (e.g. 0xEEEE)
|             |
| Newly       |
| allocated   |
|-------------|  <- New Stack Pointer (e.g. 0xAAAA)
.     ...     .
|             |
|-------------|  <- Heap Pointer      (e.g. 0x2222)
|             |
|    HEAP     |
|             |
===============     Lowest Address    (e.g. 0x0000)

ご覧のように、スタックを拡大するには減少スタックポインターを0xEEEEから0xAAAAに設定しますが、ヒープを拡大するにはヒープポインターを拡大する必要があります。

明らかに、これはメモリレイアウトの簡略化です。実際の実行可能ファイル、データセクションなどもメモリにロードされます。さらに、スレッドには独自のスタックスペースがあります。

なぜスタックが下向きに成長するのかと尋ねるかもしれません。さて、前に述べたように、一部のアーキテクチャはその逆を行い、ヒープを下向きに成長させ、スタックを上向きに成長させます。スタックとヒープを反対側に置くことは、重複を防ぎ、十分なアドレス空間が確保されている限り両方の領域を自由に拡大できるため、理にかなっています。

別の有効な質問は次のとおりです。プログラムはスタックポインター自体を減少/増加させるはずではありませんか?アーキテクチャは、プログラマーにどのように一方を他方に課すことができますか?アーキテクチャに依存するほどプログラムに依存しないのはなぜですか?アーキテクチャとかなり戦うことができ、スタックを逆方向に逃がすことができますが、特にcallretは、スタックポインタを直接変更するため、別の方向を想定して、混乱。

53
Mehrdad Afshari

今日、それは長い間そのように行われており、多くのプログラムがそれがそのように行われたと想定していて、それを変更する本当の理由がないためです。

恐竜が地球を歩き回ったとき、運がよければコンピュータに8kBのメモリがあったとき、それは重要なスペースの最適化でした。スタックの一番下をメモリの一番上に置き、下に向かって成長し、プログラムとそのデータを一番下に置き、malloc領域を大きくします。このようにして、スタックのサイズの唯一の制限は、プログラム+ヒープのサイズであり、その逆も同様です。スタックが(たとえば)4kBで開始して大きくなった場合、プログラムが数百バイトのスタックしか必要としなかったとしても、ヒープが4kB(プログラムのサイズを引いたもの)より大きくなることはありません。

21
zwol

Man CLONE:child_stack引数は、子プロセスが使用するスタックの場所を指定します。子プロセスと呼び出しプロセスはメモリを共有する可能性があるため、子プロセスを呼び出しプロセスと同じスタックで実行することはできません。したがって、呼び出しプロセスは子スタックのメモリ空間を設定し、この空間へのポインタをclone()に渡す必要があります。 Linuxを実行するすべてのプロセッサー(HP PAプロセッサーを除く)ではスタックが下向きに成長するため、child_stackは通常、子スタック用にセットアップされたメモリー空間の最上位アドレスを指します。

0

X86では、メモリアドレスの減少に向けてスタックが大きくなる主な理由は、Push命令がスタックポインタをデクリメントすることです。

スタックポインターをデクリメントし、ソースオペランドをスタックの一番上に格納します。

を参照してください。 インテル®64およびIA-32アーキテクチャの4-511ソフトウェア開発者向けマニュアル

0
David Cullen