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








Книги по Linux (с отзывами читателей)

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

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Linux-инструменты для Windows-программистов
Назад Библиотеки API POSIX Вперед

Параллельные процессы

Для всех UNIX/POSIX операционных систем классическим способом создания параллельного процесса в системе является вызов fork() (относящиеся к делу определения — в <unistd.h>). Простейший пример способа создания в UNIX параллельных процессов может выглядеть так (все примеры этого раздела в архиве fork.tgz, в этом разделе все иллюстрируемые вызовы API будут отмечены в коде жирным шрифтом) :

p2.c :

#include <stdio.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include "libdiag.h" 
int main( int argc, char *argv[] ) { 
   long cali = calibr( 1000 ); 
   uint64_t t = rdtsc(); 
   pid_t pid = fork();       // процесс разветвился 
   t = rdtsc() - t; 
   t -= cali; 
   if( pid == -1 ) perror( "fork" ), exit( EXIT_FAILURE ); 
   if( pid == 0 ) { 
      printf( "child with PID=%d finished, start delayed %lu cycles\n", getpid(), t ); 
      exit( EXIT_SUCCESS ); 
   } 
   if( pid > 0 ) { 
      int status; 
      wait( &status ); 
      printf( "parent with PID=%d finished, start delayed %lu cycles\n", getpid(), t ); 
      exit( EXIT_SUCCESS ); 
   }; 
}; 

Показательно выполнение такого простейшего примера:

$ ./p2 

child with PID=19044 finished, start delayed 855235 cycles 
parent with PID=19041 finished, start delayed 109755 cycles 

$ ./p2 

child with PID=30908 finished, start delayed 166435 cycles 
parent with PID=30904 finished, start delayed 106025 cycles 

Здесь фиксируются и выводятся временные засечки старта каждой из ветвей разветвлённого процесса (относительно момента, предшествующего вызову fork()), эти времена могут быть совершенно разными, и, самое главное, здесь нельзя утверждать кто раньше (родительский или дочерний процесс) начнёт выполняться, или даже оба они продолжаться на разных процессорах SMP. В подтверждение сказанного, рассмотрим результаты на однопроцессорном компьютере (предыдущий показанный результат получен на 2-х ядерном SMP), результаты здесь той же программы совершенно противоположны (дочерний процесс активируется значительно быстрее родительского, порядок временных величин в единицах процессорных циклов примерно сохраняется):

$ ./p2 

child with PID=6172 finished, start delayed 253986 cycles 
parent with PID=6171 finished, start delayed 964611 cycles 

$ ./p2 

child with PID=6174 finished, start delayed 259164 cycles 
parent with PID=6173 finished, start delayed 940884 cycles 

А вот для сравнения тот же тест :

$ ./p2 

child with PID=26466 finished, start delayed 232627 cycles 
parent with PID=26465 finished, start delayed 183480 cycles 

$ ./p2 

child with PID=26468 finished, start delayed 234885 cycles 
parent with PID=26467 finished, start delayed 184555 cycles 

- выполнение на 4-х ядерном процессоре, частотой в разы превосходящей выше показанный случай:

$ cat /proc/cpuinfo
...
processor	: 3
vendor_id	: GenuineIntel 
cpu family	: 6 
model		: 23 
model name	: Intel(R) Core(TM)2 Quad  CPU   Q8200  @ 2.33GHz 
stepping	: 7 
cpu MHz		: 1998.000 
...

Общая картина сохраняется: порядок временных задержек сохраняется, но порядок активации параллельных процессов может быть произвольным!

Ещё один образец использования ветвления процессов это следующий пример, тестирующий запуск в системе как можно большего числа идентичных процессов:

p4.c :
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/wait.h> 
int main( int argc, char *argv[] ) { 
   unsigned long n = 1; 
   pid_t pid;
   while( ( pid = fork() ) >= 0 ) { 
      if( pid < 0 ) break; 
      n++; 
      if( pid > 0 ) { 
         waitpid( pid, NULL, 0 ); 
         exit( EXIT_SUCCESS ); 
      }; 
   }; 
   printf( "exit with processes number: %lu\n", n ); 
   if( pid < 0 ) perror( NULL ); 
   return 0; 
}; 

Вот как происходит запуск такого теста на 2-х процессорном компьютере:

$ time ./p4 

exit with processes number: 913 
Resource temporarily unavailable 
real	0m0.199s
user	0m0.013s
sys	0m0.161s 

$ uname -r

2.6.32.9-70.fc12.i686.PAE

Система была в состоянии запустить одновременно 913 процессов в дополнение к существующим в системе:

$ ps -A | wc -l 

208 

Но вот выполнение того же теста на 1-но процессорном компьютере (квази-параллельность!), частотой процессора всего в 3 раза ниже, и объёмом RAM меньше в 4 раза:

$ time ./p4 

exit with processes number: 4028 
Resource temporarily unavailable 
real	2m59.903s
user	0m0.325s
sys	0m35.891s

$ uname -r

2.6.18-92.el5

Эта система оказалась в состоянии запустить одновременно 4084 дополнительных процесса, но это потребовало от неё затрат времени в сотни раз больше чем в предыдущем случае, при этом всё это время система была загружена близко к 100% и с большим трудом откликалась на команды с терминала, и это при том, что в ней стационарно сконфигурировано намного меньше выполняющихся процессов:

$ ps -A | wc -l 

109 

Во время этого длительного выполнения можно «подсмотреть» состояние таблицы процессов в системе:

$ ps -A 
...
 7012 pts/1   00:00:00 p4 
 7013 pts/1   00:00:00 p4 
 7014 pts/1   00:00:00 p4 
 7015 pts/1   00:00:00 p4 
 7016 pts/1   00:00:00 p4 
 7017 pts/1   00:00:00 p4 
...

На этих механизмах, совместно с отображением созданных адресных пространств на исполнимые файлы, базируются все базовые механизмы выполнения заданий UNIX/POSIX/Linux.


Предыдущий раздел: Оглавление Следующий раздел:
Окружение процесса   Время клонирования