Программа параллельной обработки файлов

Лабораторная работа

Многопроцессная обработка данных

Процессы и потоки в Windows

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

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

• Стек вызовов процедур, прерываний, обработчиков исключений и автоматических данных.

• Локальная память потока (Thread Local Storage - TLS) – массивы указателей, которые дают процессу возможность выделять память для создания собственного уникального окружения данных.

• Параметр стека, полученный от потока, создавшего данный, и обычно уникальный для каждого потока,

• Структура контекста, управляемая ядром, со значениями аппаратных регистров.

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

Функция CreateProcess - основная функция для управления процессами в Win32. Она создает процесс с одним потоком. Так как процесс требует наличия кода, в вызове функции CreateProcess необходимо указывать имя исполняемого файла программы.

Функция CreateProcess для обеспечения гибкости и мощности имеет десять параметров. Функция CreateProcess создает новый процесс с первичным потоком, функция CloseHandle лишь удалит ссылку на поток. Структура функции CreateProcess имеет следующий вид:

BOOL CreateProcess (

LPCTSTR lpszImageName,

LPTSTR lpszCommandLine,

LPSECURITY_ATTRIBOTES lpsaProcess,

LPSECURITY_ATTRIBUTES lpsaThread,

BOOL fInheritHandles,

DWORD fdwCreate,

LPVOID lpvEnvironment,

LPCTSTR lpszCurDir,

LPSTARTUPINFO lpsiStartInfo,

LPPROCESS_INFORMATION lppiProcInfo);

Возвращаемое значение: TRUE только в случае, если процесс и поток были успешно созданы.

lpszImageName, lpszCommandLine указывают на исполняемую программу и параметры командной строки так, как это указано в разделе определение исполняемого образа и командной строки, lpsaProcess и lpsaThread указывают на структуры атрибутов безопасности процесса и потока. В случае если их значение NULL, используются атрибуты по умолчанию, fInheritHandles определяет, должен ли новый процесс наследовать копии открытых наследуемых дескрипторов (файлов, отображений и т.д.) процесса, вызвавшего функцию. Унаследованные дескрипторы имеют те же атрибуты, что и их оригиналы;

fdwCreate может объединять несколько флагов:

• CREATE_SUSPENDED первичный поток находится в состоянии ожидания и будет запущен только после вызова функции ResumeThread.

• DETACHED_PROCESS и CREATE_NEW_CONSOLE - взаимоисключающие флаги, которые нельзя установить оба сразу.

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

• CREATE_NEW_PROCESS_GROUP определяет новый процесс как корневой для новой группы процессов. Если процессы группы совместно используют одну консоль, то все они получают сигнал управления консолью (<Ctrl+C> или <Ctrl+Break>).

lpvEnvironment указывает блок окружения для нового процесса.

Если этот параметр имеет значение NULL, то используется окружение родительского процесса. Блок окружения содержит строки имен и значений, такие как путь поиска.

lpszCurDir определяет диск и каталог для нового процесса. Если его значение NULL, то будет использован рабочий каталог родительского процесса.

lpsiStartInfo указывает вид основного окна программы и дескрипторы стандартных устройств для нового процесса. Обычно используется информация родителя, полученная вызовом функции GetStartupInfo. Перед вызовом функции CreateProcess структуру STARTUPINFO можно также обнулить, а затем для указания стандартных дескрипторов ввода, вывода и сообщений об ошибках установить стандартные значения соответствующих полей (hStdln, hStdOut и hStdErr) структуры STARTUPINFO. Для того чтобы это работало, необходимо присвоить другому полю структуры STARTUPINFO - dwFlags значение STARTF_USESTDHANDLES, а затем установите все дескрипторы, необходимые новому процессу.

Убедитесь, что дескрипторы могут быть унаследованы и в функции CreateProcess установлены флаги fInheritProcess.

lppiProcinfo определяет структуру, в которую будут помещены дескрипторы и идентификаторы для созданных процесса и потока.

Структура PROCESS_INFORMATION определена так:

typedef struct PROCESS_INFORMATION (

HANDLE hProcess;

HANDLE hThread,

DWORD dwProcessId;

DWORD dwThreadld;

) PROCESS_INFORMATION;

Определение исполняемого образа и командной строки

Параметры lpszImageName и lpszCommandLine определяют имя исполняемого образа, руководствуясь приведенными ниже правилами.

• Если значение параметра lpszImageName не NULL, то это имя исполняемого файла. В случае если оно содержит пробелы, можно использовать кавычки.

• В противном случае имя исполняемого файла - первый элемент параметра lpszCommandLine.

