简述

背景:

在systemd之前使用initd 管理服务(把启动脚本放在在/etc/init.d/下),但是有两个主要缺点:

  1. 线性(串行)启动导致启动时间过长
  2. initd只负责在系统启动时启动启动脚本,若要处理脚本状态需要自己实现,导致脚本复杂

systemd设计目标

  1. 为服务启动和管理提供全套解决方案
  2. 可完全取代init.d ,性能更强,功能更多

功能

系统管理

综合(systemctl))

1
2
3
4
5
6
7
8
9
#常用命令
# 重启
systemctl reboot
# 关闭电源
systemctl poweroff
# CPU暂停
systemctl halt
# 挂起
systemctl suspend

用户(loginctl)

1
2
3
4
5
6
7
8
9
10
list-sessions            List sessions
session-status [ID...] Show session status
show-session [ID...] Show properties of sessions or the manager
activate [ID] Activate a session
lock-session [ID...] Screen lock one or more sessions
unlock-session [ID...] Screen unlock one or more sessions
lock-sessions Screen lock all current sessions
unlock-sessions Screen unlock all current sessions
terminate-session ID... Terminate one or more sessions
kill-session ID... Send signal to processes of a session

时间(timedatectl)

1
2
3
4
5
6
7
8
status                   Show current time settings
show Show properties of systemd-timedated
set-time TIME Set system time
set-timezone ZONE Set system time zone
list-timezones Show known time zones
set-local-rtc BOOL Control whether RTC is in local time
set-ntp BOOL Enable or disable network time synchronization

本地化设置(localectl)

1
2
3
4
5
6
7
8
9
10
11
12
status                   Show current locale settings
set-locale LOCALE... Set system locale
list-locales Show known locales
set-keymap MAP [MAP] Set console and X11 keyboard mappings
list-keymaps Show known virtual console keyboard mappings
set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]
Set X11 and console keyboard mappings
list-x11-keymap-models Show known X11 keyboard mapping models
list-x11-keymap-layouts Show known X11 keyboard mapping layouts
list-x11-keymap-variants [LAYOUT]
Show known X11 keyboard mapping variants
list-x11-keymap-options Show known X11 keyboard mapping options

启动信息(systemd-analyze)

1
2
3
4
5
6
7
8
time                    Print time spent in the kernel
blame Print list of running units ordered by time to init
critical-chain Print a tree of the time critical chain of units
plot Output SVG graphic showing service initialization
dot Output dependency graph in dot(1) format
set-log-level LEVEL Set logging threshold for systemd
dump Output state serialization of service manager
verify FILE... Check unit files for correctness

主机信息(hostnamectl)

1
2
3
4
5
6
status                 Show current hostname settings
set-hostname NAME Set system hostname
set-icon-name NAME Set icon name for host
set-chassis NAME Set chassis type for host
set-deployment NAME Set deployment environment for host
set-location NAME Set location for host

资源管理

支持的资源

文件后缀就是资源名,例如service的后缀为.service

资源 功能
service 定义系统服务
socket 标识进程间通信使用的socket文件
target 模拟实现“运行级别”,多个 Unit 构成的一个组
device 定义内核识别的设备
snapshot 管理systemd快照
scope 非systemd启动的进程
timer 管理计划任务
slice 进程组
path 文件
swap 交换设备
mount 文件系统挂载点
automount 自动挂载点

命令

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
#基础命令
systemctl status # 状态
systemctl start # 开启
syttemctl stop # 关闭
systemctl restart # 重启
systemctl reload # 重载
systemctl kill # 杀死
systemctl enable # 开机启动
systemctl disable # 开机不启动
systemctl mask # 屏蔽
systemctl unmask # 不屏蔽
systemctl show # 查看配置
systemctl cat # 查看配置文件内容
# 查询命令
systemctl is-active
systemctl is-failed
systemctl is-enabled
# 列出依赖
systemctl list-dependencies
#查询列表
list-sockets
list-unit-files
list-jobs
list-timers
list-units

启动状态

static:不可以自己启动,可以被被其它的 enabled 的服务来唤醒。
mask:屏蔽掉了,除非unmask否则无论如何无法启动
enable:开机启动
disable:开机不启动

日志管理

1
2
3
4
5
6
7
8
9
10
11
12
13
-F --field=FIELD         List all values that a specified field takes
--new-id128 Generate a new 128-bit ID
--disk-usage Show total disk usage of all journal files
--vacuum-size=BYTES Reduce disk usage below specified size
--vacuum-time=TIME Remove journal files older than specified date
--flush Flush all journal data from /run into /var
--header Show journal header information
--list-catalog Show all message IDs in the catalog
--dump-catalog Show entries in the message catalog
--update-catalog Update the message catalog database
--setup-keys Generate a new FSS key pair
--verify Verify journal file consistency

怎么写一个Unit

最好的说明往往都是官网说明https://www.freedesktop.org/software/systemd/man/systemd.unit.html

1
2
3
4
5
6
[Unit]
属性=值
[Service]
属性=值
[Install]
属性=值

一般有 Unit(unit元数据)、Install(定义启动)、Service(配置)三个模块,很多情况下Install模块可省略

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
[Unit]
Description:简短描述
Documentation:文档地址
Requires:当前 Unit 依赖的其他 Unit,如果它们没有运行,当前 Unit 会启动失败
Wants:与当前 Unit 配合的其他 Unit,如果它们没有运行,当前 Unit 不会启动失败
BindsTo:与Requires类似,它指定的 Unit 如果退出,会导致当前 Unit 停止运行
Before:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之后启动
After:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之前启动
Conflicts:这里指定的 Unit 不能与当前 Unit 同时运行
Condition...:当前 Unit 运行必须满足的条件,否则不会运行
Assert...:当前 Unit 运行必须满足的条件,否则会报启动失败

