На высоком уровне можно разделить межпроцессное взаимодействие на следующие наиболее крупные и важные разделы:
· Сообщения: каналы (pipes) и
очередисообщений (pipes and message queues)
· Разделяемая память (Shared memory)
· Удаленный вызов процедур - RPC (remote procedure calls)
· Синхронизация: семафоры и любые виды блокирования
· Сетевое взаимодействие (API сокетов)
Из имеющихся типов IPC следующие три могут быть отнесены к
SystemVIPC, то есть к методам взаимодействия процессов, соответствующим стандарту SystemV:
·Очереди сообщений
·Семафоры
·Разделяемая память
Термин «SystemVIPC» говорит о происхождении этих средств: впервые они появились в UnixSystemV. У них много общего, например, схожи функции, с помощью которых организуется доступ к объектам, и и формы хранения информации в ядре.
Каждый объект IPC (очередь сообщений, семафор или сегмент разделяемой памяти) обладает уникальным идентификатором (Id), который позволяет ядру ОС идентифицировать этот объект. К примеру, для того, чтобы сослаться на определенный сегмент разделяемой памяти, необходимо знать уникальный идентификатор, назначенный этому сегменту. Но следует отметить, что идентификатор IPC уникален только для своего типа объектов.
Для генерации идентификатора необходим уникальный ключ. Притом пользовательские приложения должны генерировать свои собственные ключи специальной системной функцией.
Когда процесс пытается обратиться к какому-либо объекту IPC (а именно, семафору, очереди сообщений или сегменту разделяемой памяти), он совершает системный вызов sys_ipc. Под обращением здесь понимается запрос на создание, удаление объекта, управление его параметрами. При этом функции sys_ipc передаётся некий идентификатор, однозначно определяющий, что именно запрашивает процесс.
Таким образом, перехватив данный системный вызов, можно отследить процесс создания, удаления сегментов разделяемой памяти и изменения его параметров.
2.4 Разделяемая память (SharedMemory)
Данный курсового проекта заключается в мониторинге использования только одного из средств IPC, а именно разделяемой памяти, которая может быть наилучшим образом описана как отображение участка (сегмента) памяти, который будет разделён между более чем одним процессом. SharedMemory является наиболее быстрым средством межпроцессного взаимодействия. После отображения области памяти в адресное пространство процессов, совместно её использующих, для передачи данных больше не требуется участия ядра. При использовании этого средства участки виртуального адресного пространства разных процессов отображаются на одни и те же адреса реальной памяти.
При работе с общим сегментом памяти один из процессов должен создать сегмент разделяемой (общей) памяти, указав требуемый размер сегмента, и получить его идентификатор. Этот идентификатор затем используется процессом-создателем для выполнения управляющих операций с разделяемой памятью.
Получив идентификатор разделяемого сегмента памяти (создав сегмент или получив его идентификатор от другого процесса), процесс должен "присоединить" сегмент. Операция присоединения возвращает адрес разделяемого сегмента в виртуальном адресном пространстве процесса. Виртуальный адрес одного и того же сегмента может быть разным для разных процессов. Далее процессы ведут чтение и запись данных в сегменте разделяемой памяти, используя для этого те же самые машинные команды, что и при работе с обычной памятью.
Процесс, закончивший работу с сегментом разделяемой памяти должен "отсоединить" его, а по окончании использования сегмента всеми процессами он должен быть уничтожен.
При одновременной работе процессов с общей областью памяти, возможно, требуется синхронизация их доступа к области. За обеспечение такой синхронизации полностью отвечает программист, который может использовать для этого алгоритмы, исключающие конфликты одновременного доступа или системные средства, например, семафоры.
2.5 Системные вызовы shmget, shmat, shmctl, shmdt
В ядре Linux, все вызовы, имеющие отношение к systemVIPC диспетчеризуются через функцию sys_ipc (размещенную в файле arch/i386/kernel/sys_i386.c). Для идентификации системного вызова, sys_ipcиспользует номер этого вызова.
В ОС Linux механизм разделяемых сегментов памяти обеспечивается четырьмя системными вызовами: shmget, shmctl, shmat, shmdt. В функции sys_ipc они имеют номера SHMGET, SHMAT, SHMCTL, SHMDT соответственно.
Функции intshmget(key_tkey, size_tsize, intflag) позволяет создать новый сегмент разделяемой памяти или подключиться к существующему. Возвращаемое числовое значение является идентификатором сегмента, который используется при дальнейших операциях с ним.
Аргумент key – уникальный ключ, необходимый для создания идентификатора. size указывает требуемый размер сегмента в байтах.
flag представляет собой комбинацию флагов доступа на чтение и запись
После создания или открытия сегмента разделяемой памяти его нужно подключить к адресному пространству процесса вызовом
void * shmat(intshmid, void *shmaddr, intflag), который возвращает адрес начала области разделяемой памяти в адресном пространстве вызвавшего процесса. Аргумент shmid – идентификатор разделяемой памяти, возвращённый shmget. Shmaddr определяет желаемый адрес «привязки» сегмента. Но в большинстве случаев система сама выбирает начальный адрес для вызвавшего процесса. По умолчанию сегмент подключается для чтения и записи, но в аргументе flag модно указать константу SHM_RDONLY, которая позволит установить доступ только на чтение.
После завершения работы с сегментом его следует отключить вызовом int shmdt(void *shmaddr), который получает в качестве аргумента адрес, возвращённый функцией shmat. Следует отдельно отметить, что эта функция не удаляет сегмент разделяемой памяти, а всего лишь снимает «привязку» к процессу.
Функция intshmctl(intshmid, intcmd, structshmid_ds *buff) позволяет выполнять различные операции с сегментом разделяемой памяти. Аргумент cmd может принимать следующие значения:
IPC_RMID – удаление сегмента с идентификатором shmid;
IPC_SET – установка значений полей структуры shmid_ds для сегмента равными значениям соответствующим полей структуры, на которую указывает buff: shm_perm.uid, shm_perm.gid, shm_perm.mode
IPC_STAT – возвращает вызывающему процессу (через аргумент buff) текущее значение структуры shmid_ds для указанного сегмента разделяемой памяти.
Разделяемые сегменты памяти в Unix/Linux (как и семафоры) не имеют внешних имен. При получении идентификатора сегмента процесс пользуется числовым ключом. Разработчики несвязанных процессов могут договориться об общем значении ключа, который они будут использовать, но у них нет гарантии в том, что это же значение ключа не будет использовано кем-то еще. Гарантированно уникальный сегмент можно создать с использованием ключа IPC_PRIVATE, но такой ключ не может быть внешним. Поэтому сегменты используются, как правило, родственными процессами, которые имеют возможность передавать друг другу их идентификаторы, например, через наследуемые ресурсы или через параметры вызова дочерней программы.
Таким образом, для создания монитора разделяемой памяти из таблицы системных вызовов следует получить адрес обработчика sys_ipc(), перехватить его, а затем, зная, какой из вышеописанных вызовов совершает система, считать соответствующие данные.
3. Конструкторская часть
3.1 Технические требования к системе. Перекомпиляция ядра
Для создания и тестирования данной программы использовалась ОС LinuxSUSE 10 (версия ядра 2.6.13-15). Но с целью предотвращения потенциальной опасности, связанной с подменой адресов системных вызовов, ядро, начиная с версии 2.5.41, не экспортирует таблицу sys_call_table, доступ к которой необходим для создания программы мониторинга системных вызовов. Поэтому, чтобы иметь возможность перехватывать системные вызовы, надо наложить следующую «заплату» и затем перекомпилировать ядро.
"Заплата" на ядро (export_sys_call_table_patch_for_linux_2.6.x)
--- kernel/kallsyms.c.orig 2003-12-30 07:07:17.000000000 +0000
+++ kernel/kallsyms.c 2003-12-30 07:43:43.000000000 +0000
@@ -184,7 +184,7 @@
iter->pos = pos;
return get_ksymbol_mod(iter);
}
-
+
/* If we're past the desired position, reset to start. */
if (pos < iter->pos)
reset_iter(iter);
@@ -291,3 +291,11 @@
EXPORT_SYMBOL(kallsyms_lookup);
EXPORT_SYMBOL(__print_symbol);
+/* START OF DIRTY HACK:
+ * Purpose: enable interception of syscalls as shown in the
+ * Linux Kernel Module Programming Guide. */
+extern void *sys_call_table;
+EXPORT_SYMBOL(sys_call_table);
+ /* see http://marc.free.net.ph/message/20030505.081945.fa640369.html
+ * for discussion why this is a BAD THING(tm) and no longer supported by 2.6.0
+ * END OF DIRTY HACK: USE AT YOUR OWN RISK */
Эта заплата протестирована с ядрами 2.6.[0123], и может накладываться или не накладываться на другие версии.
Сценарийналоженияследуюший:
#!/bin/sh
cp export_sys_call_table_patch_for_linux_2.6.x /usr/src/linux/
cd /usr/src/linux/
patch -p0 < export_sys_call_table_patch_for_linux_2.6.x
Но такой способ эффективен только при совпадении «заплатки» с версией ядра. Если «заплата» по каким-то причинам не накладывается, нужно скопировать исходные файлы ядра в отдельную директорию, дописать в файл kernel/kallsyms.cпосле строки
EXPORT_SYMBOL(__print_symbol);
следующее:
extern void *sys_call_table;
EXPORT_SYMBOL(sys_call_table);
После этого открыть Makefile и в строке
EXTRAVERSION =
дописать новое имя ядра.
После этого следует пересобрать ядро, выполнив следующую последовательность действий:
1) Удаления временных файлов и настроек, полученных при возможной предыдущей сборке, командой
# make mrproper
1) Настройка ядра. Осуществляется одной из следущих команд:
# make xconfig
# make menuconfig
# make config
# makeoldconfig
Первый вариант (xconfig) для пользователей, у которых есть графика – запустится графическая программка для настройки, остальные – для консоли. Второй вариант (menuconfig) предлагает текстовые меню для настройки. Третий (config) задает 1000 и 1 вопрос. Четвертый (oldconfig) нужен если уже есть сформированный файл настроек .config (можно использовать файл настроек от старого ядра), при этом варианте задаются только те вопросы которые появились в этой версии ядра.