Семинар 8. Семафоры в UNIX как средство синхронизации процессов.

(Основывается на лекции 5 и лекции 6)

Предыдущий семинар | Программа курса | Следующий семинар

Программа семинара

  1. Семафоры в UNIX. Отличие операций над UNIX семафорами от классических операций.

  2. Создание массива семафоров или доступ к уже существующему. Системный вызов semget().

  3. Выполнение операций над семафорами. Системный вызов semop().

  4. Удаление набора семафоров из системы с помощью команды ipcrm или системного вызова semctl().

  5. Понятие о POSIX семафорах.

Цели занятия

  1. Дать навыки работы с семафорами в UNIX. Хотя работа с семафорами является достаточно простой идеологически, эта тема вызывает у студентов серьезные затруднения из-за сложности семантики системного вызова semop().

  2. Дать представление о существовании POSIX семафоров.

Практические работы

  1. Прогон примера с использованием семафора.

  2. Изменение предыдущего примера.

  3. Написание, компиляция и прогон программы с организацией взаимоисключения с помощью семафоров для 2-х процессов, взаимодействующих через разделяемую память.

  4. Написание, компиляция и прогон программы с организацией взаимной очередности с помощью семафоров для 2-х процессов, взаимодействующих через pipe.

 

План занятия

  1. Семафоры в UNIX. Отличие операций над UNIX семафорами от классических операций.

    На предыдущем семинаре мы говорили о необходимости синхронизации работы процессов для их корректного взаимодействия через разделяемую память. Семафоры в лекции Как упоминалось на лекции, одним из первых механизмов, предложенных для синхронизации поведения процессов, стали семафоры, концепцию которых описал Дейкстра (Dijkstra) в 1965 году. При разработке средств System V IPC семафоры вошли в их состав как неотъемлемая часть. Следует отметить, что набор операций над семафорами System V IPC отличается от классического набора операций {P, V }, предложенного Дейкстрой. Он насчитывает три операции:

    Изначально все IPC семафоры инициируются нулевым значением.

    Легко видеть, что классической операции P(S) соответствует операция D(S,1), а классической операции V(S) соответствует операция A(S,1). Аналогом ненулевой инициализации Дейкстровских семафоров значением n может служить выполнение операции A(S,n) сразу после создания семафора S, с обеспечением атомарности создания семафора и ее выполнения посредством другого семафора. Мы показали, что классические семафоры реализуются через семафоры System V IPC. Обратное не является верным. Используя операции P(S) и V(S), мы не сумеем реализовать операцию Z(S).

    Поскольку IPC семафоры являются составной частью средств System V IPC, то для них верно все, что говорилось об этих средствах в целом на предыдущем семинаре. IPC семафоры являются средством связи с непрямой адресацией, требуют инициализации для организации взаимодействия процессов и специальных действия для освобождения системных ресурсов по его окончании. Пространством имен IPC семафоров является множество значений ключа, генерируемых с помощью функции ftok(). Для совершения операций над семафорами системным вызовам в качестве параметра передаются IPC дескрипторы семафоров, однозначно идентифицирующих их во всей вычислительной системе, а вся информация о семафорах располагается в адресном пространстве ядра операционной системы. Это позволяет организовывать через семафоры взаимодействие процессов, даже не находящихся в системе одновременно.

     

  2. Создание массива семафоров или доступ к уже существующему. Системный вызов semget().

    В целях экономии системных ресурсов операционная система UNIX позволяет создавать не по одному семафору для каждого конкретного значения ключа, а связывать с ключом целый массив семафоров (в Linux - до 500 семафоров в массиве, хотя это количество может быть уменьшено системным администратором). Для создания массива семафоров, ассоциированного с определенным ключом, или доступа по ключу к уже существующему массиву используется системный вызов semget(), являющийся аналогом системного вызова shmget() для разделяемой памяти, который возвращает значение IPC дескриптора для этого массива. При этом существуют те же способы создания и доступа, что и для разделяемой памяти. Вновь созданные семафоры инициируются нулевым значением.
     

  3. Выполнение операций над семафорами. Системный вызов semop().

    Для выполнения операций A, D и Z над семафорами из массива используется системный вызов semop(), обладающий довольно сложной семантикой. Разработчики System V IPC явно перегрузили этот вызов, применяя его не только для выполнения всех трех операций, но еще и для нескольких семафоров в массиве IPC семафоров одновременно. Для правильного использования этого вызова необходимо выполнить следующие действия:

    1. Определиться, для каких семафоров из массива вы хотите выполнить операции. Необходимо иметь в виду, что все операции реально совершаются только перед успешным возвращением из системного вызова, т.е. если вы хотите выполнить операции A(S1,5) и Z(S2) в одном вызове и оказалось, что S2 != 0, то значение семафора S1 не будет изменено до тех пор, пока значение S2 не станет равным 0. Порядок выполнения операций в случае, когда процесс не переходит в состояние ожидание не определен. Так, например, при одновременном выполнении операций A(S1,1) и D(S2,1) в случае S2 > 1 неизвестно, что выполнится раньше - уменьшение значения семафора S2 или увеличение значения семафора S1. Если порядок является для вас существенным, лучше применить несколько вызовов вместо одного.

    2. После того как вы определились с количеством семафоров и совершаемыми операциями, необходимо завести в программе массив из элементов типа struct sembuf с размерностью равной определенному количеству семафоров (если операция совершается только над одним семафором можно, естественно, обойтись просто переменной). Каждый элемент этого массива будет соответствовать операции над одним семафором.

    3. Заполнить элементы массива. В поле sem_flg каждого элемента занести значение 0 (с другими значениями флагов мы на семинарах работать не будем). В поля sem_num и sem_op занести номера семафоров в массиве IPC семафоров и соответствующие коды операций. Семафоры нумеруются, начиная с 0. Если у вас в массиве всего один семафор, то он будет иметь номер 0. Операции кодируются так:

      для выполнения операции A(S,n) значение поля sem_op должно быть равно n.
      для выполнения операции D(S,n) значение поля sem_op должно быть равно -n.
      для выполнения операции Z(S) значение поля sem_op должно быть равно 0.

    4. В качестве второго параметра системного вызова semop() указать адрес заполненного массива, а в качестве третьего параметра - ранее определенное количество семафоров, над которыми совершаются операции.


     
  4. Прогон примера с использованием семафора.

    Для иллюстрации вышесказанного давайте рассмотрим простейшие программы, синхронизирующие свои действия с помощью семафоров (файлы /ftp/pub/sem8/stud/08-1a.c и /ftp/pub/sem8/stud/08-1b.c). Первая программа выполняет над семафором S операцию D(S,1), вторая программа выполняет над тем же семафором операцию A(S,1). Если семафор не существует в системе, любая программа создает его перед выполнением операции. Поскольку при создании семафор всегда инициируется 0, то программа 1 может работать без блокировки только после запуска программы 2 .
     

  5. Изменение предыдущего примера.

    Измените предыдущие программы, так чтобы первая программа могла работать без блокировки после не менее 5 запусков второй программы.
     

  6. Удаление набора семафоров из системы с помощью команды ipcrm или системного вызова semctl().

    Nota bene Как мы говорили и видели из примеров, массив семафоров может продолжать существовать в системе и после завершения использовавших его процессов, а семафоры будут сохранять свое значение. Это способно привести к неправильному поведению программ, предполагающих, что семафоры были только что созданы и, следовательно, имеют нулевое значение. Необходимо удалять семафоры из системы перед запуском таких программ или перед их завершением. Для удаления семафоров можно воспользоваться командами ipcs и ipcrm, рассмотренными на предыдущем семинаре. Команда ipcrm в этом случае должна иметь вид

    ipcrm sem <IPC идентификатор>  

    Для этой же цели мы можем применять системный вызов semctl(), который умеет выполнять и другие операции над массивом семафоров, но их рассмотрение выходит за рамки нашего курса.

  7. Написание, компиляция и прогон программы с организацией взаимоисключения с помощью семафоров для 2-х процессов, взаимодействующих через разделяемую память.

    Подсказка На прошлом занятии мы установили, что любые неатомарные операции, связанные с изменением содержимого разделяемой памяти, представляют собой критическую секцию процесса или нити исполнения. Модифицируйте программы /ftp/pub/sem6-7/stud/06-3a.c и /ftp/pub/sem6-7/stud/06-3b.c, которые иллюстрировали некорректную работу через разделяемую память, обеспечив с помощью семафоров взаимоисключения для их правильной работы.
     

  8. Написание, компиляция и прогон программы с организацией взаимной очередности с помощью семафоров для 2-х процессов, взаимодействующих через pipe.

    На семинаре 5, когда мы изучали связь родственных процессов через pipe, мы говорили о том, что pipe является однонаправленным каналом связи, и что для организации связи через один pipe в двух направлениях необходимо использовать механизмы взаимной синхронизации процессов. Организуйте двустороннюю поочередную связь процесса-родителя и процесса-ребенка через pipe, используя для синхронизации семафоры, модифицировав программу /ftp/pub/sem5/stud/05-3.c.
     

  9. Понятие о POSIX семафорах.

    В стандарте POSIX вводятся другие семафоры, полностью аналогичные семафорам Дейкстры. Для инициализации значения таких семафоров применяется функция sem_init(), аналогом операции P служит функция sem_wait(), а аналогом операции V - функция sem_post(). К сожалению, в Linux такие семафоры реализованы только для нитей исполнения внутри одного процесса, и поэтому подробно мы на них останавливаться не будем.
     

Предыдущий семинар | Программа курса | Следующий семинар