Дуже корисними при побудові програм із послідовною динамічною структурою є бібліотечні функції Turbo С spawnxxx(). У цілому вони схожі на функції ехесххх(). Різниця між ними полягає в тому, що spawnxxx() дозволяють вибрати метод завантаження образу нащадка: при використанні spawnxxx() можна завантажувати нащадок як із перекриттям образу предка, так і зі збереженням його, що уможливлює повернення в програму-батько.
#include
<process.h>
int spawnl( int mode, char
*path, char *arg0, …, NULL
);
int spawnle( int mode, char
*path, char *arg0, …, NULL,
char **env );
int spawnlp( int mode, char
*path, char *arg0, …, NULL
);
int spawnlpe( int mode, char
*path, char *arg0, …, NULL,
char **env );
int spawnv( int mode, char
*path, char *argv[ ] );
inl spawnve( int mode, char
*path, char *argv[ ], char **env );
int spawnvp( int made, char
*path, char *argv[ ] );
int spawnvpe( int mode, char
*path, char *argv[], char **env );
За винятком цілочисельного параметру mode, зміст параметрів функцій ідентичний розглянутим
раніше при описі функції ехесххх(). Параметр mode має три визначених значення, описаних у файлі <process.h>:
P_WAIT - предок зупиняється і запускається нащадок, після завершення
якого керування повертається в предок (єдино можливий спосіб у MS-DOS);
P_NOWAIT - нащадок і предок виконуються паралельно як незалежні процеси
(цей засіб не реалізується в програмах під керуванням MS-DOS, і Turbo С його не підтримує);
P_OVERLAY - нащадок замішає предка, тобто предок припиняє своє
існування і тому неможливе повернення керування в предок, функції spawnxxx() із таким прапором цілком ідентичні функціям ехесххх(),
Якщо функції spawnxx() за якимись причинами
не може бути виконана, у точку виклику повертається -1 і в зовнішню змінну еrrno записується код помилки. Можливі значення кодів
помилок і причини, їх що викликають, приведені вище (Табл. 13). Додатково функції spawnxxx()
контролюють коректність значення параметру mode. Якщо
задається неприпустиме значення параметру, зовнішня змінна errno
одержує значення EINVAL.
У випадку успішного виконання нащадка зі значенням mode
рівним P_WAIT, функції spawnxxx() повертають код
завершення нащадка.
Розходження між окремими функціями spawnxxx()
такі ж, як і для функцій ехесхх(). Буква ‘р’ у назві функції говорить про те, що для пошуку файлу
нащадка будуть проглядатися не тільки маршрут, заданий параметром path, але і маршрути, зазначені в рядку середовище PATH,
якщо пошук по маршруті path завершуйся невдачею.
Буква ‘е’ свідчить про те, що при запуску
програми-нащадка буде перевизначатися середовище. Масив
покажчиків env містить покажчики на ASCIIZ-рядки
нового середовища. Якщо буква 'е' у назві функції відсутня,
нащадок успадковує середовище предка в незмінному виді. Буква ‘l' у назві функції свідчить про те, що в точку входу main() нащадка передається відоме заздалегідь число слів,
покажчики на які задаються при виклику функції. Якщо число параметрів у функції
заздалегідь невідомо, те єдиний вихід - використовувати масив покажчиків і
передавати адресу початку цього масиву. Всі функції spawnxxx(),
що мають у назві букву ‘v’, передають у якості
другого аргументу покажчик на масив покажчиків argv.
Наведемо приклад програми-предка, що дозволяє визначити виграш у швидкості за рахунок використання сопроцесору математики з плаваючою точкою. Предок двічі викликає нащадка: спочатку з рядком середовища “87=NO”, потім із рядком середовища “87=YES”. Обидва рази нащадок передає в предок час виконання тестового набору операцій. Предок порівнює отримані значення і виводить на екран коефіцієнт прискорення, забезпечуваного використанням сопроцесору математики з плаваючою точкою. Предок на самому початку аналізує наявність у системі сопроцесору і завершується, якщо він відсутній. Алгоритм визначення присутності сопроцесору заснований на аналізі бита 1 слова області даних BIOS за адресою 40:10h. Якщо битий 1 дорівнює одиниці, у комп'ютері встановлений сопроцесор.
Нащадок виконує тестовий набір із МАХ операцій додавання, МАХ операції множення, МАХ операцій ділення чисел із плаваючою точкою типу float і компілюється з включеною опцією емуляції сопроцесору. Це призводить до генерації самоналагоджувального на апаратуру програмного коду. Нехай у системі встановлений сопроцесор математики з плаваючою точкою. Арифметичні операції над дійсними числами виконуються по спеціальних підпрограмах (емуляція сопроцесору) у випадку, коли в середовищі програми є рядок "87=NO", або використовується сопроцесор, якщо є рядок середовища "87=YES". У випадку, коли в системі відсутній сопроцесор, незалежно від установки середовища виконується програмна емуляції арифметичних операцій із плаваючою точкою. Далі наводиться текст програми-предка:
/* L5_5.C */
#include <stdio.h>
#include <dos.h>
#include <stdlib.h>
#include <process.h>
#define MAX 0xFFFF
int main(void) {
struct
OP_TIME { /* структура для часу
виконання операцій */
float summing, /*
час МАХ додавань */
multiplaying, /* час МАХ множень */
dividing; * час МАХ ділень */
} with_87, without_87;
char *envp[] = { “87=YES”, NULL };
char text_seg[7], text_offs[7];
unsigned seg, offs;
/* Тест наявності сопроцесору
/
if( !(*(unsigned far *)МК_FP( 0x40, 0x10
) & 0x0002 ) ) {
puts( “\a В
комп'ютері відсутній сопроцесор” );
return(2);
}
seg = _DS;
offs =
FP_OFF( &with_87 );
ultoa( seg, text_seg, 16 );
ultoa( offs, text_offs, 16 );
/* Запуск нащадка для роботи з
використанням сопроцесору */
if( spownpe( P_WAIT, “test.exe”, “test.exe”, text_seg,
text_offs,
NULL, envp ) == -1 ) {
perror(
“\a Невдача запуску нащадка” );
return(1);
}
offs =
FP_OFF( &without_87 );
itoa( offs, text_offs, 16 );
envp[0] =
“87=NO”;
/* Запуск нащадка без використання сопроцесору (емуляція). */
if( spawnlpe( P_WAIT,”test.exe”, ”test.exe”, text_seg,
text_offs, NULL, envp ) == -1 ) {
perror( “\а
Невдача запуску нащадка” );
return(2);
}
/* Опрацювання статистики і виведення
результатів */
printf( “час
%u додавань: з сопроцесором -%5.2f\n”\
“ без нього (емуляція) -
%5.2f\n”\
“ коефіціент
прискорення - %5.2f\n",
MAX, with_87.summing,
without_87.summing,
without_87.summing/with_87.summing );
printf( “час
%u множень: з сопроцесором -%5.2f\n”\
“ без нього (емуляція) -
%5.2f\n”\
“ коефіціент
прискорення - %5.2f\n",
MAX, with_87.multiplaying,
without_87.multiplaying,
without_87.multiplaying/with_87.multiplaying );
printf( “час
%u ділень: з сопроцесором -%5.2f\n”\
“ без нього (емуляція) -
%5.2f\n”\
“ коефіціент
прискорення - %5.2f\n",
MAX, with_87.dividing, without_87.dividing,
without_87.dividing/with_87.dividing );
return(0);
}
Предок двічі викликає програму-нащадок, передаючи їй сегмент і зсув області пам'яті, у якому повинні записуватися результати виконання предка. Передані значення перетворюються в рядок символів. Нащадок повинний бути розташований у файлі TEST.EXE. Далі наводиться Сі-текст програми нащадка. Нащадок виконує по МАХ разів додавання, множення і ділення чисел із плаваючою точкою. Визначає у форматі float витрачений час і записує його в область пам'яті, адреса якої передана через командний рядок при виклику нащадка.
/*TEST.С */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <dos.h>
#include <bios.h>
#define MAX 0xFFFF
int main( int argc, char
**argv ) {
unsigned seg, offs;
struct
OP_TIME { /* структура для часу
виконання операцій */
float summing, /*
час МАХ додавань */
multiplaying, /* час МАХ множень */
dividing; * час МАХ ділень */
} far *parent_mem;
unsigned repeat;
float first_op = 123456.789, second_op
= 9876543.21, result;
long bios_time_before, bios_time_after;
if( argc < 3 ) { /* Чи всі аргументи задано? */
puts(
“\a”);
return( 1
);
}
sscanf( argv[1], “%x”, &seg);
sscanf( argv[2], “%x”, &offs);
parent_mem =
( struct OP_TIME far*
)MK_FP( seg, offs );
bios_time_before = biostime( 0, 0L); /*
час до початку додавань */
for( repeat = 0; repeat < MAX; repeat++)
result = first_op + second_op;
bios_time_after
= biostime( 0, 0L ); /*
час після додавань */
parent_mem
-> summing =
(float)(bios_time_after – bios_time_before)
/ (float)CLK_TCK;
bios_time_before = biostime( 0, 0L); /*
час до початку множень */
for( repeat = 0; repeat < MAX; repeat++)
result = first_op * second_op;
bios_time_after
= biostime( 0, 0L ); /*
час після множень */
parent_mem
-> multiplaying =
(float)(bios_time_after – bios_time_before)
/ (float)CLK_TCK;
bios_time_before = biostime( 0, 0L); /*
час до початку ділення */
for( repeat = 0; repeat < MAX; repeat++)
result = first_op / second_op;
bios_time_after
= biostime( 0, 0L ); /*
час після ділення */
parent_mem
-> dividing =
(float)(bios_time_after – bios_time_before)
/ (float)CLK_TCK;
return( 0 );
}
Наведемо результат виконання програми-предка для комп'ютера з тактовою частотою 25 МГц:
Час 65535 додавання: з сопроцесором - 0.49
без нього (емуляція) – 637
коефіцієнт прискорення - 12.89
Час 63535 множень: із сопроцесором - 0.49
без
нього (емуляція) - 7.42
коефіцієнт
прискоренн я- 15.00
Час 65535 ділення: із сопроцесором - 0.60
без
нього (емуляція) - 8.24
коефіцієнт
прискорення -13.64
Отримані результати носять достатньо
приблизний характер, по-перше, через низьку точність виміру часу, функція biostime() повертає число “тіків”
BIOS, a у секунді усього 18.2 “тіка”. По-друге,
вимірюється не чистий час виконання операцій із плаваючою точкою, а загальний
час виконання всієї програми, у якій ряд операцій пов‘язаний з організацією
циклу й інших дій.
![]() |
![]() |
![]() |