UNIX: взаимодействие процессов
Шрифт:
Запустив
Причина, по которой в табл. 14.1 приведено значение 128 для числа идентификаторов, а наша программа выводит значение 127, заключается в том, что один сегмент разделяемой памяти всегда используется системным демоном.
14.8. Резюме
Разделяемая память System V похожа на разделяемую память Posix. Наиболее схожи функции:
■ shmget для получения идентификатора;
■ shmat для подключения сегмента разделяемой памяти к адресному пространству процесса;
■ shmctl с командой IPC_STAT для получения размера существующего сегмента разделяемой памяти;
■ shmctl с командой IPC_RMID для удаления объекта разделяемой памяти.
Одно из отличий состоит в том, что размер объекта разделяемой памяти Posix может быть изменен в любой момент вызовом ftruncate (как мы продемонстрировали в упражнении 13.1), тогда как размер объекта разделяемой памяти System V устанавливается изначально вызовом shmget и не может быть изменен.
Упражнение
Листинг 6.6 содержал измененную версию программы из листинга 6.4. Новая программа использовала для обращения к объекту IPC System V идентификатор вместо полного имени. Таким образом мы показали, что для доступа к очереди сообщений System V достаточно знать только ее идентификатор (если у нас имеются соответствующие разрешения). Сделайте аналогичные изменения в программе из листинга 14.4, продемонстрировав, что это верно и для разделяемой памяти System V.
ЧАСТЬ 5
УДАЛЕННЫЙ ВЫЗОВ ПРОЦЕДУР
ГЛАВА 15
Двери
15.1. Введение
Поговорим о схеме клиент-сервер и вызове процедур. Существуют три различных типа вызова процедур, показанные на рис. 15.1.
Рис. 15.1. Три типа вызова процедур
1. Локальный вызов процедуры (local procedure call) знаком нам по обычному программированию на языке С. Вызываемая и вызывающая процедуры (функции) при этом относятся к одному и тому же процессу. При этом обычно выполняется некая команда процессора, передающая управление новой процедуре, а вызвавшая процедура сохраняет значение регистров процессора и выделяет место в стеке Под свои локальные переменные.
2. Удаленный вызов процедуры (remote procedure call — RPC) происходит в ситуации, когда вызвавшая и вызываемая процедуры относятся к разным процессам. В такой ситуации мы обычно называем вызвавшую процедуру клиентом, а вызванную — сервером. Во втором сценарии на рис. 15.1 клиент и сервер выполняются на одном и том же узле. Это типичный частный случай третьего сценария, и это именно то, что осуществляется с помощью дверей (doors). Итак, двери дают возможность вызывать процедуру (функцию) другого процесса на том же узле. Один из процессов (сервер) делает процедуру, находящуюся внутри него, доступной для вызова другим процессам (клиентам),
создавая для этой процедуры дверь. Мы можем считать двери специальным типом IPC, поскольку при этом между процессами (клиентом и сервером) передается информация в форме аргументов функции и возвращаемых значений.3. RPC в общем случае дает возможность клиенту на одном узле вызвать процедуру сервера на другом узле, если эти два узла связаны каким-либо образом по сети (третий сценарий на рис. 15.1). Такой вид взаимодействия будет описан в главе 16.
ПРИМЕЧАНИЕ
Впервые двери были разработаны для распределенной операционной системы Spring. Детали этого проекта доступны по адресуОписание механизма дверей в этой операционной системе можно найти в книге [7].
Затем двери появились в версии Solaris 2.5, хотя единственная страница документации, к ним относящаяся, содержала только предупреждение о том, что двери являются экспериментальным интерфейсом, используемым отдельными приложениями Sun. В Solaris 2.6 описание этого интерфейса занимает уже 8 страниц, но в них он характеризуется как «развивающийся». В будущих версиях Solaris 2.6 описываемый в этой главе интерфейс API может быть изменен. Предварительная версия дверей для Linux уже разрабатывается, детали можно выяснить по адресу http://www.cs.brown.edu/~tor/doors.
Чтобы воспользоваться интерфейсом дверей в Solaris 2.6, нужно подключить соответствующую библиотеку (-ldoor), содержащую функции door_XXX, описываемые в этой главе, и использовать файловую систему ядра (/kernel/sys/doorfs).
Хотя двери поддерживаются только в системе Solaris, мы описываем их достаточно подробно, поскольку это описание позволяет подготовить читателя к удаленному вызову процедур без необходимости обсуждать какие-либо детали сетевого интерфейса. В приложении А мы увидим, что этот интерфейс достаточно быстр — едва ли не быстрее, чем все остальные средства передачи сообщений.
Локальные вызовы процедур являются синхронными (synchronous): вызывающий процесс не получает управление до тех пор, пока не происходит возврат из вызванной процедуры. Потоки могут восприниматься как средство асинхронного вызова процедур: функция (указанная в третьем аргументе pthread_create) выполняется одновременно с вызвавшим процессом. Вызвавший процесс может ожидать завершения вызванного процесса с помощью функции pthread_join. Удаленный вызов процедур может быть как синхронным, так и асинхронным, но мы увидим, что вызовы через двери являются синхронными.
Внутри процесса двери идентифицируются дескрипторами. Извне двери могут идентифицироваться именами в файловой системе. Сервер создает дверь вызовом door_create; аргументом этой функции является указатель на процедуру, которая будет связана с данной дверью, а возвращаемое значение является дескриптором двери. Затем сервер связывает полное имя файла с дескриптором двери с помощью функции fattach. Клиент открывает дверь вызовом open, при этом аргументом функции является полное имя файла, которое сервер связал с дверью, а возвращаемым значением — дескриптор, который будет использоваться клиентом для доступа к двери. Затем клиент может вызывать процедуру с помощью door_call. Естественно, программа, являющаяся сервером для некоторой двери, может являться клиентом для другой.
Мы уже сказали, что вызовы через двери являются синхронными: когда клиент вызывает door_call, возврата из этой функции не происходит до тех пор, пока процедура на сервере не завершит работу (возможно, с ошибкой). Реализация дверей в Solaris связана с потоками. Каждый раз, когда клиент вызывает процедуру сервера, для обработки этого вызова создается новый поток в процессе-сервере. Работа с потоками обычно осуществляется автоматически функциями библиотеки дверей, при этом потоки создаются по мере необходимости, но мы увидим, что сервер может и сам управлять этими потоками, если это требуется. Это также означает, что одна и та же процедура может выполняться сервером для нескольких клиентов, причем для каждого из них будет создан отдельный поток. Такой сервер является параллельным. Поскольку одновременно могут выполняться несколько экземпляров процедуры сервера (каждая из которых представляет собой отдельный поток), содержимое этих процедур должно соответствовать определенным требованиям, которые обычно предъявляются к многопоточным программам.