Sysvinit 运行级别 | Systemd 目标 | 备注 |
---|---|---|
0 | runlevel0.target, poweroff.target | 关闭系统。 |
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 | 紧急 Shell |
如前所述,在 Systemd 中,全部的服务都并发启动,好比 Avahi、D-Bus、livirtd、X十一、HAL 能够同时启动。乍一看,这彷佛有点儿问题,好比 Avahi 须要 syslog 的服务,Avahi 和 syslog 同时启动,假设 Avahi 的启动比较快,因此 syslog 尚未准备好,但是 Avahi 又须要记录日志,这岂不是会出现问题? mysql
Systemd 的开发人员仔细研究了服务之间相互依赖的本质问题,发现所谓依赖能够分为三个具体的类型,而每个类型实际上均可以经过相应的技术解除依赖关系。 redis
绝大多数的服务依赖是套接字依赖。好比服务 A 经过一个套接字端口 S1 提供本身的服务,其余的服务若是须要服务 A,则须要链接 S1。所以若是服务 A 还没有启动,S1 就不存在,其余的服务就会获得启动错误。因此传统地,人们须要先启动服务 A,等待它进入就绪状态,再启动其余须要它的服务。Systemd 认为,只要咱们预先把 S1 创建好,那么其余全部的服务就能够同时启动而无需等待服务 A 来建立 S1 了。若是服务 A 还没有启动,那么其余进程向 S1 发送的服务请求实际上会被 Linux 操做系统缓存,其余进程会在这个请求的地方等待。一旦服务 A 启动就绪,就能够当即处理缓存的请求,一切都开始正常运行。 sql
那么服务如何使用由 init 进程建立的套接字呢? ubuntu
Linux 操做系统有一个特性,当进程调用 fork 或者 exec 建立子进程以后,全部在父进程中被打开的文件句柄 (file descriptor) 都被子进程所继承。套接字也是一种文件句柄,进程 A 能够建立一个套接字,此后当进程 A 调用 exec 启动一个新的子进程时,只要确保该套接字的 close_on_exec 标志位被清空,那么新的子进程就能够继承这个套接字。子进程看到的套接字和父进程建立的套接字是同一个系统套接字,就仿佛这个套接字是子进程本身建立的同样,没有任何区别。 缓存
这个特性之前被一个叫作 inetd 的系统服务所利用。Inetd 进程会负责监控一些经常使用套接字端口,好比 Telnet,当该端口有链接请求时,inetd 才启动 telnetd 进程,并把有链接的套接字传递给新的 telnetd 进程进行处理。这样,当系统没有 telnet 客户端链接时,就不须要启动 telnetd 进程。Inetd 能够代理不少的网络服务,这样就能够节约不少的系统负载和内存资源,只有当有真正的链接请求时才启动相应服务,并把套接字传递给相应的服务进程。 网络
和 inetd 相似,systemd 是全部其余进程的父进程,它能够先创建全部须要的套接字,而后在调用 exec 的时候将该套接字传递给新的服务进程,而新进程直接使用该套接字进行服务便可。 session
D-Bus 是 desktop-bus 的简称,是一个低延迟、低开销、高可用性的进程间通讯机制。它愈来愈多地用于应用程序之间通讯,也用于应用程序和操做系统内核之间的通讯。不少现代的服务进程都使用D-Bus 取代套接字做为进程间通讯机制,对外提供服务。好比简化 Linux 网络配置的 NetworkManager 服务就使用 D-Bus 和其余的应用程序或者服务进行交互:邮件客户端软件 evolution 能够经过 D-Bus 从 NetworkManager 服务获取网络状态的改变,以便作出相应的处理。 并发
D-Bus 支持所谓"bus activation"功能。若是服务 A 须要使用服务 B 的 D-Bus 服务,而服务 B 并无运行,则 D-Bus 能够在服务 A 请求服务 B 的 D-Bus 时自动启动服务 B。而服务 A 发出的请求会被 D-Bus 缓存,服务 A 会等待服务 B 启动就绪。利用这个特性,依赖 D-Bus 的服务就能够实现并行启动。 ssh
系统启动过程当中,文件系统相关的活动是最耗时的,好比挂载文件系统,对文件系统进行磁盘检查(fsck),磁盘配额检查等都是很是耗时的操做。在等待这些工做完成的同时,系统处于空闲状态。那些想使用文件系统的服务彷佛必须等待文件系统初始化完成才能够启动。可是 systemd 发现这种依赖也是能够避免的。 socket
Systemd 参考了 autofs 的设计思路,使得依赖文件系统的服务和文件系统自己初始化二者能够并发工做。autofs 能够监测到某个文件系统挂载点真正被访问到的时候才触发挂载操做,这是经过内核 automounter 模块的支持而实现的。好比一个 open()系统调用做用在"/misc/cd/file1"的时候,/misc/cd 还没有执行挂载操做,此时 open()调用被挂起等待,Linux 内核通知 autofs,autofs 执行挂载。这时候,控制权返回给 open()系统调用,并正常打开文件。
Systemd 集成了 autofs 的实现,对于系统中的挂载点,好比/home,当系统启动的时候,systemd 为其建立一个临时的自动挂载点。在这个时刻/home 真正的挂载设备还没有启动好,真正的挂载操做尚未执行,文件系统检测也尚未完成。但是那些依赖该目录的进程已经能够并发启动,他们的 open()操做被内建在 systemd 中的 autofs 捕获,将该 open()调用挂起(可中断睡眠状态)。而后等待真正的挂载操做完成,文件系统检测也完成后,systemd 将该自动挂载点替换为真正的挂载点,并让 open()调用返回。由此,实现了那些依赖于文件系统的服务和文件系统自己同时并发启动。
固然对于"/"根目录的依赖实际上必定仍是要串行执行,由于 systemd 本身也存放在/之下,必须等待系统根目录挂载检查好。
不过对于相似/home 等挂载点,这种并发能够提升系统的启动速度,尤为是当/home 是远程的 NFS 节点,或者是加密盘等,须要耗费较长的时间才能够准备就绪的状况下,由于并发启动,这段时间内,系统并非彻底无事可作,而是能够利用这段空余时间作更多的启动进程的事情,总的来讲就缩短了系统启动时间。
开发人员须要了解 systemd 的更多细节。好比您打算开发一个新的系统服务,就必须了解如何让这个服务可以被 systemd 管理。这须要您注意如下这些要点:
对于开发者来讲,工做量最大的部分应该是编写配置单元文件,定义所须要的单元。
举例来讲,开发人员开发了一个新的服务程序,好比 httpd,就须要为其编写一个配置单元文件以便该服务能够被 systemd 管理,相似 UpStart 的工做配置文件。在该文件中定义服务启动的命令行语法,以及和其余服务的依赖关系等。
此外咱们以前已经了解到,systemd 的功能繁多,不只用来管理服务,还能够管理挂载点,定义定时任务等。这些工做都是由编辑相应的配置单元文件完成的。我在这里给出几个配置单元文件的例子。
下面是 SSH 服务的配置单元文件,服务配置单元文件以.service 为文件名后缀。
#cat /etc/system/system/sshd.service [Unit] Description=OpenSSH server daemon [Service] EnvironmentFile=/etc/sysconfig/sshd ExecStartPre=/usr/sbin/sshd-keygen ExecStart=/usrsbin/sshd –D $OPTIONS ExecReload=/bin/kill –HUP $MAINPID KillMode=process Restart=on-failure RestartSec=42s [Install] WantedBy=multi-user.target
文件分为三个小节。第一个是[Unit]部分,这里仅仅有一个描述信息。第二部分是 Service 定义,其中,ExecStartPre 定义启动服务以前应该运行的命令;ExecStart 定义启动服务的具体命令行语法。第三部分是[Install],WangtedBy 代表这个服务是在多用户模式下所须要的。
那咱们就来看下 multi-user.target 吧:
#cat multi-user.target [Unit] Description=Multi-User System Documentation=man.systemd.special(7) Requires=basic.target Conflicts=rescue.service rescure.target After=basic.target rescue.service rescue.target AllowIsolate=yes [Install] Alias=default.target
第一部分中的 Requires 定义代表 multi-user.target 启动的时候 basic.target 也必须被启动;另外 basic.target 中止的时候,multi-user.target 也必须中止。若是您接着查看 basic.target 文件,会发现它又指定了 sysinit.target 等其余的单元必须随之启动。一样 sysinit.target 也会包含其余的单元。采用这样的层层连接的结构,最终全部须要支持多用户模式的组件服务都会被初始化启动好。
在[Install]小节中有 Alias 定义,即定义本单元的别名,这样在运行 systemctl 的时候就可使用这个别名来引用本单元。这里的别名是 default.target,比 multi-user.target 要简单一些。。。
此外在/etc/systemd/system 目录下还能够看到诸如*.wants 的目录,放在该目录下的配置单元文件等同于在[Unit]小节中的 wants 关键字,即本单元启动时,还须要启动这些单元。好比您能够简单地把您本身写的 foo.service 文件放入 multi-user.target.wants 目录下,这样每次都会被默认启动了。
最后,让咱们来看看 sys-kernel-debug.mout 文件,这个文件定义了一个文件挂载点:
#cat sys-kernel-debug.mount [Unit] Description=Debug File Syste DefaultDependencies=no ConditionPathExists=/sys/kernel/debug Before=sysinit.target [Mount] What=debugfs Where=/sys/kernel/debug Type=debugfs
这个配置单元文件定义了一个挂载点。挂载配置单元文件有一个[Mount]配置小节,里面配置了 What,Where 和 Type 三个数据项。这都是挂载命令所必须的,例子中的配置等同于下面这个挂载命令:
mount –t debugfs /sys/kernel/debug debugfs
配置单元文件的编写须要不少的学习,必须参考 systemd 附带的 man 等文档进行深刻学习。但愿经过上面几个小例子,你们已经了解配置单元文件的做用和通常写法了。
多数管理员应该都已经很是熟悉系统服务和 init 系统的管理,好比 service、chkconfig 以及 telinit 命令的使用。systemd 也完成一样的管理任务,只是命令工具 systemctl 的语法有所不一样而已,所以用表格来对比 systemctl 和传统的系统管理命令会很是清晰。
Sysvinit 命令 | Systemd 命令 | 备注 |
---|---|---|
service foo start | systemctl start foo.service | 用来启动一个服务 (并不会重启现有的) |
service foo stop | systemctl stop foo.service | 用来中止一个服务 (并不会重启现有的)。 |
service foo restart | systemctl restart foo.service | 用来中止并启动一个服务。 |
service foo reload | systemctl reload foo.service | 当支持时,从新装载配置文件而不中断等待操做。 |
service foo condrestart | systemctl condrestart foo.service | 若是服务正在运行那么重启它。 |
service foo status | systemctl status foo.service | 汇报服务是否正在运行。 |
ls /etc/rc.d/init.d/ | systemctl list-unit-files --type=service | 用来列出能够启动或中止的服务列表。 |
chkconfig foo on | systemctl enable foo.service | 在下次启动时或知足其余触发条件时设置服务为启用 |
chkconfig foo off | systemctl disable foo.service | 在下次启动时或知足其余触发条件时设置服务为禁用 |
chkconfig foo | systemctl is-enabled foo.service | 用来检查一个服务在当前环境下被配置为启用仍是禁用。 |
chkconfig –list | systemctl list-unit-files --type=service | 输出在各个运行级别下服务的启用和禁用状况 |
chkconfig foo –list | ls /etc/systemd/system/*.wants/foo.service | 用来列出该服务在哪些运行级别下启用和禁用。 |
chkconfig foo –add | systemctl daemon-reload | 当您建立新服务文件或者变动设置时使用。 |
telinit 3 | systemctl isolate multi-user.target (OR systemctl isolate runlevel3.target OR telinit 3) | 改变至多用户运行级别。 |
除了表 2 列出的常见用法,系统管理员还须要了解其余一些系统配置和管理任务的改变。
首先咱们了解 systemd 如何处理电源管理,命令以下表所示:
命令 | 操做 |
---|---|
systemctl reboot | 重启机器 |
systemctl poweroff | 关机 |
systemctl suspend | 待机 |
systemctl hibernate | 休眠 |
systemctl hybrid-sleep | 混合休眠模式(同时休眠到硬盘并待机) |
关机不是每一个登陆用户在任何状况下均可以执行的,通常只有管理员才能够关机。正常状况下系统不该该容许 SSH 远程登陆的用户执行关机命令。不然其余用户正在工做,一个用户把系统关了就很差了。为了解决这个问题,传统的 Linux 系统使用 ConsoleKit 跟踪用户登陆状况,并决定是否赋予其关机的权限。如今 ConsoleKit 已经被 systemd 的 logind 所替代。
logind 不是 pid-1 的 init 进程。它的做用和 UpStart 的 session init 相似,但功能要丰富不少,它可以管理几乎全部用户会话(session)相关的事情。logind 不只是 ConsoleKit 的替代,它能够:
在不才做者看来,做为系统初始化系统,systemd 的最大特色有两个:
此外,和其前任不一样的地方在于,systemd 已经不只仅是一个初始化系统了。
Systemd 出色地替代了 sysvinit 的全部功能,但它并未就此自满。由于 init 进程是系统全部进程的父进程这样的特殊性,systemd 很是适合提供曾经由其余服务提供的功能,好比定时任务 (之前由 crond 完成) ;会话管理 (之前由 ConsoleKit/PolKit 等管理) 。仅仅从本文皮毛同样的介绍来看,Systemd 已经管得不少了,可它还在不断发展。它将逐渐成为一个多功能的系统环境,可以处理很是多的系统管理任务,有人甚至将它看做一个操做系统。
好的一点是,这很是有助于标准化 Linux 的管理!从前,不一样的 Linux 发行版各行其事,使用不一样方法管理系统,历来也不会互相妥协。好比如何将系统进入休眠状态,不一样的系统有不一样的解决方案,即使是同一个 Linux 系统,也存在不一样的方法,好比一个有趣的讨论:如何让 ubuntu 系统休眠,可使用底层的/sys/power/state 接口,也可使用诸如 pm-utility 等高层接口。存在这么多种不一样的方法作一件事情对像我这样的普通用户而言可不是件有趣的事情。systemd 提供统一的电源管理命令接口,这件事情的意义就相似全世界的人都说统一的语言,咱们不再须要学习外语了,多么美好!
若是全部的 Linux 发行版都采纳了 systemd,那么系统管理任务即可以很大程度上实现标准化。此外 systemd 有个很棒的承诺:接口保持稳定,不会再轻易改动。对于软件开发人员来讲,这是多么体贴又让人感动的承诺啊!