top命令引起系统负载升高

系统环境:CentOS  6.5 x86_64


一、问题表现

系统负载升高,某一个核的cpu资源被top进程耗尽,如下图所示:

wKiom1R3RZKjC88MABAsMsnfBE0568.jpg

二、分析过程

  1. 通过strace命令打印top进程信息,出现大量重复的系统调用,如下:


    wKiom1R3Re_QRjNBABBDWTIBdxs758.jpg

    查看top进程的fd信息如下:

    wKioL1R3Rp3R0VZzAAiOoRdPfS0454.jpg
    通过以上信息推断,top进程的输入输出异常,触发select返回文件描述符,但是文件描述符未处理或处理不当造成select无限触发,陷入死循环,占用一个核的cpu资源。

  2. 下载procps-3.2.8查看top源码,在第3398行找到了相关调用。如代码所示标准输入被放到了select调用里,因为标准输入错误导致触发select返回fd,无tv时长的等待。

  3. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    long  file_flags;
    int  rc;
    char  c;
    fd_set fs;
    FD_ZERO(&fs);
    FD_SET(STDIN_FILENO, &fs);
    file_flags = fcntl(STDIN_FILENO, F_GETFL);
    if (file_flags==-1) file_flags=0;
    fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
      
    // check 1st, in case tv zeroed (by sig handler) before it got set
    rc = chin(0, &c, 1);
    if  (rc <= 0) { 
        // EOF is pretty much a "can't happen" except for a kernel bug.
        // We should quickly die via SIGHUP, and thus not spin here.
        // if (rc == 0) end_pgm(0); /* EOF from terminal */
        fcntl(STDIN_FILENO, F_SETFL, file_flags);
        select(1, &fs, NULL, NULL, &tv);
        fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
    }        
    if  (chin(0, &c, 1) > 0) { 
        fcntl(STDIN_FILENO, F_SETFL, file_flags);
        do_key((unsigned)c);
    else  {
        fcntl(STDIN_FILENO, F_SETFL, file_flags);
    }
  4. 标准输入的处理代码如下:

  5. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    static  int  chin ( int  ech,  char  *buf, unsigned cnt)
    {
        int  rc;
      
        fflush (stdout);
        if  (!ech)
           rc = read(STDIN_FILENO, buf, cnt);
        else  {
           tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
           rc = read(STDIN_FILENO, buf, cnt);
           tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
        }
        // may be the beginning of a lengthy escape sequence
        tcflush(STDIN_FILENO, TCIFLUSH);
        return  rc;                    // note: we do NOT produce a vaid 'string'
    }
  6. rc为read返回值,分三类返回正的字节数、0(表示到达文件末尾)、返回负值-1(表示出错)。源码中 rc==0的情况被注掉了(如下图)造成标准输入fd未处理,select的超时未生效。wKioL1R3SArwI-HOAAnw9qyo9u8785.jpg

  7. top在运行时出现标准输入异常这种情况一般是很难发生的,通过ps -fe 打印出了top的进程关系如下:
    wKiom1R3R6-glxl1AALTS7jPJQw179.jpg
    sudo 进程的pid变成1了,通过 ssh -t ‘strace -o sudo.strace sudo -i’  抓取信息如下:
    wKioL1R3SF3S0eAtAAxdnsMrm18065.jpg
    在终端异常关闭(断网)后,并未退出,将父进程设成1继续运行。

  8. 经测试在ssh -t ‘sudo -i’ 这种方式登陆,sudo的父进程是sshd,当终端异常关闭时sudo会将自己的父进程改为1继续运行。正常登陆后在运行sudo的,sudo的父进程一般为bash(也可能是其他shell),不会出现类似情况。

三、总结

因线上环境很多操作需要切换到root,为了方便在ssh登陆的时候加上了 -t ‘sudo -i’ 的参数,从而导致了悲剧的发生。捷径随好但要小心陷阱,一不小心就可能酿成大错。

注:

  1. setsid top 或者其他守护进程调用top,当终端异常退出引起标准输入错误时都会造成top异常。

  2. ubuntu系统不存在此问题,debian修复了此问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
nicai@bugaosuni:~/procps-3.2.8/debian/patches$ cat top_stdin_eof.patch
Description: Check  for  stdin eof  if  term 
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Bug-Debian: http: //bugs.debian.org/458986
Reviewed-by: Craig Small <csmall@debian.org>
Index: b/top.c
===================================================================
--- a/top.c    2009-11-24 21:00:34.000000000 +1100
+++ b/top.c    2009-11-24 21:00:35.000000000 +1100
@@ -3408,9 +3408,8 @@
           // check 1st, in case tv zeroed (by sig handler) before it got set
           rc = chin(0, &c, 1);
           if  (rc <= 0) {
-             // EOF is pretty much a "can't happen" except for a kernel bug.
-             // We should quickly die via SIGHUP, and thus not spin here.
-             // if (rc == 0) end_pgm(0); /* EOF from terminal */
+             if  (rc == 0) end_pgm(0);  /* EOF from terminal, may happen if top
+                                      * erroneously gets detached from it. */
              fcntl(STDIN_FILENO, F_SETFL, file_flags);
              select(1, &fs, NULL, NULL, &tv);
              fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
nicai@bugaosuni:~/procps-3.2.8/debian/patches$
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值