linux suse 3.0.101的一次中断暴增的排查
本文相关背景知识可以在:http://man7.org/linux/man-pages/man5/proc.5.html?spm=5176.100239.blogcont6047.8.ImCGpr 看到。
在一次呼叫测试中,遇到如下的问题:
| 512k 116k| 412k 5964M| | 694k 317k|.9G 85.3M 125G .9G|- ::
| 11M| 465k 5962M| | 696k 319k|.8G 85.3M 125G .9G|- ::
| 652k | 463k 5964M| | 695k 319k|.9G 85.3M 125G .9G|- ::
| 444k| 512k 5962M| | 695k 318k|.8G 85.5M 125G .9G|- ::
6 18 16 0 0 60|1168k 116k|1394k 9420M| 0 0 |5324k 200k|91.9G 85.5M 125G 34.9G|17-09 12:54:56 missed 2 ticks
5 19 2 0 0 73| 512k 168k|1074k 5632M| 0 0 |3683k 18k|91.9G 85.5M 125G 34.8G|17-09 12:54:58 missed 3 ticks
| 504k 28k| 732k 4115M| |3035k 14k|.9G 85.5M 125G .8G|- ::
| | 186k 1171M| | 435k |.9G 85.5M 125G .8G|- ::
| 512k 168k| 347k 2303M| |1367k |.9G 85.6M 125G .8G|- ::
|1388k 132k| 738k 2844M| | 763k 149k|.9G 85.6M 125G .8G|- ::
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system-- ------memory-usage----- ----system----
usr sys idl wai hiq siq| read writ| recv send| in out | int csw | used buff cach free| date/time
| 892k 11M| 489k 5034M| | 689k 220k|.9G 85.7M 125G .9G|- ::
|1308k | 421k 5376M| | 664k 175k|.9G 85.7M 125G .9G|- ::
| 91M 908k| 355k 5876M| | 641k 153k|.8G 85.8M 125G .9G|- ::
dstat出现missed ticks,根据dstat的源码,确定说明当时没有来得及调度,因为本来我们给dstat的周期是1s打印一次。
没有调度的原因看起来也比较简单,是因为中断占用cpu过高。如上图下划线所示,
软中断占比这么高,到底是软中断数目增加了,还是平均软中断的处理时间增加了呢?同时我发现,int的值也异常增长,出于敬仰,先看硬中断,因为硬中断会引发软中断。dstat源码如下:
self.open('/proc/stat')
self.nick = ('int', 'csw')
self.vars = ('intr', 'ctxt')
打开这个 /proc/stat 文件看看:
cat /proc/stat |grep intr
intr
一开始我以为第二列的 47707586836 是后面的总和,所以很自然地将后面的中断数做了增量的排查,发现后面的中断数量没有暴增,但是总数却暴增了,
难道后面的中断数和总数并不一致?通过把后面的总和一加起来,发现真的对不上:
cat /proc/stat |grep intr|awk '{b[NR]=$0; for(i=3;i<=NF;i++)a[NR]+=$i;}END{print $1,$2, a[NR]}'
intr
官方文档是怎么说的呢:
intr
This line shows counts of interrupts serviced since
boot time, for each of the possible system interrupts.
The first column is the total of all interrupts ser‐
viced including unnumbered architecture specific inter‐
rupts; each subsequent column is the total for that
particular numbered interrupt. Unnumbered interrupts
are not shown, only summed into the total.
后面的各个irq的数量加起来,远远没有前面那个值多。看下内核源码核实一下:
根据读取的/proc/stat代码:
static int show_stat(struct seq_file *p, void *v)
{
int i, j;
unsigned long jif;
cputime64_t user, nice, system, idle, iowait, irq, softirq, steal;
cputime64_t guest, guest_nice;
u64 sum = ;-------------------------------------------硬
u64 sum_softirq = ;-----------------------------------软
unsigned int per_softirq_sums[NR_SOFTIRQS] = {};
struct timespec boottime; user = nice = system = idle = iowait =
irq = softirq = steal = cputime64_zero;
guest = guest_nice = cputime64_zero;
getboottime(&boottime);
jif = boottime.tv_sec; for_each_possible_cpu(i) {
user = cputime64_add(user, kstat_cpu(i).cpustat.user);
nice = cputime64_add(nice, kstat_cpu(i).cpustat.nice);
system = cputime64_add(system, kstat_cpu(i).cpustat.system);
idle = cputime64_add(idle, get_idle_time(i));
iowait = cputime64_add(iowait, get_iowait_time(i));
irq = cputime64_add(irq, kstat_cpu(i).cpustat.irq);
softirq = cputime64_add(softirq, kstat_cpu(i).cpustat.softirq);
steal = cputime64_add(steal, kstat_cpu(i).cpustat.steal);
guest = cputime64_add(guest, kstat_cpu(i).cpustat.guest);
guest_nice = cputime64_add(guest_nice,
kstat_cpu(i).cpustat.guest_nice);
sum += kstat_cpu_irqs_sum(i);-------------------------------sum加
sum += arch_irq_stat_cpu(i);--------------------------------sum加 for (j = ; j < NR_SOFTIRQS; j++) {
unsigned int softirq_stat = kstat_softirqs_cpu(j, i); per_softirq_sums[j] += softirq_stat;
sum_softirq += softirq_stat;
}
}
sum += arch_irq_stat();-----------------------------------------sum加
...... /* sum again ? it could be updated? */
for_each_irq_nr(j)----------------------------------------------这个只是遍历到 nr_irqs,也就是for (irq = 0; irq < nr_irqs; irq++),
seq_printf(p, " %u", kstat_irqs(j));--------------------------------打印各个中断自己的计数,时间上存在一点偏差,但不是本文的主要矛盾。
。。。。。。
这么看起来,sum主要是由三个函数的值构成:
kstat_cpu_irqs_sum,arch_irq_stat_cpu,arch_irq_stat 这三个函数,看起来这三个函数都差不多。下面依次来看这三个函数的实现:
static inline unsigned int kstat_cpu_irqs_sum(unsigned int cpu)
{
return kstat_cpu(cpu).irqs_sum;
}
而增加这个某个中断percpu的计数的时候,会同时增加percpu变量的irqs_sum,主要是针对动态申请的irq。
#define kstat_incr_irqs_this_cpu(irqno, DESC) \
do { \
__this_cpu_inc(*(DESC)->kstat_irqs); \
__this_cpu_inc(kstat.irqs_sum); \
} while ()
grep CONFIG_GENERIC_HARDIRQS /boot/config-3.0.101-0.47.52-default
CONFIG_GENERIC_HARDIRQS=y
第二个函数:arch_irq_stat_cpu ,主要是针对arch相关的,看看它的实现如下:
/*
* /proc/stat helpers
*/
u64 arch_irq_stat_cpu(unsigned int cpu)
{
u64 sum = irq_stats(cpu)->__nmi_count; #ifdef CONFIG_X86_LOCAL_APIC
sum += irq_stats(cpu)->apic_timer_irqs;
sum += irq_stats(cpu)->irq_spurious_count;
sum += irq_stats(cpu)->apic_perf_irqs;
sum += irq_stats(cpu)->apic_irq_work_irqs;
#endif
if (x86_platform_ipi_callback)
sum += irq_stats(cpu)->x86_platform_ipis;
#ifdef CONFIG_SMP
sum += irq_stats(cpu)->irq_resched_count;
sum += irq_stats(cpu)->irq_call_count;
sum += irq_stats(cpu)->irq_tlb_count;
#endif
#ifdef CONFIG_X86_THERMAL_VECTOR
sum += irq_stats(cpu)->irq_thermal_count;
#endif
#ifdef CONFIG_X86_MCE_THRESHOLD
sum += irq_stats(cpu)->irq_threshold_count;
#endif
#ifdef CONFIG_X86_MCE
sum += per_cpu(mce_exception_count, cpu);
sum += per_cpu(mce_poll_count, cpu);
#endif
return sum;
}
而且比较特殊的是,在cat /proc/stat里面的中断中,不会显示第二个函数的值。
第三个函数的是:arch_irq_stat :
u64 arch_irq_stat(void)
{
u64 sum = atomic_read(&irq_err_count);
return sum;
}
只是查看 irq_err_count 的计数而已。
这个计数一般在这个函数增长:
/*
* This interrupt should never happen with our APIC/SMP architecture
*/
void smp_error_interrupt(struct pt_regs *regs)
{
u32 v0, v1;
u32 i = ;
static const char * const error_interrupt_reason[] = {
"Send CS error", /* APIC Error Bit 0 */
"Receive CS error", /* APIC Error Bit 1 */
"Send accept error", /* APIC Error Bit 2 */
"Receive accept error", /* APIC Error Bit 3 */
"Redirectable IPI", /* APIC Error Bit 4 */
"Send illegal vector", /* APIC Error Bit 5 */
"Received illegal vector", /* APIC Error Bit 6 */
"Illegal register address", /* APIC Error Bit 7 */
}; exit_idle();
irq_enter();
/* First tickle the hardware, only then report what went on. -- REW */
v0 = apic_read(APIC_ESR);
apic_write(APIC_ESR, );
v1 = apic_read(APIC_ESR);
ack_APIC_irq();
atomic_inc(&irq_err_count);
。。。。。。
知道了sum和后面打印的各个中断计数的区别,我们来看一下有没有地方能够和sum值对上,其实在/proc/interrupt 文件中,有着能和sum对上的信息:
我用如下脚本统计一下:
linux:~ # cat /proc/interrupts |grep -v CPU |while read line; do echo $line| awk '{b[NR]=$0; for(i=2;i<=33;i++)a[NR]+=$i;}END{print $1, a[NR]}' ; done |awk 'BEGIN{sum=0}{sum+=$2}END{print sum}' && cat /proc/stat |grep -i intr|awk '{print $2}' linux:~ # cat /proc/interrupts |grep -v CPU |while read line; do echo $line| awk '{b[NR]=$0; for(i=2;i<=33;i++)a[NR]+=$i;}END{print $1, a[NR]}' ; done |awk 'BEGIN{sum=0}{sum+=$2}END{print sum}' && cat /proc/stat |grep -i intr|awk '{print $2}' linux:~ # cat /proc/interrupts |grep -v CPU |while read line; do echo $line| awk '{b[NR]=$0; for(i=2;i<=33;i++)a[NR]+=$i;}END{print $1, a[NR]}' ; done |awk 'BEGIN{sum=0}{sum+=$2}END{print sum}' && cat /proc/stat |grep -i intr|awk '{print $2}'
由于计算时间上的偏差,会导致两个有点偏差,但是 其增长量是一致的,感兴趣的可以点到这两个脚本的顺序看下,所以这个文件是和/proc/stat中intr那个总和一致的。当然,
我们也可以用源代码来确定下:
int show_interrupts(struct seq_file *p, void *v)
{
static int prec; unsigned long flags, any_count = ;
int i = *(loff_t *) v, j;
struct irqaction *action;
struct irq_desc *desc; if (i > ACTUAL_NR_IRQS)
return ; if (i == ACTUAL_NR_IRQS)
return arch_show_interrupts(p, prec);----------------------------统计arch相关的硬中断数量 /* print header and calculate the width of the first column */
if (i == ) {
for (prec = , j = ; prec < && j <= nr_irqs; ++prec)
j *= ; seq_printf(p, "%*s", prec + , "");
for_each_online_cpu(j)
seq_printf(p, "CPU%-8d", j);
seq_putc(p, '\n');
} desc = irq_to_desc(i);
if (!desc)
return ; raw_spin_lock_irqsave(&desc->lock, flags);
for_each_online_cpu(j)
any_count |= kstat_irqs_cpu(i, j);
action = desc->action;
if (!action && !any_count)
goto out; seq_printf(p, "%*d: ", prec, i);
for_each_online_cpu(j)
seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));---------------统计各个中断的各个cpu的触发数量。sum里面第一个函数的统计
。。。。。
调用了arch_show_interrupts:
/*
* /proc/interrupts printing for arch specific interrupts
*/
int arch_show_interrupts(struct seq_file *p, int prec)---------------sum里面的第二个函数的统计
{
int j; seq_printf(p, "%*s: ", prec, "NMI");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->__nmi_count);
seq_printf(p, " Non-maskable interrupts\n");
#ifdef CONFIG_X86_LOCAL_APIC
seq_printf(p, "%*s: ", prec, "LOC");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->apic_timer_irqs);
seq_printf(p, " Local timer interrupts\n"); seq_printf(p, "%*s: ", prec, "SPU");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->irq_spurious_count);
seq_printf(p, " Spurious interrupts\n");
seq_printf(p, "%*s: ", prec, "PMI");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->apic_perf_irqs);
seq_printf(p, " Performance monitoring interrupts\n");
seq_printf(p, "%*s: ", prec, "IWI");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->apic_irq_work_irqs);
seq_printf(p, " IRQ work interrupts\n");
#endif
if (x86_platform_ipi_callback) {
seq_printf(p, "%*s: ", prec, "PLT");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->x86_platform_ipis);
seq_printf(p, " Platform interrupts\n");
}
#ifdef CONFIG_SMP
seq_printf(p, "%*s: ", prec, "RES");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->irq_resched_count);
seq_printf(p, " Rescheduling interrupts\n");
seq_printf(p, "%*s: ", prec, "CAL");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->irq_call_count);
seq_printf(p, " Function call interrupts\n");
seq_printf(p, "%*s: ", prec, "TLB");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->irq_tlb_count);
seq_printf(p, " TLB shootdowns\n");
#endif
#ifdef CONFIG_X86_THERMAL_VECTOR
seq_printf(p, "%*s: ", prec, "TRM");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->irq_thermal_count);
seq_printf(p, " Thermal event interrupts\n");
#endif
#ifdef CONFIG_X86_MCE_THRESHOLD
seq_printf(p, "%*s: ", prec, "THR");
for_each_online_cpu(j)
seq_printf(p, "%10u ", irq_stats(j)->irq_threshold_count);
seq_printf(p, " Threshold APIC interrupts\n");
#endif
#ifdef CONFIG_X86_MCE
seq_printf(p, "%*s: ", prec, "MCE");
for_each_online_cpu(j)
seq_printf(p, "%10u ", per_cpu(mce_exception_count, j));
seq_printf(p, " Machine check exceptions\n");
seq_printf(p, "%*s: ", prec, "MCP");
for_each_online_cpu(j)
seq_printf(p, "%10u ", per_cpu(mce_poll_count, j));
seq_printf(p, " Machine check polls\n");
#endif
seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));------------------------这个就是sum里面最后一个函数的统计
#if defined(CONFIG_X86_IO_APIC)
seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
#endif
return ;
}
所以,源码决定了:
/proc/interrupt 中的中断计数的总数是和/proc/stat中intr 的第一列,是一致的。
下面就好办了,脚本跟踪一下看谁暴增,抓到的是这个中断:
LOC: 525581694 514172958 466070724 423266312 404025702 385033843 379205558 372825646 483824260 489761020 441090851 404241012 383122024 365739399 358834335 352940307 466060977 454682754 416203259 384527958 367710479 354374016 348030881 344457326 434930494 435822175 381035648 356172741 343146180 333338249 328147660 329348394 Local timer interrupts
通过走查代码,我们会将hrtimer的超时时间设置为当前时间+timer_offset,我们发现有一个异常分支的时候,timer_offset 会为0,这个就会导致这个hrtimer迅速触发,触发之后,如果没有别的流程影响的话,这个为0的offset会再次被设置为0,直到另外一个条件满足才回归正常值。
总结:有同事问到,为什么我们看到siq的占比 增长,以及int 异常的时候,不先查软中断,而去查硬中断,这个其实是个经验问题。
因为大体上,硬中断受软中断影响较小,而在硬中断处理的时候,会顺带处理软中断,所以遇到两者同变化的时候,优先从硬中断入手,总是没错的。
在修改了正确的流程之后,本文还有个遗留的问题,就是int数正常之后,还是会出现dstat的miss tick,siq的占比依然会异常,但硬中断数目基本上还算正常。
|4827M 17M| 378k 4969M| | 703k 312k|- ::| 117G 128M .9G .7G
|5089M 48k| 844k 6143M| | 726k 149k|- ::| 117G 128M .6G .7G missed ticks
|3332M 316k| 532k 2035M| | 513k 172k|- ::| 117G 128M .3G .0G
|4959M 304k| 470k 3855M| | 707k 259k|- ::| 117G 128M .3G .0G
|4896M 276k| 315k 4846M| | 715k 266k|- ::| 117G 128M .3G .1G
解决完硬中断暴增这个问题,软中断cpu占比这个问题留着下篇博客来讲。
linux suse 3.0.101的一次中断暴增的排查的更多相关文章
- linux内核自己添加模块(内核版本:3.0.101)
做内核驱动第一步都是学习如何添加模块,这是基础,有了这个基础,剩下就是写代码了. 由于2.4到2.6内核版本的更新,无论是系统调用还是模块添加机制都有了巨大的变化,本人也因此饱经挫折,最后在3.0.1 ...
- 如何在Ubuntu/CentOS上安装Linux内核4.0
大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为‘Hurr durr I'm a sheep’的Linux内核4.0是目前为止最新的主干内核.它是稳定版3. ...
- Linux(CentOS 7.0)安装Oracle11g R2
// 注释 # root用户 $oracle用户 1. 关闭安全措施 # chkconfig iptables off // 永久关闭防火墙 # serviceiptables stop // ...
- 安装 Linux 内核 4.0
大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为‘Hurr durr I'm a sheep’的Linux内核4.0是目前为止最新的主干内核.它是稳定版3. ...
- Linux(RHEL7.0)下安装nginx-1.10.2
查看当前系统版本是否支持 当前,nginx发布包支持以下Linux操作系统版本: RHEL/CentOS: Version Supported Platforms 5.x x86_64, i386 6 ...
- UEFI安装Kali Linux 1.1.0记录
现在使用Kali Linux 1.1.0, UEFI启动,使用Fcitx的拼音输入法,词库实在不爽,将就写一写. 本文地址: http://www.cnblogs.com/go2bed/p/42954 ...
- debian7 请把标有“Debian GNU/Linux 7.1.0 _Wheezy_ - Official amd64 DVD Binary-1 20130615-23:06”的盘片插入驱动器“/media/cdrom/”再按回车键
有时候,在通过apt-get install 安装软件的时候,会出现: 更换介质:请把标有“Debian GNU/Linux 7.1.0 _Wheezy_ - Official amd64 DVD B ...
- window 远程在Linux(centOS7.0)上安装JDK以及配置环境变量
本人是在windows 7 上安装了虚拟机,虚拟机安装的是linux(centOS7.0)系统现在在Windows 上安装SecureCRT 远程虚拟机的linux系统,安装JDK以及配置环境变量. ...
- Linux 进入 5.0 时代!
Linux 进入 5.0 时代! 为什么 Linux 4.2 之后的版本不再是 4.21 而是 5.0? 如果你非要一个理由,那就是因为 Linux 4.x 的版本如今用手指与脚趾加在一起都要数不过来 ...
随机推荐
- 让android程序根据重力感应旋转屏幕(支持4个方向旋转)
原文地址:http://blog.csdn.net/yixiaoqingyuz/article/details/6453798代码如下: ChangeOrientationHandler.java p ...
- prvReadAsyncOperation
prvReadAsyncOperation privilege is the Read privilege for System Job Entity (Role Customizationtab). ...
- [SQL]用SQL语句断开某个数据库的所有活动连接
USE master go IF EXISTS ( SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[P_KillConnectio ...
- 分享微信开发Html5轻游戏中的几个坑
这段时间团队一直在做微信端的一些产品设计和开发,当然也包含一定的运营工作.做过的东西也不少,微名片.微抢票.微活动.微招聘等一些小case. 今天想说的是我们在微信中被玩的最活跃的轻游戏--微刮奖,这 ...
- Solr高效利用:Solr实现SQL的查询与统计
1.如何高效使用Solr查询功能 ?2.单个字段分组统计如何实现? 3.IN条件查询有几种方式? 4.多个字段分组统计是否只支持count? Cloudera公司已经推出了基于Hadoop平台的查询统 ...
- python 打印调用栈
import traceback def BBQ(): traceback.print_stack() 引入 traceback 包,在某个函数中执行 traceback.print_stack().
- IE 主页被恶意篡改的解决方法
IE 主页被篡改了,在ie 的 主页设置中不起任何作用,这个时候,就要打开注册表来修改: 具体操作如下: 1.运行 regedit 打开注册表 2.找到 HKEY_LOCAL_MACHINE\SOF ...
- vue2.0自定义指令
前面一片文章说了vue2.0过滤器,其实自定义指令跟过滤器非常相似,单就定义方式而言,其与过滤器完全一致,分为局部指令,和全局指令.不过就是filter改为directive的区别. 过滤器一般用于对 ...
- StanFord ML 笔记 第十部分
第十部分: 1.PCA降维 2.LDA 注释:一直看理论感觉坚持不了,现在进行<机器学习实战>的边写代码边看理论
- 《算法》第六章部分程序 part 6
▶ 书中第六章部分程序,包括在加上自己补充的代码,包括二分图最大匹配(最小顶点覆盖)的交替路径算法和 HopcroftKarp 算法 ● 二分图最大匹配(最小顶点覆盖)的交替路径算法 package ...