如何在Linux中验证硬盘驱动器是否填充了零?


16

我的硬盘充满零。

如何使用bash检查硬盘上的所有位是否为零?


仅用零覆盖整个驱动器是否可以接受?还是您实际上需要确认当前内容?
鲍勃

我想验证硬盘驱动器是否填充了零。
gkfvbnhjh2 2013年

1
从理论上讲,数据清理工具中可能存在一些漏洞,这些漏洞会使某些数据保持完整。我不确定每一位都是零。那么,如何检查硬盘是否充满零?
gkfvbnhjh2 2013年

为什么为零?您是否会多次随机写零和一?

13
由于1比0窄-您可以更容易地看到它们之间的旧数据。
ChrisA

Answers:


29

od将用替换同一行的运行*,因此您可以轻松地使用它来扫描非零字节:

$ sudo od /dev/disk2 | head
0000000    000000  000000  000000  000000  000000  000000  000000  000000
*
234250000

8
我将| head在结尾处添加内容,以便如果发现驱动器未归零,则在产生足够的输出以显示事实后停止运行,而不是将整个驱动器转储到屏幕上。
Wyzard

2
@Wyzard:好主意;我将其添加到我的答案中。
Gordon Davisson

8

我已经编写了一个简短的C ++程序来做到这一点,可在此处获得源代码。

要构建它:

wget -O iszero.cpp https://gist.github.com/BobVul/5070989/raw/2aba8075f8ccd7eb72a718be040bb6204f70404a/iszero.cpp
g++ -o iszero iszero.cpp

要运行它:

dd if=/dev/sdX 2>/dev/null | ./iszero

它将输出任何非零字节的位置和值。您可以使用将该输出重定向到文件>,例如:

dd if=/dev/sdX 2>/dev/null | ./iszero >nonzerochars.txt

您可能想尝试进行更改BUFFER_SIZE以提高效率。我不确定最佳值是多少。请注意,这也会影响打印进度的频率,这会在一定程度上影响速度(将打印输出输出到控制台很)。添加2>/dev/null以摆脱进度输出。

我知道这没有使用标准bash,甚至没有内置函数,但是它不需要任何额外的特权。@Hennes的解决方案仍然更快(我还没有真正优化任何东西-这是天真的解决方案);但是,这个小程序可以使您更好地了解抽头丢失了多少个字节以及在什么位置。如果禁用进度输出,它仍然会比大多数消费类硬盘驱动器读取速度快(> 150 MB / s),因此这不是一个大问题。

此处提供更快的版本,输出较少的详细信息。但是,它仍然比@Hennes的解决方案慢一点。但是,此字符将在遇到的第一个非零字符处退出,因此,如果在流的开头附近有一个非零字符,则可能会更快。


在帖子中添加来源以使答案更好地独立:

#include <cstdio>

#define BUFFER_SIZE 1024

int main() {
    FILE* file = stdin;
    char buffer[BUFFER_SIZE];
    long long bytes_read = 0;
    long long progress = 0;
    long long nonzero = 0;

    while (bytes_read = fread(buffer, 1, BUFFER_SIZE, file)) {
        for (long long i = 0; i < bytes_read; i++) {
            progress++;
            if (buffer[i] != 0) {
                nonzero++;
                printf("%lld: %x\n", progress, buffer[i]);
            }
        }
        fprintf(stderr, "%lld bytes processed\r", progress);
    }

    fprintf(stderr, "\n");

    int error = 0;
    if (error = ferror(file)) {
        fprintf(stderr, "Error reading file, code: %d\n", error);
        return -1;
    }

    printf("%lld nonzero characters encountered.\n", nonzero);
    return nonzero;
}

这是一个很好的答案,但是有什么办法可以使脚本更像普通命令一样工作-使用iszero /dev/sda而不是要求将脚本与之类似iszero < /dev/sda
Hashim

1
@Hashim这是相当久以前写成一个一次性程序的东西了(如今,我至少要用Python之类的脚本语言而不是编译后的C语言来做)……也就是说,如果您想在最简单的方法是,将其放置在int main(int argc, char *argv[])then之后FILE* file = fopen(argv[1], "r");。正确执行此操作将包括检查参数是否确实存在,错误检查打开是否成功(在之后进行其他ferror检查fopen)等,但是对于一次性程序来说麻烦太多了。
鲍勃