[Service]
Type:定义启动时的进程行为。它有以下几种值。
Type=simple:默认值,执行ExecStart指定的命令,启动主进程
Type=forking:以 fork 方式从父进程创建子进程,创建后父进程会立即退出
Type=oneshot:一次性进程,Systemd 会等当前服务退出,再继续往下执行
Type=dbus:当前服务通过D-Bus启动
Type=notify:当前服务启动完毕,会通知Systemd,再继续往下执行
Type=idle:若有其他任务执行完毕,当前服务才会运行
Workingdirectory: 工作目录
ExecStart:启动当前服务的命令
ExecStartPre:启动当前服务之前执行的命令
ExecStartPost:启动当前服务之后执行的命令
ExecReload:重启当前服务时执行的命令
ExecStop:停止当前服务时执行的命令
ExecStopPost:停止当其服务之后执行的命令
RestartSec:自动重启当前服务间隔的秒数
Restart:定义何种情况 Systemd 会自动重启当前服务,可能的值包括always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog
TimeoutSec:定义 Systemd 停止当前服务之前等待的秒数
Environment:指定环境变量

[Install]
WantedBy:它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入/etc/systemd/system目录下面以 Target 名 + .wants后缀构成的子目录中
RequiredBy:它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放入/etc/systemd/system目录下面以 Target 名 + .required后缀构成的子目录中
Alias:当前 Unit 可用于启动的别名
Also:当前 Unit 激活(enable)时,会被同时激活的其他 Unit

一个简单的例子

  1. 首先我们用c实现一个简单的http helloworld

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string.h>

    #define PORT 2333
    #define BUFFER_SIZE 2048
    #define RESPONSE_HEADER "HTTP/1.1 200 OK\r\nConnection: close\r\nAccept-Ranges: bytes\r\nContent-Type: text/html\r\n\r\n"
    #define RESPONSE_BODY "Hello longtaoTechLife!"

    int handle(int conn){
    char buffer[BUFFER_SIZE];
    int len;
    bzero(buffer, BUFFER_SIZE);
    len = recv(conn, buffer, BUFFER_SIZE, 0);
    if (len <= 0 ) {
    printf ("recv len not right");
    return -1;}
    send(conn, RESPONSE_HEADER RESPONSE_BODY, sizeof(RESPONSE_HEADER RESPONSE_BODY), 0);
    close(conn);
    return 0;}

    int main(int argc,char *argv[]){
    int port = PORT;
    struct sockaddr_in clientSockAddr;
    struct sockaddr_in serverSockAddr;
    int listener = socket(AF_INET,SOCK_STREAM,0);
    int conn;
    int optval = 1;
    socklen_t len = sizeof(struct sockaddr_in);
    setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
    serverSockAddr.sin_family = AF_INET;
    serverSockAddr.sin_port = htons(port);
    serverSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(listener,(struct sockaddr *)&serverSockAddr,sizeof(serverSockAddr))==-1){
    printf("bind error!");
    return -1;}

    if(listen(listener, 10) < 0) {
    printf("listen failed!");
    return -1;}

    while(1){
    conn = accept(listener, (struct sockaddr*)&clientSockAddr, &len);
    if(conn < 0 || handle(conn) < 0){
    close(conn);
    continue;}}
    return 0;}
  2. 我们试一下正常运行,打印了我们期望的

    1
    2
    3
    4
    [root@172-20-59-110 ~]# ./hello.server&
    [1] 1460
    [root@172-20-59-110 ~]# curl -X GET localhost:2333
    Hello longtaoTechLife![root@172-20-59-110 ~]#
  3. 接下来我们写一个简单的systemd service 写入/etc/systemd/system/hello.service

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [Unit]
    Description=this is longtao's hello test

    [Service]
    Type=simple
    ExecStart=/root/hello.server #注意一定要写成绝对地址

    [Install]
    WantedBy=multi-user.target # 缺少此项无法使用systemctl enable/disable 功能
  4. 我们用systemctl status hello看一下状态

    1
    2
    3
    4
    5
    6
    [root@172-20-59-110 ~]# systemctl status hello
    ● hello.service - this is longtao's hello test
    Loaded: loaded (/etc/systemd/system/hello.service; static; vendor preset: disabled) #配置文件路径,是否enable等
    Active: inactive (dead) since 四 2021-07-15 18:14:23 CST; 18s ago # 运行状态,我们能看到未运行
    Process: 708 ExecStart=/root/hello.server
    Main PID: 708 (code=killed, signal=TERM) # 显示进程ID
  5. 我们启动服务systemctl status hello并打开开机启动 systemctl status hello,再看一下状态

    1
    2
    3
    4
    5
    6
    ● hello.service - this is longtao's hello test
    Loaded: loaded (/etc/systemd/system/hello.service; enabled; vendor preset: disabled) # 我们看到状态enbale开机启动设置成功了
    Active: active (running) since 四 2021-07-15 18:18:25 CST; 26s ago #运行状态为正在运行
    Main PID: 1328 (hello.server)
    CGroup: /system.slice/hello.service #CGroup下显示所有孜进程,CGroup常用于隔离资源,容器中常用,我们以后再说
    └─1328 /root/hello.server
  6. 这样我们就实现了一个简单的service,可以快乐的使用systemctl restart\stop\等管理我们的服务了。

优点(好的特性)

  1. 功能强大,使用便捷
  2. 启动速度快

特点(中性特性)

  1. systemd 取代了initd,成为了系统第一个进程(PID为1)

缺点(不好的特性)

  1. 体系庞大,使用起来非常复杂
  2. 与操作系统的其他模块耦合性过强,不符合Unix的KISS原则,下面是systemd架构图,可以看出需要很多模块支持
    img