本文相关背景知识可以在: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的一次中断暴增的排查的更多相关文章

  1. linux内核自己添加模块(内核版本:3.0.101)

    做内核驱动第一步都是学习如何添加模块,这是基础,有了这个基础,剩下就是写代码了. 由于2.4到2.6内核版本的更新,无论是系统调用还是模块添加机制都有了巨大的变化,本人也因此饱经挫折,最后在3.0.1 ...

  2. 如何在Ubuntu/CentOS上安装Linux内核4.0

    大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为‘Hurr durr I'm a sheep’的Linux内核4.0是目前为止最新的主干内核.它是稳定版3. ...

  3. Linux(CentOS 7.0)安装Oracle11g R2

    // 注释 # root用户 $oracle用户 1. 关闭安全措施    # chkconfig iptables off // 永久关闭防火墙 # serviceiptables stop // ...

  4. 安装 Linux 内核 4.0

    大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为‘Hurr durr I'm a sheep’的Linux内核4.0是目前为止最新的主干内核.它是稳定版3. ...

  5. Linux(RHEL7.0)下安装nginx-1.10.2

    查看当前系统版本是否支持 当前,nginx发布包支持以下Linux操作系统版本: RHEL/CentOS: Version Supported Platforms 5.x x86_64, i386 6 ...

  6. UEFI安装Kali Linux 1.1.0记录

    现在使用Kali Linux 1.1.0, UEFI启动,使用Fcitx的拼音输入法,词库实在不爽,将就写一写. 本文地址: http://www.cnblogs.com/go2bed/p/42954 ...

  7. 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 ...

  8. window 远程在Linux(centOS7.0)上安装JDK以及配置环境变量

    本人是在windows 7 上安装了虚拟机,虚拟机安装的是linux(centOS7.0)系统现在在Windows 上安装SecureCRT 远程虚拟机的linux系统,安装JDK以及配置环境变量. ...

  9. Linux 进入 5.0 时代!

    Linux 进入 5.0 时代! 为什么 Linux 4.2 之后的版本不再是 4.21 而是 5.0? 如果你非要一个理由,那就是因为 Linux 4.x 的版本如今用手指与脚趾加在一起都要数不过来 ...

随机推荐

  1. Java项目部署服务器操作

    有 2个工具需要下载,我使用的是 xshell(操作Linux命令),xftp5(操作文件传输) 需要知道服务器 ip ,账号,密码 xshell连接时,协议选择SSH连接,其他正常输入. xftp5 ...

  2. [UE4]UI之间传递数据

    通过创建对方UI类型的变量引用,初始本控件时赋值该变量,就可以对方UI内的方法了.

  3. tpadmin的坑收集 nginx下配置tp5失败

    如下: 1.ADMIN模块如要关联查询,model的函数名一定不要有“_”下划线,否则找不到 /common/model/**.php 如果把函数名file写成“**_file”,调用时,$vo.** ...

  4. F5负载均衡原理(转载)

    https://blog.csdn.net/panxueji/article/details/42647193 一. 负载均衡技术 负载均衡技术在现有网络结构之上提供了一种廉价.有效.透明的方法,来扩 ...

  5. sshd服务安装

    SSHD服务 介绍:SSH 协议:安全外壳协议.为 Secure Shell 的缩写.SSH 为建立在应用层和传输层基础上的安全协议. 作用:sshd服务使用SSH协议可以用来进行远程控制, 或在计算 ...

  6. Win7删除缓存垃圾文件

    del /s /f /q C:\*.tmp

  7. 第11章 拾遗5:IPv6和IPv4共存技术(3)_NAT-PT技术【全书完】

    6.4 NAT-PT (1)NAT-PT和NAT的差别 ①NAT-PT(附带协议转换的网络地址转换)技术秉承NAT技术的思想,但在原理方面大有不同. ②NAT-PT和NAT本质的区别在于应用场合的不同 ...

  8. spark使用hadoop native库

    默认情况下,hadoop官方发布的二进制包是不包含native库的,native库是用C++实现的,用于进行一些CPU密集型计算,如压缩.比如apache kylin在进行预计算时为了减少预计算的数据 ...

  9. golang 入门之struct继承,嵌套

    package main import "fmt" type Jocongmin struct{ Name string Home string Want string } fun ...

  10. Unable to find the wrapper ”https” - did youforget to enable it when you configured PHP?

    Unable to find the wrapper ”https” - did youforget to enable it when you configured PHP? 这是在Windows的 ...