Создание семафора
QNX поддерживает два типа семафоров — неименованные и именованные. Разница между ними заключается в том, что к именованному семафору можно обратиться из любого процесса в системе (или даже по сети QNET с другого сетевого хоста), поскольку такой семафор имеет ассоциированное с ним имя в файловой системе QNX. Необходимо помнить, что именованные семафоры, при прочих равных условиях, медленнее и требуют для своей работы запущенного в системе менеджера очередей сообщений POSIX (mqueue).
Для каждого типа семафоров существует своя группа функций, применение которых не должно смешиваться.
sem_init() и sem_destroy() — создание и разрушение неименованного семафора. При создании указывается параметр доступа из других потоков и начальное значение счетчика семафора. С неинициализированным семафором никаких операций проводить нельзя (это общее правило справедливо и для всех иных примитивов синхронизации). После разрушения семафора его необходимо повторно инициализировать для использования.
Обе функции возвращают 0 в случае успеха и -1 в случае ошибки. Код ошибки записывается в переменной errno. В частности, функция sem_init() может сигнализировать о следующих ошибках выполнения:
EAGAIN — в данный момент нет ресурсов для инициализации семафора;
EINVAL — начальное значение счетчика превышает SEM_VALUE_MAX;
EPERM — у процесса недостаточно привилегий для инициализации семафора;
ENOSPC — ресурсы, необходимые для инициализации, исчерпаны;
ENOSYS — функция sem_init() не поддерживается реализацией системы.
При вызове функции sem_destroy() может регистрироваться только одна ошибка:
EINVAL — неправильный описатель семафора.
sem_open() и sem_close() — открытие и закрытие именованного семафора (если отсутствует ранее созданный семафор с таким именем, то его создание). В вопросе подключения и отключения работа с именованным семафором аналогична работе с обычным файлом. Также для именованных семафоров существует операция sem_unlink(), аналогичная операции unlink() для обычного файла. В функцию sem_open() передается имя семафора, параметры открытия семафора и дополнительные параметры в случае создания семафора.
Операции блокировки
Для семафора определены три модификации операции блокировки:
int sem_wait(sem_t* sem);
int sem_trywait(sem_t* sem);
#include <time.h>
int sem_timedwait(sem_t* sem, const struct timespec * abs_timeout);
Все эти функции опираются на функцию (native QNX API):
int SyncSemWait(sync_t* sync, int try);
Функция простого ожидания sem_wait() пытается выполнить декремент счетчика семафора. Если исходное значение счетчика было больше или равно 1, то функция после выполнения операции декремента возвращает управление в вызвавший код. Если же значение внутреннего счетчика семафора равнялось 0, выполнение вызвавшего функцию потока блокируется до тех пор, пока какой-либо другой поток не увеличит значение счетчика семафора. После того как значение счетчика будет увеличено, блокированный поток получает управление (в порядке очереди), выполняет (завершает) декремент счетчика семафора и возвращает управление. Блокированное состояние потока может быть также прервано получением направленного ему сигнала (см. главу 3).
Функция ожидания с проверкой семафора, sem_trywait(), до выполнения операции над семафором проверяет значение счетчика. Если это значение больше нуля, функция выполняет декремент счетчика, а если значение равно нулю - возвращает управление потоку, не блокируясь на ожидании доступности семафора (но захват семафора в этом случае не происходит, о чем извещает код возврата).
Функция ожидания с тайм-аутом, sem_timedwait(), ожидает возможности уменьшить на 1 счетчик семафора (блокируется на ожидании) до момента времени abs_timeout. Это ожидание реализовано с помощью функции TimerTimeout() (native QNX API) с параметром SIGEV_UNBLOCK. Благодаря этой функции может быть прерван ряд состояний блокировки потоков (в том числе блокировки на семафоре, мьютексе и условной переменной).
Операции освобождения
int sem_post(sem_t* sem);
Эта операция увеличивает на единицу (инкремент) внутренний счетчик семафора, и в этом она диаметрально противоположна операции блокировки. Если перед этим значение счетчика семафора было равно 0, то один из потоков, ожидающих разблокирования семафора, переходит в состояние готовности. Из списка ожидающих потоков система выбирает самый приоритетный, а если таких несколько, то поток, дольше всех ждавший из очереди наиболее приоритетных потоков.
Эта функция имеет свой оригинал в native API QNX:
int SyncSemPost(sync_t* sync);
Фактически разница между POSIX и QNX API в вариантах этой функции состоит в регистрируемых ею ошибках.
Функция sem_post() сообщает о следующих ошибках:
EINVAL — неверный дескриптор семафора sem;
ENOSYS — функция sem_post() не поддерживается системой.
В отличие от sem_post(), функция SyncSemPost() может указывать на ошибки:
EAGAIN — недостаточно памяти для создания внутреннего объекта синхронизации;
EFAULT — неверный указатель на семафор sync;
EINTR — выполнение функции прервано сигналом;
EINVAL — аргумент sync не указывает на инициированный семафор.
Как видим, функция QNX API несколько разнообразнее в плане контроля передаваемых аргументов и результата выполнения функции.
