SHMOP(2)

ИМЯ
shmat, shmdt - операции с общей памятью

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

ОПИСАНИЕ
Вызов shmat() подключает сегмент общей памяти с идентификатором shmid к адресному пространству вызывающего процесса. Адрес подключения, указанный в shmaddr, учитывается следующим образом:

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

Если значение shmaddr не равно NULL, а в shmflg указан флаг SHM_RND, то подключение производится по адресу shmaddr, округлённому до ближайшего значения кратного SHMLBA. В противном случае shmaddr должно быть выровнено по адресу страницы, к которому производится подключение.

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

В shmflg может быть указан флаг SHM_REMAP (только в Linux) для обозначения того, что отображение сегмента должно замещать любые существующие отображения в диапазоне, начиная с shmaddr и до размера сегмента (обычно выдается ошибка EINVAL, если в этом диапазоне адресов уже есть отображение). В этом случае значение shmaddr не должно быть равно NULL.

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

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

    Полю shm_atime присваивается значение текущего времени.

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

    Значение shm_nattch увеличивается на 1.

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

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

    Полю shm_dtime присваивается значение текущего времени.

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

    Значение shm_nattch уменьшается на 1. Если оно становится равным 0 и сегмент помечен для удаления, то сегмент удаляется.

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

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении shmat() возвращается адрес подключённого общего сегмента памяти; при ошибке возвращается (void *) -1, а в errno содержится код ошибки. При успешном выполнении shmdt() возвращается 0; при ошибке возвращается -1, а в errno содержится код ошибки.

ОШИБКИ
Значения errno, устанавливаемые при возникновении ошибок в shmat():

EACCES
    Вызывающий процесс не имеет прав для подключения заданного типа и не имеет мандата CAP_IPC_OWNER.

EINVAL
    Неправильное значение shmid, не выровненное (по границе страницы и не указан SHM_RND) или неправильное значение shmaddr, или невозможно подключить сегмент по адресу shmaddr, или был указан SHM_REMAP, но shmaddr равно NULL.

ENOMEM
    Невозможно выделить память для дескриптора или страничных таблиц.

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

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

СООТВЕТСТВИЕ СТАНДАРТАМ
SVr4, POSIX.1-2001. В SVID 3 (или, возможно, более ранних версиях) тип аргумента shmaddr был изменён с char * на const void *, а тип результата shmat() — с char * на void * (в Linux libc4 и libc5 прототипы определены с char *; в glibc2 — void *).

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

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

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

SHMLBA
    Коэффициент округления нижней границы адреса сегмента. Должен быть выровнен со страницей. В настоящее время значение SHMLBA равно PAGE_SIZE.

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

СМОТРИТЕ ТАКЖЕ
brk(2), mmap(2), shmctl(2), shmget(2), capabilities(7), shm_overview(7), svipc(7)