Processes


The UNIX and Windows process models are very different, and the major difference lies in the creation of processes. UNIX uses fork to create a new copy of a running process and exec to replace a process s executable file with a new one. Windows does not have a fork function. Instead, Windows creates processes in one step by using CreateProcess . While there is no need in Win32 to execute the process after its creation (as it will already be executing the new code), the standard exec functions are still available in Win32.

These differences (and others) result in the need to convert the UNIX code beforeit can run on a Win32 platform.

The areas that you need to consider that are covered in this section are:

  • Process creation

  • Replacing a process s executable code

  • Spawning child processes and the process hierarchy

  • Waiting for a child process

  • Setting process resource limits

Windows jobs are also introduced. Jobs allow you to group processes together for management purposes. This functionality is not available in UNIX.

Note  

There are a number of process management functions in the Win32 API. For detailsof these functions, see the Win32 API reference on the Microsoft Developer Network (MSDN) Web site.

Creating a New Process

In UNIX, a developer creates a new process by using fork . The fork function creates a child process that is an almost exact copy of the parent process. The fact that the child is a copy of the parent ensures that the process environment is the same forthe child as it is for the parent.

In Windows, the CreateProcess function enables the parent process to create an operating environment for a new process. The environment includes the working directory, window attributes, environment variables , execution priority, and command line arguments. A handle is returned by the CreateProcess function, which enables the parent application to perform operations on the process and its environment while it is executing. Unlike UNIX, the executable file run by CreateProcess is not a copy of the parent process, and it has to be explicitly specified in the call to CreateProcess .

An alternative to using CreateProcess is to use one of the spawn functions that is present in the standard C runtime. There are 16 variations of the spawn function. Each spawn function creates and executes a new process. Many of these have the same functionality as the similarly named exec functions. The spawn functions include an additional argument that permits the new process to replace the current process, suspend the current process until the spawned process terminates, run asynchronously with the calling process, or run simultaneously and detach as a background process.

For a UNIX application to change the executable file run in the child process, the child process must explicitly call an exec function to overwrite the executable file with a new application. The combination of fork and exec is similar to, but not the same as, CreateProcess .

The following example shows a UNIX application that forks to create a child process and then runs the UNIX ps command by using execlp .

Creating a Process in UNIX Using fork and exec

 #include <unistd.h> #include <stdio.h> #include <sys/types.h> int main() {     pid_t pid;     printf("Running ps with fork and execlp\n");     pid = fork();     switch(pid)      {     case -1:         perror("fork failed");         exit(1);     case 0:         if (execlp("ps", NULL) < 0) {             perror("execlp failed");             exit(1);         }         break;     default:         break;     }     printf("Done.\n");     exit(0); } 

You can port this code to Windows by using the Win32 CreateProcess function discussed earlier, or by using a spawn function from the standard C runtime library. In both cases, the old and new processes run parallel, asynchronously.

Creating a Process in Windows Using CreateProcess

 #include <windows.h> #include <process.h> #include <stdio.h> void main() {     STARTUPINFO                si;     PROCESS_INFORMATION        pi;     GetStartupInfo(&si);     printf("Running Notepad with CreateProcess\n");     CreateProcess(NULL, "notepad",       // Name of app to launch NULL,                      // Default process security attributes NULL,                      // Default thread security attributes FALSE,                     // Dont inherit handles from the parent 0,                         // Normal priority NULL,                      // Use the same environment as the parent NULL,                      // Launch in the current directory &si,                       // Startup Information &pi);                      // Process information stored upon return     printf("Done.\n");     exit(0); } 

The arguments supported by CreateProcess (shown in the preceding example) give you a considerable degree of control over the newly created process. This contrasts with the spawn functions, which do not provide options to set process priority, security attributes, or the debug status.

The next example shows a port of the same code using the _spawnlp function.

Creating a Process in Windows Using Spawn

 #include <windows.h> #include <process.h> #include <stdio.h> void main() {     printf("Running Notepad with spawnlp\n");     _spawnlp(_P_NOWAIT, "notepad", "notepad", NULL);     printf("Done.\n");     exit(0); } 

Running either of the preceding examples yields a console window similar to that shown here:

click to expand
Figure 9.1: Output from spawn example code

Replacing a Process Image (exec)

A UNIX application replaces the executing image with that of another applicationby using one of the exec functions. As mentioned previously, a fork followed by an exec is similar to CreateProcess .

Windows supports the six POSIX variants of the exec function plus two additional ones ( execlpe and execvpe ). The function signatures are identical, and come as part of the standard C runtime. Porting UNIX code that uses exec to Win32 is easy to understand. The following is a simple UNIX example showing the use of the execlp function.

Note  

For more information about exec support on Win32, see the standard C runtime library documentation that comes with the Microsoft Visual Studio development system.

Replacing a Process Image in UNIX Using exec

 #include <unistd.h> #include <stdio.h> int main() {     printf("Running ps with execlp\n");     execlp("ps", "ps", "-l", 0);     printf("Done.\n");     exit(0); } 

The preceding example compiles and runs on Windows with only minor modifi-cations. It does, however, require an executable file called ps.exe to be available(one is included with the Interix product).

The <unistd.h> include file is not a valid header file when using Windows. To use this example when using Windows, you need to change the header file to <process.h>. Doing so allows you to compile, link, and run this simple application.

Waiting for a Spawned Process

In the preceding section, the example showed how you can create an asynchronous process where the parent and child processes execute simultaneously. No synchronization is performed. This section describes how to modify the previous exampleto include functionality that enables the parent process to wait for the child process to complete or terminate before continuing.

To accomplish this in UNIX, a developer would use one of the wait functions to suspend the parent process until the child process terminates. The same semantics are available when using Windows. The functions used are different, but the results are the same.

When you view the examples, keep in mind that this is not an exhaustive comparison between the two platforms. A very simple scenario is described, but if you needto expand the scenario to include waiting for multiple child processes, the spawn example does not map adequately as it does not include support for this functionality. In this case, you need to consider the CreateProcess approach and WaitForMultipleObjects .

To see the code for this example, see Appendix 9.7: Waiting for a Spawned Process.

Processes vs. Threads

In the next example, the UNIX code is forking a process, but not executing a separate runtime image. This creates a separate execution path within the application. When using Windows, this is achieved by using threads rather than processes. If your UNIX application creates separate threads of execution in this manner, you should use the Win32 API CreateThread .

The process of creating threads is covered in the next section, Threads.

UNIX Code with Forking Executable

 #include <unistd.h> #include <stdio.h> #include <sys/types.h> int main() {     pid_t pid;     int n;     printf("fork program started\n");     pid = fork();     switch(pid)      {     case -1:         perror("fork failed");         exit(1);     case 0:         puts("Im the child");         break;     default:         puts("Im the parent");         break;     }     exit(0); } 

Managing Process Resource Limits

Developers often want to create processes that run with a specific set of resource restrictions. In some cases, they may impose limitations for the purposes of stress testing or forced failure condition testing. In other cases, however, the limitations may be imposed to restrict runaway processes from using up all available memory, CPU cycles, or disk space.

In UNIX, the getrlimit function retrieves resource limits for a process, the getrusage function retrieves current usage, and setrlimit function sets new limits. The common limit names and their meanings are described in Table 9.1 on the next page.

Table 9.1: Common Limit Names and Definitions

Limit

Description

RLIMIT_CORE

The maximum size , in bytes, of a core file created by this process. If the core file is larger than RLIMIT_CORE, the write is terminated at this value. If the limit is set to 0, then no core files are created.

RLIMIT_CPU

The maximum time, in seconds, of CPU time a process can use. If the process exceeds this time, the system generates SIGXCPU for the process.

RLIMIT_DATA

Maximum size, in bytes, of a process s data segment. If the data segment exceeds this value, the functions brk , malloc , and sbrk will fail.

RLIMIT_FSIZE

The maximum size, in bytes, of a file created by a process. If the limit is 0, the process cannot create a file. If a write or truncation call exceeds the limit, further attempts will fail.

RLIMIT_NOFILE

The highest possible value for a file descriptor, plus one. This limits the number of file descriptors a process may allocate. If more than RLIMIT_NOFILE files are allocated, functions allocating new file descriptors may fail with the error EMFILE.

RLIMIT_STACK

The maximum size, in bytes, of a process s stack. The stack won t automatically exceed this limit; if a process tries to exceed the limit, the system generates SIGSEGV for the process.

RLIMIT_AS

Maximum size, in bytes, of a process s total available memory. If this limit is exceeded, the memory functions brk , malloc , mmap , and sbrk will fail with errno set to ENOMEM, and automatic stack growth will fail as described for RLIMIT_STACK.

Windows uses job objects to set job limits (rather than process limits). Unlike in UNIX, Windows job objects do not have File input/output (I/O) source restrictions. If you require File I/O limits in your application, you need to create your own code to handle this.

Windows Job Objects

Windows supports the concept of job objects, which allows you to group one or more processes into a single entity. Once a job object has been populated with the desired processes, the entire group can be manipulated for various purposes ranging from termination to imposing resource restrictions.

