7

私はこれらの概念に非常に慣れていませんが、非常に基本的な質問をしたいと思いますが、混乱しているので質問しています. 問題は... OSによってプロセスのサイズがどのように決定されるかです。最初に明確にさせてください。C プログラムを作成したとします。どのくらいのメモリが必要かを知りたいのですが、どうすればそれを判断できますか? 次に、コード セクション、データ セクション、プロセスの BSS など、多くのセクションがあることを知っています。これらのサイズは事前に決定されていますか?次に、スタックとヒープのサイズがどのように決定されるか。プロセスの合計サイズが計算される間、スタックとヒープのサイズも重要ですか。

繰り返しになりますが、プログラムをロードすると、プロセスにアドレス空間が与えられ(ベースレジスタとリミットレジスタによって行われ、MMUによって制御されると思います)、プロセスがその中にないメモリ位置にアクセスしようとすると、アドレス空間でセグメンテーション違反が発生します。プロセスがそのアドレス空間にないメモリにアクセスするにはどうすればよいですか? 私の理解によると、バッファオーバーフローが発生すると、アドレスが破損します。プロセスが破損した場所にアクセスしようとすると、セグメンテーション違反が発生します。アドレス違反の他の方法はありますか。

そして3番目に、スタックが下向きに成長し、上向きにヒープする理由.このプロセスはすべてのOSで同じですか. パフォーマンスにどのように影響しますか?なぜ他の方法でできないのでしょうか?

ステートメントのいずれかが間違っている場合は、修正してください。

ありがとうソーラブ

4

4 に答える 4

3

プロセスが開始されると、プロセスは独自の仮想アドレス空間を取得します。仮想アドレス空間のサイズは、オペレーティング システムによって異なります。一般に、32 ビット プロセスは 4 GiB (4 ギガ バイナリ) アドレスを取得し、64 ビット プロセスは 18 EiB (18 エクサ バイナリ) アドレスを取得します。

定義上、そこにマップされていないものにはアドレスがないため、仮想アドレス空間にマップされていないものには決してアクセスできません。現在何にもマップされていない仮想アドレス空間の領域にアクセスしようとすると、segfault 例外が発生します。

すべてのアドレス空間が常に何かにマップされるわけではありません。また、すべてがマップされるわけではありません (どの程度マップされるかは、プロセッサとオペレーティング システムによって異なります)。現在の世代の Intel プロセッサでは、最大 256 TiB のアドレス空間をマップできます。オペレーティング システムによってさらに制限される可能性があることに注意してください。たとえば、32 ビット プロセス (最大 4 GiB アドレスを持つ) の場合、Windows は既定でシステム用に 2 GiB、アプリケーション用に 2 GiB を予約します (ただし、システム用に 1 GiB、アプリケーション用に 3 GiB にする方法があります)。

使用されているアドレス空間の量とマップされている量は、アプリケーションの実行中に変化します。オペレーティング システム固有のツールを使用すると、実行中のアプリケーションに現在割り当てられているメモリと仮想アドレス空間を監視できます。

コード セクション、データ セクション、BSS などは、リンカーによって作成される実行可能ファイルのさまざまな領域を指す用語です。一般に、コードは、静的に割り当てられているが変更可能なデータとは別の静的な不変データとは別のものです。スタックとヒープは、上記のすべてとは別のものです。それらのサイズは、コンパイラとリンカによって計算されます。各バイナリ ファイルには独自のセクションがあるため、動的にリンクされたライブラリはアドレス空間に個別にマップされ、それぞれ独自のセクションがどこかにマップされることに注意してください。ただし、ヒープとスタックはバイナリ イメージの一部ではありません。通常、プロセスごとに 1 つのスタックと 1 つのヒープしかありません。

スタック (少なくとも初期スタック) のサイズは通常固定されています。コンパイラやリンカーには通常、実行時に必要なスタックのサイズを設定するために使用できるいくつかのフラグがあります。スタックは一般に「後方に成長」します。これは、プロセッサのスタック命令がそのように機能するためです。スタックを一方向に成長させ、残りを他の方向に成長させると、両方を無制限にしたいが、それぞれがどれだけ成長できるかわからない状況で、メモリを整理しやすくなります。

一般に、ヒープとは、プロセスの開始時に事前に割り当てられていないものを指します。最下位レベルには、ヒープ管理に関連するいくつかの論理操作があります (ここで説明するように、すべてのオペレーティング システムですべてが実装されているわけではありません)。

アドレス空間は固定されていますが、一部の OS は、現在プロセスによって再利用されている部分を追跡しています。そうでない場合でも、プロセス自体がそれを追跡する必要があります。したがって、最も低いレベルの操作は、アドレス空間の特定の領域が使用されることを実際に決定することです。

2 番目の低レベル操作は、その領域を何かにマップするよう OS に指示することです。これは、一般的に次のことができます。

  • スワップできないメモリ

  • スワップ可能で、システム スワップ ファイルにマップされているメモリ

  • スワップ可能で、他のファイルにマップされているメモリ

  • スワップ可能で、読み取り専用モードで他のファイルにマップされているメモリ

  • 別の仮想アドレス領域がマップされているのと同じマッピング

  • 別の仮想アドレス領域がマップされているのと同じマッピングですが、読み取り専用モードです

  • 別の仮想アドレス領域がマップされているのと同じマッピングですが、コピーされたデータがデフォルトのスワップ ファイルにマップされたコピー オン ライト モードです。

