不能在 C++ 中引发优先级反转
Posted
技术标签:
【中文标题】不能在 C++ 中引发优先级反转【英文标题】:Can't provoke Priority Inversion in C++ 【发布时间】:2012-03-19 22:46:45 【问题描述】:出于演示目的,我试图在一个小型 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, ¶ms);
pthread_create(&prio_thread, &my_rt_att, the_CPU_intensive_start_routine, NULL)
params.sched_priority = 99;
pthread_attr_setschedparam(&my_rt_att, ¶ms);
// 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) 线程锁定互斥锁和 消耗 CPU (M) 几个实时线程 (SCHED_FIFO , & priority > 0) CPU 密集且不等待锁定互斥体 (H) 一个实时线程(SCHED_FIFO 和最高优先级)等待 锁运行的实时 CPU 密集型线程 (M) 比我系统的 CPU 数量多……但是持有锁的非实时线程 (L) 仍在消耗 CPU 并完成它的工作并释放在“M”个线程完成消耗 CPU 之前互斥锁。
为什么低优先级线程没有被抢占,应用程序死锁,无法获得优先级反转?
我在内核 2.6.38-13 的 Ubuntu 桌面 11.04 上使用 g++ 4.5.2。
【问题讨论】:
请显示 the_start_routine 的所有代码。我看不到它在锁定和解锁之间做了什么。同样在关键部分之外 - 大概在某个地方有一个循环,你已经变成了点?如果它在 CS 内部,为什么它会在那里被抢占 - 它是唯一准备好/正在运行的线程,因为所有其他线程都卡在互斥体上。 移到下面的答案(无论是否) 大多数现代调度程序都有防死锁保护措施,当检测到或认为时,它们会在一两个时间片内更改优先级,以防止导致死锁的优先级倒置合适的。 linux 是否与您使用的任何调度程序一起使用,我不确定。但是,如果您还没有意识到这一点,请务必注意。 嗨@Angel,你为什么设置 PTHREAD_PRIO_NONE 而不是 PTHREAD_PRIO_INHERIT ? 嗨,@ransh!我想首先重现优先级反转场景,并且要做到这一点,“L”线程必须是非实时线程,所以我需要“PTHREAD_PRIO_NONE”。您当然知道 PTHREAD_PRIO_INHERIT 可以帮助您避免优先级反转,实现“L”继承“H”的优先级,但是如果您想解决问题并且我首先想重现它,这就是您所需要的。我的问题不在于程序,而在于实时调度可以使用多少时间的全局限制,就像 Kaz 指出的那样。干杯! /天使 【参考方案1】:回复:出于演示目的,我试图在一个小型 C++ 程序上引发优先级反转,但我不能:持有互斥锁的低优先级线程没有被抢占并继续运行......
那是优先级倒置方案的开始。低优先级线程获取独占资源(例如互斥锁),然后高优先级线程阻塞该资源。
要正确显示优先级反转的后果,您需要例如三个线程:低 (L)、中 (M) 和高 (H) 优先级线程。
L 锁定了一个互斥锁,H 争用该互斥锁。所以 L 正在运行,H 没有。这已经很糟糕了:重要的线程 H 正在等待不太重要的线程 L 做某事。
现在 M 变得可运行并且是计算密集型的。 M 不关心互斥锁;它与 H 或 L 无关。但 M 的优先级高于 L,并将 L 踢出 CPU。
所以现在 M 继续执行,阻止 L 运行。这会阻止 L 到达释放互斥锁的代码行,并阻止 H 获取互斥锁。
所以一个中等优先级的线程 M 正在运行,而不是最高优先级的线程 H。
通过阻止 L,M 也能够阻止 H:反转。
看看你能不能完全像这样编码。
【讨论】:
你可以看到各种算法是如何处理这个问题的。例如优先级继承:当 H 想要互斥体时,当前互斥体所有者被赋予临时优先级提升,使其至少等于 H。这导致 M 无法抢占 L。 我已经更新了问题,源代码是“近似的”。总结是:我不能让实时(M)线程抢占(L)非实时线程,他们只是增加负载......最后互斥锁(L)被正常释放,(H)终于可以锁定互斥体。任何帮助将不胜感激。最好的问候!【参考方案2】:您是否以 root 身份运行程序?
这些 sysctl 参数的值是多少?这是我的一个 Ubuntu 盒子。默认设置是在 1 秒切片中仅提供 0.95 秒的实时时间:
kernel.sched_rt_period_us = 1000000
kernel.sched_rt_runtime_us = 950000
这可以防止实时域占用所有 CPU。如果你想要实时,你必须禁用这些参数。
见:http://www.kernel.org/doc/Documentation/scheduler/sched-rt-group.txt
如果将 sched_rt_runtime_us
设置为 -1,则会禁用此安全机制。
【讨论】:
非常感谢 Jeremy Collake 和 Kaz。我想知道是否会发生这样的事情。我已经能够拥有一个消耗一些 CPU 周期的非 RT 线程,同时运行的 RealTime 线程比我拥有的内核数量还要多。可能只有一些这样的机制,但我什至不知道从哪里开始寻找它。你统治!!!非常感谢!!! ;) 您好!在原始帖子中还有其他错误:我没有调用 pthread_attr_setinheritsched 来设置 PTHREAD_EXPLICIT_SCHED 并且它必须生效。我在 /etc/sysctl.conf 上使用“kernel.sched_rt_runtime_us = -1”对其进行了测试,我得到了我想要的优先级反转,今天我会睡得更开心;)再次感谢大家。 /天使【参考方案3】:大多数现代调度程序都有防死锁保护措施,当检测到或认为合适时,它们会在一两个时间片内更改优先级,以防止导致死锁的优先级倒置。 linux 是否与您使用的任何调度程序一起使用,我不确定。但是,如果您还没有意识到这一点,请务必注意。
【讨论】:
以上是关于不能在 C++ 中引发优先级反转的主要内容,如果未能解决你的问题,请参考以下文章