Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект лекций 2009.doc
Скачиваний:
58
Добавлен:
13.11.2019
Размер:
2.3 Mб
Скачать

3.8Синхронизация потоков в unix

3.8.1Мьютексы

Синхронизация потоков осуществляется с помощью мьютексов. Мьютексы создаются с помощью системных вызовов pthread_mutex_init и уничтожаются - pthread_ mutex_destroy.

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *mutex,

const pthread_mutexattr_t *attr);

int pthread_mutex_destroy(pthread_mutex_t *mutex);

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

Поток блокирует мьютекс с помощью вызова pthread_ mutex_lock. Разблокирование - pthread_ mutex_unlock.

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

Мьютексы предназначены для кратковременной блокировки, например для защиты совместно используемой переменной. Они не предназначены для долговременной синхронизации, например для ожидания, когда освободиться накопитель на магнитной ленте. Для долговременной синхронизации предоставляются переменные состояния. Создаются с помощью вызова pthread_ cond _init и уничтожаются вызовом pthread_cond_destroy.

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

int pthread_cond_init(pthread_cond_t *restrict cond,

const pthread_condattr_t *restrict attr);

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

Переменные состояния используются следующим образом: один поток ждет, когда переменная примет определенное значение, а другой поток сигнализирует ему об изменении этой переменной. Например, обнаружив, что накопитель на магнитной ленте занят, поток может обратиться к вызову pthread_cond_wait, задав в качестве параметра адрес переменной, которую все потоки договорились связать с накопителем на магнитной ленте. Когда поток, использующий накопитель на магнитной ленте, освободит этот устройство (возможно через несколько часов), он обращается к вызову pthread_cond_signal, чтобы изменить переменную состояния и тем самым сообщить вызывающим потокам, что магнитофон свободен. Если ни один поток в этот момент не ждет, когда освободится накопитель, то сигнал просто теряется. Другими словами переменные состояния не считаются семафорами.

3.8.2Семафоры

Для нормальной работы семафора необходимо выполнение следующих условий:

1. Значение семафора должно быть доступно различным процессам. Семафор находится в адресном пространстве ядра.

2. Единственным способом гарантировать атомарность критических участков операций является выполнение этих операций в режиме ядра.

Семафоры SYSTEM V обладают следующими характеристиками:

Семафор представляет собой не один счетчик, а группу, состоящую из нескольких счетчиков, объединенных общими признаками (например, дескриптором объекта, права доступа т.д.)

Каждое из этих чисел может принимать любое неотрицательное значение в пределах, определенных системой ( а не только значения 0 и 1).

Для каждой группы семафоров ядро поддерживает структуру данных semid_ds, включающую следующие поля:

Таблица 3.4. Структура семафоров в ядре

Поле

Данные

sem_perm

Данные, хранящиеся в записи struct semid_ds

sem_nsems

Число семафоров в наборе

sem_base

Указатель на массив семафоров

sem_otime

Время, когда какой-либо процесс в последний раз выполняет операции над семафорами

sem_stime

Время, когда тот или иной процесс в последний раз изменяет управляющие параметры набора.

Значение конкретного семафора из набора хранится во внутренней структуре sem

Таблица 3.5. Внутренняя структура sem.

Поле

Данные

Semval

Целочисленное значение текущего семафора

Sempid

Идентификатор процесса, который выполнял операции над данным семафором в последний раз

Semncnt

Число процессов, которые заблокированы и ожидают увеличение значения семафора

Semzcnt

Число процессов, которые заблокированы и ожидают обращения значения семафора в нуль.

Помимо собственно значения семафора, в структуре sem хранится идентификатор процесса, вызвавшего последнюю операцию над семафором, число процессов, ожидающих увеличение значения семафора, и число процессов, ожидающих, когда значение семафора станет равным нулю.

Набор семафоров

s truct semid_ds, struct sem struct sem

sem_base
Таблица семафоров

