質問者
ファイルアクセスのテクニックについて

質問
-
Win32APIのCreateFile(),ReadFile(),WriteFile()を使ったファイルアクセスについての質問です。
開発環境やWindowsのバージョンには、あまり依存しないと思いますが、当方のメインツールがVC++なのと、色々なテクニックをご存じの方が多いと思い、こちらに投稿させて頂きました。
Windowsは、FILE_SHARE_READでオープンして書き込み中のファイルに対して、別プロセス等からそのファイルの読み出しを行うと、読み出されたデータの内容は保証されない(関数は成功しても、データは正しく読み出せていない場合がある)。
との事ですが、みなさんはこの様なファイルアクセスの場合でも正しく読み出し出来る様、どの様なテクニックで回避をされているのでしょうか?是非、ご伝授下さい。
すべての返信
-
ちょこころねにゃ さんからの引用 レスありがとうございます。 ですが、ファイルを書き出してる側のアプリはブラックボックスなので、読み出し側からは簡単にクローズ出来ないですし、そもそも書き込み中なのかも、簡単には分かりません。困ったものです…
ファイルを書き出してる側のアプリがブラックボックスなら基本的には打つ手なしですね.
書き込み側・呼び出し側,両方を自分で作れるなら,ファイルをメモリマップして,メモリアクセス経由で読み書きすることにすれば内容の同期は保証される(ある種の共有メモリとみなせる)のですが.
あとはスレッド間で同じメモリにアクセスするように,Interlocked APIを使ったり排他制御を使ったりします.
つまるところ,「ファイルを書き出してる側のアプリ」がそういう用途を想定し,かつトランザクションに関するプロトコルを事前に決めていないと無理な話です.見ず知らずのアプリに対して,そういうことができるとは思わない方が良いでしょう.
-
みなさまレスありがとうございます。
NyaRuRu さんからの引用 ファイルをメモリマップして,メモリアクセス経由で読み書きすることにすれば内容の同期は保証される これは、ファイルがネットワーク越しにあり、今回の様なシチュエーションの場合に、すごく悲惨な事になります。(実験済み)
NyaRuRu さんからの引用 「ファイルを書き出してる側のアプリ」がそういう用途を想定し…中略…無理な話です まったくもってその通りなのですが、別レスに書きましたが、読み込もうとしたファイルが書き出し中なのかは簡単には分からないので、乱暴な言い方ですが、読み出そうとした全てのファイルで「正しくないデータを読み出す可能性を孕んでいる」と言えます。
そのファイルのコンテナ的に破綻していればエラーの検出は可能でしょうが、そうで無ければ「文字化け」に近い状態なのでエラー検出は難しくなります。
じゃんぬねっと さんからの引用 ファイルアクセスのテクニックというよりは、「排他処理のテクニック」 相手も協力してくれれば、排他処理の問題に落ち着くのでしょうが、相手が非協力的(笑)な場合は諦めるしか無いのでしょうか…
具体例としては、ダラダラと計測ログを出力する他社のアプリのログビュアーを作っても、読み込んだデータの保証は無い訳です。
また、別の例では、WMEでエンコード出力中のファイルは、ファイル的にもコンテナ的にもまだクローズしていません。しかし、WMP等で再生可能ですが、正しいデータが再生されている保証が無い訳です。
IIJIMAS さんからの引用 CopyFile()でコピーできる所にコピーして読むのはだめでしょうか。 OSが「そーゆー使い方NO!」って言ってる訳ですから、ファイルをコピーしてもダメっぽいです。
ひょっとしたら、カーネルモードに近い側でファイルアクセスすれば良いのかもしれませんが、そこまでみなさんやっていらっしゃるのでしょうか?
-
ちょこころねにゃ さんからの引用 じゃんぬねっと さんからの引用 ファイルアクセスのテクニックというよりは、「排他処理のテクニック」 相手も協力してくれれば、排他処理の問題に落ち着くのでしょうが、相手が非協力的(笑)な場合は諦めるしか無いのでしょうか…
具体例としては、ダラダラと計測ログを出力する他社のアプリのログビュアーを作っても、読み込んだデータの保証は無い訳です。
うーん,諦めるしかないと書いたつもりなのですが,伝わってないでしょうか?
仮にカーネルモードでフラッシュしたとしても,ユーザモードでバッファリングされている場合はどうしようもないですし.
それとも,相手のプロセスはWeiteFileの前にバッファリングを行っていないことは分かっているのでしょうか?
そうだとしても,書き込みが複数回に分けて行われ,途中に無効状態がある場合はやはり問題になりますし,データ更新のプロトコルを事前に定めておかないとやっぱり無理だと思います.
自分で書き出し側・読み込み側の両方を作るのであれば,テクニック云々の話も出てくるでしょうが,書き出し側がブラックボックスなら,基本的に「諦めるしかない」で,テクニックでどうこうという問題ではないと思います.
諦めたくないのであればもはや相手のプロセスに介入するぐらいしか手はないんじゃないでしょうか?
ちょこころねにゃ さんからの引用 IIJIMAS さんからの引用 CopyFile()でコピーできる所にコピーして読むのはだめでしょうか。 OSが「そーゆー使い方NO!」って言ってる訳ですから、ファイルをコピーしてもダメっぽいです。
ひょっとしたら、カーネルモードに近い側でファイルアクセスすれば良いのかもしれませんが、そこまでみなさんやっていらっしゃるのでしょうか?
みなさんそういうときは諦めて何もしてないんじゃないかと.
それともOSの問題なんですか?
唯一,他プロセスが開いているファイルを (状態が破綻してもいいから) とにかくバックアップしたいという場合は,Volume Shadow Copy をつかってスナップショットを取ったりすることはありますが.
それにしても,今回のように破綻無く協調読み出しをしたいという目的は満たせません.
-
レスありがとうございます。
NyaRuRu さんからの引用 うーん,諦めるしかないと書いたつもりなのですが,伝わってないでしょうか? 失礼しました。諦めきれない性分なので…申し訳ない。
みなさんは「全てのファイルアクセスに対して、読み込んだデータが正しくないかも知れないけど、まあしょうがない。」って事で納得されてる(and/or 諦めている)訳なんでしょうか?
蛇足です…
NyaRuRu さんからの引用 書き込みが複数回に分けて行われ,途中に無効状態がある場合はやはり問題になりますし もし、誤解があると良くないので、具体的に書きますと、このシチュエーションでの「内容が保証されない」って言うのは、「単一ファイルに対して、プロセスAが4K分のデータの書き込みに成功して、プロセスBが4K分のデータの読み出しに成功しても、前者と後者の4Kのデータは等価である保証がない」と言う事です。(4Kじゃなくても、プロセスBが1Kしか読み出せなくても、プロセスAが書いた先頭1Kと読み出した1Kも等価では無い ってことですね)
-
外池と申します。
ちょこころねにゃさんが想定されている操作と、皆さんが「上手く行かないこともある」と懸念されている操作と、前提としている想定の範囲が違っているのではないかと思います。(だから、ちょっと議論がすれ違っている)
私は、少なくとも、シーケンシャルにデータをファイルを書き出すのみのプロセスを動かし、一方で、同一ファイルからシーケンシャルにデータを読み出すプロセスを立ち上げて動かすことをやっていますが、特に問題が起きたことはありません。特に読み出し側で、読み出し可能になっているファイルの末尾の位置をチェックして、読みこみの範囲をキチっと制御してやれば、問題が起きる可能性は、ちょと考えにくいです。(ちょこころねにゃさんの最後の文章を拝読すると、限りなくシーケンシャルっぽいのですが・・・、イケると思いますよ?)
一方で、昔風の呼び方になりますが「ランダムアクセス」のように、ファイルのいろんな位置に書き込み動作が行われるような場合、おそらく読み出しもいろんな位置からデータを拾うようなことになるかと思いますが、読み出したデータが、常に最新(ファイルの書き込み要求を最新のものまで反映している)かどうか保証されないと思います。それどころか、読み出している側のアプリにとって、書き出し側のアプリがファイルの先頭の方をイジったり、中間あたりのどこかを更新したり、そのような「上書き」操作をしていることは知りようがないですよね? (ファイルを吐き出しているアプリがこのようなことをやっている可能性がある、かつ、ブラックボックスなら、私も打つ手ナシだと思いますが)
用いている言語が違いますが、参考になれば・・・、
私が用いている環境は、Visual Baisc .Netで、ファイルの書き込みには.Net FrameworkのStreamWriterでASCIIEncodingを用いています。AutoFlushはTrueにして、Writeするごとにバッファから吐き出すようにしています。読み取りにはStreamReaderを使っています。計測器から転送されたデータを一度ファイルに書き出し、一方で、データ処理するソフトがファイルから読み出してリアルタイムで処理していくようなシステムなのですが、一度も問題が起きたことはありません。
-
外池です。
ここからは、気持ちの問題になってしまいますが・・・、ちょこころねにゃさんは、何を求めておられるのでしょうか?
A)「こうすれば上手くいくらしい」という具体的な実装の方法論としての解決方法でしょうか?
B)それとも、OS、コンパイラ、アプリなどの仕様を積み上げて、「これならば上手くいくことになっている」という、なんと言いますか、仕様上のロジックとしての解決方法でしょうか?
現在のところ、データを吐き出すアプリ側の動作がブラックボックスである、とのことですので、私としては、A)の線で、実証的に仕事を進められるしか方法はないと思います。B)は、ここまでの議論で破綻してしまっていますので、無理なのではないかと。
あと、暴論を言えば、「仕様」のハズが、実装のミスでそのように動いていないことも多々あるわけで、基本的にB)の考え方は「方針を立てる」時には良いのですが、最終的にはA)に頼らざるを得ないと思います。今の場合、「仕様」が「保証しません」というネガティブな情報なので、まぁ、どうしようもないわけですが。
私ならばA)で仕事をしますね・・・。A)で解決する線で、興味深いというか、情報をいただけると皆さんもアイデアを出しやすいと思う点は・・・、
データを吐き出すアプリは、毎秒、何バイトぐらいのデータをファイルに書き出すのでしょう?
そのファイルは、どのようなエンコードのデータなのでしょうか? バイナリー? ASCII? 日本語交じりのShift_JIS?
1行(1レコード)あたり何文字とか、何バイトとか、決まったフォーマットに整えられたファイルでしょうか?
さらに言えばですね・・・、
計測器が吐き出すログをリアルタイムに処理しても結果が「保証」はできないとしても、計測の進捗というか、上手く行っているかどうかの「目安」としては意味のあることではないかと思います。一通りの測定が終わって、ログのファイルがクローズされた後、確定のための処理を再度流せば、最終的な結果も「保証」できることになりませんか?
-
レスありがとうございます。PATIO さんからの引用 基本的にOSが保障していない上、あきらめると言うのに私も一票入れます。 ご意見ありがとうございます。
外池 さんからの引用 何を求めておられるのでしょうか? 冒頭の通り「みなさんはどうしてますか?」と出来るだけ大勢の方の意見をお聞きしたかっただけです。
レス頂いた様に「諦める」と言うのも、もちろん貴重な意見ですし(みなさん、諦めてるんだ… と)、「私はこうやって回避してますよ」と言う様な情報を頂ければ、願ったり叶ったりな訳です。
外池 さんからの引用 ログのファイルがクローズされた後、確定のための処理を再度流せば、最終的な結果も「保証」できることになりませんか? それは、運用上で「途中経過の信頼性と、最終結果の信頼性が何処まで必要なのか?」話になってしまいますので…外池さまの事例を否定している訳ではありません。
-
ファイルの状況が複雑さそうですが、
Volume Shadow Copy API を使用してファイルコピーして定番オープン・クローズでは駄目なのでしょうか。
私は使い方知りませんが・・・
http://msdn2.microsoft.com/en-us/library/aa384645.aspx
ご参考まで
-
レスありがとうございます。
言葉尻を突っついている意図はありませんので、それだけは誤解しないでくださいね。
ファイルを読み込む機能がある全てのアプリは、誰かが書き込み中のファイルを、書き込み中と気付かずに読んでしまう可能性が有る。その場合、データの内容の保証がない。
と言う仕様が存在します。ここで一つ補足すると、「読み込めた位置までのデータは正常だけど、ファイルとしては破綻している」では無く、「ファイルとして破綻していなくても、読み込めたデータ自体に意味がない可能性がある」と言うことです。
コンテナやデータ構造的にこれを検出出来れば、それに越したことはありませんが、それらでどうにもならない場合です。
で、アプローチとして(あくまで、ニュアンスとして捉えてください)
対策しない・諦める→表示や数値がおかしかったり、最悪アプリが暴走する可能性があるが、納得して貰い運用等で逃げる。
対策する・とことんやる→どんな汚い手を使ってでも、正しいデータを読んでくる or 読み込みを防止・警告する。
...etc?
と言う所に行き着くと思います。で、みなさんに、お伺いした次第です。
-
-
ちょこころねにゃ さんからの引用 対策しない・諦める→表示や数値がおかしかったり、最悪アプリが暴走する可能性があるが、納得して貰い運用等で逃げる。
対策する・とことんやる→どんな汚い手を使ってでも、正しいデータを読んでくる or 読み込みを防止・警告する。
...etc?
と言う所に行き着くと思います。で、みなさんに、お伺いした次第です。
ああ,やっと質問の意図が分かりました.
ちょこころねにゃ さんからの引用 レスありがとうございます。 ですが、ファイルを書き出してる側のアプリはブラックボックスなので、読み出し側からは簡単にクローズ出来ないですし、そもそも書き込み中なのかも、簡単には分かりません。困ったものです…
私はこの書き込みを読んで,「(一般論ではなく)特定のブラックボックスな書き出しアプリケーションがあって,そのアプリケーションが書き出すファイルを並行的に読み出したいのだが,そこで困っていて何とかしたい」という質問だと思ってました.
なのでそのケースについてずっと書いていたのですが……
そうではなくて,ちょこころねにゃさんの質問は,最初からずっと CreateFile(),ReadFile(),WriteFile() に関する一般論の話だったのですね……
『インサイド Microsoft Windows 第4版 下』の以下の章あたりが今回の事例に関係してそうですね.
-
11.1.3 キャッシュの整合性
- 12.2.2 リモート FSD
-
12.4.2 NTFSの高度な機能 -> 変更ロギング
ローカルファイルへのアクセスであれば,外池さんの書かれているようなケースでも,「11.1.3 キャッシュの整合性」に書かれているような理由によって基本的には問題は起きないかと思います.まあこの辺りは CreateFile/ReadFile/WriteFile のドキュメントを信用するか,それとも OS の実装を信頼するかの話になりますが.
また,ローカルのNTFSであれば,ある時点以降,ファイルの内容が更新されたかどうか監視できるので,ReadFile での読み出しが WriteFile と交差した可能性があるかどうか判定するという手も使えるかもしれません.
リモートのファイルアクセスの場合はファイル共有に使っているプロトコル/相手のファイルシステムにもよりますが,OpLock (便宜的ロック) を使うのが一般的かと思います.(ローカルの NTFS にも OpLock は使えますが)
.NET Framework から OpLock を使用する方法については,Jeffrey Richter が MSDN Magazine に寄稿していますので,それが参考になるかと思います.
-
-
ちょこころねにゃ さんからの引用 ファイルを読み込む機能がある全てのアプリは、誰かが書き込み中のファイルを、書き込み中と気付かずに読んでしまう可能性が有る。その場合、データの内容の保証がない。
と言う仕様が存在します。ここで一つ補足すると、「読み込めた位置までのデータは正常だけど、ファイルとしては破綻している」では無く、「ファイルとして破綻していなくても、読み込めたデータ自体に意味がない可能性がある」と言うことです。
コンテナやデータ構造的にこれを検出出来れば、それに越したことはありませんが、それらでどうにもならない場合です。
で、アプローチとして(あくまで、ニュアンスとして捉えてください)
対策しない・諦める→表示や数値がおかしかったり、最悪アプリが暴走する可能性があるが、納得して貰い運用等で逃げる。
対策する・とことんやる→どんな汚い手を使ってでも、正しいデータを読んでくる or 読み込みを防止・警告する。
...etc?
と言う所に行き着くと思います。で、みなさんに、お伺いした次第です。
前提として書き込み側に手を入れる事ができない、もしくは完全に外部のプログラムでブラックボックスである
がないと話は変わって来ると思いますよ。
私も以前に書いていますが、本来は書き込み側も含めた制御が必要でそこまで含めて書き込み中の読み込みを
うまく避ける事ができるようなシステム化をすると言う前提ならあきらめる必要はないですし、そのからくりをひねり出す
と言うのも有りでしょう。結局は何処まで表示されているデータを保障するかと言う話になりますけれども。
で、前提条件が生きているなら私としてはOSが保障していない事を保障するなんて口が避けても言えませんから
保障できませんという結論になります。その部分に対策するよりも運用でリカバリーできるような仕組みを考えた方が
現実的と言う話です。参照側であれば、最悪落っこちても再起動すればデータを参照する事はできるでしょうから。
結局、結果を保障できない以上はとことんやっても保障できない事には変わりないと言うのが私の意見です。