Главная страница Взаимодействие нетривиальных процессов Рис. 12.2. Копирование файла через разделяемую память Этот сценарий иллюстрирует рис. 12.2. Из этого рисунка видно, что копирование данных происходит всего лишь дважды: из входного файла в разделяемую память и из разделяемой памяти в выходной файл. Мы нарисовали два прямоугольника штриховыми линиями; они подчеркивают, что разделяемая память принадлежит как адресному пространству клиента, так и адресному пространству сервера. Концепции, связанные с использованием разделяемой памяти через интерфейсы Posix и System V, похожи. Первый интерфейс описан в главе 13, а второй - в главе 14. В этой главе мы возвращаемся к примеру с увеличением последовательного номера, который впервые появился в главе 9. Теперь мы будем хранить последовательный номер в сегменте разделяемой памяти, а не в файле. Сначала мы подчеркнем, что память разделяется между родительским и дочерним процессами при вызове fork. В программе из листинга 12.1 родительский и дочерний процессы по очереди увеличивают глобальный целочисленный счетчик count. Листинг 12.1. Увеличение глобального счетчика родительским и дочерним процессами shtti/incrl.c 1 finclude unpipc.h 2 fdefine SEMJAME ttiysetti count = 0; mainCint argc. char **argv) int sem t i. nloop: *mutex; if (argc 2) 10 err quit( usage: incrl <#loops> ): . 11 nloop = atoi(argv[l]): 12 /* создание, инициализация и удаление семафора */ 13 mutex = Sem open(PxJpc name(SEM NAME), 0 CREAT OJXCL, FILEJODE, 1); 14 Sem unli nk(Px i pc name(SEM NAME)); 15 setbuf(stdout. NULL): /* stdout не буферизуется */ 16 if (ForkO = 0) { /* дочерний процесс */ 17 for (i = 0: i < nloop; i++) { 18 Sem wait(mutex); 19 printf( child: %d\n . count++); 20 Sem post(mutex); 21 } 22 exit(O): 23 } 24 /* родительский процесс */ 25 for (i = 0: i < nloop: i++) { 26 Sem wait(mutex): 27 printf( parent: d\n , count++); 28 SemjD0St(mutex): 29 } 30 exit(O): 31 } Создание и инициализация семафора 12-14 Мы создаем и инициализируем семафор, защищающий переменную, которую мы считаем глобальной (count). Поскольку предположение о ее глобальности ложно, этот семафор на самом деле не нужен. Обратите внимание, что мы удаляем семафор из системы вызовом semjnl ink, но хотя файл с соответствующим полным именем при этом и удаляется, на открытый в данный момент семафор эта команда не действует. Этот вызов мы делаем для того, чтобы файл был удален даже при досрочном заверщении программы. Отключение буферизации стандартного потока вывода и вызов fork 15 Мы отключаем буферизацию стандартного потока вывода, поскольку запись в него будет производиться и родительским, и дочерним процессами. Это предотвращает смещивание вывода из двух процессов. 16-29 Родительский и дочерний процессы увеличивают глобальный счетчик в цикле заданное число раз, выполняя операции только при установленном семафоре. Если мы запустим эту программу на выполнение и посмотрим на результат, обращая внимание только на те строки, где система переключается между родительским и дочерним процессами, мы увидим вот что: child: О дочерний процесс запущен первым, count = О child: 1 child: 678 child: 679 дочерний процесс приостановлен. запускается родительский процесс и отсчет начинается с О родительский процесс приостанавливается. начинает выполняться дочерний процесс parent: О parent: 1 parent: 1220 parent; 1221 child: 680 child: 681 child; 2078 child: 2079 parent: 1222 дочерний процесс приостанавливается. начинает выполняться родительский процесс parent: 1223 и т. д. Как видно, каждый из процессов использует собственную копию глобального счетчика count. Каждый начинает со значения О и при прохождении цикла увеличивает значение своей копии счетчика. На рис. 12.3 изображен родительский процесс перед вызовом fork. родительский процесс выполняется эта команда int count; if(Forl<()==0) { Г child */ } Г parent */ Рис. 12.3. Родительский процесс перед вызовом fork При вызове fork дочерний процесс запускается с собственной копией данных родительского процесса. На рис. 12.4 изображены оба процесса после возвращения из fork. Мы видим, что родительский и дочерний процессы используют отдельные копии счетчика count. родительский процесс выполняется эта команда int count; if(Fork()==0) { Г child */ } /* parent */ выполняется эта команда - дочернего процесса дочерний процесс int count; if(Forl<()==0) { /* child */ } /* parent */ Рис. 12.4. Родительский и дочерний процессы после возвращения из fork
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |