実行中のプログラムで強制的に出力バッファをフラッシュする


20

私は次のようなもので呼び出した標準出力に定期的にデータを出力する長時間実行のPythonスクリプトを持っています:

python script.py > output.txt

このスクリプトはしばらく実行されており、Ctrl+で停止したいのですCが、出力が失われないようにします。残念ながら、スクリプトを実装したときに、出力の各行の後にsys.stdout.flush()(出力フラッシュを強制するための以前に提案された解決策)のようなものでバッファーをフラッシュするのを忘れていたため、今すぐCtrl+ を呼び出すCと、すべての出力が失われます。

実行中のpythonスクリプト(または、より一般的には実行中のプロセス)と対話して、出力バッファーを強制的にフラッシュする方法があるかどうか疑問に思っている場合。スクリプトを編集して再実行してスクリプトを正しくフラッシュする方法を尋ねるのではありません-この質問は、実行中のプロセスと対話することに関するものです(私の場合、現在のコード実行からの出力を失うことはありません)。

回答:


18

IF 1は本当にデータが、私は取り付けをお勧めしたいことを希望したGDBの Pythonインタプリタにデバッガを、一時的にタスクを停止し、呼び出しfsync(1)標準出力をそれから)、切り離し(プロセスの再開を)し、出力ファイル閲覧行きます。

/proc/$(pidof python)/fd有効なファイル記述子を確認してください。$(pidof x)' x' という名前のプロセスのPIDを返します。

# your python script is running merrily over there.... with some PID you've determined.
#
# load gdb
gdb
#
# attach to python interpreter (use the number returned by $(pidof python))
attach 1234
#
# force a sync within the program's world (1 = stdout, which is redirected in your example)
call fsync(1)
#
# the call SHOULD have returned 0x0, sync successful.   If you get 0xffffffff (-1), perhaps that wasn't stdout.  0=stdin, 1=stdout, 2=stderr
#
# remove our claws from poor python
detach
#
# we're done!
quit

このメソッドを使用して、作業ディレクトリを変更し、その場で設定を調整しました...多くのこと。残念fsyncながら、実行中のプログラムで定義されている関数のみを呼び出すことができますが、うまく機能します。

(gdbコマンド ' info functions'は、使用可能なすべての機能をリストします。ただし、注意してください。プロセスでLIVEを操作しています。)

また、プロセスのバッファに隠れているものを確認できるコマンドpeekfdpsmiscDebian Jessieなどのパッケージにあります)もあります。繰り返し/proc/$(pidof python)/fdますが、peekfdに引数として与える有効なファイル記述子が表示されます。

-upythonを覚えていない場合は、コマンドの前にstdbuf(in coreutils、既にインストールされている)を付けて、stdin / stdout / stderrを必要に応じてアンバッファー、ラインバッファー、またはブロックバッファーに設定できます。

stdbuf -i 0 -o 0 -e 0 python myscript.py > unbuffered.output

もちろん、man pagesお友達ですよね!おそらくエイリアスもここで役に立つかもしれません。

alias python='python -u'

これで、Pythonは常に-uすべてのコマンドラインの努力に使用します!


5

まず、Python(または少なくともglibc)のデバッグシンボルがあることを確認します。上のFedora 1あなたがそれらをインストールすることができます:

dnf debuginfo-install python

次に、実行中のスクリプトにgdbを添付し、次のコマンドを実行します。

[user@host ~]$ pidof python2
9219
[user@host ~]$ gdb python2 9219
GNU gdb (GDB) Fedora 7.7.1-13.fc20
...
0x00007fa934278780 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81  T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) call fflush(stdout)
$1 = 0
(gdb) call setvbuf(stdout, 0, 2, 0)
$2 = 0
(gdb) quit
A debugging session is active.

    Inferior 1 [process 9219] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2, process 9219

これにより、標準出力がフラッシュされ、バッファリングも無効になります。2以下からのsetvbuf呼び出しが値である_IONBF私のシステムで。あなたはあなたのものを見つける必要があります(grep _IONBF /usr/include/stdio.hトリックを行う必要があります)。

CPython 2.7の実装PyFile_SetBufSizePyFile_WriteStringCPython 2.7 で見たものに基づいて、それはかなりうまくいくはずですが、保証はできません。


1 Fedoraには、debuginfo rpmsと呼ばれる特別なタイプのRPMが含まれています。これらの自動作成されたRPMには、プログラムファイルからのデバッグ情報が含まれますが、外部ファイルに移動されます。


python 2.7を試したところ、同じ結果になりました。あなたが投稿したデバッグの更新を見てみましょう。
DarkHeart

CPython 3.5の価値は、I / O(fileobject.c)の実装が2.7とは異なるようです。誰かがioモジュールを掘り下げる必要があります。
クリスティアンシウピトゥ16

