Рассмотрим в качестве примера программу, приведенную на Рисунке 8.12, измеряющую продолжительность выполнения функций f и g. Сначала процесс, используя системную функцию signal, делает указание при получении сигнала о прерывании вызывать функцию theend, затем он вычисляет диапазон адресов программы, в пределах которых будет производиться измерение продолжительности (начиная с адреса функции main и кончая адресом функции theend), и, наконец, запускает функцию profil, сообщая ядру о том, что он собирается начать измерение. В результате выполнения программы в течение 10 секунд на несильно загруженной машине AT&T 3B20 были получены данные, представленные на Рисунке 8.13. Адрес функции f превышает адрес начала профилирования на 204 байта; поскольку текст функции f имеет размер 12 байт, а размер целого числа в машине AT&T 3B20 равен 4 байтам, адреса функции f отображаются на элементы массива buf с номерами 51, 52 и 53. По такому же принципу адреса функции g отображаются на элементы buf c номерами 54, 55 и 56. Элементы buf с номерами 46, 48 и 49 предназначены для адресов, принадлежащих циклу функции main. В обычном случае диапазон адресов, в пределах которого выполняется измерение параметров, определяется в результате обращения к таблице идентификаторов для данной программы, где указываются адреса программных секций. Пользователи сторонятся функции profil из-за того, что она кажется им слишком сложной; вместо нее они используют при компиляции программ на языке Си параметр, сообщающий компилятору о необходимости сгенерировать код, следящий за ходом выполнения процессов.
#include ‹signal.h›
int buffer[4096];
main {
int offset, endof, scale, eff, gee, text;
extern theend, f, g;
signal(SIGINT, theend);
endof = (int) theend;
offset = (int) main; /* вычисляется количество слов в тексте программы */
text = (endof - offset + sizeof(int) - 1) / sizeof(int);
scale = Oxffff;
printf("смещение до начала %d до конца %d длина текста %d\n", offset, endof, text);
eff = (int) f;
gee = (int) g;
printf("f %d g %d fdiff %d gdiff %d\n", eff ,gee, eff - offset, gee - offset);
profil(buffer, sizeof(int) * text, offset, scale);
for (;;) {
f; g;
}
}
f {}
g {}
theend {
int i;
for (i = 0; i ‹ 4096; i++) if (buffer[i]) printf("buf[%d] = %d\n", i, buffer[i]);
exit;
}
Рисунок 8.12. Программа, использующая системную функцию profil
смещение до начала 212 до конца 440 длина текста 57
f 416 g 428 fdiff 204 gdiff 216
buf[46] = 50
buf[48] = 8585216
buf[49] = 151
buf[51] = 12189799
buf[53] = 65
buf[54] = 10682455
buf[56] = 67
Рисунок 8.13. Пример результатов выполнения программы, использующей системную функцию profil
8.3.4 Учет и статистика
В момент поступления прерывания по таймеру система может выполняться в режиме ядра или задачи, а также находиться в состоянии простоя (бездействия). Состояние простоя означает, что все процессы приостановлены в ожидании наступления события. Для каждого состояния процессора ядро имеет внутренние счетчики, устанавливаемые при каждом прерывании по таймеру. Позже пользовательские процессы могут проанализировать накопленную ядром статистическую информацию.
В пространстве каждого процесса имеются два поля для записи продолжительности времени, проведенного процессом в режиме ядра и задачи. В ходе обработки прерываний по таймеру ядро корректирует значение поля, соответствующего текущему режиму выполнения процесса. Процессы-родители собирают статистику о своих потомках при выполнении функции wait, беря за основу информацию, поступающую от завершающих свое выполнение потомков.