内存管理之一

1. 页

内核把物理页作为内存管理的基本单位。内存管理单元(MMU)是把虚拟地址转换为物理地址的硬件。大多数32位体系结构支持4KB的页,而64位体系结构一般会支持8KB的页。内核用struct page结构表示系统中的每个物理页,在<linux/mm.h>中定义。

struct page{

   page_flags_t                flags;

   atomic_t                       _count;

   atomic_t                       _mapcount;

   unsigned long               private;

   struct address_space   *mapping;

   pgoff_t                          index;

   struct list                       lru;

   void                              *virtual;

};

flag域用来存放页的状态,包括是不是胀的,是不是锁定在内存等。

_count域存放页的引用计数--当计数值变为0时,就说明当前内核没有用该页,于是在新的分配中就可以使用它。

virual域是页的虚拟地址。

必须理解一点,page结构与物理页相关,而不是与虚拟页相关。内核仅仅用它来描述当前时刻在相关的物理页中存放的东西。它是描述物理内存本身,而不是描述包含在其中的数据。

 

2. 区

由于硬件的限制,内核把页划分为三种区(<linx/mmzone.h>):

ZONE_DMA--包含的页能用来执行DMA操作

ZONE_NORMAL--包含的页都能正常映射的页

ZONE_HIGHMEM--包含"高端内存",其中的页并不能永久地映射到内核地址空间。

在X86上,ZONE_HIGHMEM为高于896M的所有物理内存;ZONE_DMA为0-16M的物理内存。

每个区都用struct zone表示,详见<linux/mmzone.h>。内核启动期间,会初始化这三个区,代码位于<mm/page_alloc.c>。

原子操作可以保证指令以原子的方式执行,执行过程不被打断。内核提供了两组原子操作接口:一组是针对整数进行操作;另一组是针对单独的位进行操作。

 

3. 内存分配与释放

3.1. 获得页

内核提供了一种请求内存的底层机制,并提供了对它进行访问的接口。

 

标识

描述

alloc_page(flags)

只分配一页,返回指向页结构的指针

alloc_pages(flags, order)

分配(1<<order)页,返回指向第一页页结构的指针

__get_free_page(flags)

只分配一页,返回指向其逻辑地址的指针

__get_free_pages(flags, order)

只分配(1<<order)页,返回指向第一页逻辑地址的指针

get_zeroed_page(flags)

只分配一页,让其内容填充0,返回其逻辑地址的指针

Page_address(*page)

把给定的页换成它的虚拟地址

调用__get_free_pages等函数后需要检查错误,因为内核分配内存可能会失败。

 

3.2. 释放页

释放页时需要谨慎,只能释放属于自己的页。传递了错误的struct page或地址,用了错误的order值,都可能导致系统崩溃。释放页的接口有:

void __free_pages(struct page *page, unsigned int order);

void free_pages(unsigned long addr, unsigned int order);

void ree_page(unsigned long addr);

 

3.3. kmalloc和kfree函数

kmalloc函数可以获得以字节为单位的一块内核内存(<linux/slab.h>):

void *kmalloc(size_t size, int flags);

void kfree(const void *ptr);

该函数返回一个指向内存块的指针,所分配的内存区在物理上是连续的。这在出错时,它返回NULL。在调用kmalloc函数后,必须检查返回的是不是NULL。

Kfree函数释放由kmalloc分配出来的内存块,注意调用kfree(NULL)是安全的。

如果搜索的范围仅限于一个字,使用__ffs()和__ffz()这两个函数更好。

 

3.4. flags标志

这些标志分为三类:

1) 行为修饰符,表示内核应该如何分配所需内存。例如,中断程序在分配内存中的过程中不能睡眠。

2) 区修饰符,表示从哪儿(区)分配内存。

3) 类型,是组合了行为修饰符和区修饰符。

内核中常用的标志是GFP_KERNEL,这种分配可能会引起睡眠,使用的是普通优先级。另一个是GFP_ATOMIC,表示不能睡眠的内存分配,即使没有足够的连续内存块可供使用,内核可能无法释放出内存来,内核也不能让调用者睡眠。分配成功的几率比GPF_KERNEL小。GFP_DMA表示分配器必须满足从ZONE_DMA进行分配,满足某些驱动程序的需求。GFP_NOIO和GFP_NOFS,可能会引起阻塞,但它们会避免执行某些操作。用在某些低级块I/O或文件系统的代码中。

什么时候使用哪种标志,见下表:

情形

标志

进程上下文,可以睡眠

GFP_KERNEL

进程上下文,不可以睡眠

GFP_ATOMIC

中断处理程序

GFP_ATOMIC

软中断

GFP_ATOMIC

Tasklet

GFP_ATOMIC

需要用于DMA的内存,可以睡眠

GFP_DMA| GFP_KERNEL

需要用于DMA的内存,不可以睡眠

GFP_DDMA |GFP_ATOMIC

 

3.5. vmalloc和vfree函数

vmalloc函数分配的内存虚拟地址是连续的,而物理地址则无需连续。这也是用户空间的分配函数的工作方式。而kmalloc函数确保页在物理地址上时连续的,自然虚拟地址也是连续的。

在性能上,vmalloc函数为了把不连续的页转换为虚拟地址空间上连续的页,必须专门建立页表项。所以内核代码大多用kmalloc函数。在<linux/vmalloc.h>中定义:

void *vmalloc(unsigned long size);

void vfree(void * addr);

vmalloc函数返回一个指针,执行逻辑上连续的一块内存区,在发生错误时,函数返回NULL。vmalloc和vfree函数可能睡眠,因此,不能从中断上下文中进行调用,也不能从其他不允许阻塞的情况下进行调用

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值