Provided by: manpages-pl-dev_4.23.1-1_all bug

NAZWA

       shmat, shmdt - operacje na segmentach pamięci dzielonej Systemu V

BIBLIOTEKA

       Standardowa biblioteka C (libc, -lc)

SKŁADNIA

       #include <sys/shm.h>

       void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg);
       int shmdt(const void *shmaddr);

OPIS

   shmat()
       shmat()  dołącza  segment  pamięci  dzielonej  Systemu V  identyfikowany  przez  shmid  do
       przestrzeni adresowej procesu, który ją wywołał. Adres, pod którym segment ma być widoczny
       jest  przekazywany  w  parametrze  shmaddr,  przy czym system może przetworzyć ten adres w
       następujący sposób:

       •  Jeśli shmaddr jest równy NULL, to system sam wybierze odpowiedni wyrównany do  rozmiaru
          strony (nieużywany) adres, pod którym segment będzie widoczny.

       •  Jeśli shmaddr jest różny od NULL i w shmflg przekazany został znacznik SHM_RND, wówczas
          segment zostanie dołączony pod adresem shmaddr zaokrąglonym  w  dół  do  wielokrotności
          SHMLBA.

       •  W  innym  przypadku  shmaddr  musi być wyrównanym do granicy strony adresem, pod którym
          nastąpi dołączenie segmentu.

       Oprócz SHM_RND można określić następujące znaczniki w argumencie maski bitowej shmflg:

       SHM_EXEC (charakterystyczne dla Linuksa; od Linuksa w wersji 2.6.9)
              Pozwala na wykonanie zawartości segmentu.  Wywołujący  musi  mieć  prawo  wykonania
              segmentu.

       SHM_RDONLY
              Dołącza  segment  do  dostępu  tylko  do odczytu. Proces wywołujący musi mieć prawa
              odczytu segmentu.  Jeśli  znacznik  nie  jest  określona,  w  dołączanym  segmencie
              możliwy  jest  dostęp  do odczytu jak i zapisu, przy czym proces musi mieć prawa do
              odczytu i zapisu segmentu. Nie istnieje pojęcie segmentu pamięci dzielonej tylko do
              zapisu.

       SHM_REMAP (charakterystyczne dla Linuksa)
              Ten  znacznik  oznacza, że odwzorowanie tego segmentu powinno zastąpić jakiekolwiek
              istniejące wcześniej odwzorowanie w  zakresie  rozpoczynającym  się  od  shmaddr  i
              rozciągającym  się na rozmiar segmentu. (Normalnie, gdy odwzorowanie w tym zakresie
              adresów już istnieje, powinien wystąpić błąd EINVAL). W tym przypadku  shmaddr  nie
              może być równy NULL.

       Wartość  brk(2)  procesu  wywołującego  nie  jest  zmieniana  podczas dołączania segmentu.
       Segment zostanie automatycznie odłączony, gdy proces się zakończy. Ten  sam  segment  może
       być  dołączony  do przestrzeni adresowej procesu jako „tylko do odczytu” lub „do odczytu i
       zapisu” więcej niż raz.

       Pomyślne wywołanie shmdt aktualizuje pola struktury shmid_ds (patrz shmctl(2))  opisującej
       segment w następujący sposób:

       •  shm_atime przypisywany jest bieżący czas.

       •  shm_lpid jest ustawiane na identyfikator procesu wywołującego.

       •  shm_nattch jest zwiększane o jeden.

   shmdt()
       shmdt  odłącza  segment  pamięci  dzielonej  odwzorowany  pod  adresem podanym w shmaddr z
       przestrzeni adresowej procesu wywołującego tę funkcję.  Przekazany  funkcji  w  parametrze
       shmaddr adres musi być równy adresowi zwróconemu wcześniej przez wywołanie shmat().

       Pomyślne  wywołanie  shmdt  aktualizuje  pola  struktury  shmid_ds  opisującej  segment  w
       następujący sposób:

       •  shm_dtime przypisywany jest bieżący czas.

       •  shm_lpid jest ustawiane na identyfikator procesu wywołującego.

       •  shm_nattch jest zmniejszane o jeden. Jeśli pole to osiągnie 0 i segment jest zaznaczony
          do usunięcia, wówczas zostanie on usunięty.

WARTOŚĆ ZWRACANA

       Jeśli  się powiedzie, shmat() zwraca adres dołączonego segmentu pamięci dzielonej; w razie
       błędu zwracane jest (void *) -1, a zmienna errno wskazuje błąd.

       Jeśli się powiedzie, shmdt() zwraca 0; w razie błędu zwracane jest  -1,  a  zmienna  errno
       wskazuje błąd.

BŁĘDY

       shmat() może zawieść z powodu następujących błędów:

       EACCES Proces  wywołujący nie ma wystarczających uprawnień do dołączenia segmentu w żądany
              sposób oraz nie ma przywileju CAP_IPC_OWNER (ang.  capability)  w  tej  przestrzeni
              nazw użytkownika, która odpowiada za przestrzeń nazw IPC.

       EIDRM  shmid wskazuje na usunięty identyfikator.

       EINVAL Niewłaściwa  wartość  parametru shmid, niewyrównana (do granicy strony i nie podano
              SHM_RND) lub niepoprawna wartość  shmaddr  albo  nieudane  dołączenie  pod  adresem
              shmaddr lub został podany znacznik SHM_REMAP, podczas gdy shmaddr było równe NULL.

       ENOMEM Brak pamięci na deskryptor lub tablice stron.

       shmdt() może zawieść z powodu następujących błędów:

       EINVAL Żaden segment pamięci dzielonej nie jest podłączony pod adresem shmaddr lub shmaddr
              nie jest wyrównany do granicy strony.

STANDARDY

       POSIX.1-2008.

HISTORIA

       POSIX.1-2001, SVr4.

       W SVID 3 (lub być może wcześniejszym) typ parametru shmaddr został zmieniony z  char *  na
       const void *, a typ wyniku zwracanego przez shmat() z char * na void *.

UWAGI

       W wyniku wywołania fork(2) proces potomny dziedziczy dołączone segmenty pamięci dzielonej.

       Po  wykonaniu  exec(2)  wszystkie  dołączone  segmenty  pamięci  dzielonej są odłączane od
       procesu.

       Po wykonaniu _exit(2) wszystkie dołączone  segmenty  pamięci  dzielonej  są  odłączane  od
       procesu.

       Używanie  shmat()  z  shmaddr  równym  NULL  jest  zalecaną  i przenośną metodą dołączania
       segmentu pamięci dzielonej. Trzeba jednak być świadomym, że ta metoda dołączania  segmentu
       pamięci  dzielonej  może  spowodować  jego  dołączenie  pod  różnymi  adresami  w  różnych
       procesach.  W związku z tym wszystkie wskaźniki obsługiwane w pamięci dzielonej muszą  być
       względne (zazwyczaj względem adresu początkowego segmentu), nie zaś bezwzględne.

       Linux  pozwala na dołączenie segmentu pamięci dzielonej, nawet jeśli już został zaznaczony
       do usunięcia. Jednakże POSIX.1 nie określa takiego zachowania i wiele innych implementacji
       go nie obsługuje.

       Na wywołanie shmat() wpływa następujący parametr systemowy:

       SHMLBA Wielokrotność  dolnej  granicy  adresu  segmentu.  Podczas bezpośredniego wskazania
              dołączonego adresu w wywołaniu do shmat(),  wywołujący  powinien  się  upewnić,  że
              adres  jest  wielokrotnością tej wartości. Jest to konieczne w przypadku niektórych
              architektur, aby zapewnić dobrą wydajność  pamięci  podręcznej  procesora  lub  aby
              zapewnić,  że  różne  dołączenia  tego samego segmentu mają spójne widoki w pamięci
              podręcznej procesora. SHMLBA jest zwykle jakąś wielokrotnością systemowego rozmiaru
              strony.  (W  wielu  architekturach  linuksowych  wartość SHMLBA jest taka sama, jak
              systemowy rozmiar strony).

       Aktualna implementacja nie  ma  wewnętrznego  ograniczenia  na  liczbę  segmentów  pamięci
       dzielonej dołączanych do jednego procesu (SHMSEG).

PRZYKŁADY

       Dwa  programy  pokazane  poniżej, wymieniają łańcuch za pomocą segmentu pamięci dzielonej.
       Więcej szczegółów na temat programów podano  poniżej.  Na  początku  zademonstrujmy  sesję
       powłoki pokazującą działanie programów.

       W  jednym oknie terminala, uruchamiamy program „odczytujący”, który tworzy segment pamięci
       dzielonej Systemu V oraz  zestaw  semaforów  Systemu V.  Program  wypisuje  identyfikatory
       dzielonych obiektów, a następnie oczekuje na zmianę wartości przez semafor.

           $ ./svshm_string_read
           shmid = 1114194; semid = 15

       W  drugim oknie terminala, uruchamiamy program „zapisujący”. Przyjmuje on trzy argumenty w
       wierszu  polecenia:  identyfikatory:  segmentu  pamięci  dzielonej  i  zestawu   semaforów
       utworzone  przez program „odczytujący” oraz łańcuch. Dołącza on istniejący segment pamięci
       dzielonej, kopiuje łańcuch do pamięci dzielonej i modyfikuje wartość semafora.

           $ ./svshm_string_write 1114194 15 'Witaj świecie!'

       Powracając do terminala, gdzie działa program „odczytujący” widzimy, że  program  przestał
       oczekiwać  na  semafor  i  wypisał  łańcuch,  który  został skopiowany do segmentu pamięci
       dzielonej przez program „zapisujący”:

           Witaj świecie!

   Kod źródłowy: svshm_string.h
       Poniższy plik nagłówkowy jest dołączany przez programy: „odczytujący” i „zapisujący”:

           /* svshm_string.h

              Na licencji GNU General Public License v2 lub późniejszej.
           */
           #ifndef SVSHM_STRING_H
           #define SVSHM_STRING_H

           #include <stdio.h>
           #include <stdlib.h>
           #include <sys/sem.h>

           #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                                   } while (0)

           union semun {                   /* Używane w wywołaniach do semctl() */
               int                 val;
               struct semid_ds     *buf;
               unsigned short      *array;
           #if defined(__linux__)
               struct seminfo      *__buf;
           #endif
           };

           #define MEM_SIZE 4096

           #endif  // include guard (ochr. przed wielokr. przetw.)

   Kod źródłowy programu: svshm_string_read.c
       Program „odczytujący” tworzy segment pamięci dzielonej  i  zestaw  semaforów,  zawierający
       jeden  semafor. Następnie dołącza obiekt pamięci dzielonej do swojej przestrzeni adresowej
       i inicjuje wartość semafora na 1. Na końcu, oczekuje na przyjęcie przez  semafor  wartości
       0;  wówczas  wypisuje łańcuch, który został skopiowany do segmentu pamięci dzielonej przez
       program „zapisujący”.

           /* svshm_string_read.c

              Na licencji GNU General Public License v2 lub późniejszej.
           */
           #include <stdio.h>
           #include <stdlib.h>
           #include <sys/ipc.h>
           #include <sys/sem.h>
           #include <sys/shm.h>

           #include "svshm_string.h"

           int
           main(void)
           {
               int            semid, shmid;
               char           *addr;
               union semun    arg, dummy;
               struct sembuf  sop;

               /* Utworzenie pamięci dzielonej i zestawu semaforów, zawierającego
                  jeden semafor. */

               shmid = shmget(IPC_PRIVATE, MEM_SIZE, IPC_CREAT | 0600);
               if (shmid == -1)
                   errExit("shmget");

               semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
               if (semid == -1)
                   errExit("semget");

               /* Dołączenie pamięci dzielonej do naszej przestrzeni adresowej. */

               addr = shmat(shmid, NULL, SHM_RDONLY);
               if (addr == (void *) -1)
                   errExit("shmat");

               /* Zainicjowanie semafora 0 w zestawie, z wartością 1. */

               arg.val = 1;
               if (semctl(semid, 0, SETVAL, arg) == -1)
                   errExit("semctl");

               printf("shmid = %d; semid = %d\n", shmid, semid);

               /* Oczekiwanie na przyjęcie przez semafor wartości 0. */

               sop.sem_num = 0;
               sop.sem_op = 0;
               sop.sem_flg = 0;

               if (semop(semid, &sop, 1) == -1)
                   errExit("semop");

               /* Wypisanie łańcucha z pamięci dzielonej. */

               printf("%s\n", addr);

               /* Usunięcie pamięci dzielonej i zestawu semaforów. */

               if (shmctl(shmid, IPC_RMID, NULL) == -1)
                   errExit("shmctl");
               if (semctl(semid, 0, IPC_RMID, dummy) == -1)
                   errExit("semctl");

               exit(EXIT_SUCCESS);
           }

   Kod źródłowy programu: svshm_string_write.c
       Program  „zapisujący”  przyjmuje  trzy  argumenty  w  wierszu  polecenia:  identyfikatory:
       segmentu  pamięci dzielonej i zestawu semaforów utworzone przez program „odczytujący” oraz
       łańcuch. Dołącza on istniejący segment pamięci dzielonej do swojej przestrzeni adresowej i
       zmniejsza  wartość  semafora na 0, w celu poinformowania programu „odczytującego”, że może
       on teraz sprawdzić zawartość pamięci dzielonej.

           /* svshm_string_write.c

              Na licencji GNU General Public License v2 lub późniejszej.
           */
           #include <stdio.h>
           #include <stdlib.h>
           #include <string.h>
           #include <sys/sem.h>
           #include <sys/shm.h>

           #include "svshm_string.h"

           int
           main(int argc, char *argv[])
           {
               int            semid, shmid;
               char           *addr;
               size_t         len;
               struct sembuf  sop;

               if (argc != 4) {
                   fprintf(stderr, "Użycie: %s shmid semid łańcuch\n", argv[0]);
                   exit(EXIT_FAILURE);
               }

               len = strlen(argv[3]) + 1;  /* +1 aby objąć końcowe '\0' */
               if (len > MEM_SIZE) {
                   fprintf(stderr, "Łańcuch jest zbyt długi!\n");
                   exit(EXIT_FAILURE);
               }

               /* Pobranie ID obiektów z wiersza polecenia. */

               shmid = atoi(argv[1]);
               semid = atoi(argv[2]);

               /* Dołączenie pamięci dzielonej do naszej przestrzeni adresowej
                  i skopiowanie łańcucha (wraz z końcowym bajtem null) do pamięci. */

               addr = shmat(shmid, NULL, 0);
               if (addr == (void *) -1)
                   errExit("shmat");

               memcpy(addr, argv[3], len);

               /* Zmniejszenie semafora do 0. */

               sop.sem_num = 0;
               sop.sem_op = -1;
               sop.sem_flg = 0;

               if (semop(semid, &sop, 1) == -1)
                   errExit("semop");

               exit(EXIT_SUCCESS);
           }

ZOBACZ TAKŻE

       brk(2), mmap(2), shmctl(2), shmget(2), capabilities(7), shm_overview(7), sysvipc(7)

TŁUMACZENIE

       Autorami  polskiego  tłumaczenia  niniejszej  strony   podręcznika   są:   Rafał   Lewczuk
       <R.Lewczuk@elka.pw.edu.p>,   Andrzej   Krzysztofowicz  <ankry@green.mf.pg.gda.pl>,  Robert
       Luberda <robert@debian.org> 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
       ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩  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⟩.