Глава 11 , Семафоры System V
Исходники для этой страницы лежат тут
Семафоры бывают :
1 Бинарные , т.е. могут приобретать только 2 значения - 1 и 0
2 Счетчики , принимающие значения от 0 до некоторого значения
3 Набор семафоров-счетчиков ограниченного количества . для каждого такого набора
поддерживается структура semid_ds. Каждый элемент этой структуры
описывается с помощью другой структуры - sem .
Помимо этого , для каждого семафора ядро хранит 3 поля
1 id последнего процесса , изменившего процесс
2 число процессов , ждущих , когда семафор увеличит значение
3 число процессов , ждущих , когда семафор обнулится
Если в структуре 2 семафора , то она выглядит примерно так :
Функция semget создает набор семафоров либо открывает доступ к уже существующим :
int semget(key_t key,int nsems,int flag)
Возвращает положительный идентификатор в случае успеха ,
который в дальнейшем будет использоваться функциями semop и semctl
nsems задает число семафоров
флаг - на чтение-запись
Функция semop позволяет выполнить некоторые действия с созданным семафором .
int semop(int semid , struct sembuf *ptr , size_t s)
Указатель ptr указывает на массив структур sembuf
Число элементов в массиве sembuf задается 3-м параметром.
Каждый элемент этого массива определяет операцию с одним конкретным семафором .
sem_num - номер семафора . Каждая операция задается sem_op ,
которое может быть отрицательным , нулевым или положительным .
struct sem {
ushort_t semval ; // значение семафора
short sempid; // pid последнего процесса , вызвавшего semop() , SETVAL , SETALL
ushort_t semncnt; // количество ожидающих того , что значение превысит текущее
ushort_t semzcnt; // число ожидающих того , что значение семафора станет равным 0
}
semop - медленный системный вызов , который прерывается перехватываемыми сигналами .
Если семафор во время операции удаляется , semop возвращает ошибку .
В зависимости от того , каким является аргумент sem_op - отрицательным , нулем или положительным -
поведение функции semop будет следующее :
1 Если sem_op > 0 , оно добавляется к semval . Это равносильно освобождению ресурсов ,
захваченных семафором . Если указан флаг SEM_UNDO , значение semop вычитается .
2 Если sem_op = 0 , вызвавший поток блокируется до тех пор , пока знвчение semval
не станет равным нулю . Если semval = 0 , происходит возвращение из функции .
3 Если sem_op < 0 , поток блокируется , пока semval не станет большим либо равным модулю sem_op.
Это равносильно запросу ресурса .
Если semval больше либо равно модулю sem_op , sem_op вычитается из semval .
Функция semctl выполняет управляющие операции :
int semctl(int semid , int semnum , int cmd , union semum arg
Возвращает положительное число в случае успеха
Четвертый аргумент - дополнительный , добавляется в зависимости от команды cmd :
union semun{
int val;
struct semid_ds *buf;
ushort *array;
}
Его нужно декларировать в приложении . Оно передается по значению .
Аргумент cmd может приобретать значения :
GETVAL
SETVAL
GETPID
GETNCNT
GETZCNT
GETALL
SETALL
IPC_RMID
IPC_SET
IPC_STAT
Программа semcreate создает набор семафоров -
флаг -e соответствует IPC_EXCL при вызове semget :
//svsem/semcreate.c
int
main(int argc, char **argv)
{
int c, oflag, semid, nsems;
oflag = SVSEM_MODE | IPC_CREAT;
while ( (c = Getopt(argc, argv, "e")) != -1) {
switch (c) {
case 'e':
oflag |= IPC_EXCL;
break;
}
}
if (optind != argc - 2)
err_quit("usage: semcreate [ -e ] < pathname> < nsems>");
nsems = atoi(argv[optind + 1]);
semid = Semget(Ftok(argv[optind], 0), nsems, oflag);
exit(0);
}
Программа semrmid удаляет набор семафоров , для этого используется semctl с флагом IPC_RMID :
int
main(int argc, char **argv)
{
int semid;
if (argc != 2)
err_quit("usage: semrmid < pathname>");
semid = Semget(Ftok(argv[1], 0), 0, 0);
Semctl(semid, 0, IPC_RMID);
exit(0);
}
Программа semsetvalues устанавливает значения семафоров .
Вызываются semget , потом semctl с флагом IPC_STAT
int
main(int argc, char **argv)
{
int semid, nsems, i;
struct semid_ds seminfo;
unsigned short *ptr;
union semun arg;
if (argc < 2)
err_quit("usage: semsetvalues < pathname> [ values ... ]");
/* 4first get the number of semaphores in the set */
semid = Semget(Ftok(argv[1], 0), 0, 0);
arg.buf = &seminfo;
Semctl(semid, 0, IPC_STAT, arg);
nsems = arg.buf->sem_nsems;
/* 4now get the values from the command line */
if (argc != nsems + 2)
err_quit("%d semaphores in set, %d values specified", nsems, argc-2);
/* 4allocate memory to hold all the values in the set, and store */
ptr = Calloc(nsems, sizeof(unsigned short));
arg.array = ptr;
for (i = 0; i < nsems; i++)
ptr[i] = atoi(argv[i + 2]);
Semctl(semid, 0, SETALL, arg);
exit(0);
}
Программа semgetvalues получает значения семафоров :
int
main(int argc, char **argv)
{
int semid, nsems, i;
struct semid_ds seminfo;
unsigned short *ptr;
union semun arg;
if (argc != 2)
err_quit("usage: semgetvalues < pathname>");
/* 4first get the number of semaphores in the set */
semid = Semget(Ftok(argv[1], 0), 0, 0);
arg.buf = &seminfo;
Semctl(semid, 0, IPC_STAT, arg);
nsems = arg.buf->sem_nsems;
/* 4allocate memory to hold all the values in the set */
ptr = Calloc(nsems, sizeof(unsigned short));
arg.array = ptr;
/* 4fetch the values and print */
Semctl(semid, 0, GETALL, arg);
for (i = 0; i < nsems; i++)
printf("semval[%d] = %d\n", i, ptr[i]);
exit(0);
}
Программа semops запускается с флагом -n , что соответствует флагу IPC_NOWAIT ,
параметр -u соответствует флагу SEM_UNDO.
int
main(int argc, char **argv)
{
int c, i, flag, semid, nops;
struct sembuf *ptr;
flag = 0;
while ( (c = Getopt(argc, argv, "nu")) != -1) {
switch (c) {
case 'n':
flag |= IPC_NOWAIT; /* for each operation */
break;
case 'u':
flag |= SEM_UNDO; /* for each operation */
break;
}
}
if (argc - optind < 2) /* argc - optind = #args remaining */
err_quit("usage: semops [ -n ] [ -u ] operation ...");
semid = Semget(Ftok(argv[optind], 0), 0, 0);
optind++;
nops = argc - optind;
/* 4allocate memory to hold operations, store, and perform */
ptr = Calloc(nops, sizeof(struct sembuf));
for (i = 0; i < nops; i++) {
ptr[i].sem_num = i;
ptr[i].sem_op = atoi(argv[optind + i]); /* <0, 0, or >0 */
ptr[i].sem_flg = flag;
}
Semop(semid, ptr, nops);
exit(0);
}
Теперь запустим цепочку команд :
touch /tmp/rich
./semcreate -e /tmp/rich 3
./semsetvalues /tmp/rich 1 2 3
./semgetvalues /tmp/rich
Вывод :
semval[0] = 1
semval[1] = 2
semval[2] = 3
Далее выполним :
./semops -n /tmp/rich 1 2 3
./semgetvalues /tmp/rich
Вывод :
semval[0] = 2
semval[1] = 4
semval[2] = 6
На семафоры System V накладываются ограничения .
В следующей таблице в первом столбике - переменная ядра , хранящая ограничение :
Следующая система определяет ограничения для системы :
int
main(int argc, char **argv)
{
int i, j, semid, sid[MAX_NIDS], pipefd[2];
int semmni, semvmx, semmsl, semmns, semopn, semaem, semume, semmnu;
pid_t *child;
union semun arg;
struct sembuf ops[MAX_NOPS];
/* 4see how many sets with one member we can create */
for (i = 0; i <= MAX_NIDS; i++) {
sid[i] = semget(IPC_PRIVATE, 1, SVSEM_MODE | IPC_CREAT);
if (sid[i] == -1) {
semmni = i;
printf("%d identifiers open at once\n", semmni);
break;
}
}
/* 4before deleting, find maximum value using sid[0] */
for (j = 7; j < MAX_VALUE; j += 8) {
arg.val = j;
if (semctl(sid[0], 0, SETVAL, arg) == -1) {
semvmx = j - 8;
printf("max semaphore value = %d\n", semvmx);
break;
}
}
for (j = 0; j < i; j++)
Semctl(sid[j], 0, IPC_RMID);
/* 4determine max # semaphores per semaphore set */
for (i = 1; i <= MAX_MEMBERS; i++) {
semid = semget(IPC_PRIVATE, i, SVSEM_MODE | IPC_CREAT);
if (semid == -1) {
semmsl = i-1;
printf("max of %d members per set\n", semmsl);
break;
}
Semctl(semid, 0, IPC_RMID);
}
/* 4find max of total # of semaphores we can create */
semmns = 0;
for (i = 0; i < semmni; i++) {
sid[i] = semget(IPC_PRIVATE, semmsl, SVSEM_MODE | IPC_CREAT);
if (sid[i] == -1) {
/* $$.bp$$ */
/*
* Up to this point each set has been created with semmsl
* members. But this just failed, so try recreating this
* final set with one fewer member per set, until it works.
*/
for (j = semmsl-1; j > 0; j--) {
sid[i] = semget(IPC_PRIVATE, j, SVSEM_MODE | IPC_CREAT);
if (sid[i] != -1) {
semmns += j;
printf("max of %d semaphores\n", semmns);
Semctl(sid[i], 0, IPC_RMID);
goto done;
}
}
err_quit("j reached 0, semmns = %d", semmns);
}
semmns += semmsl;
}
printf("max of %d semaphores\n", semmns);
done:
for (j = 0; j < i; j++)
Semctl(sid[j], 0, IPC_RMID);
/* 4see how many operations per semop() */
semid = Semget(IPC_PRIVATE, semmsl, SVSEM_MODE | IPC_CREAT);
for (i = 1; i <= MAX_NOPS; i++) {
ops[i-1].sem_num = i-1;
ops[i-1].sem_op = 1;
ops[i-1].sem_flg = 0;
if (semop(semid, ops, i) == -1) {
if (errno != E2BIG)
err_sys("expected E2BIG from semop");
semopn = i-1;
printf("max of %d operations per semop()\n", semopn);
break;
}
}
Semctl(semid, 0, IPC_RMID);
/* 4determine the max value of semadj */
/* 4create one set with one semaphore */
semid = Semget(IPC_PRIVATE, 1, SVSEM_MODE | IPC_CREAT);
arg.val = semvmx;
Semctl(semid, 0, SETVAL, arg); /* set value to max */
for (i = semvmx-1; i > 0; i--) {
ops[0].sem_num = 0;
ops[0].sem_op = -i;
ops[0].sem_flg = SEM_UNDO;
if (semop(semid, ops, 1) != -1) {
semaem = i;
printf("max value of adjust-on-exit = %d\n", semaem);
break;
}
}
Semctl(semid, 0, IPC_RMID);
/* $$.bp$$ */
/* 4determine max # undo structures */
/* 4create one set with one semaphore; init to 0 */
semid = Semget(IPC_PRIVATE, 1, SVSEM_MODE | IPC_CREAT);
arg.val = 0;
Semctl(semid, 0, SETVAL, arg); /* set semaphore value to 0 */
Pipe(pipefd);
child = Malloc(MAX_NPROC * sizeof(pid_t));
for (i = 0; i < MAX_NPROC; i++) {
if ( (child[i] = fork()) == -1) {
semmnu = i - 1;
printf("fork failed, semmnu at least %d\n", semmnu);
break;
} else if (child[i] == 0) {
ops[0].sem_num = 0; /* child does the semop() */
ops[0].sem_op = 1;
ops[0].sem_flg = SEM_UNDO;
j = semop(semid, ops, 1); /* 0 if OK, -1 if error */
Write(pipefd[1], &j, sizeof(j));
sleep(30); /* wait to be killed by parent */
exit(0); /* just in case */
}
/* parent reads result of semop() */
Read(pipefd[0], &j, sizeof(j));
if (j == -1) {
semmnu = i;
printf("max # undo structures = %d\n", semmnu);
break;
}
}
Semctl(semid, 0, IPC_RMID);
for (j = 0; j <= i && child[j] > 0; j++)
Kill(child[j], SIGINT);
/* 4determine max # adjust entries per process */
/* 4create one set with max # of semaphores */
semid = Semget(IPC_PRIVATE, semmsl, SVSEM_MODE | IPC_CREAT);
for (i = 0; i < semmsl; i++) {
arg.val = 0;
Semctl(semid, i, SETVAL, arg); /* set semaphore value to 0 */
ops[i].sem_num = i;
ops[i].sem_op = 1; /* add 1 to the value */
ops[i].sem_flg = SEM_UNDO;
if (semop(semid, ops, i+1) == -1) {
semume = i;
printf("max # undo entries per process = %d\n", semume);
break;
}
}
Semctl(semid, 0, IPC_RMID);
exit(0);
}
|