Обновить
Комментарии 22
То что вы видите в фрагменте с NOP-ами:

4025AA 90 NOP
4025AB 90 NOP
4025AC 90 NOP
4025AD 90 NOP
4025AE 90 NOP
4025AF 8BFF MOV EDI,EDI
4025B1 55 PUSH EBP

— это не выравнивнивание. Это специальные участки, оставленные для того чтобы облегчить реализацию перехвата функции методом сплайсинга. Когда внешний код хочет перехватить функцию ядра — он записывает туда FAR JMP на себя.

Могу предположить что другие замеченные недостатки также могут иметь какие-то рациональные объяснения.

Например, пример с чтением в EAX и ECX вместо AX и CX может вызвать cache miss, по той причине, что оставшиеся два байта уже не попали в кэш или даже нарушение безопасности, если расширенный регистр будет сохранен в стек, откуда значение может быть прочитано кем-то, кого мы не ждем.

Кстати в обсуждении на RSDN откуда цельнотянута статья — на это указали. Если уж вы занимаетесь копипастой умножая энтропию в интернете, то приводите хотя бы ссылку, чтобы не изолировать обсуждения по анклавам: http://rsdn.org/forum/asm/6251024.flat. Там же, кстати, объяснения всех «несовершенств», читателям статьи будет полезно ознакомиться
Во-первых, главред Хабра сам призвал повторять материалы с других площадок, поскольку аудитория у Хабра больше.
Во-вторых, на RSDN ничего не объяснили, а некоторые даже ничего и не поняли.
В-третьих, если пустые команды (а, не MOV EDI,EDI) нужны для какой то технологии, то почему их разное количество в разных местах и почему, если их сдвинуть, то все начинает быть кратно 16?
Главред Хабра может призывать к чему угодно. У него-то мотивация понятна — увеличение базы контента и трафика.

Я не понимаю мотивации для вас. Я бы понял если бы это был ваш материал и вы хотели бы что-то донести до более широкой аудитории.

Но в таком случае автор бы как минимум отредактировал статью «по результатам обсуждения», а не изолировал бы анклавы обсуждения на разных площадках, не давая ссылки.

Кто мешал внизу оставить короткую строчку «обсуждалось там-то»? Без нее все это выглядит как копипаста ради виртуальных плюсиков в карму.

Во-вторых, на RSDN ничего не объяснили, а некоторые даже ничего и не поняли.


Вообще-то это далеко не так, перечитайте обсуждение там
Вроде «прямой» копирайт не разрешён, но это не точно,
а допускается размещение ранее опубликованных авторских статей на других ресурсах i-net.

P.S. Можно ли с RSDN копипастить материал — это отдельный вопрос.
Один из таких способов – это дизассемблирование, анализ, внесение изменений и трансляция дизассемблерного текста обратно в EXE-файл.

Смешно, когда предлагают такие способы. Печально, когда предлагают подобные извращения не в шутку, а всерьёз! Тем более, если идёт речь об ядре ОС!
Даже не стоит обсуждать уровень подобных советчиков (специалистов)!
Складывается впечатление, что одного пехотинца-программиста для исследования скомпилированного кода недостаточно, нужен хотя бы батальон, особенно с продолжающимся уходом от WinAPI и погружением в пучину Electron'а.
А после прочтения подобных исследований более современных систем хочется и вовсе затребовать соотношения писателей и читателей кода хотя бы 50/50.

Спасибо за то, что пытаетесь поднять неподъёмное.

При чем здесь Electron, если разговор про ядро?

При том что мы наблюдаем уход от железа в массовом программировании в сторону переносимости, и с одной стороны это неплохо — универсальные приложения, работающие на любой системе. Но тогда другой слой ПО должен добиваться оптимальной производительности на каждой ОС/архитектуре. А этого не происходит. В частности, программировать на WinApi считается моветоном, а ведь такие программы явно гораздо компактнее и быстрее, чем современные… «Hello World» на C/WinApi весит 2,5 кб, в 4 кб помещается многопоточное сетевое приложение. Сколько порядков нужно добавить для Electron?

Ещё, к примеру, у Windows начиная с Vista существенно загрублён системный таймер. Что толку от многогигагерцового процессора, если процесс невозможно активировать с разрешением ниже 500 мс? А что предложит приложение на Electron в этом смысле?
А он тут при том, что в его сторону идёт движение.

Я не фанат Electron. Но при чем тут это к обсуждению про ядро?

Просто от уровня Electron ещё дальше до уровня ядра. Теперь не только ядро рассматривается как нечто необязательное для рассмотрения при создании ПО, а вся ОС превращается в некую виртуальную Electron-машину, которую — и ядро и все уровни выше — в один прекрасный момент смогут безболезненно заменить, например Linux'ом и это как бы никого не должно удивить.

