最近用到的知识集合

来源:互联网 发布:颜体 行书 知乎 编辑:程序博客网 时间:2024/05/22 00:47

systemd详解

http://blog.csdn.net/frank_good/article/details/47724897

CentOS 7 使用systemd替换了SysV。Systemd目的是要取代Unix时代以来一直在使用的init系统,兼容SysV和LSB的启动脚本,而且够在进程启动过程中更有效地引导加载服务。

systemd的特性有:

  • 支持并行化任务
  • 同时采用socket式与D-Bus总线式激活服务;
  • 按需启动守护进程(daemon);
  • 利用 Linux 的 cgroups 监视进程;
  • 支持快照和系统恢复;
  • 维护挂载点和自动挂载点;
  • 各服务间基于依赖关系进行精密控制。

systemd基本工具
检视和控制systemd的主要命令是systemctl。该命令可用于查看系统状态和管理系统及服务。详见man 1 systemctl。

小贴士: 在 systemctl 参数中添加 -H <用户名>@<主机名> 可以实现对其他机器的远程控制。该过程使用ssh链接。

注意: systemadm 是 systemd 的官方图形前端。

分析系统状态
输出激活的单元:

$ systemctl

以下命令等效:

$ systemctl list-units

输出运行失败的单元:

$ systemctl --failed

所有可用的单元文件存放在 /usr/lib/systemd/system/ 和 /etc/systemd/system/ 目录(后者优先级更高)。查看所有已安装服务:

$ systemctl list-unit-files

