初识Linux(十三)------ 进程管理与 SELinux
一个程序被载入到内存当中运行,那么在内存内的那个数据就被称为进程(process)。进程是操作系统上非常重要的概念, 所有系统上面跑的数据都会以进程的形式存在。那么系统的进程有哪些状态?不同的状态会如何影响系统的运行? 进程之间是否可以互相控管等等的,这些都是我们所必须要知道的。 另外与程序有关的还有 SELinux 这个加强文件存取安全性的东西,也必须要做个了解。
1. 程序(process)
1.1 程序与进程
在 Linux 系统当中:触发任何一个事件时,系统都会将他定义成为一个进程,并且给予这个进程一个 ID ,称为 PID,同时依据启发这个程序的使用者与相关属性关系,给予这个 PID 一组有效的权限设置。 从此以后,这个 PID 能够在系统上面进行的动作,就与这个 PID 的权限有关了。
程序一般是存储在硬盘中,然后通过使用者的执行来触发。触发后会载入到内存中成为一个个体,那就是进程。 为了操作系统可管理这个进程,因此进程有给予执行者的权限/属性等参数,并包括进程所需要的指令码与数据或文件数据等, 最后再给予一个 PID 。系统就是通过这个 PID 来判断该 process 是否具有权限进行工作。
由这个程序衍生出来的其他程序在一般状态下,也会沿用这个程序的相关权限的。
子进程与父进程
当我们登陆系统后,会取得一个 bash 的 shell ,然后,我们用这个 bash 提供的接口去执行另一个指令,例如 /usr/bin/passwd 或者是 touch 等等,那些另外执行的指令也会被触发成为 PID ,那个后来执行指令才产生的 PID 就是“子进程”,而在原本的 bash 环境,就称为“父进程”。
每个进程都有一个 PID ,那某个进程的父进程该如何判断?就通过 Parent PID (PPID) 来判断。
输入 ps -l
查看
fork and exec:进程调用的流程
在 Linux 的进程调用通常称为 fork-and-exec 的流程。进程都会借由父进程以复制 (fork) 的方式产生一个一模一样的子进程, 然后被复制出来的子进程再以 exec 的方式来执行实际要进行的进程,最终就成为一个子进程的存在。 整个流程有点像下面这张图:
系统或网络服务:常驻在内存的进程
常驻在内存当中的进程通常都是负责一些系统所提供的功能以服务使用者各项任务,因此这些常驻程序就会被我们称为:服务 (daemon)。大致分为系统本身所需要的服务,如负责计划任务的crond、atd;网络服务,如 Apache、named、postfix等等。
1.2 Linux的多用户多任务环境
在 Linux 下面执行一个指令时,系统会将相关的权限、属性、程序码与数据等均载入内存, 并给予这个单元一个程序识别码 (PID),最终该指令可以进行的任务则与这个 PID 的权限有关。
在 Linux 系统上面具有多种不同的帐号, 每种帐号都有都有其特殊的权限,除了 root 之外,其他人都必须要受一些限制的。
在 Linux 当中,默认提供了六个文字界面登陆窗口,以及一个图形界面,你可以使用 [Alt]+[F1].....[F7] 来切换不同的终端机界面,而且每个终端机界面的登陆者还可以不同用户。
在Windows上经常会出现某个程序卡死,无奈只能重启电脑的情况,而在Linux上几乎不会出现死机的情况,当出现进程卡住时,可以按 [Alt]+[F1].....[F7] 来切换到其他的终端机界面,然后以 ps -aux 找出刚刚的错误程序,然后给他 kill 掉。
bash 环境下的工作管理 (job control)
在这个单一的 bash 接口下,可以同时进行多个工作。例如:
cp file1 file2 &
这条指令表示将 file1 这个文件复制为 file2 ,且放置于背景中执行,“&”表示后台执行 ,也就是说执行这一个命令之后,在这一个终端接口仍然可以做其他的工作。而当这一个指令执行完毕之后,系统将会在你的终端接口显示完成的消息。
多用户多任务的系统资源分配问题
2. 任务管理 (job control)
任务管理是指在单一终端机接口下同时进行多个任务行为的管理。当然我们可以重复登陆那六个命令行的终端机环境,不过,可以直接在一个 bash 内达成。
假设我们只有一个终端接口,因此在可以出现提示字符让你操作的环境就称为前景 (foreground),至于其他工作就可以让你放入背景 (background) 去暂停或运行。要注意的是,放入背景的工作想要运行时, 他必须不能够与使用者互动。举例来说, vim 绝对不可能在背景里面执行 (running) 的!因为你没有输入数据他就不会跑啊! 而且放入背景的工作是不可以使用 [ctrl]+c 来终止的。
总之,要进行 bash 的 job control 必须要注意到的限制是:
- 这些工作所触发的进程必须是你 shell 的子进程(只管理自己的 bash);
- 前景:你可以控制与下达指令的这个环境称为前景的工作 (foreground);
- 背景:可以自行运行的工作,你无法使用 [ctrl]+c 终止他,可使用 bg/fg 调用该工作;
- 背景中“执行”的程序不能等待 terminal/shell 的输入(input)。
2.1 任务管理
如前所述,bash 只能够管理自己的工作而不能管理其他 bash 的工作,所以即使你是 root 也不能执行别人的 bash 下面的 job 。
直接将指令丢到背景中“执行”的 &
在只有一个 bash 的环境下,如果想要同时进行多个工作, 那么可以将某些工作直接丢到背景环境当中,让我们可以继续操作前景的工作,最简单的方法就是利用“ & ”。
例如:
[root@study ~]# tar -zpcf /tmp/etc.tar.gz /etc &
[1] 14432 <== [job number] PID
[root@study ~]# tar: Removing leading `/' from member names
# 在中括号内的号码为工作号码 (job number),该号码与 bash 的控制有关。
# 后续的 14432 则是这个工作在系统中的 PID。至于后续出现的数据是 tar 执行的数据流,
# 由于我们没有加上数据流重导向,所以会影响画面!不过不会影响前景的操作
如果你操作过程中,突然出现这个数据:
[1]+ Done tar -zpcf /tmp/etc.tar.gz /etc
代表 [1] 这个工作已经完成 (Done) ,后面的字符串则是该任务的命令。
& 代表:“将工作丢到背景中去执行,这样的情况最大的好处是: 不怕被 [ctrl]+c 中断。但要特别注意数据的流向,最好是利用数据流重导向, 将输出数据传送至某个文件中。举例:
tar -zpcvf /tmp/etc.tar.gz /etc > /tmp/log.txt 2>&1 &
将“目前”的工作丢到背景中“暂停”:[ctrl]-z
想个情况:如果我正在使用 vim ,却发现我有个文件不知道放在哪里,需要到 bash 环境下进行查找,此时是否要结束 vim 呢?不需要,只要暂时将 vim 给他丢到背景当中等待即可。 例如以下的案例:
[root@study ~]# vim ~/.bashrc
# 在 vim 的一般模式下,按下 [ctrl]-z 这两个按键
[1]+ Stopped vim ~/.bashrc
[root@study ~]# <==顺利取得了前景的操控权!
在 vim 的一般模式下,按下 [ctrl] 及 z 这两个按键,屏幕上会出现 [1] ,表示这是第一个工作, 而那个 + 代表最近一个被丢进背景的工作,且目前在背景下默认会被取用的那个工作 (与 fg 这个指令有关 )。而那个 Stopped 则代表目前这个工作的状态。在默认的情况下,使用 [ctrl]-z 丢到背景当中的工作都是“暂停”的状态。
观察目前的背景工作状态: jobs
[root@study ~]# jobs [-lrs]
选项与参数:
-l :除了列出 job number 与指令串之外,同时列出 PID 的号码;
-r :仅列出正在背景 run 的工作;
-s :仅列出正在背景当中暂停 (stop) 的工作。
范例一:观察目前的 bash 当中,所有的工作,与对应的 PID
[root@study ~]# jobs -l
[1]- 14566 Stopped vim ~/.bashrc
[2]+ 14567 Stopped find / -print
那个 + 代表默认的取用工作。 如果我仅输入 fg 时,那么那个 [2] 会被拿到前景当中来处理。
+ 代表最近被放到背景的工作号码, - 代表最近最后第二个被放置到背景中的工作号码。 而超过最后第三个以后的工作,就不会有 +/- 符号存在了。
将背景工作拿到前景来处理:fg
[root@study ~]# fg %jobnumber
选项与参数:
%jobnumber :jobnumber 为工作号码(数字)。注意,那个 % 是可有可无的!
范例一:先以 jobs 观察工作,再将工作取出:
[root@study ~]# jobs -l
[1]- 14566 Stopped vim ~/.bashrc
[2]+ 14567 Stopped find / -print
[root@study ~]# fg <==默认取出那个 + 的工作,亦即 [2]。立即按下[ctrl]-z
[root@study ~]# fg %1 <==直接规定取出的那个工作号码!再按下[ctrl]-z
[root@study ~]# jobs -l
[1]+ 14566 Stopped vim ~/.bashrc
[2]- 14567 Stopped find / -print
经过 fg 指令就能够将背景工作拿到前景来处理。比较有趣的是最后一个显示的结果,我们会发现 + 出现在第一个工作后,怎么会这样?这是因为你刚刚利用 fg %1 将第一号工作捉到前景后又放回背景,此时最后一个被放入背景的将变成 vi 那个指令动作, 所以当然 [1] 后面就会出现 + 了!了解乎!另外,如果输入“ fg - ” 则代表将 - 号的那个工作号码拿出来,上面就是 [2]- 那个工作号码。
让工作在背景下的状态变成运行中: bg
刚刚提到,那个 [ctrl]-z 可以将目前的工作丢到背景下面去“暂停”, 那么如何让一个工作在背景下面“ Run ”呢?
范例一:一执行 find / -perm /7000 > /tmp/text.txt 后,立刻丢到背景去暂停!
[root@study ~]# find / -perm /7000 > /tmp/text.txt
# 此时,请立刻按下 [ctrl]-z 暂停!
[3]+ Stopped find / -perm /7000 > /tmp/text.txt
范例二:让该工作在背景下进行,并且观察他!!
[root@study ~]# jobs ; bg %3 ; jobs
[1] Stopped vim ~/.bashrc
[2]- Stopped find / -print
[3]+ Stopped find / -perm /7000 > /tmp/text.txt
[3]+ find / -perm /7000 > /tmp/text.txt &
[1]- Stopped vim ~/.bashrc
[2]+ Stopped find / -print
[3] Running find / -perm /7000 > /tmp/text.txt &
管理背景当中的工作: kill
如果想要将该工作直接移除呢?或者是将该工作重新启动呢?这个时候就得需要给予该工作一个讯号 (signal) ,此时, kill 这个指令就派上用场了。
[root@study ~]# kill -signal %jobnumber
[root@study ~]# kill -l
选项与参数:
-l :这个是 L 的小写,列出目前 kill 能够使用的讯号 (signal) 有哪些?
signal :代表给予后面接的那个工作什么样的指示啰!用 man 7 signal 可知:
-1 :重新读取一次参数的配置文件 (类似 reload);
-2 :代表与由键盘输入 [ctrl]-c 同样的动作;
-9 :立刻强制删除一个工作;
-15:以正常的程序方式终止一项工作。与 -9 是不一样的。
范例一:找出目前的 bash 环境下的背景工作,并将该工作“强制删除”。
[root@study ~]# jobs
[1]+ Stopped vim ~/.bashrc
[2] Stopped find / -print
[root@study ~]# kill -9 %2; jobs
[1]+ Stopped vim ~/.bashrc
[2] Killed find / -print
# 再过几秒你再下达 jobs 一次,就会发现 2 号工作不见了!因为被移除了!
范例二:找出目前的 bash 环境下的背景工作,并将该工作“正常终止”掉。
[root@study ~]# jobs
[1]+ Stopped vim ~/.bashrc
[root@study ~]# kill -SIGTERM %1
# -SIGTERM 与 -15 是一样的!您可以使用 kill -l 来查阅!
# 不过在这个案例中, vim 的工作无法被结束喔!因为他无法通过 kill 正常终止
-9 这个 signal 通常是用在“强制删除一个不正常的工作”时所使用的, -15 则是以正常步骤结束一项工作(15也是默认值),两者之间并不相同。举上面的例子来说, 我用 vim 的时候,会产生一个 .filename.swp 的文件, 当使用 -15 这个 signal 时, vim 会尝试以正常的步骤来结束掉该 vi 的工作, 所以 .filename.swp 会被删除。但若是使用 -9 这个 signal 时,由于该 vim 工作会被强制移除掉,因此, .filename.swp 就会继续存在文件系统当中。
2.2 离线管理
要注意的是,我们在任务管理当中提到的“背景”指的是在终端机模式下可以避免 [crtl]-c 中断的一个情境, 可以说那个是 bash 的背景,并不是放到系统的背景。所以,任务管理的背景依旧与终端机有关。在这样的情况下,如果是以远端连线方式连接到 Linux 主机,并且将工作以 & 的方式放到背景去, 在工作尚未结束的情况下离线了,该工作不会继续进行,而是会被中断掉。
可以参考前一章的 at 来处理,因为 at 是将工作放置到系统背景, 而与终端机无关。如果不想要使用 at 的话,那也可以尝试使用 nohup 这个指令来处理。这个 nohup 可以让你在离线或登出系统后,还能够让工作继续进行。他的语法有点像这样:
[root@study ~]# nohup [指令与参数] <==在终端机前景中工作
[root@study ~]# nohup [指令与参数] & <==在终端机背景中工作
需要注意的是, nohup 并不支持 bash 内置的指令,因此你的指令必须要是外部指令才行。
# 1. 先编辑一支会“睡着 500 秒”的程序:
[root@study ~]# vim sleep500.sh
#!/bin/bash
/bin/sleep 500s
/bin/echo "I have slept 500 seconds."
# 2. 丢到背景中去执行,并且立刻登出系统:
[root@study ~]# chmod a+x sleep500.sh
[root@study ~]# nohup ./sleep500.sh &
[2] 14812
[root@study ~]# nohup: ignoring input and appending output to `nohup.out' <==会告知这个讯息!
[root@study ~]# exit
如果你再次登陆的话,再使用 pstree 去查阅你的程序,会发现 sleep500.sh 还在执行中,并不会被中断掉。由于我们的程序最后会输出一个讯息,但是 nohup 与终端机其实无关了, 因此这个讯息的输出就会被导向“ ~/nohup.out ”。
3. 进程管理
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 :做一个更为完整的输出。
一般只用记两个就行,一个是只能查阅自己 bash 程序的“ ps -l ”,另一个则是可以查阅所有系统运行的程序“ ps aux“ (没有那个减号-)。
- 仅观察自己的 bash 相关程序: ps -l
范例一:将目前属于您自己这次登陆的 PID 与相关信息列示出来(只与自己的 bash 有关)
[root@study ~]# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 14830 13970 0 80 0 - 52686 poll_s pts/0 00:00:00 sudo
4 S 0 14835 14830 0 80 0 - 50511 wait pts/0 00:00:00 su
- 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 的缩写,触发此进程的指令。
范例里表明:bash 的程序属于 UID 为 0 的使用者,状态为睡眠 (sleep), 之所以为睡眠因为他触发了 ps (状态为 run) 。此程序的 PID 为 14836,优先执行顺序为 80 , 下达 bash 所取得的终端接口为 pts/0 ,运行状态为等待 (wait) 。
- 观察系统所有程序: ps aux
范例二:列出目前所有的正在内存当中的程序:
[root@study ~]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.2 60636 7948 ? Ss Aug04 0:01 /usr/lib/systemd/systemd ...
root 2 0.0 0.0 0 0 ? S Aug04 0:00 [kthreadd]
.....(中间省略).....
root 14830 0.0 0.1 210744 3988 pts/0 S Aug04 0:00 sudo su -
root 14835 0.0 0.1 202044 2996 pts/0 S Aug04 0:00 su -
root 14836 0.0 0.1 116140 2960 pts/0 S Aug04 0:00 -bash
.....(中间省略).....
root 18459 0.0 0.0 123372 1380 pts/0 R+ 00:25 0:00 ps au
- 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 的顺序来排序显示。
范例三:以范例一的显示内容,显示出所有的程序:
[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 - 15159 ep_pol ? 00:00:01 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
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
2 3 0 0 ? -1 S 0 0:00 \_ [ksoftirqd/0]
.....(中间省略).....
1 1326 1326 1326 ? -1 Ss 0 0:00 /usr/sbin/sshd -D
1326 13923 13923 13923 ? -1 Ss 0 0:00 \_ sshd: dmtsai [priv]
13923 13927 13923 13923 ? -1 S 1000 0:00 \_ sshd: dmtsai@pts/0
13927 13928 13928 13928 pts/0 18703 Ss 1000 0:00 \_ -bash
13928 13970 13970 13928 pts/0 18703 S 1000 0:00 \_ bash
13970 14830 14830 13928 pts/0 18703 S 0 0:00 \_ sudo su -
14830 14835 14830 13928 pts/0 18703 S 0 0:00 \_ su -
14835 14836 14836 13928 pts/0 18703 S 0 0:00 \_ -bash
14836 18703 18703 13928 pts/0 18703 R+ 0 0:00 \_ ps axjf
.....(后面省略).....
以上面的例子来看,是通过 sshd 提供的网络服务取得一个程序, 该程序提供 bash 使用,通过 bash 再去执行 ps axjf 。
范例五:找出与 cron 与 rsyslog 这两个服务有关的 PID 号码
[root@study ~]# ps aux | egrep '(cron|rsyslog)'
root 742 0.0 0.1 208012 4088 ? Ssl Aug04 0:00 /usr/sbin/rsyslogd -n
root 1338 0.0 0.0 126304 1704 ? Ss Aug04 0:00 /usr/sbin/crond -n
root 18740 0.0 0.0 112644 980 pts/0 S+ 00:49 0:00 grep -E --color=auto (cron|rsyslog)
# 所以号码是 742 及 1338 这两个
除此之外,我们必须要知道的是“僵尸 (zombie) ”进程是什么? 通常,造成僵尸进程的原因是该进程应该已经执行完毕,或者是因故应该要终止了, 但是该进程的父进程却无法完整的将该进程结束掉,而造成那个进程一直存在内存当中。 如果你发现在某个进程的 CMD 后面还接上 <defunct> 时,就代表该进程是僵尸进程,例如:
apache 8683 0.0 0.9 83384 9992 ? Z 14:33 0:00 /usr/sbin/httpd <defunct>
当系统不稳定的时候就容易造成所谓的僵尸进程,可能是因为程序写的不好,或者是使用者的操作习惯不良等等所造成。如果你发现系统中很多僵尸进程时,要找出该进程的父进程,然后好好的做个追踪,不要只是直接将他 kill 掉而已。
事实上,通常僵尸进程都已经无法控管,而直接是交给 systemd 这进程来负责了,偏偏 systemd 是系统第一支执行的进程, 他是所有进程的父进程!我们无法杀掉该进程的 (杀掉他,系统就死掉了!),所以,如果产生僵尸进程, 而系统过一阵子还没有办法通过核心非经常性的特殊处理来将该进程删除时,那只好通过 reboot 的方式来将该进程抹去了。
top:动态观察进程的变化
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 软件的按键。
不同于 ps 是静态的结果输出, top 这个程序可以持续的监测整个系统的程序工作状态。 在默认的情况下,每次更新程序资源的时间为 5 秒,不过,可以使用 -d 来进行修改。
范例一:每两秒钟更新一次 top ,观察整体信息:
[root@study ~]# top -d 2
top - 00:53:59 up 6:07, 3 users, load average: 0.00, 0.01, 0.05
Tasks: 179 total, 2 running, 177 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 : 2916388 total, 1839140 free, 353712 used, 723536 buff/cache
KiB Swap: 1048572 total, 1048572 free, 0 used. 2318680 avail Mem
<==如果加入 k 或 r 时,就会有相关的字样出现在这里
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18804 root 20 0 130028 1872 1276 R 0.5 0.1 0:00.02 top
1 root 20 0 60636 7948 2656 S 0.0 0.3 0:01.70 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:00.00 ksoftirqd/0
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) 作为排序。仅查看单一进程,如下所示:
范例三:我们自己的 bash PID 可由 $$ 变量取得,请使用 top 持续观察该 PID
[root@study ~]# echo $$
14836 <==就是这个数字!他是我们 bash 的 PID
[root@study ~]# top -d 2 -p 14836
top - 01:00:53 up 6:14, 3 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.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2916388 total, 1839264 free, 353424 used, 723700 buff/cache
KiB Swap: 1048572 total, 1048572 free, 0 used. 2318848 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14836 root 20 0 116272 3136 1848 S 0.0 0.1 0:00.07 bash
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---3*[{NetworkManager}] # 前面有数字,代表子进程的数量!
....(中间省略)....
|-sshd---sshd---sshd---bash---bash---sudo---su---bash---pstree <==我们指令执行的相依性
....(下面省略)....
范例二:承上题,同时列出 PID 与 users
[root@study ~]# pstree -Aup
systemd(1)-+-ModemManager(745)-+-{ModemManager}(785)
| `-{ModemManager}(790)
|-NetworkManager(870)-+-{NetworkManager}(907)
| |-{NetworkManager}(911)
| `-{NetworkManager}(914)
....(中间省略)....
|-sshd(1326)---sshd(13923)---sshd(13927,dmtsai)---bash(13928)---bash(13970)---
....(下面省略)....
# 在括号 () 内的即是 PID 以及该进程的 owner ,一般来说,如果该进程的所有人与父进程相同,
# 就不会列出,但是如果与父进程不一样,那就会列出该程序的拥有者!看上面 13927 就转变成 dmtsai 了
一般链接符号可以使用 ASCII 码即可,但有时因为语系问题会主动的以 Unicode 的符号来链接, 但因为可能终端机无法支持该编码,或许会造成乱码问题。因此可以加上 -A 选项来克服此类线段乱码问题。
由 pstree 的输出我们也可以很清楚的知道,所有的程序都是依附在 systemd 这支进程下面的! 这支进程的 PID 是一号,因为他是由 Linux 核心所主动调用的第一支程序。这也是发生僵尸进程需要重新开机的原因,因为 systemd 要重新启动,而重新启动 systemd 就是 reboot。
3.2 进程管理
通过给予该进程一个信号 (signal) 去告知该进程你想要让它做什么。那么到底有多少 signal 呢? 你可以使用 kill -l (小写的 L ) 或者是 man 7 signal 都可以查询到。主要的信号代号与名称对应及内容是:
那么我们如何传送一个信号给某个进程呢?通过 kill 或 killall 。
kill -signal PID
kill 可以帮我们将这个 signal 传送给某个任务 (%jobnumber) 或者是某个 PID (直接输入数字)。 要再次强调的是: kill 后面直接加数字与加上 %number 的情况是不同的,因为工作控制中有 1 号工作,但是 PID 1 号则是专指“ systemd ”这支程序。
例题:
以 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
如果你有看到类似“Aug 5 01:25:02 study rsyslogd: [origin software="rsyslogd" swVersion="7.4.7" x-pid="742" x-info="http://www.rsyslog.com"] rsyslogd was HUPed”之类的字样,就是表示 rsyslogd 在 8/5 有重新启动 (restart) 过了
killall -signal 指令名称
由于 kill 后面必须要加上 PID (或者是 job number),所以,通常 kill 都会配合 ps, pstree 等指令,因为我们必须要找到相对应的那个程序的 ID ,但是,如此一来,很麻烦~有没有可以利用“下达指令的名称”来给予信号的?可以的!用 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
范例三:依次询问每个 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
3.3 关于进程的执行顺序
系统同时间有非常多的进程在运行中,那么 CPU 应该要先处理那个进程呢?这就得要考虑到进程的优先执行顺序 (Priority) 与 CPU 调度。
Priority 与 Nice 值
Linux 给予进程一个“优先执行顺序 (priority, 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 14836 14835 0 90 10 - 29068 wait pts/0 00:00:00 bash
0 R 0 19848 14836 0 90 10 - 30319 - pts/0 00:00:00 ps
由于 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] 19865
[root@study ~]# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 14836 14835 0 90 10 - 29068 wait pts/0 00:00:00 bash
4 T 0 19865 14836 0 85 5 - 37757 signal pts/0 00:00:00 vim
0 R 0 19866 14836 0 90 10 - 30319 - pts/0 00:00:00 ps
# 原本的 bash PRI 为 90 ,所以 vim 默认应为 90。不过由于给予 nice 为 -5 ,
# 因此 vim 的 PRI 降低了。RPI 与 NI 各减 5 ,但不一定每次都是正好相同,因为核心会动态调整
[root@study ~]# kill -9 %1 <==测试完毕将 vim 关闭
通常什么时候要将 nice 值调大呢?举例来说,系统的背景工作中, 某些比较不重要的程序之进行:例如备份工作,由于备份工作相当的耗系统资源, 这个时候就可以将备份的指令之 nice 值调大一些,可以使系统的资源分配的更为公平。
renice :已存在程序的 nice 重新调整
[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 14836 14835 0 90 10 - 29068 wait pts/0 00:00:00 bash
0 R 0 19900 14836 0 90 10 - 30319 - pts/0 00:00:00 ps
[root@study ~]# renice -5 14836
14836 (process ID) old priority 10, new priority -5
[root@study ~]# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 14836 14835 0 75 -5 - 29068 wait pts/0 00:00:00 bash
0 R 0 19910 14836 0 75 -5 - 30319 - pts/0 00:00:00 ps
3.4 系统资源的查看
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: 2848 346 1794 8 706 2263
Swap: 1023 0 1023
在上例中,Mem 那一行显示的是实体内存的量, 有 2848MB 左右的实体内存,Swap 则是内存交换空间的量,swap 有 1GB 左右。 total 是总量, used 是已被使用的量, free 则是剩余可用的量。 后面的 shared/buffers/cached 则是在已被使用的量当中,用来作为缓冲及高速缓存的量,这些 shared/buffers/cached 的用量中,在系统比较忙碌时, 可以被释出而继续利用,因此后面就有一个 available (可用的) 数值。
一般来说, swap 最好不要被使用,尤其 swap 最好不要被使用超过 20% 以上, 如果发现 swap 的用量超过 20% ,那么,最好还是多插根内存条。因为, Swap 的性能跟内存差很多,而系统会使用到 swap , 绝对是因为内存不足才这样做。
Linux 系统为了加速系统性能,所以会将最常使用到的或者是最近使用到的文件数据高速缓存 (cache) 下来,因此cache那一栏有706MB。
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.vbird 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015
x86_64 x86_64 x86_64 GNU/Linux
范例说明该 Linux 主机使用的核心名称为 Linux,而主机名称为 study.centos.vbird,核心的版本为 3.10.0-229.el7.x86_64 ,该核心版本创建的日期为 2015-3-6,适用的硬件平台为 x86_64 以上等级的硬件平台。
uptime:观察系统启动时间与工作负载
这个指令很简单,就是显示出目前系统已经开机多长时间,以及 1, 5, 15 分钟的平均负载。还记得 top 吧?没错,这个 uptime 可以显示出 top 画面的最上面一行。
[root@study ~]# uptime
02:35:27 up 7:48, 3 users, load average: 0.00, 0.01, 0.05
# top 这个指令已经谈过相关信息,不再聊
netstat :追踪网络或插槽文件
[root@study ~]# netstat -[atunlp]
选项与参数:
-a :将目前系统上所有的连线、监听、Socket 数据都列出来
-t :列出 tcp 网络封包的数据
-u :列出 udp 网络封包的数据
-n :不以程序的服务名称,以埠号 (port number) 来显示;
-l :列出目前正在网络监听 (listen) 的服务;
-p :列出该网络服务的程序 PID
这个指令常被用在网络的监控方面,不过,在进程管理方面也是需要了解。基本上, netstat 的输出分为两大部分,分别是网络与系统自己的进程相关性部分。
范例一:列出目前系统已经创建的网络连接与 unix socket 状态
[root@study ~]# netstat
Active Internet connections (w/o servers) <==与网络较相关的部分
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 172.16.15.100:ssh 172.16.220.234:48300 ESTABLISHED
Active UNIX domain sockets (w/o servers) <==与本机的程序自己的相关性(非网络)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ] DGRAM 1902 @/org/freedesktop/systemd1/notify
unix 2 [ ] DGRAM 1944 /run/systemd/shutdownd
....(中间省略)....
unix 3 [ ] STREAM CONNECTED 25425 @/tmp/.X11-unix/X0
unix 3 [ ] STREAM CONNECTED 28893
unix 3 [ ] STREAM CONNECTED 21262
在上面的结果当中,显示了两个部分,分别是网络连接以及 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 连接,远端的 172.16.220.234:48300 连线到本地端的 172.16.15.100:ssh ,这条连接状态是创建 (ESTABLISHED) 的状态。
除了网络连接之外,Linux 系统上面的进程可以接收不同进程所发送来的信息,那就是 Linux 上头的插槽文件(socket file)。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 去看看哪些进程有启动哪些网络端口。
范例二:找出目前系统上已在监听的网络连接及其 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 0.0.0.0:22 0.0.0.0:* LISTEN 1326/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2349/master
tcp6 0 0 :::22 :::* LISTEN 1326/sshd
tcp6 0 0 ::1:25 :::* LISTEN 2349/master
udp 0 0 0.0.0.0:123 0.0.0.0:* 751/chronyd
udp 0 0 127.0.0.1:323 0.0.0.0:* 751/chronyd
udp 0 0 0.0.0.0:57808 0.0.0.0:* 743/avahi-daemon: r
udp 0 0 0.0.0.0:5353 0.0.0.0:* 743/avahi-daemon: r
udp6 0 0 :::123 :::* 751/chronyd
udp6 0 0 ::1:323 :::* 751/chronyd
# 除了可以列出监听网络的接口与状态之外,最后一个字段还能够显示此服务的
# PID 号码以及程序的指令名称,例如上头的 1326 就是该 PID。
范例三:将上述的 0.0.0.0:57808 那个网络服务关闭
[root@study ~]# kill -9 743
[root@study ~]# killall -9 avahi-daemon
dmesg :分析核心产生的信息
所有核心侦测的信息,不管是开机时候还是系统运行过程中,反正只要是核心产生的,都会被记录到内存中的某个保护区段。 dmesg 这个指令就能够将该区段的信息读出来。因为信息实在太多了,所以执行时可以加入这个管线指令“ | more ”来使画面暂停。
范例一:输出所有的核心开机时的信息
[root@study ~]# dmesg | more
范例二:查找开机的时候,硬盘的相关信息
[root@study ~]# dmesg | grep -i vda
[ 0.758551] vda: vda1 vda2 vda3 vda4 vda5 vda6 vda7 vda8 vda9
[ 3.964134] XFS (vda2): Mounting V4 Filesystem
....(下面省略)
vmstat :监测系统资源变化
如果你想要动态的了解一下系统资源的运行情况,那么vmstat 可以监测 CPU / 内存 / 磁盘输入输出状态 等等,如果你想要了解繁忙的系统到底是哪个环节最累人, 也可以使用 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 1838092 1504 722216 0 0 4 1 6 9 0 0 100 0 0
0 0 0 1838092 1504 722200 0 0 0 0 13 23 0 0 100 0 0
0 0 0 1838092 1504 722200 0 0 0 0 25 46 0 0 100 0 0
你可以使用类似“ 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 使用状态 。
4. 特殊文件与程序
曾经谈到特殊权限的 SUID/SGID/SBIT ,我们要探讨的是,这些权限对于程序是如何影响的? 此外,程序可能会使用到系统资源,举例来说,磁盘就是其中一项资源。哪天在 umount 磁盘时,系统老是出现“ device is busy ”的字样~到底是怎么回事?我们下面就来谈一谈这些和程序有关系的细节部分。
4.1 具有 SUID/SGID 权限的指令执行状态
SUID 的权限与程序的相关性非常大,来看看 SUID 的程序是如何被一般使用者执行,且具有什么特色:
- SUID 权限仅对二进制程序(binary program)有效;
- 执行者对于该程序需要具有 x 的可执行权限;
- 本权限仅在执行该程序的过程中有效 (run-time);
- 执行者将具有该程序拥有者 (owner) 的权限。
SUID 的权限生效是由于“具有该权限的程序被触发”,而一个程序被触发会变成进程, 所以,执行者可以获得程序拥有者的权限。在执行了 passwd 后,就具有 root 的权限。这是因为在触发 passwd 后,会取得一个新的进程与 PID,该 PID 产生时通过 SUID 来给予该 PID 特殊的权限设置。我们使用 dmtsai 登陆系统且执行 passwd 后,通过工作控制来理解一下这个过程:
[dmtsai@study ~]$ passwd
Changing password for user dmtsai.
Changing password for dmtsai
(current) UNIX password: <==这里按下 [ctrl]-z 并且按下 [enter]
[1]+ Stopped passwd
[dmtsai@study ~]$ pstree -uA
systemd-+-ModemManager---2*[{ModemManager}]
....(中间省略)....
|-sshd---sshd---sshd(dmtsai)---bash-+-passwd(root)
| `-pstree
....(下面省略)....
可以看出, passwd 确实是由 bash 衍生出来的。
如何查询整个系统的 SUID/SGID 的文件呢?
find / -perm /6000
4.2 /proc/* 代表的意义
进程都是在内存当中,而内存当中的数据又都是写入到 /proc/* 这个目录下的,所以,我们可以直接观察 /proc 这个目录当中的文件。如果你观察过 /proc 这个目录的话,应该会发现他有点像这样:
[root@study ~]# ll /proc
dr-xr-xr-x. 8 root root 0 Aug 4 18:46 1
dr-xr-xr-x. 8 root root 0 Aug 4 18:46 10
dr-xr-xr-x. 8 root root 0 Aug 4 18:47 10548
....(中间省略)....
-r--r--r--. 1 root root 0 Aug 5 17:48 uptime
-r--r--r--. 1 root root 0 Aug 5 17:48 version
-r--------. 1 root root 0 Aug 5 17:48 vmallocinfo
-r--r--r--. 1 root root 0 Aug 5 17:48 vmstat
-r--r--r--. 1 root root 0 Aug 5 17:48 zoneinfo
基本上,目前主机上面的各个程序的 PID 都是以目录的型态存在于 /proc 当中。 举例来说,我们开机所执行的第一支程序 systemd 他的 PID 是 1 , 这个 PID 的所有相关信息都写入在 /proc/1/* 当中。若我们直接观察 PID 为 1 的数据好了,他有点像这样:
[root@study ~]# ll /proc/1
dr-xr-xr-x. 2 root root 0 Aug 4 19:25 attr
-rw-r--r--. 1 root root 0 Aug 4 19:25 autogroup
-r--------. 1 root root 0 Aug 4 19:25 auxv
-r--r--r--. 1 root root 0 Aug 4 18:46 cgroup
--w-------. 1 root root 0 Aug 4 19:25 clear_refs
-r--r--r--. 1 root root 0 Aug 4 18:46 cmdline <==就是指令串
-r--------. 1 root root 0 Aug 4 18:46 environ <==一些环境变量
lrwxrwxrwx. 1 root root 0 Aug 4 18:46 exe
....(以下省略)....
里面的数据还挺多的,不过,比较有趣的其实是两个文件,分别是:
- cmdline:这个程序被启动的指令串;
- environ:这个程序的环境变量内容。
如果查阅一下 cmdline 的话,就会发现:
root@study ~]# cat /proc/1/cmdline
/usr/lib/systemd/systemd--switched-root--system--deserialize24
就是这个指令、选项与参数启动 systemd 。对整个 Linux 系统相关的参数在 /proc 目录下面的文件,相关的文件与对应的内容是这样的:
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 .
USER PID ACCESS COMMAND
/root: root 13888 ..c.. (root)bash
root 31743 ..c.. (root)bash
在输出的结果中,有两个 PID 分别为 13888, 31743 的进程,该进程属于 root 且指令为 bash 。 ACCESS 的项目代表的意义为:
- c :此程序在当前的目录下(非次目录);
- e :可被触发为执行状态;
- f :是一个被打开的文件;
- r :代表顶层目录 (root directory);
- F :该文件被打开了,不过在等待回应中;
- m :可能为分享的动态函数库;
范例二:找到所有使用到 /home 这个文件系统的程序
[root@study ~]# echo $$
31743 # 先确认一下,自己的 bash PID 号码
[root@study ~]# cd /home
[root@study home]# fuser -muv .
USER PID ACCESS COMMAND
/home: root kernel mount (root)/home
dmtsai 31535 ..c.. (dmtsai)bash
root 31571 ..c.. (root)passwd
root 31737 ..c.. (root)sudo
root 31743 ..c.. (root)bash # 果然,自己的 PID 在
[root@study home]# 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 ~]# fuser -mki /home
/home: 31535c 31571c 31737c # 你会发现, PID 跟上面查到的相同。
Kill process 31535 ? (y/N) # 这里会问你要不要删除,当然不要乱删除,通通取消。
可以针对整个文件系统,也可以仅针对单一文件。
范例四:找到 /run 下面属于 FIFO 类型的文件,并且找出存取该文件的程序
[root@study ~]# find /run -type p
.....(前面省略).....
/run/systemd/sessions/165.ref
/run/systemd/sessions/1.ref
/run/systemd/sessions/c1.ref # 就这个好了,来测试一下。
[root@study ~]# fuser -uv /run/systemd/sessions/c1.ref
USER PID ACCESS COMMAND
/run/systemd/sessions/c1.ref:
root 763 f.... (root)systemd-logind
root 5450 F.... (root)gdm-session-wor
# 通常系统的 FIFO 文件都会放置到 /run 下面,通过这个方式来追踪该文件被存取的 process。
通过这个 fuser 我们可以找出使用该文件、目录的程序,他的重点与 ps, pstree 不同。 fuser 可以让我们了解到某个文件 (或文件系统) 目前正在被哪些程序所使用。
lsof :列出被程序所打开的文件文件名
相对于 fuser 是由文件或者设备去找出使用该文件或设备的程序,反过来说,lsof 是查出某个程序打开或者使用的文件与设备。
[root@study ~]# lsof [-aUu] [+d]
选项与参数:
-a :多项数据需要“同时成立”才显示出结果时!
-U :仅列出 Unix like 系统的 socket 文件类型;
-u :后面接 username,列出该使用者相关程序所打开的文件;
+d :后面接目录,亦即找出某个目录下面已经被打开的文件!
范例一:列出目前系统上面所有已经被打开的文件与设备:
[root@study ~]# lsof
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 1230920 967763 /usr/lib/systemd/systemd
....(下面省略)....
# 在默认的情况下, lsof 会将目前系统上面已经打开的
# 文件全部列出来~第一个文件 systemd 执行的地方就在根目录,而根目录所在的 inode 也有显示出来
范例二:仅列出关于 root 的所有程序打开的 socket 文件
[root@study ~]# lsof -u root -a -U
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 3u unix 0xffff8800b7756580 0t0 13715 socket
systemd 1 root 7u unix 0xffff8800b7755a40 0t0 1902 @/org/freedesktop/systemd1/notify
systemd 1 root 9u unix 0xffff8800b7756d00 0t0 1903 /run/systemd/private
.....(中间省略).....
Xorg 4496 root 1u unix 0xffff8800ab107480 0t0 25981 @/tmp/.X11-unix/X0
Xorg 4496 root 3u unix 0xffff8800ab107840 0t0 25982 /tmp/.X11-unix/X0
Xorg 4496 root 16u unix 0xffff8800b7754f00 0t0 25174 @/tmp/.X11-unix/X0
.....(下面省略).....
# 注意到那个 -a 吧!如果你分别输入 lsof -u root 及 lsof -U ,会有啥信息?
# 使用 lsof -u root -U 及 lsof -u root -a -U ,都不同
# -a 的用途就是在解决同时需要两个项目都成立时啊! ^_^
范例三:请列出目前系统上面所有的被启动的周边设备
[root@study ~]# lsof +d /dev
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 0u CHR 1,3 0t0 1028 /dev/null
systemd 1 root 1u CHR 1,3 0t0 1028 /dev/null
# 因为设备都在 /dev 里面,所以使用搜寻目录。
范例四:显示属于 root 的 bash 这支程序所打开的文件
[root@study ~]# lsof -u root | grep bash
ksmtuned 781 root txt REG 253,0 960384 33867220 /usr/bin/bash
bash 13888 root cwd DIR 253,0 4096 50331777 /root
bash 13888 root rtd DIR 253,0 4096 128 /
bash 13888 root txt REG 253,0 960384 33867220 /usr/bin/bash
bash 13888 root mem REG 253,0 106065056 17331169 /usr/lib/locale/locale-archive
....(下面省略)....
pidof :找出某支正在执行的程序的 PID
[root@study ~]# pidof [-sx] program_name
选项与参数:
-s :仅列出一个 PID 而不列出所有的 PID
-x :同时列出该 program name 可能的 PPID 那个程序的 PID
范例一:列出目前系统上面 systemd 以及 rsyslogd 这两个程序的 PID
[root@study ~]# pidof systemd rsyslogd
1 742
# 理论上,应该会有两个 PID 才对。上面的显示也是出现了两个 PID 。
# 分别是 systemd 及 rsyslogd 这两支程序的 PID 。
通过这个 pidof 指令,并且配合 ps aux 与正则表达式,就可以很轻易的找到所想要的程序内容了。
5. SELinux
5.1 什么是 SELinux
SELinux,Security Enhanced Linux ,安全强化的 Linux 。那安全强化的是网络资源还是权限管理呢?
当初设计的目标:避免资源的误用
SELinux 是由美国国家安全局 (NSA) 开发的,当初开发的目的是因为很多企业界发现, 通常系统出现问题的原因大部分都在于“内部员工的资源误用”所导致的,实际由外部发动的攻击反而没有这么严重。 那么什么是“员工资源误用”呢?举例来说,如果有个不是很懂系统的系统管理员为了自己设置的方便,将网页所在目录 /var/www/html/ 的权限设置为 drwxrwxrwx 时,你觉得会有什么事情发生?
现在我们知道所有的系统资源都是通过程序来进行存取的,那么 /var/www/html/ 如果设置为 777 , 代表所有程序均可对该目录存取,万一你真的有启动 WWW 服务器软件,那么该软件所触发的程序将可以写入该目录, 而该程序却是对整个 Internet 提供服务的。只要有心人接触到这支程序,而且该程序刚好又有提供使用者进行写入的功能, 那么外部的人很可能就会对你的系统写入些莫名其妙的东西!
为了控管这方面的权限与程序的问题,所以美国国家安全局就着手处理操作系统这方面的控管。 由于 Linux 是自由软件,程序码都是公开的,因此便使用 Linux 来作为研究的目标, 最后更将研究的结果整合到 Linux 核心里面去,那就是 SELinux 。所以说, SELinux 是整合到核心的一个模块,更多的 SELinux 相关说明可以参考:
http://www.nsa.gov/research/selinux/
这也就是说:其实 SELinux 是在进行程序、文件等细部权限设置依据的一个核心模块! 由于启动网络服务的也是程序,因此刚好也能够控制网络服务能否存取系统资源。所以,在讲到 SELinux 对系统的存取控制之前,我们得先来回顾一下之前谈到的系统文件权限与使用者之间的关系。
传统的文件权限与帐号关系:自主式存取控制, DAC
系统的帐号主要分为系统管理员 (root) 与一般用户,而这两种身份能否使用系统上面的文件资源则与 rwx 的权限设置有关。 不过你要注意的是,各种权限设置对 root 是无效的。因此,当某个程序想要对文件进行存取时, 系统就会根据该程序的拥有者/群组,并比对文件的权限,若通过权限检查,就可以存取该文件了。
这种存取文件系统的方式被称为“自主式存取控制 (Discretionary Access Control, DAC)”,基本上,就是依据程序的拥有者与文件资源的 rwx 权限来决定有无存取的能力。 不过这种 DAC 的存取控制有几个困扰,那就是:
- root 具有最高的权限:如果不小心某支程序被有心人士取得, 且该程序属于 root 的权限,那么这支程序就可以在系统上进行任何资源的存取!
- 使用者可以取得程序来变更文件资源的存取权限:如果你不小心将某个目录的权限设置为 777 ,由于对任何人的权限会变成 rwx ,因此该目录就会被任何人所任意存取,这些问题是非常严重的。
以政策规则制定特定程序读取特定文件:委任式存取控制, MAC
为了避免上面的问题,SELinux 引入了委任式存取控制 (Mandatory Access Control, MAC)。
他可以针对特定的程序与特定的文件资源来进行权限的控管!,也就是说,即使你是 root ,那么在使用不同的程序时,你所能取得的权限并不一定是 root , 而得要看当时该程序的设置而定。如此一来,我们针对控制的主体变成了“程序”而不是使用者。此外,这个主体程序也不能任意使用系统文件资源,因为每个文件资源也有针对该主体程序设置可取用的权限。如此一来,控制项目就细的多了,但整个系统程序那么多、文件那么多,一项一项控制可就没完没了,所以 SELinux 也提供一些默认的政策 (Policy) ,并在该政策内提供多个规则 (rule) ,让你可以选择是否启用该控制规则。
举例来说, WWW 服务器软件的达成程序为 httpd 这支程序, 而默认情况下, httpd 仅能在 /var/www/ 这个目录下面存取文件,如果 httpd 这个程序想要到其他目录去存取数据时, 除了规则设置要开放外,目标目录也得要设置成 httpd 可读取的模式 (type) 才行,限制非常多! 所以,即使不小心 httpd 被 cracker 取得了控制权,他也无权浏览 /etc/shadow 等重要的配置文件。
针对 Apache 这个 WWW 网络服务使用 DAC 或 MAC 的结果来说,两者间的关系可以使用下图来说明:
5.2 SELinux 的运行模式
SELinux 是通过 MAC 的方式来控管程序,他控制的主体是程序, 而目标则是该程序能否读取的“文件资源”,所以先来说明一下这些对象之间的相关性。
- 主体 (Subject):
SELinux 管理的就是程序; - 目标 (Object):
主体程序能否存取的“目标资源”一般就是文件系统; - 政策 (Policy):
由于程序与文件数量庞大,因此 SELinux 会依据某些服务来制订基本的存取安全性政策。这些政策内还会有详细的规则 (rule) 来指定不同的服务开放某些资源的存取与否。在目前的 CentOS 7.x 里面仅有提供三个主要的政策,分别是:
targeted:针对网络服务限制较多,针对本机限制较少,是默认的政策;
minimum:由 target 修订而来,仅针对选择的程序来保护!
mls:完整的 SELinux 限制,限制方面较为严格。
建议使用默认的 targeted 政策即可。 - 安全性本文 (security context):
主体能不能存取目标除了政策指定之外,主体与目标的安全性本文必须一致才能够顺利存取。 这个安全性本文 (security context) 有点类似文件系统的 rwx 。安全性本文的内容与设置是非常重要的! 如果设置错误,你的某些服务(主体程序)就无法存取文件系统(目标资源),当然就会一直出现“权限不符”的错误信息了。
上图的重点在“主体”如何取得“目标”的资源存取权限。由上图我们可以发现,(1)主体程序必须要通过 SELinux 政策内的规则放行后,就可以与目标资源进行安全性本文的比对, (2)若比对失败则无法存取目标,若比对成功则可以开始存取目标。
安全性本文 (Security Context)
CentOS 7.x 的 target 政策已经帮我们制订好非常多的规则了,因此只要知道如何打开/关闭某项规则的放行与否即可。 安全性本文可能需要自行设置。
安全性本文存在于主体程序中与目标文件资源中。程序在内存内,所以安全性本文可以存入是没问题。 那文件的安全性本文是记录在哪里呢?事实上,安全性本文是放置到文件的 inode 内的,因此主体程序想要读取目标文件资源时,同样需要读取 inode , 这 inode 内就可以比对安全性本文以及 rwx 等权限值是否正确,而给予适当的读取权限依据。
那么安全性本文到底是什么样的存在呢?我们先来看看 /root 下面的文件的安全性本文好了。 观察安全性本文可使用“ ls -Z ”去观察如下:
# 先来观察一下 root 主文件夹下面的“文件的 SELinux 相关信息”
[root@study ~]# ls -Z
-rw-------. root root system_u:object_r:admin_home_t:s0 anaconda-ks.cfg
-rw-r--r--. root root system_u:object_r:admin_home_t:s0 initial-setup-ks.cfg
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 regular_express.txt
如上所示,安全性本文主要用冒号分为三个字段,这三个字段的意义为:
Identify:role:type
身份识别:角色:类型
- 身份识别 (Identify):
相当于帐号方面的身份识别!主要的身份识别常见有下面几种常见的类型:
- unconfined_u:不受限的用户,也就是说,该文件来自于不受限的程序所产生的。一般来说,我们使用可登陆帐号来取得 bash 之后, 默认的 bash 环境是不受 SELinux 管制的~因为 bash 并不是什么特别的网络服务。因此,在这个不受 SELinux 所限制的 bash 程序所产生的文件, 其身份识别大多就是 unconfined_u 这个“不受限”用户。
- system_u:系统用户,大部分就是系统自己产生的文件。
基本上,如果是系统或软件本身所提供的文件,大多就是 system_u 这个身份名称,而如果是我们用户通过 bash 自己创建的文件,大多则是不受限的unconfined_u 身份~如果是网络服务所产生的文件,或者是系统服务运行过程产生的文件,则大部分的识别就会是 system_u 。
- 角色 (Role):
通过角色字段,我们可以知道这个数据是属于程序、文件资源还是代表使用者。一般的角色有:
- object_r:代表的是文件或目录等文件资源,这应该是最常见的;
- system_r:代表的就是程序。不过,一般使用者也会被指定成为 system_r 。
- 类型 (Type) (最重要!):
在默认的 targeted 政策中, Identify 与 Role 字段基本上是不重要的,重要的在于这个类型 (type) 字段。基本上,一个主体程序能不能读取到这个文件资源,与类型字段有关,而类型字段在文件与程序的定义不太相同,分别是:
- type:在文件资源 (Object) 上面称为类型 (Type);
- domain:在主体程序 (Subject) 则称为领域 (domain) 了。
domain 需要与 type 搭配,则该程序才能够顺利的读取文件资源。
程序与文件 SELinux type 字段的相关性
# 再来观察一下系统“程序的 SELinux 相关信息”
[root@study ~]# ps -eZ
LABEL PID TTY TIME CMD
system_u:system_r:init_t:s0 1 ? 00:00:03 systemd
system_u:system_r:kernel_t:s0 2 ? 00:00:00 kthreadd
system_u:system_r:kernel_t:s0 3 ? 00:00:00 ksoftirqd/0
.....(中间省略).....
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 31513 ? 00:00:00 sshd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 31535 pts/0 00:00:00 bash
# 基本上程序主要就分为两大类,一种是系统有受限的 system_u:system_r,另一种则可能是用户自己的,
# 比较不受限的程序 (通常是本机用户自己执行的程序),亦即是 unconfined_u:unconfined_r 这两种
基本上,这些对应数据在 targeted 政策下的对应如下:
但就如上所述,在默认的 target 政策下,其实最重要的字段是类型字段 (type), 主体与目标之间是否具有可以读写的权限,与程序的 domain 及文件的 type 有关。这两者的关系我们可以使用 crond 以及他的配置文件来说明,亦即是 /usr/sbin/crond, /etc/crontab, /etc/cron.d 等文件来说明。
# 1. 先看看 crond 这个“程序”的安全本文内容:
[root@study ~]# ps -eZ | grep cron
system_u:system_r:crond_t:s0-s0:c0.c1023 1338 ? 00:00:01 crond
system_u:system_r:crond_t:s0-s0:c0.c1023 1340 ? 00:00:00 atd
# 这个安全本文的类型名称为 crond_t 格式!
# 2. 再来瞧瞧可执行文件、配置文件等等的安全本文内容为何!
[root@study ~]# ll -Zd /usr/sbin/crond /etc/crontab /etc/cron.d
drwxr-xr-x. root root system_u:object_r:system_cron_spool_t:s0 /etc/cron.d
-rw-r--r--. root root system_u:object_r:system_cron_spool_t:s0 /etc/crontab
-rwxr-xr-x. root root system_u:object_r:crond_exec_t:s0 /usr/sbin/crond
当我们执行 /usr/sbin/crond 之后,这个程序变成的程序的 domain 类型会是 crond_t 这一个~而这个 crond_t 能够读取的配置文件则为 system_cron_spool_t 这种的类型。因此不论 /etc/crontab, /etc/cron.d 以及 /var/spool/cron 都会是相关的 SELinux 类型 (/var/spool/cron 为 user_cron_spool_t)。 文字看起来不太容易了解,我们使用图示来说明这几个东西的关系:
以 crond 为例,主体程序取得的 domain 与目标文件资源的 type 相互关系
上图的意义我们可以这样看的:
- 首先,我们触发一个可执行的目标文件,那就是具有 crond_exec_t 这个类型的 /usr/sbin/crond 文件;
- 该文件的类型会让这个文件所造成的主体程序 (Subject) 具有 crond 这个领域 (domain), 我们的政策针对这个领域已经制定了许多规则,其中包括这个领域可以读取的目标资源类型;
- 由于 crond domain 被设置为可以读取 system_cron_spool_t 这个类型的目标文件 (Object), 因此你的配置文件放到 /etc/cron.d/ 目录下,就能够被 crond 那支程序所读取了;
- 但最终能不能读到正确的数据,还得要看 rwx 是否符合 Linux 权限的规范。
上述的流程说明,第一个是政策内需要制订详细的 domain/type 相关性;第二个是若文件的 type 设置错误, 那么即使权限设置为 rwx 全开的 777 ,该主体程序也无法读取目标文件资源的,也就可以避免使用者将他的主文件夹设置为 777 时所造成的权限困扰。
5.3 SELinux 三种模式的启动、关闭与观察
并非所有的 Linux distributions 都支持 SELinux 的,所以你必须要先观察一下你的系统版本,CentOS 7.x 支持 SELinux 。目前 SELinux 依据启动与否,共有三种模式,分别如下:
- enforcing:强制模式,代表 SELinux 运行中,且已经正确的开始限制 domain/type 了;
- permissive:宽容模式:代表 SELinux 运行中,不过仅会有警告讯息并不会实际限制 domain/type 的存取。这种模式可以运来作为 SELinux 的 debug 之用;
- disabled:关闭,SELinux 并没有实际运行。
SELinux 的三种类型与实际运行流程图
如上图所示,首先,并不是所有的程序都会被 SELinux 所管制,因此最左边会出现一个所谓的“有受限的程序主体”。那如何观察有没有受限 (confined )呢?通过 ps -eZ 截取,举例来说,查看下 crond 与 bash 这两只程序是否有被限制?
[root@study ~]# ps -eZ | grep -E 'cron|bash'
system_u:system_r:crond_t:s0-s0:c0.c1023 1340 ? 00:00:00 atd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 13888 tty2 00:00:00 bash
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 28054 pts/0 00:00:00 bash
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 28094 pts/0 00:00:00 bash
system_u:system_r:crond_t:s0-s0:c0.c1023 28174 ? 00:00:00 crond
如前所述,在目前 target 这个政策下面,只有第三个类型 (type) 字段会有影响,因此我们上表仅列出第三个字段的数据而已。 我们可以看到, crond 确实是有受限的主体程序,而 bash 因为是本机程序,因此就是不受限 (unconfined_t) 的类型。也就是说, bash 是不需要经过SELinux安全流程,而是直接去判断 rwx。
通过 getenforce,查看目前的 SELinux 模式。
[root@study ~]# getenforce
Enforcing <==目前的模式为 Enforcing
sestatus 来查看SELinux 的政策 (Policy)。
[root@study ~]# sestatus [-vb]
选项与参数:
-v :检查列于 /etc/sestatus.conf 内的文件与程序的安全性本文内容;
-b :将目前政策的规则布林值列出,亦即某些规则 (rule) 是否要启动 (0/1) 之意
范例一:列出目前的 SELinux 使用哪个政策 (Policy)?
[root@study ~]# sestatus
SELinux status: enabled <==是否启动 SELinux
SELinuxfs mount: /sys/fs/selinux <==SELinux 的相关文件数据挂载点
SELinux root directory: /etc/selinux <==SELinux 的根目录所在
Loaded policy name: targeted <==目前的政策为何?
Current mode: enforcing <==目前的模式
Mode from config file: enforcing <==目前配置文件内规范的 SELinux 模式
Policy MLS status: enabled <==是否含有 MLS 的模式机制
Policy deny_unknown status: allowed <==是否默认抵挡未知的主体程序
Max kernel policy version: 28
SELinux 的配置文件 /etc/selinux/config 。
[root@study ~]# vim /etc/selinux/config
SELINUX=enforcing <==调整 enforcing|disabled|permissive
SELINUXTYPE=targeted <==目前仅有 targeted, mls, minimum 三种政策
若有需要修改默认政策的话,就直接改 SELINUX=enforcing 那一行即可。
SELinux 的启动与关闭
如果改变了政策则需要重启;如果由 enforcing 或 permissive 改成 disabled ,或由 disabled 改成其他两个,那也必须要重启。这是因为 SELinux 是整合到核心里面去的, 你只可以在 SELinux 运行下切换成为强制 (enforcing) 或宽容 (permissive) 模式,不能够直接关闭 SELinux !
要注意的是,如果从 disable 转到启动 SELinux 的模式时, 由于系统必须要针对文件写入安全性本文的信息,因此开机过程会花费不少时间在等待重新写入 SELinux 安全性本文 (有时也称为 SELinux Label) ,而且在写完之后还得要再次的重启一次!你必须要等待很长一段时间,等到下次开机成功后,再使用 getenforce 或 sestatus 来观察看看有否成功的启动到 Enforcing 的模式。
如果你已经在 Enforcing 的模式,但是可能由于一些设置的问题导致 SELinux 让某些服务无法正常的运行, 此时你可以将 Enforcing 的模式改为宽容 (permissive) 的模式,让 SELinux 只会警告无法顺利连线的讯息, 而不是直接抵挡主体程序的读取权限。让 SELinux 模式在 enforcing 与 permissive 之间切换的方法为:
[root@study ~]# setenforce [0|1]
选项与参数:
0 :转成 permissive 宽容模式;
1 :转成 Enforcing 强制模式
范例一:将 SELinux 在 Enforcing 与 permissive 之间切换与观察
[root@study ~]# setenforce 0
[root@study ~]# getenforce
Permissive
[root@study ~]# setenforce 1
[root@study ~]# getenforce
Enforcing
不过请注意, setenforce 无法在 Disabled 的模式下面进行模式的切换。
在某些特殊的情况下面,从 Disabled 切换成 Enforcing 之后,竟然有一堆服务无法顺利启动,都会跟你说在 /lib/xxx 里面的数据没有权限读取,所以启动失败。这大多是由于在重新写入 SELinux type (Relabel) 出错之故,使用 Permissive 就没有这个错误。那如何处理呢?最简单的方法就是在 Permissive 的状态下,使用“ restorecon -Rv / ”重新还原所有 SELinux 的类型,就能够处理这个错误。
5.4 SELinux 政策内的规则管理
通过 getsebool 来查看目前这个政策里面到底有多少会影响到主体程序的规则。
SELinux 各个规则的布尔值查询 getsebool
如果想要查询系统上面全部规则的启动与否 (on/off,亦即布尔值),很简单的通过 sestatus -b 或 getsebool -a 均可。
[root@study ~]# getsebool [-a] [规则的名称]
选项与参数:
-a :列出目前系统上面的所有 SELinux 规则的布林值为打开或关闭值
范例一:查询本系统内所有的布林值设置状况
[root@study ~]# getsebool -a
abrt_anon_write --> off
abrt_handle_event --> off
....(中间省略)....
cron_can_relabel --> off # 这个跟 cornd 比较有关!
cron_userdomain_transition --> on
....(中间省略)....
httpd_enable_homedirs --> off # 这当然就是跟网页,亦即 http 有关!
....(下面省略)....
SELinux 各个规则规范的主体程序能够读取的文件 SELinux type 查询 seinfo, sesearch
我们现在知道有这么多的 SELinux 规则,但是每个规则内到底是在限制什么东西?如果你想要知道的话,那就得要使用 seinfo 等工具,需要下载安装。
[root@study ~]# seinfo [-Atrub]
选项与参数:
-A :列出 SELinux 的状态、规则布林值、身份识别、角色、类别等所有信息
-u :列出 SELinux 的所有身份识别 (user) 种类
-r :列出 SELinux 的所有角色 (role) 种类
-t :列出 SELinux 的所有类别 (type) 种类
-b :列出所有规则的种类 (布尔值)
范例一:列出 SELinux 在此政策下的统计状态
[root@study ~]# seinfo
Statistics for policy file: /sys/fs/selinux/policy
Policy Version & Type: v.28 (binary, mls)
Classes: 83 Permissions: 255
Sensitivities: 1 Categories: 1024
Types: 4620 Attributes: 357
Users: 8 Roles: 14
Booleans: 295 Cond. Expr.: 346
Allow: 102249 Neverallow: 0
Auditallow: 160 Dontaudit: 8413
Type_trans: 16863 Type_change: 74
Type_member: 35 Role allow: 30
Role_trans: 412 Range_trans: 5439
....(下面省略)....
# 从上面我们可以看到这个政策是 targeted ,此政策的安全本文类别有 4620 个;
# 而各种 SELinux 的规则 (Booleans) 共制订了 295 条!
如果你想要查询目前所有的身份识别与角色,就使用“ seinfo -u ”及“ seinfo -r ”就可以知道了。至于简单的统计数据,就直接输入 seinfo 即可 。
[root@study ~]# sesearch [-A] [-s 主体类别] [-t 目标类别] [-b 布尔值]
选项与参数:
-A :列出后面数据中,允许“读取或放行”的相关数据
-t :后面还要接类别,例如 -t httpd_t
-b :后面还要接SELinux的规则,例如 -b httpd_enable_ftp_server
范例一:找出 crond_t 这个主体程序能够读取的文件 SELinux type
[root@study ~]# sesearch -A -s crond_t | grep spool
allow crond_t system_cron_spool_t : file { ioctl read write create getattr ..
allow crond_t system_cron_spool_t : dir { ioctl read getattr lock search op..
allow crond_t user_cron_spool_t : file { ioctl read write create getattr se..
allow crond_t user_cron_spool_t : dir { ioctl read write getattr lock add_n..
allow crond_t user_cron_spool_t : lnk_file { read getattr } ;
# allow 后面接主体程序以及文件的 SELinux type,上面的数据是截取出来的,
# 意思是说,crond_t 可以读取 system_cron_spool_t 的文件/目录类型~等等!
范例二:找出 crond_t 是否能够读取 /etc/cron.d/checktime 这个我们自订的配置文件?
[root@study ~]# ll -Z /etc/cron.d/checktime
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 /etc/cron.d/checktime
# 两个重点,一个是 SELinux type 为 admin_home_t,一个是文件 (file)
[root@study ~]# sesearch -A -s crond_t | grep admin_home_t
allow domain admin_home_t : dir { getattr search open } ;
allow domain admin_home_t : lnk_file { read getattr } ;
allow crond_t admin_home_t : dir { ioctl read getattr lock search open } ;
allow crond_t admin_home_t : lnk_file { read getattr } ;
# 仔细看!看仔细~虽然有 crond_t admin_home_t 存在,但是这是总体的信息,
# 并没有针对某些规则的寻找~所以还是不确定 checktime 能否被读取。但是,基本上就是 SELinux
# type 出问题~因此才会无法读取的!
修改 SELinux 规则的布尔值 setsebool
[root@study ~]# setsebool [-P] “规则名称” [0|1]
选项与参数:
-P :直接将设置值写入配置文件,该设置数据未来会生效的!
范例一:查询 httpd_enable_homedirs 这个规则的状态,并且修改这个规则成为不同的布尔值
[root@study ~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off <==结果是 off ,依题意给他启动看看!
[root@study ~]# setsebool -P httpd_enable_homedirs 1 # 会跑很久很久!请耐心等待!
[root@study ~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> on
这个 setsebool 最好记得一定要加上 -P 的选项,因为这样才能将此设置写入配置文件。
5.5 SELinux 安全本文的修改
现在我们知道 SELinux 对受限的主体程序有没有影响,第一关考虑 SELinux 的三种类型,第二关考虑 SELinux 的政策规则是否放行,第三关则是开始比对 SELinux type 。
使用 chcon 手动修改文件的 SELinux type
[root@study ~]# chcon [-R] [-t type] [-u user] [-r role] 文件
[root@study ~]# chcon [-R] --reference=范例档 文件
选项与参数:
-R :连同该目录下的次目录也同时修改;
-t :后面接安全性本文的类型字段!例如 httpd_sys_content_t ;
-u :后面接身份识别,例如 system_u; (不重要)
-r :后面街角色,例如 system_r; (不重要)
-v :若有变化成功,请将变动的结果列出来
--reference=范例档:拿某个文件当范例来修改后续接的文件的类型
范例一:查询一下 /etc/hosts 的 SELinux type,并将该类型套用到 /etc/cron.d/checktime 上
[root@study ~]# ll -Z /etc/hosts
-rw-r--r--. root root system_u:object_r:net_conf_t:s0 /etc/hosts
[root@study ~]# chcon -v -t net_conf_t /etc/cron.d/checktime
changing security context of ‘/etc/cron.d/checktime’
[root@study ~]# ll -Z /etc/cron.d/checktime
-rw-r--r--. root root unconfined_u:object_r:net_conf_t:s0 /etc/cron.d/checktime
范例二:直接以 /etc/shadow SELinux type 套用到 /etc/cron.d/checktime 上!
[root@study ~]# chcon -v --reference=/etc/shadow /etc/cron.d/checktime
[root@study ~]# ll -Z /etc/shadow /etc/cron.d/checktime
-rw-r--r--. root root system_u:object_r:shadow_t:s0 /etc/cron.d/checktime
----------. root root system_u:object_r:shadow_t:s0 /etc/shadow
上面的练习“都没有正确的解答”。因为正确的 SELinux type 应该就是要以 /etc/cron.d/ 下面的文件为标准来处理。既然如此,能不能让 SELinux 自己解决默认目录下的 SELinux type 呢?可以!用 restorecon 。
使用 restorecon 让文件恢复正确的 SELinux type
[root@study ~]# restorecon [-Rv] 文件或目录
选项与参数:
-R :连同次目录一起修改;
-v :将过程显示到屏幕上
范例三:将 /etc/cron.d/ 下面的文件通通恢复成默认的 SELinux type
[root@study ~]# restorecon -Rv /etc/cron.d
restorecon reset /etc/cron.d/checktime context system_u:object_r:shadow_t:s0->
system_u:object_r:system_cron_spool_t:s0
# 上面这两行其实是同一行,表示将 checktime 由 shadow_t 改为 system_cron_spool_t
范例四:重新启动 crond 看看有没有正确启动 checktime
[root@study ~]# systemctl restart crond
[root@study ~]# tail /var/log/cron
# 再去瞧瞧这个 /var/log/cron 的内容,应该就没有错误信息了
可以忽略 chcon 这个指令了,因为 restorecon 主动的回复默认的 SELinux type 要简单很多,而且可以一口气恢复整个目录下的文件。所以,几乎只要记得 restorecon 搭配 -Rv 同时加上某个目录这样的指令串即可,修改 SELinux 的 type 就变得非常的轻松。
semanage 默认目录的安全性本文查询与修改
为什么 restorecon 可以恢复原本的 SELinux type 呢?肯定有记录每个文件/目录的 SELinux 默认类型。没错!是这样~那要如何 (1)查询默认的 SELinux type 以及 (2)如何增加/修改/删除默认的 SELinux type 呢?通过 semanage 即可,他是这样使用的:
[root@study ~]# semanage {login|user|port|interface|fcontext|translation} -l
[root@study ~]# semanage fcontext -{a|d|m} [-frst] file_spec
选项与参数:
fcontext :主要用在安全性本文方面的用途,-l :为查询的意思;
-a :增加,你可以增加一些目录的默认安全性本文类型设置;
-m :修改;
-d :删除。
范例一:查询一下 /etc /etc/cron.d 的默认 SELinux type 为何?
[root@study ~]# semanage fcontext -l | grep -E '^/etc |^/etc/cron'
SELinux fcontext type Context
/etc all files system_u:object_r:etc_t:s0
/etc/cron\.d(/.*)? all files system_u:object_r:system_cron_spool_t:s0
略
突然有一天假期结束,时来运转,人生才是真正开始了。