问题终于处理清楚了,如此坑爹的问题,陆陆续续的搞了有近月的时间,现在有时间写一个过程与总结。
问题现象:进程H需要每隔10s发消息给M(类似watchdog的功能),否则就会有功能异常的告警,业务发现了异常的告警,恰好OS监控日志中记录下了进程H当时是D状态,持续了约20s就恢复过来了,然后就没有然后,啥日志也没有。对于这个问题,各位老大进行亲切友好的交谈,双方达成一致意见:放到攻关团队中搞,OS重点投入。
拿到问题的时候,一堆日志里面翻来翻去,除了知道进程H当时D状态(而且进程H拆出了大约70~120个线程),就没有其他有效信息。问题也不重现。本着“没有搞不定的问题,只有不重现的问题”的原则,构造条件准备重现问题。
在增大业务量做压力重现的时候,神奇的事情发生了,H主线程没有发生D状态,另外一个K进程则进入了D状态,最关键的是杀掉H进程后,K进程就恢复正常了,否则就会一直D状态长时间不恢复。看来H进程和K进程是有关系的,那就先入手必现的问题,把K进程的问题搞定。 由于K D状态是必现问题,就可以通过cat /proc/`pidof K`/stack
将进程的调用栈打印出来,根据栈的信息看到看到K进程D状态的过程:(其实proc的栈缺的太多,很多中间过程全靠脑补) K进程调用了mlock用来锁定内存(使其不置换到SWAP上,降低效率),mlock干活之前使用lru_add_drain_all回刷pagevec(pagevec是什么),lru_add_drain_all对每个CPU下发work函数lru_add_drain_per_cpu。int lru_add_drain_all(void)
{
return schedule_on_each_cpu(lru_add_drain_per_cpu);
}
int schedule_on_each_cpu(work_func_t func)
{
int cpu;
struct work_struct __percpu *works;
works = alloc_percpu(struct work_struct);
if (!works)
return -ENOMEM;
get_online_cpus();
//对每个CPU循环
for_each_online_cpu(cpu) {
struct work_struct *work = per_cpu_ptr(works, cpu);
//将func添加到work结构体中
INIT_WORK(work, func);
//将work结构体添加到任务(work)队列里面
schedule_work_on(cpu, work)