Невозможно спровоцировать инверсию приоритета в C++

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

// let's declare a global mutex
pthread_mutex_t my_mutex;
  ...

int main(int argc, char **argv) {
  ...
  pthread_t normal_thread;
  pthread_t prio_thread;

  pthread_mutexattr_t attr;
  pthread_mutexattr_init (&attr);
  pthread_mutexattr_setprotocol (&attr, PTHREAD_PRIO_NONE);  // ! None !
  pthread_mutex_init(&my_mutex, &attr);

  // create first normal thread (L):
  pthread_create(&normal_thread, NULL, the_locking_start_routine, NULL);

  // just to help the normal thread enter in the critical section
  sleep(2);

  // now will launch:
  // * (M) several CPU intensive SCHED_FIFO threads with priority < 99
  // * (H) one SCHED_FIFO thread that will try to lock the mutex, with priority < 99

  // build Real Time attributes for the Real Time threads:
  pthread_attr_t my_rt_att;
  pthread_attr_init(&my_rt_att);

  // it was missing in the original post and it was also wrong:
  // even setting the SchedPolicy you have to set "InheritSched"
  pthread_attr_setinheritsched(&my_rt_att, PTHREAD_EXPLICIT_SCHED)

  pthread_attr_setschedpolicy(&my_rt_att, SCHED_FIFO);
  struct sched_param params;

  params.sched_priority = 1;
  pthread_attr_setschedparam(&my_rt_att, &params);

  pthread_create(&prio_thread, &my_rt_att, the_CPU_intensive_start_routine, NULL) 

  params.sched_priority = 99;
  pthread_attr_setschedparam(&my_rt_att, &params);

  // create one RealTime thread like this:
  pthread_create(&prio_thread, &my_rt_att, the_locking_start_routine, NULL)  //coma was missing

  ...
}

void *the_locking_start_routine(void *arg) {
  ...
  pthread_mutex_lock(&my_mutex);
  // This thread is on the critical section
  // ... (skipped)
  pthread_mutex_unlock(&my_mutex);
  ...
}

... Но это не работает, я не могу получить желаемую инверсию приоритета.

Вот что происходит:

Насколько я понимаю, с планировщиком, таким как Linux CFS, поток не в реальном времени (SCHED_OTHER) не будет работать до тех пор, пока не будет ни одного потока реального времени (SCHED_FIFO или SCHED_RR) в рабочем состоянии. Но я добился одновременной работы этих потоков:

  • (L) Один поток не в реальном времени (SCHED_OTHER), блокирующий мьютекс и потребляющий ресурсы ЦП.
  • (M) несколько потоков реального времени (SCHED_FIFO и приоритет > 0) с интенсивным использованием ЦП и без ожидания блокировки мьютекса
  • (H) Один поток реального времени (SCHED_FIFO и наивысший приоритет), ожидающий блокировки

Выполняется больше потоков с интенсивным использованием ЦП в реальном времени (M), чем количество ЦП в моей системе ... но поток не в реальном времени, удерживающий (L) блокировку, все еще потребляет ЦП и заканчивает свою работу и освобождает мьютекс до того, как Потоки «M» заканчивают потреблять ресурсы ЦП.

Почему поток с низким приоритетом не вытесняется, приложение заблокировано, и я не могу получить инверсию приоритета?

Я использую g++ 4.5.2 на Ubuntu Desktop 11.04 с ядром 2.6.38-13.


person Angel    schedule 19.03.2012    source источник
comment
Пожалуйста, покажите весь код для the_start_routine. Я не вижу, что он делает между блокировкой и разблокировкой. Также за пределами Критической секции - предположительно где-то есть петля, которую вы превратили в точки? Если он находится внутри CS, зачем ему там вытесняться - это единственный поток, готовый/работающий, поскольку все остальные застряли на мьютексе.   -  person Martin James    schedule 20.03.2012
comment
ПЕРЕМЕЩЕНО НИЖЕ, ЧТОБЫ ОТВЕТИТЬ (независимо от того, есть это или нет) Большинство современных планировщиков имеют средства защиты от взаимоблокировок, которые изменяют приоритет на один или два интервала времени, чтобы предотвратить инверсию приоритета, ведущую к взаимоблокировкам, при обнаружении или при необходимости. Справляется ли Linux с любым планировщиком, который вы используете, я точно не знаю. Однако помните об этом, если вы еще этого не сделали.   -  person dyasta    schedule 20.03.2012
comment
Привет, @Angel, почему ты установил PTHREAD_PRIO_NONE вместо PTHREAD_PRIO_INHERIT?   -  person ransh    schedule 03.10.2015
comment
Привет, @ransh! Сначала я хотел воспроизвести сценарий с инверсией приоритета, а для этого поток L должен быть потоком не реального времени, поэтому мне нужно было PTHREAD_PRIO_NONE. Вы, конечно, знаете, что PTHREAD_PRIO_INHERIT помогает вам избежать инверсии приоритетов, добиваясь того, чтобы L наследовал приоритет H, но это то, что вам нужно, если вы хотите решить проблему, и я сначала хотел ее воспроизвести. Моя проблема была не в программе, а в глобальном ограничении того, сколько времени может использовать планирование в реальном времени, как указал Каз. Ваше здоровье! /Ангел   -  person Angel    schedule 05.10.2015


Ответы (3)


  1. Вы запускаете программу как root?

  2. Каковы ваши значения этих параметров sysctl? Вот мои из коробки Ubuntu. По умолчанию реальному времени отводится только 0,95 секунды из 1-секундного среза:

    kernel.sched_rt_period_us = 1000000
    kernel.sched_rt_runtime_us = 950000
    

    Это предотвращает использование всего ЦП доменом реального времени. Если вы хотите работать в режиме реального времени, вы должны отключить эти параметры.

См.: http://www.kernel.org/doc/Documentation/scheduler/sched-rt-group.txt

Если вы установите sched_rt_runtime_us в -1, вы отключите этот механизм безопасности.

person Kaz    schedule 20.03.2012
comment
Большое спасибо, Джереми Коллейк и Каз. Мне было интересно, могло ли произойти что-то подобное. У меня был поток без RT, потребляющий несколько циклов ЦП, в то время как было запущено больше потоков RealTime, чем количество ядер, которые у меня есть. Просто могли быть такие механизмы, но я даже не знал, с чего начать их искать. Вы рулите!!! Большое спасибо!!! ;) - person Angel; 21.03.2012
comment
Привет еще раз! В исходном посте была и другая ошибка: я не вызвал pthread_attr_setinheritsched, чтобы установить PTHREAD_EXPLICIT_SCHED, и это необходимо для того, чтобы это имело эффект. Я протестировал его с помощью kernel.sched_rt_runtime_us = -1 в /etc/sysctl.conf, и у меня была желаемая инверсия приоритета, и сегодня я буду спать счастливее ;) Еще раз всем спасибо. /Ангел - person Angel; 23.03.2012

Re: Я пытаюсь спровоцировать инверсию приоритета в небольшой программе на C++ в демонстрационных целях, но не могу: поток с низким приоритетом, содержащий мьютекс, не прерывается и продолжает работать...

Это является началом сценария инверсии приоритетов. Поток с низким приоритетом захватывает эксклюзивный ресурс (например, мьютекс), на котором затем блокируются потоки с высоким приоритетом.

Чтобы правильно показать последствия инверсии приоритета, вам потребуется, например, три потока: поток с низким (L), средним (M) и высоким (H) приоритетом.

L блокирует мьютекс, за который борется H. Итак, L работает, H — нет. Это уже плохо: важный поток H ждет, пока менее важный поток L что-то сделает.

Теперь M становится работоспособным и требует больших вычислительных ресурсов. M не заботится о мьютексе; это не связано с H или L. Но M имеет более высокий приоритет, чем L, и отключает L от ЦП.

Итак, теперь M продолжает выполняться, предотвращая запуск L. Это не позволяет L достичь строки кода, где он освобождает мьютекс, и не позволяет H получить мьютекс.

Таким образом, вместо потока H с наивысшим приоритетом выполняется поток M со средним приоритетом.

Блокируя L, M может также заблокировать H: инверсия.

Посмотрите, сможете ли вы закодировать это именно так.

person Kaz    schedule 20.03.2012
comment
И вы можете увидеть, как с этим справляются различные алгоритмы. Например, наследование приоритета: когда H хочет мьютекс, текущему владельцу мьютекса дается временное повышение приоритета, так что он по крайней мере равен H. Это приводит к тому, что M не может вытеснить L. - person Kaz; 20.03.2012
comment
Я обновил вопрос, исходный код приблизительный. Резюме: я не могу заставить потоки реального времени (M) вытеснять поток (L) не в реальном времени, они просто добавляют нагрузку ... и, наконец, мьютекс (L) обычно освобождается и (H), наконец, может блокироваться мьютекс. Любая помощь будет очень признательна. С наилучшими пожеланиями! - person Angel; 20.03.2012

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

person dyasta    schedule 20.03.2012