shmat(2) операции с общей памятью System V

Other Alias

shmdt

ОБЗОР

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);

ОПИСАНИЕ

shmat()

Вызов shmat() подключает сегмент общей памяти System V с идентификатором shmid к адресному пространству вызывающего процесса. Адрес подключения, указанный в shmaddr, учитывается следующим образом:
*
Если значение shmaddr равно NULL, то система выбирает подходящий (неиспользуемый) адрес для подключения сегмента.
*
Если значение shmaddr не равно NULL, а в shmflg указан флаг SHM_RND, то подключение производится по адресу shmaddr, округлённому до ближайшего значения кратного SHMLBA.
*
В противном случае shmaddr должно быть выровнено по адресу страницы, к которому производится подключение.

В дополнении к SHM_RND, в аргументе битовой маски shmflg могут быть указаны следующие флаги:

SHM_EXEC (есть только в Linux; начиная с Linux 2.6.9)
Разрешить выполнение содержимого сегмента. Вызывающий должен иметь права на выполнение сегмента.
SHM_RDONLY
Разрешить доступ к сегменту только для чтения. Процесс должен иметь права на чтение сегмента. Если этот флаг не указан, то сегмент подключается с правом чтения и записи, и процесс должен иметь права на чтение и запись сегмента. Об общих сегментах памяти, доступных только на запись, ничего не упоминается.
SHM_REMAP (есть только в Linux)
Этот флаг указывает, что отображение сегмента должно замещать любые существующие отображения в диапазоне, начиная с shmaddr и до размера сегмента (обычно выдается ошибка EINVAL, если в этом диапазоне адресов уже есть отображение). В этом случае значение shmaddr не должно быть равно NULL.

Значение brk(2) вызывающего процесса от подключения не изменяется. При завершении работы процесса сегмент будет автоматически отсоединён. Один и тот же сегмент может быть подключён в адресное пространство процесса несколько раз, как только для чтения, так и для чтения-записи.

При успешном выполнении системный вызов shmat() обновляет поля структуры shmid_ds (см. shmctl(2)), связанной с общим сегментом памяти, следующим образом:

Полю shm_atime присваивается значение текущего времени.
Значение shm_lpid устанавливается равным идентификатору вызывающего процесса.
Значение shm_nattch увеличивается на 1.

shmdt()

Вызов shmdt() отключает сегмент общей памяти, находящийся по адресу shmaddr, от адресного пространства вызывающего процесса. Отключаемый сегмент должен быть подключён по адресу shmaddr с помощью вызова shmat().

При успешном выполнении вызов shmdt() обновляет поля структуры shmid_ds, связанной с общим сегментом памяти, следующим образом:

Полю shm_dtime присваивается значение текущего времени.
Значение shm_lpid устанавливается равным идентификатору вызывающего процесса.
Значение shm_nattch уменьшается на 1. Если оно становится равным 0 и сегмент помечен для удаления, то сегмент удаляется.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении shmat() возвращается адрес подключённого общего сегмента памяти; при ошибке возвращается (void *) -1, а в errno содержится код ошибки.

При успешном выполнении shmdt() возвращается 0; при ошибке возвращается -1, а в errno содержится код ошибки.

ОШИБКИ

Значения errno, устанавливаемые при возникновении ошибок в shmat():
EACCES
Вызывающий процесс не имеет прав для подключения заданного типа и не имеет мандата CAP_IPC_OWNER.
EIDRM
Значение shmid указывает на удалённый идентификатор.
EINVAL
Неправильное значение shmid, не выровненное (по границе страницы и не указан SHM_RND) или неправильное значение shmaddr, или невозможно подключить сегмент по адресу shmaddr, или был указан SHM_REMAP, но shmaddr равно NULL.
ENOMEM
Невозможно выделить память для дескриптора или страничных таблиц.

Значения errno, устанавливаемые при возникновении ошибок в shmdt():

EINVAL
По адресу shmaddr подключённый общий сегмент памяти отсутствует; или значение shmaddr не выровнено по границе страницы.

СООТВЕТСТВИЕ СТАНДАРТАМ

POSIX.1-2001, POSIX.1-2008, SVr4.

В SVID 3 (возможно, чуть раньше) тип аргумента shmaddr изменён с char * на const void *, а тип возвращаемого значения shmat() с char * на void *.

ЗАМЕЧАНИЯ

При вызове fork(2) потомки наследуют подключённые общие сегменты памяти.

При вызове execve(2) все подключённые общие сегменты памяти отключаются.

При вызове _exit(2) все подключённые общие сегменты памяти отключаются.

Для улучшения переносимости программ при подключении общего сегмента памяти рекомендуется использовать shmat() с аргументом shmaddr, установленным в NULL. Необходимо учитывать, что сегмент памяти, подключаемый таким способом, в разных процессах может подключаться по разным адресам. Поэтому все указатели в области общей памяти должны быть не абсолютными, а относительными (как правило относительно адреса начала сегмента).

В Linux сегмент общей памяти можно подключить даже, если он помечен для удаление. Однако в POSIX.1 об этом ничего не сказано, и многие другие реализации это не поддерживают.

На работу shmat() влияют следующие системные параметры:

SHMLBA
Кратность адреса нижней границы сегмента. При явном указании в вызове shmat() подключаемого адреса вызывающий должен убедиться, что адрес кратен этому значению. Это необходимо у некоторых архитектур, чтобы точно получить хорошую производительность от кэша ЦП или чтобы различные подключения одного сегмента имели целостный вид внутри кэша ЦП. Параметр SHMLBA, обычно, кратен нескольким размерам системной страницы (у многих архитектур Linux он совпадает с размером системной страницы).

Реализацией не ограничивается количество общих сегментов общей памяти на процесс (SHMSEG).