Използване на семафори

fprintf stderr

Четвъртото издание на популярното ръководство обхваща основите на програмирането на Linux. Преглед: Използване на библиотеки C/C ++ и­инструменти за разработка, организация на системни повиквания, вход/изход на файлове, взаимодействие на процеси, програмиране с помощта на командната обвивка, създаване на графични потребителски интерфейси с помощта на инструменти GTK + или Qt, използване на сокети и др. Компилацията на програми, свързването им с библиотеки и работа с терминален вход/изход. Дадени са техники за писане на приложения в средите GNOME® и KDE®, съхраняване на данни с помощта на СУБД MySQL® и програми за отстраняване на грешки. Книгата е добре структурирана, което прави ученето лесно и бързо.

За начинаещи програмисти на Linux

Книга: Основи на програмирането на Linux

Използване на семафори

Както можете да видите от предишните раздели, семафорните операции могат да бъдат много сложни. Това не е най-тъжното, защото програмирането на много процеси или нишки с критични секции е много трудна задача сама по себе си, а наличието на сложен софтуерен интерфейс само увеличава интелектуалното натоварване.

За щастие, повечето от задачите, които се нуждаят от семафори, могат да бъдат изпълнени с един двоичен семафор - най-простият тип семафор. В следващия пример (Упражнение 14.1) използвате пълния интерфейс за програмиране, за да създадете много прост P и V интерфейс за двоичен семафор. След това ще използвате този прост интерфейс, за да демонстрирате как работят семафорите.

Експериментирането със семафори ще използва една програма, sem1.c, която можете да стартирате няколко пъти. Ще се използва незадължителен параметър, за да се посочи дали програмата е отговорна за създаването и унищожаването на семафора.

Показването на два различни знака ще означава влизане и излизане от критична секция. Програма, стартирана с параметър, отпечатва X при влизане и излизане от критична секция. Другите екземпляри на изпълняващата се програма ще показват символ O при влизане и излизане от техните критични секции. Тъй като по всяко време само един процес може да влезе в критичния си раздел, всички символи X и O трябва да се появят по двойки.

Упражнение 14.1. Семафори

1. След директивите #include на системата включвате файла semun.h. Той дефинира обединение от тип semun съгласно стандарта X/Open, ако вече не е описано в системния файл sys/sem.h. Това е последвано от прототипите на функциите и глобалната променлива, разположена преди входа на основната функция. Той създава семафор чрез извикване на semget, който връща идентификатора на семафора. Ако програмата е извикана за първи път (т.е. извикана с параметър и argc> 1), set_semvalue се извиква, за да инициализира семафора и променливата op_char е зададена на O .

#include
#include
#include
#include
#include "semun.h"
static int set_semvalue (void);
статична празнота del_semvalue (void);
статичен int semaphore_p (void);
статичен int semaphore_v (void);
статичен int sem_id;
int main (int argc, char * argv []) int i;
int pause_time;
char op_char = 'O';
srand ((неподписан int) getpid ());
sem_id = semget ((key_t) 1234, 1, 0666 | IPC_CREAT);
if (argc> 1) if (! set_semvalue ()) fprintf (stderr, "Неуспешно инициализиране на семафорен");
изход (EXIT_FAILURE);
>
op_char = 'X';
сън (2);
>

2. Следва цикъл, в който влизането и излизането на критичния участък се извършва 10 пъти. Първо извиквате функцията semaphore_p, което кара семафора да изчака, докато тази програма е готова да влезе в критичния раздел.

3. След критичния раздел извиквате semaphore_v, който освобождава семафора, преди да премине отново през цикъла for, след изчакване на случаен период от време. След цикъла се извиква функцията del_semvalue за почистване на кода.

if (! semaphore_v ()) изход (EXIT_FAILURE);
pause_time = rand ()% 2;
сън (време на пауза);
>
printf ("n% d - завършен", getpid ());
ако (argc> 1) сън (10);
del_semvalue ();
>
изход (EXIT_SUCCESS);
>

4. Функцията set_semvalue инициализира семафора с командата SETVAL в извикването semctl. Това трябва да се направи преди да използвате семафора.

static int set_semvalue (void) съюз semun sem_union;
sem_union.val = 1;
if (semctl (sem_id, 0, SETVAL, sem_union) == -1) return (0);
връщане (1);
>

5. Функцията del_semvalue има почти същата форма, с изключение на това, че извикването на semctl използва командата IPC_RMID за премахване на идентификатора на семафора.

static void del_semvalue (void) съюз semun sem_union;
ако (semctl (sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf (stderr, "Неуспешно изтриване на семафорен");
>

6. Функцията semaphore_p променя брояча на семафора на -1. Това е процес на изчакване или пауза.

static int semaphore_p (void) struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;/* P () */
sem_b.sem_flg = SEM_UNDO;
ако (semop (sem_id, & sem_b, 1) == -1) fprintf (stderr, "семафор_p неуспешен");
връщане (0);
>
връщане (1);
>

7. Функцията semaphore_v е същата, с изключение на това, че членът sem_op на структурата sembuf е зададен на 1. Това е операция за „освобождаване“, в резултат на което семафорът отново става достъпен.

static int semaphore_v (void) struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;/* V () */
sem_b.sem_flg = SEM_UNDO;
if (semop (sem_id, & sem_b, 1) == -1) fprintf (stderr, "semaphore_v не успя");
връщане (0);
>
връщане (1);
>

Имайте предвид, че тази проста програма позволява единичен двоичен семафор за всяка програма, въпреки че може да се увеличи броят, като се предаде променлива на семафор, ако е необходимо. Обикновено е достатъчен един двоичен семафор.

Можете да тествате програмата си, като я стартирате няколко пъти. Първият път, когато предадете параметър, за да кажете на програмата, че е отговорна за създаването и изтриването на семафора. Другите екземпляри на работещата програма няма да имат параметър.

По-долу е примерен изход за два работещи екземпляра на програмата:

$ cc sem1.c -o sem1
$ ./ sem1 1 &
[1] 1082
$ ./ sem1
OOXXOOXXOOXXOOXXOOXXOOOOXXOOXXOOXXOOXXXX
1083 - завършен
1082 - завършен

За напомняне символът O представлява първия екземпляр на работеща програма, а символът X представлява втория екземпляр на работеща програма. Тъй като всеки екземпляр на програмата отпечатва знак при влизане и излизане от критична секция, всеки знак трябва да се появява само по двойки. Както можете да видите, символите O и X всъщност са сдвоени, което показва, че критичните секции се обработват правилно. Ако програмата не работи на вашата система, можете да използвате командата stty -tostop преди стартиране на програмата, за да сте сигурни, че фоновата програма, генерираща изход към tty, не повишава сигнала.

Как работи

Програмата започва с получаване на обозначение на семафор въз основа на (произволен) ключ, който сте избрали, използвайки функцията semget. Флагът IPC_CREAT кара семафора да бъде създаден, ако е необходимо.

Ако програмата има параметър, тя отговаря за инициализирането на семафора, което се прави с set_semvalue, опростена версия на функцията за общо предназначение semctl. Той също така използва присъствието на параметър, за да дефинира символа за изхода. Функцията за заспиване просто позволява известно време да стартира други екземпляри на програмата, преди дадена програма да направи твърде много проходи от своя цикъл. Използвате функциите srand и rand, за да включите множество псевдослучайни времена в програмата. .

След това програмата изпълнява 10 пъти операторите на тялото на цикъла с псевдослучайни периоди на изчакване в своите критични и некритични секции. Критичната секция се пази от извиквания към вашите функции semaphore_p и semaphore_v, опростени интерфейси към по-общата функция semop .

Преди да изтрие семафора, програмата, стартирана с параметъра, изчаква, докато други екземпляри на програмата приключат с изпълнението. Ако семафорът не бъде премахнат, той ще продължи да съществува в системата, дори ако няма програми, които го използват. В реалните програми е много важно да се уверите, че не случайно ще напуснете семафора след завършване. Това може да създаде проблеми при следващото стартиране на програмата, а семафорите са вид ограничен ресурс, който трябва да съхраните.