
СП-Язык С / Лр1 СП 2 курс
.docЛабораторная работа №1
Тема. Процессы в OS Windows
Цель. Изучить основные средства получения информации о процессах операционной системы Windows. Получить практические навыки работы с процессами операционной системы.
Теоретические сведения
Процесс определяют как экземпляр выполняемой программы, и он состоит из двух компонентов
-
объекта ядра, через который операционная система управляет процессом, там же хранится статистическая информация о процессе,
-
адресного пространства, в котором содержится код и данные всех EXE- и DLL модулей. Именно в нем находятся области памяти, динамически распределяемой для стеков потоков и других нужд.
Процессы инертны, чтобы процесс что-нибудь выполнил, в нем нужно создать поток. Именно потоки отвечаю за исполнение кода, содержащегося в адресном пространстве процесса. Один процесс может владеть несколькими потоками, и тогда они "одновременно" исполняют код в адресном пространстве процесса. Для этого каждый поток должен располагать собственным набором регистров процессора и собственным стеком. В каждом процессе есть минимум один поток. Если бы у процесса не было ни одного потока, ему нечего было бы делать, и система автоматически уничтожила бы его вместе с выделенным ему адресным пространством.
Чтобы все эти потоки работали, операционная система отводит каждому из них определенное процессорное время. Выделяя потокам отрезки времени (называемые квантами) по принципу карусели, она создает тем самым иллюзию одновременного выполнения потоков. Если в машине установлено более одного процессора, алгоритм работы операционной системы значительно усложняется (в этом случае система стремится сбалансировать нагрузку между процессорами).
Когда поток в приложении вызывает CreateProcess, система создает объект ядра "процесс" с начальным значением счетчика числа его пользователей, равным 1. Этот объект — не сам процесс, а компактная структура данных, через которую операционная система управляет процессом. (Объект ядра "процесс" следует рассматривать как структуру данных со статистической информацией о процессе.). Затем система создает для нового процесса виртуальное адресное пространство и загружает в него код и данные как для исполняемого файла, так и для любых DLL (если таковые требуются).
Далее система формирует объект ядра "поток" (со счетчиком, равным 1) для первичного потока нового процесса. Как и в первом случае, объект ядра "поток" — это компактная структура данных, через которую система управляет потоком. Первичный поток начинает с исполнения стартового кода из библиотеки С/С++, который в конечном счете вызывает функцию WinMain, wWinMain, main или wmain в программе. Если системе удастся создать новый процесс и его первичный поток, Create Process вернет TRUE
Функция CreateProcess и её параметры:
BOOL CreateProcess( PCTSTR pszApplicationName, //имя программы PTSTR pszCommandLine, //командная строка PSFCURITY_ATTRIBUTES psaProcess,/*атрибуты безопасности для дескриптора процесса*/ PSECURITY_ATTRIBUTES psaThiead, /*атрибуты безопасности для дескриптора потока*/ BOOL bInheritHandles, /*указывает, наследует ли новый процесс дескрипторы, принадлежащие текущему*/ DWORD fdwCreate, //параметры создания процесса PVOID pvEnvironment,//значение переменных окружения PCTSTR pszCurDir, //текущий каталог PSTARTUPINFO psiStartInfo, //информация о запуске процесса PPROCESS_INFORMATION ppiProcInfo);/*возвращаемые функции, дескрипторы и идентификаторы потока*/
Завершения процесса. Процесс можно завершить четырьмя способами:
-
входная функция первичного потока возвращает управление (рекомендуемый способ),
-
один из потоков процесса вызывает функцию ExitProcess (нежелательный способ);
-
поток другого процесса вызывает функцию TerminateProcess (тоже нежелательно);
-
все потоки процесса умирают по своей воле (большая редкость);
Возврат управления входной функцией первичного потока
Приложение следует проектировать так, чтобы его процесс завершался только после возврата управления входной функцией первичного потока. Это единственный способ, гарантирующий корректную очистку всех ресурсов, принадлежавших первичному потоку. При этом:
-
любые С++ объекты, созданные данным потоком, уничтожаются соответствующими деструкторами;
-
система освобождает память, которую занимал стек потока;
-
система устанавливает код завершения процесса (поддерживаемый объектом ядра "процесс") — его и возвращает Ваша входная функция;
-
счетчик пользователей данного объекта ядра "процесс" уменьшается на 1.
Функция ExitProcess
Процесс завершается, когда один из его потоков вызывает ExitProcess:
VOID ExitProcess(UINT fuExitCode);
Эта функция завершает процесс и заносит в параметр fuExitCode код завершения процесса. Возвращаемого значения у ExitProcess нет, так как результат ее действия — завершение процесса. Если за вызовом этой функции в программе присутствует какой-нибудь код, он никогда не исполняется.
Когда входная функция (WinMain, wWinMain, main или wmain) в Вашей программе возвращает управление, оно передастся стартовому коду из библиотеки С/C++, и тот проводит очистку всех ресурсов, выделенных им процессу, а затем обращается к ExitProcess, передавая ей значение, возвращенное входной функцией, поэтому возврат управления входной функцией первичного потока приводит к завершению всего процесса. Обратите внимание, что при завершении процесса прекращается выполнение и всех других его потоков. В приложении, написанном на С/С++, следует избегать вызова этих функций, так как библиотеке С/С++ скорее всего не удастся провести должную очистку.
Функция TerminateProcess
Вызов функции TerminateProcess тоже завершает процесс:
BOOL TerminateProcess( HANDLE hProcoss, UINT fuExitCode);
Главное отличие этой функции от ExitProcess в том, что ее может вызвать любой поток и завершить любой процесс. Параметр bProcess идентифицирует описатель завершаемого процесса, а в параметре fuExitCode возвращается код завершения процесса.
Пользуйтесь TerminateProcess лишь в том случае, когда иным способом завершить процесс не удается. Процесс не получает абсолютно никаких уведомлений о том, что он завершается, и приложение не может ни выполнить очистку, ни предотвратить свое неожиданное завершение (если оно, конечно, не использует механизмы защиты). При этом теряются все данные, которые процесс не успел переписать из памяти на диск.
Процесс действительно не проводит сам очистку, но операционная система высвобождает все принадлежавшие ему ресурсы: возвращает себе выделенную им память, закрывает любые открытые файлы, уменьшает счетчики соответствующих объектов ядра и разрушает все его User- и GDI-объекты.
Когда все потоки процесса уходят
В такой ситуации (а она может возникнуть, если все потоки вызвали ExitThread или их закрыли вызовом TermirmteThread) операционная система больше не считает нужным "содержать" адресное пространство данного процесса. Обнаружив, что в процессе не исполняется ни один поток, она немедленно завершает его. При этом код завершения процесса приравнивается коду завершения последнего потока.
Что происходит при завершении процесса
Выполнение всех потоков в процессе прекращается. Все User- и GDI-объекты, созданные процессом, уничтожаются, а объекты ядра закрываются (если их не использует другой процесс). Код завершения процесса меняется со значения STILL_ACTIVE на код, переданный в ExitProcess или TerminateProcess. Объект ядра "процесс" переходит в свободное, или незанятое (signaled), состояние. Прочие потоки в системе могут приостановить свое выполнение вплоть до завершения данного процесса. Счетчик объекта ядра "процесс" уменьшается на 1.
Связанный с завершаемым процессом объект ядра не высвобождается, пока не будут закрыты ссылки на него и из других процессов. В момент завершения процесса система автоматически уменьшает счетчик пользователей этого объекта на 1, и объект разрушается, как только его счетчик обнуляется. Кроме того, закрытие процесса не приводит к автоматическому завершению порожденных им процессов. По завершении процесса его код и выделенные ему ресурсы удаляются из памяти. Однако область памяти, выделенная системой для объекта ядра "процесс", не освобождается, пока счетчик числа его пользователей не достигнет нуля, а это произойдет, когда все прочие процессы, создавшие или открывшие описатели для умершего процесса, уведомят систему (вызовом CloseHandle) о том, что ссылки на этот процесс им больше не нужны.
Функции для получения информации о текущем процессе:
HANDLE GetCurrentProcess() - описатель текущего процесса;
DWORD GetCurrentProcessId - идентификатор текущего процесса;
DWORD GetCurrentDirectory (DWORD nBufferLength, LPTSTR lpBuffer) - текущий каталог текущего процесса;
LPTSTR GetCommandLine - командная строка текущего процесса;
LPVOID GetEnvironmentStrings - указатель на блок переменных окружения;
BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode); - код завершения процесса;
DWORD GetPriorityClass(HANDLE hProcess) - класс приоритета процесса;
BOOL GetProcessShutdownParameters(LPDWORD lpdwLevel, LPDWORD lpdwFlags) - параметры перезагрузки текущего процесса;
BOOL GetProcessTimes(HANDLE hProcess, LPFILETIME ipCreationTime, LPFILETIME IpExitTime, LPFILETIME ipKernelTime, LPFILETIME lpUserTime) - временная информация о процессе;
DWORD GetProcessVersion(DWORD Processld) - версия процесса;
BOOL WINAPI Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe) возвращает информацию о первом найденном процессе в списке процессов. Если функция возвратиля значение ИСТИНА, то lppe содержит адрес блока информации о процессе.
BOOL WINAPI Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe) возвращает информацию о следующем найденном процессе в списке процессов. Если функция возвратиля значение ИСТИНА, то lppe содержит адрес блока информации о процессе:
cntUsage - Количество ссылок на процесс;
th32ProcesslD - Идентификатор процесса;
th32DefaultHeapID - Идентификатор кучи для процесса;
th32ModuleID - Идентификатор модуля процесса;
cntThreads - Количество потоков процесса;
th32ParentProcessID - Идентификатор родительского процесса;
pcPriClassBase - Базовый приоритет потоков процесса;
szExeFile - Имя загрузочного модуля процесса;
BOOL WINAPI Module32First(HANDLE hSnapshot, LPPROCESSENTRY32 lpme) возвращает информацию о первом найденном модуле указанного процесса. Если функция возвратиля значение ИСТИНА, то lpme содержит адрес блока информации о модуле.
Пример:
/*В программе выводится информация о текущем процессе*/
#include <windows.h>
#include <stdio.h>
main()
{
//получаем идентификатор процесса
DWORD ProcID=GetCurrentProcessId();
//получаем HANDLE процесса (открываем процесс)
HANDLE hCur=OpenProcess(PROCESS_ALL_ACCESS|PROCESS_QUERY_INFORMATION,
true, ProcID);
DWORD i,code,pr,sh,sh1;
int k;
char buf[100];
char *b;
printf("Info about current process\n");
//Текущий каталог
i=GetCurrentDirectory(100,buf);
printf("Current Directory ->%s\n",buf);
//Командная строка
b=GetCommandLine();
printf("Command Line ->%s\n",b);
//Переменніе окружения
b=GetEnvironmentStrings();
printf("Environment Strings->%s\n",b);
//Код статуса завершения
k=GetExitCodeProcess(hCur,&code);
printf("ExitCode Process ->%d\n",code);
//Параметры перезагрузки
k=GetProcessShutdownParameters(&sh,&sh1);
if (k==1)
{ printf("Shutdown Parameters->%s Flag->%s\n",sh,sh1); }
//Версия
i=GetProcessVersion(ProcID);
printf("Pracess Version ->%d\n",i);
getchar();
}
Функция ShellExecute
Пример №1
Пусть вы хотите открыть файл документа с именем «file.doc», т.е. запустить Word (обычно именно он связан с файлами .doc), загрузив в него указанный файл:
ShellExecute(Handle,NULL,"file.doc",NULL,NULL,SW_RESTORE);
Пример №2
Если вы хотите не открыть, а напечатать документ, записывается аналогичный оператор
ShellExecute(Handle,"print","file.doc",NULL,NULL,SW_RESTORE);
Выполнение этого оператора будет протекать следующим образом. Запустится Word, связанный с файлами .doc, в него загрузится файл file.doc, затем из Word запустится печать с атрибутами по умолчанию, после чего файл file.doc выгрузится из Word.
Пример №3
Оператор открывает приложение Windows «Калькулятор»:
ShellExecute (Handle, "open", "Calc", NULL,NULL, SW_RESTORE);
Пример №4
Открывает папку c:\Program Files\Borland:
ShellExecute(Handle, "open", "c:\\Program Files\\Borland", NULL, NULL,SW_RESTORE);
Функция ShellExecute автоматически отыскивает приложение, связанное с типом открываемого документа, и запускает его.
Для получения информации о процессе, запущенном таким образом, данную функцию лучше использовать, например, так:
SHELLEXECUTEINFO si;
memset(&si, 0, sizeof(si));
si.cbSize = sizeof(si);
si.hwnd = NULL;
si.lpVerb = "open";
si.lpFile = "Calc";
si.nShow = SW_NORMAL;
si.fMask = SEE_MASK_NOCLOSEPROCESS;
bool res = ShellExecuteEx(&si);
Тогда экземпляр структуры будет хранит необходимую информацию.
Подробнее о структуре
http://msdn.microsoft.com/en-us/library/windows/desktop/bb759784%28v=vs.85%29.aspx
Пример получения информации о всех процессах
#include <stdio.h>
#include <Windows.h>
#include <Winnt.h>
//Здесь определена структура PROCESSENTRY32 и функция CreateToolhelp32Snapshot
#include <tlhelp32.h>
FILE *f; //файл, в который будем писать всю полученную информацию
bool SpisokProcessov () {
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
//Это мы находим дескриптор снимка процессов
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
return false;
pe32.dwSize = sizeof(PROCESSENTRY32);
int i= 0;
Process32First(hProcessSnap, &pe32);
printf ("%d %s\n",++i, pe32.szExeFile);
fprintf(f,"%s \n",pe32.szExeFile);
while (Process32Next(hProcessSnap, &pe32)) {
printf ("%d %s\n",++i, pe32.szExeFile);
fprintf(f,"%s \n",pe32.szExeFile);
}
CloseHandle(hProcessSnap);
return true;
}
Варианты:
-
Создать программу, в которой с помощью функции CreateProcess порождается процесс, выполняющий проверку матрицы на симметричность относительно главной диагонали. Получить информацию о запущенном процессе.
-
Создать программу, в которой с помощью функции CreateProcess порождается два процесса: один из процессов считает сумму в строках матрицы, другой в столбцах. Получить информацию о всех процессах системы.
-
Создать программу, в которой с помощью функции CreateProcess порождается процесс выполняющий чтение файла и вывод его на экран. Получить информацию о запущенном процессе.
-
Создать программу, в которой с помощью функции CreateProcess порождается два процесса: один из процессов считывает из файла данные, другой дописывает в файл строки. Получить информацию о всех процессах системы.
-
Создать программу, в которой с помощью функции ShellExecute открывается стандартный калькулятор Windows . Получить информацию о запущенном процессе.
-
Создать программу, в которой с помощью функции ShellExecute печатается любой текстовый файл. Получить информацию о всех процессах системы.
-
Создать программу, порождающую поток, который копирует файлы. Получить информацию о текущем процессе.
-
Создать программу, в которой с помощью функции CreateProcess порождается процесс, выполняющий чтение файла и вывод его на экран. Получить информацию о всех процессах системы.
-
Создать программу, в которой с помощью функции CreateProcess порождается процесс, выполняющий циклический сдвиг строк матрицы на заданное количество позиций. Получить информацию о всех процессах системы.
-
Создать программу, порождающую несколько процессов, получить всю информацию о созданных процессах.