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


8

「偉大なコードを書く」によると、ほぼすべてのOSランタイムメモリは次の領域に編成されています。

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

【アドレスを増やす方法で】

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

カーネル空間プロセスには、さまざまなタイプのデータオブジェクトもあります。これらのオブジェクトは、ユーザー空間のメモリ領域(スタック、ヒープなど)を共有していますか、それともOS領域に独自のサブセクション(ヒープ、スタックなど)がありますか? 。おかげで、

回答:


5

注文については間違っています。OSはメモリの上部にあり、通常32ビットカーネルでは3 GBマーク(0xC0000000)の上にあり、64ビットカーネルでは0x8000000000000000 IIRCの中間点です。

スタックとヒープの場所はランダム化されます。メインプログラム内のtext / data / bssセグメントの順序に関する実際の規則はなく、すべてのダイナミックライブラリにはこれらの独自のセットがあるため、それらの多くはメモリ全体に散在しています。

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

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


4

これは「ほぼすべての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から始めてください


1

答えではなく、より多くのスペースが必要な参考情報です。

論理アドレスレイアウトの概念は、まったく正しいとは思いません。

このプログラムをコンパイルして実行すると、ユーザーランドプロセスがアドレスに対して何を持っているかを確認できます。

#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得られる多くの同じアドレス指定情報を表示します。

readelfLinuxカーネル(/ boot / vmlinuzなど)では簡単に機能しないと思います。また、デフォルトでは、カーネルは独自のアドレス空間の0x80000000から始まると思います。 0x7fffffff(x86、32ビットアドレッシング)でユーザーランドスタックトップの上のアドレス。


例をありがとう!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です。
sdaau 2014年

@sdaau-引数acav自動変数localは、呼び出しごとに異なるアドレスを持つ可能性があります。最近のほとんどのLinuxカーネルには、バッファオーバーフローの悪用を困難にする「アドレススペースレイアウトのランダム化」があります。
Bruce Ediger 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.