The restrictions that job objects allow you to enforce are described in Table 9.2.

Table 9.2: Job Objects

Member

Description

Notes

PerProcessUser-TimeLimit

Specifies the maximum user -mode time allotted to each process (in 100 ns intervals).

The system automatically terminates any process that uses more than its allotted time. To set this limit, specify the JOB_OBJECT_LIMIT_PROCESS_TIME flag in the LimitFlags member.

PerJobUser-TimeLimit

Specifies how much more user-mode time the processes in this job can use (in 100 ns intervals).

By default, the system automatically terminates all processes when this time limit is reached. You can change this value periodically as the job runs. To set this limit, specify the JOB_OBJECT_LIMIT_JOB_TIME flag in the LimitFlags member.

LimitFlags

Specifies which restrictions to apply to the job.

See the job objects API reference for more information.

MinimumWorkingSetSize/
MaximumWorkingSetSize

Specifies the minimum and maximum working set size for each process (not for all processes within the job).

Normally, a process s working set cangrow above its maximum; setting MaximumWorkingSetSize forces a hard limit. Once the process s working set reaches this limit, the process pages against itself. Calls to SetProcessWorkingSetSize by an individual process are ignored unless the process is just trying to empty its working set. To set this limit, specify the JOB_OBJECT_ LIMIT_WORKINGSET flag in the LimitFlags member.

ActiveProcessLimit

Specifies the maximum number of processes that can run concurrently in the job.

Any attempt to go over this limit causes the new process to be terminated with a not enough quota error. To set this limit, specify the JOB_OBJECT_ LIMIT_ACTIVE_PROCESS flag in the LimitFlags member.

Affinity

Specifies the subset of the CPU(s) that can run the processes.

Individual processes can limit this even further. To set this limit, specify the JOB_OBJECT_ LIMIT_AFFINITY flag in the LimitFlags member.

PriorityClass

Specifies the priority class that all processes use.

If a process calls SetPriorityClass , the call will return successfully even though it actually fails. If the process calls GetPriorityClass , the function returns what the process has set the priority class to even though this might not be process s actual priority class. In addition, SetThreadPriority fails to raise threads above normal priority but can be used to lower a thread s priority. To set this limit, specify the JOB_OBJECT_LIMIT_PRIORITY_CLASS flag in the LimitFlags member.

SchedulingClass

Specifies a relative time quantum difference assigned to threads in the job.

Value can be from 0 to 9 inclusive; 5 is the default. See the text after this table for more information. To set this limit, specify the JOB_OBJECT_LIMIT_SCHEDULING_CLASS flag in the LimitFlags member.

As you may have observed by reviewing the table for setrlimit and job objects, the restrictions offered by job objects are comparable except in one major area: File I/O.

Limiting File I/O When Using Windows

When a process is created in UNIX, the Process Control Block (PCB) in kernel space contains an array of limits that is initialized with default values. In the case of the RLIMIT_FSIZE limit, the write procedures in the kernel are aware of the limit structure in the PCB, and these functions make checks to enforce the limits. The Windows operating system does not implement similar limits on files. To solve this problem, you must write your own solution and build it into your application.

This section presents a solution that you could use in your application. This solution emulates the UNIX file resource limits with:

  • An array of limits held as a static variable.

    This is similar to how some of the C runtime functions use static variables.

  • Versions of the UNIX functions getrlimit() and setrlimit() .

    These functions manipulate the limit array.

  • Wrappers for each of the disk write functions.

    These wrappers are resource limit aware.

This solution is implemented as three files. Two of the files, resource.h and resource.c, implement the getrlimit() , setrlimit() , rfwrite() , and _rwrite() functions. Only fwrite() and _ write() are wrapped since they are the most common disk write functions encountered in the UNIX world. The third file is rlimit.c, which is a very simple test program used to confirm that rfwrite() will fail when the limit was reached.

For more information, see Appendix 9.2: Limiting File I/O.

Process Accounting

The Win32 API has several functions for gathering process accounting information:

  • GetProcessShutdownParameters

  • GetProcessTimes

  • GetProcessWorkingSetSize

  • SetPriorityClass

  • SetProcessShutdownParameters

  • SetProcessWorkingSetSize

Alternatively, a better method of obtaining process information is through the Windows Management Instrumentation (WMI) API.

For more information on WMI, see http://msdn.microsoft.com/downloads/default.asp?URL=/code/sample.asp?url=/msdn-files/027/001/566/msdncompositedoc.xml .




UNIX Application Migration Guide
Unix Application Migration Guide (Patterns & Practices)
ISBN: 0735618380
EAN: 2147483647
Year: 2003
Pages: 134

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net