咸菜

啃咸菜 品生活

把玩古董Maxtor机械硬盘

折戟沉沙鐵未銷,自將磨洗認前朝。
20G迈拓机械硬盘

LBA(Logical Block Addressing)

LBA编址就是依CHS以从小到大顺序给扇区编号。是将CHS这种三维寻址通过#lba=(#c*H+#h)*S+#s-1(H:heads per cylinder, S:sectors per track)规则转换为一维线性寻址。化繁为简。

MBR(Master Boot Record)

MBR之“主”是相对于PBR(Partition Boot Record,也称VBR卷引导记录)来说的,每个分区都有自己的PBR,但整块硬盘盘只有一个MBR。BIOS从硬盘加载MBR,MBR中“引导代码”加载PBR,PBR中的“引导代码”能启动特定程序,而后程序再启动操作系统,这个过程叫“链式加载”。
PBR开始于partition(分区)的第一个扇区,如同MBR开始于整个硬盘的第一个扇区,类似于MBR关联DPT(disk partition table,分区表),PBR关联file system(文件系统)以及特定操作系统。

通常用MBR指代整个扇区,其实MBR严格讲只是扇区前446Bytes内容,其后紧接着4个16Bytes的DPT,在扇区最后偏移地址0x01fe和0x01ff分别存储0x55和0xaa,这个0x55aa是record(记录)有效标志,总共446+64+2=512Bytes写满第一扇区。

但,有些复杂MBR的引导代码量超过446Bytes(比如grub4dos)会占用16个扇区或更多,但是DPT的位置不能改变,所以备份MBR时只备份446Bytes可能并不够,而备份一个或几个扇区则连带分区表也备份了,还原时不同硬盘极可能不通用,要十分注意。 #dd bs=1 count=446 if=/dev/sda of=mbr446b.bin只适用非常简单的MBR,如果想备份16个扇区,则#dd bs=1b count=16 if=/dev/sda of=mbr16s.bin 注意dd程序bs参数默认单位就是Byte字节,当加suffix后缀时是倍乘关系,1b即1*512=512Bytes,1k即1*1024=1024Bytes。那么还原时得分两段,DPT前的446Bytes是第一段,跳过DPT的64Bytes后面的是第二段。

#dd bs=1 count=446 if=mbr16s.bin of=/dev/sda
#dd bs=1b seek=1 skip=1 count=15 if=/mbr16s.bin of=/dev/sda

DPT(Disk Partition Table)

前辈们扣扣索索的把MBR和DPT捏吧捏吧都挤进首扇区(好吧,要理解当初存储容量很宝贵),DPT总共只有64Bytes,而16Bytes描述一个分区项信息,故一块硬盘最多只能划分4个分区项(4个primary partition主分区或3个主分区+1个extended partition扩展分区)。
扩展分区像链式指针,跳转到其他扇区继续读取分区信息,春节假期也就几天,关于扩展分区就不去了解那么多了。

我Maxtor这块盘的DPT把玩记录:

#dd bs=1 skip=446 count=66 if=/dev/sda | hexdump
66+0 records in
66+0 records out
66 bytes (66B) copied, 0.000090 seconds, 716.1KB/s
0000000 4180 0001 3f06 0abf 0fff 0000 f54b 007f
0000010 4000 0a81 fe06 ffff 054a 0080 f54b 007f
0000020 fe00 ffff fe83 ffff 0000 0100 f800 009f
0000030 fe00 ffff fe0f ffff ffc1 019f 6fbf 00c2
0000040 aa55
0000042

看输出有点奇怪,DPT第一字节,表示活动分区的0x80显示在0x41后面,并且MBR记录有效标志0x55,0xaa也显示成了aa55H。这应该是hexdump程序输出显示采用了big endian(BE,大端字节序,高位字节在前)的缘故(关于big endian我在日志VIM转换文本文件字符编码中GB18030段落中有提到)
别扭是别扭点,自己把每两字节对换下前后看就行了,也不影响啥。而且我发现用hexdump -C使其带ASCII字符显示时又正常的little endian(LE,小端字节序,低位字节在前)了,想方便一点可以带参“-C“。

分析第一分区项:0x80表活动分区,从此分区启动引导;0x41表分区开始于65heads;0x01,0x00表分区开始于1sector0cylinder;0x06表此分区使用FAT16 file system;0x3f表分区结束于63heads;0xbf,0x0a表此分区结束于63===(0xbf &0b111111)sectors522===(((0xbf &0b11000000) <<2) |0x0a)cylinders (此处数大一眼看不出,借助javascript计算,1Byte是8bits,用位运算);0xff 0x0f 0x00 0x00表头前4095===0xfff个sectors保留;0x4b 0xf5 0x7f 0x00表分区总共8385867===0x7ff54b个sectors。
用fdisk直观的比对一下

#fdisk /dev/sda
command: p
Dvice  Boot StartCHS EndCHS      StrartLBA   EndLBA  Sectors Size  Id Type
/dev/sda1 * 0,65,1   522,63,63        4095  8389961  8385867 4094M  6 Fat16
Partition 1 does not end on cylinder boundary
/dev/sda2  522,64,1 1023,254,63    8389962 16775828  8385867 4094M  6 Fat16
/dev/sda3 1023,254,63 1023,254,63 16777216 27260927 10483712 5119M 83 Linux
/dev/sda4 1023,254,63 1023,254,63 27262913 40005503 12742591 6221M  f Win95 Ext't (LBA)
/dev/sda5 1023,254,63 1023,254,63 27262976 29360127  2097152 1024M 82 Linux swap
/dev/sda6 1023,254,63 1023,254,63 29362176 40005503 10643328 5196M  c Win95 FAT32 (LBA)

fdisk的交互命令可以带领着一步步改动DPT,比如d删除某分区,n新建一分区,改动后p打印可以看到修改后效果,要注意的是只有w写入后才真正修改DPT了。

dd不仅可以做备份还原用,直接“定点手术“也没毛病,比如echo -en "\x00" | dd bs=1 seek=446 of=/dev/sda将第一主分区的active标志0x80替换成了0x00,这块硬盘将不再启动引导;echo -en "\x00\x00" | dd bs=1 seek=510 of=/dev/sda擦掉记录有效标志,MBR变为无效;echo -en "\x55\xaa" seek=510 of=/dev/sda恢复有效标志。

zero-fill

我与我的迈拓注定渐行渐远,所谓“是非成败转头空”:

#fdisk -l | head -n 3
Disk /dev/sda: 19GB, 20490559488 bytes, 40020624 sectors
2491 cylinders, 255 heads, 63 sectors/track
Units: cylinders of 16065 * 512 = 8225280 bytes
#dd bs=8225280 count=2491 conv=fsync if=/dev/zero of=/dev/sda &
#watch -n 99 pkill -USR1 ^dd$
85+0 records in
85+0 records out
690923520 bytes (658.9MB) copied, 2.629941 seconds, 250.5MB/s

用后缀&使dd后台运行,再watch周期打断dd得到进度信息,注意dd不使用conv=fsync参数的话最后一些setors可能不能真的写上0以致硬盘没有擦干净。同terminal终端这样watch,进度信息是一条“刷新”的,发现dd不使用&后缀而是再开一个terminal来watch的话,原terminal的dd的进度输出是多条一直累加下去显示的。 zero-fill的场面应该很壮观:“谈笑间,樯橹灰飞烟灭。”