linux malloc和free解析
一、简介
malloc函数原型:void*malloc(size_t size);
申请size个字节的虚拟地址空间,并返回指向这块内存的指针;如果申请失败则返回一个空指针free函数原型:void free(void *ptr);
释放ptr指向的虚拟地址空间。问题:为啥只要传递地址,而不用传递释放多大空间?(在后文可以找到答案)
使用需要注意的一些地方:
A.申请内存空间后,必须检查是否分配成功
B.当不需要再使用的内存时,记得释放;释放后应该指向这块内存的指针指向NULL,防止程序后面不小心使用它
C.这两个函数应该配对,防止内存泄露
D.虽然malloc函数的类型是(void *),为了明确用途最好在前面强制类型转换。
二、uclibc库实现
主要讲述标准封装库uClibc-0.9.33.2里支持MMU的malloc-standard的malloc和free讲解,在配置里有如下选择
choice
prompt "Malloc Implementation"
default MALLOC if ! ARCH_USE_MMU
default MALLOC_STANDARD if ARCH_USE_MMU
1、数据结构
在标准库里有个专门的malloc数据结构管理申请到的虚拟内存,具体如下:
struct malloc_state {
使用fastbin的阈值 64字节
size_t max_fast; /* low 2 bits used as flags */
/*Fastbins 管理<64字节的malloc申请*/
mfastbinptr fastbins[NFASTBINS];
/* 指向最顶端的chunk,当这个chunk空闲的size大于trim_threshold就会brk系统调用释放内存 */
mchunkptr top;
/*The remainder from the most recent split of a small request */
mchunkptr last_remainder;
/* 该数组管理64字节以上通过brk产生的虚拟内存,mmap则一对一申请释放*/
mchunkptr bins[NBINS * 2];
/*Bitmap of bins. Trailing zero map handles cases of largest binned size */
unsigned int binmap[BINMAPSIZE+1];
/*Tunable parameters */
unsigned long trim_threshold;//触发brk系统调用释放内存= 256K
size_t top_pad; //最顶端的brk是否空闲,空闲就brk系统调用释放内存
size_t mmap_threshold; //触发mmap系统调用申请的阈值 =256K
/*Memory map support */
int n_mmaps;
int n_mmaps_max;
int max_n_mmaps;
/*Cache malloc_getpagesize */
unsigned int pagesize;
/*Track properties of MORECORE */
unsigned int morecore_properties;
/*Statistics */
size_t mmapped_mem;
size_t sbrked_mem;
size_t max_sbrked_mem;
size_t max_mmapped_mem;
size_t max_total_mem;
};
使用该结构体主要解决如下问题:
A. 空闲块组织:我们如何记录空闲块
B.放置:我们如何选择一个合适的空闲块来放置一个新分配的块
C.分割:在我们将一个新分配的块放置到某个空闲块之后,我们如何处理这个空闲块中剩余的部分
D.合并:我们如何处理一个刚刚被释放的块
2、malloc
malloc申请时在头上会多申请2个字用于管理该申请块,其结构如下:
An allocated chunk looks like this:
chunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previouschunk, if allocated | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, inbytes |P|
mem->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data startshere... .
. .
. (malloc_usable_space() bytes) .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
第一个字指向前一个chunk的大小,第二个字指向当前chunk的大小,P表示是否使用,mem即使malloc返回的获取到地址。因此,malloc(bytes)申请bytes个字节时,实际申请的nb=bytes + 2*sizeof(size_t)
根据源码,申请的情况如下:
A.当nb <= max_fast(64字节)
直接从fastbins数组中分配,直接返回。如果没有,则从bins里分配
B.当nb > max_fast
从bins数组里查找获取。
如果获取到一个比较大的chunk,则先进行分割,剩余的重新组织管理,直接返回给用户。
如果没有找到大的nb,则会努力对bins的空闲进行合并,如果还是没有则会通过系统调用brk或mmap从kernel申请。
C.系统调用brk或mmap
malloc管理的fastbins和bins里没有找到需要的虚拟内存时,只能通过系统调用从内核获取。
当nb > mmap_threshold=256K时,使用mmap的方式,set_head(p,size|IS_MMAPPED);会将上面第1个字的bit[1]设置成IS_MMAPPED(释放时判断)
当nb < mmap_threshold=256K时,使用brk的方式,同时调整malloc管理的相关信息。
两者细节见《第三章》
3、free
free后chunk的布局结构如下:
Free chunks are stored in circulardoubly-linked lists, and look like this:
chunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`head:' | Size of chunk, in bytes |P|
mem->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Forward pointer tonext chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Back pointer toprevious chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Unused space (may be0 bytes long) .
. .
. |
nextchunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`foot:' | Size of chunk, in bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The P (PREV_INUSE) bit
问题:为啥只要传递地址,而不用传递释放多大空间?
答:地址前面预留了2个字,其中一个字就保存了申请到的内存块大小,所以只需要传递地址就行。
根据源码,释放的情况如下:
A.当size <= max_fast时
直接释放到fastbins数组里
B.当size > max_fast&& 不是mmap时
先检查前后项是否属于空闲,如果是则可能进行合并,合并规则如下:
前面的块和后面的块都已经分配的,则只释放自己
前面的块是已分配的,后面的块空闲,则和后面的空闲块合并
前面的块是空闲,后面的块是分配的,则和前面的块合并
前后块都是空闲的,则进行前后合并
进行上面的合并操作后,判断最顶端top的空闲chunk是否超过trim_threshold,如果超过就进行brk系统调用释放的内存。
注意点:
结合brk系统调用服务程序(见下面章节),brk是线性向上增长,如果最顶端的brk在使用而brk下面的其他地址释放了,即使超过trim_threshold这时仍无法进行brk系统调用释放,这时就会导致这部分物理内存占着,从而引发内存碎片。
所以一个简单原则:通过brk申请的内存malloc后尽快free(即小内存申请<256K),对应不free的申请使用mmap的方式。
C.如果是mmap出来的内存
直接调用系统调用munmap进行内存释放
三、brk和mmap系统调用
在user/kernel为3G/1G比例的情况下,进程的内存分布图(摘自网络)如下:
这是灵活的内存增长方式,栈stack和mmap区域向下增长,堆heap向上增长。
brk和mmap的系统调用分别对应heap区域和memorymmaping segment区域。通过cat /proc/pid/maps可以看到该进程的详细内容:
00008000-00013000 r-xp00000000 1f:05 2722616 /usr/bin/sysapp
0001a000-0001b000 rw-p0000a000 1f:05 2722616 /usr/bin/sysapp
00a38000-00a39000 rw-p00000000 00:00 0 [heap]
b6cac000-b6caf000r-xp 00000000 1f:04 70 /lib/libdl.so.0
b6caf000-b6cb6000---p 00000000 00:00 0
b6cb6000-b6cb7000rw-p 00002000 1f:04 70 /lib/libdl.so.0
b6cb7000-b6cb8000r-xp 00000000 1f:04 69 /lib/libnsl.so.0
b6cb8000-b6cbf000---p 00000000 00:00 0
b6cbf000-b6cc0000rw-p 00000000 1f:04 69 /lib/libnsl.so.0
b6cc0000-b6d59000 r-xp00000000 1f:04 65 /lib/libc.so.0
b6d59000-b6d60000---p 00000000 00:00 0
b6d60000-b6d62000rw-p 00098000 1f:04 65 /lib/libc.so.0
b6d62000-b6d67000rw-p 00000000 00:00 0
b6d67000-b6d87000r-xp 00000000 1f:04 71 /lib/libgcc_s.so.1
b6d87000-b6d8e000---p 00000000 00:00 0
b6d8e000-b6d8f000rw-p 0001f000 1f:04 71 /lib/libgcc_s.so.1
b6d8f000-b6da0000r-xp 00000000 1f:04 67 /lib/libm.so.0
b6da0000-b6da7000---p 00000000 00:00 0
b6da7000-b6da8000rw-p 00010000 1f:04 67 /lib/libm.so.0
b6da8000-b6e5c000r-xp 00000000 1f:04 79 /lib/libstdc++.so.6
b6e5c000-b6e63000---p 00000000 00:00 0
b6e63000-b6e67000r--p 000b3000 1f:04 79 /lib/libstdc++.so.6
b6e67000-b6e69000rw-p 000b7000 1f:04 79 /lib/libstdc++.so.6
b6e69000-b6e6f000rw-p 00000000 00:00 0
b6e6f000-b6e83000r-xp 00000000 1f:04 74 /lib/libpthread.so.0
b6e83000-b6e8a000---p 00000000 00:00 0
b6e8a000-b6e8b000rw-p 00013000 1f:04 74 /lib/libpthread.so.0
b6e8b000-b6e8d000rw-p 00000000 00:00 0
b6e8d000-b6f03000r-xp 00000000 1f:05 4633696 /usr/lib/liblog4cpp.so.5
b6f03000-b6f0a000---p 00000000 00:00 0
b6f0a000-b6f0d000rw-p 00075000 1f:05 4633696 /usr/lib/liblog4cpp.so.5
b6f0d000-b6f14000r-xp 00000000 1f:04 77 /lib/ld-uClibc.so.0
b6f19000-b6f1b000rw-p 00000000 00:00 0
b6f1b000-b6f1c000rw-p 00006000 1f:04 77 /lib/ld-uClibc.so.0
be912000-be933000rw-p 00000000 00:00 0 [stack]
ffff0000-ffff1000 r-xp00000000 00:00 0 [vectors]
蓝色:代码和数据段
红色:堆
紫色:通过mmap出来的存放共享库文件
绿色:栈
橙色:中断向量表
1、brk系统调用
brk从heap向上简单的线性增长,从上面可以看出start_brk表示当前current task的heap起始地址,brk指向已经分配了的heap。
申请:将申请的地址和start_brk和brk进行比较,如果合法就修改brk指向地址,并返回告之成功
释放:修改brk指向,调用do_munmap进行内存释放。
2、mmap系统调用
mmap是把一个文件或posix共享内存区映射到调用进程的地址空间。三个目的:
A.使用普通文件提供内存映射IO
B.使用特殊文件提供匿名映射IO
C.使用shm_open以提供无亲缘关系的进程间posix共享内存区
其他细节见网上说明
malloc使用brk或mmap都是从currenttask的线性地址空间申请一块虚拟地址,并没有相应的物理内存。当task任务运行使用到上面的虚拟地址时,MMU转化虚拟地址,根据页表查找虚拟地址对应的物理内存,如果不存在就会产生缺页异常。缺页异常的处理大致逻辑如下图:
如果申请到了,就会将这个虚拟地址和物理内存的映射添加到current task的页表,同时刷新cache,下次访问就直接映射。
linux malloc和free解析相关推荐
- linux项目课程设计,LINUX课程设计项目需求解析.doc
LINUX课程设计项目需求解析 1引言 目前大学生就业形势越来越严峻,为了给学生增加就业砝码,学校为每个学生提供一个展示自我的平台,在学校web服务器上开通注册通道,让每个学生都可以拥有自己的个人网站 ...
- Linux Malloc分析-从用户空间到内核空间
Linux Malloc分析-从用户空间到内核空间 本文介绍malloc的实现及其malloc在进行堆扩展操作,并分析了虚拟地址到物理地址是如何实现映射关系. ordeder原创,原文链接: http ...
- jq输出文本_如何用 Linux 命令行工具解析和格式化输出 JSON | Linux 中国
我们将使用 Linux 上的命令行工具解析并格式化打印 JSON.它对于在 shell 脚本中处理大型 JSON 数据或在 shell 脚本中处理 JSON 数据非常有用.-- Ostechnix J ...
- Linux入门之inode解析及管道重定向
Linux入门之inode解析及管道重定向 inode 简介: 当磁盘分区格式化后会根据分区格式.大小等信息来指定分区分配多少个inode表,每个inode表都会有一个在当前分区中唯一的编号,可能有一 ...
- Linux文件权限的解析
Linux文件权限的解析 LINUX当前目录下可以用ls -l 命令来查看当前目录下所有文件夹和文件的权限. 用ls命令得到的权限表示格式类似这样:-rwxr-xr-x 下面解析一下格式所表示的意思. ...
- linux暂时不能域名解析,Kali Linux中暂时不能解析域名
Kali Linux中暂时不能解析域名 环境:kali linux 这个问题是再用apt install命令安装软件时发现的 用ping www.xxxxxxxx.com 再次确认无法解析域名 参考: ...
- linux服务器无法解析域名解决办法,Linux服务器内部无法解析域名
Linux服务器内部无法解析域名 问题现象 Linux 服务器内部无法正常解析域名. 问题原因 可能的原因包括: DNS 设置问题 防火墙策略问题 NSCD 服务问题 处理办法 可以依次进行如下检查: ...
- Linux的DNS正向解析和转发配置
Linux的DNS正向解析和转发配置 DNS是Domain Name System(域名系统)的简称,用来解析域名和ip的对应关系.关于域名的定义以及解析原理大家都知道. DNS搭建非常简单,所需软件 ...
- linux dns无法解析,Linux服务器内部无法解析域名
Linux服务器内部无法解析域名 问题现象 Linux 服务器内部无法正常解析域名. 问题原因 可能的原因包括: DNS 设置问题 防火墙策略问题 NSCD 服务问题 处理办法 可以依次进行如下检查: ...
- window环境下运行linux解压命令,使用压缩的方式将Windows下的zip压缩包上传到Linux系统的方法解析...
我们可以使用在Windows下压缩文件夹,然后到Linux系统下解压缩的方式,完成整个上传工作. 第一步:在Windows系统下,将整个文件夹压缩成zip后缀的压缩包 方法一: 在文件夹xtemp上, ...
最新文章
- mac地址信息查询站点
- 二章: CentOS6.5 连接FTP服务器、部署telnet服务、安装SCP、服务端FTP、SFTP部署
- 位居全国第一- 丰收节交易会·内蒙古:名特优新农产品数量
- 网络编程-之粘包现象
- SpringBoot声明式事务
- GitHub重大更新即将加入免费软件包管理服务;钉钉社区因出现违规内容将停更整改一个月;Uber上市,定价为45美元……...
- 小学计算机教师育人案例,台屯小学青年教师李春秀育人案例
- 手机型号识别 手机PID UID 驱动识别 数据库包
- android h5选择图片上传,js-微信H5选择多张图片预览并上传(兼容ios,安卓,已测试)...
- C#中IPAddress与域名的使用
- pdf阅读器修改背景颜色 护眼色
- 魔兽世界插件开发-暴雪设计工具/命令
- 关系抽取之PCNN(Piece-Wise-CNN)
- 基于 电子海图的海上搜救的研究
- eviews计算covar_第7章 我国商业银行风险溢出效应的度量—基于GARCH-CoVaR模型
- matlab图像频谱分析代码_信号频域分析方法的理解(频谱、能量谱、功率谱、倒频谱、小波分析)...
- 电脑控制android手机神器,scrcpy
- 【基础】PHP变量及变量作用域
- fnd_global和fnd_profile 的区别
- 33幅精美的拿铁图案摄影作品欣赏
热门文章
- Educational Codeforces Round 7
- Spring(十六)之MVC框架
- 关于hbase安装出现的问题
- 浅谈 C# CLR 执行模块
- 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流
- JDK的KeyTool和KeyStore等加密相关
- 个人阅读作业2016.1.10
- ChipScope用法总结
- ASP.NET MVC 3.0(十二): MVC 3.0 使用自定义的Html控件
- 剖析Disruptor:为什么会这么快?(一)Ringbuffer的特别之处