Приостановка и возобновление потоков
В объекте ядра «поток» имеется переменная – счетчик числа простоев данного потока. При вызове CreateProcess или CreateThread он инициализируется значением, равным 1, которое запрещает системе выделять новому потоку процессорное время. Такая схема весьма разумна: сразу после создания поток не готов к выполнению, ему нужно время для инициализации. После того, как поток полностью инициализирован, CreateProcess или CreateThread проверяет, не передан ли ей флаг CREATE_SUSPENDED, и, если да, возвращает управление, оставив поток в приостановленном состоянии. В ином случае счетчик простоев обнуляется, и поток включается в число планируемых – если только он не ждет какого-то события. Приостановив поток, можно настроить некоторые его свойства (например, изменить приоритет). Закончив настройку, необходимо разрешить выполнение потока вызовом функции ResumeThread: DWORD ResumeThread(HANDLE hThread); Если поток уже создан, приостановить его выполнение можно вызовом функции SuspendThread: DWORD SuspendThread(HANDLE hThread); Если выполняемый поток приостанавливается, происходит немедленное переключение к следующему потоку (рис. 10). Рисунок 10 – Приостановка потока В отличие от потока, процесс не может быть приостановлен, но можно реализовать эмуляцию приостановки процесса, приостановив выполнение всех его потоков: void SuspendProcess(DWORD processID) { // Определение списка потоков HANDLE threads = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, processID);
if (threads != INVALID_HANDLE_VALUE) { THREADENTRY32 threadEntry = { sizeof(threadEntry) }; BOOL isOk = Thread32First(threads, &threadEntry);
for (; isOk; isOk = Thread32Next(threads, &threadEntry)) { // Приостановка всех потоков данного процесса if (threadEntry.th32OwnerProcessID == processID) { HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID); SuspendThread(hThread); CloseHandle(hThread); } }
CloseHandle(threads); } } Аналогичным образом можно реализовать функцию ResumeProcess, которая будет возобновлять работу «приостановленного» процесса. Однако пользуясь данным механизмом, следует учитывать, что после вызова функции CreateToolhelp32Snapshot состав потоков в данном процессе может измениться. Поток может сообщить системе не выделять ему процессорное время на определенный период, вызвав функцию Sleep: VOID Sleep(DWORD milliseconds); Вызывая Sleep, поток добровольно отказывается от остатка выделенного ему кванта времени. С этого момента система прекращает выделять потоку процессорное время на период, примерно равный заданному, однако нет никаких гарантий, что поток возобновиться именно к этому времени (все зависит от того, какая ситуация сложится в системе на тот момент). Рисунок 11 иллюстрирует одну из возможных ситуаций, которая служит подтверждением сказанному. Поток 2 приостанавливает свое выполнение на 150 мс, однако в действительности получает процессорное время лишь через 200 мс, когда у потока 1 закончился квант. Рисунок 11 – Поток может быть приостановлен на большее время, чем ожидалось Приоритеты потоков Каждый поток получает доступ к процессору лишь на короткий промежуток времени (квант), по истечении которого происходит переключение контекста. При планировании потоков, то есть при определении того, какой поток следующим получит доступ к процессору, учитывается приоритет потока. Потоку присваивается приоритет от 0 (самый низкий) до 31 (самый высокий). Таким образом, система в первую очередь будет выделять процессорное время потокам с самым высоким приоритетом (до тех пор, пока они не завершатся). В связи с этим, возможно, что потоки с более низким приоритетом будут простаивать. Однако подобные ситуации встречаются довольно редко, так как подавляющая часть потоков выполняется с одним приоритетом, к тому же, большинство из них находится в состоянии ожидания некоторого события (например, ввода с клавиатуры) и пока таковое не произошло, эти потоки не участвуют в распределении процессорного времени. Потоки с более высоким приоритетом всегда вытесняют потоки с более низким приоритетом независимо оттого, исполняются последние или нет. Иначе говоря, при появлении потока с более высоким приоритетом, активный поток немедленно приостанавливается – даже если не истек отведенный ему квант процессорного времени – и происходит переключение на высокоприоритетный поток (рис. 12). Рисунок 12 – Потоки с высоким приоритетом вытесняют потоки с низким приоритетом Значение приоритета потока вычисляется самой системой, исходя из класса приоритета процесса и относительного приоритета потока. Класс приоритета процесса задает минимальное значение приоритета его потоков, а относительный приоритет потока – смещение от указанного минимума. Windows поддерживает шесть классов приоритета (рис. 13): - real time (реального времени); - high (высокий); - above normal (выше среднего); - normal (средний); - below normal (ниже среднего); - idle (низкий). Рисунок 13 – Классы приоритета процесса Существует семь относительных приоритетов потоков: - time-critical (критичный по времени). - highest (высший); - above normal (выше среднего); - normal (средний); - below normal (ниже среднего); - lowest (низший); - idle (простаивающий). Класс приоритета процесса можно указать при его создании (п. 4.2.5), передав функции CreateProcess нужное значение: STARTUPINFO startInfo = { sizeof(startInfo) }; PROCESS_INFORMATION procInfo;
// Создание дочернего процесса с классом приоритета "Выше среднего" if (CreateProcess(..., ABOVE_NORMAL_PRIORITY_CLASS, ...) == TRUE) { CloseHandle(procInfo.hThread); CloseHandle(procInfo.hProcess); } Функция SetPriorityClass позволяет изменить класс приоритета существующего процесса: BOOL SetPriorityClass(HANDLE hProcess, DWORD priorityClass); Все потоки создаются с относительным приоритетом normal. Функция SetThreadPriority позволяет изменить приоритет существующего потока: BOOL SetThreadPriority(HANDLE hThread, int priority); Следующий фрагмент кода демонстрирует создание процесса, первичный поток которого запускается с повышенным приоритетом: STARTUPINFO startInfo = { sizeof(startInfo) }; PROCESS_INFORMATION procInfo;
// Создание приостановленного процесса if (CreateProcess(..., CREATE_SUSPENDED, ...) == TRUE) { CloseHandle(procInfo.hProcess); // Установка относительного приоритета первичного потока // "выше среднего" SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_ABOVE_NORMAL); // "Запуск" потока ResumeThread(procInfo.hThread); CloseHandle(procInfo.hThread); } Задания Начиная с Windows 2000, появилось специализированное расширение модели процессов – объект ядра «задание» (job). Объект-задание позволяет управлять группой процессов, как единым целым, причем для такой группы можно указать ряд ограничений на определенные действия процессов. Процесс может входить только в одно задание. По умолчанию его связь с объектом «задание» нельзя разрушить, и все процессы, создаваемые данным процессом и его потомками, будут сопоставлены с тем же заданием [4]. Объект «задание» также регистрирует базовую учетную (статистическую) информацию всех включенных в него процессов, в том числе уже завершившихся. Суть группировки процессов с помощью объекта ядра «задание» сводится к следующей последовательности действий: - создание объекта-задания; - связывание задания с ограничениями; - создание процессов, которые следует включить в задание; - добавление процессов в задание. Создание задания осуществляется вызовом функции CreateJobObject: HANDLE CreateJobObject(PSECURITY_ATTRIBUTES psa, PCTSTR name); Как и любая функция, создающая объекты ядра, CreateJobObject принимает в первом параметре информацию о защите – ссылку на структуру PSECURITY_ATTRIBUTES, – где можно указать возможность наследования дескриптора созданного объекта (свойство bInheritHandle). Параметр name позволяет присвоить заданию имя, что бы к нему могли обращаться другие процессы через функцию OpenJobObject: HANDLE OpenJobObject(DWORD desiredAccess, BOOL inheritHandle, PCTSTR name); По завершению работы с заданием, необходимо закрыть его дескриптор вызовом функции CloseHandle. Закрытие объекта-задания не приводит к автоматическому завершению всех его процессов. Данный объект просто помечается как подлежащий разрушению, и система уничтожает его только после завершения всех включенных в него процессов. ©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.
|