Семафоры linux что это

Обновлено: 06.07.2024

Семафор — это объект IPC, управляющий доступом к общим ресурсам (устройствам). Семафоры не позволяют одному процессу захватить устройство до тех пор, пока с этим устройством работает другой процесс. Семафор может находиться в двух положениях: 0 (устройство занято) и 1 (устройство свободно).

Одиночный семафор используется редко, практически никогда. Для контроля доступа к ресурсам обычно используются множества семафоров, даже если это множество состоит всего из одного семафора. Например, пусть у нас есть три принтера. Когда вы посылаете задание на печать, диспетчер печати просматривает множество семафоров принтеров и выясняет, есть ли свободный принтер. Если да, то он начинает печатать ваше задание, если же нет, диспетчер ставит ваше задание в очередь печати.

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

struct ipc_perm sem_perm; /* права доступа */

time_t sem_otime; /* время последней операции */

time_t sem_ctime; /* время последнего изменения */

struct sem *sem_base; /* указатель на первый семафор */

struct wait_queue *eventn; /* очереди ожидания */

struct wait_queue *eventz;

struct sem_undo *undo; /* запросы undo в этом массиве */

ushort sem_nsems; /* номера семафоров в массиве */

Обратите внимание: в структуре есть указатель на первый семафор. Тип указателя — sem. Данный тип описывает семафор:

short sempid; /* pid последней операции */

ushort semval; /* текущее значение семафора */

