第12 章 Linux系统管理

 


本章,我们主要学习进程管理、工作管理和系统定时任务。同时解决一些问题,如什么是进程、进程的管理方式是什么、工作管理的作用是什么、系统定时任务如何实现。

Linux进程管理#

进程管理在 Windows 中更加直观,它主要是使用"任务管理器"来进行进程管理的。

通常,使用"任务管理器"主要有 3 个目的:

  1. 利用"应用程序"和"进程"标签来査看系统中到底运行了哪些程序和进程;
  2. 利用"性能"和"用户"标签来判断服务器的健康状态;
  3. 在"应用程序"和"进程"标签中强制中止任务和进程;

Linux 中虽然使用命令进行进程管理,但是进程管理的主要目的是一样的,那就查看系统中运行的程序和进程、判断服务器的健康状态和强制中止不需要的进程。
那到底什么是进程呢?它和我们平时所说的“程序”又有什么联系呢?

什么是进程和程序#

进程是正在执行的一个程序或命令,每个进程都是一个运行的实体,都有自己的地址空间,并占用一定的系统资源。程序是人使用计算机语言编写的可以实现特定目标或解决特定问题的代码集合。这么讲很难理解,那我们换一种说法。

程序是人使用计算机语言编写的,可以实现一定功能,并且可以执行的代码集合。而进程是正在执行中的程序。当程序被执行时,执行人的权限和属性,以及程序的代码都会被加载入内存,操作系统给这个进程分配一个 ID,称为 PID(进程 ID)。

也就是说,在操作系统中,所有可以执行的程序与命令都会产生进程。只是有些程序和命令非常简单,如 ls 命令、touch 命令等,它们在执行完后就会结束,相应的进程也就会终结,所以我们很难捕捉到这些进程。但是还有一些程和命令,比如 httpd 进程,启动之后就会一直驻留在系统当中,我们把这样的进程称作常驻内存进程。

某些进程会产生一些新的进程,我们把这些进程称作子进程,而把这个进程本身称作父进程。比如,我们必须正常登录到 Shell 环境中才能执行系统命令,而 Linux 的标准 Shell 是 bash。我们在 bash 当中执行了ls命令,那么 bash 就是父进程,而 ls 命令是在 bash 进程中产生的进程,所以 ls 进程是 bash 进程的子进程。也就是说,子进程是依赖父进程而产生的,如果父进程不存在,那么子进程也不存在了。

进程管理的作用#

我们在上课时,只要一问学员"进程管理是做什么的",大家会不约而同地回答我"杀死进程"。的确,这是很多使用进程管理工具或进程管理命令的人最常见的使用方法。
不过,笔者想说,杀死进程(强制中止进程)只是进程管理工作中最不常用的手段,因为每个进程都有自己正确的结束方法,而杀死进程是在正常方法已经失效的情况下的后备手段。
那么,进程管理到底应该是做什么的呢?笔者以为,进程管理主要有以下 3 个作用。

1) 判断服务器的健康状态#

运维工程师最主要的工作就是保证服务器安全、稳定地运行。理想的状态是,在服务器出现问题,但是还没有造成服务器宕机或停止服务时,就人为干预解决了问题。
进程管理最主要的工作就是判断服务器当前运行是否健康,是否需要人为干预。如果服务器的 CPU 占用率、内存占用率过高,就需要人为介入解决问题了。
这又出现了一个问题:我们发现服务器的 CPU 或内存占用率很高,该如何介入呢?是直接终止高负载的进程吗?
当然不是,应该判断这个进程是否是正常进程,如果是正常进程,则说明你的服务器已经不能满足应用需求,你需要更好的硬件或搭建集群了;如果是非法进程占用了系统资源,则更不能直接中止进程,而要判断非法进程的来源、作用和所在位置,从而把它彻底清除。
当然,如果服务器数量很少,我们完全可以人为通过进程管理命令来进行监控与干预;但如果服务器数量较多,那么人为手工监控就变得非常困难了,这时我们就需要相应的监控服务,如 cacti 或 nagios。总之,进程管理工作中最重要的工作就是判断服务器的健康状 态,最理想的状态是服务器宕机之前就解决问题,从而避免服务器的宕机。

2) 查看系统中所有的进程#

我们需要查看看系统中所有正在运行的进程,通过这些进程可以判断系统中运行了哪些服务、是否有非法服务在运行。

3) 杀死进程#

这是进程管理中最不常用的手段。当需要停止服务时,会通过正确关闭命令来停止服务(如 apache 服务可以通过 service httpd stop 命令来关闭)。只有在正确终止进程的手段失效的情况下,才会考虑使用 kill 命令杀死进程。
其实,进程管理和 Windows 中任务管理器的作用非常类似,不过大家在使用任务管理器时是为了杀死进程,而不是为了判断服务器的健康状态。

Linux ps命令#

ps 是用来静态地查看系统中正在运行的进程的命令。不过这个命令有些特殊,它的部分选项不能加入"-",比如命令"ps aux",其中"aux"是选项,但是这个选项不能加入"-"。这是因为 ps 命令的部分选项需要遵守 BSD 操作系统的格式。所以,ps 命令的常用选项的组合是固定的。
ps 命令格式如下:

1
2
3
4
[root@localhost ~]# ps aux
#查看系统中所有的进程,使用BS操作系统格式
[root@localhost ~]# ps -le
#查看系统中所有的进程,使用Linux标准命令格式

选项:

  • a:显示一个终端的所有进程,除会话引线外;
  • u:显示进程的归属用户及内存的使用情况;
  • x:显示没有控制终端的进程;
  • -l:长格式显示。显示更加详细的信息;
  • -e:显示所有进程;

大家如果执行"man ps"命令,则会发现 ps 命令的帮助为了适应不同的类 UNIX 系统,可用格式非常多,不方便记忆。所以,我建议大家记忆几个固定选项即可。比如:

  • "ps aux"可以查看系统中所有的进程;
  • "ps -le"可以查看系统中所有的进程,而且还能看到进程的父进程的PID和进程优先级;
  • "ps -l"只能看到当前Shell产生的进程;

有这三个命令就足够了,下面分别来查看。

1
2
3
4
5
6
7
8
[root@localhost ~]# ps aux
#查看系统中所有的进程
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.2 2872 1416 ? Ss Jun04 0:02 /sbin/init
root 2 0.0 0.0 0 0 ? S Jun04 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S Jun04 0:00 [migration/0]
root 4 0.0 0.0 0 0 ? S Jun04 0:00 [ksoftirqd/0]
…省略部分输出…

解释一下这个命令的输出:

  • USER:该进程是由哪个用户产生的。
  • PID:进程的 ID。
  • %CPU:该进程占用 CPU 资源的百分比,占用的百分比越高,进程越耗费资源。
  • %MEM:该进程占用物理内存的百分比,占用的百分比越高,进程越耗费资源。
  • VSZ:该进程占用虚拟内存的大小,单位为 KB。
  • RSS:该进程占用实际物理内存的大小,单位为 KB。
  • TTY:该进程是在哪个终端运行的。其中,tty1 ~ tty7 代表本地控制台终端(可以通过 Alt+F1 ~ F7 快捷键切换不同的终端),tty1~tty6 是本地的字符界面终端,tty7 是图形终端。pts/0 ~ 255 代表虚拟终端,一般是远程连接的终端,第一个远程连接占用 pts/0,第二个远程连接占用 pts/1,依次増长。
  • STAT:进程状态。常见的状态有以下几种:
    1. -D:不可被唤醒的睡眠状态,通常用于 I/O 情况。
    2. -R:该进程正在运行。
    3. -S:该进程处于睡眠状态,可被唤醒。
    4. -T:停止状态,可能是在后台暂停或进程处于除错状态。
    5. -W:内存交互状态(从 2.6 内核开始无效)。
    6. -X:死掉的进程(应该不会出现)。
    7. -Z:僵尸进程。进程已经中止,但是部分程序还在内存当中。
    8. -<:高优先级(以下状态在 BSD 格式中出现)。
    9. -N:低优先级。
    10. -L:被锁入内存。
    11. -s:包含子进程。
    12. -l:多线程(小写 L)。
    13. -+:位于后台。
  • START:该进程的启动时间。
  • TIME:该进程占用 CPU 的运算时间,注意不是系统时间。
  • COMMAND:产生此进程的命令名。

"ps aux"命令可以看到系统中所有的进程,"ps -le"命令也能看到系统中所有的进程。由于"-l"选项的作用,所以"ps -le"命令能够看到更加详细的信息,比如父进程的 PID、优先级等。但是这两个命令的基本作用是一致的,掌握其中一个就足够了。命令如下:

1
2
3
4
5
6
7
8
[root@localhost ~]# ps -le
F S UID PID PPID C PRI Nl ADDR SZ WCHAN TTY TIMECMD
4 S 0 1 0 0 80 0 - 718- ? 00:00:02 init
1 S 0 2 0 0 80 0- 0- ? 00:00:00 kthreadd
1 S 0 3 2 0-40 -- 0- ? 00:00:00 migration/0
1 S 0 4 2 0 80 0- 0- ? 00:00:00 ksoflirqd/0
1 S 0 5 2 0-40 -- 0- ? 00:00:00 migration/0
…省部分输出…

也来解释一下这个命令的输出:

  • F:进程标志,说明进程的权限,常见的标志有两个;
  • 1:进程可以被复制,但是不能被执行;
  • 4:进程使用超级用户权限;
  • S:进程状态。具体的状态和"psaux"命令中的 STAT 状态一致;
  • UID:运行此进程的用户的 ID;
  • PID:进程的 ID;
  • PPID:父进程的 ID;
  • C:该进程的 CPU 使用率,单位是百分比;
  • PRI:进程的优先级,数值越小,该进程的优先级越高,越早被 CPU 执行;
  • NI:进程的优先级,数值越小,该进程越早被执行;
  • ADDR:该进程在内存的哪个位置;
  • SZ:该进程占用多大内存;
  • WCHAN:该进程是否运行。"-"代表正在运行;
  • TTY:该进程由哪个终端产生;
  • TIME:该进程占用 CPU 的运算时间,注意不是系统时间;
  • CMD:产生此进程的命令名;

不过,有时我不想看到所有的进程,只想查看一下当前登录产生了哪些进程,那只需使用"ps -l"命令就足够了。命令如下:

1
2
3
4
5
[root@localhost ~]# ps -l
#查看当前登录产生的进程
FS UID PID PPID C PRI Nl ADDR SZ WCHAN TTY TIMECMD
4 S 0 18618 18614 0 80 0 - 1681 -pts/1 00:00:00 bash
4 R 0 18683 18618 4 80 0 - 1619 -pts/1 00:00:00 ps

可以看到,这次从 pts/1 虚拟终端登录,只产生了两个进程:一个是登录之后生成的 Shell,也就是 bash;另一个是正在执行的 ps 命令。

再来我们再来说说僵尸进程。僵尸进程的产生一般是由于进程非正常停止或程序编写错误,导致子进程先于父进程结束,而父进程又没有正确地回收子进程,从而造成子进程一直存在于内存当中,这就是僵尸进程。

僵尸进程会对主机的稳定性产生影响,所以,在产生僵尸进程后,一定要对产生僵尸进程的软件进行优化,避免一直产生僵尸进程;对于已经产生的僵尸进程,可以在查找出来之后强制中止。

Linux top命令#

ps 命令用于显示命令运行时这个时间节点的进程状态,而 top 命令则用于动态地持续监听进程的运行状态,而且可以查看系统的健康状态。
top 命令格式如下:

[root@localhost ~]#top [选项]

选项:

  • -d 秒数:指定 top 命令每隔几秒更新。默认是 3 秒;
  • -b:使用批处理模式输出。一般和"-n"选项合用,用于把 top 命令重定向到文件中;
  • -n 次数:指定 top 命令执行的次数。一般和"-"选项合用;
  • -p:指定 PID。只查看某个 PID 的进程;
  • -s:使 top 命令在安全模式中运行,避免在交互模式中出现错误;
  • -u 用户名:只监听某个用户的进程;

在 top 命令的交互模式中可以执行的命令:

  • ? 或 h:显示交互模式的帮助;
  • P:按照 CPU 的使用率排序,默认就是此选项;
  • M:按照内存的使用率排序;
  • N:按照 PID 排序;
  • T:按照 CPU 的累积运算时间排序,也就是按照 TIME+ 项排序;
  • k:按照 PID 给予某个进程一个信号。一般用于中止某个进程,信号 9 是强制中止的信号;
  • r:按照 PID 给某个进程重设优先级(Nice)值;
  • q:退出 top 命令;

我们看看 top 命令的执行结果,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@localhost ~]# top
top - 12:26:46 up 1 day, 13:32, 2 users, load average: 0.00, 0.00, 0.00
Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.1%us, 0.1%sy, 0.0%ni, 99.7%id, 0.1%wa, 0.0%hi, 0.1%si, 0.0%st
Mem: 625344k total, 571504k used, 53840k free, 65800k buffers
Swap: 524280k total, 0k used, 524280k free, 409280k cached
PID USER PR Nl VIRT RES SHR S %CPO %MEM TIME+ COMMAND
19002 root 20 0 2656 1068 856 R 0.3 0.2 0:01.87 top
1 root 20 0 2872 1416 1200 S 0.0 0.2 0:02.55 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.03 kthreadd
3 root RT 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
4 root 20 0 0 0 0 S 0.0 0.0 0:00.15 ksoftirqd/0
5 root RT 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
6 root RT 0 0 0 0 S 0.0 0.0 0:10.01 watchdog/0
7 root 20 0 0 0 0 S 0.0 0.0 0:05.01 events/0
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cgroup
9 root 20 0 0 0 0 S 0.0 0.0 0:00.00 khelper
10 root 20 0 0 0 0 S 0.0 0.0 0:00.00 netns
11 root 20 0 0 0 0 S 0.0 0.0 0:00.00 async/mgr
12 root 20 0 0 0 0 S 0.0 0.0 0:00.00 pm
13 root 20 0 0 0 0 S 0.0 0.0 0:01.70 sync_supers
14 root 20 0 0 0 0 S 0.0 0.0 0:00.63 bdi-default
15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kintegrityd/0
16 root 20 0 0 0 0 S 0.0 0.0 0:02.52 kblockd/0
17 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kacpid
18 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kacpi_notify

我们解释一下命令的输出。top 命令的输出内容是动态的,默认每隔 3 秒刷新一次。命令的输出主要分为两部分:

  1. 第一部分是前五行,显示的是整个系统的资源使用状况,我们就是通过这些输出来判断服务器的资源使用状态的;
  2. 第二部分从第六行开始,显示的是系统中进程的信息;

我们先来说明第一部分的作用。

  • 第一行为任务队列信息,具体内容如表 1 所示。

 

表 1 任务队列信息
内 容说 明
12:26:46 系统当前时间
up 1 day, 13:32 系统的运行时间.本机己经运行 1 天 13 小时 32 分钟
2 users 当前登录了两个用户
load average: 0.00,0.00,0.00 系统在之前 1 分钟、5 分钟、15 分钟的平均负载。如果 CPU 是单核的,则这个数值超过 1 就是高负载:如果 CPU 是四核的,则这个数值超过 4 就是高负载 (这个平均负载完全是依据个人经验来进行判断的,一般认为不应该超过服务器 CPU 的核数)

 

  • 第二行为进程信息,具体内容如表 2 所示。

 

表 2 进程信息
内 容说 明
Tasks: 95 total 系统中的进程总数
1 running 正在运行的进程数
94 sleeping 睡眠的进程数
0 stopped 正在停止的进程数
0 zombie 僵尸进程数。如果不是 0,则需要手工检査僵尸进程

 

  • 第三行为 CPU 信息,具体内容如表 3 所示。

 

表 3 CPU信息
内 容说 明
Cpu(s): 0.1 %us 用户模式占用的 CPU 百分比
0.1%sy 系统模式占用的 CPU 百分比
0.0%ni 改变过优先级的用户进程占用的 CPU 百分比
99.7%id 空闲 CPU 占用的 CPU 百分比
0.1%wa 等待输入/输出的进程占用的 CPU 百分比
0.0%hi 硬中断请求服务占用的 CPU 百分比
0.1%si 软中断请求服务占用的 CPU 百分比
0.0%st st(steal time)意为虚拟时间百分比,就是当有虚拟机时,虚拟 CPU 等待实际 CPU 的时间百分比

 

  • 第四行为物理内存信息,具体内容如表 4 所示。

 

表 4 物理内存信息
内 容说 明
Mem: 625344k total 物理内存的总量,单位为KB
571504k used 己经使用的物理内存数量
53840k&ee 空闲的物理内存数量。我们使用的是虚拟机,共分配了 628MB内存,所以只有53MB的空闲内存
65800k buffers 作为缓冲的内存数量

 

  • 第五行为交换分区(swap)信息,如表 5 所示。

 

表 5 交换分区信息
内 容说 明
Swap: 524280k total 交换分区(虚拟内存)的总大小
Ok used 已经使用的交换分区的大小
524280k free 空闲交换分区的大小
409280k cached 作为缓存的交换分区的大小


我们通过 top 命令的第一部分就可以判断服务器的健康状态。如果 1 分钟、5 分钟、15 分钟的平均负载高于 1,则证明系统压力较大。如果 CPU 的使用率过高或空闲率过低,则证明系统压力较大。如果物理内存的空闲内存过小,则也证明系统压力较大。

这时,我们就应该判断是什么进程占用了系统资源。如果是不必要的进程,就应该结束这些进程;如果是必需进程,那么我们该増加服务器资源(比如増加虚拟机内存),或者建立集群服务器。

我们还要解释一下缓冲(buffer)和缓存(cache)的区别:

  • 缓存(cache)是在读取硬盘中的数据时,把最常用的数据保存在内存的缓存区中,再次读取该数据时,就不去硬盘中读取了,而在缓存中读取。
  • 缓冲(buffer)是在向硬盘写入数据时,先把数据放入缓冲区,然后再一起向硬盘写入,把分散的写操作集中进行,减少磁盘碎片和硬盘的反复寻道,从而提高系统性能。

简单来说,缓存(cache)是用来加速数据从硬盘中"读取"的,而缓冲(buffer)是用来加速数据"写入"硬盘的。

再来看 top 命令的第二部分输出,主要是系统进程信息。这部分和 ps 命令的输出比较类似,只是如果在终端执行 top 命令,则不能看到所有的进程,而只能看到占比靠前的进程。

  • PID:进程的 ID。
  • USER:该进程所属的用户。
  • PR:优先级,数值越小优先级越高。
  • NI:优先级,数值越小、优先级越高。
  • VIRT:该进程使用的虚拟内存的大小,单位为 KB。
  • RES:该进程使用的物理内存的大小,单位为 KB。
  • SHR:共享内存大小,单位为 KB。
  • S:进程状态。
  • %CPU:该进程占用 CPU 的百分比。
  • %MEM:该进程占用内存的百分比。
  • TIME+:该进程共占用的 CPU 时间。
  • COMMAND:进程的命令名。


接下来我们举几个 top 命令常用的实例。比如,只想让 top 命令査看某个进程,就可以使用"-p 选项"。命令如下:

[root@localhost ~]# top -p 15273
#只査看 PID为 15273的apache进程
top - 14:28:47 up 1 day, 15:34, 3 users, load average: 0.00,0.00,0.00
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
Mem: 625344k total, 574124k used, 51220k free, 67024k buffers
Swap: 524280k total, Ok used, 524280k free, 409344k cached
PID USER PR Nl VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15273 daemon 20 0 45201192 580 S 0.0 0.2 0:00.00 httpd


top 命令如果不正确退出,则会持续运行。在 top 命令的交互界面中按"q"键会退出 top 命令;也可以按"?"或"h"键得到 top 命令交互界面的帮助信息;还可以按键中止某个进程。命令如下:

[root@localhost ~]# top
top - 14:10:15 up 1 day, 15:15, 3 users, load average: 0.00,0.00, 0.00
Tasks: 97 total, 1 running, 96 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
Mem: 625344k total, 574248k used, 51096k free, 66840k buffers
Swap: 524280k total, Ok used, 524280k free, 409324k cached
PID to kill:15273
#按"k"键,会提示输入要杀死进程的PID
PID USER PR Nl VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15273 daemon 20 0 45201192 580 S 0.0 0.2 0:00.00 httpd
..省略部分输出...


输入要中止进程的 PID,比如要中止 15273 这个 apache 进程,命令如下:

top - 14:11:42 up 1 day, 15:17, 3 users, load average: 0.00,0.00, 0.00
Tasks: 97 total, 1 running, 96 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.0%us, 0.3%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 625344k total, 574248k used, 51096k free, 66856k buffers
Swap: 524280k total, 0k used, 524280k free, 409324k cached
Kill PID 15273 with signal [15]:9
#提示输入信号,信号9代表强制中止
PID USER PR Nl VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15273 daemon 20 0 45201192 580 S 0.0 0.2 0:00.00 httpd
…省略部分输出…

接下来 top 命令提示我们输入信号,信号 9 代表强制中止,这时就能够强制中止 15273 进程了。

如果要改变某个进程的优先级,就要利用"r"交互命令。需要注意的是,我们能够修改的只有 Nice 的优先级,而不能修改 Priority 的优先级。具体修改命令如下:

[root@localhost ~]# top -p 18977
top - 14:17:09 up 1 day, 15:22, 3 users, load average: 0.00,0.00, 0.00
Tasks: 97 total, 1 running, 96 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.3%us, 0.0%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 625344k total, 574124k used, 51220k free, 66896k buffers
Swap: 524280k total, 0k used, 524280k free, 409324k cached
PID to renice:
#输入"r"交互命令之后,提示输入要修改优先级的进程的PID
PID USER PR Nl VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18977 root 20 0 11592 3304 2572 S 0.0 0.5 0:04.37 sshd

输入"r"交互命令,会提示输入需要修改优先级的进程的 PID。例如,我们想要修改 18977 这个 sshd 远程连接进程的优先级,就输入该进程的 PID。命令如下:

Renice PID 18977 to value: 10
#输入PID后,需要输入Nice的优先级号
#我们把18977进程的优先级调整为10,回车后就能看到
PID USER PR Nl VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18977 root 30 10 11592 3304 2572 R 0.0 0.5 0:04.38 sshd
#18977进程的优先级已经被修改了

如果在操作终端执行 top 命令,则并不能看到系统中所有的进程,默认看到的只是 CPU 占比靠前的进程。如果我们想要看到所有的进程,则可以把 top 命令的执行结果重定向到文件中。不过 top 命令是持续运行的,这时就需要使用"-b"和"-n"选项了。具体命令如下:

[root@localhost ~]# top -b -n 1 > /root/top.log
#让top命令只执行一次,然后把执行结果保存到top.log文件中,这样就能看到所有的进程了

Linux pstree命令#

pstree 是查看进程树的命令,也就是查看进程的相关性的命令。命令格式如下:

1
[root@localhost ~]# pstree [选项]

选项:

  • -p:菜单进程的 PID;
  • -u:显示进接的所属用户;

例如:

1
2
3
4
5
6
7
8
9
10
11
[root@1ocalhost ~]# pstree
init──┬──abrc-dump-oopa
├──abrtd
├──acpid
...省略部分输出...
├──rayslogd───3*[{rsyslogrd}]
#有3个rsyslogd进程存在
├──sshd───sshd───bash───pstree
#Pstree命令进程是在远程连接中被执行的
├──udevd───2*[udevd]
└──xinecd

我们已经知道了 init 进程,这个进程是系统启动的第一个进程,进程的 PID 是 1,也是系统中所有进程的父进程,通过 pstree 命令可以很清楚得看到这一点。

Linux常用信号(进程间通信)#

进程的管理主要是指进程的关闭与重启。我们一般关闭或重启软件,都是关闭或重启它的程序,而不是直接操作进程的。比如,要重启 apache 服务,一般使用命令"service httpd restart"重启 apache的程序。

那么,可以通过直接管理进程来关闭或重启 apache 吗?答案是肯定的,这时就要依赖进程的信号(Signal)了。我们需要给予该进程号,告诉进程我们想要让它做什么。

系统中可以识别的信号较多,我们可以使用命令"kill -l"或"man 7 signal"来查询。命令如下:

1
2
3
4
5
[root@localhost ~]#kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11)SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15)SIGTERM <br>16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG
24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO <br>30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 <br>37) SIGRTMIN+3 38) SIGRTMIN +4 39) SIGRTMIN +5 40) SIGRTMIN+6 41)SIGRTMIN+7 <br>42) SIGRTMIN+8 43) SIGRTMIN +9 44) SIGRTMIN +10 45) SIGRTMIN+11 46) SIGRTMIN+1247) SIGRTMIN+13 <br>48) SIGRTMIN +14 49) SIGRTMIN +15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 <br>54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 <br>60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX

这里介绍一下常见的进程信号,如表 1 所示。

表 1 常见的进程信号
信号代号 信号名称 说 明
1 SIGHUP 该信号让进程立即关闭.然后重新读取配置文件之后重启
2 SIGINT 程序中止信号,用于中止前台进程。相当于输出 Ctrl+C 快捷键
8 SIGFPE 在发生致命的算术运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为 0 等其他所有的算术运算错误
9 SIGKILL 用来立即结束程序的运行。本信号不能被阻塞、处理和忽略。般用于强制中止进程
14 SIGALRM 时钟定时信号,计算的是实际的时间或时钟时间。alarm 函数使用该信号
15 SIGTERM 正常结束进程的信号,kill 命令的默认信号。如果进程已经发生了问题,那么这 个信号是无法正常中止进程的,这时我们才会尝试 SIGKILL 信号,也就是信号 9
18 SIGCONT 该信号可以让暂停的进程恢复执行。本信号不能被阻断
19 SIGSTOP 该信号可以暂停前台进程,相当于输入 Ctrl+Z 快捷键。本信号不能被阻断


我们只介绍了常见的进程信号,其中最重要的就是 "1"、"9"、"15"这三个信号,我们只需要记住这三个信号即可。

关于如何把这些信号传递给进程,从而控制这个进程,这就需要使用 kill、killall 以及 pkill 命令了,我们会在后续章节中详解介绍这 3 个命令。

Linux kill命令#

kill 从字面来看,就是用来杀死进程的命令。但是,根据不同的信号,kill 命令可以完成不同的操作。
kill 命令格式如下:

1
[root@localhost ~]# kill [信号] PID

kill 命令是按照 PID 来确定进程的,所以 kill 命令只能识别 PID,而不能识别进程名。我们举几个例子来说明一下 kill 命令。

【例 1】 标准 kill 命令#

[root@localhost ~】# service httpd start
#启动RPM包默认安装的apache服务
[root@localhost ~]# pstree -p 丨 grep httpd | grep -v "grep"
#查看 httpd 的进程树及 PID。grep 命令査看 httpd 也会生成包含"httpd"关键字的进程,所以使用“-v”反向选择包含“grep”关键字的进程,这里使用 pstree 命令来查询进程,当然也可以使用 ps 和 top 命令
|-httpd(2246)-+-httpd(2247)
|    |-httpd(2248)
|    |-httpd(2249)
|    |-httpd(2250)
|    |-httpd(2251)
[root@localhost ~]# kill 2248
#杀死PID是2248的httpd进程,默认信号是15,正常停止
#如果默认信号15不能杀死进程,则可以尝试-9信号,强制杀死进程
[root@localhost ~]# pstree -p | grep httpd | grep -v "grep"
|-httpd(2246>-+-httpd(2247)
|    |-httpd(2249)
|    |-httpd(2250)
|    |-httpd(2251)
#PID是2248的httpd进程消失了

【例 2】使用“-1”信号,让进程重启#

[root@localhost ~]# kill -1 2246
使用“-1 (数字1)”信号,让httpd的主进程重新启动
[root@localhost ~]# pstree -p | grep httpd | grep -v "grep"
|-httpd(2246)-+-httpd(2270)
|    |-httpd(2271)
|    |-httpd(2272)
|    |-httpd(2273)
|    |-httpd(2274)
#子httpd进程的PID都更换了,说明httpd进程已经重启了一次

【例 3】 使用“-19”信号,让进程暂停#

[root@localhost ~]# vi test.sh #使用vi命令编辑一个文件,不要退出
[root@localhost ~]# ps aux | grep "vi" | grep -v "grep"
root 2313 0.0 0.2 7116 1544 pts/1 S+ 19:2.0 0:00 vi test.sh
#换一个不同的终端,查看一下这个进程的状态。进程状态是S(休眠)和+(位于后台),因为是在另一个终端运行的命令
[root@localhost ~]# kill -19 2313
#使用-19信号,让PID为2313的进程暂停。相当于在vi界面按Ctrl+Z快捷键
[root@localhost ~]# ps aux | grep "vi" | grep -v "grep"
root 2313 0.0 0.2 7116 1580 pts/1 T 19:20 0:00 vi test.sh
#注意2313进程的状态,变成了 T(暂停)状态。这时切换回vi的终端,发现vi命令已经暂停,又回到了命令提示符,不过2313进程就会卡在后台。如果想要恢复,可以使用"kill -9 2313”命令强制中止进程,也可以利用后续章节将要学习的工作管理来进行恢复

Linux killall命令#

killall 命令不再依靠 PID 来杀死单个进程,而是通过程序的进程名来杀死一类进程。命令格式如下:

[root@localhost ~]# killall [选项] [信号] 进程名

选项:

  • -i:交互式,询问是否要杀死某个进程;
  • -l:忽略进程名的大小写;

举几个例子。

【例 1】杀死httpd进程#

[root@localhost ~]# service httpd start
#启动RPM包默认安装的apache服务
[root@localhost ~]# ps aux | grep "httpd" | grep -v "grep"
root 1600 0.0 0.2 4520 1696? Ss 19:42 0:00 /usr/local/apache2/bin/httpd -k start
daemon 1601 0.0 0.1 4520 1188? S 19:42 0:00 /usr/local/apache2/bin/httpd -k start
daemon 1602 0.0 0.1 4520 1188? S 19:42 0:00 /usr/local/apache2/bin/httpd -k start
daemon 1603 0.0 0.1 4520 1188? S 19:42 0:00 /usr/local/apache2/bin/httpd -k start
daemon 1604 0.0 0.1 4520 1188? S 19:42 0:00 /usr/local/apache2/bin/httpd -k start
daemon 1605 0.0 0.1 4520 1188? S 19:42 0:00 /usr/local/apache2/bin/httpd -k start
#查看httpd进程
[root@localhost ~]# killall httpd
#杀死所有进程名是httpd的进程
[root@localhost ~]# ps aux | grep "httpd" | grep -v "grep"
#查询发现所有的httpd进程都消失了

【例 2】交互式杀死sshd进程#

[root@localhost ~]# ps aux | grep "sshd" | grep -v "grep"
root 1733 0.0 0.1 8508 1008? Ss 19:47 0:00/usr/sbin/sshd
root 1735 0.1 0.5 11452 3296? Ss 19:47 0:00 sshd: root@pts/0
root 1758 0.1 0.5 11452 3296? Ss 19:47 0:00 sshd: root@pts/1
#查询系统中有3个sshd进程。1733是sshd服务的进程,1735和1758是两个远程连接的进程
[root@localhost ~]# killall -i sshd
#交互式杀死sshd进程
杀死sshd(1733)?(y/N)n
#这个进程是sshd的服务进程,如果杀死,那么所有的sshd连接都不能登陆
杀死 sshd(1735)?(y/N)n
#这是当前登录终端,不能杀死我自己吧
杀死 sshd(1758)?(y/N)y
#杀死另一个sshd登陆终端

Linux pkill命令#

pkill 命令和 killall 命令非常类似,也是按照进程名来杀死进程的。命令格式如下:

1
[root@localhost ~]# pkill [选项] [信号] 进程名

选项:

  • -t 终端号:按照终端号踢出用户;

不过 pkill 命令可以按照终端号来踢出用户。不知道大家发现没有,刚刚通过 killall 命令杀死 sshd 进程的方式来踢出用户,非常容易误杀死进程,要么会把 sshd 服务杀死,要么会把自己的登录终端杀死。

所以,不管是使用 kill 命令按照 PID 杀死登录进程,还是使用 killall 命令按照进程名杀死登录进程,都是非常容易误杀死进程的。 具体命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~]# w
#使用w命令査询本机已经登录的用户
20:06:34 up 28 min, 3 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root ttyl - 19:47 18:52 0.01s 0.01s -bash
root pts/0 192.168.0.100 19:47 0.00s 0.09s 0.04s w
root pts/1 192.168.0.100 19:51 14:56 0.02s 0.02s -bash
#当前主机已经登录了三个root用户,一个是本地终端ttyl登录,另外两个是从192.168.0.100登陆的远程登录
[root@localhost ~]# pkill -9 -t pts/1
#强制杀死从pts/1虚拟终端登陆的进程
[root@localhost ~]# w
20:09:09 up 30 min, 2 users, load average: 0.00, 0.00,0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root ttyl - 19:47 21:27 0.01s 0.01s -bash
root pts/0 192.168.0.100 19:47 0.00s 0.06s 0.00s w
#虚拟终端pts/1的登录进程已经被杀死了

 

Linux进程优先级#

Linux 是一个多用户、多任务的操作系统,系统中通常运行着非常多的进程。但是 CPU 在一个时钟周期内只能运算一条指令(现在的 CPU 采用了多线程、多核心技术,所以在一个时钟周期内可以运算多条指令。 但是同时运算的指令数也远远小于系统中的进程总数),那问题来了:谁应该先运算,谁应该后运算呢?这就需要由进程的优先级来决定了。

另外,CPU 在运算数据时,不是把一个集成算完成,再进行下一个进程的运算,而是先运算进程 1,再运算进程 2,接下来运算进程 3,然后再运算进程 1,直到进程任务结束。不仅如此,由于进程优先级的存在,进程并不是依次运算的,而是哪个进程的优先级高,哪个进程会在一次运算循环中被更多次地运算。

这样说很难理解,我们换一种说法。假设我现在有 4 个孩子(进程)需要喂饭(运算),我更喜欢孩子 1(进程 1 优先级更高),孩子 2、孩子 3 和孩子 4 一视同仁(进程 2、进程 3 和进程 4 的优先级一致)。现在我开始喂饭了,我不能先把孩子 1 喂饱,再喂其他的孩子,而是需要循环喂饭(CPU 运算时所有进程循环运算)。那么,我在喂饭时(运算),会先喂孩子 1 一口饭,然后再去喂其他孩子。而且在一次循环中,先喂孩子 1 两口饭,因为我更喜欢孩子 1(优先级高),而喂其他的孩子一口饭。这样,孩子 1 会先吃饱(进程 1 运算得更快),因为我更喜欢孩子 1。

在 Linux 系统中,表示进程优先级的有两个参数:Priority 和 Nice。还记得"ps -l"命令吗?

1
2
3
4
5
[root@localhost ~]# ps -le
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 1 0 0 80 0 - 718 - ? 00:00:01 init
1 S 0 2 0 0 80 0 - 0 - ? 00:00:00 kthreadd
...省略部分输出...

其中,PRI 代表 Priority,NI 代表 Nice。这两个值都表示优先级,数值越小代表该进程越优先被 CPU 处理。不过,PRI值是由内核动态调整的,用户不能直接修改。所以我们只能通过修改 NI 值来影响 PRI 值,间接地调整进程优先级。

PRI 和 NI 的关系如下:

PRI (最终值) = PRI (原始值) +NI

其实,大家只需要记得,我们修改 NI 的值就可以改变进程的优先级即可。NI 值越小,进程的 PRI 就会降低,该进程就越优先被 CPU 处理;反之,NI 值越大,进程的 PRI 值就会増加,该进程就越靠后被 CPU 处理。

修改 NI 值时有几个注意事项:

    • NI 范围是 -20~19。
    • 普通用户调整 NI 值的范围是 0~19,而且只能调整自己的进程。
    • 普通用户只能调高 NI 值,而不能降低。如原本 NI 值为 0,则只能调整为大于 0。
    • 只有 root 用户才能设定进程 NI 值为负值,而且可以调整任何用户的进程。

Linux nice和renice命令#

Linux 中可以通过 nice 命令和 renice 命令修改进程的优先级。

nice命令#

nice 命令可以给新执行的命令直接赋予 NI 值,但是不能修改已经存在进程的 NI 值。
nice 命令格式如下:

[root@localhost ~] # nice [选项] 命令

选项:

  • -n NI值:给命令赋予 NI 值;

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@localhost ~]# service httpd start
[root@localhost ~]# ps -le 丨 grep "httd" | grep -v grep
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
1 S 0 2084 1 0 80 0 - 1130 - ? 00:00:00 httpd
5 S 2 2085 2084 0 80 0 - 1130 - ? 00:00:00 httpd
5 S 2 2086 2084 0 80 0 - 1130 - ? 00:00:00 httpd
5 S 2 2087 2084 0 80 0 - 1130 - ? 00:00:00 httpd
5 S 2 2088 2084 0 80 0 - 1130 - ? 00:00:00 httpd
5 S 2 2089 2084 0 80 0 - 1130 - ? 00:00:00 httpd
#用默认优先级自动apache服务,PRI值是80,而NI值是0
[root@localhost ~]# service httpd stop
#停止apache服务
[root@localhost ~]# nice -n -5 service httpd start
#启动apache服务,同时修改apache服务进程的NI值为-5
[rooteiocdlhost ~]# ps -le | grep "httpd" | grep -v grep
F S UID PID PPID C FRI NI ADDR SZ WCHAN TTY TIME CMD
1 S 0 2122 1 0 75 -5 - 1130 - ? 00:00:00 httpd
5 S 2 2123 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
5 S 2 2124 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
5 S 2 2125 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
5 S 2 2126 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
5 S 2 2127 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
#httpd进程的PRI值变为了75,而NI值为-5

renice 命令#

renice 命令可以修改已经存在进程的 NI 值。
renice 命令格式如下:

[root@localhost ~] # renice [优先级] PID

例如:

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# renice -10 2125
2125: old priority -5, new priority -10
[root@localhost ~]# ps -le | grep "httpd" | grep -v grep
1 S 0 2122 1 0 75 -5 - 113.0 - ? 00:00:00 httpd
5 S 2 2123 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
5 S 2 2124 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
5 S 2 2125 2122 0 70 -10 - 1130 - ? 00:00:00 httpd
5 S 2 2126 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
5 S 2 2.127 2122 0 75 -5 - 1130 - ? 00:00:00 httpd
#PID为2125的进程的PRI值为70,而NI值为-10

Linux工作管理#

工作管理指的是在单个登录终端(也就是登录的 Shell 界面)同时管理多个工作的行为。也就是说,我们登陆了一个终端,已经在执行一个操作,那么是否可以在不关闭当前操作的情况下执行其他操作呢?
当然可以,我们可以再启动一个终端,然后执行其他的操作。不过,是否可以在一个终端执行不同的操作呢?这就需要通过工作管理来实现了。
例如,我在当前终端正在 vi 一个文件,在不停止 vi 的情况下,如果我想在同一个终端执行其他的命令,就应该把 vi 命令放入后台,然后再执行其他命令。把命令放入后台,然后把命令恢复到前台,或者让命令恢复到后台执行,这些管理操作就是工作管理。
后台管理有几个事项需要大家注意:

    1. 前台是指当前可以操控和执行命令的这个操作环境;后台是指工作可以自行运行,但是不能直接用 Ctrl+C 快捷键来中止它,只能使用 fg/bg 来调用工作。
    2. 当前的登录终端只能管理当前终端的工作,而不能管理其他登录终端的工作。比如 tty1 登录的终端是不能管理 tty2 终端中的工作的。
    3. 放入后台的命令必须可以持续运行一段时间,这样我们才能捕捉和操作它。
    4. 放入后台执行的命令不能和前台用户有交互或需要前台输入,否则只能放入后台暂停,而不能执行。比如 vi 命令只能放入后台暂停,而不能执行,因为 vi 命令需要前台输入信息;top 命令也不能放入后台执行,而只能放入后台暂停,因为 top 命令需要和前台交互。

Linux命令放入后台运行#

Linux 命令放入后台的方法有两种,分别介绍如下。

"命令 &",把命令放入后台执行#

第一种把命令放入后台的方法是在命令后面加入 空格 &。使用这种方法放入后台的命令,在后台处于执行状态。

注意,放入后台执行的命令不能与前台有交互,否则这个命令是不能在后台执行的。例如:

1
2
3
4
[root@localhost ~]#find / -name install.log &
[1] 1920
#[工作号] 进程号
#把find命令放入后台执行,每个后台命令会被分配一个工作号。命令既然可以执行,就会有进程产生,所以也会有进程号

这样,虽然 find 命令在执行,但在当前终端仍然可以执行其他操作。如果在终端上出现如下信息:

[1]+ Done find / -name install.log

则证明后台的这个命令已经完成了。当然,命令如果有执行结果,则也会显示到操作终端上。其中,[1] 是这个命令的工作号,"+"代表这个命令是最近一个被放入后台的。

命令执行过裎中按 Ctrl+Z 快捷键,命令在后台处于暂停状态#

使用这种方法放入后台的命令,就算不和前台有交互,能在后台执行,也处于暂停状态,因为 Ctrl+Z 快捷键就是暂停的快捷键。

【例 1】

1
2
3
4
[root@localhost ~]#top
#在top命令执行的过程中,按下Ctrl+Z快捷键
[1]+ Stopped top
#top命令被放入后台,工作号是1,状态是暂停。而且,虽然top命令没有结束,但也能取得控制台权限

【例 2】

1
2
3
4
5
6
7
8
[root@localhost ~]# tar -zcf etc.tar.gz /etc
#压缩一下/etc/目录
tar:从成员名中删除开头的"/"
tar:从硬链接目标中删除开头的"/"
^Z
#在执行过程中,按下Ctrl+Z快捷键
[2]+ Stopped tar-zcf etc.tar.gz/etc
#tar命令被放入后台,工作号是2,状态是暂停

每个被放入后台的命令都会被分配一个工作号。第一个被放入后台的命令,工作号是 1;第二个被放入后台的命令,工作号是 2,以此类推。

Linux jobs命令#

jobs 命令可以用来查看当前终端放入后台的工作,工作管理的名字也来源于 jobs 命令。

jobs 命令格式如下:

[root@localhost ~]#jobs [选项]

选项:

  • -l:显示工作的 PID;

例如:

[root@localhost ~]#jobs -l
[1]- 2023 Stopped top
[2]+ 2034 Stopped tar -zcf etc.tar.gz /etc

可以看到,当前终端有两个后台工作:一个是 top 命令,工作号为 1,状态是暂停,标志是"-";另一个是 tar 命令,工作号为 2,状态是暂停,标志是"+"。

"+"号代表最近一个放入后台的工作,也是工作恢复时默认恢复的工作。"-"号代表倒数第二个放入后台的工作,而第三个以后的工作就没有"+-"标志了。

Linux fg命令#

如果想把后台工作恢复到前台执行,就需要执行 fg 命令。

fg 命令格式如下:

[root@localhost ~]#fg %工作号

参数:

  • %工作号:"%"可以省略,但是注意工作号和PID的区别;

例如:

[root@localhost ~]#jobs
[1]- Stopped top
[2]+ Stopped tar-zcf etc.tar.gz/etc
[root@localhost ~]# fg
#恢复“+”标志的工作,也就是tar命令
[root@localhost ~]# fg %1
#恢复1号工作,也就是top命令

top 命令是不能在后台执行的,所以,如果要想中止 top 命令,要么把 top 命令恢复到前台,然后正常退出;要么找到 top 命令的 PID,使用 kill 命令杀死这个进程。

Linux bg命令#

使用 Ctrl+Z 快捷键方式放入后台的命令,在后台都处于暂停状态,如何让这个后台工作继续在后台执行呢?这就需要使用 bg 命令了。
bg 命令格式如下:

[root@localhost ~]# bg %工作号

把刚刚放入后台的两个工作恢复运行,命令如下:

[root@localhost ~]# bg %1
[root@localhost ~]# bg %2
#把两个命令恢复到后台执行
[root@localhost @]# jobs
[1]+ Stopped top
[2]- Running tar -zcf etc.tar.gz /etc &
#tar命令的状态变为了Running,但是top命令的状态还是Stopped

可以看到,tar 命令确实已经在后台执行了,但是 top 命令怎么还处于暂停状态呢?那是因为 top 命令是需要和前台交互的,所以不能在后台执行。top 命令就是给前台用户显示系统性能的命令,所以是不能在后台恢复运行的。如果 top 命令在后台恢复运行了,那么给谁去看结果呢?

Linux后台命令脱离终端运行#

我们知道,把命令放入后台,只能在当前登录终端执行。如果是远程管理的服务器,在远程终端执行了后台命令,这时退出登录,那么这个后台命令还能继续执行吗?

当然是不行的,这个后台命令会被中止。但是我们确实需要在远程终端执行某些后台命令,该如何执行呢?有以下 3 种方法:

  1. 把需要在后台执行的命令加入 /etc/rc.local 文件,让系统在启动时执行这个后台程序。这种方法的问题是,服务器是不能随便重启的,如果有临时后台任务,就不能执行了。
  2. 使用系统定时任务,让系统在指定的时间执行某个后台命令。这样放入后台的命令与终端无关,是不依赖登录终端的。
  3. 使用 nohup 命令。


nohup 命令的作用就是让后台工作在离开操作终端时,也能够正确地在后台执行。

nohup 命令格式如下:

[root@localhost ~]# nohup [命令] &


例如:

[root@localhost ~]# nohup find / -print > /root/file.log &
[3] 2349
#使用find命令,打印/下的所有文件。放入后台执行
[root@localhost ~]# nohup:忽略输入并把输出追加到"nohup.out"
[root@localhost ~]# nohup:忽略输入并把输出追加到"nohup.out"
#有提示信息

接下来的操作要迅速,否则 find 命令就会执行结束。然后我们可以退出登录,重新登录之后,执行“ps aux”命令,会发现 find 命令还在运行。

如果 find 命令执行太快,我们就可以写一个循环脚本,然后使用 nohup 命令执行。例如:

[root@localhost ~]# vi for.sh
#!/bin/bash
for ((i=0;i<=1000;i=i+1))
#循环1000次
do
echo 11 >> /root/for.log
#在for.log文件中写入11
sleep 10s
#每次循环睡眠10秒
done
[root@localhost ~]# chmod 755 for.sh
[root@localhost ~]# nohup /root/for.sh &
[1] 2478
[root@localhost ~]# nohup:忽略输入并把输出追加到"nohup.out"
#执行脚本

接下来退出登录,重新登录之后,这个脚本仍然可以通过“ps aux”命令看到。

Linux vmstat命令#

vmstat 是 Linux 中的一个综合性能分析工具,可以用来监控 CPU 使用、进程状态、内存使用、虚拟内存使用、磁盘输入/输出状态等信息。
vmstat 命令格式如下:

1
[root@localhost ~]# vmstat [刷新延时 刷新次数]

例如:

1
2
3
4
5
6
7
8
[root@localhost proc]# vmstat 1 3
#使用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
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 407376 55772 84644 0 0 5 2 9 10 0 0 100 0 0
0 0 0 407368 55772 84644 0 0 0 0 12 10 0 0 100 0 0
0 0 0 407368 55772 84644 0 0 0 0 15 13 0 0 100 0 0

解释一下这个命令的输出。
1) procs:进程信息字段:

  • -r:等待运行的进程数,数量越大,系统越繁忙。
  • -b:不可被唤醒的进程数量,数量越大,系统越繁忙。


2) memory:内存信息字段:

  • -swpd:虚拟内存的使用情况,单位为KB。
  • -free:空闲的内存容量,单位为KB。
  • -buff:缓冲的内存容量,单位为KB。
  • -cache:缓存的内存容量,单位为KB。


3) swap:交换分区信息字段:

  • -si:从磁盘中交换到内存中数据的数量,单位为KB。
  • -so:从内存中交换到磁盘中数据的数量,单位为KB。

这两个数越大,表明数据需要经常在磁盘和内存之间进行交换,系统性能越差。

4) io:磁盘读/写信息字段:

  • -bi:从块设备中读入的数据的总量,单位是块。
  • -bo:写到块设备的数据的总量,单位是块。

这两个数越大,代表系统的 I/O 越繁忙。

5) system:系统信息字段:

  • -in:每秒被中断的进程次数。
  • -cs:每秒进行的事件切换次数。

这两个数越大,代表系统与接口设备的通信越繁忙。

6) cpu:CPU信息字段:

  • -us:非内核进程消耗CPU运算时间的百分比。
  • -sy:内核进程消耗CPU运算时间的百分比。
  • -id:空闲CPU的百分比。
  • -wa:等待I/O所消耗的CPU百分比。
  • -st:被虚拟机所盗用的CPU百分比。


本机是一台测试用的虚拟机,并没有多少资源被占用,所以资源占比都比较低。如果服务器上的资源占用率比较高,那么使用 vmstat 命令查看到的参数值就会比较大。我们就需要手工进行干预,如果是非正常进程占用了系统资源,则需要判断这些进程是如何产生的,不能一杀了之;如果是正常进程占用了系统资源,则说明服务器需要升级了。

本机是一台测试用的虚拟机,并没有多少资源被占用,所以资源占比都比较低。如果服务器上的资源占用率比较高,那么使用 vmstat 命令查看到的参数值就会比较大。我们就需要手工进行干预,如果是非正常进程占用了系统资源,则需要判断这些进程是如何产生的,不能一杀了之;如果是正常进程占用了系统资源,则说明服务器需要升级了。

Linux dmesg命令#

在系统启动过程中,内核还需要进行一次系统检测,这些内核检测信息会被记录在内存当中。我们是否可以查看内核检测信息呢?使用 dmesg 命令就可以查看这些信息。我们一般利用这个命令查看系统的硬件信息。
dmesg 命令格式如下:

[root@localhost ~]# dmesg

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@localhost ~]# dmesg | grep CPU
#查看CPU的信息
Transmeta TransmetaCPU
SMP:Allowing 1 CPUs, 0 hotplug CPUs
NR_CPUS:32 nr_cpumask_bits:32 nr_cpu_ids:1 nr_node_ids:1
PERCPU:Embedded 14 pages/cpu @c1a00000 s35928 r0 d21416 u2097152
Initializing CPU#0
CPU:Physical Processor ID:0
mce:CPU supports 0 MCE banks
CPU0:Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz stepping 09
Brought up 1 CPUs
microcode:CPU0 sig=0x306a9, pf=0x1, revision=0x12
[root@localhost ~]# dmesg | grep eth0
#查看第一块网卡的信息
eth0:registered as PCnet/PCI II 79C970A
eth0:link up
eth0:no IPv6 routers present

free 命令可以查看系统内存和 swap 交换分区的使用情况,其输出和 top 命令的内存部分非常相似。

free 命令格式如下:

[root@localhost ~]# free [选项]

选项:

  • -b:以字节为单位显示;
  • -k:以 KB 为单位显示,默认显示;
  • -m:以 MB 为单位显示;
  • -g:以 GB 为单位显示;


例如:

[root@localhost ~]# free
total used free shared buffers cached
Mem: 625344 219704 405640 0 56852 85276
-/+ buffers/cache: 77576 547768
Swap: 524280 0 524280

解释一下这个命令的输出。

    • 第一行:total 是总内存数,used 是已经使用的内存数,free 是空闲的内存数,shared 是多个进程共享的内存总数,buffers 是缓冲内存数,cached 是缓存内存数。默认单位是 KB。
    • 第二行:-/buffers/cache 的内存数,相当于第一行的 used-buffers-cached。+/buffers/cache 的内存数,相当于第一行的 free+buffers+cached。
    • 第三行:total 是 swap 交换分区的总数;used 是已经使用的 swap 交换分区数,free 是空闲的 swap 交换分区数。默认单位是 KB。

Linux查看CPU信息 #

CPU 的主要信息保存在 /proc/cpuinfo 这个文件中,我们只要查看这个文件,就可以知道 CPU 的相关信息。
命令如下:

[root@localhost ~]# cat /proc/cpuinfo
processor :0
#逻辑CPU编号
vendor_id :GenuineIntel
#CPU制造厂商
cpu family :6
#产品的系列代号
model :58
#CPU系列代号
model name :Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
#CPU系列的名字、编号、主频
stepping :9
#更新版本
cpu MHz :2394.649
#实际主频
cache size :6144 KB
#二级缓存
fdiv_bug :no
fdiv_bug :no
hlt_bug :no
f00f_bug :no
coma_bug :no
fpu :yes
fpu_exception :yes
cpuid level :13
wp :yes
flags :fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat
pse36 clflush dts acpi mmx fxsr sse sse2 ss nx rdtscp lm constant_tsc up arch_perfmon
pebs bts xtopology tsc_reliable nonstop_tsc aperfmperf unfair_spinlock pni
pclmulqdq ssse3 cx16 sse4_1 sse4_2 popcnt aes xsave avx hypervisor lahf_lm ida
arat epb xsaveopt pln pts dts
#CPU的功能标识
bogomips :4789.29
clflush size :64
cache_alignment:64
address sizes :40 bits physical, 48 bits virtual
power management:

Linux查看本机登陆用户信息#

如果我们想要知道 Linux 服务器上目前已经登录的用户信息,则可以使用 w 或 who 命令来进行查询。

w 命令#

先看看 w 命令,如下:

1
2
3
4
5
[root@localhost ~]# w
09:54:22 up 9:50, 2 users, load average:0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty1 - 09:53 57.00s 0.02s 0.02s -bash
root pts/0 192.168.0.108 09:53 0.00s 0.09s 0.06s w

解释一下这个命令的输出。

  • 第一行其实和 top 命令的第一行非常类似,主要显示了系统当前时间、开机多久(up)、有多少用户登录(users),以及 1 分钟、5 分钟、15 分钟前的平均负载(load average)。
  • 第二行是项目的说明,从第三行开始每行代表一个用户。这些项目具体如下:
    1. USER:登录的用户名。
    2. TTY:登录终端。
    3. FROM:从哪个 IP 地址登录。
    4. LOGIN@:登录时间。
    5. IDLE:用户闲置时间。
    6. JCPU:和该终端连接的所有进程占用的 CPU 运算时间。这个时间里并不包括过去的后台作业时间,但是包括当前正在运行的后台作业所占用的时间。
    7. PCPU:当前进程所占用的 CPU 运算时间。
    8. WHAT:当前正在运行的命令。

从 w 命令的输出中已知,Linux 服务器上已经登录了两个 root 用户,一个是从本地终端 1 登录的(tty1),另一个是从远程终端 1 登录的(pts/0),登录的来源 IP 是 192.168.0.108。

who命令#

who 命令比 w 命令稍微简单,也可以用来查看系统中已经登录的用户。命令如下:

1
2
3
4
[root@localhost ~]# who
root tty1 2013-06-07 09:53
root pts/0 2013-06-07 09:53 (192.168.0.108)
#用户名 登录终端 登录时间(登录来源IP)

如果原先登录的用户现在已经退出登录,那么是否还能查看呢?当然可以,这时就需要使用 last 和 lastlog 命令了。

last命令#

我们先来看看 last 命令,如下:

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# last
#查询当前已经登录和过去登录的用户信息
root pts/0 192.168.0.108 Fri Jun 7 09:53 still logged in
root tty1 Fri Jun 7 09:53 still logged in
root pts/0 192.168.0.101 Fri Jun 7 06:53 - 09:52 (02:59)
root pts/1 192.168.0.103 Fri Jun 7 04:12 - 04:30 (00:18)
#用户名 登录终端 登录IP 登录时间 - 退出时间(在线时间)
reboot system boot 2.6.32-279.el6.i Thu Jun 6 23:53 - 00:03 (00:09)
#还能看到系统的重启时间
…省略部分输出…

last 命令默认是去读取 /var/log/wtmp 日志文件的,这是一个二进制文件,不能直接用 vi 编辑,只能通过 last 命令调用。

lastlog命令#

再来看看 lastlog 命令,如下:

[root@localhost ~]# lastlog
#查看系统中所有用户的最后一次登录时间、登录端口和来源IP
用户名 端口 来自 最后登录时间
root pts/0 192.168.0.108 五 6月 7 09:53:46 +0800 2013
bin **从未登录过**
daemon **从未登录过**
adm **从未登录过**
lp **从未登录过**
sync **从未登录过**
…省略部分输出…

lastlog 命令默认是去读取 /var/log/lastlog 日志文件的,这个文件同样是二进制文件,不能直接用 vi 编辑,需要使用 lastlog 命令调用。

Linux查看系统与内核信息#

uname 命令可以用来查看系统与内核的相关信息,命令格式如下:

1
[root@localhost ~]# uname [选项]

选项:

  • -a:查看系统所有相关信息;
  • -r:查看内核版本;
  • -s:查看内核名称;

例如:

1
2
3
4
5
[root@localhost ~]# uname -a
Linux localhost.localdomain 2.6.32-279.el6.i686 #1 SMP Fri Jun 22 10:59:55 UTC
20.2 i686 i686 i386 GNU/Linux
[root@localhost ~]# uname -r
2.6.32-279.el6.i686

如果我们想要判断当前系统的位数,则可以通过 file 命令来判断系统文件(主要是系统命令)的位数,进而推断系统的位数。命令如下:

1
2
3
4
[root@localhost ~]# file /bin/ls
/bin/ls:ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically
linked (uses shared libs), for GNU/Linux 2.6.18, stripped
#很明显,当前系统是32位的


如果我们想要查询当前 Linux 系统的发行版本,则可以使用“lsb_release -a”命令。命令如下:

1
2
3
4
5
6
7
8
9
[root@localhost ~]# lsb_release -a
LSB
Version::core-4.0-ia32:core-4.0-noarch:graphics-4.0-ia32:graphics-4.0-noar
ch:printing-4.0-ia32:printing-4.0-noarch
Distributor ID:CentOS
Description: CentOS release 6.3 (Final)
Release: 6.3
Codename: Final
#当前使用的Linux的发行版本是CentOS 6.3

Linux lsof命令#

我们可以通过 ps 命令查询到系统中所有的进程,那么是否可以知道这个进程到底在调用哪些文件吗?这时就需要 lsof 命令的帮助了。
lsof 命令格式如下:

1
2
[root@localhost ~]# lsof [选项]
#列出进程调用或打开的文件信息

选项:

  • -c 字符串: 只列出以字符串开头的进程打开的文件;
  • +d 目录名: 列出某个目录中所有被进程调用的文件;
  • -u 用户名: 只列出某个用户的进程打开的文件;
  • -p pid: 列出某个 PID 进程打开的文件;

【例 1】

1
2
3
4
5
6
7
8
9
10
11
[root@localhost ~]# lsof | more
#查询系统中所有进程调用的文件
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
init 1 root cwd DIR 8,3 4096 2 /
init 1 root rtd DIR 8,3 4096 2 /
init 1 root txt REG 8,3 145180 130874 /sbin/init
init 1 root mem REG 8,3 142472 665291
/lib/ld-2.12.so
init 1 root mem REG 8,3 58704 655087
/lib/libnss_files-2.12.so
…省略部分输出…

这个命令的输出非常多。它会按照 PID,从 1 号进程开始列出系统中所有的进程正在调用的文件名。

【例 2】

1
2
3
4
[root@localhost ~]# lsof /sbin/init
#查询某个文件被哪个进程调用
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
init 1 root txt REG 8,3 145180 130874 /sbin/init

lsof 命令也可以反过来查询某个文件被哪个进程调用。这个例子就查询到 /sbin/init 文件是被 init 进程调用的。

【例 3】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@localhost ~]# lsof +d /usr/lib
#查询某个目录下所有的文件是被哪些进程调用的
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rpc.idmap 1196 root mem REG 8,3 26400 279930
/usr/lib/libnfsidmap.so.0.3.0
rpc.idmap 1196 root mem REG 8,3 108948 276696
/usr/lib/libevent-1.4.so.2.1.3
avahi-dae 1240 avahi mem REG 8,3 49124 271310
/usr/lib/libavahi-common.so.3.5.1
avahi-dae 1240 avahi mem REG 8,3 23904 283188
/usr/lib/libdaemon.so.0.5.0
avahi-dae 1240 avahi mem REG 8,3 227212 268396
/usr/lib/libavahi-core.so.6.0.1
avahi-dae 1241 avahi mem REG 8,3 49124 271310
/usr/lib/libavahi-common.so.3.5.1
avahi-dae 1241 avahi mem REG 8,3 23904 283188
/usr/lib/libdaemon.so.0.5.0
avahi-dae 1241 avahi mem REG 8,3 227212 268396
/usr/lib/libavahi-core.so.6.0.1
cupsd 1251 root mem REG 8,3 69564 270210
/usr/lib/libtasn1.so.3.1.6

使用“+d”选项可以搜索某个目录下所有的文件,查看到底哪个文件被哪个进程调用了。

【例 4】

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# lsof -c httpd
#查看以httpd开头的进程调用了哪些文件
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
httpd 4689 root cwd DIR 8,3 4096 2 /
httpd 4689 root rtd DIR 8,3 4096 2 /
httpd 4689 root txt REG 8,3 1797559 2855
/usr/local/apache2/bin/httpd
httpd 4689 root mem REG 8,3 302300 665303 /lib/libfreebl3.so
httpd 4689 root mem REG 8,3 58704 655087 /lib/libnss_files-2.12.s
httpd 4689 root mem REG 8,3 142472 665291 /lib/ld-2.12.so
httpd 4689 root mem REG 8,3 1889704 665292 /lib/libc-2.12.so
…省略部分输出…

使用“-c”选项可以查询以某个字符串开头的进程调用的所有文件,比如执行“lsof-c httpd”命令就会查询出以 httpd 开头的进程调用的所有文件。

【例 5】

1
2
3
4
5
6
7
8
[root@localhost ~]# lsof -p 1
#查询PID是1的进程调用的文件
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
init 1 root cwd DIR 8,3 4096 2 /
init 1 root rtd DIR 8,3 4096 2 /
init 1 root txt REG 8,3 145180 130874 /sbin/init
init 1 root mem REG 8,3 142472 665291 /lib/ld-2.12.so
init 1 root mem REG 8,3 58704 655087 /lib/libnss_files-2.12.so

当然,我们也可以按照 PID 查询进程调用的文件,比如执行“lsof -p 1”命令就可以查看 PID 为 1 的进程调用的所有文件。

【例 6】

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# lsof -u root
#按照用户名查询某个用户的进程调用的文件
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
init 1 root cwd DIR 8,3 4096 2 /
init 1 root rtd DIR 8,3 4096 2 /
init 1 root txt REG 8,3 145180 130874 /sbin/init
init 1 root mem REG 8,3 142472 665291 /lib/ld-2.12.so
init 1 root mem REG 8,3 58704 655087/lib/libnss_files-2.12.s
init 1 root mem REG 8,3 38768 655310/lib/libnih-dbus.so.1.0.
…省略部分输出…

我们还可以查看某个用户的进程调用了哪些文件。

Linux at命令#

at 命令要想正确执行,需要 atd 服务的支持。atd 服务是独立的服务,启动的命令如下:

1
[root@localhost ~]# service atd start

正在启动 atd: [确定]

如果想让 atd 服务开机时自启动,则可以使用如下命令:

[root@localhost ~]# chkconfig atd on

当然,独立服务的自启动也可以修改 /etc/rc.local 配置文件,具体怎么做看个人习惯。

atd 服务启动之后,at 命令才可以正常使用,不过我们还要学习一下 at 命令的访问控制。这里的访问控制指的是允许哪些用户使用 at 命令设定定时任务,或者不允许哪些用户使用 at 命令。大家可以将其想象成设定黑名单或白名单,这样更容易理解。

at 命令的访问控制是依靠 /etc/at.allow(白名单)和 /etc/at.deny(黑名单)这两个文件来实现的,具体规则如下:

  • 如果系统中有 /etc/at.allow 文件,那么只有写入 /etc/at.allow 文件(白名单)中的用户可以使用 at 命令,其他用户不能使用 at 命令(/etc/at.deny 文件会被忽略,也就是说,同一个用户既写入 /etc/at.allow 文件,又写入 /etc/at.deny 文件,那么这个用户是可以使用 at 命令的,因为 /etc/at.allow 文件的优先级更高)。
  • 如果系统中有 /etc/at.allow 文件,那么只有写入 /etc/at.allow 文件(白名单)中的用户可以使用 at 命令,其他用户不能使用 at 命令(/etc/at.deny 文件会被忽略,也就是说,同一个用户既写入 /etc/at.allow 文件,又写入 /etc/at.deny 文件,那么这个用户是可以使用 at 命令的,因为 /etc/at.allow 文件的优先级更高)。
  • 如果系统中没有 /etc/at.allow 文件,只有 /etc/at.deny 文件,那么写入 /etc/at.deny 文件(黑名单)中的用户不能使用 at 命令,其他用户可以使用 at 命令。不过这个文件对 root 用户不生效。
  • 如果系统中这两个文件都不存在,那么只有 root 用户可以使用 at 命令。


系统中默认只有 /etc/at.deny 文件,而且这个文件是空的,这样系统中所有的用户都可以使用 at 命令。不过,如果我们打算控制用户的 at 命令权限,那么只需把用户写入 /etc/at.deny 文件即可。对于 /etc/at.allow 和 /etc/at.deny 文件的优先级,我们做一个实验来验证一下,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[root@localhost ~]# ls -l /etc/at*
-rw-r--r--.1 root root 1 1月 30 2012 /etc/at.deny
#系统中默认只有at.deny文件
[root@localhost ~]# echo user1 >> /etc/at.deny
[root@localhost ~]# cat /etc/at.deny
user1
#把user1用户写入/etc/at.deny文件
[root@localhost ~]# su - user1
[user1@localhost ~]$ at 02:00
You do not have permission to use at.
#没有权限使用at命令
#切换成user1用户,这个用户已经不能执行at命令了
[user1@localhost ~]$ exit
logout
#返回root身份
[root@localhost ~]# echo user1 >> /etc/at.allow
[root@localhost ~]# cat /etc/at.allow
user1
#建立/etc/at.allow文件,并在文件中写入user1用户
[root@localhost ~]# su - user1
[user1@localhost ~]$ at 02:00
at>
#切换成user1用户,user1用户可以执行at命令。这时user1用户既在/etc/at.deny文件中,
#又在/etc/at.allow文件中,但是/etc/at.allow文件的优先级更高
[user1@localhost ~]$ exit
logout
#返回root身份
[root@localhost ~]# at 02:00
at>
#root用户虽然不在/etc/at.allow文件中,但是也能执行at命令,
#root用户虽然不在/etc/at.allow文件中,但是也能执行at命令,
#说明root用户不受这两个文件的控制

