Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Глава 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);
 }
 
  
 
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье