スタックフレームの概念を簡単に説明する


200

プログラミング言語の設計では、コールスタックの考え方を理解しているようです。しかし、私はスタックフレームが何であるかについての適切な説明を見つけることができません(おそらく、私は十分に検索していません)。

それで、私に誰かにそれを簡単に説明してもらいたいと思います。

回答:


195

スタックフレームは、スタックにプッシュされるデータのフレームです。呼び出しスタックの場合、スタックフレームは関数呼び出しとその引数データを表します。

私の記憶が正しければ、関数の戻りアドレスが最初にスタックにプッシュされ、次にローカル変数の引数とスペースがプッシュされます。これらは一緒になって「フレーム」を作りますが、これはおそらくアーキテクチャに依存します。プロセッサは各フレームのバイト数を認識しており、フレームがスタックからプッシュおよびポップされると、それに応じてスタックポインタを移動します。

編集:

上位レベルの呼び出しスタックとプロセッサの呼び出しスタックの間には大きな違いがあります。

プロセッサのコールスタックについて話すときは、アセンブリコードまたはマシンコードでバイト/ワードレベルのアドレスと値を操作することについて話しています。高水準言語について話すときは「コールスタック」がありますが、これらはランタイム環境によって管理されるデバッグ/ランタイムツールであり、プログラムで発生した問題を(高レベルで)ログに記録できます。このレベルでは、行番号、メソッド名、クラス名などがよく知られています。プロセッサがコードを取得するまでには、これらの概念はまったくありません。


6
「プロセッサは、各フレームに何バイトあるかを認識しており、フレームがスタックからプッシュおよびポップされると、それに応じてスタックポインタを移動します。」-私はプロセッサがスタックについて何かを知っているのではないかと思います。なぜなら、私たちはサブビング(割り当て)、プッシュ、ポップを介してそれを操作するからです。そしてここに、スタックの使用方法を説明する呼び出し規約があります。
Victor Polevoy

78

スタックをよく理解していれば、プログラムでメモリがどのように機能するかを理解し、プログラムでメモリがどのように機能するかを理解すれば、プログラムでの関数ストアの方法を理解し、プログラムでの関数ストアの方法を理解すれば、再帰関数の機能と再帰関数がどのように機能するかを理解し、コンパイラがどのように機能するかを理解し、コンパイラがどのように機能するかを理解すれば、コンパイラとして機能し、プログラムを非常に簡単にデバッグできます。

スタックの仕組みを説明しましょう:

最初に、関数がスタックでどのように表されるかを知る必要があります。

ヒープには、動的に割り当てられた値が格納されます。
Stackは、自動割り当てと削除の値を保存します。

ここに画像の説明を入力してください

例で理解しましょう:

def hello(x):
    if x==1:
        return "op"
    else:
        u=1
        e=12
        s=hello(x-1)
        e+=1
        print(s)
        print(x)
        u+=1
    return e

hello(4)

このプログラムの一部を理解してください:

ここに画像の説明を入力してください

スタックとは何か、スタックパーツとは何かを見てみましょう。

ここに画像の説明を入力してください

スタックの割り当て:

1つの点に注意してください。ローカル変数がロードされているかどうかに関係なく、関数の戻り条件が満たされると、関数はそのスタックフレームとともにスタックからすぐに戻ります。つまり、再帰関数が基本条件を満たし、基本条件の後にリターンを置くと、基本条件はプログラムの「else」部分にあるローカル変数のロードを待機しません。スタックから現在のフレームがすぐに返され、その後、次のフレームがアクティブ化レコードに追加されます。

実際にこれを見てください:

ここに画像の説明を入力してください

ブロックの割り当て解除:

したがって、関数がreturnステートメントに遭遇すると、現在のフレームがスタックから削除されます。

スタックから戻る際、スタックに割り当てられた元の順序とは逆の値が返されます。

ここに画像の説明を入力してください


3
スタックは下向きに成長し、ヒープは上向きに成長します。ダイアグラムでそれらを逆にします。ここで正しい図
ラファエル

