Organisasi ruang alamat logis Kernel Linux

8

Menurut "Write Great Code" di hampir semua OS run time memory diatur ke wilayah berikut:

OS | Tumpukan | Tumpukan | Teks | Statis | Penyimpanan / BSS

[Dalam mode alamat yang meningkat]

Proses ruang pengguna menggunakan wilayah memori yang lebih tinggi untuk berbagai jenis objek data.

Proses ruang kernel juga memiliki berbagai jenis objek data. Apakah benda-benda ini berbagi wilayah memori ruang pengguna (stack, heap, dll) atau apakah mereka memiliki sub-bagian yang terpisah (heap, stack, dll) yang terletak di wilayah OS. Dan, jika demikian, bagaimana urutan pengaturannya? . Terima kasih,

gkt
sumber

Jawaban:

5

Itu salah tentang pemesanan. OS terletak di bagian atas memori, yang umumnya di atas tanda 3 GB (0xC0000000) di kernel 32bit, dan di kernel 64bit itu adalah titik setengah jalan 0x800000000000000000 IIRC.

Lokasi tumpukan dan tumpukan diacak. Tidak ada aturan nyata tentang pemesanan segmen teks / data / bss dalam program utama, dan setiap perpustakaan dinamis memiliki set-nya sendiri, jadi ada banyak dari mereka yang tersebar di seluruh memori.

Kembali ketika dinosaurus memerintah bumi (20+ tahun yang lalu), ruang alamat program adalah linear (tidak ada lubang) dan urutannya adalah teks, data, bss, tumpukan, tumpukan. Juga tidak ada perpustakaan dinamis atau threading saat itu. Itu semua berubah dengan memori virtual.

Proses kernel sepenuhnya terkandung dalam bagian kernel dari ruang alamat; porsi pengguna diabaikan. Hal ini memungkinkan kernel untuk mempercepat pengalihan konteks antara utas kernel karena tidak harus memperbarui tabel halaman karena semua proses berbagi bagian kernel yang sama dari tabel halaman.

psusi
sumber
4

Ini tidak berlaku untuk "hampir semua OS". Jenis area memori yang diwakili cukup tipikal, tetapi tidak ada alasan mengapa mereka harus dalam urutan tertentu, dan mungkin ada lebih dari satu bagian dari jenis yang diberikan.

Di Linux, Anda bisa melihat pada ruang alamat proses dengan cat /proc/$pid/mapsmana $pidadalah ID proses, misalnya cat /proc/$$/mapsuntuk melihat shell Anda menjalankan catdari, atau cat /proc/self/mapsuntuk melihat pada catpemetaan proses sendiri. Perintah pmapmenghasilkan output yang sedikit lebih bagus.

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]

Anda dapat melihat kode dan data baca-tulis (teks dan BSS) dari executable, lalu heap, lalu file yang dipetakan memori, lalu sedikit lebih banyak baca-tulis data, lalu kode, data baca-saja dan baca- tulis data dari pustaka bersama (teks dan BSS lagi), lebih banyak baca-tulis data, pustaka bersama lainnya (lebih tepatnya, penghubung dinamis), dan akhirnya tumpukan utas satu-satunya.

Kode kernel menggunakan rentang alamatnya sendiri. Pada banyak platform, Linux menggunakan bagian atas ruang alamat untuk kernel, seringkali bagian atas 1GB. Idealnya, ruang ini cukup untuk memetakan kode kernel, data kernel, dan memori sistem (RAM) dan setiap perangkat yang dipetakan memori. Pada PC 32-bit biasa saat ini, ini tidak mungkin, yang memerlukan liuk yang hanya menarik bagi peretas kernel.

Ketika kode kernel menangani panggilan sistem, idealnya (ketika contortions yang disebutkan sebelumnya tidak ada di tempat) memori proses dipetakan pada alamat yang sama. Ini memungkinkan proses untuk mengirimkan data ke kernel, dan kernel dapat membaca dari pointer secara langsung. Ini bukan keuntungan besar, karena pointer tetap perlu divalidasi (sehingga proses tidak dapat menipu kernel untuk membaca dari memori yang proses tidak seharusnya memiliki akses ke).

Zona memori di dalam ruang kernel Linux cukup kompleks. Ada beberapa kumpulan memori yang berbeda, dan perbedaan utama bukan tentang dari mana memori itu berasal tetapi lebih kepada siapa itu dibagi. Jika Anda ingin tahu tentang mereka, mulailah dengan LDD3 .

Gilles 'SANGAT berhenti menjadi jahat'
sumber
1

Bukan jawaban, tetapi FYI yang membutuhkan lebih banyak ruang.

Saya tidak berpikir konsepsi tata letak alamat logis Anda sama sekali benar.

Anda dapat mengkompilasi dan menjalankan program ini untuk melihat apa yang dimiliki proses userland untuk alamat:

#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 yang saya jalankan, miliki readelf, yang dapat digunakan untuk mengatakan di mana kernel akan (secara logis) memuat file yang dapat dieksekusi:

readelf -S where

Memperlihatkan saya banyak informasi pengalamatan yang sama dengan yang diberikan oleh output where.

Saya tidak berpikir readelfakan dengan mudah bekerja pada kernel Linux (/ boot / vmlinuz atau semacamnya), dan saya pikir bahwa kernel secara default dimulai pada 0x80000000 di ruang alamatnya sendiri: itu tidak dipetakan dalam proses userland, meskipun menggunakan alamat di atas bagian atas tumpukan pengguna pada 0x7fffffff (x86, pengalamatan 32 bit).

Bruce Ediger
sumber
Terima kasih untuk contohnya! Hanya catatan saya di bagian Linux - Saya baru saja mencoba contoh ini where.c, pada Ubuntu 11.04 menggunakan gcc where.c -o where; melaporkan "main di 0x80483c4". readelf -S whereSudah mencoba , dan ini melaporkan, katakan "[13] .text PROGBITS 08048310 ..." yang terlihat benar? Meskipun saya juga mendapatkan "ac di 0xbfb035a0" dan "lokal di 0xbfb0358c", dan rentang alamat (0xbf ...) tampaknya tidak dilaporkan oleh readelf -S.
sdaau
@sdaau - Argumen acdan avdan variabel otomatis localmungkin akan memiliki alamat yang berbeda pada setiap permintaan. Sebagian besar kernel Linux modern memiliki "Address Space Layout Randomization" untuk membuat eksploitasi buffer overflows lebih sulit.
Bruce Ediger