0
点赞
收藏
分享

微信扫一扫

Linux内核设计与实现(6):内存管理


内存管理

  • ​​内存管理​​
  • ​​页​​
  • ​​区​​
  • ​​获得页​​
  • ​​获得填充为0的页​​
  • ​​释放页​​
  • ​​kmalloc()​​
  • ​​kfree()​​
  • ​​vmalloc()​​
  • ​​slab层​​
  • ​​在栈上的静态分配​​
  • ​​单页内核栈​​
  • ​​在栈上光明正大的工作​​

本系列博客追寻《Linux内核设计与实现-Robert Love》,各个Linux机中的内核源代码不一,因此直接下载官网内核源码

参考书籍:《Linux内核设计与实现-Robert Love》

内存管理

在内核中分配内存可不像在其他地方分配内存那么容易,从根本上来讲,是因为内核本身不能像用户空间那样奢侈的使用内存

自古以来,得内存者得终极

Linux内核设计与实现(6):内存管理_数据结构

1、用户空间
应用程序使用malloc()申请内存,使用free()释放内存。malloc(和free()是glibc库的内存分配器ptmalloc是供的接口,ptmalloc使用系统调用brk或mmap向内核以页为单位申请内存,然后划分成很小内存块分配给应用程序。
2、内核空间
内核空间它的基本功能:虚拟内存管理主要负责从进程的虚拟地址空间分配虚拟页,sys_ brk用来扩大或收缩堆,sys_ mmap用来在内存映射区域分配虚拟页, sys_ munmap用来释放虚拟页。
内核空间提供把页划分成小内存块分配的块分配器,提供分配内存的接口,支持3个块分配器: slab分配器、slub分配器和slob分配器。

内核把物理页作为内存管理的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转换成物理地址的硬件)通常以页为单位进行处理。从虚拟内存的角度来看,页就是最小单位。

内核用struct page结构表示系统中的每个物理页

struct page {
/* flag用来存放页的状态
状态:脏,是否锁定在内存中等等
*/
unsigned long flags;
/* _count存放页的引用计数,当为-1时说明没有引用
*/
atomic_t _count;
atomic_t _mapcount;
unsigned long private;
struct address_space *mapping;
pgoff_t index;
struct list_head lru;
/* virtual页的虚拟地址
*/
void *virtual;
}

由于硬件的限制,内核并不能对所有的页一视同仁。有些页位于内存中特定的物理地址上,所以不能将其用于一些特定的任务,所以内核把页划分为不同的区(zone)。

Linux必须处理如下两种由于硬件存在缺陷而引起的内存寻址问题:

  • 一些硬件只能用某些特定的内存地址来执行DMA(直接内存访问)
  • 一些体系结构的内存的物理寻址范围比虚拟寻址范围大得多,这样,就有一些内存不能永久的映射到内核空间上

因此,Linux使用了四种区

  • ZONE_DMA:只能用来执行DMA操作
  • ZONE_DMA32:这些页面用来执行32位设备的DMA操作
  • ZONE_BOEMAL:这个区包含的都是能正常映射的页
  • ZONE_HIGHHEM:这个区包含“高端内存”,其中的页并不能永久的映射到内核地址空间

在x86体系结构上,高于896MB的所有物理内存的范围大都是高端内存,他并不会永久的或自动的映射到内核地址空间

获得页

内核提供了一种请求内存的底层机制,并提供了访问他们的几个接口

获得填充为0的页

让返回的页的内容全为0

unsigned long get_zeroed_page(unsigned int gfp_mask);

释放页

当不在需要页时候释放他们

free_page(unsigned long addr, unsigned int order);

kmalloc()

互动二以字节为单位的一块内核内存

malloc()相比多了一个flags参数

kfree()

释放由kmalloc()分配的内存块

vmalloc()

kmalloc和vmalloc
vmalloc()分配的内存虚拟地址是连续的,而物理地址无需连续
kmalloc()函数确保页在物理地址上也是连续的(虚拟地址上也是连续的)

slab层

分配和释放数据结构是所有内核中最普遍的操作之一,为了百年与数据的频繁分配和回收,编程人员常常会使用到空闲链表。

空闲链表包含可供使用的、已经分配好的数据结构块
当代码需要一个新的数据结构实例时,就可以从空闲链表中抓取一个,而不需要分配内存,再把数据放进去
当不需要这个数据结构的实例时,就把他放回空闲链表,而不是释放他

空闲链表面临的问题之一是不能全局控制,当可用内存变的紧缺时,内核无法通知整个链表,让其收缩缓存的大小以便释放出一些内存来。实际上,内核根本不知道存在任何空闲链表

因此,Linux内核提供了slab层(页也就是所谓的slab分配器)

我们在此不做深入

在栈上的静态分配

在用户空间,不少都可用在栈上发生,用户空间可用负担很大的栈,而且栈空间还可用增长。

但是,内核栈小而且固定

当给每个进程分配一个固定大小的小栈后,不但可用减少内存的消耗,而且内核也无需负担太重的栈管理任务
每个进程的内核栈大小通产是两个页的大小

单页内核栈

单页内核栈,即每个进程的内核栈只有一页大小

原因:

  • 让每个进程减少内存消耗
  • 随着机器运行时间的增加,寻找两个未分配的、连续的页变得更加困难,物理内存渐渐变成碎片,因此,给一个新进程分配虚拟内存(VM)的压力页在增大

但是中断处理程序页放进内核栈明显怪怪的,因此中断处理程序程序自己的栈了!中断栈!

中断栈为每个进程提供了一个用于处理中断处理程序的栈,有了这个选项
中断处理程序不用再和被中断进程共享一个内核栈,他们可以使用自己的栈

在栈上光明正大的工作

进行动态分配或者选择宕机


举报

相关推荐

0 条评论