DIPC обеспечивает строгую совместимость, т. е. при чтении будет возвращаться последнее записанное значение. Это явление хорошо знакомо программистам. В одно и то же время может быть большое количество читателей разделяемой памяти, но только один писатель. Менеджер разделяемой памяти, выполняющийся на владельце разделяемой памяти, будет получать запросы о чтении или записи целыми сегментами или индивидуальными страницами. Он будет решать, кто получит право на чтение или запись и при необходимости предоставлять запрашивающей машине адекватное содержимое разделяемой памяти.
Поэтому следует всегда помнить, что процессы будут только претендовать на разделяемую память. Они никого не ставят в известность о завершении своего доступа. Значит, нет возможности ожидать завершения использования ими разделяемой памяти для того, чтобы разрешить доступ другим процессам.
Чтобы защитить страницы разделяемой памяти от записи, нужно подвергнуть изменениям таблицы MMU. Аналогично делается и защита страниц от записи (в версиях DIPC до 2.0 желательно подгрузить страницу свопа для того, чтобы защитить ее от записи). Любой процесс, при попытке прочитать или записать защищенные от чтения страницы, столкнется со страничной ошибкой и будет приостановлен ядром. Для того чтобы страницы снова стали доступными для чтения или записи, новое содержимое передается по сети и замещает старое. С этого момента пользовательские процессы могут обращаться к нему.
DIPC может воспринимать в связке множество страниц виртуальной памяти, одновременно управляя ими всеми и передавая требующиеся. Это значит, что для любого целого n >= 1:
n * <размер_страницы_виртуальной_памяти>.
Установка большего размера страницы DIPC будет означать меньшие затраты при пересылках, а также создаст возможность для компьютеров с различными исходными размерами страниц совместно работать, используя DIPC. Значение должно устанавливаться исходя из максимального размера страниц виртуальной памяти компьютеров кластера.
Писатели имеют более высокий приоритет, чем читатели. Ситуация могла бы быть обратной, но философия реализации требует обновления информации писателями в то время, когда ей пользуются только читатели. Так что, писатель будет получать доступ к разделяемой информации даже тогда, когда с ней работают другие, ``желающие быть читателями'', процессы.
Для информирования процессов о том, что они становятся писателями или читателями используются два сигнала - процессы могут выполнять любые необходимые преобразования данных в гетерогенной среде. В настоящее время при записи используется сигнал SIGURG, а при чтении - SIGPWR. В дальнейшем возможны изменения, если этим сигналам будет найдено иное применение. Они приемлемы для программ с именами DIPC_SIG_READER и DIPC_SIG_WRITER.
Все процессы на одной машине имеют одинаковый статус в отношении разделяемой памяти. Либо они все могут читать ее или записывать в нее, либо никто из них не может этого делать. Когда тип доступа машины к разделяемой памяти меняется, это затрагивает все процессы на данной машине.
Система DIPC может быть сконфигурирована для управления разделяемой памятью и ее постраничной передачей. Это так называемый режим страничной передачи. Он позволяет различным компьютерам в кластере одновременно читать или записывать различные страницы разделяемой памяти. DIPC также может воспринимать целый сегмент как неделимый блок. В этом случае можно сказать, что она оперирует в режиме посегментной передачи. Каждый участник кластера DIPC, сконфигурированный для использования собственных режимов передачи, может работать с любым другим, хотя он не всегда может получить то, что просит. Ниже показано, как два компьютера с различными режимами передачи управляют работой друг друга:
Следствия разрешения пересылок целыми сегментами при DIPC:
Компьютер-владелец разделяемой памяти - это ее первостепенный писатель, которого всегда ``окружают'' читатели (если имеется хотя бы один читатель). Последовательность действий компьютера-владельца, всегда присутствующего среди читателей разделяемой памяти, примерно такая. Владелец всегда запускается как писатель, а когда возникает запрос о чтении, он конвертируется в читателя. Если процесс на другом компьютере "желает" записать данные в разделяемую память, то владелец не сможет иметь никаких прав доступа. Как только возникает запрос о чтении, менеджер разделяемой памяти размещает его перед изначальным запросом о чтении - от имени машины-владельца. В этом случае, владелец сначала становится читателем, забирающим содержимое разделяемой памяти от текущего писателя (см. ниже). Затем он предоставляет содержимое изначальному читателю (и, возможно другим ``просителям'').
Описанное поведение упрощает алгоритмы: поскольку всегда известно, как забрать содержимое разделяемой памяти, то выбирают дополнительно к существующим читателям нового. Но если надо использовать одного читателя из группы, то следует обеспечить и способы разрешения сетевых трудностей, чтобы этот пользователь не потерпел неудачи.
Короче говоря, в текущей версии DIPC лишь одна машина всегда ответственна за предоставление другим компьютерам содержимого разделяемой памяти: если это писатели, то и машина является писателем. Если присутствуют один или более читателей, то машина является владельцем. В обоих случаях, если встретилась ошибка, по большому счету ничего нельзя сделать. Если писатель не может передать содержимое, то места с обновленными данными не может быть. И если владелец терпит неудачу при выдаче запрашивающей машине данных - либо из-за ошибок сети, либо ошибок запрашивающей машины - то опять можно сделать очень немного, потому что использование отдельного читателя, возможно, завершится с тем же результатом.
Как при запросе о чтении, так и при запросе о записи, запрашивающий компьютер (если находится на связи) в конечном счете будет искать причину ошибки посредством тайм-аута и передавать SIGSEGV (сбой сегментации) всем процессам, которые имеют распределенную память, присоединенную к их адресному пространству.
Возможны следующие четыре варианта запроса машинами доступа к разделяемой памяти:
На схеме показано, как это происходит:
Сеть
(Запрашивающий компьютер) | (Компьютер-владелец)
|
|-1->back_end >-2-+ | <----------5----+ |
| | | | | |
ядро| employer--3--|->front_end --4-->shm_man |ядро
| | | |
| | +--6-->worker |
Когда процесс "желает" прочесть разделяемую память, уместное содержимое которой отсутствует, возникает исключение, о чем уведомляется часть DIPC в ядре. Внутри ядра подготавливается запрос, который для ``пробуждения'' порождает back_end (1) и раздваивает employer (2); employer подключается к компьютеру-владельцу (3) и доставляет запрос о чтении соответствующего содержимого разделяемой памяти процессу shm_man (4); shm_man посылает сообщение front_end (используя ``разворачивающий'' Internet-сокет) (5); front_end раздваивает worker (6) для действительного выполнения пересылки.
Процесс shm_man никогда не передает данные сам, а использует для этого worker на одной из отдельных машин. Для того, чтобы соблюдать общность, он делает то же самое даже на машине, на которой запущен. Дальнейшая последовательность операций показана на следующей схеме:
Сеть
(Запрашивающий компьютер)|(Компьютер-владелец)
|
| | |
| | |
ядро | +-8-<front_end<---|-------7---worker | ядро
| | | | | |
|<-11-worker<-------------|--10--------+ +-9--<|
Итак, worker на машине-владельце ``отдает'' разделяемую память (с помощью системного вызова shmget()) и подключается к front_end на запрашивающей машине (7), который раздваивает еще один worker (8). Затем worker на машине-владельце читает соответствующее содержимое разделяемой памяти (9) и сразу передает его (без вмешательства front_end) вновь раздвоенному worker (10), который помещает его в разделяемую память запрашивающего компьютера (11). После этого, запрашивающая машина наконец получает информацию, которую хотела.
Ниже представлены оставшиеся действия:
Сеть
(Запрашивающий компьютер) | (Компьютер-владелец)
|
| worker--------12-----|------------->front_end
| | |
| | |
ядро| front_end <----|--14--shm_man <-13-+
| | |
|<-16-employer<-15-+
После того, как worker поместил содержимое в разделяемую память, он посылает подтверждение shm_man (12 и 13). Затем shm_man посылает сигнальное сообщение оригинальному employer, который передает запрос о чтении (14 и 15); employer сообщает ядру об этом (16) и ядро перезапускает процессы, "желающие" читать разделяемую память. На этом вся операция завершается.
Сеть
(Запрашивающий компьютер) | (Компьютер-владелец)
|
|-1->back_end>-2-+ | |
| | | |
ядро| employer--3----|->front_end--4-->shm_man|ядро
| | |
Запрос о записи попадает внутрь ядра, и back_end активируется для его обработки (1). В дальнейшем об этом информируется shm_man на машине-владельце (2, 3 и 4). Заметьте, что все сказанное может происходить на одном компьютере (если владелец хочет записывать в разделяемую память). Иногда все показанные процессы, действительно, выполняются на одной машине:
Сеть
(Компьютер-читатель) | (Компьютер-владелец)
| |
ядро |<-7-worker<-6-front_end<--|------------5-----shm_man
| |
Менеджер разделяемой памяти посылает защищенное от чтения сообщение всем компьютерам-читателям (среди которых может быть и машина-владелец - в этом случае используется разворачивающий адрес) (5); front_end на каждой машине раздваивает worker (6) и запрос исполняется (7). С этого момента все попытки чтения или записи соответствующего содержимого разделяемой памяти любым из этих компьютеров приведут к остановке ответственных процессов. Указанные выше действия повторяются для каждого читателя.
Если компьютер, запрашивающий возможность записи, был одним из читателей, ему содержимое не посылается: владелец ответственен за предоставление новому ``просителю'' содержимого разделяемой памяти.
Далее shm_man ожидает подтверждение, и когда оно прибывает, посылает сигнал employer, который изначально инициировал все процессы.
Если shm_man ``включает'' владельца, как ``желающую читать'' машину, происходит следующее:
Сеть
(Компьютер-писатель) | (Компьютер-владелец)
| |
ядро | worker <-6- front_end <--|------------5-----shm_man
| |
Владелец будет запрашивать у действующего компьютера - писателя о передаче ему соответствующего содержимого разделяемой памяти. Это реализуется посылкой запроса процессу front_end писателя (5), который в свою очередь разветвит worker для выполнения пересылки (6).
Соответствующее содержимое разделяемой памяти передается компьютеру - владельцу (компьютер - владелец заменяется компьютером - писателем, а запрашивающий компьютер - владельцем).
Когда владелец становится читателем разделяемой памяти, он обслуживает оригинальный запрос о чтении. Данная ситуация похожа на ситуацию, описанную в первом пункте.
Сначала shm_man уведомляется о запросе. Затем он пошлет сообщение текущему писателю защитить от записи соответствующие части разделяемой памяти и передаст содержимое новому писателю.