Обычно указывают только параметр lpszCommandLine, присваивая параметру lpszImageName значение NULL. Тем не менее существуют подробные правила для параметра lpszImageName.

• Если lpszImageName имеет значение не NULL, то он указывает имя загрузочного файла. Необходимо указывать полный путь и имя файла.

Можно указать только имя файла, тогда будут использованы текущие диск и каталог без дополнительного поиска. В имя файла включают его расширение (.ЕХЕ, .ВАТ и т.п.).

• Если параметр lpszImageName имеет значение NULL, то за имя программы принимается первый элемент параметра lpszCommandLine, отделенный пробелом. Если это имя не содержит полного пути, происходит поиск в следующей последовательности:

1. Каталог образа текущего процесса.

2. Текущий каталог.

3. Системный каталог Windows, полученный посредством

вызова функции GetSystemDirectory.

4. Каталог Windows, полученный посредством вызова

функции GetWindowsDirectory.

5. Каталоги, указанные в переменной окружения PATH.

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

Отметим, что командная строка не является константой. Это следует из того факта, что параметры argv для главной программы тоже не константы. Программа может изменять свои параметры, поэтому желательно делать изменения в копии строки параметров.

Программа параллельной обработки файлов

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

Программа выполняет следующую обработку:

• В каждом файле, от F1 до FN, проводится поиск с использованием отдельных процессов, запускающих один и тот же исполняемый файл. Программа создает командную строку в виде grep pattern FK (pattern – это шаблон для поиска в файлах). При запуске программы из командной строки он задается в виде текста в кавычках, например “ dot “.

• Дескриптор временного файла, определенный как наследуемый, назначается полю hstdOut структуры запуска нового процесса.

• Используя функцию WaitForMultipleObjects, программа ожидает завершения всех процессов поиска.

• После того как поиск закончен, по порядку выводятся результаты (временные файлы). Во временный файл также выводятся результаты при выполнении команды cat.

• Функция WaitForMultipleObjects ограничена в числе дескрипторов величиной MAXIMUM_WAIT_OBJECTS , поэтому она вызывается несколько раз.

• Чтобы установить, найден ли образец определенным процессом, программа определяет код завершения процесса функцией GetExitCodeProcess.

Листинг программы параллельной обработки файлов

#include "stdafx.h"

//#include "EvryThng.h"

#define BUF_SIZE 256

static VOID cat(LPTSTR, LPTSTR);

int _tmain (DWORD argc, LPTSTR argv [])

/* Создает отдельный процесс для каждого файла из командной строки. Для результатов предоставляется временный файл в текущем каталоге. */

{

HANDLE hTempFile;

BOOL prov;

TCHAR outFile[MAX_PATH + 100];

SECURITY_ATTRIBUTES StdOutSA =

/* права доступа для наследуемого дескриптора. */

{sizeof

(SECURITY_ATTRIBUTES), NULL, TRUE};

TCHAR CommandLine [MAX_PATH + 100];

STARTUPINFO StartUpSearch, StartUp;

PROCESS_INFORMATION ProcessInfo;

DWORD iProc, ExCode;

HANDLE *hProc;

/* Указатель на массив дескрипторов процессов. */

typedef struct

{TCHAR TempFile [MAX_PATH];}

PROCFILE;

PROCFILE *ProcFile;

/* Указатель на массив имен временных файлов. */

/* Информация запуска для каждого дочернего процесса поиска и для процесса, который будет выводить результаты. */

GetStartupInfo (&StartUpSearch);

GetStartupInfo (&StartUp);

/* Зарезервировать место для массива структур данных процессов,

содержащих дескриптор процесса и имя временного файла. */

ProcFile = (PROCFILE *)malloc ((argc - 2) *sizeof (PROCFILE));

hProc = (HANDLE *)malloc ((argc-2) *sizeof (HANDLE));

/* Создать отдельный процесс "grep" для каждого файла из командной

строки*/

for (iProc = 0; iProc < argc - 2; iProc++)

{

/* Создать командную строку вида grep argv [1] argv [iProc + 2] */

_stprintf (CommandLine, _T ("%s%s%s"),

_T ("grep "), argv [1] , argv [iProc + 2]);

_tprintf(_T("%s\n"),CommandLine);

if (GetTempFileName (_T ("."), _T ("gtm"), 0,

ProcFile [iProc].TempFile) == 0)

/* Указываем стандартный вывод для процесса поиска. */

hTempFile = /* Этот дескриптор наследуемый */

CreateFile (ProcFile [iProc].TempFile, GENERIC_WRITE,

FILE_SHARE_READ | FILE_SHARE_WRITE, &StdOutSA,

CREATE_ALWAYS,

FILE_ATTRIBUTE_NORMAL, NULL);

StartUpSearch.dwFlags = STARTF_USESTDHANDLES;

StartUpSearch.hStdOutput = hTempFile;

StartUpSearch.hStdError = hTempFile;

StartUpSearch.hStdInput = GetStdHandle (STD_INPUT_HANDLE) ;

/* Создаем процесс для выполнения командной строки. */

CreateProcess (NULL, CommandLine, NULL, NULL,

TRUE, 0, NULL, NULL, &StartUpSearch, &ProcessInfo);

/* Закрываем ненужные дескрипторы */

CloseHandle (hTempFile) ;

CloseHandle (ProcessInfo.hThread) ;

/* Сохраняем дескриптор процесса. */

hProc [iProc] = ProcessInfo.hProcess;

}

/* Все процессы выполняются, подождем их завершения. */

for (iProc = 0; iProc < argc-2; iProc += MAXIMUM_WAIT_OBJECTS)

WaitForMultipleObjects (min (MAXIMUM_WAIT_OBJECTS, argc - 2

-iProc),

&hProc[iProc] , TRUE, INFINITE);

/* Результирующие файлы отсылаются на стандартный вывод с

помощью "cat". */

for (iProc = 0; iProc < argc - 2; iProc++)

{

printf("Proc= %d\n", iProc);

prov=GetExitCodeProcess (hProc [iProc], &ExCode);

if(ExCode != 0) DeleteFile (ProcFile [iProc].TempFile);

if (GetExitCodeProcess (hProc [iProc], &ExCode) && ExCode == 0)

{

/* Образец найден - показать результаты. */

if (argc > 3) _tprintf (_T ("%s : \n" ), argv [iProc+2]);

fflush (stdout) ;

/* требуется для использования стандартного вывода

несколькими процессами */

_stprintf (outFile, _T("%s"),ProcFile[iProc].TempFile);

cat( argv[iProc+2], (LPTSTR) outFile);

_stprintf (CommandLine, _T("%s%s"),

_T ("cat "), ProcFile[iProc].TempFile);

_tprintf(_T("%s\n"),CommandLine);

CreateProcess (NULL, CommandLine, NULL, NULL,

TRUE, 0, NULL, NULL, &StartUp, &ProcessInfo);

WaitForSingleObject (ProcessInfo.hProcess, INFINITE);

CloseHandle (ProcessInfo.hProcess);

CloseHandle (ProcessInfo.hThread);

}

CloseHandle (hProc [iProc] );

/*DeleteFile (ProcFile [iProc].TempFile); */

}

free (ProcFile);

free (hProc) ;

return 0;

}

static VOID cat(LPTSTR hInFile, LPTSTR hOutFile)

{

CopyFile( hInFile, hOutFile, FALSE);

return;

}

Содержание работы

1.Изучить основные функции управления процессами: CreateProcess, OpenProcess, ExitProcess и их параметры.

2.Изучить функции завершения процессов WaitForSingleObject, WaitForMultipleObjects и возможные возвращаемые значения в случае успешного выполнения этих функций.

3.Набрать и отладить программу параллельной обработки файлов в среде Visual Studio C++ .NET 2003.

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

5.Разобраться с работой программы и объяснить ее функционирование.

6.Подготовить отчет по выполненной работе.

7.В отчете ответить на контрольные вопросы.

4.Порядок выполнения программ в среде Visual Studio C++ .NET 2003

1. Запустить Microsoft Visual Studio C++ .NET 2003.

2. Создать проект под именем lab_proc.

3. В файл lab_proc.cpp набрать текст программы.

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

5. Запустить программу из командной строки, задавая шаблон поиска и

имена файлов, в которых следует найти шаблон.

Содержание отчета

В отчет должны быть включены следующие разделы:

1. Общие сведения о механизме управления процессами.

2. Основные функции управления процессами и их параметры.

3. Листинг программы параллельной обработки файлов.

4. Пояснения работы программы.

5. Письменные ответы на вопросы.

Контрольные вопросы

1.Какую фунцию выполняет CreateProcess?

2.Какую фунцию выполняет OpenProcess?

3.Какую фунцию выполняет ExitProcess ?

4.Какую фунцию выполняет WaitForSingleObject?

5.Как запускается программа параллельной обработки файлов?

6.Поясните работу программы параллельной обработки файлов.

Наши рекомендации