1
@Hashim我怀疑numpy中的SIMD向量化操作将接近C语言中的向量化指令。这是假设C编译器足够聪明,可以对朴素的C程序中的循环进行向量化。必须进行基准测试以确保;不幸的是,我现在真的没有时间这样做。Python(等)的主要优点是,它通常可以在没有编译器的情况下运行,并且gcc在没有拉下其他软件包的情况下不一定可以在所有Linux发行版中使用。再一次,numpy也不是标准Python包的一部分...
Bob

1
@Hashim如果你编译-O3-march=native您可能会看到一些的加速; 应该确保GCC启用了自动矢量化功能,并使用当前CPU可用的最佳功能(AVX,SSE2 / SSE3等)。除此之外,您还可以使用缓冲区大小;使用向量化循环时,不同的缓冲区大小可能更为最佳(我将使用1MB +的内存,当前为1kB)。
鲍勃,

1
@Hashim以上评论已编辑,以防您看不到。除此之外,如果您想进一步讨论,可以@Bob在聊天中对我()进行ping :chat.stackexchange.com/rooms/118/root-access
Bob的

7

扩展Gordon的答案,pv可以说明这一过程有多远:

$ sudo pv -tpreb /dev/sda | od | head
0000000 000000 000000 000000 000000 000000 000000 000000 000000
*
9.76GiB 0:06:30 [25.3MiB/s] [=================>               ] 59% ETA 0:04:56

这对于大硬盘非常有用!
Martin Hansen

5

这似乎是一个效率低下的丑陋解决方案,但是如果您只需要检查一次:

dd if=/dev/sdX | tr --squeeze-repeats "\000" "T"

使用dd从磁盘读取sdX。(将X替换为您要读取的驱动器),
然后所有不可打印的零字节转换为我们可以处理的内容。

接下来,我们要么计算可以处理的字节数,然后检查它是否正确(wc -c用于此目的),要么跳过计数并使用-s--squeeze-repeats将所有多次出现的字符压缩为单个char。

因此dd if=/dev/sdX | tr --squeeze-repeats "\000" "T"应该只打印一个T。

如果要定期执行此操作,则需要更有效的方法。
如果您只想这样做一次,则此垃圾可能会验证您的普通刮水器是否正常工作并且可以信任它。


您为什么认为此解决方案效率低下?是否有一些缓冲区需要读取远远超出第一个非NUL位置的信息?
丹尼尔·贝克

文字中的“ T”作为唯一的非零字符出现在一个潜在的问题上吗?
鲍勃

真正。那是设计的缺陷。我也不使用bash(shell本身),但是我假设使用“ Bash”表示“不是来自bash,而是来自使用任何shell提示和标准文本模式工具”。
Hennes

3
@daniel:一个简单的C程序应该能够读取所有数据,而无需更改每个读取的字节。这将是更有效和美观的。编写这样的程序可能比以低效的方式仅使用可用的工具花费更多的时间。
Hennes

3

仅检查,您会看到列出的任何不匹配的块

sudo badblocks -sv -t 0x00 /dev/sdX

或者使用badblocks来编写它们并进行检查:

sudo badblocks -svw -t 0x00 /dev/sdX

默认的破坏性测试是我选择的安全擦除

sudo badblocks -svw /dev/sdX

如果有人在用0和1交替填充驱动器后能检索到任何东西,那么它们的补码,然后是全1,然后是全0,并且每经过一遍验证就可以了,那么祝他们好运!

还要对新驱动器进行良好的部署前检查

man badblocks

其他选择

并不是说它很快,但是它可以工作...


2

两全其美。此命令将跳过坏扇区:

sudo dd if=/dev/sdX conv=noerror,sync | od | head

使用kill -USR1 <pid of dd>看到进展。


0

前段时间我很好奇AIO。结果是一个示例测试程序,该程序正好检查了的扇区(512字节块)NUL。您可以将其视为稀疏文件区域检测器的变体。我认为消息来源说明了一切。

  • 如果NUL输出了整个文件/驱动器,则显示为0000000000-eof。请注意,程序中有一个技巧,fin()没有故意在第107行调用该函数以提供显示的输出。
  • 未经严格测试,因此可能包含错误
  • 代码要长一些,AIO不像其他方法那么直接,
  • 但是,AIO可能是使驱动器忙于读取的最快方法,因为NUL比较是在读入下一个数据块时完成的。(我们可以通过重叠来挤出几毫秒的时间AIO,但是我真的不认为这值得努力。)
  • true如果文件可读且一切正常,它将始终返回。false如果文件不是,则不返回NUL
  • 它假定文件大小是512的倍数。最后一个扇区有一个错误,但是在文件上NUL,由于内存缓冲区已经包含,它仍然可以正常工作NUL。如果有人认为这需要解决,请在第95行中memcmp(nullblock, buf+off, SECTOR)阅读memcmp(nullblock, buf+off, len-off<SECTOR : len-off : SECTOR)。但是唯一的区别是,“结束报告”可能有点随机(不是针对完全是的文件NUL)。
  • memcmp()所做的更改还解决了不NUL alloc()编辑内存的平台上的另一个问题,因为代码不执行此操作。但这只能在小于4 MiB的文件中看到,但是checknul对于这么小的任务,这显然是过分的;)