Статья возникла из-за необходимости достижения более детерминированного поведения системы, которого не удавалось добиться настройкой планировщика, пришлось ломать его поведение.
Да, Windows не является RTOS, но на её основе разные компании с большим или меньшим успехом клепают около-реального времени решения.
Тем временем Windows всё дальше и дальше уходит от возможности реализации чего-либо достаточно предсказуемого, в том числе в связи с уходом от WinAPI в сторону Electron — когда простые системные приложения типа просмотра фотографии начинают весить как профессиональный фоторедактор с несопоставимо большей функциональностью. В какую сторону пойдёт развитие функциональности ситемного API в такой ситуации? В сторону оптимизации производительности и отзывчивости? Не думаю, скорее напротив в угоду пущей межплатформенности будут заброшены последние попытки такой оптимизации.

Почему автор выбрал для переработки Windows XP? Да потому что она гораздо ближе к цели, в ней для успешного достижения поставленной задачи оказалось достаточно лёгкой модификации планировщика.
А ведь первоначально в ядро автор заглядывать не планировал, была задача обеспечить детерминированное выполнение прикладной задачи.

В огороде бузина, а в киеве дядька.


  1. Windows никогда не была, не будет и не планируется RTOS.
  2. Какая разница. сколько весит просмотр фотографии? Может наоборот за счет того, что меньше ресурсов программистов потрачено на создание просмотрщика фотографий, удается освободить больше ресурсов на вылизывание ядра?
  3. Современные браузерные движки (к разговору про Electron) сложные программы, активно использующие низкоуровневые API и предъявляющие жесткие требования к ядру. Так что курсу на производительности это опять не мешает.

Мы же тут не на пикабу прости господи, а технические люди. Какие аргументы есть за «Windows плевать на качество ядра»?

1. Статья о чём? Автор добивался и добился детерминированного поведения Windows XP в рамках своей задачи.
Он мог, к примеру, купить задорого LabVIEW с RT модулем, или QNX, перенести своё приложение в незнакомую среду, отработать, оттестировать и получить результаты.
Но он прогнул под себя XP. И да, в рамках задачи XP повела себя как RTOS.

2. Разница в том, что компания зыбиыла, как оптимизируется ПО. Это такой принципиальный подход. Ждать появления стандартного ПО, написанного с другим подходом думаю будет слишком оптимистично.
С таким подходом к написанию ПО действительно сложно понять, "почему для открытия меню Windows читает один файл сто тысяч раз". А главное, что этим некому заниматься кроме единичных энтузиастов.

3. Отличные новости. Только вот я не просил делать системный просмотрщик фотографий на основе браузерного движка. И в другие укромные места его пихать. К тому же этот движок даже не является разработкой компании-производителя ОС, но пихается во все щели. В итоге мне приходится докупать ОЗУ для выполнения тех же задач, что и несколько лет назад.

Про плевать на качество ядрв я вроде не говорил, но попробуйте добиться детерминированности поведения Windows 10 по аналогии с описанным в статье на XP. Покажите время реакции на уровне десятков-сотен миллисекунд, как в XP.
  1. Вовсе не добился, как видно из статьи.
  2. Почему «забыла»? А вот веб-фреймворк от MS держит первое место по бенчмарку https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext Этот аргумент имеет ТАКОЕ ЖЕ отношение к ядру, как и просмотрщик фото (то есть никакое), но если вы воспринимаете аргумент про просмотрщик фото, то примите и этот.
  3. А с ядром-то какая связь?

Попробуйте добиться детерминированности поведения Windows 10 по аналогии с описанным в статье на XP. Покажите время реакции на уровне десятков-сотен миллисекунд, как в XP.

Как я и говорил, Windows — не RTOS.

1. Вовсе добился, как видно из предыдущей статьи:
«Теперь, наконец, анализ кода можно считать оконченным. В более чем полумиллионе ассемблерных строк с помощью контекстного поиска и общих соображений достаточно легко нашлось несколько десятков команд полностью и в строгом соответствии с документацией объясняющих поведение ОС при переключении потоков.

Анализ и исправление ядра ОС занял примерно неделю, зато все прикладное ПО осталось без изменения, за исключением добавления в одном месте в главный цикл основного модуля подпрограммы выдачи сообщения ядру.

Достаточно хотя бы раз в 2-3 секунды (т.е. пока не истечет внутренний счетчик, нужно опять успеть присвоить ему максимальное значение) обращаться из задачи пользователя к этой процедуре, как данный поток будет работать, не прерываясь на целые кванты для менее приоритетных потоков.»

2. Буду иметь в виду.

3. А 500 мс время реакции системы при 5 ГГц частоты процессора, т.е. каждый 2,5 милионный тик, точно всем должно хватать? Особенно учитывая, что на XP было на порядки лучше?

Биться за превосходный код на ассемблере, когда достоподлинно известно, что на высоком уровне этот самый код далёк от этого самого превосходства? Абсолютно никакого смысла. Причём, ещё задолго до утечки было известно о количестве дыр в ядре, вшитом нестандартном поведении и мёртвом коде, которые оставлены ради сохранения обратной совместимости.

Недостаток здесь в том, что у транслятора не хватило «смелости» всегда читать эти объекты в четырехбайтовые регистры
Читать двухбайтовую переменную в четырёхбайтный регистр — так себе затея. Рано или поздно вы выйдете за пределы страницы, и вылетите по access violation. Никакой компилятор находясь в здравом уме не будет так делать.

5 NOP + MOV EDI, EDI — как уже ответили, для hot patch


«заячьи петли» — результат PGO, когда редко исполняемый код отделяется от часто исполняемого, что улучшает работу кеша кода


PUSH EBP; MOV EBP, ESP; POP EBP — может быть вызвано требованиями обратной совместимости. Многие антивирусы и античиты любят патчить систему, и ожидают увидеть в начале не jump, а обычный пролог. А может быть и тупо неоптимальный компилятор.


большое число пересылок регистров — это да, есть такое. MS в новых компиляторах запилили SSA и обещали улучшить инлайнинг; подозреваю, что там все стало лучше.

Как ни горько это писать, ошибся с выравниванием на 16. Это трансляторы IBM любители везде перед подпрограммами вставлять align 10h.
Невнимательно посмотрел. Пришлось посмотреть повнимательней.
И хотя в ядре есть и выравнивание на 8:
0740BD5C DC0D90BD4000............. FMUL64 [0040BD90]
0740BD62 EB96..................... JMPS 0740BCFA
0740BD64 90...................... NOP
0740BD65 90...................... NOP
0740BD66 90...................... NOP
0740BD67 90...................... NOP
0740BD68 DD 00000000 3FF00000-FFFFFFFF 7FEFFFFF

И выравнивание на 16:
0740A86E 74C1.................. JZ 0740A831
0740A870 E949FFFFFF.............. JMP 0740A7BE
0740A875 90..................... NOP
0740A876 90..................... NOP
0740A877 90..................... NOP
0740A878 90..................... NOP
0740A879 90..................... NOP
0740A87A 90..................... NOP
0740A87B 90..................... NOP
0740A87C 90..................... NOP
0740A87D 90..................... NOP
0740A87E 90..................... NOP
0740A87F 90..................... NOP
0740A880 803DD04D480000.......... CMP B PTR [00484DD0],00
0740A887 53...................... PUSH EBX
0740A888 56...................... PUSH ESI

Все-таки подпрограммы в ядре XP или не выравниваются или выравниваются только на слово:
07404E93 F390.................. REP PAUSE
07404E95 EBF4.................. JMPS 07404E8B
07404E97 90.................... NOP
07404E98 8BFF.................. MOV EDI,EDI
07404E9A 9C.................... PUSHF
07404E9B 8B542410.............. MOV EDX,[ESP]+10

0740E150 FC...................... CLD
0740E151 EBE5.................. JMPS 0740E138
0740E153 90..................... NOP
0740E154 55..................... PUSH EBP
0740E155 8BEC................... MOV EBP,ESP
0740E157 56................... . PUSH ESI
0740E158 57..................... PUSH EDI

И именно это выравнивание на слово (а не мифическое выравнивание на 16) иногда сползает:
0740E392 5D..................... POP EBP
0740E393 C20C00................. RET 000C
0740E396 90..................... NOP
0740E397 55..................... PUSH EBP
0740E398 8BEC................... MOV EBP,ESP
0740E39A 56..................... PUSH ESI
0740E39B 57..................... PUSH EDI

От остальных претензий не отказываюсь.
Объяснение скачков туда-сюда улучшением работы кэша, так себе объяснение.
Ведь в приведенном примере переходом в 6 байт выполняется «удаленный» код лишь из 7 байт. Если бы он был не «удаленный» и обходился двухбайтовым условным переходом, то вместо 6 байт условного перехода было бы 9 байт кода. Неужели из-за этих жалких трех байт разницы длины будет ощутимо быстрее выполнение? Скорее уж, это какие-нибудь белые нитки от редактора связей.

Не 9, а 11 байт, судя по примеру — второй jmp не удаляется. Кроме того, неважно, насколько мал выигрыш в размере; важно, что он есть. Вряд ли писатели компилятора стали бы делать такую эвристику, чтобы пропускать "маленькие" оптимизации.
От редактора связей такие нитки быть не могут, он работает куда грубее, чем уровень базовых блоков (если не говорим о LTO, но тогда это уже не редактор связей по сути своей).
Это известная оптимизация, называется Hot/cold code split, ее все основные компиляторы делают (например, у gcc включается через -freorder-blocks-and-partition).
Вот здесь в примере показан как раз такой сценарий с вынесением ветки кода: https://docs.microsoft.com/en-us/archive/msdn-magazine/2015/september/compiler-optimizations-streamline-code-with-native-profile-guided-optimization

Тогда получается, что только если действие короче 5 байт (что мало реально, разве какой-нибудь int 3), его не надо выносить из кода. Потому, что даже без подготовки параметров один вызов и короткий условный его обхода на байт длиннее, чем длинный условный. Правда, есть нюанс: длинный условный вроде декодируется дольше из-за префикса 0F и выигрыш в байт не обязательно даст или необязательно всегда даст выигрыш в скорости.
«Как ни горько это писать, ошибся с выравниванием на 16.»
Не ошибается тот, кто ничего не делает. Но это явно не про Вас.
в ядре нет команд перехода на адрес 411ACE (что также легко проверить)

А если туда прыгают вычисляя адрес?

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