失敗したファイル転送を再開できるプログラムは、データの追加を開始する場所をどのように知っていますか?


23

以下のようないくつかのファイルコピープログラムrsyncとは、curl失敗した転送/コピーを再開する能力を持っています。

これらの失敗の多くの原因が存在する可能性があることに注意してください。場合によっては、プログラムができない「クリーンアップ」を行うことができます。

これらのプログラムが再開すると、正常に転送されたファイル/データのサイズを計算し、ソースから次のバイトの読み取りを開始し、ファイルフラグメントに追加するだけのようです。

たとえば、宛先に「作成」されたファイルフラグメントのサイズは1378バイトなので、元のバイト1379から読み取りを開始し、フラグメントに追加します。

私の質問は、バイトがビットで構成されており、すべてのファイルのデータがきれいなバイトサイズのチャンクにセグメント化されているわけではないことを知っています。これらのプログラムは、データの追加を開始することを選択したポイントが正しいことをどのように知るのですか?

宛先ファイルの書き込み時に、プログラム、カーネル、またはファイルシステムレベルで発生するSQLデータベースに似た何らかのバッファリングまたは「トランザクション」が行われ、クリーンで整形式のバイトのみが基礎となるブロックデバイスに送られるようにしますか?
または、プログラムは最新のバイトが不完全である可能性があると想定しているので、不良であると想定して削除し、バイトを再コピーしてそこから追加を開始しますか?

すべてのデータがバイトとして表されるわけではないことを知っているため、これらの推測は間違っているように見えます。

これらのプログラムが「再開」するとき、適切な場所から開始していることをどのようにして知るのでしょうか?


21
「すべてのファイルのデータがクリーンバイトサイズのチャンクにセグメント化されているわけではありません」ファイルに1バイト未満を書き込むにはどうすればよいですか?
ムル

17
1バイト未満を書き込むことができるシステムコールはありません。また、ディスク自体に関しては、512バイトブロック(または4096バイトブロック)未満を書き込むディスクは今日ないと思います。
ムル

8
いいえ、最小値は1バイトです。正常なアプリケーションは4KBまたは8KBのチャンクを使用しますhead -c 20480 /dev/zero | strace -e write tee foo >/dev/null。OSはそれらをバッファリングし、さらに大きなチャンクでディスクに送信します。
ムル

9
@the_velour_fog:どうやって1ビットだけ書くのfwrite()
psmears

9
すべての実用的な目的のために、データバイトで構成され、すべてが最小単位としてそれらで動作します。一部のシステム(主にgzip、h264などの圧縮に関連する)は、バイトから個々のビットをアンパックしますが、オペレーティングシステムとメモリ操作はバイトレベルです。
pjc50

回答:


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)ファイルブロック情報の更新までずっと進んでいる場合、それらのバイトはすべて良好であることがわかります。それから、前の段階にあったバイトがディスクに
到達

4
@the_velour_fogそして最後から2番目の段落を補完するために- ジャーナリングを実装していないファイルシステムを使用している場合、実際に「壊れた」データを取得して、履歴書を失敗さ、エラー与えることなく文字化けしたファイル生成することができます。これは、特に高遅延デバイス(フロッピーなど)用に設計されたファイルシステムで特に発生していました。ファイルシステムがこのように信頼できない場合でも、これを回避するためのいくつかのコツがありましたが、それを補うためによりスマートなアプリケーションといくつかのシステムで間違っていたかもしれないいくつかの仮定が必要でした。
ルアーン

この回答は、ファイルシステムでのジャーナリングの有用性を誇張しています。ユーザー空間アプリケーション(経由)やハードドライブコントローラー(多くの場合、「エンタープライズ」ドライブであっても)を含むトランザクションセマンティクスをすべて実装しない限り、確実に動作しませんfsync。なしfsync直感的に注文し、アトミックされている多くのファイル操作は、されている保証はない POSIXによって、このようなこと:で開かれたファイルは、O_APPENDファイルの整合性への最も重要な鍵はカーネルVFSシステムとディスクキャッシュされている実際にはなどなしのものとは異なる動作をする可能性があります。それ以外はほとんどが綿毛です。
user1643723

11

注:私は、ソースrsyncや他のファイル転送ユーティリティを見ていません。

ファイルの末尾にジャンプしてその場所の位置をバイト単位で取得するCプログラムを記述するのは簡単です。

両方の操作は、標準のCライブラリ関数への単一の呼び出しで行われlseek()lseek(fd, 0, SEEK_END)ファイル記述子のための開かれたファイルの長さを返しfdバイトで測定し、)。

これがターゲットファイルに対してlseek()実行されると、ソースファイルに対して同様の呼び出しが実行され、適切な位置にジャンプしますlseek(fd, pos, SEEK_SET)。その後、ソースファイルの以前の部分が変更されていないと識別されたと仮定して、転送はその時点で続行されます(異なるユーティリティは異なる方法でこれを行うことがあります)。

ファイルはディスク上で断片化される場合がありますが、ファイルシステムは、アプリケーションがファイルをバイトのシーケンシャルシーケンスとして認識することを保証します。


ビットとバイトに関するコメントでの議論について:ディスクに書き込まれるデータの最小単位はバイトです。1バイトでは、少なくとも1 ブロックのデータをディスクに割り当てる必要があります。ブロックのサイズは、ファイルシステムのタイプと、場合によってはファイルシステムの初期化時に管理者が使用するパラメーターにも依存しますが、通常は512バイトから4 KiBの間です。書き込み操作は、カーネル、基盤となるCライブラリ、またはアプリケーション自体によってバッファされ、実際のディスクへの書き込みは、最適化として適切なブロックサイズの倍数で行われる場合があります。

ファイルに単一ビットを書き込むことはできません。書き込み操作が失敗した場合、ファイルに「半分書き込まれたバイト」が残ることはありません。


おかげで、書き込み操作が失敗した場合に保証するのは何ですか-半分の書き込みバイトを残さないでしょうか?カーネルバッファリングミュールが記述していたものですか?-すなわち、プロセスがカーネルへの8KBチャンクの送信の途中で中断され、予期せず終了した場合-その8KBチャンクはカーネルに到達しません-しかし、カーネルとファイルシステムに到達した以前のものは正常であると想定できますか?
the_velour_fog

6
@the_velour_fogは、プロセスがI / Oシステムコールの途中で中断できないため、この種の予期しない終了は発生しません(それが、NFSファイルのファイルシステムアクセスコールで停止できないプロセスを見るのは珍しいことではありません)。参照:unix.stackexchange.com/q/62697/70524
muru

2
正確なタイミングでシステムの電源が切れると、問題が発生する可能性があります。これにより、ファイルの最後の書き込みポイントでガベージが発生することがあります。データベースの設計では非常に難しい問題です。ただし、「有効」または「無効」の通常の最小単位はディスクブロックです。
pjc50

1
@the_velour_fog 半分書き込まれたバイトは(全体として)書き込まれたとして記録されないため、「半分書き込まれたバイト」(より正確には、半分書き込まれたバイトのブロック)を取得できないほどではありません。)-LSerniの回答のステップ(3)および(4)を参照してください。
-TripeHound

5

curlとrsyncのようなプログラムは非常に異なるため、これは基本的に2つの質問です。

curlのようなHTTPクライアントの場合、現在のファイルのサイズを確認Content-Rangeし、リクエストとともにヘッダーを送信します。サーバー206は、200(成功)の代わりにステータスコード(部分コンテンツ)を使用してファイルの範囲の送信を再開し、ダウンロードを再開するか、ヘッダーを無視して最初から開始し、HTTPクライアントはすべてを再ダウンロードする以外の選択肢はありません再び。

さらに、サーバーはContent-Lengthヘッダーを送信する場合と送信しない場合があります。一部のダウンロードでは、割合とファイルサイズが表示されないことに気づいたかもしれません。これらは、サーバーがクライアントに長さを通知しないダウンロードであるため、クライアントはダウンロードした量のみを知っていますが、後に続くバイト数は知りません。

いくつかのダウンロードマネージャーはContent-Range、開始位置停止位置でヘッダーを使用して、さまざまなソースからファイルを一度にダウンロードします。これにより、各ミラー自体がネットワーク接続よりも遅い場合、転送が高速化されます。

一方、rsyncは、増分ファイル転送用の高度なプロトコルです。サーバー側とクライアント側でファイルの一部のチェックサムを生成し、どのバイトが同じかを検出します。次に、差分のみを送信します。つまり、ダウンロードを再開することはできませんが、ファイルを再ダウンロードせずに非常に大きなファイルの途中で数バイトを変更した場合、変更されたバイトをダウンロードすることさえできます。

転送を再開するために作成された別のプロトコルはビットトレント.torrentです。ファイルには、ファイルからのブロックのチェックサムのリストが含まれているため、ブロックをダウンロードして、任意の順序で異なるソースから並行して検証できます。

rsyncとbittorentはディスク上の部分的なデータを検証しますが、HTTPダウンロードの再開は検証しません。そのため、部分データが破損している疑いがある場合は、整合性を確認する必要があります。つまり、最終ファイルのチェックサムを使用します。ただし、ダウンロードを中断したり、ネットワーク接続を失ったりしても、通常、部分的なファイルは破損しませんが、転送中の電源障害は発生します。


4

TL; DR:使用するプロトコルで許可されない限り、できません。

プログラムは常に任意の場所から再開できるわけではありません。たとえば、HTTPリクエストは、サーバーがサポートし、クライアントがそれを実装している場合にのみ再起動可能です。これは普遍的ではありません。サーバーがサポートしている場合、プログラムはプロトコルの一部として単純に尋ねることで転送を再開できます。通常、ダウンロードディレクトリには部分的な転送が表示されます(通常、「。partial」拡張子などが付いています)。

ファイルのダウンロードが一時停止または停止した場合、クライアントはファイルをディスクに書き込むことができ、再開する場所を明確に把握できます。一方、クライアントがクラッシュしたり、ファイルへの書き込みエラーが発生した場合、クライアントはファイルが破損していると見なして最初からやり直す必要があります。BitTorrentは、ファイルを「チャンク」に分割し、どのファイルが正常にダウンロードされたかを追跡することで、これを多少軽減します。再実行する必要があるのは、いくつかのチャンクです。Rsyncは同様のことを行います。

プログラムは、コンテンツが同じであることをどのように知るのですか?1つの方法は、一部の識別子がクライアントとサーバー間で同じであることを確認することです。これのいくつかの例はタイムスタンプとサイズですが、プロトコルに固有のメカニズムあります。識別子が一致する場合、クライアントは再開が機能すると想定できます。

より明確な検証が必要な場合は、HTTPと友人を最初に選択しないでください。ダウンロードのチェックサムをサーバーのコンピューターのチェックサムと比較できるように、ファイル全体および各転送チャンクのチェックサムまたはハッシュも含むプロトコルを使用する必要があります。一致しないものはすべて再ダウンロードされます。繰り返しますが、BitTorrentはこの種のプロトコルの例です。rsyncはオプションでこれを行うこともできます。


rsyncの例では、rsyncプロトコルが1つしかないため、簡単になります。httpダウンロードの場合、標準として範囲要求があります。アップロードの標準的なセマンティクスはmultipart / form-data(wgetおよびcurl)であるため、curlが実際に再開アップロードで行うことを知りたいのですが、アップロード再開セマンティクスが普遍的に合意されているとは思いません。たとえば、YouTubeとNginxはこれを異なる方法で行う場合があります。
ロブ

1

転送に使用されるプロトコルに依存します。しかし、curlはhttpを使用し、ファイルに表示される順序でデータを順番に転送します。したがって、カールは部分的に完了した転送のファイルサイズに基づいて再開できます。実際、長さN(何でも)のファイルを作成し、そのファイルを部分的に完了したダウンロードとして扱うように要求することで、最初のNバイトをスキップするように仕掛けることができます(そして最初のNバイトを破棄します)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.