Linux动态内存管理机制
进程与内存
1、进程如何使用内存
毫无疑问,所有进程(执行的程序)都必须占有一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存取自用户输入的数据等等。不过进程对这些内存的管理方式因为内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是需要动态分配和回收的。
对于任何一个普通进程来讲,它都会涉及到5种不同的数据段。稍有些编程知识的朋友都能想到这几个数据段中包含有“程序代码段”、“程序数据段”、“程序堆栈段等”。不错,这几个数据段都在其中,但除了以上几种数据段之外,进程还另外包含两种数据段。
代码段:代码段是用来存放可执行文件的操作指令,也就是说它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只允许读取操作,而不允许写入操作–它是不可写的。
数据段:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。
BSS段:BSS段包含了程序汇总未初始化的全局变量,在内存中bss全部置零。
堆段:堆时用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程用malloc等函数分配内存时,新分配的内存就被添加到堆上(堆被扩张);当利用free等函数释放内存,被释放的内存从堆中被剔除(堆被缩减)
栈:栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此之外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈中后进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
2、进程如何组织这些区域?
上述几种内存区域中数据段、BSS和堆通常是被连续存储的–内存位置上是连续的,而代码段和栈往往被独立存放。有趣的是,堆和栈两个区域关系很暧昧,他们一个向下长,一个向上长,但你不必担心他们会碰头,因为他们之间间隔很大。
进入操作系统内核看看,进程对内存具体是如何进行分配和管理的。
从用户向内核看,所使用的内存表象形式会依次经历“逻辑地址”–“线性地址”–“物理地址”几种形式。逻辑地址经段机制转化成线性地址;线性地址经过页机制转化为物理地址。(但是我们要知道Linux虽然保留了段机制,但是将所有程序的段地址都定死为0-4G,所以虽然逻辑地址和线性地址是两种不同的地址空间,但在linux中逻辑地址就等于线性地址)沿着这条线索,我们所研究的主要问题也就集中在下面几个问题。
1、进程地址空间如何管理?
2、进程地址如何映射到物理内存?
3、物理内存如何被管理?
进程内存空间
linux操作系统曹勇虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间。该空间是块大小为4G的线性虚拟空间,用户所看到和接触到的都是该虚拟地址,无法看到实际的物理内存地址。利用这种虚拟地址不但能起到保护操作系统的效果(用户不能直接访问物理内存),而且更重要的是,用户程序可使用比实际物理内存更大的地址空间。
在讨论进程空间细节前,这里先要澄清下面介个问题:
一、4G的进程地址空间被认为的分为两个部分–用户空间与内核空间。用户空间从0到3G(0xC0000000),内核空间占据3G到4G。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间虚拟地址。只有用户进程进行系统调用(代表用户进程在内核态执行)等时刻才可以访问到内核空间。
二、用户空间对应进程,每当进程切换,用户空间就会跟着变化;而内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。内核空间地址有着自己对应的页表(init_mm.pgd),用户进程各有不同的页表。
三、每个进程的用户空间都是完全独立、互不相干的。不信的话,你可以把上面的程序同时运行10次,你会看到10个进程占用的线性地址一模一样。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!