前言
以上内容是博主学习了 《鸟哥的Linux私房菜》 这本书后整理总结的,作者语言幽默风趣,把枯燥的操作系统指令进行了生动地讲解,让博主受益匪浅!在此感谢鸟哥大佬的无私的知识分享!
在鸟哥原书中,进程管理这一章节后面还有一些内容,但受限于博主的时间与精力不足,便只整理了实用的部分,若对剩余内容感兴趣的朋友,可以自行阅读原书,相信一定能收获良多。
最后,本文若有错漏以及不足之处,欢迎朋友们在评论区进行批评指正。如果本文对您有所帮助,就给博主点赞+关注
吧!
看前导入
当一个程序被载入到内存中运行,那么在内存中的那个程序就被称为进程(process
)。进程是操作系统上非常重要的概念, 所有系统上面跑的数据都会以进程的形态存在。
那么系统的进程有哪些状态?不同的状态会如何影响系统的运行? 进程之间是否可以互相控管等等, 这些问题都是由本章“进程管理”来讲解的,我们一起来学习吧。
一、什么是进程(process)
在Linux系统当中,触发任何一个事件时,系统都会将它定义为一个进程,并且给予这个进程一个ID
,称为PID
,同时依据启动这个进程的使用者与相关属性关系,给予这个PID
一组有效的权限设置。
从此,这个 PID
能够在系统上面进行的动作,就与其权限有关了。那么什么叫做 “触发事件”?在什么情况下会触发一个事件?同一个事件是否能被触发多次?我们继续往下看,解决这些问题。
1. 程序与进程
1.1 程序与进程的区别
如何产生一个进程呢?其实很简单,“执行一个程序或指令”就可以触发一个进程,而取得一个 PID
。系统是只认识二进制文件的,当我们要让系统工作的时候,需要启动一个二进制文件,这个二进制文件就是程序 (program
) 。
我们知道,每个程序都有三组人马的权限,每组人马都具有 r/w/x
的权限,所以,当不同的使用者执行这个程序(program
)时,系统给予的权限也都不相同。
举例来说,我们可以利用 touch
指令来创建一个空的文件,当 root用户执行这个 touch
指令时,他取得的是 UID/GID = 0/0
的权限,而当 dmtsai 用户(UID/GID=501/501
) 执行这个 touch
指令 时,他取得的权限就跟 root 用户不同了。
举个更常见的例子,我们要操作系统的时候,通常是利用连线程序或者直接在主机前面登陆,然后取得我们的 shell
,我们的 shell都是 bash ,这个 bash 在 /bin/bash
程序下。那么在同一个时间的每个用户登陆都是执行 /bin/bash
这个程序,不过,每个用户得到的权限不同。我们可以这样看:
也就是说,当我们登陆并执行 bash 时,系统已经给我们分配一个 PID
了,这个 PID
就是依据登陆者的 UID/GID (/etc/passwd)
来分配的。 当 dmtsai 登陆后,他取得一个 PID
号码为 2234 的进程,这个进程的 User/Group
都是 dmtsai
,而当这个进程进行其他动作时,例如上面提到的 touch
这个指令, 那么由这个进程衍生出来的其他进程在一般状态下,也会沿用 2234这个进程的相关权限的。
让我们将程序与进程作个总结:
- 程序 (
program
):通常为二进制文件 ,放置在储存媒体中 (如硬盘、光盘、软 盘、磁带等), 为实体文件的形态存在;- 进程 (
process
):程序被触发后,执行者的权限与属性、程序的代码与所需数据等都会被载入内存中, 操作系统会给予这个内存中的单元一个识别码 (PID
)。可以说,进程就是一个正在运行中的程序。
1.2 子进程与父进程
在上面的说明里面,我们有提到所谓的“衍生出来的进程”。当用户登陆系统后,会取得一个 bash 的 shell ,然后,用户使用这个 bash 提供的接口去执行另一个指令,例如touch
指令等等。这些另外执行的指令也会被触发成为一个个 PID
,后来执行指令而产生的 PID
就是“子进程”,而在我们原本的 bash 环境下的这个进程,就称为 “父进程”。
以上面的图示来看,连续执行两个 bash 后,第二个 bash 的父进程就是第一个 bash。因为每个进程都有一个 PID
,那某个进程的父进程该如何判断?通过 Parent PID (PPID)
来判断即可。
例题:请在目前的 bash 环境下,再触发一次 bash ,并以“
ps -l
”这个指令观察程序相关的输出信息。
[root@LinEYueLunix01 ~] ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 8930 8922 0 80 0 - 29163 do_wai pts/0 00:00:00 bash
0 R 0 8974 8930 0 80 0 - 38309 - pts/0 00:00:00 ps
[root@LinEYueLunix01 ~] bash # 再次触发一次bash
[root@LinEYueLunix01 ~] ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 8930 8922 0 80 0 - 29163 do_wai pts/0 00:00:00 bash
4 S 0 8983 8930 1 80 0 - 29215 do_wai pts/0 00:00:00 bash
0 R 0 9013 8983 0 80 0 - 38309 - pts/0 00:00:00 ps
# 可以发现,第二个bash 进程的PPID与第一个bash 进程的PID相同,说明第二个 bash 的父进程就是第一个 bash。
在第一次执行ps -l
指令时,只显示了两个进程,当再一次触发bash 指令后,执行ps -l
指令,出现了三个进程。我们注意到,第二个bash 进程的PPID
与第一个bash 进程的PID
相同都是 8930,说明第二个 bash 的父进程就是第一个 bash。
另外,每部主机的程序启动状态都不一样, 所以在你的系统上面看到的 PID
与本例显示不同,那是正常的。 详细的 ps
指令我们会在稍后介绍, 这里只要知道 ps -l
可以查阅到相关的进程信息即可。
1.3 进程调用的流程(fork and exec)
其实子进程与父进程之间的关系还挺复杂的,最大的复杂点在于进程互相之间的调用。在 Linux 的进程调用通常称为 fork-and-exec
的流程。
进程都会借由父进程以复制 (fork
) 的方式产生一个与父进程一模一样的子进程, 然后被复制出来的子进程再以 exec
的方式来执行实际要进行的程序,最终就成为一个子进程的存在。 如下图:
系统先以 fork
的方式复制一个与父进程相同的暂存进程,这个进程与父进程唯一的差别就是 PID
不同! 但是这个暂存进程还会多一个 PPID
的参数,PPID
如前所述,与父程序的PID
相同。
暂存进程开始以 exec
的方式载入实际要执行的程序,以上述图示来讲,新的进程代码为 qqq ,最终子进程的代码就会变成 qqq 了。
1.4 系统或网络服务:常驻在内存的进程
我们之前下达的简单指令都是执行完就结束了,也就是说,该项指令被触发后所产生的 PID 很快就会终止。 那有没有一直在执行的程序?当然有。
举个简单的例子来说好了,系统每分钟都会去扫瞄
/etc/crontab
以及相关的配置文件, 来进行工作调度。工作调度是crond
这个程序所管理的,我们将它启动在背景当中一直持续不断的运行,这个crond
指令产生的进程就是 “常驻在内存当中的进程”了。
常驻在内存当中的进程通常都是负责一些系统所提供的功能以服务使用者各项任务,因此这些常驻进程被称为:服务 (daemon
)。
系统的服务非常多, 不过主要大致分成系统本身所需要的服务,例如刚刚提到的 crond
及 atd
,还有 rsyslogd
等等。还有一些则是负责网络连线的服务,例如 Apache, named, postfix, vsftpd...
等等,这些指令就不在本文中讲解啦。
2. Linux 的多用户多任务环境
我们现在知道了,其实在 Linux 下面执行一个指令时,系统会将相关的权限、属性、代码与数据等均载入内存, 并给予这个内存单元一个程序识别码 (PID),最终该指令可以进行的动作则与这个 PID 的权限有关。
根据这个说明,我们就可以简单的了解,为什么 Linux 这么多用户,但是每个用户都可以拥有自己的环境了。下面我们来谈谈 Linux 的多用户多任务环境特色。
2.1 多人环境
什么是 “多用户多任务”?在 Linux 系统上具有多种不同的帐号, 每种帐号都有都有其特殊的权限,只有一个账号具有至高无上的权力,那就是 root (系统管理员)。除了 root 之外,其他用户都必须要受一些限制。
而每个用户进入 Linux 的环境设置都可以随着个人的喜好来设置 ,是因为每个用户登陆后取得的 shell 进程的 PID
不同。
2.2 多任务行为
我们的 Linux 可以让 CPU 在各个工作间进行切换, 也就是说, 其实每个工作都仅占去 CPU 的几个指令次数,所以 CPU 每秒能够在各个进程之间进行切换。
CPU 切换进程的工作,与这些工作进入到 CPU 运行的调度 会影响到系统的整体性能。 目前 Linux 使用的多任务切换行为是非常棒的一个机制,几乎可以将 PC 的性能整个压榨出来。 由于性能非常好,因此当多用户同时登陆系统时,会感受到整部主机好像就为了自己而存在一般。 这就是多用户多任务的环境。
2.3 多重登陆环境的七个基本终端窗口
Linux 当中,默认提供了六个文字界面登陆窗口,以及一个图形界面,你可以使用 [Alt] + [F1].....[F7]
来切换不同的终端机界面,而且每个终端机界面的登陆者还可以是不同用户。
其实,这也是多任务环境下所产生的一个情况, Linux 默认会启动六个终端机登陆环境的程序,所以我们就会有六个终端机接口。 我们通过减少启动的终端机程序来减少登录环境就可以了。
2.4 特殊的程序管理行为
Linux 几乎可以说绝对不会死机。因为它可以在任何时候, 将某个被困住的进程杀掉,然后再重新执行该程序而不用重新开机。
例如,如果我在 Linux 下以文字界面登陆,在屏幕当中显示错误讯息后就卡住了, 这个时候那默认的七个窗口就帮上忙了,你可以随意的按 [Alt]+[F1].....[F7]
来 切换到其他的终端机界面,然后以ps -aux
指令找出刚刚的错误进程,然后给它 kill
一下, 回到刚刚的终端机界面就发现恢复正常了.
二、工作管理 (job control)
工作管理 (job control
) 是使用在 bash 环境下的,也就是说:“当用户登陆系统取得 bash shell 之后,在单一终端机接口下同时进行多个工作的行为管理 ”。
举例来说,我们在登陆 bash 后, 想要一边复制文件、一边进行数据搜寻、一边进行编译,还可以一边进行 vim
程序撰写(当然我们可以重复登陆进那六个命令行的终端机环境中)。不过,能不能在单一终端机接口下的一个 bash 内完成这些工作呢? 当然可以啊,就是使用 job control
。
1. 什么是工作管理?
“ 进行工作管理时, 每个工作都是目前 bash 的子进程,它们彼此之间是有相关性的。 我们无法以 job control 的方式由终端接口1 的环境去管理 终端接口2 环境 的 bash 。” 这个概念得先创建起来,后续的范例介绍之后,你就会清楚的了解工作管理的概念。
由于假设我们只有一个终端接口,因此可以出现提示字符让你操作的环境就称为前景 (foreground),至于其他工作就可以让你放入背景 (background) 去暂停或运行。 要注意的是,放入背景的工作想要运行时, 它必须不能够与用户互动。
举例来说, vim 指令是绝对不可能在背景里面执行的(running) !因为如果没有输入数据 vim 是不会执行任何动作的。而且放入背景的工作是不可以使用 [ctrl]+c 来终止。
总之,要进行 bash 的 job control 必须要注意到的限制是:
- 这些工作所触发的程序必须来自于你 shell 的子进程(只管理自己的 bash)。
- 前景:你可以控制与下达指令的这个环境称为前景的工作 (
foreground
)。 - 背景:可以自动运行的工作,你无法使用
[ctrl]+c
终止它,但可使用bg / fg
指令调用该工作。 - 背景中“执行”的工作不能等待
terminal /shell
的输入(input
)。
2. job control 的管理
如前所述,每一个用户的 bash 只能够管理自己的工作而不能管理其他用户的 bash 的工作,所以即使你是 root用户也不能够将别人的 bash 下面的 job
拿过来执行。
此外,job
的环境又分前景与背景,然后在背景里面的job
状态又可以分为“暂停 (stop
)”与“运行中 (running
)。
接下来我们谈谈实际进行 job
控制的指令
2.1 直接将指令丢到背景中“执行”的(&)
如果想要同时进行多个工作, 那么可以将某些工作直接丢到背景环境当中,让我们可以继续操作前景的工作。那么如何将工作丢到背景中? 最简单的方法就是利用(&)这个指令。
举个简单的例子,我们要将
/etc/
整个备份成为/tmp/etc.tar.gz
且不想要等待,那么可以这样做:
分析:第二行,在中括号 []
内的号码为工作号码 (job number
),该号码与 bash 的控制有关。后续的 10326 则是这个工作在系统中的 PID
。至于第三行出现的数据则是 tar
执行完成后的数据流。
由于我们没有加上数据流重导向,所以会影响前景画面,便出现了第三行的数据,不过不会影响前景的操作。
用法:输入一个指令后,在该指令的最后面加上一个(&
) 代表将该指令丢到背景中, 此时 bash 会给予这个指令一个 “工作号码(job number
)”,就是那个 [1]
,至于后面的10326 则是该指令所触发的“ PID
”了。
那么丢到背景中的工作什么时候完成?完成的时候会显示什么?如果你输入几个指令后,突然出现下面的数据:
这就代表 [1]
这个工作已经完成 (Done
) ,该工作执行的指令则是接在后面那一串命令行。
符号(&
)代表:“将工作丢到背景中去执行”。 注意到“执行”的字眼!这样的情况最大的好处是: 不怕被 [ctrl] + c
中断
将工作丢到背景当中执行时要特别注意数据流的流向。上面的指令就出现了错误数据流信息,导致前景显示被影响。如果将上面的指令改为下面:
你会发现你的屏幕输出了很多数据,是因为:在背景当中执行的指令,如果有标准输出及标准错误信息时,它的数据依旧是输出到前景屏幕上面的 , 所以,我们将无法看到提示字符,当然也就无法完好的掌握前景正在进行的工作。
同时由于在背景工作的是 tar
指令 , 所以此时你无论怎么按下 [ctrl] + c
也无法停止屏幕被搞的花花绿绿。所以,最佳的方法就是利用数据流重导向, 将输出数据流传送至某个文件中。
举例来说:
(>
) 符号将背景工作输出的数据流都传送到 /tmp/log.txt
文件当中,就不会影响到我们前景的工作了。 这样说,应该可以更清楚数据流重导向的重要性了吧。
2.2 将“目前”的工作丢到背景中“暂停”:[ctrl] z
如果我正在使用
vim
,却发现我有个文件不知道放在哪里,需要到 bash 环境下进行搜寻,此时是否要结束vim
?当然不!只要暂时将vim
工作丢到背景当中等待/暂停即可。例如下面的案例:
[root@study ~]$ vim ~/.bashrc
# 在 vim 的一般模式下,按下 [ctrl]-z 这两个按键
[1]+ Stopped vim ~/.bashrc
[root@study ~]$ # 顺利取得了前景的操控权!
[root@study ~]$ find / -print
....(输出省略)....
此时屏幕会非常的忙碌!因为屏幕上会显示所有的文件名。请按下 [ctrl] z 暂停
[2]+ Stopped find / -print
[root@study ~]$ # 再次顺利取得了前景的操控权!
用法:vim
的一般模式下,按下 [ctrl] z
这两个按键,屏幕上会出现 [1]
,表示这是第一个工作, 而那个 “+
” 代表最近一个被丢进背景的工作,而且是在目前背景下默认会被取用的那个工作 (与 fg
这个指令有关 )。而 Stopped
则代表目前这个工作的状态。在默认的情况下, 使用 [ctrl] z
丢到背景当中的工作都是“暂停”的状态。
2.3 观察目前的背景工作状态: jobs
如果想要知道目前有多少的工作在背景当中,就用jobs
这个指令。一般来说,直接下达 jobs
即可。 如果想知道该 job number
的 PID
号码,可以加上-l
这个参数。
[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]+
的工作会第一个被拿到前景当中来处理”。
其实(+
)号代表最后一个被放到背景的工作号码, (-
)号代表最后第二个被放置到背景中的工作号码。 而超过最后第三个以后的工作,就不会有 “ +/-
” 符号存在了。
2.4 将背景工作拿到前景来处理:fg
将背景工作拿到前景来处理的指令使用
fg (foreground)
,举例来说,我们想要将上头范例当中的工作拿出来处理时:
[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
” 将第 [1]
号工作捉到前景后又放回背景,此时最后一个被放入背景的将变成 vim
那个指令动作, 所以当然[1]
后面就会出现“ +
”了。
另外,如果输入“ fg -
” 则代表将“-
”号的那个工作号码拿出来,以上面最后为例,就是拿出 [2]-
这个工作号码。
2.5 让工作在背景下的状态变成运行中: bg
如何让一个工作在背景下面直接“ 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 &
我们可以看到,工作 [3]
以经由 Stopping
变成了 Running
。注意![3]
号命令行最后方多了一个“ &
” 的符号, 代表该工作被启动在背景当中了。
2.6 管理背景当中的工作: kill
刚刚我们可以让一个已经在背景当中的工作继续工作,也可以让该工作以 fg
拿到前景来。 那么,如果想要将该工作直接移除呢?或者是将该工作重新启动呢?这个时候就得需要给予该工作一个讯号 (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 -15 %1
# -SIGTERM 与 -15 是一样的!您可以使用 kill -l 来查阅!
# 不过在这个案例中, vim 的工作无法被结束喔!因为他无法通过 kill 正常终止的意思!
特别留意一下, -9
这个 signal
通常是用在“强制删除一个不正常的工作”时所使用的, -15
则 是以正常步骤结束一项工作(15
也是默认值),两者之间并不相同。通常使用 -9
是因为某些进程你真的不知道怎么通过正常手段去终止它,这才用到 -9
。
另外, kill
后面接的数字默认会是 PID
,如果想要管理 bash 的工作控制,就得要加上 “ %数字
” 了, 这点也得特别留意。signal
除了以数值来表示之外,也可以使用讯号名称表示。
关于 kill指令,我们在后面还会详细介绍。
3. 离线管理问题
要注意的是,我们在工作管理当中提到的“背景”指的是在终端机模式下可以避免 [crtl] c
中断 的一个情境, 你可以说那个是 bash 的背景,并不是放到系统的背景中!所以,工作管理的背景依旧与终端机有关。
在这样的情况下,如果你是以远端连线方式连接到你的 Linux 主机,并且将工作以(&
)的方式放到背景去, 请问,在工作尚未结束的情况下你离线了,该工作还会继续进行吗?答案是“否”。不会继续进行,而是会被中断掉。
如果我的工作需要进行一大段时间,又不能放置在背景下面,那该如何处理? 你可以尝试使用
nohup
这个指令来处理,这个nohup
可以让你在离线或登出系统后,还能够让工作继续进行。
- 用法如下:
[root@study ~]$ nohup [指令与参数] # 在终端机前景中工作
[root@study ~]$ nohup [指令与参数] # 在终端机背景中工作
上述指令需要注意的是, nohup
并不支持 bash 内置的指令,因此你的指令必须要是外部指令才行。
如果想要让在背景的工作在你登出后还能够继续的执行,那么使用 nohup
搭配 “&
” 是不错的运行情境, 可以参考看看。
三、进程管理
为什么程序管理这么重要呢?
- 我们在操作系统时的各项工作其实都是经过某个
PID
来达成的 (包括你的 bash 环境),因此,能不能进行某项工作,就与该进程的权限有关了。 - 当整个系统资源快要被使用光时, 您是否能够找出最耗系统的那个进程,然后删除该进程,让系统恢复正常呢?
- 如果由于某个程序写的不好,导致产生一个有问题的进程在内存当中,您又该如何找出他,然后将他移除?
- 如果同时有五六项工作在您的系统当中运行,但其中有一项工作才是最重要的, 该如何 让那一项重要的工作被最优先执行?
所以,一个称职的系统管理员,必须要熟悉进程的管理流程才行,否则当系统发生问题时,真的很难解决问题 。下面我们会先介绍如何观察进程与程序的状态,然后再加以进程控制。
1. 程序的观察
我们如何查阅系统上面正在运行当中的程序呢?很简单,利用静态的 ps
指令 或者是动态的 top
指令,还能以 pstree
指令来查阅进程树之间的关系。
1.1 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
”。注意,你没看错,是“ps aux
”,没有那个减号 (-
)。
1.1.1 仅观察自己的 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
4 S 0 14836 14835 0 80 0 - 29035 wait pts/0 00:00:00 bash
0 R 0 15011 14836 0 80 0 - 30319 - pts/0 00:00:00 ps
# 还记得鸟哥说过,非必要不要使用 root 直接登陆吧?从这个 ps -l 的分析,你也可以发现
# 鸟哥其实是使用 sudo 才转成 root 的身份。否则连测试机,鸟哥都是使用一般帐号登陆的!
系统整体的进程运行是非常多的,但如果使用’ ps -l
'则仅列出与你的操作环境 (bash) 有关的进程而已, 即最上层的父进程会是你自己的 bash 而没有延伸到 systemd
(后续会讲解) 这支进程。
看看第一行各个字段代表什么含义:
所以你看到的’ ps -l
'输出信息中,说明的是:“bash 的进程属于 UID
为 0
的使用者,状态为睡眠 (sleep
), 之所以为睡眠因为他触发了 ps指令 (状态为 run
) 之故。此程序的 PID
为 14836,优先执行顺序为 80 , 下达 bash 所取得的终端接口为 pts/0
,运行状态为等待(wait
)。
1.1.2 观察系统所有程序: 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 aux
你会发现’ ps -l
‘与’ ps aux
'显示的项目并不相同。 在ps aux
指令显示的项目中,各字段的意义为:
一般来说,'ps aux
’会依照 PID
的顺序来排序显示,我们还是以 14836 那个 PID
那行来说明。 该行的意义为 “ root 用户执行的 bash PID
为 14836,占用了 0.1%
的内存容量百分比,状态为休眠 (S
),该进程启动的时间为 8 月 4 号,因此启动太久了, 所以没有列出实际的时间点。且取得的终端机环境为 pts/0
。” 与’ ps -l
'指令看到的其实是同一个进程。
1.1.3 ps 指令的其他选项
用法如下:
除此之外,我们必须要知道的是“僵尸 (zombie
) ”进程是什么? 通常,造成僵尸进程是因为某进程应该已经执行完毕,或者是因故应该要终止了, 但是该进程的父进程却无法完整的将该进程结束掉,从而造成该进程一直存在于内存中。 如果你发现在某个进程的 CMD 字段后面还接上 <defunct>
时,就代表该进程是僵尸进程。
如果你发现系统中有僵尸进程时,记得要找出该进程的父进程,然后做个追踪,进行主机的环境最优化,看看有什么地方需要改善,不要只是直接将该僵尸进程kill
掉而已。不然的话,万一它一直产生,可就麻烦了。
事实上,通常僵尸进程都已经无法控管,而是应该直接交给 systemd
这支进程来负责了。偏偏 systemd
进程是系统第一支执行的程序, 他是所有进程的父进程。我们是无法杀掉systemd
进程的 (杀掉他,系统就死掉了)。所以,如果产生僵尸进程, 而系统过一阵子还没有办法通过核心非经常性的特殊处理来删除该僵尸进程时,那只能通过 reboot
的方式来将该进程抹去了。
1.2 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
指令 与 top
指令显示的画面:
不同于 ps
指令是静态的结果输出, top
这个指令可以持续的监测整个系统的进程工作状态。 在默认的情况下,每次更新程序资源的时间为 5 秒,不过,可以使用 -d
选项来进行修改。
以上图为例,top
指令输出主要分为两个画面,上面的画面为整个系统的资源使用状态,基本上总共有六行,显示的内容依序是:
至于top
下半部分的画面,则是每个 process
使用的资源情况。比较需要注意的是:
top
默认使用 CPU 使用率 (%CPU
) 作为排序的依据,如果你想要依据内存使用率排序,则可以按下“M
”键, 如果想要离开 top
动态界面则按下“q
”键。
如果你想要将 top
的结果输出成为文件时,可以这样做:
# 范例二:将 top 的输出信息执行 2 次,然后将结果输出到 /tmp/top.txt
[root@study ~]$ top -b -n 2 > /tmp/top.txt
# 这样一来,就可以将 top 的输出信息存到 /tmp/top.txt 文件中了。
那如果你想要观察的进程 CPU 与内存使用率都很低,结果老是无法在第一行显示时,该怎办?我们可以仅观察单一程序。如下:
那么如果我想要在
top
的画面下进行一些动作,比方说,修改NI
这个数值呢?可以这样做:
输入“r
” 键后,在原来空白的第6行会出现上图的 ’ PID to renice [default pid = 14836]
’ 信息,接着在该行输入想要更改NI
值的进程的 PID 14836
。
在你完成上面的动作后,在第6行状态列会出现:’ Renice PID 29671 to value
’ 提示信息,如下:
然后接着在value
后面输入你想要更改的NI
值,比如说 10, 接下来你就会看到如下的显示画面:
最后一行就是修改了之后所产生的效果,NI
值变成了10。 一般来说,如果想要找出损耗 CPU 资源最大的那个进程时,大多使用的就是 top
指令了,然后强制以 CPU 使用资源来排序 (在 top
当中按下 P
即可),便可以很快得到。
1.3 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 了。
直接输入 pstree
指令可以查到进程相关性,如上表所示,还会使用线段将相关性程序链接起来。
由 pstree
的输出我们也可以很清楚的知道,所有的进程都是依附在 systemd
这支进程下面的。 仔细看一下,这支进程的 PID
是 1号,因为他是由 Linux 核心所主动调用的第一支进程,所以 PID
就是1 号了。 这也是我们刚刚提到僵尸进程,为啥产生僵尸进程后需要重新开机? 因为 systemd
进程要重新启动,而重新启动 systemd
就是 reboot
重启了。
如果还想要知道 PID
与所属使用者,加上 -u
及 -p
两个参数即可。我们前面不是一直提到, 如果子程序挂点或者是老是杀不掉子进程时,该如何找到其父进程吗?用这个’ pstree -u -p
’ 指令就可以了。
2. 进程的管理
进程之间是可以互相控制的,举例来说,你可以关闭、重新启动服务器软件,服务器软件本身是个进程, 你既然可以让它关闭或启动,就是可以控制该进程。
那么程序是如何互相管理的?其实是通过给予该进程一个讯号 (signal
) 去告知该进程你想要让它执行什么任务。
我们也在本章之前的 bash 工作管理当中提到过, 要给予某个已经存在背景中的工作某些动作时,是直接给予一个讯号给该工作号码即可。那么到底有多少 signal
呢? 可以使用’ kill -l
‘(小写的 L ) 或者是’ man 7 signal
'都可以查询到。
主要的讯号代号与名称对应及内容如下:
一般来说,你只要记 得“1, 9, 15
”这三个号码的意义即可。那么我们如何传送一个讯号给某个进程呢?就通过 kill 或 killall这两个指令了。
2.1 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
有重新启动 (restart
) 过了。
了解这个用法以后,如果未来你想要将某个的登陆者的连线删除的话,就可以通过使用 pstree -p
找到相关进程, 然后再以 kill -9
将该进程删除,该条连线就会被踢掉了。
2.2 killall -signal 启动指令名称
由于 kill
后面必须要加上 PID
(或者是 job number
),所以,通常 kill
都会配合 ps, pstree
等指令,因为我们必须要找到相对应的那个进程的 PID
。
有没有可以利用“下达指令的名称”来给予讯号的?举例来说,能不能直接将 rsyslogd
这个进程给予一 个 SIGHUP
的讯号呢?可以的,使用 killall
指令即可。
用法如下:
总之,要删除某个进程,我们可以使用进程的 PID
或者是启动该进程的指令名称。而如果要删除某个服务呢?最简单的方法就是利用 killall
, 因为他可以将系统当中所有以某个指令名称启动的进程全部删除。
3. 关于程序的执行顺序
思考一下,如果所有的进程同时被唤醒,那么 CPU 应该要先处理那个进程?也就是说,哪个进程被执行的优先级比较高? 这就得要考虑到进程的优先执行序 (Priority
) 与 CPU 调度了。
3.1 Priority 与 Nice 值
我们 Linux 给予进程一个所谓的“优先执行序(priority, PRI
)”, 这个 PRI
值越低代表越优先的意思。不过这个 PRI
值是由核心动态调整的, 使用者无法直接调整 PRI
值。
由于 PRI
值是核心动态调整的,我们使用者也无权去干涉 PRI 值。那如果想要调整进程的优先执行序,就得要通过调整 Nice
值了,Nice
值就是前面出现过的 NI
,一般来说, PRI
与 NI
的相关性如下:
PRI(new) = PRI(old) + nice
不过你要特别留意到,如果原本的 PRI
值是 50 ,并不是我们给予 一个 nice = 5
,就会让 PRI
值变成 55 。 因为 PRI
值是系统“动态”决定的,所以,虽然Nice
值是可以影响 PRI
,不过, 最终的 PR
值仍是要经过系统分析后才会决定的。
另外, nice
值是有正负的,而既然 PRI
越小越早被执行, 所以,当 nice
值为负值时,那么该进程就会降低 PRI
值,会较优先被处理。
所以要调整某个进程的优先执行序,就是“调整该进程的 nice
值”。那么如何给予某个进程 nice
值呢?有两种方式,分别是:
- 一开始执行程序就立即给予一个特定的
nice
值:用nice
指令; - 调整某个已经存在的进程的
nice
值:用renice
指令。
3.2 nice :新执行的指令即给予新的 nice 值
用法如下:
通常什么时候要将 nice
值调大呢?举例来说,系统的背景工作中, 某些比较不重要的进程的进行:例如备份工作。由于备份工作相当的耗系统资源, 这个时候就可以将备份的指令之 nice
值调大一些,可以使系统的资源分配的更为公平。
3.3 renice :已存在进程的 nice 重新调整
用法如下:
如果要调整的是已经存在的某个进程的话,那么就得要使用 renice
了。使用的方法很简单, renice
后面接上数值及 PID
即可。因为后面接的是 PID
,所以你务必要以 ps
或者其他进程观察的指令去找出 PID
才行。
由上面这个范例当中我们也看的出来,虽然修改的是 bash 那个程序,但是该程序所触发的 ps
指令当中的 nice
也会继承而为 -5 ,整个 nice
值是可以在父进程 --> 子进程之间传递的。 另外,除了 renice
之外, 前面提到的 top
指令同样可以调整 nice/NI
值。
四、 总结
程序 (program):通常为二进制文件 ,放置在储存媒体中 (如硬盘、光盘、软 盘、磁带等),为实体文件的型态存在;
进程 (process
):程序被触发后,执行者的权限与属性、程序的代码与所需数据等都会被载入内存中, 操作系统并给予这个内存内的单元一个识别码 (PID
),可以说,进程就是一个正在运行中的程序。
进程彼此之间是有相关性的,故有父进程与子进程之分。而 Linux 系统所有程序的父进程就是 init
这个 PID
为 1 号的进程。
在 Linux 的进程调用通常称为 fork-and-exec
的流程!进程都会借由父程序以复制 (fork
) 的方式产生一个一模一样的子进程, 然后被复制出来的子进程再以 exec
的方式来执行实际要进行的程序,最终就成为一个子程序的存在。
常驻在内存当中的进程通常都是负责一些系统所提供的功能以服务使用者各项任务,因此这些常驻进程就会被我们称为:服务 (daemon
)。
在工作管理 (job control
) 中,可以出现提示字符让你操作的环境就称为前景 (foreground
),至于其他工作就可以让你放入背景 (background
) 去暂停或运行。
与 job control
有关的按键与关键字有: & , [ctrl]-z, jobs, fg, bg, kill %n
等。
程序管理的观察指令有: ps, top, pstree
等等。
进程之间是可以互相控制的,传递的讯息 (signal
) 主要通过 kill 这个指令在处理。
进程是有优先顺序的,该字段为 Priority
,但 PRI
是核心动态调整的,使用者只能使用 nice
值去微调 PRI
。
nice
值的调整指令有: nice, renice, top
等指令。