牛骨文教育服务平台(让学习变的简单)

     今天我们继续redis源码test测试包下的其他文件,今天看完的是memtest文件,翻译器起来,就是memory test 内存检测的意思,这个文件虽然说代码量不是很多,但是里面的提及了很多东西,也给我涨了很多见识,网上关于memtest这种类似的redis内部边缘的文件解析基本没有,所以自己从头开始学习。机器的内存检测会和机器的CPU位数有关,32位或64位会影响后面的一些宏定义参数。首先亮出memtest中的API:

/* 内存检测API */
void memtest_progress_start(char *title, int pass) /* 内存检测加载开始,输出开始的一些图线显示 */
void memtest_progress_end(void) /* progress bar加载完再次清屏操作 */
void memtest_progress_step(size_t curr, size_t size, char c) /* progress填充自己设置的字符串 */
void memtest_addressing(unsigned long *l, size_t bytes) /* 地址检测方法 */
void memtest_fill_random(unsigned long *l, size_t bytes) /* 随机填充内存 */
void memtest_fill_value(unsigned long *l, size_t bytes, unsigned long v1, unsigned long v2, char sym) /* 像上面的方法,只不过这是特定2种值的填充v1,v2 */
void memtest_compare(unsigned long *l, size_t bytes) /* 内存比较方法 */
void memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times) /* 进行多次内存compare比较操作 */
void memtest_test(size_t megabytes, int passes) /* 整个内存检测类操作的测试方法,passes为目标的循环数 */
void memtest_non_destructive_invert(void *addr, size_t size) /* 将内存地址,进行了按位取反操作,不具有对数据的破坏性 */
void memtest_non_destructive_swap(void *addr, size_t size) /* 将内存地址,2个,2个内部之间做交换,同样不对数据具有破坏性 */
void memtest(size_t megabytes, int passes) /* 开发给整个系统使用的内存检测方法 */

里面主要的方法就几个,每当一个内存检测的开始,都会出现progress bar的图线输出:

/* 内存检测加载开始,输出开始的一些图线显示 */
void memtest_progress_start(char *title, int pass) {
    int j;
	
	/*这里其实包含2个命令,"xlb[H","xlb[2j",后面的命令是主要的操作
	 *"x1b" 是ESC的16进制ASCII码值,这里也可经表示成八进制的33,
	 *[是一个CSI(Control sequence introducer),转义序列的作用由最后一个字符决定的,
	 *这里J表示删除,默认情况下它删除从当前光标处到行尾的内容,
	 *这里的2为参数,它表示删除所有的显示内容。也可以使用printf "x1b[2J"。*/
	//现定位home最开始的位置,然后实现请屏幕操作
    printf("x1b[Hx1b[2J");    /* Cursor home, clear screen. */
    	
    /* Fill with dots. */
    /* 输出.符号填充屏幕 */
    for (j = 0; j < ws.ws_col*(ws.ws_row-2); j++) printf(".");
    printf("Please keep the test running several minutes per GB of memory.
");
    printf("Also check http://www.memtest86.com/ and http://pyropus.ca/software/memtester/");
    //最后一个参数变了,为K,意义也不一样了变成删除当前行操作
    printf("x1b[Hx1b[2K");          /* Cursor home, clear current line.  */
    printf("%s [%d]
", title, pass); /* Print title. */
    progress_printed = 0;
    //求出填满progress bar所需点的个数
    progress_full = ws.ws_col*(ws.ws_row-3);
    fflush(stdout);
}

我在里面解释了一下清屏操作的,printf输出形式。内存地址有效性的核心方法;

/* Test that addressing is fine. Every location is populated with its own
 * address, and finally verified. This test is very fast but may detect
 * ASAP big issues with the memory subsystem. */
/* 此方法是测试内存地址是否有效,此种检测的速度是非常快的,但可能会检测出ASAP的巨大问题 */
/* ASAP网上查了下:(可能为)Automated Statistical Analysis Programme 自动统计分析程序 */
void memtest_addressing(unsigned long *l, size_t bytes) {
	//算出地址的长度
    unsigned long words = bytes/sizeof(unsigned long);
    unsigned long j, *p;

    /* Fill */
    p = l;
    for (j = 0; j < words; j++) {
    	//将(unsigned long)p强制类型转换到此时的*p,后面以此来判断,没有转换成功,说明存在内存地址的问题
        *p = (unsigned long)p;
        p++;
        //用A字符填充部分progress bar
        if ((j & 0xffff) == 0) memtest_progress_step(j,words*2,"A");
    }
    /* Test */
    p = l;
    for (j = 0; j < words; j++) {
    	//比较Address的关键在于
        if (*p != (unsigned long)p) {
            printf("
***MEMORY ADDRESSING ERROR: %p contains %lu
",
                (void*) p, *p);
            exit(1);
        }
        p++;
        if ((j & 0xffff) == 0) memtest_progress_step(j+words,words*2,"A");
    }
}

为什么这样的方法去检测,本人也是带着比较疑惑的感觉。在内存检测中,内存地址有效性的检查只是其中的一个方法,还有一个是内存地址的填充测试,分为随机填充,和给定值的填充。下面给出随机填充的方法实现:

/* Fill words stepping a single page at every write, so we continue to
 * touch all the pages in the smallest amount of time reducing the
 * effectiveness of caches, and making it hard for the OS to transfer
 * pages on the swap. */
/* 在每次写操作的时候,在单页上填满整个字符,这样可以做到最快速的触及所有的页面 */
/* 减少了低效率的缓存使用,但是会让分区在转移页面时会比较困难 */
/* 随机填充内存 */
void memtest_fill_random(unsigned long *l, size_t bytes) {
    unsigned long step = 4096/sizeof(unsigned long);
    unsigned long words = bytes/sizeof(unsigned long)/2;
    unsigned long iwords = words/step;  /* words per iteration */
    unsigned long off, w, *l1, *l2;

    assert((bytes & 4095) == 0);
    for (off = 0; off < step; off++) {
        l1 = l+off;
        l2 = l1+words;
        for (w = 0; w < iwords; w++) {
        	//下面的rand()达到了随机存储的目的
#ifdef MEMTEST_32BIT
            *l1 = *l2 = ((unsigned long)     (rand()&0xffff)) |
                        (((unsigned long)    (rand()&0xffff)) << 16);
#else
            *l1 = *l2 = ((unsigned long)     (rand()&0xffff)) |
                        (((unsigned long)    (rand()&0xffff)) << 16) |
                        (((unsigned long)    (rand()&0xffff)) << 32) |
                        (((unsigned long)    (rand()&0xffff)) << 48);
#endif
            l1 += step;
            l2 += step;
            if ((w & 0xffff) == 0)
                memtest_progress_step(w+iwords*off,words,"R");
        }
    }
}

填充内存的好处是访问页面速度更快,不会出现断断续续的内存片,但是做页面交换的时候,由于没有空间,效率恐怕会比较低下。给定数值的填充与上面的类似,不展开说了。那么,内存测试程序到底是测哪些东西的呢,也就是说,他这个文件开放给外部的一个最直接的test方法是什么呢?

/* 整个内存检测类操作的测试方法,passes为目标的循环数 */
void memtest_test(size_t megabytes, int passes) {
    size_t bytes = megabytes*1024*1024;
    unsigned long *m = malloc(bytes);
    int pass = 0;

    if (m == NULL) {
        fprintf(stderr,"Unable to allocate %zu megabytes: %s",
            megabytes, strerror(errno));
        exit(1);
    }
    
    //必须经过passes论循环测试
    while (pass != passes) {
        pass++;

		//地址检测
        memtest_progress_start("Addressing test",pass);
        memtest_addressing(m,bytes);
        memtest_progress_end();

		//随机填充检测
        memtest_progress_start("Random fill",pass);
        memtest_fill_random(m,bytes);
        memtest_progress_end();
        //填充后比较四次
        memtest_compare_times(m,bytes,pass,4);

		//给定数值填充,这里称为Solid fill固态填充
        memtest_progress_start("Solid fill",pass);
        memtest_fill_value(m,bytes,0,(unsigned long)-1,"S");
        memtest_progress_end();
        //填充后比较四次
        memtest_compare_times(m,bytes,pass,4);
		
		//也是属于给定数值填充,这里叫Checkerboard fill键盘填充
        memtest_progress_start("Checkerboard fill",pass);
        memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,"C");
        memtest_progress_end();
        //填充后比较四次
        memtest_compare_times(m,bytes,pass,4);
    }
    free(m);
}

可以看见,分为4中情况,1内存地址测试,2,3,4都为填充测试,分为3种类型的填充Random fill, Solid fill, Checkboard fill 。填充之后再做内存比较,这里的内存比较是在内存内部做前半部分的内存和后半部分的内存比较操作。在memtest文件的最后,提到了一个这样的方法:

/* 开发给整个系统使用的内存检测方法 */
void memtest(size_t megabytes, int passes) {
    if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
        ws.ws_col = 80;
        ws.ws_row = 20;
    }
    memtest_test(megabytes,passes);
    printf("
Your memory passed this test.
");
    printf("Please if you are still in doubt use the following two tools:
");
    printf("1) memtest86: http://www.memtest86.com/
");
    printf("2) memtester: http://pyropus.ca/software/memtester/
");
    exit(0);
}

这里提到了memtester和memtest86,上网查了一下,大体如下:

    • memtest和memtest86是2款内存检测软件 
  • memtest不但可以彻底的检测出内存的稳定度,还可同时测试记忆的储存与检索数据的能力,让你可以确实掌控到目前你机器上正在使用的内存到底可不可信赖。 
  • MemTest是一个绿色软件,直接点击执行文件即可运行 
  • memtest86这是一款小巧而专业的内存测试程序,是在著名的内存测试软件Memtest86基础上开发的。 
  • Memtest86+的安装和使用和其它内存测试软件有些不同,因为他不能在Windows下运行。
  • 不过还是有四种方式可以运行此程序,分别为ISO引导盘、Linux下使用的bin文件、
  • USB启动盘使用的EXE文件和软盘引导制作包。由于Memtest86+测试耗时较长,因此它不仅可以用于内存测试,还可以用于系统稳定性测试。Memtest86+测试完毕后,
  • 按下“Esc”键退出并重新启动系统。