30.13 Summary
In this chapter, we looked at nine different
server designs and ran them all against the same Web-style client,
comparing the amount of CPU time spent performing process
control:
0. Iterative server (baseline measurement; no
process control)
1. Concurrent server, one fork per
client
2. Preforked, with each child calling
accept
3. Preforked, with file locking to protect
accept
4. Preforked, with thread mutex locking to
protect accept
5. Preforked, with parent passing socket
descriptor to child
6. Concurrent server, create one thread per
client request
7. Prethreaded with mutex locking to protect
accept
8. Prethreaded with main thread calling
accept
We can make a few summary comments:
-
First, if the server is not heavily used, the
traditional concurrent server model, with one fork per
client, is fine. This can even be combined with inetd,
letting it handle the accepting of each connection. The remainder
of our comments are meant for heavily used servers, such as Web
servers.
-
Creating a pool of children or a pool of threads
reduces the process control CPU time compared to the traditional
one-fork-per-client design by a factor of 10 or more. The
coding is not complicated, but what is required, above and beyond
the examples that we have shown, is monitoring the number of free
children and increasing or decreasing this number as the number of
clients being served changes dynamically.
-
Some implementations allow multiple children or
threads to block in a call to accept, while on other
implementations, we must place some type of lock around the call to
accept. Either file locking or Pthread mutex locking can
be used.
-
Having all the children or threads call
accept is normally simpler and faster than having the main
thread call accept and then pass the descriptor to the
child or thread.
-
Having all the children or threads block in a
call to accept is preferable over blocking in a call to
select because of the potential for select
collisions.
-
Using threads is normally faster than using
processes. But, the choice of one-child-per-client or
one-thread-per-client depends on what the OS provides and can also
depend on what other programs, if any, are invoked to service each
client. For example, if the server that accepts the
client's connection calls fork and exec, it can
be faster to fork a single threaded process than to
fork a multithreaded process.
|