初探Linux内核中的内存管理
Linux内核设计与实现之内存管理的读书笔记
初探Linux内核管理
- 内核本身不像用户空间那样奢侈的使用内存;
- 内核不支持简单快捷的内存分配机制, 用户空间支持? 这种简单快捷的内存分配机制是什么呢?
- 内核不能睡眠;
- 内核空间和用户空间分配内存是不一样的, 差一点在哪里呢?
- 内核是如何管理内存?
- 内核把物理页作为内存管理的基本单位;
- 因为内存管理单元通常以页为单位进行处理;
- 从内存管理单元的角度来看, 页是最小的单位;
- 什么是内存管理单元(MMU) -- 就是把虚拟地址转换为物理地址的硬件; 那什么是虚拟地址呢?
- 体系结构不同, 页的大小也可能不同; 体系结构不同代表的含义可能是芯片的架构不一样, 32位机, 64位机;
- 虚拟内存和物理内存有什么区别?
- 内核把物理页作为内存管理的基本单位;
- 高端内存并不永久的映射到内核地址空间上, 什么是高端内存?
- 内核用page数据结构来描述当前时刻在相关物理内存中存放的东西;
- page结构体的目的在于描述物理内存本身, 而不是描述包含其中的数据;
- 内核用page结构管理所有的页, 内核需要知道每一个页是否空闲, 如果已经被分配, 内核需要知道谁拥有这个页;
- 拥有者可能是用户空间进程, 动态分配的内核数据, 高速缓存等;
- 每一个物理页都对应一个page结构体
- 区的概念?
- 内核不能对所有的页一视同仁, 一些页位于特定的物理内存上, 用于特定的任务;
- 内核会把页分成区; 内核使用区对相似的页进行分组;
- 内核必须处理两种情况(由硬件缺陷引起的)
- 一些硬件只能使用某些特定的内存来执行DMA(直接内存访问);
- 一些体系结构的内存的物理寻址范围比虚拟寻址范围大得多, 有一些内存就不能永久的映射到内核空间上;
- 四种不同的分区
- ZONE_DMA
- ZONE_DMA32
- ZONE_NORMAL
- ZONE_HIGHEM -- 高端内存中的页不能永久的映射到内核空间中, 32位体系结构中有用;
- 其他体系结构上, 所有内存都被直接映射;
- 区的实际分布和使用和体系结构相关
- Linux把系统的页划分为区, 形成不同的内存池, 根据不同的用途进行分配;
- 区的划分没有任何物理意义, 只是内核为了管理页而采取的一种逻辑上的分组;
- 内核不能对所有的页一视同仁, 一些页位于特定的物理内存上, 用于特定的任务;
- 64位机上的zone结构体有一个自旋锁, 它防止该结构体被并发访问;
- 内核提供了一种请求内存的底层机制, 并提供了对它的访问接口;
- alloc_pages函数分配2^order个连续的物理页, 返回的指针指向第一个页的page结构体;
- page_address把页转换为逻辑地址, 返回当前页的逻辑地址;
- __get_free_pages直接返回的是所请求的第一个页的物理地址, 应该封装了alloc_pages和page_address函数;
- 如果分配的页是给用户空间的, 调用get_zeroed_page来分配全为0的页;
- 释放页, 只能释放属于自己的页;
- __free_pages, free_pages, free_page
- 内核是完全相信自己的;
- 如果以字节为单位分配内存, 直接调用内核函数kmalloc就可以了, 返回的是连续的物理地址;
- kfree释放由kmalloc分配出来的内存块;
- 注意的是调用kfree(NULL)是安全的;
- 分配器标志-gfp_mask标志
- 行为修饰符 -- 内核应该如何分配所需的内存
- 区修饰符 -- 表示从哪里分配内存
- 类型 -- 指定所需的行为和区描述符以完成特殊类型的处理;
- 内核最常用的标志是GFP_KERNEL, 这种分配会引起睡眠, 它使用的是普通优先级(在没有锁被持有等情况下使用);
- vmalloc也是以字节为单位分配内存, 但它与kmalloc不同的是, 分配的内存虚拟地址是连续的, 物理地址不一定是连续的;
- 和malloc类似的, malloc返回的页在进程的虚拟地址空间内是连续的, 但不能保证在物理RAM中也是连续的;
- 只有硬件设备需要得到物理空间连续的地址;
- 对内存而言, 所有的内存看起来都是逻辑上连续的;
- 出于性能考虑, 内核很多代码都是直接调用kmalloc来获得内存, 而不是调用vmalloc;
- vmalloc为了把物理上不连续的页转换为虚拟空间中连续的页, 必须建立专门的页表;
- 更糟糕的是vmalloc获得页需要一个一个地进行映射;
- 导致TLB(一种硬缓冲区)抖动;
- 什么时候用vmalloc, 获得大内存块时, 模块动态地插入到内核中, 把模块装载到由vmalloc分配的内存上;
- vmalloc函数可能会睡眠, 不能在中断上下文进行调用;
- slab 层(slab分配器, )
- slab层是为了便于数据的频繁分配和回收;
- 空闲链表包含可用的, 已经分配好的数据结构体块, 代码直接从空闲链表中获得一个数据块, 不用重新分配, 不用了再放回空闲链表;
- 空闲链表相当于对象高速缓存 -- 快速存储频繁使用的对象类型;
- 空闲链表不能全局控制;
- slab分配器扮演了通用数据结构缓存层的角色;
- SMP(对称多处理结构)锁
- slab层把不同的对象划分为高速缓存组, 每个缓存组存放不同类型的对象;
- 一个高速缓存用于存放进程描述符, 一个高速缓存存放索引节点对象;
- kmalloc接口建立在slab层上, 使用了一组通用的高速缓存;
- 高速缓存又被划分为slab, 这里的slab是由一个或多个连续的页组成, 一般slab仅仅是由一页组成, 但每个高速缓存可以由多个slab组成;
- slab有三种状态: 满, 部分满或空, --- 最终目的是为了减少内存碎片;
- inode结点是磁盘索引节点在内存中的体现, 会频繁的创建和释放, 用slab来管理inode节点;
- 每个slab包含尽可能多的inode对象, 创建一个对象, 从部分满或者空的slab中返回一个指向已分配但未使用的结构的指针。
- 内核用完一个对象后, slab分配器就把该对象标记为空闲;
- 高速缓存 >> slab >> 具体对象
- 高速缓存是较大的内存空间, 里面包含了几个slab;
- 而一个slab中又包含好几个同一类的对象;
- 每个高速缓存都使用kmem_cache结构来表示, 包含三个链表:
- slabs_full
- slabs_partial
- slabs_empty
- slab分配器可以创建新的slab, 通过__get_free_pages低级内核页分配器进行;
- __get_free_pages为高速缓存分配足够多的内存, 分配的页为2的幂次方;
- NUMA系统上较好的性能(非一致的内存访问);
- slab的管理是在每个高速缓存的基础上, 并提供内核一个简单的接口;
- 通过接口可以创建和撤销新的高速缓存, 并在高速缓存内分配和释放对象;
- 高速缓存及其内部slab的复杂管理完全通过slab层的内部机制来处理;
- 当创建一个高速缓存后, slab层所起得作用就像一个专用的分配器, 可以为具体的对象类型进行分配;
- 要理解slab层的具体实现;
- slab分配器的接口
- 新建一个高速缓存 -- kmem_cache_create;
- 对齐越严格, 浪费的内存越多
- 撤销一个高速缓存 -- kmem_cache_destroy, 可能睡眠;
- 调用kmem_cache_destroy过程中(调用之后), 不再访问这个高速缓存;
- 调用者必须确保这种同步;
- 从缓存中分配: kmem_cache_alloc, 从给定的高速缓存中返回对象的指针;
- 如果高速缓存的所有slab中都没有空闲的对象, 那么slab层必须通过kmem_getpages来获取新的页;
- 释放一个对象 -- kmem_cache_free;
- 进程描述符是内核的核心组成部分;
- slab层负责内存紧缺情况下所有底层的对齐, 着色, 分配, 和回收等;
- 如果要频繁的创建很多相同类型的对象, 应该考虑使用slab高速缓存, 不要自己去实现空闲链表;
- 在栈上的静态分配
- 用户空间的内存分配在栈上进行静态分配;
- 用户空间能够非常奢侈的负担起非常大的栈, 而且栈空间还可以动态增长;
- 内核栈小而固定, 给每个进程分配一个固定大小的小栈后, 不但可以减少内存的消耗, 而且内核也无需负担太重的栈管理任务;
- 每个进程都有两页的内核栈, 32位机一页为4K, 64位机一页为8K, 所以内核栈的大小分别为8KB和16KB;
- 随着机器运行时间的增加, 寻找两个未分配的, 连续的页变得越来越困难, 内存渐渐变成碎片, 因此给一个新进程分配一个虚拟内存(VM)的压力也在增大;
- 中断栈: 中断栈为每个进程提供一个用于中断处理程序的栈;
- 中断处理程序不用再和被中断的进程共享一个内核栈, 它可以使用自己的栈;
- 内核栈可以是一页, 也可以是两页, 所以栈的大小为4~16KB;
- 再具体的函数中, 让所有局部变量(即所谓的自动变量)所占空间之和不要超过几百字节;
- 在栈上进行大量的静态分配是很危险的(分配大型数组或者大型结构体);
- 用户空间的内存分配在栈上进行静态分配;
- 高端内存的映射
- 高端内存中的页不能永久地映射到内核地址空间上;
- 通过alloc_pages函数以__GFP_HIGHMEM标志获得的页不可能有逻辑地址;
- x86体系结构上, 896MB以上的物理内存大都是高端内存, 它并不会永久地或自动地映射到内核地址空间;
- 一旦这些页被分配, 就必须映射到内核的逻辑地址空间上;
- 在x86上, 高端内存中的页被映射到3G~4G;
- 什么是永久映射?
- kmap -- 映射一个给定的page结构到内核地址空间, 高端内存和低端内存中都能使用;
- 函数返回该页的虚拟地址;
- 可以永久地映射高端内存;
- kunmap解除映射;
- kmap -- 映射一个给定的page结构到内核地址空间, 高端内存和低端内存中都能使用;
- 临时映射
- 临时映射可以用到不能睡眠的地方(中断处理程序);
- kmap_atomic -- 建立一个临时映射;
- kunmap_atomic -- 取消映射;
- 临时映射可以用到不能睡眠的地方(中断处理程序);
- 每个CPU的分配
- SMP(对称多处理器)的现代操作系统使用每个CPU上的数据, 对于给定的处理器其数据是唯一的
- 一般来说, 每个CPU的数据存放着一个数组中, 数组的每一项对应着系统上一个存在的处理器;
- 按处理器号, 确定这个数组的当前元素;
- my_percpu[NR_CPUS];
- get_cpu -- 获得当前处理器, 并禁止内核抢占;
- put_cpu -- 激活内核抢占;
- 新的每个CPU接口
- percpu 简化了创建和操作每个CPU的数据;
- 更适合大型对称多处理器计算机的要求;
- 编译时定义每个CPU数据
- DEFINE_PER_CPU(type, name); -- 为每个处理器创建一个类型为type, 名字为name的变量实例;
- DECLARE_PER_CPU -- 声明变量;
- get_cpu_var(增加该处理器上的 name变量的值)和put_cpu_var(完成: 重新激活内核的抢占);
- per_cpu(name, cpu)++, 增加指定处理器上的name变量的值;
- 不会禁止内核抢占, 也不会提供任何形式的锁保护;
- 编译时每个CPU数据的例子并不能在模块内使用, 因为链接程序实际上将他们创建在一个唯一的可执行段中(.data.percpu)
- percpu 简化了创建和操作每个CPU的数据;
- SMP(对称多处理器)的现代操作系统使用每个CPU上的数据, 对于给定的处理器其数据是唯一的
- 分配函数的选择
- 如果需要连续的物理页, 就可以使用某个低级页分配器或者kmalloc;
- 要创建和撤销很多大的数据结构, 那么考虑建立slab高速缓存;
- 虚拟文件系统(VFS) -- 负责管理文件系统且为用户空间程序提供一致性接口的内核子系统;
初探Linux内核中的内存管理的更多相关文章
- KSM剖析——Linux 内核中的内存去耦合
简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging) 允许这个系统管理程序通 ...
- Linux内核中常见内存分配函数(一)
linux内核中采 用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表. * 页全局目录(Page Global Dir ...
- Linux内核中常见内存分配函数【转】
转自:http://blog.csdn.net/wzhwho/article/details/4996510 1. 原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页 ...
- Linux内核中常见内存分配函数
1. 原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示.四级页表分 ...
- linux内核分析之内存管理
1.struct page /* Each physical page in the system has a struct page associated with * it to keep tra ...
- linux内核--用户态内存管理
在上一篇博客“内核内存管理”中,描述的内核内存管理的相关算法和数据结构,在这里简单描述用户态内存管理的数据结构和算法. 一,相关结构体 与进程地址空间相关的全部信息都包含在一个叫做“内存描述符”的数据 ...
- Linux内核中常见内存分配函数(三)
ioremap void * ioremap (unsigned long offset, unsigned long size) ioremap是一种更直接的内存“分配”方式,使用时直接指定物理起始 ...
- Linux内核剖析 之 内存管理
1. 内存管理区 为什么分成不同的内存管理区? ISA总线的DMA处理器有严格的限制:仅仅能对物理内存前16M寻址. 内核线性地址空间仅仅有1G,CPU不能直接訪问全部的物理内存. ZONE_DMA ...
- [转]linux内核分析笔记----内存管理
转自:http://blog.csdn.net/Baiduluckyboy/article/details/9667933 内存管理,不用多说,言简意赅.在内核里分配内存还真不是件容易的事情,根本上是 ...
随机推荐
- ZOJ - 3261 Connections in Galaxy War(并查集删边)
https://cn.vjudge.net/problem/ZOJ-3261 题意 银河系各大星球之间有不同的能量值, 并且他们之间互相有通道连接起来,可以用来传递信息,这样一旦有星球被怪兽攻击,便可 ...
- sqlalchemy外键和relationship查询
前面的文章中讲解了外键的基础知识和操作,上一篇文章讲解了sqlalchemy的基本操作.前面两篇文章都是作为铺垫,为下面的文章打好基础.记得初一时第一次期中考试时考的不好,老爸安慰我说:“学习是一个循 ...
- 建立爬虫代理IP池
单线程构建爬虫代理IP池 #!/usr/bin/python3.5 # -*- coding:utf-8 -*- import time import tempfile from lxml impor ...
- SpringBoot系列: 单元测试
SpringBoot 项目单元测试也很方便, Web项目中单元测试应该覆盖:1. Service 层2. Controller 层 本文前半部分讲解是一些测试基础配置. 对于Service和Contr ...
- Kettle系列:使用Kudu API插入数据到Kudu中
本文详细介绍了在Kettle中使用 Kudu API将数据写入Kudu中, 从本文可以学习到:1. 如何编写一个简单的 Kettle 的 Used defined Java class.2. 如何读取 ...
- Java爬网页数据,并存储到本地数据库中
由于开发一个人工智能项目,需要强大的后台数据库加持,所以,没有办法,又是需要医疗数据,只能自己爬某医疗网站数据,进行分析,但是由于不同网站的结构不一样,所以这个程序只能爬该网站的,第一次爬网页数据,自 ...
- 迅为IMX6开发板支持全网通4G模块丨GPS模块丨WIFI蓝牙丨千兆以太网
迅为i.MX6开发板丨迅为i.MX6Q开发板丨四核imx6开发板丨Cortec-A9开发板丨资料介绍: 特点: 处理器:Freescale Cortex-A9四核i.MX6Q主频1GHz 核心板配置: ...
- TensorFlow从入门到理解(三):你的第一个卷积神经网络(CNN)
运行代码: from __future__ import print_function import tensorflow as tf from tensorflow.examples.tutoria ...
- 牛客寒假算法基础集训营4 F(二分+拓扑判环)
题目链接 题目的输出:对于每次提问,输出一行"Yes"表示大家都遵守了群规,反之输出"No". 那么输出的就是一连串的yes和no了,二分一下无环的最大提问位置 ...
- python中读取mongodb数据并保存为csv格式的文件
import pandas as pd import matplotlib.pyplot as plt import pymongo %matplotlib inline # 连接mongodb数据库 ...