systemd/User
systemd 提供了在用户控制下管理服务的能力,通过每个用户的 systemd 实例,使用户能够启动、停止、启用和禁用他们自己的用户单元。这对于通常为单个用户运行的守护进程和其他服务非常方便,例如 mpd,或执行自动化任务(如获取邮件)。
工作原理
根据 /etc/pam.d/system-login
中的默认配置,当用户首次登录时,pam_systemd
模块会自动启动一个 systemd --user
实例。只要该用户存在会话,此进程就会一直存在,并在该用户的最后一个会话关闭后立即被杀死。当启用 #自动启动 systemd 用户实例 时,该实例将在启动时启动,并且不会被杀死。systemd 用户实例负责管理用户服务,这些服务可用于运行守护进程或自动化任务,并具有 systemd 的所有优点,例如套接字激活、定时器、依赖系统以及通过 cgroup 进行严格的进程控制。
与系统单元类似,用户单元位于以下目录中(按升序优先级排序)
/usr/lib/systemd/user/
:安装的软件包提供的单元所在位置。~/.local/share/systemd/user/
:已安装在主目录中的软件包的单元所在位置。/etc/systemd/user/
:系统管理员放置系统范围用户单元的位置。~/.config/systemd/user/
:用户放置自己的单元的位置。
当 systemd 用户实例启动时,它会启动每个用户的目标 default.target
。可以使用 systemctl --user
手动控制其他单元。请参阅 systemd.special(7) § 用户服务管理器管理的单元。
基本设置
所有用户单元都将放置在 ~/.config/systemd/user/
中。如果要在首次登录时启动单元,请对要自动启动的任何单元执行 systemctl --user enable unit
。
systemctl --global enable unit
。disable
命令也是如此。环境变量
由 systemd 用户实例启动的单元不会继承在 .bashrc
等位置设置的任何 环境变量。有几种方法可以为它们设置环境变量
- 对于具有
$HOME
目录的用户,在~/.config/environment.d/
目录中创建一个 .conf 文件,其中包含NAME=VAL
形式的行。仅影响该用户的用户单元。有关更多信息,请参阅 environment.d(5)。 - 使用
/etc/systemd/user.conf
文件中的DefaultEnvironment
选项。影响所有用户单元。 - 在
/etc/systemd/system/user@UID.service.d/
中添加一个 drop-in 配置文件,请参阅 #服务示例 - 在
/etc/systemd/system/user@.service.d/
中添加一个 drop-in 配置文件(影响所有用户),请参阅 #服务示例 - 在任何时候,使用
systemctl --user set-environment
或systemctl --user import-environment
。影响设置环境变量后启动的所有用户单元,但不影响已在运行的单元。 - 使用 dbus 提供的
dbus-update-activation-environment --systemd --all
命令。具有与systemctl --user import-environment
相同的效果,但也影响 D-Bus 会话。您可以将其添加到 shell 初始化文件的末尾。 - 对于用户环境的“全局”环境变量,您可以使用
environment.d
目录,这些目录由某些生成器解析。有关更多信息,请参阅 environment.d(5) 和 systemd.generator(7)。 - 您还可以编写一个 systemd.environment-generator(7) 脚本,该脚本可以生成因用户而异的环境变量,如果您需要每个用户的环境(
XDG_RUNTIME_DIR
、DBUS_SESSION_BUS_ADDRESS
等就是这种情况),这可能是最佳方法。
您可能想要设置的一个变量是 PATH
。
配置完成后,可以使用命令 systemctl --user show-environment
来验证值是否正确。您可能需要运行 systemctl --user daemon-reload
以使更改立即生效。
Systemd 用户实例
以上仅解决了用户单元的默认环境变量。但是,systemd 用户实例本身也受某些环境变量的影响。特别是,某些说明符(请参阅 systemd.unit(5) § 说明符)受 XDG 变量的影响。
但是,systemd 用户实例仅使用启动时设置的环境变量。特别是,它不会尝试解析文件,请参阅 上游错误 #29414(已关闭 WONTFIX)。因此,如果需要此类环境变量,则应在 drop-in 配置文件中设置它们,请参阅 #服务示例。
Systemd 不提供内省工具来检查这些值,但是,可以使用如下所示的服务来帮助检查说明符是否按预期扩展
$XDG_CONFIG_HOME/systemd/user/test-specifiers.service
[Service] Type=oneshot ExecStart=printf '(systemd)=(envvar)\n' ExecStart=printf '%%s=%%s\n' %C "${XDG_CACHE_HOME}" ExecStart=printf '%%s=%%s\n' %E "${XDG_CONFIG_HOME}" ExecStart=printf '%%s=%%s\n' %L "${XDG_STATE_HOME}"/log ExecStart=printf '%%s=%%s\n' %S "${XDG_STATE_HOME}" ExecStart=printf '%%s=%%s\n' %t "${XDG_RUNTIME_DIR}"
服务示例
创建 drop-in 目录 /etc/systemd/system/user@.service.d/
,并在其中创建一个扩展名为 .conf 的文件(例如 local.conf
)
/etc/systemd/system/user@.service.d/local.conf
[Service] Environment="PATH=/usr/lib/ccache/bin:/usr/local/sbin:/usr/local/bin:/usr/bin" Environment="EDITOR=nano -c" Environment="BROWSER=firefox" Environment="NO_AT_BRIDGE=1" Environment="XDG_STATE_HOME=%h/.local/var/state"
DISPLAY 和 XAUTHORITY
任何 X 应用程序都使用 DISPLAY
来了解要使用的显示器,而 XAUTHORITY
提供用户 .Xauthority
文件的路径,以及访问 X 服务器所需的 cookie。如果您计划从 systemd 单元启动 X 应用程序,则需要设置这些变量。Systemd 在 /etc/X11/xinit/xinitrc.d/50-systemd-user.sh
中提供了一个脚本,用于在 X 启动时将这些变量导入到 systemd 用户会话中。[3] 因此,除非您以非标准方式启动 X,否则用户服务应知道 DISPLAY
和 XAUTHORITY
。
PATH
如果您自定义了 PATH
并计划从 systemd 单元启动使用它的应用程序,则应确保在 systemd 环境中设置了修改后的 PATH
。假设您在 .bash_profile
中设置了 PATH
,则使 systemd 知道修改后的 PATH
的最佳方法是在设置 PATH
变量后将以下内容添加到 .bash_profile
中
~/.bash_profile
systemctl --user import-environment PATH
- 这不会影响在导入
PATH
之前启动的 systemd 服务。 - systemd 在解析非绝对二进制文件本身时,不会查看设置的
PATH
。
pam_env
可以通过使用 pam_env.so
模块来使环境变量可用。有关配置详细信息,请参阅 环境变量#使用 pam_env。
自动启动 systemd 用户实例
systemd 用户实例在用户首次登录后启动,并在用户的最后一个会话关闭后被杀死。有时,在启动后立即启动它,并在最后一个会话关闭后保持 systemd 用户实例运行可能很有用,例如,让某些用户进程在没有任何打开的会话的情况下运行。持久化用于此目的。如果安装了 polkit,请使用以下命令为您自己的用户启用持久化
$ loginctl enable-linger
如果没有 polkit 或要为其他用户启用持久化
# loginctl enable-linger username
要列出所有具有持久化权限的用户,请查看列 "LINGER",其中包含 yes
$ loginctl list-users
或检查 /var/lib/systemd/linger
。要撤销持久化
# loginctl disable-linger username
编写用户单元
有关编写 systemd 单元文件的常规信息,请参阅 systemd#编写单元文件。
示例
以下是 mpd 服务的用户版本示例
~/.config/systemd/user/mpd.service
[Unit] Description=Music Player Daemon [Service] ExecStart=/usr/bin/mpd --no-daemon [Install] WantedBy=default.target
带变量的示例
以下是 foldingathomeAUR 使用的用户服务,它考虑了变量主目录,Folding@home 可以在其中找到某些文件
~/.config/systemd/user/foldingathome-user.service
[Unit] Description=Folding@home distributed computing client After=network.target [Service] Type=simple WorkingDirectory=%h/.config/fah ExecStart=/usr/bin/FAHClient CPUSchedulingPolicy=idle IOSchedulingClass=3 [Install] WantedBy=default.target
如 systemd.unit(5) § 说明符 中详述,%h
变量被运行服务的用户的家目录替换。在 systemd 手册页中还有其他可以考虑的变量。
读取日志
可以使用类似的命令读取用户的日志
$ journalctl --user
要指定单元,可以使用
$ journalctl --user-unit myunit.service
或者,等效地
$ journalctl --user -u myunit.service
临时文件
systemd-tmpfiles 允许用户像在系统范围内一样管理自定义的易失性和临时文件和目录(请参阅 systemd#systemd-tmpfiles - 临时文件)。用户特定的配置文件从 ~/.config/user-tmpfiles.d/
和 ~/.local/share/user-tmpfiles.d/
中读取,按此顺序。要使用此功能,需要为您的用户启用必要的 systemd 用户单元
$ systemctl --user enable systemd-tmpfiles-setup.service systemd-tmpfiles-clean.timer
配置文件的语法与系统范围内使用的语法相同。有关详细信息,请参阅 systemd-tmpfiles(8) 和 tmpfiles.d(5) 手册页。
Xorg 和 systemd
有几种在 systemd 单元中运行 xorg 的方法。以下是两种选择,要么通过启动带有 xorg 进程的新用户会话,要么从 systemd 用户服务启动 xorg。
无需显示管理器自动登录 Xorg
此选项将启动一个系统单元,该单元将启动一个带有 xorg 服务器的用户会话,然后运行常用的 ~/.xinitrc
以启动窗口管理器等。您需要安装 xlogin-gitAUR。按照 Xinit#xinitrc 部分中的规定设置您的 xinitrc。
会话将使用自己的 dbus 守护程序,但是各种 systemd 实用程序将自动连接到 dbus.service
实例。最后,启用 xlogin@username
服务以在启动时自动登录。用户会话完全存在于 systemd 范围之内,并且用户会话中的所有内容都应正常工作。
将 Xorg 作为 systemd 用户服务
或者,可以从 systemd 用户服务中运行 xorg。这很好,因为可以使其他与 X 相关的单元依赖于 xorg 等,但另一方面,它也有一些缺点,如下所述。
xorg-server 通过两种方式与 systemd 集成
不幸的是,为了能够以非特权模式运行 xorg,它需要在会话内运行。因此,目前将 xorg 作为用户服务运行的障碍是它必须以 root 权限运行(如 1.16 之前那样),并且无法利用 1.16 中引入的非特权模式。
GetSessionByPID
并使用自己的 pid 作为参数来获取此信息。请参阅 此线程 和 xorg 源代码。xorg 似乎可以被修改为从它附加到的 tty 获取会话,然后它可以从会话外的用户服务以非特权模式运行。这是从用户服务启动 xorg 的方法
1. 通过编辑 /etc/X11/Xwrapper.config
,使 xorg 以 root 权限为任何用户运行。这建立在 Xorg#以 Root 身份运行 Xorg 的基础上,增加了不必从物理控制台执行此操作的规定。也就是说,allowed_user
的默认值 console
被 anybody
覆盖;请参阅 Xorg.wrap(1)。
/etc/X11/Xwrapper.config
allowed_users=anybody needs_root_rights=yes
2. 将以下单元添加到 ~/.config/systemd/user
~/.config/systemd/user/xorg@.socket
[Unit] Description=Socket for xorg at display %i [Socket] ListenStream=/tmp/.X11-unix/X%i
~/.config/systemd/user/xorg@.service
[Unit] Description=Xorg server at display %i Requires=xorg@%i.socket After=xorg@%i.socket [Service] Type=simple SuccessExitStatus=0 1 ExecStart=/usr/bin/Xorg :%i -nolisten tcp -noreset -verbose 2 "vt${XDG_VTNR}"
其中 ${XDG_VTNR}
是 xorg 将启动的虚拟终端,可以硬编码在服务单元中,也可以使用以下命令在 systemd 环境中设置
$ systemctl --user set-environment XDG_VTNR=1
3. 确保按照 上面 解释的配置 DISPLAY
环境变量。
4. 然后,要为显示器 0 和 tty 2 启用 xorg 的套接字激活,可以执行
$ systemctl --user set-environment XDG_VTNR=2 # So that xorg@.service knows which vt use $ systemctl --user start xorg@0.socket # Start listening on the socket for display 0
现在,运行任何 X 应用程序都将自动在虚拟终端 2 上启动 xorg。
环境变量 XDG_VTNR
可以在 .bash_profile
中的 systemd 环境中设置,然后可以启动任何 X 应用程序(包括窗口管理器)作为依赖于 xorg@0.socket
的 systemd 单元。
一些用例
窗口管理器
要将窗口管理器作为 systemd 服务运行,您首先需要运行 #将 Xorg 作为 systemd 用户服务。在下面,我们将使用 awesome 作为示例
~/.config/systemd/user/awesome.service
[Unit] Description=Awesome window manager After=xorg.target Requires=xorg.target [Service] ExecStart=/usr/bin/awesome Restart=always RestartSec=10 [Install] WantedBy=wm.target
[Install]
部分包含 WantedBy
部分。当使用 systemctl --user enable
时,它会将其链接为 ~/.config/systemd/user/wm.target.wants/window_manager.service
,从而允许在登录时启动它。建议启用此服务,而不是手动链接它。持久终端复用器
您可能希望默认情况下将终端复用器(如 screen 或 tmux)在后台自动运行,而不是登录到窗口管理器会话。
创建 以下内容
~/.config/systemd/user/multiplexer.target
[Unit] Description=Terminal multiplexer Documentation=info:screen man:screen(1) man:tmux(1) After=cruft.target Wants=cruft.target [Install] Alias=default.target
将登录与 X 登录分离很可能仅对那些启动到 TTY 而不是显示管理器的人员有用(在这种情况下,您可以简单地将您在 mystuff.target
中启动的所有内容捆绑在一起)。
依赖项 cruft.target
,如上面的 mystuff.target
,允许启动应在多路复用器启动之前运行的任何内容(或者您希望在启动时启动的任何内容,而与时间无关),例如 GnuPG 守护程序会话。
然后,您需要为您的多路复用器会话创建一个服务。这是一个示例服务,使用 tmux 作为示例,并获取 gpg-agent 会话,该会话将其信息写入 /tmp/gpg-agent-info
。当您启动 X 时,此示例会话也将能够运行 X 程序,因为已设置 $DISPLAY
~/.config/systemd/user/tmux.service
[Unit] Description=tmux: A terminal multiplexer Documentation=man:tmux(1) After=gpg-agent.service Wants=gpg-agent.service [Service] Type=forking ExecStart=/usr/bin/tmux start ExecStop=/usr/bin/tmux kill-server Environment=DISPLAY=:0 EnvironmentFile=/tmp/gpg-agent-info [Install] WantedBy=multiplexer.target
启用 tmux.service
、multiplexer.target
和您创建的要由 cruft.target
运行的任何服务,启动 user@.service
照常,您应该就完成了。
注销时杀死用户进程
Arch Linux 构建的 systemd 软件包带有 --without-kill-user-processes
,默认情况下将 KillUserProcesses
设置为 no
。此设置会导致用户进程在用户注销时不被杀死。要更改此行为以在用户注销时杀死所有用户进程,请在 /etc/systemd/logind.conf
中设置 KillUserProcesses=yes
。
请注意,更改此设置会破坏终端复用器,例如 tmux 和 GNU Screen。如果您更改此设置,您仍然可以使用终端复用器,方法是使用 systemd-run
,如下所示
$ systemd-run --scope --user command args
例如,要运行 screen
,您需要执行
$ systemd-run --scope --user screen -S foo
使用 systemd-run
将使进程在注销后继续运行,前提是用户在系统中的其他位置至少登录过一次,并且 user@.service
仍在运行。
在用户注销所有会话后,默认情况下,user@.service
也将被终止,除非用户启用了“持久化” [8]。为了有效地允许用户运行长期任务,即使他们完全注销,也必须为他们启用持久化。有关详细信息,请参阅 #自动启动 systemd 用户实例 和 loginctl(1)。
故障排除
运行时目录 '/run/user/1000' 不应由 UID 1000 拥有,但实际上并非如此
systemd[1867]: pam_systemd(systemd-user:session): Runtime directory '/run/user/1000' is not owned by UID 1000, as it should. systemd[1867]: Trying to run as user instance, but $XDG_RUNTIME_DIR is not set
如果您看到诸如此类的错误,并且您的登录会话已中断,则可能是您系统上的另一个系统(非用户)服务正在创建此目录。例如,如果您使用 docker 容器,该容器具有到 /run/user/1000
的绑定挂载,则可能会发生这种情况。要解决此问题,您可以修复容器(删除挂载),或禁用/延迟 docker 服务。