Октябрь 23 2017 07:14:56
Навигация
Анекдот
Чем ближе утро, тем удобнее подушка.

DSI 23.08.12

Просмотреть все »
Случайное с баша
Сервер FarCry 1.4

Адрес: farcry.su
Игроков: 0
Карта: am_union
Тип игры: Assault
Время игры: 30 мин.
Режим сервера: LAN
Провайдер: SUMTEL
TeamSpeak2 farcry.su
TeamSpeak farcry.su
Welcome
FarCry
Красные
Синие
Admin Channel
FarCry 3
Болталка
Кто здесь
Иерархия статей
Статьи » Windows » Процессы
Процессы
Процессы в Windows
Статья написана специально для    http://www.uinc.ru
Автор: TN

 

Уважаемым читателям, которые (пока еще)
не знают кто такой Дж.Рихтер и что такое SDK.


Процессы в Windows

 

Процессы

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

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

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

- Адресное пространство - диапазон адресов виртуальной памяти, которым может пользоваться процесс;

- Исполняемая программа и данные, проецируемые на виртуальное адресное пространство процесса.

Потоки

Процессы инертны. Отвечают же за исполнение кода, содержащегося в адресном пространстве процесса, потоки. Поток (thread) - некая сущность внутри процесса, получающая процессорное время для выполнения. В каждом процессе есть минимум один поток. Этот первичный поток создается системой автоматически при создании процесса. Далее этот поток может породить другие потоки, те в свою очередь новые и т.д. Таким образом, один процесс может владеть несколькими потоками, и тогда они одновременно исполняют код в адресном пространстве процесса. Каждый поток имеет:

- Уникальный идентификатор потока;

- Содержимое набора регистров процессора, отражающих состояние процессора;

- Два стека, один из которых используется потоком при выполнении в режиме ядра, а другой - в пользовательском режиме;

- Закрытую область памяти, называемую локальной памятью потока (thread local storage, TLS) и используемую подсистемами, run-time библиотеками и DLL.

Планирование потоков

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

Квант не измеряется в каких бы то ни было единицах времени, а выражается целым числом. Для каждого потока хранится текущее значение его кванта. Когда потоку выделяется квант процессорного времени, это значит, что его квант устанавливается в начальное значение. Оно зависит от операционной системы. Например, для Win2000 Professional начальное значение кванта равно 6, а для Win2000 Server - 36.

Это значение можно изменить вызвав Control Panel - > System -> Advanced -> Performance options. Значение "Applications" - как для Win2000 Professional; "Background Services" - как для Win2000 Server.
Или напрямую в ключе реестра HKLM Win32PrioritySeparation.

Всякий раз, когда возникает прерывание от таймера, из кванта потока вычитается 3, и так до тех пор, пока он не достигнет нуля. Частота срабатывания таймера зависит от аппаратной платформы. Например, для большинства однопроцессорных x86 систем он составляет 10мс, а на большинстве многопроцессорных x86 систем - 15мс.

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

Планирование в Windows осуществляется на уровне потоков, а не процессов. Это кажется понятным, так как сами процессы не выполняются, а лишь предоставляют ресурсы и контекст для выполнения потоков. Поэтому при планировании потоков, система не обращает внимания на то, какому процессу они принадлежат. Например, если процесс А имеет 10 готовых к выполнению потоков, а процесс Б - два, и все 12 потоков имеют одинаковый приоритет, каждый из потоков получит 1/12 процессорного времени.

Приоритеты

В Windows существует 32 уровня приоритета, от 0 до 31. Они группируются так: 31 - 16 уровни реального времени; 15 - 1 динамические уровни; 0 - системный уровень, зарезервированный для потока обнуления страниц (zero-page thread).

При создании процесса, ему назначается один из шести классов приоритетов:

Real time class (значение 24),

High class (значение 13),

Above normal class (значение 10),

Normal class (значение 8),

Below normal class (значение 6),

и Idle class (значение 4).

В Windows NT/2000/XP можно посмотреть приоритет процесса в Task Manager.

Above normal и Below normal появились начиная с Win2000.

Приоритет каждого потока (базовый приоритет потока) складывается из приоритета его процесса и относительного приоритета самого потока. Есть семь относительных приоритетов потоков:

Normal: такой же как и у процесса;

Above normal: +1 к приоритету процесса;

Below normal: -1;

Highest: +2;

Lowest: -2;

Time critical: устанавливает базовый приоритет потока для Real time класса в 31, для остальных классов в 15.

Idle: устанавливает базовый приоритет потока для Real time класса в 16, для остальных классов в 1.

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

Приоритет потока

Класс процесса

Класс процесса

   

Idle class

Below normal class

Normal class

Above normal class

High class

Real time class

1

 

Idle

Idle

Idle

Idle

Idle

 

2

 

Lowest

         

3

 

Below ...

         

4

Idle class

Normal

Lowest

       

5

 

Above ...

Below ...

       

6

Below normal class

Highest

Normal

Lowest

     

7

   

Above ...

Below ...

     

8

Normal class

 

Highest

Normal

Lowest

   

9

     

Above ...

Below ...

   

10

Above normal class

   

Highest

Normal

   

11

       

Above ...

Lowest

 

12

       

Highest

Below ...

 

13

High class

       

Normal

 

14

         

Above ...

 

15

         

Highest

 

15

 

Time critical

Time critical

Time critical

Time critical

Time critical

 

16

           

Idle

17

             

18

             

19

             

20

             

21

             

22

           

Lowest

23

           

Below ...

24

Real time class

         

Normal

25

           

Above ...

26

           

Highest

27

             

28

             

29

             

30

             

31

           

Time critical

Привязка к процессорам

Если операционная система выполняется на машине, где установлено более одного процессора, то по умолчанию, поток выполняется на любом доступном процессоре. Однако в некоторых случаях, набор процессоров, на которых поток может работать, может быть ограничен. Это явление называется привязкой к процессорам (processor affinity). Можно изменить привязку к процессорам программно, через Win32-функции планирования.

Память

Каждому процессу в Win32 доступно линейное 4-гигабайтное (2^32 = 4 294 967 296) виртуальное адресное пространство. Обычно верхняя половина этого пространства резервируется за операционной системой, а вторая половина доступна процессу.

Виртуальное адресное пространство процесса доступно всем потокам этого процесса. Иными словами, все потоки одного процесса выполняются в едином адресном пространстве.

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

Виртуальная память может вовсе не соответствовать структуре физической памяти. Диспетчер памяти транслирует виртуальные адреса на физические, по которым реально хранятся данные. Поскольку далеко не всякий компьютер в состоянии выделить по 4 Гбайт физической памяти на каждый процесс, используется механизм подкачки (swapping). Когда оперативной памяти не хватает, операционная система перемещает часть содержимого памяти на диск, в файл (swap file или page file), освобождая, таким образом, физическую память для других процессов. Когда поток обращается к странице виртуальной памяти, записанной на диск, диспетчер виртуальной памяти загружает эту информацию с диска обратно в память.

Создание процессов

Создание Win32 процесса осуществляется вызовом одной из таких функций, как CreateProcess, CreateProcessAsUser (для Win NT/2000) и CreateProcessWithLogonW (начиная с Win2000) и происходит в несколько этапов:

- Открывается файл образа (EXE), который будет выполняться в процессе. Если исполняемый файл не является Win32 приложением, то ищется образ поддержки (support image) для запуска этой программы. Например, если исполняется файл с расширением .bat, запускается cmd.exe и т.п.

В WinNT/2000 для отладки программ реализовано следующее. CreateProcess, найдя исполняемый Win32 файл, ищет в SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Option раздел с именем и расширением запускаемого файла, затем ищет в нем параметр Debugger, и если строка не пуста, запускает то, что в ней написано вместо данной программы.

- Создается объект Win32 "процесс".

- Создается первичный поток (стек, контекст и объект "поток").

- Подсистема Win32 уведомляется о создании нового процесса и потока.

- Начинается выполнение первичного потока.

- В контексте нового процесса и потока инициализируется адресное пространство (например, загружаются требуемые DLL) и начинается выполнение программы.

Завершение процессов

Процесс завершается если:

- Входная функция первичного потока возвратила управление.

- Один из потоков процесса вызвал функцию ExitProcess.

- Поток другого процесса вызвал функцию TerminateProcess.

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

Пример 1. Программа создает процесс "Калькулятор".

#include <windows.h>
int main(int argc, char* argv[])
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	ZeroMemory( &si, sizeof(si) );
	si.cb = sizeof(si);
	ZeroMemory( &pi, sizeof(pi) );
	if( !CreateProcess( NULL, "c:/windows/calc.exe", NULL, NULL, FALSE,
		0, NULL, NULL, &si, &pi))
		return 0;
	// Close process and thread handles.
	CloseHandle( pi.hProcess );
	CloseHandle( pi.hThread );
	return 0;
}

Создание потоков

Первичный поток создается автоматически при создании процесса. Остальные потоки создаются функциями CreateThread и CreateRemoteThread (только в Win NT/2000/XP).

Завершение потоков

Поток завершается если

- Функция потока возвращает управление.

- Поток самоуничтожается, вызвав ExitThread.

- Другой поток данного или стороннего процесса вызывает TerminateThread.

- Завершается процесс, содержащий данный поток.


Если вы используете в своем приложении LibCMt C run-time библиотеку, Microsoft настоятельно рекомендует вместо Win32 API функций использовать их аналоги из C run-time: _beginthread, _beginthreadex, _endthread и _endthreadex.


Объекты ядра

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

Kernel object

Объект ядра

Kernel object

Объект ядра

Access token

Маркер доступа

Module

Подгружаемый модуль (DLL)

Change notification

Уведомление об изменениях на диске

Mutex

Мьютекс

I/O completion ports

Порт завершения ввода-вывода

Pipe

Канал

Event

Событие

Process

Процесс

File

Файл

Semaphore

Семафор

File mapping

Проекция файла

Socket

Сокет

Heap

Куча

Thread

Поток

Job

Задание

Timer

Ожидаемый таймер

Mailslot

Почтовый слот

   

Объект ядра это, по сути, структура, созданная ядром и доступная только ему. В пользовательское приложение передается только описатель (handle) объекта, а управлять объектом ядра можно с помощью функций Win32 API.

Wait функции

Как можно приостановить работу потока? Существует много способов. Вот некоторые из них.

Функция Sleep() приостанавливает работу потока на заданное число миллисекунд. Если в качестве аргумента вы укажите 0 ms, то произойдет следующее. Поток откажется от своего кванта процессорного времени, однако тут же появится в списке потоков готовых к выполнению. Иными словами произойдет намеренное переключение потоков. (Вернее сказать, попытка переключения. Ведь следующим для выполнения потоком вполне может стать тот же самый.)

Функция WaitForSingleObject() приостанавливает выполнение потока до тех пор, пока не произойдет одно из двух событий:

- истечет таймаут ожидания;

- ожидаемый объект перейдет в сигнальное (signaled) состояние.

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

Функции WaitForMultipleObjects передается сразу массив объектов. Можно ожидать срабатывания сразу всех объектов или какого-то одного из них.

Пример 2. Программа создает два одинаковых потока и ожидает их завершения.
Потоки просто выводят текстовое сообщение, которое передано им при инициализации.

#include <windows.h>
#include <process.h>

unsigned __stdcall ThreadFunc( void * arg) // Функция потока
{
	char ** str = (char**)arg;
	MessageBox(0,str[0],str[1],0);
	_endthreadex( 0 );
	return 0;
};
int main(int argc, char* argv[])
{
	char * InitStr1[2] = {"First thread running!","11111"};// строка для первого потока
	char * InitStr2[2] = {"Second thread running!","22222"};// строка для второго потока
	unsigned uThreadIDs[2];

	HANDLE hThreads[2];
	hThreads[0] = (HANDLE)_beginthreadex( NULL, 0, &ThreadFunc, InitStr1, 0,&uThreadIDs[0]);
	hThreads[1] = (HANDLE)_beginthreadex( NULL, 0, &ThreadFunc, InitStr2, 0,&uThreadIDs[1]);

	// Ждем, пока потоки не завершат свою работу
	WaitForMultipleObjects(2, hThreads, TRUE, INFINITE ); // Set no time-out
	// Закрываем дескрипторы
	CloseHandle( hThreads[0] );
	CloseHandle( hThreads[1] );
	return 0;
}

Синхронизация потоков

Работая параллельно, потоки совместно используют адресное пространство процесса. Также все они имеют доступ к описателям (handles) открытых в процессе объектов. А что делать, если несколько потоков одновременно обращаются к одному ресурсу или необходимо как-то упорядочить работу потоков? Для этого используют объекты синхронизации и соответствующие механизмы.

Мьютексы.

Мьютексы (Mutex) это объекты ядра, которые создаются функцией CreateMutex(). Мьютекс бывает в двух состояниях - занятом и свободном. Мьютексом хорошо защищать единичный ресурс от одновременного обращения к нему разными потоками.

