Компания
524,66
рейтинг
27 декабря 2012 в 16:34

Разработка → Процесс загрузки Windows или что спрятано под стартовым логотипом

А вы никогда не задумывались над тем, что же происходит с операционной системой в тот момент, когда она рисует свой логотип и говорит «Starting Windows»? И вообще, почему она долго загружается? Ведь при старте системы уж точно не решаются никакие задачи, сложные с вычислительной точки зрения!

Что тогда подразумевает под собой загрузка операционной системы? По большей части это проецирование в память исполняемых модулей и инициализация служебных структур данных. Структуры данных живут в памяти, поэтому операции с ними по идее должны быть быстрыми. Все наталкивает на мысль о том, что время съедается именно процессом загрузки исполняемых модулей в память.

Давайте интереса ради разберемся, какие модули, в каком количестве и в каком порядке загружаются при старте ОС. Чтобы выяснить это, можно, например, получить лог загрузки системы. Подопытная ОС в моем случае — Windows 7 Enterprise x64. Логировать процесс загрузки будем при помощи отладчика ядра. Существует несколько вариантов отладчиков ядра, лично я предпочитаю WinDbg. Также нам понадобятся некоторые вспомогательные средства для волшебного превращения лога в нечто более приятное глазу.

Mining and crafting


Настройка отладки хорошо гуглится, поэтому описывать подробно этот процесс я не буду. Поскольку нас интересует все происходящее с момента старта системы, нам нужно отметить пункт «Cycle Initial Break», с помощью чего отладчик остановится, как только в отлаживаемой системе будет загружена подсистема отладки ядра. Дублирование вывода в файл можно осуществить при помощи команд ".logopen" и ".logclose", это просто. Другая полезная команда — ".cls". Она очищает экран команд, и да, только экран команд.

Интересующая нас функция — «MiCreateImageFileMap». Это внутренняя функция менеджера памяти, проецирующая исполняемый файл в память. Проецирование в память происходит при создании секции, например, при запуске исполняемого файла. Однако учтите, что если исполняемый файл проецируется в память, это не гарантия того, что будет выполнен его код! Эта функция просто создает проекцию, чаще всего «про запас», чтобы, если кто-то надумает запустить модуль на исполнение, можно было сэкономить время его загрузки. На эту функцию поставим логирующую точку останова.

Если у вас достаточно маны, вводите следующую команду:
bu nt!MiCreateImageFileMap "dt nt!_EPROCESS -d ImageFileName @$proc; dt nt!_FILE_OBJECT -d FileName @rcx; g"

Магическая строчка буквально означает следующее:
  • bu (Set Unresolved Breakpoint) — установить неразрешенную точку останова. Не то чтобы кто-то или что-то не разрешал, просто для ее установки необходимо определиться, по какому адресу ее ставить. Дело в том, что заранее не известно, по какому адресу она должна располагаться. При загрузке любого модуля проверяется присутствие в нем необходимой функции, и если такая функция найдена, точка останова устанавливается автоматически. Такой способ установки незаменим при включенном ASLR — рандомизации адресного пространства, поскольку модули будут загружаться каждый раз по разным адресам, и точка останова, установленная по фиксированному адресу, с большой вероятностью окажется не у дел.
  • nt!MiCreateImageFileMap — символ, на котором нужно останавливаться. В WinDbg принята запись в форме 'module_name!function_name'. В данном случае nt является предопределенным псевдонимом для ntoskrnl.exe.
  • далее следует часть WinDbg-скрипта, которая будет выполняться каждый раз при остановке на этой функции. «dt nt!_EPROCESS -d ImageFileName @$proc» по-русски означает «отобразить поле ImageFileName структуры _EPROCESS из модуля nt при условии ее отображения по адресу, определенному в псевдорегистре «текущий процесс»». Следующая после разделителя ";" команда означает примерно то же самое, только адрес структуры берется из регистра rcx, в котором в Microsoft x64 ABI передается первый параметр функции. «g» означает «go», т.е. продолжить исполнение.


Небольшая рекомендация по использованию логирующих точек останова: старайтесь не использовать расширения отладчика (команды, начинающиеся с "!"), поскольку в таком случае логирование будет выполняться на порядок медленнее.

Поехали! Отжимаем тормоз точки останова и ждем. Я ждал, пока не прогрузится рабочий стол, т.е. я залогинился. Полученный «урожай» немного редактируется, обрезается все лишнее для удобства дальнейшей обработки и скармливается дружище питону. Не будем заострять внимание на парсинге лога. Отметим только, что граф укладывался в форму спирали Архимеда с дальнейшей коррекцией вручную, поскольку происходило наложение узлов друг на друга. В полученном графе учитывается порядок загрузки библиотек. К сожалению, пришлось пожертвовать учетом порядка загрузки исполняемых файлов относительно библиотек в угоду удобочитаемости графа.

Карта звездного неба




Условно выделим несколько групп загрузки.

Начинается работа OC в модуле ntoskrnl.exe, являющимся ядром ОС. А если еще конкретнее — с функции KiSystemStartup(). Вместе с загружаемыми системными компонентами она формирует фундамент ОС: разделение режимов работы, базовые сервисы для пользовательских приложений и т.п. В эту же группу входят драйверы, отмеченные для загрузки во время старта системы. В двух словах, в этой ракушке зарождается ОС Windows.



Следующий узел — менеджер сессий (session manager). Его представляет первый после системного процесс, стартующий в Windows — smss.exe. Процесс примечателен тем, что является родным (native) процессом Windows, то есть он не использует подсистему Win32, которая в общем-то еще не загружена. Этот процесс использует только нативные сервисы операционной системы посредством ntdll.dll, представляющей собой интерфейс режима пользователя для сервисов ОС. Также этот процесс является доверенным компонентом операционной системы и обладает исключительными правами, например, он может создавать маркеры безопасности (security tokens). Но главное его предназначение — создание сеансов и инициализация подсистем, как графической, так и различных исполняемых (Windows, POSIX). Эта ракушка воздает каждому по потребностям.



Группа входа в систему (logon) состоит из нескольких процессов. В целом они отвечают за инициализацию сеансов. Это включает в себя отображение экрана приветствия, создание рабочих столов, запуск процессов автозагрузки и инициализацию подсистемы безопасности и т.п. Этот веник отметает всех посторонних.



Самой массивной оказалась группа сервисов. Во многом она обязана своим объемом службе SuperFetch. Эта та самая, про которую говорят, что она по выходным заранее прогружает офисный пакет, а в начале рабочей недели — Steam с игрушками. Superfetch прогружает огромное количество модулей при старте системы, чтобы потом «все быстрее работало». Да и кроме него в системе хватает сервисных приложений и автозапускающихся драйверов. Думаю, все видели оснастку «Службы и приложения». Эта звезда жизни заводит в системе все, что нужно и не очень.



Последним отмечу любимый всеми explorer.exe. Примечательно, что к моменту его запуска все используемые им модули уже загружены в память. В скриншот также попал некий vcredist_x64.exe — бедолага лежал на рабочем столе подопытной виртуальной машины и был прогружен в память проводником.



Вообще способов оказаться загруженным в память у модуля много. Например, достаточно запросить информацию из ресурсов исполняемого файла, в том числе его иконку. Конкретно в данном примере проводник проверял, является ли эта программа требующей повышенных привилегий, т.е. стоит ли дорисовывать к иконке соответствующий рисуночек с желто-голубым щитом. Еще раз отмечу, что загрузка модуля в память не означает выполнение его кода!

Лично я держу получившуюся картинку под боком. По ней хорошо прослеживаются зависимости, например, драйверов. Также в паре с утилитой Sysinternals Autoruns можно увидеть, на каком этапе загрузки подтягиваются те или иные модули.

Граф загрузки был построен для ОС Windows 7 Enterprise x64, установленной на виртуальной машине VMware. Ниже приведены векторное изображение графа и непосредственно файл в формате gml, с которым можно поиграться в любом редакторе графов.
Граф в формате GML
Векторное изображение графа

Внимание! Бонус!
Граф загрузки для чистой ОС Windows 8 Enterprise x64 на живой машине ;)
Граф в формате GML
Векторное изображение графа

Ингредиенты:
WinDbg @ msdn.microsoft.com/en-us/windows/hardware/gg463009.aspx
Python @ www.python.org
NetworkX @ networkx.lanl.gov
yEd Graph Editor @ www.yworks.com/en/products_yed_about.html
Руки @ Плечи
Автор: @HonoraryBoT
Positive Technologies
рейтинг 524,66

Комментарии (26)

  • +4
    Красиво, блин.
    Интересно, а что из себя будет представлять граф загрузки Linux?
    • 0
      Не знаю. Попробуйте =)
    • +15
      Держите, отрыл свой старый
      • +1
        Заценил, круто!
      • НЛО прилетело и опубликовало эту надпись здесь
        • +4
          Ну, август почти полгода назад был; сейчас у меня уже другой дистрибутив :)
  • 0
    Еще было бы неплохо посмотреть на загрузку Windows 8, ведь Microsoft неплохо поработал в этом направлении.
    • +1
      В конце статьи ссылка на граф загрузки Windows 8
      • +2
        Ох, спасибо. Надо бы провериться у окулиста…
    • –2
      Граф загрузки для чистой ОС Windows 8 Enterprise x64 на живой машине ;)

      Статью внимательно читали?
  • 0
    Вы не поверите, но недавно об этом задумывался. Спасибо вам :)
  • +5
    Глядя на картинку я сначала подумал статья будет о сложном реалтайм рендеринге загрузочной анимации.
    • +1
      Хотел сделать, но времени хватило только на картинки.
  • +2
    Microsoft предоставляет профайлер загрузки,
    habrahabr.ru/post/106684/

    Но сделать свой велосипед всегда увлекательно
    • +2
      Это не профайлер загрузки, даже не близко. И профайлер не даст вам информации о зависимости. Пост не читали =)
      • +1
        Вообще-то это в том числе и профайлер (и sampled, и instrumented). В том числе и загрузки.
        Если под «информацией о зависимости» Вы имеете в виду информацию о том, какой процесс загрузил образ, то эта информация доступна.

        Ну и чтоб два раза не вставать, пара небольших замечаний (сразу скажу, что несмотря ни на что статья мне понравилась — интересный подход к сбору данных и очень красивая визуализация результата — браво).

        1. Сама методология несколько… натянута, что ли. То, что ввод/вывод занимает большую часть времени при загрузке современных ОС (включая Windows) — это в целом правда (по крайней мере на обычном десктопном «железе»), но меппинг секций достаточно слабо корреллирует со вводом/выводом во время загрузки по как минимум двум причинам: a) то, что секция отображена в память ничего не говорит о том, сколько всего страниц будет иметь hardfault (Вы и сами упомянули, что меппинг не означает исполнение кода, но он не означает и то, что хоть одна страница будет фактически прочитана с диска); b) при «правильной» работе ReadyBoot, именно на его долю будет приходится основная часть ввода/вывода при загрузке, а страницы в секции будут впечатываться прямиком из стендбай списка.

        2. Выводы по сервисам тоже не совсем верны. svchost — это не SuperFetch, а Service Host Process. Практически все inbox сервисы в Windows находятся в dll-ках, а разные svchost-ы объединяют сервисы с одинаковыми привилегиями внутри одного процесса. На картинке ниже SuperFetch (System Maintenance Service или SysMain) — всего лишь один из множества сервисов, хостящихся не только в svchost-ах, но и конкретно в svchost (1052)

        • 0
          Вообще-то я говорю про свою поделку — это не профайлер, и не предназначен был для каких-то количественных оценок производительности. Мне нужна была последовательность загрузки. Я же ничего нигде не считал. =)

          1. Согласен с заметками о том, что файлы не будут полностью прочитаны с диска, только частично. Однако минимум нужно прочитать с диска данные о структуре файла и разобрать PE заголовок.

          2. Я и не говорил, что svchost — это SuperFetch, в курсе, что сервисы составные. Но так все эти dllки в графе по радиусу и расставлены.
          • 0
            P.S. Я-то как бы понадеялся, что все в курсе и так, что большая часть виндовых сервисов живет в svchost в виде модулей.
            • 0
              Это то известно всем, я просто уточнил, потому что исходя из написанного Вами можно сделать вывод, что за большую часть модулей, загружаемых svchost-ом, отвественннен SuperFetch, в то время как — это просто три десятка сервисов загружают свои зависимости.
  • +2
    > Вообще способов оказаться загруженным в память у модуля много. Например, достаточно запросить информацию из ресурсов исполняемого файла, в том числе его иконку.

    Хм, значит забитый ярлычками «рабочий стол» и меню «пуск» увеличивает время загрузки системы? Всмысле, я всегда это подозревал, но теперь нашёл этому подтверждение =)
    • 0
      Сложно однозначно ответить. Для отображения иконки, например, нужна только секция ресурсов.
    • 0
      еще во времена не NT-шных версий виндоус, когда все любили играться с PE файлами я заметил тот факт, что даже дефективные исполняемые файлы часто отдавали свои ресурсы без каких либо проблем. Думаю ресурсы загружаются без инициализации модуля.
      • 0
        Посмотрите функцию RtlImageNtHeader и её кросс-рефы.
    • 0
      Ну как сказать, не то чтобы прямо, но и совсем не косвенно.
      Когда explorer Загружается, он показывает рабочий стол, чтобы показать содержимое рабочего стола, чтобы показать содержимое, опрашивает каждый файл, для бинарников и ярлыков — выдергивает иконку, спрашивает про UAC, для неисполняемых файлов — лезет в реестр, спрашивает где взять иконку, лезет за иконкой. На всё это тратятся циклы процессора и операции дисковой подсистемы, что, есстественно задерживает время до «комп загрузился и можно работать».
      Другой вопрос что если файлов немного, то задержка вряд ли осязаема (если сравнить с чистым столом без файлов), но вот если понакидать FullHD видюшек штук 40, пару сотен фоток по 15 Мб каждая, да приказать системе не создавать Thumbs.db, то да, загрузка ощутимо замедлится на генерацию превьюшек.
      • –1
        Да все эти операции — смех для современных компьютеров, это для человека звучит сложно и долго «лезет туда, потом туда, потом туда, потом делает то, потом сё», а на деле фьють и всё загружено, реестр в памяти, кеши иконок тоже там, потому что не первый раз всё грузится, ну и т.д. А последний вариант про сотни фоток просто мазохизм какой-то :)))
    • 0
      «Обои-ня» для FullHD разрешения увеличивает время загрузки гораздо больше чем эти ярлычки )

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка