【问题标题】:Kworker threads getting blocked by SCHED_RR userspace threadsKworker 线程被 SCHED_RR 用户空间线程阻塞
【发布时间】:2017-04-20 00:24:43
【问题描述】:

我们有一个使用内核 3.14.17、PREEMPT RT 的 Linux 系统。它是一个单核系统。

对于延迟问题,我们的应用程序将其某些线程的调度类型设置为 SCHED_RR。但是,这会导致内核中的 kworkers 被阻塞,因为它们仅在 SCHED_OTHER 模式下运行。这会导致一种优先级倒置,因为低优先级的 SCHED_RR 线程可以阻止更高优先级的 SHED_RR 接收来自驱动程序的数据。

是 TTY 驱动程序被阻止。它使用函数 tty_flip_buffer_push 中的工作队列。可能会有更多电话,但这是我们已经确定的电话。

有什么方法可以轻松解决这个问题——RT 应用程序依赖于 kworker?我们希望我们不必自己破解驱动程序/内核。 RT 内核中是否有任何内核配置选项可用于此类内容?我们可以吗,

  • 为 kworker 设置 SCHED_RR 优先级?
  • 禁用特定驱动程序的工作队列?

如果我们必须破解驱动程序,我们可能会给它自己的工作队列,使用 SCHED_RR kworker。

当然,任何其他解决方案也很有趣。如果有一些新功能,我们可以升级到更高版本的内核。

【问题讨论】:

  • 你是否使用了没有 CPU pinning 的 RR 优先级?这注定要失败。
  • 好吧,我们只有一个核心。我会更新问题。
  • 你不应该使用实时优先级。就这么简单。
  • 为什么让内核线程实时运行不可行?
  • 因为你只有一个 CPU。

标签: c++ linux multithreading linux-kernel real-time


【解决方案1】:

这种行为的根本原因是tty_flip_buffer_push()

kernel/drivers/tty/tty_buffer.c:518, tty_flip_buffer_push 调度一个异步任务。这很快就会由 kworker 线程异步执行。

但是,如果任何实时线程在系统上执行并使其保持忙碌状态,那么 kworker 线程将很快执行的机会就会非常少。最终,一旦 RT 线程放弃 CPU 或触发RT-throttling,它最终可能会为 kworker 线程提供执行的机会。


较旧的内核支持 TTY 子系统中的 low_latency 标志。

Linux 内核 v3.15 之前,tty_flip_buffer_push() 支持 tty 端口的 low_latency 标志。

如果 low_latency 标志由 UART 驱动程序设置如下(通常在其 .startup() 函数中),

t->uport.state->port.tty->low_latency = 1;

然后tty_flip_buffer_push() 在当前函数调用本身的上下文中执行同步复制。因此它会自动继承当前任务的优先级,即不会因异步调度工作任务而导致优先级反转。

注意:如果串行驱动程序设置了low_latency 标志,它必须避免在 ISR(中断上下文)中调用tty_flip_buffer_push()。设置low_latency 标志后,tty_flip_buffer_push() 不使用单独的工作队列,而是直接调用函数。因此,如果在中断上下文中调用,ISR 将需要更长的时间来执行。这将增加内核/系统其他部分的延迟。同样在某些条件下(取决于串行缓冲区中有多少数据可用)tty_flip_buffer_push() 可能会尝试休眠(获取互斥锁)。在 Linux 内核的 ISR 中调用 sleep 会导致 kernel bug


随着 Linux 内核中的工作队列实现迁移到 CMWQ
不再可能确定性地获得独立的执行上下文
(即单独的线程)用于各个工作队列。

系统中的所有工作队列都由系统中的kworker/* 线程支持。

注意:此部分已过时!!
保留以下内容作为旧版本 Linux 内核的参考。

低延迟/实时 UART/TTY 的定制:

1。为 TTY 层创建和使用个人工作队列。

在 tty_init() 中创建一个新的工作队列。

使用create_workqueue() 创建的工作队列将为系统上的每个 CPU 提供 1 个工作线程。

struct workqueue_struct *create_workqueue(const char *name);

使用 create_singlethread_workqueue() 代替,创建一个带有单个 kworker 进程的工作队列

struct workqueue_struct *create_singlethread_workqueue(const char *name);

2。使用私有工作队列。

在上述私有工作队列而不是内核的全局全局工作队列上排队翻转缓冲区。

int queue_work(struct workqueue_struct *queue, struct work_struct *work);

将 tty_flip_buffer_push() 调用的函数中的 schedule_work() 替换为 queue_work()

3。调整私有工作队列的执行优先级。

启动时,TTY 层工作队列正在使用的 kworker 线程可以通过创建它时使用的字符串name 来识别。根据系统设计的要求,在该线程上使用chrt 设置适当的更高RT 优先级。

【讨论】:

  • 谢谢!我们还发现了一个补丁,它似乎做了类似的事情,但使用的是 kworker 而不是正确的工作队列。补丁在这里:spinics.net/lists/linux-serial/msg17782.html 似乎是唯一的方法 - 遗憾的是没有现成的解决方案。除非链接的补丁被集成。
猜你喜欢
  • 2012-01-25
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多