这个实验说明了 /etc/at.allow 文件的优先级更高,如果 /etc/at.allow 文件存在,则 /etc/at.deny 文件失效。/etc/at.allow 文件的管理更加严格,因为只有写入这个文件的用户才能使用 at 命令,如果需要禁用 at 命令的用户较多,则可以把少数用户写入这个文件。/etc/at.deny 文件的管理较为松散,如果允许使用 at 命令的用户较多,则可以把禁用的用户写入这个文件。不过这两个文件都不能对 root 用户生效。

at命令#

at 命令的格式非常简单,只需在 at 命令后面加入时间即可,这样 at 命令就会在指定的时间执行。
at 命令格式如下:

1
[root@localhost ~] # at [选项] 时间

【选项】

  • -m:当 at 工作宪成后,无论命令是否有输出,都用 E-mail 通知执行 at 命令的用户;
  • -c 工作号: 显示该 at 工作的实际内容;

【时间】

  • HH:MM 在指定的“小时:分钟”执行命令,如 02:30;
  • HH:MM YYYY-MM-DD 在指定的“小时:分钟 年-月-日”执行命令,如 02:30 2013-07-25;
  • HH:MM[am|pm] [month] [date] 在指定的“小时:分钟[上午|下午][月][日]”执行命令,如 02:30 July 25;
  • HH:MM[am|pm] + [minutes | hours | days | weeks ]在指定的时间“再加多久"执行命令,如 now + 5 minutes, 05am +2 hours;

at 命令只要指定正确的时间,就可以输入需要在指定时间执行的命令。这个命令可以是系统命令,也可以是 Shell 脚本。举几个例子。

【例 1】

[coot@localhost ~]# cat /root/hello.sh
#!/bin/bash
echo "hello world!!"
#该脚本会打印"hello world!!"
[root@localhost ~]# at now +2 minutes
at> /root/hello.sh >> /root/hello.log
#执行hello.sh脚本,并把输出写入/root/hello.log文件
at> <EOT>
#使用Ctrl+D快捷键保存at任务
job 8 at 2013-07-25 20:54 #这是第8个at任务,会在2013年7月25日20:54执行
[root@localhost ~]# at -c 8
#查询第8个at任务的内容
...省略部分内容...
#主要定义系统的环境变量
/root/hello.sh >> /root/hello.log
#可以看到at执行的任务


【例 2】

[root@localhost ~J# at 02:00 2013-07-26
at> /bin/sync
at> /sbin/shutdown -h now
at> <EOT>
job 9 at 2013-07-26 02:00
#在指定的时间关机。在一个at任务中是可以执行多个系统命令的


在使用系统定时任务时,不论执行的是系统命令还是 Shell 脚本,最好使用绝对路径来写命令,这样不容易报错。at 任务一旦使用 Ctrl+D 快捷键保存,实际上写入了 /var/spool/at/ 这个目录,这个目录内的文件可以直接被 atd 服务调用和执行。

其他at管理命令#

at 还有查询和删除命令,命令如下:

[root@localhost ~]# atq
#查询当前服务器上的at任务


例如:

[root@localhost ~]# atq
9 2013-07-26 02:00 a root
#说明root用户有一个at任务在2013年7月26日02:00执行,工作号是9
[root@localhost ~]# atrm [工作号]
#删除指定的at任务


例如:

[root@localhost ~]# atrm 9
[root@localhost ~]# atq
#删除9号at任务,再查询就没有at任务存在了

Linux crontab命令#

at 命令仅仅可以在指定的时间执行一次任务,但是在实际工作中,系统的定时任务一般是需要重复执行的。这时 at 命令已经不够使用了,我们就需要利用 crontab 命令来执行循环定时任务

crond服务管理与访问控制#

crontab 命令是需要 crond 服务支持的。crond 服务同样是独立的服务,启动和自启动方法如下:

[root@localhost ~]# service crond restart
停止 crond: [确定]
正在启动 crond: [确定]
#重新启动crond服务
[root@localhost ~]# chkconfig crond on
#设定crond服务为开机自启动

crond 服务默认是自启动的。如果服务器上有循环执行的系统定时任务,就不要关闭 crond 服务了。

crontab 命令和 at 命令类似,也是通过 /etc/cron.allow 和 /etc/cron.deny 文件来限制某些用户是否可以使用 crontab 命令的。而且原则也非常相似:

  • 当系统中有 /etc/cron.allow 文件时,只有写入此文件的用户可以使用 crontab 命令,没有写入的用户不能使用 crontab 命令。同样,如果有此文件,/etc/cron.deny 文件会被忽略,因为 /etc/cron.allow 文件的优先级更高。
  • 当系统中只有 /etc/cron.deny 文件时,写入此文件的用户不能使用 crontab 命令,没有写入文件的用户可以使用 crontab 命令。
  • 这个规则基本和 at 命令的规则一致,同样是 /etc/cron.allow 文件比 /etc/cron.deny 文件的优先级高,Linux 系统中默认只有 /etc/cron.deny 文件。

用户的crontab设置#

每个用户都可以实现自己的 crontab 定时任务,只需使用这个用户身份执行“crontab -e”命令即可。当然,这个用户不能写入 /etc/cron.deny 文件。
crontab 命令格式如下:

1
[root@localhost ~]# crontab [选项]

选项:

  • -e:编辑 crontab 定时任务;
  • -l:查询 crontab 定时任务;
  • -r:删除当前用户所有的 crontab 定时任务。如果有多个定时任务,只想删除一个,则可以使用“crontab -e”;
  • -u 用户名:修改或删除其他用户的 crontab 定时任务。只有 root 用户可用;


其实 crontab 定时任务非常简单,只需执行“crontab -e”命令,然后输入想要定时执行的任务即可。不过,当我们执行“crontab-e”命令时,打开的是一个空文件,而且操作方法和 Vim 是一致的。那么,这个文件的格式才是我们真正需要学习的内容。文件格式如下:

[root@localhost !]# crontab -e
#进入 crontab 编辑界面。会打开Vim编辑你的任务
* * * * * 执行的任务

这个文件中是通过5个“*”来确定命令或任务的执行时间的,这 5 个“*”的具体含义如表 1 所示。

表 1 crontab时间表示
项目含义范围
第一个"*" 一小时当中的第几分钟 0~59
第二个"*" 一天当中的第几小时 0~23
第三个"*" 一个月当中的第几天 1~31
第四个"*" 一年当中的第几个月 1~12
第五个"*" 一周当中的星期几 0~7(0和7都代表星期日)


在时间表示中,还有一些特殊符号需要学习,如表 2 所示。

表 2 时间特殊符号
特殊符号含义
* 代表任何时间。比如第一个"*"就代表一小时种每分钟都执行一次的意思
, 代表不连续的时间。比如"0 8,12,16***命令"就代表在每天的8点0分、12点0分、16点0分都执行一次命令
- 代表连续的时间范围。比如"0 5 ** 1-6命令",代表在周一到周六的凌晨5点0分执行命令
*/n 代表每隔多久执行一次。比如"*/10****命令",代表每隔10分钟就执行一次命令


当“crontab -e”编辑完成之后,一旦保存退出,那么这个定时任务实际就会写入 /var/spool/cron/ 目录中,每个用户的定时任务用自己的用户名进行区分。而且 crontab 命令只要保存就会生效,只要 crond 服务是启动的。知道了这 5 个时间字段的含义,我们多举几个时间的例子来熟悉一下时间字段,如表 3 所示。

表 3 crontab举例
时间含义
45 22 ***命令 在22点45分执行命令
0 17 ** 1命令 在每周一的17点0分执行命令
0 5 1,15**命令 在每月1日和15日的凌晨5点0分执行命令
40 4 ** 1-5命令 在每周一到周五的凌晨4点40分执行命令
*/10 4 ***命令 在每天的凌晨4点,每隔10分钟执行一次命令
0 0 1,15 * 1命令 在每月1日和15日,每周一个0点0分都会执行命令,注意:星期几和几日最好不要同时出现,因为它们定义的都是天,非常容易让管理员混淆


现在我们已经对这 5 个时间字段非常熟悉了,可是在“执行的任务”字段中都可以写什么呢?既可以定时执行系统命令,也可以定时执行某个 Shell 脚本。我们举几个实际的例子。

【例 1】让系统每隔 5 分钟就向 /tmp/test 文件中写入一行“11”,验证一下系统定时任务是否会执行。

[root@localhost ~]# crontab -e
#进入编辑界面
*/5 * * * * /bin/echo "11" >> /tmp/test

这个任务在时间工作中没有任何意义,但是可以很简单地验证我们的定时任务是否可以正常执行。如果觉得每隔 5 分钟太长,那就换成“*”,让它每分钟执行一次。而且和 at 命令一样,如果我们定时执行的是系统命令,那么最好使用绝对路径。

【例 2】让系统在每周二的凌晨5点05分重启一次。

[root@localhost ~]# crontab -e
5.5 * * 2 /sbin/shutdown -r now

如果服务器的负载压力比较大,则建议每周重启一次,让系统状态归零。比如绝大多数游戏服务器每周维护一次,维护时最主要的工作就是重启,让系统状态归零。这时可以让我们的服务器自动来定时执行。

【例 3】在每月1日、10日、15 日的凌晨 3 点 30 分都定时执行日志备份脚本 autobak.sh。

[root@localhost ~]# crontab -e
30.3 1,10,15 * * /root/sh/autobak.sh

这些定时任务保存之后,就可以在指定的时间执行了。我们可以使用命令来查看和删除定时任务,命令如下:

[root@localhost ~]# crontab -l
#查看root用户的crontab任务
*/5 * * * * /bin/echo "11" >> /tmp/test
5.5 * * 2 /sbin/shutdown -r now
30.3 1,10,15 * * /root/sh/autobak.sh
[root@localhost ~]# crontab -r
#删除root用户所有的定时任务。如果只想删除某个定时任务,则可以执行“crontab -e”命令进入
#编辑模式手工删除
[root@localhost ~]# crontab -l
no crontab for root
#删除后,再查询就没有root用户的定时任务了

crontab的注意事项#

在书写 crontab 定时任务时,需要注意以下几个事项:

  • 6 个选项都不能为空,必须填写。如果不确定,则使用“*”代表任意时间。
  • crontab 定时任务的最小有效时间是分钟,最大有效时间是月。像 2018 年某时执行、3 点 30 分 30 秒这样的时间都不能被识别。
  • 在定义时间时,日期和星期最好不要在一条定时任务中出现,因为它们都以天为单位,非常容易让管理员混淆。
  • 在定时任务中,不管是直接写命令,还是在脚本中写命令,最好都使用绝对路径。有时使用相对路径的命令会报错。

系统的crontab设置#

“crontab -e”是每个用户都可以执行的命令,也就是说,不同的用户身份可以执行自己的定时任务。但是有些定时任务需要系统执行,这时就需要编辑 /etc/crontab 这个配置文件了。

当然,并不是说写入 /etc/crontab 配置文件中的定时任务在执行时不需要用户身份,而是“crontab -e”命令在定义定时任务时,默认用户身份是当前登录用户。而在修改 /etc/crontab 配置文件时,定时任务的执行者身份是可以手工指定的。这样定时任务的执行会更加灵活,修改起来也更加方便。

那我们打开这个文件看看吧,如下:

[root@localhost ~]# vi /etc/crontab
SHELL=/bin/bash
#标识使用哪种Shell
PATH=/sbin:/bin:/usr/sbin:/usr/bin
#指定PATH环境变量。crontab使用自己的PATH,而不使用系统默认的PATH,所以在定时任务中出现的
#命令最好使用大写
MAILTO=root
#如果有报错输出,或命令结果有输出,则会向root发送信息
HOME=/
#标识主目录
# For details see man 4 crontabs
#提示大家可以去“man 4 crontabs”查看帮助
# Example of job definition:
#.——————————— minute (0 - 59)
# |.———————— hour (0 - 23)
# | |.——————— day of month (1 - 31)
# | | |.———— month (1 - 12) OR jan,feb,mar,apr……
# | | | | .———day of week(0-6)(Sunday=0 or 7)OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
#分 时日月周执行者身份命令
#列出文件格式,并加入了注释

在 CentOS 6.x 中,/etc/crontab 文件也发生了变化。在 CentOS 5.x 中,这个文件的内容大概是这样子的:

#以下输出在CentOS 5.5中
[root@sc ~]# vi /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
# run-parts
01 * * * * root run-parts /etc/cron.hourly
02.4 * * * root run-parts /etc/cron.daily
22.4 * * 0 root run-parts /etc/cron.weekly
42.4 1 * * root run-parts /etc/cron.monthly

在 CentOS 5.x 中,/etc/crontab 文件会使用 run-parts 脚本执行 /etc/cron.{daily,weekly,monthly} 目录中的所有文件。这个 run-parts 其实是一个 Shell 脚本,保存在 /usr/bin/run-parts 中,它的作用就是把其后面跟随的目录中的所有可执行文件依次执行。也就是说,如果我们想让某个脚本在每天都执行一次,那么可以不用手工去写定时任务,而只需要给这个脚本赋予执行权限,并把它放入 /etc/cron.daily/ 目录中,这样这个脚本就会在每天的凌晨 4 点 02 分执行了。

但是在 CentOS 6.x 中,/etc/crontab 文件中不再有相关的段落,那么 run-ptars 这种定时任务的执行方法是否不可用了呢?

我们看到 /etc/crontab 中有一句提示,让我们“man 4 crontabs”来查看帮助,那么我们就看看这个帮助吧!在这个帮助中,写明了“在旧版本的 crontab 配置文件中,通过 run-parts 脚本来调用 cron.{daily,weekly,monthly} 目录,定时执行这些目录中的脚本。在当前系统中,为了避免 cron 和 anacron 之间产生冲突,只要 anacron 已经安装,就使用 anacron 来执行这些目录中的脚本。具体可以查看 anacron(8) 的帮助”。对于 anacron 的用法,后续章节再详解介绍。

其实,对用户来讲,并不需要知道这个定时任务到底是由哪个程序调用的。我们需要知道的事情是如何使用系统的 crontab 设置。对此,新、老版本的 CentOS 没有区别,配置方法都有两种。

第一种方法就是把需要定时执行的工作写成脚本程序,并赋予执行权限,然后直接把这个脚本复制到 /etc/cron.{daily,weekly,monthly} 目录中的任意一个。比如,我需要让某个脚本每周执行,就把这个脚本复制到 /etc/cron.weekly/ 目录中。这样这个脚本就会每周执行一次,具体的执行时间要参考 anacron 的配置文件。

第二种方法就是修改 /etc/crontab 这个配置文件,加入自己的定时任务,不过需要注意指定脚本的执行者身份。例如:

[root@localhost ~]# vi /etc/crontab
…省略部分输出…
* * * * * root run-parts /root/cron/
#让系统每分钟都执行一次/root/cron/目录中的脚本,脚本执行者是root用户
#虽然在CentOS 6.x中不直接通过此配置文件调用/etc/cron.{daily,weekly,monthly}这些目录,但是run-parts脚本还是可以使用的。所以我完全可以参照CentOS 5.x的方法来写定时任务
#使用run-parts脚本调用并执行/root/cron/目录中所有的可执行文件
[root@localhost ~]# mkdir cron
#建立/root/cron/目录
[root@localhost cron]# vi /root/cron/hello.sh
#/bin/bash
echo "hello" >> /root/cron/hello.log
#在/root/cron/hello.log文件中写入“hello”
[root@localhost cron]# chmod 755 hello.sh
#赋予执行权限
#因为hello.sh脚本放入了/root/cron/目录中,所以每分钟执行一次。

只要保存 /etc/crontab 文件,这个定时任务就可以执行了。当然要确定 crond 服务是运行的。

这两种方法都是可以使用的,具体看个人的习惯。不过,要想修改 /etc/crontab 文件,必须是 root 用户,普通用户不能修改,只能使用用户身份的 crontab 命令。

Linux anacron命令#

anacron 是用来做什么的呢?我们的 Linux 服务器如果不是 24 小时开机的,而刚好在关机的时间段之内有系统定时任务(cron)需要执行,那么这些定时任务是不会执行的。

换句话说,假设我们需要在凌晨 5 点 05 分执行系统的日志备份,但是我们的 Linux 服务器不是 24 小时开机的,在晚上需要关机,在白天上班之后才会再次开机,在这个定时任务的执行时间我们的服务器刚好没有开机,那么这个定时任务就不会执行了。anacron 就是用来解决这个问题的。

anacron 会使用 1 天、7 天、一个月作为检测周期,用来判断是否有定时任务在关机之后没有执行。如果有这样的任务,那么 anacron 会在特定的时间重新执行这些定时任务。

那么,anacron 是如何判断这些定时任务已经超过执行时间的呢?在系统的 /var/spool/anacron/ 目录中存在 cron.{daily,weekly,monthly} 文件,这些文件中都保存着 anacron 上次执行的时间。anacron 会读取这些文件中的时间,然后和当前时间进行比较,如果两个时间的差值超过 anacron 的指定时间差值(一般是 1 天、7 天和一个月),就说明有定时任务没有执行,这时 anacron 会介入而执行这个漏掉的定时任务,从而保证在关机时没有执行的定时任务不会被漏掉。

在 CentOS 6.x 中使用 cronie-anacron 取代了 vixie-cron 软件包。而且在原先的 CentOS 版本的 /etc/cron.{daily,weekly,monthly} 目录中的定时任务会同时被 cron 和 anacron 调用,这样非常容易出现重复执行同一个定时任务的错误。在 CentOS 6.x 中,/etc/cron.{daily,weekly,monthly} 目录中的定时任务只会被 anacron 调用,从而保证这些定时任务只会在每天、每周或每月定时执行一次,而不会重复执行。

在 CentOS 6.x 中 anacron 还有一个变化,那就是 anacron 不再是单独的服务,而变成了系统命令。也就是说,我们不再使用“service anacron restart”命令来管理 anacron 服务了,而需要使用 anacron 命令来管理 anacron 工作。

anacron命令格式如下:

[root@localhost ~]# anacron [选项] [参数]


选项:

  • -s:开始执行 anacron 工作,依据 /etc/anacrontab 文件中设定的延迟时间执行;
  • -s:开始执行 anacron 工作,依据 /etc/anacrontab 文件中设定的延迟时间执行;
  • -n:立即执行 /etc/anacrontab 中所有的工作,忽略所有的延迟时间;
  • -u:更新 /var/spool/anacron/cron.{daily,weekly,monthly} 文件中的时间戳,但不执行任何工作;


参数:

  • 工作名:依据 /etc/anacrontab 文件中定义的工作名;


在当前的 Linux 中,其实不需要执行任何 anacron 命令,只需要配置好 /etc/anacrontab 文件,系统就会依赖这个文件中的设定来通过 anacron 执行定时任务了。那么,关键就是 /etc/anacrontab 文件的内容了。这个文件的内容如下:

[root@localhost ~]# vi /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root
#前面的内容和/etc/crontab类似
#the maximal random delay added to the base delay of the jobs RANDOM_DELAY=45
#最大随机廷迟
#the jobs will be started during the following hours only START_H0URS_RANGE=3-22
fanacron的执行时间范围是3:00~22:00
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
©monthly 45 cron.monthly nice run-parts /etc/cron.monthly
#天数 强制延迟(分) 工作名称 实际执行的命令
#当时间差超过天数时,强制延迟多少分钟之后就执行命令

在这个文件中,“RANDOM_DELAY”定义的是最大随机延迟,也就是说,cron.daily 工作如果超过 1 天没有执行,则并不会马上执行,而是先延迟强制延迟时间,再延迟随机延迟时间,之后再执行命令;“START_HOURS_RANGE”的是定义 anacron 执行时间范围,anacron 只会在这个时间范围内执行。

我们用 cron.daily 工作来说明一下 /etc/anacrontab 的执行过程:

  1. 读取 /var/spool/anacron/cron.daily 文件中 anacron 上一次执行的时间。
  2. 和当前时间比较,如果两个时间的差值超过1天,就执行cron.daily工作。
  3. 只能在 03:00-22:00 执行这个工作。
  4. 执行工作时强制延迟时间为 5 分钟,再随机延迟 0~45 分钟。
  5. 使用 nice 命令指定默认优先级,使用 run-parts 脚本执行 /etc/cron.daily 目录中所有的可执行文件。


大家会发现,/etc/cron.{daily,weekly,monthly} 目录中的脚本在当前的 Linux 中是被 anacron 调用的,不再依靠 cron 服务。不过,anacron 不用设置多余的配置,我们只需要把需要定时执行的脚本放入 /etc/cron.{daily,weekly,monthly} 目录中,就会每天、每周或每月执行,而且也不再需要启动 anacron 服务了。如果需要进行修改,则只需修改 /etc/anacrontab 配置文件即可。

比如,我更加习惯让定时任务在凌晨 03:00-05:00 执行,就可以进行如下修改:

[root@localhost ~] # vi /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL-/bin/sh
PATH-/sbin:/bin:/usr/sbin:/usr/bin MAILTO-root
# the maximal random delay added to the base delay of the jobs RANDOM_DELAY=0
#把最大随机廷迟改为0分钟,不再随机廷迟
# the jobs will be started during the following hours only START_HOORS_RANGE=3-5
#执行时间范围为03:00—05:00
#period in days delay in minutes job-identifier command
1 0 cron.daily nice run-parts /etc/cron.daily
7 0 cron.weekly nice run-parts /etc/cron.weekly
@monthly 0 cron.monthly nice run-parts /etc/cron.monthly
#把强制延迟也改为0分钟,不再强制廷迟

这样,所有放入 /etc/cron.{daily,weekly,monthly} 目录中的脚本都会在指定时间执行,而且也不怕服务器万一关机的情况了。

posted @   鲸鱼的海老大  阅读(41)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示
CONTENTS