Уильям Стивенс - UNIX: взаимодействие процессов
Таблица А.7. Время увеличения счетчика в разделяемой памяти для Digital Unix 4.0B (в секундах)
Количество процессов Семафор Posix в памяти Именованный семафор Posix Семафор System V Семафор System V с UNDO Блокировка записей fcntl 1 12,8 12,5 30,1 49,0 98,1 2 664,8 659,2 58,6 95,7 477,1 3 1236,1 1269,8 96,4 146,2 1785,2 4 1772,9 1804,1 120,3 197,0 2582,8 5 2179,9 2196,8 147,7 250,9 3419,2А.З. Измерение полосы пропускания: программы
В этом разделе приведены тексты трех программ, измеряющих полосу пропускания каналов, очередей сообщений Posix и System V. Результаты работы этих программ приведены в табл. А.2 и А.З.
Измерение полосы пропускания канала
На рис. А.7 приведена схема описываемой программы.
Рис. А.7. Схема программы измерения полосы пропускания канала
В листинге А.1 приведен текст первой половины программы bw_pipe, измеряющей полосу пропускания канала.
Листинг А.1. Функция main, измеряющая полосу пропускания канала//bench/bw_pipe.c
1 #include "unpipc.h"
2 void reader(int, int, int);
3 void writer(int, int);
4 void *buf;
5 int totalnbytes, xfersize;
6 int
7 main(int argc, char **argv)
8 {
9 int i, nLoop, contpipe[2], datapipe[2];
10 pid_t childpid;
11 if (argc != 4)
12 err_quit("usage: bw_pipe <#loops> <#mbytes> <#bytes/write>");
13 nloop = atoi(argv[1]);
14 totalnbytes = atoi(argv[2]) * 1024 * 1024;
15 xfersize = atoi(argv[3]);
16 buf = Valloc(xfersize);
17 Touch(buf, xfersize);
18 Pipe(contpipe);
19 Pipe(datapipe);
20 if ((childpid = Fork()) == 0) {
21 writer(contpipe[0], datapipe[1]); /* child */
22 exit(0);
23 }
24 /* 4parent */
25 Start_time();
26 for (i = 0; i < nloop; i++)
27 reader(contpipe[1], datapipe[0], totalnbytes);
28 printf("bandwidth: %.3f MB/secn",
29 totalnbytes / Stop_time() * nloop);
30 kill(childpid, SIGTERM);
31 exit(0);
32 }
Аргументы командной строки11-15 Аргументы командной строки задают количество повторов (обычно 5), количество передаваемых мегабайтов (если указать 10, будет передано 10×1024×1024 байт) и количество байтов для каждой операции read и write (которое может принимать значения от 1024 до 65536 в наших измерениях).
Выделение буфера и помещение начального значения16-17 Вызов valloc аналогичен malloc, но выделяемая память начинается с границы страницы памяти. Функция touch (листинг А.3) помещает 1 байт данных в каждую страницу буфера, заставляя ядро считать в память все страницы данного буфера. Мы всегда выполняем это перед проведением измерений.
ПРИМЕЧАНИЕ
Функция valloc не входит в стандарт Posix.1 и названа устаревшей в Unix 98. Она требовалась в ранних версиях спецификаций Х/Open, но уже не является необходимой. Обертка Valloc вызывает функцию malloc, если valloc недоступна.
Создание двух каналов18-19 Создаются два канала: contpipe[0] и contpipe[1] используются для синхронизации процессов перед началом передачи, a datapipe[0] и datapipe[1] используются для передачи самих данных.
Вызов fork20-31 Создается дочерний процесс, вызывающий функцию writer, а родительский процесс в это время вызывает функцию reader. Функция reader вызывается nlоор раз. Функция start_time вызывается непосредственно перед началом цикла, a stop_time — сразу после его окончания. Эти функции даны в листинге А.З. Полоса пропускания представляет собой количество байтов, переданных за все проходы цикла, поделенное на время, затраченное на передачу (stop_time возвращает количество микросекунд, прошедшее с момент запуска start_time). Затем дочерний процесс завершается сигналом SIGTERM и программа завершает свою работу. Вторая половина программы приведена в листинге А.2. Она состоит из функций reader и writer.
Листинг А.2. Функции reader и writer//bench/bw_pipe.cvoid
33 void
34 writer(int contfd, int datafd)
35 {
36 int ntowrite;
37 for(;;) {
38 Read(contfd, &ntowrite, sizeof(ntowrite));
39 while (ntowrite > 0) {
40 Write(datafd, buf, xfersize);
41 ntowrite –= xfersize;
42 }
43 }
44 }
45 void
46 reader(int contfd, int datafd, int nbytes)
47 {
48 ssize_t n;
49 Write(contfd, &nbytes, sizeof(nbytes));
50 while ((nbytes > 0) &&
51 ((n = Read(datafd, buf, xfersize)) > 0)) {
52 nbytes –= n;
53 }
54 }
Функция writer33-44 Функция writer представляет собой бесконечный цикл, вызываемый дочерним процессом. Он ожидает сообщения родительского процесса о готовности к приему данных, считывая целое число из управляющего канала. Это целое число определяет количество байтов, которое будет записано в канал данных. При получении этого числа дочерний процесс записывает данные в канал, отправляя их родителю. За один вызов write записывается xfersize байтов.
Функция reader45-54 Эта функция вызывается родительским процессом в цикле. Каждый раз при вызове функции в управляющий канал записывается целое число, указывающее дочернему процессу на необходимость помещения соответствующего количества данных в канал данных. Затем функция вызывает read в цикле до тех пор, пока не будут приняты все данные.
Текст функций start_time, stop_time и touch приведен в листинге А.З.
Листинг А.З. Функции start_sime, stop_time и touch//lib/timing.с
1 #include "unpipc.h"
2 static struct timeval tv_start, tv_stop;
3 int
4 start_time(void)
5 {
6 return(gettimeofday(&tv_start, NULL));
7 }
8 double
9 stop_time(void)
10 {
11 double clockus;
12 if (gettimeofday(&tv_stop, NULL) == –1)
13 return(0.0);
14 tv_sub(&tv_stop, &tv_start);
15 clockus = tv_stop.tv_sec * 1000000.0 + tv_stop.tv_usec;
16 return(clockus);
17 }
18 int
19 touch(void *vptr, int nbytes)
20 {
21 char *cptr;
22 static int pagesize = 0;
23 if (pagesize == 0) {
24 errno = 0;
25 #ifdef _SC_PAGESIZE
26 if ((pagesize = sysconf(_SC_PAGESIZE)) == –1)
27 return(-1);
28 #else
29 pagesize = getpagesize(); /* BSD */
30 #endif
31 }
32 cptr = vptr;
33 while (nbytes > 0) {
34 *cptr = 1;
35 cptr += pagesize;
36 nbytes –= pagesize;
37 }
38 return(0);
39 }
Текст функции tv_sub приведен в листинге А.4. Она осуществляет вычитание двух структур timeval, сохраняя результат в первой структуре.
Листинг А.4. Функция tv_sub: вычитание двух структур timeval//lib/tv_sub.c
1 #include "unpipc.h"
2 void
3 tv_sub(struct timeval *out, struct timeval *in)
4 {
5 if ((out->tv_usec –= in->tv_usec) < 0) { /* out –= in */
6 --out->tv_sec;
7 out->tv_usec += 1000000;
8 }
9 out->tv_sec –= in->tv_sec;
10 }
На компьютере Sparc под управлением Solaris 2.6 при выполнении программы пять раз подряд получим следующий результат:
solaris % bw_pipe 5 10 65536
bandwidth: 13.722 MB/sec
solaris % bw_pipe 5 10 65536
bandwidth: 13.781 MB/sec
solaris % bw_pipe 5 10 65536
bandwidth: 13.685 MB/sec
solaris % bw_pipe 5 10 65536
bandwidth: 13.665 MB/sec
solaris % bw_pipe 5 10 65536
bandwidth: 13.584 MB/sec
Каждый раз мы задаем пять циклов, 10 Мбайт за цикл и 65536 байт за один вызов write или read. Среднее от этих пяти результатов даст величину 13,7 Мбайт в секунду, приведенную в табл. А.2.
Измерение полосы пропускания очереди сообщений Posix
В листинге А.5 приведена функция main программы, измеряющей полосу пропускания очереди сообщений Posix. Листинг А.6 содержит функции reader и writer. Эта программа устроена аналогично предыдущей, измерявшей полосу пропускания канала.