运行结果如下:
或者利用size命令也可以看到程序各段的大小
@:$ size test text data bss dec hex filename 2611 640 8 3259 cbb test这种方法获取到的程序各个段大小的数据是程序编译的静态统计,而上面执行程序获取到程序各个段大小的数据是进程运行过程中的动态值,但两者是一一对应的。
通过上面的示例程序,我们对进程使用的罗技内存分布先睹为快。接下来就继续进入操作系统内核,看看进程对内存具体是如何进行分配和管理的。
从用户态到内核态,所使用的内存表象形式会依次经历“逻辑地址”——“线性地址”——“物理地址”几种形式(关于几种地址的解释在前面已经讲述了)。(但要知道Linux系统虽然保留了段机制,但是将所有程序的段地址都定死为0-4G,所以虽然逻辑地址和线性地址是两种不同的地址空间,但在Linux中逻辑地址就等于线性地址,它们的值是一样的)。根据这条线索,我们所研究的主要问题也就集中在下面几个问题上。
进程空间地址如何管理?
进程地址如何映射到物理内存?
物理内存如何被管理?
以及由上述问题引发的一些子问题。如:系统虚拟地址分布;内存分配接口;连续内存分配与非连续内存分配等。
Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不心干涉的进程地址空间。该空间是一个大小为4G的线性虚拟空间,用户所看到和接触到的都是该虚拟地址,无法看到实际的物理内存地址。利用这种虚拟地址不但能起到保护操作系统的效果(即由于用户无法直接访问物理内存,从而保护了内存的安全),而且更重要的是,用户程序可使用比实际物理内存更大的内存空间(具体原因请看硬件基础部分)。
在讨论进程空间细节前,这里首先说明以下几个问题:
第一: 4G的进程空间被人为的分为两个部分——用户空间和内核空间。从0到3G(0xC0000000) 属于用户空间,从3G到4G属于内核空间。用户进程通常情况下只能访问用户空间的虚拟地址,不可以访问内核空间虚拟地址。只有用户进程进行系统调用(代表用户进程需要在内核态执行)等时刻,可以将用户程序从用户态转为内核态去访问内核空间。
第二: 用户空间对应进程,所有每当进程切换是,用户空间就会跟着变化。而内核空间是由内核负责映射,它并不会随着进程改变,是固定的。内核空间地址有自己对应的页表(init_mm.pgd),用户进程各自有不同的页表。
第三: 每个进程的用户空间都是完全独立、互补相干的。不信的话,你可以把上面的程序同时运行10次(当然为了同时运行,让它们在返回前一同睡眠100秒吧),你会看到10个进程占用的线性地址一模一样。
进程内存管理进程内存管理的对象是进程线性地址空间上的内存镜像,这些内存镜像其实就是进程使用的虚拟内存区域(memory region)。进程虚拟空间是个32或64位的”平坦“(独立的连续区间)地址空间(空间的具体大小取决于体系结构)。要统一管理这么大的平坦空间可绝非易事,为了方便管理,虚拟空间被划分为许多大小可变的(但必须是4096的倍数)内存区域,这些区域在进程线性地址中像停车位一样有序排列。这些区域的划分原则是”将访问属性一致的地址空间存放在一起“,所谓访问属性在这里无非指的是”可读、可写、可执行等“。