Çalışan programda çıktı tamponunu yıkamayı zorla


20

Periyodik olarak gibi bir şey ile çağırdım standart çıktı veri çıkışı uzun süre çalışan bir python komut dosyası var:

python script.py > output.txt

Bu komut dosyası bir süredir çalışıyor ve Ctrl+ ile durdurmak istiyorum, Cancak çıktılarını kaybetmiyorum. Ne yazık ki betiği uyguladığımda, her çıktı satırından sonra arabelleği (çıktı çıktısını zorlamak sys.stdout.flush()için daha önce önerilen çözüm) temizlemeyi unuttum , bu yüzden Ctrl+ ' Cyı çağırmak tüm çıktımı kaybetmeme neden olacak.

Çalışan bir python betiğiyle (veya daha genel olarak çalışan bir işlemle) çıktı arabelleğini temizlemeye zorlamak için herhangi bir yol olup olmadığını merak ediyorsanız. Nasıl düzgün bir şekilde temizlemek için komut dosyasını düzenlemek ve yeniden çalıştırmak için sormuyorum - bu soru özellikle çalışan bir işlem ile etkileşim hakkında (ve benim durumumda, geçerli kod yürütme çıktı kaybetme değil).

Yanıtlar:


18

EĞER biri gerçekten bu verileri isteyen, ben takılarak öneririm gdb çağırarak, piton tercüman hata ayıklayıcı anlık olarak görev durdurma fsync(1)( stdout'u (sürecinin devamının olarak),) durumda ayırma ve çıkış dosyası incelemek gidin.

Bak /proc/$(pidof python)/fdgeçerli bir dosya tanımlayıcıları görmek için. $(pidof x)' x' adlı işlemin PID'sini döndürür .

# 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

Ben çalışma dir's değiştirmek için bu yöntemi kullandım, anında ayarları tweak ... birçok şey. Ne yazık ki, sadece çalışan programda tanımlanan işlevleri çağırabilirsiniz, fsyncancak güzel çalışır.

(gdb komutu ' info functions' kullanılabilir tüm işlevleri listeler. Yine de dikkatli olun. LIVE'ı bir işlemde çalıştırıyorsunuz.)

Ayrıca, bir işlemin arabelleklerinde nelerin gizlendiğini görmenizi sağlayan komut peekfd( psmiscDebian Jessie ve diğerleri üzerinde pakette bulunur) vardır. Yine, /proc/$(pidof python)/fdpeekfd'ye argüman olarak vermek için geçerli dosya tanımlayıcılarını gösterecektir.

-uPython'u hatırlamıyorsanız , stdin / stdout / stderr öğesini arabelleğe alınmamış, satır arabelleğe alınmış veya istendiği gibi arabelleğe alınmış olarak ayarlamak için her zaman stdbuf( coreutilsönceden yüklenmiş olarak) ile bir komutun önüne ön ek ekleyebilirsiniz :

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

Tabii ki man pagesarkadaşlarınız, hey! belki de bir takma ad burada yararlı olabilir.

alias python='python -u'

Şimdi python'unuz her zaman -utüm komut satırı çabalarınız için kullanır !


5

Öncelikle Python (veya en azından glibc) için hata ayıklama simgelerine sahip olduğunuzdan emin olun. On Fedora 1 bunları birlikte yükleyebilirsiniz:

dnf debuginfo-install python

Ardından çalışan betiğe gdb'yi ekleyin ve aşağıdaki komutları çalıştırın:

[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

Bu stdout'u temizler ve ayrıca arabelleğe almayı devre dışı bırakır. 2Dan setvbufçağrı değeridir _IONBFsistemimde. Sizinkinin ne olduğunu bulmanız gerekecek ( grep _IONBF /usr/include/stdio.hhile yapmalı).

CPython 2.7 uygulamasında PyFile_SetBufSizeve PyFile_WriteStringCPython 2.7 uygulamasında gördüklerime dayanarak, oldukça iyi çalışmalıdır, ancak herhangi bir garanti veremem.


1 Fedora, debuginfo rpms adı verilen özel bir RPM türü içerir . Otomatik olarak oluşturulan bu RPM'ler, program dosyalarından hata ayıklama bilgilerini içerir, ancak harici bir dosyaya taşınır.


Python 2.7'yi denedim ve aynı sonucu elde ettim. Gönderdiğiniz hata ayıklama güncellemesine bir göz atacağım.
DarkHeart

Değeri için, CPython 3.5 , G / Ç ( fileobject.c) 'nin 2.7'den farklı bir uygulamasına sahip gibi görünüyor . Birinin iomodüle girmesi gerekiyor .
Cristian Ciupitu

@DarkHeart, sen gibi basit bir programla ilk test etmek isteyebilirsiniz bu bir .
Cristian Ciupitu

4

Derhal sorununuza bir çözüm yoktur. Komut dosyanız zaten başlamışsa, bu olaydan sonra arabelleğe alma modunu değiştiremezsiniz. Bunların tümü bellek içi arabellekleridir ve bunların tümü komut dosyası başlatıldığında, dosya tanıtıcıları açıldığında, borular oluşturulduğunda vb.

Uzun bir atış olarak, ve yalnızca söz konusu tamponlamanın bir kısmı veya tamamı çıktıda IO seviyesinde yapılıyorsa, bir synckomut yapabilirsiniz; ancak böyle bir durumda bu genellikle olası değildir.

Gelecekte komut dosyasını çalıştırmak için Python'un *-u seçeneğini kullanabilirsiniz . Genel olarak, birçok komut stdin / stdout arabelleğe almayı devre dışı bırakmak için komuta özgü seçeneklere sahiptir ve ayrıca paketteki komutla genel bir başarı elde edebilirsiniz .unbufferexpect

A Ctrl+ , arabellekleme Python'un kendisi tarafından yapılmadıkça ve kendi arabelleklerini + ile temizlemek için mantığı uygulamadığı süreceC , program kesintiye uğradığında sistem düzeyinde arabelleklerin temizlenmesine neden olur . Bir askıya alma, çarpışma veya öldürme o kadar nazik olmaz.CtrlC

* Stdin, stdout ve stderr'i tamamen arabelleksiz olmaya zorlayın.


2

Python 2.7.7 Dokümantasyon, bölüm "Python Kurulumu ve Kullanımı", alt bölüm 1. Komut satırı ve ortamı , bu Python argümanını açıklar:

-u

Stdin, stdout ve stderr'i tamamen arabelleksiz olmaya zorlayın. Önemli olduğu sistemlerde, stdin, stdout ve stderr'i ikili moda da koyun.

Bu seçenekten etkilenmeyen file.readlines () ve Dosya Nesnelerinde (sys.stdin'deki satır için) dahili arabellekleme olduğuna dikkat edin. Bu sorunu gidermek için, bir süre 1: döngü içinde file.readline () kullanmak isteyeceksiniz.

Ve ayrıca bu ortam değişkeni:

PYTHONUNBUFFERED

Bu boş olmayan bir dizeye ayarlanırsa, -u seçeneğini belirtmeye eşdeğerdir.


1
Teşekkürler - ama bu ikisi de python betiğimi ilk çalıştırdığımda belirtmem gereken seçenekler gibi geliyor. Çıkışını dökmek için çalışan bir komut dosyası almanın bir yolu olup olmadığını merak ediyorum.
josliber

Böyle bir çözüm olduğuna inanmıyorum, çünkü veriler muhtemelen bir yerde bir bellek arabelleğinde. Eğer tampon nerede ve nasıl yazmak için yeterince iyi onun yürütülebilir bilen python bir dll enjekte gerekir. Çoğu insanın yukarıdaki 2 yöntemden birini kullanacağına inanıyorum. Sonuçta, bir ortam değişkeni eklemek oldukça kolaydır.
harrymc

Tamam, iyi bir çözüm olmayabilir. Sorumda belirtildiği gibi, ben python tamponları temizlemek için biliyorum (kullanmış olurdu sys.stdout.flush(), ama -useçenek daha kolay görünüyor), ama sadece kodumu çağırırken bunu unutmuştu. Kodumu zaten bir haftadan fazla çalıştırdıktan sonra, kodu başka bir hafta boyunca yeniden çalıştırmaya gerek kalmadan çıktımı almanın bir yolu olduğunu umuyordum.
josliber

Verilerin neye benzediğini biliyorsanız, getirilen bir yöntem, İşlem Gezgini'ni kullanarak sürecin tam bir bellek dökümü almaktır , ardından dosyadaki dizeleri aramaktır. Bu işlemi sonlandırmaz, böylece başka yöntemleri de deneyebilirsiniz.
harrymc

Linux'tayım - bu yazılımın linux eşdeğeri var mı?
josliber

2

Görünüşe göre Ctrl-C'yi çalıştırdıktan sonra arabelleğe alınan çıktıyı kaybetme konusunda aşırı ihtiyatlı davranıyordum; Bu yazıya göre, programımın normal bir çıkışı varsa tamponun temizlenmesini beklemeliyim, bu Ctrl-C'ye basarsam olur. Diğer taraftan, senaryoyu SIGKILL veya benzeri biriyle öldürürsem arabelleğe alınan çıktıyı kaybederdim.


Öğrenmek için denemelisin. Ctrl-C düşük seviyeli G / Ç arabelleklerinin temizlenmesine neden olur. Python kendi arabelleklemesini yaparsa, Ctrl-C sadece Python mantığı uygulamak için yeterince nazikse onları temizler. Umarım Python bir tekerleği yeniden icat etmemeye karar verir ve sistemin normal tamponlama seviyesine güvenir. Durumun bu olup olmadığı hakkında hiçbir fikrim yok. Ancak dikkatli olun.
Jason C

İşletim sistemi asla programın bellek alanında ne olduğunu temizleyemez. Temizlenen şey sistem belleğindeki verilerdir, yani sistem çağrılarını kullanarak program tarafından önceden yazılmış verilerdir. Hata çıkışı durumunda, bu sistem arabellekleri bile atılır. Kısacası, henüz Python tarafından yazılmayan veriler temizlenemez ve her durumda kaybolur.
harrymc

0

Başka bir olası çözümün, çekirdek dökümü ile süreç öldürmeye zorlamak ve sonra ölümünden sonra bellek içeriğini analiz etmek olabileceğini düşünüyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.