回答:
注文については間違っています。OSはメモリの上部にあり、通常32ビットカーネルでは3 GBマーク(0xC0000000)の上にあり、64ビットカーネルでは0x8000000000000000 IIRCの中間点です。
スタックとヒープの場所はランダム化されます。メインプログラム内の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カーネルスペース内のメモリゾーンはかなり複雑です。いくつかの異なるメモリプールがあり、主な違いは、メモリがどこから来たかではなく、誰と共有されているかです。それらに興味がある場合は、LDD3から始めてください。
答えではなく、より多くのスペースが必要な参考情報です。
論理アドレスレイアウトの概念は、まったく正しいとは思いません。
このプログラムをコンパイルして実行すると、ユーザーランドプロセスがアドレスに対して何を持っているかを確認できます。
#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;
}
私が実行しているRed Hat Enterprise Serverのhasはreadelf
、カーネルが(論理的に)実行可能ファイルをロードする場所を示すために使用できます。
readelf -S where
の出力からwhere
得られる多くの同じアドレス指定情報を表示します。
readelf
Linuxカーネル(/ boot / vmlinuzなど)では簡単に機能しないと思います。また、デフォルトでは、カーネルは独自のアドレス空間の0x80000000から始まると思います。 0x7fffffff(x86、32ビットアドレッシング)でユーザーランドスタックトップの上のアドレス。
ac
とav
自動変数local
は、呼び出しごとに異なるアドレスを持つ可能性があります。最近のほとんどのLinuxカーネルには、バッファオーバーフローの悪用を困難にする「アドレススペースレイアウトのランダム化」があります。
where.c
使用してgcc where.c -o where
、Ubuntu 11.04で; 「main at 0x80483c4」を報告します。試してみたところreadelf -S where
、「[13] .text PROGBITS 08048310 ...」と表示されました。「ac at 0xbfb035a0」と「local at 0xbfb0358c」も表示されますが、そのアドレス範囲(0xbf ...)はから報告されていないようreadelf -S
です。