[学习笔记]达内纪录片day21 linux内存管理

    选择打赏方式

一、内存管理

从底层硬件到上层应用,各层都提供了各自的内存管理接口

TIM截图20190729230422.png

对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。

操心系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核,保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。

针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。


每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。


需要注意的细节问题:
(1)内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中。 
(2) Linux使用两级保护机制:0级供内核使用,3级供用户程序使用。


内核态与用户态:
(1)当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。
(2)当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。此时处理器在特权级最低的(3级)用户代码中运行。当正在执行用户程序而突然被中断程序中断时,此时用户程序也可以象征性地称为处于进程的内核态。因为中断处理程序将使用当前进程的内核栈。


虚拟内存概念参考:http://www.8964cn.net/?post=83


二、进程映射

进程的内存布局

程序是保存在磁盘上的可执行文件运行程序时,需要将可执行文件加载到内存,形成进程一个程序(文件)可以同时存在多个进程(内存)进程在内存空间中的布局形成进程映像。

从低地址到高地址依次为:
-代码区(text)

-数据区(data)

-BSS区(bss)
-堆区(heap)
-栈区(stack)
-参数和环境区


3G内存布局

TIM截图20190729232157.png

进程的3G虚拟内存空间,实际上是根据ELF加载的。


查看文件ELF

readelf-h 查看elf 文件头部信息
readelf-S查看段表信息
readelf-s查看符号表信息


.bss未初始化段
.data数据段
.rodata只读数据段
.text代码段


多个目标文件在链接的时候会把同样的多个段合并成一个段。

函数的虚拟地址在编译的时候就已经确定了。


进程的映像

在/proc目录下可以看到很多以进程标识(PID)命名的子目录,这些子目录中存放的并不是真正的磁盘文件,而是操作系统内核为每个目前处于运行状态的进程,在内存中构建的审计数据结构,把它们挂载到文件系统中,只是为了方便系统管理人员实时监控系统的运行情况。


查看
进程映像:

cat /proc/进程的pid/maps


/proc/<PID>/maps
查看进程的虚拟地址空间是如何使用的。
该文件有6列,分别为:
地址:库在进程里地址范围
权限:虚拟内存的权限,r=读,w=写,x=,s=共享,p=私有;
偏移量:库在进程里地址范围
设备:映像文件的主设备号和次设备号;
节点:映像文件的节点号;
路径: 映像文件的路径
每项都与一个vm_area_struct结构成员对应


各共享库的代码段,存放着二进制可执行的机器指令,是由kernel把该库ELF文件的代码段map到虚存空间;
各共享库的数据段,存放着程序执行所需的全局变量,是由kernel把ELF文件的数据段map到虚存空间;
用户代码段,存放着二进制形式的可执行的机器指令,是由kernel把ELF文件的代码段map到虚存空间;
用户数据段之上是代码段,存放着程序执行所需的全局变量,是由kernel把ELF文件的数据段map到虚存空间;
用户数据段之下是堆(heap),当且仅当malloc调用时存在,是由kernel把匿名内存map到虚存空间,堆则在程序中没有调用malloc的情况下不存在;
用户数据段之下是栈(stack),作为进程的临时数据区,是由kernel把匿名内存map到虚存空间,栈空间的增长方向是从高地址到低地址。

获取进程PID

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void){
    pid_t pid=getpid();
    printf("pid:%d\n",pid);
    getchar(); 
    return 0;
}

运行结果:

pid:3888

栈段  一个栈段有多个栈帧组成。每个函数都有自己的栈栈帧。函数调用完毕,函数对应的栈帧也就消失掉了。如果变量或常量的空间分配在栈帧里,那么这些变量或常量也就生命结束了。

函数的生命周期
自动局部变量   函数的形参  栈帧中

进程的生命周期
代码段  数据段的空间

静态局部变量的空间分配在数据段
数据段  只读数据段   数据段  未初始化的数据段(bss)

size命令
通过size命令可以观察特定可执行程序的代码区(text)、数据区(data)和BSS区(bss)的大小,以字节为单位

size a.out(可执行文件名)


段错误

一切对虚拟内存的越权访问,都将导致段错误
-试图访问没有映射到物理内存的虚拟内存
-试图以非法方式访问虚拟内存,如对只读内存做写操作等


标准内存分配函数
·标准库提供的内存分配函数(malloc/calloc/realloc)在标准库内部维护一个线性链表,管理堆中动态分配的内存
·标准内存分配函数在分配内存时会附加若干(通常是12个)字节,存放控制信息(MCB,内存控制块),该信息一旦被意外损坏,可能在后续操作中引发异常

TIM截图20190730215257.png

虚拟内存到物理内存的映射以页(4K=4096字节)为单位

通过malloc函数首次分配内存,至少映射33页,即使通过free函数释放掉全部内存,最初的33页仍然保留


THE END!


版权声明:若无特殊注明,本文皆为《 8964CN 》原创,转载请保留文章出处。
本文链接: [学习笔记]达内纪录片day21 linux内存管理 http://www.8964cn.net/?post=82
正文到此结束

热门推荐

发表吐槽

你肿么看?

你还可以输入 250 / 250 个字

嘻嘻 大笑 可怜 吃惊 害羞 调皮 鄙视 示爱 大哭 开心 偷笑 嘘 奸笑 委屈 抱抱 愤怒 思考 日了狗 胜利 不高兴 阴险 乖 酷 滑稽

评论信息框

吃奶的力气提交吐槽中...


既然没有吐槽,那就赶紧抢沙发吧!