注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

top、ncurses、ps、tail及其它  

2013-08-11 01:04:50|  分类: 脚本语言 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、杂七杂八的工具
正如名字所显示的,这里的内容杂乱而关系淡化,但是正如马克思主义所说的,事物的联系是绝对的,而不是孤立静止的,通过一些信步漫游,可以将这些东西联系起来。
正如之前所说,很多本来奇妙或者美妙的东西在你大致看来非常平常,但是平常的原因可能只是你本身有审美疲劳了,而不是因为事物本身缺少美感。这是普通青年的说法,文艺青年的说法是生活中不是缺少美,而是缺少发现;通俗的说法就是审美疲劳?
top的功能实现这里就没什么好说的了,top本身是procps的一个工具,所以top的实现应该是周期性的从系统的proc文件系统下读取系统信息,然后展示出来。这个本身也不是美的所在,问题在于大家不觉得top在每次刷新之后它并没有滚屏,通过q退出之后屏幕干干净净,环保意识非常强。估计很多人都没有考虑这个东西是怎么实现的。
其实procps工具包中包含了很多的工具,这些工具只是没哟ps的名气那么大而已,或者说名气也很大,只是大家不知道它们和ps实在同一个工具包中而已。这些工具包括了pgrep,pmap、sysctl、free、vmstat、pkill、skill等。其它的一些
二、top的实现
看一下top的代码,top并没有什么非常大的模块来执行这个刷屏的操作,理论上讲,这个东西的实现是需要一个单独的模块来完成的,所以代码中应该有不少来处理这部分显示,但是惊喜的是依然是没有,只是在top的开始看到了whack_terminal函数中的注释中对于ncurses的说明 注释
static void whack_terminal (void)
{
   struct termios newtty;

   // the curses part...
#ifdef PRETENDNOCAP
   setupterm("dumb", STDOUT_FILENO, NULL);
#else
   setupterm(NULL, STDOUT_FILENO, NULL);
#endif
搜索一下这个setupterm函数,发现它的确是ncurses的一个库函数,所以top的显示使用了ncurses这一点我想大家是不用怀疑的。我们可以想象,这个top的刷屏功能是使用了终端的一些特殊功能来实现的。具体是什么功能,我也不是很清楚,但是光标定位,清屏这些功能可能是一个终端驱动的基本功能,有兴趣的同学可以看一下内核的vt.c文件中对于终端可识别功能的说明。
这里就引出了我们所说的问题,这些功能是依赖于输出设备是一个终端设备来实现屏幕操作功能,那么如果top的输出不是一个终端,而是一个文件呢?此时ncurses好像没有做任何处理,而是不管三七二十一、霸气的将给终端的命令打印在了输出文件里。
例如执行
[root@Harry ~]# top > top.out
之后,cat 输出文件,此时依然是不会滚屏。这个其实并不奇怪,因为cat也只是将文件中的内容按照内码直接打印到终端上,此时输出还是终端,给终端的控制字符被识别出来,然后显示显示在终端上。
从这点看,其实终端更像是一个编程环境,或者说是冯诺依曼体系的忠实践行者。它在同一个通道中即保存了数据,也包含了指令。此时通过hexdump或者vi可以看到top的输出内容,有大量的控制字符:
    1 ^[[H^[[2J^[(B^[[mtop - 00:06:53 up  4:16,  5 users,  load average: 0.00,         0.07, 0.18^[(B^[[m^[[39;49m^[[K
      2 Tasks:^[(B^[[m^[[39;49m^[(B^[[m 151 ^[(B^[[m^[[39;49mtotal,^[(B^[[m^[[39        ;49m^[(B^[[m   3 ^[(B^[[m^[[39;49mrunning,^[(B^[[m^[[39;49m^[(B^[[m 147         ^[(B^[[m^[[39;49msleeping,^[(B^[[m^[[39;49m^[(B^[[m   1 ^[(B^[[m^[[39;49        mstopped,^[(B^[[m^[[39;49m^[(B^[[m   0 ^[(B^[[m^[[39;49mzombie^[(B^[[m^[        [39;49m^[[K
三、ncurses的另一个场合
这个top使用了ncurses之后,突然想起之前经常使用的linux内核的menuconfig配置文件,那个也是一个命令行形式的简陋的图形界面,它的实现是不是也是跟ncurses有关系呢。看了一下内核关于图形界面的处理,并没有找到对于ncurses头文件的包含。但是在很多时候,注释可以给我们很多的帮助,这也是丰富的源代码和冰冷的二进制文件之间的一个重要区别。
在内核构建的图形文件中,可以看到dialog.h文件中有下面的说明
#include CURSES_LOC

/*
 * Colors in ncurses 1.9.9e do not work properly since foreground and
 * background colors are OR'd rather than separately masked.  This version
 * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
 * with standard curses.  The simplest fix (to make this work with standard
 * curses) uses the wbkgdset() function, not used in the original hack.
 * Turn it off if we're building with 1.9.9e, since it just confuses things.
 */
#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
#define OLD_NCURSES 1
#undef  wbkgdset
#define wbkgdset(w,p)        /*nothing */
#else
#define OLD_NCURSES 0
#endif
这个地方有一个逆天的头文件包含宏,也就是说include之后的头文件名称是一个宏,我之前反正是从来没有用过甚至想到过这么上流的用法,小伙伴们再次被惊呆了。
这个宏的生成在相同文件夹下的shell脚本中
ccflags()
{
    if [ -f /usr/include/ncurses/ncurses.h ]; then
        echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
    elif [ -f /usr/include/ncurses/curses.h ]; then
        echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"'
    elif [ -f /usr/include/ncurses.h ]; then
        echo '-DCURSES_LOC="<ncurses.h>"'
    else
        echo '-DCURSES_LOC="<curses.h>"'
    fi
}
或许工具的使用有时候也跟人的关系一样:本来两个人只是普通同事,突然哪一天发现大家都和某个人认识或者是亲戚,是不是感觉距离突然被拉近了呢。
四、显示一个终端的前台进程
这个是之前想知道的一个问题。因为之前有段时间在看终端的一些相关内容,而终端中有一个重要的概念就是前台进程,这个进程是唯一的。但是苦于当时在用户态并没有找到合适的工具来显示这个前端进程信息。例如当前系统中所有终端的所有前端进程的名字等信息。
后台看ps的手册,发现原来ps的tpgid就是表示的
tpgid      TPGID    ID of the foreground process group on the tty (terminal)
                    that the process is connected to, or -1 if the process is
                    not connected to a tty.
但是为什么这个名字这么奇怪,没有反应出前端的概念,也即是单词foreground的任何一个字符,只能说TP的T体现的是terminal,P是process G为group,不过好在这个TP和war3里的回城卷轴相同,也不再纠结了。
1、列出系统所有终端的前台进程
[root@Harry ~]# ps a -otpgid
TPGID
 1447
 1448
 1449
 1450
 1451
 1529
 2058
 2336
 2336
 2453
 2453
 2453
 4984
 4984
 4984
可以看到少了很多东西,但是这问题不大,因为这种工具实用性问题用一下google就行,使用大写的O即可增加而不是仅仅输出这些信息。
[root@Harry ~]# ps a -Otpgid
  PID TPGID S TTY          TIME COMMAND
 1447  1447 S tty4     00:00:00 /sbin/mingetty tty4
 1448  1448 S tty5     00:00:00 /sbin/mingetty tty5
 1449  1449 S tty2     00:00:00 /sbin/mingetty tty2
 1450  1450 S tty3     00:00:00 /sbin/mingetty tty3
 1451  1451 S tty6     00:00:00 /sbin/mingetty tty6
 1529  1529 S tty1     00:02:24 /usr/bin/Xorg :0 -nr -verbose -auth /var/run/gdm
 2058  2058 S pts/0    00:00:00 bash
 2317  2336 S pts/1    00:00:00 bash
 2336  2336 S pts/1    00:00:02 gdb vmlinux
 2340  2453 S pts/2    00:00:00 bash
 2453  2453 S pts/2    00:00:00 /bin/sh ./BootQemu.sh
 2493  2453 S pts/2    00:04:10 ./qemu -initrd /home/tsecer/KernelDebug/BusyboxI
 4334  4989 S pts/3    00:00:00 bash
 4803  4989 T pts/3    00:00:00 top
 4989  4989 R pts/3    00:00:00 ps a -Otpgid
问题是这里的输出有重复的,如何去掉重复的终端
2、去掉重复内容
[root@Harry ~]# ps a -ott,tpgid
TT       TPGID
tty4      1447
tty5      1448
tty2      1449
tty3      1450
tty6      1451
tty1      1529
pts/0     2058
pts/1     2336
pts/1     2336
pts/2     2453
pts/2     2453
pts/2     2453
pts/3     5006
pts/3     5006
pts/3     5006
使用sort去掉重复
[root@Harry ~]# ps a -ott,tpgid | sort -u
pts/0     2058
pts/1     2336
pts/2     2453
pts/3     5010
TT       TPGID
tty1      1529
tty2      1449
tty3      1450
tty4      1447
tty5      1448
tty6      1451
3、去掉header
此时可以看到中间有一个TT TPGID显示是多余的,造成排序之后顺序换乱,所以需要把它删除。查看ps的说明,是有 no-header这个选项的,所以
[root@Harry ~]# ps --no-header a -ott,tpgid | sort -u
pts/0     2058
pts/1     2336
pts/2     2453
pts/3     5018
tty1      1529
tty2      1449
tty3      1450
tty4      1447
tty5      1448
tty6      1451
但有些资料显示这个选项不是ps的通用功能,而只是gnu的扩展,所以可以通过其他已知工具删除第一行即可。此时最直观的工具就是awk,
[root@Harry ~]# ps a -ott,tpgid | awk "NR > 1" | sort -u
pts/0     2006
tty1      1498
tty2      1463
tty3      1464
tty4      1461
tty5      1462
tty6      1465
有没有更简单的工具呢?有,那就是tail
[root@Harry ~]# ps a -ott,tpgid | tail -n+2 | sort -u
pts/0     2015
tty1      1498
tty2      1463
tty3      1464
tty4      1461
tty5      1462
tty6      1465
4、显示更多信息
这里虽然看到了各个终端上前端进程的名字,但是此时也少了进程名称的信息。
[root@Harry ~]# ps a -otpgid | tail -n+2 | sort -u | xargs  -i ps --no-header  -p{}
 1461 tty4     00:00:00 mingetty
 1462 tty5     00:00:00 mingetty
 1463 tty2     00:00:00 mingetty
 1464 tty3     00:00:00 mingetty
 1465 tty6     00:00:00 mingetty
 1498 tty1     00:00:17 Xorg
 2218 pts/1    00:00:00 bash
这个地方有一个地方比较奇怪,就是没有显示自己所在的终端,也就是pts/0的信息,这一点之后可以在分析,暂时觉得可能是和多进程有关,当执行xargs的ps时,该前端进程已经退出,这个退出的进程很可能是xargs创建的第一个ps子进程。
  评论这张
 
阅读(849)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017