カーネル バッファーの同期、fsync、および fdatasync 関数を同期します。

転載アドレスクリックしてリンクを開く

カーネル バッファを同期する

1. バッファーの紹介

人生における 3 つの主要な錯覚の 1 つ: 関数 write() を呼び出すとき、関数が戻ると、データがファイルに書き込まれていると考えます. しかし、この概念は巨視的です. 実際には、 オペレーティング システムは / O (ディスクファイルなど) の場合、I/O の効率を確保するために、通常、専用の領域 (メモリまたは独立した I/O アドレス空間) がカーネル内の I/O データバッファーとして使用されます。デバイスと CPU の間でデータをキャッシュするために使用されるため、低速デバイスと高速 CPU が連携して、低速入出力デバイスが CPU を占有することを回避できます。システムコールを減らし、CPUの作業効率を向上させます。

2.非同期書き込み()

従来の UNIX または LINUX システムでは、設計中にカーネル バッファが使用され、高速バッファまたはページ高速バッファが使用され、ほとんどのディスク I/O はバッファを介して実行されます. データをファイルに書き込む場合、通常、カーネルは最初に書き込みデータをコピーしますバッファーの 1 つに追加し、バッファーがまだいっぱいになっていない場合は、出力キューには入れられませんが、満杯になるまで、またはカーネルが他のディスク ブロック データのためにバッファーを再利用する必要があるときまで待機します。出力キュー; その後、キューの先頭に到達すると、実際の I/O 操作が実行されます. この出力方法は、遅延書き込みと呼ばれます. データを書き出すために write() 関数が呼び出されると、データがバッファに書き込まれると
、 (キー: バッファに書き込むだけ), 関数はすぐに戻ります. このときに書き込まれたデータは read() で読み戻すことができます, また、他のプロセスで読み取ることもできますが, 書き込まれたことを意味するものではありません.これは、ファイルを閉じるために close() を呼び出した後でさえもそうである可能性があります. バッファ内のデータはまだ出力を待っている可能性があるためです. したがって, 実際にデータが書き込まれるという観点から
,ディスク, write() による書き込み ファイル データは外部ストレージ デバイスと完全には同期されていません. 同期が取れなくなる時間間隔は非常に短く、通常は数秒から数十秒ですが, 書き込まれたデータの量とI/O データ バッファの状態. 同期の時間間隔は非常に短いですが、この間に停電やシステム クラッシュが発生した場合、ディスクへの書き込みが遅れると、書き込まれたデータが失われます。注: カーネルは
バッファ内のデータを標準入力ディスクに「書き込み」ます。ここでの「書き込み」とは、バッファ内のデータをディスク ファイルに移動することではなく、ディスク ファイルにコピーすることです。つまり、この時点ではまだバッファの内容のバックアップがディスク ファイルに保持されています. 図 1 に示すように、この設計を行うのが合理的です. ディスク ファイルに書き込むと、ディスクが壊れているか、いっぱいになっているなど. , つまり, データを送信できません. バックアップがない場合, データは失われません. つまり, カーネルはバックアップされたデータを削除する前にディスクへの書き込みの完了を待ちます. このプロセスも図 1 ディスク上の実際のファイル システムがバッファ キャッシュ内のコンテンツの一貫性

確保するためのデータ転送プロセスの概略図、

UNIX システムでは 3 つの機能が提供されています。関数: sync、fsync、および fdatasync。

3.同期機能

ヘッダー ファイル: #include<unistd.h>
定義関数: void sync(void);
戻り値: 成功した場合は 0 を返し、エラーが発生した場合は -1 を返し、エラーを示すために errno を設定します
関数の説明:
sync はバッファリングを担当しますシステム ゾーン内のデータは、データの一貫性と同期を確保するためにディスクに「書き込まれます」 注: 同期機能は、変更されたすべてのブロック バッファを書き込みキューに入れるだけで、その後戻ります。実際の I/O を待ちません。操作終了. 同期関数を呼び出した後, データが安全にディスク ファイルに送信されたとは思わないでください. 問題があるかもしれませんが, 同期関数を知ることはできません. システム デーモンは通常, 同期関数を一度に 1 回呼び出します. while
. カーネルのブロックキャッシュを定期的に更新してください. UNIX システムでは, システムデーモンの更新は定期的に (通常は 30 秒ごとに) sync 関数を呼び出します. コマンド sync(1) も sync 関数を呼び出します.

4. fsync 関数

ヘッダーファイル: #include<unistd.h>
定義関数: int fsync(int filedes);
戻り値: 成功した場合は 0 を返し、エラーが発生した場合は -1 を返し、エラーを示すために errno を設定します 関数の
説明:
sync 関数とは異なりますfsync 関数は、ファイル記述子 filedes によって指定された単一のファイルに対してのみ機能し、記述子 fildes に接続されたファイルのすべての変更されたデータ (カーネル I/O バッファー内のデータを含む) を外部の永続的なメディアに強制的に転送します。 fildes で指定されたファイルのすべての情報を更新し, ディスク書き込み操作の終了を待ってから戻ります. fsync() を呼び出すプロセスは, デバイスが転送が完了したことを報告するまでブロックされます. この fsync は安全. プログラムがデータを書き込んだ後
, 後続の処理に進む前に書き込まれたデータがディスクに書き込まれたことを確認する必要がある場合, fsync() を呼び出す必要があります. たとえば, データベースアプリケーションは通常, write ( ) 主要な取引データを保存する. これにより、データのセキュリティと信頼性がより保証されます.

5. fdatasync 関数

ヘッダーファイル: #include<unistd.h>
定義関数: int fdatasync(int filedes);
戻り値: 成功した場合は 0 を返し、エラーが発生した場合は -1 を返し、エラーを示すために errno を設定します 関数の
説明:
fdatasync 関数は類似していますfsync関数に影響を与えますが、ファイルのデータ部分にのみ影響を与えるため、ファイル自体の特性データを除いて、ユーザーが物理ストレージデバイスに書き込むデータを強制します.これにより、ファイルのデータ転送量を適切に削減できます.データに加えて、fdatasync ファイルのプロパティも同期的に更新されます。

6. エラーコード

EBADF: ファイル記述子が無効であるか、ファイルが閉じられています
EIO: 読み取りおよび書き込み中にエラーが発生しました
EROFS、EINVAL: ファイルが存在するファイル システムは同期をサポートしていません。

7.fflush()与fsync()的联系

内核I/O缓冲区是由操作系统管理的空间,而流缓冲区是由标准I/O库管理的用户空间.fflush()只刷新位于用户空间中的流缓冲区.fflush()返回后,只保证数据已不在流缓冲区中,并不保证它们一定被写到了磁盘.此时,从流缓冲区刷新的数据可能已被写至磁盘,也可能还待在内核I/O缓冲区中.要确保流I/O写出的数据已写至磁盘,那么在调用fflush()后还应当调用fsync().

8.综述

虽然延迟写减少了磁盘读写次数,但是却降低了文件内容的更新速度,使得欲写到文件中数据在一段时间内并没有写到磁盘上。当系统发生故障时,这种延迟可能造成文件更新内容的丢失。

おすすめ

転載: blog.csdn.net/edw200/article/details/73527762