我用来systemd-analyze plot
分析服务在系统启动时运行所花费的时间。有一个相当简单的oneshot服务,update_sysfs_cpu_permissions.service
它花费的时间出乎意料的长(755ms),而且大部分时间都花在Activating状态,而不是Active状态。
服务文件的内容:
# /etc/systemd/system/update_sysfs_cpu_permissions.service
[Unit]
Description=Give the group `cpu_tuners` permissions to write to the files /sys/.../cpufreq/policy*/* if the `root` user can write to them
[Service]
Type=oneshot
User=root
# For every file of mode rw-r--r-- change mode to rw-rw-r-- and owners to root:cpu_tuners (instead of root:root)
# mindepth and maxdepth are to identify we only want to change files inside policy* (though, it's not a very strong constraint and could be replaced with something stricter)
ExecStart=/usr/bin/find /sys/devices/system/cpu/cpufreq/ -mindepth 2 -maxdepth 2 -type f -perm 644 -exec /bin/sh -c 'chown root:cpu_tuners "$1"; chmod 664 "$1"' _ {} \;
[Install]
WantedBy=multi-user.target
手动运行服务并不需要太多时间:
$ time sudo systemctl start update_sysfs_cpu_permissions.service
real 0m0.155s
user 0m0.019s
sys 0m0.010s
$
完整剧情:https://i.stack.imgur.com/EPh93.jpg
服务处于激活状态意味着什么?为什么启动时需要这么长时间才能运行?
答案1
为什么启动时需要这么长时间才能运行?
如果没有启动过程的更详细的跟踪(例如 bootchart),就不可能说,但是我猜是在启动期间它与其他服务竞争 CPU – 也可能是磁盘输入/输出,因为它需要将/bin/find
、/bin/sh
、/bin/chmod
和/bin/chown
可执行文件从磁盘加载到内存中。
而且,大部分时间都花在Activating状态,而不是Active状态。
这是设计使然 – Type=oneshot 服务具有不默认为“活动”状态。 Type=oneshot 服务的全部目的是在所有 ExecStart= 运行时处于“激活”状态,以便只有在 ExecStart= 命令完成后“启动”作业才会完成。
服务处于激活状态意味着什么?
“激活”意味着该单元已被调用,但尚未“准备好”用于依赖性和排序目的。例如,如果您采用数据库服务器守护程序,它可能会花费一些时间启动、建立套接字、重播日志等,在此期间进程正在运行,但尚未接受客户端连接,这意味着任何其他服务尝试使用它将会失败。
因此,当启动作业运行时,它等待让服务在继续启动其依赖项(如果有)之前离开“正在激活”状态。例如,httpd.service 将在队列中等待,而 php-fpm.service 仍处于“激活”状态。
对于 Type=forking,“激活”持续到 systemd 看到进程“守护进程”(以传统的分叉或双分叉方式 - 更具体地说,它持续到初始父进程退出,让其孩子接管)。
对于 Type=notify,“激活”将持续到进程通过指定套接字或管道向 systemd 发送“READY=1”消息为止。
对于 Type=simple,默认情况下没有“激活”状态(当人们抱怨“Type=simple 是使用 systemd 的真正方式”时,他们往往会忘记这一点)。
对于 Type=oneshot,如您的情况,“激活”持续到服务完成其任务为止,即直到 ExecStart= 中的所有命令都完成。
(ExecStartPost= 还可以扩展“激活”状态,即使对于 Type=simple,也可以用于某些任务必须在服务启动后、真正准备好使用之前完成。)
因此,在此示例中,保持“正在激活”状态意味着如果另一个启动服务将您的设备列在 中After=
,它将被正确延迟,直到 /bin/find 完成并且权限确实已更新。
尝试删除 User=root,因为这可能会产生一些记录不足的副作用。
设置用户=没有PAMName= 仅具有更改 UID/GID 的作用。