Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
экзамен СП.docx
Скачиваний:
13
Добавлен:
22.04.2019
Размер:
515.86 Кб
Скачать

19. Многозадачность, многозадачные операционные системы, особенности выполнения приложений в многозадачной среде.

20. Состояния процессов (многозадачная среда). 21. Многозадачность в Win32. Планирование выполнения программ Win32. 22. Процессы (process) Win32. Атрибуты и состояния процессов. Порождение процессов и управление ими. 23. Потоки (thread) и многопотоные приложения. Порождение потоков, состояния потоков, управление ими.

24. Нити (fiber) - альтернативное управление выполнением программы. 25. Распределение времени выполнения программ в многозадачной системе. Приоритеты. 26. Приоритеты процессов и потоков Win32. Управление приоритетами.

Модель многозадачности Win 32 ( 19 – 26 )

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

Синонимом термина вычислительный процесс обычно является термин задача. В дальнейшем по возможности будут использоваться именно он, т.к. в контексте Win 32 термин процесс имеет собственное специфическое значения. В свою очередь, задаче синонимично приложение, однако в контексте Win 32 под приложением чаще понимается конкретно оконное приложение (см. раздел окна и сообщения)

В многозадачной среде одновременно может выполняться более одной задачи (вычислительного процесса). Истинное параллельное выполнение задач возможно только на ЭВМ с соответствующей архитектурой – параллельных ЭВМ (см. раздел параллельные вычис­лительные системы). Для ЭВМ обычного (последовательного) типа доступно лишь псевдо­параллельное выполнение задач, обычно реализуемое на основании квантования времени: каждой задаче выделяется определенный промежуток (квант) времени, в течение которого центральный процессор обслуживает именно эту задачу, затем управление пере­дается следу­ющей задаче. Все функции по переключению задач и контролю выполняются планиров­щиком задач, являющимся неотъемлемой частью многозадачной ОС. Решение о моментах и порядке передачи управления принимается на основании дисциплины плани­рования и системы приоритетов, специфичных для каждой конкретной ОС.

Выделяются 2 основных типа многозадачности:

1) истинная или вытесняющая (preemptive) – смена активной задачи производится по инициативе планировщика, независимо от "желания" задачи и, как правило, прозрачно для нее (естественно, выбор ограничивается только готовыми к исполнению задачами, см. состояния процесса);

2) корпоративная или невытесняющая (not-preemptive) – переключение на новую задачу возможно только в моменты, выбираемые текущей задачей.

Вытесняющая многозадачность естественна для Unix, OS/2, Win 32. Примерами ОС с корпоративной многозадачностью могут служить Win 16 и сетевые ОС Novell Netware. Вытесняющая многозадачность обеспечивает большую управляемость прикладных задач, но корпоративная может быть более эффективной.

В Win 32 определены два основных объекта, представляющих выполняющуюся задачу: процесс и поток.

Процесс (process) в терминах Win 32 – динамический объект, ассоциируемый с выполняющейся программой. Каждый процесс имеет свое собственное, изолированное от других процессов, виртуальное адресное пространство в 4 Гбайт, а также собственное пространство описателей объектов, которыми он владеет. Процесс – базовый объект, которому ОС (точнее, системные планировщики ресурсов) распределяют ресурсы.

Поток (thread) – динамический объект, ассоциируемый с последовательностью ("по­током") команд, выполняемой независимо и асинхронно по отношению к другим подоб­ным последовательностям. Поток – базовый объект, которому ОС (точнее, системный плани­ровщик задач) распределяет время центрального процессора.

Каждый процесс имеет как минимум один главный (первичный – primary) поток. Однако главный поток может порождать подчиненные (вторичные) потоки, которые будут выполняться одновременно с ним, равно как и с потоками прочих процессов.

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

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

Помимо потоков, начиная с Windows NT 3.51 (SP3) и Windows 98, введена еще один объект – т.н. нить (fiber). Нить, подобно потоку, также участвует в распределении процес­сорного времени, но принцип управления ими совершенно иной и напоминает реализацию т.н. корпоративной или невытесняющей многозадачности. Нити функционируют в рамках потоков, следовательно, потоки и нити не являются альтернативными механизмами.

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

Примечание. В ранней русскоязычной литературе по Win 32 термин "thread" мог переводиться и как "поток", и как "нить", т.е. "поток" и "нить" считались синонимами. В связи с реальным появлением объекта "fiber", обладающего собственным набором свойств, подобная синонимичность стала некорректной. Применительно к OS/2 для того же термина употреблялся перевод "шаг".

Состояния процессов (потоков)

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

A – выполнение или активность (active) – процесс контролирует все свои ресурсы, включая выделенное ему процессорное время, т.е. выполняется;

R – готовность (ready) – процесс контролирует все ресурсы, кроме процессорного времени, по предоставлении которого немедленно активизируется;

W – ожидание или сон (wait) – процесс не имеет всех необходимых ресурсов и ожидает их предоставления, при наступлении соответствующих условий планируется активизация; помимо запросов на выделение тех или иных ресурсов, к этому состоянию, как правило, приводят и все прочие системные запросы, длительность выполнения которых заранее не определена;

P – пассивное (passive) – процесс не может выполняться в настоящее время, и его активизация не планируется (с точки зрения планировщика!);

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

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

Особо следует коснуться состояния suspend (см. управление потоками). В этом состоянии вычислительный процесс обладает требуемыми ресурсами, может выполняться, но приостановлен – принудительно исключен из числа конкурирующих за процессорное время. Наиболее близко это соответствует пассивному состоянию P, с тем отличием, что активизация процесса возможна в любой момент.

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

Следует напомнить, что единицей планирования в Win 32 является поток, поэтому следует иметь в виду состояния именно потоков. Так, состояние однопоточного процесса фактически есть состояние его единственного потока, а в многопоточном процессе различ­ные потоки могут находиться в различных состояниях в общем независимо друг от друга.

Более подробно о переключении состояний и их использовании см. темы "Управление процессами и потоками...", "Синхронизация...".

Основы управления процессами Win32

Процесс Win 32 – основная единица планирования ресурсов. Процесс владеет обособ­ленным (изолированным) пространством адресов памяти и пространствами описателей объектов различного рода. Адресное пространство процесса является виртуальным (см. под­система памяти) и делится на системную область, общую для всех процессов, но защищен­ную от них (в Win NT), и пользовательскую, индивидуальную для каждого процесса. Описатели представляют собой указатели (индексы) в таблицах, описывающих созданные объекты. Процесс считается владельцем объекта, но непосредственного доступа к нему не имеет, реально объект контролируется системой. Пространства описателей также изолиро­ванные: описатели в различных процессах не связаны друг с другом независимо от их числовых значений.

Процессы в Win 32 идентифицируются двояко: с помощью идентификатора (тип DWORD) и описателя (тип HANDLE). Идентификатор (PID) всегда уникален в рамках систе­мы, но описатель действует только в рамках одного процесса. Кроме того, имеется т.н. ло­кальный описатель или pseudo-handle, определенный для самого процесса и численно равный 0xFFFFFFFF.

Процесс в Win 32 всегда порождается из некоторого исполняемого файла. Клас­сический вызов Unix fork() хотя и декларирован в Win 32 API, но реально не работает.

Основной функцией создания процесса является CreateProcess(), имеющая достаточно сложный формат вызова; одновременно с процессом создается и его первичный (primary) поток.

BOOL CreateProcess(

LPCTSTR lpAppName, //имя приложения

LPTSTR lpCmdLine, //командная строка с параметрами в обычном формате

LPSECURITY_ATTRIBUTES lpProcessAttrib, //атрибуты безопасности процесса

LPSECURITY_ATTRIBUTES lpThreadAttrib, //атрибуты безопасности потока

BOOL bInheritHandles, //флаг наследования описателей от процесса-родителя

DWORD dwCreationFlags, //флаги создания процесса

LPVOID lpEnvironment, //указатель блока параметров окружения

LPCTSTR lpCurDirectory, //имя текущего каталога для процесса

LPSTARTUPINFO lpStartupInfo, //блок начальных параметров

LPPROCESS_INFORMATION lpProcessInfo //структура данных созданного процесса

)

Функция возвращает признак успешности создания процесса.

Об атрибутах защиты и правах доступа (см. ниже) более подробно см. подсистема безопасности Windows NT. Если структура атрибутов защиты не задана (NULL), средства безопасности не задействуются. В ОС Windows 9x/Me параметры безопасности игнори­руются всегда.

Определены также функции, представляющие собой "надстройку" над базовой CreateProcess() (детализация формата вызова опущена):

WinExec() – порождение процесса из исполняемого файла с упрощенным форматом;

LoadModule() – загрузка исполняемого файла или динамической библиотеки (DLL);

ShellExecute() – порождение процесса из исполняемого файла или активизация приложения, ассоциированного с файлами этого типа и передача его (файла) на обработку;

exec..() – семейство функций стандартной библиотеки RTL.

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

Флаги создания процесса представляют собой битовую OR-маску, отдельные разряды которой указывают на дополнительные возможности и особенности поведения процесса:

CREATE_NEW_CONSOLE – старт консольного приложения в новой консоли;

CREATE_DETACHED_PROCESS – без доступа к консоли родителя;

CREATE_NEW_PROCESS_GROUP – процесс – лидер группы (для консольных);

CREATE_NO_WINDOW – консольное приложение без окна;

CREATE_SUSPENDED – процесс с неактивным первичным потоком;

DEBUG_PROCESS – открыть созданный процесс для отладки;

и т.д.

В том же параметре может быть задан класс приоритета создаваемого процесса (см. планирование и приоритеты).

Структура STARTUPINFO, передаваемая в функцию по указателю, если данный параметр не NULL, может быть использована для задания внешнего вида главного окна порождаемого приложения. Содержимое этой структуры будет использовано системой при первом вызове функций CreateWindow() и ShowWindow() в порожденном процессе, либо при создании окна консоли консольного приложения. Сам порожденный процесс может получить доступ к этой информации посредством вызова GetStartupInfo().

Как идентификатор, так и описатель процесса могут быть получены из структуры PROCESS_INFORMATION, заполняемой при создании процесса функцией CreateProcess.

typedef struct _PROCESS_INFORMATION {

HANDLE hProcess; // описатель процесса

HANDLE hThread; // описатель первичного потока процесса

DWORD dwProcessId; // идентификатор процесса

DWORD dwThreadId; // идентификатор первичного потока

} PROCESS_INFORMATION;

Определены функции для получения процессом собственного идентификатора и описателя: GetCurrentProcessId() и GetCurrentProcess() соответственно.

Процесс, обладающий идентификатором другого процесса, может получить и его описатель (в рамках прав доступа к процессу и собственных полномочий):

HANDLE OpenProcess(

DWORD dwDesiredAccess // запрашиваемые права доступа

BOOL bInheritHandle, // флаг возможности наследования полученного описателя

DWORD dwProcessId // идентификатор "открываемого" процесса

)

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

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

Для корректного завершения процесса может быть использована функция, вызыва­емая самим процессом:

void ExitProcess(UINT uExitCode)

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

ExitProcess() будет вызван автоматически при выполнении оператора return головного модуля программы main или WinMain либо при вызове функции RTL exit() (язык C/C++), аналогично ведут себя и программы, созданные в других средах.

Для "жесткого" завершения произвольного (в пределах прав и привелегий) процессо служит функция

BOOL TerminateProcess(HANDLE hProcess,DWORD dwExitCode)

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

Код возврата завершившегося процесса может быть получен с помощью функции:

BOOL GetExitCodeProcess(

HANDLE hProcess, // описатель процесса

LPDWORD lpdwExitCode // указатель на переменную, которой передается значение

)

Возвращаемое функцией значение – признак успешности ее выполнения.

Пример. Программа, запускающая внешнее приложение (MS Word)

#include <windows.h>

#include <conio.h>

#include <stdio.h>

main()

{

PROCESS_INFORMATION pi;

STARTUPINFO si;

ZeroMemory(&si,sizeof(si)); //все обнулить

si.cb = sizeof(si); //только устанавливаем поле размера структуры

printf("Press any key to start WinWord -- ");

getch();

CreateProcess( //создание процесса

NULL, //имя программы – не используется

"WinWord", //командная строка – имя программы, без параметров, поиск

NULL, //параметры защиты процесса – по умолчанию

NULL, //параметры защиты потока – по умолчанию

FALSE, //флаг наследования – не наследовать объекты родителя

0, //доп. флаги порождения процесса – все стандартно (по умолчанию)

NULL, //параметры окружения – наследуются родительские

NULL, //начальный директорий – наследуется родительский

&si, //блок начальных параметров окна процесса

&pi); //информация о процессе

return 0 ; //и завершение, порожденный процесс остается

}

Основы управления потоками Win32

Поток Win 32 – базовый объект планирования выполнения задач и распределения процессорного времени. Каждый процесс имеет первоначально один первичный (главный, primary) поток и может затем создавать произвольное (ограниченное доступными ресурсами) количество вторичных потоков. Потоки одного процесса, равно как и потоки различных процессов, выполняются одновременно, асинхронно и независимо друг от друга.

Переключение потоков осуществляется планировщиком задач прозрачно для самих потоков. Текущее состояние потока описывается его контекстом исполнения, куда входят регистры процессора, переменные окружения, стеки ядра и пользователя. Заполнение структуры контекста происходит в те моменты, когда планировщик "отбирает" управление у потока. Продолжительность распределяемого потоку кванта времени в различных ОС семей­ства Win 32 составляет 10..20 мс, но величина эта недостаточно стабильна, и ориентиро­ваться на нее, например, при измерении интервалов времени, проблематично. Уменьшение кванта повышает оперативность реагирования системы на события, но увеличивает непроиз­водительные затраты на переключение потоков.

Все потоки одного процесса находятся в его адресном пространстве (за исключением раздельных стеков) и могут без ограничений пользоваться глобальными переменными и объектами (при соблюдении требований синхронизации).

С точки зрения программиста поток описывается как вполне "нормальная" функция, однако вызов ее осуществляется не явно, а путем передачи ее адреса в функцию Create­Thread. После этого функция начинает выполняться в новом отдельном потоке, и все ее действия, включая вызовы других функций, остаются в рамках этого потока. Поток завер­шается завершением его функции.

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

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

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

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttrib, //атрибуты защиты потока

DWORD dwStackSize, //размер стека потока

LPTHREAD_START_ROUTINE lpStartAddr, //адрес функции потока

LPVOID lpParam, //параметр функции потока

DWORD dwCreationFlags, //флаги создания потока

LPDWORD lpThreadId //указатель переменной – идентификатора созданного потока

)

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

Типу LPTHREAD_START_ROUTINE соответствует следующий формат функции:

DWORD WINAPI ThreadRoutineName(LPVOID lpParam).

Наличие передаваемого параметра и возвращаемого значения обязательны, но их интерпретация в программе произвольна (целое значение, указатель, игнорирование).

В качестве флагов создания потока может быть задано значение CREATE_SUSPENDED – создавать поток в приостановленном состоянии.

Доступ к потоку может быть получен функцией OpenThread(), формат которой подобен OpenProcess():

HANDLE OpenThread(

DWORD dwDesiredAccess, // запрашиваемые права доступа

BOOL bInheritHandle, // флаг разрешения наследования описателя

DWORD dwThreadId // идентификатор "заказанного" потока

)

Сам поток может получить свои идентификатор и описатель с помощью функций GetCurrentThreadId() и GetCurrentThread() соответственно.

Для управления состоянием созданного потока служат следующие функции:

ResumeThread(HANDLE hThread) – активизация или возобновление выполнения потока;

SuspendThread(HANDLE hThread) – приостановка выполнения потока.

В действительности функции управляют лишь флагом приостановки потока, который представляет собой целое неотрицательное значение: SuspendThread() увеличивает его на единицу, а ResumeThread() уменьшает, но не ниже 0. Пока значение флага больше нуля, поток остается приостановленным. Очевидно, многократные "приостановки" потока требуют такого же количества его "запусков". Обе функции возвращают предыдущее значение флага, что позволяет ориентироваться в случаях, когда оно заранее не известно.

Корректное завершение потока "изнутри" осуществляется вызовом

void ExitThread(DWORD dwExitCode).

Единственный параметр – код возврата, который подобен коду возврата процесса и может быть затем получен "извне" вызовом

BOOL GetExitCodeThread(

HANDLE hThread, // описатель завершившегося потока

LPDWORD lpdwExitCode // указатель на переменную, которой передается значение

)

Для принудительного "жесткого" завершения потока "извне" служит функция

BOOL TerminateThread(

HANDLE hThread, //описатель потока

DWORD dwExitCode //его будущий код возврата

)

Замечания относительно принудительного завершения потока те же, что и в отношении процесса.

Завершение первичного потока процесса вызывает завершение процесса в целом. Завершение процесса сопровождается завершением всех его потоков.

Пример. Программа, порождающая поток

#include <stdio.h>

#include <conio.h>

#include <windows.h>

DWORD WINAPI Output(LPVOID Param) //стандартный вид заголовка функции

{ //функция потока – бесконечный замедленный вывод символов

while(TRUE)

{

printf( "A" );

Sleep(100);

}

return(0); //достигаться не будет

}

main()

{

HANDLE hThread;

DWORD ThreadId;

hThread = CreateThread( //порождение потока

NULL, //параметры защиты потока – по умолчанию

0, //размер стека – как у первичного потока

Output, //указатель на функцию потока

NULL, //параметр функции – не используется

0, //флаги создания потока – стандартные

&ThreadId); //ID потока, параметр по значению (возвращаемый)

getch(); //задержка до нажатия клавиши

TerminateThread(hThread,0); //завершение потока

return(0); //завершение main

}

Управление нитями

Как было отмечено, нить Win 32 сходна с потоком и также является динамическим объектом, претендующим на процессорное время. Нить стоит в иерархии ниже потока: поток может состоять из одной и более нитей. Для идентификации нити служит "указатель на нить" – указатель на внутреннюю структуру, которая эту нить описывает. С точки зрения ОС нить представлена потоком, который в данный момент ее выполняет, переключение таких потоков планировщиком осуществляется обычным порядком, нити внутри потока для него безразличны. В любой момент из всех нитей потока активной может быть лишь одна. Одна­ко выбор активной нити среди прочих нитей потока производится самими нитями, вернее, активная в данный момент нить может передать управление любой другой, принадлежащей тому же потоку. Таким образом, вводится дополнительная возможность управления выпол­нением задачи, напоминающая невытесняющую (корпоративную) многозадачность и меха­низм "длинных переходов" (setjump() – longjump()), существовавший в классическом C.

Подобно потоку, нить порождается из функции с соответствующим форматом вызова. Нить-родитель создает новую нить с помощью функции CreateFiber(), но, в отличие от потока, новая нить всегда неактивна, активизировать ее можно только явно, поэтому выполнение нити-родителя не прерывается. Т.к. в передаче управления между нитями должны участвовать только нити, "первичная" нить создается путем преобразования потока с помощью функции ConvertThreadToFiber(), при этом выполнение текущего кода про­должается в естественном порядке, но уже "внутри" нити.

LPVOID ConvertThreadToFiber(LPVOID lpParam)

LPVOID CreateFiber(

DWORD dwStackSize, //размер стека для нити, 0 – стек по умолчанию

LPFIBER_START_ROUTINE lpStartAddr, //функция нити

LPVOID lpParam //параметр функции нити

)

Обе функции возвращают "указатель на нить" типа LPVOID.

Типу функции нити FIBER_START_ROUTINE соответствует формат вызова:

VOID CALLBACK FiberRouting(LPVOID lpParam)

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

VOID SwitchToFiber(LPVOID lpFiber)

Для получения указателя текущей нити служит функция:

PVOID GetFiberData(VOID)

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

Локальная память потоков

Помимо всех объектов, существующих в едином адресном пространстве процесса и разделяемых всеми потоками, Win 32 API позволяет создавать т.н. локальные переменные потоков. Для этого используется т.н. TLS – локальная память потоков (Thread Local Storage). Принцип организации TLS-переменных достаточно прост: для каждой переменной создается специальная TLS-таблица из 2 колонок: идентификатор потока и значение переменной, т.о., таблица содержит записи (в оригинале такая запись называется slot), каждая из которых соответствует одному потоку. При доступе к записям таблицы идентификатор выступает в качестве ключа, по которому выбирается только одно значение. Допускается единственных формат хранимых значений – 4-байтовое слово, следовательно, тип локальной переменной – длинное целое или указатель.

Для работы с TLS-переменными служат следующие функции API.

DWORD TlsAlloc(VOID) – создание новой TLS-таблицы для локальной переменной, возвращает индекс таблицы или 0xFFFFFFFF в случае ошибки.

BOOL TlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue) – в запись указанной таблицы, соответствующую текущему потоку, заносится заданное значение. Если такой записи пока нет, она будет создана. Ожидаемый тип значения – указатель, целое подвергается формальному преобразованию типа.

LPVOID TlsGetValue(DWORD dwTlsIndex) – возвращает значение из записи в указанной таблице, соответствующей текущему потоку. Тип возвращаемого значения – указатель, при необходимости преобразуется в целое.

BOOL TlsFree(DWORD dwTlsIndex) – прекращает действие указанной TLS-переменной и удаляет ее таблицу.

Количество TLS-таблиц и, следовательно, локальных переменных потоков одного процесса ограничено 64.

Распределение времени между потоками (управление приоритетами)

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

Процессорное время выделяется потокам в соответствии с их уровнем приоритета. Потоку с более низким приоритетом не выделяется время, если на него претендует поток с более высоким уровнем приоритета. Более того, процесс с более низким приоритетом прерывается до истечения кванта времени, если на процессор претендует поток с более высоким уровнем приоритета. Необходимо помнить, что в среде Windows основная “работа” потока состоит в ожидании события и реагировании на него. Это дает шанс на исполнения потокам с низким уровнем приоритета.

Уровни приоритетов варьируются в диапазоне от 0 (низший) до 31 (высший).

Действующий приоритет каждого потока образуют три составляющие:

– класс приоритета процесса (простаивающий, нормальный, высокий, реального времени);

– уровень приоритета потока внутри класса приоритета процесса (нижний, ниже нормального, нормальный, выше нормального, высший);

– динамический уровень приоритета.

Класс приоритета процесса и уровень приоритета потока внутри класса определяют базовый уровень приоритета потока.

Определены следующие классы приоритетов, которым соответствуют константы:

– Idle (простаивающий) – IDLE_PRIORITY_CLASS (4), процесс активизируется только при простое других процессов;

– Normal (нормальный) – NORMAL_PRIORITY_CLASS (7), большинство процессов в системе, в частности, все процессы пользователя; приоритет владеющего активным окном процесса повышается на 2 и составляет 9;

– High (высокий) – HIGH_PRIORITY_CLASS (13), системные процессы, реагиру­ющие на соответствующие события;

– Real time (реального времени) – REALTIME_PRIORITY_CLASS (24), некоторые системные процессы в "особых случаях".

Внутри классов приоритетов процессов определены уровни приоритетов потоков:

– низший (THREAD_PRIORITY_LOWEST) – ‑2 от уровня класса;

– пониженный (THREAD_PRIORITY_BELOW_NORMAL) – ‑1 от уровня класса;

– нормальный (THREAD_PRIORITY_NORMAL) – равен уровню класса;

– повышенный (THREAD_PRIORITY_ABOVE_NORMAL) – +1 от уровня класса;

– высший (THREAD_PRIORITY_HIGHEST) – +2 от уровня класса;

– простаивающий (THREAD_PRIORITY_IDLE) – равен 16 для REALTIME_­PRIORITY_­CLASS и 1 для остальных классов;

– реального времени (THREAD_PRIORITY_TIME_CRITICAL) – равен 31 для REALTIME_PRIORITY_CLASS и 15 для остальных классов.

Динамический уровень приоритета образуется повышением базового уровня потока на 2 единицы при поступлении сообщений в его очередь; по истечении некоторого времени восстанавливается исходное значение. Данное правило действует только для потоков с уровнем приоритета не выше 15.

Примечание. Очевидно, что и базовый, и динамический приоритет любого (даже простаивающего) потока процесса, отнесенного к классу Real-time, всегда будет выше, чем у любого потока любого другого процесса.

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

Для управления приоритетами выполнения процессов и потоков служат следующие функции.

GetPriorityClass() – получение текущего класса приоритета для процесса.

SetPriorityClass() – установка класса приоритета для процесса.

GetThreadPriority() – получение текущего приоритета выполнения потока.

SetThreadPriority() – установка приоритета выполнения потока.

Особенности управления потоками в MFC

Проблема работоспособности библиотек.

В стандартной библиотеке C++ (заголовочный файл process.h):

unsigned long _beginthread(void(__cdecl *start_address)(void*), unsigned stack_size, void *arglist ) – создание потока;

unsigned long _beginthreadex(void *security, unsigned stack_size, unsigned (__stdcall *start_address )(void*),void *arglist, unsigned initflag, unsigned *thrdaddr) – создание потока, расширенная версия;

void _endthread(void) – завершение потока;

void _endthreadex(unsigned retval) – завершение "расширенного" потока.

Библиотеки LIBCMT.LIB, MSVCRT.LIB.

В библиотеке MFC – класс CWinThread, функция AfxBeginThread. Класс обычно переопределяется и дополняется собственными методами. В зависимости от конкретного варианта использования позволяет создавать либо "рабочий" поток без окна и цикла обработки сообщений, либо "полноценный" "оконный" поток, участвующий в стандартном интерфейсе Windows.

AttachThreadInput Переключение механизмов ввода с одного потока на другой

CommandLineToArgvW Производит разбор командной строки в Unicode

CreateProcess Создает процесс

CreateRemoteThread Создает поток в адресном пространстве другого процесса

CreateThread Создает поток

ExitProcess Завершает процесс и все его потоки

ExitThread Завершает поток

FreeEnvironmentStrings Освобождает память области переменных среды

GetCommandLine Возвращает указатель на командную строку

GetCurrentProcess Возвращает описатель (handle) текущего процесса

GetCurrentProcessId Возвращает идентификатор текущего процесса

GetCurrentThread Возвращает описатель (handle) текущего потока

GetCurrentThreadId Возвращает идентификатор текущего потока

GetEnvironmentStrings Возвращает строку переменных среды

GetEnvironmentVariable Возвращает значение указанной переменной среды

GetExitCodeProcess Возвращает код завершения процесса

GetExitCodeThread Возвращает код завершения потока

GetPriorityClass Возвращает класс приоритета процесса

GetProcessAffinityMask Сообщает, на каких процессорах разрешено исполнение процесса

GetProcessShutdownParameters Сообщает параметры поведения процесса при завершении работы системы

GetProcessTimes Возвращает временные характеристики указанного процесса

GetProcessVersion Сообщает версию Windows, для которой предназначен процесс

GetProcessWorkingSetSize Возвращает характеристики доступного процессу адресного пространства

GetStartupInfo Возвращает параметры процесса, полученные им при создании

GetThreadPriority Сообщает приоритет указанного потока

GetThreadTimes Возвращает временные характеристики указанного потока

OpenProcess Возвращает описатель (handle) указанного процесса

ResumeThread Уменьшает счетчик задержаний потока (или запускает его)

SetEnvironmentVariable Устанавливает значение указанной переменной среды

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

SetProcessShutdownParameters Устанавливает параметры поведения процесса при завершении работы системы

SetThreadAffinityMask Устанавливает, на каких процессорах разрешено исполнение потока

SetThreadPriority Устанавливает приоритет указанного потока

Sleep Задерживает исполнение потока на указанное количество миллисекунд

SleepEx Задерживает исполнение до наступления события ввода/вывода или на время

SetProcessWorkingSetSize Устанавливает характеристики доступного процессу адресного пространства

SuspendThread Приостанавливает исполнение указанного потока

TerminateProcess Завершает указанный процесс

TerminateThread Завершает указанный поток

TlsAlloc Распределяет индекс локальной памяти потока (thread local storage TLS)

TlsFree Освобождает индекс TLS

TlsGetValue Возвращает данные, размещенные в TLS с указанным индексом

TlsSetValue Помещает данные в TLS с указанным индексом

WaitForInputIdle Ждет, пока не начнется ввод для указанного процесса

WinExec Выполняет указанное приложение

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]