我试图获取 /dev/sda2 的最后 1024 个字节。当我这样做时sudo tail -c 1024 /dev/sda2 | hd
,提示符会一直挂起,直到我按下 Ctrl-C。但是,当我tail -c 1024 ddfilecopyofsda2 | hd
做时,我立即得到了文件最后 1024 个字节的良好输出。我在这里读到 (https://unix.stackexchange.com/questions/60034/what-are-character-special-and-block-special-files-in-a-unix-system)“块设备通常是可寻址的”,那么我遗漏了什么?
答案1
以下是获取块设备最后 1024 个字节的一种方法:
last_bytes() { sudo dd if=$2 iflag=skip_bytes skip=$(($(sudo blockdev --getsize64 $2) - $1)) bs=1M ; } ; last_bytes 1024 DEVICE
替换DEVICE
为设备路径。对于您的情况,可以使用/dev/sda2
。
现在来回答一个更有趣的问题……
为什么要tail -c 1024 /dev/sda2
搜索整个磁盘?
原因在于tail
实现方式。当tail
知道正在读取的文件的大小时,它就知道要查找多少。否则,它必须一直读取文件或流才能找出要回溯多远。
使用管道,这是有意义的,就像cat /dev/sda2 | tail -c 1024
. tail
以流的形式接收内容并且无法知道数据何时结束。
您可能希望tail -c 1024 /dev/sda2
能够确定的大小/dev/sda2
,但实际上,当tail
查找时/dev/sda2
,它会作为块设备而不是常规文件打开。
实现细节是tail
调用fstat()
获取有关该文件的信息。
tail
在普通文件上
以下是打开文件strace
示例的相关部分:tail
21:30:27 open("/var/log/syslog", O_RDONLY) = 3
21:30:27 fstat(3, {st_dev=makedev(0, 22), st_ino=4715, st_mode=S_IFREG|0640, st_nlink=1, st_uid=104, st_gid=4, st_blksize=131072, st_blocks=54, st_size=175500, st_atime=2017/11/10-21:28:39.243133398, st_mtime=2017/11/10-21:30:20.438031639, st_ctime=2017/11/10-21:30:20.438031639}) = 0
21:30:27 lseek(3, 0, SEEK_CUR) = 0
21:30:27 lseek(3, 174476, SEEK_SET) = 174476
fstat()
提供st_size=175500
。现在tail
只需要往回数1024个字节:
175500-1024=174476
…这正是
tail
:lseek(3, 174476, SEEK_SET) = 174476
tail
在块设备上
fstat()
这次不返回尺寸!:
21:29:43 open("/dev/sda", O_RDONLY) = 3
21:29:43 fstat(3, {st_dev=makedev(0, 6), st_ino=17488, st_mode=S_IFBLK|0660, st_nlink=1, st_uid=0, st_gid=6, st_blksize=4096, st_blocks=0, st_rdev=makedev(8, 0), st_atime=2017/11/10-09:21:15.643998960, st_mtime=2017/11/10-09:21:15.555998962, st_ctime=2017/11/10-09:21:15.555998962}) = 0
如果没有st_size
,tail
就无法知道要寻找多远,因此它默认读取整个块设备直到最后。
这就是为什么您通常应该使用诸如的块设备工具dd
来操作块设备而不是诸如的用于常规文件的工具tail
。
你可能会问“如何blockdev --getsize64
快速获取块设备的大小?”
这里是sudo strace -vvvfts1000 blockdev --getsize64 /dev/sda
:
21:53:15 open("/dev/sda", O_RDONLY) = 3
21:53:15 ioctl(3, BLKGETSIZE64, [512110190592]) = 0
blockdev
用于获取块设备ioctl,并BLKGETSIZE64
获取块设备的大小。
至于为什么 tail
不行BLKGETSIZE64
,我不知道。源代码显示:
#define IS_TAILABLE_FILE_TYPE(Mode) \
(S_ISREG (Mode) || S_ISFIFO (Mode) || S_ISSOCK (Mode) || S_ISCHR (Mode))
我只从那句话中知道,没有S_ISBLK()
,作者并不打算tail
支持块设备。