一种内存池管理技术
本文介绍一种内存池管理技术。
在m公司工作了4年多,一直负责内存池模块问题的处理,比如内存越界,data abort 系统异常的处理,本文加以总结,以便后续参考。
读本文之前,先有个约定,本文中提到的pool指的就是内存池,buffer就是内存池中的一个存储单元,一个pool包含多个buffer。
1. 内存池整体规划
首先介绍下内存池的布局,pool共12个,pool[0]包含930个buffer,每个buffer的大小是 32 bytes, 12个pool的详细信息见下图:
2. buffer 的布局
2.1 buffer示意图
以pool[0] 为例介绍一下buffer的布局,示意图(将来画一个漂亮的图替代之)如下:
上图左半部分: pool[0]包含930个buffer,每个buffer 的大小是32bytes.
上图右半部分:是buffer[3]的内存布局,可见除了用户申请的32个bytes之外,还有20个bytes的额外开销(管理成本),这20个bytes的额外开销包含5个4bytes: 4x4bytes 位于分配的内存之前(图中用1,2,3,4表示),1x4bytes位于分配的内存之后。
头部的4x4bytes | 作用 | 解释 |
---|---|---|
1(POOL_NEXT) | Next available buffer address | 如果当前buffer的状态为free,指的是本pool中下一个可用buffer的地址.如果当前状态的状态是allocated, 为magic number:0x50555345 |
2(POOL_ID) | 指向当前buffer所属pool 的info | 当前pool中的所有buffer 中的这一地址存储一样的地址。(后面会详细介绍pool info) |
3(HEAD) | 固定魔数: 0XF1F1F1F1 | Header |
4(TASK) | 当前buffer所属的owner | 当前buffer是被那个task申请的 |
尾部的1x4bytes | 作用 | 解释 |
---|---|---|
1 | Buffer的footer | 后2个buffer固定0xF2F2,前2个buffer位当前buffer所在pool的index.上图为:0x03F2F2 |
要注意一点如果用户申请的内存小于32个bytes, 我们也会分配一个32个bytes的buffer给用户。 用户申请n个bytes, n<32, 系统会将[n+1,n+4]这4个bytes设置为0xF2F2F2F2, 我们把这个魔数叫做Footer. 上图中用户实际申请16个bytes,内存系统会将17直20这4个bytes设置为0xF2F2F2F2.
2.2 Trace32 恢复出的buffer布局
0x02F1F2F2: 是第0x02F1个buffer
接下来的52个bytes[0x649AA870, 0x649AA8A4]为第0x02F2个buffer的地址范围,可以对照上面的解释详细分析下这52个bytes:
0x50555345: 这个buffer的状态为allocated.
0x649A0EFC: 这个地址存储的是此buffer所属内存池的pool info.
0xF1F1F1F1: Header,
0x64B1B448: 这个buffer属于哪个task分配的。
[0x649A A880, 0x649A A89F]:这32个bytes 为实际分配的内存,其中有2个0xF2F2F2F2, 所以可以推测出用户实际申请的real size 为16 bytes 或者 24 bytes, 但是从目前的信息无法确实是16还是24. (参考第六节:内存系统的不足)
0x02F2F2F2:指示这是第0x02F2个buffer.
2.3 申请和释放buffer时的错误检查
通过以上分析,每个buffer有20 bytes的额外开销,这些开销对后续定位内存越界有很大的帮助。内存管理系统在用户申请/释放buffer时,会对buffer的布局进行检查。
Pool初始化完后内部buffer如下。在分配内存时会检查 PM_NEXT/POOL_ID/HEAD信息是否正确,如果存在问题,会报异常出来,我们称简称这种错误为HEAD被踩.
Buffer被分配出去(Allocated)后的布局如下,在释放buffer时除了检查上述的HEAD被踩的情况,还需要检查是否FOOTER被踩
被踩类型 | 原因 |
---|---|
HEAD 被踩 | 前一个buffer发生越界,需要检查前一个buffer的内存布局是否正确。如果前一个buffer的HEAD也被踩,需要继续向前查前一个buffer. |
FOOTER被踩 | (HEAD未被踩) 当前buffer越界,需要检查当前buffer所属task的上下文。 |
3. pool info介绍
第2节中有介绍每个buffer的头部的4*4bytes中,有4个bytes(POOL_ID)存储的是当前buffer所属pool 的信息,pool结构体细节如下:
4. buffer statistics
第3节中提到了在pool的信息中存储了buffer的统计信息,也就是buffer的history,存储了3个数组。这3个数组中也包含了当前buffer的使用信息,这3个数组可以理解为一个环形队列,由于buffer只有2中状态:ALLOCATED/FREE, 所以这3个数组中的buffer_status依次只有如下可能:ALLOCATED/FREE/ ALLOCATED 或者FREE/ALLOCATED/FREE, 根据owner_task或buffer_status就可以推导出那个node 属于当前buffer的信息。
下图为buffer[860]对应的buf_statistics中的3个历史节点,通过以上分析,可知node[2]为当前buffer 对应的节点。
如果这个队列中出现了2个ALLOCATED或者FREE依次出现,就表明程序存在bug。举例:History node[0].buffer_status等于ALLOCATED,History node[1].buffer_status也等于ALLOCATED, 则程序存在bug.
5. pool中可用的buffer链表
第3节中提到了在pool的信息中存储了可用buffer的链表available_list,另外在第2节中提到的buffer头中的POOL_NEXT,这2者之间存在关联,我们摘取Pool中的buffer初始化状态:
user_ptr是buffer中实际内存的地址(不包含buffer头部的16 bytes (0x10)的额外开销),PM_NEXT是buffer中开头地址(包含buffer头部的16bytes的额外开销)
buffer[n]中的PM_NEXT+0x10 指向buffer[n-1]中user_ptr
比如,buf[8]中的PM_NEXT(0x635E 3824 + 0x10)指向buf[7]中的user_ptr(0x635E 3834)
通过上图可以推测,以pool[0]为例(930个buffer), pool[0]初始化时,pool info中的available_list存储的是buffer[929]中的user_ptr, 当用户第1次申请buffer(小于等于32 bytes)时, 会将buffer[929]中PM_NEXT+0x10赋值给available_list,依次类推….,所以pool中的buffer是从后往前依次使用的。
扩展:释放内存时,内存系统怎么处理呢?
一种可能是:假设buffer[x]要释放,当前available_list.head 指向的是buffer[800], 伪代码如下:
buffer[x].PM_NEXT = buffer[800].user_ptr – 0x10;
avaialble_list.head = buffer[x].user_ptr;
这种做法的特点是:被释放的内存会很快被再次分配出去
还有一种可能是:假设buffer[x]要释放, available_list.tail 指向的是buffer[0], 伪代码如下:
buffer[0].PM_NEXT = buffer[x].user_ptr – 0x10;
buffer[x].PM_NEXT = NULL;
这种做法的特点是:被释放的内存会最后被分配出去,符合FIFO. 还记得pool_info.pm_fifo_suspend吗?推测和内存释放的策略有关系。
6. 内存系统中的不足和可改善之处
第5章提到的available_list,其实可以不用链表,如果为了节省空间的话,只需要存储一个地址即可:指向下一个可用的buffer的user_ptr. 但是这样做的缺点时,如果buffer中的PM_NEXT由于内存越界被踩的话,可用buffer链表可能会断掉。
此内存系统在free buffer时,没有clean buffer. 从已经分配buffer中存在多个0xF2F2F2F2就证明了这一点。 难道是为了提高程序的运行效率?
尽可能少的报异常出来
如果只是踩了buffer自己的Footer(0xF2F2F2F2),是不是可以考虑不报异常出来,尽可能让程序运行下去呢?提高用户体验。
第1节提到的每个pool中的buffer个数是固定的,所以每一代产品都需要评估这个数量是否够用。如果buffer被申请完了,再申请的话,就会报错(pool_info.task_waiting推测与此有关,如果申请不到,task是否需要等待,此内存池系统无内存回收机制)。另外,pool_info.buf_statistic.time_stamp存储了buffer申请的时间,是不是可以考虑,如果某个buffer长久不释放的话,需要让buffer申请者检查下程序是否存在bug, 从而控制buffer的无限制扩大。
7. 扩展
use_after_free检查
buffer头中的PM_NEXT,如果不是0x50555345,则表示buffer为free状态,如果这时要访问此buffer,则发生use_after_free.
可以考虑在debug版本下打开此功能。release版本下打开会影响性能。
相关文章:

企业支付宝账号开发接口实现
转载自:http://my.oschina.net/xshuai/blog/313809 关于即时到账的开发。审核通过。简单测试如下。 希望看的可以收藏或者赞一下哦。 1:拥有自己的支付宝企业账号。去产品商店选择适合自己的方案。并签约合同。 2:选择合适的商家收款产品并去签约。填写相应的信息 3…
Fastadmin管理Mysql_FastAdmin-CMS模版制作(6)-正式部署
一、工具信息介绍(1)服务器系统:CentOS7.2 64位系统;(2)服务器面板:宝塔,官网地址:https://www.bt.cn/;(3)PHP7.2;(4)mysql5.6;(5)Nginx;二、运行环境安装(1)进入宝塔官网…

使用微软提供的Office Online实现Office文档的在线查看,编辑等功能
使用微软提供的Office Online平台只需要一个网址即可在线查看Xls,doc,PPT等文档http://view.officeapps.live.com/op/view.aspx?src要查看的文档地址在线编辑需要登录live.com并从onedrive中打开或新建文档也可以来自在线模板(下面的Excel来自Excel Online模板,编辑…

HDU(1847)Good Luck in CET-4 Everybody!
利用PN分析求解此题。递推下去会发现3和3的倍数都是P点。 #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <set> using namespace std; int main() {int n;while(~scanf("%d",&n)){i…
uboot引导kernel - 1 - Flash的分区
uboot启动Linux内核过程分为4大步骤: 问题1:Flash的分区相关问题 在 上述步骤1/2/4 中都提到了从启动介质(iNand/SD)中读取uboot/kernel到SRAM/DDR中,那么具体从启动介质的什么位置分别读取呢? 上述步骤1中,iROM的…

fedora mysql默认密码忘记_Linux fedora 24 忘记密码图形化界面修改root密码的方法
方法及其简单,只需要两步即可:1、第一步:打开终端,输入sudo su命令。–此处的密码为普通用户的密码,也就是开机时输入的密码。2、第二步:直接sudo passwd root就重置了roor密码了。此时输入新的密码即可&am…

Java Web项目结构
Java Web项目结构(一般) 1、Java src 2、JRE System Library 3、Java EE 6 Libraries 4、Web App Libraries 5、WebRoot 版权声明:本文博客原创文章,博客,未经同意,不得转载。转载于:https://www.cnblogs.c…

WTForms 小计1 forms
2019独角兽企业重金招聘Python工程师标准>>> 定义 from wtforms import Form, StringField, validators class MyForm(Form):first_name StringField(uFirst Name, validators[validators.input_required()])last_name StringField(uLast Name, validators[vali…

day32-1 事件Event
事件Event-线程 每一个线程都是独立运行且状态不可预测。你把一个任务丢到子线程中,这个任务将异步执行,如何获取到这个任务的执行状态?使用threading库中的Event对象。对象包含一个可由线程设置的信号标志,线程直到等到该标志为真…
uboot引导kernel - 2- uboot/kernel需要放在DDR什么位置的问题
uboot启动Linux内核过程分为4大步骤: 问题2: uboot阶段DDR的分区的问题 上述步骤2和步骤4中,有将uboot/kernel拷贝纸DDR的步骤,具体要拷贝到DDR的什么位置呢? 分清楚这两个概念: 链接地址:链接…

java ftp pasv_Ftp主动模式和被动模式以及java连接ftp模式设置
FTP的主动模式与被动模式FTP服务器使用20和21两个网络端口与FTP客户端进行通信。FTP服务器的21端口用于传输FTP的控制命令,20端口用于传输文件数据。FTP主动模式:FTP客户端向服务器的FTP控制端口(默认是21)发送连接请求,服务器接受连接&#…
Python2.7 安装numpy报错解决方法
Windows 10下用pip安装numpy包报错: Microsoft Visual C 9.0 is required Unable to find vcvarsall.bat Get it from http://aka.ms/vcpython27 通过报错提示信息,打开http://aka.ms/vcpython27会跳转到Microsoft Visual C Compiler for Python 2.7的下…

异常The Struts dispatcher cannot be found. This is
2019独角兽企业重金招聘Python工程师标准>>> 原因:struts2的过滤器映射路径写错 解决方案:在web.xml中配置struts2的过滤器如下: <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*&…

UNDO表空间损坏,爆满,ORA-600[4194]/[4193]错误解决
模拟手工删除UNDO表空间 在ORADATA 中把UNDOTBS01.DBF 删除模拟启库SQL> STARUP;*第 1 行出现错误:ORA-01157: 无法标识/锁定数据文件 2 - 请参阅 DBWR 跟踪文件ORA-01110: 数据文件 2: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\UNDOTBS01.DBF利用系统本身的默认手动管理 MAN…
uboot引导kernel - 3 -uboot给内核传参详解
uboot中执行theKernel函数后,kernel正式启动。如下函数,我们发现有3个参数。 1. 参数 0; 2. 参数machid; 如下code 中获取machid, gd是个全局变量. 2.1 bi_arch_number是board_info中的一个元素,含义是:开发板的机器…

hdu 1166 敌兵布阵(树状数组)
题意:区间和 思路:树状数组 #include<iostream> #include<stdio.h> #include<string.h> using namespace std;int n,c[50005];int lowbit(int i){return i&-i; }void update(int i,int val){//更新函数while(i<n){//注意这个n的…

java jdbc 表存在_使用JDBC查询是否存在某表或视图,按月动态生成表
查询数据库是否有某表的存在,主要用的就是Connection对象对元数据的操作,代码很简单,贴出来大家参考。/*** 查询数据库是否有某表* param cnn* param tableName* return* throws Exception*/SuppressWarnings("unchecked")public b…

linux串口驱动分析
linux串口驱动分析硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作。UART 使用系统时钟能够支持最高 115.2Kbps 的波特…
计算TD-LTE DL 峰值速率的工具和相关参数
前一段时间测试DT 基站,需要配置TDD LTE cell 的UDC、TM模式来验证不同组合下的下行峰值速率,趁此机会我用excel写了一个计算下行峰值速率的工具。工具上传至我的github: https://github.com/greenricky/tdlte_dl_rate 计算峰值速率的常用办法是参考36.2…

java配置中心开源项目_配置中心搭建(spring-cloud-config-server)
1.github创建配置库2.配置服务端①创建项目②导入jarorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-testorg.springframework.cloudspring-cloud-starter-netflix-eureka-clientorg.springframework.cloudspring-cloud-config-…

detection in video and image
video中的detection,背景更加复杂,目标更加不聚焦,同时由于图片分辨率低于图像,因此更加难做。 image中的Detection,背景相对简单些,目标更加聚焦,同时图片分辨率高,因此更加容易些。 转载于:ht…

烂泥:U盘安装Centos6.5
本文首发于烂泥行天下。使用U盘安装Centos6.5,需要以下几个步骤:1、 制作U盘linux系统2、 设置服务器BIOS3、 安装Centos,注意引导分区的安装首先要把U盘做成linux启动盘。网上有关这方面的软件比较多,在此我使用的是WinSetupFrom…

http status 汇总
http status 汇总 常见HTTP状态码 200 OK301 Moved Permanently302 Found304 Not Modified307 Temporary Redirect400 Bad Request401 Unauthorized403 Forbidden404 Not Found410 Gone500 Internal Server Error501 Not Implemented100 Continue 初始的请求已经接受…

hdu 4278 2012天津赛区网络赛 数学 *
8进制转为10进制 1 #include<cstdio>2 #include<iostream>3 #include<algorithm>4 #include<cstring>5 #include<cmath>6 #include<queue>7 #include<map>8 using namespace std;9 #define MOD 1000000007 10 const int INF0x3f3f3f…
slub object 内存布局
我在 https://blog.csdn.net/wowricky/article/details/83218126 介绍了一种内存池,它的实现类似于linux 中打开slub_debug (1. make menuconfig: Kenel hacking -> Memory Debugging, 2. comand line中传入slub_debugPZU) 时slub 对象池。 首先我们先看一下slub…

java wait abc_java----wait/notify
解释wait/notify必须配合synchronized使用democlass NotifyStop2 {private final Object lock new Object();public void add(String s) throws InterruptedException {Thread.sleep(1000);synchronized (lock) {System.out.println("add notify qian");//唤醒其他线…

hdu-1166敌兵布阵
这个题目就是考察线段树的基本用法,我自己打了代码,其实就是照模板来的,大概思想已经弄懂了。用c不能过,说我超时,就改成c的读入读出,这坑爹的过了。我最爱的c,你肿么了。。。 这是ac的代码&…

Android数据存储(三)——SQLite
如果需要一个更加健壮的数据存储机制,则需要使用一个关系型数据库,在Android上,则为SQLlite。 SQLite的特点:轻量级、嵌入式的、关系型数据库。可移植性好,易使用,小,高效且可靠,与使…
3GPP组织和协议概述
3GPP组织概述 1. TSG/WG 3GPP是以工作组开展工作的,目前有3个大的技术规范组:RAN, SA, CT, 这一级别的工作组英语写为 TSG (Technical Specification Group)。每个TSG下面又分了很多工作组(WG: work group). 详见下表: https://www.3gpp.or…

mysql被拖垮_说几个拖垮系统的小细节!
本文首发于个人微信公众号《andyqian》,期待你的关注!前言有好几天没有更新了,期间确实比较忙些,以至于周末也没休息。在这期间,感触还是蛮深的。现在碎片化的想法等整理好后,再以文章的形式分享出来。今天要说的是另外…