Лекції "Системне програмування"
4. Лекція 5.1 Багатозадачність. Процеси і потоки
Багатозадачність
Процеси і потоки
Процес - завантажена в пам'ять і готова до виконання програма.
Кожен процес має свій власний віртуальний адресний простір (4Gb). Процес складається з коду, даних і інших системних ресурсів, таких як відкриті файли, канали (pipes), що синхронізують об'єкти.
Потік (thread) - базовий об'єкт, якому операційна система розподіляє час центрального процесора.
Виконання процесу починається зі стартового потоку. Надалі він може породжувати інші потоки. Ресурси процесу доступні всім його потокам. Кожен потік використовує структуру даних, для збереження контексту виконання, у той час, коли в нього віднімається процесор. У контекст входять регістри процесора, перемінні оточення, стеки ядра і користувача. Усі потоки одного процесу спільно використовують його віртуальний адресний простір.
Процесорний час розподіляється по черзі між потоками, а не між процесами. Тривалість кванта виділення часу складає близько 20 мс.
Розподіл часу між потоками
Процесорний час виділяється потокам відповідно до їхнього рівня пріоритету. Потоку з більш низьким пріоритетом не виділяється час, якщо на нього претендує потік з більш високим рівнем пріоритету. Більш того, процес з більш низьким пріоритетом переривається до витікання кванта часу, якщо на процесор претендує потік з більш високим рівнем пріоритету. Необхідно пам'ятати, що в середовищі Windows основна “робота” потоку складається в чеканні події і реагуванні на нього. Це дає шанс на виконання потокам з низьким рівнем пріоритету.
Рівні пріоритетів варіюються в діапазоні від 0 (нижчий) до 31 (вищий).
Рівень пріоритету кожного потоку складається з трьох складових
- клас пріоритету процесу (простоючий, нормальний, високий, реального часу)
- рівень пріоритету потоку усередині класу пріоритету процесу (нижній, нижче нормального, нормальний, вище нормального, вищий)
- динамічно встановлений рівень пріоритету.
-
Клас пріоритету процесу
Клас |
Прапор у функції CreateProcess |
Числовий рівень |
Idle (простоючий) |
IDLE_PRIORITY_CLASS |
4 |
Normal (нормальний) |
NORMAL_PRIORITY_CLASS |
7-9 |
High (високий) |
HIGH_PRIORITY_CLASS |
13 |
Real time(реального часу) |
REALTIME_PRIORITY_CLASS |
24 |
2. При запуску на виконання процесу призначається один з чотирьох класів пріоритету.
Рівень Idle призначається процесу, що нічого не повинний робити у випадку активності інших процесів (наприклад, хоронитель екрана).
Процесам, що запускається користувачем, привласнюється нормальний рівень. Користувач може запустити кілька процесів. Тому процесу, з яким користувач безпосередньо працює (а це може бути тільки один процес) рівень пріоритету піднімається на двох одиниць (7+2 = 9).
Це робить спілкування з прикладною програмою більш “комфортабельним”. Високий клас пріоритету призначається деяким системним процесам, що простоюють до виникнення визначених подій і, тому, не заважають іншим процесам. Тільки в особливих випадках процес може відноситися до класу Real time.
-
Рівень пріоритету потоку усередині класу пріоритетів
Ідентифікатор |
Рівень пріоритету потоку |
THREAD_PRIORITY_LOWEST |
На 2 нижче рівні класу |
THREAD_PRIORITY_BELOW_NORMAL |
На 1 нижче рівня класу |
THREAD_PRIORITY_NORMAL |
Дорівнює рівню класу |
THREAD_PRIORITY_ABOVE_NORMAL |
На 1 вище рівня класу |
THREAD_PRIORITY_HIGHEST |
На 2 вище рівні класу |
THREAD_PRIORITY_IDLE |
Дорівнює 1 для процесів класу IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, і HIGH_PRIORITY_CLASS і дорівнює 16 для REALTIME_PRIORITY_CLASS |
THREAD_PRIORITY_TIME_CRITICAL |
Дорівнює 15 для процесів класу IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, і HIGH_PRIORITY_CLASS, і дорівнює 31 для REALTIME_PRIORITY_CLASS |
-
Динамічна зміна рівня пріоритету потоку
Клас пріоритету процесу і рівень пріоритету потоку усередині класу визначають базовий рівень пріоритету потоку. Цей рівень може динамічно змінюватися системою, а саме, підвищуватися на 2 одиниці у відповідь на надходження повідомлень у чергу потоку з наступним зниженням до базового рівня після закінчення визначеного проміжку часу. Це правило діє тільки для потоків до 15 рівня.
Робота з процесами і потоками в Win32® API.
Список функцій
У Win32® API визначені наступні функції роботи з процесами і потоками
Назва функції |
Виконувана дія |
AttachThreadInput |
Переключення механізмів уведення з однієї нитки на іншу |
CommandLineToArgv |
Робить розбір командного рядка в 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 |
Виконує зазначений додаток |
Докладний опис функцій приведений у Win32® Programmer’s Reference. Тут будуть докладно розглянуті тільки деякі функції.
· Функція CreateProcess()
Створює новий процес і його первинний потік. Новий процес виконує зазначений файл, що виконується.
Формат функції:
BOOL CreateProcess(
LPCTSTR lpApplicationName, // ім'я файлу, що виконується
LPTSTR lpCommandLine, // командний рядок
LPSECURITY_ATTRIBUTES lpProcessAttributes, // атрибути захисту процесу
LPSECURITY_ATTRIBUTES lpThreadAttributes, // атрибути захисту
// потоку
BOOL bInheritHandles, // прапор спадкування описувачей
DWORD dwCreationFlags, // прапори створення
LPVOID lpEnvironment, // покажчик блоку перемінні середовища
LPCTSTR lpCurrentDirectory, // поточний каталог
LPSTARTUPINFO lpStartupInfo, // блок початкових параметрів
LPPROCESS_INFORMATION lpProcessInformation // покажчик
// структури, що описує породжений // процес
);
Функція повертає TRUE у випадку успіху і FALSE - у випадку невдачі.
Параметри:
lpApplicationName - покажчик на рядок, що містить ім'я програми, що виконується. Ім'я може бути повне. Якщо воно не повне, то пошук файлу виробляється в поточному каталозі. Параметру може бути привласнене значення NULL. У цьому випадку як ім'я файлу виступає перша виділена пробілами лексема з рядка lpCommandLine;
lpCommandLine - покажчик командного рядка. Якщо параметр lpApplicationName має значення NULL, то ім'я файлу, що виконується, виділяється з lpCommandLine, а пошук файлу, що виконується, виробляється відповідно до правил, що діють у системі;
lpProcessAttributes - покажчик на структуру, що описує параметри захисту процесу. Якщо параметру привласнене значення NULL, то встановлюються атрибути “за замовчуванням”;
lpThreadAttributes- покажчик на структуру, що описує параметри захисту первинного потоку. Якщо параметру привласнене значення NULL, то встановлюються атрибути “за замовчуванням”;
bInheritHandles - визначає, чи буде породжений процес успадковувати описувачі (handles) об'єктів батьківського процесу. Наприклад, якщо батьківський процес A уже до цього породжував процес B, те він одержав описувач процесу B і може їм маніпулювати. Якщо тепер він породжує процес C з параметром bInheritHandles рівним TRUE, то і процес C зможе працювати з описувачем процесу B;
dwCreationFlags - визначає деякі додаткові умови створення процесу і його клас пріоритету;
lpEnvironment- покажчик на блок перемінні середовища породженого процесу. Якщо цей параметр дорівнює NULL, то породжений процес успадковує середовище батька. Інакше він повинний указувати на блок рядків, що завершується нулем, кожна з який завершується нулем (аналогічно DOS);
lpCurrentDirectory - покажчик на рядок, що містить повне ім'я поточного каталогу породженого процесу. Якщо цей параметр дорівнює NULL, то породжений процес успадковує каталог батька;
lpStartupInfo - покажчик на структуру STARTUPINFO, що визначає параметри головного вікна породженого процесу;
lpProcessInformation - покажчик на структуру, що буде заповнена інформацією про породжений процес після повернення з функції.
Приклад: програма, що запускає Microsoft® 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 ;
}
· Функція CreateThread()
Створює новий потік в адресному просторі процесу.
Формат функції:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // атрибути захисту
// потоку
DWORD dwStackSize, // розмір стека в байтах
LPTHREAD_START_ROUTINE lpStartAddress, //покажчик на функцію
// потоку
LPVOID lpParameter, // аргумент, переданий у функцію
// потоку
DWORD dwCreationFlags, // прапори керування створенням потоку
LPDWORD lpThreadId // область пам'яті для повернення
// ідентифікатора потоку
);
Функція повертає описувач породженого потоку.
Параметри:
lpThreadAttributes - покажчик на структуру, що описує параметри захисту потоку. Якщо параметру привласнене значення NULL, то встановлюються атрибути “за замовчуванням”;
dwStackSize - установлює розмір стека, що приділяється потоку. Якщо параметр дорівнює нулю, то встановлюється стек, рівний стеку первинного потоку;
lpStartAddress - адреса функції, которую буде виконувати потік. Функція має один 32-бітний аргумент і повертає 32 бітне значення;
lpParameter - параметр, переданий у функцію, которую буде виконувати потік;
dwCreationFlags - додатковий прапор, що керує створенням потоку. Якщо цей параметр дорівнює CREATE_SUSPENDED, то потік після породження не запускається на виконання до виклику функціїResumeThread;
lpThreadId - покажчик на 32-бітну перемінну, котрої буде привласнене значення унікального ідентифікатора потоку.
Приклад: програма, що породжує потік
#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 ) ;
getch() ;
TerminateThread( hThread, 0 ) ;
return(0) ;
}