ushort semncnt; /* число процессов, ожидающих

ushort semzcnt; /* число процессов, ожидающих

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

Текущее значение семафора.

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

Число процессов, ожидающих освобождения всех ресурсов.

Данный текст является ознакомительным фрагментом.

Продолжение на ЛитРес

Семафоры

Семафоры В операционной системе Linux семафоры (semaphore) — это блокировки, которые переводят процессы в состояние ожидания. Когда задание пытается захватить семафор, который уже удерживается, семафор помещает это задание в очередь ожидания (wait queue) и переводит это задание в

Семафоры чтения-записи

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

Семафоры

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

Семафоры

12.3 СЕМАФОРЫ

12.3 СЕМАФОРЫ Поддержка системы UNIX в многопроцессорной конфигурации может включать в себя разбиение ядра системы на критические участки, параллельное выполнение которых на нескольких процессорах не допускается. Такие системы предназначались для работы на машинах AT amp;T

26.6. Семафоры

26.6. Семафоры Семафор — это объект IPC, управляющий доступом к общим ресурсам (устройствам). Семафоры не позволяют одному процессу захватить устройство до тех пор, пока с этим устройством работает другой процесс. Семафор может находиться в двух положениях: 0 (устройство

10.13. Ограничения на семафоры

10.13. Ограничения на семафоры Стандартом Posix определены два ограничения на семафоры:? SEM_NSEMS_MAX — максимальное количество одновременно открытых семафоров для одного процесса (Posix требует, чтобы это значение было не менее 256);? SEM_VALUE_MAX — максимальное значение семафора (Posix

ГЛАВА 11 Семафоры System V

ГЛАВА 11 Семафоры System V 11.1.Введение В главе 10 мы описывали различные виды семафоров, начав с:? бинарного семафора, который может принимать только два значения: 0 и 1. По своим свойствам такой семафор аналогичен взаимному исключению (глава 7), причем значение 0 для семафора

Семафоры Posix, размещаемые в памяти

Семафоры Posix, размещаемые в памяти Мы измеряем скорость работы семафоров Posix (именованных и размещаемых в памяти). В листинге А.24 приведен текст функции main, а в листинге А.23 — текст функции incr.Листинг А.23. Увеличение счетчика с использованием семафоров Posix в

Именованные семафоры Posix

Именованные семафоры Posix В листинге А.26 приведен текст функции main, измеряющей быстродействие именованных семафоров Posix, а в листинге А.25 — соответствующая функция incr.Листинг А.25. Увеличение общего счетчика с использованием именованного семафора Posix//bench/incr_pxsem2.c40 void

Семафоры System V

4.4.2. Исключающие семафоры

4.4.2. Исключающие семафоры Решение проблемы гонки заключается в том, чтобы позволить только одному потоку обращаться к очереди в конкретный момент времени. Когда поток начинает просматривать очередь, все остальные потоки вынуждены дожидаться, пока он удалит очередное

4.4.5. Обычные потоковые семафоры

4.4.5. Обычные потоковые семафоры В предыдущем примере, в котором группа потоков обрабатывает задания из очереди, потоковая функция запрашивает задания до тех пор, пока очередь не опустеет, после чего поток завершается. Эта схема работает в том случае, когда все задания

5.2. Семафоры для процессов

5.2. Семафоры для процессов Как говорилось в предыдущем разделе, процессы должны координировать свои усилия при совместном доступе к памяти. Вспомните: в разделе 4.4.5, "Обычные потоковые семафоры", рассказывалось о семафорах, которые являются счетчиками, позволяющими

В этой главе мы обсудим набор средств, обеспечивающих взаимодействие процессов и первоначально введенных в версии ОС UNIX AT&T System V.2. Поскольку все эти средства появились в одном выпуске системы и обладают одинаковым программным интерфейсом, их часто называют средствами IPC (Inter-Process Communication, взаимодействие между процессами) или более полно System V IPC. Как вы уже видели, это далеко не единственный способ установления связи между процессами, но термин "System V IPC" обычно применяется для обозначения именно этих конкретных средств.

В данной главе мы рассмотрим следующие темы:

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

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

В первом приложении-примере в главе 7, использующем средство dbm для доступа к базе данных, данные могли бы быть повреждены множественными программами, пытавшимися обновить базу данных в одно и то же время. Никакого сбоя не произойдет, если две разные программы запрашивают у двух разных пользователей ввод данных для базы данных, единственная потенциальная проблема кроется в частях программного кода, обновляющих базу данных. Эти секции программы, действительно выполняющие обновления и нуждающиеся в монопольном режиме выполнения, называются критическими секциями. Часто они занимают всего несколько строк кода в гораздо больших по объему программах.

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

Примечание

Функции семафоров, применяемые в потоках и обсуждавшиеся в главе 12, не относятся к наиболее общим функциям, которые мы рассматриваем в этой главе, поэтому будьте внимательны и не путайте функции этих двух типов.

Написать программный код общего назначения, который гарантирует одной программе монопольный доступ к конкретному ресурсу, на удивление сложно, несмотря на то, что существует решение, известное как алгоритм Деккера (Dekker's Algorithm). К сожалению, этот алгоритм полагается на состояние активного ожидания или спин-блокировки, в котором процесс выполняется непрерывно, ожидая изменения адреса памяти. В многозадачной среде, какой является ОС Linux, это нежелательные расходы ресурсов ЦПУ. Ситуация существенно облегчается, когда для обеспечения монопольного доступа есть аппаратная поддержка, обычно в виде специальных команд ЦПУ. Примером аппаратной поддержки могла бы быть команда обращения к ресурсу и приращения регистра атомарным образом, так чтобы никакая другая команда (даже прерывание) не могла появиться между операциями чтения/инкремента/записи.

Одним из возможных решений проблемы можно считать уже знакомое вам создание файла с помощью флага

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

Важный шаг вперед в сфере параллельного программирования был сделан, когда голландский специалист в области компьютерных наук Эдсгер Дейкстра (Edsger Dijkstra) предложил идею семафоров. Как уже кратко упоминалось в главе 12, семафор — это специальная переменная, которая принимает только целые положительные значения и с помощью которой программы могут действовать только атомарно. В этой главе мы расширим данное ранее упрощенное определение. Будет более подробно рассказано, как действуют семафоры и как для взаимодействия отдельных процессов применяются функции общего назначения вместо особого случая многопоточных программ, которые рассматривались в главе 12.

Определяя более строго, семафор — это специальная переменная, для которой разрешены только две операции, формально именуемые ожиданием или приостановкой (wait) и оповещением (signal). Поскольку в программировании Linux у приостановки и оповещения уже есть специальные значения, мы будем применять оригинальное обозначение:

Эти буквы взяты из голландских слов для приостановки (passeren — проходить, пропускать как в случае контрольной точки перед критической секцией) и для оповещения (vrijgeven — предоставлять или освобождать, как в случае отказа от контроля критической секции). Вы можете встретить термины "вверх" (up) и "вниз" (down), применяемые в отношении семафоров по аналогии с использованием сигнальных флажков.

Простейший семафор — это переменная, способная принимать только значения 0 и 1, бинарный или двоичный семафор. Это наиболее распространенный вид семафора. Семафоры, принимающие много положительных значений, называют семафорами общего вида. В оставшейся части главы мы сосредоточимся на двоичных семафорах.

Следующие функции используются исключительно для поддержки механизма семафоров:

newary()

newary() обращается к ipc_alloc() для распределения памяти под новый набор семафоров. Она распределяет объем памяти достаточный для размещения дескриптора набора и всего набора семафоров. Распределенная память очищается и адрес первого элемента набора семафоров передается в ipc_addid(). Функция ipc_addid() резервирует память под массив элементов нового набора семафоров и инициализирует ( struct kern_ipc_perm) набор. Глобальная переменная used_sems увеличивается на количество семафоров в новом наборе и на этом инициализация данных ( struct kern_ipc_perm) для нового набора завершается. Дополнительно выполняются следующие действия:

  • В поле sem_base заносится адрес первого семафора в наборе.
  • Очередь sem_pending объявляетяс пустой.

Все операции, следующие за вызовом ipc_addid(), выполняются под глобальной блокировкой семафоров. После снятия блокировки вызывается ipc_buildid() (через sem_buildid()). Эта функция создает уникальный ID (используя индекс дескриптора набора семафоров), который и возвращается в вызывающую программу.

freeary()

Функция freeary() вызывается из semctl_down() для выполнения действий, перечисленных ниже. Вызов функции осуществляется под глобальной блокировкой семафоров, возврат управления происходит со снятой блокировкой.

  • Вызывается ipc_rmid() (через "обертку" sem_rmid()), чтобы удалить ID набора семафоров и получить указатель на набор семафоров.
  • Аннулируется список откатов для данного набора семафоров
  • Все ожидающие процессы пробуждаются, чтобы получить код ошибки EIDRM.
  • Общее количество семафоров уменьшается на количество семафоров в удаляемом наборе.
  • Память, занимаемая набором семафоров, освобождается.

semctl_down()

Функция semctl_down() предназначена для выполнения операций IPC_RMID и IPC_SET системного вызова semctl(). Перед выполнением этих операций проверяется ID набора семафоров и права доступа. Обе эти операции выполняются под глобальной блокировкой семафоров.

IPC_RMID

Операция IPC_RMID вызывает freeary() для удаления набора семафоров.

IPC_SET

Операция IPC_SET изменяет элементы uid , gid , mode и ctime в наборе семафоров.

semctl_nolock()

Функция semctl_nolock() вызывается из sys_semctl() для выполнения операций IPC_INFO, SEM_INFO и SEM_STAT.

IPC_INFO и SEM_INFO

Операции IPC_INFO и SEM_INFO заполняют временный буфер seminfo статическими данными. Затем под глобальной блокировкой семафора ядра sem_ids.sem заполняются элементы semusz и semaem структуры seminfo в соответствии с требуемой операцией (IPC_INFO или SEM_INFO) и в качестве результата возвращается максимальный ID.

SEM_STAT

Операция SEM_STAT инициализирует временный буфер semid64_ds. На время копирования значений sem_otime , sem_ctime , и sem_nsems в буфер выполняется глобальная блокировка семафора. И затем данные копируются в пространство пользователя.

semctl_main()

Функция semctl_main() вызывается из sys_semctl() для выполнения ряда операций, которые описаны ниже. Перед выполнением операций, semctl_main() блокирует семафор и проверяет ID набора семафоров и права доступа. Перед возвратом блокировка снимается.

GETALL

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

SETALL

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

  • Информация копируется в набор семафоров.
  • Очищается очередь откатов для заданного набора семафоров.
  • Устанавливается значение sem_ctime для набора семафоров.
  • Вызывается функция update_queue() , которая проходит по списку ожидающих операций в поисках задач, которые могут быть завершены в результате выполнения операции SETALL. Будятся любые ожидающие задачи, которые больше не нужно блокировать.

IPC_STAT

Операция IPC_STAT копирует значения sem_otime , sem_ctime и sem_nsems во временный буфер на стеке. После снятия блокировки данные копируются в пользовательское пространство.

GETVAL

Операция GETVAL возвращает значение заданного семафора.

GETPID

Операция GETPID возвращает pid последней операции, выполненной над семафором.

GETNCNT

Операция GETNCNT возвращает число процессов, ожидающих на семафоре, когда тот станет меньше нуля. Это число подсчитывается функцией count_semncnt().

GETZCNT

Операция GETZCNT возвращает число процессов, ожидающих на семафоре, когда тот станет равным нулю. Это число подсчитывается функцией count_semzcnt().

SETVAL

Проверяет новое значение семафора и выполняет следующие действия:

  • В очереди отката отыскиваются любые корректировки данного семафора и эти корректировки сбрасываются в ноль.
  • Значение семафора устанавливается в заданное.
  • Корректируется значение sem_ctime .
  • Вызывается функция update_queue(), которая проходит по очереди ожидающих операций в поисках тех из них, которые могут быть завершены в результате выполнения операции SETVAL. Все задачи которые оказываются больше незаблокированными - пробуждаются.

count_semncnt()

count_semncnt() возвращает число процессов, ожидающих на семафоре, когда тот станет меньше нуля.

count_semzcnt()

count_semzcnt() возвращает число процессов, ожидающих на семафоре, когда тот станет равным нулю.

update_queue()

update_queue() проходит по очереди ожидающих операций заданного набора семафоров и вызывает try_atomic_semop() для каждой последовательности операций. Если статус элемента очереди показывает, что заблокированная задача уже была разбужена, то такой элемент пропускается. В качестве аргумента do_undo в функцию try_atomic_semop() передается флаг q-alter , который указывает на то, что любые изменяющие операции необходимо "откатить" перед возвратом управления.

Если последовательность операций заблокирована, то update_queue() возвращает управление без внесения каких-либо изменений.

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

Если последовательность операций не предполагает внесения изменений, то в качестве аргумента do_undo в функцию try_atomic_semop() передается ноль. Если выполнение этих операций увенчалось успехом, то они считаются выполненными и удаляются из очереди. Ожидающая задача активируется, а в поле status ей передается признак успешного завершения операций.

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

try_atomic_semop()

Функция try_atomic_semop() вызывается из sys_semop() и update_queue() и пытается выполнить каждую из операций в последовательности.

Если была встречена заблокированная операция, то процесс исполнения последовательности прерывается и все операции "откатываются". Если последовательность имела флаг IPC_NOWAIT, то возвращается код ошибки -EAGAIN. Иначе возвращается 1 для индикации того, что последовательность операций заблокирована.

Если значение семафора вышло за рамки системных ограничений, то выполняется "откат" всех операций и возвращается код ошибки -ERANGE.

Если последовательность операций была успешно выполнена и при этом аргумент do_undo не равен нулю, то выполняется "откат" всех операций и возвращается 0. Если аргумент do_undo равен нулю, то результат операций остается в силе и обновляется поле sem_otime .

sem_revalidate()

Функция sem_revalidate() вызывается, когда глобальная блокировка семафора временно была снята и необходимо снова ее получить. Вызывается из semctl_main() и alloc_undo(). Производит проверку ID семафора и права доступа, в случае успеха выполняет глобальную блокировку семафора.

freeundos()

Функция freeundos() проходит по списку "откатов" процесса в поисках заданной структуры. Если таковая найдена, то она изымается из списка и память, занимаемая ею, освобождается. В качестве результата возвращается указатель на следующую структуру "отката"в списке.

alloc_undo()

Вызов функции alloc_undo() должен производиться под установленной глобальной блокировкой семафора. В случае возникновения ошибки - функция завершает работу со снятой блокировкой.

Перед тем как вызовом kmalloc() распределить память под структуру sem_undo и массив корректировок, блокировка снимается. Если память была успешно выделена, то она восстанавливается вызовом sem_revalidate().

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

sem_exit()

Функция sem_exit() вызывается из do_exit() и отвечает за выполнение всех "откатов" по завершении процесса.

Если процесс находится в состоянии ожидания на семафоре, то он удаляется из списка sem_queue при заблокированном семафоре.

Производится просмотр списка "откатов" текущего процесса и для каждого элемента списка выполняются следующие действия:

  • Проверяется структура "отката" и ID набора семафоров.
  • В списке "откатов" соответствующего набора семафоров отыскиваются ссылки на структуры, которые удаляются из списка.
  • К набору семафоров применяются корректировки из структуры "отката".
  • Обновляется поле sem_otime в наборе семафоров.
  • Вызывается update_queue(), которая просматривает список отложенных операций и активирует задачи, которые могут быть разблокированы в результате "отката".
  • Память, занимаемая структурой, освобождается.

По окончании обработки списка очищается поле current->semundo.

Интерфейс системных вызовов

sys_msgget()

sys_msgctl()

IPC_INFO ( или MSG_INFO)

IPC_STAT ( или MSG_STAT)

IPC_SET

IPC_RMID

sys_msgsnd()

sys_msgrcv()

Рассматриваемые в данном разделе средства позволяют процессам взаимодействовать, изменяя значения объектов, называемых семафорами. Значение семафора - это целое число в диапазоне от 0 до 32767. Поскольку во многих приложениях требуется более одного семафора, ОС UNIX предоставляет возможность создавать множества семафоров. Их максимальный размер ограничен системным парамет- ром SEMMSL. Множества семафоров создаются при помощи системного вызова semget.

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

  • Увеличить значение.
  • Уменьшить значение.
  • Дождаться обнуления.

Операции могут снабжаться флагами. Флаг SEM_UNDO означает, что операция выполняется в проверочном режиме, то есть требуется только узнать, можно ли успешно выполнить данную операцию.

При отсутствии флага IPC_NOWAIT системный вызов semop может быть приостановлен до тех пор, пока значение семафора, благодаря действиям другого процесса, не позволит успешно завершить операцию (ликвидация множества семафоров также приведет к завершению системного вызова). Подобные операции называются ``операциями с блокировкой'' . С другой стороны, если обработка завершается неудачей и не указано, что выполнение процесса должно быть приостановлено, операция над семафором называется ``операцией без блокировки'' .

Системный вызов semop оперирует не с отдельным семафором, а с множеством семафоров, применяя к нему ``массив операций''. Массив содержит информацию о том, с какими семафорами нужно оперировать и каким образом. Выполнение массива операций с точки зрения пользовательского процесса является неделимым действием. Это значит, во-первых, что если операции выполняются, то только все вместе и, во-вторых, что другой процесс не может получить доступ к промежуточному состоянию множества семафоров, когда часть операций из массива уже выполнилась, а другая часть еще не успела.

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

Читайте также: