内核采用 struct page 来表示一个物理页,在其中记载了诸多物理页的属性,比如 物理页被几个线程使用(如若没有则表示该页可以释放),页对应的虚拟地址。

  首先需要知道的是,分配物理页可以分为两个步骤 :

    1) 寻找内核中空闲 处于3-4G 4K对齐的虚拟地址。

        i)虚拟页是否空闲 由页表项的属性决定,里面会有标志位记录。

        ii)虚拟页需要4K对齐是强制性的,因为页目录项的低12 bit是用来记录页目录属性的,高20 bit才是记录页目录项的物理地址。

    2) 寻找内核中空闲的物理页。

    3) 通过改变页表项来修改映射关系,如何修改的请看 我的另一篇博客 : https://www.cnblogs.com/vizdl/p/11925205.html

 // 物理页表示
struct page {
unsigned 1ong flags ;
atomic_t _count ;
atomic_t mapcount;
unsigned 1ong private;
struct address_space * mapping;
pgoff_t index;
struct list_ head lru;
void *virtual;
}; 

  由物理地址进行分区 : 在系列文章2中也讲了分区的一些缘由,详情可看系列博客2中内核空间分区部分 https://www.cnblogs.com/vizdl/p/12233623.html

  内核空间一般被分为三个区 : ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM,直接提供的接口可以建议内核从特定区域申请物理页。

struct zone
{
unsigned long
watermark[NR_ WMARK];
unsigned 1ong lowmem_ reserve[MAX_ NR_ ZONES];
struct per_cpu_pageset pageset[NR_CPUS];
spinlock_t lock;
struct free_ area
free_area[MAX_ ORDER] spinlock_t
lru_1ock;
struct zone_1ru
{
struct list_head list;
unsigned 1ong nr_saved_scan;
} lru[NR_ LRU_ LISTS];
struct zone_reclaim_ stat reclaim_ stat;
unsigned long pages_scanned;
unsigned long flags;
atomic_1ong_t vm_stat[NR_ _VM_ ZONE STAT_ ITEMS];
int prev_priority;
unsigned int inactive_ratio;
wait_queue_head_t *wait_ table;
unsigned 1ong wait_ table_ hash_ nr_ entries;
unsigned 1ong wait_ table_ bits;
struct pglist_ data *zone_ pgdat;
unsigned long zone_ start_ pfn;
unsigned long spanned_ pages;
unsigned 1ong present_ pages;
const char *name;
};

  对物理页操作接口: 物理页采用buddy算法来进行分配,单位最小为1页。只能申请2^order页,这也是buddy算法的特性。

  特别说明 :

  1) gfp_t gfp_mask,这个参数就是用来建议内核在具体哪个区域得到物理页。

  2) 如若已经无可用物理页了,就会采用一系列 页面置换算法, 将物理页上的内容保存到磁盘中的swap sapce(交换空间)中去。

// 分配 2^order个连续的物理页(使用buddy);
struct page *al1oc_ pages(gfp_t gfp_mask, unsigned int order);
// 利用page中的 virtual属性 得到对应的虚拟地址。
void *page_address(struct page *page);
// 分配 2^order个连续的物理页,且直接返回逻辑地址
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
// 分配一页物理内存
struct page *alloc_page(gfp_t pfp_mask);
// 分配一页物理内存 并返回逻辑地址
struct page *__get_free_page(gfp_t pfp_mask);
// 获取填充为0的页
unsigned long get_zeroed_page(unsigned int gfp_mask);

  物理页的释放函数 :

// 释放物理地址
void __free_pages(struct page *page, uns igned int order);
void free_pages(unsigned long addr, unsigned int order);
void free_page(unsigned 1ong addr);

  

  对于小块内存分配,内核也提供了Slab分配器来进行分配,这个分配器可以提供一个缓冲,不会将申请的物理解除绑定虚拟地址,理由是一些小块的内存(特别是结构体)会在内核中频繁地申请与释放。

  Slab层的一些接口 :

// 字节为单位的内存分配(slab层)
// kmalloc()在<linux/slab.h>中声明:
// 分配物理地址连续的地址, 出错返回NULL
void *kmalloc(size_t size, gfp_t flags);
// 物理不一定连续,虚拟连续。
void *vmalloc(unsigned long size);
// 释放
void kfree(const void *ptr);
void vfree(const void *addr);

  slab层的好处 :

    1) 避免了频繁地寻找空闲虚拟页和空闲物理页,提高效率。

    2) 避免了因频繁修改物理页与虚拟页映射关系而导致的物理页无法有大块平坦(连续)物理空间。

  slab本质其实就是一个内存池。 而内存池之后我可能会总结malloc的内存分配算法,那个算法完全可以用于slab。不过一个是堆内存池,一个是内核内存池。

  slab与Buddy的关系:

  

  由上述 : 目前对于内核空间的内存分配已经有一个简单的策略认识了, 即 小块内存 用slab分配器分配,大块内存(最小单位为页)用 Buddy系统 直接分配。而slab层内存池的内存也是Buddy分配出来的。只不过Slab在此做了一个缓冲罢了。

Linux内存管理解析(三) : 内核对内核空间的内存管理的更多相关文章

  1. linux(centOS7)的基本操作(三) 用户、组、权限管理

    用户和组 1.用户.组.家目录的概念 linux系统支持多用户,除了管理员,其他用户一般不应该使用root,而是应该向管理员申请一个账号.组类似于角色,系统可以通过组对有共性的用户进行统一管理.每个用 ...

  2. Android内存优化(三)避免可控的内存泄漏

    相关文章 Android性能优化系列 Java虚拟机系列 前言 内存泄漏向来都是内存优化的重点,它如同幽灵一般存于我们的应用当中,有时它不会现身,但一旦现身就会让你头疼不已.因此,如何避免.发现和解决 ...

  3. Linux内核入门到放弃-内存管理-《深入Linux内核架构》笔记

    概述 内存管理的实现涵盖了许多领域: 内存中的物理内存页管理 分配大块内存的伙伴系统 分配较小内存块的slab.slub和slob分配器 分配非连续内存块的vmalloc机制 进程的地址空间 在IA- ...

  4. (转)Linux环境进程间通信系列(五):共享内存

    原文地址:http://www.cppblog.com/mydriverc/articles/29741.html 共享内存可以说是最有用的进程间通信方式,也是最快的 IPC 形式.两个不同进程 A ...

  5. Java中OutOfMemoryError(内存溢出)的三种情况及解决办法

    转载自:http://blog.sina.com.cn/s/blog_701c951f0100n1sp.html 相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题, ...

  6. jvm内存溢出的三种情况以及解决办法

    1       前言相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深 ...

  7. Android内存优化(三)详解内存分析工具MAT

    前言 在这个系列的前四篇文章中,我分别介绍了DVM.ART.内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT. 1.概述 在进行内存分析时,我们可以使用M ...

  8. Linux内核设计笔记12——内存管理

    内存管理学习笔记 页 页是内核管理内存的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转化为物理地址的硬件)通常以页为单位进行处理,从虚拟内存的角度看,页就是最小单位. struct page{ ...

  9. 解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】

    转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的 ...

随机推荐

  1. 9、python判断语句与循环语句

    前言:本文主要介绍python判断语句与循环语句,包括if语句.while循环.for循环.range函数. 一.if语句  关键字:if.elif.else,写法如下: # if if 条件: # ...

  2. MySQL 相关规约(v1.0) (转)

    0)前言 a. 基本规约 [强制]表存储引擎必须使用InnoDB(针对主库一般是强制要求的) [强制]表字符集默认使用utf8,必要时候使用utf8mb4(个人踩坑:emoji表情存储问题)•说明:• ...

  3. 每天敲一点code

    下面这段代码摘自 <C#并发编程经典实例> 并行LINQ static IEnumerable<int> MultiplyBy2(IEnumerable<int> ...

  4. 源码详解系列(七) ------ 全面讲解logback的使用和源码

    什么是logback logback 用于日志记录,可以将日志输出到控制台.文件.数据库和邮件等,相比其它所有的日志系统,logback 更快并且更小,包含了许多独特并且有用的特性. logback ...

  5. Linux 高压缩率工具 XZ 压缩详解

    目录 一.XZ 基础信息 二.安装 三.详解 3.1.常用的参数 3.2. 常用命令 四.扩展 4.1.unxz 4.2.xzcat 4.3.lzma 4.4.unlzma 4.5.lzcat 一.X ...

  6. springboot中使用Caffeine本地缓存

    Caffeine是使用Java8对Guava缓存的重写版本性能有很大提升 一 依赖 <dependency> <groupId>org.springframework.boot ...

  7. 实验11:EIGRP

    实验8-1:EIGRP 基本配置 实验目的通过本实验可以掌握:(1)在路由器上启动EIGRP 路由进程(2)启用参与路由协议的接口,并且通告网络(3)EIGRP 度量值的计算方法(4)可行距离(FD) ...

  8. FFmpeg被声明为已否决的解决方案

    参考雷神的最简单的打印Hello World的程序: #include <stdio.h> #include <string.h> extern "C" { ...

  9. FFMPEG学习----使用SDL播放PCM数据

    参考雷神的代码: /** * 最简单的SDL2播放音频的例子(SDL2播放PCM) * Simplest Audio Play SDL2 (SDL2 play PCM) * * 本程序使用SDL2播放 ...

  10. 解决Apple Mobile Device USB Driver

    在设备管理器里找到便携设备:APPLE IPHONE 更新驱动 自定义更新:在设备管理器里找到便携设备:APPLE IPHONE 更新驱动 自定义更新:C:\Program Files\Common ...