Реализация процессов и потоков

Процессы и потоки имеют большее значение и являются более сложными, чем за­дания и волокна. Процесс со­здается другим процессом при помощи вызова интерфейса Win32 CreateProcess. Этот вызов обращается (в режиме пользователя) к процедуре в динамической биб­лиотеке kernel32.dll, которая в несколько этапов создает процесс, используя при этом множество системных вызовов и других действий.

Создание потока также состоит из нескольких этапов. Сначала работающий процесс обращается к функции CreateThread, которая вызывает процедуру внутри kernel32.dll. Эта процедура вы­деляет в вызывающем процессе память для стека режима пользователя, а затем обращается к системному вызову NtCreateThread, чтобы создать объект потока для исполняющей системы, проинициализировать его, а также создать и проинициализировать блок управления потоком. Затем поток начинает работу с собственной инициализации.

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

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

Текущий поток выполняет программу планировщика при одном из следующих условий:

1) поток блокируется на семафоре, мьютексе, событии, операции ввода-выво­да и т. д;

2) поток сигнализирует каким-либо объектом (например, выполняет операцию up на семафоре);

3) истекает квант времени работающего потока.

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

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

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

Планировщик также вызывается при еще двух условиях:

1) завершается операция ввода-вывода;

2) истекает ожидание таймера.

В первом случае какой-нибудь поток, возможно, ожидал окончания этой опе­рации ввода-вывода и теперь может продолжить свою работу. Необходимо опре­делить, должен ли этот поток прервать выполнение текущего потока, так как по­токам не гарантируется минимальный рабочий интервал времени. Планировщик не запускается во время работы самой процедуры обработки прерываний (так как при этом прерывания могут оказаться запрещенными на слишком долгий срок). Вместо этого отложенный вызов процедуры устанавливается в очередь и вы­полняется немного позднее, после того как процедура обработки прерываний закончит свою работу. Во втором случае поток выполнил операцию down на сема­форе или блокировался на каком-либо другом объекте, но установленное время ожидания истекло. И в этом случае обработчик прерываний должен установить процедуру в очередь, чтобы она не была запущена во время работы обработчика преры­ваний. Если в результате тайм-аута поток оказался готовым к работе, будет запу­щен планировщик, и если ничего более важного в данный момент нет, будет вы­полнен отложенный вызов процедуры.

Теперь рассмотрим сам алгоритм планирования. Интерфейс Win32 API содержит два вызова, предоставляющих процессам возможность влиять на пла­нирование потоками. Алгоритм планирования в значительной степени определяется этими вызовами.

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

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

Планировщик работает следующим образом. В системе существует 32 уровня приоритета, пронумерованные от 0 до 31. 42 комбинации отображаются на эти 32 приоритета, определяя базовый приоритет потока. Кроме того, у каждого потока есть текущий приоритет, кото­рый может быть выше (но не ниже) базового приоритета.

Чтобы использовать эти приоритеты для планирования, система содержит мас­сив из 32 элементов, соответствующих приоритетам от 0 до 31. Каждый элемент массива указывает на начало списка готовых пото­ков с соответствующим приоритетом. Базовый алгоритм планирования состоит из процедуры сканирования массива от приоритета 31 до приоритета 0. Как только найден непустой элемент, выбирается поток в начале очереди и запускается на один квант времени. Когда квант истекает, поток направляется в конец очереди своего приоритета, а следующим выбирается поток в начале очереди. Другими сло­вами, когда есть несколько готовых потоков с наивысшим уровнем приоритета, они запускаются поочередно, получая каждый по одному кванту времени. Если гото­вых потоков нет, запускается бездействующий поток.

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

В системе Windows 2000 Professional длительность кванта по умолчанию равна 20 мс; на однопроцессорных серверах его значение равно 120 мс; на многопроцессорных системах используют­ся различные другие варианты в зависимости от частоты процессора. Более ко­роткий квант улучшает работу интерактивных процессов, тогда как более длин­ный квант снижает количество переключений контекста и тем самым увеличивает производительность. Значения по умолчанию при желании могут быть увеличены в 2, 4 или 6 раз.

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


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: