Home arrow Site Administration arrow Page 4 - Design and Architecture

Preforking: Process Pools - Administration

Servers typically need to be able to handle multiple clients simultaneously. This presents several problems that need to be solved. This article addresses three of those issues: allowing multiple clients to connect and stay connnected, efficient use of resources, and keeping the server responsive to each of the clients. It is excerpted from chapter five of the book The Definitive Guide to Linux Networking Programming, written by Keir Davis et. al. (Apress, 2004; ISBN: 1590593227).

TABLE OF CONTENTS:
  1. Design and Architecture
  2. Multiplexing
  3. Forking
  4. Preforking: Process Pools
  5. Multithreading
  6. Combining Preforking and Prethreading
  7. Dealing with Large Amounts of Data
By: Apress Publishing
Rating: starstarstarstarstar / 22
November 03, 2005

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

While the preceding strategy is simple to implement, there is a performance penalty to be paid. Creating a copy of a running process is expensive (in terms of time as well as resources), especially for large applications. As clients start connecting in large numbers, there can be a noticeable delay in launching the child process.

One strategy to mitigate the startup costs for a process is to fork a number of processes into a “process pool” when the application starts. This is called pre-forking, and it restricts all of the costs associated with creating a child process to the initialization section of the application. When a client connects, the process to handle it has already been created. Using this method,accept()is not called in the parent process, but in each child process. Unlike the previous example, the listening socket descriptor will not be closed in the child process. In fact, all of the children will be callingaccept()on the same listening socket. When a client connects, the kernel chooses one of the children to handle the connection. Since the child is already running, there is no process creation delay.

Example: Apache Web Server

The original Apache Web Server (prior to version 2),http://httpd.apache.org, uses process pools. However, it takes them one step further by making the process pool size dynamic. In the Apache configuration file, you are able to specify the number of initial children, the maximum number of children, the minimum number of idle children, and the maximum number of idle children.

The initial and maximum number of children is pretty straightforward. Specifying the minimum and maximum number of idle children allows the server to handle sudden spikes in usage. The parent process continually checks on the child processes to see how many are idle. It then terminates extra children or creates new children depending on the settings. Using configuration settings, the server can be finely tuned for maximum performance.

Apache version 2 takes this even a step further by introducing thread pools. Thread pools are similar to process pools in that you generate the handlers to deal with connecting clients during the initialization of the application, but you are creating threads instead of processes. We’ll talk about thread pools in the section “Prethreading: Thread Pools.”

A Preforking Server

In the following program (server3.c), the parent server process uses a loop to create the specified number of child processes. On execution, we can pass in the number of children to fork, on the command line. The parent server process then callswait()to keep it from returning before any of its children. If we don’t insert this call, the parent process will end immediately. Each child then calls accept on the same listening socket and waits for a client connection. When a connection is made, the operating system chooses one of the children to signal using a “first in, first out” methodology. That child receives the data from the client and echoes it back. Finally, the connection is closed, and the child callsaccept()again to wait for another client. Figure 5-5 shows the basic architecture for process pools.


Figure 5-5.  Basic architecture for process pools

The initial section is again similar to the earlier programs, except that we check the command line to see how large a process pool to create:

/* server3.c */
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{

First, we declare the variables that we will need.

  struct sockaddr_in sAddr;
  int listensock;
  int newsock;
  char buffer[25];
  int result;
  int nread;
  int pid;
  int nchildren = 1;
  int x;
  int val;

Then, we check the command line to see how many processes will be in our process pool. If nothing is specified, then we create only one listening process.

  if (argc > 1) {
    nchildren = atoi(argv[1]);
  }

We create the socket that will listen for incoming connections.

  listensock = socket(AF_INET, SOCK_STREAM, 
IPPROTO_TCP);

Again, we set theSO_REUSEADDRoption.

  val = 1;
  result = setsockopt(listensock, 
SOL_SOCKET, SO_REUSEADDR, &val, sizeof
(val));
  if (result < 0) {
         
perror("server3");
          return 0;
  }

Next, we bind it to a local port and all addresses associated with the machine.

  sAddr.sin_family = AF_INET;
  sAddr.sin_port = htons(1972); 
  sAddr.sin_addr.s_addr = INADDR_ANY;
 
result = bind(listensock, (struct sockaddr
*) &sAddr, sizeof(sAddr));
 
if (result < 0) {
    perror("server3");
    return 0;
 
}

Now we put it into listening mode.

  result = listen(listensock, 5);
 
if (result < 0) {
    perror("server3");
    return 0;
 
}

We create the specified number of child processes for the process pool using thefork()system call:

  for (x = 0; x < nchildren; x++) {
    if ((pid = fork()) == 0) {

Each child process calls accept on the same listening socket. When a client connects, the system will choose the next child in line to notify:

  while (1) {
    newsock = accept(listensock, NULL,NULL);

Once a client connects, we read characters it sends, echo them to the screen and client, and close the connection:

      printf("client connected to child 
process %i.\n", getpid());
      nread = recv(newsock, buffer, 25, 0);
      buffer[nread] = '\0';
      printf("%s\n", buffer);
      send(newsock, buffer, nread, 0);
      close(newsock);
      printf( "client disconnected from 
child process %i.\n", getpid());
    }
  }
}

This tells the parent process to wait until all of the children have been completed, before continuing. Of course, none of the children in this example will ever be completed:

  wait(NULL);
}

Figure 5-6 shows a sample of the output obtained on executing the program. The client was run with five child processes.


Figure 5-6.  Output from a preforking server

Notice that the processes are used in the order in which they calledaccept(). Since we have more clients than processes in the process pool, earlier processes are reused for new clients once they become free.



 
 
>>> More Site Administration Articles          >>> More By Apress Publishing
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

SITE ADMINISTRATION ARTICLES

- Coding: Not Just for Developers
- To Support or Not Support IE?
- Administration: Networking OSX and Win 7
- DotNetNuke Gets Social
- Integrating MailChimp with Joomla: Creating ...
- Integrating MailChimp with Joomla: List Mana...
- Integrating MailChimp with Joomla: Building ...
- Integrating MailChimp with Joomla
- More Top WordPress Plugins for Social Media
- Optimizing Security: SSH Public Key Authenti...
- Patches and Rejects in Software Configuratio...
- Configuring a CVS Server
- Managing Code and Teams for Cross-Platform S...
- Software Configuration Management
- Back Up a Joomla Site with Akeeba Backup

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: