10

わかりました、初心者の学生に少し質問があります。

したがって、スタックにはサブルーチン呼び出しが含まれ、ヒープには可変長のデータ構造が含まれ、グローバル静的変数は永続的なメモリ位置に割り当てられるという事実に精通しています。

しかし、理論的でないレベルではどのように機能するのでしょうか?

コンパイラは、アドレス 0 からアドレス無限大までのメモリ領域全体を持っていると想定していますか? そして、ものを割り当て始めますか?

また、命令、スタック、ヒープをどこに配置するのでしょうか? メモリ領域の一番上、メモリ領域の終わり?

そして、これは仮想メモリでどのように機能しますか? 仮想メモリはプログラムに対して透過的ですか?

途方もない質問で申し訳ありませんが、私はプログラミング言語の構造を取り上げており、これらの領域を参照し続けており、より実用的なレベルでそれらを理解したいと思っています。

よろしくお願いします!

4

3 に答える 3

11

包括的な説明は、おそらくこのフォーラムの範囲を超えています。テキスト全体が主題に専念しています。ただし、単純化したレベルでは、このように見ることができます。

コンパイラはコードをメモリに配置しません。それ自体にメモリ領域全体があると想定しています。コンパイラは、オブジェクト ファイル内のシンボルが通常オフセット 0 で始まるオブジェクト ファイルを生成します。

リンカーは、オブジェクト ファイルをまとめて、シンボルをリンクされたオブジェクト内の新しいオフセット位置にリンクし、実行可能ファイル形式を生成する役割を果たします。

リンカはコードをメモリに配置しません。コードとデータを、通常.textは実行可能コードの指示や.data、グローバル変数や文字列定数などのラベルが付けられたセクションにパッケージ化します。(そして、さまざまな目的のための他のセクションもあります) リンカは、シンボルを再配置するオペレーティング システム ローダーにヒントを提供する場合がありますが、ローダーは強制する必要はありません。

実行可能ファイルを解析し、コードとデータをメモリ内のどこに配置するかを決定するのは、オペレーティング システムのローダーです。その場所は、オペレーティング システムに完全に依存します。通常、スタックはプログラムの命令やデータよりも上位のメモリ領域に配置され、下に向かって成長します。

各プログラムは、それ自体にアドレス空間全体があるという前提でコンパイル/リンクされます。ここで仮想メモリの出番です。仮想メモリはプログラムに対して完全に透過的であり、オペレーティング システムによって完全に管理されます。

仮想メモリの範囲は通常、アドレス 0 から、プラットフォームでサポートされている最大アドレス (無限ではありません) までです。この仮想アドレス空間は、オペレーティング システムによってカーネル アドレス空間とユーザー アドレス空間に分割されます。架空の 32 ビット OS では、上のアドレス0x80000000はオペレーティング システム用に予約されており、下のアドレスはプログラムで使用するためのものです。プログラムがこのパーティションより上のメモリにアクセスしようとすると、中断されます。

オペレーティング システムは、スタックがアドレス指定可能な最上位のユーザー メモリから開始し、プログラム コードがはるかに低いアドレスにあると判断する場合があります。

通常、ヒープの場所は、プログラムをビルドしたランタイム ライブラリによって管理されます。プログラムコードとデータの後に利用可能な次のアドレスから始まる可能性があります。

于 2013-09-30T19:17:35.347 に答える
2

これは、多くのトピックを含む広く開かれた質問です。

典型的なコンパイラ -> アセンブラ -> リンカー ツールチェーンを想定しています。コンパイラは多くのことを知りません。スタックの相対的なものをエンコードするだけで、スタックの量や場所は気にしません。それがスタックの目的/美しさです。気にしません。コンパイラはアセンブラを生成し、アセンブラはオブジェクトにアセンブルされます。リンカは、メモリ空間の詳細を伝えるいくつかのフレーバーまたはコマンド ライン引数の情報リンカ スクリプトを受け取ります。

gcc hello.c -o hello

binutils のインストールには、ターゲット (Windows、Mac、Linux、実行しているすべてのもの) に合わせて調整されたデフォルトのリンカー スクリプトがあります。そして、そのスクリプトにはプログラム空間の開始位置に関する情報が含まれており、そこからヒープの開始位置 (テキスト、データ、および bss の後) を認識します。スタック ポインターは、そのリンカー スクリプトによって設定されるか、OS によって別の方法で管理される可能性があります。そして、それがスタックを定義します。

Windows、Linux、Mac、BSDラップトップまたはデスクトップコンピューターに搭載されているmmuを備えたオペレーティングシステムの場合、はい、各プログラムは、0x0000から始まる独自のアドレス空間があると想定してコンパイルされます。これは、プログラムがリンクされていることを意味しません0x0000 で実行を開始します。オペレーティング システムの規則が何であるかは、オペレーティング システムによって異なります。たとえば、0x8000 で開始するものもあります。

プログラムの観点から見るとやや単一の線形アドレス空間であるデスクトップのようなアプリケーションの場合、最初に .text があり、次に .data または .bss があり、その後、ヒープはその後のある時点で整列されます。スタックは設定されていますが、通常は高く設定されていますが、プロセッサやオペレーティング システムによって異なります。そのスタックは通常、そのメモリの最上部であるプログラム ビュー内にあります。

仮想メモリは、これらすべてからは見えません。アプリケーションは通常、仮想メモリを認識したり気にしたりしません。アプリケーションが命令をフェッチしたり、データ転送を行ったりする場合、オペレーティング システムによって構成され、仮想と物理の間で変換を行うハードウェアを経由します。mmu が障害を示している場合、つまりスペースが物理アドレスにマップされていないことを意味する場合、それは意図的なものである可能性があり、「仮想メモリ」という用語の別の使用法が適用されます。この 2 番目の定義では、オペレーティング システムは、たとえば、他のメモリ チャンクを取得し、それをハードディスクに移動して、他のチャンクを存在しないものとしてマークすることができます。次に、チャンクをラムを持つものとしてマークし、他の誰かから取らなければならないことを知らなかったラムで中断されたことを知らずに実行させます。設計上、アプリケーションはこれを知りたくありません。ただ実行したいだけで、オペレーティングシステムが物理メモリと仮想(ゼロベース)アドレス空間を提供する mmu を管理します...

最初は mmu を使用せずに、後でマイクロコントローラー、qemu、raspberry pi、beaglebone などを使用してベアメタル プログラミングを少し行う場合、コンパイラー、リンカー スクリプト、および mmu の構成の両方で手を汚すことができます。これにはx86ではなくアームまたはミップを使用しますが、あなたの人生を楽にするために、全体的な全体像はすべてターゲット間で直接変換されます.

于 2013-09-30T19:23:55.713 に答える
1

場合によります。

ゼロから始めなければならないブートローダをコンパイルしている場合は、メモリ全体を自分で持っていると想定できます。

一方、アプリケーションをコンパイルしている場合は、メモリ全体を自分用に持っていると想定できます。

小さな違いは、最初のケースでは、すべての物理メモリを自分用に持っていることです。ブートローダーとして、RAM にはまだ何もありません。2 番目のケースでは、メモリ内に OS がありますが、(通常は) 仮想メモリが設定されるため、アドレス空間全体を自分用に持っているように見えます。ただし、通常は OS に実際のメモリを要求する必要があります。

後者は、OS がいくつかの規則を課すことを意味します。たとえば、OS は、プログラムの最初の命令がどこにあるかを非常に知りたがっています。簡単なルールとして、プログラムは常にアドレス 0 から始まるので、C コンパイラはint main()そこに置くことができます。OS は通常、スタックがどこにあるかを知りたがりますが、これはすでにより柔軟なルールです。「ヒープ」に関する限り、OSは本当に気にしませんでした。

于 2013-09-30T19:13:13.100 に答える