一、进程的由来
为了描述多个程序在并发执行时所呈现出的特征。在操作系统中,为了不破坏“程序”这个词原有的含义,即“一个在时间上严格有序的指令集合,是静态的保存在存储介质上”,同时又能刻画多个程序共同运行时的新特征,所以引入了进程这一概念。
程序是指令、数据及其组织形式的描述,而进程是程序的实体,是程序在某个数据集上的执行。进程是一个动态的实体,有自己的生命周期,因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤销。
操作系统通过进程来区分不同的程序执行。每个进程都是一个独立的执行实体,拥有自己的系统资源,如内存空间、文件描述符等。操作系统通过进程来管理和调度程序的执行,分配系统资源,并处理进程之间的通信和同步。
PID是英文“Process Identification”的缩写,中文意思是“进程识别号”,也称为进程标识符。在操作系统中,PID是每个进程的唯一标识符,用于标识和管理进程。PID为进程分配资源、管理进程的执行,以及实现进程之间的通信和同步。同时,PID也可以用于监控和调试进程,通过PID可以获取进程的状态信息和控制进程的行为。
二、进程主要概念
1、进程组
进程组是一组相关进程的集合。每个进程组都有一个唯一的进程组ID,且进程组内的所有进程共享相同的进程组ID。这些进程之间可能存在父子关系或兄弟关系,形成一个层次结构。
进程组主要用于作业控制、信号传递和进程状态管理等操作。当一个进程(通常是一个可执行文件或脚本)在shell中被执行时,它通常会创建一个新的进程组。这个新进程成为进程组的首进程,进程组的ID与该进程的PID相同。
当一个进程调用fork()
函数时,会创建一个新的子进程。子进程与父进程属于同一个进程组,并且子进程的PID与父进程的PID不同,但它们的PGID(进程组ID)是相同的。这样,父子进程可以相互协作,因为它们属于同一进程组。在shell中,当使用管道(|
)来连接两个或多个命令时,每个命令都会被视为一个单独的进程组。这些进程组之间通过管道进行通信。第一个程序的输出作为第二个程序的输入。虽然这些程序同属一个管道关联的进程组,但它们不是同一个进程组。也就是说,它们有各自的进程组ID。但为了管道通信的目的,它们被组织在一起。
2、会话
会话是一组相关进程组的集合。会话的概念主要是为了解决多用户使用终端的问题,使得多个用户可以在同一时间使用同一台终端。每个会话都有一个唯一的会话ID,并且会话的第一个进程组被视为该会话的控制进程组。
一个进程组的PGID是由创建该进程组的会话的首进程(通常是登录会话的shell)来决定的。这意味着不同的会话可以有不同的PID和PGID映射关系。前台进程组的首进程会占用会话所关联的终端来运行,shell启动其他应用程序时,其他程序成为首进程。
后台进程中的程序是不会占用终端在shell进程里启动程序时,加上&符号可以指定程序运行在后台进程组里面。
3、终端
终端是一种用户与操作系统交互的设备或界面。根据不同的分类标准,可以将终端分为多种类型,如物理终端、串口终端、LCD终端等。这些终端都需要依赖具体的设备进行输出,例如打印机、显示器等。伪终端和SSH远程连接产生的终端则是虚拟出来的终端,用于实现远程登录和虚拟控制等功能。
三、进程管理
Linux进程状态
状态码 | 进程状态 | 含义 |
---|---|---|
R | 运行(runnable (on run queue)) | 正在运行或在运行队列中等待 |
S | 中断(sleeping) | 休眠中, 受阻, 在等待某个条件的形成或接受到信号 |
D | 不可中断(uninterruptible sleep )(usually IO) | 收到信号不唤醒和不可运行, 进程必须等待直到有中断发生 |
Z | 僵死(a defunct (”zombie”) process) | 进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放 |
T | 停止(traced or stopped) | 进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行 |
1、ps命令:查看静态的进程统计信息
ps [options] [--help]
- a:显示当前终端下的所有进程信息,包括其他用户的进程。与"x"选项结合时将显示系统中所有的进程信息。
- u:使用以用户为主的格式输出进程信息。
- x:显示当前用户在所有终端下的进程信息。
- -e:显示系统内的所有进程信息。
- -l:使用长格式显示进程信息。
- -f:使用完整的格式显示进程信息。
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.5 128372 6988 ? Ss 21:14 0:01 /usr/lib/systemd/systemd --switched-root --system --deseriali
root 2 0.0 0.0 0 0 ? S 21:14 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? S< 21:14 0:00 [kworker/0:0H]
...
root 27082 0.0 0.1 51752 1716 pts/2 R+ 21:41 0:00 ps aux
会发现 ps -l
与 ps aux
显示的项目也不一样
- USER:该 process 属于哪个使用者账户
- PID:进程标识符
%CPU
:该进程使用掉的 CPU 资源百分比%MEM
:占用的虚拟内存(KBytes)- RSS:占用的固定内存(KBytes)
- TTY:在哪个终端机上面运行?
?
:与终端机无关tty1-tty6
:本机上登录的pts/0
等:是由网络连接进入的进程
- STAT:目前的状态,与
ps -l
中的状态相同含义 - START:该进程被触发启动时间(如果太久不会显示具体时间)
- TIME:该进程实际使用 CPU 运行的时间
- COMMAND:进程执行的指令
一般来说,ps aux 会按照 PID 的顺序来排序显示。
# 范例 3:以范例 1 的显示内容,显示出所有的进程
# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 25710 1956 0 80 0 - 57972 do_wai pts/2 00:00:00 su
4 S 0 25917 25710 0 80 0 - 29090 do_wai pts/2 00:00:00 bash
0 R 0 32189 25917 0 80 0 - 12407 - pts/2 00:00:00 ps
# ps -lA
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 1 0 0 80 0 - 32093 ep_pol ? 00:00:01 systemd
1 S 0 2 0 0 80 0 - 0 kthrea ? 00:00:00 kthreadd
1 S 0 4 2 0 60 -20 - 0 worker ? 00:00:00 kworker/0:0H
1 S 0 6 2 0 80 0 - 0 smpboo ? 00:00:00 ksoftirqd/0
1 S 0 7 2 0 -40 - - 0 smpboo ? 00:00:00 migration/0
1 S 0 8 2 0 80 0 - 0 rcu_gp ? 00:00:00 rcu_bh
1 R 0 9 2 0 80 0 - 0 - ? 00:00:01 rcu_sched
# 会发现,与 ps -l 显示类似,不过显示的是系统的所有进程
# 范例 4:列出类似进程树的进程显示
# ps axjf
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
2 4 0 0 ? -1 S< 0 0:00 \_ [kworker/0:0H]
2 6 0 0 ? -1 S 0 0:00 \_ [ksoftirqd/0]
1 1269 1269 1269 ? -1 Ss 0 0:00 /usr/sbin/sshd -D
1269 1922 1922 1922 ? -1 Ss 0 0:01 \_ sshd: mrcode [priv]
1922 1932 1922 1922 ? -1 S 1000 0:09 | \_ sshd: mrcode@pts/0,pts/1
1932 1934 1934 1934 pts/0 1934 Ss+ 1000 0:00 | \_ -bash
1932 1939 1939 1939 ? -1 Ss 1000 0:00 | \_ /usr/libexec/openssh/sftp-server
1932 1941 1941 1941 pts/1 2573 Ss 1000 0:00 | \_ -bash
1941 2573 2573 1941 pts/1 2573 S+ 1000 0:04 | | \_ top
1932 7742 7742 7742 ? -1 Ss 1000 0:00 | \_ bash -c export LANG="en_US.UTF-8";export LANGUAGE="en_US.
7742 7789 7742 7742 ? -1 S 1000 0:00 | \_ sleep 1
1269 1926 1926 1926 ? -1 Ss 0 0:01 \_ sshd: mrcode [priv]
1926 1950 1926 1926 ? -1 S 1000 0:09 \_ sshd: mrcode@pts/2,pts/3
1950 1956 1956 1956 pts/2 7790 Ss 1000 0:00 \_ -bash
1956 25710 25710 1956 pts/2 7790 S 0 0:00 | \_ su -
25710 25917 25917 1956 pts/2 7790 S 0 0:00 | \_ -bash
25917 7790 7790 1956 pts/2 7790 R+ 0 0:00 | \_ ps axjf
1950 2009 2009 2009 ? -1 Ss 1000 0:00 \_ /usr/libexec/openssh/sftp-server
1950 2012 2012 2012 pts/3 2574 Ss 1000 0:00 \_ -bash
看上面 PPID 为 1269 的那一行开始,我这里使用了 ssh 远程链接,用的是 mrcode 账户,登录成功后,获得了一个 bash 环境,后面我使用了 su -
指令切换到了 root 的 bash 环境,然后执行了刚刚的 ps axjf 指令。这样就比较清楚了。
还可以通过 pstree 指令来显示进程树,不过貌似没有这么详细
# 范例 5:找出与 cron 和 rsyslog 这两个服务有关的 PID 号码
# ps aux | egrep '(cron|rsyslog)'
root 1273 0.0 0.3 215672 3652 ? Ssl 21:15 0:00 /usr/sbin/rsyslogd -n
root 1285 0.0 0.1 126288 1696 ? Ss 21:15 0:00 /usr/sbin/crond -n
root 4838 0.0 0.0 9096 932 pts/2 R+ 21:58 0:00 grep -E --color=auto (cron|rsyslog)
STAT状态位常见的状态字符
状态字符 | 含义 |
---|---|
D | 无法中断的休眠状态(通常 IO 的进程) |
R | 正在运行可中在队列中可过行的 |
S | 处于休眠状态 |
T | 停止或被追踪 |
X | 死掉的进程 (基本很少见) |
Z | 僵尸进程 |
< | 优先级高的进程 |
N | 优先级较低的进程 |
L | 有些页被锁进内存 |
s | 进程的领导者(在它之下有子进程) |
l | 多线程,克隆线程(使用 CLONE_THREAD, 类似 NPTL pthreads) |
+ | 位于后台的进程组 |
1、查看CPU占用最多的前10个进程
ps auxw|head -1;ps auxw|sort -rn -k3|head -10
2、显示所有当前进程
ps -ax
-a参数,-a 代表 all。
-x参数会显示没有控制终端的进程
3、查看虚拟内存使用最多的前10个进程
ps auxw | head -1 ; ps auxw|sort -rn -k5|head -10
4、根据用户名过滤进程
# ps -u root
PID TTY TIME CMD
1 ? 00:00:00 systemd
2 ? 00:00:00 kthreadd
3 ? 00:00:00 kworker/0:0
4 ? 00:00:00 kworker/0:0H
5 ? 00:00:00 kworker/u256:0
6 ? 00:00:00 ksoftirqd/0
7 ? 00:00:00 migration/0
8 ? 00:00:00 rcu_bh
9 ? 00:00:00 rcu_sched
5、根据内存使用来升序排序
# ps -aux --sort -pmem
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1062 0.1 0.4 564132 17464 ? Ssl 10:06 0:00 /usr/bin/python2 -Es /usr/sbin/tuned -l -P
root 600 0.1 0.3 87176 12300 ? Ss 10:06 0:00 /usr/lib/systemd/systemd-journald
polkitd 785 0.0 0.2 604048 11056 ? Ssl 10:06 0:00 /usr/lib/polkit-1/polkitd --no-debug
root 801 0.0 0.2 461332 8948 ? Ssl 10:06 0:00 /usr/sbin/NetworkManager --no-daemon
6、根据 CPU 使用来升序排序
# ps -aux --sort -pcpu
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.7 0.1 153876 5644 ? Ss 10:06 0:00 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root 635 0.6 0.0 49668 3536 ? Ss 10:06 0:00 /usr/lib/systemd/systemd-udevd
root 1062 0.1 0.4 564132 17464 ? Ssl 10:06 0:00 /usr/bin/python2 -Es /usr/sbin/tuned -l -P
7、格式化输出 指定用户(真实或有效的UID)创建的进程
ps -U pid_user -u pid_user u
-U 参数按真实用户 ID(RUID) 筛选进程,它会从用户列表中选择真实用户名或 ID。真实用户即实际创建该进程的用户
-u 参数用来筛选有效用户 ID(EUID)
u 参数用来决定以针对用户的格式输出,由 User, PID, %CPU, %MEM, VSZ, RSS, TTY, STAT, START, TIME 和 COMMAND这几列组成
# pid_user=ceph && ps -U $pid_user -u $pid_user u
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
ceph 1474 0.2 0.0 398604 40772 ? Ssl 09:27 2:10 /usr/bin/ceph-mon -f --cluster ceph --id ...
ceph 1478 0.0 0.0 393520 34540 ? Ssl 09:27 0:21 /usr/bin/ceph-mds -f --cluster ceph --id ...
ceph 1845 0.1 0.3 1104056 219284 ? Ssl 09:27 0:59 /usr/bin/ceph-osd -f --cluster ceph --id ...
8、仅查看自己的 bash 相关进程
# 范例 1: 将目前属于您自己这次登录的 PID 与相关信息列出来(只与自己的 bash 有关)
# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 29260 28796 0 80 0 - 57972 do_wai pts/0 00:00:00 su
4 S 0 29473 29260 0 80 0 - 29090 do_wai pts/0 00:00:00 bash
0 R 0 30444 29473 0 80 0 - 12407 - pts/0 00:00:00 ps
# 前面三项,最初是用了普通账户登录的,使用了 su - 切换到了一个 bash
这里列出的只是与你操作环境 bash 有关的进程,没有延伸到 systemd:
- F:进程旗标(process flags),说明这个进程的总结权限,常见的号码有:
- 4:表示此进程的权限为 root
- 1:则表示此子进程仅进行 复制(fork)而没有实际执行(exec)
- S:进程状态(STAT),主要状态有:
- R(Running):正在运行中
- S(Sleep):该程序目前正在睡眠状态(idle),但可以被唤醒(signal)
- D:不可被唤醒的睡眠状态,通常该程序可能在等待 I/O 的情况
- T:停止状态(stop),可能是在工作控制(背景暂停)或除错(traced)状态
- Z(Zombie):僵尸状态,进程已终止但却无法被移除至内存外
- UUID/PID/PPID:代表此进程被该 UID 所拥有、进程的 PID 、此进程的父进程 PID
- C:代表 CPU 使用率,单位为百分比
- PRI/NI:Priority/Nice 的缩写,代表此进程被 CPU 所执行的优先级,数值越小表示该进程越快被 CPU 执行。详细的 PRI 与 NI 将在下一小节讲解
- ADDR/SZ/WCHAN:都与内存有关
- ADDR:kernel function,该进程在内存的哪个部分,如果是 running 的进程,一般会显示
-
- SZ:该进程用掉多少内存
- WCHAN 该进程是否运行中,若为
-
表示正在运行中
- ADDR:kernel function,该进程在内存的哪个部分,如果是 running 的进程,一般会显示
- TTY:登陆者的终端机位置,若为远程登录则使用动态终端接口(pts/n)
- TIME:使用掉的 CPU 时间。注意:是此进程实际花费 CPU 运行的时间
- CMD:command 的缩写,此进程的触发程序指令
如上列出的信息表示, bash 的程序属于 UID 为 0 的使用者,状态是睡眠(sleep),他睡眠是因为他触发了 ps(状态为 R,run)的原因,ps 的 PID=30444,优先执行顺序为 80,下达 bash 所取得的终端机接口为 pts/0,运行状态为 do_wai
2、top命令:动态观察进程的变化
ps 可以显示一个时间点的进程状态,而 top 则可以持续的侦测进程运行状态
top [-d 数字] | top [-bnp]
选项与参数:
-d:后面可以接秒数,整个进程画面更新的秒数,预设是 5 秒更新一次
-b:以批次的方式执行 top,还有更多的参数可以使用(莫名其妙啊,啥参数?),通常会搭配数据流重导向来将批次的结果输出为文件
-n:与 -b 搭配,需要进行几次 top 的输出
-p:指定某些 PID 来进行观察
在 top 执行过程中可以使用的按键指令:
?:显示在 top 中可以输入的按键指令
P:以 CPU 的使用资源排序显示
M:以 Memory 的使用资源排序显示
N:以 PID 排序
T:由该进程使用 CPU 时间累积(TIME+)排序
k:给予某个 PID 一个信号(signal)
r:给予某个 PID 重新制定一个 nice 值
q:离开 top 软件的按键
E:切换单位显示,比如从 KB 切换为 G 显示
c:切换 COMMAND 的信息,name/完成指令
top 的功能太多,可用的按键也很多,可以参考 man top 的内部文件说明,上面只是列出常用的选项
# 范例 1:每两秒更新一次 top,观察整体信息
# top -d 2
top - 22:20:11 up 1:05, 4 users, load average: 0.52, 0.53, 0.52
Tasks: 186 total, 2 running, 184 sleeping, 0 stopped, 0 zombie
%Cpu(s): 7.7 us, 9.7 sy, 0.0 ni, 82.1 id, 0.0 wa, 0.0 hi, 0.5 si, 0.0 st
KiB Mem : 1190952 total, 428928 free, 402624 used, 359400 buff/cache
KiB Swap: 1048572 total, 1048572 free, 0 used. 632160 avail Mem
# <<< 如果按下 k 或 r 时,有相关的提示在这里出现
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1699 gdm 20 0 2947388 136736 61224 S 0.5 11.5 0:04.00 gnome-shell
1932 mrcode 20 0 161324 3016 1296 S 0.5 0.3 0:17.28 sshd
1950 mrcode 20 0 161324 3028 1296 S 0.5 0.3 0:17.41 sshd
2573 mrcode 20 0 162820 3068 1576 S 0.5 0.3 0:07.43 top
1 root 20 0 128372 6988 4196 S 0.0 0.6 0:01.67 systemd
top 的信息基本上分为两个区域,上面 6 行,和下面的列表
-
第一行信息:top -
-
目前开机时间:22:20:11 这个
-
开机到目前为止所经过的时间:up 1:05 这个
-
已经登录系统的用户人数:4 users
-
系统在 1、5、15 分钟的平均工作负载
在第 15 章谈到过 batch 工作方式负载小于 0.8 就是这里显示的值了。
表示的是,系统平均要负责运行几个进程,这里是三个值,也就是对应平均 1/5/15 分钟
越小达标系统越空闲,若高于 1 ,那么你的系统进程执行太频繁了
-
-
第二行:tasks
显示的是目前进程的总量与各个状态(running、sleeping、stopped、zombie)的进程数量
如果发现有 zombie 进程的话,就需要找下是哪个进程变成了僵尸进程了
-
第三行:
$Cpus
CPU 整体负载,每个项目可使用 ? 查询。
需要特别注意的是 wa 项,表示 I/O wait,通常系统变慢,都是 I/O 产生的问题比较大,需要特别注意该项占用的 CPU 资源,如果是多核 CPU,可以按下数字键「1」来切换成不同 CPU
-
第四行和第五行
目前的物理内存与虚拟内存(Mem/Swap)的使用情况。要注意的是 swap 的使用量要尽量的少,如果 swap 被大量使用,表示系统的物理内存不足
-
第六行:当在 top 程序中输入指令时,显示状态的地方
下面的列表部分大部分都见过了,下面再列出含义:
- PID:进程 ID
- USER:进程所属使用者
- PR(priority):进程优先执行顺序,越小越早被执行
- NI(nice):与 PR 有关,越小越早被执行
%CPU
:CPU 使用率%MEM
:内存使用率TIME+
:CPU 使用时间的累加
top 预设使用 CPU 使用率 %CPU
作为排序的重点,如果想要使用内存使用率排序,可以按下 M 键,要离开按下 q 键
# 范例 2:将 top 的信息进行 2 次,然后将结果输出到 /tmp/top.txt
# top -b -n 2 > /tmp/top.txt
# 这里的结果就是,写入了执行 2 次的结果信息。是追加写入的
由于只有一屏显示,所以当你要观察的进程没有排序到最前面的时候,还可以单独观察该线程
# 范例 3:我们自己的 bash PID 可以由 $$ 变量取得,使用 top 持续观察该 PID
# top -d 2 -p $$
top - 22:53:55 up 1:39, 2 users, load average: 0.59, 0.28, 0.32
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.1 us, 4.6 sy, 0.0 ni, 92.8 id, 0.0 wa, 0.0 hi, 0.5 si, 0.0 st
KiB Mem : 1190952 total, 435612 free, 392456 used, 362884 buff/cache
KiB Swap: 1048572 total, 1048572 free, 0 used. 642448 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9051 root 20 0 116472 3172 1780 S 0.0 0.3 0:00.04 bash
如果想要找出最耗 CPU 资源的进程时,大多使用 top 指令,然后以 CPU 使用资源来排序(-p)
3、pgrep命令:查询进程信息
1、查询进程
查询进程名中包含"log"的进程及其PID号
# pgrep -l 'log'
507 xfs-log/dm-0
717 xfs-log/sda1
795 systemd-logind
1075 rsyslogd
查询由用户teacher在tty2终端上运行的进程及PD号
# pgrep -l -U teacher -t tty2
47239 bash
2、改变进程的运行方式
挂起当前的进程:当Linux操作系统中的命令正在前台执行时(运行尚未结束),按Ctrl+Z组合键可以将当前进程挂起(调入后台并停止执行)
查看后台的进程:使用jobs -l命令,同时显示该进程对应的PID号
将后台的进程恢复运行:使用bg(BackGround,后台)命令,可以将后台中暂停执行(如按Ctrl+Z组合键挂起)的任务恢复运行,继续在后台执行操作;而使用fg命令(ForeGround,前台),可以将后台任务重新恢复到 前台运行。
3、终止进程执行
使用kill命令终止进程
# pgrep -l sshd
1060 sshd
1562 sshd
1564 sshd
# kill 1060
# pgrep -l sshd
1562 sshd
1564 sshd
强制终止vim进程
# kill -9 7095
将所有名为vim的进程都强行终止
# killall -9 vim
终止由用户teacher启动的进程(包括登录Shell)
# pgrep -l -U teacher
47239 bash
# pkill -9 -U teacher
# pgrep -l -U teacher
4、pstree命令:查看进程树
pstree [-AIU] [-up]
选项与参数:
-A:各进程之间的连接以 ASCII 字符来连接
-U:各进程之间的连接以万国码的字符来连接。在某些终端机接口下可能会有错误
-p:并同时列出每个 process 的 PID
-u:并同时列出每个 process 的所属账户名称
示例
# 范例 1:列出目前系统上所有的进程树的相关性
# pstree -A
systemd-+-ModemManager---2*[{ModemManager}] # ModenManager 与其子进程
|-NetworkManager---2*[{NetworkManager}]
|-2*[abrt-watch-log]
|-abrtd
|-accounts-daemon---2*[{accounts-daemon}]
....
|-sshd---sshd---sshd-+-bash---su---bash---pstree # 我们指令执行的相依性
| |-bash---top
| |-bash
| `-sftp-server
# 看下这个相依性,差不多就是登陆之后,在 su 切换账户之后,执行的
# 范例 2:同时显示出 PID 与 users
# pstree -Aup
systemd(1)-+-ModemManager(871)-+-{ModemManager}(881)
| `-{ModemManager}(891)
|-NetworkManager(935)-+-{NetworkManager}(941)
| `-{NetworkManager}(945)
|-abrt-watch-log(856)
|-sshd(1269)---sshd(7771)---sshd(7779,mrcode)-+-bash(3239)---sleep(3263)
| |-bash(7780)---su(8985,root)---bash(9051)---pstree(3264)
| |-bash(7835)---top(8102)
| `-sftp-server(7833)
# 可以看到 sshd 登录的 PID 是 7779 ,用 mrcode 账户登录的。后续用 su 切换到了 root,这个时候新开了一个进程 7780 的 bash
用 pstree 来找相关性,同时使用 -A
来让连线不断开。默认的 Unicode 连线有可能出现断线,整体画面显示错位的问题
由 pstree 的输出我们可以知道,所有的进程都是依附在 systemd 程序下面的,systemd 的进程 ID 是 1 号,是 LInux 核心主动运行的第一个程序
5、taskset 命令
1、操作场景
taskset命令,可用于进程的CPU调优,可以把云服务器上运行的某个进程,指定在某个CPU上工作。
本节操作指导用户使用taskset命令让进程运行在指定CPU上。
2、操作步骤
1、执行如下命令,查看服务器CPU核数。
# cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 85
model name : Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz
stepping : 7
microcode : 0x1
cpu MHz : 2500.002
cache size : 36608 KB
physical id : 0
siblings : 8
core id : 0
cpu cores : 4
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 22
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc eagerfpu pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 arat avx512_vnni
bogomips : 5000.00
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:
关于CPU的核心参数说明:
- processor:指明第几个CPU处理器
- cpu cores:指明每个处理器的核心数
2、执行以下命令,获取进程状态(以下操作以进程test.sh为例,对应的pid为23989)
# ps aux | grep test.sh
执行以下命令,查看进程当前运行在哪个CPU上。
#taskset -p *进程号*
# taskset -p 23989
3、显示的是十进制数字1,转换为2进制为1。
4、执行以下命令,指定进程运行在第二个CPU(CPU1)上。
#taskset -pc 1 *进程号*
# taskset -pc 1 23989
说明:
CPU的标号是从0开始的,所以CPU1表示第二个CPU(第一个CPU的标号是0),这样就把应用程序test.sh绑定到了CPU1上运行
也可以使用如下命令在启动程序时绑定CPU(启动时绑定到第二个CPU)上。
# taskset -c 1 ./test.sh&
参考:https://support.huaweicloud.com/trouble-ecs/ecs_trouble_0355.html
6、mpstat 命令
mpstat (multiprocessor state) 可以查看所有cpu的平均负载,也可以查看指定cpu的负载。所以mpstat其实就是主要查看CPU负载的一个工具。是一款常用的多核CPU性能分析工具,用来实时查询每个CPU的性能指标,以及所有CPU的平均指标。
1、下载软件包
它是Linux性能工具集sysstat中的一个工具,所以我们要装上sysstat,安装方法随不同的系统略有不同,sysstat是一个软件包,包含监测系统性能及效率的一组工具,这些工具对于我们收集系统性能数据,比如:CPU 使用率、硬盘和网络吞吐数据,这些数据的收集和分析,有利于我们判断系统是否正常运行
yum install sysstat -y
2、命令参数
参数 | 描述 |
---|---|
-P {cpu ALL} | 指定要监控哪个CPU ,范围是 [0~n-1] ,ALL表示监控所有CPU都监控 |
internval | 相邻两次采样的间隔时间 |
count | 采样的次数,count只能和internval一起使用 |
# mpstat -P ALL 3 2 # -P ALL 统计所有CPU信息;3:3秒执行一次;2:执行两次
Linux 3.10.0-1160.53.1.el7.x86_64 (iZ8vb6wledf94lam5hg1o7Z) 02/28/2023 _x86_64_ (8 CPU)
# 第一次执行
05:37:18 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
05:37:21 PM all 2.11 0.00 1.22 0.00 0.00 0.04 0.00 0.00 0.00 96.62
05:37:21 PM 0 2.72 0.00 2.04 0.00 0.00 0.00 0.00 0.00 0.00 95.24
05:37:21 PM 1 1.00 0.00 0.67 0.00 0.00 0.00 0.00 0.00 0.00 98.33
05:37:21 PM 2 3.07 0.00 1.71 0.00 0.00 0.00 0.00 0.00 0.00 95.22
05:37:21 PM 3 0.67 0.00 0.67 0.00 0.00 0.00 0.00 0.00 0.00 98.65
05:37:21 PM 4 2.74 0.00 2.05 0.00 0.00 0.00 0.00 0.00 0.00 95.21
05:37:21 PM 5 1.00 0.00 0.33 0.00 0.00 0.00 0.00 0.00 0.00 98.67
05:37:21 PM 6 3.75 0.00 1.71 0.00 0.00 0.00 0.00 0.00 0.00 94.54
05:37:21 PM 7 2.33 0.00 0.67 0.00 0.00 0.00 0.00 0.00 0.00 97.00
# 第二次执行
05:37:21 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
05:37:24 PM all 4.27 0.00 1.48 0.00 0.00 0.00 0.00 0.00 0.00 94.26
05:37:24 PM 0 5.07 0.00 2.03 0.00 0.00 0.34 0.00 0.00 0.00 92.57
05:37:24 PM 1 2.36 0.00 1.01 0.00 0.00 0.00 0.00 0.00 0.00 96.62
05:37:24 PM 2 4.44 0.00 1.71 0.00 0.00 0.00 0.00 0.00 0.00 93.86
05:37:24 PM 3 3.36 0.00 1.34 0.00 0.00 0.00 0.00 0.00 0.00 95.30
05:37:24 PM 4 8.19 0.00 2.05 0.00 0.00 0.00 0.00 0.00 0.00 89.76
05:37:24 PM 5 2.02 0.34 0.67 0.00 0.00 0.00 0.00 0.00 0.00 96.97
05:37:24 PM 6 5.07 0.00 1.69 0.00 0.00 0.00 0.00 0.00 0.00 93.24
05:37:24 PM 7 3.36 0.00 0.67 0.00 0.00 0.34 0.00 0.00 0.00 95.64
# 平均
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
Average: all 3.19 0.00 1.35 0.00 0.00 0.02 0.00 0.00 0.00 95.44
Average: 0 3.90 0.00 2.03 0.00 0.00 0.17 0.00 0.00 0.00 93.90
Average: 1 1.68 0.00 0.84 0.00 0.00 0.00 0.00 0.00 0.00 97.48
Average: 2 3.75 0.00 1.71 0.00 0.00 0.00 0.00 0.00 0.00 94.54
Average: 3 2.02 0.00 1.01 0.00 0.00 0.00 0.00 0.00 0.00 96.97
Average: 4 5.47 0.00 2.05 0.00 0.00 0.00 0.00 0.00 0.00 92.48
Average: 5 1.51 0.17 0.50 0.00 0.00 0.00 0.00 0.00 0.00 97.82
Average: 6 4.41 0.00 1.70 0.00 0.00 0.00 0.00 0.00 0.00 93.89
Average: 7 2.84 0.00 0.67 0.00 0.00 0.17 0.00 0.00 0.00 96.32
3、参数含义
参数 | 描述 |
---|---|
CPU | 显示的是某个CPU 还是全部CPU all=全部 |
%usr | 表示用户所有使用的CPU百分比 |
%nice | 表示使用 nice 值的 CPU 的百分比 |
%sys | 表示内核进程使用的 CPU 百分比 |
%iowait | 表示等待进行 I/O 所使用的 CPU 时间百分比 |
%irq | 表示用于处理系统中断的CPU百分比 |
%soft | 表示用于软件中断的CPU百分比 |
%steal | 虚拟机强制CPU等待的时间百分比 |
%guest | 虚拟机占用CPU时间的百分比 |
%gnice | CPU运行niced guest虚拟机所花费的时间百分比 |
%idle | CPU的空闲时间的百分比 |
注: mpstat主要用在当系统变慢,平均负载增大时,我们想判断到底是CPU的使用率增大了,还是IO压力增大的情况。
4、平均负载信息
当系统变慢,用top或uptime来了解系统的负载情况,系统平均负载是指在特定时间间隔内运行队列中的平均进程数,如果单个CPU内核的当前活动进程数不大于3的话,那么系统的性能是良好的。
# uptime
17:48:22 up 325 days, 6:58, 1 user, load average: 0.21, 0.31, 0.36
- 1users:当前有1个用户登录;
- load average:平均负载
0.21:21分钟
0.31:31分钟
0.36:36分钟
7、lsof 命令
lsof是一个用于查看当前系统打开文件的工具,它可以列出进程打开的文件、打开文件的进程、进程打开的端口等信息。
1、作用
lsof命令的作用包括:
- 列出进程打开的文件:通过lsof命令可以查看某个进程打开了哪些文件,以及文件的状态、类型、大小等信息。
- 查找特定文件的使用者:lsof命令可以用来查找哪些进程正在使用某个文件,这对于确定文件被占用的情况非常有用。
- 查看网络连接:lsof命令也可以用来查看进程打开的网络连接,包括TCP和UDP协议的连接。
- 查找特定目录或文件的使用情况:lsof命令可以用来查找哪些进程正在访问某个目录或文件,这对于监控目录或文件的访问情况非常有用。
2、参数
参数 | 说明 |
---|---|
-a | 列出打开文件存在的进程 |
-c <进程名> | 列出指定进程所打开的文件 |
-g | 列出GID号进程详情 |
-d <文件号> | 列出占用该文件号的进程 |
+d <目录> | 列出目录下被打开的文件 |
+D <目录> | 递归列出目录下被打开的文件 |
-n <目录> | 列出使用NFS的文件 |
-i <条件> | 列出符合条件的进程 |
-p <进程号> | 列出指定进程号所打开的文件 |
-u | 列出UID号进程详情 |
-h | 显示帮助信息 |
-v | 显示版本信息 |
3、常用方法
显示当前使用互联网连接的进程
lsof -P -i -n
显示使用特定端口号的进程
lsof -i tcp:443
列出所有侦听端口以及关联进程的 PID
lsof -Pan -i tcp -i udp
列出所有打开的端口及其所属的可执行文件
lsof -i -P | grep -i "listen"
显示所有开放端口
lsof -Pnl -i
显示开放端口 (LISTEN)
lsof -Pni4 | grep LISTEN | column -t
列出由特定命令打开的所有文件
lsof -c "process"
查看每个目录的用户活动
lsof -u username -a +D /etc
显示根目录下 10 个最大的打开文件,并按大小排序
lsof -w / | \
awk '{ if($7 > 1048576) print $7/1048576 "MB" " " $9 " " $1 }' | \
sort -n -u | tail | column -t
显示进程的当前工作目录
lsof -p <PID> | grep cwd
四、孤儿进程orphaned与僵尸进程Zombie
1、概念
我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
2、僵尸进程
在linux系统中,当用ps命令观察进程的执行状态时,经常看到某些进程的状态栏为defunct,这就是所谓的“僵尸”进程。“僵尸”进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。由于进程表的容量是有限的,所以,defunct进程不仅占用系统的内存资源,影响系统的性能,而且如果其数目太多,还会导致系统瘫痪。
1、僵尸进程的产生原因
我们知道,每个进程在进程表里都有一个进入点(entry),核心程序执行该进程时使用到的一切信息都存储在进入点。当用ps命令察看系统中的进程信息时,看到的就是进程表中的相关数据。
所以,当一个父进程以fork()系统调用建立一个新的子进程后,核心进程就会在进程表中给这个子进程分配一个进入点,然后将相关信息存储在该进入点所对应的进程表内。这些信息中有一项是其父进程的识别码。
而当这个子进程结束的时候(比如调用exit命令结束),其实他并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit的作用是使进程退出,但是也仅仅限于一个正常的进程变成了一个僵尸进程,并不能完全将其销毁)。此时原来进程表中的数据会被该进程的退出码(exit code)、执行时所用的CPU时间等数据所取代,这些数据会一直保留到系统将它传递给它的父进程为止。由此可见,defunct进程的出现时间是在子进程终止后,但是父进程尚未读取这些数据之前。
此时,该僵尸子进程已经放弃了几乎所有的内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态信息供其他进程收集,除此之外,僵尸进程不再占有任何存储空间。他需要他的父进程来为他收尸,如果他的父进程没有安装SIGCHLD信号处理函数调用wait 或 waitpid() 等待子进程结束,也没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时候父进程结束了,那么init进程会自动接手这个子进程,为他收尸,他还是能被清除掉的。但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是系统中为什么有时候会有很多的僵尸进程。
2、如何杀死僵尸进程
如上可知,僵尸进程一旦出现之后,很难自己消亡,会一直存在下去,直至系统重启。虽然僵尸进程几乎不占系统资源,但是,这样下去,数量太多了之后,终究会给系统带来其他的影响。因此,如果一旦见到僵尸进程,我们就要将其杀掉。如何杀掉僵尸进程呢?
有同学可能会说,很简单嘛,直接使用kill命令就好啊。或者,实在不行,加一个-9的后缀(kill -9),肯定杀掉!
请注意:defunct状态下的僵尸进程是不能直接使用kill -9命令杀掉的,否则就不叫僵尸进程了。那么,该如何杀呢?
在进程中它的标识是在 CMD 后面有 <defunct>
标识,例如下面这样
apache 8683 0.0 0.9 83383 9992 ?Z 14:33 0:00 /usr/sbin/httpd<defunct>
发现有僵尸进程时,应该找出来,分析原因,否则有可能一直产生僵尸进程
事实上,通常僵尸进程都已经无法管控,而直接交给 systemd 程序来负责了,偏偏 systemd 是系统第一个执行的程序,它是所有程序的父程序,无法杀掉该程序(杀掉它,系统就死了),所以,经过一段时间后,系统无法通过核心非经常性的特殊处理来将该进程删除时,那只有重启机器了
方法有二:
重启服务器电脑,这个是最简单,最易用的方法,但是如果你服务器电脑上运行有其他的程序,那么这个方法,代价很大。所以,尽量使用下面一种方法。
找到该defunct僵尸进程的父进程,将该进程的父进程杀掉,则此defunct进程将自动消失。
问题又来了,如何找到defunct僵尸进程的父进程呢?
很简单,一句命令就够了:
# ps -ef | grep defunct_process_pid
3、如何预防僵尸进程
以上介绍的只是在发现了僵尸进程之后,如何去杀死它。那么,有同学可能会说了,这个是治标不治本的。真正的办法是,不让它产生,问题才能彻底解决。OK,那我们就来介绍一下,如何预防僵尸进程的产生。
在父进程创建子进程之前,就向系统申明自己并不会对这个子进程的exit动作进行任何关注行为,这样的话,子进程一旦退出后,系统就不会去等待父进程的操作,而是直接将该子进程的资源回收掉,也就不会出现僵尸进程了。具体的办法就是,在父进程的初始化函数中,调用这个函数:signal(SIGCHLD,SIG_IGN);
如果上述语句没来得及调用,也有另外一个办法。那就是在创建完子进程后,用waitpid等待子进程返回,也能达到上述效果;
如果上述两个办法都不愿意采用,那还有一招:在父进程创建子进程的时候,连续调用两次fork(),而且使紧跟的子进程直接退出,使其孙子进程成为孤儿进程,从而init进程将代替父进程来接手,负责清除这个孤儿进程。于是,父进程就无需进行任何的清理行为,系统会自动处理;
3、问题及危害
unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 但这样就导致了问题,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
**任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。**这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
3、僵尸进程危害场景
例如有个进程,它定期的产生一个子进程,这个子进程需要做的事情很少,做完它该做的事情之后就退出了,因此这个子进程的生命周期很短,但是,父进程只管生成新的子进程,至于子进程 退出之后的事情,则一概不闻不问,这样,系统运行上一段时间之后,系统中就会存在很多的僵死进程,倘若用ps命令查看的话,就会看到很多状态为Z的进程。 严格地来说,僵死进程并不是问题的根源,罪魁祸首是产生出大量僵死进程的那个父进程。因此,当我们寻求如何消灭系统中大量的僵死进程时,答案就是把产生大 量僵死进程的那个元凶枪毙掉(也就是通过kill发送SIGTERM或者SIGKILL信号啦)。枪毙了元凶进程之后,它产生的僵死进程就变成了孤儿进 程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源,这样,这些已经僵死的孤儿进程 就能瞑目而去了。
五、守护进程
1、前台任务与后台任务
下面这样启动的脚本,称为"前台任务"(foreground job)。它会独占命令行窗口,只有运行完了或者手动中止,才能执行其他命令。
变成守护进程的第一步,就是把它改成"后台任务"(background job)。
# node server.js &
只要在命令的尾部加上符号&,启动的进程就会成为"后台任务"。如果要让正在运行的"前台任务"变为"后台任务",可以先按ctrl + z,然后执行bg命令(让最近一个暂停的"后台任务"继续执行)。
"后台任务"有两个特点。
(1)继承当前 session (对话)的标准输出(stdout)和标准错误(stderr)。因此,后台任务的所有输出依然会同步地在命令行下显示。
(2)不再继承当前 session 的标准输入(stdin)。你无法向这个任务输入指令了。如果它试图读取标准输入,就会暂停执行(halt)。
可以看到,"后台任务"与"前台任务"的本质区别只有一个:是否继承标准输入。所以,执行后台任务的同时,用户还可以输入其他命令。
2、SIGHUP信号
变为"后台任务"后,一个进程是否就成为了守护进程呢?或者说,用户退出 session 以后,"后台任务"是否还会继续执行?
Linux系统是这样设计的。
- 用户准备退出 session
- 系统向该 session 发出SIGHUP信号
- session 将SIGHUP信号发给所有子进程
- 子进程收到SIGHUP信号后,自动退出
上面的流程解释了,为什么"前台任务"会随着 session 的退出而退出:因为它收到了SIGHUP信号。
那么,"后台任务"是否也会收到SIGHUP信号?
这由 Shell 的huponexit参数决定的。
# shopt | grep huponexit
执行上面的命令,就会看到huponexit参数的值。
大多数Linux系统,这个参数默认关闭(off)。因此,session 退出的时候,不会把SIGHUP信号发给"后台任务"。所以,一般来说,"后台任务"不会随着 session 一起退出。
3、disown 命令
通过"后台任务"启动"守护进程"并不保险,因为有的系统的huponexit参数可能是打开的(on)。
更保险的方法是使用disown命令。它可以将指定任务从"后台任务"列表(jobs命令的返回结果)之中移除。一个"后台任务"只要不在这个列表之中,session 就肯定不会向它发出SIGHUP信号。
# node server.js &
# disown
执行上面的命令以后,server.js进程就被移出了"后台任务"列表。你可以执行jobs命令验证,输出结果里面,不会有这个进程。
disown的用法如下。
# 移出最近一个正在执行的后台任务
# disown
# 移出所有正在执行的后台任务
# disown -r
# 移出所有后台任务
# disown -a
# 不移出后台任务,但是让它们不会收到SIGHUP信号
# disown -h
# 根据jobId,移出指定的后台任务
# disown %2
# disown -h %2
4、标准 I/O
使用disown命令之后,还有一个问题。那就是,退出 session 以后,如果后台进程与标准I/O有交互,它还是会挂掉。还是以上面的脚本为例,现在加入一行。
var http = require('http');
http.createServer(function(req, res) {
console.log('server starts...'); // 加入此行
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World');
}).listen(5000);
启动上面的脚本,然后再执行disown命令。
# node server.js &
# disown
接着,你退出 session,访问5000端口,就会发现连不上。这是因为"后台任务"的标准 I/O 继承自当前 session,disown命令并没有改变这一点。一旦"后台任务"读写标准 I/O,就会发现它已经不存在了,所以就报错终止执行。
为了解决这个问题,需要对"后台任务"的标准 I/O 进行重定向。
# node server.js > stdout.txt 2> stderr.txt < /dev/null &
# disown
5、nohup 命令
还有比disown更方便的命令,就是nohup。
# nohup node server.js &
nohup命令对server.js进程做了三件事。
- 阻止SIGHUP信号发到这个进程。
- 关闭标准输入。该进程不再能够接收任何输入,即使运行在前台。
- 重定向标准输出和标准错误到文件nohup.out。
也就是说,nohup命令实际上将子进程与它所在的 session 分离了。
注意,nohup命令不会自动把进程变为"后台任务",所以必须加上&符号。
手动后台运行进程并记录进程号
nohup jar -jar jar包 </dev/null > /data/app/logs/app.log 2>&1 &
echo $! > /data/app/run.pid
2>&1是把标准错误2重定向到标准输出1中,而标准输出又导入文件里面,所以标准错误和标准输出都会输出到文件。同时把启动的进程号pid输出到文件
6、Screen 命令与 Tmux 命令
另一种思路是使用 terminal multiplexer (终端复用器:在同一个终端里面,管理多个session),典型的就是 Screen 命令和 Tmux 命令。
它们可以在当前 session 里面,新建另一个 session。这样的话,当前 session 一旦结束,不影响其他 session。而且,以后重新登录,还可以再连上早先新建的 session。
Screen 的用法如下。
# 新建一个 session
# screen
# node server.js
然后,按下ctrl + A和ctrl + D,回到原来的 session,从那里退出登录。下次登录时,再切回去。
# screen -r
如果新建多个后台 session,就需要为它们指定名字。
# screen -S name
# 切回指定 session
# screen -r name
# screen -r pid_number
# 列出所有 session
# screen -ls
如果要停掉某个 session,可以先切回它,然后按下ctrl + c和ctrl + d。
Tmux 比 Screen 功能更多、更强大,它的基本用法如下。
# tmux
# node server.js
# 返回原来的session
# tmux detach
除了tmux detach,另一种方法是按下Ctrl + B和d ,也可以回到原来的 session。
# 下次登录时,返回后台正在运行服务session
# tmux attach
如果新建多个 session,就需要为每个 session 指定名字。
# 新建 session
# tmux new -s session_name
# 切换到指定 session
# tmux attach -t session_name
# 列出所有 session
# tmux list-sessions
# 退出当前 session,返回前一个 session
# tmux detach
# 杀死指定 session
# tmux kill-session -t session-name
7、Node 工具
对于 Node 应用来说,可以不用上面的方法,有一些专门用来启动的工具:forever,nodemon 和 pm2。
forever 的功能很简单,就是保证进程退出时,应用会自动重启。
# 作为前台任务启动
# forever server.js
# 作为服务进程启动
# forever start app.js
# 停止服务进程
# forever stop Id
# 重启服务进程
# forever restart Id
# 监视当前目录的文件变动,一有变动就重启
# forever -w server.js
# -m 参数指定最多重启次数
# forever -m 5 server.js
# 列出所有进程
# forever list
nodemon一般只在开发时使用,它最大的长处在于 watch 功能,一旦文件发生变化,就自动重启进程。
# 默认监视当前目录的文件变化
# nodemon server.js
# 监视指定文件的变化
# nodemon --watch app --watch libs server.js
pm2 的功能最强大,除了重启进程以外,还能实时收集日志和监控。
# 启动应用
# pm2 start app.js
# 指定同时起多少个进程(由CPU核心数决定),组成一个集群
# pm2 start app.js -i max
# 列出所有任务
# pm2 list
# 停止指定任务
# pm2 stop 0
# 重启指定任务
# pm2 restart 0
# 删除指定任务
# pm2 delete 0
# 保存当前的所有任务,以后可以恢复
# pm2 save
# 列出每个进程的统计数据
# pm2 monit
# 查看所有日志
# pm2 logs
# 导出数据
# pm2 dump
# 重启所有进程
# pm2 kill
# pm2 resurect
# 启动web界面 http://localhost:9615
# pm2 web
六、计划任务管理
1、at 一次性任务设置
如果没有该服务需要提前安装并启用
# yum -y install at
# systemctl start atd
# systemctl enable atd
# systemctl status atd
● atd.service - Job spooling tools
Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
Active: active (running) since 二 2020-05-12 15:22:13 CST; 45min ago
Main PID: 1153 (atd)
Tasks: 1
CGroup: /system.slice/atd.service
└─1153 /usr/sbin/atd -f
5月 12 15:22:13 localhost.localdomain systemd[1]: Started Job spooling tools.
生成一次性任务计划命令格式:at [option]... [TIME]
- -d 作业编号:删除指定的待执行任务,相当于atrm
- -l:显示待执行的任务的列表,相当于atq
- -m:任务执行完成后向用户发送E-mail(默认)
- -c 作业编号:显示指定作业的实际内容
TIME格式:
- HH:MM 如14:30
- HH:MM YYYY-MM-DD,如14:30 2015-10-07
- HH:MM[am|pm] [Month] [Date],如02:30pm October 07
- HH:MM[am|pm] + number [minutes|hours|days|weeks],如14:30 + 2 weeks
- now + #UNIT,如now + 5minutes
- noon,midnight,teatime,tomrrow
常用时间中英文对照
- 一月January —— Jan.
- 二月February —— Feb.
- 三月March —— Mar.
- 四月April —— Apr.
- 五月May —— May.
- 六月June —— Jun.
- 七月July —— Jul.
- 八月August—— Aug.
- 九月September —— Sept.
- 十月October —— Oct.
- 十一月November —— Nov.
- 十二月December—— Dec.
- 星期一:Monday, 英文缩写Mon。
- 星期二:Tuesday,英文缩写Tue。
- 星期三:Wednesday,英文缩写Wed。
- 星期四:Thursday,英文缩写Thur。
- 星期五:Friday,英文缩写Fri。
- 星期六:Saturday,英文缩写Sat。
- 星期日:Sunday,英文缩写Sun
在2020年6月20号晚上11点关机
# at 23:00 2020-06-20
at> /usr/sbin/shutdown -h now //命令路径查询为which+命令
at> <EOT> //ctrl+D保存并推出
job 1 at Sat Jun 20 23:00:00 2020
查看工作
# at -l
1 Sat Jun 20 23:00:00 2020 a root
# atq
1 Sat Jun 20 23:00:00 2020 a root
查看具体信息
# at -c 1 | tail -5 //1为工作序号
}
${SHELL:-/bin/sh} << 'marcinDELIMITER2a7561ad'
/usr/sbin/shutdown -h now
marcinDELIMITER2a7561ad
删除工作
# at -d 1
# atrm 1
压缩日志,并加上时间参数
# date
2020年 05月 12日 星期二 16:22:22 CST
# date +%F
2020-05-12
# date "+%Y-%m-%d %H:%M:%S"
2020-05-12 16:22:48
# tar -zcP -f log_$(date +%F).tar.gz /var/log/messages
在7月1号晚上,10点运行此脚本
# at 22:00 2020-07-01
at> /usr/bin/bash /root/logbak.sh
at> <EOT>
job 2 at Wed Jul 1 22:00:00 2020
为安全起见,我们通常需要编辑/etc/at.allow(白名单)或/etc/at.deny(黑名单)以允许或拒绝某些特定用户制定任务计划,这两个文件对at和batch命令都适用,这两个文件保留一个即可。
2、batch任务设置
其所生成的任务计划是在系统空闲(CPU的工作负载小于0.8)时执行的,故batch命令后不能指定时间
查看CPU负载
# uptime
16:29:43 up 1:08, 2 users, load average: 0.00, 0.01, 0.05
batch也是利用 at 来进行指令的下达,但是当CPU工作负载大时不会立即执行
# batch
at> /usr/bin/echo Hello batch > /tmp/test.txt
at> <EOT>
job 3 at Tue May 12 16:32:00 2020
3、crontab
通过 crontab
命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script 脚本。时间间隔的单位可以是分钟、小时、日、月、周及以上的任意组合。这个命令非常适合周期性的日志分析或数据备份等工作。
1、用户周期性任务
系统通常自带cron服务程序和crontab命令,若没有,可使用如下命令安装:
# yum -y install vixie-cron # cron服务程序包
# yum -y install crontabs # crontab程序包
检查 crond
服务
使用 systemctl list-unit-files
命令确认 crond
服务是否已安装。
# systemctl list-unit-files | grep crond
crond.service enabled
如果为 enabled,表示服务正运行。
开机自动启动 crond 服务:chkconfig crond on
或者,按以下命令手动启动:
systemctl enable crond.service # 开启服务(开机自动启动服务)
systemctl disable crond.service # 关闭服务(开机不会自动启动服务)
systemctl start crond.service # 启动服务
systemctl stop crond.service # 停止服务
systemctl restart crond.service # 重启服务
systemctl reload crond.service # 重新载入配置
systemctl status crond.service # 查看服务状态
crontab 命令格式如下:
crontab [-u user] file crontab [-u user] [ -e | -l | -r ]
与at一样,我们也可以设置白名单(/etc/cron.allow)或黑名单(/etc/cron.deny)来限制使用crontab的用户,这两个文件保留一个即可。
使用crontab命令管理用户的计划任务:crontab [-u username] [-elr]
说明:
-u user
:用来设定某个用户的 crontab 服务;file
:file 是命令文件的名字,表示将 file 做为 crontab 的任务列表文件并载入 crontab。如果在命令行中没有指定这个文件,crontab 命令将接受标准输入(键盘)上键入的命令,并将它们载入 crontab。-e
:编辑某个用户的 crontab 文件内容。如果不指定用户,则表示编辑当前用户的 crontab 文件。-l
:显示某个用户的 crontab 文件内容,如果不指定用户,则表示显示当前用户的 crontab 文件内容。-r
:从/var/spool/cron 目录中删除某个用户的 crontab 文件,如果不指定用户,则默认删除当前用户的 crontab 文件。-i
:在删除用户的 crontab 文件时给确认提示。
有两种方法写入定时任务:
- 在命令行输入:
crontab -e
然后添加相应的任务,存盘退出。 - 直接编辑
/etc/crontab
文件,即vi /etc/crontab
,添加相应的任务。
crontab 要执行的定时任务都被保存在 /etc/crontab
文件中。
crontab 的文件格式如下:
crontab计划任务的配置格式
逗号用于分隔列表。例如,在第 5 个字段(星期几)中使用 MON,WED,FRI
表示周一、周三和周五。
连字符定义范围。例如,2000-2010
表示 2000 年至 2010 年期间的每年,包括 2000 年和 2010 年。
除非用反斜杠()转义,否则命令中的**百分号(%)**会被替换成换行符,第一个百分号后面的所有数据都会作为标准输入发送给命令。
字段 | 是否必填 | 允许值 | 允许特殊字符 |
---|---|---|---|
Minutes | 是 | 0–59 | * ,- |
Hours | 是 | 0–23 | * ,- |
Day of month | 是 | 1–31 | * ,- |
Month | 是 | 1–12 or JAN–DEC | * ,- |
Day of week | 是 | 0–6 or SUN–SAT | * ,- |
- 减号可以表示一个连续的时间范围,如"1-4"表示整数1、2、3、4。
- 逗号",":可以表示一个间隔的不连续范围,如"3, 4, 6, 8"。
- 斜杠符号"/":可以用来指定间隔频率,如在日期字段中的"*/3"表示每隔3天。
- “ * ” 表示任何时刻都接受的意思,所有时间段位上不能同时为“ * ”
# crontab -e
59 23 30 06 * echo "LOVE U 3000+" > /root/lover.txt //6月30号的23:59,写一封内容为“LOVE U 3000+”的信,写在了/home/’user‘/lover.txt中
查看任务
# crontab -l
59 23 30 06 * echo "LOVE U 3000+" > /root/lover.txt
每1分钟需要执行一次打印当前时间的脚本test.sh,并追加在/tmp/time.txt文件中,
# date "+%Y-%m-%d %H:%M:%S"
2020-05-12 17:56:01
# vim time.sh
#!/bin/bash
echo `date "+%Y-%m-%d %H:%M:%S"` >> /tmp/time.txt
# crontab -e
*/1 * * * * /usr/bin/bash /root/time.sh
crontab: installing new crontab
查看其他用户的计划任务
# crontab -l -u root
删除任务
# crontab -r # 注意:这个命令会将所有的crontab数据内容都删掉,一般只需用crontab -e编辑即可
# crontab -l
no crontab for root
2、系统周期性任务计划
编辑完 /etc/crontab 并储存后,cron 的设置就会自动执行(只限使用root用户操作)
# vim /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see 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
# 每两个小时以root身份执行 /home/hello.sh 脚本
0 */2 * * * root /home/hello.sh
3、crontab 实例
实例 1:每 1 分钟执行一次 myCommand
* * * * * myCommand
实例 2:每小时的第 3 和第 15 分钟执行
3,15 * * * * myCommand
实例 3:在上午 8 点到 11 点的第 3 和第 15 分钟执行
3,15 8-11 * * * myCommand
实例 4:每隔两天的上午 8 点到 11 点的第 3 和第 15 分钟执行
3,15 8-11 */2 * * myCommand
实例 5:每周一上午 8 点到 11 点的第 3 和第 15 分钟执行
3,15 8-11 * * 1 myCommand
实例 6:每晚的 21:30 重启 smb
30 21 * * * /etc/init.d/smb restart
实例 7:每月 1、10、22 日的 4 : 45 重启 smb
45 4 1,10,22 * * /etc/init.d/smb restart
实例 8:每周六、周日的 1 : 10 重启 smb
10 1 * * 6,0 /etc/init.d/smb restart
实例 9:每天 18 : 00 至 23 : 00 之间每隔 30 分钟重启 smb
0,30 18-23 * * * /etc/init.d/smb restart
实例 10:每星期六的晚上 11 : 00 pm 重启 smb
0 23 * * 6 /etc/init.d/smb restart
实例 11:每一小时重启 smb
* */1 * * * /etc/init.d/smb restart
实例 12:晚上 11 点到早上 7 点之间,每隔一小时重启 smb
0 23-7 * * * /etc/init.d/smb restart
评论区