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_*.

Strony mogą być wykonywane.
Strony mogą być odczytywane.
Strony mogą być zapisywane.
Strony mogą nie być dostępne.

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:

Dzieli zadane mapowanie. Aktualizacje mapowania są widoczne dla innych procesów mapowanych do tego samego miejsca i (w przypadku mapowań opartych na pliku) są przenoszone do samego pliku (aby kontrolować dokładnie, kiedy aktualizacje są przenoszone na sam plik, konieczne jest użycie msync(2)).
Znacznik zapewnia takie same zachowanie jak MAP_SHARED z tą różnicą, że mapowania MAP_SHARED ignorują nieznane znaczniki we flags. Przy tworzeniu mapowań z użyciem MAP_SHARED_VALIDATE, jądro sprawdza natomiast, czy wszystkie przekazane znaczniki są znane i odmawia mapowania z błędem EOPNOTSUPP, jeśli wystąpią nieznane znaczniki. Ten typ mapowania jest również wymagany, aby móc korzystać z niektórych znaczników mapowań (np. MAP_SYNC).
Tworzy prywatne mapowanie, typu „kopiowanie podczas zapisu”. Aktualizacje mapowania nie są widoczne dla innych procesów mapujących ten sam plik i nie są przenoszone na sam plik. Nie jest określone, czy zmiany zawartości pliku wykonane po wywołaniu mmap() będą uwidocznione w mapowanym obszarze.

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:

Umieszcza mapowanie w pierwszych 2 gigabajtach przestrzeni adresowej procesu. Znacznik jest obsługiwany tylko na architekturze x86-64, w przypadku programów 64-bitowych. Znacznik dodano, aby pozwolić stosom wątków na zaalokowanie w pierwszych 2 GB pamięci, co usprawniało wydajność przełączania kontekstów na pewnych wczesnych procesorach 64-bitowych. Współczesne procesory x86-64 nie mają takiego problemu z wydajnością, zatem znacznik ten nie jest wymagany na tych systemach. Znacznik MAP_32BIT jest ignorowany, gdy ustawiony jest MAP_FIXED.
Równoważne MAP_ANONYMOUS; istnieje dla kompatybilności z innymi implementacjami.
Mapowanie nie jest oparte na pliku; jego zawartość jest inicjowana jako zero. Argument fd jest ignorowany, jednak niektóre implementacje wymagają, aby fd wynosiło -1 jeśli podano MAP_ANONYMOUS (lub MAP_ANON), dlatego przenośne aplikacje powinny używać tej wartości. Argument offset powinien wynosić zero. Obsługę MAP_ANONYMOUS w połączeniu z MAP_SHARED dodano w Linuksie 2.4.
Ten znacznik jest ignorowany (dawno temu — Linux 2.0 i wcześniejsze — sygnalizował on, że próba zapisu to mapowanego pliku powinna zawieść z ETXTBSY; było to jednak źródłem ataków blokujących usługę (DoS)).
Ten znacznik jest ignorowany.
Znacznik służący zgodności. Ignorowany.
Nie interpretuje addr jako wskazówki: umieszcza mapowanie dokładnie na podanym adresie. addr musi być właściwie wyrównany: w przypadku większości architektur, wystarczająca jest wielokrotność rozmiaru strony; jednak niektóre architektury mogą narzucać dodatkowe ograniczenia. Jeśli obszar pamięci podany w addr i length nachodzi na strony jakiegoś istniejącego mapowania, to te nachodzące części istniejącego mapowania zostaną odrzucone. Jeśli podany adres nie może być użyty, wywołanie mmap() zawiedzie.
Oprogramowanie, które ma zamiar być przenośne, powinno bardzo ostrożnie korzystać ze znacznika MAP_FIXED, mając na względzie, że dokładny schemat mapowań pamięci procesu może się znacznie zmieniać pomiędzy wersjami Linuksa, wersjami biblioteki C i wydaniami systemu operacyjnego. Proszę dokładnie przeczytać omówienie tego znacznika w UWAGACH!
Znacznik zapewnia zachowanie podobne do MAP_FIXED, jeśli chodzi o wymuszenie adresu addr, lecz z tą różnicą, że MAP_FIXED_NOREPLACE nigdy nie narusza wcześniejszego zakresu mapowania. Jeśli żądany zakres kolidowałby z istniejącym mapowaniem, to wywołanie zawiedzie z błędem EEXIST. Znacznik ten może zatem posłużyć do niepodzielnej (z uwzględnieniem innym wątków) próby mapowania zakresu adresowego: u jednego wątku powiedzie się to, wszystkie inne zawiodą.
Proszę zauważyć, że starsze jądra, które nie rozpoznają znacznika MAP_FIXED_NOREPLACE, zwykle (po wykryciu kolizji z wcześniejszym mapowaniem) awaryjnie zachowają się jak „nie-MAP_FIXED”: zwrócą adres, który jest odmienny od adresu żądanego. Oprogramowanie, które ma być kompatybilne wstecznie, powinno zatem sprawdzać zwracany adres z adresem żądanym.
Znacznik jest używany do stosów. Wskazuje systemowi pamięci wirtualnej jądra, że mapowanie ma kontynuować się w dół pamięci. Zwracany adres jest o jedną stronę niżej, niż obszar pamięci, który jest w rzeczywistości tworzony w wirtualnej przestrzeni adresowej procesu. Dotknięcie adresu w „strzeżonej” stronie (guard page) poniżej mapowania spowoduje zwiększenie mapowania o jedną stronę. Ten wzrost można powtarzać do momentu, gdy mapowanie osiągnie wysoki koniec strony następnego niższego mapowania; wówczas dotknięcie „strzeżonej” strony spowoduje sygnał SIGSEGV.
Przydziela mapowanie za pomocą dużych („huge”) stron. Więcej informacji w pliku Documentation/admin-guide/mm/hugetlbpage.rst w źródłach jądra Linux oraz w UWAGACH poniżej.
Używane w połączeniu z MAP_HUGETLB, do wybrania alternatywnych rozmiarów strony hugetlb (odpowiednio, 2 MB i 1 GB) w systemach, które obsługują wiele rozmiarów stron hugetlb.
Ogólniej, żądany rozmiar dużych stron można skonfigurować, podając logarytm o podstawie 2, żądanego rozmiaru strony, w sześciu bitach na przesunięciu MAP_HUGE_SHIFT (wartość zero w tym polu bitowym oznacza domyślny rozmiar dużej strony; można go sprawdzić za pomocą pola Hugepagesize ujawnianego w /proc/meminfo). Zatem, powyższe dwie stałe są zdefiniowane jako:

#define MAP_HUGE_2MB    (21 << MAP_HUGE_SHIFT)
#define MAP_HUGE_1GB    (30 << MAP_HUGE_SHIFT)

Zakres obsługiwanych rozmiarów dużych stron w systemie można sprawdzić, wypisując podkatalogi w katalogu /sys/kernel/mm/hugepages.
Oznacza mapowany obszar jako zablokowany w ten sam sposób, jak robi to mlock(2). Ta implementacja postara się wypełnić (prefault) cały zakres, lecz wywołanie mmap() nie zawodzi z błędem ENOMEM gdy się to nie powiedzie. Z tego względu później mogą wystąpić główne chybienia stron (ang. major page fault). Semantyka ta nie jest zatem tak mocna jak mlock(2). Jeśli po zainicjowaniu mapowania nie są później dopuszczalne główne chybienia stron, należy korzystać w zamian z mmap() wraz z mlock(2). Znacznik MAP_LOCKED jest ignorowany przez starsze jądra.
Znacznik ten ma znaczenie tylko w połączeniu z MAP_POPULATE. Nie przeprowadza odczytu naprzód: wpisy tablicy stron są tworzone tylko w przypadku stron, które są już obecne w RAM. Od Linuksa 2.6.23, znacznik ten nie ma wpływu na MAP_POPULATE. W przyszłości, połączenie MAP_POPULATE z MAP_NONBLOCK może zostać na nowo zaimplementowane.
Poleca nie rezerwować przestrzeni wymiany dla tego mapowania. Gdy przestrzeń wymiany jest zarezerwowana, ma się gwarancję, że istnieje możliwość modyfikacji mapowania. Gdy przestrzeń wymiany nie jest zarezerwowana, można otrzymać SIGSEGV podczas zapisu, jeśli braknie pamięci fizycznej. Proszę zapoznać się również z opisem pliku /proc/sys/vm/overcommit_memory w proc(5). Przed Linuksem 2.6, znacznik ten działał tylko dla prywatnych, zapisywalnych mapowań.
Wypełnia (prefault) tablice strony dla mapowania. W przypadku mapowania pliku, powoduje to odczyt z wyprzedzeniem pliku. W ten sposób redukuje się blokowanie, przy późniejszych chybieniach stron (ang. page fault). Wywołanie mmap() nie zawodzi, jeśli nie da się wypełnić mapowania (np. ze względu na ograniczenie liczby mapowanych dużych (huge) stron, za pomocą MAP_HUGETLB). Obsługę MAP_POPULATE w połączeniu z mapowaniami prywatnymi dodano w Linuksie 2.6.23.
Przydziela mapowanie na adresie odpowiednim dla stosu: procesu lub wątku.
Znacznik ten jest w Linuksie instrukcją pustą. Jednak podając go, przenośne aplikacje mogą zapewnić zyskanie obsługi znacznika, gdy zostanie on w przyszłości zaimplementowany. Z tego względu jest używany w implementacji wątkowania glibc, aby uwzględnić fakt, że niektóre architektury mogą (w przyszłości) wymagać specjalnego traktowania przy alokowaniu stosu. Kolejnym powodem korzystania z tego znacznika jest przenośność: MAP_STACK istnieje (i działa) na niektórych innych systemach (np. niektórych BSD).
Znacznik ten jest dostępny jedynie z typem mapowania MAP_SHARED_VALIDATE; mapowania typu MAP_SHARED po cichu go zignorują. Znacznik ten jest obsługiwany jedynie dla plików obsługujących DAX (bezpośrednie mapowanie do pamięci trwałej). W przypadku innych plików, utworzenie mapowania z tym znacznikiem spowoduje wystąpienie błędu EOPNOTSUPP.
Dzielone mapowania pliku z tym znacznikiem zapewniają gwarancję, że choć pewna część pamięci będzie zmapowana jako zapisywalna w przestrzeni adresowej procesu, będzie ona widoczna w tym samym pliku, na tym samym przesunięciu, nawet po załamaniu lub przeładowaniu systemu. W połączeniu z odpowiednimi instrukcjami procesora, użytkownicy takich mapowań mają zapewnioną lepszy tryb czynienia modyfikacji danych trwałymi.
Nie czyści stron anonimowych. Znacznik ten ma polepszyć wydajność na urządzeniach wbudowanych. Znacznik jest przestrzegany tylko, gdy jądro skonfigurowano z opcją CONFIG_MMAP_ALLOW_UNINITIALIZED. Ze względu na skutki w zakresie bezpieczeństwa, opcja ta jest zwykle włączana wyłącznie na urządzeniach wbudowanych (tj. mających całkowitą kontrolę nad zawartością pamięci użytkownika).

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).

