Процессы и потоки. Операционные системы

Под процессом понимается программа в стадии выполнения. Процесс можно рассматривать также как единицу работы для . Для современных типов процессоров существует и более мелкая единица работы поток или нить. Другими словами процесс может породить один и более потоков.

В чем же состоит принципиальное различие в понятиях процесс и поток. Процесс рассматривается ОС, как заявка на все виды ресурсов (память, файлы и пр.), кроме одного — процессорного времени. Поток — это заявка на процессорное время.

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

Планирование процессов и потоков

Планирование процессов и потоков включает:

  • Создание-уничтожение процессов
  • Взаимодействие между процессами
  • распределение процессорного времени
  • Обеспечение процессов необходимыми ресурсами (единолично, совместно)
  • Синхронизация (контроль за возникновением «гонок», блокировок)
  • После завершения процесса — «зачистка», т.е. удаление следов пребывания в системе

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

Для взаимодействия, процессы обращаются к ОС, которая предоставляет средства общения (конвейеры, почтовые ящики, разделяемые секции памяти и др.)

Возможность распараллеливания вычислений в рамках процесса на потоки повышает эффективность . Механизм распараллеливания вычислений для одного приложения называется многопоточной обработкой (multithreading). Потоки процесса имеют одно адресное виртуальное пространство. Распараллеливание ускоряет выполнение процесса за счет отсутствия переключения ОС с одного адресного пространства на другое, которое имеет место при выполнении процессов. Программы становятся более логичны. Особый эффект при этом достигается в мультипроцессорных системах.

Примером многопоточной обработки может служить выполнение запросов MS SQL Server

Создание процессов

Создать процесс — это создать описатель процесса (информационная структура, содержащая сведения необходимые для управления этим процессом)

Примеры описателей для:

  • Windows NT/2000/XP — объект-процесс (object-process)
  • UNIX — дескриптор процесса
  • OS/2 — управляющий блок процесса (PCB -Process Control Block)

Кроме того создать процесс — это включает также следующие действия:

  • Найти программу на диске
  • перераспределить оперативную память
  • выделить память новому процессу
  • переписать программу в выделенную память
  • изменить некоторые параметры программы

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

Создание потоков

В многопоточной системе при создании процесса создается хотя бы один поток. Для потока ОС генерирует описатель потока (идентификатор потока, данные о правах, приоритете, состояние потока и пр.).Исходное состояние потока — приостановленное.

Поток может породить другой поток — потомок. При завершения потока-родителя используются разные алгоритмы. Асинхронное завершение предполагает продолжение выполнения потоков-потомков после завершения потока-родителя. Синхронное завершение потока-родителя приводит к завершению всех его потомков.

Добрый день. Сегодня хотелось бы разобрать, что такое потоки в процессоре. Те самые, о функциях и возможностях которых большинство и не догадывается, однако любят хвастаться остальным.

Цель функции заключается в том, что на 1 ядро может одновременно обрабатывать несколько потоков данных. Пока первый поток простаивает, а второй занимается вычислением, запущенное приложение может воспользоваться вакантной логической мощью для своих целей. В результате, прерывания случаются гораздо реже, а вы не ощущаете тормозов и прочих неудобств при работе.

Недостаток технологии заключается в следующем:

  • оба потока обращаются к единой кэш-памяти 2 и 3 уровней;
  • тяжелые вычислительные процессы могут вызвать конфликт в системе.

Если очень грубо, то все кирпичи с одного места на другое можно перенести в одной руке (1 поток), либо в двух (2 потока), но человек при этом один (1 ядро) и устает одинаково при любых условиях, хоть его производительность фактически увеличивается вдвое. Иными словами, мы упираемся в производительность ЦП, а конкретней в его частоту.

8.2.1. Основные понятия