@Rafael混乱して申し訳ありません。成長の方向について話していました。スタックの成長の方向については話していませんでした。成長の方向とスタックの成長の方向には違いがあります。ここを参照してください stackoverflow.com/questions/1677415/...
Aaditya裏

2
ラファエルは正しいです。また、最初の画像は間違っています。それを別のもので置き換えてください(「ヒープスタック」のgoogle写真を検索してください)。
ニコス2017年

したがって、私が正しく理解している場合、3番目の図には、が再帰的に呼び出され、その後(再び)hello()が再帰的に呼び出されhello()たため、3つのスタックフレームがhello()あり、グローバルフレームは最初の関数を呼び出した元の関数hello()ですか?
アンディJ

1
リンクは私たちをどこに導きますか?セキュリティの深刻な懸念事項として、これらのリンクはできるだけ早く削除する必要があります。
Shivanshu

45

簡単にまとめます。多分誰かがより良い説明をしています。

呼び出しスタックは、1つまたは複数のスタックフレームで構成されます。各スタックフレームは、リターンでまだ終了していない関数またはプロシージャの呼び出しに対応しています。

スタックフレームを使用するために、スレッドは2つのポインターを保持します。1つはスタックポインター(SP)と呼ばれ、もう1つはフレームポインター(FP)と呼ばれます。SPは常にスタックの「トップ」を指し、FPは常にフレームの「トップ」を指します。さらに、スレッドは、実行される次の命令を指すプログラムカウンター(PC)も保持します。

スタックには、ローカル変数と一時変数、現在の命令の実際のパラメーター(手順、関数など)が格納されます。

スタックのクリーニングに関しては、さまざまな呼び出し規約があります。


7
サブルーチンの戻りアドレスがスタックに置かれることを忘れないでください。
トニーR

4
フレームポインターはx86用語でもベースポインターです
peterchaula

1
フレームポインターが現在アクティブなプロシージャインカネーションのスタックフレームの先頭を指していることを強調したいと思います。
サーバーカリロフ2017年

13

「コールスタックはスタックフレームで構成されています...」-  ウィキペディア

スタックフレームは、スタックに置くものです。これらは、呼び出すサブルーチンに関する情報を含むデータ構造です。


申し訳ありませんが、私がこれをwikiでどうやって見逃したのかわかりません。ありがとう。動的言語では、関数のローカルが正確にわかっていないため、フレームのサイズが定数値ではないことを正しく理解していますか?
ikostia

フレームのサイズと性質は、マシンのアーキテクチャに大きく依存しています。実際、コールスタックのパラダイムはアーキテクチャ固有です。私の知る限り、関数呼び出しが異なれば引数データの量も異なるため、常に可変です。
トニーR

スタックフレームのサイズは、操作時にプロセッサによって認識される必要があることに注意してください。これが発生している場合、データのサイズはすでに決定されています。動的言語は静的言語と同じようにマシンコードにコンパイルされますが、コンパイラーがダイナミズムを維持し、プロセッサーが「既知の」フレームサイズで動作できるように、多くの場合ジャストインタイムで実行されます。高級言語と機械のコード/アセンブリを混同しないでください。これが実際に発生している場所です。
トニーR

まあ、でも動的言語にもコールスタックがありますよね。つまり、たとえば、Pythonがいくつかのプロシージャを実行したい場合、このプロシージャに関するデータは一部のPythonインタープリターの構造内に格納されていますが、正しいですか?つまり、コールスタックが存在するのは低レベルだけではないということです。
ikostia

そのウィキペディアの記事を少し読んだ後、私は(少し)修正しました。スタックフレームのサイズは、コンパイル時に不明のままになることがあります。しかし、プロセッサがスタック+フレームポインタで動作しているときには、そのサイズを知る必要があります。サイズは可変にすることができますが、プロセッサはサイズを知っています。
トニーR

5

プログラマーは、スタックフレームについて幅広い用語(1つの関数呼び出しのみを提供し、戻りアドレス、引数、およびローカル変数を保持するスタック内の単一のエンティティ)ではなく、狭い意味で質問があります–用語stack framesがコンパイラオプションのコンテキスト。

質問の作成者が意図したものかどうかは関係ありませんが、コンパイラオプションの観点から見たスタックフレームの概念は非常に重要な問題であり、ここでは他の回答では扱いません。

たとえば、Microsoft Visual Studio 2015 C / C ++コンパイラには、に関連する次のオプションがありますstack frames

  • / Oy(フレームポインターの省略)

GCCには次のものがあります。

  • -fomit-frame-pointer(フレームポインターを必要としない関数については、レジスターにフレームポインターを保持しないでください。これにより、フレームポインターの保存、セットアップ、および復元の指示が回避されます。また、多くの関数で追加のレジスターを使用できるようになります。 )

インテルC ++コンパイラーには次のものがあります。

  • -fomit-frame-pointer(EBPが最適化で汎用レジスターとして使用されるかどうかを決定します)

次のエイリアスがあります:

  • /オイ

Delphiには、次のコマンドラインオプションがあります。

  • -$ W +(スタックフレームの生成)

その特定の意味では、コンパイラーの観点から、スタックフレームは、アンカーをスタックにプッシュするルーチンの入り口と出口のコードにすぎません。デバッグや例外処理にも使用できます。デバッグツールはスタックデータをスキャンし、これらのアンカーを使用call sitesしてスタック内を検索します。つまり、階層的に呼び出された順序で関数の名前を表示します。Intelアーキテクチャーの場合、これはpush ebp; mov ebp, espまたはenterエントリー用mov esp, ebp; pop ebpまたはleave出口用です。

コンパイラがこのコードを生成するかどうかを制御できるため、コンパイラオプションに関しては、スタックフレームが何であるかをプログラマが理解することが非常に重要です。

場合によっては、スタックフレーム(ルーチンのエントリコードと終了コード)をコンパイラで省略でき、変数には、便利なベースポインタ(BP /ではなく、スタックポインタ(SP / ESP / RSP)を介して直接アクセスします。 ESP / RSP)。スタックフレームの省略の条件。例:

  • 関数はリーフ関数(つまり、他の関数を呼び出さないエンドエンティティ)です。
  • try / finally、try / except、または同様の構成はありません。つまり、例外は使用されません。
  • スタック上の発信パラメーターでルーチンが呼び出されることはありません。
  • 関数にはパラメーターがありません。
  • 関数にはインラインアセンブリコードがありません。
  • 等...

スタックフレーム(ルーチンのエントリコードと終了コード)を省略すると、コードが小さくて高速になりますが、スタック内のデータをバックトレースしてプログラマーに表示するデバッガーの機能に悪影響を与える可能性もあります。これらは、関数がエントリコードと終了コードを持つ条件を決定するコンパイラオプションです。たとえば、(a)常に、(b)決して、(c)必要な場合(条件を指定)です。


-1

スタックフレームは、関数呼び出しに関連するパックされた情報です。この情報には通常、関数に渡される引数、ローカル変数、終了時に戻る場所が含まれます。アクティベーションレコードは、スタックフレームの別名です。スタックフレームのレイアウトはABIでメーカーによって決定され、ISAをサポートするすべてのコンパイラはこの標準に準拠する必要がありますが、レイアウトスキームはコンパイラに依存する場合があります。一般的にスタックフレームのサイズは制限されていませんが、「レッド/保護ゾーン」と呼ばれる概念があり、システムコールなどをスタックフレームに干渉することなく実行できます。

常にSPがありますが、一部のABI(ARMやPowerPCなど)では、FPはオプションです。スタックに配置する必要がある引数は、SPのみを使用してオフセットできます。スタックフレームが関数呼び出し用に生成されるかどうかは、引数のタイプと数、ローカル変数、ローカル変数への一般的なアクセス方法によって異なります。ほとんどのISAでは、最初にレジスタが使用され、引数を渡すための専用レジスタよりも引数が多い場合、それらはスタックに配置されます(たとえば、x86 ABIには整数引数を渡すための6つのレジスタがあります)。したがって、一部の関数では、スタックにスタックフレームを配置する必要がなく、戻りアドレスがスタックにプッシュされるだけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.