Принудительное планирование потоков Win32 в определенной последовательности на основе приоритета - PullRequest
6 голосов
/ 18 ноября 2010

Я - встроенный программист, пытающийся моделировать вытесняющий планировщик в реальном времени в среде Win32, используя Visual Studio 2010 и MingW (как две отдельные среды сборки).Я очень зеленый в среде планирования Win32 и столкнулся с кирпичной стеной с тем, что я пытаюсь сделать.Я не пытаюсь добиться поведения в реальном времени - просто заставить симулированные задачи работать в том же порядке и последовательности, что и на реальном целевом оборудовании.

Имитируемый планировщик реального времени имеет простую цель -всегда выполняйте задачу с наивысшим приоритетом (поток), которая может выполняться.Как только задача становится способной выполняться - она ​​должна выгрузить текущую задачу, если у нее приоритет выше текущей задачи.Задача может стать способной выполняться из-за внешнего события, которого она ждала, или истечения времени ожидания / времени блокировки / истечения времени ожидания - с прерыванием по тику, генерирующим временную базу.

В дополнение к этому упреждающему поведению, задача может дать или добровольно отказаться от своего временного интервала, потому что она выполняет функцию типа сна или ожидания.

Я имитирую это, создавая поток Win32 с низким приоритетом для каждой задачи, которая создается реальноймоделируемый планировщик времени (поток фактически выполняет переключение контекста, которое планировщик выполняет для реальной встроенной цели), поток Win32 со средним приоритетом в качестве обработчика псевдо-прерываний (обрабатывает смоделированные тиковые прерывания и выдает запросы, которые ему сообщаются с использованием события Win32объект) и поток Win32 с более высоким приоритетом для имитации периферийного устройства, генерирующего тиковые прерывания.

Когда обработчик псевдо-прерываний устанавливает, что должно произойти переключение задач, он приостанавливает текущий выполняющийся поток с использованием SuspendThread () и возобновляет поток, который выполняет вновь выбранную задачу, используя ResumeThread ().Из множества задач и связанных с ними потоков Win32, которые могут быть созданы, только один поток, который управляет задачей, когда-либо будет находиться в состоянии ожидания в любой момент времени.

Важно, чтобы приостановленный поток немедленно приостанавливал вызов SuspendThread () и чтобы поток обработки псевдо-прерываний выполнялся, как только событие, сообщающее ему, что прерывание находится в состоянии ожидания, сигнализируется - но это неПоведение, которое я наблюдаю.

В качестве примера проблемы, для которой у меня уже есть обходной путь: Когда задача / поток дает результат, событие yield фиксируется в переменной, а поток обработки прерываний сигнализируется, поскольку существует псевдопрерывание (выход), который нуждается в обработке.Теперь в системе реального времени, поскольку я привык к программированию, я ожидал бы, что поток обработки прерываний немедленно выполнит то, о чем он сигнализирует, потому что он имеет более высокий приоритет, чем поток, который его сигнализирует.В среде Win32 я вижу, что поток, сигнализирующий о потоке с более высоким приоритетом, продолжает работать некоторое время, прежде чем будет приостановлен - либо потому, что требуется некоторое время, прежде чем сигнальный поток с более высоким приоритетом начинает выполняться, либо потому, что требуется некоторое время для приостановкизадача на самом деле перестать работать - я не уверен, какой.В любом случае это может быть легко исправлено путем создания сигнального блока потока Win32 на семафоре после сигнализации потока обработки прерываний Win32, и заставить поток обработки прерываний Win32 разблокировать поток, когда он завершит свою функцию (рукопожатие).Эффективно используя синхронизацию потоков, чтобы заставить шаблон планирования к тому, что мне нужно.Для этой цели я использую SignalObjectAndWait ().

При использовании этого метода моделирование работает идеально, когда моделируемый планировщик реального времени работает в кооперативном режиме, но не (как требуется) в вытесняющем режиме.

Проблема с преимущественным переключением задач состоит в том, что, я думаю, то же самое, задача продолжает выполняться в течение некоторого времени после того, как ей было приказано приостановить работу, прежде чем она фактически перестает работать, поэтому нельзя гарантировать, что система будет оставаться в согласованном состоянии, когда поток, который запускает задачу, приостанавливается. Однако в упреждающем случае, поскольку задача не знает, когда это произойдет, нельзя использовать тот же метод использования семафора, чтобы предотвратить продолжение игры Win32 до ее следующего возобновления.

Кто-нибудь сделал это так далеко внизу этого поста - извините за его длину!

Мои вопросы:

  • Как я могу заставить планирование Win32 (XP) запускать и останавливать задачи немедленно при вызове функций потоков приостановки и возобновления - или - как я могу заставить поток Win32 с более высоким приоритетом немедленно начать выполнение, чтобы он мог сделать это (объект, на котором он заблокирован, сигнализируется). Эффективно заставляет Win32 перепланировать запущенные процессы.

  • Существует ли способ асинхронной остановки задачи для ожидания события, когда его нет в пути последовательного выполнения задачи / потоков.

  • Симулятор хорошо работает в среде Linux, где сигналы POSIX используются для эффективного прерывания потоков - есть ли эквивалент в Win32?

Спасибо всем, кто нашел время для прочтения этого длинного поста, и особенно спасибо всем, кто сможет протянуть руку «инженеров реального времени» через этот лабиринт Win32.

Ответы [ 4 ]

5 голосов
/ 18 ноября 2010

Если вам нужно сделать свое собственное планирование, то вы можете рассмотреть возможность использования волокон вместо потоков. Волокна похожи на потоки в том смысле, что они представляют собой отдельные блоки исполняемого кода, однако волокна могут планироваться в пользовательском коде, тогда как потоки планируются только ОС. Один поток может размещать и управлять расписанием нескольких волокон, а волокна могут даже планировать друг друга.

1 голос
/ 21 ноября 2010

[РЕДАКТИРОВАТЬ] - проще, чем взломать ниже - кажется, просто обеспечение того, что все потоки работают на одном и том же ядре ЦП, также решает проблему: o) После всего этого. Единственная проблема в том, что процессор работает почти на 100%, и я не уверен, что ему вредно тепло. [/ EDIT]

ахи! Я думаю, у меня есть обходной путь для этого - но это уродливо. Хотя уродство сохраняется в слое порта.

Теперь я сохраняю идентификатор потока каждый раз, когда создается поток для выполнения задачи (поток Win32 создается для каждой создаваемой задачи реального времени). Затем я добавил функцию, которая вызывается с помощью макросов трассировки. Макросы трассировки могут быть определены, чтобы делать все, что вы хотите, и оказались очень полезными в этом случае. Комментарии в коде ниже объясняют. Симуляция не идеальна, и все, что она делает, это корректирует планирование потоков, когда оно уже отклонилось от планирования в реальном времени, в то время как я предпочел бы, чтобы он не ошибался с самого начала, но расположение макросов трассировки делает код Это решение прошло все испытания:

void vPortCheckCorrectThreadIsRunning( void )
{
xThreadState *pxThreadState;

    /* When switching threads, Windows does not always seem to run the selected
    thread immediately.  This function can be called to check if the thread
    that is currently running is the thread that is responsible for executing
    the task selected by the real time scheduler.  The demo project for the Win32
    port calls this function from the trace macros which are seeded throughout 
    the real time kernel code at points where something significant occurs.
    Adding this functionality allows all the standard tests to pass, but users
    should still be aware that extra calls to this function could be required
    if their application requires absolute fixes and predictable sequencing (as
    the port tests do).  This is still a simulation - not the real thing! */
    if( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED )
    {
        /* Obtain the real time task to Win32 mapping state information. */
        pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );

        if( GetCurrentThreadId() != pxThreadState->ulThreadId )
        {
            SwitchToThread();
        }
    }
}
1 голос
/ 20 ноября 2010

@ Энтони У - спасибо за совет.Я запускал потоки Win32, которые имитировали задачи реального времени в THREAD_PRIORITY_ABOVE_NORMAL, и потоки, которые запускали обработчик псевдо-прерываний и генератор прерываний тиков в THREAD_PRIORITY_HIGHEST.Потоки, которые были приостановлены, менялись на THREAD_PRIORITY_IDLE на случай, если что-то изменится.Я только что попробовал ваше предложение использовать THREAD_PRIORITY_TIME_CRITICAL, но, к сожалению, это не имело никакого значения.

