26

実行すると、プログラムは仮想アドレス 0x80482c0 から実行を開始します。このアドレスは、プロシージャーではなく、リンカーによって作成されたmain()という名前のプロシージャーを指しています。_start

これまでの私の Google の調査では、次のような (漠然とした) 歴史的な憶測にたどり着きました。

0x08048000 はかつて、カリフォルニア州サンタクルーズのグループによって公布された *NIX から i386 への移植で、0x08048000 付近から 0x08048000 付近から下方に成長したスタックであったという伝承があります。これは、128MB の RAM が高価であり、4GB の RAM は考えられない時代でした。

誰でもこれを確認/拒否できますか?

4

2 に答える 2

37

Mads が指摘したように、null ポインターを介してほとんどのアクセスをキャッチするために、Unix ライクなシステムはアドレス 0 のページを「マップされていない」ようにする傾向があります。したがって、アクセスはすぐに CPU 例外、つまりセグメンテーション違反を引き起こします。これは、アプリケーションを不正な状態にするよりもはるかに優れています。ただし、例外ベクトル テーブルは、少なくとも x86 プロセッサでは任意のアドレスに配置できます (そのための特別なレジスタがあり、lidtオペコードがロードされます)。

開始点アドレスは、メモリの配置方法を説明する一連の規則の一部です。リンカーは、実行可能なバイナリを生成するときに、これらの規則を知っている必要があるため、変更される可能性は低くなります。基本的に、Linux のメモリ レイアウト規則は、90 年代初頭の Linux の最初のバージョンから継承されています。プロセスは、いくつかの領域にアクセスできる必要があります。

  • コードは、開始点を含む範囲内にある必要があります。
  • スタックが必要です。
  • ヒープが必要で、システム コールbrk()sbrk()システム コールで制限が増加します。
  • mmap()共有ライブラリのロードなど、システム コール用の余地が必要です。

現在、ヒープは、カーネルが適合すると判断したアドレスでメモリのチャンクを取得malloc()する呼び出しによってサポートされています。mmap()しかし、以前の Linux は以前の Unix に似たシステムのようであり、そのヒープは 1 つの途切れのないチャンクに大きな領域を必要とし、アドレスの増加に合わせて大きくなる可能性がありました。したがって、規則が何であれ、コードを詰め込み、下位アドレスに向かってスタックし、特定のポイントの後にアドレス空間のすべてのチャンクをヒープに与える必要がありました。

しかし、通常は非常に小さいスタックもありますが、場合によっては非常に劇的に大きくなる可能性があります。スタックが減少し、スタックがいっぱいになったときに、一部のデータを上書きするのではなく、プロセスが予測どおりにクラッシュすることを本当に望んでいます。そのため、スタックには広い領域が必要で、その領域の下端にはマップされていないページがありました。そして見よ!null ポインターの逆参照をキャッチするために、アドレス 0 にマップされていないページがあります。したがって、最初のページを除いて、スタックがアドレス空間の最初の 128 MB を取得することが定義されました。これは、コードが 0x080xxxxx のようなアドレスで、これらの 128 MB の後に移動する必要があることを意味します。

Michael が指摘するように、128 MB のアドレス空間を「失う」ことは大した問題ではありませんでした。なぜなら、アドレス空間は実際に使用できるものに関して非常に大きかったからです。当時、Linux カーネルは単一プロセスのアドレス空間を 1 GB に制限していましたが、これはハードウェアで許可されている最大 4 GB を超えており、大きな問題とは見なされていませんでした。

于 2010-02-02T21:13:07.967 に答える
7

アドレス 0x0 から開始しないのはなぜですか? これには少なくとも 2 つの理由があります。

  • アドレス 0 は NULL ポインターとして有名であり、プログラミング言語で正常なチェック ポインターに使用されるためです。そこでコードを実行する場合、そのアドレス値を使用することはできません。
  • アドレス 0 の実際の内容は、多くの場合 (常にではありません) 例外ベクタ テーブルであるため、非特権モードではアクセスできません。特定のアーキテクチャのドキュメントを参照してください。

エントリポイント_startvsについてmain: C ランタイム (C 標準ライブラリ) に対してリンクすると、ライブラリは という名前の関数をラップするmainため、呼び出される前に環境を初期化できますmain。Linux では、これらはアプリケーションへのargcおよびargvパラメーター、 env変数、およびおそらくいくつかの同期プリミティブとロックです。また、メインから戻るときにステータス コードが渡され_exit、プロセスを終了する関数が呼び出されるようにします。

于 2010-02-02T20:49:15.580 に答える