8. týždeň

Medziprocesová komunikácia - Zdieľana pamäť

Ciele

  1. Oboznámiť sa s pojmom Zdieľaná pamäť
  2. Naučiť sa vytvárať zdieľanú pamäť
  3. 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).

Zdieľaná pamäť
Obr. 1: Zdieľaná pamäť

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.

Koľko bitov parametra shmflg služby shmget() slúži na definovanie prístupových práv k segmentu zdieľanej pamäte?

Odoslať odpoveď
Správna odpoveď: 9
Nesprávna odpoveď

Ktorý z príznakov prístupových práv nie je podporovaný pre zdieľanú pamäť? Odpoveď uveďte vo forme 1 písmena bez medzier, úvodzoviek a iných nealfanumerických znakov.

Správna odpoveď: x
Nesprávna odpoveď

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.

Parameter cmd môže nadobúdať hodnotu:

Správna odpoveď: IPC_STAT / IPC_SET / IPC_RMID / IPC_INFO / SHM_INFO / SHM_STAT / SHM_STAT_ANY / SHM_LOCK / SHM_UNLOCK
Nesprávna odpoveď

Uveďte názov premennej prostredníctvom ktorej vieme zmeniť prístupové práva na segment zdieľanej pamäte. Premenú uveďte v tvare "nazov_struktury.nazov_zlozky", teda presne tak, ako by ste ju uviedli v zdrojovom kóde jazyka C.

Správna odpoveď: ipc_perm.mode
Nesprávna odpoveď

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

Pripojené segmenty zdieľanej pamäte po úspešnom vykonaní služby fork() potomok ________ . (Doplňte jedno slovo).

Správna odpoveď: zdedí
Nesprávna odpoveď

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

Hodnotu parametra služby shmdt() získame pomocou služby: (Jedno slovo bez zátvoriek).

Správna odpoveď: shmat
Nesprávna odpoveď

Pri úspešnom volaní služby shmdt() systém aktualizuje nasledujúce časti shmid_ds štruktúry: (môže byť viac správnych odpovedí)

Odoslať odpoveď
Správna odpoveď: shm_dtime, shm_lpid, shm_nattch
Nesprávna odpoveď

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:

1. Pomocou akej služby priradíte procesu konkrétny program uvedený v parametri? [Uveďte v tvare: nazovSluzby bez medzier!]

Správna odpoveď: execve
Nesprávna odpoveď

2. Pomocou akej služby vytvoríte zdieľanú pamäť? [Uveďte v tvare: nazovSluzby bez medzier!]

Správna odpoveď: shmget
Nesprávna odpoveď

3. Aký príznak použijete ak chcete vytvoriť zdieľanú pamäť? [Uveďte v tvare: nazovPríznaku bez medzier! V prípade, že súčasťou názvu sú nejaké nealfanumerické znaky (napríklad .,-_!?), tak ich uveďte.]

Správna odpoveď: IPC_CREAT
Nesprávna odpoveď

4. Pomocou akej služby pripojíte zdieľanú pamäť do adresného priestoru procesu? [Uveďte v tvare: nazovSluzby bez medzier!]

Správna odpoveď: shmat
Nesprávna odpoveď

5. Pomocou akej služby odpojíte zdieľanú pamäť z adresného priestoru procesu? [Uveďte v tvare: nazovSluzby bez medzier!]

Správna odpoveď: shmdt
Nesprávna odpoveď

6. Pomocou akej služby ovládate zdieľanú pamäť? [Uveďte v tvare: nazovSluzby bez medzier!]

Správna odpoveď: shmctl
Nesprávna odpoveď

7. Aký príznak musíte použiť, aby ste zdieľanú pamäť v tomto príklade odstránili? [Uveďte v tvare: nazovSluzby bez medzier!]

Správna odpoveď: IPC_RMID
Nesprávna odpoveď

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