大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是内存读写正确性压力测试程序memtester

  在嵌入式系统中,内存(RAM)的重要性不言而喻,系统性能及稳定性都与内存息息相关。关于内存性能有很多个不同指标,其中最基础的指标便是访问可靠性(即读写的正确性),只有稳定可靠的内存访问才能确保系统正常运行。很多时候简单地内存读写测试并不能发现隐藏的问题,因此我们需要一个完备的内存访问压力测试程序,今天痞子衡就和大家详细聊一聊memtester。

一、内存性能测试程序集

  在讲memtester之前,痞子衡先给大家科普一下Linux系统下常用的内存性能测试工具,它们分别是mbw、memtester、lmbench、sysbench。这几个测试工具(程序)各有侧重点:

内存带宽测试工具            --mbw;
内存压力测试工具 --memtester;
内存综合性能测试工具 --lmbench;
内存申请与读写速度测试工具 --sysbench;

二、memtester程序

  memtester是Simon Kirby在1999年编写的测试程序(v1版),后来由Charles Cazabon一直维护更新(v2及之后版本),主要面向Unix-like系统,官方主页上介绍的是“A userspace utility for testing the memory subsystem for faults.”,其实就是为了测试内存(主要DDR)的读写访问可靠性(仅正确性,与速度性能无关),这是验证板级硬件设备必不可少的一项测试。

  整个memtester测试的视角就是从用户的角度来看的,从用户角度设立不同的测试场景即测试用例,然后针对性地进行功能测试,注意是从系统级来测试,也就是说关注的不单单是内存颗粒了,还有系统板级的连线、IO性能、PCB等等相关的因素,在这些因素的影响下,内存是否还能正常工作。

2.1 获取程序

  memtester程序的最新版本是4.5.0,早期的v1/v2/v3版本目前下载不到了,2012年Charles Cazabon重写了程序并发布了全新v4.0.0,此后一直不定期更新,v4.x也是当前最流行的版本。

核心程序下载: http://pyropus.ca/software/memtester/

  核心程序包下载后,在\memtester-4.5.0\下可找到源代码。详细源文件目录如下:

\memtester-4.5.0
\memtester.h
\memtester.c --主程序入口
\sizes.h --关于系统位数(32/64bit)的一些定义
\types.h --所用数据类型的定义
\tests.h
\tests.c --测试算法子程序

  如果是移植到ARM Cortex-M平台下裸系统运行,一般只需要简单修改memtester.c文件即可,其他源文件就是一些头文件包含方面的改动,memtester本身并没有太多移植工作,其源码本是用作在Unix-like系统上运行的,而在嵌入式系统里运行仅需要把一些跟系统平台相关的代码删除即可,此外就是打印函数的实现。

2.2 配置参数

  memtester源码里的配置选项主要是如下五个宏:

/* 如下需用户自定义 */
ULONG_MAX -- 确定系统是32bit还是64bit
TEST_NARROW_WRITES -- 确定是否要包含8/16 bit写测试 /* 如下借助linux头文件 */
_SC_VERSION -- posix system版本检查
_SC_PAGE_SIZE -- 内存page大小获取
MAP_LOCKED -- Linux里mmap里的swap特性

2.3 程序解析

  让我们尝试分析memtester主函数入口main,main()函数最开始都是一些输入参数解析,其实主要就是为了获取三个重要变量:内存测试起始地址、内存测试总长度、压力测试循环次数,有了这三个变量值之后便开始逐一跑tests.c文件里各项测试算法小函数:

struct test {
char *name;
int (*fp)();
}; struct test tests[] = {
{ "Random Value", test_random_value },
{ "Compare XOR", test_xor_comparison },
{ "Compare SUB", test_sub_comparison },
{ "Compare MUL", test_mul_comparison },
{ "Compare DIV",test_div_comparison },
{ "Compare OR", test_or_comparison },
{ "Compare AND", test_and_comparison },
{ "Sequential Increment", test_seqinc_comparison },
{ "Solid Bits", test_solidbits_comparison },
{ "Block Sequential", test_blockseq_comparison },
{ "Checkerboard", test_checkerboard_comparison },
{ "Bit Spread", test_bitspread_comparison },
{ "Bit Flip", test_bitflip_comparison },
{ "Walking Ones", test_walkbits1_comparison },
{ "Walking Zeroes", test_walkbits0_comparison },
#ifdef TEST_NARROW_WRITES
{ "8-bit Writes", test_8bit_wide_random },
{ "16-bit Writes", test_16bit_wide_random },
#endif
{ NULL, NULL }
}; /* Function definitions */
void usage(char *me) {
fprintf(stderr, "\n"
"Usage: %s [-p physaddrbase [-d device]] <mem>[B|K|M|G] [loops]\n",
me);
exit(EXIT_FAIL_NONSTARTER);
} int main(int argc, char **argv) {
ul loops, loop, i;
size_t bufsize, halflen, count;
void volatile *buf, *aligned;
ulv *bufa, *bufb;
ul testmask = 0; // 省略若干变量定义代码 printf("memtester version " __version__ " (%d-bit)\n", UL_LEN);
printf("Copyright (C) 2001-2020 Charles Cazabon.\n");
printf("Licensed under the GNU General Public License version 2 (only).\n");
printf("\n"); // 省略若干初始检查代码
// 从输入参数里获取physaddrbase计算出内存测试起始地址aligned
// 从输入参数里获取mem及B|K|M|G计算出内存测试总长度bufsize halflen = bufsize / 2;
count = halflen / sizeof(ul);
bufa = (ulv *) aligned;
bufb = (ulv *) ((size_t) aligned + halflen); // 压力测试的重要变量, loops即重复次数
for(loop=1; ((!loops) || loop <= loops); loop++) {
printf("Loop %lu", loop);
if (loops) {
printf("/%lu", loops);
}
printf(":\n");
printf(" %-20s: ", "Stuck Address");
fflush(stdout); // 第一个测试 stuck_address
if (!test_stuck_address(aligned, bufsize / sizeof(ul))) {
printf("ok\n");
} else {
exit_code |= EXIT_FAIL_ADDRESSLINES;
} // 遍历tests.c里的所有测试子程序
for (i=0;;i++) {
if (!tests[i].name) break;
if (testmask && (!((1 << i) & testmask))) {
continue;
}
printf(" %-20s: ", tests[i].name);
// 可以看到将内存测试总空间一分为二,传给子程序做处理的
if (!tests[i].fp(bufa, bufb, count)) {
printf("ok\n");
} else {
exit_code |= EXIT_FAIL_OTHERTEST;
}
fflush(stdout);
/* clear buffer */
memset((void *) buf, 255, wantbytes);
}
printf("\n");
fflush(stdout);
}
}

  tests.c文件里才是最核心的压力测试算法子程序,一共17个函数,涉及各种内存访问经验操作,具体可以看网上的一篇详细解析文章 https://www.jianshu.com/p/ef203c360c4f。

测试函数名 测试作用
test_stuck_address 先全部把地址值交替取反放入对应存储位置,然后再读出比较,重复n次
test_random_value 等效test_random_comparison(bufa, bufb, count):数据敏感型测试用例
test_xor_comparison 与test_random_value比多了个异或操作
test_sub_comparison 与test_random_value比多了个减法操作
test_mul_comparisone 与test_random_value比多了个乘法操作
test_div_comparison 与test_random_value比多了个除法操作
test_or_comparison 在test_random_comparison()里面合并了
test_and_comparison 在test_random_comparison()里面合并了
test_seqinc_comparison 是 test_blockseq_comparison的一个子集;模拟客户压力测试场景
test_solidbits_comparison 固定全1后写入两个buffer,然后读出比较,然后全0写入读出比较;这就是Zero-One算法
test_blockseq_comparison 一次写一个count大小的块,写的值是拿byte级的数填充32bit,然后取出对比,接着重复256次;也是压力用例,只是次数变多了;
test_checkerboard_comparison 把设定好的几组Data BackGround,依次写入,然后读出比较
test_bitspread_comparison 还是在32bit里面移动,只是这次移动的不是单单的一个0或者1,而是两个1,这两个1之间隔着两个空位/td>
test_bitflip_comparison 也是32bit里面的一个bit=1不断移动生成data pattern然后,每个pattern均执行
test_walkbits1_comparison 与test_walkbits0_comparison同理
test_walkbits0_comparison 就是bit=1的位置在32bit里面移动,每移动一次就全部填满buffer,先是从低位往高位移,再是从高位往低位移动
test_8bit_wide_random 以char指针存值,也就是每次存8bit,粒度更细;
test_16bit_wide_random 以unsigned short指针存值,也就是每次存16bit,不同粒度检测;

2.4 结果格式

  在Unix-like系统下使用make && make install命令进行编译可得到一个可执行的memtester,可以随便执行memtester 10M 1,即申请10M的内存测试1次,结果如下:

[root@as150 ~] memtester 10M 1

memtester version 4.5.0 (64-bit)
Copyright (C) 2001-2020 Charles Cazabon.
Licensed under the GNU General Public License version 2 (only). pagesize is 4096
pagesizemask is 0xfffffffffffff000
want 10MB (10485760 bytes)
got 10MB (10485760 bytes), trying mlock ...locked.
Loop 1/1:
Stuck Address: ok
Random Value: ok
Compare XOR: ok
Compare SUB: ok
Compare MUL: ok
Compare DIV: ok
Compare OR: ok
Compare AND: ok
Sequential Increment: ok
Solid Bits: ok
Block Sequential: ok
Checkerboard: ok
Bit Spread: ok
Bit Flip: ok
Walking Ones: ok
Walking Zeroes: ok
8-bit Writes: ok
16-bit Writes: ok Done.

  至此,内存读写正确性压力测试程序memtester痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到我的 博客园主页CSDN主页知乎主页微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:内存读写正确性压力测试程序(memtester)的更多相关文章

  1. 痞子衡嵌入式:i.MXRT1010, 1170型号上不一样的SNVS GPR寄存器读写控制设计

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1010, 1170型号上不一样的SNVS GPR寄存器读写控制设计. 痞子衡之前两篇文章 <在SBL项目实战中妙用i ...

  2. 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(6)- Bootable image格式与加载(elftosb/.bd)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Bootable image格式与加载过程. 在i.MXRT启动系列第三篇文章 Serial Down ...

  3. 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(8)- 从Raw NAND启动

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Raw NAND启动. 前面铺垫了七篇启动系列文章,终于该讲具体Boot Device了,我们知道i. ...

  4. 痞子衡嵌入式:并行接口NAND标准(ONFI)及SLC Raw NAND简介

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是ONFI标准及SLC Raw NAND. NAND Flash是嵌入式世界里常见的存储器,对于嵌入式开发而言,NAND主要分为两大类:S ...

  5. 痞子衡嵌入式:通用NOR接口标准(CFI-JESD68)及SLC Parallel NOR简介

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是CFI标准及SLC Parallel NOR. NOR Flash是嵌入式世界里最常见的存储器,常常内嵌在微控制器里(Parallel型 ...

  6. 痞子衡嵌入式:串行EEPROM接口事实标准及SPI EEPROM简介

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是EEPROM接口标准及SPI EEPROM. 痞子衡之前写过一篇文章 <SLC Parallel NOR简介>,介绍过并行N ...

  7. 痞子衡嵌入式:简析i.MXRT1170 Cortex-M7 FlexRAM ECC功能特点、开启步骤、性能影响

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M7内核的FlexRAM ECC功能. ECC是"Error Correcting ...

  8. 痞子衡嵌入式:简析i.MXRT1170 Cortex-M4 L-MEM ECC功能特点、开启步骤、性能影响

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M4内核的L-MEM ECC功能. 本篇是 <简析i.MXRT1170 Cortex-M ...

  9. 痞子衡嵌入式:JLink Script文件基础及其在IAR下调用方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是JLink Script文件基础及其在IAR下调用方法. JLink可以说是MCU开发者最熟悉的调试工具了,相比于其他调试器(比如DAP ...

  10. 痞子衡嵌入式:串行NAND Flash的两大特性导致其在i.MXRT FlexSPI下无法XiP

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是串行NAND Flash的两大特性导致其在i.MXRT FlexSPI下无法XiP. 在嵌入式世界里,当我们提起XiP设备(支持代码原地 ...

随机推荐

  1. kiaba启动报 FATAL ResponseError: access_control_exception,ES报:java.lang.SecurityException: access denied ("java.io.FilePermission"“文件地址”)

    查了许多博客,找的头都大了还是没有发现问题的根源,之前以为是插件包文件名改了之后还是一样,当我差点放弃的时候 一位博主的瞬间把我点醒https://www.cnblogs.com/personblog ...

  2. mybatis plus 分页总查出来全部数据

    需要配置过滤器 package com.tyyy.example.coreurl.config; import com.baomidou.mybatisplus.annotation.DbType; ...

  3. PostScript语言教程(六、图形变换)

    6.1.坐标系变换 POSTSCRIPT图形操作是在一个坐标系中,这个坐标系被称为用户坐标系或用户空间,该坐标系独立于任何物理设备.POSTSCRIPT在用户空间中进行绘制,并将结果传输到特定的打印机 ...

  4. 「SOL」射命丸文的笔记 (洛谷)

    讲题人:"这是一个很经典的模型,大家应该都会" 我:"???" # 题面 给出 \(m\),求所有 \(m\) 个点的有标号强联通竞赛图的哈密顿回路数量的平均数 ...

  5. npm config get prefer-offline

    npm config get prefer-offline

  6. oracle 存过调试 stepinto stepover stepout

    step into:单步执行,遇到子函数就进入并且继续单步执行(简而言之,进入子函数): step over:在单步执行时,在函数内遇到子函数时不会进入子函数内单步执行,而是将子函数整个执行完再停止, ...

  7. Educational Codeforces Round 1 个人总结A-E

    Educational Codeforces Round 1 A. Tricky Sum 数学,求\(1 \dots n\)的和减去 小于等于n的二次幂乘2之和 LL f[40]; void solv ...

  8. Think in UML 其二

    UML基本元素 参与者 1.参与者位于系统边界之外. 思考参与者究竟是谁时,以下两个问题有助于了解 ·谁对系统有着明确的目标和要求并且主动发出动作? ·系统是为谁服务的? 2.参与者可以非人 功能性需 ...

  9. Java流程控制1

    Scanner对象 java.util.Scanner 通过Scanner类来获取用户输入 next()和nextline()来获取输入的字符串,读取前我们一般需要使用hasnext()和hasnex ...

  10. 2020.3.9 ~ 2020.3.15 ACM训练周总结

    一.本周ACM学习相关内容 学习了dfs和bfs -- 4小时 课上系统的学习了vector等stl函数的使用即注意事项-3小时 二.题数与耗时 师哥安排了12道题,做了五道(不包含比赛题):大概4个 ...