大多数操作系统根本不管复制有没有出错,只管复制过程有没有出错。这中间是有区别的。得说细一些。
检查复制没出错需要做复制校验,这事是有几个层次的。
最直观的,也是成本最高的,就是把复制过去的东西再拿来读一遍,和来源全部对照一遍。叫做全读校验。很显然,这是一定能确认复制没有错的方法。然而它也很显然太“贵”了,因为等于源数据要读至少两遍,拷贝数据要读至少一遍写至少一遍,相比不检查多出了许多工作量。而且对很多应用场景来说,这甚至是做不到的。所以大多数操作系统默认不做这种程度的校验。
为什么很多场景下做不到?因为复制数据的场景比大多数人直观想象要复杂得多,简单直接顺利的情景占不到全部的九成。比如,最令人讨厌情况有复制数据到慢速设备,写入10Mbps读取0.1Mbps,全读校验花的时间是复制本身的100倍。还有各种复制锁无法保证的情况。例如源数据在校验过程中改变了,或者你只有目标的写权限没有读权限,或者你的来源数据只能读一遍的情况。大量常见场景使得全读校验无法实现。
不用这种一定能确认复制没错的方法,还有什么别的办法吗?那就分好几种妥协方法了。
有些操作系统采用的妥协省事方法是hash校验。复制的目标端有某种内置方法生成文件hash值,复制过程生成源数据的hash值,复制完成时对照一下两个hash,一致就ok。这是一种比较聪明的低成本近似全读校验的办法。这个方法显然需要目标支持生成hash的方法,不然就得再读一遍了,所以适用场景有限。
再弱一些,也就是Windows和大多数操作系统都支持的方法,就是管道可靠性校验,也就是只管复制过程有没有出错。思路是这样的:我读的时候要求读数据管道确认读没出错,写的时候要求写数据管道确认写没出错,那基本的数据一致性就得到保证了。具体实现细节就不展开说了,情景其实也很复杂。只要知道这种校验其实可以很弱,但总归比没有强太多。Windows用户在复制文件时看到的CRC循环冗余校验错误实际上就是在写管道上的校验机制不能通过报的错。这种方法也往往是所有其他更复杂校验的基础。
为什么说这种校验可以很弱呢?因为管道的可验证性在很多常见条件下是很弱的。有时候甚至管道并没有办法去确认有没有出错。比如直到SATA年代硬盘的指令才有统一的校验机制,在此之前很可能你让硬盘写数据你是无法判断硬盘到底有没有干这事的。外加这个方法其实不能覆盖端到端,因为读出来的数据会停留在内存一段时间,而普通的内存是没有数据一致性保护的。所以有少数运气不好的用户会发现内存损坏导致复制出现错误,而复制过程不报错的现象。
文件按byte为单位复制,每复制1byte做个检验,这样一个byte一个byte的校验可以保证没错,原理是这样,实际上可能是多少bit来一次校验码,海明码了解一下,windows下硬盘到硬盘和硬盘到网络到硬盘之间拷贝方式是没差的。
特别大的直接走DMA(前提是驱动器支持,现在硬盘基本上都支持),这样两个驱动器之间直接对拷,DMA控制器集成校验单元可以直接边传输边校验所以不用担心出错问题。