嵌入式Linux磁盘(硬盘、SD卡)读写性能测试

前言

    SD卡的读写速度,受限制于SD卡本身固有的性能之外,还跟读卡器,主CPU性能等有一定的关系。所以,同一张SD卡,想要知道其在嵌入式设备中实际的读写性能,还需要在嵌入式环境中实际测试下才准确,linux下命令dd使用指定的输入和输出块大小来拷贝文件,它每次从输入读取指定大小的一个块写到独立的输出块去,通过这种方法来测试读写速度。

测试环境

硬件:嵌入式ARM

系统:Linux version 3.10.0

内存:512M


一、内核缓存

    在开始测试之前,必须先弄明白“内核缓存”的概念,否则很容易测出错误的数据。向文件中写入数据时,数据会先缓存在Page Cache中,内存中的这部分数据被标注为Dirty Page,linux系统上的pdflush守护进程会跟进系统设置将将这部分Dirty Page刷到磁盘上,也可以通过fsync系统调用在数据写入后强制刷到磁盘上。将写入的数据刷入磁盘时,是以Buffer Cache为单位,每次回写若干个Buffer Cache。

    读取文件内容时,系统会一次性连续读取包括所请求页面在内的多个页面(如预读页面个数为n)。如果请求的页面在page cache中命中的话,会从缓存中返回页面内容,增加读取的页面数量,异步读取2n个页面;如果请求的页面没有在page cache中命中,也会增加读取页面数量,同步读取2n个页面。

为了避免内核缓存对测试带来影响,在每条测试语句之前,都要先清除linux内核缓存,命令如下:

# sync; echo 3 > /proc/sys/vm/drop_caches

二、测试磁盘写能力

1、正确的命令

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=1M count=500 if=/dev/zero of=/mnt/sdcard/outfile 
500+0 records in
500+0 records out
524288000 bytes (500.0MB) copied, 122.160615 seconds, 4.1MB/s


# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=20k count=25600 if=/dev/zero of=/mnt/sdcard/outfile
25600+0 records in
25600+0 records out
524288000 bytes (500.0MB) copied, 123.208135 seconds, 4.1MB/s

因为/dev/zero是一个伪设备,它只产生空字符流,对它不会产生IO,所以,IO都会集中在of文件中,of文件只用于写,所以这个命令相当于测试磁盘的写能力。

以上dd命令结束的时候,数据其实还没有真正写到磁盘上去,而是写到内核缓存就返回了,究竟有多少数据写入磁盘了是由内核控制的。有两种方法可以让内核缓存数据写入磁盘:

方法一:使用conv=fsync选项,明确要求dd命令每写一次数据,都强制将内核缓存写入磁盘中。如果count设置很大的值(N),会触发N次的强制写入磁盘,这不符合实际的使用情况,除非像数据库插入这种写动作,否则一般的应用程序都不会频繁要求系统缓存数据强制刷入磁盘,这样反而不能发挥出写速度的最佳性能,而是让内核自己控制。

方法二:写入的数据量要足够大,越大越好,超过内存值为佳,让内核缓存放不小,间接的逼迫内核将数据写到磁盘中,以此测试出更接近实际的速度值,越大越能测出接近真实的速度值。这种方法dd命令返回,虽然有可能还是有数据残留在内核缓存中未写入磁盘,但只要写入的数据足够大,这点缓存带来的影响是可以忽略不计的,以上测试就是采用本方法。

2、错误的命令

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=20M count=1 if=/dev/zero of=/mnt/sdcard/outfile
20+0 records in
20+0 records out
20971520 bytes (20.0MB) copied, 0.258525 seconds, 77.4MB/s

77.4M/s,严重超出SD卡的标称值,肯定不可信。以上dd命令结束的时候,数据其实还没有真正写到磁盘上去,而是写到内核缓存就返回了,由于数据量太小,很快就写入内核缓存了,都没有触发写入磁盘,当然速度快到离谱。

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=20M count=1 conv=fsync if=/dev/zero of=/mnt/sdcard/outfile
1+0 records in
1+0 records out
20971520 bytes (20.0MB) copied, 5.810785 seconds, 3.4MB/s

加上conv=fsync选项,强制dd命令每次写数据都写入磁盘,如此数据会更真实,但conv=fsync和count搭配是有陷阱的,往下看:

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=40k count=512 conv=fsync if=/dev/zero of=/mnt/sdcard/outfile
512+0 records in
512+0 records out
20971520 bytes (20.0MB) copied, 7.742188 seconds, 2.6MB/s

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=20k count=1024 conv=fsync if=/dev/zero of=/mnt/sdcard/outfile
1024+0 records in
1024+0 records out
20971520 bytes (20.0MB) copied, 13.830230 seconds, 1.4MB/s

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=10k count=2048 conv=fsync if=/dev/zero of=/mnt/sdcard/outfile
2048+0 records in
2048+0 records out
20971520 bytes (20.0MB) copied, 21.752566 seconds, 941.5KB/s


