Процессы и потоки в Windows: объяснение

Вы когда-нибудь задумывались, как работают программы на вашем компьютере? Как одновременно запускаются разные приложения, как они взаимодействуют друг с другом и с операционной системой? За все это отвечают две важные сущности: процессы и потоки. В этой статье мы расскажем, что такое процессы и потоки, зачем они нужны и как Windows управляет ими.

Что такое процессы и потоки?

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

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

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

Зачем нужны процессы и потоки?

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

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

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

Windows поддерживает вытесняющую многозадачность, что создает эффект одновременного выполнения нескольких потоков из нескольких процессов. На многопроцессорном компьютере система может одновременно выполнять столько потоков, сколько процессоров на компьютере.

Как Windows управляет процессами и потоками?

Windows управляет процессами и потоками с помощью нескольких механизмов и технологий, которые мы рассмотрим далее.

Планировщик

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

Приоритеты

Приоритет — это число от 0 до 31, которое показывает, насколько важен поток для системы. Чем выше приоритет, тем больше шансов у потока получить процессорное время. Приоритеты делятся на две группы: статические и динамические.

Статический приоритет — это базовый приоритет потока, который определяется классом приоритета его процесса и уровнем приоритета потока внутри процесса. Класс приоритета процесса может быть одним из следующих: реального времени (24-31), высокий (13-15), выше обычного (10-12), обычный (8-9), ниже обычного (6-7) или простой (4-5). Уровень приоритета потока внутри процесса может быть одним из следующих: критический (15), наивысший (2), выше обычного (1), обычный (0), ниже обычного (-1), низкий (-2) или нулевой (-15). Статический приоритет потока определяется как сумма класса приоритета процесса и уровня приоритета потока. 

Например, если процесс имеет класс приоритета выше обычного (10), а поток имеет уровень приоритета выше обычного (1), то статический приоритет потока будет равен 11.

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

Планировщик использует следующие правила для определения динамического приоритета потоков:

Если поток готов к выполнению, его динамический приоритет равен его статическому приоритету.

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

Если поток ждет завершения операции ввода-вывода или события ядра более чем 36 тиков таймера (примерно 540 миллисекунд), его динамический приоритет повышается до максимального значения 15 для обычных потоков или 31 для потоков реального времени.

Если поток выполняется на процессоре, его динамический приоритет понижается на единицу каждые два тика таймера (примерно 7,5 миллисекунд) до минимального значения 1 для обычных потоков или 16 для потоков реального времени.

Если поток выполняется на процессоре более чем 12 тиков таймера (примерно 180 миллисекунд), его динамический приоритет понижается до минимального значения 1 для обычных потоков или 16 для потоков реального времени.

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

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

Очереди

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

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

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

Планировщик использует следующие правила для работы с очередями:

Планировщик всегда проверяет очередь реального времени перед очередью процессора и выбирает для выполнения поток с наивысшим динамическим приоритетом из этой очереди.

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

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

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

Синхронизация

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

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

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

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

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

Поток может ожидать один или несколько объектов ожидания одновременно. Для этого он использует функции ядра, такие как WaitForSingleObject, WaitForMultipleObjects или WaitForMultipleObjectsEx. Эти функции проверяют состояние сигнальности объектов ожидания и возвращают результат в виде кода завершения. Код завершения показывает, какой объект ожидания перешел в состояние сигнала или какая ошибка произошла при ожидании. Если ни один из объектов ожидания не в состоянии сигнала, то функция блокирует поток до тех пор, пока не наступит одно из следующих условий:

Один из объектов ожидания перешел в состояние сигнала.
Истекло время ожидания, заданное параметром функции.
Поток получил запрос на завершение работы от другого потока или системы.
Поток получил асинхронное исключение или прерывание.

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

Заключение

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

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