他にも忘れた組み合わせがあるかもしれませんが、主なものは以上です。

もちろん、使用される合計スペースは、定義方法によって異なります。現在使用されている RAM は、現在マップされているアドレス空間とは異なります。しかし、上で書いたように、オペレーティング システムに依存するツールを使用すると、現在何が起こっているかを知ることができます。

于 2012-09-19T09:09:46.003 に答える
2

セクションは、実行可能ファイルによって事前に決定されます。

それ以外にも、動的にリンクされたライブラリのものが存在する場合があります。DLL のコードと定数データは、DLL を使用する複数のプロセス間で共有され、複数回カウントされることは想定されていませんが、そのプロセス固有の非定数データは、すべてのプロセスで考慮する必要があります。

さらに、プロセス内に動的に割り当てられたメモリが存在する可能性があります。

さらに、プロセスに複数のスレッドがある場合、それぞれに独自のスタックがあります。

さらに、スレッドごと、プロセスごと、ライブラリごとのデータ構造が、プロセス自体とその代わりにカーネルに存在します (スレッドローカル ストレージ、コマンド ライン パラメーター、さまざまなリソースへのハンドル、それらの構造リソースなど)。

すべてがどのように実装されているかを知らずに、完全なプロセス サイズを正確に計算することは困難です。ただし、妥当な見積もりが得られる場合があります。

WrtAccording to my understanding when some buffer overflows happens then the address gets corrupted.必ずしもそうではありません。まず、住所は?それは、バッファの近くのメモリに何が起こるかによって異なります。アドレスがある場合、バッファ オーバーフロー中に上書きされる可能性があります。しかし、あなたの写真を含む別のバッファーが近くにある場合、写真のピクセルが上書きされる可能性があります。

必要なアクセス許可を持っていないメモリにアクセスしようとすると、セグメンテーションまたはページ フォールトが発生する可能性があります (たとえば、プロセス アドレス空間にマップされているか、別の方法で存在するカーネル部分)。または、読み取り専用の場所にすることもできます。または、場所に物理メモリへのマッピングがない場合もあります。

スタックとヒープの場所とレイアウトがパフォーマンスにどのように影響するかは、話している内容のパフォーマンスを知らずに判断するのは困難です。推測することはできますが、推測が間違っていることが判明する可能性があります。

ところで、別の問題について SO で別の質問をすることを本当に検討する必要があります。

于 2012-09-19T08:47:18.377 に答える
1

「プロセスがそのアドレス空間にないメモリにアクセスすることはどのように可能ですか?」

メモリ保護を考えると、それは不可能です。しかし、それは試みられるかもしれません。ランダム ポインターまたはバッファーを超えたアクセスを検討してください。ポインターを十分に長くインクリメントすると、ほとんどの場合、マップされていないアドレス範囲に移動します。簡単な例:

 char *p = "some string";

 while (*p++ != 256)  /* Always true. Keeps incrementing p until segfault. */
     ;

控えめに言っても、このような単純なエラーは前代未聞ではありません。

于 2012-09-19T08:55:03.787 に答える
0

質問2と3に答えることができます。

回答#2

Cでポインタを使用する場合、実際にはメモリへのアドレスとして解釈される数値を使用しています(最新のOSでは論理アドレス。脚注を参照)。このアドレスは自由に変更できます。値がアドレス空間にないアドレスを指している場合は、セグメンテーション違反が発生しています。

たとえば、このシナリオを考えてみましょう。OSがプロセスに0x01000から0x09000までのアドレス範囲を与えます。それで

int * ptr = 0x01000;
printf("%d", ptr[0]); // * prints 4 bytes (sizeof(int) bytes) of your address space
int * ptr = 0x09100;
printf("%d", ptr[0]); // * You are accessing out of your space: segfault

ご指摘のとおり、セグメンテーション違反の主な原因は、NULLへのポインターの使用(ほとんどは0x00アドレスですが、実装に依存します)または破損したアドレスの使用です。

Linux i386では、ご想像のとおり、ベースレジスタとリミットレジスタは使用されないことに注意してください。これらはプロセスごとの制限ではありませんが、ユーザースペースまたはカーネルスペースの2種類のセグメントを指します。

回答#3

スタックの増加はハードウェアに依存し、OSには依存しません。プッシュやポップなどのi386アセンブリ命令では、スタック関連のレジスタに関してスタックを下に成長させます。たとえば、スタックポインタは、プッシュを実行すると自動的に減少し、ポップを実行すると増加します。OSはそれを処理できません。

脚注

最新のOSでは、プロセスはいわゆる論理アドレスを使用します。このアドレスは、OSによって物理アドレスにマップされます。これをメモするには、この単純なプログラムを自分でコンパイルします。

#include <stdio.h>

int main()
{
    int a = 10;
    printf("%p\n", &a);
    return 0;
}

このプログラムを複数回(同時にでも)実行すると、インスタンスが異なっていても、同じアドレスが出力されます。もちろん、これは実際のメモリアドレスではありませんが、必要に応じて物理アドレスにマップされる論理アドレスです。

于 2012-09-19T08:59:41.973 に答える