psutil vs dd:监视磁盘I / O.

时间:2015-12-22 10:42:27

标签: python-3.x dd psutil

我正在写y.a.t. (还有另一个工具:))用于监视Linux上的磁盘使用情况。 我使用 python 3.3.2 psutil 3.3.0

我监控的过程非常基本:我使用 dd 工具,我改变块大小(128,512,1024,4096)

#!/bin/bash
dd if=./bigfile.txt of=./copy.img bs=4096

bigfile.txt:

$ stat bigfile.txt 
     File: ‘bigfile.txt’
     Size: 87851423     Blocks: 171600     IO Block: 4096   regular file

显示器的片段如下:

def poll(interval, proc):
    d_before = proc.io_counters()
    time.sleep(interval)
    tst = time.time()
    d_after = proc.io_counters()

    usage = OrderedDict.fromkeys(d_after.__dict__.keys())
    for k, v in usage.items():
        usage[k] = d_after.__dict__[k] - d_before.__dict__[k]

    return tst, usage

在每次运行时,我清除缓存(在stackoverflow上多次建议):

rm copy.img && sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"

我的问题是:为什么数字不匹配?

bs = 128

DD

686339+1 records in
686339+1 records out
87851423 bytes (88 MB) copied, 1.21664 s, 72.2 MB/s

monitor.py:

1450778750.104943 OrderedDict([('read_count', 686352), ('write_count', 686343), ('read_bytes', 87920640), ('write_bytes', 87855104)])

BS = 4096

DD

21448+1 records in
21448+1 records out
87851423 bytes (88 MB) copied, 0.223911 s, 392 MB/s

monitor.py:

1450779294.5541275 OrderedDict([('read_count', 21468), ('write_count', 21452), ('read_bytes', 88252416), ('write_bytes', 87855104)])

bs 的所有值仍然存在差异。

是否确定读/写未被计算? psutil会执行一些额外的工作吗?例如,使用 bs = 4096 ,为什么在psutil 400993中会报告更多字节(用于读取)和3681(用于写入)?

我错过了一些大事吗?

非常感谢。

EDIT :作为更新,测量时间的粒度,即time.sleep(interval)调用并不重要。我尝试了不同的值,并总结了psutil报告的读写总数。差异仍然存在。

EDIT2 :代码段中的拼写错误

1 个答案:

答案 0 :(得分:2)

write_bytes

read_byteswrite_bytes对应/proc/<PID>/io中的相同字段。引用documentation(强调我的):

read_bytes
----------

I/O counter: bytes read
Attempt to count the number of bytes which this process really did cause to
be fetched from the storage layer. Done at the submit_bio() level, so it is
accurate for block-backed filesystems. 


write_bytes
-----------

I/O counter: bytes written
Attempt to count the number of bytes which this process caused to be sent to
the storage layer. This is done at page-dirtying time.

如您所知,大多数(所有?)文件系统都是基于块的。这意味着如果你有一个程序,比如说,只向文件写入5个字节,如果你的块大小为4 KiB,那么将写入4 KiB。

如果您不信任dd,让我们尝试使用简单的Python脚本:

with open('something', 'wb') as f:
    f.write(b'12345')
input('press Enter to exit')

这个脚本应该只写5个字节,但是如果我们检查/proc/<PID>/io,我们可以看到写了4 KiB:

$ cat /proc/3455/io 
rchar: 215317
wchar: 24
syscr: 66
syscw: 2
read_bytes: 0
write_bytes: 4096
cancelled_write_bytes: 0

这与你的dd中的情况相同。

您已要求dd写入87851423个字节。有多少4个KiB块是87851423个字节?

87851423 - (87851423 mod 4096) + 4096 = 87855104

不是偶然的87855104是psutil报告的数字。

read_bytes

read_bytes怎么样?理论上我们应该read_bytes等于write_bytes,但实际上read_bytes在第一次运行中显示16个块,在第二次运行中显示97个块。

嗯,首先,让我们看看dd实际上正在阅读的文件:

$ strace -e trace=open,read -- dd if=/dev/zero of=zero bs=1M count=2
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\v\2\0\0\0\0\0"..., 832) = 832
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/dev/zero", O_RDONLY)             = 3
open("zero", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 0
read(0, "# Locale name alias data base.\n#"..., 4096) = 2570
read(0, "", 4096)                       = 0
open("/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = 0
+++ exited with 0 +++

如您所见,dd正在打开并读取链接器,GNU C库和区域设置文件。它读取的字节数比您在上面看到的字节多,因为它还使用mmap,而不只是read

重点是:dd读取的文件比源文件多得多,因此read_bytes远远高于write_bytes。但为什么它不一致?

dd读取的那些文件也被许多其他程序使用。即使您在执行drop_caches之前dd,也有可能某些其他进程可能会将其中一个文件重新加载到内存中。您可以尝试使用这个非常简单的C程序:

int main()
{
    while(1) {
    }
}

使用默认的GCC选项编译,除了打开链接器和GNU C库之外,该程序不执行任何操作。如果您尝试drop_caches,多次执行该计划和cat /proc/<PID>/IO,您会看到read_bytes在不同的运行中会有所不同(除非您执行这些步骤非常快,在这种情况下某些其他程序将某些文件加载​​到缓存中的概率很低。