Pull to refresh

Что такое нити (threads)?

Reading time 3 min
Views 69K
Навеяно предыдущей статьей на эту тему.
Для того чтобы, структурировать свое понимание – что представляют собой threads (это слово переводят на русский язык как «нити» почти везде, кроме книг по Win32 API, где его переводят как «потоки») и чем они отличаются от процессов, можно воспользоваться следующими двумя определениями:
  • Thread – это виртуальный процессор, имеющий свой собственный набор регистров, аналогичных регистрам настоящего центрального процессора. Один из наиважнейших регистров у виртуального процессора, как и у реального – это индивидуальный указатель на текущую инструкцию (например, индивидуальный регистр EIP на процессорах семейства x86),
  • Процесс – это в первую очередь адресное пространство. В современной архитектуре создаваемое ядром ОС посредством манипуляции страничными таблицами. И уже во вторую очередь на процесс следует смотреть как на точку привязки «ресурсов» в ОC. Если мы разбираем такой аспект, как многозадачность для того, чтобы понять суть threads, то нам не нужно в этот момент думать о «ресурсах» ОС типа файлов и к чему они привязаны.
Очень важно понять, что thread – это концептуально именно виртуальный процессор и когда мы пишем реализацию threads в ядре ОС или в user-level библиотеке, то мы решаем именно задачу «размножения» центрального процессора во многих виртуальных экземплярах, которые логически или даже физически (на SMP, SMT и multi-core CPU платформах) работают параллельно друг с другом.
На основном, концептуальном уровне, нет никакого «контекста». Контекст – это просто название той структуры данных, в которую ядро ОС или наша библиотека (реализующая threads) сохраняет регистры виртуального процессора, когда она переключается между ними, эмулируя их параллельную работу. Переключение контекстов – это способ реализации threads, а не более фундаментальное понятие, через которое нужно определять thread.
При подходе к определению понятия thread через анализ API конкретных ОС обычно вводят слишком много сущностей – тут тебе и процессы, и адресные пространства, и контексты, и переключения этих контекстов, и прерывания от таймера, и кванты времени с приоритетами, и даже «ресурсы», привязанные к процессам (в противовес threads). И все это сплетено в один клубок и зачастую мы видим, что идем по кругу, читая определения. Увы, это распространенный способ объяснять суть threads в книгах, но такой подход сильно путает начинающих программистов и привязывает их понимание к конкретике реализации.
Понятное дело, что все эти термины имеют право на существование и возникли не случайно, за каждым из них стоит какая-то важная сущность. Но среди них нужно выделить главные и второстепенные (введенные для реализации главных сущностей или навешанные на них сверху, уже на следующих уровнях абстракции).
Главная идея thread – это виртуализация регистров центрального процессора – эмуляция на одном физическом процессоре нескольких логических процессоров, каждый из которых имеет свое собственное состояние регистров (включая указатель команд) и работает параллельно с остальными.
Главное свойство процесса в контексте этого разговора – наличие у него своих собственных страничных таблиц, образующих его индивидуальное адресное пространство. Процесс не является сам по себе чем-то исполнимым.
Можно говорить в определении, что «у каждого процесса в системе всегда есть по крайней мере один thread». А можно сказать иначе –адресное пространство логически лишено смысла для пользователя, если оно не видно хотя бы одному виртуальному процессору (thread). Поэтому логично, что все современные ОС уничтожают адресное пространство (завершают процесс) при завершении работы последнего thread, работающего на данном адресном пространстве. И можно не говорить в определении процесса, что в нем есть «по крайней мере, один thread». Тем более, что на нижнем системном уровне процесс (как правило) может существовать как объект ОС даже не имея в своем составе threads.
Если Вы посмотрите исходники, например, ядра Windows, то Вы увидите, что адресное пространство и прочие структуры процесса конструируются до создания в нем начальной нити (начальной thread для этого процесса). По сути, изначально в процессе не существует threads вообще. В Windows можно даже создать thread в чужом адресном пространстве через user-level API…
Если смотреть на thread как на виртуальный процессор – то его привязка к адресному пространству представляет собой загрузку в виртуальный регистр базы станичных таблиц нужного значения. :) Тем более, что на нижнем уровне именно это и происходит – каждый раз при переключении на thread, связанную с другим процессом, ядро ОС перезагружает регистр указателя на страничные таблицы (на тех процессорах, которые не поддерживают на аппаратном уровне работу со многими пространствами одновременно).
Tags:
Hubs:
+29
Comments 45
Comments Comments 45

Articles