周期性的高内核CPU负载?

周期性的高内核CPU负载?,c,linux,centos,kernel,cpu-usage,C,Linux,Centos,Kernel,Cpu Usage,对于通常使用很少CPU的程序来说,内核CPU惊人地高。Linux机器在状态之间交替。大多数情况下,程序使用低CPU正常执行。在CPU“激增”期间,程序使用100%可用CPU的高内核CPU 下面是示例C程序和输出 机器大约每五分钟就会进入和退出一个奇怪的状态,其中一些程序(但不是全部)使用高内核CPU。CPU“激增”可能会持续一分钟,然后机器会再恢复正常状态5-10分钟。重新启动有时会有所帮助,但浪涌会在一周内逐渐累积,直到问题变得足够严重,需要再次重新启动。有时重新启动没有帮助,唯一的临时修复方

对于通常使用很少CPU的程序来说,内核CPU惊人地高。Linux机器在状态之间交替。大多数情况下,程序使用低CPU正常执行。在CPU“激增”期间,程序使用100%可用CPU的高内核CPU

下面是示例C程序和输出

机器大约每五分钟就会进入和退出一个奇怪的状态,其中一些程序(但不是全部)使用高内核CPU。CPU“激增”可能会持续一分钟,然后机器会再恢复正常状态5-10分钟。重新启动有时会有所帮助,但浪涌会在一周内逐渐累积,直到问题变得足够严重,需要再次重新启动。有时重新启动没有帮助,唯一的临时修复方法是尝试另一次重新启动

  • CentOS 6.9版
  • 具有14个CPU、32 GB Ram的Dell PowerEdge R630
  • Linux 2.6.32-696.30.1.el6.x86_64 x86_64
我能够用这个示例C程序重现CPU问题。它运行一个shell脚本,执行
睡眠
0.01秒,并打印10次迭代的运行时间。当机器处于正常状态时,它运行迅速;当机器处于异常状态时,它运行缓慢

测试系统

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int i, n;
    char cmd[100];

    if (argc == 2) {
        n = atoi(argv[1]);
    }
    else {
        n = 1;
    }

    printf("n=%d\n", n);

    for (i=0; i<n; i++) {
        system("ts=$(date +%s%N) ; sleep 0.01 ; tt=$((($(date +%s%N) - $ts)/1000000)) ; echo \"Time taken: $tt milliseconds\"");
    }
}
这是机器处于CPU“喘振”模式时的输出。我在出现两次长时间停顿的地方添加了注释。延迟是由于机器CPU过载造成的。运行时间为35.6秒,比正常时间长170倍。此运行的内核CPU使用率为7.2秒,比正常运行增加了480倍

$ time test_system 10
n=10
Time taken: 161 milliseconds
Time taken: 406 milliseconds
Time taken: 58 milliseconds
Time taken: 176 milliseconds
Time taken: 189 milliseconds
--- approx. 17 sec delay ---
Time taken: 25 milliseconds
Time taken: 127 milliseconds
Time taken: 82 milliseconds
Time taken: 84 milliseconds
Time taken: 12 milliseconds
--- approx. 17 sec delay ---

real    0m35.641s
user    0m0.077s
sys     0m7.233s
$
这表明分配给I/O缓冲区的内存过多可能导致此问题,因为内核必须努力回收内存才能运行程序。但是没有内存交换或不足的迹象。我运行了一个分配100 MB内存的单独测试,即使在CPU激增期间也没有看到延迟或高CPU

关于导致这种行为的原因还有其他建议吗

这是我最新的测试程序,分别对
fork()
exec()进行测试

测试叉

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <assert.h>

#define ELAPSED_USEC(t1, t2)  (SEC2USEC((t2).tv_sec - (t1).tv_sec) + (t2).tv_usec - (t1).tv_usec)
#define SEC2USEC(sec)         ((sec)*1000000)


int main(int argc, char *argv[])
{
    int i, n;
    struct timeval start_time, end_time;
    struct timezone tz;
    pid_t pid;
    char *shell = "/bin/bash";
    char *shell_cmd;
    int status;

    if (argc == 3) {
        n = atoi(argv[1]);
        shell_cmd = argv[2];
    }
    else {
        fprintf(stderr, "Usage: %s count shell_cmd\n", argv[0]);
        exit(1);
    }

    printf("n=%d shell_cmd=[%s]\n", n, shell_cmd);

    for (i=0; i<n; i++) {
        gettimeofday(&start_time, &tz);
        pid = fork();
        if (pid == -1)
        {   
            fprintf(stderr, "fork failed.\n");
            exit(1);
        }
        else if (pid > 0)
        {   
            gettimeofday(&end_time, &tz);
            printf("fork: %ld usec, ", ELAPSED_USEC(start_time, end_time));

            gettimeofday(&start_time, &tz);
            waitpid(pid, &status, 0);
            gettimeofday(&end_time, &tz);
            printf("exec: %ld msec\n", ELAPSED_USEC(start_time, end_time)/1000);  // 1 msec = 1000 usec
            //assert(WEXITSTATUS(status) == 123);
        }
        else
        {
            // we are the child
            execl(shell, shell, "-c", shell_cmd, NULL);
            _exit(EXIT_FAILURE);   // exec never returns
        }
    }
}
当系统速度减慢时,安装并运行类似以下dTrace脚本的程序:

#!/usr/sbin/dtrace -s

#pragma D option quiet

profile:::profile-1001hz
/ arg0 /
{
    @hot[ arg0 ] = count();
}

dtrace:::END
{
    printa( "%@u %a\n", @hot );
}
你可能得换个口型

当系统有一个“插曲”时运行它(可能根据您的问题中的一个时间测试程序自动启动),让它运行10-15秒(如果您想查看一些详细信息,请去掉
#pragma D option quiet
),使用键盘上的
CTRL-C
或进程中的
SIGINT
杀死它

然后,脚本将发出它所采样的所有内核堆栈跟踪,最常见的跟踪出现在最后—您可以看到它们的地方

最后几个内核堆栈跟踪将告诉您在“事件”期间内核在哪里花费时间

不涉及猜测。没有复活节彩蛋狩猎。你会被告知发生了什么事

该脚本,在Solaris 11.4框中运行,在执行<代码> ZFS发送……zfs接收…

backup,显示以下内容:

   .
   .
   .
1729 zfs`zfs_lzjb_compress+0xcd
1834 zfs`zfs_lzjb_compress+0xe8
1883 zfs`zfs_lzjb_compress+0xf1
1991 zfs`zfs_lzjb_compress+0xbc
1994 unix`wrmsr+0xd
2015 unix`sys_syscall+0x1b9
2089 zfs`zfs_lzjb_compress+0x131
2182 zfs`zfs_lzjb_compress+0x115
2346 zfs`zfs_lzjb_compress+0x1bd
2363 zfs`zfs_lzjb_compress+0x93
2376 zfs`zfs_lzjb_compress+0x1a6
2869 unix`mutex_enter+0x10
3619 zfs`zfs_lzjb_compress+0x135
4223 zfs`zfs_lzjb_compress+0x108
5982 unix`mutex_delay_default+0xa
7480 unix`mutex_delay_default+0x7
8548 unix`bcopy+0x55a
3148971 unix`i86_mwait+0xd

请注意,在本例中,绝大多数时间(按三个数量级…)都花在空闲循环中,因为它是一个24核服务器,目前除了
zfs
备份之外什么都不做。其他注意事项几乎都与备份有关。

内存不足、进程列表已满、竞争进程太多、任何事情都会发生……我们几乎不可能在信息如此之少的情况下判断您的系统出了什么问题。。。但是:Linux 2.6.32?老天爷,你考虑过升级吗?特别是在那台电脑上。自该版本以来,可能已经修复了无数个错误。如果在C程序中
nanosleep()
,而不产生外部进程,那么17秒延迟是否可以重现?关于“激增”:CPU是否会因为内核/包温度高而周期性地受到限制?如果时钟频率降得太低,真的很低,这也许可以解释为什么琐碎的任务突然变得如此缓慢,至少部分如此。(
dmesg
,lm传感器,
/sys/devices/system/cpu/cpu0/thermal\u throttle/*
/sys/devices/system/cpu/cpu0/cpufreq/scaling\u cur\u freq
可能是您在这里的朋友。)如果温度看起来不错,但内存可能有问题,你可以
swapoff
交换你所有的交换设备/文件,然后等着看是否有人调用OOM杀手。@vicky-I监控核心温度。没问题。我同意,我需要看看问题是否特定于生成外壳。
#!/usr/sbin/dtrace -s

#pragma D option quiet

profile:::profile-1001hz
/ arg0 /
{
    @hot[ arg0 ] = count();
}

dtrace:::END
{
    printa( "%@u %a\n", @hot );
}
   .
   .
   .
1729 zfs`zfs_lzjb_compress+0xcd
1834 zfs`zfs_lzjb_compress+0xe8
1883 zfs`zfs_lzjb_compress+0xf1
1991 zfs`zfs_lzjb_compress+0xbc
1994 unix`wrmsr+0xd
2015 unix`sys_syscall+0x1b9
2089 zfs`zfs_lzjb_compress+0x131
2182 zfs`zfs_lzjb_compress+0x115
2346 zfs`zfs_lzjb_compress+0x1bd
2363 zfs`zfs_lzjb_compress+0x93
2376 zfs`zfs_lzjb_compress+0x1a6
2869 unix`mutex_enter+0x10
3619 zfs`zfs_lzjb_compress+0x135
4223 zfs`zfs_lzjb_compress+0x108
5982 unix`mutex_delay_default+0xa
7480 unix`mutex_delay_default+0x7
8548 unix`bcopy+0x55a
3148971 unix`i86_mwait+0xd