Linux内核逻辑地址空间组织


8

根据“编写出色代码”,几乎所有操作系统运行时内存都分为以下区域:

操作系统| 堆叠 堆| 文字| 静态 储存/ BSS

[以增加地址的方式]

用户空间进程针对其不同类型的数据对象使用更高的内存区域。

内核空间进程还具有不同类型的数据对象。这些对象共享用户空间内存区域(堆栈,堆等)还是在OS区域中有单独的子区域(堆,堆栈等),如果是的话,它们的排列顺序是什么。谢谢,

Answers:


5

订购是错误的。操作系统位于内存顶部,通常在32位内核中位于3 GB标记(0xC0000000)之上,而在64位内核中,它是0IRC8000000000 IIRC的中点。

堆栈和堆的位置是随机的。关于主程序中text / data / bss段的顺序没有真正的规则,每个动态库都有它们自己的一组,因此其中许多分散在整个内存中。

回到20年前的恐龙统治地球时,程序的地址空间是线性的(没有孔),顺序是文本,数据,bss,堆栈,堆。那时也没有动态库或线程。这一切都随着虚拟内存而改变。

内核进程完全包含在地址空间的内核部分内。用户部分将被忽略。由于所有进程共享页表的相同内核部分,因此不必更新页表,这使内核可以加快内核线程之间的上下文切换。


4

对于“几乎所有操作系统”而言,情况并非如此。所表示的存储区域的类型是相当典型的,但是没有理由为什么它们应该以任何特定顺序排列,并且给定类型可以有多个存储区域。

在Linux下,你可以看看一个进程的地址空间cat /proc/$pid/maps,其中$pid是进程ID,例如cat /proc/$$/maps看你正在运行的外壳cat从,或cat /proc/self/mapscat过程自身的映射。该命令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

不是答案,而是需要更多空间的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;
}

我正在运行的Red Hat Enterprise Server具有readelf,可以用来表示内核在逻辑上加载可执行文件的位置:

readelf -S where

向我展示了很多与输出相同的寻址信息where

我认为readelf在Linux内核(/ boot / vmlinuz或类似的内核)上不容易工作,并且我认为默认情况下内核在其自己的地址空间中从0x80000000开始:尽管使用了用户区堆栈顶部上方的地址为0x7fffffff(x86,32位寻址)。


谢谢你的例子!只是我在Linux部分上的笔记-我只是where.c在Ubuntu 11.04上使用gcc where.c -o where; 报告“主要位于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年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.