47678 (Мониторинг виртуальной памяти в ОС Linux), страница 2

2016-07-30СтудИзба

Описание файла

Документ из архива "Мониторинг виртуальной памяти в ОС Linux", который расположен в категории "". Всё это находится в предмете "информатика" из 1 семестр, которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "курсовые/домашние работы", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "47678"

Текст 2 страницы из документа "47678"

Для перехвата системных вызовов имеются 3 таблицы указателей – оригинальных (системных) обработчиков, наших пре-обработчиков (вызываемых ДО оригинального, и принимающих аргументы) и пост-обработчиков (вызываемых ПОСЛЕ и принимающих возвращаемое значение):

void *old_sys_call [NR_syscalls];

void *sys_call_trap [NR_syscalls];

void *sys_call_exit [NR_syscalls];

Кроме того, есть общая таблица структур, каждый элемент которой описывает один из перехватываемых системных вызовов:

struct syscall_handler

{

/* Syscall nr */

int nr;

/* Pre-call & post-call handler */

void *hand1, *hand2;

};

Функция capture_syscalls(), вызываемая при инициализации драйвера, копирует эти адреса в предыдущие 2 таблицы и записывает в sys_call_table по нужным номерам адрес универсального перехватчика – syscalls_entry, находящегося в файле syscalls-entry.

Необходимость в ассемблерном коде обусловлена механизмом обработки системных вызовов в Linux. На рис. 3 показан стек на момент вызова обработчика системного вызова (замененного или стандартного). Проблема заключается в том, что некоторые стандартные обработчики требуют, чтобы стек имел именно такой вид, и если вызывать их из нового обработчика, они правильно работать не будут. В связи с этим syscalls_entry сначала вызывает пре-обработчик системного вызова, затем заменяет в стеке адрес возврата на адрес следующей инструкции и делает переход на стандартный обработчик, который получает кадр стека в изначальном виде. Затем, при возврате, мы попадаем на следующую инструкцию, которая вызывает пост-обработчик и делает перход на изначальный адрес возврата, на код в arch/i386/kernel/entry.S (точки входа всех системных вызовов в Linux). Этот адрес сохраняется внизу стека ядра, там же где хранится указатель на текущую задачу и некоторая другая служебная информация ядра. Данные действия продемонстрированы на рис. 3.3–3.5.

Данный драйвер перехватывает следующие системные вызовы:

m[un] map() – выделение / освобождение региона памяти

mremap() – перемещение региона памяти

brk() – расширение / сужение сегмента данных программы

m[un] lock[all]() – блокировка набора страниц в рабочем множестве процесса

fsync() – используется в качестве маркера в журнале событий

Для каждого из вызовов в журнал печатается имя вызова, PID вызвавшего процесса и список (с расшифровкой, там, где это имеет смысл) аргументов.

    1. Кольцевой буфер событий

Для хранения журнала событий, таких как выделение блоков виртуальной памяти и страниц, использует кольцевой буфер, защищенный спин-блокировкой. Размер данного буфера может задаваться при загрузке драйвера в качестве его параметра, значение по умолчанию – 32 кб, минимальное – 8 кб. Память для буфера выделяется функцией kzalloc() – аналогом фукнций malloc/calloc() из стандартной библиотеки С. Передаваемый ей параметр GFP_KERNEL означает, что память выделяется для ядра (т.е. не может быть позже выгружена с диска), но не в атомарном контексте (т.е. текущий процесс может быть отложен до освобождения необходимой памяти).

Каждая запись в буфере представляет из себя следующую структуру:

enum memmon_event_type – тип события

{

NOTUSED = 0

MMAP2,

MUNMAP,

MREMAP,

MLOCK,

MUNLOCK,

MLOCKALL,

MUNLOCKALL,

BRK,

FSYNC,

ANON_PF, – страничная ошибка на анонимной странице

SWAP_PF, – на странице из файла подкачки

FILE_PF, – из разделяемого файла

SYSCALLRET – возврат из системного вызова

};

struct memmon_event

{

enum memmon_event_type type; – тип события

pid_t pid; – PID вызвавшего процесса

union – специфичны для события данные

{

struct

{

void __user *start;

size_t len;

} munmap;

struct

{

void __user *start;

size_t len;

unsigned long prot, flags;

unsigned long fd, off;

} mmap2;

……

};

}

С буфером событий связаны следующие переменные:

static struct memmon_event *events; – собственно буфер

static int ev_start; – индекс самой старой записи в буфере

static int ev_end; – индекс последней записи

static int ev_ovf = 0; – было ли уже переполнение буфера

DECLARE_WAIT_QUEUE_HEAD (ev_waitq); – очередь ожидания (для блокирующего чтения)

spinlock_t ev_lock = SPIN_LOCK_UNLOCKED; – спин-блокировка для защиты от гонок при обращении к буферу

Пользовательские приложения запрашивают содержимое буфера событий, читая файл /proc/memmon/events. Если при открытии файла не был установлен флаг O_NONBLOCK, операция чтения по нему блокирующая – т.е., если новых данных в буфере нет, read() переводит процесс в состояние ожидания (interruptible sleep) вызовом функции wait_event_interruptible() до получения сигнала либо появления новых данных в буфере.

Помимо open() и release(), вызываемых при открытии (создании новой структуры file) и ее уничтожении, в file_operations данного файла определены всего 2 точки входа – read() и poll(). Обработчик poll() вызываемается, когда какой-то процесс делает вызов select() по данному файлу – ожидает, пока на нем будут доступные для чтения данные. Кроме того, в флагах структуры file вызовом nonseekable_open() сбрасывается бит, позволяющий делать вызов llseek() по файлу (т. к. данная операция лишена смысла для кольцевого буфера).

Для реализации функции read() используется абстракция под названием seq_file, предназначенная для буферизации считываемых данных. Она требует задания 4 функций – seq_start(), вызываемой при начале чтения из файла, seq_next(), вызываемой перед копированием в буфер пользователя записи об очередном событии, seq_show(), собственно осуществляющющей это копирование, и seq_stop(), вызываемой при завершении передачи данных в пользовательский буфер (когда скопировано затребованное количество данных или не осталось событий в буфере).

Рис. показывает связь между этими функциями и структурами.

При добавлении нового события в буфер происходит пробуждение все ожидающих на очереди процессов вызовом wake_up_interruptible_sync() (sync означает, что текущий процесс не будет вытеснен до разблокировки всех процессов).

    1. Набор отслеживаемых процессов

Набор отслеживаемых процессов представляется в виде битовой карты на 65536 записей. При запуске нового процесса ядро дает ему PID, на 1 больший, чем PID предыдущего процесса. При достижении очередным процессом PID, равного 65536, новым процессам PID выделяется с единицы (т.е. с первого положительного незанятого). Лишь когда количество процессов в системе превышает эту цифру, ядро начинает выделять бОльшие номера. Данная ситуация крайне маловероятна и драйвером не обрабатывается.

Для добавления и удаления PID'ов отслеживаемых процессов создается файл /proc/memmon/watch-pids с обработчиками open(), release(), read(), write().

Обработчик open(), в случае, если файл открыт для чтения, выделяет буфер ядра и распечатывает туда содержимое текущей битовой карты (при помощи функции bitmap_scnlistprintf()). Попытка открыть файл одновременно на чтение и запись приводит к ошибке EINVAL.

Обработчик read() считывает запрошенный пользователем блок данных из этого буфера

Обработчик write() считывает одно число (возможно, со знаком) из пользовательского буфера. Если оно положительное, оно воспринимается как PID процесса, который необходимо добавить в битовую карту. Если отрицательное – соответствующий PID удаляется. Если 0 – битовая карта обнуляется (не отслеживается ни один процесс).

    1. Перехват страничных ошибок и выделений страничных фреймов

