Kworker 线程被 SCHED_RR 用户空间线程阻塞

raa*_*hlb 6 c++ linux multithreading real-time linux-kernel

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

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

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

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

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

如果我们必须破解驱动程序,我们可能会为其提供自己的工作队列,并带有 SCHED_RR kworker。

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

The*_*ist 4

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

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

然而,如果系统上有任何实时线程执行并使其保持忙碌,那么 kworker 线程很快执行的机会就会非常小。最终,一旦 RT 线程放弃 CPU 或触发RT 限制,它最终可能会为 kworker 线程提供执行的机会。


较旧的内核支持low_latencyTTY 子系统中的该标志。

在Linux 内核 v3.15 之前,tty_flip_buffer_push()支持low_latencytty 端口标志。

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

t->uport.state->port.tty->low_latency = 1;
Run Code Online (Sandbox Code Playgroud)

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

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


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

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

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

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

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

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

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

struct workqueue_struct *create_workqueue(const char *name);
Run Code Online (Sandbox Code Playgroud)

相反,使用create_singlethread_workqueue()单个 kworker 进程创建工作队列

struct workqueue_struct *create_singlethread_workqueue(const char *name);
Run Code Online (Sandbox Code Playgroud)

2. 使用私有工作队列。

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

int queue_work(struct workqueue_struct *queue, struct work_struct *work);
Run Code Online (Sandbox Code Playgroud)

替换schedule_work()queue_work()tty_flip_buffer_push() 调用的函数。

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

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