ไพพ์เนม, ตัวอธิบายไฟล์และ EOF


10

สองหน้าต่างผู้ใช้เดียวกันพร้อมกับ bash พร้อมต์ ในประเภท window-1:

$ mkfifo f; exec <f

ดังนั้นทุบตีอยู่ในขณะนี้พยายามอ่านจากไฟล์อธิบาย 0 fซึ่งถูกแมปไปไปป์ที่มีชื่อ ในประเภท window-2:

$ echo ls > f

ตอนนี้ window-1 จะพิมพ์ ls แล้วเชลล์ก็จะตาย ทำไม?

ทดลองถัดไป: เปิดหน้าต่าง-1 exec <fอีกครั้งกับ ในประเภท window-2:

$ exec 3>f
$ echo ls >&3

หลังจากบรรทัดแรกด้านบน window-1 จะปลุกและพิมพ์พรอมต์ ทำไม? หลังจากบรรทัดที่สองข้างต้น window-1 จะพิมพ์lsเอาต์พุตและเชลล์ยังมีชีวิตอยู่ ทำไม? อันที่จริงตอนนี้ใน window-2 echo ls > fไม่ได้ปิดเชลล์ window-1

คำตอบต้องเกี่ยวกับการมีอยู่ของ file descriptor 3 จาก window-2 ที่อ้างอิงไปป์ที่มีชื่อ!


1
หลังจากที่exec <f, bashไม่ได้พยายามที่จะอ่านจากfมันเป็นครั้งแรกที่พยายามที่จะเปิดมัน open()จะไม่กลับมาจนกว่าจะมีกระบวนการบางอย่างทำอีกเปิดในโหมดการเขียนไปยังท่อ (ตรงจุดนี้ท่อจะถูก instantiated และเปลือกจะอ่านข้อมูลจากมัน)
Stéphane Chazelas

จุดยอดเยี่ยม @ StéphaneChazelas นี่เป็นเหตุผลว่าทำไมเมื่อexec 3>fเชลล์ถูกเรียกใช้ครั้งแรกแล้วจะให้พรอมต์ (จุดไมเนอร์คุณไม่ได้หมายความว่า "ในการเขียนโหมด" ในความคิดเห็นของคุณ?)
Fixee

1
ใช่ขอโทษ. แก้ไขตอนนี้ก่อนกำหนดเวลา 5 นาที
Stéphane Chazelas

คำตอบ:


12

มันจะทำอย่างไรกับการปิดตัวอธิบายไฟล์

ในตัวอย่างแรกของคุณechoเขียนไปยังสตรีมเอาต์พุตมาตรฐานที่เชลล์เปิดเพื่อเชื่อมต่อกับfและเมื่อมันสิ้นสุด descriptor ของมันจะถูกปิด (โดยเชลล์) ในส่วนปลายที่ได้รับเชลล์ซึ่งอ่านอินพุตจากอินพุตสตรีมมาตรฐาน (เชื่อมต่อกับf) อ่านlsเรียกใช้lsและหยุดทำงานเนื่องจากเงื่อนไขสิ้นสุดไฟล์บนอินพุตมาตรฐาน

เงื่อนไขการสิ้นสุดไฟล์เกิดขึ้นเนื่องจากตัวเขียนทั้งหมดไปยังไปป์ที่ระบุชื่อ (หนึ่งในตัวอย่างนี้เท่านั้น) ได้ปิดจุดสิ้นสุดของไพพ์

ในตัวอย่างที่สองของคุณให้exec 3>fเปิดไฟล์ descriptor 3 สำหรับการเขียนfจากนั้นจึงechoเขียนlsลงไป มันคือเชลล์ที่ตอนนี้มีไฟล์ descriptor เปิดอยู่ไม่ใช่echoคำสั่ง exec 3>&-อธิบายยังคงเปิดอยู่จนกว่าคุณจะทำ ในตอนท้ายที่ได้รับเชลล์ซึ่งอ่านอินพุตจากอินพุตสตรีมมาตรฐาน (เชื่อมต่อกับf) อ่านlsเรียกใช้lsแล้วรออินพุตเพิ่มเติม (เนื่องจากสตรีมยังคงเปิดอยู่)

กระแสข้อมูลยังคงเปิดอยู่เนื่องจากตัวเขียนทั้งหมดไปยัง (เชลล์ผ่านexec 3>fและecho) ยังไม่ได้ปิดจุดสิ้นสุดของไพพ์ ( exec 3>fยังคงมีผลอยู่)


ฉันได้เขียนเกี่ยวกับechoข้างต้นราวกับว่ามันเป็นคำสั่งภายนอก เป็นไปได้มากว่ามันจะถูกสร้างไว้ในเปลือก ผลกระทบยังคงเหมือนเดิม


6

ไม่มีอะไรมาก: เมื่อไม่มีผู้เขียนไปที่ไพพ์ดูเหมือนว่าผู้อ่านจะปิดตัวเองเช่นส่งคืน EOF เมื่ออ่านและบล็อกเมื่อเปิด

จากหน้าลินุกซ์ ( pipe(7)แต่ดูด้วยfifo(7)):

หากตัวอธิบายไฟล์ทั้งหมดที่อ้างถึงการเขียนจบของไปป์นั้นถูกปิดดังนั้นความพยายามที่จะread(2)ออกจากไปป์นั้นจะเห็นจุดสิ้นสุดของไฟล์ ( read(2)จะคืนค่า 0)

การปิดท้ายการเขียนคือสิ่งที่เกิดขึ้นโดยปริยายในตอนท้ายของการecho ls >fและในขณะที่คุณพูดในกรณีอื่นตัวบ่งชี้ไฟล์จะถูกเปิดไว้


ดูเหมือนว่าคล้ายคลึงกับจำนวนการอ้างอิงใน Java (และภาษา OO อื่น ๆ )! ทำให้รู้สึกว่า
Fixee

2

หลังจากอ่านคำตอบทั้งสองจาก @Kusalananda และ @ikkachu ฉันคิดว่าฉันเข้าใจ ในหน้าต่าง -1 เชลล์กำลังรอบางสิ่งเพื่อเปิดปลายการเขียนของไพพ์แล้วปิดมัน เมื่อสิ้นสุดการเขียนจะเปิดขึ้นเชลล์ใน window-1 พิมพ์พรอมต์ เมื่อสิ้นสุดการเขียนถูกปิดเชลล์จะได้รับ EOF และตาย

ในหน้าต่างด้าน 2 เรามีสองสถานการณ์ที่อธิบายไว้ในคำถามของฉัน: ในสถานการณ์แรกด้วยecho ls > fไม่มีตัวบ่งชี้ไฟล์ 3 ดังนั้นเราจึงมีการechoวางไข่และมันstdinและstdoutมีลักษณะดังนี้:

0 --> tty
1 --> f

จากนั้นechoยกเลิกและเชลล์ปิดตัวอธิบายทั้งสอง เนื่องจาก file descriptor 1 ถูกปิดและการอ้างอิงfสิ้นสุดการเขียนของfถูกปิดและทำให้ EOF ไปที่ window-1

ในสถานการณ์ที่สองเราเรียกใช้exec 3>fในเชลล์ของเราทำให้เชลล์ใช้สภาพแวดล้อมนี้:

bash:
0 --> tty
1 --> tty
2 --> tty
3 --> f

ตอนนี้เรารันecho ls >& 3และเชลล์จัดสรร file descriptors echoดังนี้:

echo:
0 --> tty
1 --> f     # because 3 points to f
2 --> tty

จากนั้นเชลล์จะปิดตัวอธิบายสามตัวด้านบนรวมถึงfแต่fยังคงมีการอ้างอิงถึงมันจากเชลล์เอง นี่คือความแตกต่างที่สำคัญ การปิด descriptor 3 ที่มีexec 3>&-จะปิดการอ้างอิงที่เปิดล่าสุดและทำให้ EOF ไปที่ window-1 ตามที่ @Kusalananda ตั้งข้อสังเกต


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

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