Дедупликация страницы исполняемого кода драйверов OC Windows (1187398), страница 5
Текст из файла (страница 5)
РеализацияЭтот пункт посвящен реализации представленного выше алгоритма. Вначале будетуточнено, для каких именно драйверов и страниц производилась реализация, а послеизложены основные моменты реализации, не касающиеся работы механизма Copy OnWrite. Имплементация Copy On Write была вынесена в отдельный пункт, посколькуявляется основополагающей частью работы и может иметь более широкое применение,чем дедупликация кода драйверов в целом.2.2.1. ОграниченияПрежде чем рассматривать реализацию этого алгоритма, уточним, с какими именнотипами драйверов и с какими их страницами будем работать.
А так же опишем основныеограничения, с которыми пришлось столкнуться в процессе реализации этого алгоритма.2.2.1.1. Рассматриваемые типы драйверовЯдро или User SpaceСледует уточнить, что исследовалась только ядерная часть.Сессионные или Не сессионные драйверыВ данной работе, не рассматриваются сессионные драйверы. Так как они ненуждаются в дедупликации кода, поскольку для них механизм Copy On Write изначальноиспользуется менеджером памяти.
(См. пункт 1.4. Обзор литературы.)2.2.1.2. Рассматриваемые типы страницВ данной работе речь идет только о целиковых страницах, а не о отдельныхзаписях на страницу.СекцииПрежде всего необходимо отметить, что в данном пункте речь идет скорее осекциях, так как загрузчик исполняемых образов в Windows во время загрузки драйвера впамять устанавливает атрибуты его страниц в соответствии с теми, которые имеет секция,содержащая эту страницу. Этот момент будет далее рассмотрен подробнее в пункте 2.2.2.про поиск подходящих для слияния страниц в драйвере.CodeВ данной работе рассматриваются только страницы кода драйверов, так как оченьредко драйвер изменяет свой код, и эти страницы имеет смысл сливать в одну, посколькуони совпадают с большой вероятностью.
Страницы данных было решено незадействовать, так как данные часто подвергаются изменениям, и, следовательно, неприносят выгоды при данной оптимизации.24Pageable и NonPageableКак было сказано во введении (См. пункт 1.4. Обзор литературы), в ОС Windowsсуществуютдвапулапамятиподкачиваемыйинеподкачиваемый.Проблемадедупликации кода драйверов особенно остро стоит для неподкачиваемого.
Страницыкода драйверов из неподкачиваемого пула всегда находятся в оперативной памяти, азначит, занимают драгоценное место, и их нельзя заменить на что-то более полезное, покаэти драйверы не выгрузятся. Чем больше кол-во одинаковых драйверов, тем большее колво памяти заполнено по сути не нужными экземплярами кода, то есть тратится зря. Такимобразом, имея конечный размер и будучи заполненной по сути не нужными экземплярамикода, эта область памяти начинает накладывать ограничения по памяти на работусистемы. В случае с подкачиваемым пулом такой проблемы нет, и большое кол-воодинаковых страниц влияет только на частоту подкачки и сброса страниц на диск.В связи с этим, было решено в первую очередь решить задачу дедупликации длянеподкачиваемого пула памяти.
Поскольку аналогичная задача для подкачиваемого пулаочень сложна, она в данной работе не рассматривается.Итак, алгоритм дедупликации кода идентичных драйверов был реализован дляпула неподкачиваемой памяти. Это значит, что работа механизма Copy On Write ведетсясо страницами из невыгружаемого пула памяти.
Однако, чтобы иметь возможностьиспользовать данный алгоритм и для выгружаемых страниц, такие страницы делаютсяневыгружаемыми (NonPageable), то есть блокируются в памяти. Для этого в драйверезапрещается отгрузка страниц на диск (то естьсвопинг. См. пункт 1.4. Обзорлитературы). А после, с ними можно работать так же, как с остальными.Таким образом, в системе в место большого кол-ва наборов Pageable страницсуществует один набор в неподкачиваемой памяти.Такой подход при большом кол-ве драйверов значительно снижает нагрузку наподсистему подкачки страниц, и, следовательно, хорошо влияет на производительность.Кроме того, он эффективен и по памяти. Рассмотрим ситуацию, когда несколькоодинаковых наборов страниц находятся в подкачиваемом пуле памяти, и с каждым из нихработает свой экземпляр драйвера.
Тогда, усредненное по времени кол-во страниц,находящихся в памяти, будет зависеть от кол-ва драйверов и интенсивности их работы.Однако, оно с очень большой вероятностью будет больше, чем страниц в одном наборе.Что означает, что объединение и перенос страниц в неподкачиваемый пул памяти быливыгодны, ведь в этом случае, в системе в любой момент времени ровно один набор (дляread-only страниц). Такой подход хорошо подходит для драйверов, которые частообращаются к своим страницам. Однако, он не эффективен (а даже, наоборот, вреден) для25малоактивных драйверов, которые длительное время бездействуют, и все страницыкоторых как правило большую часть времени выгружены на диск (то есть среднее повремени кол-во страниц в памяти близко к 0).В пункте 2.3.
"Математическая модель" подробнее говорится о целесообразностивключения pageable секций драйвера в рассмотрение. Однако, решение должноприниматься в каждом случае отдельно и основываться на характере поведения драйвераи кол-ве драйверов в системе. Например, в зависимости от типа драйвера, и от его"активности" его Pageable страницы могут быть как все подгружены в память, так и всеотгружены на диск [3].Writable/NonwritableОбъединение страниц в одну особенно эффективно для nonwritable страниц.Однако, было решено так же объединять и Writable страницы. Это связано с тем, что вовремя компиляции драйвера секция кода помечается как Writable, даже если записьпроисходит всего в несколько страниц из этой секции. Таким образом в процессе работыразъединяться будут не все страницы помеченные, как Writable, а лишь небольшая ихчасть.
(Подробнее см. в пункте 2.3."Математическая модель" и 2.4."Тестирование").Следовательно, объединение остается эффективным и выгодным с точки зрения кол-вазанимаемой памяти.LargeВ данной работе Large Pages не рассматривались.Поскольку в данной работе речь идет только о целиковых страницах, а не оотдельных записях на страницу, то Large Pages не рассматривались, из-за того, чтопрактически невозможно найти две одинаковые большие страницы. Хотя конечно,существует алгоритм работы и с ними, когда большая страница делится на несколькомаленьких, которые уже объединяются. и т.д.2.2.1.3.
Copy On Write в WindowsКак уже было сказано выше, в данной работе рассматривается механизмдедупликации страниц, с помощью копирования при записи, только в неподкачиваемомпуле памяти. Однако, в нем в Windows не существует механизма Copy On Write. Данныймеханизм, в ОС Windows работает только для неподкачиваемой памяти. Причина состоитв том, что страницы неподкачиваемой памяти могут вызываться на высоких IRQL(уровнях прерываний, см.
1.4. Обзор литературы), где нельзя выделять страницы памяти.А механизм копирования при записи подразумевает выделение новой страницы - копиидля передачи вызвавшему потоку.26В данном случае, такой механизм необходим хотя бы для read-only страниц, вкоторые не будут производиться записи, и, значит, не понадобятся дополнительныестраницы.
Следовательно, он был создан. А так же модифицирован так, чтобы могобслуживать и writable страницы тоже.2.2.1.4. ИтогИтог:•драйверы - не сессионные•страницы - целиком, только код, nonpage и page; writable и nonwritable.Для реализации всего этого, пришлось имплементировать Copy On Write вNonPageable памяти, вклиниться в обработчик Page Fault, создать дополнительныйрезервный буфер страниц для высоких IRQL и т.д. Обо всем этом и пойдет речь вследующих двух пунктах.272.2.2.
Основные этапы реализацииВ данном пункте рассматриваются тонкости реализации предложенного метода вядре Windows.2.2.2.1. Отслеживание загрузки и отгрузки каждого экземпляра драйвераОтслеживание загрузкиОтслеживание загрузки реализуется с помощью системных средств. В ОС Windowsсуществует системный вызов PsSetLoadImageNotifyRoutine, устанавливающий функциюNotifyRoutine, которая срабатывает, когда в систему грузится новый драйвер. На этапезагрузки нового драйвера, когда страницы с его кодом уже загружены в память, но передпередачей управления его функции инициализации DriverInit, вызывается наш обработчик(NotifyRoutine), который может выбрать с драйверы с определенным именем и начатьработать с ними, в рамках дедупликации страниц их кода.Отслеживание отгрузкиОтслеживание отгрузки драйвера является гораздо более сложной задачей в силуотсутствия аналогичной функции.
Существует несколько способов поймать этом момент.Из-за своей простоты и наименьшего количества изменений в коде ядра и драйверов былвыбран следующий.Поскольку при отгрузке драйверов в ОС Windows сначала вызываетсяустановленная драйвером функция DriverUnload, и только потом подсистема памятиначинает выгружать его страницы из памяти (а именно до этого момента нужно успеть"разъединить" все ранее "объединенные" страницы), то нужно поймать момент вызовафункции DriverUnload драйвера и "вклиниться" сразу после нее.В тот момент, когда драйвер только что был загружен в память, но еще неприступил к инициализации (см.