作者 青鸟

会话与进程

命令行的典型使用方式是,打开一个终端窗口,在里面输入命令。用户与计算机的这种临时的交互,称为一次"会话"(session) 。

会话的一个重要特点是,窗口与其中启动的进程是连在一起的。打开窗口,会话开始;关闭窗口,会话结束,会话内部的进程也会随之终止,不管有没有运行完。

这就带来一个问题,当我们运行后端程序时,我们的程序进程是和我们的会话绑定的。当我们退出命令行的时候,程序进程就难以管理,这就不符合我们对于后端程序进程的管理需求。于是就有了进程守护这一概念。

Tmux

什么是Tmux

tmux是一个优秀的终端复用软件,类似GNU Screen,但来自于OpenBSD,采用BSD授权。使用它最直观的好处就是,通过一个终端登录远程主机并运行tmux后,在其中可以开启多个控制台而无需再“浪费”多余的终端来连接这台远程主机。

tmux可以将会话与窗口"解绑":窗口关闭时,会话并不终止,而是继续运行,等到以后需要的时候,再让会话"绑定"其他窗口。

于是乎,tmux就可以起到守护进程的作用。

tmux守护进程

新建会话

第一个启动的 Tmux 窗口,编号是0,第二个窗口的编号是1,以此类推。这些窗口对应的会话,就是 0 号会话、1 号会话。

使用编号区分会话,不太直观,更好的方法是为会话起名。

1
tmux new -s <session-name>

上面命令新建一个指定名称的会话。

###分离会话 在 Tmux 窗口中,按下Ctrl+b d或者输入tmux detach命令,就会将当前会话与窗口分离。

1
tmux detach

上面命令执行后,就会退出当前 Tmux 窗口,但是会话和里面的进程仍然在后台运行。

tmux ls命令可以查看当前所有的 Tmux 会话。

1
2
$ tmux ls
$ tmux list-session

接入会话

tmux attach命令用于重新接入某个已存在的会话。

1
2
3
4
5
# 使用会话编号
$ tmux attach -t 0

# 使用会话名称
$ tmux attach -t <session-name>

杀死会话

tmux kill-session命令用于杀死某个会话。

1
2
3
4
5
# 使用会话编号
$ tmux kill-session -t 0

# 使用会话名称
$ tmux kill-session -t <session-name>

切换会话

tmux switch命令用于切换会话。

1
2
3
4
5
# 使用会话编号
$ tmux switch -t 0

# 使用会话名称
$ tmux switch -t <session-name>

重命名会话

tmux rename-session命令用于重命名会话。

1
$ tmux rename-session -t 0 <new-name>

上面命令将0号会话重命名。

会话快捷键

下面是一些会话相关的快捷键。

1
2
3
Ctrl+b d:分离当前会话。
Ctrl+b s:列出所有会话。
Ctrl+b $:重命名当前会话。

systemed

简介

Systemd是一个系统管理守护进程、工具和库的集合

很多时候在工作或者自己搭建服务的时候会遇到可能需要手动编写Systemed Service文件的情况。Systemed我们也常用来守护进程。这里对Service文件编写进行解析。

每个 Systemed Unit 都有对应的配置文件,它们分散在三个目录里。

/lib/systemd/system:系统默认的单元文件

/etc/systemd/system:用户安装的软件的单元文件

/usr/lib/systemd/system:用户自己定义的单元文件

Service文件中包含三个区块,分别是[Unit]、[Service]、[Install],下面对每个区块及其内参数进行说明:

[Unit]描述信息

该区块用于指定服务名称、服务文档、启动顺序、服务依赖等

  • Description:用于指定服务名称。

  • Documentation:用于指定服务文档,非必须项。

  • After:用于指定当前服务需要在那些服务启动后启动。

  • Before:用于指定当前服务需要在那些服务启动前启动。

  • Wants:用于指定哪些服务对于当前服务弱依赖,指定服务没有运行,不会影响当前服务执行。

  • Requires:用于指定哪些服务对于当前服务强依赖,指定服务没有运行,当前服务会启动失败。

  • BindsTo:与Requires类似,不同之处在于,指定服务停止运行,会导致当前服务停止运行。

  • Conflicts:指定服务与当前服务互斥运行,即指定服务不能与当前服务同时运行。

[Service]启动信息

  • Type:用于指定服务启动类型。

子项参数解析:

参数作用
simple(默认值)ExecStart启动的进程为该服务主进程。
exec与simple类似,不同之处在于,只有在该服务的主进程执行完成之后,systemd才认为该服务启动完成。 其他后继单元必须一直阻塞到此时间后才能继续启动
forking以fork()方式启动,启动时父进程将会退出,子进程将成为主进程
oneshot与simple类似,不同之处在于,只有在该服务的主进程退出之后,systemd才认为该服务启动完成,才能开始启动后继单元
dbus类似于simple,区别在于会等待D-Bus信号后才启动
notify类似于simple,启动结束后会发出通知信号,然后systemd才会启动其他服务
idle类似于simple,区别是idle要等到其他任务都执行完,才会启动该服务
  • EnvironmentFile:用于指定当前服务的环境参数文件

  • ExecStart:用于指定服务启动需要执行的命令

  • ExecReload:用于指定服务重启需要执行的命令

  • ExecStop:用于指定服务停止需要执行的命令

  • ExecStartPre:用于指定服务启动前执行的命令。

  • ExecStartPost:用于指定服务启动后执行的命令。

  • ExecStopPost:用于指定服务停止后执行的命令。

  • PrivateTmp:用于设置服务启动时挂载私有的/tmp、/var/tmp目录,此行为的目的在于提高服务进程临时文件的安全性。

