Programowanie współbieżne i rozproszone/PWR Ćwiczenia shm: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Mengel (dyskusja | edycje)
Nie podano opisu zmian
Mengel (dyskusja | edycje)
Nie podano opisu zmian
Linia 44: Linia 44:
=== Przyłączanie segmentu pamięci dzielonej ===
=== Przyłączanie segmentu pamięci dzielonej ===


<!--
Do przyłączenia istniejącego segmentu pamięci dzielonej do przestrzeni adresowej procesu służy funkcja systemowa <tt>shmat</tt>:
 
<Source>
Aby proces mogl skorzystac z segmentu pamieci dzielonej musi:
- utworzyc (lub otworzyc istniejacy segment)
- przylaczyc go do swojej przestrzeni adresowej
 
Do wykonywania operacji przylaczania sluzy funkcja systemowa 'shmat'.
 
     void *shmat (int ident, const void *adres, int flagi)
     void *shmat (int ident, const void *adres, int flagi)
Znaczenie parametrow:
</Source>
* ident - identyfikator segmentu (przekazany przez 'shmget')
* Pierwszy parametr jest identyfikatorem segmentu pamięci dzielonej.
* adres - sugerowany adres wirtualny, pod ktory nalezy przylaczyc
* Drugi parametr jest to adres wirtualny, pod który zostanie przyłączony segment. Wartość tego parametru może być:
          segment
** zerowa, wtedy system sam dobiera odpowiednio adres. Taka postać operacji <tt>shmat</tt> jest zdecydowanie zalecana, a w niektórych wariantach Uniksa jest nawet jedyną dopuszczalną!
* flagi - rozne znaczniki kontrolujace sposob wykonania przylaczenia
** niezerowa, wtedy system próbuje przyłączyć segment pod wskazany adres. System może, w zależności od opcji podanych na trzecim argumencie, dokonać korekty tego adresu, ale nie będziemy się tym zajmować, stosując zawsze zalecaną wersję z wartością parametru adres równą zero.  
 
* Trzeci parametr to opcje, które mają wpływ na sposób przyłączenia. Jest wśród nich między innymi opcja <tt>SHM_RDONLY</tt>, wymuszająca przyłączenie w trybie jedynie do odczytu
Funkcja 'shmat' dziala nastepujaco:
Wynikiem funkcji systemowej jest adres, pod który przyłączono segment lub wartość <tt>(void *) -1</tt> w przypadku błędu.
  * jesli adres=0, to system przylacza segment pamieci o
    identyfikatorze ident pod wybrany przez siebie, dogodny adres
    wirtualny. Adres ten jest przekazywany jako wynik funkcji.
  * jesli adres<>0, to system probuje przylaczyc segment pamieci pod
    podany adres wirtualny. Adres musi byc wielokrotnoscia wartosci
    SHMLBA, chyba ze wsrod flag uzyto opcji SHM_RND, ktora wymusza
    automatyczne dostosowanie podanej wartosci adresu do
    wielokrotnosci SHMLBA. Wynikiem funkcji jest rzeczywisty adres
    dokonania przylaczenia.
  * jesli wsrod flag uzyto opcji SHM_RDONLY przylaczony segment mozna
    jedynie odczytywac.
  * jesli funkcja shmat nie powiodla sie, to jej wynikiem jest
    (void *) -1
 
Uwaga. Preferowana jest metoda z wartoscia adres = 0. Programista nie
musi wtedy znac szczegolow organizacji pamieci (ktore w zaleznosci od
wariantu systemu moga byc rozne), uzyskuje przenosny program. Niektore
systemy, wrecz ignoruja wartosc parametru adres i dokonuja
przylaczenia tam, gdzie im jest wygodnie.


*********************************
A oto typowy fragment programu działający na segmencie pamięci dzielonej:
Przeczytaj man do funkcji shmat
<Source>
*********************************
Przyklad:
   int  id;
   int  id;
   char* adr;
   char* adr;


   if ((id=shmget (KLUCZ, 100, 0666|IPC_CREAT)) == -1)
   if ((id=shmget (KLUCZ, 100, 0666|IPC_CREAT)) == -1) /* Stworzenie segementu */
     syserr ("blad");
     syserr ("blad");
   if ((adr=shmat(id, 0, 0)) < 0)
   if ((adr=shmat(id, 0, 0)) < 0) /* Przyłączenie */
     syserr ("blad")
     syserr ("blad")  
</Source>


  Teraz wszystkie operacji odbywajace sie na napisie 'adr'
Później w programie można napisać na przykład:
  (np. strcpy (adr, "ala ma kota")) beda dotyczyc segmentu pamieci
<Source>
  dzielonej.
  strcpy (adr, "Hello")
</Source>
co powoduje umieszczenie napisu w segmencie pamięci dzielonej.  


<!--
1.3 Odlaczanie segmentu pamieci dzielonej
1.3 Odlaczanie segmentu pamieci dzielonej
-----------------------------------------
-----------------------------------------

Wersja z 12:16, 17 paź 2006

Tematyka zajęć

Segmenty pamięci dzielonej

Literatura

Scenariusz

Wstęp

Segmenty pamięci dzielonej to trzeci, ostatni już mechanizm z pakietu IPC. Jak zwykle do segmentów pamięci dzielonej stosują się wszystkie uwagi dotyczące mechanizmów IPC, omówione przy okazji kolejek komunikatów. Dotychczas wszystkie programy, które pisaliśmy w środowisku Unix składały się z procesów, z których każdy korzystał z własnych zmiennych. Zmienne współdzielone, dostępne dla wielu procesów nie występowały, a synchronizacja odbywała się poprzez wymianę komunikatów bądź to za pomocą łączy bądź kolejek komunikatów. Pewne odstępstwo od tej zasady nastąpiło z chwilą wprowadzenia semaforów, ale ponieważ to system dba o zarządzanie nimi i przydzielenie im miejsca w pamięci, więc programista nie musiał się tym zajmować. Dziś chcemy korzystać ze wspólnej pamięci w postaci zmiennych dostępnych dla wielu procesów.

Choć procesy uniksowe uruchamiane na tym samym komputerze wykonują się we wspólnej pamięci, to jednak z logicznego punktu widzenia przestrzenie adresowe różnych procesów są rozłączne. Aby umożliwić korzystanie na przykład ze wspólnych zmiennych wprowadzono, początkowo do Uniksa Systemu V, a później już do wszystkich wersji systemu, mechanizm segmentow pamieci dzielonej.

Segment pamięci dzielonej jest fragmentem pamięci, który może być podpinany do przestrzeni adresowych różnych procesów. Zarządzaniem segmentami pamięci dzielonej zajmuje się system, ale to programista jest odpowiedzialny za utworzenie segmentu o potrzebnej mu wielkości oraz jego podpięcie pod pewien adres przestrzeni adresowej (najczęściej wybrany przez system operacyjny). Jeśli ten sam segment zostanie przyłączony do przestrzeni adresowych różnych procesów, to wszelkie dane (a więc zmienne) w nim przechowywanie stają się widoczne i dostępne dla tych procesów. Oczywiście dostęp do nich wymaga wówczas synchronizacji, ale mamy już do tego odpowiednie mechanizmy --- na przykład semafory.

Operacje zapisu i odczytu z segmentu pamięci wirtualnej do zwykłe operacje dostępu do pamięci. Dzięki temu są one realizowane szybko i wydajnie, znacznie szybciej niż na przykład wysłanie komunikatu za pomocą kolejki komunikatów, które wymaga kopiowania komunikatu między przestrzenią adresową nadawcy, jądra oraz odbiorcy. Tutaj występuje zapis po stronie nadawcy i odczyt po stronie odbiorcy.

Ponieważ segment pamięci jest zasobem IPC, trzeba go najpierw utworzyć lub uzyskać dostęp do segmentu już istniejącego. Identyfikacja segmentów odbywa się jak zwykle za pomocą ustalonego klucza, a następnie identyfikatora przekazywanego przez funkcję get, która w przypadku segmentów pamięci dzielonej nazywa się shmget. Ale to jeszcze nie koniec. Aby proces mógł coś zapisać do segmentu lub z niego odczytać, segment musi zostać podpięty do przestrzeni adresowej. Podobnie, gdy segment staje się niepotrzebny w danym procesie należy go odłączyc od przestrzeni adresowej. Dopóki jednak segment jest przyłączony do jakiekolwiek procesu, dane w nim zapisywane są utrzymywane. Segment, który jest już niepotrzebny w żadnym procesie należy usunąć, jak każdy zasób IPC.

Tak wygląda typowy scenariusz pracy z segmentami pamięci dzielonej. Do jego realizacji służą następujące funkcje systemowe:

  • shmget --- tworząca segment pamięci dzielonej
  • shmat --- przyłączająca segmentu pamięci dzielonej do przestrzeni adresowej procesu
  • shmdt --- odlączająca segmentu pamięci dzielonej od przestrzeni adresowej procesu
  • shmctl --- realizująca operacje sterujące na segmencie pamięci dzielonej

Program w C wykorzystujący mechanizm pamięci dzielonej musi wykorzystać następujące pliki nagłówkowe:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

Tworzenie (uzyskanie dostępu do) segmentu pamięci dzielonej

Do utworzenia segmentu pamięci dzielonej służy funkcja systemowa shmget:

     int shmget (key_t klucz, int rozmiar, int flagi)
  • Pierwszym parametrem tej funkcji jest jak zwykle klucz zasobu
  • Trzecim parametrem są opcje, które jak zwykle określają prawa dostępu do pamięci oraz ewentualne opcje IPC_CREAT, IPC_EXCL lub IPC_PRIVATE
  • Wynikiem funkcji shmget jest identyfikator segmentu pamięci dzielonej lub -1 w przypadku błędu

Parametrem specyficznym dla segmentów pamięci dzielonej jest rozmiar, który określa rozmiar tworzonego segmentu pamięci dzielonej. Ma on znaczenie jedynie wtedy, gdy segment jest faktycznie tworzony. Jeśli funkcja powoduje jedynie uzyskanie dostępu do już istniejącego segmentu, to parametr ten jest ignorowany. Tak naprawdę, utworzony segment będzie miał będzie składał się z pewnej (odpowiednio dużej) liczby stron, więc jego rozmiar jest zawsze wielokrotnością rozmiaru strony.

Przyłączanie segmentu pamięci dzielonej

Do przyłączenia istniejącego segmentu pamięci dzielonej do przestrzeni adresowej procesu służy funkcja systemowa shmat:

     void *shmat (int ident, const void *adres, int flagi)
  • Pierwszy parametr jest identyfikatorem segmentu pamięci dzielonej.
  • Drugi parametr jest to adres wirtualny, pod który zostanie przyłączony segment. Wartość tego parametru może być:
    • zerowa, wtedy system sam dobiera odpowiednio adres. Taka postać operacji shmat jest zdecydowanie zalecana, a w niektórych wariantach Uniksa jest nawet jedyną dopuszczalną!
    • niezerowa, wtedy system próbuje przyłączyć segment pod wskazany adres. System może, w zależności od opcji podanych na trzecim argumencie, dokonać korekty tego adresu, ale nie będziemy się tym zajmować, stosując zawsze zalecaną wersję z wartością parametru adres równą zero.
  • Trzeci parametr to opcje, które mają wpływ na sposób przyłączenia. Jest wśród nich między innymi opcja SHM_RDONLY, wymuszająca przyłączenie w trybie jedynie do odczytu

Wynikiem funkcji systemowej jest adres, pod który przyłączono segment lub wartość (void *) -1 w przypadku błędu.

A oto typowy fragment programu działający na segmencie pamięci dzielonej:

   int   id;
   char* adr;

   if ((id=shmget (KLUCZ, 100, 0666|IPC_CREAT)) == -1)  /* Stworzenie segementu */
     syserr ("blad");  
   if ((adr=shmat(id, 0, 0)) < 0) /* Przyłączenie */
     syserr ("blad")

Później w programie można napisać na przykład:

  strcpy (adr, "Hello")

co powoduje umieszczenie napisu w segmencie pamięci dzielonej.