前言
在早期的systemV模式下编写Damon程序略显麻烦,基本思路为两次fork使子进程脱离父进程并脱离原会话成为init进程接管的一个进程来达到Damon的目的
最近的一些linux distribution开始采用systemd作为sysv init和upstart的替代,如果我们想让自己的程序(服务)被systemd管理,则编写守护进程则要遵循一些systemd下的规则了....
一. SystemD简介
摘自:http://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html
Systemd 是 Linux 系统中最新的初始化系统(init),它主要的设计目标是克服 sysvinit 固有的缺点,提高系统的启动速度。systemd 和 ubuntu 的 upstart 是竞争对手,预计会取代 UpStart。
Systemd 的很多概念来源于苹果 Mac OS 操作系统上的 launchd,不过 launchd 专用于苹果系统,因此长期未能获得应有的广泛关注。Systemd 借鉴了很多 launchd 的思想,它的重要特性如下:
1.同 SysVinit 和 LSB init scripts 兼容
2.更快的启动速度
3.systemd 提供按需启动能力
4.Systemd 采用 Linux 的 Cgroup 特性跟踪和管理进程的生命周期
5.启动挂载点和自动挂载的管理
6.实现事务性依赖关系管理
7.能够对系统进行快照和恢复
8.日志服务
二.systemd下编写守护进程遵循的几点规则
开发人员需要了解 systemd 的更多细节。比如您打算开发一个新的系统服务,就必须了解如何让这个服务能够被 systemd 管理。
这需要您注意以下这些要点(前3点注意与SysV区分):
- 后台服务进程代码不需要执行两次派生来实现后台精灵进程,只需要实现服务本身的主循环即可。(传统编写守护进程要至少调用一次fork,然后停止父进程)
- 不要调用 setsid(),交给 systemd 处理
- 不再需要维护 pid 文件。(传统编写守护进程,会自己在某个目录下,生成pid文件,一是记录本守护进程pid,另外一点是防止本守护进程被多次重启而导致出错或者导致多个实例在运行)
- Systemd 提供了日志功能,服务进程只需要输出到 stderr 即可,无需使用 syslog。(传统编写守护进程我们要将标准输出、出错、输入关闭或者重定向,日志信息都是发往rsyslog)
- 处理信号 SIGTERM,这个信号的唯一正确作用就是停止当前服务,不要做其他的事情。(传统守护进程一般SIGTERM也是用来做这种事情的)
- SIGHUP 信号的作用是重启服务。(传统数据进程一般SIGHUP也是做这种事情的)
- 需要套接字的服务,不要自己创建套接字,让 systemd 传入套接字。(这个承接systemd快速启动优点而设立的,可以实现这个特点,也可以不实现)
- 使用 sd_notify()函数通知 systemd 服务自己的状态改变。一般地,当服务初始化结束,进入服务就绪状态时,可以调用它。(没用过)
三.SystemD基本概念
1.单元的概念
系统初始化需要做的事情非常多。需要启动后台服务,比如启动 SSHD 服务;需要做配置工作,比如挂载文件系统。这个过程中的每一步都被 systemd 抽象为一个配置单元,即 unit。可以认为一个服务是一个配置单元;一个挂载点是一个配置单元;一个交换分区的配置是一个配置单元;等等。systemd 将配置单元归纳为以下一些不同的类型。然而,systemd 正在快速发展,新功能不断增加。所以配置单元类型可能在不久的将来继续增加。
- service :代表一个后台服务进程,比如 mysqld。这是最常用的一类。
- socket :此类配置单元封装系统和互联网中的一个 套接字 。当下,systemd 支持流式、数据报和连续包的 AF_INET、AF_INET6、AF_UNIX socket 。每一个套接字配置单元都有一个相应的服务配置单元 。相应的服务在第一个"连接"进入套接字时就会启动(例如:nscd.socket 在有新连接后便启动 nscd.service)。
- device :此类配置单元封装一个存在于 Linux 设备树中的设备。每一个使用 udev 规则标记的设备都将会在 systemd 中作为一个设备配置单元出现。
- mount :此类配置单元封装文件系统结构层次中的一个挂载点。Systemd 将对这个挂载点进行监控和管理。比如可以在启动时自动将其挂载;可以在某些条件下自动卸载。Systemd 会将/etc/fstab 中的条目都转换为挂载点,并在开机时处理。
- automount :此类配置单元封装系统结构层次中的一个自挂载点。每一个自挂载配置单元对应一个挂载配置单元 ,当该自动挂载点被访问时,systemd 执行挂载点中定义的挂载行为。
- swap: 和挂载配置单元类似,交换配置单元用来管理交换分区。用户可以用交换配置单元来定义系统中的交换分区,可以让这些交换分区在启动时被激活。
- target :此类配置单元为其他配置单元进行逻辑分组。它们本身实际上并不做什么,只是引用其他配置单元而已。这样便可以对配置单元做一个统一的控制。这样就可以实现大家都已经非常熟悉的运行级别概念。比如想让系统进入图形化模式,需要运行许多服务和配置命令,这些操作都由一个个的配置单元表示,将所有这些配置单元组合为一个目标(target),就表示需要将这些配置单元全部执行一遍以便进入目标所代表的系统运行状态。 (例如:multi-user.target 相当于在传统使用 SysV 的系统中运行级别 5)
- timer:定时器配置单元用来定时触发用户定义的操作,这类配置单元取代了 atd、crond 等传统的定时服务。
- snapshot :与 target 配置单元相似,快照是一组配置单元。它保存了系统当前的运行状态。
2.依赖关系(会在.service文件中注明)
虽然 systemd 将大量的启动工作解除了依赖,使得它们可以并发启动。但还是存在有些任务,它们之间存在天生的依赖,不能用"套接字激活"(socket activation)、D-Bus activation 和 autofs 三大方法来解除依赖(三大方法详情见后续描述)。比如:挂载必须等待挂载点在文件系统中被创建;挂载也必须等待相应的物理设备就绪。为了解决这类依赖问题,systemd 的配置单元之间可以彼此定义依赖关系。
Systemd 用配置单元定义文件中的关键字来描述配置单元之间的依赖关系。比如:unit A 依赖 unit B,可以在 unit B 的定义中用"require A"来表示。这样 systemd 就会保证先启动 A 再启动 B。
3.Systemd 事务
Systemd 能保证事务完整性。Systemd 的事务概念和数据库中的有所不同,主要是为了保证多个依赖的配置单元之间没有环形引用。比如 unit A、B、C,假如它们的依赖关系为:图 4, Unit 的循环依赖
存在循环依赖,那么 systemd 将无法启动任意一个服务。此时 systemd 将会尝试解决这个问题,因为配置单元之间的依赖关系有两种:required 是强依赖;want 则是弱依赖,systemd 将去掉 wants 关键字指定的依赖看看是否能打破循环。如果无法修复,systemd 会报错。
4.Target 和运行级别
systemd 用目标(target)替代了运行级别的概念,提供了更大的灵活性,如您可以继承一个已有的目标,并添加其它服务,来创建自己的目标。下表列举了 systemd 下的目标和常见 runlevel 的对应关系:
表 1. Sysvinit 运行级别和 systemd 目标的对应表
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 |