同样是写入20M数据,使用了conv=fsync选项,但count设置为N多次,这样会触发N次的强制写入磁盘(每次写入的数据bs都很小),如此也会降低写入速度。这种方式也不符合实际情况。

3、似是而非的命令

conv=fsync选项搭配count=1,同时bs设置很大,让dd命令返回前保证数据已经被刷入磁盘,岂不是更能测试SD卡的写速度值。如:

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=100M count=1 conv=fsync if=/dev/zero of=/mnt/sdcard/outfile
1+0 records in
1+0 records out
104857600 bytes (100.0MB) copied, 25.407060 seconds, 3.9MB/s

理论上这么说是对的,但仔细想想,不太对劲。第一,应用程序(或者内核缓存),数据不会缓存到那么大(如上100M)才触发写一次,这样丢失数据的风险太大。第二受限制于物理内存大小,bs能设置的上限值有限,超过可用的内存大小就会报错,如下所示:

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=500M count=1 conv=fsync if=/dev/zero of=/mnt/sdcard/outfile
dd: out of memory
# 

三、测试磁盘读能力

1、正确的命令

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=20k count=4096 if=/dev/sda1 of=/dev/null 
4096+0 records in
4096+0 records out
83886080 bytes (80.0MB) copied, 5.119588 seconds, 15.6MB/s
#

因为/dev/sda1是一个物理分区,对它的读取会产生IO,/dev/null是伪设备,相当于黑洞,of到该设备不会产生IO,所以,这个命令的IO只发生在/dev/sda1上,也相当于测试磁盘的读能力。

2、错误的命令

不清理内核缓存,对同一个文件连续进行多次读速度测试,如下所示:

# dd bs=20k count=4096 if=/dev/sda1 of=/dev/null 
4096+0 records in
4096+0 records out
83886080 bytes (80.0MB) copied, 5.119588 seconds, 15.6MB/s

# dd bs=20k count=4096 if=/dev/sda1 of=/dev/null
4096+0 records in
4096+0 records out
83886080 bytes (80.0MB) copied, 0.307665 seconds, 260.0MB/s

# dd bs=20k count=4096 if=/dev/sda1 of=/dev/null
4096+0 records in
4096+0 records out
83886080 bytes (80.0MB) copied, 0.290451 seconds, 275.4MB/s
 
# dd bs=20k count=4096 if=/dev/sda1 of=/dev/null
4096+0 records in
4096+0 records out
83886080 bytes (80.0MB) copied, 0.237559 seconds, 336.8MB/s


你会发现,对同一个文件(在linux中“一切皆文件”,设备也是文件)进行多次读速度测试,第一次读速度是15MB/s,第二次之后竟然都飙升到260MB/s,远远超出SD卡的标称值,不科学啊。这其实是linux内核缓存I/O中的“文件Cache”在作祟;

我对这种现象简单粗暴的理解:第一次读文件,是从磁盘卡中读取,是真实的读速度,读完之后该文件被内核缓存在内存中,第二次读时,只是从内存中获取文件数据(专业名词是cache命中),并没有真正的从磁盘读,所以速度贼快。

非要对同一文件进行多次的读测试,我们需要先清除linux内核的缓存,可以使用如下命令

# sync; echo 3 > /proc/sys/vm/drop_caches

来清除内核缓存,最后测试结果如下所示:

# dd bs=20k count=4096 if=/dev/sda1 of=/dev/null
4096+0 records in
4096+0 records out
83886080 bytes (80.0MB) copied, 5.136499 seconds, 15.6MB/s
 
# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=20k count=4096 if=/dev/sda1 of=/dev/null
4096+0 records in
4096+0 records out
83886080 bytes (80.0MB) copied, 5.189734 seconds, 15.4MB/s


四、测试同时读写能力

# sync; echo 3 > /proc/sys/vm/drop_caches
# dd bs=20k count=25600 if=/dev/sda1 of=/mnt/sdcard/outfile
25600+0 records in
25600+0 records out
524288000 bytes (500.0MB) copied, 151.556191 seconds, 3.3MB/s

这个命令下,一个是物理分区,一个是实际的文件,对它们的读写都会产生IO(对/dev/sda1是读,对mnt/sdisk/ftp/outfile写),假设他们都在一个磁盘中,这个命令就相当于测试磁盘的同时读写能力。

同样的,同一命令连续多次测试时,要记得清除内核缓存。

你可能感兴趣的:(linux环境开发)