可以恢复失败文件传输的程序如何知道从何处开始追加数据?


23

某些文件复制程序类似,rsync并且curl能够恢复失败的传输/复制。

注意这些失败可能有很多原因,在某些情况下程序可以执行“清理”,在某些情况下程序无法执行。

当这些程序恢复时,它们似乎只是在计算成功传输的文件/数据的大小,然后才开始从源中读取下一个字节并将其附加到文件片段上。

例如,“到达目的地”的文件片段的大小为1378字节,因此它们只是从原始文件的1379字节开始读取并添加到片段中。

我的问题是,知道字节是由位组成的,并且并非所有文件的数据都按原始字节大小的块进行分段,这些程序如何知道它们选择开始向其添加数据的正确点呢?

在写目标文件时,是否在程序,内核或文件系统级别发生某种类似于SQL数据库的缓冲或“事务”,以确保只有干净,格式正确的字节才能到达基础块设备?
还是程序假设最新的字节可能不完整,所以他们假设它坏了就将其删除,重新复制该字节并从那里开始追加?

知道并非所有数据都以字节表示,所以这些猜测似乎是不正确的。

当这些程序“恢复”时,他们如何知道它们从正确的位置开始?


21
“不是所有文件的数据都按干净的字节大小的块进行分段”,不是吗?您如何将少于一个字节的内容写入文件?
muru

17
我知道没有系统调用可以写少于一个字节的任何内容,至于磁盘本身,我认为今天没有磁盘写少于512字节的块(或4096字节的块)。
muru

8
不,我是说最小值是一个字节。Sane应用程序将使用4KB或8KB块:head -c 20480 /dev/zero | strace -e write tee foo >/dev/null,然后操作系统将对其进行缓冲,并以更大的块将其发送到磁盘。
muru

9
@the_velour_fog:您如何用编写一点fwrite()
psmears

9
对于所有的实际目的,数据由字节的一切与他们工作的最小单位。一些系统(主要与压缩有关,例如gzip,h264)从字节中解包各个位,但是操作系统和内存操作处于字节级别。
pjc50

Answers:


40

为了清楚起见-真正的机制更加复杂以提供更好的安全性-您可以想象这样的写磁盘操作:

  • 应用程序写入字节(1)
  • 内核(和/或文件系统IOSS)对它们进行缓冲
  • 一旦缓冲区已满,它将刷新到文件系统:
    • 分配了块(2)
    • 块被写入(3)
    • 文件和块信息已更新(4)

如果该过程在(1)处中断,则说明磁盘上没有任何内容,该文件是完整的,并且在上一个块处被截断了。您发送了5000个字节,磁盘上只有4096个,然后从偏移4096重新开始传输。

如果在(2)处,则除了内存以外什么都不会发生。与(1)相同。如果在(3)处写入了数据,但是没有人记得它。您发送了9000个字节,写入了4096个字节,写入了4096个字节并丢失了,其余的则丢失了。传输从偏移4096恢复。

如果位于(4),则数据现在应该已经提交到磁盘上。流中的下一个字节可能会丢失。您发送了9000个字节,写入了8192个字节,其余的丢失了,传输在偏移量8192处恢复。

这是一个简化的过程。例如,阶段3-4中的每个“逻辑”写入都不是“原子的”,而是产生了另一个序列(将其编号为#5),由此该块被细分为适合于目标设备(例如硬盘)的子块。 )发送到设备的主机控制器,后者也具有缓存机制,最后存储在磁碟上。该子序列并不总是完全在系统的控制之下,因此,将数据发送到硬盘并不能保证它已被实际写入并且可以被读回。

几个文件系统实现了日记功能,以确保最脆弱的点(4)实际上不容易受到攻击,通过在您猜到的情况下写入元数据,无论阶段(5)发生什么事务交易都将始终如一地工作。

如果系统在事务处理过程中被重置,则可以恢复到最近的完整检查点的方式。与情况(1)相同,写入的数据仍然丢失,但是恢复将解决该问题。实际上没有信息丢失。


1
很好的解释。这一切都说得通。因此,如果某个进程确实一直更新(4)文件块信息,则您知道所有这些字节都是好的。那么在任何先前阶段的任何字节要么都没有放入磁盘,要么(如果这样做了)它们将被“记住”(无引用)
the_velour_fog

4
@the_velour_fog仅用于补充倒数​​第二段-如果您使用的是未实现日记功能的文件系统,确实可以获取“损坏的”数据,从而导致简历失败并产生乱码,而不会给您带来错误。过去,这种情况一直都在发生,尤其是在为高延迟设备(如软盘)设计的文件系统中。即使文件系统不是以这种方式可靠,仍有一些技巧可以避免这种情况,但是它需要一个更智能的应用程序来进行补偿,并且某些假设可能在某些系统上是错误的。
a安

此答案夸大了文件系统中日记功能的用途。除非一切都实现事务语义,包括用户空间应用程序(通过fsync)和硬盘驱动器控制器(即使在所谓的“企业”驱动器中也经常损坏),否则它不会可靠地工作。如果没有fsync许多文件操作,则POSIX 不能保证这些文件的直观排列和原子操作:用打开的文件的O_APPEND行为可能与没有进行文件操作的行为不同。实际上,文件一致性最重要的关键是内核VFS系统和磁盘缓存。其他所有东西都是绒毛。
user1643723 '02

11

注意:我没有查看源rsync或任何其他文件传输实用程序。

编写一个C语言程序来跳过文件末尾并以字节为单位获取该位置的位置很简单。

只需调用标准C库函数即可完成这两项操作lseek()lseek(fd, 0, SEEK_END)返回为文件描述符打开的文件长度fd,以字节为单位)。

对目标文件完成此操作后,lseek()可以在源文件上执行到的类似调用,以跳转到适当的位置:lseek(fd, pos, SEEK_SET)。然后,假设已将源文件的较早部分标识为未更改(不同的实用程序可能以不同的方式执行此操作),则传输可能会在此时继续。

文件可能会在磁盘上碎片化,但是文件系统将确保应用程序将文件视为字节的顺序序列。


关于注释中有关位和字节的讨论:可以写入磁盘的最小数据单位是byte。一个字节需要在磁盘上分配至少一个数据。块的大小取决于文件系统的类型,还可能取决于管理员在初始化文件系统时使用的参数,但通常在512字节和4 KiB之间。写入操作可以由内核,底层C库或应用程序本身进行缓冲,并且作为优化,实际写入磁盘的操作可能以适当块大小的倍数进行。

不能将单个位写入文件,并且如果写操作失败,它将不会在文件中留下“半写字节”。


谢谢,那是什么确保写操作失败-它不会留下一半的写字节数?是内核缓冲大师描述的吗?-即,如果某个进程在向内核发送8KB块的过程中被中断并意外终止-那8KB的块将永远不会到达内核-但是任何以前到达内核和文件系统的进程都可以被认为是好的?
the_velour_fog '18

6
@the_velour_fog不会发生意外的终止,因为该过程在I / O系统调用的中间是不间断的(这就是为什么看到不可杀死的进程卡在NFS文件的文件系统访问调用中并不少见的原因)。另请参见:unix.stackexchange.com/q/62697/70524
muru,

2
如果系统在完全错误的时间断电,可能会出现问题。这有时会在文件的最后写入点导致垃圾。在数据库设计中,这是一个非常棘手的问题。但是,通常最小的“有效”或“无效”的单位仍是磁盘块。
pjc50

1
@the_velour_fog并不是因为您无法获得“ 一半写入的字节 ”(或更准确地说,是一半写入的字节),因为一半写入的块不会被记录为已写入(全部) )-请参阅LSerni答案的步骤(3)和(4)。
TripeHound18年

5

这基本上是两个问题,因为curl和rsync之类的程序非常不同。

对于curl这样的HTTP客户端,它们检查当前文件的大小,然后发送Content-Range带有请求的标头。服务器或者使用状态码206(部分内容)而不是200(成功)恢复发送文件范围,并且恢复下载,或者它忽略标头并从头开始,并且HTTP客户端除了重新下载所有内容外别无选择再次。

此外,服务器可以发送也可以不发送Content-Length头。您可能已经注意到,某些下载没有显示百分比和文件大小。这些下载是在服务器不告诉客户端长度的情况下进行的,因此客户端仅知道其下载量,而不知道将跟随多少字节。

一些下载管理器使用Content-Range带有开始停止位置的标头来一次从不同来源下载文件,如果每个镜像本身比您的网络连接慢,则可以加快传输速度。

另一方面,rsync是用于增量文件传输的高级协议。它在服务器和客户端上生成文件部分的校验和,以检测哪些字节相同。然后,它仅发送差异。这意味着它不仅可以继续下载,而且即使您在非常大的文件中间更改了几个字节而无需重新下载文件,它甚至可以下载更改的字节。

另一个用于恢复传输的协议是bittorrent,其中.torrent文件包含文件中块的校验和列表,因此可以以任意顺序从不同来源并行下载和验证块。

请注意,rsync和bittorent将验证磁盘上的部分数据,而不会继续进行HTTP下载。因此,如果您怀疑部分数据已损坏,则需要检查完整性,即使用最终文件的校验和。但是,仅中断下载或断开网络连接通常不会损坏部分文件,而传输过程中可能会出现电源故障。


4

TL; DR:他们不能这样做,除非他们使用的协议允许这样做。

程序不能总是从任意位置恢复:例如,HTTP请求仅在服务器支持且客户端实现时才可重新启动:这不是通用的,因此请查看程序的文档。如果服务器确实支持它,则程序可以通过简单地要求作为协议的一部分来恢复传输。您通常会在下载目录中看到部分传输(它们通常标有“ .partial”扩展名或类似名称。)

如果文件下载被暂停或中止,客户端可以将文件写入磁盘,并确定恢复位置。另一方面,如果客户端崩溃或写入文件时出错,则客户端必须假定该文件已损坏并重新开始。BitTorrent通过将文件分成“块”并跟踪已成功下载的文件来缓解这种情况。它需要重做的最大部分是几块。Rsync做类似的事情。

程序如何知道内容相同?一种方法是验证客户端和服务器之间的某些标识符是否相同。时间戳和大小是其中的一些示例,但是有些机制可能特定于协议。如果标识符匹配,则客户端可以假定继续进行操作。

如果您想进行更确定的验证,则HTTP和好友应该不是您的首选。您将要使用一个协议,该协议还对整个文件和每个传输的块都具有校验和或哈希,以便您可以将下载的校验和与服务器的计算机校验和进行比较:不匹配的所有内容将被重新下载。同样,BitTorrent是这种协议的一个示例。rsync也可以选择执行此操作。


对于rsync示例,这将很简单,因为只有一个rsync协议。对于http下载,有一个范围请求作为标准。我很好奇知道curl在恢复上传时实际上会做什么,因为上载的标准语义是multipart / form-data(用于wget和curl),但是我不相信上载恢复语义是普遍同意的。例如,YouTube和Nginx可能会有不同的做法。
罗布

1

取决于用于传输的协议。但是curl使用http并按文件中出现的顺序顺序传输数据。因此,curl可以根据部分完成的传输的文件大小来恢复。实际上,您可以通过创建一个长度为N(长度为N)的文件并要求它将该文件视为部分完成的下载(然后丢弃前N个字节)来欺骗它跳过前N个字节。

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.