zoukankan      html  css  js  c++  java
  • 信号处理引发的cpu高

    背景知识:

    1.tty

    终端是一种字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备。

    tty指的是七个alt+crtl+F1~F7。tty1-tty6表示文字界面,可以用Ctrl+Alt+F1-F6切换,+F7就是切换回图形界面,tty7一般是表示图形界面。

    2. pty(虚拟终端):
    我们远程telnet,ssh,serial到主机时也需要一个终端交互么,这就是虚拟终端pty(pseudo-tty)
     
    3. pts/ptmx(pts/ptmx结合使用,进而实现pty):
    pts(pseudo-terminal slave)是pty的实现方法,与ptmx(pseudo-terminal master)配合使用实现pty。
    man里面是这样说的:ptmx and pts - pseudo-terminal master and slave,pts是所谓的伪终端或虚拟终端,具体表现就是你打开一个终端,这个终端就叫pts/0,如果你再打开一个终端,这个新的终端就叫pts /1。
    比如用who命令查询当前登录的用户,可以看到每个用户的TTY设备(简单来说就是用户输入命令还有显示信息的设备,比如终端)。

    要确定当前的终端,可以敲入tty命令即可显示。

    --------------------------------正文,我只是分割线---------------------------

    下面描述故障现象:

    首先实现一个telnet服务器端程序,监听某个端口,登录之后,执行某些命令,比如top命令,这个时候肯定先创建一个会话,
    openpty(&masterfd,&slavefd,tty_name,NULL,NULL),再使用tcsetattr等来设pts的属性。

    然后要vfork一下,将对应pts的slavefd传给对应的子进程。父进程当然还得对telnet的客户端回复一些可协商的项目,比如

     TELOPT_ECHO,
    TELOPT_NAWS,
    TELOPT_LFLOW,
    TELOPT_ECHO, 
    ITELOPT_SGA

     

     构建命令行参数:

    new_argv[0] = "sh";
    new_argv[1] = "-c";
    new_argv[2] = cmd;
    new_argv[3] = NULL;


    然后调用execvp("/bin/sh", (char **)new_argv) 来执行这个命令。
    子进程流程是:
     fd = open(ptyname, O_RDWR);

    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);

    这样输入输出就通过这个pts设备来通信了。而子进程对标准输入,输出,和error的操作都将是操作对应的pts设备。

    问题出现在什么地方:如果上面的创建子进程的命令是su,切换到root,然后再执行命令top命令,这时候的父子关系如下:

    telnet_proc----su----bash---top

    当直接关闭telnet界面的时候,按道理发信号给进程组,然后全部退出,

    但实际情况是,top有一定的几率获取不到这个信号,不光是top,整个进程组都没有收到信号,那么top的fd就会变为如下:

    top 19663 root 0u CHR 136,26 0t0 29 /dev/pts/26 (deleted)
    top 19663 root 1u CHR 136,26 0t0 29 /dev/pts/26 (deleted)
    top 19663 root 2u CHR 136,26 0t0 29 /dev/pts/26 (deleted)

    [root@localhost ~]# rpm -qf /usr/bin/top
    procps-3.2.8-21.el6.x86_64

    而top这个版本的代码中,是通过select超时来控制的,并且,没有对select的返回值做判断。

    原本默认3秒钟一次的top,变成了1秒钟执行3次,(此时select操作而定fd1,是指向/dev/pts/26的,而这个设备,已经被masterfd删除了)

    11:24:30 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:30 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:30 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:31 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999990})
    11:24:31 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:31 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:32 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:32 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:32 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:33 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:33 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:33 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:33 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:34 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})
    11:24:34 select(1, [0], NULL, NULL, {3, 0}) = 1 (in [0], left {2, 999998})

    其实这个是在系统负荷大的时候的结果,系统系统负荷小,就是遍历的proc下的pid少的话,top在1秒钟之内,能执行很多次遍历,而这种遍历,带来的结果是top占用的那个核,cpu接近了100%。
    当然还有些原因,是因为top继承了实时进程的属性,导致别人也不好抢它cpu。否则cpu会稍微低一些,但也不会低到哪去。

    [root@localhost ~]# chrt -p 19663 
    pid 19663 's current scheduling policy: SCHED_FIFO
    pid 19663 's current scheduling priority: 76



    接下来,回到问题的本质,为什么这种情况下没有接收到退出信号呢?


    水平有限,如果有错误,请帮忙提醒我。如果您觉得本文对您有帮助,可以点击下面的 推荐 支持一下我。版权所有,需要转发请带上本文源地址,博客一直在更新,欢迎 关注 。
  • 相关阅读:
    SQL字段排序
    delphi MessageBox 用法
    delphi获取当前计算机所有盘符
    PHP限制IP
    delphi 显示数据
    GUI模块分解与开发征集
    delphi 用户登录 代码
    Hello China GUI模块鼠标指针的实现
    DELPHI 托盘 实例
    delphi 内存流 操作
  • 原文地址:https://www.cnblogs.com/10087622blog/p/7487970.html
Copyright © 2011-2022 走看看