SHMOP(2) System Calls Manual SHMOP(2) NAZWA shmat, shmdt - operacje na segmentach pamieci dzielonej Systemu V BIBLIOTEKA Standardowa biblioteka C (libc, -lc) SKLADNIA #include void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg); int shmdt(const void *shmaddr); OPIS shmat() shmat() dolacza segment pamieci dzielonej Systemu V identyfikowany przez shmid do przestrzeni adresowej procesu, ktory ja wywolal. Adres, pod ktorym segment ma byc widoczny jest przekazywany w parametrze shmaddr, przy czym system moze przetworzyc ten adres w nastepujacy sposob: o Jesli shmaddr jest rowny NULL, to system sam wybierze odpowiedni wyrownany do rozmiaru strony (nieuzywany) adres, pod ktorym segment bedzie widoczny. o Jesli shmaddr jest rozny od NULL i w shmflg przekazany zostal znacznik SHM_RND, wowczas segment zostanie dolaczony pod adresem shmaddr zaokraglonym w dol do wielokrotnosci SHMLBA. o W innym przypadku shmaddr musi byc wyrownanym do granicy strony adresem, pod ktorym nastapi dolaczenie segmentu. Oprocz SHM_RND mozna okreslic nastepujace znaczniki w argumencie maski bitowej shmflg: SHM_EXEC (charakterystyczne dla Linuksa; od Linuksa w wersji 2.6.9) Pozwala na wykonanie zawartosci segmentu. Wywolujacy musi miec prawo wykonania segmentu. SHM_RDONLY Dolacza segment do dostepu tylko do odczytu. Proces wywolujacy musi miec prawa odczytu segmentu. Jesli znacznik nie jest okreslona, w dolaczanym segmencie mozliwy jest dostep do odczytu jak i zapisu, przy czym proces musi miec prawa do odczytu i zapisu segmentu. Nie istnieje pojecie segmentu pamieci dzielonej tylko do zapisu. SHM_REMAP (charakterystyczne dla Linuksa) Ten znacznik oznacza, ze odwzorowanie tego segmentu powinno zastapic jakiekolwiek istniejace wczesniej odwzorowanie w zakresie rozpoczynajacym sie od shmaddr i rozciagajacym sie na rozmiar segmentu. (Normalnie, gdy odwzorowanie w tym zakresie adresow juz istnieje, powinien wystapic blad EINVAL). W tym przypadku shmaddr nie moze byc rowny NULL. Wartosc brk(2) procesu wywolujacego nie jest zmieniana podczas dolaczania segmentu. Segment zostanie automatycznie odlaczony, gdy proces sie zakonczy. Ten sam segment moze byc dolaczony do przestrzeni adresowej procesu jako ,,tylko do odczytu" lub ,,do odczytu i zapisu" wiecej niz raz. Pomyslne wywolanie shmdt aktualizuje pola struktury shmid_ds (patrz shmctl(2)) opisujacej segment w nastepujacy sposob: o shm_atime przypisywany jest biezacy czas. o shm_lpid jest ustawiane na identyfikator procesu wywolujacego. o shm_nattch jest zwiekszane o jeden. shmdt() shmdt odlacza segment pamieci dzielonej odwzorowany pod adresem podanym w shmaddr z przestrzeni adresowej procesu wywolujacego te funkcje. Przekazany funkcji w parametrze shmaddr adres musi byc rowny adresowi zwroconemu wczesniej przez wywolanie shmat(). Pomyslne wywolanie shmdt aktualizuje pola struktury shmid_ds opisujacej segment w nastepujacy sposob: o shm_dtime przypisywany jest biezacy czas. o shm_lpid jest ustawiane na identyfikator procesu wywolujacego. o shm_nattch jest zmniejszane o jeden. Jesli pole to osiagnie 0 i segment jest zaznaczony do usuniecia, wowczas zostanie on usuniety. WARTOSC ZWRACANA Jesli sie powiedzie, shmat() zwraca adres dolaczonego segmentu pamieci dzielonej; w razie bledu zwracane jest (void *) -1, a zmienna errno wskazuje blad. Jesli sie powiedzie, shmdt() zwraca 0; w razie bledu zwracane jest -1, a zmienna errno wskazuje blad. BLEDY shmat() moze zawiesc z powodu nastepujacych bledow: EACCES Proces wywolujacy nie ma wystarczajacych uprawnien do dolaczenia segmentu w zadany sposob oraz nie ma przywileju CAP_IPC_OWNER (ang. capability) w tej przestrzeni nazw uzytkownika, ktora odpowiada za przestrzen nazw IPC. EIDRM shmid wskazuje na usuniety identyfikator. EINVAL Niewlasciwa wartosc parametru shmid, niewyrownana (do granicy strony i nie podano SHM_RND) lub niepoprawna wartosc shmaddr albo nieudane dolaczenie pod adresem shmaddr lub zostal podany znacznik SHM_REMAP, podczas gdy shmaddr bylo rowne NULL. ENOMEM Brak pamieci na deskryptor lub tablice stron. shmdt() moze zawiesc z powodu nastepujacych bledow: EINVAL Zaden segment pamieci dzielonej nie jest podlaczony pod adresem shmaddr lub shmaddr nie jest wyrownany do granicy strony. STANDARDY POSIX.1-2008. HISTORIA POSIX.1-2001, SVr4. W SVID 3 (lub byc moze wczesniejszym) typ parametru shmaddr zostal zmieniony z char * na const void *, a typ wyniku zwracanego przez shmat() z char * na void *. UWAGI W wyniku wywolania fork(2) proces potomny dziedziczy dolaczone segmenty pamieci dzielonej. Po wykonaniu exec(2) wszystkie dolaczone segmenty pamieci dzielonej sa odlaczane od procesu. Po wykonaniu _exit(2) wszystkie dolaczone segmenty pamieci dzielonej sa odlaczane od procesu. Uzywanie shmat() z shmaddr rownym NULL jest zalecana i przenosna metoda dolaczania segmentu pamieci dzielonej. Trzeba jednak byc swiadomym, ze ta metoda dolaczania segmentu pamieci dzielonej moze spowodowac jego dolaczenie pod roznymi adresami w roznych procesach. W zwiazku z tym wszystkie wskazniki obslugiwane w pamieci dzielonej musza byc wzgledne (zazwyczaj wzgledem adresu poczatkowego segmentu), nie zas bezwzgledne. Linux pozwala na dolaczenie segmentu pamieci dzielonej, nawet jesli juz zostal zaznaczony do usuniecia. Jednakze POSIX.1 nie okresla takiego zachowania i wiele innych implementacji go nie obsluguje. Na wywolanie shmat() wplywa nastepujacy parametr systemowy: SHMLBA Wielokrotnosc dolnej granicy adresu segmentu. Podczas bezposredniego wskazania dolaczonego adresu w wywolaniu do shmat(), wywolujacy powinien sie upewnic, ze adres jest wielokrotnoscia tej wartosci. Jest to konieczne w przypadku niektorych architektur, aby zapewnic dobra wydajnosc pamieci podrecznej procesora lub aby zapewnic, ze rozne dolaczenia tego samego segmentu maja spojne widoki w pamieci podrecznej procesora. SHMLBA jest zwykle jakas wielokrotnoscia systemowego rozmiaru strony. (W wielu architekturach linuksowych wartosc SHMLBA jest taka sama, jak systemowy rozmiar strony). Aktualna implementacja nie ma wewnetrznego ograniczenia na liczbe segmentow pamieci dzielonej dolaczanych do jednego procesu (SHMSEG). PRZYKLADY Dwa programy pokazane ponizej, wymieniaja lancuch za pomoca segmentu pamieci dzielonej. Wiecej szczegolow na temat programow podano ponizej. Na poczatku zademonstrujmy sesje powloki pokazujaca dzialanie programow. W jednym oknie terminala, uruchamiamy program ,,odczytujacy", ktory tworzy segment pamieci dzielonej Systemu V oraz zestaw semaforow Systemu V. Program wypisuje identyfikatory dzielonych obiektow, a nastepnie oczekuje na zmiane wartosci przez semafor. $ ./svshm_string_read shmid = 1114194; semid = 15 W drugim oknie terminala, uruchamiamy program ,,zapisujacy". Przyjmuje on trzy argumenty w wierszu polecenia: identyfikatory: segmentu pamieci dzielonej i zestawu semaforow utworzone przez program ,,odczytujacy" oraz lancuch. Dolacza on istniejacy segment pamieci dzielonej, kopiuje lancuch do pamieci dzielonej i modyfikuje wartosc semafora. $ ./svshm_string_write 1114194 15 'Witaj swiecie!' Powracajac do terminala, gdzie dziala program ,,odczytujacy" widzimy, ze program przestal oczekiwac na semafor i wypisal lancuch, ktory zostal skopiowany do segmentu pamieci dzielonej przez program ,,zapisujacy": Witaj swiecie! Kod zrodlowy: svshm_string.h Ponizszy plik naglowkowy jest dolaczany przez programy: ,,odczytujacy" i ,,zapisujacy": /* svshm_string.h Na licencji GNU General Public License v2 lub pozniejszej. */ #ifndef SVSHM_STRING_H #define SVSHM_STRING_H #include #include #include #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) union semun { /* Uzywane w wywolaniach 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 zrodlowy programu: svshm_string_read.c Program ,,odczytujacy" tworzy segment pamieci dzielonej i zestaw semaforow, zawierajacy jeden semafor. Nastepnie dolacza obiekt pamieci dzielonej do swojej przestrzeni adresowej i inicjuje wartosc semafora na 1. Na koncu, oczekuje na przyjecie przez semafor wartosci 0; wowczas wypisuje lancuch, ktory zostal skopiowany do segmentu pamieci dzielonej przez program ,,zapisujacy". /* svshm_string_read.c Na licencji GNU General Public License v2 lub pozniejszej. */ #include #include #include #include #include #include "svshm_string.h" int main(void) { int semid, shmid; char *addr; union semun arg, dummy; struct sembuf sop; /* Utworzenie pamieci dzielonej i zestawu semaforow, zawierajacego 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"); /* Dolaczenie pamieci dzielonej do naszej przestrzeni adresowej. */ addr = shmat(shmid, NULL, SHM_RDONLY); if (addr == (void *) -1) errExit("shmat"); /* Zainicjowanie semafora 0 w zestawie, z wartoscia 1. */ arg.val = 1; if (semctl(semid, 0, SETVAL, arg) == -1) errExit("semctl"); printf("shmid = %d; semid = %d\n", shmid, semid); /* Oczekiwanie na przyjecie przez semafor wartosci 0. */ sop.sem_num = 0; sop.sem_op = 0; sop.sem_flg = 0; if (semop(semid, &sop, 1) == -1) errExit("semop"); /* Wypisanie lancucha z pamieci dzielonej. */ printf("%s\n", addr); /* Usuniecie pamieci dzielonej i zestawu semaforow. */ if (shmctl(shmid, IPC_RMID, NULL) == -1) errExit("shmctl"); if (semctl(semid, 0, IPC_RMID, dummy) == -1) errExit("semctl"); exit(EXIT_SUCCESS); } Kod zrodlowy programu: svshm_string_write.c Program ,,zapisujacy" przyjmuje trzy argumenty w wierszu polecenia: identyfikatory: segmentu pamieci dzielonej i zestawu semaforow utworzone przez program ,,odczytujacy" oraz lancuch. Dolacza on istniejacy segment pamieci dzielonej do swojej przestrzeni adresowej i zmniejsza wartosc semafora na 0, w celu poinformowania programu ,,odczytujacego", ze moze on teraz sprawdzic zawartosc pamieci dzielonej. /* svshm_string_write.c Na licencji GNU General Public License v2 lub pozniejszej. */ #include #include #include #include #include #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, "Uzycie: %s shmid semid lancuch\n", argv[0]); exit(EXIT_FAILURE); } len = strlen(argv[3]) + 1; /* +1 aby objac koncowe '\0' */ if (len > MEM_SIZE) { fprintf(stderr, "Lancuch jest zbyt dlugi!\n"); exit(EXIT_FAILURE); } /* Pobranie ID obiektow z wiersza polecenia. */ shmid = atoi(argv[1]); semid = atoi(argv[2]); /* Dolaczenie pamieci dzielonej do naszej przestrzeni adresowej i skopiowanie lancucha (wraz z koncowym bajtem null) do pamieci. */ 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 TAKZE brk(2), mmap(2), shmctl(2), shmget(2), capabilities(7), shm_overview(7), sysvipc(7) TLUMACZENIE Autorami polskiego tlumaczenia niniejszej strony podrecznika sa: Rafal Lewczuk , Andrzej Krzysztofowicz , Robert Luberda i Michal Kulach Niniejsze tlumaczenie jest wolna dokumentacja. Blizsze informacje o warunkach licencji mozna uzyskac zapoznajac sie z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje sie ZADNEJ ODPOWIEDZIALNOSCI. Bledy w tlumaczeniu strony podrecznika prosimy zglaszac na adres listy dyskusyjnej . Linux man-pages 6.8 2 maja 2024 r. SHMOP(2)