@DarkHeart、最初にこのような簡単なプログラムでテストしたいかもしれません。
クリスチャン・シウピトゥ16

4

差し迫った問題に対する解決策はありません。スクリプトがすでに開始されている場合、事後はバッファリングモードを変更できません。これらはすべてインメモリバッファであり、スクリプトの起動時、ファイルハンドルのオープン時、パイプの作成時などにすべて設定されます。

ロングショットとして、問題のバッファリングの一部またはすべてが出力のIOレベルで実行されている場合にのみ、syncコマンドを実行できます。しかし、これは一般的にこのような場合には起こりそうにありません。

将来、Pythonの-uオプション*を使用してスクリプトを実行できます。一般に、多くのコマンドにはstdin / stdoutバッファリングを無効にするコマンド固有のオプションがありunbufferexpectパッケージのコマンドで一般的な成功を収めることもできます。

Ctrl+は、Cプログラムが中断されたときに、システム・レベルのバッファがフラッシュされる原因となる場合を除き、バッファリングはPython自体によって行われ、それが持つ独自のバッファをフラッシュするためのロジックを実装していませんCtrl+ C。サスペンド、クラッシュ、またはキルはそれほど親切ではありません。

* stdin、stdout、stderrを強制的に完全にバッファリング解除します。


2

Python 2.7.7のドキュメント、セクション「Pythonのセットアップと使用」、サブセクション 1。コマンドラインと環境では、このPython引数について説明しています。

-u

stdin、stdout、およびstderrを完全にバッファーなしにします。重要なシステムでは、stdin、stdout、stderrもバイナリモードにします。

file.readlines()およびファイルオブジェクト(sys.stdinの行用)には、このオプションの影響を受けない内部バッファリングがあることに注意してください。これを回避するには、while 1:ループ内でfile.readline()を使用します。

また、この環境変数:

ピトノンバッファリング

これが空でない文字列に設定されている場合、-uオプションを指定するのと同等です。


1
感謝します-しかし、これらはどちらもPythonスクリプトを最初に実行したときに指定する必要があるオプションのように聞こえます。実行中のスクリプトを取得してその出力をダンプする方法があるかどうか疑問に思っています。
josliber

データはおそらくどこかのメモリバッファにあるため、このような解決策があるとは思わない。実行可能ファイルを十分に知っているpythonにdllを挿入して、バッファの場所と書き込み方法を知る必要があります。私は、ほとんどの人が上記の2つの方法のいずれかを使用すると信じています。結局のところ、環境変数の追加はかなり簡単です。
harrymc 14年

OK、解決策がないかもしれません。私の質問で述べたように、私はPythonでバッファをフラッシュする方法を知っています(使用sys.stdout.flush()していましたが、あなたの-uオプションはさらに簡単だと思われます)が、コードを呼び出すときにそうするのを忘れていました。すでに1週間以上コードを実行しているので、もう1週間コードを再実行することなく出力を取得する方法があることを望んでいました。
josliber

ファーフェッチ方式は、データがどのように見えるかを知っている場合、Process Explorerを使用してプロセスの完全なメモリダンプを取得し、ファイル内の文字列を検索することです。これはプロセスを終了しませんので、他の方法を試すことができます。
harrymc 14年

私はLinuxを使用しています-そのソフトウェアに相当するLinuxはありますか?
josliber

2

Ctrl-Cを実行した後、バッファリングされた出力によって失うことについて、私は過度に注意を払っていたようです。この投稿によると、プログラムに通常の終了がある場合はバッファーがフラッシュされるはずです。Ctrl-Cを押すとそうなります。一方、SIGKILLまたは同様のスクリプトを強制終了すると、バッファリングされた出力が失われます。


あなたはそれを見つけるためにそれを試してみる必要があるでしょう。Ctrl-Cを押すと、低レベルのIOバッファーがフラッシュされます。Pythonが独自のバッファリングを行う場合、Ctrl-Cは、Pythonがそれを行うロジックを実装するのに十分な種類の場合にのみそれらをフラッシュします。Pythonが車輪を再発明しないことを決定し、システムの通常レベルのバッファリングに依存することを願っています。それが当てはまるかどうかはわかりません。しかし、注意してください。
ジェイソンC 14年

OSは、プログラムのメモリ空間にあるものをフラッシュすることはできません。フラッシュされるのは、システムメモリ内のデータです。つまり、システムコールを使用してプログラムによって既に書き込まれたデータです。エラー終了の場合、これらのシステムバッファも破棄されます。つまり、Pythonによってまだ書き出されていないデータはフラッシュできず、すべての場合に失われます。
ハリーマック14年

0

別の可能な解決策は、コアダンプでプロセスを強制終了し、死後のメモリ内容を分析することだと思います。

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