В операционной системе Windows 2000 поддерживаются традиционные процес­сы, способные общаться и синхронизироваться друг с другом так же, как это дела­ют процессы в UNIX. Каждый процесс содержит по крайней мере один поток, со­держащий, в свою очередь, как минимум одно волокно (облегченный поток). Более того, для управления определенными ресурсами процессы могут объединяться в задания. Все вместе – задания, процессы, потоки и волокна – образует общий набор инструментов для управления ресурсами и реализации параллелизма как на однопроцессорных, так и на многопроцессорных машинах.

Задание в Windows 2000 представляет собой набор, состоящий из одного или нескольких процессов, управляемых как единое целое. В частности, с каждым заданием ассоциированы квоты и лимиты ресурсов, хранящиеся в соот­ветствующем объекте задания. Квоты включают такие пункты, как максимальное количество процессов (не позволяющее процессам задания создавать бесконтроль­ное количество дочерних процессов), суммарное время центрального процессора, доступное для каждого процесса в отдельности и для всех процессов вместе, а также максимальное количество используемой памяти для процесса и для всего задания. Задания также могут ограничивать свои процессы в вопросах безопасности, на­пример, запрещать им получать права администратора (суперпользователя) даже при наличии правильного пароля.

Как и в системе UNIX, процессы представляют собой контейнеры для ресур­сов. У каждого процесса есть 4-гигабайтное адресное пространство, в котором пользователь занимает нижние 2 Гбайт (в версиях Windows 2000 Advanced Server и Datacenter Server этот размер может быть по желанию увеличен до 3 Гбайт), а операционная система занимает остальную его часть. Таким образом, операци­онная система присутствует в адресном пространстве каждого процесса, хотя она и защищена от изменений с помощью аппаратного блока управления памятью MMU. У процесса есть идентификатор процесса, один или несколько потоков, список дескрипторов (управляемых в режиме ядра) и маркер доступа, хранящий информацию защиты. Процессы создаются с помощью вызова Win32, который принимает на входе имя исполняемого файла, определяющего начальное содер­жимое адресного пространства, и создает первый поток.

Каждый процесс начинается с одного потока, но новые потоки могут создавать­ся динамически. Потоки формируют основу планирования центрального процес­сора, так как операционная система всегда для запуска выбирает поток, а не про­цесс. Соответственно, у каждого потока есть состояние (готовый, работающий, блокированный и т. д.), тогда как у процессов состояний нет. Потоки могут дина­мически создаваться вызовом Win32, которому в адресном пространстве процесса задается адрес начала исполнения. У каждого потока есть идентификатор потока, выбираемый из того же пространства, что и идентификаторы процессов, поэтому один и тот же идентификатор никогда не будет использован одновременно для процесса и для потока. Идентификаторы процессов и потоков кратны четырем, поэтому они могут использоваться в роли байтовых индексов в таблицах ядра, как и другие объекты.

Как правило, поток работает в пользовательском режиме, но когда он обраща­ется к системному вызову, то переключается в режим ядра, после чего продолжает выполнять тот же поток, с теми же свойствами и ограничениями, которые были у него в режиме пользователя. У каждого потока есть два стека – один используется в режиме ядра, а другой в режиме пользователя. Помимо состояния, идентифика­тора и двух стеков, у каждого потока есть контекст (в котором сохраняются его регистры, когда он не работает), приватная область для локальных переменных, а также может быть свой собственный маркер доступа. Если у потока есть свой мар­кер доступа, то он перекрывает маркер доступа процесса, чтобы клиентские потоки могли передать свои права доступа серверным потокам, выполняющим работу для них. Когда поток завершает свою работу, он может прекратить свое существование. Когда прекращает существование последний активный поток, процесс завершается.

Важно понимать, что потоки представляют собой концепцию планирования, а не концепцию владения ресурсами. Любой поток может получить доступ ко всем объектам его процесса. Все, что ему для этого нужно сделать, – это заполучить дес­криптор и обратиться к соответствующему вызову Win32. Для потока нет ника­ких ограничений доступа к объекту, связанных с тем, что этот объект создан или открыт другим потоком. Система даже не следит за тем, какой объект каким потоком создан. Как только дескриптор объекта помещен в таблицу дескрипторов про­цесса, любой поток процесса может его использовать.

