เมื่อใดที่ฉันควรใช้ mmap สำหรับการเข้าถึงไฟล์


276

สภาวะแวดล้อม POSIX จัดเตรียมการเข้าถึงไฟล์อย่างน้อยสองวิธี มีมาตรฐานสายระบบopen(), read(), write()และเพื่อน ๆ แต่ยังมีตัวเลือกของการใช้mmap()เพื่อแมไฟล์ลงในหน่วยความจำเสมือน

เมื่อใดควรเลือกใช้อีกอันหนึ่ง อะไรคือข้อดีของแต่ละบุคคลที่รวมทั้งสองอินเทอร์เฟซ


16
ดูmmap () เทียบกับบล็อกการอ่านและโพสต์นี้โดย Linus Torvalds ที่อ้างอิงในคำตอบข้อใดข้อหนึ่งที่นั่น
MvG

คำตอบ:


299

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

mmapยังช่วยให้ระบบปฏิบัติการเพิ่มประสิทธิภาพการดำเนินการเพจ ตัวอย่างเช่นพิจารณาสองโปรแกรม โปรแกรมAที่อ่าน1MBไฟล์เป็นบัฟเฟอร์ที่สร้างด้วยmallocและโปรแกรม B ซึ่งmmapsไฟล์ 1MB ไปยังหน่วยความจำ หากระบบปฏิบัติการต้องสลับส่วนหนึ่งของAหน่วยความจำของออกมันจะต้องเขียนเนื้อหาของบัฟเฟอร์เพื่อสลับก่อนจึงจะสามารถนำหน่วยความจำกลับมาใช้ใหม่ได้ ในBกรณีที่ไม่มีการแก้ไขใด ๆ s mmap'd หน้าสามารถนำกลับมาทันทีเพราะระบบปฏิบัติการรู้วิธีที่จะเรียกคืนได้จากไฟล์ที่มีอยู่ที่พวกเขาmmap'd จาก (ระบบปฏิบัติการสามารถตรวจสอบว่าหน้าใดที่ไม่ได้แก้ไขโดยเริ่มต้นทำเครื่องหมายmmapหน้า d ที่เขียนได้เป็นอ่านอย่างเดียวและจับข้อบกพร่อง segคล้ายกับกลยุทธ์Copy on Write )

mmapยังเป็นประโยชน์สำหรับการสื่อสารระหว่างกระบวนการ คุณสามารถmmapไฟล์เป็นอ่าน / เขียนในกระบวนการที่ต้องสื่อสารและจากนั้นใช้การซิงโครไนซ์แบบดั้งเดิมในmmap'dภูมิภาค (นี่คือสิ่งที่MAP_HASSEMAPHOREธงมีไว้สำหรับ)

สถานที่หนึ่งที่mmapน่าอึดอัดใจคือถ้าคุณต้องการทำงานกับไฟล์ที่มีขนาดใหญ่มากบนเครื่อง 32 บิต นี่คือเนื่องจากmmapต้องค้นหาบล็อกที่อยู่ติดกันในพื้นที่ที่อยู่ของกระบวนการของคุณที่มีขนาดใหญ่พอที่จะพอดีกับช่วงทั้งหมดของไฟล์ที่ถูกแมป สิ่งนี้อาจกลายเป็นปัญหาได้หากพื้นที่ที่อยู่ของคุณมีการแยกส่วนซึ่งคุณอาจมีพื้นที่ว่าง 2 GB ที่ว่าง แต่ไม่มีช่วงใดช่วงหนึ่งที่เหมาะสมกับการแมปไฟล์ 1 GB ในกรณีนี้คุณอาจต้องแมปไฟล์เป็นชิ้นเล็กกว่าที่คุณต้องการเพื่อให้พอดี

ความอึดอัดใจที่อาจเกิดขึ้นได้อีกอย่างหนึ่งที่mmapใช้แทนการอ่าน / เขียนคือคุณต้องเริ่มต้นการแมปของคุณเมื่อออฟเซ็ตขนาดหน้ากระดาษ ถ้าคุณเพียงต้องการที่จะได้รับข้อมูลบางส่วนที่ชดเชยXคุณจะต้อง fixup mmapที่ชดเชยเพื่อให้มันเข้ากันได้กับ

และสุดท้ายการอ่าน / เขียนเป็นวิธีเดียวที่คุณสามารถทำงานกับไฟล์บางประเภท mmapไม่สามารถนำมาใช้ในสิ่งที่ต้องการท่อและttys


10
คุณสามารถใช้ mmap () กับไฟล์ที่กำลังเติบโตได้หรือไม่? หรือขนาดถูกแก้ไขที่จุดเมื่อคุณจัดสรร mmap () หน่วยความจำ / ไฟล์?
Jonathan Leffler

29
เมื่อคุณทำการเรียก mmap คุณต้องระบุขนาด ดังนั้นหากคุณต้องการทำบางอย่างเช่นการตัดหางมันไม่เหมาะ
Don Neufeld

5
Afaik ใช้MAP_HASSEMAPHOREเฉพาะกับ BSD
Patrick Schlüter

6
@JonathanLeffler แน่นอนคุณสามารถใช้ mmap () กับไฟล์ที่กำลังเติบโต แต่คุณต้องเรียก mmap () อีกครั้งด้วยขนาดใหม่เมื่อไฟล์ถึงขีด จำกัด ของพื้นที่ที่คุณจัดสรรไว้ในตอนแรก PosixMmapFile ของ LevelDB เป็นตัวอย่างที่ดีให้กับคุณ แต่มันหยุดใช้ mmap จาก 1.15 คุณสามารถรับเวอร์ชันเก่าได้จากGithub
baotiao

4
mmap อาจมีประโยชน์ในกรณีที่ไฟล์ต้องประมวลผลหลายรอบ: ค่าใช้จ่ายในการจัดสรรหน้าหน่วยความจำเสมือนจะได้รับการชำระเพียงครั้งเดียว
Jib

69

พื้นที่หนึ่งที่ฉันพบ mmap () ไม่เป็นข้อได้เปรียบคือเมื่ออ่านไฟล์ขนาดเล็ก (ต่ำกว่า 16K) โอเวอร์เฮดของข้อบกพร่องของหน้าการอ่านไฟล์ทั้งหมดนั้นสูงมากเมื่อเทียบกับการเรียกใช้ระบบ read () เพียงครั้งเดียว นี่เป็นเพราะบางครั้งเคอร์เนลสามารถทำให้การอ่านของคุณพอใจในส่วนของเวลาทั้งหมดซึ่งหมายความว่าโค้ดของคุณจะไม่เปลี่ยน ด้วยความผิดพลาดของหน้าดูเหมือนว่าโปรแกรมอื่นจะถูกกำหนดเวลาไว้ทำให้การทำงานของไฟล์มีเวลาแฝงที่สูงขึ้น


4
+1 ฉันสามารถยืนยันได้ สำหรับไฟล์ขนาดเล็กมันจะเร็วกว่าไปยังmallocหน่วยความจำและทำให้ 1 readเป็นมัน สิ่งนี้อนุญาตให้มีรหัสเดียวกันที่จัดการกับหน่วยความจำแมปจัดการ malloc'ed
Patrick Schlüter

35
สิ่งนี้กล่าวว่าการให้เหตุผลของคุณไม่ถูกต้อง ตัวกำหนดตารางเวลาไม่มีอะไรเกี่ยวข้องกับความแตกต่าง ความแตกต่างมาจากการเข้าถึงการเขียนไปยังตารางหน้าซึ่งเป็นโครงสร้างส่วนกลางของเคอร์เนลที่ถือกระบวนการที่ถือหน้าหน่วยความจำและสิทธิ์การเข้าถึง การดำเนินการนี้อาจมีค่าใช้จ่ายสูงมาก (มันสามารถทำให้บรรทัดแคชสามารถผ่าน TLB ไปได้ตลอดเวลาตารางนั้นเป็นโกลบอลดังนั้นจึงต้องป้องกันการเข้าถึงพร้อมกันเป็นต้น) คุณจำเป็นต้องมีขนาดของแผนที่เพื่อให้ค่าใช้จ่ายในการreadเข้าถึงสูงกว่าค่าใช้จ่ายของการจัดการหน่วยความจำเสมือน
Patrick Schlüter

1
@ PatrickSchlüterโอเคฉันเข้าใจว่ามีค่าใช้จ่ายในช่วงเริ่มต้นของ mmap () ซึ่งเกี่ยวข้องกับการปรับเปลี่ยนตารางหน้า สมมติว่าเราจับคู่ไฟล์กับหน่วยความจำ 16K สำหรับขนาดหน้า 4K mmapต้องอัปเดต 4 รายการในตารางหน้า แต่การใช้readเพื่อคัดลอกลงในบัฟเฟอร์ของ 16K ยังเกี่ยวข้องกับการปรับปรุงรายการตาราง 4 หน้าไม่ต้องพูดถึงมันต้องคัดลอก 16K ลงในพื้นที่ addr ผู้ใช้ ดังนั้นคุณสามารถอธิบายรายละเอียดเกี่ยวกับความแตกต่างของการดำเนินการในตารางหน้าและมันมีราคาแพงกว่าได้mmapอย่างไร
flow2k

45

mmapมีข้อได้เปรียบเมื่อคุณเข้าถึงแบบสุ่มบนไฟล์ขนาดใหญ่ ข้อดีอีกอย่างคือคุณเข้าถึงด้วยการดำเนินการของหน่วยความจำ (memcpy, ตัวคำนวณเลขคณิต) โดยไม่รบกวนการบัฟเฟอร์ I / O ปกติบางครั้งอาจค่อนข้างยากเมื่อใช้บัฟเฟอร์เมื่อคุณมีโครงสร้างที่ใหญ่กว่าบัฟเฟอร์ของคุณ รหัสในการจัดการที่มักจะยากที่จะได้รับ mmap โดยทั่วไปจะง่ายขึ้น mmapนี้กล่าวว่ามีบางอย่างกับดักเมื่อทำงานกับ ตามที่ผู้คนได้กล่าวไปแล้วmmapมีค่าใช้จ่ายค่อนข้างสูงในการติดตั้งดังนั้นจึงคุ้มค่าที่จะใช้สำหรับขนาดที่กำหนดเท่านั้น

สำหรับการเข้าถึงไฟล์แบบลำดับต่อเนื่องมันไม่ได้เป็นทางออกที่ดีกว่าเสมอไปแม้ว่าการเรียกที่เหมาะสมจะmadviseช่วยลดปัญหาได้

คุณต้องระวังด้วยข้อ จำกัด การจัดตำแหน่งของสถาปัตยกรรมของคุณ (SPARC, itanium) ด้วยการอ่าน / เขียน IO บัฟเฟอร์จะถูกจัดตำแหน่งอย่างถูกต้องและไม่ได้ดักจับเมื่อทำการชี้พอยเตอร์ที่ถูกทิ้ง

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


30

นอกจากคำตอบที่ดีอื่น ๆ แล้วคำพูดจากการเขียนโปรแกรมระบบ Linux ที่เขียนโดย Robert Love ผู้เชี่ยวชาญของ Google:

ข้อดีของ mmap( )

การจัดการไฟล์ผ่านmmap( )มีข้อดีอยู่เหนือการเรียกมาตรฐานread( )และการwrite( )เรียกระบบ ในหมู่พวกเขาคือ:

  • การอ่านและการเขียนไปยังไฟล์ที่แม็พหน่วยความจำจะหลีกเลี่ยงการคัดลอกภายนอกที่เกิดขึ้นเมื่อใช้read( )หรือการwrite( )เรียกของระบบโดยที่ข้อมูลจะต้องคัดลอกไปยังและจากบัฟเฟอร์พื้นที่ผู้ใช้

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

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

  • การค้นหารอบ ๆ การทำแผนที่นั้นเกี่ยวข้องกับการเปลี่ยนแปลงตัวชี้เล็กน้อย ไม่จำเป็นต้องมีการlseek( )เรียกระบบ

ด้วยเหตุผลเหล่านี้mmap( )จึงเป็นตัวเลือกที่ชาญฉลาดสำหรับแอพพลิเคชั่นมากมาย

ข้อเสียของ mmap( )

