none
ファイルの書込みと、読み出しを同時に行った時のOS内の処理はどうなる?

    質問

  • 現在C++でアプリケーションを作成していますが、SSDに対してファイルを書き込む処理と、ファイルを読み出す処理が同時期に実行される時があります。このとき書込みファイルと読み出しファイルは別ファイルになります。
    (マルチスレッドでプログラムしてあります)

    大きなファイル(1MBytes程度)の書込みを実行している時に、小さなファイル(数10Kbytes)の読み出しをしたいのですが、読み出しがどの位の時間で終わるのか気になっています。

    この場合、OS内部で書込みと読み出しを並行で処理しているものだと思っていますが、どの位の細かい処理単位で書込みと読み出しが行われるのでしょうか?(OS内部の話ですいません)
    また、SSDですと書込みの際にウェアレベリングが働く時があると思いますが、その時には読み出しも待たされる事になるのでしょうか?

    処理の優先度を変えるとか、書込み・読み出し単位を変えるとかである程度回避する事は可能でしょうか?

    SATAとOSの仕様が分からないので質問させていただきました。

    以上、よろしくお願いします。


    2018年3月28日 10:06

回答

  • > 私の元々の質問の意図ですが、
    > SSDからのファイル読み込みを一定時間内(たとえば30msec以内)で行いたいとして、
    > 同時に別ファイルの書込みがあった場合、
    > 読み込み時間を確保できるか・30msecよりも大きく遅くなる事があるかが
    > 知りたかったところでした。

    例として出してるだけかもしれませんが、"30msec" はかなりシビアだと思います。
    Windows における個々の処理実行単位は「スレッド」ですが、それらスレッドのどれを実行させるかは、NT カーネル内のスケジューラが管理しています。
    で、スケジューラは実行するスレッドに対して「クォンタム (Quantum)」という単位の「時間」を割り当てる訳ですが、大抵の場合ユーザ モード スレッド実行時に割り当てられるクォンタムは、1 とか 2 とかそんなレベルだったと思います。
    1 クォンタムの「時間」はクロック割り込み間隔や Windows バージョン等に依存して変化しますが、大抵の Windows PC では 1 クロック は 15msec で 1 クォンタムは 2 クロックだったと記憶しています。(あんまり自信ない。)
    つまり 1 クォンタムが 2 クロックの Windows PC では、ユーザ モード スレッド実行時に割り当てられたクォンタムが 1 だった場合、30msec 以内での処理の完了を前提とするのは無謀ということになります。
    (上記は、あくまでも私の当てにならない記憶を基にした例ですけど。)
    ファイル I/O の場合、Cached I/O を前提とすると先の返信で説明したように "Lazy Write" を考慮する必要があるので、30msec は非現実的な時間だと思います。

    タイミング的にシビアなプログラムを開発する場合には、この手の問題はよくある話だと思いますが、私に言わせれば、時間を気にした実装をするからこの手の問題が発生するのだと思います。
    要するに「時間」を気にするのではなく「タイミング」に着目すれば、大抵のこの手の問題は解決できると思っています。

    具体的には。。。。

    > 大きなファイル(1MBytes程度)の書込みを実行している時に、
    > 小さなファイル(数10Kbytes)の読み出しをしたいのですが、
    > 読み出しがどの位の時間で終わるのか気になっています。

    上記を例にすると、大きなファイルの書き込み処理を行うスレッドを A、小さなファイルの読み出しを行うスレッドを B とした場合、スレッド A とスレッド B 間でお互いが期待する処理が終わっているかを確認するための同期オブジェクトを生成し、それで排他制御を行えばいいだけでは?
    要するにスレッド A では、スレッド B での読み出しが自身の期待するところまでの処理を完了した時点で、書き込み処理を開始するように排他制御を行えば、「時間」を気にする必要はなくなると思います。
    つまり同期オブジェクトで「タイミング」を取ることだけに着目すればいい。
    この手のすべての問題が上記のような方法で解決できるとは思いませんが、「時間」に依存する実装をしてしまうと、結局はその「時間」の縛りに苦しむことになると思います。
    • 回答としてマーク Masato_55 2018年3月30日 1:10
    2018年3月29日 7:52

すべての返信

  • 直接的な回答ではありませんが、Windows Vista以降であればタスクマネージャーのパフォーマンスタブに「リソースモニター」が用意されているので起動してみてください。

    現代のオペレーティングシステムでは常時大量のファイルアクセスを捌いています。それらはもちろんReadもWriteもありますが、IOスケジューラは経験に基づいて実行順序を決定しています。その中で一般論ではありますが、Readの場合、Read結果を待ち受けているアプリケーションがあるためある程度優先されますし、Writeの場合、Writeが完了してもその結果に依存している処理は比較的少ないためReadよりは優先度が下げられます。
    ですので、一般的なアプリケーションであれば特に気になさらず普通にファイルアクセスを行えば、それなりの結果が得られることが期待できます。

    もちろん、I/O優先度を設定することもできますが、そこまでする必要はないと思いますので手順は特に紹介しません。

    2018年3月28日 13:35
  • やってみるのが一番良いのですが、数10MByte程度なら多分一瞬で終わります。
    リムーバブルメディアでない場合のディスクIOの多くはOSやハード側に実装されているメモリーに
    キャッシュしてしまうので非常に高速です。

    さて、これらのキャッシュがまったく働かない場合(最初の一発目のRead/Writeは効かないかも)、
    SATA2.0の300[MByte/s]というスペックが、話半分(150[MByte/s])だと考えても、
    1MByteを読み込むのに7[ms]程度つまり1000分の7秒しかかかりません。
    この間にスレッドを同期させてさらに読み込みを行うのがむしろ困難に感じるほどの短い時間であると言えます。

    2018年3月29日 1:30
  • "Process Monitor" ツールを使って調べれば、その疑問を解消できると思います。
    ------------------------------
    Process Monitor
    https://technet.microsoft.com/ja-jp/sysinternals/processmonitor.aspx
    ------------------------------

    ちなみにファイル アクセスに対する OS 内部での挙動は、そのファイルをどのようにオープンしたのかによって、全く異なってきます。
    ファイル キャッシュやバッファ使用の有無、共有/排他アクセス等の差異によって、NT カーネルはその挙動を変化させます。
    例えばファイル キャッシュを使用したファイル アクセスの場合、ファイル システム側からのその書き込み要求は "Lazy Write (遅延書き込み)" によって行われるので、リアルタイムに処理される訳ではありません。
    さらに言えば、ファイル I/O はファイル システム ドライバから "ボリューム デバイス I/O" → "ディスク デバイス I/O" → "バス I/O" と下層デバイスに渡されそれぞれのドライバで処理が行われるので、最上位のアプリ側から見えるような単純な構造ではないのです。
    "ボリューム デバイス" や "ディスク デバイス" に 3rd ベンダー製フィルタ ドライバが介在する場合は、さらに話が複雑になります。
    ですが、これら OS 内部の細かい挙動をアプリ開発者は気にする必要はありませんし、気にしたところでほとんど意味がありません。
    気にしなくて済むために、Win32 等の API セットが提供されているのです。
    なので、そんなところを気にする必要はないと思います。

    そもそもご質問されている内容は、作成されているアプリ側でのファイル I/O 排他処理に問題があるだけで、OS 内部の挙動にまで言及するような問題ではないと思います。
    (もしこの問題が OS 内部の挙動に起因しているなら、そこらじゅうで同様の問題が発生しているのでは?)
    2018年3月29日 2:33
  • お返事ありがとうございます。

    Process Monitorを使用すると、IOがどのように動いているか見ることが出来ました。
    OSは様々な条件を加味しながら細かくスケジューリングしてIO処理をしているのですね。参考になります。

    私の元々の質問の意図ですが、SSDからのファイル読み込みを一定時間内(たとえば30msec以内)で行いたいとして、同時に別ファイルの書込みがあった場合、読み込み時間を確保できるか・30msecよりも大きく遅くなる事があるかが知りたかったところでした。
    特にSSDへの書込みはウェアレベリングによって時々(?)時間がかかりそうな感じがしますが、その際に次の読み込みが遅れるのは回避できないのかなと。

    このようなシビアな処理をWindowsやSSDで実現するのは難しいでしょうか?

    2018年3月29日 6:23
  • > 私の元々の質問の意図ですが、
    > SSDからのファイル読み込みを一定時間内(たとえば30msec以内)で行いたいとして、
    > 同時に別ファイルの書込みがあった場合、
    > 読み込み時間を確保できるか・30msecよりも大きく遅くなる事があるかが
    > 知りたかったところでした。

    例として出してるだけかもしれませんが、"30msec" はかなりシビアだと思います。
    Windows における個々の処理実行単位は「スレッド」ですが、それらスレッドのどれを実行させるかは、NT カーネル内のスケジューラが管理しています。
    で、スケジューラは実行するスレッドに対して「クォンタム (Quantum)」という単位の「時間」を割り当てる訳ですが、大抵の場合ユーザ モード スレッド実行時に割り当てられるクォンタムは、1 とか 2 とかそんなレベルだったと思います。
    1 クォンタムの「時間」はクロック割り込み間隔や Windows バージョン等に依存して変化しますが、大抵の Windows PC では 1 クロック は 15msec で 1 クォンタムは 2 クロックだったと記憶しています。(あんまり自信ない。)
    つまり 1 クォンタムが 2 クロックの Windows PC では、ユーザ モード スレッド実行時に割り当てられたクォンタムが 1 だった場合、30msec 以内での処理の完了を前提とするのは無謀ということになります。
    (上記は、あくまでも私の当てにならない記憶を基にした例ですけど。)
    ファイル I/O の場合、Cached I/O を前提とすると先の返信で説明したように "Lazy Write" を考慮する必要があるので、30msec は非現実的な時間だと思います。

    タイミング的にシビアなプログラムを開発する場合には、この手の問題はよくある話だと思いますが、私に言わせれば、時間を気にした実装をするからこの手の問題が発生するのだと思います。
    要するに「時間」を気にするのではなく「タイミング」に着目すれば、大抵のこの手の問題は解決できると思っています。

    具体的には。。。。

    > 大きなファイル(1MBytes程度)の書込みを実行している時に、
    > 小さなファイル(数10Kbytes)の読み出しをしたいのですが、
    > 読み出しがどの位の時間で終わるのか気になっています。

    上記を例にすると、大きなファイルの書き込み処理を行うスレッドを A、小さなファイルの読み出しを行うスレッドを B とした場合、スレッド A とスレッド B 間でお互いが期待する処理が終わっているかを確認するための同期オブジェクトを生成し、それで排他制御を行えばいいだけでは?
    要するにスレッド A では、スレッド B での読み出しが自身の期待するところまでの処理を完了した時点で、書き込み処理を開始するように排他制御を行えば、「時間」を気にする必要はなくなると思います。
    つまり同期オブジェクトで「タイミング」を取ることだけに着目すればいい。
    この手のすべての問題が上記のような方法で解決できるとは思いませんが、「時間」に依存する実装をしてしまうと、結局はその「時間」の縛りに苦しむことになると思います。
    • 回答としてマーク Masato_55 2018年3月30日 1:10
    2018年3月29日 7:52
  • お返事ありがとうございます。素早い反応で感謝しています。

    この手の処理を制御するにはOSに任せるのではなくてアプリでコントロール(排他制御)するしか無さそうと言うことですね。

    あと、最初の質問のうち、疑問だった、

    >また、SSDですと書込みの際にウェアレベリングが働く時があると思いますが、その時には読み出しも待たされる事になるのでしょうか?

    に、関しては何か分かるでしょうか?

    2018年3月30日 1:10
  • > また、SSDですと書込みの際にウェアレベリングが働く時があると思いますが、
    > その時には読み出しも待たされる事になるのでしょうか?

    ウェアレベリングについては全然詳しくないので、以下は参考程度に。。。
    (私とは別の意見をお持ちの回答者さんもいらっしゃるようなので。)

    ウェアレベリングは、ハードウェア側での対応とソフトウェア側での対応 (とその両方) があると思います。
    ハードウェア側での対応を考えた場合、ウェアレベリング機能が実施するためには、SSD 等のストレージ側が PC に対して何らかの割込みを上げる必要があると思います。
    ハードウェア側からの割込み要求は、ユーザ モードあるいはカーネル モードで処理中のスレッドよりもずっと高い優先順位が与えられているので、現在実行中のクォンタムを使い切っていないスレッドであっても、一時待たされることになります。
    つまり、ファイルからの読み出し処理を行っているスレッドが実行中であったとしても、ウェアレベリングに限らず何らかのハードウェア割込みが発生した時点で待たされる。。。ということです。
    (具体的な事例は思い浮かびませんが) 頻繁に割込みを上げるハードウェアが接続されている PC では、実行中のスレッドが待たされる確率は高くなると考えられます。
    ソフトウェア側でのウェアレベリングでも、基本的には同じような原理で待たされる思います。
    例えば Windows システムでは仮想メモリを実現するために、ページ ファイルを利用することで各プロセスが物理メモリを効率的に使用できるよう工夫されています。
    これは、現在実行中のスレッドが必要とするデータが、そのタイミングにおいて必ずしも物理メモリ上に存在しないということを意味し、そのような場合は必要に応じてページ ファイルへの読み書き (ページ フォールト) が行われるということです。
    実はこのページ フォールトはとても頻繁に発生しており、そのたびに、現在物理メモリ上にあるアクセス頻度の低いコードやデータがページ ファイルに退避 (ページ アウト) され、ページ ファイル内にある現在必要とされるコードやデータが物理メモリ上にロード (ページ イン) されます。
    このページ フォールト機能はソフトウェア割込みにより実現されていますが、ハードウェア割込みよりもソフトウェア割込みの方が優先順が低いという点を除けば、基本的な動作はハードウェア割込みのときと同じはずです。
    で、ソフトウェア側でのウェアレベリングの実現も、ページ フォールトと同様の仕組みが必要になると思いますので、やっぱり待たされるんぢゃないかな。。。。

    Windows Embedded では古くから EWF (Enhanced Write Filter) というファイル システム フィルタ ドライバが提供されており、こいつがファイル システム内でのウェアレベリング「みたいな機能」を実現してたような。(二日酔いなので自信ない。)
    Windows 10 からは EWF に相当する UWF (「猪木ぃぃーーーー」ぢゃなくって、Unified Write Filter) という機能が提供されているので、そこら辺を調べてみると面白いかも。(でもやっぱり違うかも。)
    ----------------------------------------
    統合書き込みフィルター (Unified Write Filter (UWF)) について
    https://blogs.technet.microsoft.com/askcorejp/2016/03/25/unified-write-filter-info/
    ----------------------------------------

    というわけで、私の拙い理解。。。。というか「脳内妄想仕様」では、いずれの場合にせよ待たされるのではないかと。。。。

    ちなみに、ハードウェアの割込み要求がソフトウェア処理も優先される「仕組み」については、下記サイトが参考になると思います。
    ----------------------------------------
    【 デバドラ講座11 : スケジューリングと割り込み要求レベル 】
    https://sciencepark.co.jp/device_driver/dvdr/report-11/
    ----------------------------------------
    2018年3月30日 2:56
  • >> また、SSDですと書込みの際にウェアレベリングが働く時があると思いますが、
    >> その時には読み出しも待たされる事になるのでしょうか?

    >ウェアレベリングについては全然詳しくないので、以下は参考程度に。。。

    >というわけで、私の拙い理解。。。。というか「脳内妄想仕様」では、いずれの場合にせよ待たされるのではないか>と。。。。

    途中かなり省略させていただきました。様々な観点での指南ありがとうございます。
    私はSSD内部で行われるウェアレベリングを気にしていましたが、これも待たされることになりそうですし、それ以外でも様々な要因で、アプリケーションの動作は待たされることになりそうですね。

    ほぼ全てのコードを書くマイコンの組み込み系では、この辺りある程度予測が可能ですが、汎用OSが入ってくるとこの辺はなかなか思うようには行きませんね。

    ありがとうございました。

    2018年4月2日 9:33
  • Windows のような汎用 OS 環境下では、「たぶんこぅ動いてくれるだろう」と希望的観測を前提としてコードを書くと、OS 様は必ず期待を裏切ってくれます。
    そもそも Windows は FIFO やラウンドロビン方式のような RTOS ではないので、自分で「同期」を取らない限りタイミングを期待しても無意味。。。。というのが私の経験則です。
    2018年4月3日 2:00