Пример 3. Допустим, в программе используется ресурс, например, файл или буфер в памяти. Функция WriteToBuffer() вызывается из разных потоков. Чтобы избежать коллизий при одновременном обращении к буферу из разных потоков, используем мьютекс. Прежде чем обратиться к буферу, ожидаем <освобождения> мютекса.


HANDLE hMutex;

int main()
{
	hMutex = CreateMutex( NULL, FALSE, NULL); // Создаем мьютекс в свободном состоянии
	...
	// Создание потоков, и т.д.
	...
}
BOOL WriteToBuffer()
{
	DWORD dwWaitResult;
	// Ждем освобождения мьютекса перед тем как обратиться к буферу.
	dwWaitResult = WaitForSingleObject( hMutex, 5000L); // 5 секунд на таймаут
 
	if (dwWaitResult == WAIT_TIMEOUT) // Таймаут. Мьютекс за это время не освободился.
	{
		return FALSE; 
	}
	else // Мьютекс освободился, и наш поток его занял. Можно работать.
	{
		Write_to_the_buffer().
		...
		ReleaseMutex(hMutex); // Освобождаем мьютекс.
	}
	return TRUE;
}

Семафоры.

Семафор (Semaphore) создается функцией CreateSemaphore(). Он очень похож на мьютекс, только в отличие от него у семафора есть счетчик. Семафор открыт если счетчик больше 0 и закрыт, если счетчик равен 0. Семафором обычно "огораживают" наборы равнозначных ресурсов (элементов), например очередь, список и т.п.

Пример 4. Классический пример использования семафора это очередь элементов, которую обрабатывают несколько потоков. Потоки "разбирают" элементы из очереди. Если очередь пуста, потоки должны "спать", ожидая появления новых элементов. Для учета элементов в очереди используется семафор.


class CMyQueue
{
	HANDLE m_hSemaphore; // Семафор для учета элементов очереди
	// Описание других объектов для хранения элементов очереди

	public:
	CMyQueue()
	{
		m_hSemaphore = CreateSemaphore(NULL, 0, 1000, NULL);	//начальное значение счетчика = 0

									//максимальное значение = 1000
		// Инициализация других объектов
	}
	~CMyQueue()
	{
		CloseHandle( m_hSemaphore);
		// Удаление других объектов
	}
	void AddItem(void * NewItem)
	{
		// Добавляем элемент в очередь
		// Увеличиваем счетчик семафора на 1.
		ReleaseSemaphore(m_hSemaphore,1, NULL);
	}
	void GetItem(void * Item)
	{
		// Если очередь пуста, то потоки, вызвавшие этот метод,
		// будут находиться в ожидании...
		WaitForSingleObject(m_hSemaphore,INFINITE);
		 
		// Удаляем элемент из очереди
	}
};

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

События.

События (Event), также как и мьютексы имеют два состояния - установленное и сброшенное. События бывают со сбросом вручную и с автосбросом. Когда поток дождался (wait-функция вернула управление) события с автосбросом, такое событие автоматически сбрасывается. В противном случае событие нужно сбрасывать вручную, вызвав функцию ResetEvent(). Допустим, сразу несколько потоков ожидают одного и того же события, и событие сработало. Если это было событие с автосбросом, то оно позволит работать только одному потоку (ведь сразу же после возврата из его wait-функции событие сбросится автоматически!), а остальные потоки останутся ждать. Если же это было событие

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

Рейтинг доступен только для пользователей.

Пожалуйста, авторизуйтесь для голосования.


Общая панель
Добро пожаловать, посетитель!





Не могу войти

Мини-чат
Только пользователи могут отправлять сообщения.

[off] 21-05-2017 19:09
Улыбчивый

[off] 28-03-2017 00:16
Спасибо пацаны !!! Усмехающийся

[off] 25-03-2017 21:15
С Днюхой Саня vinsent

[off] 25-03-2017 00:40
С Днюхой Саня и кучной стрельбы! vinsent

[off] 05-03-2017 15:24
всем привет ! Нас приглашают по участвовать в турнире , турнир состоится в апреле , вся инфа на сайте клана RnT Шок

[off] 27-02-2017 19:10
С прошедшим праздником, бойцы! drinks

[off] 23-02-2017 13:05
всех праздником 23 феврвля comando

[off] 23-02-2017 00:52
С 23 парни! vinsent

[off] 13-01-2017 22:03
В сех со старым Н coolовым Годом

[off] 02-01-2017 19:16

Счетчики

Яндекс.Метрика