在我们打开Linux电脑的电源后第一个启动的进程就是init。分配给init进程的PID是1。它是系统其他所有进程的父进程。当一台Linux电脑启动后,处理器会先在系统存储中查找BIOS,之后BIOS会检测系统资源然后找到第一个引导设备,通常为硬盘,然后会查找硬盘的主引导记录(MBR),然后加载到内存中并把控制权交给它,以后的启动过程就由MBR控制。
主引导记录会初始化引导程序(Linux上有两个著名的引导程序,GRUB和LILO,80%的Linux系统在用GRUB引导程序),这个时候GRUB或LILO会加载内核模块。内核会马上查找/sbin下的“init”程序并执行它。从这里开始init成为了Linux系统的父进程。init读取的第一个文件是/etc/inittab,通过它init会确定我们Linux操作系统的运行级别。它会从文件/etc/fstab里查找分区表信息然后做相应的挂载。然后init会启动/etc/init.d里指定的默认启动级别的所有服务/脚本。所有服务在这里通过init一个一个被初始化。在这个过程里,init每次只启动一个服务,所有服务/守护进程都在后台执行并由init来管理。
关机过程差不多是相反的过程,首先init停止所有服务,最后阶段会卸载文件系统。
以上提到的启动过程有一些不足的地方。而用一种更好的方式来替代传统init的需求已经存在很长时间了。也产生了许多替代方案。其中比较著名的有Upstart,Epoch,Muda和Systemd。而Systemd获得最多关注并被认为是目前最佳的方案。
开发Systemd的主要目的就是减少系统引导时间和计算开销。Systemd(系统管理守护进程),最开始以GNU GPL协议授权开发,现在已转为使用GNU LGPL协议,它是如今讨论最热烈的引导和服务管理程序。如果你的Linux系统配置为使用Systemd引导程序,它取替传统的SysV init,启动过程将交给systemd处理。Systemd的一个核心功能是它同时支持SysV init的后开机启动脚本。
Systemd引入了并行启动的概念,它会为每个需要启动的守护进程建立一个套接字,这些套接字对于使用它们的进程来说是抽象的,这样它们可以允许不同守护进程之间进行交互。Systemd会创建新进程并为每个进程分配一个控制组(cgroup)。处于不同控制组的进程之间可以通过内核来互相通信。systemd处理开机启动进程的方式非常漂亮,和传统基于init的系统比起来优化了太多。让我们看下Systemd的一些核心功能。
· 和init比起来引导过程简化了很多
· Systemd支持并发引导过程从而可以更快启动
· 通过控制组来追踪进程,而不是PID
· 优化了处理引导过程和服务之间依赖的方式
· 支持系统快照和恢复
· 监控已启动的服务;也支持重启已崩溃服务
· 包含了systemd-login模块用于控制用户登录
· 支持加载和卸载组件
· 低内存使用痕迹以及任务调度能力
· 记录事件的Journald模块和记录系统日志的syslogd模块
Systemd同时也清晰地处理了系统关机过程。它在/usr/lib/systemd/目录下有三个脚本,分别叫systemd-halt.service,systemd-poweroff.service,systemd-reboot.service。这几个脚本会在用户选择关机,重启或待机时执行。在接收到关机事件时,systemd首先卸载所有文件系统并停止所有内存交换设备,断开存储设备,之后停止所有剩下的进程。
监视和控制systemd的主要命令是systemctl。该命令可用于查看系统状态和管理系统及服务。详见man1 systemctl。
注意:
§ 在 systemctl 参数中添加 -H <用户名>@<主机名> 可以实现对其他机器的远程控制。该过程使用ssh链接。
§ systemadm是systemd 的官方图形前端
输出激活的单元:
1. $ systemctl
以下命令等效:
1. $ systemctl list-units
输出运行失败的单元:
1. $ systemctl --failed
所有可用的单元文件存放在 /usr/lib/systemd/system/ 和/etc/systemd/system/ 目录(后者优先级更高)。查看所有已安装服务:
1. $ systemctl list-unit-files
一个单元配置文件可以描述如下内容之一:系统服务(.service)、挂载点(.mount)、sockets(.sockets) 、系统设备(.device)、交换分区(.swap)、文件路径(.path)、启动目标(.target)、由 systemd 管理的计时器(.timer)。详情参阅 man 5 systemd.unit。
使用 systemctl 控制单元时,通常需要使用单元文件的全名,包括扩展名(例如sshd.service)。但是有些单元可以在systemctl中使用简写方式。
§ 如果无扩展名,systemctl 默认把扩展名当作 .service。例如 netcfg 和 netcfg.service 是等价的。
§ 挂载点会自动转化为相应的 .mount 单元。例如 /home 等价于 home.mount。
§ 设备会自动转化为相应的 .device 单元,所以 /dev/sda2 等价于 dev-sda2.device。
注: 有一些单元的名称包含一个 @ 标记, (e.g. name@string.service): 这意味着它是模板单元name@.service 的一个 实例。 string 被称作实例标识符, 在 systemctl 调用模板单元时,会将其当作一个参数传给模板单元,模板单元会使用这个传入的参数代替模板中的 %I 指示符。 在实例化之前,systemd 会先检查 name@string.suffix 文件是否存在(如果存在,应该就是直接使用这个文件,而不是模板实例化了)。大多数情况下,包换 @ 标记都意味着这个文件是模板。如果一个模板单元没有实例化就调用,该调用会返回失败,因为模板单元中的 %I 指示符没有被替换。
立即激活单元:
1. # systemctl start <单元>
立即停止单元:
1. # systemctl stop <单元>
重启单元:
1. # systemctl restart <单元>
命令单元重新读取配置:
1. # systemctl reload <单元>
输出单元运行状态:
1. $ systemctl status <单元>
检查单元是否配置为自动启动:
1. $ systemctl is-enabled <单元>
开机自动激活单元:
1. # systemctl enable <单元>
注意: 如果服务没有Install段落,一般意味着应该通过其它服务自动调用它们。如果真的需要手动安装,可以直接连接服务,如下(将foo替换为真实的服务名):
1. # ln -s /usr/lib/systemd/system/foo.service /etc/systemd/system/graphical.target.wants/
取消开机自动激活单元:
1. # systemctl disable <单元>
显示单元的手册页(必须由单元文件提供):
1. # systemctl help <单元>
重新载入 systemd,扫描新的或有变动的单元:
1. # systemctl daemon-reload
安装 polkit 后才可使用电源管理。
如果你正登录在一个本地的systemd-logind用户会话,且当前没有其它活动的会话,那么以下命令无需root权限即可执行。否则(例如,当前有另一个用户登录在某个tty),systemd 将会自动请求输入root密码。
重启:
1. $ systemctl reboot
退出系统并停止电源:
1. $ systemctl poweroff
待机:
1. $ systemctl suspend
休眠:
1. $ systemctl hibernate
混合休眠模式(同时休眠到硬盘并待机):
1. $ systemctl hybrid-sleep
systemd单元文件的语法来源于 XDG桌面入口配置文件.desktop文件,最初的源头则是Microsoft Windows的.ini文件。单元文件可以从两个地方加载,优先级从低到高分别是:
§ /usr/lib/systemd/system/: 软件包安装的单元
§ /etc/systemd/system/: 系统管理员安装的单元
注意: 当systemd运行在用户模式下时,使用的加载路径是完全不同的。
单元文件的语法,可以参考系统已经安装的单元,也可以参考man systemd.service中的EXAMPLES章节。
小贴士: 以 # 开头的注释可能也能用在 unit-files 中, 但是只能在新行中使用。 不要在 systemd 的参数后面使用行末注释, 否则 unit 将会启动失败。
使用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= 参数进行设置。
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认为服务就绪。
Type=idle: systemd会等待所有任务(Jobs)处理完成后,才开始执行idle类型的单元。除此之外,其他行为和Type=simple 类似。
type的更多解释可以参考 systemd.service(5)。
要更改由软件包提供的单元文件,先创建名为 /etc/systemd/system/<单元名>.d/ 的目录(如 /etc/systemd/system/httpd.service.d/),然后放入*.conf 文件,其中可以添加或重置参数。这里设置的参数优先级高于原来的单元文件。例如,如果想添加一个额外的依赖,创建这么一个文件即可:
/etc/systemd/system/<unit>.d/customdependency.conf
[Unit]
Requires=<新依赖>
After=<新依赖>
其它举例,
/etc/systemd/system/unit.d/customexec.conf
[Service]
ExecStartExecStart=
ExecStart=new command
想知道为什么修改 ExecStart 前必须将其置空
下面是自动重启服务的一个例子:
/etc/systemd/system/unit.d/restart.conf
[Service]
Restart=always
RestartSec=30
然后运行以下命令使更改生效:
# systemctl daemon-reload
# systemctl restart <单元>
此外,把旧的单元文件从 /usr/lib/systemd/system/ 复制到/etc/systemd/system/,然后进行修改,也可以达到同样效果。在/etc/systemd/system/ 目录中的单元文件的优先级总是高于/usr/lib/systemd/system/ 目录中的同名单元文件。注意,当 /usr/lib/ 中的单元文件因软件包升级变更时,/etc/ 中自定义的单元文件不会同步更新。此外,你还得执行 systemctlreenable <unit>,手动重新启用该单元。因此,建议使用前面一种利用 *.conf 的方法。
小贴士: 用 systemd-delta 命令来查看哪些单元文件被覆盖、哪些被修改。系统维护的时候需要及时了解哪些单元已经有了更新
单元配置文件的 vim 语法高亮支持
可从官方仓库安装 vim-systemd 软件包,使 unit 配置文件在 Vim 下支持语法高亮。
启 动级别(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 目标 | 注释 |
0 | runlevel0.target, poweroff.target | 中断系统(halt) |
1, s, single | runlevel1.target, rescue.target | 单用户模式 |
2, 4 | runlevel2.target, runlevel4.target, multi-user.target | 用户自定义启动级别,通常识别为级别3。 |
3 | runlevel3.target, multi-user.target | 多用户,无图形界面。用户可以通过终端或网络登录。 |
5 | runlevel5.target, graphical.target | 多用户,图形界面。继承级别3的服务,并启动图形界面服务。 |
6 | runlevel6.target, reboot.target | 重启 |
emergency | emergency.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)
可以通过systemctl 修改它:
# systemctl enable multi-user.target
命令执行情况由 systemctl 显示:链接/etc/systemd/system/default.target 被创建,指向新的默认启动级别。该方法当且仅当目标配置文件中有以下内容时有效:
[Install]
Alias=default.target
目前,multi-user.target、graphical.target 都包含这段内容。
/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.conf
D /run/samba 0755 root root
此外,临时文件还可以用来在开机时向特定文件写入某些内容。比如,要禁止系统从USB设备唤醒,利用旧的 /etc/rc.local 可以用 echo USBE > /proc/acpi/wakeup,而现在可以这么做:
/etc/tmpfiles.d/disable-usb-wake.conf
w /proc/acpi/wakeup - - - - USBE
详情参见 man 5 tmpfiles.d。
注意: 该方法不能向 /sys 中的配置文件添加参数,因为 systemd-tmpfiles-setup 有可能在相关模块加载前运行。这种情况下,需要首先通过 modinfo <模块名> 确认需要的参数,并在 /etc/modprobe.d 下的一个文件中设置改参数。另外,还可以使用 udev 规则,在设备就绪时设置相应属性。
定时器是以 .timer 为后缀的配置文件,记录由system的里面由时间触发的动作, 定时器可以替代 cron 的大部分功能。
systemd提供了自己日志系统(logging system),称为 journal. 使用 systemd 日志,无需额外安装日志服务(syslog)。读取日志的命令:
# journalctl
默认情况下(当 Storage= 在文件 /etc/systemd/journald.conf 中被设置为 auto),日志记录将被写入 /var/log/journal/。该目录是 systemd 软件包的一部分。若被删除,systemd 不会自动创建它,直到下次升级软件包时重建该目录。如果该目录缺失,systemd 会将日志记录写入 /run/systemd/journal。这意味着,系统重启后日志将丢失。
Tip: 如果 /var/log/journal/ 位于btrfs 文件系统,应该考虑对这个目录禁用写入时复制
journalctl可以根据特定字段过滤输出,例如:
显示本次启动后的所有日志:
# journalctl -b
不过,一般大家更关心的不是本次启动后的日志,而是上次启动时的(例如,刚刚系统崩溃了)。可以使用 -b 参数:
journalctl -b -0 显示本次启动的信息
journalctl -b -1 显示上次启动的信息
journalctl -b -2 显示上上次启动的信息 journalctl -b -2
Show all messages from date (and optional time):
# journalctl --since="2012-10-3018:17:16"
Show all messages since 20 minutes ago:
# journalctl --since "20 min ago"
显示最新信息
# journalctl -f
显示特定程序的所有消息:
# journalctl /usr/lib/systemd/systemd
显示特定进程的所有消息:
# journalctl _PID=1
显示指定单元的所有消息:
# journalctl -u netcfg
Show kernel ring buffer:
# journalctl -k
Show auth.log equivalent by filtering on syslog facility:
# journalctl -f -l SYSLOG_FACILITY=10
详情参阅man journalctl、man systemd.journal-fields,以及Lennert的这篇博文
如果按上面的操作保留日志的话,默认日志最大限制为所在文件系统容量的 10%,即:如果 /var/log/journal 储存在 50GiB 的根分区中,那么日志最多存储 5GiB 数据。可以修改 /etc/systemd/journald.conf 中的 SystemMaxUse 来指定该最大限制。如限制日志最大 50MiB:
SystemMaxUse=50M
详情参见 man journald.conf.
systemd提供了 socket /run/systemd/journal/syslog,以兼容传统日志服务。所有系统信息都会被传入。要使传统日志服务工作,需要让服务链接该 socket,而非 /dev/log(官方说明)。Arch 软件仓库中的 syslog-ng 已经包含了需要的配置。
设置开机启动 syslog-ng:
# systemctl enable syslog-ng
这里有一份很不错的journalctl指南。
In /etc/systemd/journald.conf enable the following:
1. ForwardToConsole=yes
2. TTYPath=/dev/tty12
3. MaxLevelConsole=info
重启journald:
1. # systemctl restart systemd-journald
如果关机特别慢(甚至跟死机了一样),很可能是某个拒不退出的服务在作怪。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
要使用老的内核转储,创建下面文件:
1. /etc/sysctl.d/49-coredump.conf
2.
3. kernel.core_pattern = core
4. kernel.core_uses_pid = 0
然后运行:
1. # /usr/lib/systemd/systemd-sysctl
同样可能需要执行"unlimit"设置文件大小:
1. $ ulimit -c unlimited
原文地址:http://13155479.blog.51cto.com/13145479/1968197