嵌入式里如何给内存做压力测试?不妨试试memtester
大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是內(nèi)存讀寫正確性壓力測試程序memtester。
在嵌入式系統(tǒng)中,內(nèi)存(RAM)的重要性不言而喻,系統(tǒng)性能及穩(wěn)定性都與內(nèi)存息息相關(guān)。關(guān)于內(nèi)存性能有很多個不同指標(biāo),其中最基礎(chǔ)的指標(biāo)便是訪問可靠性(即讀寫的正確性),只有穩(wěn)定可靠的內(nèi)存訪問才能確保系統(tǒng)正常運行。很多時候簡單地內(nèi)存讀寫測試并不能發(fā)現(xiàn)隱藏的問題,因此我們需要一個完備的內(nèi)存訪問壓力測試程序,今天痞子衡就和大家詳細(xì)聊一聊memtester。
一、內(nèi)存性能測試程序集
在講memtester之前,痞子衡先給大家科普一下Linux系統(tǒng)下常用的內(nèi)存性能測試工具,它們分別是mbw、memtester、lmbench、sysbench。這幾個測試工具(程序)各有側(cè)重點:
內(nèi)存帶寬測試工具 --mbw; 內(nèi)存壓力測試工具 --memtester; 內(nèi)存綜合性能測試工具 --lmbench; 內(nèi)存申請與讀寫速度測試工具 --sysbench;二、memtester程序
memtester是Simon Kirby在1999年編寫的測試程序(v1版),后來由Charles Cazabon一直維護(hù)更新(v2及之后版本),主要面向Unix-like系統(tǒng),官方主頁上介紹的是“A userspace utility for testing the memory subsystem for faults.”,其實就是為了測試內(nèi)存(主要DDR)的讀寫訪問可靠性(僅正確性,與速度性能無關(guān)),這是驗證板級硬件設(shè)備必不可少的一項測試。
整個memtester測試的視角就是從用戶的角度來看的,從用戶角度設(shè)立不同的測試場景即測試用例,然后針對性地進(jìn)行功能測試,注意是從系統(tǒng)級來測試,也就是說關(guān)注的不單單是內(nèi)存顆粒了,還有系統(tǒng)板級的連線、IO性能、PCB等等相關(guān)的因素,在這些因素的影響下,內(nèi)存是否還能正常工作。
2.1 獲取程序
memtester程序的最新版本是4.5.0,早期的v1/v2/v3版本目前下載不到了,2012年Charles Cazabon重寫了程序并發(fā)布了全新v4.0.0,此后一直不定期更新,v4.x也是當(dāng)前最流行的版本。
核心程序下載:http://pyropus.ca/software/memtester/
核心程序包下載后,在\memtester-4.5.0\下可找到源代碼。詳細(xì)源文件目錄如下:
\memtester-4.5.0\memtester.h\memtester.c --主程序入口\sizes.h --關(guān)于系統(tǒng)位數(shù)(32/64bit)的一些定義\types.h --所用數(shù)據(jù)類型的定義\tests.h\tests.c --測試算法子程序如果是移植到ARM Cortex-M平臺下裸系統(tǒng)運行,一般只需要簡單修改memtester.c文件即可,其他源文件就是一些頭文件包含方面的改動,memtester本身并沒有太多移植工作,其源碼本是用作在Unix-like系統(tǒng)上運行的,而在嵌入式系統(tǒng)里運行僅需要把一些跟系統(tǒng)平臺相關(guān)的代碼刪除即可,此外就是打印函數(shù)的實現(xiàn)。
2.2 配置參數(shù)
memtester源碼里的配置選項主要是如下五個宏:
/* 如下需用戶自定義 */ ULONG_MAX -- 確定系統(tǒng)是32bit還是64bit TEST_NARROW_WRITES -- 確定是否要包含8/16 bit寫測試/* 如下借助linux頭文件 */ _SC_VERSION -- posix system版本檢查 _SC_PAGE_SIZE -- 內(nèi)存page大小獲取 MAP_LOCKED -- Linux里mmap里的swap特性2.3 程序解析
讓我們嘗試分析memtester主函數(shù)入口main,main()函數(shù)最開始都是一些輸入?yún)?shù)解析,其實主要就是為了獲取三個重要變量:內(nèi)存測試起始地址、內(nèi)存測試總長度、壓力測試循環(huán)次數(shù),有了這三個變量值之后便開始逐一跑tests.c文件里各項測試算法小函數(shù):
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");//?省略若干初始檢查代碼//?從輸入?yún)?shù)里獲取physaddrbase計算出內(nèi)存測試起始地址aligned//?從輸入?yún)?shù)里獲取mem及B|K|M|G計算出內(nèi)存測試總長度bufsizehalflen?=?bufsize?/?2;count?=?halflen?/?sizeof(ul);bufa?=?(ulv?*)?aligned;bufb?=?(ulv?*)?((size_t)?aligned?+?halflen);//?壓力測試的重要變量,?loops即重復(fù)次數(shù)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_addressif?(!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);//?可以看到將內(nèi)存測試總空間一分為二,傳給子程序做處理的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個函數(shù),涉及各種內(nèi)存訪問經(jīng)驗操作,具體可以看網(wǎng)上的一篇詳細(xì)解析文章 https://www.jianshu.com/p/ef203c360c4f。
| test_stuck_address | 先全部把地址值交替取反放入對應(yīng)存儲位置,然后再讀出比較,重復(fù)n次 |
| test_random_value | 等效test_random_comparison(bufa, bufb, count):數(shù)據(jù)敏感型測試用例 |
| 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級的數(shù)填充32bit,然后取出對比,接著重復(fù)256次;也是壓力用例,只是次數(shù)變多了; |
| test_checkerboard_comparison | 把設(shè)定好的幾組Data BackGround,依次寫入,然后讀出比較 |
| test_bitspread_comparison | 還是在32bit里面移動,只是這次移動的不是單單的一個0或者1,而是兩個1,這兩個1之間隔著兩個空位/td> |
| test_bitflip_comparison | 也是32bit里面的一個bit=1不斷移動生成data pattern然后,每個pattern均執(zhí)行 |
| test_walkbits1_comparison | 與test_walkbits0_comparison同理 |
| test_walkbits0_comparison | 就是bit=1的位置在32bit里面移動,每移動一次就全部填滿buffer,先是從低位往高位移,再是從高位往低位移動 |
| test_8bit_wide_random | 以char指針存值,也就是每次存8bit,粒度更細(xì); |
| test_16bit_wide_random | 以unsigned short指針存值,也就是每次存16bit,不同粒度檢測; |
2.4 結(jié)果格式
在Unix-like系統(tǒng)下使用make && make install命令進(jìn)行編譯可得到一個可執(zhí)行的memtester,可以隨便執(zhí)行memtester 10M 1,即申請10M的內(nèi)存測試1次,結(jié)果如下:
[root@as150 ~] memtester 10M 1memtester 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: okRandom Value: okCompare XOR: okCompare SUB: okCompare MUL: okCompare DIV: okCompare OR: okCompare AND: okSequential Increment: okSolid Bits: okBlock Sequential: okCheckerboard: okBit Spread: okBit Flip: okWalking Ones: okWalking Zeroes: ok8-bit Writes: ok16-bit Writes: okDone.至此,內(nèi)存讀寫正確性壓力測試程序memtester痞子衡便介紹完畢了,掌聲在哪里~~~
推薦閱讀:
專輯|Linux文章匯總
專輯|程序人生
專輯|C語言
我的知識小密圈
關(guān)注公眾號,后臺回復(fù)「1024」獲取學(xué)習(xí)資料網(wǎng)盤鏈接。
歡迎點贊,關(guān)注,轉(zhuǎn)發(fā),在看,您的每一次鼓勵,我都將銘記于心~
總結(jié)
以上是生活随笔為你收集整理的嵌入式里如何给内存做压力测试?不妨试试memtester的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 懂一些数据分析工具,为啥还要考CPDA数
- 下一篇: 《显微镜下的大明》马伯庸2019年1月新