Linux カーネル論理アドレス空間の構成

Linux カーネル論理アドレス空間の構成

「Writing Great Code」によると、ほぼすべてのオペレーティングシステムのランタイムメモリは、次の領域で構成されています。

OS|スタック|ヒップ|リテラル|静的|ストレージ/BSS

[住所を追加して]

ユーザ空間プロセスは、様々なタイプのデータオブジェクトに対してより高いメモリ領域を使用する。

カーネル空間プロセスには、さまざまな種類のデータオブジェクトもあります。これらのオブジェクトはユーザー空間メモリ領域(スタック、ヒープなど)を共有しますか、またはオペレーティングシステム領域(ヒープ、スタックなど)に別々のサブセクションがありますか?では、どのような順序でリストされていますか?ありがとう、

答え1

何か順番が間違っていますね。オペレーティングシステムはメモリの上にあり、32ビットカーネルでは通常3GB表示(0xC0000000)の上にあり、64ビットカーネルでは0x80000000000000000 IIRCの中間点にあります。

スタックとヒープの位置はランダムに指定されます。メインプログラムには、テキスト/データ/BSSセグメントの順序に関する実際の規則はなく、各ダイナミックライブラリにはこれらのセグメントの独自のセットがあるため、その多くはメモリに分散されています。

恐竜が地球を支配したとき(20年前)、プログラムのアドレス空間は線形(穴なし)で、順序はテキスト、データ、bss、スタック、ヒープでした。当時は動的ライブラリやスレッドはありませんでした。これらはすべて仮想メモリによって変わります。

カーネルプロセスは、アドレス空間のカーネル部分内に完全に含まれる。ユーザー部分は無視されます。これにより、カーネルはカーネルスレッド間のコンテキスト切り替え速度を向上させることができる。これは、すべてのプロセスがページテーブルの同じカーネル部分を共有するため、ページテーブルを更新する必要がないためです。

答え2

「ほとんどすべてのオペレーティングシステム」には該当しません。表示されるメモリ領域の種類は非常に一般的ですが、特定の順序で表示する必要はありません。特定の種類に複数のメモリ領域がある可能性があります。

cat /proc/$pid/mapsLinuxでは、実行中のシェルを表示したり、プロセス自体のマッピングを表示したりするなど、プロセスIDでプロセスのアドレス空間を表示できます。注文する$pidcat /proc/$$/mapscatcat /proc/self/mapscatpmapやや良い出力を生成します。

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

答え3

答えではありませんが、より多くのスペースが必要な注意事項です。

私は論理アドレスのレイアウトのためのあなたの概念は単に正しくないと思います。

このプログラムをコンパイルして実行して、ユーザーモードプロセスのアドレスを表示できます。

#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には、readelfカーネルが(論理的に)実行可能ファイルをロードする場所を示すために使用できるものがあります。

readelf -S where

whereの出力と同じアドレス指定情報がたくさん表示されます。

私はreadelfLinuxカーネル(/boot/vmlinuzなど)で作業するのは簡単ではないと思います。ユーザーアドレスを使用しますが、ユーザーゾーンプロセスにマップされません。ステータススタックの上部は0x7fffffff(x86、32ビットアドレス指定)にあります。

関連情報