Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Назад Сервер TCP/IP ... много серверов хороших и разных Вперед

Сервер с предварительным созданием копий процесса

Так что же получается: для серверов, работающих на высоко интенсивных потоках запросов, с традиционным fork-методом всё так плохо? Отнюдь! Нужно только поменять вызовы fork() и accept() местами – создать заранее некоторый пул обслуживающих процессов, каждый из которых до прихода клиентского запроса будет заблокирован на accept() (кстати, все accept() на одном и том же прослушиваемом сокете, что не предусмотрено спецификацией, но работает!). А после отработки клиентского запроса заблаговременно создать новый обслуживающий процесс. Эта техника известна как «предварительный fork» или pre-fork. Меняем текст сервера (файл ech11.cc):

#include "common.h" 
#include <sys/wait.h> 
const int NUMPROC = 3; 
// ретранслятор c предварительным fork (pefork) 
int main( int argc, char *argv[] ) { 
   int ls = getsocket( PREFORK_PORT ), rs; 
   setv( argc, argv ); 
   for( int i = 0; i < NUMPROC; i++ ) { 
      if( fork() == 0 ) { 
         int rs; 
         while( true ) { 
            if( ( rs = accept( ls, NULL, NULL ) ) < 0 ) errx( "accept error" ); 
            retrans( rs ); 
            close( rs ); 
            if( debug ) cout << i << flush; 
            delay( 250 ); // пауза 250 usec. 
         }; 
      }; 
   }; 
   for( int i = 0; i < NUMPROC; i++ ) wait( NULL ); 
   exit( EXIT_SUCCESS ); 
}; 

Результаты:

$ sudo nice -n19 ./ech11 -v 
waiting on port 51003 ... 
verbose mode 
00210210210210210210

$ ./cli -a 192.168.1.5 -p 51003 -n 20 
host: 192.168.1.5, TCP port = 51003, number of echoes = 20 
time of reply - Cycles [usec.] : 
555370[180]	542984[176]	539994[175]	451536[147]	456090[148]
446096[145]	470856[153]	448753[146]	451605[147]	473512[154]
491579[160]	463611[151]	523503[170]	459356[149]	476031[155]
470672[153]	464738[151]	447155[145]	460655[150]	445211[145]

Очень неплохо! Время реакции приближается к последовательному серверу, но сохраняется параллельность, привносимая fork().

При написании этого текста я несколько схитрил и упростил его логику в сравнении с предложенной выше моделью. Здесь три обслуживающих процесса сделаны циклическими и не завершаются по окончанию обслуживания, а снова блокируются на accept(), но для наблюдения эффектов этого вполне достаточно (а последняя строка примера нужна вообще только для блокировки родительского процесса, и сохранения за процессами управляющего терминала — для возможности прекращения всей группы по ^C):

$ ps -A | grep ech 
10783 pts/15   00:00:00 ech11 
10784 pts/15   00:00:00 ech11 
10785 pts/15   00:00:00 ech11 
10786 pts/15   00:00:00 ech11 

В этом варианте добавлен вывод идентификатора (i) обрабатывающего процесса для идентификации обрабатывающего в каждом случае процесса. Дополнительно добавлена и задержка переактивизации процесса delay(), чтоб заставить обрабатывающие процессы чередоваться. Сервер в примере выше запускался в режиме повышенного уровня диагностики (-v) и можно было наблюдать последовательность активизации обрабатывающих процессов: 00210210210210210210.

В принципе, не так и сложно в такой схеме сделать и динамический пул процессов, как будет обсуждено ниже это для потоков – с той лишь некоторой сложностью, что здесь каждый процесс выполняется в своём закрытом адресном пространстве, и для их взаимной синхронизации придётся использовать что-то из механизмов IPC.


Назад Сервер TCP/IP ... много серверов хороших и разных Вперед