Semafor predstavlja generički mehanizam kontrole pristupa. Može da se koristi za kontrolu pristupa fajlovima, deljenoj memoriji isl. Osnovne funkcionalnosti semafora su: njegovo postavljanje, provera ili čekanje dok se ne ispune uslovi i postavljanje (“test-and-set”).
Skup semafora je predstavljen strukturom semid_ds iz linux/sem.h (jedna semid_ds struktura za svaki skup semafora na sistemu).
struct semid_ds
{
struct ipc_perm sem_perm; /* permissions.. */
time_t sem_otime; /* last semop time */
time_t sem_ctime; /* last change time */
struct sem *sem_base; /* ptr to first semaphore in array */
struct wait_queue *eventn;
struct wait_queue *eventz;
struct sem_undo *undo; /* undo requests on this array */
ushort sem_nsems; /* no. of semaphores in array */
};
U semid_ds strukturi postoji pokazivač na niz semafora (sem_base). Svaki član niza je struktura struct sem (linux/sem.h):
/* One semaphore structure for each semaphore in the system. */
struct sem
{
short sempid; /* pid of last operation */
ushort semval; /* current value */
ushort semncnt; /* num procs awaiting increase in semval */
ushort semzcnt; /* num procs awaiting semval = 0 */
};
Pozivom funkcije semget() dobija se identifikator skupa semafora.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
Povratna vrednost je identifikator skupa semafora ako je uspešno ili -1 i errno:
Primer
int open_semaphore_set (key_t keyval, int numsems)
{
int sid;
if (!numsems)
return ‐1;
if((sid = semget (mykey, numsems, IPC_CREAT | 0660)) == ‐1)
return ‐1;
return sid;
}
Vrši operacije nad semaforima.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
Povratna vrednost je 0 ako je uspešno izvedeno (sve operacije izvršene) ili -1 i errno:
Argument sops je pokazivač ka strukturi tipa sembuf (linux/sem.h).
struct sembuf
{
ushort sem_num; /* semaphore index in array */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
Promenljiva sem_num je broj semafora u skupu kojim se manipuliše, sem_op je operacija koja se vrši nad semaforom. Ponašanje sistemskog poziva semop() zavisi od toga da li je sem_op nula, negativan ili pozitivan broj.
Promenljiva sem_flg se koristi da bi se dodatno modifikovali efekti poziva semop(). Najčešće korišćene opcije su:
Primer
struct sembuf sem_lock = {0, -1, IPC_NOWAIT};
if ((semop(sid, &sem_lock, 1) == -1)
perror("semop");
Vrši kontrolne operacije nad skupom semafora definisanim sa semid ili nad nekim semaforom skupa definisanim sa semnum.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ... /* arg */);
Povratna vrednost je pozitivan ceo broj ili -1 i errno:
Poslednji argument arg, ako je potreban, mora biti definisan na sledeći način.
union semun {
int val; /* used for SETVAL only */
struct semid_ds *buf; /* used for IPC_STAT and IPC_SET */
ushort *array; /* used for GETALL and SETALL */
};
Različita polja union semun se koriste zavisno od vrednosti cmd parametra poziva setctl(). Najznačajnije vrednosti cmd parametra mogu biti (za ostale man strane):
Primer 1.
int get_sem_val (int sid, int semnum)
{
return (semctl(sid, semnum, GETVAL, 0));
}
Primer 2.
#define MAX_PRINTERS 5
printer_usage()
{
int x;
for (x = 0; x < MAX_PRINTERS; x++)
printf("Printer %d: %d\n", x, get_sem_val(sid, x));
}
Primer 3.
void init_semaphore(int sid, int sumnum, int initval)
{
union semun semopts;
semopts.val = initval;
semctl(sid, semnum, SETVAL, semopts);
}
/* semdemo.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define MAX_RETRIES 10
union semun
{
int val;
struct semid_ds *buf;
ushort *array;
};
/*
** initsem() -- more-than-inspired by W. Richard Stevens' UNIX Network
** Programming 2nd edition, volume 2, lockvsem.c, page 295.
*/
int initsem(key_t key, int nsems) /* key from ftok() */
{
int i;
union semun arg;
struct semid_ds buf;
struct sembuf sb;
int semid;
semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | 0666);
if (semid >= 0)
{ /* we got it first */
sb.sem_op = 1;
sb.sem_flg = 0;
arg.val = 1;
printf("press return\n");
getchar();
for(sb.sem_num = 0; sb.sem_num < nsems; sb.sem_num++)
{
/* do a semop() to "free" the semaphores. */
/* this sets the sem_otime field, as needed below. */
if (semop(semid, &sb, 1) == -1)
{
int e = errno;
semctl(semid, 0, IPC_RMID); /* clean up */
errno = e;
return -1; /* error, check errno */
}
}
}
else if (errno == EEXIST)
{ /* someone else got it first */
int ready = 0;
semid = semget(key, nsems, 0); /* get the id */
if (semid < 0)
return semid; /* error, check errno */
/* wait for other process to initialize the semaphore: */
arg.buf = &buf;
for(i = 0; i < MAX_RETRIES && !ready; i++)
{
semctl(semid, nsems-1, IPC_STAT, arg);
if (arg.buf->sem_otime != 0)
ready = 1;
else
sleep(1);
}
if (!ready)
{
errno = ETIME;
return -1;
}
}
else
return semid; /* error, check errno */
return semid;
}
int main(void)
{
key_t key;
int semid;
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1; /* set to allocate resource */
sb.sem_flg = SEM_UNDO;
if ((key = ftok("semdemo.c", 'J')) == -1)
{
perror("ftok");
exit(1);
}
/* grab the semaphore set created by seminit.c: */
if ((semid = initsem(key, 1)) == -1)
{
perror("initsem");
exit(1);
}
printf("Press return to lock: ");
getchar();
printf("Trying to lock...\n");
if (semop(semid, &sb, 1) == -1)
{
perror("semop");
exit(1);
}
printf("Locked.\n");
printf("Press return to unlock: ");
getchar();
sb.sem_op = 1; /* free resource */
if (semop(semid, &sb, 1) == -1)
{
perror("semop");
exit(1);
}
printf("Unlocked\n");
return 0;
}
/* semrm.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union semun
{
int val;
struct semid_ds *buf;
ushort *array;
};
int main(void)
{
key_t key;
int semid;
union semun arg;
if ((key = ftok("semdemo.c", 'J')) == -1)
{
perror("ftok");
exit(1);
}
/* grab the semaphore set created by seminit.c: */
if ((semid = semget(key, 1, 0)) == -1)
{
perror("semget");
exit(1);
}
/* remove it: */
if (semctl(semid, 0, IPC_RMID, arg) == -1)
{
perror("semctl");
exit(1);
}
return 0;
}