systemd即为system daemon,是linux下的一种init软件,由Lennart Poettering带头开发,并在LGPL 2.1及其后续版本许可证下开源发布,开发目标是提供更优秀的框架以表示系统服务间的依赖关系,并依此实现系统初始化时服务的并行启动,同时达到降低Shell的系统开销的效果,最终代替现在常用的System V与BSD风格init程序。
与多数发行版使用的System V风格init相比,systemd采用了以下新技术:
systemd已纳入众多Linux发行版的软件源中,以下简表: 作用
作为init软件,systemd程序的任务有如下工作
systemd 开启和监督整个系统是基于 unit 的概念。unit 是由一个与配置文件对应的名字和类型组成的. 一个unit配置文件,封装了后台服务,socket,设备,挂载,自动挂载,交换文件或分区,启动目标,文件系统路径,或者定时器控制,这些其中的一种。配置文件语法源于XDG Desktop Entry Specification的.desktop文件,微软的.ini文件语法格式。
unit 类型如下: 配置文件所有配置文件存放的目录可以在以下任一目录之中 /etc/systemd/system /etc/systemd/system/ /usr/lib/systemd/system/ /lib/systemd/system/ 加载的第一个文件为default.target, systemd的启动逻辑顺序默认如下 local-fs-pre.target | v (various mounts and (various swap (various cryptsetup fsck services...) devices...) devices...) (various low-level | | | services: udevd, v v v tmpfiles, random local-fs.target swap.target cryptsetup.target seed, sysctl, ...) | | | | \__________________|_______________ | _________________/ \|/ v sysinit.target | _______________/|\___________________ / | \ | | | v | v (various | rescue.service sockets...) | | | | v v | rescue.target sockets.target | | | \_______________ | \| v basic.target | ________________________________/| emergency.service / | | | | | | v v v v emergency.target display- (various system (various system manager.service services services) | required for | | graphical UIs) v | | multi-user.target | | | \_______________ | _______________/ \|/ v graphical.target (如果,我们的启动级别为5, 那么我们可以将 default.target 链接到graphical.target上) 例如我们需要添加一个服务, 应用程序为example, 那么我们在目录/usr/lib/systemd/system/下创建文件example.service [Unit] Description=Example Service [Service] ExecStart=/usr/bin/example [Install] WantedBy=multi-user.target 如果我们监视该程序,当程序退出时,能够自动重新启动。那么我们可以设置如下 [Unit] Description=Example Service [Service] ExecStart=/usr/bin/example Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
Restart=可选项为
如果我们要求该服务在某些服务启动之后,才能启动。比如要求在dbus.service启动之后, [Unit] Description=Example Service After=dbus.service [Service] ExecStart=/usr/bin/example Restart=always RestartSec=10 [Install] WantedBy=multi-user.target 执行一个脚本
如我们需要在开机初始时,执行一个shell脚本,并且我们在脚本内部会启动某些后台服务程序。那么我们在写service文件的时候,则需要加入Type=forking段,否则shell执行结束退出后, [Unit] Description=panda launcher Wants=syslog.target dbus.service [Service] Type=forking ExecStart=/usr/bin/panda-launcher [Install] Alias=display-manager.service WantedBy=graphical.target
Service的Type几种类型介绍 特殊的unit一些unit被systemd特别的处理。他们有特别的内部语法,并且不能修改名称。basic.target, bluetooth.target, ctrl-alt-del.target, cryptsetup.target, dbus.service, dbus.socket, default.target, 等等. 调试根据类型列举出当前的状态 systemctl list-units -t service --all 列举出所有的service和他们的当前状态 systemctl status sshd.service 检查当前运行中服务的状态 systemctl show -p "Wants" multi-user.target 列出一个target组合着哪些service. systemd的mount会去根据/etc/fstab内容,进行挂载 函数分析
+---------+ | manager | +---------+ | +-------------------------------+ | | | | +-------+ +------+ +------+ +------+ |service| |target| |socket| | ... | +-------+ +------+ +------+ +------+ | | | | +-------------------------------+ | +---------+ +-----+ | unit |<---->| job | +---------+ +-----+
main中的manager +-------------+ +->| manager_new | set path | +-------------+ | | +-----------------+ +->| manager_startup | read file name | +-----------------+ | +------+ | +-------------------+ | main |-+->| manager_load_unit | anaylse file +------+ | +-------------------+ | | +-----------------+ +->| manager_add_job | add each target to job | +-----------------+ | | +--------------+ +->| manager_loop | +--------------+ 设置路径部分代码 int main() { if ((r = manager_new(arg_running_as, &m)) < 0) { goto finish; } } int manager_new(ManagerRunningAs running_as, Manager **_m) { if ((r = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0) goto fail } int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) { if (running_as == MANAGER_USER) { } else if (!(p->unit_path = strv_new( /* If you modify this you also want to modify * systemdsystemunitpath= in systemd.pc.in! */ "/run/systemd/system", SYSTEM_CONFIG_UNIT_PATH, "/etc/systemd/system", "/usr/local/lib/systemd/system", "/usr/lib/systemd/system", SYSTEM_DATA_UNIT_PATH, "/lib/systemd/system", NULL))) } 加载文件流程图 +------+ +-------------+ +-------------------+ +-----------------+ | main |------| manager_new |---| lookup_paths_init |---| m->lookup_paths | +------+ | +-------------+ +-------------------+ +-----------------+ | +-----------------+ set path +->| manager_startup | +-----------------+ | +-------------------------------+ | manager_build_unit_path_cache | +-------------------------------+ | +--------------------------------------------+ | STRV_FOREACH(i, m->lookup_paths.unit_path) | | set_put(m->unit_path_cache, p) |-------unit_path_cache +--------------------------------------------+ +-------------+ | iterate_dir | +-------------+ | +------------------------------------------------+ | set_get(u->manager->unit_path_cache, filename) | +------------------------------------------------+ | +----------------+ | load_from_path | +----------------+ | |.wants .requires +--------------------+ +------------------+ | unit_load_fragment | | unit_load_dropin | +--------------------+ +------------------+ | | +------------------------------------------+ | +------------------------------------+---------------+ | | | +-------------------------------+ +--------------+ | unit_load_fragment_and_dropin | | service_load | ...... +-------------------------------+ +--------------+ main() { /* Initialize default unit */ if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0) goto finish; // arg_default_unit is "default.target" manager_load_unit(m, arg_default_unit, NULL, &error, &target)) < 0) if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, NULL)) < 0) { goto finish; } } int manager_load_unit() { /* This will load the service information files, but not actually * start any services or anything. */ if ((r = manager_load_unit_prepare(m, name, path, e, _ret)) != 0) return r; manager_dispatch_load_queue(m); } unsigned manager_dispatch_load_queue(Manager *m) { while ((meta = m->load_queue)) { unit_load((Unit*) meta); } } int unit_load(Unit *u) { if (u->meta.in_load_queue) { //remove unit frome load_queue LIST_REMOVE(Meta, load_queue, u->meta.manager->load_queue, &u->meta); u->meta.in_load_queue = false; } if (UNIT_VTABLE(u)->load) if ((r = UNIT_VTABLE(u)->load(u)) < 0) goto fail; if (u->meta.load_state == UNIT_LOADED && u->meta.default_dependencies) if ((r = unit_add_default_dependencies(u)) < 0) goto fail; } //take service load for example static int service_load(Unit *u) { /* Load a .service file */ if ((r = unit_load_fragment(u)) < 0) return r; /* We were able to load something, then let's add in the * dropin directories. */ if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) return r; } int unit_load_fragment(Unit *u) { if ((r = load_from_path(u, u->meta.id)) < 0) return r; } static int load_from_path(Unit *u, const char *path) { if (path_is_absolute(path)) { } else { STRV_FOREACH(p, u->meta.manager->lookup_paths.unit_path) { if (!(filename = path_make_absolute(path, *p))) { } } } } 执行 +--------------+ +----------------------------+ | manager_loop |---->| manager_dispatch_run_queue | +--------------+ +----------------------------+ | +--------------------------+ | if(j = m->run_queue) | | job_run_and_invalidate | +--------------------------+ | +----------------------------------------------+ |LIST_REMOVE(Job,...,j->manager->run_queue, j);| +---------------------+ | unit_start |->|UNIT_VTABLE(u)->start| +----------------------------------------------+ +---------------------+ | +-----------------------+ +---------------------+ +-------------+ | service_vtable = { | | service_enter_start |<--|service_start|<--|.suffix = ".service" | +---------------------+ +-------------+ |.start = service_start,| | |}; | +---------------+ +-----------------------+ | service_spawn | +---------------+ | +------------+ | exec_spawn | +------------+ 并发
例如 SysV的系统启动,一次只能启动一个进程,例如将会有如下的启动顺序, Syslog -> D-Bus -> Avahi -> Blutooth. 一些版本试图改进严格的按照序列化启动,如Avahi和Bluetooth彼此独立,他们能够并发的启动。但是这个并发改进的效果并不明显. 参考
http://0pointer.de/blog/projects/systemd-docs.html (责任编辑:IT) |