Примусове вимикання буфера виходу в запущену програму


20

У мене давно працює сценарій python, який періодично виводить дані на стандартний вихід, на який я викликав щось на кшталт:

python script.py > output.txt

Цей сценарій працює деякий час, і я хочу зупинити його з Ctrl+, Cале не втрачати жодного його результату. На жаль, коли я реалізував сценарій, я забув очистити буфер після кожного рядка виводу чимось на кшталт sys.stdout.flush()( раніше запропоноване рішення для примусового промивання вихідних даних), тому виклик Ctrl+ Cпрямо зараз призведе до втрати всього мого результату.

Якщо вам цікаво, чи є який-небудь спосіб взаємодіяти із запущеним сценарієм python (або, загалом, запущеним процесом), щоб змусити його очистити вихідний буфер. Я не запитую, як редагувати та повторно запускати скрипт, щоб змусити його правильно розмиватися - це питання стосується конкретного взаємодії з запущеним процесом (і, в моєму випадку, не втрачаючи висновок від мого поточного виконання коду).

Відповіді:


18

Якщо б хтось справді бажав цих даних, я б запропонував приєднати налагоджувач gdb до інтерпретатора python, на мить зупинивши завдання, викликавши fsync(1)( stdout ), відірвіться від нього (відновивши процес) та перейдіть до вивчення вихідного файлу.

Загляньте, /proc/$(pidof python)/fdщоб побачити дійсні дескриптори файлів. $(pidof x)повертає PID процесу з назвою ' 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

Я використовував цей метод, щоб змінити налаштування робочого режиму, налаштувати під час руху багато речей. На жаль, ви можете зателефонувати лише до функцій, визначених у запущеній програмі, fsyncале добре працює.

(Команда gdb ' info functions' відобразить всі доступні функції. Хоча будьте обережні. Ви працюєте LIVE на процесі.)

Існує також команда peekfd(знайдена в psmiscпакеті Debian Jessie та інших), яка дозволить вам побачити, що ховається в буферах процесу. Знову /proc/$(pidof python)/fdпокажемо ваші дійсні дескриптори файлів, які подавати як аргументи peekfd.

Якщо ви не пам’ятаєте -uпро python, ви завжди можете префіксувати команду за допомогою stdbufcoreutils, вже встановлений), щоб встановити stdin / stdout / stderr на небуферований, буферний або блокований рядок за бажанням:

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

Звичайно, man pagesваші друзі, ей! можливо, псевдонім може бути корисним і тут.

alias python='python -u'

Тепер ваш пітон завжди використовує -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

Це призведе до вимивання stdout, а також відключення буферизації. Значення 2від setvbufдзвінка - це значення _IONBFв моїй системі. Вам потрібно буде з’ясувати, що у вас є ( grep _IONBF /usr/include/stdio.hслід зробити трюк).

Виходячи з того, що я бачив у впровадженні PyFile_SetBufSizeта PyFile_WriteStringв CPython 2.7, він повинен працювати досить добре, але я не можу давати жодних гарантій.


1 Fedora включає в себе спеціальний тип RPM, який називається debuginfo rpms . Ці автоматично створені RPM містять інформацію про налагодження з програмних файлів, але переміщуються у зовнішній файл.


Я спробував python 2.7 і закінчився тим самим результатом. Я погляну на оновлення налагодження, яке ви опублікували.
DarkHeart

Для чого це варто, схоже , CPython 3.5 має іншу реалізацію вводу-виводу ( fileobject.c), ніж 2,7 . Комусь потрібно копатися в ioмодулі.
Крістіан Цюпіту

@DarkHeart, ви можете спробувати спробувати спочатку просту програму, як ця .
Крістіан Цюпіту

4

Немає рішення вашої найближчої проблеми. Якщо ваш сценарій вже запущений, ви не можете змінити режим буферизації після факту. Це все буфери в пам'яті, і все це налаштовується при запуску сценарію, відкриваються ручки файлів, створюються труби тощо.

Як довгостроковий, якщо і лише в тому випадку, якщо деякі або всі буферизації, про які йде мова, на виході виконується на рівні IO, ви можете виконати syncкоманду; але це в цілому малоймовірно.

Надалі ви можете скористатися -uпараметром Python * для запуску сценарію. Загалом, у багатьох командах є спеціальні параметри для відключення буфінування stdin / stdout, і ви також можете мати певний загальний успіх із unbufferкомандою з expectпакету.

A Ctrl+ Cпризведе до того, що буфери системного рівня змиваються, коли програма переривається, якщо буферизація не проводиться самим Python, і вона не реалізує логіку, щоб промити власні буфери з Ctrl+ C. Призупинення, збій або вбивство не було б таким добрим.

* Примушуйте stdin, stdout та stderr бути повністю розблокованими.


2

Документація Python 2.7.7, розділ "Налаштування та використання Python", підрозділ 1. Командний рядок та середовище описує цей аргумент Python:

Примушуйте stdin, stdout та stderr бути абсолютно нерозкритими. У системах, де це має значення, також поставте stdin, stdout та stderr у бінарний режим.

Зауважте, що у файлі file.readlines () та File Object (для рядка в sys.stdin) є внутрішня буферизація, на яку ця опція не впливає. Щоб вирішити це, вам потрібно буде використовувати файл file.readline () всередині 1: цикл.

А також ця змінна середовище:

ПІТОНУНБУФЕР

Якщо для цього встановлено не порожню рядок, це рівнозначно параметру -u.


1
Дякую - але ці обидва звучать як варіанти, які мені потрібно було б вказати, коли я вперше запустив свій скрипт пітона. Мені цікаво, чи є спосіб отримати запущений скрипт, щоб скинути його вихід.
josliber

Я не вірю, що таке рішення є, тому що дані, ймовірно, є десь в буфері пам'яті. Вам потрібно буде ввести DLL в python, який досить добре знає його виконуваний файл, щоб знати, де знаходиться буфер і як його виписати. Я вважаю, що більшість людей просто використовуватимуть один із наведених вище методів. Зрештою, додати змінну середовища досить просто.
harrymc

Гаразд, добре знати, що рішення не може бути. Як зазначено в моєму запитанні, я знаю, як відмивати буфери в python (я б використав sys.stdout.flush(), але ваш -uваріант здається ще простішим), але я просто забув це зробити, коли викликав мій код. Я вже запускав свій код більше тижня, я сподівався, що є спосіб отримати свій вихід, не потребуючи повторного запуску коду ще тиждень.
josliber

Надуманий метод, якщо ви знаєте, як виглядають дані, полягає в тому, щоб зробити повний дамб оперативної пам’яті за допомогою Process Explorer , а потім шукати рядки у файлі. Це не припинить процес, тому ви можете спробувати інші методи.
harrymc

Я в Linux - чи є еквіваленти цього програмного забезпечення Linux?
josliber

2

Здається, що я надто обережно ставився до втрати за допомогою буферного виводу після запуску Ctrl-C; відповідно до цієї публікації, я повинен очікувати, що буфер буде розмитий, якщо у моєї програми нормальний вихід, що було б у випадку, якщо я натиснув Ctrl-C. З іншого боку, я втратив би захищений вихід, якби вбив сценарій за допомогою SIGKILL або подібного.


Вам доведеться спробувати це, щоб дізнатися. Ctrl-C призведе до промивання буферів низького рівня вводу-виводу. Якщо Python робить власну буферизацію, тоді Ctrl-C їх змиє лише тоді, коли Python буде досить люб'язним, щоб реалізувати логіку для цього. Сподіваємось, Python вирішив не винаходити колесо і покладається на нормальний рівень буферизації системи. Я поняття не маю, якщо це так. Але будьте попереджені.
Джейсон C

ОС ніколи не може вимити те, що знаходиться в пам’яті програми. Промивання - це дані в системній пам'яті, тобто дані, вже виписані програмою за допомогою системних викликів. У разі виходу з помилки навіть ці системні буфери відкидаються. Коротше кажучи, дані, які ще не були виписані Python, не можуть бути стерти і втрачаються у всіх випадках.
harrymc

0

Я думаю, що іншим можливим рішенням може бути змусити процес вбивати за допомогою ядра, а потім аналізувати посмертно вміст пам'яті.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.