第七章 进程管理
主要内容
1.进程的概念
2.进程及作业
3.管理进程
4.任务计划 7.1 进程的概念 Linux是一个多用户多任务的操作系统。多用户是指多个用户可以在同一时间使用计算机系统。多任务是指Linux可以同时执行几个任务,它可以在还未执行完一个任务时又执行另一项任务。
操作系统管理多个用户的请求和多个任务。大多数系统都只有一个CPU和一个主存,但一个系统可能有多个二级存储磁盘和多个输入/输出设备。操作系统管理这些资源并在多个用户间共享资源,当您提出一个请求时,给您造成一种假象,好像系统只被您独自占用。而实际上操作系统监控着一个等待执行的任务队列,这些任务包括用户作业、操作系统任务、邮件和打印作业等。操作系统根据每个任务的优先级为每个任务分配合适的时间片,每个时间片大约都有零点几秒,虽然看起来很短,但实际上已经足够计算机完成成千上万的指令集。每个任务都会被系统运行一段时间,然后挂起,系统转而处理其他任务。过一段时间以后再回来处理这个任务,直到某个任务完成,从任务队列中去除。 7.2 进程及作业 Linux系统上所有运行的东西都可以称之为一个进程。每个用户任务、每个系统管理守护进程,都可以称之为进程。Linux用分时管理方法使所有的任务共同分享系统资源。我们所关心的是如何去控制这些进程,让它们能够很好地为用户服务。
进程的一个比较正式的定义是:在自身的虚拟地址空间运行的一个单独的程序。进程与程序是有区别的,进程不是程序,虽然它由程序产生。程序只是一个静态的指令集合,不占系统的运行资源。而进程是一个随时都可能发生变化的、动态的、使用系统运行资源的程序。而且一个程序,可以启动多个进程。
进程和作业的概念也有区别。一个正在执行的进程称为一个作业,而且作业可以包含一个或多个进程,尤其是当使用了管道和重定向命令。
作业控制指的是控制正在运行的进程的行为。比如,用户可以挂起一个进程,等一会儿再继续执行该进程。shell将记录所有启动的进程情况,在每个进程过程中,用户可以任意地挂起进程或重新启动进程。作业控制是许多shell(包括bash和tcsh)的一个特性,使用户能在多个独立作业间进行切换。
例如,当用户编辑一个文本文件,并需要中止编辑做其他事情时,利用作业控制,用户可以让编辑器暂时挂起,返回shell提示符开始做其他的事情。其他事情做完以后,用户可以重新启动挂起的编辑器,返回到刚才中止的地方,就像用户从来没有离开编辑器一样。这只是一个例子,作业控制还有许多其他实际的用途。 7.3 管理进程 键入需要运行的程序的程序名,执行一个程序,其实也就是启动了一个进程。在Linux系统中每个进程都具有一个进程号(PID),用于系统识别和调度进程。启动一个进程有两个主要途径:手工启动和调度启动,后者是事先进行设置,根据用户要求自行启动。关于调度启动,将在后面的任务计划中讨论。
1.启动进程
由用户输入命令,直接启动一个进程便是手工启动进程。但手工启动进程又可以分为前台启动和后台启动。
前台启动是手工启动一个进程的最常用的方式。一般地,用户键入一个命令“ls-l”,这就已经启动了一个进程,而且是一个前台的进程。这时候系统其实已经处于一个多进程状态。或许有些用户会疑惑:我只启动了一个进程而已。但实际上有许多运行在后台的、系统启动时就已经自动启动的进程正在悄悄运行着。还有的用户在键入“ls-l”命令以后赶紧使用“ps-x”查看,却没有看到ls进程,也觉得很奇怪。其实这是因为ls这个进程结束太快,使用ps查看时该进程已经执行结束了。
直接从后台手工启动一个进程用得比较少一些,除非是该进程甚为耗时,且用户也不急着需要结果的时候。假设用户要启动一个需要长时间运行的格式化文本文件的进程。为了不使整个shell在格式化过程中都处于”瘫痪”状态,从后台启动这个进程是明智的选择。
在Linux中,进程与进程之间存在族亲关系。所有的进程,都会有一个父进程。一个进程创造出的新进程叫做老进程的子进程,老进程就是新进程的父进程。所有的进程有一个共同的祖先进程,叫做init,它的进程号为1。
Linux中不存在“孤儿进程”,即没有父进程的进程,所以每个子进程都必须先于父进程结束。也就是说,如果一个进程结束前,它会首先结束它的所有子进程。这样做是为了在进程结束后能够有进程负责清理已结束进程的一些还在内存中的数据结构,如进程控制块等。但是,这样做的结果是,如果从shell中启动了一个后台进程,这个进程非常耗时,以至于当用户要退出shell登录时,这个进程还没有结束。因为shell中启动的任务都以这个shell作为它们的父进程,那么shell退出登录时,就会结束所有的尚未结束的后台进程。
解决这个问题的方法是如果要启动一个非常耗时,可能在退出登录前不能完成的任务时,使用nohup命令。如:
nohup long_task>result &
这样,当shell退出登录时,这个long_task不会被结束,而是继续运行,只不过它的父进程改为祖先进程init。
2.查看进程
由于Linux是个多用户系统,有时候也要了解其他用户现在在干什么,同时Linux是一个多进程系统,经常需要对这些进程进行一些调配和管理。而要进行管理,首先就要知道现在的进程情况:究竟有哪些进程?进程情况如何?等等。所以需要进程查看方面的工作。
who命令
该命令主要用于查看当前在线上的用户情况。这个命令非常有用。如果用户想和其他用户建立即时通信,比如使用talk命令,那么首先要确定的就是该用户确实在线上,不然talk进程就无法建立起来。又如,系统管理员希望监视每个登录的用户此时此刻的所作所为,也要使用who命令。who命令应用起来非常简单,可以比较准确地掌握用户的情况,所以使用非常广泛。
w命令
该命令也用于显示登录到系统的用户情况,但是与who不同的是,w命令功能更加强大,它不但可以显示有谁登录到系统,还可以显示出这些用户当前正在进行的工作,并且统计数据相对who命令来说更加详细和科学,可以认为w命令就是who命令的一个增强版。w命令的显示项目按以下顺序排列:当前时间,系统启动到现在的时间,登录用户的数目,系统在最近1秒、5秒和15秒的平均负载。然后是每个用户的各项数据,项目显示顺序如下:登录帐号、终端名称、远程主机名、登录时间、空闲时间、JCPU、PCPU、当前正在运行进程的命令行。其中JCPU时间指的是和该终端(tty)连接的所有进程占用的时间。这个时间里并不包括过去的后台作业时间,但却包括当前正在运行的后台作业所占用的时间。而PCPU时间则是指当前进程(即在WHAT项中显示的进程)所占用的时间。
ps命令
ps命令就是最基本同时也是非常强大的进程查看命令。使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵尸、哪些进程占用了过多的资源等等。总之大部分信息都是可以通过执行该命令得到的。
ps命令最常用的还是用于监控后台进程的工作情况,因为后台进程是不和屏幕键盘这些标准输入/输出设备进行通信的,所以如果需要检测其情况,便可以使用ps命令了。
ps[选项]
下面对命令选项进行说明:
-e显示所有进程。
-f全格式。
-h不显示标题。
-l长格式。
-w宽输出。
a显示终端上的所有进程,包括其他用户的进程。
r只显示正在运行的进程。
x显示没有控制终端的进程。
最常用的三个参数是u、a、x。
查看系统中所有的进程,常用的两种格式为:
ps aux或ps-ef
pstree
pstree命令与ps类似,可以显示系统中所有的进程,而且,pstree以树状形式显示进程的族亲关系,看起来一目了然。
top命令
top命令和ps命令的基本作用是相同的,显示系统当前的进程和其他状况。但是top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态。如果在前台执行该命令,它将独占前台,直到用户终止该程序为止。比较准确地说,top命令提供了实时的对系统处理器的状态监视。它将显示系统中CPU最“敏感”的任务列表。该命令可以按CPU使用、内存使用和执行时间对任务进行排序。而且该命令的很多特性都可以通过交互式命令或者在个人定制文件中进行设定。
3.进程优先级
在Linux中,进程可以以不同的优先级运行。表示进程优先级关系的参数叫做nice值,nice值越大,进程就越“谦让”,优先级就越低,运行就越慢。一般进程的nice值的取值范围为-20~19,进程缺省的nice值为0。
启动一个进程时可以用nice命令设置nice值,如
nice-n 10 myprog1 &
以nice为10的优先级运行进程。普通用户只能将自己的进程的nice设为一个正值,这样这个进程运行起来就比较慢。root可以设置一个进程的nice值为负值。
对于系统中的进程,可以使用ps l命令来查看进程的nice值。
已经开始运行的进程,可以通过renice命令来调整nice值。同样的,普通用户只能将一个进程的nice值变大,而不能变小,root则都可以。
如:renice 15 3456就是将进程PID为3456的进程的nice值改为15。
同样使用top命令,也可以查看或修改进程的nice值。
4.进程调度
进程的挂起及恢复命令bg、fg
作业控制允许将进程挂起并可以在需要时恢复进程的运行,被挂起的作业恢复后将从中止处开始继续运行。只要在键盘上按Ctrl+Z,即可挂起当前的前台作业。在键盘上按Ctrl+Z后,将挂起当前执行的命令cat。使用jobs命令可以显示shell的作业清单,包括具体的作业、作业号以及作业当前所处的状态。恢复进程执行时,有两种选择:用fg命令将挂起的作业放回到前台执行。用bg命令将挂起的作业放到后台执行。灵活使用上述命令,将给自己带来很大的方便。
当需要中断一个前台进程的时候,通常是使用Ctrl+C组合键。但是对于一个后台进程恐怕就不是一个组合键所能解决的了,这时就必须求助于kill命令。该命令可以终止后台进程。至于终止后台进程的原因很多,或许是该进程占用的CPU时间过多,或许是该进程已经挂死。总之这种情况是经常发生的。
kill命令是通过向进程发送指定的信号来结束进程的。如果没有发送指定信号,那么默认值为TERM信号。TERM信号将终止所有不能捕获该信号的进程。至于那些可以捕获该信号的进程可能就需要使用SIGKILL(9)信号了,该信号是不能被捕捉的。
kill命令的语法格式很简单,大致有以下两种方式:
kill[-s信号|-p][-a]进程号…
kill-l[信号]
下图是从用户角度来说,进程的三种运行状态及转换关系图。
图7.1 用户角度的进程状态及转换图 7.4 任务计划 调度启动
有时候需要对系统进行一些比较费时而且占用资源的维护工作,这些工作适合在深夜进行,这时候用户就可以事先进行调度安排,指定任务运行的时间或者场合,到时候系统会自动完成这一切工作。要使用自动启动进程的功能,就需要掌握以下几个启动命令。
第一个重要的命令是at命令
用户使用at命令在指定时刻执行指定的命令序列。也就是说,该命令至少需要指定一个命令、一个执行时间才可以正常运行。at命令可以只指定时间,也可以时间和日期一起指定。需要注意的是,指定时间有个系统判别问题。比如说:用户现在指定了一个执行时间:凌晨3:20,而发出at命令的时间是头天晚上的20:00,那么究竟是在哪一天执行该命令呢?如果用户在3:20以前仍然在工作,那么该命令将在这个时候完成。如果用户3:20以前就退出了工作状态,那么该命令将在第二天凌晨才得到执行。下面是at命令的语法格式:
at[-V][-q队列][-f文件名][-mldbv]时间
at-c作业[作业…]
at允许使用一套相当复杂的指定时间的方法,它可以接受在当天的hh:mm(小时:分钟)式的时间指定。如果该时间已经过去,那么就放在第二天执行。当然也可以使用midnight(深夜),noon(中午),teatime(饮茶时间,一般是下午4点)等比较模糊的词语来指定时间。用户还可以采用12小时计时制,即在时间后面加上AM(上午)或者PM(下午)来说明是上午还是下午。也可以指定命令执行的具体日期,指定格式为month day(月日)或者mm/dd/yy(月/日/年)或者dd.mm.yy(日.月.年)。指定的日期必须跟在指定时间的后面。
上面介绍的都是绝对计时法,其实还可以使用相对计时法,这对于安排不久就要执行的命令是很有好处的。指定格式为:now+count time-units,now就是当前时间,time-units是时间单位,这里可以是minutes(分钟)、hours(小时)、days(天)、weeks(星期)。count是时间的数量,究竟是几天,还是几小时,等等。
还有一种计时方法就是直接使用today(今天)、tomorrow(明天)来指定完成命令的时间。下面通过一些例子来说明具体用法。
[例]指定在今天下午5∶30执行某命令。假设现在时间是中午12∶30,2001年2月24日,其命令格式如下:
at 5:30pm
at 17:30
at 17:30 today
at now+5 hours
at now+300 minutes
at 17:30 24.2.99
at 17:30 2/24/99
at 17:30 Feb 24
以上这些命令表达的意义是完全一样的,所以在安排时间的时候完全可以根据个人喜好和具体情况自由选择。一般采用绝对时间的24小时计时法可以避免由于用户自己的疏忽造成计时错误的情况发生,例如上例可以写成:
at 17:30 2/24/99
这样非常清楚,而且别人也看得懂。
对于at命令来说,需要定时执行的命令是从标准输入或者使用-f选项指定的文件中读取并执行的。如果at命令是从一个使用su命令切换到用户shell中执行的,那么当前用户被认为是执行用户,所有的错误和输出结果都会送给这个用户。但是如果有邮件送出的话,收到邮件的将是原来的用户,也就是登录时shell的所有者。在7月31日上午10点执行文件work中的作业。
在任何情况下,超级用户都可以使用这个命令。对于其他用户来说,是否可以使用就取决于两个文件:/etc/at.allow和/etc/at.deny。
cron命令
前面介绍的两条命令都会在一定时间内完成一定任务,但是要注意它们都只能执行一次。也就是说,当指定了运行命令后,系统在指定时间完成任务,一切就结束了。但是在很多时候需要不断重复一些命令,比如:某公司每周一自动向员工报告头一周公司的活动情况,这时候就需要使用cron命令来完成任务了。实际上,cron命令是不应该手工启动的。cron命令在系统启动时就由一个shell脚本自动启动,进入后台(所以不需要使用&符号)。一般的用户没有运行该命令的权限,虽然超级用户可以手工启动cron,不过还是建议将其放到shell脚本中由系统自行启动。
首先cron命令会搜索/var/spool/cron目录,寻找以/etc/passwd文件中的用户名命名的crontab文件,被找到的这种文件将载入内存。例如一个用户名为foxy的用户,它所对应的crontab文件就应该是/var/spool/cron/foxy。也就是说,以该用户命名的crontab文件存放在/var/spool/cron目录下面。cron命令还将搜索/etc/crontab文件,这个文件是用不同的格式写成的。cron启动以后,它将首先检查是否有用户设置了crontab文件,如果没有就转入“休眠”状态,释放系统资源。所以该后台进程占用资源极少。它每分钟“醒”过来一次,查看当前是否有需要运行的命令。命令执行结束后,任何输出都将作为邮件发送给crontab的所有者,或者是/etc/crontab文件中MAILTO环境变量中指定的用户。上面简单介绍了一些cron的工作原理,但是cron命令的执行不需要用户干涉。需要用户修改的是crontab中要执行的命令序列,所以下面介绍crontab命令。
crontab命令
crontab命令用于安装、删除或者列出用于驱动cron后台进程的表格。也就是说,用户把需要执行的命令序列放到crontab文件中以获得执行。每个用户都可以有自己的crontab文件。下面就来看看如何创建一个crontab文件。在/var/spool/cron下的crontab文件不可以直接创建或者直接修改。crontab文件是通过crontab命令得到的。现在假设有个用户名为foxy,需要创建自己的一个crontab文件。首先可以使用任何文本编辑器建立一个新文件,然后向其中写入需要运行的命令和要定期执行的时间。然后存盘退出。假设该文件为/tmp/test.cron。然后就是使用crontab命令来安装这个文件,使之成为该用户的crontab文件。键入:crontab test.cron。
这样一个crontab文件就建立好了。可以转到/var/spool/cron目录下面查看,发现多了一个foxy文件。这个文件就是所需的crontab文件。
在crontab文件中如何输入需要执行的命令和时间。该文件中每行都包括六个域,其中前五个域是指定命令被执行的时间,最后一个域是要被执行的命令。每个域之间使用空格或者制表符分隔。格式如下:
minute hour day-of-month month-of-year day-of-week commands
第一项是分钟,第二项是小时,第三项是一个月的第几天,第四项是一年的第几个月,第五项是一周的星期几,第六项是要执行的命令。这些项都不能为空,必须填入。如果用户不需要指定其中的几项,那么可以使用代替。因为是通配符,可以代替任何字符,所以就可以认为是任何时间,也就是该项被忽略了。
例:
30 8 * task1 每天早上8:30分执行一次
0,30 8-16 * task2 从早上8:00到下午4:30分,每半小时执行一次
0 12 1 5 * task3 每年的5月1日中午12点执行一次
59 7 1 task4 每周1的早上7:59执行一次
进程、程序、作业、后台进程、任务计划
1.进程与程序有什么区别?
2.如何启动一个后台进程?如何运行一个耗时非常长的进程?
3.如何控制前台进程和后台进程?
4.如何设置一个进程在后来的时间运行一次?
5.如何设置一个周期性运行的任务?