Рис 5.1. Структура данных ядра, используемая для манипулирования семафорами.

Как и сообщения, семафоры хранятся в адресном пространстве ядра и являются устойчивыми, т.е. сохраняются независимо от завершения создавшего их процесса. У каждого набора семафоров есть назначенный владелец, и удалить набор или изменить его управляющие параметры могут только процессы, имеющие право привилегированного пользователя, создателя набора или назначенного владельца. Если набор семафоров удаляется, то ядро активизирует все процессы, которые в данный момент заблокированы семафорами этого набора; все произведенные данными процессами системные вызовы прерываются и возвращают код неудачного завершения, -1.

В ОС UNIX SYSTEM V имеются три API, предназначенные для манипулирования семафорами

API

Назначение

Semget

Открытие и создание набора семафоров

Semop

Запрос и изменение значение семафора

Semctl

Запрос и изменение управляющих параметров набора семафоров, удаление набора.

Для этих API необходимы следующие файлы заголовков:

#include<sys/types.h>

#include<sys/ips.h>

#include<sys/sem.h>

Функция semget

int semget( key_t key, int num_sem, int flag);

Функция открывает набор семафоров, идентификатор которого задан значением аргумента key, и возвращает неотрицательный целочисленный дескриптор. Его можно использовать в других API семафоров, а также для запроса и установки управляющих параметров набора семафоров.

Если значение аргумента keyположительное целое, рассматриваемый API пробует открыть набор семафоров, ключевой идентификатор совпадает с указанным значением. Если же значением key является макрос IPC_PRIVATE, API создает новый набор семафоров, который будет использоваться исключительно вызывающим процессом. Такие «частные» семафоры обычно выделяются родительским процессом, создающим один или несколько порожденных процессов. Родительский и порожденный процессы пользуются этими семафорами для синхронизации своей работы.

При нулевом значении flag API прерывает свою работу, если отсутствует набор семафоров, ключевой идентификатор которого совпадает с указанным значением key; в противном случае возвращается дескриптор этого набора. Если процессу необходимо создать новый набор с идентификатором key (и набора с таким идентификатором нет), то значение аргумента flag должно представлять собой результат побитового логического сложения константы IPC_CREAT и числовых значений прав доступа к этому набору для чтения и записи.Значение num_sem может быть равно нулю, если флаг IPC_CREAT в аргументе flag не указан, или числу семафоров во вновь создаваемом наборе.

Пример.

int semd=semget(15, 2, IPC_CREAT | 0544);

Вызов создает двухсемафорный набор с идентификатором 15

Для гарантированного создания нового набора семафоров можно указывать одновременно с флагом IPC_CREAT флаг IPC_EXCL. Тогда API будет успешно выполнен только в том случае, если он создаст новый набор с заданным значением key. В случае неудачи возвращает –1.

Функция semop

int semop(int semid, struct sembuf * opPtr , int nops);

В качестве второго аргумента функции передается указатель на структуру данных, определяющую операции, которые требуется произвести над семафором с дескриптором semid. Операций может быть несколько и их число указывается в последнем аргументе nops. Ядро обеспечивает атомарность всех операций . указанных в этой структуре.

Тип данных struct sembuf определяется в заголовке <sys/sem.h>

struct sembuf

{

short sem_num;// индекс семафора

short sem_op;// операция над семафором

short sem_flg;// флаги операции

}

Переменная sem_op может иметь следующие значения

Значение semop

Смысл

Положительное число

Увеличить значение указанного семафора на эту величину

Отрицательное число

Процесс ожидает, пока значение семафора не станет большим либо равным по абсолютной величине semop. Затем абсолютная величина semop вычитается из значения семафора

0

Процесс ожидает, пока семафор не обнулится.

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

Пример №1: 0 – разрешающее значение; 1 – запрещающее

Операция, запирающая ресурс выглядит следующим образом:

static struct sembuf sop_lock[2]={