Hlavní navigace

Programování pod Linuxem pro všechny (22)

Jakub Matys

Dnes se podíváme na synchronizaci procesů pomocí semaforů. Probereme zde alokaci a dealokaci semaforů, inicializaci a známé operace wait a post.

V minulém dílu jsem zmiňoval, že procesy musí synchronizovat přístup do sdílené paměťi. Využívají se pro to semafory procesů. Tyto semafory však pracují jiným způsobem než semafory pro vlákna, které jsou definovány jako čítače umožňující synchronizaci činnosti vláken. Semafory pro synchronizaci procesů se používají podobně jako segmenty sdílené paměťi. Semafory procesů můžete využívat v celých množinách (většinou však stačí jeden semafor).

Alokace a dealokace

Funkcemi pro alokaci a dealokaci semaforů jsou semget() asemctl(), které se používají podobně jako funkce pro sdílenou paměť – shmget() a shmctl(). Funkci semget() se předává klíč specifikující množinu semaforů, počet semaforů v množině a přístupová práva. Funkce vrací identifikátor množiny semaforů.

Semafory v systému existují i v případě, kdy všechny procesy, které je používaly, jsou již ukončeny. Poslední proces používající semafory je musí odstranit (počet semaforů je omezený). Využívá se k tomu funkce semctl(). Prvním argumentem je identifikátor semaforu, druhým počet semaforů v množině, třetím IPC_RMID a čtvrtým je hodnota union semun, která je ignorována. Odstranění semaforu probíhá bezprostředně!

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

/* Definovani unionu semun */
union semun{
  int val;
  struct semid_ds *buf;
  unsigned short int *array;
  struct seminfo *__buf;
};

/* Funkce ziska identifikator semaforu, nebo jej alokuje */

int binary_semaphore_allocation(key_t key, int sem_flags)
{
  return semget(key, 1, sem_flags);
}

/* Dealokace semaforu */

int binary_semaphore_deallocate(int semid)
{
  union semun ignored_argument;
  return semctl(semid, 1, IPC_RMID, ignored_argument);
} 

Inicializace semaforů

Inicializace je jiná operace než alokace. K inicializaci semaforu se používá funkce semctl(). Druhý argument je nulový, třetí obsahuje hodnotu SETALL. Pro čtvrtý argument musíte vytvořit objekt union semun a musíte použít ukazatel na jeho pole array. Každá hodnota je použita pro inicializaci jednoho semaforu v množině.

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

/* Definovani uinonu semun */
union semun{
  int val;
  struct semid_ds *buf;
  unsigned short int *array;
  struct seminfo *__buf;
};

/* Inicializace semaforu s hodnotou 1 */

int binary_semaphore_initialize(int semid)
{
  union semun argument;
  unsigned short values[1];
  values[0] = 1;
  argument.array = values;
  return semctl(semid, 0, SETALL, argument);
}

Operace wait a post

Každý semafor má nezápornou hodnotu a podporuje operace wait a post. Funkce semop() provede obě operace. Prvním parametrem je množina semaforů, druhým pole elementů struct sembuf, které specifikuje požadované operace, a třetím parametrem je délka tohoto pole.

Položky struktury struct sembuf:

  • sem_num – číslo semaforu v množině semaforů, pro které se má operace realizovat
  • sem_op – celé číslo specifikující operaci se semaforem. Pokud je sem_op kladné, je okamžitě přičteno k hodnotě semaforu. Je-li záporné, je jeho absolutní hodnota odečtena od hodnoty semaforu. Je-li po odečtení hodnota semaforu záporná, volání se zablokuje, dokud není hodnota semaforu tak velká jako absolutní hodnota sem_op. Pokud je hodnota sem_op nulová, zablokuje se volání, dokud není hodnota semaforu nula.
  • sem_flg – hodnota příznaku. Pokud chcete zabránit zablokování operace, používejte hodnotu IPC_NOWAIT. Pokud by byla operace již zablokovaná, funkce vrátí chybový návratový kód. Jestliže specifikujete hodnotu SEM_UNDO, systém automaticky vrátí operaci semaforu zpět v okamžiku, kdy proces skončí.
#include <sys/types.h>

#include <sys/ipc.h>
#include <sys/sem.h>

/* Operace wait */

int binary_semaphore_wait(int semid)
{
  struct sembuf operations[1];
  /* Pouzij prvni(jediny) semafor */
  operations[0].sem_num = 0;
  /* Zmensi o 1 */
  operations[0].sem_op = -1;
  /* Nastaveni hodnoty SEM_NUDO */
  operations[0].sem_flg = SEM_UNDO;

  return semop(semid, operations, 1);
}

/* Operace post */

int binary_semaphore_post(int semid)
{
  struct sembuf operations[1];
  operations[0].sem_num = 0;
  operations[0].sem_op = 1;
  operations[0].sem_flg = SEM_UNDO;

  return semop(semid, operations, 1);
}

To by bylo pro dnešek vše. Příště se podíváme na mapovanou paměť, ta umožňuje různým procesům komunikovat prostřednictvím sdíleného souboru.

Našli jste v článku chybu?