Buộc bộ đệm đầu ra tuôn ra trong chương trình đang chạy


20

Tôi có một tập lệnh python chạy dài, định kỳ xuất dữ liệu thành đầu ra tiêu chuẩn mà tôi đã gọi với một cái gì đó như:

python script.py > output.txt

Kịch bản này đã chạy được một lúc và tôi muốn dừng nó với Ctrl+ Cnhưng không mất bất kỳ đầu ra nào. Thật không may khi tôi triển khai tập lệnh, tôi đã quên xóa bộ đệm sau mỗi dòng đầu ra với một cái gì đó giống như sys.stdout.flush()( giải pháp được đề xuất trước đây để buộc xả đầu ra), vì vậy việc gọi Ctrl+ Cngay bây giờ sẽ khiến tôi mất tất cả đầu ra.

Nếu tự hỏi liệu có cách nào để tương tác với một tập lệnh python đang chạy (hay nói chung hơn là một quy trình đang chạy) để buộc nó xóa bộ đệm đầu ra của nó. Tôi không hỏi làm thế nào để chỉnh sửa và chạy lại tập lệnh để làm cho nó hoàn chỉnh chính xác - câu hỏi này cụ thể là về việc tương tác với một quy trình đang chạy (và, trong trường hợp của tôi, không làm mất đầu ra từ việc thực thi mã hiện tại của tôi).

Câu trả lời:


18

Nếu ai đó thực sự muốn dữ liệu đó, tôi khuyên bạn nên đính kèm trình gỡ lỗi gdb vào trình thông dịch python, tạm thời dừng tác vụ, gọi fsync(1)( stdout ), tách khỏi nó (tiếp tục quá trình) và kiểm tra lại tệp đầu ra.

Nhìn vào /proc/$(pidof python)/fdđể xem mô tả tập tin hợp lệ. $(pidof x)trả về PID của quá trình có tên ' 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

Tôi đã sử dụng phương pháp này để thay đổi thư mục làm việc, chỉnh sửa cài đặt ... nhiều thứ. Than ôi, bạn chỉ có thể gọi các chức năng được xác định trong chương trình đang chạy, fsyncmặc dù hoạt động độc đáo.

(lệnh gdb ' info functions' sẽ liệt kê tất cả các chức năng có sẵn. Mặc dù vậy, hãy cẩn thận. Bạn đang hoạt động TRỰC TIẾP trên một quy trình.)

Ngoài ra còn có lệnh peekfd(được tìm thấy trong psmiscgói trên Debian Jessie và những người khác) sẽ cho phép bạn xem những gì ẩn trong bộ đệm của một tiến trình. Một lần nữa, /proc/$(pidof python)/fdsẽ hiển thị cho bạn các mô tả tệp hợp lệ để đưa ra làm đối số cho peekfd.

Nếu bạn không nhớ -upython, bạn luôn có thể đặt tiền tố một lệnh với stdbuf(trong coreutils, đã được cài đặt) để đặt stdin / stdout / stderr thành unbuffered, đệm dòng hoặc chặn bộ đệm như mong muốn:

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

Tất nhiên, man pageslà bạn của bạn, hey! có lẽ một bí danh cũng có thể hữu ích ở đây.

alias python='python -u'

Bây giờ python của bạn luôn sử dụng -ucho tất cả các nỗ lực dòng lệnh của bạn!


5

Trước tiên hãy đảm bảo bạn có các biểu tượng gỡ lỗi cho Python (hoặc ít nhất là glibc). Trên Fedora 1, bạn có thể cài đặt chúng với:

dnf debuginfo-install python

Sau đó đính kèm gdb vào tập lệnh đang chạy và chạy các lệnh sau:

