fork () คัดลอก heap ของกระบวนการทั้งหมดใน Linux ทันทีหรือไม่


30

การfork()เรียกระบบโคลนกระบวนการลูกจากกระบวนการทำงาน กระบวนการทั้งสองนั้นเหมือนกันยกเว้น PID

ตามธรรมชาติแล้วหากกระบวนการเพิ่งอ่านจากกองของพวกเขามากกว่าที่จะเขียนลงไปการคัดลอกฮีปนั้นจะเป็นการสูญเสียความทรงจำครั้งใหญ่

คัดลอกฮีปกระบวนการทั้งหมดหรือไม่ มันได้รับการปรับให้เหมาะสมในวิธีที่เขียนเฉพาะการทริกเกอร์สำเนาฮีป

คำตอบ:


19

ความสมบูรณ์ทั้งหมดของfork()การดำเนินการโดยใช้ mmap / คัดลอกเมื่อเขียน

สิ่งนี้ไม่เพียงส่งผลกระทบต่อฮีป แต่ยังแชร์ไลบรารีสแต็กพื้นที่ BSS ด้วย

ซึ่งหมายความว่า fork นั้นเป็นการดำเนินการที่เบามากจนกระทั่งกระบวนการ 2 ผลลัพธ์ (พาเรนต์และชายด์) เริ่มเขียนลงในช่วงหน่วยความจำ คุณลักษณะนี้เป็นผู้สนับสนุนหลักสำหรับ lethality ของ fork-bombs - คุณจบลงด้วยกระบวนการมากเกินไปก่อนที่เคอร์เนลจะโอเวอร์โหลดด้วยการจำลองหน้าและการสร้างความแตกต่าง

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

แม้execve()เป็นหลัก "โปรด mmap binary / ld.so / whatnot ตามด้วย execute" - และ VM จัดการการโหลดที่แท้จริงของกระบวนการเป็น RAM และการดำเนินการ ตัวแปรที่ไม่มีการกำหนดค่าท้องถิ่นจบลงด้วยการ mmaped จาก 'zero-page' - หน้า copy-on-write พิเศษสำหรับอ่านอย่างเดียวที่มีเลขศูนย์ตัวแปรเริ่มต้นในท้องถิ่นสิ้นสุดลงเป็น mmaped (copy-on-write อีกครั้ง) จากไฟล์ไบนารีของตัวเอง เป็นต้น


ข้อยกเว้นที่น่าสังเกตอย่างหนึ่งคือกระบวนการของ Java ค้นหา"หน่วยความจำ fork java"และคุณจะพบปัญหาหลายสิบปัญหาที่มีผลต่อเซิร์ฟเวอร์ขนาดใหญ่JVMหรือJVM ในตัวที่พยายามเรียกใช้คำสั่งเชลล์ขนาดเล็กและล้มเหลวอย่างน่าสมเพชในข้อยกเว้น"ไม่สามารถจัดสรรหน่วยความจำ" (นี่เป็นเพียงการเชื่อมโยงแบบสุ่ม กับสภาพแวดล้อม Java) คำตอบ SOนี้กล่าวโทษตัวรวบรวมขยะและตัวรวบรวม JIT ของ JVM เพื่อป้องกันไม่ให้กระบวนการหน่วยความจำถูกแบ่งใช้
WhiteWinterWolf

24

เคอร์เนล Linux ใช้งาน Copy-on-Write เมื่อfork()มีการเรียกใช้ เมื่อเรียกใช้ syscall หน้าเว็บที่ผู้ปกครองและเด็กแชร์จะถูกทำเครื่องหมายว่าอ่านอย่างเดียว

หากการเขียนถูกดำเนินการบนหน้าแบบอ่านอย่างเดียวก็จะถูกคัดลอกเนื่องจากหน่วยความจำจะไม่เหมือนกันระหว่างสองกระบวนการ ดังนั้นหากการดำเนินการอ่านเท่านั้นที่จะดำเนินการหน้าจะไม่คัดลอกเลย


1
+1 ขอบคุณ! 1. คุณช่วยจัดหาลิงค์อ้างอิงได้ไหม 2. กองสำเนาทั้งหมดหรือในบางส่วนหรือไม่?
Adam Matan

4
2. - ในหน้า :) เคอร์เนลมีความเข้าใจน้อยมากในสิ่งที่ "heap" คือ - สำหรับเคอร์เนลมันเป็นเพียงส่วนหนึ่งของเพจส่วนบุคคล mmapped ที่ตัวจัดสรร libc จัดการตามที่ต้องการ
qdot

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

@mmk FYI ฉันรู้สึกประหลาดใจมากกับ "หมายเหตุด้านที่น่าสนใจของคุณ" และดังนั้นฉันจึงทดสอบ (บน Linux 3.2.0) เพื่อดูและดูเหมือนจะไม่เป็นจริง ฉันใช้/proc/self/pagemapเพื่อกำหนดที่อยู่เสมือนกับการทำแผนที่หน้าทางกายภาพสำหรับวัตถุประสงค์ของการทดสอบ อย่างที่ฉันคาดไว้ถ้าหลานและเฉพาะหลานเขียนหน้าร่วมกันดังนั้นผู้ปกครองและเด็กเดิมยังคงแบ่งปันต่อไป เฉพาะหลานสาวเท่านั้นที่จบลงด้วยสำเนาส่วนตัว
Celada

@Celada อืมมม ฉันได้อ่านสิ่งนี้และฉันไม่จำรุ่นเคอร์เนลที่อ้างถึง (อาจเป็นรุ่นเก่ากว่า) ดังนั้นจึงอาจไม่ถูกต้องอีกต่อไป
mmk

10

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

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