Khi nào tôi nên sử dụng mmap để truy cập tập tin?


276

Môi trường POSIX cung cấp ít nhất hai cách truy cập tệp. Có tiêu chuẩn cuộc gọi hệ thống open(), read(), write(), và bạn bè, nhưng cũng có tùy chọn sử dụng mmap()để ánh xạ các tập tin vào bộ nhớ ảo.

Khi nào nên sử dụng cái này hơn cái kia? Lợi thế cá nhân của họ là gì bao gồm hai giao diện?


16
Xem thêm mmap () so với các khối đọcbài đăng này của Linus Torvalds được tham chiếu trong một trong những câu trả lời ở đó.
MvG

Câu trả lời:


299

mmapthật tuyệt nếu bạn có nhiều quy trình truy cập dữ liệu theo kiểu chỉ đọc từ cùng một tệp, điều này phổ biến trong các loại hệ thống máy chủ tôi viết. mmapcho phép tất cả các quá trình đó chia sẻ cùng một trang bộ nhớ vật lý, tiết kiệm rất nhiều bộ nhớ.

mmapcũng cho phép hệ điều hành tối ưu hóa các hoạt động phân trang. Ví dụ, xem xét hai chương trình; chương trình Ađọc 1MBtệp vào bộ đệm tạo mallocvà chương trình B mmapstệp 1MB vào bộ nhớ. Nếu hệ điều hành phải hoán đổi một phần Abộ nhớ, nó phải ghi nội dung của bộ đệm để trao đổi trước khi có thể sử dụng lại bộ nhớ. Trong Btrường hợp, bất kỳ trang nào chưa được sửa đổi mmapcó thể được sử dụng lại ngay lập tức vì HĐH biết cách khôi phục chúng từ tệp hiện có mà chúng được tạo mmaptừ đó. (HĐH có thể phát hiện các trang nào không được sửa đổi bằng cách đánh dấu ban đầu các trang có thể ghi mmaplà chỉ đọc và bắt lỗi seg , tương tự như chiến lược Copy on Write ).

mmapcũng hữu ích cho giao tiếp quá trình . Bạn có thể mmapmột tệp dưới dạng đọc / ghi trong các quy trình cần giao tiếp và sau đó sử dụng các nguyên hàm đồng bộ hóa trong mmap'dkhu vực (đây là những gì MAP_HASSEMAPHOREcờ được dùng cho).

Một nơi mmapcó thể khó xử là nếu bạn cần làm việc với các tệp rất lớn trên máy 32 bit. Điều này là do mmapphải tìm một khối địa chỉ liền kề trong không gian địa chỉ của quy trình của bạn đủ lớn để phù hợp với toàn bộ phạm vi của tệp được ánh xạ. Điều này có thể trở thành vấn đề nếu không gian địa chỉ của bạn bị phân mảnh, trong đó bạn có thể có 2 GB không gian địa chỉ miễn phí, nhưng không có phạm vi riêng lẻ nào có thể phù hợp với ánh xạ tệp 1 GB. Trong trường hợp này, bạn có thể phải ánh xạ tệp thành các phần nhỏ hơn bạn muốn làm cho nó phù hợp.

Một sự lúng túng tiềm năng khác với mmapviệc thay thế cho việc đọc / ghi là bạn phải bắt đầu lập bản đồ của mình trên các độ lệch của kích thước trang. Nếu bạn chỉ muốn lấy một số dữ liệu ở phần bù, Xbạn sẽ cần sửa phần bù đó để nó tương thích mmap.

Và cuối cùng, đọc / ghi là cách duy nhất bạn có thể làm việc với một số loại tệp. mmapkhông thể được sử dụng trên những thứ như ốngttys .


10
Bạn có thể sử dụng mmap () trên các tệp đang phát triển không? Hoặc kích thước được cố định tại điểm khi bạn phân bổ bộ nhớ / tệp mmap ()?
Jonathan Leffler

29
Khi bạn thực hiện cuộc gọi mmap, bạn phải chỉ định kích thước. Vì vậy, nếu bạn muốn làm một cái gì đó như thao tác đuôi, nó không phù hợp lắm.
Don Neufeld

5
Afaik MAP_HASSEMAPHORElà đặc trưng cho BSD.
Patrick Schlüter

6
@JonathanLeffler Chắc chắn bạn có thể sử dụng mmap () trên các tệp đang phát triển, nhưng bạn phải gọi lại mmap () với kích thước mới khi tệp đạt đến giới hạn không gian bạn phân bổ ban đầu. PosixMmapFile của LevelDB cho bạn một ví dụ điển hình. Nhưng nó đã ngừng sử dụng mmap từ 1.15. Bạn có thể lấy phiên bản cũ từ Github
baotiao

4
mmap cũng có thể hữu ích trong trường hợp một tệp cần được xử lý trong nhiều lần: chi phí phân bổ các trang bộ nhớ ảo chỉ được thanh toán một lần.
Jib

69

Một lĩnh vực mà tôi thấy mmap () không phải là một lợi thế là khi đọc các tệp nhỏ (dưới 16K). Chi phí hoạt động của lỗi trang để đọc toàn bộ tệp là rất cao so với việc chỉ thực hiện một cuộc gọi hệ thống read (). Điều này là do hạt nhân đôi khi có thể làm bão hòa việc đọc hoàn toàn trong lát thời gian của bạn, có nghĩa là mã của bạn không chuyển đi. Với một lỗi trang, có vẻ như nhiều chương trình khác sẽ được lên lịch, làm cho hoạt động của tệp có độ trễ cao hơn.


4
+1 Tôi có thể xác nhận điều đó. Đối với các tệp nhỏ, nó nhanh hơn mallocmột phần bộ nhớ và tạo 1 readvào nó. Điều này cho phép có cùng mã xử lý bản đồ bộ nhớ xử lý malloc'ed.
Patrick Schlüter

35
Điều này nói rằng, biện minh của bạn cho nó là không đúng. Bộ lập lịch không có gì để làm với sự khác biệt. Sự khác biệt đến từ việc truy cập ghi vào các bảng trang, đây là cấu trúc toàn cầu của hạt nhân đang giữ những tiến trình nào giữ trang bộ nhớ và quyền truy cập của nó. Hoạt động này có thể rất tốn kém (nó có thể vô hiệu hóa các dòng bộ đệm, nó có thể thông qua TLB, bảng là toàn cầu nên phải được bảo vệ chống lại truy cập đồng thời, v.v.). Bạn cần một kích thước bản đồ nhất định để chi phí readtruy cập cao hơn chi phí thao tác bộ nhớ ảo.
Patrick Schlüter

1
@ PatrickSchlüter Được rồi, tôi hiểu rằng có phí trên đầu khi bắt đầu mmap () liên quan đến sửa đổi bảng trang. Nói rằng chúng tôi ánh xạ 16K của một tập tin vào bộ nhớ. Đối với kích thước trang 4K, mmapphải cập nhật 4 mục trong bảng trang. Nhưng sử dụng readđể sao chép vào bộ đệm 16K cũng liên quan đến việc cập nhật 4 mục trong bảng, chưa kể nó cần sao chép 16K vào không gian addr của người dùng. Vì vậy, bạn có thể giải thích về sự khác biệt của các hoạt động trên bảng trang và làm thế nào nó đắt hơn mmap?
Flow2k

45

mmapcó lợi thế khi bạn có quyền truy cập ngẫu nhiên vào các tệp lớn. Một lợi thế khác là bạn truy cập nó với các hoạt động bộ nhớ (memcpy, số học con trỏ), mà không bận tâm đến bộ đệm. I / O bình thường đôi khi có thể khá khó khăn khi sử dụng bộ đệm khi bạn có cấu trúc lớn hơn bộ đệm của mình. Mã để xử lý thường khó lấy đúng, mmap thường dễ hơn. Điều này nói rằng, có những cái bẫy nhất định khi làm việc với mmap. Như mọi người đã đề cập, mmapviệc thiết lập khá tốn kém, vì vậy chỉ đáng sử dụng cho một kích thước nhất định (thay đổi từ máy này sang máy khác).

Đối với các truy cập tuần tự thuần túy vào tệp, nó cũng không phải luôn luôn là giải pháp tốt hơn, mặc dù một cuộc gọi thích hợp để madvisecó thể giảm thiểu vấn đề.

