暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Linux性能利器Htop:完胜top、strace

神机喵算 2016-12-12
943

写在之前:此文翻译自:https://peteris.rocks/blog/htop,做了少许改动,感谢原作者。

长久以来,我只知Linux有个神器htop,却不知道htop的各项指标的内涵。

比如,2核的服务器 CPU利用率为 50%,那为啥load average 却显示 1.0?那接下来开始捋捋。。。

俗话说得好,好记性不如个烂笔头。

Htop on CentOS

来个htop全身照:

  • Uptime

uptime:系统运行的时长。

当然,你可以用uptime

 $ uptime
 12:17:58 up 111 days, 31 min,  1 user,  load average: 0.00, 0.01, 0.05

uptime
从 /proc/uptime
文件获取信息:

 9592411.58 9566042.33

前者数字(9592411.58)代表系统运行的秒值,后者(9566042.33)代表服务器空闲秒数。一般多核系统后者秒值会比系统的uptime值大,因为它是取和。作者是怎么知道这个原因的呢?通过跟踪 uptime
 程序运行打开的文件,这里是用的 strace
 工具:

 strace uptime

从strace输出中grep
查找系统调用的open
 函数。由于strace标准输出内容较多,可以使用2>&1
重定向:

 $ strace uptime 2>&1 | grep open
...
open("/proc/uptime", O_RDONLY)          = 3
open("/var/run/utmp", O_RDONLY|O_CLOEXEC) = 4
open("/proc/loadavg", O_RDONLY)         = 4

其中,包括前面提到的 /proc/uptime
文件。这说明我们可以使用 strace -e open uptime
来代替strace uptime 2>&1 | grep open
。Linux的uptime
命令提供易读、宜用的方法。

  • Load average

除了uptime,有三个数字表示 load average:

 $ uptime
 12:59:09 up 32 min,  1 user,  load average: 0.00, 0.01, 0.03

load average值是从 /proc/loadavg
文件获得,同时你也可以用 strace
 验证。

 $ cat /proc/loadavg
0.00 0.01 0.03 1/120 1500

前三个数字分别表示最近1分钟、5分钟、15分钟的CPU和IO的利用率。第四行显示当前正在运行的进程数和进程总数,最后一行显示最近使用的进程ID。

下面讲下进程ID。当你启动一个新进程,它将会分到一个ID数字。进程ID通常是递增的,除非进程退出后进程ID重新复用。特殊进程ID 1 是属于/sbin/init
 ,系统启动时即分配。

让我们再来看下 /proc/loadavg
文件的内容,然后后台启动 sleep
 命令,这时会输出进程ID。

 $ cat /proc/loadavg
0.00 0.01 0.03 1/123 1566
$ sleep 10 &
[1] 1567

所以,1/123 代表一个进程正在运行,总共有123个进程运行过。

当运行cat /dev/urandom > /dev/null
 (重复生成随机数)时,你会发现有 2 个进程在运行:

 $ cat /dev/urandom > /dev/null &
[1] 1639
$ cat /proc/loadavg
1.00 0.69 0.35 2/124 1679

这里的两个进程是:随机数生成、cat /proc/loadavg
,同时load average值也在增加。

System load average是runnable或uninterruptable状态的进程数的平均数。所以上面的 load average为 1(平均1个运行的进程),是因为作者演示的服务器是单核CPU,一次跑一个进程那CPU的利用率是100%。如果服务器是双核,那CPU利用率就是50%。双核CPU的利用率如果是100%,那load average 将会是2.0。CPU的核数可以从htop的左上角看到,或者运行 nproc

  • Processes

在htop的右上角显示进程数和多少个进程正在运行。但是htop使用 Tasks 代表进程(注:Tasks 是进程的一个别名)。

在htop中,使用键盘上的Shift
+H
组合键也可轻松看到线程数Tasks: 23, 10 thr,使用Shift
+K
组合键可以看到内核线程数,Tasks: 23, 40 kthr

  • Process ID / PID

每个进程启动都会分配一个唯一的进程ID,称作进程ID或者PID。如果你在bash中使用 (&
)在后台执行,你将会看到输出的PID。

 $ sleep 1000 &
[1] 12503

如果你手滑没看到,那也可以在bash中使用 $!
内建变量查看到最近一次后台运行的PID:

 $ echo $!
12503

进程ID是非常有用的,具体为什么可以看维基百科。

procfs
是伪文件系统,procfs可以让用户的程序通过读取文件的方法从Linux内核中获取信息。它经常被挂载在 /proc/
下,伪装的看起来像个正规文件目录,你也可以使用 ls
 和 cd
命令。

某进程相关的所有信息都放在 /proc/<pid>/
下:

 $ ls /proc/12503
attr        coredump_filter  fdinfo     maps        ns             personality  smaps    task
auxv        cpuset           gid_map    mem         numa_maps      projid_map   stack    uid_map
cgroup      cwd              io         mountinfo   oom_adj        root         stat     wchan
clear_refs  environ          limits     mounts      oom_score      schedstat    statm
cmdline     exe              loginuid   mountstats  oom_score_adj  sessionid    status
comm        fd               map_files  net         pagemap        setgroups    syscall

比如,  /proc/<pid>/cmdline  会让你知道这个进程是如何启动的:

 $ cat /proc/12503/cmdline
sleep1000$

正确的查看姿势是(因为命令是用\0 分隔):

 $ od -c /proc/12503/cmdline
0000000   s   l   e   e   p  \0   1   0   0   0  \0
0000013

或者:

 $ tr '\0' '\n' < /proc/12503/cmdline
sleep
1000
$ strings /proc/12503/cmdline
sleep
1000

进程的进程目录还包含有链接(link),比如: cwd
指向工作目录,exe
指向可执行的二进制文件:

 $ ls -l /proc/12503/{cwd,exe}
lrwxrwxrwx 1 ubuntu ubuntu 0 Jul  6 10:10 /proc/12503/cwd -> /home/username
lrwxrwxrwx 1 ubuntu ubuntu 0 Jul  6 10:10 /proc/12503/exe -> /bin/sleep

以上就是htop
top
, ps
这些诊断工具是为啥可以获取到一个进程的详细信息的,/proc/<pid>/<file>。

  • Process tree

在htop中使用F5
即可看到进程树,当然也可以用ps f

 $ ps f
  PID TTY      STAT   TIME COMMAND
12472 pts/0    Ss     0:00 -bash
12684 pts/0    R+     0:00  \_ ps f

或者 pstree

 $ pstree -a
init
  ├─atd
  ├─cron
  ├─sshd -D
  │   └─sshd
  │       └─sshd
  │           └─bash
  │               └─pstree -a
...

从这里你就可以知道为啥 bash
 或者 sshd
 是其它进程的父进程。

 /sbin/init
 作为系统启动进程,进程ID为1,接着是SSH的守护进程 sshd
(当你用ssh连接到服务器),接着是 bash
 shell。

  • Process user

每个进程属于一个user,user以数值ID代表:

 $ sleep 1000 &
[1] 2045
$  grep Uid /proc/2045/status
Uid:    1000    1000    1000    1000

可以用id
命令发现更多关于此user的信息:

 $ id 1000
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm)

通过如下证明id
是从/etc/passwd
 和 /etc/group
 文件获取信息的:

 $ strace -e open id 1000
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
open("/etc/group", O_RDONLY|O_CLOEXEC)  = 3

查看/etc/passwd
 和 /etc/group
 文件的内容:

 $ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
xxx:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
$ cat /etc/group
root:x:0:
adm:x:4:syslog,ubuntu
xxx:x:1000:

passwd文件内没有密码,那密码存储在哪里呢?实际存在/etc/shadow

 $ sudo cat /etc/shadow
root:$6$mS9o0QBw$P1ojPSTexV2PQ.Z./rqzYex.k7TJE2nVeIVL0dql/:17126:0:99999:7:::
daemon:*:17109:0:99999:7:::
ubuntu:$6$GIfdqlb/$ms9ZoxfrUq455K6UbmHyOfz7DVf7TWaveyHcp.:17126:0:99999:7:::

如果你想以root用户来运行程序,得用sudo

 $ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm)
$ sudo id
uid=0(root) gid=0(root) groups=0(root)
$ sudo -u ubuntu id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm)
$ sudo -u daemon id
uid=1(daemon) gid=1(daemon) groups=1(daemon)

如果你想登录到另外一个用户并启动各种命令,使用sudo bash
 或者 sudo -u user bash

当你不想输入密码登录服务器,则可以增加user到 /etc/sudoers
 文件。

 $ echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
-bash: /etc/sudoers: Permission denied

你会发现只有root用户可以操作。

 $ sudo echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
-bash: /etc/sudoers: Permission denied

咋回事呢?还是不行。。。

当你以root权限执行echo
命令追加一行到/etc/sudoers
 ,仍然使用的原user。

通常有两种解决方法:

  1. echo "$USER ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers

  2. sudo bash -c "echo '$USER ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers"

第一种,tee -a
追加标准输入到文件,这时执行以root权限;

第二种,我们以root用户执行bash,用 (-c
) 以root执行整个命令。注意双引号/单引号,因为 $USER
变量转义的问题。

当你想更改密码时,可用 passwd
,也可用 /etc/shadow
 文件,这个文件必须用root权限:

 $ ls -l /etc/shadow
-rw-r----- 1 root shadow 1122 Nov 27 18:52 /etc/shadow

 passwd
 如何才能被常规user执行往具有保护权限的文件写入?

当你启动一个进程时,那这个进程属于你的用户,即使这个可执行文件的拥有者是其它user。

你能改变文件的权限:

 $ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 54256 Mar 29  2016 /usr/bin/passwd

注意 s
 字符,它是sudo chmod u+s /usr/bin/passwd
实现的,意味着能以拥有者root的身份运行可执行文件。

你使用 find /bin -user root -perm -u+s
会发现一个setuid
 可执行文件。同理,对用户组可以用 (g+s
)。

未完待续。。。

Enjoy!

============友情推荐===========

介绍一个本周末技术社区活动,GIAC 全球互联网架构大会将在 12 月 16 ~ 17 日在北京举行,PHP7 核心开发者,链家网技术副总裁惠新宸(鸟哥)作为本次大会联席主席。

架构师最重要的是了解做事的方法和方向,通过 GIAC 两天 40 个案例,可以了解互联网架构发展的最新的动态,提升自己的架构视野。


大会精心策划了语言与架构专题,由知名程序员陈皓(左耳朵耗子)担任出品人,陈皓将在专题分享及对比 7 种语言的编程范式。


通过解决一系列在编程中的问题,在不断进行代码抽象的过程中,讲述编程中常用到的主要编程范式和相关的代码设计方式。其中涉及到的语言:C、C++、Go、Java、Python、Javascript、Scheme(Lisp)等。


GIAC 编程与架构详细议程


参加 GIAC,认识更多技术牛人,最后一周优惠,购买双日套票,高可用架构后花园会员最低仅需 900 元,非会员最低只需 1,260 元,点击阅读原文进入购买页面。


侠天,专注于大数据、机器学习和数学相关的内容,并有个人公众号:bigdata_ny分享相关技术文章。

若发现以上文章有任何不妥,请联系我。

文章转载自神机喵算,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论