高温超导

/* Output offset of NUL sector spans on disk/partition/file
 *
 * This uses an AIO recipe to speed up reading,
 * so "processing" can take place while data is read into the buffers.
 *
 * usage: ./checknul device_or_file
 *
 * This Works is placed under the terms of the Copyright Less License,
 * see file COPYRIGHT.CLL.  USE AT OWN RISK, ABSOLUTELY NO WARRANTY.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <malloc.h>
#include <aio.h>

#define SECTOR  512
#define SECTORS 40960
#define BUFFERLEN   (SECTOR*SECTORS)

static void
oops(const char *s)
{
  perror(s);
  exit(1);
}

static void *
my_memalign(size_t len)
{
  void      *ptr;
  static size_t pagesize;

  if (!pagesize)
    pagesize = sysconf(_SC_PAGESIZE);
  if (len%pagesize)
    oops("alignment?");
  ptr = memalign(pagesize, len);
  if (!ptr)
    oops("OOM");
  return ptr;
}

static struct aiocb aio;

static void
my_aio_read(void *buf)
{
  int   ret;

  aio.aio_buf = buf;
  ret = aio_read(&aio);
  if (ret<0)
    oops("aio_read");
}

static int
my_aio_wait(void)
{
  const struct aiocb    *cb;
  int           ret;

  cb = &aio;
  ret = aio_suspend(&cb, 1, NULL);
  if (ret<0)
    oops("aio_suspend");
  if (aio_error(&aio))
    return -1;
  return aio_return(&aio);
}

static unsigned long long   nul_last;
static int          nul_was;

static void
fin(void)
{
  if (!nul_was)
    return;
  printf("%010llx\n", nul_last);
  fflush(stdout);
  nul_was   = 0;
}

static void
checknul(unsigned long long pos, unsigned char *buf, int len)
{
  static unsigned char  nullblock[SECTOR];
  int           off;

  for (off=0; off<len; off+=SECTOR)
    if (memcmp(nullblock, buf+off, SECTOR))
      fin();
    else
      {
        if (!nul_was)
          {
            printf("%010llx-", pos+off);
            fflush(stdout);
            nul_was = 1;
          }
        nul_last    = pos+off+SECTOR-1;
      }
}

int
main(int argc, char **argv)
{
  unsigned char *buf[2];
  int       fd;
  int       io, got;

  buf[0] = my_memalign(BUFFERLEN);
  buf[1] = my_memalign(BUFFERLEN);

  if (argc!=2)
    oops("Usage: checknul file");
  if ((fd=open(argv[1], O_RDONLY))<0)
    oops(argv[1]);

  aio.aio_nbytes    = BUFFERLEN;
  aio.aio_fildes    = fd;
  aio.aio_offset    = 0;

  io = 0;
  my_aio_read(buf[io]);
  while ((got=my_aio_wait())>0)
    {
      unsigned long long    pos;

      pos   = aio.aio_offset;

      aio.aio_offset += got;
      my_aio_read(buf[1-io]);

      checknul(pos, buf[io], got);

      io    = 1-io;
    }
  if (got<0)
    oops("read error");
  printf("eof\n");
  close(fd);
  return 0;
}

0

想要从一个类似但较早的问题中发布这个聪明的解决方案,该问题由一段时间未登录的用户发布:

/dev/zeroLinux系统上有一个设备在读取时总是给出零。

因此,如何将硬盘驱动器与此设备进行比较:

cmp /dev/sdX /dev/zero

如果将硬盘驱动器清零一切正常,它将终止于:

cmp: EOF on /dev/sdb

告诉您,两个文件在硬盘驱动器末尾之前都是相同的。如果硬盘驱动器上有一个非零位,cmp则会告诉您它在文件中的位置。

如果已pv安装软件包,则:

pv /dev/sdX | cmp /dev/zero

将使用进度条执行相同的操作,以使您在检查驱动器时感到很开心(但EOF现在将位于STDIN而不是sdX上)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.