一 ,内核管理内存的方式

(1)内核把物理页作为内存管理的基本单位,内存管理单元通常以页为单位进行处理,所以,从虚拟内存角度来看,页就是最小单位。

大多数32位系统支持4kb的页,64位系统支持8kb的页。

(2)内核用这个结构体表示并管理系统中每个物理页。

(3)由于硬件的限制,内核需要把页划分为不同的区,形成不同的内存池,根据用途进行分配。

其中ZONE_DMA 物理内存 <16MB

ZONE_NORMAL 物理内存16-896MB

ZONE_HIGHMEM 物理内存>896MB

二,内核分配和释放内存

获得页。

Struct page * alloc_pages(unsigned int gfp_mask,unsigned int order);

这个函数可以分配2的order次方个连续的物理页,并返回一个指向第一个页的page结构体的指针,如果出错,返回NULL。

如果想得到逻辑地址,使用 void *page_address(struct page *page);

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

(2)释放页

三,Slab分配器

(1)什么是slab分配器

Linux内核中基于伙伴算法实现的分区页框分配器 为避免产生内部碎片

slab分配器中用到了对象这个概念,所谓对象就是内核中的数据结构以及对该数据结构进行创建和撤销的操作。它的基本思想是将内核中经常使用的对象 放到高速缓存中,并且由系统保持为初始的可利用状态。比如进程描述符,内核中会频繁对此数据进行申请和释放。当一个新进程创建时,内核会直接从slab分配器的高速缓存中获取一个已经初始化了的对象;当进程结束时,该结构所占的页框并不被释放,而是重新返回slab分配器中。如果没有基于对象的slab分 配器,内核将花费更多的时间去分配、初始化以及释放一个对象。

(2)slab分配器作用:

将频繁使用的对象缓存起来,减少分配、初始化和释放对象的时间开销。

vmalloc kmalloc 基于Slab 实现

vmalloc 在ZONE_HIGHMEM区,分配大内存块,物理地址不连续,适合对地址连续无要求的场合 因为虚拟地址更新页表,故效率较低

kmalloc 通常在ZONE_NORMOL 适合分配内存大小小于页框的场合 不会触发页表更新,分配效率高

(3)slab层

在栈上的静态分配

在任何情况下,无限制的递归和alloca()是不允许的。

任意函数必须尽量节省栈资源,方法就是所有函数让局部变量所占空间之和不要超过几百字节。如果栈溢出,可能会对连着内核栈末端的thread_info结构产生很大的影响。

进行动态分配是明智的选择。

高端内存的映射

什么是高端内存?Linux把内存地址空间分为三部分:ZONE_DMA ZONE_NORMAL ZONE_HIGHMEM 高端内存的地址范围是0xf80000000~0xffffffff,他不会永久的自动的映射到内核地址空间。

永久映射

这个函数在高端内存或低端内存上都可以使用。

如果page结构对应的是低端内存中的一页,返回的是虚拟地址;如果是高端内存,则会建立一个永久映射。

注意:永久映射的数量是有限的,当不需要的时候,需要归还,应解除映射。

临时映射

当必须创建一个映射而当前的上下文不能睡眠时,内核提供临时映射。有一组保留的映射,他们可以存放新创建的临时映射。

注意:临时映射可以用在不能睡眠的地方。

参数type时面熟临时映射的目的

这个函数不会阻塞,因此可以用在中断上下文和其他不能重新调度的地方。还可以禁止内存抢占。

临时内核映射和永久内核映射相比,其最大的特点就是不会阻塞请求映射页框的进程,因此临时内核映射请求可以发生在中断和可延迟函数中。

临时映射可以在不能睡眠的地方,如中断处理程序中,因为获取映射时,绝对不会阻塞。它也禁止内核抢占,因为映射对每个处理器都是唯一的。

四,每个CPU的分配

(1)支持SMP的现代操作系统使用每个CPU上的数据,对于给定的处理器其数据是唯一的。一般来说,每个CPU的数据存放在一个数组中。数组中的每一项对应着系统上一个存在的处理器。当前处理器号确定这个数据的当前元素。像如下声明数据:

上面的代码并没有出现锁,这是因为所操作的数据对当前处理器来说是惟一的。除了当前处理器之外,没有其他处理器可以接触到这个数据,不存在并发访问的问题。

因此,内核抢占成为了唯一要关注的问题:

(2)内核抢占

  1.如果你的代码被其他处理器抢占并重新调度,那么这时cpu变量就会无效,因为它指向的是错误的处理器(通常,代码获得当前处理器后是不可以睡眠的)。

  2.如果别的任务抢占了你的代码,那么有可能在同一处理器上发生并发访问My_percup的情况,显然这处于一种竞争状况。

  不必惊慌,应为在获取当前处理器号,即调用get_cpu()时,就已经禁止了内核抢占。相应的smp_processor_id()在调用put_cpu()时又会重新激活当前处理器号。注意,如果你使用对smp_processor_id()的调用来获得当前处理器号,只要你总是用上述方法来保护数据安全,那么内核抢占并不需要你自己去禁止。

  新的每个CPU接口

  2.6内核为了方便创建和操作每个CPU数据,从而引进了新的操作接口,称作percpu。该接口归纳了前面所述的操作行为,并使每个CPU数据的创建和操作得以简化。

  编译时的每个CPU数据

  DEFINE_PRE_CPU(type, name);

  这个语句为系统中的每一个处理器都创建了一个内型为type,名字为name的变量实例,如果你需要在别处声明变量,以防范编译时警告,那么下面的宏将是你的好帮手:

  DECLARE_PER_CPU(type, name);

  你可以利用get_cpu_var()和put_cpu_var()例程操作变量。调用get_cpu_var()返回当前处理器上的指定变量。同时它将禁止抢占,另一方面put_cpu_var()将相应的重新激活抢占。

使用此方法要格外小心,因为per_cpu()函数既不会禁止内核抢占,也不会提供任何形式的锁保护。如果一些处理器可以接触到其他处理器的数据。那么就必须要给数据上锁。此外,如果你需要从模块中访问每个CPU数据,或者如果你需要动态创建这些数据。

(3)运行时的每个CPU数据

  内核实现每个CPU数据的动态分配方法类似于kmalloc()。该例程为系统上的每个处理器创建所需内存的实例:

宏alloc_perpu()给系统中的每个处理器分配一个指定类型对象的实例。它其实是宏__alloc_percpu的一个封装,这个原始的宏接收的参数有两个:一个是要分配的实际字节数,一个是分配时要按多少字节对齐。而封装后的_alloc_percpu()按照单字节对齐——按照给定类型的自然边界对齐,比如:

  _alignof_结构是gcc的一个功能,它会返回指定的类型或lvalue所需的对齐节数。它的语意和sizeof一样,比如:

  _alignof_(unsigned long)

  在x86体系中将返回4。如果指定一个lvalud,那么将返回lvalud的最大对齐字节数。比如一个结构中的lvalue相比结构外的lvalue可能有更大的对齐字节数需求,这是结构本身的对齐要求的缘故。

  调用函数 free_percpu() 将释放所有处理器上指定的每个CPU数据。

  无论是alloc_percpu()还是__alloc_percpu都会返回一个指针,用来间接引用动态创建的每个CPU数据,内核提供了两个宏来利用指针获取每个CPU数据:

  get_cpu_ptr()宏返回了一个指向当前处理器数据的特殊实例。它同时会禁止内核抢占,而在put_cpu_ptr()宏中会重新激活内核抢占。

 (4)使用每个CPU数据的原因

  使用每个CPU数据好处如下:

  1.减少了数据锁定,因为按照每个处理器访问每个CPU数据的逻辑,你可以不在需要任何锁。

  2.可以大大的减少缓存失效,失效发生在处理器视图使他们的缓存保持同步时,如果一个处理器操作某个数据,而该数据又存放在其他处理器缓存中,那么存放该数据的那个处理器必须清理或刷新它自己的缓存。持续不断的缓存失效称为缓存抖动,这样对系统性能影响颇大。

  它唯一的安全要求就是禁止内核抢占。

以上内容是小组一同完成的。

博主已将这些基础知识汇总成了一个视频版的C语言基础知识大全加博主的QQ号1625358265 备注【GGG】不备注不通过哦 加上QQ后即可获取!更多C/C++(Linux内核)入门、进阶教程也可以添加博主QQ 号备注【GGG即可获取!!!

学习直通车

资料免费领

Linux内核中的内存管理(图例解析)相关推荐

  1. [十月往昔]——Linux内核中的内存管理浅谈

    为什么要叫做"十月往昔"呢,是为了纪念我的原博客,http://www.casual0402.cn. 不知道为什么,突然想来一个新的开始--而那个博客存活至今刚好十个月,也有十个月 ...

  2. linux内核acpi,Linux内核中ACPI电源管理部分解析

    ACPI包括很多功能,电源管理是其功能之一,具体的ACPI的介绍可以参考ACPI的技术文档. Linux中利用模块机制,实现ACPI对电源的管理: static struct cpufreq_driv ...

  3. linux内核工程导论,Linux内核工程导论——内存管理(3)

    Linux内核工程导论--内存管理(三) 用户端内核内存参数调整 /proc/sys/vm/ (需要根据内核版本调整) 交换相关 swap_token_timeout Thisfile contain ...

  4. Linux内核初始化阶段内存管理的几种阶段

    本系列旨在讲述从引导到完全建立内存管理体系过程中,内核对内存管理所经历的几种状态.阅读本系列前,建议先阅读memblock的相关文章. 一些讲在前面的话 在很久很久以前,linux内核还是支持直接从磁 ...

  5. epub 深入linux内核架构_深入分析Linux内核源代码6-Linux 内存管理(2)

    每天十五分钟,熟读一个技术点,水滴石穿,一切只为渴望更优秀的你! ----零声学院 6.3 内存的分配和回收 在内存初始化完成以后,内存中就常驻有内核映像(内核代码和数据).以后,随着用 户程序的执行 ...

  6. Linux内核中常见内存分配函数

    1.      原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示.四级页表分 ...

  7. KSM(Kernel Samepage Merging) 剖析:Linux 内核中的内存去耦合

    简介:作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通过 ...

  8. 详细讲解Linux内核源码内存管理(值得收藏)

    Linux的内存管理是一个非常复杂的过程,主要分成两个大的部分:内核的内存管理和进程虚拟内存.内核的内存管理是Linux内存管理的核心,所以我们先对内核的内存管理进行简介. 一.物理内存模型 物理内存 ...

  9. Linux内核中使用内存检测

    目录 一.slub内存检测 1.越界访问 2.释放后再访问 3.无效的释放 4.实验输出 二.KASAN 内存检测 1.数组越界 2.栈的越界访问 3.实验输出 一般的内存访问错误如下: 越界访问 访 ...

最新文章

  1. 用c#建立webservice
  2. 17个之多!Windows Vista各版本功能区别详解
  3. 获取进程号并赋值判断进程状态
  4. 零基础学Python(第十三章 元组)
  5. centos6.5虚拟机安装后,没有iptables配置文件
  6. 相机+激光雷达重绘3D场景
  7. 用ubuntu远程连接mysql_Ubuntu 安装 MySQL 和远程连接
  8. 训练日志 2018.9.12
  9. 上机练习 实现消费单的打印 需求不明确要补充
  10. 深入理解Java中的底层阻塞原理及实现
  11. Python使用pyopencl在GPU上并行处理批量判断素数
  12. while循环练习23
  13. 北大先修课 计算机,解读:北大清华大学先修课的那些事
  14. 查看python安装的库_查看python及其第三方库的版本和安装位置
  15. Newsmy纽曼星云1000G移动硬盘拆解教程
  16. 设置一个类似手机的锁屏界面但又不知道如何操作
  17. 千万不要攻击中国网站!传奇美国黑客凯文·米特尼克的警告
  18. java处理生信数据,生信Java软件安装
  19. AtCoder Beginner Contest 267 ABC题解
  20. 【Python/爬虫】爬取网易云音乐评论区热评(练习)

热门文章

  1. adb shell 模拟鼠标导入通讯录
  2. 产品和商品的区别 sku spu的区别 什么事关键属性?什么事商品属性?
  3. 手机服务器证书验证失败怎么回事,安卓手机连接websocket证书校验失败已解决
  4. 赛尔号7月17日服务器维护,赛尔号7月17日更新攻略汇总
  5. 自荐信当计算机课代表,北京邮电大学自主招生考生自荐信范文
  6. 码代码微信号今日上线,为互联网同仁提供最前沿咨询
  7. matlab求勒让德多项式零点,有没有勒让德多项式导数 零点程序
  8. htmlunit取消css,javascript支持
  9. 图片裁剪(压缩,裁剪图片)
  10. ios 开源的电子书阅读器项目