伙伴算法、slab机制、内存管理函数

文章目录

  • 1 伙伴算法
    • 页框操作
      • alloc_pages()
  • 2 slab
    • slab机制要解决的问题
    • 使用高速缓存
  • 3 内存管理函数
    • kmalloc
    • kzalloc
    • vmalloc
    • vzalloc
    • 区别
  • 参考文章

内核使用struct page结构体描述每个物理页,也叫页框。内核在很多情况下,需要申请连续的页框,而且数量不定,比如4个、5个、9个等。如果频繁地请求和释放不同大小的一组连续的页框,必然导致在已分配的块内分散了许多小块的空闲页面,由此带来的问题是,即使有足够的空闲页框可以满足请求,但要分配一个大块的连续页框可能无法满足请求。为了避免这种情况,Linux内核引入了伙伴算法。

1 伙伴算法

伙伴算法把所有的空闲页框分为11个块链表,每块链表中分布包含特定的连续页框内存空间,在第i条链表中,每个链表元素包含2的i次方个连续页框。
请添加图片描述
假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。
从上面可以知道Buddy算法一直在对页框做拆开合并拆开合并的动作。Buddy算法牛逼就牛逼在运用了世界上任何正整数都可以由2^n的和组成。这也是Buddy算法管理空闲页表的本质。

页框操作

alloc_pages()

static inline struct page *
alloc_pages(unsigned int gfp_mask, unsigned int order);

该函数分配2的order次方个连续的页框,并返回一个指针,该指针指向第一个页page结构体,如果出错,返回NULL。可以使用下面这个函数把给定的页转为它的逻辑地址:

void *page_address(struct page *page);

该函数返回一个指针,指向给定物理页当前所在的逻辑地址。

其他页框操作可以看这篇文章:https://blog.csdn.net/qq_41683305/article/details/123966721

2 slab

在Linux中,伙伴算法是以页为单位管理和分配内存。但是现实的需求却以字节为单位,假如我们需要申请20Bytes,总不能分配一页吧!那此不是严重浪费内存。那么该如何分配呢?slab分配器就应运而生了,专为小内存分配而生。slab分配器分配内存以字节为单位。但是slab分配器并没有脱离伙伴算法,而是基于伙伴算法分配的大内存进一步细分成小内存分配。
我们先来看一张图:
请添加图片描述

kmem_cache是cache_chain上的一个元素,kmem_cache描述了一个高速缓存,每个高速缓存包含了一个slabs的列表,这通常是一段连续的内存块。存在3种slab:

  • slabs_full:slab都已经分配完
  • slabs_partial:slab部分分配
  • slab_empty:空slab或者没有对象被分配。

kmem_cache高速缓存以缓存对象的大小来区分,所包含的三种slab都是链表,里面有一个或多个slab,每个slab由一个或多个连续的物理页组成,在物理页上保存的才是对象。

slab是slab分配器的最小单位,在实现上一个slab由一个或多个连续的物理页组成(通常只有一页)。单个slab可以在slab链表之间移动,例如如果一个半满slab被分配了对象后变满了,就要从slabs_partial中被删除,同时插入到slabs_full中去。

slab机制要解决的问题

  1. 减少伙伴算法在分配小块连续内存时所产生的内部碎片
  2. 将频繁使用的对象缓存起来,减少分配、初始化和释放对象的时间开销
  3. 通过着色技术调整对象以更好的使用硬件高速缓存

使用高速缓存

一个新的高速缓存是通过以下函数创建的:

kmem_cache_t *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags,void (*ctor)(void *, kmem_cache_t *, unsigned long),void (*dtor)(void *, kmem_cache_t *, unsigned long));

第一个参数是字符串,存放着高速缓存的名字。第二个参数是高速缓存中每个元素的大小,第三个参数就是高速缓存内第一个对象的偏移,这用来确保在页内进行特定的对齐,通常情况,0就可以满足要求,也就是标准对齐。flags是可选的设置项,用来控制高速缓存的行为。

我们使用cat /proc/slabinfo可以看到系统所有的高速缓存:
在这里插入图片描述
创建高速缓存之后,就可以通过下列函数从中获取对象:

void *kmem_cache_alloc(kmem_cache_t *cachep,int flags);

该函数从给定的高速缓存cachep中返回一个指向对象的指针。如果高速缓存的所有slab中都没有空闲的对象,那么slab层必须通过kmem_getpages()获取新的页,flags的值传递给__get_free_pages()。

最后释放一个对象,并把它返回给原先的slab,可以使用下面的函数:

void kmem_cache_free(kmem_cache_t *cachep,void *objp);

这样就能把高速缓存cachep中的对象objp标记为空闲了。

要销毁一个高速缓存,则调用:

int kmem_cache_destroy(kmem_cache_t *cachep);

同样,也不能从中断上下文中调用这个函数,因为它也可能会睡眠。调用该函数之前必须确保以下两个条件:

  • 高速缓存中的所有slab都必须为空
  • 在调用kmem_cache_destroy()期间,不能再访问这个高速缓存

3 内存管理函数

kmalloc

void *kmalloc(size_t size, int flags);
  • size:是指要分配的内存的字节数。
  • flags:是分配标志,它提供了多种kmalloc( )的行为

这个函数返回一个指向内存块的指针,其内存块至少要有size大小,所分配的内存区在物理上是连续的,在出错时,它返回NULL,除非没有足够的内存可用,否则内核总能分配成功。在使用kmalloc()时,必须检查返回值是不是NULL。

kzalloc

void *kzalloc(size_t size, int flags);

kzalloc的功能比kmalloc多了一步,会将申请到连续物理内存数据置为0

vmalloc

void *vmalloc(unsigned long size);

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

vzalloc

void *valloc(unsigned long size);

vzalloc比vmalloc步骤多了一步,将申请的逻辑地址连续的内存数据置为0。

区别

kmalloc和kzalloc申请的内存在物理上连续,vmalloc和vzalloc申请的内存在物理上不需要连续,它们在逻辑上连续。kmalloc和kzalloc申请的内存可由kfree函数释放

void kfree(const void *ptr);

vmalloc和vzalloc申请的内存可由vfree函数释放

void vfree(void *addr);

参考文章

https://zhuanlan.zhihu.com/p/36140017

https://www.cnblogs.com/cherishui/p/4246133.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Javaweb---监听器

1.什么是监听器 监听器就是监听某个对象的状态变化的组件。 事件源:被监听的对象 ----- 三个域对象 request session servletContext 监听器:监听事件源对象 事件源对象的状态的变化都会触发监听器 ---- 62 注册监听器:将监听器与事件源进行…

Linux中的Ramdisk和Initrd

Ramdisk简介先简单介绍一下ramdisk,Ramdisk是虚拟于RAM中的盘(Disk)。对于用户来说,能把RAM disk和通常的硬盘分区(如/dev/hda1)同等对待来使用,例如:redice # mkfs.ext2 /dev/ram0mke2fs 1.38 (30-Jun-200…

slab下kmalloc内核函数实现

文章目录kmalloc的整体实现获取高速缓存高速缓存获取index总结https://blog.csdn.net/qq_41683305/article/details/124554490,在这篇文章中,我们介绍了伙伴算法、slab机制和常见的内存管理函数,接下来,我们看看kmalloc内核函数的…

标题:三羊献瑞

标题:观察下面的加法算式: 其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。 请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。 思路分析: 首先…

进程虚拟地址管理

文章目录1 地址分布实际使用中的内存区域2 进程的虚拟地址描述用户空间mmap线程之间共享内存地址的实现机制1 地址分布 现在采用虚拟内存的操作系统通常都使用平坦地址空间,平坦地址空间是指地址空间范围是一个独立的连续空间(比如,地址从0扩…

标题:加法变乘法

标题:我们都知道:123 … 49 1225 现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015 比如: 123…10*1112…27*2829…49 2015 就是符合要求的答案。 请你寻找另外一个可能的答案,并把位置靠前的那个乘号左…

【翻译】eXpressAppFramework QuickStart 业务模型设计(四)—— 实现自定义业务类...

这一讲,你将学到如何从头开始实现业务类。为此,将要实现Department和Position业务类。这些类将被应用到之前实现的Contact类中。你将学到引用对象自动生成用户界面的基本要素。 在此之前,我建议你去阅读一下 【翻译】eXpressAppFramework Qui…

内存重映射

文章目录1 kmap2 映射内核内存到用户空间使用remap_pfn_range使用io_remap_pfn_rangemmap文件操作建立VMA和实际物理地址的映射mmap 之前分配 一次性映射mmap 之前分配 Page FaultPage Fault 中分配 映射内核内存有时需要重新映射,无论是从内核到用户空间还是从内…

math.sqrt 有问题_JavaScript中带有示例的Math.sqrt()方法

math.sqrt 有问题JavaScript | Math.sqrt()方法 (JavaScript | Math.sqrt() Method) The Math.sqrt() method is inbuilt in JavaScript to find the square root of a number. In this tutorial, we will learn about the sqrt() method with examples. JavaScript中内置了Mat…

ISAPI Rewrite 实现简单url重写、二级域名重写

实现步骤: 第一步:下载ISAPI_Rewrite.rar,将Rewrite文件夹和httpd.ini直接放在项目根目录下面。 第二步:IIS配置,筛选Rewrite文件夹里面的Rewrite.dll文件,如图: 第三步:在httpd.ini…

用户登录

用户登录 代码namespace 用户登录 {public partial class Form1 : Form{public Form1(){InitializeComponent();}bool b1, b2, b3, b4, b5, b6;private void button1_Click(object sender, EventArgs e){try{if (b1 && b2 && b3 && b4 && b5 &…

进程上下文和中断上下文

文章目录进程的preempt_count变量thread_infopreempt_counthardirq相关softirq相关上下文原文链接: https://zhuanlan.zhihu.com/p/88883239进程的preempt_count变量 thread_info 在内核中,上下文的设置和判断接口可以参考 include/linux/preempt.h 文…

标题:凑算式

标题:凑算式 这个算式中AI代表19的数字,不同的字母代表不同的数字。 比如: 68/3952/714 就是一种解法, 53/1972/486 是另一种解法。 这个算式一共有多少种解法? 注意:你提交应该是个整数,不要…

Linux内存地址管理

文章目录系统内存布局内核地址的低端和高端内存概念低端内存高端内存地址转换和MMULinux中的四级分页模型虚拟地址字段页表处理将虚拟地址转换物理地址Linux系统中的每个内存地址都是虚拟的,它们不直接指向任何物理内存地址。每当访问内存位置时,可以执行…

录制caf 转 mp3

编译需要使用的 lame库http://www.cocoachina.com/bbs/read.php?tid108237参考的文章http://blog.csdn.net/ysy441088327/article/details/7392842说起来,我一直在找一个音频转换成mp3的方法。一年前,我成功编译出了一个lame for armv7的库。苦于不会使…

開發記要 詭異的變量

告別繁體文盲,從寫blog開始 Variable命名很重要,有多重要,看看.net和java的加密就知道, 都是把variable改到一塌糊塗,你想看看都沒門. 但是這幾天看遺留系統的代碼,真是大開眼界。 我一直以為別人寫a,b,c,d這些單字節variable已經很過分。直到我看到以下這幾個&#xff0…

排序算法---快速排序、堆排序、冒泡排序

排序算法1 快速排序代码实现stdlib库快排2 堆排序堆排序的基本思想如何构造一个大顶堆排序3 冒泡排序1 快速排序 文章原地址:https://blog.csdn.net/morewindows/article/details/6684558 快速排序的平均时间复杂度是0(NlogN),它采用了一种分治的策略&a…

项目总结:华南师范大学校园开发教育android客户端总结

忽略之前小打小闹,这个项目算是我的第一个项目--SCNU的网络公选课的android版本的客户端。项目是从5月中旬开始的,中间经历了几个星期的复习考试时间,到现在可以说是完工了吧(或许还有写细节要修改)。这个项目带给我蛮…

Linux系统编程---守护进程

1 守护进程的概述 Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大…

邮箱服务器

一.邮箱服务器的基本概念 邮件的客户端:可以只安装在电脑上(C/S)的也可以是网页形式(B/S)的 邮件服务器:起到邮件的接受与推送的作用 邮件发送的协议: 协议:就是数据传输…