Paksa buffer output flush dalam menjalankan program


20

Saya memiliki skrip python yang sudah berjalan lama yang secara berkala menampilkan data ke keluaran standar yang saya gunakan dengan sesuatu seperti:

python script.py > output.txt

Script ini telah berjalan beberapa saat dan saya ingin menghentikannya dengan Ctrl+ Ctetapi tidak kehilangan outputnya. Sayangnya ketika saya mengimplementasikan skrip, saya lupa menyiram buffer setelah setiap baris output dengan sesuatu seperti sys.stdout.flush()( solusi yang disarankan sebelumnya untuk memaksa pembilasan output), sehingga memohon Ctrl+ Csekarang akan menyebabkan saya kehilangan semua output saya.

Jika bertanya-tanya apakah ada cara untuk berinteraksi dengan skrip python yang berjalan (atau, lebih umum, proses yang berjalan) untuk memaksanya mem-flush buffer outputnya. Saya tidak bertanya bagaimana cara mengedit dan menjalankan kembali skrip untuk mem-flush dengan benar - pertanyaan ini khusus tentang berinteraksi dengan proses yang sedang berjalan (dan, dalam kasus saya, tidak kehilangan output dari eksekusi kode saya saat ini).

Jawaban:


18

JIKA ada yang benar-benar menginginkan data itu, saya sarankan melampirkan debugger gdb ke juru bahasa python, menghentikan sementara tugas, memanggil fsync(1)( stdout ), melepaskan dari itu (melanjutkan proses) dan pergi membaca file output dengan teliti.

Lihat /proc/$(pidof python)/fduntuk melihat deskriptor file yang valid. $(pidof x)mengembalikan PID proses bernama ' x'.

# 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

Saya telah menggunakan metode ini untuk mengubah pengaturan dir, pengaturan tweak on the fly ... banyak hal. Sayangnya, Anda hanya dapat memanggil fungsi yang didefinisikan dalam program yang sedang berjalan, fsyncbekerja dengan baik.

(Perintah gdb ' info functions' akan mencantumkan semua fungsi yang tersedia. Berhati-hatilah. Anda sedang mengoperasikan LIVE pada suatu proses.)

Ada juga perintah peekfd(ditemukan dalam psmiscpaket di Debian Jessie dan lainnya) yang akan memungkinkan Anda untuk melihat apa yang bersembunyi di buffer suatu proses. Sekali lagi, /proc/$(pidof python)/fdakan menunjukkan kepada Anda deskriptor file yang valid untuk diberikan sebagai argumen kepada peekfd.

Jika Anda tidak ingat -uuntuk python, Anda selalu dapat awalan perintah dengan stdbuf(dalam coreutils, sudah diinstal) untuk mengatur stdin / stdout / stderr ke unbuffered, line buffered atau block buffered sesuai keinginan:

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

Tentu saja, man pagesapakah teman-temanmu, hei! mungkin sebuah alias mungkin berguna di sini juga.

alias python='python -u'

Sekarang python Anda selalu digunakan -uuntuk semua upaya baris perintah Anda!


5

Pertama, pastikan Anda memiliki simbol debugging untuk Python (atau setidaknya glibc). Pada Fedora 1 Anda dapat menginstalnya dengan:

dnf debuginfo-install python

Kemudian lampirkan gdb ke skrip yang berjalan dan jalankan perintah berikut:

[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

Ini akan menghilangkan stdout dan juga menonaktifkan buffering. The 2dari setvbufpanggilan adalah nilai _IONBFpada sistem saya. Anda harus mencari tahu apa yang ada di hati Anda ( grep _IONBF /usr/include/stdio.hsebaiknya lakukan triknya).

Berdasarkan apa yang saya lihat dalam implementasi PyFile_SetBufSizedan PyFile_WriteStringdalam CPython 2.7, itu seharusnya bekerja dengan cukup baik, tapi saya tidak bisa membuat jaminan.


1 Fedora termasuk jenis RPM khusus yang disebut debuginfo rpms . RPM yang dibuat secara otomatis ini mengandung informasi debug dari file program, tetapi dipindahkan ke file eksternal.


Saya mencoba python 2.7 dan berakhir dengan hasil yang sama. Saya akan melihat pembaruan debug yang Anda posting.
DarkHeart

Untuk apa nilainya, CPython 3.5 tampaknya memiliki implementasi I / O ( fileobject.c) yang berbeda dari 2,7 . Seseorang perlu menggali iomodul.
Cristian Ciupitu

@DarkHeart, Anda mungkin ingin menguji dulu dengan program sederhana seperti ini .
Cristian Ciupitu

4

Tidak ada solusi untuk masalah langsung Anda. Jika skrip Anda sudah dimulai, Anda tidak dapat mengubah mode buffering setelah fakta. Ini semua adalah buffer dalam memori dan semua itu diatur ketika skrip dimulai, pegangan file dibuka, pipa dibuat, dll.

Sebagai upaya jangka panjang, jika dan hanya jika beberapa atau semua buffering yang dimaksud dilakukan pada level IO pada output, Anda dapat melakukan syncperintah; tetapi ini umumnya tidak mungkin dalam kasus seperti ini.

Di masa depan Anda dapat menggunakan -uopsi * Python untuk menjalankan skrip. Secara umum, banyak perintah memiliki opsi spesifik perintah untuk menonaktifkan buffer stdin / stdout, dan Anda mungkin juga memiliki beberapa kesuksesan generik dengan unbufferperintah dari expectpaket.

A Ctrl+ Cakan menyebabkan buffer tingkat sistem memerah ketika program terganggu kecuali buffering dilakukan oleh Python sendiri dan belum menerapkan logika untuk menyiram buffernya sendiri dengan Ctrl+ C. Menangguhkan, menabrak, atau membunuh tidak akan baik.

* Memaksa stdin, stdout, dan stderr untuk sepenuhnya tidak terganggu.


2

Python 2.7.7 Dokumentasi, bagian "Pengaturan dan Penggunaan Python", ayat 1. Baris perintah dan lingkungan , menjelaskan argumen Python ini:

-u

Paksa stdin, stdout dan stderr untuk sepenuhnya tidak terganggu. Pada sistem yang penting, juga letakkan stdin, stdout dan stderr dalam mode biner.

Perhatikan bahwa ada buffering internal di file.readlines () dan File Objects (untuk baris di sys.stdin) yang tidak dipengaruhi oleh opsi ini. Untuk mengatasinya, Anda harus menggunakan file.readline () di dalam 1: loop sementara.

Dan juga variabel lingkungan ini:

PYTHONUNBUFFERED

Jika ini disetel ke string yang tidak kosong, itu sama dengan menentukan opsi -u.


1
Terima kasih - tetapi kedua opsi ini kedengarannya seperti yang perlu saya tentukan ketika saya pertama kali menjalankan skrip python saya. Saya bertanya-tanya apakah ada cara untuk mendapatkan skrip yang berjalan untuk membuang hasilnya.
josliber

Saya tidak percaya ada solusi seperti itu, karena data mungkin ada di buffer memori di suatu tempat. Anda akan perlu menyuntikkan dll ke dalam python yang tahu itu dapat dieksekusi dengan cukup baik untuk mengetahui di mana buffer dan bagaimana cara menuliskannya. Saya percaya kebanyakan orang hanya akan menggunakan salah satu dari 2 metode di atas. Menambahkan variabel lingkungan agak mudah.
harrymc

OK, bagus untuk tahu mungkin tidak ada solusi. Seperti yang dinyatakan dalam pertanyaan saya, saya tahu cara menyiram buffer dengan python (saya akan menggunakan sys.stdout.flush(), tetapi -upilihan Anda tampaknya lebih mudah), tetapi baru saja lupa melakukannya ketika menjalankan kode saya. Setelah menjalankan kode saya selama lebih dari seminggu, saya berharap ada cara untuk mendapatkan hasil tanpa harus menjalankan kembali kode tersebut selama seminggu lagi.
josliber

Metode yang dibuat-buat, jika Anda tahu seperti apa data itu, adalah mengambil dump memori penuh dari proses menggunakan Process Explorer , kemudian mencari string dalam file. Ini tidak akan menghentikan proses, jadi Anda masih dapat mencoba metode lain.
harrymc

Saya menggunakan linux - adakah yang setara dengan software itu di linux?
josliber

2

Tampaknya saya terlalu berhati-hati tentang kehilangan oleh buffered output setelah menjalankan Ctrl-C; menurut posting ini saya harus mengharapkan buffer akan memerah jika program saya memiliki keluar normal, yang akan terjadi jika saya menekan Ctrl-C. Di sisi lain, saya akan kehilangan output buffered jika saya membunuh skrip dengan SIGKILL atau serupa.


Anda harus mencobanya untuk mengetahuinya. Ctrl-C akan menyebabkan buffer IO tingkat rendah memerah. Jika Python melakukan buffering sendiri maka Ctrl-C hanya akan menyiram mereka jika Python cukup baik untuk mengimplementasikan logika untuk melakukannya. Semoga Python memutuskan untuk tidak menemukan kembali roda dan bergantung pada tingkat buffering normal sistem. Saya tidak tahu apakah itu masalahnya. Tapi berhati-hatilah.
Jason C

OS tidak pernah dapat membersihkan apa yang ada dalam ruang memori program. Yang memerah adalah data dalam memori sistem, yang berarti data sudah ditulis oleh program menggunakan panggilan sistem. Jika terjadi kesalahan keluar, bahkan buffer sistem ini akan dibuang. Singkatnya, data yang belum ditulis oleh Python tidak dapat dihapus dan hilang dalam semua kasus.
harrymc

0

Saya pikir solusi lain yang mungkin bisa memaksa proses kill dengan core dumped dan kemudian menganalisis konten memori anumerta.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.