
- •Введение
- •Глава 1. Фундаментальные концепции unix Систем
- •Программы, процессы и потоки
- •Сигналы
- •Идентификаторы процессов, группы процессов и сеансы
- •Система прав
- •Другие атрибуты процесса
- •Межпроцессное взаимодействие
- •Использование системных вызовов
- •Краткие описания функций и обработка ошибок
- •Контрольные вопросы
- •Литература
- •Глава 2. Базовые операции ввода-вывода
- •Файловые операции ввода - вывода
- •Стандартные дескрипторы
- •Системные вызовы open и creat
- •Системный вызов umask
- •Системный вызов unlink
- •Текущая позиция в файле
- •Системный вызов write
- •2.8. Системный вызов read
- •2.9. Системный вызов close
- •2.10. Системный вызов lseek
- •2.11. Системные вызовы pread и pwrite
- •2.12. Системные вызовы truncate и ftruncate
- •Контрольные вопросы
- •Литература
- •Глава 3. Дополнительные операции файлового ввода_вывода
- •Низкоуровневый доступ к файловой системе
- •Жесткие и символические ссылки
- •Системный вызов getcwd
- •Отображение метаданных файла
- •Системные вызовы getpwuid, getgrgid и getlogin
- •Каталоги
- •Системные вызовы chdir и fchdir
- •Системные вызовы mkdir и rmdir
- •Контрольные вопросы
- •Литература
- •Глава 4. Процессы и потоки
- •4.1. Среда окружения
- •Системный вызов exec
- •Системный вызов fork
- •Завершение процесса и системные вызовы exit
- •Системные вызовы wait, waitpid и waitid
- •Получение и изменение идентификаторов пользователя и группы
- •Получение и изменение приоритета
- •Контрольные вопросы
- •Литература
- •Глава 5. Механизмы межпроцессного взаимодействия
- •5.1. Каналы
- •5.2. Системные вызовы dup и dup2
- •5.3. Двунаправленное взаимодействие с использованием однонаправленных каналов
- •Контрольные вопросы
- •Литература
- •Глава 6.Механизмы взаимодействия процессов
- •Именованные каналы (fifo)
- •Системные вызовы для работы с очередями сообщений posix
- •Семафоры
- •Системные вызовы для работы с общей памятью posix
- •Контрольные вопросы
- •Литература
- •Глава 7.Сетевое взаимодействие и сокеты
- •Основные системные вызовы для работы с сокетами, образующими логические соединения
- •Обслуживание нескольких клиентов
- •Адресация сокетов
- •In_port_t sin_port; /* номер порта (uint16_t) */
- •In_addr_t s_addr; /* адрес iPv4 (uint32_t) */
- •Домен адресов af_inet6
- •In_port_t sin6_port; /* номер порта (uint16_t) */
- •Доменная система именования
- •Параметры сокетов
- •Контрольные вопросы
- •Литература
- •Глава 8.Сигналы и таймеры
- •Введение в сигналы
- •Жизненный цикл сигналов
- •Типы сигналов
- •Системный вызов sigaction
- •Контрольные вопросы
- •Литература
- •Заключение
- •Список литературы
- •Глава 2. Базовые операции ввода-вывода 14
- •Глава 3. Дополнительные операции файлового ввода_вывода 25
- •Глава 6. Механизмы взаимодействия процессов 58
Семафоры
Семафор – это счетчик, который способствует предотвращению одновременного доступа нескольких процессов или потоков к совместно используемому ресурсу. Если процесс или поток не проверяют состояние семафора перед выполнением операций с совместно используемым ресурсом, последствия могут оказаться самыми печальными.
Двоичный семафор имеет два состояния: закрыто и открыто. В общем случае семафоры могут иметь если не бесконечное, то достаточно большое число состояний. По сути это обычный счетчик. Он уменьшается при захвате семафора процессом (запирается) и увеличивается при освобождении (отпирается). Если счетчик равен нулю, процесс, пытающийся захватить семафор, должен подождать, пока другой процесс не освободит его, увеличив значение счетчика - значение семафора не может быть отрицательным числом. Над семафорами обычно выполняются два действия, которые абстрактно можно представить как semwait и sempost. Выразим эти операции на языке С:
void semwait(int *sem)
{
while (*sem <= 0)
; /* просто ждать, ничего не делая*/
(*sem)-;
}
void sempost(int *sem)
{
(*sem)++;
}
Семафор должен быть инициализирован обращением к sempost, иначе никто не сможет захватить его. Например, пусть семафор представляет собой счетчик свободных буферов. Первоначально имеется пять буферов. Поэтому при запуске необходимо пять раз обратиться к sempost, либо нужно записать число 5 в переменную счетчик.
Реализация семафоров должна находиться в ядре, которое обладает возможностью организации совместного доступа к данным из нескольких процессов, в состоянии обеспечить атомарность операций и может выделить лишние кванты времени одним процессам, пока другие находятся в состоянии блокировки.
Для ускорения работы с семафорами можно напрямую объявить объект sem_t в программе: sem_t sem; или разместить в динамической памяти: sem_t *semp = malloc(sizeof(sem_t));
Если объект размещается таким образом, необходимо инициализировать его вызовом sem_init:
sem_init - инициализирует неименованный семафор
#include <semaphore.h>
int sem_init (
sem_t *sem, /* семафор */
int pshared, /*доступен другим процессам */
unsigned value /* начальное значение */
);
/* Возвращает 0 в случае успеха или -1 в случае ошибки (код ошибки - в errno) */
Для разрушения неименованного семафора следует использовать системный вызов sem_destroy:
sem_destroy - разрушает неименованный семафор
#include <semaphore.h>
int sem_ destroy (
sem_t *sem /* семафор */
);
/* Возвращает 0 в случае успеха или -1 в случае ошибки (код ошибки - в errno) */
Неименованные семафоры работают быстрее и это дает нам вот что:
они прекрасно работают с потоками, особенно когда нужен семафор со счетчиком;
они могут работать и с процессами, если находятся в разделяемой памяти. В этом случае аргумент pshared, вызова sem_init должен иметь ненулевое значение.
Основная область применений неименованных семафоров – работа с потоками, особенно в Linux.