写在之前:此文翻译自: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
$ sleep10 &
[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。
$ sleep1000 &
[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   1000  \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  610:10 /proc/12503/cwd -> /home/username
lrwxrwxrwx 1 ubuntu ubuntu 0 Jul  610:10 /proc/12503/exe -> /bin/sleep
以上就是htoptop, 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代表:
$ sleep1000 &
[1] 2045
$ grep Uid /proc/2045/status
Uid:    100010001000    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
$ sudocat /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"$USERALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
-bash: /etc/sudoers: Permission denied
你会发现只有root用户可以操作。
$ sudoecho"$USERALL=(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 2718:52 /etc/shadow
passwd 如何才能被常规user执行往具有保护权限的文件写入?
当你启动一个进程时,那这个进程属于你的用户,即使这个可执行文件的拥有者是其它user。
你能改变文件的权限:
$ ls-l /usr/bin/passwd
-rwsr-xr-x1 root root 54256 Mar 292016 /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分享相关技术文章。
若发现以上文章有任何不妥,请联系我。
继续阅读
阅读原文