| SHMOP(2) | System Calls Manual | SHMOP(2) |
shmat, shmdt - operacje na segmentach pamięci dzielonej Systemu V
Standardowa biblioteka C (libc, -lc)
#include <sys/shm.h>
void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg); int shmdt(const void *shmaddr);
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:
Oprócz SHM_RND można określić następujące znaczniki w argumencie maski bitowej shmflg:
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:
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:
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.
shmat() może zawieść z powodu następujących błędów:
shmdt() może zawieść z powodu następujących błędów:
POSIX.1-2008.
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 *.
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:
Aktualna implementacja nie ma wewnętrznego ograniczenia na liczbę segmentów pamięci dzielonej dołączanych do jednego procesu (SHMSEG).
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!
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.)
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); }
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); }
brk(2), mmap(2), shmctl(2), shmget(2), capabilities(7), shm_overview(7), sysvipc(7)
Tłumaczenie niniejszej strony podręcznika: 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 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 |