systemd/用户
systemd 會給每個用户生成一個 systemd 實例,用户可以在這個實例下管理服務,啟動、停止、啟用以及禁用他們自己的單元。這個能力大大方便了那些通常在特定用户下運行的守護進程和服務,比如 Music Player Daemon, 還有像拉取郵件等需要自動執行的任務。
工作原理[編輯 | 編輯原始碼]
從 systemd 226 版本開始,/etc/pam.d/system-login
默認配置中的 pam_systemd
模塊會在用户首次登錄的時候, 自動運行一個 systemd --user
實例。 只要用户還有會話存在,這個進程就不會退出;用户所有會話退出時,進程將會被銷毀。當#隨系統自動啟動 systemd 用户實例啟用時, 這個用户實例將在系統啟動時加載,並且不會被銷毀。systemd 用户實例負責管理用户服務,用户服務可以使用systemd提供的各種便捷機制來運行守護進程或自動化任務,如 socket 激活、定時器、依賴體系以及通過 cgroup 限制進程等。
和系統單元類似,用户單元可以在以下目錄找到(按優先級從低到高排序):
/usr/lib/systemd/user/
這裏存放的是各個軟件包安裝的服務。~/.local/share/systemd/user/
這裏存放的是HOME目錄中已安裝的軟件包的單元。/etc/systemd/user/
這裏存放的是由系統管理員維護的系統範圍的用户服務。~/.config/systemd/user/
這裏存放的是用户自身的服務。
當 systemd 用户實例啟動時,它會將用户個人的 default.target
帶起來。其他用户單元可以通過 systemctl --user
手動管理。參考 systemd.special(7) § 用户服务管理器管理的单元.
基礎設置[編輯 | 編輯原始碼]
所有的用户單元都位於 ~/.config/systemd/user
路徑下。 如果你想在首次用户登錄時運行單元,對想要自動啟動的單元執行 systemctl --user enable unit
即可。
systemctl --global enable service
命令。環境變量[編輯 | 編輯原始碼]
systemd 用户實例不會繼承類似 .bashrc
中定義的環境變量。systemd 用户實例有三種設置環境變量的方式:
- 對於有
$HOME
目錄的用户,可以在~/.config/environment.d/
目錄中新建一個".conf"文件,然後在其中寫入格式為NAME=VAL
這樣的行。這些設置只對指定用户的用户單元有效 。更多信息可以參考 environment.d(5) 。 - 使用
/etc/systemd/user.conf
文件中的DefaultEnvironment
選項。這個配置在所有的用户單元中可見。 - 在
/etc/systemd/system/user@.service.d/
下增加配置文件設置。 這個配置在所有的用户單元中可見。 - 在任何時候, 使用
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
等就是這種情況 )。
- 提示:如果想一次設置多個環境變量,可以寫一個配置文件,文件裏面每一行定義一個環境變量,用 "key=value" 的鍵值對表示,然後在你的啟動腳本裡添加
xargs systemctl --user set-environment < /path/to/file.conf
。
一般情況下,你需要設置 PATH
這個環境變量。
配置完成後,可以使用命令 systemctl --user show-environment
來驗證值是否正確。
Service 文件例子[編輯 | 編輯原始碼]
新建 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/bin:/usr/bin:/bin" Environment="EDITOR=nano -c" Environment="BROWSER=firefox" Environment="NO_AT_BRIDGE=1"
DISPLAY 和 XAUTHORITY[編輯 | 編輯原始碼]
任何一個 X 應用程式都需要使用 DISPLAY
來指示使用哪個顯示器,而 XAUTHORITY
則是保存了用户授權文件 .Xauthority
的路徑,X 應用需要用户授權文件中的 cookie 信息才能訪問 X Server。如果你想通過 systemd 單元啟動一個 X 應用,必須先設置這兩個環境變量。systemd 提供了一個腳本 /etc/X11/xinit/xinitrc.d/50-systemd-user.sh
,在 X 啟動的時候,將這些環境變量導入到 systemd 用户會話中。所以除非你不是通過正常的途逕啟動X,systemd用户服務應該已經包含了這兩個變量。
PATH[編輯 | 編輯原始碼]
通過 .bashrc
或者 .bash_profile
設置的環境變量,對 systemd 都是不可見的。 如果你改變了你的 PATH
變量,並且準備在 systemd 單元運行的應用中使用這個環境變量,你必須在 systemd 的環境中設置 PATH
。假設你在 .bash_profile
中設置了 PATH
,讓 systemd 感知到這個變化的最好方法是在修改 PATH
之後,加入以下行通知 systemd:
~/.bash_profile
systemctl --user import-environment PATH
- 不會影響導入
PATH
之前啟用的程序. - systemd 只有在處理絕對路徑二進制時才會使用設置的
PATH
。
pam_env[編輯 | 編輯原始碼]
環境變量可以通過 pam_env.so
模塊來設置。參閱環境變量#使用 pam_env 獲取配置詳情。
隨系統自動啟動 systemd 用户實例[編輯 | 編輯原始碼]
systemd 用户實例在用户首次登錄時啟動,並在最後一個會話退出時終止。 但有時候,對於一些不依賴於會話的用户進程,在系統啟動時加載用户實例,在會話全部結束時,也不停止用户實例是比較有用的。Lingering 就是用來實現這個的。 使用以下命令來啟用駐留:
$ loginctl enable-linger
為另一個用户啟用駐留:
# loginctl enable-linger username
開發用户單元[編輯 | 編輯原始碼]
通用的 systemd unit 文件編寫請參考 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) 的 SPECIFIERS 章節中,詳細介紹了各種變量, %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 守護,而需要用到 dbus.service
的 systemd 工具會自動連接到會話的 dbus 實例上。最後啟用 xlogin@username
服務來達成開機自動登錄。 整個用户會話都在 systemd 的作用域下運行,會話內的一切都能正常工作。
將 Xorg 作為 systemd 用户服務[編輯 | 編輯原始碼]
另外一種選擇是將 xorg 作為一個 systemd 用户服務。這是一種不錯的方案,因為其他的 X-related units 可以依賴於 xorg 服務。 但另一方面,這個方案存在某些倒退,這在下面會提到。
xorg-server包 提供了兩種整合到 systemd 的方法:
但非常不幸,xorg 的無特權模式需要在用户會話裏面運行。所以,xorg 的用户服務只能在 root 權限下運行(和 1.16 版本之前一樣),而不能使用 1.16 版本提供的無特權模式。
GetSessionByPID
來獲取這個信息(使用 xorg 自身的 pid 作為參數)。參見這個話題 和 xorg 源碼. 看上去如果 xorg 通過其依附的 tty 來獲取會話信息的話,這個問題將得到解決。下面是從用户服務運行 xorg 的步驟:
1. 通過編輯 /etc/X11/Xwrapper.config
文件,允許所有用户使用 root 權限運行 xorg。這是在 Xorg#Xorg as Root 的基礎上,無需用户在實際控制台上登錄,而是將 allowed_user
的默認 console
控制台定義成了 anybody
。參見 Xorg.wrap(1)。
/etc/X11/Xwrapper.config
allowed_users=anybody needs_root_rights=yes
2. 把下面 systemd 單元加到 ~/.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 將要運行的虛擬終端,可以在服務單元文件裏面硬編碼,也可像下面那樣在環境變量裡指定:
$ systemctl --user set-environment XDG_VTNR=1
3. 確保 DISPLAY
環境變量已經配置,參考這裏.
4. 接下來,執行以下命令,使得 xorg 在 display 0 和 tty 2 上可以通過 socket 激活:
$ systemctl --user set-environment XDG_VTNR=2 # 这样 xorg@.service 知道该用哪个虚拟终端 $ systemctl --user start xorg@0.socket # 开始为 display 0 监听 socket
現在,在 tty 2上運行任意的X應用,xorg 都會自動啟動。
可以在 .bash_profile
裏面把環境變量 XDG_VTNR
設置到 systemd 環境裏面。在這之後,你可以使用 systemd 單元啟動任意的X應用,包括窗口管理器。當然,這些 systemd 單元必須依賴於 xorg@0.socket
。
一些用例[編輯 | 編輯原始碼]
窗口管理器[編輯 | 編輯原始碼]
以一個 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 會話,這個 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
Enable tmux.service
、multiplexer.target
和由 cruft.target
運行的服務,像往常一樣 start user@.service
即可。
在註銷後結束用户進程[編輯 | 編輯原始碼]
Arch Linux 用 --without-kill-user-processes
參數編譯了 systemd包 包,默認下 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
只有在用户在某處登錄過系統至少一次,且 user@.service
仍在運行的情況下,使用 systemd-run
可以保持進程在用户註銷後繼續運行。
用户註銷所有會話後,默認 user@.service
也會終止,除非用户啟用了 "lingering" [7] 。為了讓用户在註銷後也能運行長期任務,需要為他們啟用 lingering。詳情參閱 #隨系統自動啟動 systemd 用户實例和 loginctl(1)。
疑難解答[編輯 | 編輯原始碼]
Runtime directory '/run/user/1000' is not owned by UID 1000, as it should[編輯 | 編輯原始碼]
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 服務來修復此問題。