mmap 测试的一些坑
最近遇到一个mmap的问题,然后为了测试该问题,写了如下测试代码:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{
char *addr;
int fd;
struct stat sb;
off_t offset, pa_offset;
size_t length;
off_t file_len;
ssize_t s;
int iRet;
if (argc < 3 || argc > 4) {
fprintf(stderr, "%s file offset [length]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDWR);
if (fd == -1)
handle_error("open");
#if 0
file_len = lseek(fd, 400*1024*1024, SEEK_CUR);------------lseek和ftruncate,truncate都可以达到修改文件可映射大小的结果,不过lseek可以在readonly的情况下修改,而truncate不行。
#endif
offset = 400*1024*1024;
iRet = ftruncate(fd,offset);
if (0 != iRet)
{
close(fd);
printf("ftruncate in OpenShem fail\n");
return 0;
}
if (fstat(fd, &sb) == -1) /* To obtain file size */
handle_error("fstat");
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
/* offset for mmap() must be page aligned */
if (offset >= sb.st_size) {
fprintf(stderr, "offset is past end of file\n");
exit(EXIT_FAILURE);
}
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size)
length = sb.st_size - offset;
/* Can't display bytes past end of file */
} else { /* No length arg ==> display to end of file */
length = sb.st_size - offset;
}
addr = mmap(NULL, length + offset - pa_offset, PROT_READ,MAP_PRIVATE, fd, pa_offset);---------map调用,进行映射,注意此处采用的是MAP_PRIVATE
if (addr == MAP_FAILED)
handle_error("mmap");
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);-------------------第一次读取该map地址
if (s != length) {
if (s == -1)
handle_error("write");
fprintf(stderr, "partial write");
exit(EXIT_FAILURE);
}
/*second excute*/
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);------------------第二次读取该map地址
if (s != length) {
if (s == -1)
handle_error("write");
fprintf(stderr, "partial write");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
测试发现,当mmap调用前,内存占用如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
24441 root 20 0 4156 356 264 t 0.0 0.0 0:00.00 map.o
调用mmap之后,内存占用如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
24441 root 20 0 394784 356 264 t 0.0 0.0 0:00.00 map.o
很明显,虚拟内存增长了,但res和shr并没有增长。
然后调用到s = write(STDOUT_FILENO, addr + offset - pa_offset, length),第一次的时候,内存占用如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
24441 root 20 0 394784 391072 390968 t 0.0 1.6 0:02.35 map.o
发现res和shr也增加了,但是res-shr的值,并没有显著增长。
第二次调用s = write(STDOUT_FILENO, addr + offset - pa_offset, length),内存占用不变,符合预期。
代码中关于几个关键函数的理解如下:
关于文件大小的修改部分--------------------
lseek和ftruncate,truncate都可以达到修改文件可映射大小的结果,注意是修改可映射大小,不是文件实际大小。
事实上,lseek不改变文件实际大小,而truncate 以及 ftruncate是会改变文件实际大小的。所谓的实际大小,是指通过fstat等接口获取的文件size,不是占用的block的大小。
lseek可以在readonly的情况下修改,而truncate不行。
truncate 在缩小了文件之后,如果原来seek的位置小于缩小之后的文件大小,则保持不变,如果大于,则位于文件尾,如果truncate 放大了文件,则seek位置不变。
关于mmap的参数部分--------------------
从MAP_PRIVATE的理解来看,因为使用的是copy-on-write mapping,那么在write之后,rss才随着virt增长是正常的。
不管是使用MAP_PRIVATE还是使用MAP_SHARED,当关闭fd之后,都不受影响,这个一般新手一开始都认为需要保持fd打开,其实不需要,fd也就是只是用来临时用一下,MAP_ANONYMOUS的时候,fd甚至都会忽略。比如fork调用的时候,实际参数就是MAP_ANONYMOUS | MAP_SHARED,不仅如此,甚至也可以使用unlink来删除该文件,也不影响其他已经map的进程来进行通信,因为unlink减少的文件引用记数,在内核中该文件还是存在的,不过显示为del状态。map的时候,内核的函数中,已经通过mmap_region函数,通过atomic_inc(&inode->i_writecount),增加了引用计数。
另外,测试结果表明,当使用MAP_PRIVATE的时候,map所写的内容不会影响到其他进程,也就是说你cat 对应的文件,也是看不到修改的内容的。但是如果map同样的
一个fd,然后A进程使用MAP_SHARED标志,B进程使用MAP_PRIVATE标志,则A修改的内容,可以在B进程中体现。而B修改的内容,在A中不可见。不但不可见,根据copy-on-write的原则,
之后A和B也不能通过该共享内存来通信了,因为这个时候已经指向两个不同的segment了。当然这个只是测试,有兴趣的同学可以测试一下,一般不会有谁这么变态对于同一个fd,一个采用
MAP_PRIVATE,一个采用MAP_SHARED,这个也是在帮别人查问题时候发现的,而且程序运行很多年是ok的原因是因为,private这一侧,从来都是读,不去写,没有触发写时复制。
要注意的是,mmap的返回值,在多个进程中,有可能是一样的,但是这个是不能充分说明map的内存是共用一个内核的页表项,因为这个只是该进程的虚拟地址而已。举个栗子,比如三个进程,A,B进程map完之后,得到的地址都是一样的,比如A使用MAP_SHARED标志,B使用MAP_PRIVATE标志映射同一个文件,
然后A使用unlink来删除这个文件,由于文件已经删除了,对应的inode号不一样了,A和B照样能够通过共享内存通信,然后C使用B一样的代码执行一份,得到的mmap返回值的地址也是和A,B一样的,但是C真正map的地址,和A以及B的mmap的内存,不是同一块内存,重要的事情说三遍,不是同一块内存。
mmap 测试的一些坑的更多相关文章
- 『动善时』JMeter基础 — 59、进行JMeter分布式测试遇到的坑
目录 1.控制机端 (1)执行机没有关闭防火墙 (2)内存溢出 2.执行机端 (1)启动jmeter-server服务情况一 (2)启动jmeter-server服务情况二 (3)启动jmeter-s ...
- 一个Monkey测试的小坑
环境:Genymotion模拟器+Custome Phone-6.0.0,API 23 操作步骤如下: cd data/app ls //为了获取待测apk的包名 获取结果如下: 执行命令,其中包名使 ...
- 说说初用 Mock 工具测试碰到的坑
我是一个在校实习生,作为一个程序猿,是个菜鸟中战斗机!对于测试,只写过一点点简单到不能再简单了的 Junit 单元测试的例子(因为当时这足以应付学校课程的内容与要求).这几天在公司里要真枪实弹做测试的 ...
- Jmeter分布式测试的各种坑之jmeter-server修改ip
第一坑:启动压力机的时候,直接./jmeter-server,会报如下错误 错误原因:127.0.0.1是本机, 一个回路地址, 没有指定地址 正确的启动方式:启动命令加一个参数, IP地址写压力机对 ...
- selenium IDE测试中的坑
selenium IDE工具是firefox自带的一个网页自动化测试工具,因为它是IDE所以它很方便使用,但也因为它是IDE所以它有那么些坑. 问题:selenium回放中timeout问题 网页的打 ...
- Android定位测试(深坑)
问题:我们是一个海外app,市场部去马来西亚打开那边的市场,发现了一个问题,就是我们的app定位有问题,还是成都的定位,主要原因是在马来西亚使用这个app,请求中带的经纬度参数是成都的,导致服务器返回 ...
- APP测试的那些坑
在记录app测试走过的那些坑之前,先总结下app测试的工作主要有哪些: 1.功能测试,无论是什么软件产品,必不可少的就是功能测试.我们需要测试这款app产品的功能是否完善,是否符合客户需求,是否符 ...
- 从零开始学AB测试:躲坑篇
AB测试的原理很简单,只用到了最简单的统计假设检验,但表面的简单通常都隐藏着陷阱,这一点没有经过实践的摸爬滚打是不容易看到的,今天我就把前人已经踩过的坑,一共15个,给大家分享一下.在分享之前,大家脑 ...
- 一次单体测试的采坑--MatcherAssert.assertThat---org.hamcrest 和org.mockito
单体测试测试环境ci上报这个错, 本地没问题. org.hamcrest.Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Descri ...
随机推荐
- [js高手之路] es6系列教程 - Map详解以及常用api
ECMAScript 6中的Map类型是一种存储着许多键值对的有序列表.键值对支持所有的数据类型. 键 0 和 ‘0’会被当做两个不同的键,不会发生强制类型转换. 如何使用Map? let map = ...
- Pycharm使用总结
1.代码整体向右移动 按住Win+TAB可以快速向右缩进一个tab 的距离,按住Shift + TAB反方向前进一个TAB距离 2.Model加入get,set 方法 在编辑框中右击,选择genera ...
- Wing IDE设置(自动补全&注释快捷键)
自动补全(默认使用Tab键自动补全) 修改方法:edit -> preferences -> Editor -> Auto-completion 把右边keys的地方,从tab改成 ...
- JQueryPagination分页插件,ajax从struts请求数据
2017-07-16 学完了struts,做了个关于分页的小例子,用到了JQuery分页插件Pagination,先贴下插件下载地址 http://www.jq22.com/jquery-info13 ...
- Centos7下配置Python3和Python2共存,以及对应版本Ipython安装配置
1.查看是否已经安装Python Centos7默认安装了python2.7.5 因为一些命令要用它比如yum 它使用的是python2.7.5. 使用python -V命令查看一下是否安装Pytho ...
- inline-block元素间留白现象探究
现象说明 最近在项目发布的时候遇到了一个奇怪的问题,在项目使用gulp打包压缩后发现之前一些行内元素间的空白消失了,导致页面中一些布局出现了问题 正常样式如下: 最开始出现这个问题的时候以为是g ...
- vim保存时提示: 无法打开并写入文件
命名内容已经写入,但是不知怎的就是没法保存,估计是权限不足的问题. 切换到root用户,进行了同样的操作,发现没有该问题了. 经验教训:编辑配置文件时,记得切换到root用户进行编辑.
- 《跟我学IDEA》六、插件(编码利器)
idea的另一个可爱之处,就是它的强大的插件,下面我以CodeGlance插件为例,这个可以快速定位代码. 第一节:安装插件 ● All plugins 显示所有插件. ● Enabled 显示当前所 ...
- Linux下zeromq.js安装
本文章主要阐述在离线环境下安装zeromq.js的方法和步骤.zeromq.js下载地址: https://www.npmjs.com/package/zeromq或者 https://github. ...
- CSS的box-sizing属性
box-sizing属性可以为三个值之一:content-box(default),border-box,padding-box. content-box,border和padding不计算入widt ...