| mmap(2) | System Calls Manual | mmap(2) |
mmap, munmap - mapuje lub usuwa mapowanie plików lub urządzeń w pamięci
Standardowa biblioteka C (libc, -lc)
#include <sys/mman.h>
void *mmap(void addr[.length], size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void addr[.length], size_t length);
Informacje o wymaganych makrach sprawdzania cech znajdują się w rozdziale UWAGI.
mmap() tworzy nowe mapowanie w wirtualnej przestrzeni adresowej procesu wywołującego. Początkowy adres nowego mapowania podaje się w addr. Argument length określa długość mapowania (musi być ono większe niż 0).
Jeśli addr wynosi NULL, to jądro wybiera (wyrównany do strony) adres, na którym utworzy mapowanie; jest to najbardziej przenośna metoda tworzenia nowych mapowań. Jeśli addr nie wynosi NULL, to jądro traktuje go jako wskazówkę na temat miejsca umieszczenia mapowania; w Linuksie, jądro wybierze najbliższą granicę strony (jednak będzie ona zawsze równa lub większa wartości określonej w /proc/sys/vm/mmap_min_addr) i spróbuje utworzyć tu nowe mapowanie. Jeśli inne mapowanie już tu istnieje, jądro wybierze nowy adres, który może, lecz nie musi, zależeć od wskazówki. Adres nowego mapowania jest zwracany jako wynik wywołania.
Zawartość mapowania pliku (w przeciwieństwie do mapowania anonimowego; zob. MAP_ANONYMOUS poniżej) jest inicjowana za pomocą length bajtów zaczynających się w przesunięciu offset w pliku (lub innym obiekcie), do którego odnosi się deskryptor pliku fd. offset musi być wielokrotnością rozmiaru strony, jaki zwraca sysconf(_SC_PAGE_SIZE).
Po powrocie wywołania mmap(), deskryptor pliku fd może być niezwłocznie zamknięty, nie unieważniając mapowania.
Argument prot opisuje oczekiwany sposób ochrony pamięci mapowania (i nie może być sprzeczny z trybem otwarcia pliku). Może on być równy PROT_NONE lub może być sumą bitową (OR) jednego lub więcej spośród innych znaczników PROT_*.
Argument flags określa, czy aktualizacje mapowania są widoczne dla innych procesów mapowanych do tego samego miejsca i czy aktualizacje są przenoszone na sam plik. To zachowanie zależy od podanej, dokładnie jednej z poniższych wartości flags:
MAP_SHARED i MAP_PRIVATE są opisane w POSIX.1-2001 i POSIX.1-2008. MAP_SHARED_VALIDATE jest rozszerzeniem Linuksa.
Ponadto, zero lub więcej poniższych wartości można zsumować bitowo (OR) we flags:
#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT)
#define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)
Z powyższych znaczników, jedynie MAP_FIXED jest określony przez POSIX.1-2001 i POSIX.1-2008. Większość systemów obsługuje jednak także MAP_ANONYMOUS (lub jego synonim MAP_ANON).
Wywołanie systemowe munmap() usuwa mapowanie z podanego zakresu adresów i powoduje, że dalsze odwołania do adresów z tego zakresu będą generować nieprawidłowe odwołania do pamięci. Mapowanie obszaru jest również automatycznie usuwane, gdy proces się zakończy. Z drugiej strony, zamknięcie deskryptora pliku nie usuwa mapowania obszaru.
Adres addr musi być wielokrotnością rozmiaru strony (ale length już nie musi). Usuwane jest mapowanie wszystkich stron zawierających fragmenty ze wskazanego zakresu, wszystkie późniejsze odwołania do tych stron wygenerują SIGSEGV. Nie jest błędem, gdy brak w podanym zakresie zamapowanych stron.
Po pomyślnym zakończeniu mmap() zwraca wskaźnik do mapowanego obszaru. Po błędzie zwracane jest MAP_FAILED (tj. (void *) -1) i ustawiane errno, wskazując błąd.
Po pomyślnym zakończeniu munmap() zwraca 0. Po błędzie zwracane jest -1 i ustawiane errno, wskazując błąd (prawdopodobnie 1EINVAL).
Użycie zamapowanego obszaru może spowodować wystąpienie następujących sygnałów:
Informacje o pojęciach używanych w tym rozdziale można znaleźć w podręczniku attributes(7).
| Interfejs | Atrybut | Wartość |
| mmap(), munmap() | Bezpieczeństwo wątkowe | MT-bezpieczne |
Na niektórych architekturach sprzętowych (np. i386), PROT_WRITE wymusza PROT_READ. Od architektury zależy, czy PROT_READ wymusza PROT_EXEC, czy nie. Programy przenośne powinny zawsze ustawiać PROT_EXEC, jeśli mają zamiar wykonywać kod w nowym mapowaniu.
Przenośnym sposobem tworzenia mapowań jest podanie addr jako 0 (NULL) i pominięcie MAP_FIXED z flags. W takim przypadku to system wybierze adres mapowania; wybrany adres uniknie konfliktu z istniejącymi mapowaniami i nie będzie wynosił 0. Jeśli podano znacznik MAP_FIXED , a addr wynosi 0 (NULL), to mapowany adres będzie wynosił 0 (NULL).
Pewne stałe flags są zdefiniowane tylko wtedy, gdy zdefiniowano określone makro sprawdzania cech (być może stało się to domyślnie): _DEFAULT_SOURCE z glibc 2.19 lub późniejszymi; _BSD_SOURCE lub _SVID_SOURCE w glibc 2.19 i wcześniejszych (podanie _GNU_SOURCE jest również wystarczające i wymaganie właśnie tego makra byłoby logiczniejsze, skoro wszystkie te znaczniki są typowo linuksowe). Chodzi tu o znaczniki: MAP_32BIT, MAP_ANONYMOUS (i synonim MAP_ANON), MAP_DENYWRITE, MAP_EXECUTABLE, MAP_FILE, MAP_GROWSDOWN, MAP_HUGETLB, MAP_LOCKED, MAP_NONBLOCK, MAP_NORESERVE, MAP_POPULATE oraz MAP_STACK.
Niniejszy podręcznik opisuje interfejs zapewniany przez funkcję opakowującą mmap() z glibc. Pierwotnie, funkcja ta przywoływała wywołanie systemowe o tej samej nazwie. Od Linuksa 2.4, to wywołanie zostało zastąpione wywołaniem mmap2(2), dlatego obecnie funkcja opakowująca mmap() z glibc przywołuje mmap2(2) z odpowiednio dostosowanymi wartościami offset.
POSIX.1-2008.
POSIX.1-2001, SVr4, 4.4BSD.
W systemach POSIX, na których dostępne są mmap(), msync(2) i munmap(), _POSIX_MAPPED_FILES jest zdefiniowane w <unistd.h> na wartość większą niż 0 (zob. też sysconf(3)).
Pamięć mapowana za pomocą mmap() jest zachowywana poprzez fork(2) z tymi samymi atrybutami.
Plik jest mapowany w wielokrotnościach rozmiaru strony. Dla plików, które nie są wielokrotnościami rozmiaru strony, pozostałe bajty w częściowej stronie na końcu mapowania są zerowane podczas mapowania, a modyfikacje tego obszaru nie są zapisywane w pliku. Efekt zmiany rozmiaru zamapowanego pliku na zamapowane strony, które odpowiadają dodanym lub usuniętym obszarom pliku, jest nieokreślony.
Aplikacje mogą sprawdzić, które strony mapowania są aktualnie rezydentne w buforze/buforze strony za pomocą mincore(2).
Jedyne bezpieczne zastosowanie MAP_FIXED jest wtedy, gdy podany za pomocą addr i length zakres adresów był uprzednio zarezerwowany przy użyciu innego mapowania; w przeciwnym przypadku MAP_FIXED jest niebezpieczne, ponieważ przymusowo usuwa wcześniejsze mapowania, przez co w przypadku procesów wielowątkowych, łatwe staje się uszkodzenie swojej przestrzeni adresowej.
Przykładowo załóżmy, że wątek A przegląda /proc/pid/maps, aby odszukać nieużywany zakres adresów, które można zmapować przy użyciu MAP_FIXED, a wątek B w tym samym czasie uzyska część lub całość tego samego zakresu adresów. Gdy wątek A następnie użyje mmap(MAP_FIXED), efektywnie naruszy mapowanie, które utworzył wątek B. W opisywanym scenariuszu, wątek B nie musi bezpośrednio tworzyć mapowania; wystarczy utworzenie wywołania bibliotecznego, które wewnętrznie korzysta z dlopen(3) do załadowania jakiejś innej biblioteki współdzielonej. Wywołanie dlopen(3) zmapuje bibliotekę do przestrzeni adresowej procesu. Co więcej, niemal każde wywołanie biblioteczne może być zaimplementowane w sposób, który dodaje mapowania pamięci do przestrzeni adresowej, albo za pomocą tej techniki, albo jedynie alokując pamięć. Przykładem mogą być brk(2), malloc(3), pthread_create(3) i biblioteki PAM http://www.linux-pam.org.
Od Linuksa 4.17, program wielowątkowy może skorzystać ze znacznika MAP_FIXED_NOREPLACE, aby uniknąć niebezpieczeństwa opisanego wyżej, przy tworzeniu mapowania pod konkretnym adresem, który nie został zarezerwowany wcześniejszym mapowaniem.
Dla mapowań opartych na plikach pole st_atime zamapowanego pliku może zostać zaktualizowane w dowolnym momencie pomiędzy mmap() i usunięciem odpowiedniego mapowania; pierwsze odwołanie do zamapowanej strony spowoduje zaktualizowanie tego pola, jeśli nie stało się to wcześniej.
Pola st_ctime i st_mtime pliku zamapowanego z PROT_WRITE i MAP_SHARED zostanie zaktualizowane po zapisie do mapowanego obszaru, a przed późniejszym wywołaniem msync(2) ze znacznikiem MS_SYNC lub MS_ASYNC, jeśli taki wywołanie wystąpi.
W przypadku mapowań korzystających z dużych (huge) stron, wymagania dotyczące argumentów mmap() i munmap() różnią się nieco, od wymagań mapowań, korzystających z natywnego systemowego rozmiaru strony.
W przypadku mmap(), offset musi być wielokrotnością używanego rozmiaru dużej strony. System automatycznie wyrówna length do wielokrotności używanego rozmiaru dużej strony.
W przypadku munmap(), addr i length muszą być wielokrotnościami używanego systemowego rozmiaru dużej strony.
W Linuksie, gwarancje zasugerowane powyżej w opisie MAP_NORESERVE nie istnieją. Domyślnie, każdy proces może być zabity w dowolnym momencie, gdy systemowi wyczerpie się pamięć.
Przed Linuksem 2.6.7, znacznik MAP_POPULATE działał tylko, gdy prot określono jako PROT_NONE.
SUSv3 określa, że mmap() powinno zawieść, gdy length wynosi 0. Jednak przed Linuksem 2.6.12, mmap() było w takim przypadku pomyślne: nie tworzono mapowania, a wywołanie zwracało addr. Od Linuksa 2.6.12, mmap() zawodzi w takim przypadku z błędem EINVAL.
POSIX określa, ze system powinien zawsze wypełniać zerami wszelkie strony częściowe na końcu obiektu, a system nigdy nie zapisze żadnych modyfikacji obiektu poza jego końcem. W Linuksie, jeśli zapisze się dane do takiej częściowej strony, poza końcem obiektu, dane pozostają w buforze strony nawet po zamknięciu i odmapowaniu pliku, a choć dane nigdy nie są zapisywane do samego pliku, kolejne mapowania mogą zobaczyć zmodyfikowaną zawartość. W niektórych przypadkach, to zachowanie można poprawić wywołując msync(2) przed dokonaniem odmapowania; jednak nie działa to na tmpfs(5) (np. przy użyciu interfejsu pamięci dzielonej POSIX opisanego w podręczniku shm_overview(7)).
Poniższy program wypisuje część pliku, określonego w jego pierwszym argumencie wiersza poleceń, na standardowe wyjście. Zakres wypisywanych bajtów podaje się za pomocą wartości przesunięcia i długości, w drugim i trzecim argumencie wiersza poleceń. Program tworzy mapowania pamięci wymaganych stron pliku, a następnie używa write(2) do wypisania żądanych bajtów.
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) {
int fd;
char *addr;
off_t offset, pa_offset;
size_t length;
ssize_t s;
struct stat sb;
if (argc < 3 || argc > 4) {
fprintf(stderr, "przesunięcie pliku %s [długość]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
handle_error("open");
if (fstat(fd, &sb) == -1) /* Do pobrania rozmiaru pliku */
handle_error("fstat");
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
/* przesunięcie dla mmap() musi być wyrównane do strony */
if (offset >= sb.st_size) {
fprintf(stderr, "przesunięcie za końcem pliku\n");
exit(EXIT_FAILURE);
}
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size)
length = sb.st_size - offset;
/* Nie można wypisać bajtów poza końcem pliku */
} else { /* Brak arg. długości ==> wyświetl do końca pliku */
length = sb.st_size - offset;
}
addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
MAP_PRIVATE, fd, pa_offset);
if (addr == MAP_FAILED)
handle_error("mmap");
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
if (s != length) {
if (s == -1)
handle_error("write");
fprintf(stderr, "częściowy zapis");
exit(EXIT_FAILURE);
}
munmap(addr, length + offset - pa_offset);
close(fd);
exit(EXIT_SUCCESS); }
ftruncate(2), getpagesize(2), memfd_create(2), mincore(2), mlock(2), mmap2(2), mprotect(2), mremap(2), msync(2), remap_file_pages(2), setrlimit(2), shmat(2), userfaultfd(2), shm_open(3), shm_overview(7)
Opis poniższych plików w proc(5): /proc/pid/maps, /proc/pid/map_files i /proc/pid/smaps.
B.O. Gallmeister, POSIX.4, O'Reilly, str. 128–129 i 389–391.
Tłumaczenie niniejszej strony podręcznika: Przemek Borys <pborys@dione.ids.pl>, Andrzej Krzysztofowicz <ankry@green.mf.pg.gda.pl> i Michał Kułach <michal.kulach@gmail.com>
Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.
Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-list@lists.sourceforge.net.
| 15 czerwca 2024 r. | Linux man-pages 6.9.1 |