Ciele
- Oboznámiť sa s pojmom Zdieľaná pamäť
- Naučiť sa vytvárať zdieľanú pamäť
- Pochopiť, ako sa so zdieľanou pamäťou pracuje
Postup
Krok 1: Zdieľaná pamäť
Príprava
Pre prácu na cvičení si stiahnite súbor k cvičeniu.
Obsah súboru:
.
└── 08
├──u1_1.c
├──u1_2.c
├──Makefile
└──tests
Poznámka
Programy je možné kompilovať pomocou make nazov_suboru.c
Napríklad:
make u1_1.c
Úvod
Je to spôsob odovzdávania dát medzi dvoma nezávislými procesmi, ktorý je veľmi efektívny. Patrí medzi nástroje medziprocesovej komunikácie (IPC).
Rozlišuje sa: + fyzický adresný priestor (FAP) - operačná pamäť fyzicky prítomná v počítači, + logický adresný priestor procesu (LAP) - pamäť ako abstrakcia, na ktorú sa jednotlivé programy a procesy odvolávajú pri adresácií.
Zdieľaná pamäť je pamäťový segment, ktorý je mapovaný do adresných priestorov dvoch alebo viacerých procesov.
Proces mapovania pamäťového segmentu do adresného priestoru procesu je nasledovný:
Jeden proces vytvorí segment zdieľanej pamäte vo FAP. Procesy si potom môžu tento segment zdieľanej pamäti vo FAP „pripojiť“ ku svojmu vlastnému LAP. To znamená, že rovnaký segment operačnej pamäte počítača sa objaví v LAP niekoľkých procesov (pozri Obr. 1).

Ak v zdieľanej pamäti urobí jeden proces nejaké zmeny tak tieto zmeny budú hneď viditeľné všetkým ostatným procesom pristupujúcim k rovnakej zdieľanej pamäti. Zdieľaná pamäť ale neposkytuje spôsob synchronizácie k zdieľaným dátam a z toho dôvodu je potrebné túto synchronizáciu zaistiť.
Poznámka
Podrobnejšie vysvetlenie zdieľanej pamäte nájdete v manuáli man 2 shm.
Krok 2: Vytvorenie zdieľanej pamäte
Služba jadra shmget()
Služba shmget() vytvorí zdieľanú pamäť a vráti identifikátor na novovytvorenú pamäť.
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
Parametere | |
---|---|
key_t key | pomocou ktorého je možné globálne identifikovať blok zdieľanej pamäte v operačnom systéme. |
size_t size | počet bajtov požadovanej pamäte |
int shmflg | je tvorený príznakmi. Existujú 2 základné príznaky: |
IPC_CREAT - používa sa ak chceme vytvoriť zdieľanú pamäť. | |
IPC_EXCL - ak už je na daný kľúč zaregistrovaná zdieľaná pamäť, tak volanie zlyhá. (Používa sa spoločne s príznakom IPC_CREAT). |
Úloha 2.1
Podrobnejšie vysvetlenie parametrov si naštudujte v manuáli man 2 shmget
Návratová hodnota |
---|
Pri úspešnom vykonaní vráti nezápornú celočíselnú hodnotu, ktorá predstavuje identifikátor zdieľanej pamäte. |
Pri chybe -1. |
Poznámka
Parameter shmflg obsahuje okrem príznakov aj prístupové práva (podobné ako pri vytváraní súborov), ktoré sa aplikujú len ak dôjde k vytvoreniu. Ak je použitý príznak IPC_CREAT samostatne (bez IPC_EXCL), tak je vrátený identifikátor buď na novovytvorený segment (ak neexistuje segment pamäte s rovnakým kľúčom) alebo na existujúci segment.
Príklad použitia:
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
int main(){
//Vytvorenie zdieľanej pamäte:
int shm_id = shmget(12345,sizeof(char*),0666 | IPC_CREAT);
if(shm_id!=-1){
printf("Identifikator zdielanej pamate: %d.\n", shm_id);
exit(0);
}
//V prípade chyby:
else{
perror("shmget(IPC_CREAT)");
exit(1);
}
}
Opis kódu:
Prostredníctvom služby shmget()
je vytvorená zdieľaná pamäť s kľúčom 12345
a boli použité príznaky 0666 | IPC_CREAT
.
Krok 3: Práca so zdieľanou pamäťou
Služba jadra shmctl()
Slúži na ovládanie zaregistrovaných zdieľaných pamätí.
#include <sys/types.h>
#include <sys/shm.h>
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
Parametere | |
---|---|
int shm_id | je vrátený službou shmget() a jedná sa o lokálny identifikátor zdieľanej pamäte |
int cmd | špecifikuje akciu, ktorá sa vykoná so zdieľanou pamäťou. Môže nadobúdať hodnoty: |
IPC_STAT - skopíruje dáta z jadra OS pre segment zdieľanej pamäte shm_id do štruktúry shmid_ds | |
IPC_SET - ak na to má daný proces oprávnenie, tak nastaví hodnoty, ktoré sú spojené so zdieľanou pamäťou podľa údajov, ktoré sú v štruktúre shmid_ds | |
IPC_RMID - zmaže segment zdieľanej pamäte | |
struct shmid_ds *buf | smerník na štruktúru shmid_ds definovanú nižšie pod návratovými hodnotami |
Návratová hodnota |
---|
Pri úspešnom vykonaní vráti 0. |
Pri chybe -1. |
Štruktúra shmid_ds:
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat()/shmdt() */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
Úloha 3.1
Podrobnejšie informácie k službe shmctl() si naštudujte v manuáli: man 2 shmctl
Príklad použitia:
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(){
//Vytvorenie zdieľanej pamäte:
int shm_id = shmget(12345,sizeof(char*),0666 | IPC_CREAT);
if(shm_id!=-1){
printf("Identifikator zdielanej pamate: %d.\n", shm_id);
}
//V prípade chyby:
else{
perror("shmget(IPC_CREAT)");
exit(1);
}
//Odstránenie zdieľanej pamäte:
if(shmctl(shm_id, IPC_RMID, 0) == -1){
//V prípade chyby:
perror("shmctl(IPC_RMID) failed");
exit(1);
}
printf("Zdielana pamat s ID = %d bola odstranena.\n", shm_id);
exit(0);
}
Opis kódu:
Na začiatku je vytvorená zdieľaná pamäť. Následne je prostredníctvom služby shmctl()
táto zdieľaná pamäť odstránená. To je zabezpečené použitím príznaku IPC_RMID
.
Služba jadra shmat()
Keď je pamäť vytvorená operačným systémom, tak nie je prístupná žiadnemu procesu. Pomocou služby shmat() je možné pamäť pripojiť do adresného priestoru procesu a tým ju pre daný proces sprístupniť.
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
Parametere | |
---|---|
int shm_id | identifikátor zdieľanej pamäte, ktorý bol získaný pri volaní služby shmget() |
const void *shm_addr | adresa, na ktorú bude zdieľaná pamäť pripojená v LAP. V prípade použitia hodnoty NULL operačný systém sám rozhodne, kam zdieľanú pamäť pripojí. |
int shmflg | určuje spôsob, akým bude zdieľaná pamäť pripojená (napríklad SHM_RDONLY - zdieľaná pamäť je sprístupnená len pre čítanie, SHM_RDN) |
Návratová hodnota |
---|
Pri úspešnom vykonaní vráti ukazovateľ (smerník) na prvý bajt zdieľanej pamäte. |
Pri chybe -1. |
Úloha 3.2
Podrobnejšie informácie k službe shmat() si naštudujte v manuáli: man 2 shmat
Príklad použitia:
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(){
void *shared_memory = (void *)0;
//Vytvorenie zdieľanej pamäte:
int shm_id = shmget(12345,sizeof(char*),0666 | IPC_CREAT);
if(shm_id!=-1){
printf("Identifikator zdielanej pamate: %d.\n", shm_id);
}
//V prípade chyby:
else{
perror("shmget(IPC_CREAT)");
exit(1);
}
//Pripojenie zdieľanej pamäte:
shared_memory = shmat(shm_id, (void *)0, 0);
if (shared_memory == (void *)-1) {
//V prípade chyby:
perror("shmat failed");
exit(1);
}
printf("Smernik na prvy bajt zdielanej pamate: %X.\n", (int)shared_memory);
//Odstránenie zdieľanej pamäte:
if(shmctl(shm_id, IPC_RMID, 0) == -1){
//V prípade chyby:
perror("shmctl(IPC_RMID) failed");
exit(1);
}
printf("Zdielana pamat s ID = %d bola odstranena.\n", shm_id);
exit(0);
}
Opis kódu:
Po vytvorení zdieľanej pamäte je pamäť pripojená do adresného priestoru procesu prostredníctvom shmat()
.
Služba jadra shmdt()
Odpojí zdieľanú pamäť od LAP aktuálneho procesu.
Poznámka
To, že je pamäť odpojená neznamená, že je systémom uvoľnená. Zmení sa tým len to, že k nej už aktuálny proces nemá prístup.
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
Parametere | |
---|---|
const void *shm_addr | adresa, z ktorej chceme odpojiť pamäť od LAP aktuálneho procesu. Používa sa hodnota, ktorú vrátila služba shmat |
Návratová hodnota |
---|
Pri úspešnom vykonaní vráti 0. |
Pri chybe -1. |
Úloha 3.3
Podrobnejšie informácie k službe shmdt() si naštudujte v manuáli: man 2 shmdt
Príklad použitia:
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(){
void *shared_memory = (void *)0;
//Vytvorenie zdieľanej pamäte:
int shm_id = shmget(12345,sizeof(char*),0666 | IPC_CREAT);
if(shm_id!=-1){
printf("Identifikator zdielanej pamate: %d.\n", shm_id);
}
//V prípade chyby:
else{
perror("shmget(IPC_CREAT)");
exit(1);
}
//Pripojenie zdieľanej pamäte:
shared_memory = shmat(shm_id, (void *)0, 0);
if (shared_memory == (void *)-1) {
//V prípade chyby:
perror("shmat failed");
exit(1);
}
printf("Smernik na prvy bajt zdielanej pamate: %X.\n", (int)shared_memory);
//Odpojenie zdieľanej pamäte:
if (shmdt(shared_memory) == -1) {
//V prípade chyby:
perror("shmdt failed");
exit(1);
}
else{
printf("Zdielana pamat bola odpojena.\n");
}
//Odstránenie zdieľanej pamäte:
if(shmctl(shm_id, IPC_RMID, 0) == -1){
//V prípade chyby:
perror("shmctl(IPC_RMID) failed");
exit(1);
}
printf("Zdielana pamat s ID = %d bola odstranena.\n", shm_id);
exit(0);
}
Opis kódu:
Potom, čo bola zdieľaná pamäť vytvorená a pripojená, bola následne pomocou služby shmdt()
odpojena a následne odstránená.
Aplikovanie poznatkov v praxi
V tomto kroku sa nachádza príklad použitia, no v jeho kóde sa nachádzajú prázdne miesta. Namiesto nich doplňte správne odpovede v otázkach. Každá otázka je očíslovaná rovnakým číslom, aké je uvedené v kóde.
Napríklad fragmentu kódu:
[1......]
odpovedá otázke č.1.
Upozornenie
Zdrojové kódy máte dostupné pod patričnými názvami v priečinku 08. Také zdrojové kódy, aké sú uvedené v príklade, sa nachádzajú aj v zdrojových súboroch. To znamená, že napríklad fragment kódu:
[1......]
nahradíte správnou odpoveďou z otázky č.1.
u1_1.c
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
struct shared_content {
bool permission;
char text[2048];
};
int main(){
void *shared_memory = (void *)0;
struct shared_content *shared_content;
bool reading = true;
//Vytvorenie procesu potomka pre program shm2.c:
if(fork()==0){
//Priradenie programu shm2.c pre proces potomka:
[1......]("u1_2",(char *[]){NULL}, (char *[]){NULL});
exit(0);
}
//Vytvorenie zdieľanej pamäte:
int shm_id = [2......](1234,sizeof(struct shared_content),0666 | [3.........]);
//V prípade chyby:
if(shm_id==-1){
exit(1);
}
//Pripojenie zdieľanej pamäte:
shared_memory = [4.....](shm_id, (void *)0, 0);
if(shared_memory == (void *)-1) {
//V prípade chyby:
exit(1);
}
//printf("Smernik na prvy bajt zdielanej pamate: %X.\n", (int)shared_memory);
shared_content = (struct shared_content*) shared_memory;
shared_content->permission = false;
while(reading){
if(shared_content->permission==true){
printf("Zapisal si: %s", shared_content->text);
sleep(rand() % 4);
shared_content->permission = false;//Povolenie zápisu pre u1_2
if(strncmp(shared_content->text, "end", 3) == 0) {
reading = false;
/*Ak používateľ zadá reťazec "end" tak sa ukončí vykonávanie a zdieľaná
pamäť sa odpojí a odstráni.*/
}
}
}
//Odpojenie zdieľanej pamäte:
if([5.....](shared_memory) == -1) {
//V prípade chyby:
exit(1);
}
else{
printf("Zdielana pamat bola odpojena.\n");
}
//Odstránenie zdieľanej pamäte:
if([6......](shm_id, [7........], 0) == -1){
//V prípade chyby:
exit(1);
}
printf("Zdielana pamat s ID = %d bola odstranena.\n", shm_id);
exit(0);
}
Opis kódu u1_1.c:
Na začiatku je vytvorený proces potomka, ktorému je priradený program u1_2.c. Následne je vytvorená zdieľaná pamäť pod kľúčom 1234. Tak je táto zdieľaná pamäť pripojená adresnému priestoru procesu rodiča. Po úspešnom pripojení je text zo zdieľanej pamäte vypísaný (len v prípade, že tam bol nejaký zapísaný - permission == true). Toto čítanie zo zdieľanej pamäte sa opakuje, pokiaľ používateľ nezadá na štandardný vstup reťazec "end". Po vykonávaní je zdieľaná pamäť odpojená a následne odstránená.
u1_2.c
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
struct shared_content {
bool permission;
char text[2048];
};
int main(){
void *shared_memory = (void *)0;
struct shared_content *shared_content;
bool reading = true;
char buffer[BUFSIZ];
//Vytvorenie zdieľanej pamäte:
int shm_id = [2......](1234,sizeof(struct shared_content),0666 | IPC_CREAT);
//V prípade chyby:
if(shm_id==-1){
exit(1);
}
//Pripojenie zdieľanej pamäte:
shared_memory = [4.....](shm_id, (void *)0, 0);
if(shared_memory == (void *)-1) {
//V prípade chyby:
exit(1);
}
//printf("Smernik na prvy bajt zdielanej pamate: %X.\n", (int)shared_memory);
shared_content = (struct shared_content*) shared_memory;
while(reading){
while(shared_content->permission){
sleep(1);
printf("Cakanie na precitanie obsahu...\n");
}
printf("Vloz nejaky text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared_content->text, buffer, 2048);
shared_content->permission = true;
if(strncmp(buffer, "end", 3) == 0) {
reading = false;
/*Ak používateľ zadá reťazec "end" tak sa ukončí vykonávanie a zdieľaná
pamäť sa odpojí.*/
}
}
//Odpojenie zdieľanej pamäte:
if ([5.....](shared_memory) == -1) {
//V prípade chyby:
exit(1);
}
else{
printf("Zdielana pamat bola odpojena.\n");
}
exit(0);
}
Opis kódu u1_2.c:
Tento program sa spustí v procese potomka. Jedná sa o potomka rodiča, ktorý obsluhuje program u1_1.c. Najprv je získaný identifikátor na vytvorenú zdieľanú pamäť. Potom je, ako aj v prípade rodičovského procesu, zdieľaná pamäť pripojená do adresného priestoru procesu potomka. Pokiaľ nie je obsah pamäte prečítaný, tak do nej nie je možné nič zapisovať. Po tom, čo používateľ zadá reťazec "end" je zdieľaná pamäť odpojená od adresného priestoru procesu potomka.
Otázky:
Poznámka
V praxi sa na rozdiel od uvedeného príkladu, používajú na synchronizáciu čítania a zapisovania signály, semafóry alebo posielanie správ (napríklad pomocou rúr).