ทำไม fork () ได้รับการออกแบบมาเพื่อคืน file descriptor?


16

เมื่อเขาหน้าเว็บเกี่ยวกับเคล็ดลับท่อตนเองแดน Bernstein อธิบายสภาพการแข่งขันด้วยselect()และสัญญาณข้อเสนอการแก้ปัญหาและสรุปว่า

แน่นอนว่าสิ่งที่ถูกต้องคือการfork()ส่งคืนไฟล์อธิบายไม่ใช่รหัสกระบวนการ

เขาหมายความว่าอย่างไร - เป็นเรื่องเกี่ยวกับความสามารถselect()ในกระบวนการลูกเพื่อจัดการการเปลี่ยนแปลงสถานะของพวกเขาแทนที่จะต้องใช้เครื่องจัดการสัญญาณเพื่อรับการแจ้งเตือนการเปลี่ยนแปลงสถานะเหล่านั้นหรือไม่


บทความนั้นได้รับการผสมเข้าและออกหรือไม่หรือฉันอ่านมันไม่ถูกต้อง?
ctrl-alt-delor

คุณสามารถขอให้ส่งสัญญาณผ่านท่อได้ นั่นคือสิ่งที่ฉันทำ
ctrl-alt-delor

@ ctrl-alt-delor ใช่เขาดูเหมือนจะใช้ "pipe input / output" แปลกไปหน่อย แต่ฉันคิดว่ามันชัดเจนว่าเขาเขียนอยู่ที่ไหนและอ่านจากไปป์ที่ใด ข้อความนั้นมาจากปี 2003 และฉันไม่แน่ใจsignalfdและสิ่งนั้นกลับมาแล้วเหรอ?
ilkkachu

5
แดนรู้ว่าเขากำลังพูดถึงอะไรถึงแม้ว่าเขาจะสามารถยั่วยุอย่างจงใจได้ก็ตาม หากฉันถูกยั่วยุอย่างจงใจฉันก็จะเข้าใจว่าแน่นอนสิ่งที่ถูกต้องคือการกำจัด SIGCHLD
Steve Summit

1
@mosvy ฉันพูดเกินจริงเล็กน้อย แต่ทุกโปรแกรมและโปรแกรมเมอร์ทุกคนที่ฉันเคยเห็นที่พยายามใช้ SIGCHLD มีปัญหากับมัน มันเป็นสภาพการแข่งขันที่รอให้เกิดขึ้น ย้อนกลับไปเมื่อเราปิดกั้นwait()มีบางสิ่งที่คุณทำไม่ได้ดังนั้นมีคนคิดค้น SIGCHLD แต่มันก็เป็นงานที่แย่ จากประสบการณ์ของผมและตอนนี้ที่พวกเขามีอยู่โรยดี nonblocking wait3(), wait4()และ / หรือwaitpid()การโทรที่สถานที่สำคัญ (อาจจะห่วงเหตุการณ์หลักของคุณ) เป็นทางเลือกที่เหนือกว่าอย่างมากมาย
Steve Summit

คำตอบ:


14

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

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

ใช่คำอธิบายของคุณในวรรคสุดท้ายดูเหมือนจะถูกต้องสำหรับฉัน


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


ที่กล่าวว่าฉันจำไม่ได้ว่ากรณีที่ฉันได้อ่านเกี่ยวกับการใช้ PID ซ้ำเป็นปัญหาดังนั้นหากใครต้องการที่จะอธิบายอย่างละเอียดหรือชี้แจงเกี่ยวกับเรื่องนั้นหรือแม้แต่แก้ไขด้านบนอย่าลังเลที่จะทำเช่นนั้น
ilkkachu

2
ตามที่คุณได้กล่าวไปแล้วไม่มีข้อแก้ตัวใด ๆ สำหรับผู้ปกครองที่พบว่า pid ลูกของตัวเองถูกนำกลับมาใช้ใหม่ wait()มันอยู่ในการควบคุมเต็มรูปแบบของสถานการณ์ที่เพราะมันเป็นหนึ่งที่เรียกว่า
Joshua

สิ่งเหล่านี้เรียกว่ากระบวนการ zombie : "กระบวนการที่เสร็จสิ้นการประมวลผล แต่ยังคงมีรายการในตารางกระบวนการ: มันเป็นกระบวนการใน" สถานะ Terminated "สิ่งนี้เกิดขึ้นสำหรับกระบวนการลูกซึ่งรายการยังคงต้องอนุญาตให้ผู้ปกครอง กระบวนการในการอ่านสถานะการออกของเด็ก "
Lassi

6
เป็นมูลค่าการกล่าวขวัญว่าตอนนี้ Linux สามารถส่งคืน file descriptor (pidfd) จากcloneซึ่งเป็นการเรียกระบบจริงที่ fork เรียกใช้บน LInux ธงเพื่อเปิดใช้งานนี้เรียกว่าCLONE_PIDFD- ดูตัวอย่างlwn.net/Articles/784831
KJ Tsanaktsidis

1
@Lie Ryan, Re " การมี fork () จะส่งกลับหมายเลขอ้างอิงระดับโลกมากกว่าหมายเลขอ้างอิงแบบโลคัลจะถูกต้องมากกว่าเนื่องจาก ", Windows ใช้ตัวจัดการกระบวนการ รหัส pid และ exit ติดอยู่รอบ ๆ จนกว่าตัวจัดการทั้งหมดของกระบวนการจะถูกปิด (แทนที่จะรอให้ผู้ปกครองสามารถเก็บเกี่ยวได้) หลีกเลี่ยงสภาพการแข่งขันที่พบบ่อยในระบบยูนิกซ์ เมื่อมือจับทำให้กระบวนการมีชีวิตอยู่มันก็ยิ่งทำให้พวกเขาเป็นมือจับในท้องถิ่นมากกว่าคนทั่วโลก
ikegami

9

มันเป็นเพียงการรำพึงในบรรทัดของ "มันจะดีถ้า Unix ได้รับการออกแบบที่แตกต่างกว่าที่เป็นอยู่"

ปัญหาเกี่ยวกับ PIDs คือพวกเขาอาศัยอยู่ใน namespace ส่วนกลางที่พวกเขาสามารถนำกลับมาใช้ใหม่สำหรับกระบวนการอื่นและมันจะดีถ้าfork()กลับมาในการจัดการบางชนิดที่ผู้ปกครองจะรับประกันได้ว่าจะอ้างถึงกระบวนการเด็กและเสมอ สามารถส่งผ่านไปยังกระบวนการอื่น ๆ ผ่านทางมรดกหรือซ็อกเก็ตยูนิกซ์ / SCM_RIGHTS[1]

ดูการอภิปรายที่นี่สำหรับความพยายามล่าสุดในการ "แก้ไข" ใน Linux รวมถึงการเพิ่มการตั้งค่าสถานะclone()ซึ่งจะทำให้การคืนค่า pid-fd แทนที่จะเป็น PID

แต่ถึงอย่างนั้นก็ไม่ได้กำจัดความต้องการแฮ็คไปป์ [2] หรืออินเทอร์เฟซที่ดีกว่าเนื่องจากสัญญาณที่แจ้งให้ทราบถึงกระบวนการหลักเกี่ยวกับสถานะของเด็กไม่ใช่คนเดียวที่คุณต้องการจัดการในลูปหลัก ของโปรแกรม น่าเสียดายที่สิ่งต่าง ๆ เช่นepoll(7) + signalfd(2)บน Linux หรือkqueue(2)บน BSD ไม่ได้เป็นมาตรฐาน - อินเตอร์เฟสมาตรฐานเดียว(แต่ไม่รองรับในระบบเก่า) นั้นด้อยกว่าpselect(2)มาก

[1] การป้องกัน PID จากการถูกขี่จักรยานใหม่เมื่อถึงเวลาที่waitpid()syscall กลับมาและอาจใช้ค่าส่งคืนบนระบบที่ใหม่กว่าโดยใช้waitid(.., WNOWAIT)แทน

[2] ฉันจะไม่แสดงความคิดเห็นกับ DJ Bernstein อ้างว่าเขาคิดค้นมันขึ้นมา (ขออภัยสำหรับ apophasis ;-))


8

เบิร์นสไตน์ไม่ได้ให้บริบทมากนักสำหรับคำพูด "สิ่งที่ถูกต้อง" แต่ฉันจะเสี่ยงต่อการเดา: การมีส้อม (2) การคืนค่า PID นั้นไม่สอดคล้องกับ open (2), creat (2) และตัวส่งคืนไฟล์ descriptors ส่วนที่เหลือของระบบ Unix สามารถทำการจัดการกระบวนการโดยใช้ file descriptor ซึ่งเป็นตัวแทนของกระบวนการแทนที่จะเป็น PID มี call callalfd (2)อยู่ซึ่งอนุญาตให้มีการโต้ตอบที่ค่อนข้างดีขึ้นระหว่างสัญญาณและตัวอธิบายไฟล์


signalfd (2) ดูดีมากขอบคุณที่พูดถึงมัน! น่าเสียดายที่มันเป็น Linux เท่านั้น
Lassi

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