Linux 的进程管理,我悟了

678 阅读14分钟

1. 进程的概念

Linux 是一个多用户多任务的操作系统。多任务是指 Linux 可以同时执行几个任务,由操作系统管理多个用户的请求和多个任务。大多数系统都只有一个 CPU 和一个主存,但可能有多个二级存储磁盘和多个输人/输出设备,操作系统管理这些资源并在多个用户间共享资源。

Linux 用分时管理方法使所有的任务共享系统资源,当执行一个任务时,用户觉得系统只被自己独自占用,而实际上,操作系统监控着一个等待执行的任务队列,这些任务包括用户作业、操作系统任务、邮件和打印作业等。操作系统将 CPU 的时间分成一个一个片断,这些片断称为时间片,操作系统根据每个任务的优先级为每个任务分配合适的时间片。每个时间片大约有零点几秒,虽然看起来很短,但对计算机完成成千上万条的指令已经足够。一个任务被系统运行一段时间后挂起,系统转去处理其他任务,过一段时间以后再继续处理这个任务,直到该任务完成,从任务队列中去除为止。

Linux 系统上运行的任何内容都可以称为一个进程。每个用户任务、每个系统管理任务都可以称为进程。进程是一个程序的运行。虽然进程由程序产生,但进程与程序是有区别的。程序是一个静态的指令集合,不占系统的运行资源;而进程是一个随时都可能发生变化的、动态的、使用系统资源的程序。一个程序可以启动多个进程。Linux 操作系统包括三种不同类型的进程,每种进程都有自己的特点和属性:

  • 交互进程:由 shell 启动的进程
  • 批处理进程:这种进程和终端没有联系,是一个进程序列。
  • 守护进程:在后台持续运行的进程。

上述三种进程各有各的作用,使用场合也有所不同。

2. 启动进程

输人需要运行的程序的程序名,执行该程序,实际上就启动了一个进程。在 Linux 系统中,每个进程都具有一个进程号(PID),用于系统识别和调度进程。启动一个进程可以采用手工启动和调度启动两种方式,后者是事先根据用户的要求进行设置,在满足条件时自行启动。

2.1 手工启动进程

由用户输入命令,直接启动一个进程便是手工启动进程。手工启动进程又可以分为前台进程和后台进程。所谓前台,是指一个进程控制着标准输入和输出。在程序执行时,shell 暂时挂起,程序执行完毕后回到 shell。前台进程运行时,在同一个控制台上用户不能再执行其他程序。所谓后台进程,是指一个程序不从标准输入设备接受输入,一般也不将结果输出到标准输出上。一些运行时间较长、运行时不需要用户干预的程序适合运行在后台。

2.1.1 启动前台进程

这是手工启动一个进程的最常用的方式。一般来说,用户输人一个命令,例如“ls -l”,就已经启动了一个进程,而且是一个前台进程。实际上,这时系统已经处于一个多进程状态,因为有许多运行在后台的、系统启动时就已经自动启动的进程正在悄悄地运行着。

2.1.2 启动后台进程

如果用户要启动一个需要长时间运行的进程,并且也不要求立即得到结果,那么可以手工启动一个从后台运行的进程,例如,要启动一个格式化文本文件的进程,由于该过程非常耗时,最好在后有运行,命令如下:

troft -me notes > note_form &

可见,启动后台进程其实就是在命令结尾加上一个“&”符号,输人命令以后,就会出现一个数字,这个数字就是该进程的编号,然后会出现 shell 提示符,用户可以继续其他工作。

手工启动前台进程和启动后台进程有个共同的特点,就是新进程都是由当前的 shell 进程产生的。也就是说,是 shell 创建了新进程,这种关系称为进程间的父子关系。这里 shell 是父进程,而新进程是子进程。一个父进程可以有多个子进程,通常,子进程结束后才能继续父进程,但如果子进程是后台进程,父进程就不用等待子进程结束了。 除了手工启动进程外,有些任务可以预先安排好后让其自动运行,这就是调度启动进程。

2.2 调度启动进程

Linux 系统提供了几个调度启动命令,下面分别介绍。

2.2.1 at 命令

at 命令用于在指定时刻执行指定的命令序列。at 命令有以下两种使用方法:

  • 在shell 提示符下输人 “at <时间>”,然后按回车键,这时在下一行 shell 会等待用户继续输入要执行的命令,每一行输入一个命令,所有命令都输人完毕后按 Ctrl +d 组合键结束。
  • 将各个命令写入 shell 脚本中,然后使用下面格式设置在指定时间执行 shell 脚本中的命令: at 时间 -f 脚本文件

at 允许使用一套相当复杂的指定时间的方法。at 命令可以接受 hh:mm(小时:分钟)格式的时间,指定在某个时间运行程序,如果该时间已经过去,那么就放在第二天执行。在 at 命令中也可以使用 midnight(深夜)、nocn(中午)、teatime(饮茶时间,一般是下午4点)等比较模糊的词语来指定时间。此外,还可以采用12 小时计时制,在时间后面加上 am 或者 pm 来说明是上午还是下午。

用户可以指定命令执行的具体日期,日期格式为month day(月 日)、mm/dd/yy(月/日/年)或者 dd.mm.yy(日.月.年)。指定日期必须跟在指定时间的后面。 用户还可以使用相对计时法,格式为“now + 数字 + 时间单位”,now 就是当前时间,时间单位以是 minute(分钟)、hour(小时)、day(天)或者 week(星期)。

2.2.2 atq 命令

该命令用于查看安排的作业序列,它将列出用户排在队列中的作业,如果是超级用户,则列出队列中的所有工作。用命令 at -l 也能列出这些任务。

2.2.3 atrm 命令

该命令用于删除指定的命令序列,语法格式如下:

atrm 作业 [作业...]

注意,at -d 与该命令的作用相同。

2.2.4 batch 命令

batch 命令用低优先级运行作业,该命令和 at 命令的功能几乎完全相同,唯一的区别在于,at 命令是在指定时间执行指定命令,而 batch 却是在系统负载较低,资源比较空闲的时候执行指定命令。该命令适合于执行占用资源较多的命令。 batch 命令的语法格式也和 at 命令十分相似,具体的参数说明请参考 at 命令。一般来说,不用为 batch 命令指定时间参数,因为 batch 本身的特点就是由系统决定执行任务的时间,如果用户指定一个时间,就失去了本来的意义。而且 batch 和 at 命令都将自动转入后台,所以启动的时候也不需要加“&”符号。

2.2.5 cron 命令

前面介绍的命令都会在一定时间内完成一定任务,但是它们都只能执行一次,也就是说,系统在指定的时间完成任务之后,一切就结束了。但是很多时候需要不断重复一些命令,这时候就需要使具 cron 命令来完成任务了。 cron 命令在系统启动时由一个 shell 脚本自动启动,进入后台。一般的用户没有运行该命令的权限,虽然超级用户可以手工启动cron,不过还是建议将其放到shell脚本中由系统自行启动。

cron 启动后搜索 /var/spool/cron 目录,寻找以 /etc/passwd 文件中的用户名命名的 crontab 文件,找到这种文件后将其载人内存。例如,用户名为 test 的用户,它所对应的 crontab 文件就应该是 /va/ spool/cron/test。如果没有找到 crontab 文件,cron就转入“休眠”状态,释放系统资源。cron 每分钟“醒”过来一次,查看当前是否有需要运行的命令。如果发现某个用户设置了 crontab 文件,它将以该用户的身份运行文件中指定的命令。命令执行结束后,任何输出都将作为邮件发送给 crontab 的所有者,或者 /etc/crontab 文件的 MAILTO 环境变量中指定的用户。用户不需要干涉 cron 命令的执行,但需要修改 crontab 文件中要执行的命令序列,该命令序列由命令 crontab 形成。

  • crontab 文件

    每个用户都可以有自己的 crontab 文件。在 /var/spool/cron 下的 crontab 文件不能直接创建或者直接修改,必须通过 crontab 命令得到。crontab 文件的每一行都有5个时间日期域和一个命令域。下面列出了用户 thy的 crontab文件:

    59 23 * * * tar czvf lhy.tar.gz /home/lhy
    

    表示用户 lhy 每天 23 点 59 分时备份自己主目录下的文件到 Ihy.tar.gz

  • crontab 命令

    crontab 命令用于安装、删除或者列出用于驱动 cron 后台进程的 crontab 文件。crontab 命令的语法格式如下:

    crontab [-u <user>] <file>
    crontab [-u <user>] {-l |-r|-e}
    

    第一种格式用于根据指定的文件安装一个新的 crontab 文件,如果文件名参数为“-”,则使用标准 输入作为文件来源。

    主要选项的含义如下所示:

    • -u :指定修改用户 user 的 crontab 文件。如果不指定该选项,crontab 默认修改执行该命令的用户的 crontab 文件。

    • -l:在标准输出上显示当前的 crontab 文件。

    • -r:删除当前的 crontab 文件。

    • -e:使用 Visual 或者 Editor 环境变量所指的编辑器编辑当前的 crontab 文件。当结束编辑时,编辑后的文件将自动安装。

      例如,假设当前的用户名为 lhy ,需要创建自己的 crontab 文件。首先使用任意一种文本编辑器建立一个新文件,向其中写入需要运行的命令和要定期执行的时间,然后存盘退出。假设该文件为 /tmp/lhy.cron ,内容只包含如下一行文本:

      59 23 * * * tar czvf lhy.tar.gz /home/lhy
      

      表示用户 lhy 希望每天 23 点 59 分时备份自己主目录下的文件到 Ihy.tar.gz 。

      接着使用 crontab 命令来安装这个文件,执行命令:

      crontab /tmp/lhy.cron
      

      这样,在目录 /var/spool/cron 下就为用户 lhy 创建了 crontab 文件,文件名为 lhy 。文件的内容如下:

      59 23 * * * tar czvf lhy.tar.gz /home/lhy

      不要手工编辑该文件,如果需要改变其中的命令内容,则需要重新编辑原来的文件,然后再使用 crontab 命令重新安装。

  • crontab 源文件

    要使用 cron 定期执行任务,用户必须建立自己的 crontab 源文件。可以使用任意一种文本编辑器建立和编辑 crontab 源文件。在 crontab 源文件中,前五个字段指定命令被执行的时间,最后一个字段是要执行的命令,每个字段之间使用空格或者制表符分隔。格式如下:

    <minute> <hour> <day-of-month> <month-of-year> <day-of-week> <commands>
    

    每项的含义如下:

    minutehourday-of-monthmonth-of-yearday-of-weekcommands
    分钟小时一个月的第几天一年的第几个月一周的星期几要执行的命令

    这六个字段都必须填入。如果不需要指定其中的某项,可以使用符号 “*” 代替,用于代表任何时间。每个时间域的合法值范围如下:

    时间合法值
    minute00~59
    hour00~23
    day-of-month01~31
    month-of-year01~12
    day-of-week0~6

    用户可以在 crontab 源文件中写入无限多的行以完成无限多的命令。可以在命令字段中写入所有可以在命令行上输入的命令和符号。所有的时间字段都支持枚举,也就是说字段中可以写入多个时间值,每两个时间值中间使用逗号分隔,只要满足这些时间值中的任何一个都执行命令。

    例如,“5,15,25,35,45,55 16,17,18 * * * command”表示每天下午 4 点、5 点、6 点的 5 分、15 分、25 分、35 分、45 分、55 分时执行命令。

    要想在每周一、三、五的下午 3:00 系统进入维护状态,然后重新启动系统,那么在 crontab 源文件中就应该写入如下文本:

    0 15 * * 1,3,5 shutdown -r +5
    

3. 进程管理命令

3.1 进程查看命令 ps

要对进程进行监测和控制,首先必须了解当前进程的情况,也就是需要查看当前进程,这时就需要应用 ps 命令。ps 命令是最基本也是功能非常强大的进程查看命令,使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等。ps 命令的格式如下:

ps [选项]

主要选项的含义如下所示:

  • -e:显示所有进程
  • -h:不显示标题
  • -l:采用详细的格式来显示进程
  • a:显示所有终端上的进程,包括其他用户的进程。
  • r:只显示当前终端上正在运行的进程。
  • x:显示所有进程,不以终端来区分。
  • u:以用户为主的格式来显示进程。

例如,要查看目前进程状况,可以使用命令 “ps”;使用选项组合 aux 的 ps 命令“ps -aux”可以显示最详细的进程情况。

3.2 删除进程命令 kill

当需要中断一个前台进程的时候,通常使用组合键 Ctrl + c 即可;但是要中断一个后台进程,就不能使用该组合键,这时必须使用 kill 命令。需要终止后台进程的原因很多,如进程占用的 CPU 时间过多或者是该进程已经挂死,总之,用户主动中断进程的情况经常发生。 kill 命令是通过向进程或者进程组发送指定的信号来结束进程的。如果不指定发送的信号,那么默认为 TERM 信号,TERM 信号将终止所有不能捕获该信号的进程。那些可以捕获该信号的进程,必须使用kill(9)信号,该信号是不能被捕捉的。kill 命令的语法格式如下:

kill [-s <信号> | -p] [-a] <进程号> 
kill -l [信号]

其中各选项的含义如下所示:

  • -s:指定需要送出的信号,既可以是信号名也可以是信号名对应的数字。
  • -p:指定 kill 命令只显示命名进程的 pid,并不真正送出任何信号。
  • -l:显示信号名称列表,该列表也可以在 /usr/include/linux/signal.h 文件中找到。

例如,一条 find 指令由于执行时间过长,用户决定终止该进程。首先应该使用 ps 命令来查看该进程对应的 pid,假如该进程的 pid 为 345,现在使用 kill 命令来终止该进程。输入命令:

kill 345

再用 ps 命令查看,就可以看到 find 进程已经被删除了。

有时候可能会遇到这样的情况,某个进程已经挂起或闲置,但是使用 kill 命令却无法删除。这时候就必须发送信号9,强行关闭此进程。但这种“强制”方法很可能会导致打开的文件出现错误或数据丢失之类的错误,所以不到万不得已不要使用强制结束的办法。如果该进程连信号 9 都不响应,就只能重新启动计算机了。