我有一个应用程序,其中一些线程使用CPU亲和性固定到特定的核心。
我已经尝试使用SCHED_FIFO/SCHED_RR将这些线程移动到实时优先级策略,其中SCHED_FFIFO/SCHED_RR在这些策略中具有系统上可用的最低优先级:
param.sched_priority = sched_get_priority_min(SCHED_FIFO);
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
因此,这些线程的性能受到了很大的影响。
经过一些研究,我发现原因是一个线程调用:kworker/1:0处于R状态,但由于它有一个常规策略而不是实时的,因此无法运行。
当我把这个kworker线程移到RT优先级时,我的应用程序又恢复了高性能:
chrt --fifo -p 99 <kworker_pid>
这看起来像是某种优先级反转,我的线程正在等待这个优先级较低的kworker,因此无法运行。
我试图弄清楚这个kwroker线程到底做了什么,以及为什么它被卡住会对我的应用程序产生如此大的影响,然而,我从它的内核回溯中得到的唯一线索是:
/proc/<kworker PID>/stack
[<ffffffff8107c1f3>] worker_thread+0x123/0x400
[<ffffffff810820be>] kthread+0xce/0xe0
[<ffffffff81602cec>] ret_from_fork+0x7c/0xb0
有什么想法吗?
要了解kworker线程正在做什么,可以使用perf
调查情况。
由于kworker线程通常执行工作队列项目,因此可以查看工作队列跟踪事件,例如:
perf trace -e 'workqueue:workqueue_queue_work' --call-graph dwarf -C $CPU_NO
其中$CPU_NO
是应用程序的固定线程被阻止的CPU核心编号。
如果工作项是由另一个CPU执行的操作排队的,则必须使用bcc跟踪或bpftrace添加一个条件,例如:
/usr/share/bcc/tools/trace -C -t -K -U
't:workqueue:workqueue_queue_work (args->req_cpu==1)
"%d => %d", args->cpu, args->req_cpu'
或
bpftrace -e ' tracepoint:workqueue:workqueue_queue_work /args->req_cpu==1/
{ printf("%d => %u %s %sn", args->cpu, args->req_cpu, kstack, ustack) } '
(跟踪点还有一个带有工作队列名称的字符串参数,这在这里也很有趣,但访问起来可能很棘手)
堆栈跟踪可能包括足够的提示,以了解应用程序是如何触发该工作队列项的。你会得到一些线索来研究你是否可以对此做点什么,例如,这样它就不会再发生在CPU上了(另请参阅Documentation/kernel-per-CPU-kthreads.txt以获得一些初步提示)。