Команда ipcs в linux

Обновлено: 05.07.2024

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

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

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

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

Оптимизируем работу ядра на серверах с большим размером ОЗУ в ситуациях повышенной сетевой и дисковой нагрузки.

В моем примере рассмотрен сервер с 128Gb ОЗУ и 10Gb подключением.


Шаг 1. Выполним команду ipcs -l для отображения текущих параметров параметров ядра:

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

IPC kernel parameter Enforced minimum setting
kernel.shmmni (SHMMNI) 256 * (size of RAM in GB)
kernel.shmmax (SHMMAX) (size of RAM in bytes)
kernel.shmall (SHMALL) 2 * (size of RAM in the default system page size)
kernel.sem (SEMMNI) 256 * (size of RAM in GB)
kernel.sem (SEMMSL) 250
kernel.sem (SEMMNS) 256 000
kernel.sem (SEMOPM) 32
kernel.msgmni (MSGMNI) 1 024 * (size of RAM in GB)
kernel.msgmax (MSGMAX) 65 536
kernel.msgmnb (MSGMNB) 65 536

SHMALL ограничивает общий объем виртуальной совместной памяти, которая может быть выделена системе. Каждый сервер данных эффективно управляет тем объемом системной памяти, который он использует; ее называют также переданная память (committed memory). Сервер данных выделяет больше виртуальной памяти, чем ему передано, чтобы поддерживать предварительное выделение памяти и динамическое управление памятью. Предварительное выделение памяти повышает производительность. Динамическое управление памятью - это процесс увеличения и сокращения действительного использования памяти в отдельных областях виртуальной памяти совместного использования. Для поддержки предварительного выделения памяти и динамического управления памятью серверам данных часто необходимо выделять в системе больший объем виртуальной памяти совместного использования, чем физический объем оперативной памяти. Но для ядра это значение задается как число страниц.

В первом разделе, Shared Memory Limits (Предельные значения для совместно используемой памяти), предельное значение SHMMAX - это максимальный размер сегмента совместно используемой памяти в системе Linux. Предельное значение SHMALL - это максимальное выделение страниц совместно используемой памяти в системе.

Рекомендуется задать для SHMMAX значение, численно равное объему физической памяти в системе. Однако минимально необходимое в системах x86 значение равно 268435456 (256 Мбайт), а в 64-битных системах - 1073741824 (1 Гбайт).

Следующий раздел описывает количество семафоров, доступных для операционной системы. Параметр ядра sem состоит из четырех элементов: SEMMSL, SEMMNS, SEMOPM и SEMMNI. Значение SEMMNS равно произведению SEMMSL на SEMMNI. Для менеджера данных требуется соответственным образом увеличить число массивов (SEMMNI). Обычно значение SEMMNI должно быть вдвое больше максимального разрешенного числа ожидаемых в системе агентов, умноженного на число логических разделов на компьютере сервера данных плюс число соединений локальных программ с компьютером сервера данных.

Параметры :

Примеры по команде IPCS:

1: перечислить все средства IPC



Все средства IPC имеют уникальный ключ и идентификатор, который используется для идентификации средства IPC.



3. Перечислить все Семафоры



4. Перечислить всю общую память



5. Получить подробную информацию об объекте IPC


6. Перечислить лимиты для средств IPC




7. Перечислите информацию о Создателе и Владельце для МПК.


8. Чтобы получить идентификаторы процессов, которые недавно обращались к средству IPC


9. Чтобы получить последний доступ к времени



10. Получить статус текущего использования


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

НАЗВАНИЕ
ipcs - выдача информации о состоянии средств межпроцессной связи

-c Выводить входное и групповое имя создателя.

-a Использовать все опции, выводящие информацию. (Это просто краткая запись для -bcopt).

-C образ_памяти Использовать файл образ_памяти вместо файла /dev/kmem.

-N файл_с_таблицей_имен Использовать файл_с_таблицей_имен вместо подразумеваемого файла /unix.

Ниже раз ясняется смысл информации в каждой колонке выдачи. Буквы в скобках после заголовка колонки обозначают опции, вызывающие появление данной колонки; отсутствие букв означает, что колонка присутствует всегда. Заметим, что данные опции определяют лишь вид информации, предоставляемой для каждого средства связи между процессами, а не само средство. T Тип средства связи:

KEY Ключ, использованный в качестве аргумента функций msgget, semget или shmget при создании элемента. (Замечание: при удалении сегмента разделяемой памяти ключ сегмента изменяется на IPC_PRIVATE до тех пор, пока все присоединенные процессы не отсоединят его.)