[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

Điều này sẽ tuôn ra thiết bị xuất chuẩn và cũng vô hiệu hóa bộ đệm. Các 2từ setvbufgọi là giá trị của _IONBFtrên hệ thống của tôi. Bạn sẽ cần tìm hiểu những gì thuộc về bạn ( grep _IONBF /usr/include/stdio.hnên thực hiện thủ thuật).

Dựa trên những gì tôi đã thấy trong quá trình triển khai PyFile_SetBufSizePyFile_WriteStringtrong CPython 2.7, nó sẽ hoạt động khá tốt, nhưng tôi không thể đảm bảo.


1 Fedora bao gồm một loại RPM đặc biệt gọi là debuginfo rpms . Các RPM được tạo tự động này chứa thông tin gỡ lỗi từ các tệp chương trình, nhưng được chuyển sang tệp bên ngoài.


Tôi đã thử python 2.7 và kết quả tương tự. Tôi sẽ xem bản cập nhật gỡ lỗi mà bạn đã đăng.
DarkHeart

Đối với giá trị của nó, CPython 3.5 dường như có cách triển khai I / O ( fileobject.c) khác với 2.7 . Ai đó cần phải đào sâu vào iomô-đun.
Cristian Ciupitu

@DarkHeart, bạn có thể muốn thử nghiệm đầu tiên với một chương trình đơn giản như thế này .
Cristian Ciupitu

4

Không có giải pháp cho vấn đề trước mắt của bạn. Nếu tập lệnh của bạn đã bắt đầu, bạn không thể thay đổi chế độ đệm sau khi thực tế. Đây là tất cả các bộ đệm trong bộ nhớ và tất cả các bộ đệm được thiết lập khi tập lệnh bắt đầu, xử lý tệp được mở, đường ống được tạo, v.v.

Là một cú sút xa, nếu và chỉ khi một số hoặc tất cả bộ đệm trong câu hỏi đang được thực hiện ở mức IO trên đầu ra, bạn có thể thực hiện một synclệnh; nhưng điều này thường không thể xảy ra trong trường hợp như thế này.

Trong tương lai, bạn có thể sử dụng -utùy chọn của Python * để chạy tập lệnh. Nói chung, nhiều lệnh có các tùy chọn dành riêng cho lệnh để vô hiệu hóa bộ đệm stdin / stdout và bạn cũng có thể có một số thành công chung với unbufferlệnh từ expectgói.

A Ctrl+ Csẽ khiến bộ đệm ở cấp hệ thống bị xóa khi chương trình bị gián đoạn trừ khi bộ đệm được thực hiện bởi chính Python và nó đã không triển khai logic để xóa bộ đệm của chính nó bằng Ctrl+ C. Một sự đình chỉ, sụp đổ hoặc giết sẽ không tốt như vậy.

* Buộc stdin, stdout và stderr hoàn toàn không có bộ đệm.


2

Tài liệu Python 2.7.7, phần "Thiết lập và sử dụng Python", tiểu mục 1. Dòng lệnh và môi trường , mô tả đối số Python này:

-u

Buộc stdin, stdout và stderr phải hoàn toàn không có bộ đệm. Trên các hệ thống có vấn đề, cũng đặt stdin, stdout và stderr ở chế độ nhị phân.

Lưu ý rằng có bộ đệm nội bộ trong file.readlines () và Đối tượng tệp (đối với dòng trong sys.stdin) không bị ảnh hưởng bởi tùy chọn này. Để giải quyết vấn đề này, bạn sẽ muốn sử dụng file.readline () trong vòng lặp 1:.

Và cũng là biến môi trường này:

PYTHONUNBUFFERED

Nếu điều này được đặt thành một chuỗi không trống, nó tương đương với việc chỉ định tùy chọn -u.


1
Cảm ơn - nhưng cả hai đều giống như các tùy chọn mà tôi sẽ cần chỉ định khi lần đầu tiên chạy tập lệnh python của mình. Tôi tự hỏi liệu có cách nào để có được một tập lệnh đang chạy để kết xuất đầu ra của nó không.
josliber

Tôi không tin có một giải pháp như vậy, bởi vì dữ liệu có thể nằm trong bộ nhớ đệm ở đâu đó. Bạn sẽ cần phải tiêm một dll vào python để biết rõ khả năng thực thi của nó đủ để biết bộ đệm ở đâu và làm thế nào để viết nó ra. Tôi tin rằng hầu hết mọi người sẽ chỉ sử dụng một trong 2 phương pháp trên. Rốt cuộc, việc thêm một biến môi trường khá dễ dàng.
harrymc

OK, tốt để biết có thể không có một giải pháp. Như đã nêu trong câu hỏi của tôi, tôi biết cách xóa bộ đệm trong python (tôi sẽ sử dụng sys.stdout.flush(), nhưng -utùy chọn của bạn có vẻ dễ dàng hơn), nhưng đã quên làm như vậy khi gọi mã của tôi. Đã chạy mã của tôi hơn một tuần, tôi hy vọng có một cách để có được đầu ra của mình mà không cần phải chạy lại mã trong một tuần nữa.
josliber

Một phương thức rất xa, nếu bạn biết dữ liệu trông như thế nào, là lấy một bộ nhớ đầy đủ của quá trình bằng Process Explorer , sau đó tìm kiếm các chuỗi trong tệp. Điều này sẽ không chấm dứt quá trình, vì vậy bạn vẫn có thể thử các phương pháp khác.
harrymc

Tôi đang dùng linux - có phần mềm tương đương linux của phần mềm đó không?
josliber

2

Có vẻ như tôi đã quá thận trọng về việc mất đầu ra được đệm sau khi chạy Ctrl-C; Theo bài đăng này, tôi sẽ hy vọng bộ đệm sẽ bị xóa nếu chương trình của tôi có lối thoát bình thường, đó sẽ là trường hợp nếu tôi nhấn Ctrl-C. Mặt khác, tôi sẽ mất đầu ra được đệm nếu tôi giết tập lệnh bằng SIGKILL hoặc tương tự.


Bạn sẽ phải thử nó để tìm hiểu. Ctrl-C sẽ khiến bộ đệm IO mức thấp bị xóa. Nếu Python thực hiện bộ đệm riêng thì Ctrl-C sẽ chỉ xóa chúng nếu Python đủ tử tế để thực hiện logic để làm như vậy. Hy vọng rằng Python đã quyết định không phát minh lại một bánh xe và dựa vào mức độ đệm thông thường của hệ thống. Tôi không biết nếu đó là trường hợp. Nhưng được cảnh báo.
Jason C

HĐH không bao giờ có thể tuôn ra những gì trong không gian bộ nhớ của chương trình. Những gì được xóa là dữ liệu trong bộ nhớ hệ thống, nghĩa là dữ liệu đã được chương trình viết ra bằng các cuộc gọi hệ thống. Trong trường hợp thoát lỗi, ngay cả những bộ đệm hệ thống này cũng bị loại bỏ. Nói tóm lại, dữ liệu chưa được Python viết ra có thể bị xóa và bị mất trong mọi trường hợp.
harrymc

0

Tôi nghĩ một giải pháp khả thi khác có thể là buộc quá trình tiêu diệt với lõi bị đổ và sau đó phân tích nội dung bộ nhớ truy tặng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.