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 的版本如今用手指与脚趾加在一起都要数不过来 ...
随机推荐
- [UE4]Get Parent,widget获得父容器实例对象
- T-SQL 事务
use StudentManager go declare @errorSum int --定义变量,用于累计事务执行过程中的错误 --初始化为0,即无错误 begin transaction beg ...
- IOS 7层协议
ios七层 (1)物理层——Physical 这是整个OSI参考模型的最低层,它的任务就是提供网络的物理连接.所以,物理层是建立在物理介质上(而不是逻辑上的协议和会话),它提供的是机械和电气接口.主要 ...
- Linux性能优化 第三章 性能工具:系统内存
3.1内存性能统计信息 3.1.1 内存子系统和性能 和CPU相比,内存的读写速度都大大落后于CPU.为了弥补这个差距,通常CPU会采用高速缓存的机制(高cache). 3.1.2 内存子系统(虚拟存 ...
- TextView右上角显示小红点,小红点根据TextView的长度移动,小红点被TextView挤出去不显示的问题;
大概就是图片这个样,这个功能很常见,本来我以为很简单,谁知道真的很简单: 遇到点小问题,记录一下,哈哈: 小红点的Drawable: <?xml version="1.0" ...
- 动手动脑-java重载
有以下例子: 例: Using overloaded methods public class MethodOverload { public static void main(String[] ar ...
- mysql数据库优化(二)
1.sql防止注入 https://www.cnblogs.com/sevck/p/6733702.html 结果: C:\Users\ASUS\kuaigong3.6.5\lib\site-pack ...
- 微信小程序 setData 的坑(转)
最近在使用微信小程序的setData时,遇到了以下问题.如下: 官网文档在使用setData()设置数组对象的某个元素的属性时,是这么使用的: Page({ data: { array: [{text ...
- python中logging模块的一些简单用法
用Python写代码的时候,在想看的地方写个print xx 就能在控制台上显示打印信息,这样子就能知道它是什么了,但是当我需要看大量的地方或者在一个文件中查看的时候,这时候print就不大方便了,所 ...
- 《GPU高性能编程CUDA实战》第三章 CUDA设备相关
▶ 这章介绍了与CUDA设备相关的参数,并给出了了若干用于查询参数的函数. ● 代码(已合并) #include <stdio.h> #include "cuda_runtime ...