Força a liberação do buffer de saída no programa em execução

17

Eu tenho um script python de longa duração que periodicamente envia dados para a saída padrão que invoquei com algo como:

python script.py > output.txt

Este script está sendo executado há algum tempo e eu quero pará-lo com Ctrl + C , mas não perco sua saída. Infelizmente, quando implementei o script, esqueci de liberar o buffer após cada linha de saída com algo como sys.stdout.flush() (o solução sugerida anteriormente para forçar o fluxo de saída), então invocar Ctrl + C agora fará com que eu perca toda a minha saída.

Se estiver se perguntando se existe alguma maneira de interagir com um script python em execução (ou, mais geralmente, um processo em execução) para forçá-lo a liberar seu buffer de saída. Eu não estou perguntando como editar e re-executar o script para obtê-lo para liberar corretamente - esta questão é especificamente sobre a interação com um processo em execução (e, no meu caso, não perdendo a saída da minha execução de código atual).

    
por josliber 06.06.2014 / 15:50

6 respostas

2

Parece que eu estava sendo cauteloso em perder por uma saída em buffer depois de executar Ctrl-C; de acordo com este post eu devo esperar que o buffer para ser liberado se meu programa tiver uma saída normal, o que seria o caso se eu pressionasse Ctrl-C. Por outro lado, eu perderia a saída do buffer se eu matasse o script com o SIGKILL ou algo semelhante.

    
por 16.06.2014 / 17:33
16

SE um realmente queria esses dados, sugiro anexar o gdb depurador ao interpretador python, parando a tarefa momentaneamente, chamando fsync(1) ( stdout ), desanexar (continuando o processo) e ir para o arquivo de saída.

Procure em /proc/$(pidof python)/fd para ver descritores de arquivos válidos. $(pidof x) retorna o PID do processo chamado ' 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

Eu usei esse método para alterar as configurações do diretório de trabalho, ajustar as configurações ... muitas coisas. Infelizmente, você só pode chamar funções definidas no programa em execução, fsync funciona muito bem.

(O comando gdb ' info functions ' listará todas as funções disponíveis. Tenha cuidado, porém. Você está operando LIVE em um processo.)

Existe também o comando peekfd (encontrado em psmisc package no Debian Jessie e outros) que permitirá que você veja o que está escondido nos buffers de um processo. Novamente, /proc/$(pidof python)/fd mostrará descritores de arquivos válidos para dar como argumentos para peekfd.

Se você não se lembra de -u para python, você sempre pode prefixar um comando com stdbuf (em coreutils , já instalado) para definir stdin / stdout / stderr para buffer, buffer de linha ou block buffered conforme desejado:

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

Claro, man pages são seus amigos, ei! talvez um apelido possa ser útil aqui também.

alias python='python -u'

Agora seu python sempre usa -u para todos os esforços de sua linha de comando!

    
por 19.06.2014 / 01:44
5

Primeiro, certifique-se de ter os símbolos de depuração para o Python (ou pelo menos o glibc). Em Fedora 1 você pode instalá-los com:

dnf debuginfo-install python

Em seguida, anexe gdb ao script em execução e execute os seguintes comandos:

[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

Isso liberará stdout e também desativará o armazenamento em buffer. O 2 da chamada setvbuf é o valor de _IONBF on meu sistema. Você precisará descobrir o que está no seu (um grep _IONBF /usr/include/stdio.h deve fazer o truque).

Com base no que vi na implementação de PyFile_SetBufSize e PyFile_WriteString no CPython 2.7, deve funcionar muito bem, mas Eu não posso fazer nenhuma garantia.

1 O Fedora inclui um tipo especial de RPMs chamado debuginfo rpms . Esses RPMs criados automaticamente contêm as informações de depuração dos arquivos do programa, mas movidos para um arquivo externo.

    
por 20.06.2014 / 02:09
4

Não há solução para o seu problema imediato. Se o seu script já foi iniciado, você não poderá alterar o modo de armazenamento após o fato. Esses são todos os buffers na memória e tudo isso é configurado quando o script é iniciado, as alças de arquivos são abertas, os canais são criados, etc.

Como uma tentativa, se e somente se algum ou todo o buffer em questão estiver sendo feito no nível de IO na saída, você poderia fazer um comando sync ; mas isso é geralmente improvável em um caso como este.

No futuro, você pode usar a opção -u do Python * para executar o script. Em geral, muitos comandos têm opções específicas de comando para desabilitar o armazenamento em buffer stdin / stdout, e você também pode ter algum sucesso genérico com o comando unbuffer do pacote expect .

Uma Ctrl + C faria com que os buffers no nível do sistema fossem liberados quando o programa fosse interrompido a menos que o buffer fosse feito pelo próprio Python e não implementou a lógica para limpar seus próprios buffers com Ctrl + C . Uma suspensão, falha ou morte não seria tão gentil.

* Forçar stdin, stdout e stderr para serem totalmente sem buffer.

    
por 16.06.2014 / 17:39
2

Documentação do Python 2.7.7, seção "Configuração e uso do Python", subseção 1. Linha de comando e ambiente , descreve este argumento do Python:

-u

Force stdin, stdout and stderr to be totally unbuffered. On systems where it matters, also put stdin, stdout and stderr in binary mode.

Note that there is internal buffering in file.readlines() and File Objects (for line in sys.stdin) which is not influenced by this option. To work around this, you will want to use file.readline() inside a while 1: loop.

E também esta variável de ambiente:

PYTHONUNBUFFERED

If this is set to a non-empty string it is equivalent to specifying the -u option.

    
por 13.06.2014 / 19:17
0

Eu acho que outra solução possível pode ser forçar o processo de matar com núcleo despejado e, em seguida, analisar conteúdo de memória postumamente.

    
por 18.12.2014 / 18:03

Tags