Программирование PC Использование функций с семафорами в Linux Thu, November 21 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


Использование функций с семафорами в Linux Печать
Добавил(а) microsin   

sem_wait, sem_timedwait, sem_trywait - эти функции используются для синхронизации между потоками путем блокировки на семафоре. Функции соответствуют стандарту POSIX.1-2001.

#include < semaphore.h>
 
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

Линковка должна происходить с -pthread. Макрос проверки требований для glibc (см. feature_test_macros(7)):

sem_timedwait(): _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600

[Описание]

sem_wait() декрементирует (или блокирует выполнение вызвавшего потока) семафор, на который указывает sem. Если значение семафора больше нуля, то декремент выполняется, и функция выполнит немедленный возврат. Если значение семафора в настоящий момент равно 0, то функция блокирует выполнение до тех пор, пока не будет возможность выполнить декремент (т. е. пока значение семафора не станет больше 0), или пока обработчик сигнала (signal handler) не прервет вызов.

sem_trywait() работает так же, как и sem_wait(), за исключением того, что если декремент не может быть произведен немедленно, то вместо блокировки вызов возвратит ошибку (errno установится в EAGAIN).

sem_timedwait() также использует блокировку в случае невозможности декремента, как и sem_wait(), за исключением того, что abs_timeout задает предел интервала ожидания, в течение которого функция должна удерживать блокировку. Аргумент abs_timeout указывает на структуру timespec, которая задает абсолютную точку таймаута в секундах и наносекундах. Это время относительно Epoch, 1970-01-01 00:00:00 +0000 (UTC). Структура timespec определена следующим образом:

struct timespec
{ time_t tv_sec; /* секунды */ long tv_nsec; /* наносекунды [0 .. 999999999] */
};

Если таймаут истек в течение вызова sem_timedwait(), и семафор не удалось немендленно заблокировать, то sem_timedwait() вызовет возврат по ошибке таймаута (будет возвращено значение -1, и errno установится в ETIMEDOUT).

Если операция декремента может быть выполнена немедленно, то sem_timedwait() сделает возврат без ошибки (вернет 0), независимо от значения abs_timeout. Кроме того, для этого случая значение abs_timeout не проверяется.

Возвращаемое значение: все эти 3 функции в случае успеха вернут 0. В случае ошибки значение семафора на выходе остается неизменным, будет возвращено -1, и переменная errno будет установлена для описания ошибки.

EINTR. Вызов был прерван сигналом обработчика, см. signal(7).

EINVAL. Параметр sem не указывает на допустимый семафор.

Для sem_trywait() могут быть дополнительные значения errno:

EAGAIN. Операция не может быть выполнена без блокировки (например у семафора в настоящий момент нулевое значение).

Для sem_timedwait() могут быть дополнительные значения errno:

EINVAL. Значение abs_timeout.tv_nsecs меньше 0, либо больше или равно 1000 миллионов.

ETIMEDOUT. Время ожидания вызова истекло до блокировки семафора.

Замечание: обработчик сигнала (signal handler) всегда обрывает блокировку на вызове этих функций, независимо от использования флага sigaction(2) SA_RESTART.

[Пример использования]

Ниже показан простой пример программы, демонстрирующий принцип работы на основе неименованого (unnamed) семафора. Программа принимает на входе 2 числовых аргумента. Первый аргумент задает значение в секундах, которое используется для взвода таймера alarm, чтобы сгенерировался сигнал SIGALRM. Этот обработчик выполнит sem_post(3), чтобы инкрементировать семафор, на котором производит ожидание функция sem_timedwait(), вызыванная в теле main(). Второй аргумент командной строки указывает длительность таймаута в секундах для sem_timedwait().

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

$ ./testsem 2 3
About to call sem_timedwait()
sem_post() from handler
sem_timedwait() succeeded
$ ./testsem 2 1
About to call sem_timedwait()
sem_timedwait() timed out

Исходный код программы:

// Пример использования семафора с таймаутом из статьи [1].
#include < unistd.h>
#include < stdio.h>
#include < stdlib.h>
#include < semaphore.h>
#include < time.h>
#include < assert.h>
#include < errno.h>
#include < signal.h>   sem_t sem;  
#define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0)  
static void handler(int sig)
{ write(STDOUT_FILENO, "sem_post() from handler\n", 24); if (sem_post(&sem) == -1) { write(STDERR_FILENO, "sem_post() failed\n", 18); _exit(EXIT_FAILURE); }
}  
int main(int argc, char *argv[])
{ struct sigaction sa; struct timespec ts; int s;   if (argc != 3) { fprintf(stderr, "Usage: %s < alarm-secs> < wait-secs>\n", argv[0]); exit(EXIT_FAILURE); }   if (sem_init(&sem, 0, 0) == -1) handle_error("sem_init");   /* Установка обработчика сигнала SIGALRM; устанавливает таймер alarm в секундах из первого параметра (argv[1]) */ sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGALRM, &sa, NULL) == -1) handle_error("sigaction"); alarm(atoi(argv[1]));   /* Вычисление отностительного интервала как текущее время плюс секунды из второго параметра (argv[2]) */ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) handle_error("clock_gettime"); ts.tv_sec += atoi(argv[2]);   printf("main() about to call sem_timedwait()\n"); while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR) continue; /* Перезапуск, если было прерывание от handler */   /* Проверка, что произошло: */ if (s == -1) { if (errno == ETIMEDOUT) printf("sem_timedwait() timed out\n"); else perror("sem_timedwait"); } else printf("sem_timedwait() succeeded\n");   exit((s == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}

Makefile для компиляции:

SRC     := main.c
 
all:
→gcc $(CFLAGS_LINUX) -o testsem $(SRC) -lm $(INCLUDE) -L./lib -no-pie
 
clean:
→rm testsem -f

Как компилировать:

$ make clean
$ make

[Ссылки]

1. sem_timedwait(3) Linux man page site:linux.die.net.
2. How to block on a semaphore until its value is positive site:stackoverflow.com.
3. Linux: задержки в программе на языке C.

 

Добавить комментарий


Защитный код
Обновить

Top of Page