質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1955閲覧

Python 「with open」を使って開いたファイルは自動的にflushされますか?

_kari_

総合スコア34

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2023/05/09 06:23

ファイルにバイナリデータを書き込み、そのファイルを後続の処理で使うスクリプトを書いています(後続の処理ではどうしても実ファイルが必要になります)。
そのため、後続の処理に行く前にファイルへの出力が確実にflushされていてほしいのですが(fsync/fdatasyncまでは恐らく不要)、

Python

1with open(file_path, "wb") as f: 2 f.write(buffer) 3 f.flush() # ←これは必要?

上のコードのように組み込み関数open()をwith文とともに使った場合、flush()を明示的に実行しないとflushされないのでしょうか?
それとも、withブロックを抜けてcloseされるのに伴って自動的にflushも行われるのでしょうか?

現在のPythonではclose()と記述すると内部的にflushの処理も走るので、with文による自動的なcloseでもflushしてくれるのかなと思っていたのですが、
https://seizo-igawa.hatenablog.jp/entry/2015/05/09/113405
こちらのウェブページでcodecs.open()を使った場合は明示的にflush()を呼ばないとうまく動作しなかったという情報があり、まさか組み込み関数のopen()も同じなのか?と対応に困っています。

codecsのソースコードを読むぶんには内部で組み込み関数のopen()を使っているみたいだけど、でもPythonのioのcloseの実装はflushも中で呼ぶようだし、一方で「うまく動作しなかった」という報告があるのも事実だし…と迷走中です。

とりあえず明示的にflush()を書いておけば確実で安心ではあるのですが、「必要か不要かわからないけど書いておこう」というのもすっきりしないので、詳しい方がいらっしゃったらwith文を使った場合のflush()の必要性について教えてください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

melian

2023/05/09 06:40

参考までに、ドキュメントに以下の記載があります。 os - File Descriptor Operations - os.fsync(fd) https://docs.python.org/3/library/os.html#os.fsync > If you're starting with a buffered Python file object f, first do f.flush(), and then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
otn

2023/05/09 10:52

リンク先のブログを見ました。何らかの事象があったので記事を書いたのでしょうが、実際に起こったこと=記事に書かれていること、とは限らない感じです。 一般に、個人の書いたブログやSNS記事で、コメント欄が無い物については、誰の検証も受けてない可能性が高いので、「こんなことを書いている人がいる」以上の期待はしない方が良いです(自分で検証が必要)。はてなブログでもコメント欄を付ける機能はあります。まあ、コメント欄があっても、読者が少ないと同じですが。 ・「インタプリンタ」と書いている ・subprocess.callの引数がおそらく間違っている(Windowsだとたまたま動くが) などの誤りがあるし、「ディスクへの書き出し」と書きながらfsyncの話が無いですね。
guest

回答1

0

ベストアンサー

自動的にflushされますか?

はい、されます。
ただし、pythonでflush()してclose()しても、実際のディスクへの書き込みはOSの責任範囲なので、flush()してもディスクへの書き込みは保証されません。OSに強制したいのであれば、以下のドキュメントにあるとおり、os.fsync()を呼ぶのがいいでしょう。

https://docs.python.org/ja/3/library/os.html#os.fsync

一方で「うまく動作しなかった」

flush()するとうまく行くことがあるのは、flush()で時間がかかるので、次にファイルを開くまでに、OSが書き込んでいる確率が上るからでしょう。

投稿2023/05/09 06:39

編集2023/05/09 06:42
TakaiY

総合スコア14253

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

_kari_

2023/05/09 06:54

「flushされたがfsyncされていない状態」は、要はページキャッシュ上にファイルの内容が乗っている状態ですよね?(合っているでしょうか…) サブプロセスと親プロセス(Pythonスクリプト)はページキャッシュを共有しないということですか?
TakaiY

2023/05/09 07:10

ユーザプロセスから見てページキャッシュはディスク上のファイルと同一ですので、共有しています。 flushしても即座に(使っているとして)ページキャッシュに書かれるわけではないということです。OSはその前の段階でバッファを持っているということです。
_kari_

2023/05/09 07:37

つまり…順序的には、 ①write(→出力は書き込みバッファに入る) ②flush(→書き込みバッファがクリアされ、データが別のバッファ(?)に入る) ③(バッファからページキャッシュに書き込まれる) ④fsync(→ディスクに書き込まれる) という感じでしょうか。 なかなか難しいですね…
TakaiY

2023/05/09 08:58

最後が違います。プロセスからみて、ページキャッシュ上のファイルとディスク上のファイルは同一で、ファイルOpenでは、ページキャッシュにそのファイルがあればそこから、無ければディスクから読まれます。 また、その動きはファイルシステムがやっているので、プロセスからは見えません。 ③ fsync(バッファからページキャッシュに書き込まれる) ④いずれ ディスクに書き込まれる という感じです。
_kari_

2023/05/09 10:07

んん? fsyncしてもディスクに書き込まれず、メモリ上に残ったままということですか? それだと、fsyncした直後にコンピューターの電源が落ちると情報が失われますよね。 https://man7.org/linux/man-pages/man2/fsync.2.html fsync() transfers ("flushes") all modified in-core data of (i.e., modified buffer cache pages for) the file referred to by the file descriptor fd to the disk device (or other permanent storage device) so that all changed information can be retrieved even if the system crashes or is rebooted. 「ディスクに書き込むのでシステムがクラッシュしたり再起動してもデータを取り出せます」という内容に矛盾しませんか?
otn

2023/05/09 11:18

> fsyncしてもディスクに書き込まれず、メモリ上に残ったままということですか? 最近のLinuxはわかりませんが、(少なくとも昔の)Unixのsyncシステムコールは、実際にディスクに書くのが完了してからリターンするのでなく、書き込みをスケジュールしたらすぐにリターンします。昔はディスクが遅かったので。 なので、コマンドラインでディスクに書き出したいときは、sync;syncとコマンドを2回以上実行してました。1回目のsyncシステムコールでスケジュールされた書き込みが完了してないと、2回目のsyncコールがリターンしてこないので、2回目のsyncコマンドが終了すれば、1回目のsyncコマンド時点のキャッシュは書かれている。 今はディスクが速いのでどうだろうとググってみると、RedHatのページに https://access.redhat.com/ja/solutions/539953 「Unixの場合は sync;sync;sync」と書いてあります。Unixじゃなくて、RHELはどうやねん?という感じですが。 os.fsyncについては不明ですが、おそらく書き込みを待たずにリターンする気がします。
otn

2023/05/09 11:28

入れ違いになりました。fsyncシステムコールは書き込みを待つのですね。勉強になりました。 kariさんのコメントは引用部分しか見てませんでした。 The call blocks until the device reports that the transfer has completed. ですね。
TakaiY

2023/05/09 12:23 編集

しつこい感じであれですが、気になったので。 fsync()システムコールを呼ぶとベージキャッシュからディスク(物理ストレージ)まで書き込まれるところは間違えてましたが、ファイルのデータがページキャッシュに入っていれば、そのデータがdirty(=ディスクに書き込まれていない状態)であってもキャッシュの内容がreadされるのは確かなので、flushを呼んでもページキャッシュにすら即座には書き込まれないということになります。 これは、pythonのインタープリタの動作ということだと思います(未確認)。
_kari_

2023/05/10 01:05 編集

しつこいだなんてとんでもないです。質問に丁寧に向き合ってくださってありがたい限りです。 otnさんが質問へのコメントのほうで仰っている通り、自分で実験してみることにしました(質問投稿の前にやっておくべきでした…)。 import os with open("flushtest.txt", "wb") as f:     f.write(b"lorem ipsum") os.system("type flushtest.txt") Windows 10上のPython 3.11.3で上記コードを実行したところ、typeコマンドの出力は lorem ipsum と、意図通りのものになりました。 Ubuntu 20.04.6 LTS上のPython 3.9.15でもtypeをcatに変えて試しましたが同様でした。 運良くページキャッシュへの書き込みがtype/catコマンドの実行より先に行われた可能性もありますが、正直私はflushされればページキャッシュへの書き込みは行われるのではないかと思っています。 もしflush()実行後でもページキャッシュに乗らないタイミングがあるとするならfsyncという"過剰な"手段に頼らないと確実なreadができないような設計にPythonがなっているということでしょうが、そんな微妙な状態が長年放置されていることはないのでは…と。 じゃあリンク先のブログの報告は何なんだ、という話になりますが…(otnさんが指摘された通り、ブログ主さんが何かの勘違いをなさっている可能性はある)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問