Bạn phải cẩn thận với các hạn chế căn chỉnh của kiến ​​trúc của mình (SPARC, itanium), với IO đọc / ghi, bộ đệm thường được căn chỉnh chính xác và không bị mắc kẹt khi bỏ qua một con trỏ được đúc.

Bạn cũng phải cẩn thận rằng bạn không truy cập bên ngoài bản đồ. Điều này có thể dễ dàng xảy ra nếu bạn sử dụng các hàm chuỗi trên bản đồ của mình và tệp của bạn không chứa \ 0 ở cuối. Nó sẽ hoạt động hầu hết thời gian khi kích thước tệp của bạn không phải là bội số của kích thước trang vì trang cuối cùng được điền 0 (vùng được ánh xạ luôn ở kích thước bội số của kích thước trang của bạn).


30

Ngoài các câu trả lời hay khác, một trích dẫn từ lập trình hệ thống Linux được viết bởi chuyên gia Robert Love của Google:

Lợi ích của mmap( )

Thao tác các tệp thông qua mmap( )có một số lợi thế so với các cuộc gọi hệ thống read( )và tiêu chuẩn write( ). Trong số đó là:

  • Đọc và ghi vào tệp ánh xạ bộ nhớ sẽ tránh được bản sao ngoại lai xảy ra khi sử dụng các cuộc gọi read( )hoặc write( )hệ thống, trong đó dữ liệu phải được sao chép vào và từ bộ đệm không gian người dùng.

  • Ngoài bất kỳ lỗi trang tiềm năng nào, việc đọc và ghi vào tệp ánh xạ bộ nhớ sẽ không phát sinh bất kỳ cuộc gọi hệ thống hoặc chi phí chuyển đổi ngữ cảnh nào. Nó đơn giản như truy cập bộ nhớ.

  • Khi nhiều quy trình ánh xạ cùng một đối tượng vào bộ nhớ, dữ liệu được chia sẻ giữa tất cả các quy trình. Ánh xạ có thể ghi chỉ đọc và chia sẻ được chia sẻ toàn bộ; ánh xạ có thể ghi riêng tư có các trang chưa được COW (sao chép khi viết) được chia sẻ.

  • Tìm kiếm xung quanh ánh xạ liên quan đến các thao tác con trỏ tầm thường. Không cần cho lseek( )cuộc gọi hệ thống.

Vì những lý do này, mmap( )là một lựa chọn thông minh cho nhiều ứng dụng.

Nhược điểm của mmap( )

Có một vài điểm cần lưu ý khi sử dụng mmap( ):

  • Ánh xạ bộ nhớ luôn là số nguyên trang có kích thước. Do đó, sự khác biệt giữa kích thước của tệp sao lưu và số lượng trang nguyên là "lãng phí" dưới dạng không gian chùng. Đối với các tệp nhỏ, một tỷ lệ đáng kể của ánh xạ có thể bị lãng phí. Ví dụ: với các trang 4 KB, ánh xạ 7 byte làm lãng phí 4.089 byte.

  • Ánh xạ bộ nhớ phải vừa với không gian địa chỉ của tiến trình. Với không gian địa chỉ 32 bit, một số lượng lớn ánh xạ có kích thước khác nhau có thể dẫn đến sự phân mảnh không gian địa chỉ, khiến bạn khó tìm thấy các vùng tiếp giáp lớn miễn phí. Vấn đề này, tất nhiên, ít rõ ràng hơn với không gian địa chỉ 64 bit.

  • Có chi phí trong việc tạo và duy trì ánh xạ bộ nhớ và các cấu trúc dữ liệu liên quan bên trong kernel. Chi phí chung này thường bị cản trở bởi việc loại bỏ bản sao đôi được đề cập trong phần trước, đặc biệt đối với các tệp lớn hơn và thường xuyên truy cập.

Vì những lý do này, lợi ích của mmap( )được nhận ra nhiều nhất khi tệp được ánh xạ lớn (và do đó, bất kỳ không gian bị lãng phí nào là một tỷ lệ nhỏ của tổng ánh xạ) hoặc khi tổng kích thước của tệp được ánh xạ chia hết cho kích thước trang ( và do đó không có không gian lãng phí).


13

Ánh xạ bộ nhớ có tiềm năng cho lợi thế tốc độ rất lớn so với IO truyền thống. Nó cho phép hệ điều hành đọc dữ liệu từ tệp nguồn khi các trang trong tệp ánh xạ bộ nhớ được chạm vào. Điều này hoạt động bằng cách tạo các trang bị lỗi, mà HĐH phát hiện và sau đó HĐH sẽ tự động tải dữ liệu tương ứng từ tệp.

Điều này hoạt động giống như cơ chế phân trang và thường được tối ưu hóa cho I / O tốc độ cao bằng cách đọc dữ liệu về ranh giới và kích thước trang hệ thống (thường là 4K) - một kích thước mà hầu hết các bộ đệm hệ thống tệp được tối ưu hóa.


15
Lưu ý rằng mmap () không phải lúc nào cũng nhanh hơn read (). Đối với các lần đọc tuần tự, mmap () sẽ không cung cấp cho bạn lợi thế có thể đo lường được - điều này dựa trên bằng chứng thực nghiệm và lý thuyết. Nếu bạn không tin tôi, hãy viết bài kiểm tra của riêng bạn.
Tim Cooper

1
Tôi có thể đưa ra những con số đến từ dự án của chúng tôi, một loại chỉ mục văn bản cho cơ sở dữ liệu cụm từ. Chỉ số này là một số Gigabyte lớn và các phím được giữ trong một cây ternary. Chỉ mục vẫn đang phát triển song song để truy cập đọc, truy cập bên ngoài các phần được ánh xạ được thực hiện thông qua pread. Trên Solaris 9 Sparc (V890), truy cập pread chậm hơn memcpytừ 2 đến 3 lần so với từ mmap. Nhưng bạn đã đúng rằng việc truy cập tuần tự không nhất thiết phải nhanh hơn.
Patrick Schlüter

19
Chỉ cần một chút nitpick. Nó không hoạt động như cơ chế phân trang, nó là cơ chế phân trang. Ánh xạ tệp là gán vùng nhớ cho tệp thay vì tệp hoán đổi ẩn danh.
Patrick Schlüter

2

Một lợi thế chưa được liệt kê là khả năng mmap()giữ ánh xạ chỉ đọc dưới dạng các trang sạch . Nếu một người cấp phát một bộ đệm trong không gian địa chỉ của quy trình, sau đó sử dụng read()để điền vào bộ đệm từ một tệp, các trang bộ nhớ tương ứng với bộ đệm đó sẽ bị bẩn vì chúng đã được ghi vào.

Các trang bẩn không thể được loại bỏ khỏi RAM bởi kernel. Nếu có không gian hoán đổi, thì chúng có thể được phân trang để trao đổi. Nhưng điều này rất tốn kém và trên một số hệ thống, chẳng hạn như các thiết bị nhúng nhỏ chỉ có bộ nhớ flash, không có sự trao đổi nào cả. Trong trường hợp đó, bộ đệm sẽ bị kẹt trong RAM cho đến khi quá trình thoát, hoặc có thể đưa nó trở lại với madvise().

Không viết vào mmap()các trang được sạch sẽ. Nếu hạt nhân cần RAM, đơn giản là nó có thể thả chúng xuống và sử dụng RAM mà các trang đã vào. Nếu quá trình có ánh xạ truy cập lại, nó gây ra lỗi trang, hạt nhân tải lại các trang từ tệp mà chúng xuất phát ban đầu . Giống như cách họ đã được cư trú ở nơi đầu tiên.

Điều này không yêu cầu nhiều hơn một quá trình sử dụng tệp được ánh xạ là một lợi thế.


Hạt nhân có thể làm rơi trang mmap'd 'bẩn' bằng cách viết nội dung của nó ra tệp bên dưới không?
Jeremy Friesner

2
Khi sử dụng read(), các trang mà dữ liệu cuối cùng được đưa vào không có mối quan hệ nào với tệp mà chúng có thể đến từ đó. Vì vậy, chúng không thể được viết ra, ngoại trừ để trao đổi không gian. Nếu một tệp là mmap()edvà ánh xạ có thể ghi được (trái ngược với chỉ đọc) và được ghi vào, thì nó phụ thuộc vào việc ánh xạ là MAP_SHAREDhay MAP_PRIVATE. Ánh xạ được chia sẻ có thể / phải được ghi vào tệp, nhưng riêng tư thì không thể.
TrentP
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.