FREE BOOK

Chapter 6: Process Management

Posted by Addison Wesley Free Book | Windows Forms February 10, 2010
This chapter explains the basics of process management and also introduces the basic synchronization operations and wait functions that will be important throughout the rest of the book.

Process Security

Normally, CreateProcess gives PROCESS_ALL_ACCESS rights. There are, however, several specific rights, including PROCESS_QUERY_INFORMATION, CREATE_PROCESS, PROCESS_TERMINATE, PROCESS_SET_INFORMATION, DUPLICATE_HANDLE, and CREATE_THREAD. In particular, it can be useful to limit PROCESS_TERMINATE rights to the parent process given the frequently mentioned dangers of terminating a running process. Chapter 15 describes security attributes for processes and other objects.

UNIX waits for process termination using wait and waitpid, but there are no time-outs even though waitpid can poll (there is a nonblocking option). These functions wait only for child processes, and there is no equivalent to the multiple wait on a collection of processes, although it is possible to wait for all processes in a process group. One slight difference is that the exit code is returned with wait and waitpid, so there is no need for a separate function equivalent to GetExit- CodeProcess.

UNIX also supports environment strings similar to those in Windows. getenv (in the C library) has the same functionality as GetEnvironmentVariable except that the programmer must be sure to have a sufficiently large buffer. putenv, setenv, and unsetenv (not in the C library) are different ways to add, change, and remove variables and their values, with functionality equivalent to SetEnvironmentVariable.

Example: Parallel Pattern Searching

Now is the time to put Windows processes to the test. This example, grepMP, creates processes to search for patterns in files, one process per search file. The simple pattern search program is modeled after the UNIX grep utility, although the technique would apply to any program that uses standard output. The search program should be regarded as a black box and is simply an executable program to be controlled by a parent process; however, the project and executable (grep.exe) are in the Examples file.

The command line to the program is of the form

grepMP pattern F1 F2 ... FN

The program, Program 6-1, performs the following processing:

  • Each input file, F1 to FN, is searched using a separate process running the same executable. The program creates a command line of the form grep pattern FK.
  • The temporary file handle, specified to be inheritable, is assigned to the hStdOutput field in the new process's start-up information structure.
  • Using WaitForMultipleObjects, the program waits for all search processes to complete.
  • As soon as all searches are complete, the results (temporary files) are displayed in order, one at a time. A process to execute the cat utility (Program 2-3) outputs the temporary file.
  • WaitForMultipleObjects is limited to MAXIMUM_WAIT_OBJECTS (64) handles, so the program calls it multiple times.
  • The program uses the grep process exit code to determine whether a specific process detected the pattern.

Figure 6-3 shows the processing performed by Program 6-1, and Run 6-1 shows program execution and timing results.



Figure 6.3 - File Searching using Multiple Processes

Program 6-1 grepMP: Parallel Searching

/* Chapter 6. grepMP. */
/* Multiple process version of grep command. */
#include "Everything.h"
int _tmain (DWORD argc, LPTSTR argv[])
/* Create a separate process to search each file on the command line. Each process is given a temporary file, in the current directory, to receive the results. */
{
HANDLE hTempFile;
SECURITY_ATTRIBUTES stdOutSA = /* SA for inheritable handle. */
{sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
TCHAR commandLine[MAX_PATH + 100];
STARTUPINFO startUpSearch, startUp;
PROCESS_INFORMATION processInfo;
DWORD iProc, exitCode, dwCreationFlags = 0;
HANDLE *hProc; /* Pointer to an array of proc handles. */
typedef struct {TCHAR tempFile[MAX_PATH];} PROCFILE;
PROCFILE *procFile; /* Pointer to array of temp file names. */
grep pattern argv [3]
argv [1], argv [2], ..., argv [N+1]
for (i = 1; i <= N; i++) {
StartUp.hStdOut =
CreateFile (Temp [i])
CreateProcess (grep pattern
argv [i+1])
}
WaitForMultipleObjects;
ยทยทยท
/* Display search results */
for (i = 1; i <= N; i++) {
CreateProcess (cat Temp [i])
WaitForSingleObject;
}
                ExitProcess()
grep pattern argv
                ExitProcess()
grep pattern argv [2]
                ExitProcess()
GetStartupInfo (&startUpSearch);
GetStartupInfo (&startUp);
procFile = malloc ((argc - 2) * sizeof (PROCFILE));
hProc = malloc ((argc - 2) * sizeof (HANDLE));
/* Create a separate "grep" process for each file. */
for (iProc = 0; iProc < argc - 2; iProc++) {
_stprintf (commandLine, _T ("grep \"%s\" \"%s\""),
argv[1], argv[iProc + 2]);
GetTempFileName (_T ("."), _T ("gtm"), 0,
procFile[iProc].tempFile); /* For search results. */
hTempFile = /* This handle is inheritable */
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);
/* Create a process to execute the command line. */
CreateProcess (NULL, commandLine, NULL, NULL, TRUE,
dwCreationFlags, NULL, NULL, &startUpSearch, &processInfo);
/* Close unwanted handles. */
CloseHandle (hTempFile); CloseHandle (processInfo.hThread);
hProc[iProc] = processInfo.hProcess;
}
/* Processes are all running. Wait for them to complete. */
for (iProc = 0; iProc < argc - 2; iProc += MAXIMUM_WAIT_OBJECTS)
WaitForMultipleObjects ( /* Allows a large # of processes */
min (MAXIMUM_WAIT_OBJECTS, argc - 2 - iProc),
&hProc[iProc], TRUE, INFINITE);
/* Result files sent to std output using "cat." */
for (iProc = 0; iProc < argc - 2; iProc++) {
if (GetExitCodeProcess(hProc[iProc], &exitCode) && exitCode==0)
{
/* Pattern was detected -- List results. */
if (argc > 3) _tprintf (_T ("%s:\n"), argv[iProc + 2]);
_stprintf (commandLine, _T ("cat \"%s\""),
procFile[iProc].tempFile);
CreateProcess (NULL, commandLine, NULL, NULL, TRUE,
dwCreationFlags, 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;
}

Run 6-1 shows grepMP execution for large and small files, and the run contrasts sequential grep execution with parallel grepMP execution to perform the same task. The test computer has four processors; a single or dual processor computer will give different timing results. Notes after the run explain the test operation and results.



Run 6-1 grepMP: Parallel Searching

Run 6-1 uses files and obtains results as follows:

  • The small file test searches two Examples files, Presidents.txt and Monarchs. txt, which contain names of U.S. presidents and English monarchs, along with their dates of birth, death, and term in office. The "i" at the right end of each line is a visual cue and has no other meaning. The same is true of the "x" at the end of the randfile-generated files.
  • The large file test searches four randfile-generated files, each with 10 million 64-byte records. The search is for a specific record number (1234562), and each file has a different random key (the first 8 bytes).
  • grepMP is more than four times faster than four sequential grep executions (Real Time is 15 seconds compared to 77 seconds), so the multiple processes gain even more performance than expected, despite the process creation overhead.
  • timep is Program 6-2, the next example. Notice, however, that the grepMP system time is zero, as the time applies to grepMP itself, not the grep processes that it creates.

Processes in a Multiprocessor Environment

In Program 6-1, the processes and their primary (and only) threads run almost totally independently of one another. The only dependence is created at the end of the parent process as it waits for all the processes to complete so that the output files can be processed sequentially. Therefore, the Windows scheduler can and will run the process threads concurrently on the separate processors of a multiprocessor computer. As Run 6-1 shows, this can result in substantial performance improvement when performance is measured as elapsed time to execute the program, and no explicit program actions are required to get the performance improvement.

The performance improvement is not linear in terms of the number of processors due to overhead costs and the need to output the results sequentially. Nonetheless, the improvements are worthwhile and result automatically as a consequence of the program design, which delegates independent computational tasks to independent processes.

It is possible, however, to constrain the processes to specific processors if you wish to be sure that other processors are free to be allocated to other critical tasks. This can be accomplished using the processor affinity mask (see Chapter 9) for a process or thread.

Finally, it is possible to create independent threads within a process, and these threads will also be scheduled on separate processors. Chapter 7 describes threads and related performance issues.

Total Pages : 7 34567

comments