MODE Режимы доступа и флаги элемента средства связи. Режим состоит из 11 символов, интерпретируемых следующим образом.

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

GROUP Групповое имя владельца элемента средства связи.

CGROUP (a,c) Групповое имя создателя элемента средства связи.

CTIME (a,t) Время, когда соответствующий элемент был создан или изменен.

NATTCH (a,o) Количество процессов, присоединенных к соответствующему разделяемому сегменту памяти.

SEGSZ (a,b) Размер разделяемого сегмента памяти.

CPID (a,p) Идентификатор процесса, создавшего разделяемый сегмент памяти.

LPID (a,p) Идентификатор последнего процесса, присоединившего или отсоединившего разделяемый сегмент памяти.

ATIME (a,t) Время, когда было завершено последнее присоединение к разделяемому сегменту памяти.

DTIME (a,t) Время, когда было завершено последнее отсоединение разделяемого сегмента памяти.

NSEMS (a,b) Число семафоров в множестве, связанном с данным элементом.

OTIME (a,t) Время завершения последней семафорной операции с множеством, связанным с данным элементом.

Отступление: данная статья является учебной и расчитана на людей, только еще вступающих на путь системного программирования. Ее главный замысел — познакомиться с различными способами взаимодействия между процессами на POSIX-совместимой ОС.

Именованный канал

Примечание: mode используется в сочетании с текущим значением umask следующим образом: (mode &

umask). Результатом этой операции и будет новое значение umask для создаваемого нами файла. По этой причине мы используем 0777 (S_IRWXO | S_IRWXG | S_IRWXU), чтобы не затирать ни один бит текущей маски.

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

В случае успешного создания FIFO файла, mkfifo() возвращает 0 (нуль). В случае каких либо ошибок, функция возвращает -1 и выставляет код ошибки в переменную errno.

  • EACCES — нет прав на запуск (execute) в одной из директорий в пути pathname
  • EEXIST — файл pathname уже существует, даже если файл — символическая ссылка
  • ENOENT — не существует какой-либо директории, упомянутой в pathname, либо является битой ссылкой
  • ENOSPC — нет места для создания нового файла
  • ENOTDIR — одна из директорий, упомянутых в pathname, на самом деле не является таковой
  • EROFS — попытка создать FIFO файл на файловой системе «только-на-чтение»

Пример

mkfifo.c

Мы открываем файл только для чтения (O_RDONLY). И могли бы использовать O_NONBLOCK модификатор, предназначенный специально для FIFO файлов, чтобы не ждать когда с другой стороны файл откроют для записи. Но в приведенном коде такой способ неудобен.

Компилируем программу, затем запускаем ее:

В соседнем терминальном окне выполняем:

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

Разделяемая память


Следующий тип межпроцессного взаимодействия — разделяемая память (shared memory). Схематично изобразим ее как некую именованную область в памяти, к которой обращаются одновременно два процесса:

Для выделения разделяемой памяти будем использовать POSIX функцию shm_open():

Функция возвращает файловый дескриптор, который связан с объектом памяти. Этот дескриптор в дальнейшем можно использовать другими функциями (к примеру, mmap() или mprotect()).

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

  • O_RDONLY — открыть только с правами на чтение
  • O_RDWR — открыть с правами на чтение и запись
  • O_CREAT — если объект уже существует, то от флага никакого эффекта. Иначе, объект создается и для него выставляются права доступа в соответствии с mode.
  • O_EXCL — установка этого флага в сочетании с O_CREATE приведет к возврату функцией shm_open ошибки, если сегмент общей памяти уже существует.

После создания общего объекта памяти, мы задаем размер разделяемой памяти вызовом ftruncate(). На входе у функции файловый дескриптор нашего объекта и необходимый нам размер.

Пример

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

shm_open.c

После создания объекта памяти мы установили нужный нам размер shared memory вызовом ftruncate(). Затем мы получили доступ к разделяемой памяти при помощи mmap(). (Вообще говоря, даже с помощью самого вызова mmap() можно создать разделяемую память. Но отличие вызова shm_open() в том, что память будет оставаться выделенной до момента удаления или перезагрузки компьютера.)

Компилировать код на этот раз нужно с опцией -lrt:

Смотрим что получилось:

Аргумент «create» в нашей программе мы используем как для создания разделенной памяти, так и для изменения ее содержимого.

Зная имя объекта памяти, мы можем менять содержимое разделяемой памяти. Но стоит нам вызвать shm_unlink(), как память перестает быть нам доступна и shm_open() без параметра O_CREATE возвращает ошибку «No such file or directory».

Семафор

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

  1. семафор со счетчиком (counting semaphore), определяющий лимит ресурсов для процессов, получающих доступ к ним
  2. бинарный семафор (binary semaphore), имеющий два состояния «0» или «1» (чаще: «занят» или «не занят»)

Семафор со счетчиком

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

Итак, для реализации семафоров будем использовать POSIX функцию sem_open():

В функцию для создания семафора мы передаем имя семафора, построенное по определенным правилам и управляющие флаги. Таким образом у нас получится именованный семафор.
Имя семафора строится следующим образом: в начале идет символ "/" (косая черта), а следом латинские символы. Символ «косая черта» при этом больше не должен применяться. Длина имени семафора может быть вплоть до 251 знака.

Если нам необходимо создать семафор, то передается управляющий флаг O_CREATE. Чтобы начать использовать уже существующий семафор, то oflag равняется нулю. Если вместе с флагом O_CREATE передать флаг O_EXCL, то функция sem_open() вернет ошибку, в случае если семафор с указанным именем уже существует.

Параметр mode задает права доступа таким же образом, как это объяснено в предыдущих главах. А переменной value инициализируется начальное значение семафора. Оба параметра mode и value игнорируются в случае, когда семафор с указанным именем уже существует, а sem_open() вызван вместе с флагом O_CREATE.

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

Пример семафора со счетчиком

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

sem_open.c

В одной консоли запускаем:

В соседней консоли запускаем:

Бинарный семафор

Вместо бинарного семафора, для которого так же используется функция sem_open, я рассмотрю гораздо чаще употребляемый семафор, называемый «мьютекс» (mutex).

Мьютекс по существу является тем же самым, чем является бинарный семафор (т.е. семафор с двумя состояниями: «занят» и «не занят»). Но термин «mutex» чаще используется чтобы описать схему, которая предохраняет два процесса от одновременного использования общих данных/переменных. В то время как термин «бинарный семафор» чаще употребляется для описания конструкции, которая ограничивает доступ к одному ресурсу. То есть бинарный семафор используют там, где один процесс «занимает» семафор, а другой его «освобождает». В то время как мьютекс освобождается тем же процессом/потоком, который занял его.

Без мьютекса не обойтись в написании, к примеру базы данных, к которой доступ могут иметь множество клиентов.

Для использования мьютекса необходимо вызвать функцию pthread_mutex_init():

Функция инициализирует мьютекс (перемнную mutex) аттрибутом mutexattr. Если mutexattr равен NULL, то мьютекс инициализируется значением по умолчанию. В случае успешного выполнения функции (код возрата 0), мьютекс считается инициализированным и «свободным».

  • EAGAIN — недостаточно необходимых ресурсов (кроме памяти) для инициализации мьютекса
  • ENOMEM — недостаточно памяти
  • EPERM — нет прав для выполнения операции
  • EBUSY — попытка инициализировать мьютекс, который уже был инициализирован, но не унечтожен
  • EINVAL — значение mutexattr не валидно

Функция pthread_mutex_lock(), если mutex еще не занят, то занимает его, становится его обладателем и сразу же выходит. Если мьютекс занят, то блокирует дальнейшее выполнение процесса и ждет освобождения мьютекса.
Функция pthread_mutex_trylock() идентична по поведению функции pthread_mutex_lock(), с одним исключением — она не блокирует процесс, если mutex занят, а возвращает EBUSY код.
Фунция pthread_mutex_unlock() освобождает занятый мьютекс.

  • EINVAL — mutex неправильно инициализирован
  • EDEADLK — мьютекс уже занят текущим процессом
  • EBUSY — мьютекс уже занят
  • EINVAL — мьютекс неправильно инициализирован
  • EINVAL — мьютекс неправильно инициализирован
  • EPERM — вызывающий процесс не является обладателем мьютекса

Пример mutex

mutex.c

Данный пример демонстрирует совместный доступ двух потоков к общей переменной. Один поток (первый поток) в автоматическом режиме постоянно увеличивает переменную counter на единицу, при этом занимая эту переменную на целую секунду. Этот первый поток дает второму доступ к переменной count только на 10 миллисекунд, затем снова занимает ее на секунду. Во втором потоке предлагается ввести новое значение для переменной с терминала.

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

Компилировать код нужно с дополнительным параметром -lpthread:

Запускаем и меняем значение переменной просто вводя новое значение в терминальном окне:

Вместо заключения

В следующих статьях я хочу рассмотреть технологии d-bus и RPC. Если есть интерес, дайте знать.
Спасибо.

UPD: Обновил 3-ю главу про семафоры. Добавил подглаву про мьютекс.

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