[Install] 安装信息

该区块用于指定服务的用户信息,此部分的参数只在systemctl enable和systemctl disable时生效。

  • WantedBy:一个或多个 Target,当前服务enable时,符号链接会放入/etc/systemd/system目录下面以 Target 名 + .wants后缀构成的子目录中。

  • RequiredBy:一个或多个 Target,当前服务enable时,符号链接会放入/etc/systemd/system目录下面以 Target 名 + .required后缀构成的子目录中。

  • Alias:用于指定当前服务可用于启动的别名。

  • Also:用于指定当前服务enable时,会被同时enable的其他服务。

Target的含义是服务组,表示一组服务。WantedBy=multi-user.target指的是,sshd 所在的 Target 即multi-user.target

这个设置非常重要,因为执行systemctl enable sshd.service命令时,sshd.service的一个符号链接,就会放在/etc/systemd/system目录下面的multi-user.target.wants子目录之中。

Systemd 有默认的启动 Target。

1
2
$ systemctl get-default
multi-user.target

上面的结果表示,默认的启动 Target 是multi-user.target。在这个组里的所有服务,都将开机启动。这就是为什么systemctl enable命令能设置开机启动的原因。

使用 Target 的时候,systemctl list-dependencies命令和systemctl isolate命令也很有用。

1
2
3
4
5
6
# 查看 multi-user.target 包含的所有服务
$ systemctl list-dependencies multi-user.target

# 切换到另一个 target
# shutdown.target 就是关机状态
$ sudo systemctl isolate shutdown.target

一般来说,常用的 Target 有两个:一个是multi-user.target,表示多用户命令行状态;另一个是graphical.target,表示图形用户状态,它依赖于multi-user.target

常见命令

 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 重新加载配置文件
$ sudo systemctl daemon-reload

# 列出正在运行的 Unit
$ systemctl list-units

# 列出所有Unit,包括没有找到配置文件的或者启动失败的
$ systemctl list-units --all

# 列出所有正在运行的、类型为 service 的 Unit
$ systemctl list-units --type=service

# 显示系统状态
$ systemctl status

# 显示单个 Unit 的状态
$ sysystemctl status bluetooth.service

# 显示某个 Unit 是否正在运行
$ systemctl is-active application.service

# 显示某个 Unit 是否处于启动失败状态
$ systemctl is-failed application.service

# 显示某个 Unit 服务是否建立了启动链接
$ systemctl is-enabled application.service

# 立即启动一个服务
$ sudo systemctl start apache.service

# 立即停止一个服务
$ sudo systemctl stop apache.service

# 重启一个服务
$ sudo systemctl restart apache.service

# 杀死一个服务的所有子进程
$ sudo systemctl kill apache.service

# 开机自动执行该单元
$ systemctl enable [UnitName]

# 关闭开机自动执行
$ systemctl disable [UnitName]

# 重新加载一个服务的配置文件
$ sudo systemctl reload apache.service

# 显示某个 Unit 的所有底层参数
$ systemctl show httpd.service

# 显示某个 Unit 的指定属性的值
$ systemctl show -p CPUShares httpd.service

# 设置某个 Unit 的指定属性
$ sudo systemctl set-property httpd.service CPUShares=500

# 查看配置文件的内容。
$ systemctl cat atd.service

Systemd日志管理

Systemd 统一管理所有 Unit 的启动日志。带来的好处就是,可以只用journalctl一个命令,查看所有日志(内核日志和应用日志)。日志的配置文件是/etc/systemd/journald.conf。对日志的的便捷管理也方便我们的运维工作。

常见用法如下:

 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
# 查看所有日志(默认情况下 ,只保存本次启动的日志)
$ sudo journalctl

# 查看指定时间的日志
$ sudo journalctl --since="2012-10-30 18:17:16"
$ sudo journalctl --since "20 min ago"
$ sudo journalctl --since yesterday
$ sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00"
$ sudo journalctl --since 09:00 --until "1 hour ago"

# 显示尾部的最新10行日志
$ sudo journalctl -n

# 显示尾部指定行数的日志
$ sudo journalctl -n 20

# 查看某个 Unit 的日志
$ sudo journalctl -u nginx.service
$ sudo journalctl -u nginx.service --since today

# 实时滚动显示某个 Unit 的最新日志
$ sudo journalctl -u nginx.service -f

# 合并显示多个 Unit 的日志
$ journalctl -u nginx.service -u php-fpm.service --since today

一个非常好用的查看日志的手段

1
journalctl -xe |grep etcd

案例:编写unit文件,并注册到systemd服务中

首先编写shell脚本,执行vim start.sh

1
2
3
#!/bin/sh
cd /root/test
./test

其次,创建unit文件

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

Description=test

Requires=network-online.target

After=network.target

[Service]

Type=simple

ExecStart=/root/test/start.sh

ExecReload=/bin/kill -s HUP $MAINPID

ExecStop=/bin/kill -s QUIT $MAINPID

PrivateTmp=true

[Install]

WantedBy=multi-user.target

第三步,将我的unit文件注册到systemd中systemctl enable my.service

第四步,查看该服务的状态systemctl status my.service

参考文章:

Tmux 使用教程

systemd.unit 中文手册

Systemed Service文件解析

利用 Systemd 守护进程