чего не происходит в стандартном цикле работы процессора
Процедура выполнения команд. Рабочий цикл процессора
Функционирование процессора в основном состоит из повторяющихся рабочих циклов, каждый из которых соответствует выполнению одной команды программы. Завершив рабочий цикл для текущей команды, процессор переходит к выполнению рабочего цикла для следующей команды программы.
На рис. 9.34 представлена схема рабочего цикла процессора. Эта схема имеет достаточно общий характер.
Рис. 9.34. Рабочий цикл процессора
На схеме показаны варианты рабочего цикла для четырех групп команд: 1) основных (осуществляющих арифметические, логические и пересылочные операции), 2) передачи управления, 3) ввода-вывода и 4) системных (устанавливающих состояние процессора, маску прерывания, слово состояния программы и др.).
Рабочий цикл начинается с распознавания состояния процессора. Устанавливается, какое из альтернативных состояний — Счет или Ожидание — имеет место. Далее проверяется наличие незамаскированных прерываний.
В состоянии Ожидание никакие программы не выполняются. Процессор ждет прихода запроса прерывания, после чего управление переходит к соответствующей прерывающей программе, переводящей процессор в состояние Счет.
В состоянии Счет при наличии незамаскированных прерываний происходят выход из нормального рабочего цикла н переход к процедуре обработки запросов прерывания.
При отсутствии в состоянии Счет запросов прерывания последовательно выполняются этапы рабочего цикла: выборка очередной команды и определение по коду операции команды ее группы, подготовка операндов (формирование исполнительных адресов и выборка операндов из памяти), обработка операндов в АЛУ и запоминание результата.
На этапе выборки очередной команды образуется согласно естественному порядку адрес следующей за ней команды (продвинутый адрес), при этом содержимое счетчика команд (соответствующего поля ССП) увеличивается на число, равное числу байт в очередной команде. В некоторых ЭВМ формирование адреса следующей команды составляет отдельный этап, завершающий рабочий цикл.
В процессе выполнения заданной командой операции формируется признак результата операции, используемый командами условного перехода при организации ветвлений в программах.
Указанная выше последовательность этапов составляет основной вариант рабочего цикла, реализуемый при выполнении основных команд.
При выполнении команд передачи управления проверяется заданное командой (например, ее полем маски) условие. Если условие не выполняется, то следующую команду указывает продвинутый адрес, ранее установленный в СчК (регистре ССП). Если условие выполняется или имеется один из вариантов команды безусловного перехода, то адрес, задаваемый командой передачи управления, передается в СчК.
Команды ввода-вывода инициируют в канале операцию обмена информацией между ядром ЭВМ (основной памятью) и периферийным устройством. Сама эта операция выполняется каналом под управлением его собственной. программы. Поэтому на долю процессора остается только процедура опроса состояний канала и периферийного устройства — свободны ли они для операции ввода-вывода. Если свободны, процессор выдает в канал информацию, необходимую для начала операции ввода-вывода. В противном случае процессор переключается в состоянии Ожидание и ждет сигнала прерывания от этого канала.
Системные команды осуществляют переключение состояния процессора (программы) путем загрузки нового ССП или его части. В частности, эти команды изменяют маски прерывания, устанавливают ключи памяти и ключи защиты в ССП, реализуют операции прямого управления.
Рабочий цикл процессора
Сдаю экзамен по ЭВМ, на тему «Рабочий цикл процессора». Преподавателем была предложена для изучения Книга «Каган Б.М. Электронные вычислительные машины и системы», в ней на стр. 297 имеется схема: и пояснение к ней:
. Рабочий цикл начинается с распознавания состояния про- цессора. Устанавливается, какое из альтернативных состоя- ний — Счет или Ожидание — имеет место. Далее проверяется наличие незамаскированных прерываний.
В состоянии Ожидание никакие программы не выполняются. Процессор ждет прихода запроса прерывания, после чего управ- ление переходит к соответствующей прерывающей программе, переводящей процессор в состояние Счет.
В состоянии Счет при наличии незамаскированных прерыва- ний происходят выход из нормального рабочего цикла и переход к процедуре обработки запросов прерывання.
При отсутствии в состоянии Счет запросов прерывания по- следовательно выполняются этапы рабочего цикла: выборка очередной команды и определение по коду операции команды ее группы, подготовка операндов (формирование исполнительных адресов и выборка операндов из памяти), обработка операндов в АЛУ и запоминание результата.
Преподаватель задает вопрос «Чего ожидает процессор?» и утверждает что ответ на этой схеме есть. Отвечал что «Процессор ждет прихода запроса прерывания» как об этом говорится в книге, на что был получен ответ что это не так и процессор ждет чего то другого. Прошу помочь разобраться чего же именно ожидает процессор?
Из чего состоит центральный процессор?
Центральный процессор часто называют «мозгом» компьютера, ведь он, как и человеческий мозг, состоит из нескольких частей, собранных воедино для работы над информацией. Среди них есть те, что отвечают за прием информации, ее хранение, обработку и вывод. В этой статье портал TechSpot разбирает все ключевые элементы процессора, за счет которых и работают ваши компьютеры.
Этот текст входит в серию статей, в которых тщательно разбирается работа ключевых компонентов компьютера. Кроме того, если вы заинтересовались темой, рекомендуем ознакомиться с переводами статей серии «Как разрабатываются и создаются процессоры?».
Итак, приступим. Любому вычислительному устройству нужно нечто наподобие центрального процессора. По сути, программист пишет код для выполнения собственных целей, а затем процессор выполняет его для получения необходимого результата. Процессор также подключен к другим частям системы, вроде памяти и устройств ввода/вывода, чтобы обеспечить загрузку необходимых данных, но в этой статье мы не будем акцентировать на них внимание.
Фундамент любого процессора: архитектура набора команд
Чуть менее распространенными и более нишевыми являются MIPS, RISC-V и PowerPC. Архитектура набора отвечает за ряд основных вещей: какие инструкции процессор может обрабатывать, как он взаимодействует с памятью и кэшем, как задача распределяется по нескольким этапам обработки и др.
Чтобы лучше понять устройство процессора, разберем его элементы в том порядке, по которому выполняются команды. Различные типы инструкций могут следовать разными путями и использовать разные компоненты ЦП, поэтому здесь они будут обобщены, чтобы охватить максимум. Начнем с базового дизайна одноядерных процессоров и постепенно будем переходить к более продвинутым и сложным экземплярам.
Блок управления и исполнительный тракт
Исполнительный тракт подобен двигателю и, как следует из названия, это путь, по которому данные передаются при их обработке. Он получает входные данные, обрабатывает их и отправляет в нужное место после завершения операции. Блок управления, в свою очередь, направляет этот поток данных. В зависимости от инструкции, исполнительный тракт будет направлять сигналы к различным компонентам процессора, включать и выключать различные части пути, а также отслеживать состояние всего процессора.
3 основных типа команд
Команда памяти может представлять собой нечто вроде «прочтите значение из адреса памяти 1234 вместо значения А» или «запишите значение Б в адрес памяти 5678». Арифметические команды имеют вид в духе «добавьте значение А к значению Б и сохраните результат в значении В». Инструкции перехода, в свою очередь, похожи на «выполните этот код, если значение В положительное, или выполните другой код, если значение В отрицательное». Зачастую в программах используется цепочка сразу из нескольких вышеупомянутых примеров, из-за чего конечный результат выглядит примерно так: «добавьте значение адреса памяти 1234 к значению адреса памяти 5678 и сохраните его в адресе памяти 4321, если результат положительный, либо в адрес 8765, если результат отрицательный».
Перед тем, как перейти к выполнению декодированной команды, давайте уделим немного внимания регистрам.
Регистрами называются немногочисленные, но крайне быстрые фрагменты памяти процессора. У 64-битных процессоров каждый из них вмещает 64 бита, а всего их может быть несколько десятков на одно ядро. Регистры используются для хранения используемых в данный момент значений и их можно считать чем-то вроде кэша нулевого уровня. В приведенных выше примерах команд значения А, Б и В будут сохранены именно в регистре.
Арифметико-логическое устройство
Вернемся к этапу выполнения команд. Сразу отметим, что он отличается для всех трех вышеупомянутых типов команд, поэтому давайте рассмотрим каждый их них.
Самыми простыми для понимания являются арифметические команды. Эти команды отправляются в арифметическо-логическое устройство (ALU) для последующей обработки. Устройство представляет собой цепь, которая чаще всего работает с двумя значениями, отмеченными сигналом, и выдает результат.
Представьте себе обычный калькулятор. Для любого вычисления вы вводите значения, выбираете необходимую арифметическую операцию и получаете результат. Арифметическо-логическое устройство (ALU) работает по похожему принципу. Тип операции зависит от опкода команды, который управляющий автомат отправляет в ALU и которое в дополнение к базовой арифметике может производить со значениями такие битовые операции, как AND, OR, NOT и XOR. Кроме того, арифметическо-логическое устройство выводит информацию о проведенном вычислении для управляющего автомата (например, оказалось ли оно положительным, отрицательным, равным нулю или вызвало переполнение).
Несмотря на то, что арифметическо-логическое устройство чаще всего связано именно с арифметическими операциями, оно находит свое применение и в инструкциях памяти или перехода. Например, если процессору нужно вычислить адрес памяти, заданный в результате прошлого вычисления, либо в случае необходимости вычислить переход для добавления в счетчик программ, если инструкция того требует (пример: «если предыдущий результат отрицателен, перейти на 20 команд вперед»).
Команды и иерархия памяти
Команды перехода и ветвления
Кроме обычных команд перехода, существуют и условные переходы, с которыми процессору работать особенно сложно, поскольку он может выполнять несколько инструкций одновременно и конечный результат всей ветки может быть нельзя определить пока не начата работа над выполнением связанных команд.
Графическое представление конвейера, используемого в ядрах процессоров AMD Bobcat (2011). Обратите внимание, как много в нем различных элементов и стадий.
Процессоры используют тот же принцип для повышения пропускной способности команд. Конвейеры современных процессоров на архитектуре ARM или x86 могут использовать свыше 20 стадий вычислительного конвейера, а это значит, что ядро процессора одновременно обрабатывает свыше 20 различных команд. Процессоры могут отличаться по разделению этих стадий под различные нужды, но в одном из примеров, принцип работы которого находится в открытом доступе, имеется 4 цикла для выборки, 6 циклов для декодирования, 3 цикла для выполнения команд и 7 циклов для отправки результатов в память.
После того, как точно станет известен результат перехода (т.е. завершился конкретный этап на конвейере), счетчик команд обновится и процессор приступит к выполнению следующей операции. Если же результат не совпал с тем, который предугадал предсказатель команд, процессор сбросит все команды, которые начал выполнять по ошибке, и запустит работу с правильной точки.
Внеочередное исполнение
Теперь, когда вы знаете принцип работы трех наиболее распространенных типов команд, давайте уделим внимание более продвинутыми функциям процессоров. Практически все современные модели ЦП фактически исполняют команды не в порядке их получения. Существует такая функция, как внеочередное исполнение, призванная сократить время простоя процессора во время ожидания завершения остальных команд.
Ускорители и будущее процессоров
Конечно, процессор может делать все это самостоятельно, но созданный конкретно для этой цели блок будет намного более эффективен. Наглядным показателем мощностей ускорителей будет сравнение встроенного графического процессора с дискретной видеокартой. Разумеется, процессор может выполнять вычисления, необходимые для обработки графики, но наличие отдельного блока обеспечивает намного более высокую производительность. С ростом числа ускорителей фактическое ядро центрального процессора может занимать всего лишь небольшую часть чипа.
На первом рисунке снизу изображено устройство процессора Intel, выпущенного более десяти лет назад, где большая часть занята ядрами и кешем, а на втором показан гораздо более современный чип от AMD. Как мы видим, во втором случае большая часть кристалла отведена не под ядра, а под другие компоненты.
Кристалл процессора Intel первого поколения архитектуры Nehalem. Обратите внимание: ядра и кэш занимают подавляющее часть площади.
Кристалл системы на чипе от AMD. Много места отведено под ускорители и внешние интерфейсы.
Многоядерность
В случае с четырьмя ядрами процессору необходимо отправлять команды в 4 раза быстрее. Также нужно четыре раздельных интерфейса для памяти. Именно из-за наличия нескольких ядер на одном чипе, потенциально работающих с одними и теми же частями данных, возникает проблема слаженности и согласованности их работы. Предположим, если два ядра обрабатывали команду, использующую одни и те же данные, то как процессор определяет, у которого из них правильное значение? А что, если одно ядро модифицировало данные, но они не успели вовремя дойти до второго ядра? Поскольку у них есть отдельные кэши, в которых могут храниться пересекающиеся данные, для устранения возможных конфликтов необходимо использовать сложные алгоритмы и контроллеры.
Чрезвычайно важную роль в многоядерных процессорах играет и точность прогнозирования переходов. Чем больше в процессоре ядер, тем выше вероятность того, что одной из исполняемых команд будет именно команда перехода, способная в любое время изменить общий поток задач.
Физическая оболочка процессора
Несмотря на то, что большая часть этой статьи была посвящена сложным механизмам работы архитектуры процессора, не стоит забывать и о том, что все это должно быть создано и работать в виде реального, физического объекта.
Для того, чтобы синхронизировать работу всех компонентов процессора, используется тактовый сигнал. Современные процессоры обычно работают на частотах от 3.0 ГГц до 5.0 ГГц, и за последнее десятилетие ситуация особо не изменилась. При каждом цикле внутри чипа включаются и выключаются миллиарды транзисторов.
Такты важны для того, чтобы обеспечить идеальную работу каждой стадии вычислительного конвейера. Количество команд, обрабатываемых процессором за каждую секунду, зависит именно от них. Частоту можно увеличить путем разгона, сделав чип быстрее, но это в свою очередь повысит энергопотребление и тепловыделение.
Фото: Michael Dziedzic
Подытожим на примере
Чтобы подвести итоги, кратко рассмотрим архитектуру процессора Intel Core 2. Это было еще в 2006 году, поэтому некоторые детали могут быть устаревшими, но информации о новых разработках отсутствуют в публичном доступе.
На самом верху располагается кэш команд и буфер ассоциативной трансляции. Буфер помогает процессору определить, где в памяти располагаются необходимые команды. Эти инструкции хранятся в кэше команд первого уровня, а после этого отправляются в предекодер, так как из-за сложностей архитектуры x86 декодирование происходит во множество этапов. Сразу же за ними идет предсказатель переходов и предвыборщик кода, которые снижают вероятность возникновения потенциальных проблем со следующими командами.
Далее команды отправляются в очередь команд. Вспомните, как внеочередное исполнение позволяет процессору выбрать именно ту команду, которую практичнее всего выполнить в конкретный момент из очереди текущих инструкций. После того, как процессор определил нужную команду, та декодируется во множество микроопераций. В то время как команда может содержать сложную для ЦП задачу, микрооперации представляют собой детализированные задачи, которые процессору легче интерпретировать.
На самом деле, у каждого ядра процессора множество арифметическо-логических устройств и портов памяти. Команды отправляются в станцию резервации, пока не освободится устройство или порт. Затем команда обрабатывается с помощью кэша данных первого уровня, а полученный результат сохраняется для дальнейшего использования, после чего процессор может приступать к следующей задаче. На этом все!
Другие материалы по теме
Если вам хочется узнать больше о том, как создаются различные компоненты, описанные в этом тексте, то настоятельно советуем обратить внимание на вторую часть серии статей «Как разрабатываются и создаются процессоры?». Если же вы больше заинтересованы в том, как производятся физические оболочки процессоров, то вам стоит ознакомиться с третьей статьей той же серии.
Анализ использования циклов процессоров Intel Core 2
При анализе производительности приложений с целью ее повышения, наиболее мощной из доступных является методика детального анализа циклов центрального процессора. Традиционный анализ завершения инструкций вряд ли поможет, когда мы говорим об архитектуре, применяющей переупорядочивание инструкций (Out of Order, OOO), чья основная задача и состоит в том, что бы продолжать исполнять инструкции, пока их завершение невозможно.
Чтобы разработать методику на основе подсчета использования циклов, необходимо познакомиться с основами механизма OOO выполнения. Чрезвычайно упрощенная блок-схема представлена на рисунке 1.
После того, как инструкции декодированы в микрооперации (uops), они передаются дальше, если доступны необходимые ресурсы.
«Волшебство» такого дизайна состоит в том, что, если самая старая инструкция блокирована, например, ожиданием прибытия данных из памяти, последующие независимые инструкции, операнды которых доступны, могут быть отправлены операционным устройствам и складированы в ROB, в ожидании завершения. Они будут вытеснены, когда вся предыдущая работа завершена.
Трудность, с точки зрения анализа производительности, состоит в том, что завершение инструкций в системе происходит «пачками», то есть некоторые циклы не порождают завершенных инструкций, а некоторые сопровождаются большими их потоками. Таким образом, то, что случается в отдельном цикле на блоке завершения, ужасно неинформативно, и нужно использовать осредненные значения времени и отношений, чтобы понять, что же происходит на самом деле. Возникающая проблема состоит в том, что когда отношения используются как метрика производительности, процесс оптимизации приложения неизбежно изменит и числитель и знаменатель, ставя под сомнения сделанные успехи. Рассмотрим стандартный показатель выполнения инструкций, «Количество циклов на завершенную инструкцию» (Cycles per Instruction, CPI), в случае оптимизации цикла.
Высокое значение CPI считают признаком плохой производительности, в то время как низкое значение считают признаком хорошей производительности. Если во время процесса оптимизации цикл векторизован, то есть при компиляции генерирует инструкции из набора Streaming SIMD Extension (SSE), то число завершенных инструкций понизится значительно (в 2 или более раза). Однако количество циклов, затраченных на счет, вряд ли уменьшится в той же мере, поскольку эта оптимизация не будет влиять на такие вещи как неудачные обращения в КЭШ последнего уровня, останавливающие исполнение. В конечном счете, векторизация цикла, лишь гарантирует возрастание числа CPI. Фактически, чтобы уменьшить CPI, необходимо увеличить число завершенных инструкций!
Вместо того чтобы сосредотачиваться на метриках, исходящих из отношений числа событий жизнедеятельности центрального процессора, можно просто сосредоточиться на уменьшении числа циклов, потребляемых при выполнении желаемой работы.
Анализ использования циклов упрощает задачу, позволяя разработчику снижать индивидуальный вклад компонент, потребляющих циклы, считая их. Основным показателем в таком случае являются только циклы, и процесс оптимизации приложения всегда понижает основную метрику.
О работе ПК ч.3: От включения до полной загрузки Windows 10
Мы продолжаем разбираться как работает ПК на примере клавиатуры и Windows 10. В этой статье поговорим о том как происходит единение софта и железа.
Старт системы
Полностью компьютер выключен когда он отключен от питания и конденсаторы на материнской плате разрядились. До эры смартфонов мобильные телефоны часто глючили и если перезагрузка не лечила проблему, то приходилось доставать батарею и ждать 10 секунд, потому что сбрасывалось программное состояние ОС, в то время как чипы на материнской плате и контроллеры устройств оставались активными сохраняя состояние, драйвера ОС к ним просто реконнектились. 10 секунд — время на разрядку конденсаторов, состояние чипов сбрасывается только при полном отключении.
Если же ПК подключен к розетке или батарее, то он находится в режиме Stand-By, это значит что по шине питания подаётся маленькое напряжения (5В) от которого запитываются некоторые чипы на материнке. Как минимум это системный контроллер, по сути это мини-компьютер запускающий большой компьютер. Получив уведомление о нажатии кнопки Power он просит блок питания/батарею подать больше напряжения и после инициализирует весь чип-сет, в том числе и процессор. Инициализация включает в себя перекачку кода и данных прошивки материнки (BIOS/UEFI) в оперативную память и настройку CPU на её исполнение.
Думать что кнопка Power это рубильник который подаёт электричество на CPU и тот начинает исполнять с заранее известного адреса прошивку BIOS неправильно. Возможно старые компьютеры так и работали. Кнопка включения находится на своей плате, вместе со светодиодами состояний и к материнке она подключается через специальный разъём. На картинке ниже видны контакты для кнопки Power, Reset, а также светодиодов с состоянием Power и чтения жёсткого диска. Нажатие кнопки включения переводится в сигнал на контакты материнки, откуда он достигает системный контроллер.
Контакты на материнке для подключения кнопки включения, светодиодов состояния Power, жёсткого диска и динамиков.
Плата ноутбука с кнопкой включения и светодиодом состояния
Прикладная иллюстрация как блок питания получает сигнал от материнки на включение. Если вы задумаете установить мощную видеокарту (Nvidia 2070 S) на офисный ПК, то просто вставить её недостаточно, потому как она требует питание в 600W, в то время как такой ПК имеет блок на
500W. Первое что придёт в голову – купить новый блок питания на 650W с отдельной линией для видеокарты. Но и здесь будут разочарования, потому как разъёмы материнки будут не совпадать с разъёмами БП, а если его отдельно воткнуть в розетку и подключить к видюхе тоже ничего не будет – в блоке питания вентилятор не крутится и изображения нет. Так происходит, потому что БП должен получить сигнал от материнки на полное включение. Очевидное решение – новая материнка с совместимыми разъёмами, однако она стоит
$300. Есть решение проще, хоть оно и вызывает опасения пожаробезопасности. Берём скрепку, разгибаем и вставляем в зелёный (PS_ON) и один из чёрных пинов (COM). Теперь всё должно работать.
Поиск загрузчика ОС
Есть два вида прошивки материнки – BIOS (Basic Input Output System) на старых машинах и UEFI (Unified Extensible Firmware Interface) на новых. Windows 10 поддерживает обе и абстрагирует различия между ними. UEFI правильней называть ОС чем прошивкой, потому как он предлагает больше возможностей, к примеру богатый графический интерфейс вместо текстового, наличие мышки, больший объём доступной памяти, улучшенная модель безопасности и валидации файлов ОС, взаимодействие с железом через API, вместо прерываний как в BIOS.
Пример экрана монитора BIOS.
Программа BIOS хранится на отдельном чипе, подключенном к Южному мосту. Этот чип можно достать и перепрошить новой программой, по факту это просто носитель памяти, а не самостоятельный микрокомпьютер.
Настройки BIOS (системное время, например), хранятся на другом чипе который как правило находится возле круглой батарейки, которая на самом деле является литиевым аккумулятором, подзаряжающимся во время работы ПК. Называется он CMOS, что означает Complementary Metal Oxide Semiconductor, а по-русски просто — КМОП, что есть комплементарная структура металл-оксид-полупроводник.
Если всё прошло успешно, BIOS начинает процесс поиска загрузчика ОС. Для этого он начинает просматривать все подключенные к материнской плате жёсткие диски. Данные на физических дисках адресуются в единицах называемых сектор, обычно он 512 байт, однако современный стандарт – 4096 байт. Установщик Windows в самый первый сектор на диске записывает специальный программный код и данные о разделах. Этот сектор называется Master Boot Record. Диск разбивается на разделы (partitions), отформатированный своей файловой системой. Максимум 4 раздела, каждый из который может быть расширенным (extended partition), такой можно рекурсивно делить на 4 раздела и теоретически их число не ограничено. Как только BIOS находит Master Boot Record он считывает оттуда код и передаёт ему управление. Этот код поочередно просматривает данные о разделах и находит тот который помечен как активный, в нём находится код загрузчика Windows (Это не раздел с C:\Windows\System32!), этот раздел называется system partition. Как правило он занимает 100Мб и скрыт от пользователя. В первом секторе этого раздела хранится загрузочный код, которому передаётся управление. Это volume boot sector, код в нём ищет файл Bootmgr, с которого и начинается процесс загрузки Windows. Файл Bootmgr создан через соединение в один файлов Startup.com и Bootmgr.exe.
Запуск на UEFI
Пример экрана загрузки UEFI
BIOS существует больше 30 лет и в попытках исправить его недостатки компания Intel в 1998 году создала стандарт Intel Boot Initiative, позже переименованный в EFI и в 2005 году пожертвованный организации EFI Forum. Недостатки BIOS:
• Работает только в 16-битном режиме
• Может адресовать только 1Mb оперативной памяти
• Часто имеет проблемы совместимости
• MBR ограничен только четырьмя главными разделами диска
• Диск с ОС не может быть больше чем 2.2Tb.
• Имеет очень ограниченные возможности для валидации загрузчика ОС.
На смену BIOS пришёл UEFI, по сути это миниатюрная ОС которая может работать и в 32-bit и в 64-bit. Для совместимости есть опция Compatibility Support Module, которая включается в настройках и эмулирует работу BIOS.
Инициализация ядра
Запуск подсистем – SMSS, CSRSS, WinInit
SMSS.exe отличается от пользовательских процессов, это нативный процесс и это даёт ему дополнительные полномочия. SMSS.exe работает с ядром в обход Windows API, он использует то что называется Native API. Windows API – обёртка вокруг Native API. SMSS.exe первым делом запускает подсистему Windows (CSRSS.exe – Client Server Runtime Sub System) и заканчивает инициализацию реестра.
Процесс и потоки SMSS.exe помечены как критические, это значит что если они неожиданно завершаться, к примеру из-за ошибки, это приведёт к падению системы. Для общения с подсистемами, к примеру вызову API создающему новую сессию, SMSS создаёт ALPC-порт с именем SmApiPort. Загружаются из реестра переменные среды окружения, запускаются программы такие как Check Disk (autochk.exe, эти программы записаны в реестре HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute). SMSS.exe запускается для каждой пользовательской сессии. Глобальные переменные (очередь сообщений например) у каждой сессии своя за счёт механизма виртуальной памяти. В Windows есть контексты потока, процесса и сессии. Каждый SMSS.exe запускает свой экземпляр подсистемы, на данный момент это только CSRSS.exe (Windows), в прошлом поддерживались операционные системы OS/2 (os2ss.exe) и POSIX (psxss.exe), но эта идея была неудачной. Самый первый SMSS.exe засыпает в ожидании процесса WinInit.exe. Остальные экземпляры вместо этого создают процесс WinLogon который показывает UI для входа.
Дерево процессов выглядит следующим образом, на нём можно увидеть кто и кого создал (показаны не все процессы, может немного отличаться от последний версий Windows).
Где здесь клавиатура?
Во время запуска ядро Windows считывает из реестра информацию о контроллере системной шины, как правило это шина PCI (реже MSI), к ней подключены контроллеры портов ввода-вывода, в том числе и USB, PS/2. Информация о нём записывается во время установки Windows. Система загружает для него драйвер и рекурсивно обходит все порты так же загружая для каждого из них свой драйвер. Драйвера могут комбинироваться в узлы (driver node), к примеру драйвер клавиатуры, будет соединён с драйвером порта PS2. А вот порт USB сложнее — сначала драйвер порта, потом драйвер для работы с протоколом HID и только потом клавиатура.
Каждый порт контроллируется своим чипом, который мониторит подключение, принимает/отправляет сигналы между CPU и устройством. Если чип-сет Южный мост не встроен в CPU, как это часто делают в ноутбуках, а существует отдельным чипом на материнке, то правильней говорить: сигнал между Южным мостом и контроллером порта. Чип контроллирующий порт имеет выделенную линию с контроллером прерываний (PIC или APIC), по которой он может попросить обратить на себя внимание CPU, к примеру считать данные от клавиатуры (порт PS/2, с USB другая история). Поскольку ОС загрузила для порта драйвер, она может отдавать ему команды, читать и отправлять данные. В нашем примере был загружен драйвер из C:\Windows\System32\i8042prt.sys. Давайте вспомним предыдущую статью. В старых компьютерах с PIC на чипе Intel 8259 было 15 линий прерываний, где клавиатура была подключена к ножке IRQ1, таймер IRQ0, а мышка к IRQ12, который на самом деле был пятой ножкой второго чипа 8259, который мультиплексировал свои прерывания через ножку IRQ2 первого контроллера. В современных PIC могут быть 255 контактов для сигналов прерываний. Во время загрузки ОС программирует APIC/PIC возвращать определённое число когда скажем пришло прерывание от порта клавиатуры или USB и по этому номеру CPU находит в таблице векторов прерываний функцию которую надо выполнить. Номер прерываний определяют HAL и Plug’n’Play Manager. Контроллер прерываний ищет сигнал на своих ножках в определённом порядке, к примеру в бесконечном цикле проверяет напряжение на ножках от 1 до MAX_PIN. Этот порядок определяет приоритет, к примеру клавиатура будет замечена раньше мышки, а таймер раньше клавиатуры. Чтобы не зависеть от особенностей работы контроллеров прерываний Windows абстрагирует концепцию IRQ (Interrupt Request) в IRQL (Interrupt Request Level). Будь у контроллера прерываний хоть 15 хоть 255 линий они все будут отображены на 32 IRQL для x86 и 15 IRQL для x64 и IA64.
Механизм IRQL реализовывается на уровне софта в Hardware Abstraction Layer (HAL.dll), а не железа. В Windows системах есть драйвер шины (bus driver), который определяет наличие устройств подключенных к шинам – PCI, USB и др. и номера прерываний которые могут быть назначены каждому устройству. Драйвер шины сообщает эту информацию Plug and play manager, который уже решает какие номера прерываний назначить каждому устройству. Далее арбитр прерываний внутри PnP Mgr (PnP interrupt arbiter) устанавливает связи между IRQ и IRQL.
Так вот, драйвер клавиатуры (kbdclass.sys) получает данные от порта (USB, PS2) через прерывание и записывает их через WriteFile, компонент внутри ядра Windows просыпается, считывает их используя API ReadFile и добавляет в очередь сообщений с клавиатуры. API для работы с файлом могут использоваться для чтения данных с драйверов. С этого момента начинается обработка данных стеком ввода Windows, об этом в следующей статье.
Если у вас есть ПК с PS2 портом и вы умеете пользоваться WinDbg в режиме ядра, то можете легко найти обработчик прерываний клавиатуры напечатав команду !idt, которая выведет на экран всю таблицу векторов прерываний. Прерывание вклинивается в ход выполнения программы, слово вектор здесь подразумевает направление, направление исполнения программы. WinDbg был сделан специально для отладки Windows, самая последняя версия называется WinDbgX. Он имеет текстовый интерфейс, который отпугивает людей привыкших к Visual Studio, однако предоставляет гораздо больше возможностей, в частности исполнение скриптов. Прерывание фиолетового порта PS2 выделено красным. Функция которая его обрабатывает называется I8042KeyboardInterruptService, которая находится в файле i8042prt.sys.
Сейчас возникает вопрос, откуда у обработчика прерываний аргумент? Кто его передаёт? Ведь CPU ничего не знает о нём. Если поставите в неё breakpoint, то удивитесь ещё больше увидев несколько функций выше по стеку:
0: kd> kC
# Call Site
00 i8042prt!I8042KeyboardInterruptService
01 nt!KiCallInterruptServiceRoutine
02 nt!KiInterruptSubDispatch
03 nt!KiInterruptDispatch
04 nt!KiIdleLoop
Пару слов о USB
Ознакомление с работой порта USB потребовало бы отдельной статьи описывающей его работу и плюс описание обработки данных HID на Windows. Это очень сильно усложнило бы материал, к тому же уже есть хорошие статьи по теме, поэтому PS2 идеальный пример из-за своей простоты.
USB создавался как универсальный порт для всех устройств, будь то клавиатура, фотоаппарат, сканнер, игровой руль с педалями, принтер и пр. Вдобавок он поддерживает вложенность портов – USB материнки => монитор с USB => клавиатура с USB к которой подключена мышка, флешка и USB-hub к которому подключен жёсткий диск. Взглянув на контакты USB 2.0 вы увидите что они не заточены под передачу каких-то определённых данных, как у PS2. Их всего четыре – витая пара для передачи битов данных, плюс и минус питания.
Провода кабеля USB 2.0
USB 3.0 быстрее за счёт дополнительных пяти контактов. Как видите там нету линии CLOCK для синхронизации, поэтому логика передачи данных сложнее. Слева USB 2.0 и справа USB 3.0 для сравнения.
Все данные передаются через протокол HID (Human Interface Device), который описывает форматы, порядок взаимодействия и передачи данных и всё остальное. Стандарт USB 2.0 занимает 650 страниц, документ HID Class Specification, описывающий работу устройств (мыши, клавиатуры и пр) – 97 страниц, их рекомендуется изучить если вы работаете с USB.
Первым делом подключенное устройство должно рассказать о себе, для этого оно отправляет несколько структур данных, в которых указывается ID устройства и ID производителя по которым Plug’n’Play manager может найти в реестре информацию, загрузить и соединить драйвера. USB устройства пассивны, т.е. хост должен сам с определённым интервалом проверять наличие данных. Частота опроса и размер пакета данных задаются в одном из дескрипторов устройства USB. Максимальный размер пакета – 64 байта, что для информации о нажатых клавишах более чем достаточно.
В Windows есть встроенная поддержка HID, она не такая простая как связь драйвера порта PS2 с драйвером клавиатуры, потому что драйвер HID должен уметь обрабатывать все поддерживаемые протоколом сценарии. Вне зависимости от провайдера данных — порты PS2, USB или Remote Desktop или виртуальная машина – на самом верху driver node будет находится Kbdclass, от которого ядро ОС и будет получать информацию. Уведомление о подсоединении клавиатуры будет обрабатываться через Plug’n’Play Manager, так что для ядра Windows не имеет значение какой порт или источник данных от устройства используется.