มีบางจุดที่ควรทราบเมื่อใช้mmap( ):

  • การแม็พหน่วยความจำมักเป็นจำนวนเต็มของหน้า ดังนั้นความแตกต่างระหว่างขนาดของไฟล์สำรองและจำนวนหน้าจำนวนเต็มจึงเป็น "การสูญเสีย" เป็นพื้นที่ว่าง สำหรับไฟล์ขนาดเล็กเปอร์เซ็นต์ที่สำคัญของการทำแผนที่อาจสูญเปล่า ตัวอย่างเช่นด้วยเพจขนาด 4 KB การแมปขนาด 7 ไบต์จะเสีย 4,089 ไบต์

  • การแมปหน่วยความจำจะต้องพอดีกับพื้นที่ที่อยู่ของกระบวนการ ด้วยพื้นที่ที่อยู่แบบ 32 บิตการแมปหลายขนาดจำนวนมากอาจส่งผลให้มีการกระจายตัวของพื้นที่ที่อยู่ทำให้ยากต่อการค้นหาพื้นที่ว่างขนาดใหญ่ที่ต่อเนื่องกัน แน่นอนว่าปัญหานี้มีความชัดเจนน้อยกว่าด้วยพื้นที่ที่อยู่ 64 บิต

  • มีค่าใช้จ่ายในการสร้างและบำรุงรักษาการแมปหน่วยความจำและโครงสร้างข้อมูลที่เกี่ยวข้องภายในเคอร์เนล โดยทั่วไปค่าโสหุ้ยนี้จะถูกลบล้างโดยการคัดลอกสำเนาสองครั้งที่กล่าวถึงในส่วนก่อนหน้าโดยเฉพาะอย่างยิ่งสำหรับไฟล์ที่มีขนาดใหญ่และเข้าถึงได้บ่อย

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


13

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

วิธีนี้ทำงานในลักษณะเดียวกับกลไกการเลื่อนหน้าและมักจะปรับให้เหมาะสมสำหรับ I / O ความเร็วสูงโดยการอ่านข้อมูลเกี่ยวกับขอบเขตและขนาดหน้าเพจของระบบ (ปกติคือ 4K) - ขนาดที่แคชระบบไฟล์ส่วนใหญ่ปรับให้เหมาะสม


15
โปรดทราบว่า mmap () ไม่ได้เร็วกว่าการอ่าน () เสมอไป สำหรับการอ่านตามลำดับ mmap () จะทำให้คุณไม่มีข้อได้เปรียบที่วัดได้ - สิ่งนี้ขึ้นอยู่กับหลักฐานเชิงประจักษ์และเชิงทฤษฎี หากคุณไม่เชื่อฉันเขียนการทดสอบของคุณเอง
Tim Cooper

1
ฉันสามารถให้ตัวเลขที่มาจากโครงการของเราซึ่งเป็นดัชนีข้อความชนิดหนึ่งสำหรับฐานข้อมูลวลี ดัชนีมีขนาดใหญ่หลายกิกะไบต์และปุ่มจะถูกเก็บไว้ในต้นไม้ประกอบไปด้วย preadดัชนีจะยังคงเติบโตควบคู่ไปกับการอ่านการเข้าถึงการเข้าถึงนอกส่วนแมปจะทำผ่านทาง บน Solaris 9 Sparc (V890) การเข้าถึง pread อยู่ระหว่าง 2 ถึง 3 เท่าช้ากว่าmemcpyจาก mmap แต่คุณพูดถูกที่การเข้าถึงแบบลำดับไม่จำเป็นต้องเร็วกว่า
Patrick Schlüter

19
แค่นิดหน่อยนิดหน่อย มันไม่ทำงานเหมือนกลไกการเลื่อนหน้า แต่เป็นกลไกการเลื่อนหน้า การแม็พไฟล์กำลังกำหนดพื้นที่หน่วยความจำให้กับไฟล์แทนไฟล์ swap แบบไม่ระบุชื่อ
Patrick Schlüter

2

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

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

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

สิ่งนี้ไม่ต้องการมากกว่าหนึ่งกระบวนการโดยใช้ไฟล์ที่แมปเพื่อพิจารณาเป็นพิเศษ


เคอร์เนลไม่สามารถส่งหน้า mmap ที่ 'สกปรก' โดยการเขียนเนื้อหาลงในไฟล์ต้นแบบก่อนได้หรือไม่
Jeremy Friesner

2
เมื่อใช้read()งานหน้าเว็บที่ใส่ข้อมูลในที่สุดจะไม่มีความสัมพันธ์กับไฟล์ที่อาจมาจาก ดังนั้นจึงไม่สามารถเขียนออกมาได้ยกเว้นเปลี่ยนพื้นที่ หากไฟล์อยู่mmap()edและการทำแผนที่สามารถเขียนได้ (เมื่อเทียบกับการอ่านเท่านั้น) และเขียนไปแล้วมันก็ขึ้นอยู่กับว่าการทำแผนที่เป็นหรือMAP_SHARED MAP_PRIVATEการแมปที่ใช้ร่วมกันสามารถ / ต้องเขียนลงไฟล์ แต่ส่วนตัวไม่สามารถ
TrentP
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.