Библиотека сайта rus-linux.net
Цилюрик О.И. 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.
Предыдущий раздел: | Оглавление | Следующий раздел: |
Окружение процесса | Время клонирования |