Linux常用命令--进程管理

摘要

  • 本文介绍Linux进程管理等相关命令

  • 本文基于CentOS8(x86_64)

ps :查看进程

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
33
34
35
36
37
38
39
40
41
42
43
44
# 与top命令类似,可以查看进程信息
$ ps -aux
-a:所有用户
-x:没有控制终端的
-u:显示用户名和启动时间
# 根据关键字过滤
$ ps -aux | grep nginx

$ ps -ef
-e:显示所有进程,包括用户和没有控制终端
-f:显示所有信息

# 查看线程,相同PID表示的是同一个进程启动的线程
$ ps -eLf
-L:显示线程

# 查看进程优先级 NI
$ ps -el

# 查看进程运行在第几个cpu逻辑核心上
$ ps -eo psr,user,pid,ppid,pri,ni,pgid,command | grep nginx
psr:cpu逻辑核号
pri :默认19,优先级 0~99,越大优先级越高,pri(new) = pri(old) + nice
ni :默认0,影响优先级的因子 -20~19,越小优先级越高,改变nice值可以改变pri
pgid :进程组id,等同于 pgrp
# 绑定PID为10288的进程到第一块CPU,这样该进程就不会占用其它的CPU资源了
$ taskset -cp 1 10288

# 按pid排序
$ ps -aux --sort pid
# 按用户排序
$ ps -aux --sort user

# 说明:
USER:进程的执行用户
PID:进程号
PPID:父进程号
TTY:进程启动的终端
STAT:进程的当前状态,S:休眠 D:不可中断的休眠 R:运行 Z:僵死 T:停止 I:空闲内核线程
NI:进程优先级
TIME:进程自启动以来占用CPU的总时间
CMD/COMMAND:执行的命令
%CPU:占用CPU时间和总时间的百分比
%MEM:占用内存与系统内存总量的百分比
  • ps 命令也可以查看指定进程的线程信息

1
2
3
4
5
6
7
$ ps -Lp <pid>
# 输出,这里LWP(Light Weight Process)就是线程id的十进制
PID LWP TTY TIME CMD
25238 25238 ? 00:00:00 java
25238 25241 ? 00:00:13 java
25238 25242 ? 00:00:03 GC task thread#
25238 25243 ? 00:00:03 GC task thread#

pstree :树状查看进行信息

1
2
3
4
5
6
# 折叠展示,只展示父进程和子进程的数量
$ pstree
# 展开所有子进程,并显示完整命令
$ pstree -a
# 展开所有子进程,并显示PID
$ pstree -p

kill :终止进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 正常关闭进程
kill 进程号
# 强行关闭,遇到进程不能正常关闭时使用
kill -9 进程号
# 查看所有信号
kill -l

# 杀死进程名称所关联的全部进程
killall 进程名称
killall -9 进程名称

# 杀死进程名称所关联的全部进程,同killall
pkill 进程名称
pkill -9 进程名称

# 查找服务的进程号
pgrep 进程名称 : 效果同 ps -aux | grep 进程名 | grep -v grep| awk '{print $2}'
# 终止进程名称为mysql的全部进程
pgrep mysql | xargs kill -s 9

pgrep -f 匹配字符串 :其表示查找所有包含‘匹配字符串’的进程
#比如:查询 elasticsearch
pgrep elasticsearch # 此时查询不出进程Id,因为elasticsearch的进程是java
pgrep -f elasticsearch # 可以查询出进程Id,因为只要进程完整路径中包含elasticsearch就行

信号量

  • 通过kill -l可以查看所有信号的编号和名称
信号编号 信号名称 说明
1 SIGHUP 挂起信号,通常用于通知进程终端断开或重新加载配置。
2 SIGINT 中断信号,通常由用户通过键盘 (Ctrl+C) 发送,用于中断一个进程。
3 SIGQUIT 退出信号,通常由键盘 (Ctrl+\) 发送,要求进程生成核心转储并退出。
4 SIGILL 非法指令信号,表示进程执行了非法或未定义的机器指令。
5 SIGTRAP 陷阱信号,通常用于调试程序(如断点异常)。
6 SIGABRT 异常终止信号,进程调用 abort() 函数时触发。
7 SIGBUS 总线错误信号,通常由于未对齐的内存访问引发。
8 SIGFPE 浮点异常信号,例如除零或溢出。
9 SIGKILL 强制终止信号,无法被捕获或忽略,立即终止进程。
10 SIGUSR1 用户自定义信号 1,用户或应用程序自定义用途。
11 SIGSEGV 段错误信号,表示进程访问了非法的内存地址。
12 SIGUSR2 用户自定义信号 2,用户或应用程序自定义用途。
13 SIGPIPE 管道破裂信号,通常在写入一个已关闭的管道或套接字时触发。
14 SIGALRM 定时器信号,由 alarm() 函数触发,用于定时操作。
15 SIGTERM 终止信号,允许进程执行清理操作后终止。这个也是缺省的信号。
16 SIGSTKFLT 堆栈错误信号,主要用于硬件相关的堆栈操作(极少使用)。
17 SIGCHLD 子进程状态改变信号,当子进程退出或停止时触发。
18 SIGCONT 继续信号,用于恢复被暂停的进程。
19 SIGSTOP 暂停信号,无法被捕获或忽略,立即暂停进程。
20 SIGTSTP 终端暂停信号,通常通过键盘 (Ctrl+Z) 发送。
21 SIGTTIN 后台进程尝试从终端读取输入时触发。
22 SIGTTOU 后台进程尝试向终端写入输出时触发。
23 SIGURG 紧急条件信号,通常用于套接字通信中的紧急数据。
24 SIGXCPU 超过 CPU 时间限制信号。
25 SIGXFSZ 超过文件大小限制信号。
26 SIGVTALRM 虚拟定时器信号,由 setitimer() 触发,用于进程执行时间的计时。
27 SIGPROF 统计定时器信号,用于程序性能分析。
28 SIGWINCH 窗口大小改变信号,终端窗口调整时触发。
29 SIGIO I/O 可用信号,表示文件描述符可用。
30 SIGPWR 电源故障信号。
31 SIGSYS 无效的系统调用信号。
  • 从 SIGRTMIN(34)到 SIGRTMAX(64)的信号是实时信号,供用户或应用程序使用,具有更高的优先级和灵活性。

  • 使用时,可以通过数字或名称来指定信号,例如 kill -9kill -SIGKILL,当然也支持去掉SIG的简称,例如 kill -KILL

  • 咋一看有很多,但实际上我们日常通过键盘能操作的就几个。

      1. SIGHUP:挂起信号,通常用于通知进程终端断开或重新加载配置。
    1
    2
    3
    4
    # 重新加载nginx配置文件
    kill -HUP `cat /var/run/nginx.pid`
    # 或者
    sudo systemctl reload nginx
      1. SIGINT:中断信号,通常由用户通过键盘 (Ctrl+C) 发送,用于中断一个进程。
      1. SIGQUIT:退出信号,通常由键盘 (Ctrl+\) 发送,要求进程生成核心转储并退出。
      1. SIGKILL:强制终止信号,无法被捕获或忽略,立即终止进程。
    1
    2
    # 强制终止进程
    kill -9 `cat /var/run/nginx.pid`
      1. SIGTERM:用于优雅的终止进程,通常由用户或应用程序发送,用于通知进程进行清理操作。这个也是缺省的信号。
    1
    2
    3
    4
    # 终止进程
    kill -TERM `cat /var/run/nginx.pid`
    # 或者
    kill `cat /var/run/nginx.pid`

进程的挂起和恢复

1
2
3
4
5
6
7
8
9
10
11
12
13
ctrl+c :终止

ctrl+z :挂起,暂停

bg num :转到后台继续运行

fg num :从后台恢复到前台继续运行

jobs :查看被挂起或正在后台运行的进程,会显示编号num

commond & :后台运行,只在当前终端下有效

nohup commond & :后台执行,退出终端依然继续执行

进程运行优先级

  • 进程优先级是 PRI(top中的PR),其表示程序被 CPU 执行的先后顺序,此值越小进程的优先级别越高

  • PRI 值不是 Nice 值,但是 Nice 值会影响优先级,PRI(new) = PRI(old) + nice,所以我们调整 Nice 值,就可以改变进程的优先级

  • linux下的进程调度优先级 Nice 是从 -20 到 19 ,一共 40 个级别,数字越大,表示进程的优先级越低。默认时候,进程的调度优先级是0。

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
33
34
35
36
37
38
39
40
41
# 指定命令的运行优先级
nice -n commond
# 改变一个正在运行的进程的优先级
renice n pid
# 改变进程组内全部进程的优先级
renice n -g pid
# 说明
n :取值范围(-20,19),越小优先级越高

# 示例
# 默认启动nginx
$ nginx
# 查看优先级,可以看到此时默认优先级是0
ps -eo pgid,user,pid,pri,ni,command | grep nginx | grep -v grep
23348 root 23348 19 0 nginx: master process nginx
23348 nginx 23349 19 0 nginx: worker process
23348 nginx 23350 19 0 nginx: worker process
# 终止nginx进程
$ pkill nginx
# 重新指定优先级启动,指定优先级为10
$ nice -10 nginx
# 查看优先级,可以看到此时默认优先级是10
$ ps -eo pgid,user,pid,pri,ni,command | grep nginx | grep -v grep
23388 root 23388 9 10 nginx: master process nginx
23388 nginx 23389 9 10 nginx: worker process
23388 nginx 23390 9 10 nginx: worker process
# 改变运行中的nginx的优先级为-10,此时只会改变master的优先级
$ renice -10 23388
23388 (进程 ID) 旧优先级为 10,新优先级为 -10
$ ps -eo pgid,user,pid,pri,ni,command | grep nginx | grep -v grep
23388 root 23388 29 -10 nginx: master process nginx
23388 nginx 23389 9 10 nginx: worker process
23388 nginx 23390 9 10 nginx: worker process

# 改变运行中的nginx的优先级为-10,此时只会改变master的优先级
$ renice -10 -g 23388
23388 (进程组 ID) 旧优先级为 -10,新优先级为 -10
$ ps -eo pgid,user,pid,pri,ni,command | grep nginx | grep -v grep
23388 root 23388 29 -10 nginx: master process nginx
23388 nginx 23389 29 -10 nginx: worker process
23388 nginx 23390 29 -10 nginx: worker process

at :执行一次的计划任务

  • /var/spool/at:at任务存放在该目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 启动at服务
systemctl start atd

# 今天10:46执行 test.sh
at -f test.sh 10:46
# 时间格式:
5:30pm :今天下午5点30分
17:30 :今天下午5点30分
17:30 today :今天下午5点30分
now +3 hours :3小时后
now +180 minutes :3小时后
17:30 23.2.28 :2023年2月28日下午5点30分

# 查看当前at队列,显示编号num
at -l ==== atq

# 删除指定编号的任务
at -d num ==== atrm num
  • 用户限制:
    /etc/at.allow :如果文件存在,则只有此文件中列出的用户可以使用at命令
    /etc/at.deny :如果文件存在,则此文件中列出的用户不可以使用at命令
    如果以上两个文件都不存在,则只有root用户可以使用at命令
    如果以上两个文件都存在,但是都为空,则所有用户都可以使用at命令

crontab :周期性计划任务

  • systemctl start crond :启动cron服务,默认启动

  • crontab -l :显示当前用户下的计划任务

  • crontab -e :编辑当前用户下的计划任务

  • /var/spool/cron :计划任务保存在该路径下

  • /etc/cron.allow/etc/cron.deny :用户限制,规则同 at

  • 格式:分钟[0~59] 小时[0~23] 日期[1~31] 月份[1~12] 星期[0~6] commands

1
2
3
4
5
6
7
8
9
10
11
# 每5分钟执行一次
*/5 * * * * command

# 每天2点执行一次
0 2 * * * command

# 每天2、3、4、5点各执行一次
0 2-5 * * * command

# 周一至周五每天9点和18点各执行一次
0 9,18 * * 1-5 command

systemctl :服务管理

  • centOS7以后使用systemd进行服务管理,其命令接口为systemctl

  • systemctl兼容了service,即systemctl也会去/etc/init.d目录下,查看、执行相关程序

  • 如下centOS6及之前的服务启动方式,centOS7及之后仍然可以通过这种方式管理服务,实际上会重定向到systemctl命令

1
2
3
4
service redis start
service redis stop
service redis restart
service redis status
  • systemd的服务配置放在目录/usr/lib/systemd/system (Centos)/etc/systemd/system (Ubuntu)

  • 配置目录下有多种类型文件.mount,.service,.target,.socket,.timer等等

    • 不同的文件类型代表不同的资源,统称为 Unit(单位),Unit 一共分成12种类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Service unit:系统服务
    Target unit:多个 Unit 构成的一个组
    Device Unit:硬件设备
    Mount Unit:文件系统的挂载点
    Automount Unit:自动挂载点
    Path Unit:文件或路径
    Scope Unit:不是由 Systemd 启动的外部进程
    Slice Unit:进程组
    Snapshot Unit:Systemd 快照,可以切回某个快照
    Socket Unit:进程间通信的 socket
    Swap Unit:swap 文件
    Timer Unit:定时器
  • 重点学习.service文件,其定义了一个服务,分为[Unit],[Service],[Install]三个小节

[Unit]:描述服务的元信息和依赖关系

指令名 说明
Description 服务的简要描述(推荐设置)
Documentation 文档链接,可是 URL 或 man page 名
Requires= 硬依赖的其他 unit,若失败则本服务也失败
Wants= 弱依赖,失败不影响当前服务
Before= 本服务启动应在指定服务之前
After= 本服务启动应在指定服务之后
Conflicts= 指定服务不能同时运行(冲突)
ConditionPathExists= 若路径存在则运行,否则跳过服务
Condition...(系列) 条件判断,如 ConditionKernelCommandLine=ConditionUser=
StartLimitBurst 限制单位时间内最大重启次数
StartLimitIntervalSec 限制重启频率(配合 Burst 使用)

[Service]:定义服务的行为、运行方式、启动/停止命令等

指令名 说明
Type= 服务类型:simpleforkingoneshotnotifydbusidle
ExecStart= 启动服务时执行的命令
ExecStop= 停止服务时执行的命令
ExecReload= 重载服务时执行的命令(如 nginx -s reload
Restart= 服务异常退出后的重启策略(on-failurealways 等)
RestartSec= 重启前等待时间(秒)
User= 以指定用户身份运行服务
Group= 以指定组身份运行服务
WorkingDirectory= 设置工作目录
Environment= 设置环境变量(如 Environment=JAVA_HOME=/opt/java
EnvironmentFile= 从文件中加载环境变量
PIDFile= 指定服务主进程 PID 文件路径
TimeoutStartSec= 启动命令最长等待时间
TimeoutStopSec= 停止命令最长等待时间
KillSignal= 停止服务时发送的信号(默认 SIGTERM)
SendSIGKILL= 是否在超时后发送 SIGKILL(默认 yes)
KillMode= 控制如何终止进程(如 control-groupprocess
StandardOutput= 控制标准输出(如 journalsyslognull
StandardError= 控制标准错误输出
LimitNOFILE= 设置文件描述符上限等资源限制
ExecStartPre= 启动前先执行的命令
ExecStartPost= 启动后再执行的命令
ExecStopPost= 停止后执行的命令
RemainAfterExit= 启动脚本退出后服务仍保持 active 状态(用于 oneshot
SuccessExitStatus= 自定义哪些退出码视为成功

[Install]:定义服务如何集成到启动流程(开机启用)

指令名 说明
WantedBy= 绑定到哪个目标(如 multi-user.target),用于 enable 开机启动
RequiredBy= 强绑定,失败会阻止目标加载
Also= 启用/禁用此服务时,也启用/禁用列出的其他 unit
Alias= 创建服务别名(可用 systemctl start <别名> 来代替)
DefaultInstance= 用于模板服务(@.service)的默认实例名

systemd Target 与传统 RunLevel 对照表

systemd Target 传统 RunLevel 说明
poweroff.target 0 关机。系统停止所有服务并关闭电源。等价于 runlevel 0
rescue.target 1 单用户模式。挂载文件系统、提供 root shell,不启动网络等服务(等价 runlevel 1
multi-user.target 3 多用户命令行模式,无图形界面。正常服务器模式(等价 runlevel 3
graphical.target 5 多用户 + 图形界面。启动 display manager(如 GDM/XDM)(等价 runlevel 5
reboot.target 6 重启。等价于 runlevel 6(但 systemd 使用 isolate reboot.target
emergency.target 最低限度的系统,仅挂载 /,只提供 root shell。不等价于任何传统 RunLevel(比 runlevel 1 更底层)
default.target 可配置 指向系统默认启动目标,通常是 graphical.targetmulti-user.target
halt.target (0) 仅停止系统(不一定断电)
shutdown.target (0/6) 提供通用的关机目标,常在 halt, poweroff, reboot 前被调用
  • 查看服务

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
33
34
35
36
37
38
39
# 列出当前已经加载启动的 unit,如果添加 -all 选项会同时列出没有启动的 unit
systemctl # ==== systemctl list-units
# 加载后有几种情况
# loaded active running 加载文件成功,启动成功,正在运行
# loaded active exited 加载文件成功,启动成功,运行一次正常退出
# loaded active waiting 加载文件成功,启动成功,正在运行,不过还在等待其它事件才能继续处理
# loaded failed failed 加载文件成功,启动失败,运行失败

systemctl -all
# 会多几个状态
# not-found inactive dead 加载文件失败[没找到文件]
# loaded inactive dead 加载文件成功 但没有设置问开机启动,所以没有运行过

# 只查看服务类型为service的服务
systemctl --type service
systemctl list-units --type=service -all

# 查看是否有安装图形化环境,这里要注意,即便运行级别为5(graphical.target),如果没有安装图形化环境则会降级到命令行界面
systemctl --type=service | grep -E '(gdm|kdm|lightdm)'
# 或者
systemctl status gdm kdm lightdm


# 根据 /lib/systemd/system/ 目录内的文件列出所有的服务及其当前的状态,如是否开机启动等等
systemctl list-unit-files
# 状态类型
# enabled 设置为开机启动
# disabled 没有设置为开机启动
# masked 注销状态,此时不能通过systemctl进行启动,需要先取消注销状态
# static 该配置文件没有[Install]部分(无法执行),只能作为其他配置文件的依赖

# 只列出设置为开机启动的项
systemctl list-unit-files | grep enabled

# 只列出service
systemctl list-unit-files --type service

# 如果发现某个 服务 不工作,可以查看是否有 服务 加载失败
systemctl --failed
  • service :系统服务

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
33
34
35
36
# 启动redis服务
systemctl start redis
# 重新启动redis服务
systemctl restart redis
# 关闭redis服务
systemctl stop redis
# 不关闭redis的情况下重新加载配置文件,让新的设置生效
systemctl reload redis
# 加入开机自启动
systemctl enable redis
# 关闭开机自启动
systemctl disable redis
# 查看redis服务状态
systemctl status redis
# 目前有没有正在运行中
systemctl is-active redis
# 开机时有没有默认要启用
systemctl is-enabled redis
# 是否启动失败
systemctl is-failed redis
# 列出 redis服务 的配置
systemctl show redis
# 注销 redis服务 ,注销后就无法启动
systemctl mask redis
# 取消 redis服务 的注销
systemctl unmask redis

# 在脚本中判断服务是否启动
if systemctl --quiet is-active $serviceName
then
echo "$serviceName is running"
# do something,for example: stop $serviceName
systemctl stop $serviceName
else
echo "$serviceName is not running"
fi

小贴士

1
2
3
4
5
6
7
8
9
10
11
# 查看系统启动耗时
$ systemd-analyze

# 查看每个服务的启动耗时
$ systemd-analyze blame

# 显示瀑布状的启动过程流
$ systemd-analyze critical-chain

# 显示指定服务的启动流
$ systemd-analyze critical-chain sshd.service
  • target :类似于运行级别,支持多个target同时启动。target其实是多个unit的组合,系统启动说白了就是启动多个unit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 只查看服务类型为target的服务
systemctl list-units --type=target -all
# 取得目前缺省的 target
systemctl get-default
# 几个常用的 target。
graphical.target:多人模式,支持图形和命令行两种登录,对应之前的3,5级别。
multi-user.target:多人模式,只支持命令行登录,对应3级别!
rescue.target:单人模式,对应1级别,在无法使用 root 登陆的情况下,systemd 在开机时会多加一个额外的临时系统,与你原本的系统无关。这时你可以取得 root 的权限来维护你的系统。
emergency.target:单人模式,不过系统进入后根目录是只读的,紧急处理系统的错误,在无法使用 rescue.target 时,可以尝试使用这种模式!

# 设置缺省的 target
systemctl set-default multi-user.target
# 切换到指定的 target
systemctl isolate multi-user.target

# 查看依赖关系,查看 multi-user.target 依赖哪些服务,默认树形展示
systemctl list-dependencies multi-user.target
# 查看依赖关系,查看 multi-user.target 依赖哪些服务,--plain:去掉了树形结构的视觉效果,将输出更改为平面列表。这样可以方便地用其他命令(例如grep)进行进一步的处理。--all:这个选项表示显示所有依赖,包括那些因某种原因没有启动或无法访问的依赖项。
systemctl list-dependencies --all --plain multi-user.target

# 反向查看依赖关系, --reverse 选项查看 multi-user.target 被谁使用
systemctl list-dependencies multi-user.target --reverse

小贴士

  • 1.systemd 主配置文件 /etc/systemd/system.conf
  • 2.开机会先加载 /etc/systemd/system/default.target
  • 3.所有的 servicetarget 都在 /usr/lib/systemd/system/目录下
  • 4./etc/systemd/system/defaut.target 是一个软连接,软连接到了/usr/lib/systemd/system/multi-user.target,它会加载/usr/lib/systemd/system/multi-user.target.wants下面的service
  • 5.查看一个service属于哪个target,需要查看具体的service文件,如:cat /usr/lib/systemd/system/sshd.service,看里面[install]部分

rc.local :本地服务启动文件

  • centos8 之后默认是不开启rc-local服务的,而是推荐使用systemctl来管理开机启动,但是仍然可以手动开启rc-local服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 如果该文件不存在就创建
sudo touch /etc/rc.d/rc.local
# 授予执行权限
sudo chmod +x /etc/rc.d/rc.local
# 软连接到/etc下,这个只是个习惯,可以不软连接
sudo ln -s /etc/rc.d/rc.local /etc/rc.local
# 在rc.local中添加开机启动的命令
echo "your command" >> /etc/rc.d/rc.local
# 重新加载
sudo systemctl daemon-reload
# 设置开机启动
sudo systemctl enable rc-local
# 启动
sudo systemctl start rc-local

使用 systemctl 来替换 rc.local

  • 比如开机时以 ec2-user 身份运行 /home/ec2-user/Jenkins/start.sh 脚本。

  • 如果编写到 /etc/rc.d/rc.local 中,则如下

1
su - ec2-user -c /home/ec2-user/Jenkins/start.sh
  • 如果编写到 /etc/systemd/system/jenkins.service中,则如下

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=Start Jenkins Script as ec2-user
After=network.target

[Service]
Type=forking
User=ec2-user
ExecStart=/bin/bash -lc '/home/ec2-user/Jenkins/start.sh'
WorkingDirectory=/home/ec2-user/Jenkins
Restart=on-failure

[Install]
WantedBy=multi-user.target
  • 此时已经可以通过systemctl来管理 jenkins 了,如果你有自己的 stoprestart 脚本,可以配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Unit]
Description=Start Jenkins Script as ec2-user
After=network.target

[Service]
Type=forking
User=ec2-user
WorkingDirectory=/home/ec2-user/Jenkins

ExecStart=/bin/bash -lc '/home/ec2-user/Jenkins/start.sh'
ExecStop=/bin/bash -lc '/home/ec2-user/Jenkins/stop.sh'
ExecReload=/bin/bash -lc '/home/ec2-user/Jenkins/restart.sh'

Restart=on-failure
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
  • 指令简介

指令 含义
ExecStart 启动 Jenkins 的脚本(模拟 su - ec2-user -c
ExecStop 停止 Jenkins 的脚本 (如果没有提供,systemd 会默认发送 SIGTERM 给主进程,让它优雅退出)
ExecReload 重启 Jenkins 脚本(如果没有提供,systemd 也能用 ExecStop + ExecStart 自动重启)
Type=forking 表示脚本会 fork 到后台运行(如 Jenkins 通常会这样做)
RemainAfterExit=yes 启动后保持服务为 active,即使脚本退出 ,一般只在 simple 或 oneshot 模式下使用,本示例不需要设置
Restart=on-failure 如果脚本失败,systemd 自动尝试重启
User 指定以哪个用户身份运行脚本
WorkingDirectory 设置脚本运行时的工作目录(可选,但推荐设置)
  • Restart 可选值

Restart 值 成功退出(exit 0) 异常退出(exit ≠ 0) 信号终止(如 SIGKILL) Core Dump / Watchdog 故障 说明
no (默认) ✖️ 不重启 ✖️ 不重启 ✖️ 不重启 ✖️ 不重启 永远不自动重启
on-success ✅ 重启 ✖️ 不重启 ✖️ 不重启 ✖️ 不重启 仅当进程正常退出时重启(很少使用)
on-failure ✖️ 不重启 ✅ 重启 ✅ 重启 ✅ 重启 常用。非正常退出时重启
on-abnormal ✖️ 不重启 ✖️ 不重启 ✅ 重启(信号终止) ✅ 重启 仅当进程被信号或异常终止时重启
on-watchdog ✖️ 不重启 ✖️ 不重启 ✖️ 不重启 ✅ 重启(watchdog 故障) 仅在 watchdog 超时才重启
on-abort ✖️ 不重启 ✖️ 不重启 ✅ 重启(信号终止) ✖️ 不重启 仅在收到信号(如 SIGTERM)时重启
always ✅ 重启 ✅ 重启 ✅ 重启 ✅ 重启 所有退出都重启(守护进程建议使用)
  • Type 可选值

Type 类型 含义与行为 启动时 systemd 的行为 适用场景 示例
simple (默认) 直接启动 ExecStart 指定的命令,不等待子进程 执行后立即认为服务已启动 脚本/服务在前台运行,不会 fork 到后台 ExecStart=/usr/bin/python3 app.py
forking 启动脚本fork 出后台进程并自行退出 systemd 认为服务启动完成于脚本退出之后,主服务 PID 由其追踪 传统后台服务或使用 nohup/& 的脚本 ExecStart=/etc/init.d/nginx start
oneshot 用于执行一次性命令,命令运行完即结束 等待命令执行完毕再继续启动其他服务 初始化任务、配置脚本、挂载命令等 ExecStart=/usr/bin/initialize.sh
dbus 启动服务后,等待其注册 D-Bus 名称 视为启动完成 服务必须使用 D-Bus 提供名称 D-Bus 服务(如 NetworkManager) BusName=org.freedesktop.NetworkManager
notify 启动服务后,等待其通过 sd_notify 告知已就绪 服务必须主动通知 systemd 启动完成 高级服务(如 systemd 自带服务) NotifyAccess=main(需程序配合)
idle 等系统空闲时再启动,其他服务都初始化后执行 类似 simple,但延迟执行 非关键服务、后台任务 ExecStart=/usr/bin/postinit.sh
  • 管理服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 重新加载配置
sudo systemctl daemon-reload
# 开机启用服务
sudo systemctl enable jenkins
# 启动服务
sudo systemctl start jenkins
# 停止服务
sudo systemctl stop jenkins
# 重启服务
sudo systemctl restart jenkins
# 查看服务状态
sudo systemctl status jenkins
# 查看运行日志
journalctl -u jenkins

chkconfig :设置系统服务在哪些运行级别下开机启动

  • centOS7之后不再使用这种方式,而是使用systemctl,但是仍然可以在/etc/init.d(实际上是/etc/rc.d/init.d的软链)目录下创建管理脚本,然后通过chkconfig进行管理

1
2
3
4
5
6
chkconfig --list :查看全部系统服务的运行级别
chkconfig --list sshd :查看sshd系统服务的运行级别
chkconfig --level 2345 sshd on :修改sshd服务的运行级别为2345都启动,on/off
chkconfig sshd on :默认就是开启2345运行级别
chkconfig --add nginx :添加服务到管理列表中,需要在`/etc/init.d`目录下有对应的nginx文件
chkconfig --del nginx :从管理列表中删除

rc.d

1
2
3
4
tree /etc/rc.d/
├── init.d/ # 启动/停止服务脚本主目录 ,软链到 /etc/init.d/
├── rc.local # 用户自定义开机启动脚本, 软链到 /etc/rc.local
├── rc0.d/ ~ rc6.d/ # 各运行级别(runlevel)的启动顺序目录,不需要手工维护,运行 chkconfig --level 2345 sshd on 时,会自动将 sshd 的启动文件软链到对应目录中

ntsysv :通过界面设置服务是否开机启动

  • centOS7之后不再使用这种方式,而是使用systemctl,但是仍然可以使用其管理当前运行级别下的服务

  • 运行命令后会弹出设置界面,服务前面有*号的表示开机启动,使用空格修改,Tab键进行跳转

1
2
3
4
5
6
# 修改当前的运行级别下的服务
ntsysv
# 修改3运行级别下的服务
ntsysv --level 3
# 同时修改3和5运行级别下的服务
ntsysv --level 35

Linux运行级别
0:系统关机模式,系统默认运行级别不能设置为0,否则无法正常启动系统
1:单用户模式,也称为救援模式,root权限,用于系统维护,禁止远程登陆,类似Windows下的安全模式登录。
2:无网络支持的多用户模式
3:有网络支持的多用户模式(文本模式,工作中最常使用的模式)
4:保留,未使用
5:有网络支持的图形化模式,支持多用户模式,登陆后进入图形GUI模式或GNOME、KDE图形化界面,如X Window系统。
6:重启模式,重新引导系统,即重启

运行级别切换

1
2
3
4
5
6
7
8
9
# 查看当前的运行级别,输出结果为: 上一次运行级别 当前运行级别
runlevel

# 切换运行级别
init 0 ==== systemctl isolate poweroff.target ==== systemctl poweroff
init 1 ==== systemctl isolate rescue.target
init 3 ==== systemctl isolate multi-user.target
init 5 ==== systemctl isolate graphical.target
init 6 ==== systemctl isolate reboot.target ==== systemctl reboot

fuser :可以显示出当前哪个程序在使用磁盘上的某个文件、挂载点、甚至网络端口,并给出程序进程的详细信息

  • fuser通常被用在诊断系统的resource busy问题,通常是在你希望umount指定的挂载点得时候遇到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看哪个进程在访问/mnt目录
fuser /mnt :只显示PID和权限
fuser -u /mnt :-u,在每个PID后面添加进程拥有者的用户名称
fuser -v /mnt :-v,显示详细信息,包含PID,USER,COMMAND等许多域

# 把访问mnt目录的进程杀掉
fuser -kv /mnt :-k,杀掉进程
fuser -kvi /mnt :-i,会询问你是否杀掉对应的进程

# 查看80端口被哪个进程占用
fuser -n tcp 80 :-n,指定协议和端口
fuser 80/tcp :不加-n,需要这样指定协议和端口

fuser -un tcp 80
fuser -u 80/tcp

fuser -vn tcp 80
fuser -v 80/tcp

# 杀掉占用80端口的进程
fuser -kvn tcp 80
fuser -kvin tcp 80
fuser -kvi 80/tcp

查看进程启动时的环境变量

  • 所有启动的进程都会在/proc/下创建以其进程IP命名的文件夹,其下为与当前进程相关为文件,其中environ中的没人就是进程启动时的环境变量,比如我们查看elasticsearch的进程

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
$ pgrep -f elasticsearch
4603

$ sudo more /proc/4603/environ
XDG_SESSION_ID=c2SIZE=1000usersql8/bin:/usr/local/soft/redis-5.0.14/src:/home/ec2-user/.local/bin:/home/ec2-user/binvar/spool/mail/ec2-usergnoredupsME=ec2-user

# 上面看着比较混乱,因为每个环境变量之间是通过'\0'分隔的,我们将其替换为'\n'
# 在/proc/[PID]/environ文件中,进程的环境变量是以null字符分隔的,即以'\0'(空字符)结尾
$ sudo more /proc/4603/environ | tr '\0' '\n'
XDG_SESSION_ID=c2
HOSTNAME=ip-10-250-0-214.cn-northwest-1.compute.internal
SHELL=/bin/bash
HISTSIZE=1000
OLDPWD=/home/ec2-user
ES_JAVA_HOME=/usr/local/soft/elasticsearch-7.17.6/jdk
USER=ec2-user
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/usr/local/bin:/usr/local/soft/mysql8/bin:/usr/local/soft/redis-5.0.14/src:/home/ec2-user/.local/bin:/home/ec2-user/bin
MAIL=/var/spool/mail/ec2-user
PWD=/usr/local/soft/elasticsearch-7.17.6
LANG=en_US.UTF-8
HISTCONTROL=ignoredups
HOME=/home/ec2-user
SHLVL=1
LIBFFI_TMPDIR=/tmp/elasticsearch-4722185969271784034
LOGNAME=ec2-user
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/1000