Что касается вашего вопроса, я уверен, что проблема приостановки и возобновления не происходит немедленно - ну, нет, я не,Это мое лучшее предположение в среде, с которой я незнаком.Мое мышление о невозможности приостановить работу и возобновить работу немедленно вытекает из моих наблюдений, когда задание выполнено.Если я сделаю вызов yield (сигнализирую [используя событие Win32] поток Win32 с более высоким приоритетом, чтобы переключиться на следующую задачу в реальном времени), я могу поставить точку останова после выхода, и она получит удар перед точкой останова с более высоким приоритетом.нить.Неясно, была ли причина этого в задержке сигнализации о событии и выполнении задачи с более высоким приоритетом, или в задержке приостановки потока и фактического прекращения работы потока - но поведение определенно наблюдалось.Это было исправлено с помощью рукопожатия семафора, но этого нельзя сделать для прерываний, вызванных тиковыми прерываниями.

Я знаю, что моделирование не выполняется так, как я ожидаю, потому что набор тестов, проверяющих последовательность планирования в реальном временизадачи не удается.Всегда возможно, что у планировщика есть проблема, или у теста есть проблема, но тест будет работать в течение нескольких недель без сбоев на реальном реальном времени, поэтому я склонен думать, что тест и планировщик в порядке.Большая разница в цели реального времени, частота тиков составляет 1 мс, в то время как в моделируемой цели Win32 она составляет 15 мс с довольно большой вариацией даже тогда.

@ Реми - Сегодня я немного прочитал о волокнах, и я пришел к выводу, что для моделирования планировщика в кооперативном режиме они были бы идеальными.Однако, насколько я вижу, они могут быть запланированы только самими волокнами, вызывающими функцию SwitchToFiber ().Можно ли сделать так, чтобы поток блокировался на таймере или в спящем режиме, чтобы он периодически запускался, эффективно вытесняя волокно, которое работало в то время?Из того, что я прочитал, ответ «нет», потому что блокировка одного волокна заблокирует все волокна, работающие в потоке.Если это можно заставить работать, может ли периодически выполняющееся волокно вызывать функцию SwitchToFiber (), чтобы выбрать следующее волокно, которое будет запущено, прежде чем снова спать в течение фиксированного периода?Опять же, я думаю, что ответ отрицательный, потому что, как только он переключается на другое волокно, он больше не будет выполняться и поэтому не будет вызывать функцию Sleep (), пока в следующий раз исполняющее волокно не переключится обратно на него.Пожалуйста, исправьте мою логику, если у меня неверное представление о том, как работают волокна.

Я думаю, что это могло бы работать, если бы периодическая функциональность могла остаться в своем собственном потоке, отдельно от потока, который выполнял волокна - но (опять же из того, что я прочитал) Я не думаю, что один поток может повлиять на выполнение волокон, работающих в другом потоке.Опять же, я был бы признателен, если бы вы могли исправить мои выводы здесь, если они ошибочны.

1 голос
/ 19 ноября 2010

Во-первых, какие значения приоритетов вы используете для своих тем?

Если вы установите поток с высоким приоритетом на THREAD_PRIORITY_TIME_CRITICAL, он должен запускаться практически сразу - только те потоки, которые связаны с процессом в реальном времени, будут иметь более высокий приоритет.

Во-вторых, как вы узнаете, что приостановка и возобновление происходят не сразу? Вы уверены, что это проблема?

Вы не можете заставить поток ждать чего-то извне, не приостанавливая поток для ввода кода ожидания; если SuspendThread не работает для вас, то это не поможет.

Наиболее близким к сигналу, вероятно, является QueueUserAPC, который запланирует обратный вызов для запуска в следующий раз, когда поток войдет в "состояние ожидания с оповещением", например, позвонив по номеру SleepEx или WaitForSingleObjectEx или подобному.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...