https://blog.csdn.net/zhangge3663/article/details/85334176
inux内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍linux内存组织结构和页面布局,内存碎片产生原因和优化算法,linux内核几种内存管理的方法,内存使用场景以及内存使用的那些坑。从内存的原理和结构,到内存的算法优化,再到使用场景,去探寻内存管理的机制和奥秘。
# 内存是什么
内存又称主存,是CPU能直接寻址的存储空间,由半导体器件制成
# 存储的速率对比
内存的特点是存取速率快

# linux内存地址空间Linux内存管理全貌

# 内存地址--用户态: Ring3运行于用户态的代码则要受到处理器的诸多
用户态: Ring3运行于用户态的代码则要受到处理器的诸多
内核态: Ring0在处理器的存储保护中,核心态
用户态切换到内核态的3种方式: 系统调用、异常、外设中断
区别: 每个进程都有完全属于自己的,独立的,不被干扰的内存空间;用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用;内核态线程共享内核地址空间;
# 内存地址--MMU地址转换
MMU是一种硬件电路,它包含两个部件,一个是分段部件,一个是分页部件
分段机制把一个逻辑地址转换为线性地址
分页机制把一个线性地址转换为物理地址

# 内核中获取4M以上大内存的方法
1、修改MAX_ORDER,重新编译内核
2、内核启动选型传递"mem="参数,如"mem=80M,预留部分内存;然后通过
request_mem_region和ioremap_nocache将预留的内存映射到模块中。需要修改内核启动参数,无需重新编译内核,但这种方法不支持x86架构,只支持ARM,PowerPC等非x86架构
3、在start_kernel中mem_init函数之前调用alloc_boot_mem函数预分配大块内存,需要重新编译内核
4、vmalloc函数,内核代码使用它来分配在虚拟内存中连续但物理内存中不一定连续的内存
# slab高速缓存
## 1) 普通高速缓存
slab分配器所提供的小块连续内存的分配是通过通用高速缓存实现的
通用高速缓存所提供的对象具有几何分布的大小,范围为32到131072字节。
内核中提供了kmalloc()和kfree()两个接口分别进行内存的申请和释放
。
## 2) 专用高速缓存
内核为专用高速缓存的申请和释放提供了一套完整的接口,根据所传入的参数为具体的对象分配slab缓存
kmem_cache_create()用于对一个指定的对象创建高速缓存。它从cache_cache普通高速缓存中为新的专有缓存分配一个高速缓存描述符,并把这个描述符插入到高速缓存描述符形成的cache_chain链表中
kmem_cache_alloc()在其参数所指定的高速缓存中分配一个slab。相反,kmem_cache_free()在其参数所指定的高速缓存中释放一个slab
# 内核态内存池
## 1) 基本原理
先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用
当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够在继续申请新的内存
这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到了提升
## 2) 内核API
mempool_create创建内存池对象
mempool_alloc分配函数获得该对象
mempool_free释放一个对象
mempool_destroy销毁内存池

# 用户态内存池
```
template <int N>
class heappool
{
private:
typedef struct { char data[N]; } block_type;
block_type *ptr;
private:
static size_t count;
static std::list<block_type *> L;
public:
heappool() {
if (L.empty()) ptr = new block_type; else { ptr = L.back(); L.pop_back(); }
}
~heappool() {
L.push_back(ptr); if (L.size() > count) { delete L.front(); L.pop_front(); }
}
static void set_block_count(size_t cnt) { count = cnt; }
public:
char *data() { return (char *) ptr; }
size_t size() { return N;}
}
```
# 内存的使用场景
* page管理
* slab(kmalloc、内存池)
* 用户态内存使用(malloc、relloc文件映射、共享内存)
* 程序的内存map(栈、堆、code、data)
* 内核和用户态的数据传递(copy\_from\_user、copy\_to\_user)
* 内存映射(硬件寄存器、保留内存)
* DMA内存
# 用户态内存分配函数
* alloca是向栈申请内存,因此无需释放
* malloc所分配的内存空间未被初始化,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常运行,但经过一段时间后(内存空间已被重新分配)可能会出现问题
* calloc会将所分配的内存空间中的每一位都初始化为零
* realloc扩展现有内存空间大小
a) 如果当前连续内存块足够realloc的话,只是将p所指向的空间扩大,并返回p的指针地址。这个时候q和p指向的地址是一样的
b)如果当前连续内存块不够长度,再找一个足够长的地方,分配一块新的内存,q,并将p指向的内容copy到q,返回q,并将p所指向的内存空间删除。
* mmap将一个文件或者其它对象映射到内存,多进程可访问
# 内核态内存分配函数
函数分配原理最大内存其他
_get_free_pages直接对页框进行操作,4MB适用于分配较大量的连续物理内存
kmem_cache_alloc基于 slab 机制实现128KB适合需要频繁申请释放相同大小内存块时使用
kmalloc基于 kmem_cache_alloc 实现128KB最常见的分配方式,需要小于页框大小的内存时可以使用vmalloc建立非连续物理内存到虚拟地址的映射物理不连续,适合需要大内存,但是对地址连续性没有要求的场合
dma_alloc_coherent基于_alloc_pages 实现4MB适用于 DMA 操作
**ioremap实现已知物理地址到虚拟地址的映射,适用于物理地址已知的场合,如设备驱动alloc_bootmem在启动 kernel 时,预留一段内存,内核看不见小于物理内存大小,内存管理要求较高(这个很重要)**
# malloc 申请内存
调用 malloc 函数时,它沿 free_chuck_list 连接表寻找一个大到足以满足用户请求所需要的内存块

