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 内核中是否有针对此类内容的内核配置选项?我们可以吗,
如果我们必须破解驱动程序,我们可能会为其提供自己的工作队列,并带有 SCHED_RR kworker。
当然,任何其他解决方案也是令人感兴趣的。如果有一些新功能,我们可以升级到更高的内核版本。
这种行为的根本原因是tty_flip_buffer_push()
在 中kernel/drivers/tty/tty_buffer.c:518
,tty_flip_buffer_push 调度一个异步任务。这很快就会由 kworker 线程异步执行。
然而,如果系统上有任何实时线程执行并使其保持忙碌,那么 kworker 线程很快执行的机会就会非常小。最终,一旦 RT 线程放弃 CPU 或触发RT 限制,它最终可能会为 kworker 线程提供执行的机会。
low_latency
TTY 子系统中的该标志。在Linux 内核 v3.15 之前,tty_flip_buffer_push()
支持low_latency
tty 端口标志。
如果该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_latency
,tty_flip_buffer_push()
不使用单独的工作队列,而是直接调用函数。因此,如果在中断上下文中调用,ISR 将需要更长的时间来执行。这将增加内核/系统其他部分的延迟。此外,在某些条件下(取决于串行缓冲区中有多少可用数据)tty_flip_buffer_push()
可能会尝试休眠(获取互斥锁)。在 Linux 内核的 ISR 中调用 sleep 会导致内核错误。
随着Linux内核中的工作队列实现迁移到CMWQ,
不再可能确定地获取
各个工作队列的独立执行上下文(即单独的线程)。
kworker/*
系统中的所有工作队列都由系统中的线程支持。
注意:此部分已过时!
保留以下内容不变,作为旧版本 Linux 内核的参考。
在 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)
将翻转缓冲区工作排队到上述私有工作队列上,而不是内核的全局全局工作队列上。
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() 调用的函数。
启动时,TTY 层工作队列使用的 kworker 线程可以name
通过创建它时使用的字符串来识别。chrt
根据系统设计的要求,为此线程设置适当的较高 RT 优先级。