ほとんどすべてのOSランタイムの「WriteGreatCode」によると、メモリは次の領域に編成されています。
OS |スタック|ヒープ|テキスト|静的|ストレージ/ BSS
【アドレスファッションの増加】
ユーザースペースプロセスは、さまざまなタイプのデータオブジェクトに高いメモリ領域を使用します。
カーネル空間プロセスにも、さまざまなタイプのデータオブジェクトがあります。これらのオブジェクトは、ユーザースペースのメモリ領域(スタック、ヒープなど)を共有しますか、それともOS領域に独自のサブセクション(ヒープ、スタックなど)がありますか?その場合、それらが配置される順序は何ですか? 。ありがとう、
注文が間違っています。 OSはメモリの最上部にあり、通常、32ビットカーネルでは3 GBマーク(0xC0000000)より上にあり、64ビットカーネルでは0x8000000000000000IIRCの中間点です。
スタックとヒープの場所はランダム化されます。メインプログラム内のtext/data/bssセグメントの順序については実際の規則はなく、すべてのダイナミックライブラリには独自のセットがあるため、それらの多くはメモリ全体に散在しています。
恐竜が地球を支配していたとき(20年以上前)、プログラムのアドレス空間は線形(穴なし)で、順序はテキスト、データ、bss、スタック、ヒープでした。当時、ダイナミックライブラリやスレッドもありませんでした。それはすべて仮想メモリで変わりました。
カーネルプロセスは、アドレス空間のカーネル部分に完全に含まれています。ユーザー部分は無視されます。これにより、すべてのプロセスがページテーブルの同じカーネル部分を共有するため、カーネルはページテーブルを更新する必要がないため、カーネルスレッド間のコンテキスト切り替えを高速化できます。
これは「ほぼすべてのOS」には当てはまりません。表示されるメモリ領域の種類はかなり一般的ですが、特定の順序にする必要がある理由はなく、特定の種類の部分が複数存在する可能性があります。
Linuxでは、cat /proc/$pid/maps
を使用してプロセスのアドレス空間を確認できます。ここで、$pid
はプロセスIDです。例: cat /proc/$$/maps
はcat
から実行しているシェルを確認するか、cat /proc/self/maps
はcat
プロセス自体のマッピングを確認します。コマンド pmap
は、わずかに優れた出力を生成します。
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08054000-08055000 r--p 0000b000 08:01 828061 /bin/cat
08055000-08056000 rw-p 0000c000 08:01 828061 /bin/cat
08c7f000-08ca0000 rw-p 00000000 00:00 0 [heap]
b755a000-b7599000 r--p 00000000 08:01 273200 /usr/lib/locale/en_US.utf8/LC_CTYPE
b7599000-b759a000 rw-p 00000000 00:00 0
b759a000-b76ed000 r-xp 00000000 08:01 269273 /lib/tls/i686/cmov/libc-2.11.1.so
b76ed000-b76ee000 ---p 00153000 08:01 269273 /lib/tls/i686/cmov/libc-2.11.1.so
b76ee000-b76f0000 r--p 00153000 08:01 269273 /lib/tls/i686/cmov/libc-2.11.1.so
b76f0000-b76f1000 rw-p 00155000 08:01 269273 /lib/tls/i686/cmov/libc-2.11.1.so
b76f1000-b76f4000 rw-p 00000000 00:00 0
b770b000-b7712000 r--s 00000000 08:01 271618 /usr/lib/gconv/gconv-modules.cache
b7712000-b7714000 rw-p 00000000 00:00 0
b7714000-b7715000 r-xp 00000000 00:00 0 [vdso]
b7715000-b7730000 r-xp 00000000 08:01 263049 /lib/ld-2.11.1.so
b7730000-b7731000 r--p 0001a000 08:01 263049 /lib/ld-2.11.1.so
b7731000-b7732000 rw-p 0001b000 08:01 263049 /lib/ld-2.11.1.so
bfbec000-bfc01000 rw-p 00000000 00:00 0 [stack]
実行可能ファイル、ヒープ、メモリマップファイル、もう少し読み取り/書き込みデータ、コード、読み取り専用データ、読み取りからコードと読み取り/書き込みデータ(テキストとBSS)を確認できます。共有ライブラリ(テキストとBSS)からデータを書き込み、より多くの読み取り/書き込みデータ、別の共有ライブラリ(より正確には、ダイナミックリンカー)、そして最後に唯一のスレッドのスタック。
カーネルコードは独自のアドレス範囲を使用します。多くのプラットフォームでは、Linuxはカーネルのアドレス空間の上部(多くの場合、上部1GB)を使用します。理想的には、このスペースは、カーネルコード、カーネルデータ、システムメモリ(RAM)、およびすべてのメモリマップデバイスをマップするのに十分です。今日の典型的な32ビットPCでは、これは不可能であり、カーネルハッカーだけが関心を持つゆがみが必要です。
カーネルコードがシステムコールを処理している間、理想的には(前述のゆがみがない場合)、プロセスのメモリは同じアドレスにマップされます。これにより、プロセスはデータをカーネルに渡すことができ、カーネルはポインターから直接読み取ることができます。ただし、ポインタを検証する必要があるため(プロセスがアクセスできないはずのメモリからカーネルをだまして読み取ることができないようにするため)、大きなメリットにはなりません。
Linuxカーネルスペース内のメモリゾーンはかなり複雑です。いくつかの異なるメモリプールがあり、主な違いは、メモリがどこから来ているかではなく、誰と共有されているかです。それらに興味がある場合は、 LDD から始めてください。
答えではありませんが、より多くのスペースが必要なFYIです。
あなたの論理アドレスレイアウトの概念はまったく正しいとは思いません。
このプログラムをコンパイルして実行すると、ユーザーランドプロセスのアドレスの内容を確認できます。
#include <stdio.h>
long global_initialized = 119234;
long global_uninitialized;
extern int _end, _edata, _etext;
int
main(int ac, char **av)
{
long local;
printf("main at 0x%lx\n", main);
printf("ac at 0x%lx\n", &ac);
printf("av at 0x%lx\n", &av);
printf("av has 0x%lx\n", av);
printf("initialized global at 0x%lx\n", &global_initialized);
printf("global at 0x%lx\n", &global_uninitialized);
printf("local at 0x%lx\n", &local);
printf("_end at 0x%lx\n", &_end);
printf("_edata at 0x%lx\n", &_edata);
printf("_etext at 0x%lx\n", &_etext);
return 0;
}
私が実行しているRedHat EnterpriseServerにはreadelf
があります。これは、カーネルが実行可能ファイルを(論理的に)ロードする場所を示すために使用できます。
readelf -S where
where
の出力が提供するのと同じアドレス指定情報をたくさん表示します。
readelf
がLinuxカーネル(/ boot/vmlinuzなど)で簡単に機能するとは思いません。カーネルはデフォルトで、独自のアドレス空間で0x80000000から始まると思います。カーネルはマップされていません。 0x7fffffff(x86、32ビットアドレス指定)のユーザーランドスタックトップより上のアドレスを使用しているにもかかわらず、ユーザーランドプロセス。