free_chunk_list链接表的主要工作是维护一个空闲的堆空间缓冲区链表
如果空间缓冲区链表没有找到对应的节点,需要通过系统调用sys_brk延伸进程的栈空间

# 缺页异常(分配内存)
通过get_free_pages申请一个或多个物理页面
换算addr在进程pdg映射中所在的pte地址
将addr对应的pte设置为物理页面的首地址
系统调用: Brk-----申请内存小于等于128kb,do_map----申请内存大于128kb

# 用户进程访问内存分析
用户态进程独占虚拟地址空间,两个进程的虚拟地址可相同
在访问用户态虚拟地址空间时,如果没有映射物理地址,通过系统调用发出缺页异常
缺页异常陷入内核,分配物理地址空间,与用户态虚拟地址建立映射

# 共享内存
## 1) 原理
它允许多个不相关的进程去访问同一部分逻辑内存
两个运行中的进程之间传输数据,共享内存将是一种效率极高的解决方案
两个运行中的进程共享数据,是进程间通信的高效方法,可有效减少数据拷贝的次数

## 2) shm接口
shmget创建共享内存
shmat启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间
shmdt将共享内存从当前进程中分离
转自:https://mp.weixin.qq.com/s?__biz=MjM5NTU2MTQwNA==&mid=2650658341&idx=1&sn=24f70b560c7509816fd5323035d4bedb&chksm=beffdcf6898855e0ba3e1e3c7969a3bb37cb7661d08f66b1f88f00599a4b759097dc11938205&mpshare=1&scene=1&srcid=#rd
- 程序优化
- vtune
- linux性能监控软件Perf
- 系统级性能分析工具perf的介绍与使用
- perf的二级命令
- 全局性概况
- 全局细节
- 最常用功能perf record
- 可视化工具perf timechart
- perf引入的overhead
- perf stat
- gprof
- 三种Linux性能分析工具的比较
- perf+gprof+gprof2dot+graphviz进行性能分析热点
- 英特尔多核平台编程优化大赛报告
- 内存操作
- mmap
- mmap的分类
- 深入理解内存映射mmap
- 计算机底层知识拾遗(九)深入理解内存映射mmap
- 内核驱动mmap Handler利用技术(一)
- Windows内存管理机制及C++内存分配实例
- Linux内存管理初探
- Windows CPU信息查看
- Linux CPU信息查看
- 预留大内存
- Linux下试验大页面映射
- /dev/mem
- Linux中通过/dev/mem操控物理地址
- /dev/mem分析
- 用法举例
- Linux下直接读写物理地址内存
- 查看内存信息
- Cache Memory
- 页面缓存
- 查看各级cache信息的方法
- dmidecode命令查看cache size
- CPU Cache 机制以及 Cache miss
- ARM体系关闭mmu和cache
- CR0-4寄存器介绍
- 查看CR0,CR2,CR3的值
- Linux 下如何禁用CPU cache
- 7个示例科普CPU Cache
- 第一个例子的C代码
- 其中之一
- Linux 从虚拟地址到物理地址
- 内存测试例子
- 每个程序员都应该了解的内存
- Part 1
- 程序员能够做什么
- 3 CPU caches
- 6 What Programmers Can Do
- VirtualAlloc
- Large-Page Support
- Some remarks on VirtualAlloc and MEM_LARGE_PAGES
- DMA
- MOV和MOVS的效率问题?如何高效的拷贝内存 中的数据
- how to use movntdqa to avoid cache pollution
- 计算机底层知识拾遗(一)理解虚拟内存机制
- How to access the control registers cr0,cr2,cr3 from a program
- 细说Cache-L1/L2/L3/TLB
- what-is-the-meaning-of-non-temporal-memory-accesses-in-x86
- How can the L1, L2, L3 CPU caches be turned off on modern x86/amd64 chips?
- UA list
- GDB
- 程序运行参数
- Linux下GDB的多线程调试
- CMake
- CMake快速入门教程:实战
- cmake打印变量值
- function
- source_group
- cmake_parse_arguments
- 编译.S文件
- add_definitions
- CMake添加-g编译选项
- Debug模式下启动
- Mysql
- Mysql联合查询union和union all的使用介绍
- MySQL数据库导入错误:ERROR 1064 (42000) 和 ERROR at line xx: Unknown command '\Z'.
- 解决MYSQL数据库 Table ‘xxx’ is marked as crashed and should be repaired 145错误
- C/C++
- c语言中static的作用
- strlen和sizeof有什么区别?
- printf
- Libuv中文文档之线程
- RapidJSON
- gcc/g++ 实战之编译的四个过程
- __thread
- TARGET_LINK_LIBRARIES
- MAP_HUGETLB
- 使用Intel格式的汇编
- __m128i
- emmintrin.h
- _mm_stream_si128
- _mm_stream_load_si128
- _mm_load_si128
- _mm_xor_si128
- _mm_store_si128
- _mm_cvtsi128_si64
- Intel SSE指令集
- _mm_set_epi64x
- _mm_aesenc_si128
- _umul128
- _mm_malloc
- reinterpret_cast
- strlen
- 读取UTF-8的txt文件发现开头的多三个字节的问题
- PHP
- php计算函数执行时间的方法
- 框架
- Json Rpc远程调用框架
- PHP多进程
- PHP CLI模式下的多进程应用
- php多进程总结
- 优化
- PHP7 优化
- 让你的PHP7更快(GCC PGO)
- PHP的性能演进(从PHP5.0到PHP7.1的性能全评测)
- PHP字符串全排列算法
- 获取服务器基本信息
- cookie
- phpstudy2018 安装xdebug扩展
- 软件下载
- PHP mysqli_error() 函数
- PHP Session 变量
- curl
- curl_getinfo
- 获取请求头
- PHP使用CURL获取302跳转后的地址实例
- PHP基于cURL实现自动模拟登录
- PHP获取远程图片大小(CURL实现)
- CURL模拟登录
- curl模拟登录提交(从目录中获取文件)
- CURL HTTPS
- curl帮v
- rename
- copy
- JSON
- json_encode
- json_decode
- json_last_error_msg
- json_last_error
- PHP json_encode中文乱码解决方法
- var_dump
- PHPStorm与Xdebug设置
- Xdebug原理以notepad为例
- str_pad
- pack
- PHP二进制与字符串之间的相互转换
- PHP执行系统命令(简介及方法)
- 函数
- 十进制转二进制
- 字符串到ASSCI
- 字符串转二进制
- 合并两个表
- 图像识别
- Tesseract
- 虚拟机
- vmware下Kali 2.0安装VMware Tools
- 安装 VMware tools出现“正在进行简易安装时,无法手动启动VMware tools安装”
- 爬虫
- 有哪些好的数据来源或者大数据平台?
- Cygwin
- Git 常用命令
- 排列组合
- 含重复元素序列的全排列
- 全排列的非递归和递归实现(含重复元素)
- GitBook
- 编辑环境
- visual studio code
- 2名数学家或发现史上最快超大乘法运算法,欲破解困扰人类近半个世纪的问题
- 系统预定义常量
- 指令集
- SSE
- _MSC_VER
- msys2
- 安装cmake
- MSYS2 更新源
- 讲Cmake msys32使用问题解答 CXX CMAKE_C_COMPILER配置详解
- VirtualBox
- 解决virtualbox只能安装32位系统的问题
- Ubuntu
- 使用AES-NI的编译参数
- debian下安装内核源码的方法
- tar.xz结尾的文件的解压方法
- Linux命令
- insmod
- fatal error: openssl/bio.h
- 准备module的编译环境(kali)
- Ubuntu/Debian 之内核模块开发准备
- dmesg的详细用法
- Linux系统开机自动加载驱动module
- linux /Module 浅析(转载)
- Kali
- 找回gpedit
- Enable the Lock Pages in Memory Option (Windows)
- TLA
- 双系统
- 显卡
- 显示no CUDA的解决过程
