操作系统内存管理

一、进程与内存:

1.代码段:代码段用来存放可执行文件的操作指令,也就是说它是可执行程序在内存中的镜像,代码段需要防止在运行时被非法修改,所以只允许读取操作,不允许写操作,—它是不可写的
2.数据段:数据段用来存放可执行文件中的已初始化的全局变量,换句话说就是存放程序静态分配的变量和全局变量
3.BSS段:BSS段包含了程序中未初始化的全局变量,在内存中bss段全部置零
4.:堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或者缩减,当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除
5.:栈时用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且等到调用结束后,函数的返回值也会被存放回栈中
这里也就能提出三个问题:
1.进程空间地址如何管理
2.进程地址如何映射到物理内存
3.物理内存如何被管理
同样由上述问题引发的一些子问题:比如系统虚拟地址分布,内存分配接口,连续内存分配与非连续内存分配

二、进程内存空间

Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间,该空间是大小为4G的线性虚拟空间,用户所看到和接触到的都是虚拟地址,无法看到实际的物理内存地址。利用这种虚拟地址不但能祈祷保护操作系统的效果(用户不能直接访问物理内存),而且更重要的是,用户程序可使用比实际物理内存更大的地址空间。
需要澄清的几个问题

  • 4G进程的地址空间被人为的分为两个部分—用户空间和内核空间,用户空间从0到3G(0xC0000000),内核空间是从3G到4G。用户进程通常情况下只能访问用户的虚拟地址,不能访问内核空间的虚拟地址,只有用户进程进行系统调用(代表用户进程在内核态执行)等十克可以访问到内核空间
  • 用户空间对应进程,所以每当进程切换,用户空间就会跟着变化,而内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。内核空间地址有自己对应的页表,用户空间进程各自有不同的页表
  • 每个进程的用户空间都是完全独立,互不相干的。

进程内存管理

进程内存管理的对象是进程线性空间上的内存镜像,这些内存镜像其实就是进程使用的虚拟内存区域,进程的虚拟空间是个32或64为平台的地址空间,为了方便管理,虚拟空间被划分为大小可变化的(4096的倍数)内存区域,这些区域在进程线性地址中有序排列。这些区域的划分原则是"将访问属性一致的地址空间存放在一起",可读可写可执行。
可以通过cat/proc//maps获得某个进程所占用的内存区域。
在这里插入图片描述
每行数据的格式如下:
(内存区域)开始 - 结束 访问权限 偏移 主设备号:次设备号 i节点 文件

在Linux内核中对应进程内存区域的数据结构是:vm_area_struct,内核将每个内存区域作为一个单独的内存对象管理,相应的操作也都一致。采用面向对象是VMA结构体可以代表多种类型的内存区域—比如内存映射文件或者进程的用户空间栈等,对这些区域的操作也都不尽相同。
vm_area_struct结构比较复杂,关于它的详细结构请参阅相关材料。我们这里只对它的组织方法做一些补充说明,vm_area_struct是描述进程地址空间的基本管理单元,对于一个进程来说往往需要多个内存区域描述它的虚拟空间,如何关联这些不同的内存区域呢?大家可能都会想到使用链表,的确vm_area_struct结构缺实是以链表形式链接,不过为了方便查找,内核又以红黑树(以前的内核使用平衡树)的形式组织内存区域,以便降低搜索耗时。并存的两种组织形式,并非冗余,链表用于需要遍历全部节点的时候用,而红黑树是用于地址空间中定位特定的内存区域的时候用,内核为了内存区域上的各种不同的操作都能过得高性能,所以同时使用了两种数据结构。在这里插入图片描述
进程的地址空间对应的描述结构是"内存描述符结构",它表示进程的全部地址空间,——包含了和进程地址空间有关的全部信息,其中当然包含进程的内存区域。

进程内存的分配与回收

创建进程fork()、程序载入execve()、映射文件mmap()、动态内存分配malloc()/brk()等进程相关操作都需要分配内存给进程。不过这时进程申请和获得的还不是实际内存,而是虚拟内存,准确的说是"内存区域"。进程对内存区域的分配最终都会归结到do_mmap()函数上来(brk调用被单独以系统调用实现,不用do_mmap())。
内核使用do_mmap()函数创建一个新的线性地址区间。但是说该函数创建一个新的VMA并不非常准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个,如果不能合并,那么就确实需要创建一个新的VMA了。但无论那种情况,do_mmap()函数都会将一个地址区间加入到进程的地址空间中——无论是扩展已存在的内存区域还是创建一个新的区域,同样释放一个内存区域使用函数do_ummap(),它会销毁对应的内存区域。
如果想了解更多的关于进程的内存分配与回收的相关问题可以看下这篇文章
https://blog.csdn.net/C1029323236/article/details/94320370

如何由虚变实

我们之前就知道,进程所能直接操作的地址是虚拟地址,但是当进程运行时,从内核获得的仅仅是虚拟的内存区域,而不是实际的物理地址,获得的仅仅是对一个新的线性地址区间的使用权,实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由"请求页机制"产生缺页异常,从而进入分配实际页面的例程。
该异常是虚拟内存机制赖以存在的基本保证——它会告诉内核去真正为进程分配物理页,并建立对应的页表,这时候虚拟地址才实实在在地映射到了系统的物理内存上。
这种请求页机制把页面的分配推迟到不能再推迟为止。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值