Помимо нормальных потоков, работающих в процессах пользователя, в опера­ционной системе Windows 2000 есть множество процессов-демонов, не связанных ни с каким пользовательским процессом (они ассоциированы со специальной си­стемой или простаивающими процессами). Некоторые демоны выполняют адми­нистративные задачи, как, например, запись «грязных» (модифицированных) страниц на диск, тогда как другие формируют пул, и ими могут пользоваться компоненты исполняющей системы или драйверы, которым нужно выполнить какие-либо асинхронные за­дачи в фоновом режиме. Переключение потоков в операционной системе Windows 2000 занимает до­вольно много времени, так как для этого необходимо переключение в режим ядра, а затем возврат в режим пользователя. Для предоставления сильно облегченного псевдопараллелизма в Windows 2000 используются волокна, подобные потокам, но планируемые в пространстве пользователя создавшей их программой (или ее системой поддержки исполнения). У каждого потока может быть несколько воло­кон, так же как у процесса может быть несколько потоков, с той разницей, что когда волокно логически блокируется, оно помещается в очередь блокированных волокон, после чего для работы выбирается другое волокно в контексте того же потока. Операционная система не знает о смене волокон, так как все тот же поток продолжает работу. Так как операционная система ничего не знает о волокнах, то с ними, в отличие от заданий, процессов и потоков, не связаны объекты испол­няющей системы. Для управления волокнами нет и настоящих системных вызо­вов. Однако для этого есть вызовы Win32 API. Они относятся к тем вызовам Win32 API, которые не обращаются к системным вызовам.

Отметим, что операци­онная система Windows 2000 может работать на симметричных многопроцессор­ных системах. Это означает, что код операционной системы должен быть полнос­тью реентерабельным, то есть каждая процедура должна быть написана таким образом, чтобы два или более центральных процессора могли поменять свои пере­менные без особых проблем. Во многих случаях это означает, что программные секции должны быть защищены при помощи спин-блокировки или мьютексов, удерживающих дополнительные центральные процессоры в режиме ожидания, пока первый центральный процессор не выполнит свою работу (при помощи по­следовательного доступа к критическим областям).

Верхний предел в 32 центральных процессора является жестким пределом, так как во многих местах операционной системы для учета использования централь­ных процессоров используются битовые массивы размером в 32-разрядное машин­ное слово. Например, один однословный битовый массив используется для того, чтобы следить, какой из центральных процессоров свободен в данный момент, а другой массив используется в каждом процессе для перечисления центральных процессоров, на которых этому процессу разрешено работать. 64-разрядная версия Windows 2000 должна будет без особых усилий поддерживать до 64 центральных процессоров. Для превышения этого ограничения потребуется существенная пере­делка программы (с использованием по нескольку слов для битовых массивов).

7 ответов

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

Кроме того, даже если вы можете запустить 5000+ потоков, в зависимости от вашего оборудования, которые могут работать намного медленнее, чем 10-нить-эквивалентная программа. Я думаю, вы должны взглянуть на объединение потоков .

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

Сколько потоков, которые вы можете запускать одновременно в вашем приложении, зависит от большого количества факторов. Наилучшим номером (легким человеком) было бы количество ядер на машине, но, конечно же, как будто никто не притворяется (ни одно другое приложение):).

Честно говоря, я бы сказал, что я много занимаюсь многопоточным в.NET/Windows, потому что у человека обычно есть больше "повреждений", чем пользы, когда у вас нет реального понимания..NET имеет концепцию пула потоков, и вам нужно знать, как это работает в дополнение к Windows.

В.NET 3.5/4.0 вы должны смотреть на Задачи (Task Parallel Library), поскольку библиотека намного лучше определяет, как много нитей (если вообще) икру. С TPL threadpool получает капитальный ремонт, и он намного умнее в вопросе о размножении потоков и краже задач и т.д. Но вы обычно работаете с задачами, а не с потоками.

Это сложная область, и в результате платформа.NET ввела Задачи, чтобы отвлечь программистов от потоков и, таким образом, позволить времени выполнения быть умным об этом, в то время как программист просто скажет, что она хочет, и не столько о том, как для этого.

Каждый поток потребляет больше памяти (стек ядра, блок среды потока, thread-local, stack....). AFAIK в Windows нет явного ограничения, поэтому ограничение будет памятью (вероятно, стек для каждого потока).

В потоках Linux больше похожие процессы (с разделяемой памятью), и вы ограничены:

Cat /proc/sys/kernel/threads-max

Довольно хорошее эмпирическое правило при запуске интенсивных задач состоит в том, чтобы запустить тот же номер, что и ваш физический подсчет ядра.

Да, вы можете запускать больше задач, но они будут ждать ресурсов (или потоков в пуле потоков), и ваш ящик, независимо от размера, не может полностью распределить все основные ресурсы процессора в 100% случаев поток из-за фона/других процессов. Таким образом, чем больше задач вы создаете, тем больше потоков вы создаете, поскольку они превосходят фактические возможные параллельные потоки (1 на ядро), тем больше будет выполняться управление ресурсами, очередность и свопинг.

Тест, который мы выполнили, теперь, когда я работаю с использованием вирусного шаблона, чтобы запустить дополнительные задачи, показал, что оптимальный уровень близок к счету процессора как кепку. Задачи, запущенные при соотношении "один к одному" с физическим числом ядер, выполнялись примерно на 1 минуту за каждую задачу. Устанавливается в два раза по счетчику процессора, время задачи перешло с 1 минуты в среднем до 5 минут среднего времени для завершения. Он становится геометрически медленнее, чем больше задач, инициированных за счет ядра.

Так, например, если у вас есть 8 физических ядер, то должны быть самыми быстрыми 8 задач (и использование TPL, по существу 8 одновременных потоков в активном процессе). Существует ваш основной поток или процесс, который создает другие задачи и другие фоновые процессы, но если ящик довольно изолирован для удовольствия от использования ресурсов, они будут довольно минимальными.

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

Чтобы определить это программно, мы используем

var CoreCount = System.Environment.ProcessorCount / 2;

Зачем делиться на две, спросите вы? Потому что почти все современные процессоры используют логические ядра или гиперпотоки. Вы должны найти со своим собственным тестированием, что, если вы используете логический счет, ваша общая скорость на одну задачу и, следовательно, весь процесс значительно снизится. Физические ядра - это ключ. Мы не могли видеть быстрый способ найти физическое против логического, но быстрый обзор наших ящиков показал, что это последовательно верно. YMMV, но это может стать довольно быстрым.

Мне удалось запустить 4 потока одновременно на моем текущем старом процессоре (2005). Использование EVGA CPU burner до того, как прозвучал мой зуммер процессора. (Запрограммировано в меню BIOS). Значение я превысило 90 * c. Имейте в виду, что мы говорим о потоках данных, работающих одновременно. хорошим примером может быть одновременное открытие нескольких программ. Но в целом это зависит от того, насколько хорош ваш процессор с многозадачностью. (другими словами, можно обрабатывать многие активные потоки). Безопасным способом тестирования является загрузка "ocscanner (By EVGA)" и "CPU Thermometer" с использованием центрального процессора в OC Scanner. Во время тестирования убедитесь, что ваша температура не превышает 90 * c (или любую температуру, в которой вы чувствуете себя в безопасности) и посмотрите на текущее количество потоков, которые вы запускаете, бросили ваш процессор. начните с 2 потоков, подождите 3-5 минут, наблюдая температуру процессора, добавьте еще один поток, повторите. (НЕ ПРИНИМАЙТЕ СВОЮ УДАЧУ!!!) (НЕ ПОПЫТАЙТЕ, ЕСЛИ ТЕРМОМЕТР ЦП НЕ МОЖЕТ СДЕЛАТЬ ВАШУ ТЕМПЕРАТУРУ!!!)

  • Tutorial

В этой статье я попытаюсь описать терминологию, используемую для описания систем, способных исполнять несколько программ параллельно, то есть многоядерных, многопроцессорных, многопоточных. Разные виды параллелизма в ЦПУ IA-32 появлялись в разное время и в несколько непоследовательном порядке. Во всём этом довольно легко запутаться, особенно учитывая, что операционные системы заботливо прячут детали от не слишком искушённых прикладных программ.

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

Предупреждение о знаках ®, ™, в статье

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

Процессор

Конечно же, самый древний, чаще всего используемый и неоднозначный термин - это «процессор».

В современном мире процессор - это то (package), что мы покупаем в красивой Retail коробке или не очень красивом OEM-пакетике. Неделимая сущность, вставляемая в разъём (socket) на материнской плате. Даже если никакого разъёма нет и снять его нельзя, то есть если он намертво припаян, это один чип.

Мобильные системы (телефоны, планшеты, ноутбуки) и большинство десктопов имеют один процессор. Рабочие станции и сервера иногда могут похвастаться двумя или больше процессорами на одной материнской плате.

Поддержка нескольких центральных процессоров в одной системе требует многочисленных изменений в её дизайне. Как минимум, необходимо обеспечить их физическое подключение (предусмотреть несколько сокетов на материнской плате), решить вопросы идентификации процессоров (см. далее в этой статье, а также мою предыдущую заметку), согласования доступов к памяти и доставки прерываний (контроллер прерываний должен уметь маршрутизировать прерывания на несколько процессоров) и, конечно же, поддержки со стороны операционной системы. Я, к сожалению, не смог найти документального упоминания момента создания первой многопроцессорной системы на процессорах Intel, однако Википедия утверждает , что Sequent Computer Systems поставляла их уже в 1987 году, используя процессоры Intel 80386. Широко распространённой поддержка же нескольких чипов в одной системе становится доступной, начиная с Intel® Pentium.

Если процессоров несколько, то каждый из них имеет собственный разъём на плате. У каждого из них при этом имеются полные независимые копии всех ресурсов, таких как регистры, исполняющие устройства, кэши. Делят они общую память - RAM. Память может подключаться к ним различными и довольно нетривиальными способами, но это отдельная история, выходящая за рамки этой статьи. Важно то, что при любом раскладе для исполняемых программ должна создаваться иллюзия однородной общей памяти, доступной со всех входящих в систему процессоров.


К взлёту готов! Intel® Desktop Board D5400XS

Ядро

Исторически многоядерность в Intel IA-32 появилась позже Intel® HyperThreading, однако в логической иерархии она идёт следующей.

Казалось бы, если в системе больше процессоров, то выше её производительность (на задачах, способных задействовать все ресурсы). Однако, если стоимость коммуникаций между ними слишком велика, то весь выигрыш от параллелизма убивается длительными задержками на передачу общих данных. Именно это наблюдается в многопроцессорных системах - как физически, так и логически они находятся очень далеко друг от друга. Для эффективной коммуникации в таких условиях приходится придумывать специализированные шины, такие как Intel® QuickPath Interconnect. Энергопотребление, размеры и цена конечного решения, конечно, от всего этого не понижаются. На помощь должна прийти высокая интеграция компонент - схемы, исполняющие части параллельной программы, надо подтащить поближе друг к другу, желательно на один кристалл. Другими словами, в одном процессоре следует организовать несколько ядер , во всём идентичных друг другу, но работающих независимо.

Первые многоядерные процессоры IA-32 от Intel были представлены в 2005 году. С тех пор среднее число ядер в серверных, десктопных, а ныне и мобильных платформах неуклонно растёт.

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


Микроснимок четырёхядерного процессора Intel с кодовым именем Nehalem. Выделены отдельные ядра, общий кэш третьего уровня, а также линки QPI к другим процессорам и общий контроллер памяти.

Гиперпоток

До примерно 2002 года единственный способ получить систему IA-32, способную параллельно исполнять две или более программы, состоял в использовании именно многопроцессорных систем. В Intel® Pentium® 4, а также линейке Xeon с кодовым именем Foster (Netburst) была представлена новая технология - гипертреды или гиперпотоки, - Intel® HyperThreading (далее HT).

Ничто не ново под луной. HT - это частный случай того, что в литературе именуется одновременной многопоточностью (simultaneous multithreading, SMT). В отличие от «настоящих» ядер, являющихся полными и независимыми копиями, в случае HT в одном процессоре дублируется лишь часть внутренних узлов, в первую очередь отвечающих за хранение архитектурного состояния - регистры. Исполнительные же узлы, ответственные за организацию и обработку данных, остаются в единственном числе, и в любой момент времени используются максимум одним из потоков. Как и ядра, гиперпотоки делят между собой кэши, однако начиная с какого уровня - это зависит от конкретной системы.

Я не буду пытаться объяснить все плюсы и минусы дизайнов с SMT вообще и с HT в частности. Интересующийся читатель может найти довольно подробное обсуждение технологии во многих источниках, и, конечно же, в Википедии . Однако отмечу следующий важный момент, объясняющий текущие ограничения на число гиперпотоков в реальной продукции.

Ограничения потоков
В каких случаях наличие «нечестной» многоядерности в виде HT оправдано? Если один поток приложения не в состоянии загрузить все исполняющие узлы внутри ядра, то их можно «одолжить» другому потоку. Это типично для приложений, имеющих «узкое место» не в вычислениях, а при доступе к данным, то есть часто генерирующих промахи кэша и вынужденных ожидать доставку данных из памяти. В это время ядро без HT будет вынуждено простаивать. Наличие же HT позволяет быстро переключить свободные исполняющие узлы к другому архитектурному состоянию (т.к. оно как раз дублируется) и исполнять его инструкции. Это - частный случай приёма под названием latency hiding, когда одна длительная операция, в течение которой полезные ресурсы простаивают, маскируется параллельным выполнением других задач. Если приложение уже имеет высокую степень утилизации ресурсов ядра, наличие гиперпотоков не позволит получить ускорение - здесь нужны «честные» ядра.

Типичные сценарии работы десктопных и серверных приложений, рассчитанных на машинные архитектуры общего назначения, имеют потенциал к параллелизму, реализуемому с помощью HT. Однако этот потенциал быстро «расходуется». Возможно, по этой причине почти на всех процессорах IA-32 число аппаратных гиперпотоков не превышает двух. На типичных сценариях выигрыш от использования трёх и более гиперпотоков был бы невелик, а вот проигрыш в размере кристалла, его энергопотреблении и стоимости значителен.

Другая ситуация наблюдается на типичных задачах, выполняемых на видеоускорителях. Поэтому для этих архитектур характерно использование техники SMT с бóльшим числом потоков. Так как сопроцессоры Intel® Xeon Phi (представленные в 2010 году) идеологически и генеалогически довольно близки к видеокартам, на них может быть четыре гиперпотока на каждом ядре - уникальная для IA-32 конфигурация.

Логический процессор

Из трёх описанных «уровней» параллелизма (процессоры, ядра, гиперпотоки) в конкретной системе могут отсутствовать некоторые или даже все. На это влияют настройки BIOS (многоядерность и многопоточность отключаются независимо), особенности микроархитектуры (например, HT отсутствовал в Intel® Core™ Duo, но был возвращён с выпуском Nehalem) и события при работе системы (многопроцессорные сервера могут выключать отказавшие процессоры в случае обнаружения неисправностей и продолжать «лететь» на оставшихся). Каким образом этот многоуровневый зоопарк параллелизма виден операционной системе и, в конечном счёте, прикладным приложениям?

Далее для удобства обозначим количества процессоров, ядер и потоков в некоторой системе тройкой (x , y , z ), где x - это число процессоров, y - число ядер в каждом процессоре, а z - число гиперпотоков в каждом ядре. Далее я буду называть эту тройку топологией - устоявшийся термин, мало что имеющий с разделом математики. Произведение p = xyz определяет число сущностей, именуемых логическими процессорами системы. Оно определяет полное число независимых контекстов прикладных процессов в системе с общей памятью, исполняющихся параллельно, которые операционная система вынуждена учитывать. Я говорю «вынуждена», потому что она не может управлять порядком исполнения двух процессов, находящихся на различных логических процессорах. Это относится в том числе к гиперпотокам: хотя они и работают «последовательно» на одном ядре, конкретный порядок диктуется аппаратурой и недоступен для наблюдения или управления программам.

Чаще всего операционная система прячет от конечных приложений особенности физической топологии системы, на которой она запущена. Например, три следующие топологии: (2, 1, 1), (1, 2, 1) и (1, 1, 2) - ОС будет представлять в виде двух логических процессоров, хотя первая из них имеет два процессора, вторая - два ядра, а третья - всего лишь два потока.


Windows Task Manager показывает 8 логических процессоров; но сколько это в процессорах, ядрах и гиперпотоках?


Linux top показывает 4 логических процессора.

Это довольно удобно для создателей прикладных приложений - им не приходится иметь дело с зачастую несущественными для них особенностями аппаратуры.

Программное определение топологии

Конечно, абстрагирование топологии в единственное число логических процессоров в ряде случаев создаёт достаточно оснований для путаницы и недоразумений (в жарких Интернет-спорах). Вычислительные приложения, желающие выжать из железа максимум производительности, требуют детального контроля над тем, где будут размещены их потоки: поближе друг к другу на соседних гиперпотоках или же наоборот, подальше на разных процессорах. Скорость коммуникаций между логическими процессорами в составе одного ядра или процессора значительно выше, чем скорость передачи данных между процессорами. Возможность неоднородности в организации оперативной памяти также усложняет картину.

Информация о топологии системы в целом, а также положении каждого логического процессора в IA-32 доступна с помощью инструкции CPUID. С момента появления первых многопроцессорных систем схема идентификации логических процессоров несколько раз расширялась. К настоящему моменту её части содержатся в листах 1, 4 и 11 CPUID. Какой из листов следует смотреть, можно определить из следующей блок-схемы, взятой из статьи :

Я не буду здесь утомлять всеми подробностями отдельных частей этого алгоритма. Если возникнет интерес, то этому можно посвятить следующую часть этой статьи. Отошлю интересующегося читателя к , в которой этот вопрос разбирается максимально подробно. Здесь же я сначала кратко опишу, что такое APIC и как он связан с топологией. Затем рассмотрим работу с листом 0xB (одиннадцать в десятичном счислении), который на настоящий момент является последним словом в «апикостроении».

APIC ID
Local APIC (advanced programmable interrupt controller) - это устройство (ныне входящее в состав процессора), отвечающее за работу с прерываниями, приходящими к конкретному логическому процессору. Свой собственный APIC есть у каждого логического процессора. И каждый из них в системе должен иметь уникальное значение APIC ID. Это число используется контроллерами прерываний для адресации при доставке сообщений, а всеми остальными (например, операционной системой) - для идентификации логических процессоров. Спецификация на этот контроллер прерываний эволюционировала, пройдя от микросхемы Intel 8259 PIC через Dual PIC, APIC и xAPIC к x2APIC .

