Idle thread это изначально определенный системой VDK поток ожидания, который автоматически создается в наборе идентификаторов потоков с идентификатором ThreadID, равным 0, и приоритетом, который ниже самого низкоприоритетного потока из всех потоков пользователя. Таким образом, когда в очереди готовности (ready queue) нет ни одного потока пользователя (все потоки находятся в состоянии сна на заданное время, либо заблокированы на семафорах), будет работать поток Idle. Единственное, что делает Idle thread, это уборка мусора, т. е. освобождение ресурсов потоков, которые были уничтожены. Другими словами, поток ожидания обрабатывает уничтожение потоков, которые были переданы в вызов DestroyThread() со значением FALSE в параметре inDestroyNow. После выполнения этой работы поток ожидания просто зацикливается, не выполняя никакую работу и не переводя процессор в состояние ожидания или режим пониженного энергопотребления, потому что тогда остановится таймер ядра и все сервисы VDK, работающие с реальным временем (см. ниже раздел "Таймер").
В зависимости от платформы имеется возможность настроить определенные параметры Idle thread, такие как размер стека и кучу, из которой удовлетворяются запросы к выделениям памяти (включая стек для Idle). Для получения дополнительных подробностей см. онлайн-Help. Здесь могут быть зависящие от процессора требования к свойствам потока Idle (см. приложение A "Processor-Specific Notes" [1], или перевод этой документации [4]).
Процессорное время, которое приложение тратит во всех потоках кроме Idle thread, показано графиком на закладке Target Load информационного окна State History среды разработки VisualDSP++ (открывается через меню View -> VDK Windows -> History). Подробнее про окно истории см. раздел "VDK State History Window" онлайн-Help.
При сборке проекта VDK у Вас есть опция добавить дополнительную отладочную информацию и исполняемый код путем выбора в свойствах проекта варианта Full Instrumentation (закладка Kernel, раздел дерева System -> Instrumentation Level). Инструментальная сборка отличается от не инструментальной тем, что в инструментальной сборке добавлен лишний код, собирающий статистику по выполняемым потокам (thread statistic logging). Эта дополнительная запись в лог немного отнимает ресурсы процессора на вызовы специального API, но помогает в отладке, чтобы можно было отследить активность компонентов системы.
[Окно VDK State History]
VDK записывает в лог определенные пользователем события и изменения состояния системы с использованием кольцевого буфера. Событие регистрируется в буфере истории (history buffer) с помощью вызова LogHistoryEvent(). Вызов LogHistoryEvent() записывает в лог 4 значения данных: ThreadID вызывавшего потока, тик в момент вызоваd, перечисление и значение, специфичное для этого перечисления. Перечисления меньше, чем 0, зарезервированы для использования внутри VDK. Для дополнительной информации по типу перечисления истории (history enumeration), см. определение HistoryEnum.
Предоставляется API для получения почти всех данных, которые показываются в окне VDK Status, так что эта информация становится доступной в приложении VDK. Подробнее это API описано в главе 5, "VDK API Reference", также см. [2, 3].
Используя лог истории, среда разработки VisualDSP++ графически отображает работающие потоки и изменения системного состояния в окне State History. Обратите внимание, что данные, отображаемые в этом окне, обновляются только в момент остановки программы отладчиком (halt). Подробнее окно State History, легенды Thread Status и Thread Event описаны в онлайн-Help.
Есть возможность либо заменить подпрограммы VDK history logging, либо добавить определяемую пользователем функцию в существующий механизм history logging (см. [2]).
[Окно Target Load]
Инструментальные сборки VDK позволяют разработчику анализировать загрузку процессора в течение прошедшего периода времени. Вычисленная нагрузка отображается графически в окне Target Load. Хотя это вычисление не точное, график помогает оценить уровень использования ресурсов процессора. Обратите внимание, что информация на графике обновляется в момент остановки приложения VDK в отладчике. Для более точного вычисления см. пример LoadMeasurement, который находится в каталоге установки VisualDSP++.
График Target Load показывает процент времени, которое целевой процессор (target) проводит в вычислениях на потоке ожидания (Idle thread). Нагрузка 0% означает, что приложение VDK все свое время проводит в потоке ожидания. Нагрузка 100% означает, что код потока ожидания практически не выполняется. Данные нагрузки обрабатываются перемещающимся окном усреднения. Нагрузка в процентах вычисляется для каждого тика, и все тики усредняются. Для вычисления процентной нагрузки (Load) на каждом тике используется следующая формула:
Load = 100% * (1 - (#idle / (#threads + #idle)))
#idle количество времени, проведенное в потоке ожидания на момент этого тика #threads количество времени, проведенное в потоках на момент этого тика
Дополнительную информацию про график Target Load см. в онлайн-Help.
Примечание: информация VDK в окнах State History и Target Load доступна только для сборки в конфигурации Debug, даже если для конфигурации Release на закладке Kernel выбрано Instrumentation Level -> Full Instrumentation.
[Окно VDK Status]
Совместно с информацией истории и загрузки процессора инструментальная сборка собирает статистику по компонентам VDK: когда поток был создан, его последний запуск, количество запусков потока, использование стека потоком и т. д. Эти данные отображаются в окне статуса (VDK Status), и информация в нем обновляется в момент остановки отладчика.
Для получения большинства информации из этого окна предоставляется API, благодаря чему можно получать эти данные статистики во время работы приложения, без остановки отладчика. Подробнее это API описано в главе 5 "VDK API Reference" (см. также [2, 3]).
Дополнительную информацию про график VDK Status см. в онлайн-Help.
Примечание: информация VDK Status доступна как для сборки Debug, так и для сборки Release, если включена инструментальная поддержка (в свойствах проекта на закладке Kernel выбрано Instrumentation Level -> Full Instrumentation).
[Таймер]
Тики (VDK Ticks) вычисляются по таймеру, реализованному во внутреннем ядре процессоров Blackfin. Этот таймер засинхронизирован с с основной частотой ядра CCLK. Однако работа этого таймера запрещена, когда процессор Blackfin входит в режим низкого энергопотребления (low power mode). Таким образом, все службы VDK, связанные отсчетом реального времени (такие как вход потока в сон на заданное время, работа периодических семафоров), не работают, когда ядро находится в состоянии IDLE или low power mode. По умолчанию VDK не использует и не модифицирует таймеры общего назначения.
На процессорах Blackfin можно зарегистрировать прерывания с помощью библиотечной функции register_handler() или через вызов API системных служб adi_int_CECHook(), тогда не надо декларировать прерывания на закладке проекта VDK Kernel. Прерывания, зарегистрированные вне конфигурации VDK, не должны использовать тот же уровень IVG, что и любое прерывание, определенное на закладке VDK Kernel.
[Использование прерываниями стека потока]
Из-за того, что код всех потоков работает в режиме супервизора, нет автоматического переключения между указателями пользовательского и системного стека. Таким образом, все ISR выполняются с использованием стека текущего работающего потока, во время активности которого произошло прерывание. Это означает, что размер стека каждого потока должен иметь достаточно места как для самого потока, так и для требований обработчиков прерываний (ISR). Это также относится и к размеру стека потока ожидания (Idle thread), что можно сконфигурировать средствами среды разработки VisualDSP++ (для дополнительной информации см. онлайн-Help). Когда разрешено вложение прерываний друг в друга, то наступает самый худший сценарий для выделения места в памяти под стек, потому что требования к стеку от отдельных ISR могут складываться. Когда вложение прерываний запрещено, то для удовлетворения требований к размеру стеку со стороны ISR нужно учитывать только требование от потока, который больше всего занимает памяти в стеке.
[Как определить загрузку процессора?]
Процессор, который выполняет VDK-приложение Blackfin, никогда не останавливается, и всегда выполняет какие-то вычисления, которые распределены между потоками системы. Не производительные вычисления, которые не связаны с полезной работой приложения, сосредоточены в потоке ожидания (Idle Thread). Таким образом, чтобы узнать, насколько загружен процессор полезной работой, нужно:
1. Узнать, сколько процессорного времени прошло. В тиках или в единицах счета таймера ядра, не важно. Предположим, что это время равно T. 2. Узнать, сколько времени процессор находится в потоке ожидания. Предположим, что это время равно Tidle. 3. Вычислить загрузку процессора полезной работой по формуле (в процентах):
LOAD = 100 * (1 - Tidle/T)
Все довольно просто, осталось только получить значения T и Tidle. Проще всего это сделать, если воспользоваться статистикой, которую накапливает так называемая инструментальная сборка проекта VDK. Сборка называется инструментальной по терминологии компании Analog Devices, которая разработала библиотеки VDK таким образом, что проект VDK можно собрать в 3 вариантах - Full Instrumentation (вариант полной инструментальной поддержки, который накапливает статистику по системе и потокам), Error Checking (этот вариант снабжен только проверкой ошибок) и None (программисту не предоставляется дополнительной отладочной информации по отлаживаемому проекту VDK).
Информацию Full Instrumentation можно, если остановить приложение в отладчике, и открыть окошки VDK State History, VDK Target Load, VDK Status (доступно через меню View -> VDK Windows). Но как получить эту информацию в приложении, runtime, не прибегая к помощи отладчика, не нарушая работы приложения?
К счастью, существует специальное API [2, 3], которое позволяет добраться до этой информации из кода приложения. Значение времени T в тиках можно получить вызовом функции GetUptime(), а время Tidle в тиках можно получить вызовом функции GetThreadTickData. Пример:
VDK::Ticks outUpTime; //Это общее время T
VDK::Ticks outIdleTime; //Это время Tidle
float Load =100* (1- (float)outIdleTime/outUpTime);
printf ("Загрузка системы %.1%%\n", Load);
Обратите внимание, что в функцию GetThreadTickData передан 0 в качестве идентификатора потока, потому что этому значению всегда равен идентификатор потока ожидания. Имейте в виду, что выведенная загрузка в процентах не учитывает процессорное время, которое ядро проводит в вычислениях драйверов устройств, если их код работает в контексте домена прерываний.
А как узнать, сколько процессорного времени отнимает каждый поток? Тоже довольно просто, если получить список работающих потоков и узнать их идентификаторы. Это тоже делается вызовами API. Пример:
VDK::Ticks outUpTime; //Общее время, сколько проработала система
VDK::Ticks outThreadTime; //Время работы потока
int totalthreads; //Сколько потоков имеется в системе
//Выделение памяти под идентификаторы потоков:
VDK::ThreadID *outThreadIDArray =
(VDK::ThreadID *)heap_malloc(1, VDK_kMaxNumThreads *sizeof(VDK::ThreadID));
//Получение информации о потоках:
totalthreads = VDK::GetAllThreads (outThreadIDArray, VDK_kMaxNumThreads);
//Сколько проработала система всего:
outUpTime = VDK::GetUptime();
//Цикл по потокам:
for (int idx=0; idx < totalthreads; idx++)
{
//Получение имени потока:char*outName; //Указатель на имя потока
VDK::GetThreadTemplateName(outThreadIDArray[idx],
&outName);
//Получение времени, сколько проработал поток:
VDK::GetThreadTickData (outThreadIDArray[idx],
(VDK::Ticks *)NULL,
(VDK::Ticks *)NULL,
(VDK::Ticks *)NULL,
&outThreadTime);
printf ("%i: %s, %.1f%%\n", outThreadIDArray[idx],
outName,
(float)100*outThreadTime/outUpTime);
}
heap_free(1, outThreadIDArray);
Будет выведен список наподобие следующего (ID потока, имя потока, % процессорного времени):
Имейте в виду, что эти примеры кода будут работать и для конфигурации Debug, и для конфигурации Release, но только при условии, если выбран вариант библиотек VDK с полной инструментальной поддержкой (настраивается в свойствах проекта на закладке Kernel, раздел System -> Instrumentation Level, здесь должен быть выбран вариант Full Instrumentation).