ทำไม vfork () ตั้งใจที่จะใช้เมื่อกระบวนการลูกเรียก exec () หรือ exit () ทันทีหลังจากการสร้าง?


11

แนวคิดระบบปฏิบัติการและ APUE พูด

ด้วย vfork () กระบวนการหลักถูกระงับและกระบวนการลูกใช้พื้นที่ที่อยู่ของพาเรนต์ เนื่องจาก vfork () ไม่ได้ใช้ copy-on-write ถ้ากระบวนการลูกเปลี่ยนแปลงหน้าใด ๆ ของพื้นที่ที่อยู่ของผู้ปกครองเพจที่ถูกแก้ไขจะปรากฏให้ผู้ปกครองเห็นได้เมื่อดำเนินการต่อ ดังนั้นvfork () ต้องใช้ด้วยความระมัดระวังเพื่อให้แน่ใจว่ากระบวนการลูกไม่ได้แก้ไขพื้นที่ที่อยู่ของผู้ปกครอง

vfork () มีวัตถุประสงค์เพื่อใช้เมื่อกระบวนการลูกเรียกใช้ exec () หรือ exit () ทันทีหลังจากการสร้าง

ฉันจะเข้าใจประโยคสุดท้ายได้อย่างไร

เมื่อกระบวนการลูกสร้างโดยการvfork()โทรexec()ไม่ได้exec()แก้ไขพื้นที่ที่อยู่ของกระบวนการหลักโดยการโหลดโปรแกรมใหม่หรือไม่

เมื่อกระบวนการลูกที่สร้างโดยการvfork()โทรexit()ไม่ exit()ได้แก้ไขพื้นที่ที่อยู่ของกระบวนการหลักเมื่อยกเลิกการเป็นเด็ก?

ฉันชอบลีนุกซ์

ขอบคุณ

คำตอบ:


15

เมื่อกระบวนการลูกสร้างโดยการvfork()โทรexec()ไม่ได้exec()แก้ไขพื้นที่ที่อยู่ของกระบวนการหลักโดยการโหลดโปรแกรมใหม่หรือไม่

ไม่exec()ให้พื้นที่ที่อยู่ใหม่สำหรับโปรแกรมใหม่ มันไม่ได้ปรับเปลี่ยนพื้นที่ที่อยู่ของผู้ปกครอง ดูตัวอย่างการอภิปรายของexecฟังก์ชั่นใน POSIXและลินุกซ์execve() manpage

เมื่อกระบวนการลูกที่สร้างโดย vfork () เรียก exit () exit () จะไม่แก้ไขพื้นที่ที่อยู่ของกระบวนการหลักเมื่อยกเลิกการเป็นเด็กหรือไม่

exit()อาจธรรมดา- มันรัน exit hooks ที่ติดตั้งโดยโปรแกรมที่กำลังทำงานอยู่ (รวมถึงไลบรารี) vfork()มีข้อ จำกัด มากขึ้น บนลีนุกซ์, มันสั่งการใช้งาน_exit()ซึ่งไม่ได้เรียกฟังก์ชันการล้างค่าของไลบรารี C

vfork()กลายเป็นเรื่องยากมากที่จะได้รับสิทธิ; มันถูกลบออกในเวอร์ชันปัจจุบันของมาตรฐาน POSIX และposix_spawn()ควรใช้แทน

อย่างไรก็ตามหากคุณไม่ทราบว่ากำลังทำอะไรอยู่คุณไม่ควรใช้อย่างใดอย่างหนึ่งvfork()หรือposix_spawn(); ติดเก่าที่ดีและfork()exec()

manpage Linux ที่ลิงก์ด้านบนมีบริบทเพิ่มเติม:

อย่างไรก็ตามในวันเก่าที่ไม่ดีfork(2) จะต้องมีการทำสำเนาที่สมบูรณ์ของพื้นที่ข้อมูลที่โทรมามักจะไม่มีความจำเป็นเนื่องจากมักจะทันทีหลังจากexec(3)จะทำ ดังนั้นเพื่อประสิทธิภาพที่สูงขึ้น BSD แนะนำการvfork() เรียกใช้ระบบซึ่งไม่ได้คัดลอกพื้นที่ที่อยู่ของกระบวนการหลักอย่างเต็มที่ แต่ได้ยืมหน่วยความจำของผู้ปกครองและชุดควบคุมของเธรดจนกว่าจะมีการเรียกexecve(2)หรือออกจากระบบ กระบวนการหลักถูกระงับในขณะที่เด็กกำลังใช้ทรัพยากร การใช้งาน vfork()นั้นยุ่งยาก: ตัวอย่างเช่นการไม่แก้ไขข้อมูลในกระบวนการหลักขึ้นอยู่กับการรู้ว่าตัวแปรใดถูกเก็บไว้ในรีจิสเตอร์


ขอบคุณ "exec () ให้พื้นที่ที่อยู่ใหม่สำหรับโปรแกรมใหม่" พฤติกรรมปกติของ exec () เพื่อโหลดโปรแกรมลงในพื้นที่ที่อยู่ของกระบวนการหรือไม่? ฉันไม่พบลิงก์สองรายการที่สร้างพื้นที่ที่อยู่ใหม่ไม่ว่าจะเป็นปกติหรือโดยเฉพาะสำหรับ vfork ()
ทิม

1
สิ่งที่ตลกคือ vfork () กำลังเอาชนะทุกอย่างได้แล้ว มันเร็วกว่าทางส้อม () เมื่อคุณมีหน่วยความจำที่สามารถเขียนได้กิกะไบต์
โจชัว

2
posix_spawnกรุณาอย่าบอกคนที่จะใช้ มันเป็นอย่างยากที่จะเขียนรหัสที่ถูกต้องโดยใช้posix_spawnกว่าเก่าธรรมดาforkและถ้าคุณพยายามที่คุณอาจวิ่งเข้าไปในผนังอิฐจากที่นั่นไม่ได้ถูกดำเนินการแฟ้มหรือแอตทริบิวต์ที่ไม่สิ่งที่คุณต้องทำในระหว่างและfork execและไม่รับประกันว่าจะมีประสิทธิภาพเหมือน vfork ดังนั้นจึงไม่ได้แก้ปัญหาที่คนต้องการแก้
zwol

1
@zwol: นั่นเป็นคำแนะนำที่ไม่ดีจริงๆ ในขณะที่posix_spawnอาจไม่มีฟังก์ชั่นที่คุณต้องการ (คุณสามารถแก้ปัญหานี้ผ่านโปรแกรมตัวช่วยตัวกลางซึ่งเขียนในสคริปต์เชลล์ C หรือ inline-on-cmdline) ความพยายามใด ๆ ที่จะบรรลุสิ่งที่คุณต้องการด้วยvforkพฤติกรรมที่ไม่เป็นอันตราย ข้อมูลจำเพาะสำหรับvforkไม่อนุญาตให้เรียกฟังก์ชั่นแบบสุ่มเพื่อตั้งค่าสถานะสำหรับเด็กที่จะสืบทอดมาก่อนexecveและความพยายามที่จะทำเช่นนั้นอาจทำให้สถานะของผู้ปกครองเสียหาย
. GitHub หยุดช่วยน้ำแข็ง

1
@ โจชัว: การใช้งานที่ทันสมัยของposix_spawnการแสดงประมาณเดียวกับvforkภายใต้เงื่อนไขส่วนใหญ่ กรณีที่มีความแตกต่างมีแนวโน้มที่จะเป็นกรณีที่vforkไม่ปลอดภัยอย่างมาก: ที่มีการติดตั้งตัวจัดการสัญญาณที่posix_spawnต้องระงับการทำงานในเด็กก่อนที่จะ exec
.. GitHub หยุดช่วยเหลือน้ำแข็ง

4

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

ในขณะที่เด็กกำลังเรียกใช้กระบวนการหลักถูกบล็อกในขณะที่เด็กยืมพื้นที่ที่อยู่ของผู้ปกครอง

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

สิ่งที่แก้ไขข้อมูลทั่วโลกเช่น:

  • โทร malloc () หรือฟรี ()

  • ใช้ stdio

  • แก้ไขการตั้งค่าสัญญาณ

  • vfork()การปรับเปลี่ยนตัวแปรที่ไม่ได้อยู่ในท้องถิ่นเพื่อฟังก์ชั่นที่เรียกว่า

  • ...

เมื่อคุณโทร_exit()(สำคัญไม่เคยโทรexit()) เด็กจะถูกยกเลิกและการควบคุมจะถูกส่งกลับไปยังผู้ปกครอง

หากคุณเรียกใช้ฟังก์ชันใด ๆ จากexec*()ตระกูลระบบจะสร้างพื้นที่ที่อยู่ใหม่ด้วยรหัสโปรแกรมใหม่ข้อมูลใหม่และส่วนหนึ่งของสแต็กจากพาเรนต์ (ดูด้านล่าง) เมื่อพร้อมแล้วเด็กจะไม่ยืมพื้นที่ที่อยู่จากเด็กอีกต่อไป แต่ใช้พื้นที่ที่อยู่ของตนเอง

การควบคุมจะถูกส่งกลับไปยังพาเรนต์เนื่องจากพื้นที่ของแอดเดรสไม่ได้ถูกใช้โดยกระบวนการอื่นอีกต่อไป

สำคัญ: บน Linux ไม่มีvfork()การใช้งานจริง Linux ค่อนข้างใช้งานvfork()ตามfork()แนวคิดCopy on Write ที่นำเสนอโดย SunOS-4.0 ในปี 1988 เพื่อให้ผู้ใช้เชื่อว่าพวกเขาใช้งานvfork()Linux เพียงแค่ตั้งค่าข้อมูลที่ใช้ร่วมกันและระงับผู้ปกครองในขณะที่เด็กไม่ได้โทร_exit()หรือหนึ่งในexec*()หน้าที่

Linux จึงไม่ได้รับประโยชน์จากการที่จริงvfork()ไม่จำเป็นต้องตั้งค่าคำอธิบายพื้นที่ที่อยู่สำหรับเด็กในเคอร์เนล ผลนี้ในที่ไม่ได้เร็วกว่าvfork() fork()บนระบบที่ใช้จริงvfork()มันเป็นปกติได้เร็วกว่า 3 เท่าfork()และมีผลต่อประสิทธิภาพการทำงานของเปลือกหอยที่ใช้vfork()- ksh93เมื่อเร็ว ๆ นี้และBourne Shellcsh

เหตุผลที่คุณไม่ควรเรียกexit()จากvfork()เด็กเอ็ดคือว่าexit()วูบวาบ stdio ในกรณีที่มีข้อมูล unflushed vfork()จากเวลาก่อนที่จะเรียก นี่อาจทำให้เกิดผลลัพธ์ที่แปลก

BTW: posix_spawn()มีการใช้งานด้านบนvfork()ดังนั้นvfork()จะไม่ถูกลบออกจากระบบปฏิบัติการ จะได้รับการกล่าวถึงว่าลินุกซ์ไม่ได้ใช้สำหรับvfork()posix_spawn()

สำหรับสแต็กมีเอกสารน้อยนี่คือสิ่งที่หน้า man ของ Solaris พูดว่า:

 The vfork() and vforkx() functions can normally be used  the
 same  way  as  fork() and forkx(), respectively. The calling
 procedure, however, should not return while running  in  the
 child's  context,  since the eventual return from vfork() or
 vforkx() in the parent would be to a  stack  frame  that  no
 longer  exists. 

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


4
ทั้งห้องสมุด GNU C หรือ C คิดถึงดำเนินห้องสมุดด้านบนของลินุกซ์posix_spawn() พวกเขาทั้งสองใช้มันอยู่ด้านบนของvfork() __clone()
JdeBP

1
@JdeBP: คุณรู้vfork()แค่โทรclone()ใช่มั้ย มันเป็นหนึ่งซับในเคอร์เนล
Joshua

1
"สำคัญ: บน Linux ไม่มีการนำ vfork () ไปใช้จริง" <- สิ่งนี้ไม่เป็นความจริงและไม่เป็นความจริงเป็นเวลาอย่างน้อยหนึ่งทศวรรษ หากเกณฑ์มาตรฐานเชลล์ของคุณไม่ได้สังเกตเห็นความแตกต่างของประสิทธิภาพระหว่างvforkและforkบน Linux แสดงว่าทำสิ่งผิดปกติ
zwol

1
ช่วงครึ่งหลังของคำตอบนี้เริ่มต้นด้วย "สำคัญ: บน Linux ไม่มีการใช้งาน vfork () จริง ๆ " ส่วนใหญ่หรือผิดทั้งหมด
.. GitHub หยุดช่วยเหลือน้ำแข็ง

1
กรุณาอย่าทำการเรียกร้องโดยไม่ตรวจสอบ Bourne Shell ปัจจุบันสามารถคอมไพล์ด้วยและไม่มีการสนับสนุน vfork ดังนั้นแม้ว่าคุณเชื่อว่าคุณลักษณะการดีบักจาก Linux ไม่สามารถให้ผลลัพธ์ที่เชื่อถือได้คุณสามารถเปรียบเทียบเวลาดำเนินการสำหรับการกำหนดค่าการโทรด้วยและกับ vfork ในเชลล์ ฉันใช้สคริปต์กำหนดค่าด้วยการทดสอบ 800 ครั้ง บน Solaris Bourne Shell ที่ใช้ vfork ต้องการเวลารวมของ cpu ระบบน้อยกว่าถึง 30% เมื่อเทียบกับ fork case บน Linux การทดสอบเดียวกันจะทำให้เวลา cpu ระบบน้อยกว่า 10% บน Solaris ไม่ใช่ 3x เนื่องจากมีการเรียกคอมไพเลอร์จำนวนมาก
schily
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.