Forza il flush del buffer di output nel programma in esecuzione


20

Ho uno script Python di lunga durata che invia periodicamente dati all'output standard che ho invocato con qualcosa del tipo:

python script.py > output.txt

Questo script è in esecuzione da un po 'e voglio interromperlo con Ctrl+ Cma non perderne alcun output. Sfortunatamente quando ho implementato lo script ho dimenticato di svuotare il buffer dopo ogni riga di output con qualcosa come la sys.stdout.flush()(la soluzione precedentemente suggerita per forzare il flush dell'output), quindi invocare Ctrl+ Cadesso mi farà perdere tutto il mio output.

Se ti stai chiedendo se esiste un modo per interagire con uno script python in esecuzione (o, più in generale, un processo in esecuzione) per forzarlo a svuotare il suo buffer di output. Non sto chiedendo come modificare e rieseguire lo script per farlo scorrere correttamente - questa domanda riguarda in particolare l'interazione con un processo in esecuzione (e, nel mio caso, non perdere l'output della mia attuale esecuzione del codice).

Risposte:


18

Se uno volesse davvero quei dati, suggerirei di collegare il debugger gdb all'interprete python, interrompere momentaneamente l'attività, chiamare fsync(1)( stdout ), staccarlo da esso (riprendere il processo) e andare in giro per il file di output.

Cerca /proc/$(pidof python)/fdper vedere descrittori di file validi. $(pidof x)restituisce il PID del processo denominato ' 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

Ho usato questo metodo per modificare dir di lavoro, modificare le impostazioni al volo ... molte cose. Purtroppo, puoi solo chiamare funzioni definite nel programma in esecuzione, ma fsyncfunziona bene.

(il comando gdb ' info functions' elencherà tutte le funzioni disponibili. Fai attenzione però. Stai operando LIVE su un processo.)

C'è anche il comando peekfd(che si trova nel psmiscpacchetto su Debian Jessie e altri) che ti permetterà di vedere cosa si nasconde nei buffer di un processo. Ancora una volta, /proc/$(pidof python)/fdti mostrerà descrittori di file validi da dare come argomenti a peekfd.

Se non ricordi -uper Python, puoi sempre aggiungere un comando con stdbuf(in coreutils, già installato) per impostare stdin / stdout / stderr su senza buffer, con buffer di linea o con buffer a blocchi come desiderato:

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

Certo, man pagessono i tuoi amici, ehi! forse un alias potrebbe essere utile anche qui.

alias python='python -u'

Ora il tuo pitone usa sempre -uper tutte le tue attività da riga di comando!


5

Per prima cosa assicurati di avere i simboli di debug per Python (o almeno glibc). Su Fedora 1 puoi installarli con:

dnf debuginfo-install python

Quindi collegare gdb allo script in esecuzione ed eseguire i comandi seguenti:

[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

Questo eliminerà lo stdout e disabiliterà anche il buffering. Il 2dalla setvbufchiamata è il valore di _IONBFsul mio sistema. Dovrai scoprire cosa c'è nel tuo (a grep _IONBF /usr/include/stdio.hdovrebbe fare il trucco).

Sulla base di ciò che ho visto nell'implementazione di PyFile_SetBufSizee PyFile_WriteStringin CPython 2.7, dovrebbe funzionare abbastanza bene, ma non posso fornire alcuna garanzia.


1 Fedora include un tipo speciale di RPM chiamato debuginfo rpms . Questi RPM creati automaticamente contengono le informazioni di debug dai file di programma, ma sono stati spostati in un file esterno.


Ho provato Python 2.7 e ho finito con lo stesso risultato. Diamo un'occhiata all'aggiornamento di debug che hai pubblicato.
DarkHeart,

Per quello che vale, CPython 3.5 sembra avere un'implementazione diversa di I / O ( fileobject.c) rispetto a 2.7 . Qualcuno deve scavare nel iomodulo.
Cristian Ciupitu,

@DarkHeart, potresti voler prima provare con un semplice programma come questo .
Cristian Ciupitu,

4

Non esiste una soluzione al tuo problema immediato. Se lo script è già stato avviato, non è possibile modificare la modalità di buffering dopo il fatto. Questi sono tutti buffer in memoria e tutto ciò viene impostato all'avvio dello script, all'apertura degli handle di file, alla creazione di pipe, ecc.

Come possibilità remota, se e solo se parte o tutto il buffering in questione viene eseguito a livello di I / O in uscita, è possibile eseguire un synccomando; ma questo è generalmente improbabile in un caso come questo.

In futuro puoi usare l' -uopzione * di Python per eseguire lo script. In generale, molti comandi hanno opzioni specifiche del comando per disabilitare il buffering stdin / stdout e si può anche avere qualche successo generico con il unbuffercomando dal expectpacchetto.

Un Ctrl+ Ccauserebbe lo svuotamento dei buffer a livello di sistema quando il programma viene interrotto a meno che il buffering non venga eseguito dallo stesso Python e non abbia implementato la logica per svuotare i propri buffer con Ctrl+ C. Una sospensione, un incidente o un'uccisione non sarebbero così gentili.

* Forza lo stdin, lo stdout e lo stderr ad essere totalmente senza buffer.


2

Documentazione Python 2.7.7, sezione "Installazione e utilizzo di Python", sottosezione 1. Riga di comando e ambiente , descrive questo argomento di Python:

-u

Forza lo stdin, lo stdout e lo stderr ad essere totalmente senza buffer. Sui sistemi in cui è importante, metti anche stdin, stdout e stderr in modalità binaria.

Si noti che esiste un buffering interno in file.readlines () e File Objects (per la riga in sys.stdin) che non è influenzato da questa opzione. Per ovviare a questo, ti consigliamo di utilizzare file.readline () all'interno di un ciclo while 1:.

E anche questa variabile d'ambiente:

PYTHONUNBUFFERED

Se impostato su una stringa non vuota, equivale a specificare l'opzione -u.


1
Grazie - ma entrambi sembrano opzioni che dovrei specificare la prima volta che eseguo il mio script Python. Mi chiedo se c'è un modo per ottenere uno script in esecuzione per scaricare il suo output.
josliber,

Non credo che esista una soluzione del genere, perché i dati si trovano probabilmente in un buffer di memoria da qualche parte. Dovresti iniettare una DLL in Python che conosce abbastanza bene il suo eseguibile per sapere dove si trova il buffer e come scriverlo. Credo che la maggior parte delle persone userebbe solo uno dei 2 metodi sopra. L'aggiunta di una variabile d'ambiente è piuttosto semplice, dopo tutto.
harrymc,

OK, buono a sapersi che potrebbe non esserci una soluzione. Come affermato nella mia domanda, so come svuotare i buffer in Python (avrei usato sys.stdout.flush(), ma la tua -uopzione sembra ancora più semplice), ma mi ero appena dimenticato di farlo quando invocavo il mio codice. Avendo già eseguito il mio codice per più di una settimana, speravo che ci fosse un modo per ottenere il mio output senza dover rieseguire il codice per un'altra settimana.
josliber,

Un metodo inverosimile, se si conosce l'aspetto dei dati, è eseguire un dump della memoria completa del processo utilizzando Process Explorer , quindi cercare le stringhe nel file. Questo non terminerà il processo, quindi puoi ancora provare altri metodi.
harrymc,

Sono su Linux - ci sono equivalenti Linux di quel software?
josliber,

2

Sembra che stavo diventando troppo cauto nel perdere l'output bufferato dopo aver eseguito Ctrl-C; secondo questo post dovrei aspettarmi che il buffer venga svuotato se il mio programma ha una normale uscita, che sarebbe il caso se premessi Ctrl-C. D'altra parte, perderei l'output nel buffer se avessi ucciso lo script con SIGKILL o simili.


Dovresti provarlo per scoprirlo. Ctrl-C provocherà lo svuotamento dei buffer IO di basso livello. Se Python esegue il proprio buffering, Ctrl-C li cancellerà solo se Python è abbastanza gentile da implementare la logica per farlo. Speriamo che Python abbia deciso di non reinventare una ruota e si affida al normale livello di buffering del sistema. Non ho idea se sia così. Ma attenzione.
Jason C,

Il sistema operativo non può mai eliminare ciò che si trova nello spazio di memoria del programma. Ciò che viene scaricato sono i dati nella memoria di sistema, ovvero i dati già scritti dal programma utilizzando le chiamate di sistema. In caso di uscita di errore, anche questi buffer di sistema vengono eliminati. In breve, i dati non ancora scritti da Python non possono essere cancellati e vanno persi in tutti i casi.
harrymc,

0

Penso che un'altra possibile soluzione possa essere quella di forzare l'uccisione del processo con core scaricato e quindi analizzare il contenuto di memoria postumo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.