Deskryptor pliku odnosi się do pliku innego niż zwykły plik. Lub zażądano mapowania pliku, lecz fd nie jest otwarty do odczytu. Lub zażądano MAP_SHARED i ustawiono PROT_WRITE, lecz fd nie jest otwarte w trybie do odczytu i do zapisu (O_RDWR). Lub ustawiono PROT_WRITE, lecz plik jest otwarty tylko do dopisywania.
Plik został zablokowany lub zablokowano zbyt wiele pamięci (zob. setrlimit(2)).
fd nie jest prawidłowym deskryptorem pliku (a nie ustawiono MAP_ANONYMOUS).
We flags podano MAP_FIXED_NOREPLACE, a zakres który opisuje addr i length koliduje z istniejącym mapowaniem.
Niewłaściwe addr, length lub offset (np. mogą być zbyt duże lub niewyrównane do granicy strony).
(od Linuksa 2.6.12) length wynosiła 0.
We flags nie wystąpiło żadne z: MAP_PRIVATE, MAP_SHARED lub MAP_SHARED_VALIDATE.
Zostało osiągnięte systemowe ograniczenie na całkowitą liczbę otwartych plików.
System plików, na którym znajduje się podany plik, nie obsługuje mapowania w pamięci.
Brak dostępnej pamięci.
Doszłoby do przekroczenia maksymalnej liczby mapowań procesu. Błąd ten może wystąpić również dla munmap(), przy usuwaniu mapowania z obszaru w środku istniejącego mapowania, ponieważ spowodowałoby to powstanie dwóch mniejszych mapowań po obu stronach niezmapowanego obszaru.
(od Linuksa 4.7) Doszłoby do przekroczenia limitu RLIMIT_DATA procesu, opisanego w podręczniku getrlimit(2).
addr przekroczyłoby wirtualną przestrzeń adresową procesora.
Na architekturze 32-bitowej z rozszerzeniem dużych plików (tj. korzystając z 64-bitowego off_t): liczba stron użytych dla length wraz z liczbą stron użytych dla offset przepełniłaby liczbę typu unsigned long (32-bitową).
Argument prot pyta o PROT_EXEC lecz mapowany obszar należy do pliku zamontowanego na systemie plików z opcją no-exec (bez zezwolenia na wykonywanie).
Operacja zablokowana, z powodu zapieczętowania pliku (ang. file seal); zob. fcntl(2).
Podano znacznik MAP_HUGETLB, lecz wywołujący nie był uprzywilejowany (nie miał przywileju CAP_IPC_LOCK (ang. capability)) i nie jest członkiem grupy sysctl_hugetlb_shm_group; zob. opis /proc/sys/vm/sysctl_hugetlb_shm_group w proc_sys(5).
Ustawiono MAP_DENYWRITE, lecz obiekt wskazywany przez fd jest otwarty do zapisu.

Użycie zamapowanego obszaru może spowodować wystąpienie następujących sygnałów:

Próba zapisu do obszaru zmapowanego tylko do odczytu.
Próba dostępu do strony bufora, która leży poza końcem mapowanego pliku. Wyjaśnienie traktowania bajtów w stronie, która odnosi się do końca mapowanego pliku i nie jest wielokrotnością rozmiaru strony zamieszczono w UWAGACH.

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, "%s file offset [length]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    fd = open(argv[1], O_RDONLY);
    if (fd == -1)
        handle_error("open");
    if (fstat(fd, &sb) == -1)           /* To obtain file size */
        handle_error("fstat");
    offset = atoi(argv[2]);
    pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
        /* offset for mmap() must be page aligned */
    if (offset >= sb.st_size) {
        fprintf(stderr, "offset is past end of file\n");
        exit(EXIT_FAILURE);
    }
    if (argc == 4) {
        length = atoi(argv[3]);
        if (offset + length > sb.st_size)
            length = sb.st_size - offset;
                /* Can't display bytes past end of file */
    } else {    /* No length arg ==> display to end of file */
        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, "partial write");
        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.

Autorami polskiego tłumaczenia niniejszej strony podręcznika są: 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