บังคับบัฟเฟอร์เอาต์พุตฟลัชในโปรแกรมที่กำลังรัน


20

ฉันมีสคริปต์ไพ ธ อนที่ใช้เวลานานซึ่งส่งออกข้อมูลไปยังเอาต์พุตมาตรฐานเป็นระยะ ๆ ซึ่งฉันได้เรียกด้วยเช่น:

python script.py > output.txt

สคริปต์นี้ทำงานมาระยะหนึ่งแล้วและฉันต้องการหยุดด้วยCtrl+ Cแต่จะไม่สูญเสียผลลัพธ์ใด ๆ น่าเสียดายที่เมื่อฉันติดตั้งสคริปต์ฉันลืมล้างบัฟเฟอร์หลังจากแต่ละบรรทัดของเอาต์พุตด้วยบางสิ่งเช่นsys.stdout.flush()( โซลูชันที่แนะนำก่อนหน้านี้สำหรับการบังคับให้ล้างข้อมูลเอาต์พุต) ดังนั้นการเรียกใช้Ctrl+ Cในตอนนี้จะทำให้ฉันเสียเอาต์พุตทั้งหมด

หากสงสัยว่ามีวิธีใดในการโต้ตอบกับสคริปต์ python ที่กำลังทำงานอยู่ (หรือมากกว่านั้นคือกระบวนการที่กำลังทำงานอยู่) เพื่อบังคับให้ล้างข้อมูลบัฟเฟอร์ออก ฉันไม่ได้ถามวิธีแก้ไขและเรียกใช้สคริปต์อีกครั้งเพื่อให้ล้างได้อย่างถูกต้อง - คำถามนี้เกี่ยวกับการโต้ตอบกับกระบวนการทำงาน (โดยเฉพาะในกรณีของฉันจะไม่สูญเสียผลลัพธ์จากการเรียกใช้รหัสปัจจุบันของฉัน)

คำตอบ:


18

หากมีใครต้องการข้อมูลอย่างแท้จริงฉันขอแนะนำให้แนบgdb debugger กับ python interpreter ชั่วขณะหยุดภารกิจเรียกfsync(1)( stdout ) ออกจากมัน (กลับมาทำงานต่อ) และอ่านไฟล์เอาต์พุต

ค้นหา/proc/$(pidof python)/fdเพื่อดู descriptor ไฟล์ที่ถูกต้อง $(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' จะแสดงรายการทั้งหมดของฟังก์ชั่นที่มีอยู่. เป็น แต่ระวัง. คุณกำลังปฏิบัติการสดในกระบวนการ.)

นอกจากนี้ยังมีคำสั่งpeekfd(พบในpsmiscแพ็คเกจบน Debian Jessie และอื่น ๆ ) ซึ่งจะช่วยให้คุณเห็นสิ่งที่ซ่อนอยู่ในบัฟเฟอร์ของกระบวนการ อีกครั้ง/proc/$(pidof python)/fdจะแสดง file descriptors ที่ถูกต้องให้เป็นอาร์กิวเมนต์สำหรับ peekfd

หากคุณจำไม่ได้ว่า-uสำหรับไพ ธ อนคุณสามารถใส่คำสั่งล่วงหน้าด้วยstdbuf(ในcoreutilsติดตั้งไว้แล้ว) เพื่อตั้งค่า 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 รวมถึงชนิดพิเศษที่เรียกว่า RPMs debuginfo RPMs RPMs ที่สร้างขึ้นโดยอัตโนมัติเหล่านี้มีข้อมูลการดีบักจากไฟล์โปรแกรม แต่ย้ายไปยังไฟล์ภายนอก


ฉันลอง python 2.7 และลงเอยด้วยผลลัพธ์เดียวกัน ฉันจะดูการอัปเดตการดีบักที่คุณโพสต์
DarkHeart

สำหรับสิ่งที่มันคุ้มค่า CPython 3.5ดูเหมือนว่าจะมีการดำเนินงานที่แตกต่างกันของ I / O ( fileobject.c) มากกว่า2.7 มีคนต้องการขุดลงในioโมดูล
Cristian Ciupitu

@DarkHeart คุณอาจต้องการที่จะทดสอบครั้งแรกกับโปรแกรมง่ายๆเช่นนี้
Cristian Ciupitu

4

ไม่มีวิธีแก้ไขปัญหาเฉพาะหน้าของคุณ หากสคริปต์ของคุณเริ่มต้นแล้วคุณจะไม่สามารถเปลี่ยนโหมดบัฟเฟอร์หลังจากข้อเท็จจริง เหล่านี้คือบัฟเฟอร์ในหน่วยความจำทั้งหมดและทั้งหมดถูกตั้งค่าเมื่อสคริปต์เริ่มทำงานการจัดการไฟล์จะถูกเปิดการสร้างไปป์ ฯลฯ

ในฐานะที่เป็นช็อตยาวถ้าหากว่ามีการทำบัฟเฟอร์บางส่วนหรือทั้งหมดในระดับ IO ในเอาต์พุตคุณสามารถทำsyncคำสั่งได้ แต่โดยทั่วไปไม่น่าเป็นไปได้ในกรณีเช่นนี้

ในอนาคตคุณสามารถใช้-uตัวเลือกPython *เพื่อเรียกใช้สคริปต์ โดยทั่วไปคำสั่งจำนวนมากมีตัวเลือกเฉพาะคำสั่งเพื่อปิดใช้งานการบัฟเฟอร์ stdin / stdout และคุณอาจประสบความสำเร็จโดยทั่วไปด้วยunbufferคำสั่งจากexpectแพคเกจ

Ctrl+ Cจะทำให้เกิดบัฟเฟอร์ระดับระบบที่จะล้างเมื่อโปรแกรมถูกขัดจังหวะเว้นแต่บัฟเฟอร์จะกระทำโดยงูหลามตัวเองและมันก็ยังไม่ได้ดำเนินการตรรกะในการล้างบัฟเฟอร์ของตัวเองด้วย+Ctrl Cการหยุดทำงานหยุดทำงานหรือฆ่าจะไม่ใจดี

*บังคับ stdin, stdout และ stderr เพื่อให้ไม่มีข้อผิดพลาดโดยสิ้นเชิง


2

Python 2.7.7 เอกสารประกอบส่วน "การตั้งค่าและการใช้งานของ Python" ส่วนย่อย 1. บรรทัดคำสั่งและสภาพแวดล้อมอธิบายอาร์กิวเมนต์ Python นี้:

-ยู

บังคับ stdin, stdout และ stderr ให้เต็มเปา บนระบบที่มีความสำคัญให้ใส่ stdin, stdout และ stderr ในโหมดไบนารี

โปรดทราบว่ามีการบัฟเฟอร์ภายในใน file.readlines () และ File Objects (สำหรับบรรทัดใน sys.stdin) ซึ่งไม่ได้รับอิทธิพลจากตัวเลือกนี้ ในการหลีกเลี่ยงปัญหานี้คุณจะต้องใช้ file.readline () ในขณะที่ 1: ลูป

และตัวแปรสภาพแวดล้อมนี้:

PYTHONUNBUFFERED

หากสิ่งนี้ถูกตั้งค่าเป็นสตริงที่ไม่ว่างจะเท่ากับการระบุอ็อพชัน -u


1
ขอบคุณ - แต่เสียงทั้งสองนี้เหมือนตัวเลือกที่ฉันต้องระบุเมื่อฉันรันสคริปต์ python ของฉันครั้งแรก ฉันสงสัยว่าจะมีวิธีในการเรียกใช้สคริปต์เพื่อถ่ายโอนข้อมูลผลลัพธ์หรือไม่
josliber

ฉันไม่เชื่อว่ามีวิธีแก้ปัญหาเช่นนี้เพราะข้อมูลอาจอยู่ในบัฟเฟอร์หน่วยความจำที่ไหนซักแห่ง คุณจะต้องฉีด dll ลงในไพ ธ อนที่รู้ว่ามันทำงานได้ดีพอที่จะรู้ว่าบัฟเฟอร์อยู่ที่ไหนและวิธีเขียนมันออกมา ฉันเชื่อว่าคนส่วนใหญ่จะใช้วิธีใดวิธีหนึ่งจาก 2 วิธีข้างต้น การเพิ่มตัวแปรสภาพแวดล้อมนั้นค่อนข้างง่ายหลังจากทั้งหมด
harrymc

ตกลงดีที่รู้ว่าอาจไม่มีวิธีแก้ปัญหา ตามที่ระบุไว้ในคำถามของฉันฉันรู้วิธีล้างบัฟเฟอร์ในหลาม (ฉันจะใช้sys.stdout.flush()แต่-uตัวเลือกของคุณดูง่ายยิ่งขึ้น) แต่เพิ่งลืมทำเช่นนั้นเมื่อเรียกใช้รหัสของฉัน หลังจากใช้งานโค้ดของฉันมานานกว่าหนึ่งสัปดาห์แล้วฉันก็หวังว่าจะมีวิธีที่จะได้ผลลัพธ์ของฉันโดยไม่จำเป็นต้องเรียกใช้รหัสอีกครั้งในอีกหนึ่งสัปดาห์
josliber

วิธีการที่นำมาใช้อย่างกว้างขวางหากคุณทราบว่าข้อมูลมีลักษณะอย่างไรคือใช้การถ่ายโอนข้อมูลหน่วยความจำเต็มรูปแบบของกระบวนการโดยใช้Process Explorerจากนั้นค้นหาสตริงในไฟล์ สิ่งนี้จะไม่ยุติกระบวนการดังนั้นคุณยังสามารถลองวิธีอื่นได้
harrymc

ฉันใช้ linux อยู่ - มีซอฟต์แวร์ linux ที่เทียบเท่าหรือไม่
josliber

2

ดูเหมือนว่าฉันกำลังระมัดระวังเกี่ยวกับการสูญเสียโดยบัฟเฟอร์ผลลัพธ์หลังจากใช้ Ctrl-C; ตามโพสต์นี้ฉันควรคาดหวังบัฟเฟอร์ที่จะล้างหากโปรแกรมของฉันมีทางออกปกติซึ่งจะเป็นกรณีถ้าฉันกด Ctrl-C ในทางกลับกันฉันจะสูญเสียผลลัพธ์บัฟเฟอร์ถ้าฉันฆ่าสคริปต์ด้วย SIGKILL หรือคล้ายกัน


คุณต้องลองดู Ctrl-C จะทำให้บัฟเฟอร์ IO ระดับต่ำถูกล้างออก ถ้า Python ทำการบัฟเฟอร์ของมันเอง Ctrl-C จะล้างออกถ้า Python นั้นเพียงพอที่จะใช้ตรรกะในการทำเช่นนั้น หวังว่า Python ตัดสินใจที่จะไม่สร้างล้อใหม่และอาศัยการบัฟเฟอร์ระดับปกติของระบบ ฉันไม่รู้ว่ามันเป็นอย่างนั้นหรือเปล่า แต่ถูกเตือน
Jason C

ระบบปฏิบัติการไม่สามารถลบสิ่งที่อยู่ในพื้นที่หน่วยความจำของโปรแกรม สิ่งที่ได้รับการล้างคือข้อมูลในหน่วยความจำระบบหมายถึงข้อมูลที่เขียนไว้แล้วโดยโปรแกรมโดยใช้การเรียกของระบบ ในกรณีที่เกิดข้อผิดพลาดออกแม้บัฟเฟอร์ระบบเหล่านี้จะถูกยกเลิก กล่าวโดยสรุปข้อมูลที่ยังไม่ได้เขียนโดย Python ไม่สามารถล้างออกได้และหายไปในทุกกรณี
harrymc

0

ฉันคิดว่าวิธีแก้ปัญหาที่เป็นไปได้อีกประการหนึ่งคือบังคับให้กระบวนการฆ่าด้วยคอร์ที่ทิ้งแล้ววิเคราะห์เนื้อหาหน่วยความจำที่ไม่ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.