龙之介大人

Linux进程管理与资源管理
16.3 进程管理本章一开始就提到所谓的『进程』的概念,包括进程的触发、子进程与父进程的相关性等等,此外,还有那个...
扫描右侧二维码阅读全文
24
2019/10

Linux进程管理与资源管理

16.3 进程管理

本章一开始就提到所谓的『进程』的概念,包括进程的触发、子进程与父进程的相关性等等,此外,还有那个『进程的相依性』以及所谓的『殭尸进程』等等需要说明的呢!为什么进程管理这么重要呢? 这是因为:

  • 首先,本章一开始就谈到的,我们在操作系统时的各项工作其实都是经过某个 PID 来达成的 (包括你的 bash 环境),因此,能不能进行某项工作,就与该进程的权限有关了。
  • 再来,如果您的 Linux 系统是个很忙碌的系统,那么当整个系统资源快要被使用光时,您是否能够找出最耗系统的那个进程,然后删除该进程,让系统恢复正常呢?
  • 此外,如果由于某个程序写的不好,导致产生一个有问题的进程在内存当中,您又该如何找出他,然后将 他移除呢?
  • 如果同时有五六项工作在您的系统当中运作,但其中有一项工作才是最重要的, 该如何让那一项重要的工 作被最优先执行呢?

所以,一个称职的系统管理员,必须要熟悉进程的管理流程才行,否则当系统发生问题时,还真是很难解决问题!底下我们会先介绍如何观察进程与进程的状态,然后再加以进程控制!

16.3.1 进程的观察

既然进程这么重要,那么我们如何查阅系统上面正在运作当中的进程呢?很简单啊! 利用静态的 ps 或者是动态的 top,还能以 pstree 来查阅进程树之间的关系喔!

将某个时间点的进程运作情况撷取下来:ps

[root@study ~]# ps aux <==观察系统所有的进程数据
[root@study ~]# ps -lA <==也是能够观察所有系统的数据 
[root@study ~]# ps axjf <==连同部分进程树状态
选项与参数:
-A :所有的 process 均显示出来,与 -e 具有同样的效用;
-a :不与 terminal 有关的所有 process ;
-u :有效使用者 (effective user) 相关的 process ; 
x :通常与 a 这个参数一起使用,可列出较完整信息。
输出格式规划:
l :较长、较详细的将该 PID 的的信息列出;
j :工作的格式 (jobs format) 
-f :做一个更为完整的输出。

鸟哥个人认为 ps 这个指令的 man page 不是很好查阅,因为很多不同的 Unix 都使用这个 ps 来查阅进程状态,为了要符合不同版本的需求,所以这个 man page 写的非常的庞大!因此,建议,直接背两个比较不同的选项,一个是只能查阅自己 bash 进程的『 ps-l 』一个则是可 以查阅所有系统运作的进程『 ps aux 』!注意,你没看错,是『 ps aux 』没有那个减号 (-) !先 来看看关于自己 bash 进程状态的观察:

  • 仅观察自己的 bash 相关进程: ps -l
#范例一:将目前属于您自己这次登入的 PID 与相关信息列示出来(只与自己的 bash 有关)
[root@i7dom ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 11028  5900  1  80   0 - 48491 do_wai pts/0    00:00:00 su
4 S     0 11088 11028  1  80   0 - 29125 do_wai pts/0    00:00:00 bash
0 R     0 11177 11088  0  80   0 - 38309 -      pts/0    00:00:00 ps
#还记得之前说过,非必要不要使用 root 直接登入吧?从这个 ps -l 的分析,你也可以发现, 
#其实是使用 sudo 才转成 root 的身份~

系统整体的进程运作是非常多的,但如果使用 ps -l 则仅列出与你的操作环境 (bash) 有关的进程而已,亦即最上层的父进程会是你自己的 bash 而没有延伸到 systemd 这个进程去!那么 ps -l 秀出来的资料有哪些呢? 我们就来观察看看:

  • F:代表这个进程旗标 (process flags),说明这个进程的总结权限,常见号码有:

    • 若为 4 表示此进程的权限为 root ;
    • 若为 1 则表示此子进程仅进行复制(fork)而没有实际执行(exec)。
  • S:代表这个进程的状态 (STAT),主要的状态有:

    • R(Running):该程序正在运作中;
    • S (Sleep):该程序目前正在睡眠状态(idle),但可以被唤醒(signal)。
    • D :不可被唤醒的睡眠状态,通常这支程序可能在等待 I/O 的情况(ex>打印)
    • T :停止状态(stop),可能是在工作控制(背景暂停)或除错 (traced) 状态;
    • Z(Zombie):僵尸状态,进程已经终止但却无法被移除至内存外。
  • UID/PID/PPID:代表『此进程被该 UID 所拥有/进程的 PID 号码/此进程的父进程 PID 号码』
  • C:代表 CPU 使用率,单位为百分比;
  • PRI/NI:Priority/Nice 的缩写,代表此进程被 CPU 所执行的优先级,数值越小代表该进程越快被 CPU 执行。详细的 PRI 与 NI 将在下一小节说明。
  • ADDR/SZ/WCHAN:都与内存有关,ADDR 是 kernel function,指出该进程在内存的哪个部分,如果是个 running 的进程,一般就会显示『 - 』 / SZ 代表此进程用掉多少内存 / WCHAN 表示目前进程是否运作中,同样的, 若为 - 表示正在运作中。
  • TTY:登入者的终端机位置,若为远程登录则使用动态终端接口 (pts/n);
  • TIME:使用掉的 CPU 时间,注意,是此进程实际花费 CPU 运作的时间,而不是系统时间;
  • CMD:就是 command 的缩写,造成此进程的触发程序之指令为何。

所以你看到的 ps -l 输出讯息中,他说明的是:『bash 的程序属于 UID 为 0 的使用者,状态为 睡眠 (sleep), 之所以为睡眠因为他触发了 ps (状态为 run) 之故。此进程的 PID 为 14836,优先执行顺序为 80 ,下达 bash 所取得的终端接口为 pts/0 ,运作状态为等待 (wait) 。』这样已经够清楚了吧? 自己尝试解析一下那么 ps 那一行代表的意义为何呢?

观察系统所有进程:ps aux

#范例二:列出目前所有的正在内存当中的进程:
[root@i7dom ~]# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0 125584  3604 ?        Ss   Aug01   9:05 /usr/lib/systemd/systemd --switched-root --sys
root         2  0.0  0.0      0     0 ?        S    Aug01   0:01 [kthreadd]
......
root      1135  0.1  0.0 116372  3032 pts/3    Ss+  Sep07   0:00 -bash
root     11028  0.0  0.0 193964  2468 pts/0    S    17:17   0:00 su -
......
root     14208  0.0  0.0 107892   620 ?        S    17:26   0:00 sleep 60
root     14209  0.0  0.0 139492  1636 pts/0    R+   17:26   0:00 ps aux

你会发现 ps -l 与 ps aux 显示的项目并不相同!在 ps aux 显示的项目中,各字段的意义为:

  • USER:该 process 属于那个使用者账号的?
  • PID :该 process 的进程标识符。
  • %CPU:该 process 使用掉的 CPU 资源百分比;
  • %MEM:该 process 所占用的物理内存百分比;
  • VSZ :该 process 使用掉的虚拟内存量 (Kbytes) RSS :该 process 占用的固定的内存量 (Kbytes)
  • TTY :该 process 是在那个终端机上面运作,若与终端机无关则显示 ?,另外, tty1-tty6 是本机上面的登入者进程,若为 pts/0 等等的,则表示为由网络连接进主机的进程。
  • STAT:该进程目前的状态,状态显示与 ps -l 的 S 旗标相同 (R/S/T/Z)
  • START:该 process 被触发启动的时间;
  • TIME :该 process 实际使用 CPU 运作的时间。
  • COMMAND:该进程的实际指令为何?

一般来说,ps aux 会依照 PID 的顺序来排序显示,我们还是以 1135 那个 PID 那行来说明!该行的意义为『 root 执行的 bash PID 为 1135,占用了 0.1% 的内存容量百分比,状态为休眠 (S),该进程启动的时间为 9月7号,因此启动太久了,所以没有列出实际的时间点。且取得的终端机环境为 pts/3 。』与 ps aux 看到的其实是同一个进程啦!这样可以理解吗? 让我们继续使用 ps 来观察一下其他的信息吧!

范例三:以范例一的显示内容,显示出所有的进程:
[root@study ~]# ps -lA
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0     1     0  0  80   0 - 32159 ep_pol ?        00:00:03 systemd
1 S     0     2     0  0  80   0 -     0 kthrea ?        00:00:00 kthreadd
1 S     0     3     2  0  80   0 -     0 smpboo ?        00:00:00 ksoftirqd/0
#你会发现每个字段与 ps -l 的输出情况相同,但显示的进程则包括系统所有的进程。


范例四:列出类似进程树的进程显示:
[root@study ~]# ps axjf
    1   783   777   777 ?           -1 S        0   0:00 /bin/bash /usr/sbin/ksmtuned
  783 14346   777   777 ?           -1 S        0   0:00  \_ sleep 60
......
    1  1460  1460  1460 ?           -1 Ss       0   0:00 /usr/sbin/sshd -D
 1460 12993 12993 12993 ?           -1 Ss       0   0:00  \_ sshd: root@pts/0
12993 12999 12999 12999 pts/0    14532 Ss       0   0:00      \_ -bash
12999 14364 14364 12999 pts/0    14532 S        0   0:00          \_ bash
14364 14395 14395 12999 pts/0    14532 S        0   0:00              \_ bash
14395 14422 14422 12999 pts/0    14532 S        0   0:00                  \_ sudo su -
14422 14423 14422 12999 pts/0    14532 S        0   0:00                      \_ su -
14423 14424 14424 12999 pts/0    14532 S        0   0:00                          \_ -bash
14424 14464 14464 12999 pts/0    14532 S        0   0:00                              \_ su -
14464 14465 14465 12999 pts/0    14532 S        0   0:00                                  \_ -bash
14465 14505 14505 12999 pts/0    14532 S        0   0:00                                      \_ bash
14505 14532 14532 12999 pts/0    14532 R+       0   0:00                                          \_ ps axjf

看出来了吧?其实在进行测试时,都是以网络联机进虚拟机来测试的,所以啰,你会发现其实进程之间是有相关性的!不过,其实还可以使用 pstree 来达成这个进程树喔!以上面 的例子来看,透过 sshd 提供的网络服务取得一个进程,该进程提供 bash 给我使用,而我透过 bash 再去执行 ps axjf !这样可以看的懂了吗?其他各字段的意义请 man ps !

#范例五:找出与 cron 与 rsyslog 这两个服务有关的 PID 号码?
[root@i7dom ~]# ps aux | egrep '(cron|rsyslog)'
root      1350  0.0  2.3 741380 93116 ?        Ssl  Aug01  14:04 /usr/sbin/rsyslogd -n
root      1383  0.0  0.0 126316  1668 ?        Ss   Aug01   0:47 /usr/sbin/crond -n
root     27480  0.0  0.0 112712  1008 pts/0    S+   17:38   0:00 grep -E --color=auto (cron|rsyslog)
所以号码是 742 及 1338 这两个啰!就是这样找的!

除此之外,我们必须要知道的是『僵尸 (zombie) 』进程是什么? 通常,造成僵尸进程的原因是因为该进程应该已经执行完毕,或者是因故应该要终止了,但是该进程的父进程却无法完整的将该进程结束掉,而造成那个进程一直存在内存当中。如果你发现在某个进程的 CMD 后面还接上 时,就代表该进程是僵尸进程啦,例如:

apache 8683 0.0 0.9 83384 9992 ? Z 14:33 0:00 /usr/sbin/httpd <defunct>

当系统不稳定的时候就容易造成所谓的僵尸进程,可能是因为程序写的不好啦,或者是使用者的操作习惯不良等等所造成。如果你发现系统中很多僵尸进程时,记得啊!要找出该进程的父进程,然后好好的做个追踪,好好的进行主机的环境优化啊!看看有什么地方需要改善的,不要只是直接将他 kill 掉而已呢!不然的话,万一他一直产生,那可就麻烦了!

事实上,通常僵尸进程都已经无法控管,而直接是交给 systemd 这支程序来负责了,偏偏 systemd 是系统第一支执行的程序,他是所有程序的父程序!我们无法杀掉该程序的 (杀掉他,系统就死了!),所以,如果产生僵尸进程,而系统过一阵子还没有办法透过核心非经常性的特殊处理来将该进程删除时,那你只好透过 reboot 的方式来将该进程抹去了!

动态观察进程的变化:top

相对于 ps 是撷取一个时间点的进程状态, top 则可以持续侦测进程运作的状态!使用方式如下:

[root@study ~]# top [-d 数字] | top [-bnp]
选项与参数:
-d :后面可以接秒数,就是整个进程画面更新的秒数。预设是 5 秒;
-b :以批次的方式执行 top ,还有更多的参数可以使用喔!
    通常会搭配数据流重导向来将批次的结果输出成为文件。
-n :与 -b 搭配,意义是,需要进行几次 top 的输出结果。 
-p :指定某些个 PID 来进行观察监测而已。    
在 top 执行过程当中可以使用的按键指令:
    ? :显示在 top 当中可以输入的按键指令; 
    P :以 CPU 的使用资源排序显示;
    M :以 Memory 的使用资源排序显示;
    N :以 PID 来排序喔!
    T :由该 Process 使用的 CPU 时间累积 (TIME+) 排序。 
    k :给予某个 PID 一个讯号 (signal)
    r :给予某个 PID 重新制订一个 nice 值。 
    q :离开 top 软件的按键。

其实 top 的功能非常多!可以用的按键也非常的多!可以参考 man top 的内部说明文件! 这里仅是列出一些自己常用的选项而已。接下来让我们实际观察一下如何使用 top 与 top 的画面!

#范例一:每两秒钟更新一次 top ,观察整体信息:
[root@i7dom ~]# top -d 2
top - 18:03:23 up 10:30,  2 users,  load average: 0.00, 0.01, 0.05
Tasks: 215 total,   2 running, 213 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1884508 total,   833820 free,   393868 used,   656820 buff/cache
KiB Swap:  1048572 total,  1048572 free,        0 used.  1236248 avail Mem 
        <==如果加入 k 或 r 时,就会有相关的字样出现在这里喔!
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                    
 1462 root      20   0  558384  18564   5864 S  0.5  1.0   0:02.96 tuned                                      
15092 root      20   0  146144   2140   1448 R  0.5  0.1   0:00.04 top                                        
    1 root      20   0  128636   9408   2652 S  0.0  0.5   0:03.18 systemd                                    
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.01 kthreadd   
......

top 也是个挺不错的进程观察工具!但不同于 ps 是静态的结果输出,top 这个程序可以持续的监测整个系统的进程工作状态。在预设的情况下,每次更新进程资源的时间为 5 秒,不过,可以使用 -d 来进行修改。 top 主要分为两个画面,上面的画面为整个系统的资源使用状态,基本上总共有六行,显示的内容依序是:

  • 第一行(top...):这一行显示的信息分别为:

    • 目前的时间,亦即是 00:53:59 那个项目;
    • 开机到目前为止所经过的时间,亦即是 up 6:07, 那个项目;
    • 已经登入系统的用户人数,亦即是 3 users, 项目;
    • 系统在 1, 5, 15 分钟的平均工作负载。我们在第十五章谈到的 batch 工作方式为负载小于 0.8 就是这个负载啰!代表的是 1, 5, 15 分钟,系统平均要负责运作几个进程(工作)的意思。 越小代表系统 越闲置,若高于 1 得要注意你的系统进程是否太过繁复了!
  • 第二行(Tasks...):显示的是目前进程的总量与个别进程在什么状态(running, sleeping, stopped, zombie)。 比较需要注意的是最后的 zombie 那个数值,如果不是 0 !好好看看到底是那个 process 变成僵尸了吧?
  • 第三行(%Cpus...):显示的是 CPU 的整体负载,每个项目可使用 ? 查阅。需要特别注意的是 wa 项目,那个项目代表的是 I/O wait,通常你的系统会变慢都是 I/O 产生的问题比较大!因此这里得要注意这个项目耗用 CPU 的资源喔! 另外,如果是多核心的设备,可以按下数字键『1』来切换成不同 CPU 的负载率。
  • 第四行与第五行:表示目前的物理内存与虚拟内存 (Mem/Swap) 的使用情况。再次重申,要注意的是 swap 的使用量要尽量的少!如果 swap 被用的很大量,表示系统的物理内存实在不足!
  • 第六行:这个是当在 top 程序当中输入指令时,显示状态的地方。
  • 至于 top 下半部分的画面,则是每个 process 使用的资源情况。比较需要注意的是:

    • PID :每个 process 的 ID 啦!
    • USER:该 process 所属的使用者;
    • PR :Priority 的简写,进程的优先执行顺序,越小越早被执行;
    • NI :Nice 的简写,与 Priority 有关,也是越小越早被执行;
    • %CPU:CPU 的使用率;
    • %MEM:内存的使用率;
    • TIME+:CPU 使用时间的累加;

top 预设使用 CPU 使用率 (%CPU) 作为排序的重点,如果你想要使用内存使用率排序,则可以按下『M』,若要回复则按下『P』即可。如果想要离开 top 则按下『 q 』!如果你想要将 top 的结果输出成为文件时,可以这样做:

#范例二:将 top 的信息进行 2 次,然后将结果输出到 /tmp/top.txt
[root@study ~]# top -b -n 2 > /tmp/top.txt
#这样一来,就可以将 top 的信息存到 /tmp/top.txt 文件中了。

这玩意儿很有趣!可以帮助你将某个时段 top 观察到的结果存成文件,可以用在你想要在系统背景底下执行。由于是背景底下执行,与终端机的屏幕大小无关,因此可以得到全部的进程画面!那如果你想要观察的进程 CPU 与内存使用率都很低,结果老是无法在第一行显示时,该怎办?我们可以仅观察单一进程喔!如下所示:

#范例三:我们自己的 bash PID 可由 $$ 变量取得,请使用 top 持续观察该 PID
[root@study ~]# echo $$
14505    <--就是这个数字!他是我们 bash 的 PID

[root@study ~]# top -d 2 -p 14505
top - 18:25:23 up 10:52,  2 users,  load average: 0.00, 0.01, 0.05
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1884508 total,   833840 free,   393804 used,   656864 buff/cache
KiB Swap:  1048572 total,  1048572 free,        0 used.  1236316 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                    
14505 root      20   0  116780   3480   1840 S  0.0  0.2   0:00.05 bash    

看到没!就只会有一支进程给你看!很容易观察吧!好,那么如果我想要在 top 底下进行一些动作呢? 比方说,修改 NI 这个数值呢?可以这样做:

#范例四:承上题,上面的 NI 值是 0 ,想要改成 10 的话?
#在范例三的 top 画面当中直接按下 r 之后,会出现如下的图样!
[root@study ~]# top -d 2 -p 14505
top - 18:30:46 up 10:57,  2 users,  load average: 0.00, 0.01, 0.05
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.1 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1884508 total,   833604 free,   394012 used,   656892 buff/cache
KiB Swap:  1048572 total,  1048572 free,        0 used.  1236088 avail Mem 
PID to renice [default pid = 14505] 14505    
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                    
14505 root      20   0  116780   3480   1840 S  0.0  0.2   0:00.07 bash  

Renice PID 14505 to value 10    <--这是 nice 值
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND 

接下来你就会看到如下的显示画面!

top - 18:32:30 up 10:59,  2 users,  load average: 0.00, 0.01, 0.05
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1884508 total,   833464 free,   394136 used,   656908 buff/cache
KiB Swap:  1048572 total,  1048572 free,        0 used.  1235948 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                    
14505 root      30  10  116780   3480   1840 S  0.0  0.2   0:00.07 bash 

看到不同处了吧?底线的地方就是修改了之后所产生的效果!一般来说,如果鸟哥想要找出最损耗 CPU 资源的那个进程时,大多使用的就是 top 这支程序啦!然后强制以 CPU 使用资源来排序 (在 top 当中按下 P 即可), 就可以很快的知道啦!

pstree

[root@study ~]# pstree [-A|U] [-up]
选项与参数:
-A :各进程树之间的连接以 ASCII 字符来连接;
-U :各进程树之间的连接以万国码的字符来连接。在某些终端接口下可能会有错误; 
-p :并同时列出每个 process 的 PID;
-u :并同时列出每个 process 的所属账号名称。

范例一:列出目前系统上面所有的进程树的相关性:
[root@study ~]# pstree -A
systemd-+-ModemManager---2*[{ModemManager}]        <--这行是 ModenManager 与其子进程
        |-NetworkManager-+-2*[dhclient]            <--前面有数字,代表子进程的数量!
        |                `-2*[{NetworkManager}]
........
        |-sshd---sshd---bash---bash---bash---sudo---su---bash---su---bash---bash---pstree    <--我们指令执行的相依性
........

#范例二:承上题,同时秀出 PID 与 users
[root@study ~]# pstree -Aup
systemd(1)-+-ModemManager(750)-+-{ModemManager}(792)
           |                   `-{ModemManager}(794)
           |-NetworkManager(909)-+-dhclient(1236)
           |                     |-dhclient(1237)
           |                     |-{NetworkManager}(1109)
           |                     `-{NetworkManager}(1112)
    ..........
           |-sshd(1460)---sshd(15692)---bash(15700)---bash(15788)---su(15823)---bash(15824,xiaoqi)---bash(15855)---pstree(16014)
    ..........
    
#在括号 () 内的即是 PID 以及该进程的 owner 喔!一般来说,如果该进程的所有人与父进程同,
#就不会列出,但是如果与父进程不一样,那就会列出该进程的拥有者!看上面 15824 就转变成 xiaoqi 了

如果要找进程之间的相关性,这个 pstree 真是好用到不行!直接输入 pstree 可以查到进程相关性,如上表所示,还会使用线段将相关性进程连结起来哩! 一般链接符号可以使用 ASCII 码即可,但有时因为语系问题会主动的以 Unicode 的符号来链接,但因为可能终端机无法支持该编码,或许会造成乱码问题。因此可以加上 -A 选项来克服此类线段乱码问题。

由 pstree 的输出我们也可以很清楚的知道,所有的进程都是依附在 systemd 这支进程底下的! 仔细看一下,这支进程的 PID 是一号喔!因为他是由 Linux 核心所主动呼叫的第一支程序!所以 PID 就是一号了。这也是我们刚刚提到僵尸进程时有提到,为啥发生僵尸进程需要重新启动? 因为 systemd 要重新启动,而重新启动 systemd 就是 reboot 啰!

如果还想要知道 PID 与所属使用者,加上 -u 及 -p 两个参数即可。我们前面不是一直提到,如果子进程挂点或者是老是砍不掉子进程时,该如何找到父进程吗?呵呵!用这个 pstree 就对了!

16.3.2 进程的管理

进程之间是可以互相控制的!举例来说,你可以关闭、重新启动服务器软件,服务器软件本身是个进程, 你既然可以让她关闭或启动,当然就是可以控制该进程啦!那么进程是如何互相管理的呢?其实是透过给予该进程一个讯号 (signal) 去告知该进程你想要让她作什么!因此这个讯号就很重要啦!

我们也在本章之前的 bash 工作管理当中提到过,要给予某个已经存在背景中的工作某些动作时,是直接给予一个讯号给该工作号码即可。那么到底有多少 signal 呢? 你可以使用 kill -l (小写的 L ) 或者是 man 7 signal 都可以查询到!主要的讯号代号与名称对应及内容是:

代号名称内容
1SIGHUP启动被终止的进程,可让该 PID 重新读取自己的配置文件,类似重新启动
2SIGINT相当于用键盘输入 [ctrl]-c 来中断一个进程的进行
9SIGKILL代表强制中断一个进程的进行,如果该进程进行到一半,那么尚未完成的部分可能会有『半产品』产生,类似 vim 会有 .filename.swp 保留下来。
15SIGTERM以正常的结束进程来终止该进程。由于是正常的终止,所以后续的动作会将他完成。不过,如果该进程已经发生问题,就是无法使用正常的方法终止时,输入这个 signal 也是没有用的.
19SIGTOP相当于用键盘输入 [ctrl]-z 来暂停一个进程的进行

上面仅是常见的 signal 而已,更多的讯号信息请自行 man 7 signal 吧!一般来说,你只要记得『1, 9, 15』这三个号码的意义即可。那么我们如何传送一个讯号给某个进程呢?就透过 kill 或 killall 吧! 底下分别来看看:

kill-signalPID

kill 可以帮我们将这个 signal 传送给某个工作 (%jobnumber) 或者是某个 PID (直接输入数字)。 要再次强调的是: kill 后面直接加数字与加上 %number 的情况是不同的!这个很重要喔!因为工作控制中有 1 号工作,但是 PID 1 号则是专指『 systemd 』这支程序!你怎么可以将 systemd 关闭呢?关闭 systemd ,你的系统就当掉了啊!所以记得那个 % 是专门用在工作控制的喔! 我们就活用一下 kill 与刚刚上面提到的 ps 来做个简单的练习吧!

例题:
以 ps 找出 rsyslogd 这个进程的 PID 后,再使用 kill 传送讯息,使得 rsyslogd 可以重新读取配置文件。

答:
由于需要重新读取配置文件,因此 signal 是 1 号。至于找出 rsyslogd 的 PID 可以是这样做:
ps aux | grep 'rsyslogd' | grep -v 'grep'| awk '{print $2}'
接下来则是实际使用 kill -1 PID,因此,整串指令会是这样:
kill -SIGHUP $(ps aux | grep 'rsyslogd' | grep -v 'grep'| awk '{print $2}')
如果要确认有没有重新启动 syslog ,可以参考登录档的内容,使用如下指令查阅:
tail -5 /var/log/messages
如果你有看到类似:[ Oct 21 19:00:12 study rsyslogd: [origin software="rsyslogd" swVersion="7.4.7" x-pid="727" x-info="http://www.rsyslog.com"] rsyslogd was HUPed ]之类的字样,就是表示 rsyslogd 在 10/21 有重新启动 (restart) 过了!

了解了这个用法以后,如果未来你想要将某个莫名其妙的登入者的联机删除的话,就可以透过使用 pstree -p 找到相关进程, 然后再以 kill -9 将该进程删除,该条联机就会被踢掉了!这样很简单吧!

killall -signal 指令名称

由于 kill 后面必须要加上 PID (或者是 job number),所以,通常 kill 都会配合 ps, pstree 等指令,因为我们必须要找到相对应的那个进程的 ID 嘛!但是,如此一来,很麻烦~有没有可以利用『下达指令的名称』来给予讯号的?举例来说,能不能直接将 rsyslogd 这个进程给予一个 SIGHUP 的讯号呢? 可以的!用 killall 吧!

[root@study ~]# killall [-iIe] [command name]
选项与参数:
-i :interactive 的意思,交互式的,若需要删除时,会出现提示字符给用户; 
-e :exact 的意思,表示『后面接的 command name 要一致』,但整个完整的指令不能超过 15 个字符。
-I :指令名称(可能含参数)忽略大小写。

#范例一:给予 rsyslogd 这个指令启动的 PID 一个 SIGHUP 的讯号
[root@study ~]# killall -1 rsyslogd
如果用 ps aux 仔细看一下,若包含所有参数,则 /usr/sbin/rsyslogd -n 才是最完整的!


#范例二:强制终止所有以 httpd 启动的进程 (其实并没有此进程在系统内)
[root@study ~]# killall -9 httpd
httpd: no process found


#范例三:依次询问每个 bash 程序是否需要被终止运作!
[root@study ~]# killall -i -9 bash
Signal bash(13888) ? (y/N) n <==这个不杀!
Signal bash(13928) ? (y/N) n <==这个不杀!
Signal bash(13970) ? (y/N) n <==这个不杀!
Signal bash(14836) ? (y/N) y <==这个杀掉!
#具有互动的功能!可以询问你是否要删除 bash 这个程序。要注意,若没有 -i 的参数, 
#所有的 bash 都会被这个 root 给杀掉!包括 root 自己的 bash 喔! 

总之,要删除某个进程,我们可以使用 PID 或者是启动该进程的指令名称,而如果要删除某个服务呢?最简单的方法就是利用 killall ,因为他可以将系统当中所有以某个指令名称启动的进程全部删除。举例来说,上面的范例二当中,系统内所有以 httpd 启动的进程,就会通通的被删除啦!

16.3.3 于进程的执行顺序

我们知道 Linux 是多人多任务的环境,由 top 的输出结果我们也发现,系统同时间有非常多的进程在运行中,只是绝大部分的进程都在休眠 (sleeping) 状态而已。想一想,如果所有的进程同时被唤醒,那么 CPU 应该要先处理那个进程呢?也就是说,那个进程被执行的优先序比较高? 这就得要考虑到进程的优先执行序 (Priority) 与 CPU 排程啰!

CPU 排程与前一章的例行性工作排程并不一样。 CPU 排程指的是每支进程被 CPU运作的演算规则,而例行性工作排程则是将某支程序安排在某个时间再交由系统执行。CPU 排程与操作系统较具有相关性!

Priority 与 Nice 值

我们知道 CPU 一秒钟可以运作多达数 G 的微指令次数,透过核心的 CPU 排程可以让各进程被 CPU 所切换运作,因此每个进程在一秒钟内或多或少都会被 CPU 执行部分的脚本。如果进程都是集中在一个队列中等待 CPU 的运作,而不具有优先级之分,也就是像我们去游乐场玩热门游戏需要排队一样,每个人都是照顺序来!你玩过一遍后还想再玩 (没有执行完毕),请到后面继续排队等待。情况有点像底下这样:

上图中假设 pro1, pro2 是紧急的进程, pro3, pro4 是一般的进程,在这样的环境中,由于不具有优先级!pro1, pro2 还是得要继续等待而没有优待呢!如果 pro3, pro4 的工作又臭又长!那么 紧急的 pro1, pro2 就得要等待个老半天才能够完成!真麻烦啊!所以,我们想要将进程分优先级啦!如果优先序较高则运作次数可以较多次,而不需要与较慢优先的进程抢位置!我们可以将进程的优先级与 CPU 排程进行如下图的解释:

如上图所示,具高优先权的 pro1, pro2 可以被取用两次,而较不重要的 pro3, pro4 则运作次数较少。如此一来 pro1, pro2 就可以较快被完成啦!要注意,上图仅是示意图,并非较优先者一定会被运作两次啦! 为了要达到上述的功能,我们 Linux 给予进程一个所谓的『优先执行序 (priority, PRI)』,这个 PRI 值越低代表越优先的意思。不过这个 PRI 值是由核心动态调整的,用户无法直接调整 PRI 值的。先来瞧瞧 PRI 曾在哪里出现?

[root@study ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 15700 15692  0  90  10 - 29160 wait   pts/0    00:00:00 bash
0 R     0 17905 15700  0  90  10 - 34343 -      pts/0    00:00:00 ps
#你应该要好奇,怎么我的 NI 已经是 10 了?还记得刚刚 top 的测试吗?我们在那边就有改过一次喔!

由于 PRI 是核心动态调整的,我们用户也无权去干涉 PRI !那如果你想要调整进程的优先执行序时,就得要透过 Nice 值了!Nice 值就是上表的 NI 啦!一般来说,PRI 与 NI 的相关性如下:

  • PRI(new) = PRI(old) + nice

不过你要特别留意到,如果原本的 PRI 是 50 ,并不是我们给予一个 nice = 5 ,就会让 PRI 变成 55 喔! 因为 PRI 是系统『动态』决定的,所以,虽然 nice 值是可以影响 PRI ,不过, 最终的 PRI 仍是要经过系统分析后才会决定的。另外, nice 值是有正负的喔,而既然 PRI 越小越早被执行,所以,当 nice 值为负值时,那么该进程就会降低 PRI 值,亦即会变的较优先被处理。此外,你必须要留意到:

  • nice 值可调整的范围为 -20 ~ 19 ;
  • root 可随意调整自己或他人进程的 Nice 值,且范围为 -20 ~ 19 ;
  • 一般使用者仅可调整自己进程的 Nice 值,且范围仅为 0 ~ 19 (避免一般用户抢占系统资源);
  • 一般使用者仅可将 nice 值越调越高,例如本来 nice 为 5 ,则未来仅能调整到大于 5;

这也就是说,要调整某个进程的优先执行序,就是『调整该进程的 nice 值』啦!那么如何给予某个进程 nice 值呢?有两种方式,分别是:

  • 一开始执行程序就立即给予一个特定的 nice 值:用 nice 指令;
  • 调整某个已经存在的 PID 的 nice 值:用 renice 指令。

新执行的指令即给予新的nice值:nice

[root@study ~]# nice [-n 数字] command
选项与参数:
-n :后面接一个数值,数值的范围 -20~19.


#范例一:用 root 给一个 nice 值为 -5 ,用于执行 vim ,并观察该进程!
[root@study ~]# nice -n -5 vim &
[1] 17955
[root@study ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 15700 15692  0  90  10 - 29160 wait   pts/0    00:00:00 bash
4 T     0 17955 15700  1  85   5 - 37896 signal pts/0    00:00:00 vim
0 R     0 17956 15700  0  90  10 - 34343 -      pts/0    00:00:00 ps

[1]+  已停止               nice -n -5 vim
[root@study ~]# 
#原本的 bash PRI 为 90 ,所以 vim 预设应为 90。不过由于给予 nice 为 -5 ,
#因此 vim 的 PRI 降低了!RPI 与 NI 各减 5 !但不一定每次都是正好相同喔!因为核心会动态调整


[root@study ~]# kill -9 %1 <==测试完毕将 vim 关闭

就如同前面说的, nice 是用来调整进程的执行优先级!这里只是一个执行的范例罢了! 通常什么时候要将 nice 值调大呢?举例来说,系统的背景工作中,某些比较不重要的进程之进行:例如备份工作!由于备份工作相当的耗系统资源 这个时候就可以将备份的指令之 nice 值调大一些,可以使系统的资源分配的更为公平!

已存在进程的 nice 重新调整:renice

[root@study ~]# renice [number] PID
选项与参数:
PID :某个进程的 ID 啊!


#范例一:找出自己的 bash PID ,并将该 PID 的 nice 调整到 -5
[root@study ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 15700 15692  0  90  10 - 29191 wait   pts/0    00:00:00 bash
0 R     0 18172 15700  0  90  10 - 34343 -      pts/0    00:00:00 ps


[root@study ~]# renice -5 15700
15700 (进程 ID) 旧优先级为 10,新优先级为 -5


[root@study ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 15700 15692  0  75  -5 - 29191 wait   pts/0    00:00:00 bash
0 R     0 18183 15700  0  75  -5 - 34343 -      pts/0    00:00:00 ps

如果要调整的是已经存在的某个进程的话,那么就得要使用 renice 了。使用的方法很简单,renice 后面接上数值及 PID 即可。因为后面接的是 PID ,所以你务必要以 ps或者其他进程观察的指令去找出 PID 才行啊!

由上面这个范例当中我们也看的出来,虽然修改的是 bash 那个进程,但是该进程所触发的 ps 指令当中的 nice 也会继承而为 -5 !整个 nice 值是可以在父进程 --> 子进程之间传递的呢! 另外,除了 renice 之外,其实那个 top 同样的也是可以调整 nice 值的!

16.3.4 系统资源的观察

除了系统的进程之外,我们还必须就系统的一些资源进行检查啊!举例来说,我们使用 top 可以看到很多系统的资源!那么,还有没有其他的工具可以查阅的? 当然有啊!底下这些工具指令可以玩一玩!

观察内存使用情况:free

[root@study ~]# free [-b|-k|-m|-g|-h] [-t] [-s N -c N]
选项与参数:
-b :直接输入 free 时,显示的单位是 Kbytes,我们可以使用 b(bytes), m(Mbytes)
k(Kbytes), 及 g(Gbytes) 来显示单位喔!也可以直接让系统自己指定单位 (-h) 
-t :在输出的最终结果,显示物理内存与 swap 的总量。
-s :可以让系统每几秒钟输出一次,不间断的一直输出的意思!对于系统观察挺有效! 
-c :与 -s 同时处理~让 free 列出几次的意思~


#范例一:显示目前系统的内存容量
[root@study ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:           1840         360         837           8         642        1231
Swap:          1023           0        1023

仔细看看,我的系统当中有 2848MB 左右的物理内存,我的 swap 有 1GB 左右,那我使用 free -m 以 MBytes 来显示时,就会出现上面的信息。Mem 那一行显示的是物理内存的量,Swap 则是内存 置换空间的量。 total 是总量,used 是已被使用的量,free 则是剩余可用的量。后面的 shared/buffers/cached 则是在已被使用的量当中,用来作为缓冲及快取的量,这些 shared/buffers/cached 的用量中,在系统比较忙碌时,可以被释出而继续利用!因此后面就有一个 available (可用的) 数值!。

请看上头范例一的输出,我们可以发现这部测试机根本没有什么特别的服务,但是竟然有 642MB 左右的 cache ! 因为在测试过程中还是有读/写/执行很多的文件嘛!这些文件就会被系统暂时快取下来,等待下次运作时可以更快速的取出之意!也就是说,系统是『很有效率的将所有的内存用光光』,目的是为了让系统的存取效能加速啦!

很多朋友都会问到这个问题『我的系统明明很轻松,为何内存会被用光光?』现在瞭了吧? 被用光是正常的!而需要注意的反而是 swap 的量。一般来说,swap 最好不要被使用,尤其 swap 最好不要被使用超过 20% 以上,如果您发现 swap 的用量超过 20% ,那么,最好还是增加物理内存! 因为,Swap 的效能跟物理内存实在差很多,而系统会使用到 swap ,绝对是因为物理内存不足了才会这样做的!

Linux 系统为了要加速系统效能,所以会将最常使用到的或者是最近使用到的文件数据缓存到 (cache)中, 这样未来系统要使用该文件时,就直接由内存中搜寻取出,而不需要重新读取硬盘,速度上面当然就加快了! 因此,物理内存被用光是正常的喔!

查阅系统与核心相关信息:uname

[root@study ~]# uname [-asrmpi]
选项与参数:
-a :所有系统相关的信息,包括底下的数据都会被列出来; 
-s :系统核心名称
-r :核心的版本
-m :本系统的硬件名称,例如 i686 或 x86_64 等;
-p :CPU 的类型,与 -m 类似,只是显示的是 CPU 的类型! 
-i :硬件的平台 (ix86)

#范例一:输出系统的基本信息
[root@study ~]# uname  -a
Linux study.centos.xiaoqi 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

这个咚咚我们前面使用过很多次了喔!uname 可以列出目前系统的核心版本、主要硬件平台以及 CPU 类型等等的信息。以上面范例一的状态来说,我的 Linux 主机使用的核心名称为 Linux,而主机名为 study.centos.xiaoqi,核心的版本为 3.10.0-327.el7.x86_64 ,该核心版本建立的日期为 2015-3-6,适用的硬件平台为 x86_64 以上等级的硬件平台喔。

观察系统启动时间与工作负载:uptime

这个指令很单纯呢!就是显示出目前系统已经开机多久的时间,以及 1, 5, 15 分钟的平均负载就是了。还记得 top 吧?这个 uptime 可以显示出 top 画面的最上面一行!

[root@study ~]# uptime 
 21:18:12 up 1 day, 13:45,  2 users,  load average: 0.00, 0.01, 0.05

追踪网络或插槽文件:netstat

这个 netstat 也是挺好玩的,其实这个指令比较常被用在网络的监控方面,不过,在进程管理方面也是需要了解的啦! 这个指令的执行如下所示:基本上,netstat 的输出分为两大部分,分别是网络与系统自己的进程相关性部分:

[root@study ~]# netstat -[atunlp]
选项与参数:
-a :将目前系统上所有的联机、监听、Socket 数据都列出来 
-t :列出 tcp 网络封包的数据
-u :列出 udp 网络封包的数据
-n :不以进程的服务名称,以埠号 (port number) 来显示; 
-l :列出目前正在网络监听 (listen) 的服务;
-p :列出该网络服务的进程 PID


#范例一:列出目前系统已经建立的网络联机与 unix socket 状态
[root@study ~]# netstat 
Active Internet connections (w/o servers)    <--与网络较相关的部分
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0    208 study.lan:ssh           182.101.158.98:53192    ESTABLISHED
Active UNIX domain sockets (w/o servers)    <--与本机的进程自己的相关性(非网络)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  5      [ ]         DGRAM                    6915     /run/systemd/journal/socket
unix  24     [ ]         DGRAM                    6917     /dev/log
unix  2      [ ]         DGRAM                    11871    /run/systemd/shutdownd
unix  2      [ ]         DGRAM                    6895     /run/systemd/notify
unix  2      [ ]         DGRAM                    25625    
unix  3      [ ]         STREAM     CONNECTED     23822    
......
  • 在上面的结果当中,显示了两个部分,分别是网络的联机以及 linux 上面的 socket 进程相关性部分。 我们先来看看因特网联机情况的部分:

    • Proto :网络的封包协议,主要分为 TCP 与 UDP 封包,相关资料请参考服务器篇;
    • Recv-Q:非由用户程序链接到此 socket 的复制的总 bytes 数;
    • Send-Q:非由远程主机传送过来的 acknowledged 总 bytes 数;
    • Local Address :本地端的 IP:port 情况
    • Foreign Address:远程主机的 IP:port 情况
    • State :联机状态,主要有建立(ESTABLISED)及监听(LISTEN);

我们看上面仅有一条联机的数据,他的意义是:『透过 TCP 封包的联机,远程的 182.101.158.98:53192 联机到本地端的 study.lan:ssh,这条联机状态是建立 (ESTABLISHED) 的状态!』

除了网络上的联机之外,其实 Linux 系统上面的进程是可以接收不同进程所发送来的信息,那就是 Linux 上头的插槽档 (socket file)。我们在第五章的文件种类有稍微提到socket 文件, 但当时未谈到进程的概念,所以没有深入谈论。socket file 可以沟通两个进程之间的信息,因此进程可以取得对方传送过来的资料。由于有 socket file,因此类似 X Window 这种需要透过网络连接的软件,目前新版的 distributions 就以 socket 来进行窗口接口的联机沟通了。上表中 socket file 的输出字段有:

  • Proto :一般就是 unix 啦;
  • RefCnt:连接到此 socket 的进程数量;
  • Flags :联机的旗标;
  • Type :socket 存取的类型。主要有确认联机的 STREAM 与不需确认的 DGRAM 两种;
  • State :若为 CONNECTED 表示多个进程之间已经联机建立。
  • Path :连接到此 socket 的相关程序的路径!或者是相关数据输出的路径。

以上表的输出为例,最后那三行在 /tmp/.xx 底下的数据,就是 X Window 窗口接口的相关进程啦! 而 PATH 指向的就是这些进程要交换数据的插槽文件啰!好!那么 netstat 可以帮我们进行什么任务呢?我们先来看看,利用 netstat 去看看我们的哪些进程有启动哪些网络的『后门』呢?

#范例二:找出目前系统上已在监听的网络联机及其 PID
[root@study ~]# netstat -tulnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN      2749/dnsmasq        
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1460/sshd           
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      1459/cupsd          
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      2626/master         
tcp6       0      0 :::22                   :::*                    LISTEN      1460/sshd           
tcp6       0      0 ::1:631                 :::*                    LISTEN      1459/cupsd          
tcp6       0      0 ::1:25                  :::*                    LISTEN      2626/master         
udp        0      0 0.0.0.0:54289           0.0.0.0:*                           1237/dhclient       
udp        0      0 192.168.122.1:53        0.0.0.0:*                           2749/dnsmasq        
udp        0      0 0.0.0.0:67              0.0.0.0:*                           2749/dnsmasq        
udp        0      0 0.0.0.0:68              0.0.0.0:*                           1236/dhclient       
udp        0      0 0.0.0.0:40159           0.0.0.0:*                           766/avahi-daemon: r 
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           766/avahi-daemon: r 
udp        0      0 127.0.0.1:323           0.0.0.0:*                           782/chronyd         
udp        0      0 0.0.0.0:59012           0.0.0.0:*                           1236/dhclient       
udp6       0      0 :::25408                :::*                                1236/dhclient       
udp6       0      0 ::1:323                 :::*                                782/chronyd         
udp6       0      0 fe80::20c:29ff:fe61:546 :::*                                1237/dhclient       
udp6       0      0 :::59012                :::*                                1237/dhclient 
#除了可以列出监听网络的接口与状态之外,最后一个字段还能够显示此服务的
#PID 号码以及进程的指令名称喔!例如上头的 1460 就是该 PID


#范例三:将上述的 0.0.0.0:57808 那个网络服务关闭的话?
[root@study ~]# kill -9 766
[root@study ~]# killall -9 avahi-daemon

分析核心产生的讯息:dmesg

系统在开机的时候,核心会去侦测系统的硬件,你的某些硬件到底有没有被捉到,那就与这个时候的侦测有关。但是这些侦测的过程要不是没有显示在屏幕上,就是很飞快的在屏幕上一闪而逝!能不能把核心侦测的讯息捉出来瞧瞧? 可以的,那就使用 dmesg 吧!

所有核心侦测的讯息,不管是开机时候还是系统运作过程中,反正只要是核心产生的讯息,都会被记录到内存中的某个保护区段。dmesg 这个指令就能够将该区段的讯息读出来的!因为讯息实在太多了,所以执行时可以加入这个管线指令『 | more 』来使画面暂停!

#范例一:输出所有的核心开机时的信息
[root@study ~]# dmesg | more

#范例二:搜寻开机的时候,硬盘的相关信息为何?
[root@study ~]# dmesg | grep -i 'sda'
[    1.447609] sd 0:0:0:0: [sda] 83886080 512-byte logical blocks: (42.9 GB/40.0 GiB)
[    1.447689] sd 0:0:0:0: [sda] Write Protect is off
[    1.447691] sd 0:0:0:0: [sda] Mode Sense: 61 00 00 00
[    1.447708] sd 0:0:0:0: [sda] Cache data unavailable
[    1.447710] sd 0:0:0:0: [sda] Assuming drive cache: write through
[    1.668186]  sda: sda1 sda2 sda3
[    1.668646] sd 0:0:0:0: [sda] Attached SCSI disk
[  102.593405] XFS (sda2): Mounting V4 Filesystem
[  104.871425] XFS (sda2): Ending clean mount

侦测系统资源变化:vmstat

[root@study ~]# vmstat [-a] [延迟 [总计侦测次数]]     <---CPU/内存等信息
[root@study ~]# vmstat [-fs]                    <---内存相关
[root@study ~]# vmstat [-S 单位]                    <---设定显示数据的单位
[root@study ~]# vmstat [-d]                        <---与磁盘有关
[root@study ~]# vmstat [-p 分区槽] 选项与参数:        <---与磁盘有关
选项与参数:
-a :使用 inactive/active(活跃与否) 取代 buffer/cache 的内存输出信息; 
-f :开机到目前为止,系统复制 (fork) 的进程数;
-s :将一些事件 (开机至目前为止) 导致的内存变化情况列表说明;
-S :后面可以接单位,让显示的数据有单位。例如 K/M 取代 bytes 的容量; 
-d :列出磁盘的读写总量统计表
-p :后面列出分区槽,可显示该分区槽的读写总量统计表


#范例一:统计目前主机 CPU 状态,每秒一次,共计三次!
[root@study ~]# vmstat 1 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 858000   1260 656244    0    0     3     1   26   41  0  0 100  0  0
 0  0      0 858000   1260 656244    0    0     0     0   29   35  0  0 100  0  0
 0  0      0 858000   1260 656244    0    0     0     0   30   40  0  0 100  0  0

利用 vmstat 甚至可以进行追踪喔!你可以使用类似『 vmstat 5 』代表每五秒钟更新一次,且无穷的更新!直到你按下 [ctrl]-c 为止。如果你想要实时的知道系统资源的运作状态,这个指令就不能不知道!那么上面的表格各项字段的意义为何? 基本说明如下:

  • 进程字段 (procs) 的项目分别为:

    • r :等待运作中的进程数量;
    • b:不可被唤醒的进程数量。这两个项目越多,代表系统越忙碌 (因为系统太忙,所以很多进程就无法被执行或一直在等待而无法被唤醒之故)。
  • 内存字段 (memory) 项目分别为:

    • swpd:虚拟内存被使用的容量;
    • free:未被使用的内存容量;
    • buff:用于缓冲存储器;
    • cache:用于高速缓存。 这部份则与 free 是相同的。
  • 内存置换空间 (swap) 的项目分别为:

    • si:由磁盘中将进程取出的量;
    • so:由于内存不足而将没用到的进程写入到磁盘的 swap 的容量。如果 si/so 的数值太大,表示内存内的数据常常得在磁盘与主存储器之间传来传去,系统效能会很差!
  • 磁盘读写 (io) 的项目分别为:

    • bi:由磁盘读入的区块数量;
    • bo:写入到磁盘去的区块数量。如果这部份的值越高,代表系统的 I/O 非常忙碌!
  • 系统 (system) 的项目分别为:

    • in:每秒被中断的进程次数;
    • cs:每秒钟进行的事件切换次数;这两个数值越大,代表系统与接口设备的沟通非常频繁! 这些接口设备当然包括磁盘、网络卡、时间钟等。
  • CPU 的项目分别为:

    • us:非核心层的 CPU 使用状态;
    • sy:核心层所使用的 CPU 状态;
    • id:闲置的状态;
    • wa:等待 I/O 所 耗费的 CPU 状态;
    • st:被虚拟机 (virtual machine) 所盗用的 CPU 使用状态 (2.6.11 以后才支持)。

由于我的机器是测试机,所以并没有什么 I/O 或者是 CPU 忙碌的情况。如果改天你的服务器非常忙碌时,记得使用 vmstat 去看看,到底是哪个部分的资源被使用的最为频繁!一般来说,如果 I/O部分很忙碌的话,你的系统会变的非常慢! 让我们再来看看,那么磁盘的部分该如何观察:

#范例二:系统上面所有的磁盘的读写状态
[root@i7dom ~]# vmstat -d
disk- ------------reads------------ ------------writes----------- -----IO------
       total merged sectors      ms  total merged sectors      ms    cur    sec
vda   171489   2775 11962130 2868497 38562337 32013083 634678104 328491698      0  32413
sr0      109      0    1908     166      0      0       0       0      0      0
loop0      0      0       0       0      0      0       0       0      0      0

16.4 特殊文件与进程

我们在第六章曾经谈到特殊权限的 SUID/SGID/SBIT ,虽然第六章已经将这三种特殊权限作了详细的解释,不过,我们依旧要来探讨的是,那么到底这些权限对于你的『进程』是如何影响的? 此外,进程可能会使用到系统资源,举例来说,磁盘就是其中一项资源。哪天你在 umount 磁盘时,系统老是出现『 device is busy 』的字样~到底是怎么回事啊?我们底下就来谈一谈这些和进程有关系的细节部分:

16.4.1 具有SUID/SGID权限的指令状态

SUID 的权限其实与进程的相关性非常的大!为什么呢?先来看看 SUID 的程序是如何被一般用户执行,且具有什么特色呢?

  • SUID 权限仅对二进制程序(binary program)有效;
  • 执行者对于该程序需要具有 x 的可执行权限;
  • 本权限仅在执行该程序的过程中有效 (run-time);
  • 执行者将具有该程序拥有者 (owner) 的权限。

所以说,整个 SUID 的权限会生效是由于『具有该权限的程序被触发』,而我们知道一个程序被触发会变成进程,所以啰,执行者可以具有程序拥有者的权限就是在该程序变成进程的那个时候! 第六章我们还没谈到进程的概念,所以你或许那时候会觉得很奇怪,为啥执行了 passwd 后你就具有 root 的权限呢?不都是一般使用者执行的吗? 这是因为你在触发 passwd 后,会取得一个新的进 程与 PID,该 PID 产生时透过 SUID 来给予该 PID 特殊的权限设定啦!我们使用 dmtsai 登入系统且执行 passwd 后,透过工作控制来理解一下!

[xiaoqi@i7dom ~]$ ls
[xiaoqi@i7dom ~]$ passwd 
Changing password for user xiaoqi.
Changing password for xiaoqi.
(current) UNIX password:     <-这里按下 [ctrl]-z 并且按下 [enter]

[1]+  Stopped                 passwd

[xiaoqi@i7dom ~]$ pstree -uA
systemd-+-YDLive---{YDLive}
......
        |-sshd---sshd---sshd(xiaoqi)---bash-+-passwd(root)
        |                                   `-pstree
......

从上表的结果我们可以发现,底线的部分是属于 xiaoqi 这个一般账号的权限,特殊字体的则是 root 的权限! 但你看到了 passwd 确实是由 bash 衍生出来的!不过就是权限不一样!透过这样的解析 你也会比较清楚为何不同程序所产生的权限不同了吧!这是由于『SUID 程序运作过程中产生的进程』的关系!

那么既然 SUID/SGID 的权限是比较可怕的,您该如何查询整个系统的 SUID/SGID 的文件呢?使用 find 即可!

[root@study ~]# find / -perm /6000

16.4.2 /proc/* 代表的意义

其实,我们之前提到的所谓的进程都是在内存当中!而内存当中的数据又都是写入到 /proc/* 这个目录下的,所以啰,我们当然可以直接观察 /proc 这个目录当中的文件啊! 如果你观察过 /proc 这个目录的话,应该会发现他有点像这样:

[root@study ~]# ll /proc/
总用量 0
dr-xr-xr-x.  9 root           root                         0 10月 23 10:50 1
dr-xr-xr-x.  9 root           root                         0 10月 23 10:50 10
dr-xr-xr-x.  9 root           root                         0 10月 23 10:50 11
....
dr-xr-xr-x.  4 root           root                         0 10月 23 22:03 tty
-r--r--r--.  1 root           root                         0 10月 23 22:03 uptime
-r--r--r--.  1 root           root                         0 10月 23 22:03 version
-r--------.  1 root           root                         0 10月 23 22:03 vmallocinfo
-r--r--r--.  1 root           root                         0 10月 23 10:51 vmstat
-r--r--r--.  1 root           root                         0 10月 23 22:03 zoneinfo

基本上,目前主机上面的各个进程的 PID 都是以目录的型态存在于 /proc 当中。 举例来说,我们开机所执行的第一支程序 systemd 他的 PID 是 1 ,这个 PID 的所有相关信息都写入在 /proc/1/* 当中!若我们直接观察 PID 为 1 的数据好了,他有点像这样:

[root@study ~]# ll /proc/1/
总用量 0
dr-xr-xr-x. 2 root root 0 10月 23 18:32 attr
-rw-r--r--. 1 root root 0 10月 23 18:32 autogroup
-r--------. 1 root root 0 10月 23 18:32 auxv
-r--r--r--. 1 root root 0 10月 23 15:06 cgroup
--w-------. 1 root root 0 10月 23 18:32 clear_refs
-r--r--r--. 1 root root 0 10月 23 15:06 cmdline
-rw-r--r--. 1 root root 0 10月 23 15:06 comm
-rw-r--r--. 1 root root 0 10月 23 18:32 coredump_filter
-r--r--r--. 1 root root 0 10月 23 18:32 cpuset
lrwxrwxrwx. 1 root root 0 10月 23 18:32 cwd -> /
-r--------. 1 root root 0 10月 23 15:06 environ
lrwxrwxrwx. 1 root root 0 10月 23 15:06 exe -> /usr/lib/systemd/systemd
....
里面的数据还挺多的,不过,比较有趣的其实是两个文件,分别是:
  • cmdline:这个进程被启动的指令串;
  • environ:这个进程的环境变量内容。

如果你查阅一下 cmdline 的话,就会发现:

[root@study ~]# cat /proc/1/cmdline 
/usr/lib/systemd/systemd--system--deserialize28

就是这个指令、选项与参数启动 systemd 的!这还是跟某个特定的 PID 有关的内容呢,如果是针对整个 Linux 系统相关的参数呢?那就是在 /proc 目录底下的文件啦!相关的文件与对应的内容是这样的:

文件名文件内容
/proc/cmdline加载 kernel 时所下达的相关指令与参数!查阅此文件,可了解指令是如何启动的!
/proc/cpuinfo本机的 CPU 的相关信息,包含频率、类型与运算功能等
/proc/devices这个文件记录了系统各个主要装置的主要装置代号,与 mknod 有关!
/proc/filesystems目前系统已经加载的文件系统!
/proc/interrupts目前系统上面的 IRQ 分配状态。
/proc/ioports目前系统上面各个装置所配置的 I/O 地址。
/proc/kcore这个就是内存的大小啦!好大对吧!但是不要读他啦!
/proc/loadavg还记得 top 以及 uptime 吧?没错!上头的三个平均数值就是记录在此!
/proc/meminfo使用 free 列出的内存信息,嘿嘿!在这里也能够查阅到!
/proc/modules目前我们的 Linux 已经加载的模块列表,也可以想成是驱动程序!
/proc/mounts系统已经挂载的数据,就是用 mount 这个指令呼叫出来的数据!
/proc/swaps到底系统挂加载的内存在哪里?呵呵!使用掉的 partition 就记录在此!
/proc/partitions使用 fdisk -l 会出现目前所有的 partition 吧?在这个文件当中也有纪录!
/proc/uptime就是用 uptime 的时候,会出现的信息!
/proc/version核心的版本,就是用 uname -a 显示的内容!

其实,上面这些文件建议您可以使用 cat 去查阅看看,不必深入了解,不过,观看过文件内容后,毕竟会比较有感觉啦!如果未来您想要自行撰写某些工具软件,那么这个目录底下的相关文件可能会对您有点帮助的喔!

16.4.3 查询已开启文件或已执行进程开启之文件

其实还有一些与进程相关的指令可以值得参考与应用的,我们来谈一谈:

藉由文件(或文件系统)找出正在使用该文件的进程:fuser

有的时候我想要知道我的进程到底在这次启动过程中开启了多少文件,可以利用 fuser 来观察啦!举例来说,你如果卸除时发现系统通知:『 device is busy 』,那表示这个文件系统正在忙碌中,表示有某支进程有利用到该文件系统啦!那么你就可以利用 fuser 来追踪啰!fuser 语法有点像这样:

[root@study ~]# fuser [-umv] [-k [i] [-signal]] file/dir
选项与参数:
-u :除了进程的 PID 之外,同时列出该进程的拥有者;
-m :后面接的那个档名会主动的上提到该文件系统的最顶层,对 umount 不成功很有效! 
-v :可以列出每个文件与进程还有指令的完整相关性!
-k :找出使用该文件/目录的 PID ,并试图以 SIGKILL 这个讯号给予该 PID; 
-i :必须与 -k 配合,在删除 PID 之前会先询问使用者意愿!
-signal:例如 -1 -15 等等,若不加的话,预设是 SIGKILL (-9) 啰!


#范例一:找出目前所在目录的使用 PID/所属账号/权限为何?
[root@study ~]# fuser -uv .
                     用户     进程号 权限   命令
/root:               root      26524 ..c.. (root)bash
                     root      26574 ..c.. (root)bash
                     root      31730 ..c.. (root)bash

看到输出的结果没?他说『.』底下有两个 PID 分别为 26524, 26574 的进程,该进程属于 root 且指令为 bash 。 比较有趣的是那个 ACCESS 的项目,那个项目代表的意义为:

  • c :此进程在当前的目录下(非次目录);
  • e :可被触发为执行状态;
  • f :是一个被开启的文件;
  • r :代表顶层目录 (root directory);
  • F :该文件被开启了,不过在等待回应中;
  • m :可能为分享的动态函式库;

那如果你想要查阅某个文件系统底下有多少进程正在占用该文件系统时,那个 -m 的选项就很有帮助了!让我们来做几个简单的测试,包括实体的文件系统挂载与 /proc 这个虚拟文件系统的内容,看看有多少的进程对这些挂载点或其他目录的使用状态吧!

#范例二:找到所有使用到 /proc 这个文件系统的进程吧!
[root@study ~]# fuser -uv /proc/
                     用户     进程号 权限   命令
/proc:               root     kernel mount (root)/proc
                     rtkit       745 .rc.. (rtkit)rtkit-daemon
#数据量还不会很多,虽然这个目录很繁忙~没关系!我们可以继续这样作,看看其他的进程!


[root@study ~]# fuser -muv /proc/
                     用户     进程号 权限   命令
/proc:               root     kernel mount (root)/proc
                     root          1 f.... (root)systemd
                     root          2 ...e. (root)kthreadd
                     root          3 ...e. (root)ksoftirqd/0
                     root          7 ...e. (root)migration/0
......
#有这几支进程在进行 /proc 文件系统的存取喔!


#范例三:找到所有使用到 /home 这个文件系统的进程!
[root@study ~]# echo $$
26524        <--先确认一下,自己的 bash PID 号!
[root@study ~]# cd /home/
[root@study home]# fuser -muv .
                     用户     进程号 权限   命令
/home:               root     kernel mount (root)/home
                     root      26524 ..c.. (root)bash    <--果然是自己的PID


[root@study ~]# cd ~
[root@study ~]# umount /home
umount: /home: target is busy.
        (In some cases useful info about processes that use 
        the device is found by lsof(8) or fuser(1))

#从 fuser 的结果可以知道,总共有五只 process 在该目录下运作,那即使 root 离开了 /home,
#当然还是无法 umount 的!那要怎办?可以透过如下方法一个一个删除~

[root@study home]# fuser -mki /home/
/home:               26524c        <--你会发现, PID 跟上面查到的相同!
杀死进程 26524 ? (y/N)     <--这里会问你要不要删除!当然不要乱删除,选择取消!

既然可以针对整个文件系统,那么能不能仅针对单一文件啊?当然可以啰!看一下底下的案例先:

#范例四:找到 /run 底下属于 FIFO 类型的文件,并且找出存取该文件的进程
[root@study home]# find /run/ -type p
/run/dmeventd-client
/run/dmeventd-server
......
/run/systemd/sessions/178.ref
/run/systemd/sessions/177.ref
/run/systemd/sessions/66.ref
/run/systemd/sessions/c1.ref    <--测试这个
/run/systemd/initctl/fifo


[root@study home]# fuser -uv /run/systemd/sessions/c1.ref 
                     用户     进程号 权限   命令
/run/systemd/sessions/c1.ref:
                     root        729 f.... (root)systemd-logind
                     root       2793 F.... (root)gdm-session-wor
#通常系统的 FIFO 文件都会放置到 /run 底下,透过这个方式来追踪该文件被存取的 process! 
#也能够晓得系统有多忙碌!

列出被进程所开启的文件档名:lsof

相对于 fuser 是由文件或者装置去找出使用该文件或装置的进程,反过来说,如何查出某个进程开启或者使用的文件与装置呢?那就是使用 lsof

[root@study ~]# lsof [-aUu] [+d]
选项与参数:
-a :多项数据需要『同时成立』才显示出结果时!
-U :仅列出 Unix like 系统的 socket 文件类型;
-u :后面接 username,列出该使用者相关进程所开启的文件; 
+d :后面接目录,亦即找出某个目录底下已经被开启的文件!


#范例一:列出目前系统上面所有已经被开启的文件与装置:
[root@study home]# lsof | more
COMMAND     PID  TID           USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
systemd       1                root  cwd       DIR              253,0      4096        128 /
systemd       1                root  rtd       DIR              253,0      4096        128 /
systemd       1                root  txt       REG              253,0   1624448   19988449 /usr/lib/systemd/systemd
......
#注意到了吗?在预设的情况下,lsof 会将目前系统上面已经开启的文件全部列出来~
#可以注意到,第一个文件 systemd 执行的地方就在根目录,而根目录!所在的 inode 也有显示出来喔!


#范例二:仅列出关于 root 的所有进程开启的 socket 文件
[root@study home]# lsof -u root -U -a | more   
COMMAND     PID USER   FD   TYPE             DEVICE SIZE/OFF   NODE NAME
systemd       1 root    3u  unix 0xffff880035f34b40      0t0 158668 socket
systemd       1 root   12u  unix 0xffff88005b7d83c0      0t0 157516 socket
systemd       1 root   14u  unix 0xffff88005b775a40      0t0 157518 /run/systemd/private
systemd       1 root   24u  unix 0xffff88007c0cc780      0t0   6895 /run/systemd/notify
systemd       1 root   25u  unix 0xffff880035f352c0      0t0 145673 /run/systemd/cgroups-agent
......
sleep     28086 root    1u  unix 0xffff88007a6e3a40      0t0  16832 socket
sleep     28086 root    2u  unix 0xffff88007a6e3a40      0t0  16832 socket
sshd      31724 root    4u  unix 0xffff880035f343c0      0t0 253648 socket
#注意到那个 -a 吧!如果你分别输入 lsof -u root 及 lsof -U ,会有啥信息? 
#使用 lsof -u root -U 及 lsof -u root -a -U 结果都不同啦!
#-a 的用途就是在解决同时需要两个项目都成立时!


#范例三:请列出目前系统上面所有的被启动的周边装置
[root@study home]# lsof +d /dev/
COMMAND     PID           USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
systemd       1           root    0u   CHR                1,3      0t0 4674 /dev/null
systemd       1           root    1u   CHR                1,3      0t0 4674 /dev/null
systemd       1           root    2u   CHR                1,3      0t0 4674 /dev/null
systemd       1           root   15r   CHR             10,235      0t0 6571 /dev/autofs


#范例四:秀出属于 root 的 bash 这支程序所开启的文件
[root@study home]# lsof -u root | grep '^bash'
bash      26524 root  cwd       DIR              253,2      4096        128 /home
bash      26524 root  rtd       DIR              253,0      4096        128 /
bash      26524 root  txt       REG              253,0    960376   33659032 /usr/bin/bash
bash      26524 root  mem       REG              253,0 106065056   50887180 /usr/lib/locale/locale-archive
bash      26524 root  mem       REG              253,0     57824   50887151 /usr/lib64/libnss_files-2.17.so
......

这个指令可以找出我们想要知道的某个进程是否有启用哪些信息?例如上头提到的范例四的执行结果呢!

找出某支正在执行的程序的 PID:pidof

[root@study ~]# pidof [-sx] program_name
选项与参数:
-s :仅列出一个 PID 而不列出所有的 PID
-x :同时列出该 program name 可能的 PPID 那个进程的 PID


#范例一:列出目前系统上面 systemd 以及 rsyslogd 这两个程序的 PID
[root@study home]# pidof systemd rsyslogd
1 736
#理论上,应该会有两个 PID 才对。上面的显示也是出现了两个 PID。分别是 systemd 及 rsyslogd 这两个程序的 PID。

很简单的用法,透过这个 pidof 指令,并且配合 ps aux 与正规表示法,就可以很轻易的找到您所想要的进程内容了呢。如果要找的是 bash ,那就 pidof bash ,立刻列出一堆 PID 号码了~

最后修改:2020 年 01 月 21 日 04 : 33 PM

发表评论