Возможно осуществить перехват страничных отказов, подменив смещение обработчика исключения в IDT, однако данный метод требует повторения того же анализа сбойного адреса, который делает и стандартный системный обработчик, (файл mm/memory.c) что привело бы к падению производительности системы. В связи с этим было решено внести еще одну модификацию в ядро. При обработке страничного отказа ядро сначала определяет, относится ли сбойная страница к виртуальной памяти процесса (если нет, ему посылается сигнал SIGSEGV), после чего вызывает функцию handle_pte_fault(). Та анализирует причину отказа и либо подгружает страницу из своп-файла / файла отображения, либо выделяет процессу новую страницу, либо посылает ему SIGSEGV (например, при попытке записи в регион памяти, доступной только для чтения), либо происходит ситуация OOM (out of memory), в результате которой сбойный процесс уничтожается с целью освобождения памяти. Модификация ядра добавляет глобальную переменную-указатель на внешнюю функцию, которую каждый раз вызывает handle_pte_fault. При инициализации драйвер заносит туда адрес своей функции mm_handle_fault(), которая, в зависимости от причины страничного отказа, заносит в буфер одно из трех событий (вместе с адресом и типом доступа – чтение либо запись):

ANON_PF – сбой при обращении к новой (еще не выделенной из физической памяти) страницы;

SWAP_PF – сбой при обращении к странице, которая находится в файле подкачки;

FILE_PF – сбой при обращении к странице, которая может быть загружена из файла, отображенного в память (например, код разделяемой библиотеки).

Алгоритм анализа причины страничного отказа продемонтсрирован на рис. 3.7.

    1. Синхронизация

Так как доступ к кольцевому буферу происходит из различных процессов, необходимо какое-то средство предохранения от «гонок» (race conditions). Ядро Linux предоставляет целый набор примитивов синхронизации. Однако, т. к. блокировка выполняется в том числе и в атомарном контексте, т.е. когда текущий процесс не может быть переведен в режим ожидания (например, при помещении события из обработчика прерывания), единственный подходящий примитив – спин-блокировка (spin-lock), т.е. простая бинарная переменная (со значением 0 либо 1) и активным ожиданием на ней. Захват спин-блокировки происходит при начале операции чтения буфера событий и перед добавлением в буфер нового события. Освобождение – по завершении чтения и добавления события, а так же перед переводом текущего процесса в режим ожидании в случае, когда новых данных в буфере нет.

  1. Технологический раздел

    1. Язык и средства программирования

Ядро Linux написано на языке С с небольшим количеством кода на ассемблере, и его система сборки, вообще говоря, поддерживает только данные языки для использования в модулях. Выбор из них был сделан в пользу С по причине значительно большей структурированности написанного на нем кода. Однако, небольшой объем кода драйвера пришлось написать на ассемблере, в силу чего он не является платформенно-независмым и привязан к архитектуре x86.

Для сборки драйвера используется сборочная система ядра make/Kbuild и компилятор С из gcc (GNU Compiler Collection). Особенностью ядра Linux является отсутствие совместимости на бинарном уровне, совместимость присутствует лишь на уровне исходных текстов, вследствие чего все модули и само ядро должны быть собраны одной и той же версией одного и того же компилятора. Само ядро изначально написано для компиляции посредством gcc – основного компилятора С в GNU‑системах, поэтому он же должен использоваться и при компиляции модулей для него.

    1. Компиляция драйвера

Сначала необходимо применить исправление к ядру и перекомпилировать его. Это делается командами:

# cd /usr/src/linux

# patch – p0 < memmon.patch

# make

# make install INSTALL_PATH=/boot modules_install

# lilo

# reboot

После этого возможна сама сборка драйвера. Для этого из папки с его исходниками надо выполнить команду $ make.

    1. Работа с драйвером

Для загрузки драйвера необходимо выполнить команду

# insmod procmon.ko

Можно указать размер буфера событий:

# insmod procmon.ko buflen=65536

Добавить процесс с PID = 3000 к отслеживаемым:

$ echo 3000 > /proc/memmon/watch-pids

Свежие статьи
Популярно сейчас
Зачем заказывать выполнение своего задания, если оно уже было выполнено много много раз? Его можно просто купить или даже скачать бесплатно на СтудИзбе. Найдите нужный учебный материал у нас!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5231
Авторов
на СтудИзбе
425
Средний доход
с одного платного файла
Обучение Подробнее