使用单元
一个单元配置文件可以描述如下内容之一:系统服务(.service)、挂载点(.mount)、sockets(.sockets 、系统设备、交换分区/文件、启动目标(target)、文件系统路径、由 systemd 管理的计时器。详情参阅 man 5 systemd.unit.

使用 systemctl 控制单元时,通常需要使用单元文件的全名,包括扩展名(例如 sshd.service)。但是有些单元可以在systemctl中使用简写方式。

  • 如果无扩展名,systemctl 默认把扩展名当作 .service。例如 netcfg 和 netcfg.service 是等价的。
  • 挂载点会自动转化为相应的 .mount 单元。例如 /home 等价于 home.mount。
  • 设备会自动转化为相应的 .device 单元,所以 /dev/sda2 等价于 dev-sda2.device。

立即激活单元:

# systemctl start <单元>

立即停止单元:

# systemctl stop <单元>

重启单元:

# systemctl restart <单元>

命令单元重新读取配置:

# systemctl reload <单元>

输出单元运行状态:

$ systemctl status <单元>

检查单元是否配置为自动启动:

$ systemctl is-enabled <单元>

开机自动激活单元:

# systemctl enable <单元>

注意: 如果服务没有Install段落,一般意味着应该通过其它服务自动调用它们。如果真的需要手动安装,可以直接连接服务,如下(将foo替换为真实的服务名):

# ln -s /usr/lib/systemd/system/foo.service /etc/systemd/system/graphical.target.wants/

取消开机自动激活单元:

# systemctl disable <单元>

显示单元的手册页(必须由单元文件提供):

# systemctl help <单元>

重新载入 systemd,扫描新的或有变动的单元:

# systemctl daemon-reload

电源管理
安装 polkit 后才可使用电源管理。

如果你正登录在一个本地的systemd-logind用户会话,且当前没有其它活动的会话,那么以下命令无需root权限即可执行。否则(例如,当前有另一个用户登录在某个tty),systemd 将会自动请求输入root密码。

重启:

$ systemctl reboot

退出系统并停止电源:

$ systemctl poweroff

待机:

$ systemctl suspend

休眠:

$ systemctl hibernate

混合休眠模式(同时休眠到硬盘并待机):

$ systemctl hybrid-sleep

原生 systemd 配置文件

注意: 可能需要手动创建某些文件。所有文件的权限都是644,属主 root,属组 root。

虚拟控制台
可以用/etc/vconsole.conf 文件或者localectl 配置虚拟控制台,包括键盘布局和控制台字体。详情请访问 控制台字体 和 键盘布局。

硬件时钟
systemd 默认硬件时钟为协调世界时(UTC)。

小贴士: 推荐使用NTP服务来在线同步硬件时钟。

硬件时钟设定为地方时
将硬件时钟配置为地方时(不建议):

# timedatectl set-local-rtc true

重新调整为 UTC:

# timedatectl set-local-rtc false

如果设置成本地时间,处理夏令时有些麻烦。如果夏令时调整发生在关机时,下次启动时时间会出现问题。最新的内核直接从实时时钟芯片(RTC)读取时间,不使用 hwclock,内核把从 RTC 读取的时间当作 UTC 处理。所以如果硬件时间是地方时,系统启动一开始识别的时间是错误的,之后很快会进行矫正。这可能导致一些问题(尤其是时间倒退时)。

如果同时安装了 Windows 操作系统(默认使用地方时),那么一般 RTC 会被设置为地方时。Windows 其实也能处理 UTC,需要修改注册表。建议让 Windows 使用 UTC,而非让 Linux 使用地方时。Windows 使用 UTC 后,请记得禁用 Windows 的时间同步功能,以防 Windows 错误设置硬件时间。如上文所说,Linux 可以使用NTP服务来在线同步硬件时钟。

内核模块
请访问Kernel modules

文件系统挂载
默认行为是:在启动一个需要挂载特定分区的服务之前,系统自动检查并挂载分区。/etc/fstab 中设定的网络文件系统(如 NFS、Samba)无需配置即可正常工作,systemd 将确保网络文件系统在网络链接就绪后挂载。

详情参阅:man 5 systemd.mount。

LVM
如果装有不通过 initramfs 激活的LVM卷,则需启动 lvm-monitoring 服务(由 lvm2 软件包提供):

# systemctl enable lvm-monitoring

ACPI 电源管理
参阅 Power Management

临时文件
/usr/lib/tmpfiles.d/ 和 /etc/tmpfiles.d/ 中的文件描述了 systemd-tmpfiles 如何创建、清理、删除临时文件和目录,这些文件和目录通常存放在 /run 和 /tmp 中。配置文件名称为 /etc/tmpfiles.d/<program>.conf。此处的配置能覆盖 /usr/lib/tmpfiles.d/ 目录中的同名配置。

临时文件通常和服务文件同时提供,以生成守护进程需要的文件和目录。例如 Samba 服务需要目录 /run/samba 存在并设置正确的权限位,就象这样:

/usr/lib/tmpfiles.d/samba.confD /run/samba 0755 root root

此外,临时文件还可以用来在开机时向特定文件写入某些内容。比如,要禁止系统从USB设备唤醒,利用旧的 /etc/rc.local 可以用 echo USBE > /proc/acpi/wakeup,而现在可以这么做:

/etc/tmpfiles.d/disable-usb-wake.confw /proc/acpi/wakeup - - - - USBE

详情参见 man 5 tmpfiles.d。

注意: 该方法不能向 /sys 中的配置文件添加参数,因为 systemd-tmpfiles-setup 有可能在相关模块加载前运行。这种情况下,需要首先通过 modinfo <模块名> 确认需要的参数,并在 /etc/modprobe.d 下的一个文件中设置改参数。另外,还可以使用 udev 规则,在设备就绪时设置相应属性。

自己编写 .service 文件
systemd 的单元文件是受 XDG Desktop Entry .desktop 文件启发而产生,而最初起源是 Windows 下的 .ini 文件。

处理依赖关系
使用systemd时,可通过正确编写单元配置文件来解决其依赖关系。典型的情况是,单元A要求单元B在A启动之前运行。在此情况下,向单元A配置文件中的 [Unit] 段添加 Requires=B 和 After=B 即可。若此依赖关系是可选的,可添加 Wants=B 和 After=B。请注意 Wants= 和 Requires= 并不意味着 After=,即如果 After= 选项没有制定,这两个单元将被并行启动。

依赖关系通常被用在服务(service)而不是目标(target)上。例如, network.target 一般会被某个配置网络接口的服务引入,所以,将自定义的单元排在该服务之后即可,因为 network.target 已经启动。

启动方式
编写自定义的service文件时,可以选择几种不同的服务启动方式。启动方式可通过配置文件 [Service] 段中的 Type= 参数进行设置。具体的参数说明请参阅 man systemd.service 。

  • Type=simple(默认值):systemd认为该服务将立即启动。服务进程不会fork。如果该服务要启动其他服务,不要使用此类型启动,除非该服务是socket激活型。
  • Type=forking:systemd认为当该服务进程fork,且父进程退出后服务启动成功。对于常规的守护进程(daemon),除非你确定此启动方式无法满足需求,使用此类型启动即可。使用此启动类型应同时指定 PIDFile=,以便systemd能够跟踪服务的主进程。
  • Type=oneshot:这一选项适用于只执行一项任务、随后立即退出的服务。可能需要同时设置 RemainAfterExit=yes 使得 systemd 在服务进程退出之后仍然认为服务处于激活状态。
  • Type=notify:与 Type=simple 相同,但约定服务会在就绪后向 systemd 发送一个信号。这一通知的实现由 libsystemd-daemon.so 提供。
  • Type=dbus:若以此方式启动,当指定的 BusName 出现在DBus系统总线上时,systemd认为服务就绪。

修改现存单元文件
要更改由软件包提供的单元文件,先创建名为 /etc/systemd/system/<单元名>.d/ 的目录(如 /etc/systemd/system/httpd.service.d/),然后放入 *.conf 文件,其中可以添加或重置参数。这里设置的参数优先级高于原来的单元文件。例如,如果想添加一个额外的依赖,创建这么一个文件即可:

/etc/systemd/system/<unit>.d/customdependency.conf[Unit]Requires=<新依赖>After=<新依赖>

然后运行以下命令使更改生效:

# systemctl daemon-reload# systemctl restart <单元>

此外,把旧的单元文件从 /usr/lib/systemd/system/ 复制到 /etc/systemd/system/,然后进行修改,也可以达到同样效果。在 /etc/systemd/system/ 目录中的单元文件的优先级总是高于 /usr/lib/systemd/system/ 目录中的同名单元文件。注意,当 /usr/lib/ 中的单元文件因软件包升级变更时,/etc/ 中自定义的单元文件不会同步更新。此外,你还得执行 systemctl reenable <unit>,手动重新启用该单元。因此,建议使用前面一种利用 *.conf 的方法。

小贴士: 可以用 systemd-delta 命令来查看哪些单元文件被覆盖、哪些被修改。

单元配置文件的 vim 语法高亮支持
可从官方仓库安装 vim-systemd 软件包,使 unit 配置文件在 Vim 下支持语法高亮。

目标(target)
启动级别(runlevel)是一个旧的概念。现在,systemd 引入了一个和启动级别功能相似又不同的概念——目标(target)。不像数字表示的启动级别,每个目标都有名字和独特的功能,并且能同时启用多个。一些目标继承其他目标的服务,并启动新服务。systemd 提供了一些模仿 sysvinit 启动级别的目标,仍可以使用旧的 telinit 启动级别 命令切换。
获取当前目标

不要使用 runlevel 命令了:

$ systemctl list-units --type=target

创建新目标
在 Fedora 中,启动级别 0、1、3、5、6 都被赋予特定用途,并且都对应一个 systemd 的目标。然而,没有什么很好的移植用户定义的启动级别(2、4)的方法。要实现类似功能,可以以原有的启动级别为基础,创建一个新的目标 /etc/systemd/system/<新目标>(可以参考 /usr/lib/systemd/system/graphical.target),创建 /etc/systemd/system/<新目标>.wants 目录,向其中加入额外服务的链接(指向 /usr/lib/systemd/system/ 中的单元文件)。

目标表

SysV 启动级别Systemd 目标注释0runlevel0.target, poweroff.target中断系统(halt)1, s, singlerunlevel1.target, rescue.target单用户模式2, 4runlevel2.target, runlevel4.target, multi-user.target用户自定义启动级别,通常识别为级别3。3runlevel3.target, multi-user.target多用户,无图形界面。用户可以通过终端或网络登录。5runlevel5.target, graphical.target多用户,图形界面。继承级别3的服务,并启动图形界面服务。6runlevel6.target, reboot.target重启emergencyemergency.target急救模式(Emergency shell)

切换启动级别/目标
systemd 中,启动级别通过“目标单元”访问。通过如下命令切换:

# systemctl isolate graphical.target

该命令对下次启动无影响。等价于telinit 3 或 telinit 5。

修改默认启动级别/目标
开机启动进的目标是 default.target,默认链接到 graphical.target (大致相当于原来的启动级别5)。可以通过内核参数更改默认启动级别:

小贴士: 可以省略扩展名 .target。

  •  systemd.unit=multi-user.target (大致相当于级别3)
  •  systemd.unit=rescue.target (大致相当于级别1)

另一个方法是修改 default.target。可以通过 systemctl 修改它:

# systemctl enable multi-user.target

命令执行情况由 systemctl 显示:链接 /etc/systemd/system/default.target 被创建,指向新的默认启动级别。该方法当且仅当目标配置文件中有以下内容时有效:

[Install]Alias=default.target

目前,multi-user.target、graphical.target 都包含这段内容。

日志
systemd提供了自己日志系统(logging system),称为 journal. 使用 systemd 日志,无需额外安装日志服务(syslog)。读取日志的命令:

# journalctl

默认情况下(当 Storage= 在文件 /etc/systemd/journald.conf 中被设置为 auto),日志记录将被写入 /var/log/journal/。该目录是 systemd 软件包的一部分。若被删除,systemd 不会自动创建它,直到下次升级软件包时重建该目录。如果该目录缺失,systemd 会将日志记录写入 /run/systemd/journal。这意味着,系统重启后日志将丢失。

过滤输出
journalctl可以根据特定字段过滤输出,例如:

显示本次启动后的所有日志:

# journalctl -b

不过,一般大家更关心的不是本次启动后的日志,而是上次启动时的(例如,刚刚系统崩溃了)。目前还没有这项功能,正在 systemd-devel@lists.freedesktop.org 讨论中。

目前的折中方案是:

# journalctl --since=today | tac | sed -n '/-- Reboot --/{n;:r;/-- Reboot --/q;p;n;b r}' | tac

以上命令输出本日内的所有启动信息。但要注意,如果日志很多,该命令执行时间会比较漫长。

动态跟踪最新信息:

# journalctl -f

显示特定程序的所有消息:

 # journalctl /usr/lib/systemd/systemd

显示特定进程的所有消息:

# journalctl _PID=1

显示指定单元的所有消息:

# journalctl -u netcfg

详情参阅man journalctl、man systemd.journal-fields

日志大小限制
如果按上面的操作保留日志的话,默认日志最大限制为所在文件系统容量的 10%,即:如果 /var/log/journal 储存在 50GiB 的根分区中,那么日志最多存储 5GiB 数据。可以修改 /etc/systemd/journald.conf 中的 SystemMaxUse 来指定该最大限制。如限制日志最大 50MiB:

SystemMaxUse=50M

详情参见 man journald.conf.

配合syslog使用
systemd提供了 socket /run/systemd/journal/syslog,以兼容传统日志服务。所有系统信息都会被传入。要使传统日志服务工作,需要让服务链接该 socket,而非 /dev/log(官方说明)。Arch 软件仓库中的 syslog-ng 已经包含了需要的配置。

设置开机启动 syslog-ng:

 # systemctl enable syslog-ng

疑难解答
关机/重启十分缓慢
如果关机特别慢(甚至跟死机了一样),很可能是某个拒不退出的服务在作怪。systemd 会等待一段时间,然后再尝试杀死它。请阅读这篇文章,确认你是否是该问题受害者。

短时进程无日志记录
若 journalctl -u foounit.service 没有显示某个短时进程的任何输出,那么改用 PID 试试。例如,若 systemd-modules-load.service 执行失败,那么先用 systemctl status systemd-modules-load 查询其 PID(比如是123),然后检索该 PID 相关的日志 journalctl -b _PID=123。运行时进程的日志元数据(诸如 _SYSTEMD_UNIT 和 _COMM)被乱序收集在 /proc 目录。要修复该问题,必须修改内核,使其通过套接字连接来提供上述数据,该过程类似于 SCM_CREDENTIALS。

诊断启动问题
使用如下内核参数引导: systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M

禁止在程序崩溃时转储内存
要使用老的内核转储,创建下面文件:

/etc/sysctl.d/49-coredump.confkernel.core_pattern = corekernel.core_uses_pid = 0

然后运行:

# /usr/lib/systemd/systemd-sysctl

同样可能需要执行“unlimit”设置文件大小:

$ ulimit -c unlimited

Socket activation in systemd

http://www.300168.com/yunwei/show-8464.html
核心提示:Introduction systemd 为了加快系统的启动速度,使用 socket activation 的方式让所有系统服务并发启动。 socket activation 的思想由来所久, inetd 使用它来实现按需启动网络服务。socket activation 的核心在于将创建 listen socket 的过程从 service daem

Introduction

systemd 为了加快系统的启动速度,使用 socket activation 的方式让所有系统服务并发启动。 socket activation 的思想由来所久, inetd 使用它来实现按需启动网络服务。

socket activation 的核心在于将创建 listen socket 的过程从 service daemon 移到systemd,即使该服务本身没有启动,其它依赖的服务也可以连接 listen socket ,然后 systemd 创建服务进程,并将 listen socket 转给该daemon进程,由后者处理 listen socket 的各种请求。这样使得所有的服务守护进程都可以同时启动。

Socket activation makes it possible to start all four services completely simultaneously, without any kind of ordering. Since the creation of the listening sockets is moved outside of the daemons themselves we can start them all at the same time, and they are able to connect to each other’s sockets right-away.

Socket activation in systemd

Write socket activation daemon

使用 socket activation 的服务,必须从 systemd 接收socket,而不是自己创建socket.

A service capable of socket activation must be able to receive its preinitialized sockets from systemd, instead of creating them internally.

  • NON-SOCKET-ACTIVATABLE SERVICE

非 socket activation 的服务一般是自己创建socket:

/* Source Code Example #1: ORIGINAL, NOT SOCKET-ACTIVATABLE SERVICE */...union {        struct sockaddr sa;        struct sockaddr_un un;} sa;int fd;fd = socket(AF_UNIX, SOCK_STREAM, 0);if (fd < 0) {        fprintf(stderr, "socket(): %m\n");        exit(1);}memset(&sa, 0, sizeof(sa));sa.un.sun_family = AF_UNIX;strncpy(sa.un.sun_path, "/run/foobar.sk", sizeof(sa.un.sun_path));if (bind(fd, &sa.sa, sizeof(sa)) < 0) {        fprintf(stderr, "bind(): %m\n");        exit(1);}if (listen(fd, SOMAXCONN) < 0) {        fprintf(stderr, "listen(): %m\n");        exit(1);}...

这种方式下,其它依赖的服务必须在该daemon进程启动后之能访问该服务。

  • SOCKET-ACTIVATABLE SERVICE
/* Source Code Example #2: UPDATED, SOCKET-ACTIVATABLE SERVICE */...#include "sd-daemon.h"...int fd;if (sd_listen_fds(0) != 1) { ///使用systemd创建的socket        fprintf(stderr, "No or too many file descriptors received.\n");        exit(1);}fd = SD_LISTEN_FDS_START + 0;...

这种方式下,传统的启动服务服务方式将不再可用。为了兼容两种方式,可以使用下面的方法:

  • SOCKET-ACTIVATABLE SERVICE WITH COMPATIBILITY
/* Source Code Example #3: UPDATED, SOCKET-ACTIVATABLE SERVICE WITH COMPATIBILITY */...#include "sd-daemon.h"...int fd, n;n = sd_listen_fds(0);if (n > 1) {        fprintf(stderr, "Too many file descriptors received.\n");        exit(1);} else if (n == 1)        fd = SD_LISTEN_FDS_START + 0;else {        union {                struct sockaddr sa;                struct sockaddr_un un;        } sa;        fd = socket(AF_UNIX, SOCK_STREAM, 0);        if (fd < 0) {                fprintf(stderr, "socket(): %m\n");                exit(1);        }        memset(&sa, 0, sizeof(sa));        sa.un.sun_family = AF_UNIX;        strncpy(sa.un.sun_path, "/run/foobar.sk", sizeof(sa.un.sun_path));        if (bind(fd, &sa.sa, sizeof(sa)) < 0) {                fprintf(stderr, "bind(): %m\n");                exit(1);        }        if (listen(fd, SOMAXCONN) < 0) {                fprintf(stderr, "listen(): %m\n");                exit(1);        }}...

完整程序参考 这里 。另外, 这里 还有一个Go语言的示例。

  • Enable service in systemd

创建socket unit file:

# cat /etc/systemd/system/foobar.socket [Socket]ListenStream=/run/foobar.sk[Install]WantedBy=sockets.target

创建对应的service file:

# cat /etc/systemd/system/foobar.service [Service]ExecStart=/usr/local/bin/foobard

启动socket:

# systemctl enable foobar.socket# systemctl start foobar.socket# systemctl status foobar.socket● foobar.socket   Loaded: loaded (/etc/systemd/system/foobar.socket; enabled; vendor preset: disabled)   Active: active (listening) since 四 2017-01-05 18:59:41 CST; 31s ago   Listen: /run/foobar.sk (Stream)1月 05 18:59:41 centos7 systemd[1]: Listening on foobar.socket.1月 05 18:59:41 centos7 systemd[1]: Starting foobar.socket.# lsof /run/foobar.sk COMMAND PID USER   FD   TYPE             DEVICE SIZE/OFF  NODE NAMEsystemd   1 root   28u  unix 0xffff88002db53400      0t0 29058 /run/foobar.sk

可以看到 systemd 创建了对应的socket。但此时, foobard 进程并没有启动。

当我们连接 /run/foobar.sk 时, foobard 进程就会被 systemd 拉起:

# socat - unix-connect:/run/foobar.sk hello worldhello worldagainagain# ps -ef|grep foobroot      3589  3338  0 19:01 pts/1    00:00:00 socat - unix-connect:/run/foobar.skroot      3590     1  0 19:01 ?        00:00:00 /usr/local/bin/foobard

Internal

systemd 在启动服务进程前,会设置环境变量 LISTEN_PID 和 LISTEN_FDS :

static int build_environment(                const ExecContext *c,                unsigned n_fds,                usec_t watchdog_usec,                const char *home,                const char *username,                const char *shell,                char ***ret) {        _cleanup_strv_free_ char **our_env = NULL;        unsigned n_env = 0;        char *x;        assert(c);        assert(ret);        our_env = new0(char*, 10);        if (!our_env)                return -ENOMEM;        if (n_fds > 0) {                if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0)                        return -ENOMEM;                our_env[n_env++] = x;                if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0)                        return -ENOMEM;                our_env[n_env++] = x;        }...

服务进程通过 sd_listen_fds 获取对应的环境变量:

_public_ int sd_listen_fds(int unset_environment) {        const char *e;        int n, r, fd;        pid_t pid;        e = getenv("LISTEN_PID");        if (!e) {                r = 0;                goto finish;        }        r = parse_pid(e, &pid);        if (r < 0)                goto finish;        /* Is this for us? */        if (getpid() != pid) {                r = 0;                goto finish;        }        e = getenv("LISTEN_FDS");        if (!e) {                r = 0;                goto finish;        }

systemd.socket 中文手册


http://www.jinbuguo.com/systemd/systemd.socket.html


Socket通信原理和实践  ,这个文章讲的比较通俗易懂

http://blog.csdn.net/jiajia4336/article/details/8798421

原创粉丝点击