0

我正在写 yat (yet-another-tool :)) 来监控 Linux 上的磁盘使用情况。我正在使用python 3.3.2psutil 3.3.0

我正在监控的进程做了一些非常基本的事情:我使用dd工具并改变块大小(128、512、1024、4096)

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

大文件.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

监视器.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

监视器.py:

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

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

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

我错过了什么大事吗?

非常感谢。

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

EDIT2:代码段中的错字

4

1 回答 1

3

write_bytes

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

读取字节
----------

I/O 计数器:读取的字节数
尝试计算此过程确实导致的字节数
从存储层获取。在 submit_bio() 级别完成,所以它是
对于块支持的文件系统是准确的。


写字节
------------

I/O 计数器:写入的字节数
尝试计算此进程导致发送到的字节数
存储层。这是在页面弄脏时完成的。

如您所知,大多数(全部?)文件系统都是基于块的。这意味着如果您有一个程序,例如,仅将 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:

$ 猫 /proc/3455/io
rchar:215317
wchar: 24
系统号:66
系统时钟:2
读字节:0
写入字节:4096
取消写入字节:0

这与dd您的情况相同。

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

87851423 - (87851423 mod 4096) + 4096 = 87855104

psutil 报告的数字并非偶然 87855104。

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
打开(“/lib/x86_64-linux-gnu/libc.so.6”,O_RDONLY|O_CLOEXEC)= 3
读(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
打开(“/开发/零”,O_RDONLY)= 3
打开(“零”,O_WRONLY|O_CREAT|O_TRUNC,0666)= 3
读(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
读(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
打开(“/usr/share/locale/locale.alias”,O_RDONLY|O_CLOEXEC)= 0
read(0, "# 语言环境名称别名数据库。\n#"..., 4096) = 2570
读取(0,“”,4096)= 0
open("/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有这样的文件或目录)
open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有这样的文件或目录)
open("/usr/share/locale-langpack/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有这样的文件或目录)
打开(“/usr/share/locale-langpack/en/LC_MESSAGES/coreutils.mo”,O_RDONLY)= 0
+++ 以 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运行过程中会有所不同(除非您非常快地执行这些步骤,在这种情况下,某些其他程序已将某些文件加载​​到缓存中的可能性很低)。

于 2015-12-26T10:28:30.850 回答