В настоящий момент ширина числа, хранящегося в APIC ID, достигла полных 32 бит, хотя в прошлом оно было ограничено 16, а ещё раньше - только 8 битами. Нынче остатки старых дней раскиданы по всему CPUID, однако в CPUID.0xB.EDX возвращаются все 32 бита APIC ID. На каждом логическом процессоре, независимо исполняющем инструкцию CPUID, возвращаться будет своё значение.

Выяснение родственных связей
Значение APIC ID само по себе ничего не говорит о топологии. Чтобы узнать, какие два логических процессора находятся внутри одного физического (т.е. являются «братьями» гипертредами), какие два - внутри одного процессора, а какие оказались и вовсе в разных процессорах, надо сравнить их значения APIC ID. В зависимости от степени родства некоторые их биты будут совпадать. Эта информация содержится в подлистьях CPUID.0xB, которые кодируются с помощью операнда в ECX. Каждый из них описывает положение битового поля одного из уровней топологии в EAX (точнее, число бит, которые нужно сдвинуть в APIC ID вправо, чтобы убрать нижние уровни топологии), а также тип этого уровня - гиперпоток, ядро или процессор, - в ECX.

У логических процессоров, находящихся внутри одного ядра, будут совпадать все биты APIC ID, кроме принадлежащих полю SMT. Для логических процессоров, находящихся в одном процессоре, - все биты, кроме полей Core и SMT. Поскольку число подлистов у CPUID.0xB может расти, данная схема позволит поддержать описание топологий и с бóльшим числом уровней, если в будущем возникнет необходимость. Более того, можно будет ввести промежуточные уровни между уже существующими.

Важное следствие из организации данной схемы заключается в том, что в наборе всех APIC ID всех логических процессоров системы могут быть «дыры», т.е. они не будут идти последовательно. Например, во многоядерном процессоре с выключенным HT все APIC ID могут оказаться чётными, так как младший бит, отвечающий за кодирование номера гиперпотока, будет всегда нулевым.

Отмечу, что CPUID.0xB - не единственный источник информации о логических процессорах, доступный операционной системе. Список всех процессоров, доступный ей, вместе с их значениями APIC ID, кодируется в таблице MADT ACPI .

Операционные системы и топология

Операционные системы предоставляют информацию о топологии логических процессоров приложениям с помощью своих собственных интерфейсов.

В Linux информация о топологии содержится в псевдофайле /proc/cpuinfo , а также выводе команды dmidecode . В примере ниже я фильтрую содержимое cpuinfo на некоторой четырёхядерной системе без HT, оставляя только записи, относящиеся к топологии:

Скрытый текст

ggg@shadowbox:~$ cat /proc/cpuinfo |grep "processor\|physical\ id\|siblings\|core\|cores\|apicid" processor: 0 physical id: 0 siblings: 4 core id: 0 cpu cores: 2 apicid: 0 initial apicid: 0 processor: 1 physical id: 0 siblings: 4 core id: 0 cpu cores: 2 apicid: 1 initial apicid: 1 processor: 2 physical id: 0 siblings: 4 core id: 1 cpu cores: 2 apicid: 2 initial apicid: 2 processor: 3 physical id: 0 siblings: 4 core id: 1 cpu cores: 2 apicid: 3 initial apicid: 3

В FreeBSD топология сообщается через механизм sysctl в переменной kern.sched.topology_spec в виде XML:

Скрытый текст

user@host:~$ sysctl kern.sched.topology_spec kern.sched.topology_spec: 0, 1, 2, 3, 4, 5, 6, 7 0, 1, 2, 3, 4, 5, 6, 7 0, 1 THREAD groupSMT group 2, 3 THREAD groupSMT group 4, 5 THREAD groupSMT group 6, 7 THREAD groupSMT group

В MS Windows 8 сведения о топологии можно увидеть в диспетчере задач Task Manager.