systemd/用户 - Arch Linux 中文维基

跳至內容

# systemd/用户

出自 Arch Linux 中文维基
< Systemd

翻譯狀態：

- 本文（或部分內容）譯自 Systemd/User，最近一次同步於 2023-11-20，若英文版本有所更改，則您可以幫助同步與翻譯更改的內容。
- 您可以在 ArchWiki 的對應頁面找到本文翻譯的原始修訂歷史。
- 本文可能與英文原文存在出入。

相關文章

- systemd
- Automatic login to virtual console
- Start X at login

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) § 用户服务管理器管理的单元.

注意：

- `systemd --user` 實例是針對每個用戶處理的，而不是針對會話。這樣做的原理是用戶服務處理的大部分資源，像 socket 或狀態文件是針對每個用戶的（存活於用戶的主目錄下）而不是會話。這意味著所有的用戶服務是獨立於會話之外運行的。最終，我們得出結論：基於會話運行的程序可能會導致用戶服務中斷。systemd 處理用戶會話的方式是非常生硬的（pretty much in flux）。 單會話支持的進展參考 [1] 和 [2] 。
- `systemd --user` 和 `systemd --system` 運行於不同的進程裡面，所以用戶單元不能引用或依賴於系統單元或其他用戶的單元。

## 基礎設置

所有的用戶單元都位於 `~/.config/systemd/user` 路徑下。 如果你想在首次用戶登錄時運行單元，對想要自動啟動的單元執行 `systemctl --user enable unit` 即可。

提示：如果不是只讓發出"systemctl"命令的用戶啟用某個service單元，而是想要讓所有用戶都生效，請以root權限執行`systemctl --global enable service` 命令。

### 環境變量

systemd 用戶實例不會繼承類似 `.bashrc` 中定義的環境變量。systemd 用戶實例有三種設置環境變量的方式：

1. 對於有 `$HOME` 目錄的用戶，可以在 `~/.config/environment.d/` 目錄中新建一個".conf"文件，然後在其中寫入格式為`NAME=VAL`這樣的行。這些設置只對指定用戶的用戶單元有效 。更多信息可以參考 environment.d(5) 。
2. 使用 `/etc/systemd/user.conf` 文件中的 `DefaultEnvironment` 選項。這個配置在所有的用戶單元中可見。
3. 在 `/etc/systemd/system/user@.service.d/` 下增加配置文件設置。 這個配置在所有的用戶單元中可見。
4. 在任何時候， 使用 `systemctl --user set-environment` 或 `systemctl --user import-environment`. 對設置環境變量之後啟動的所有用戶單元有效，但已經啟動的用戶單元不會生效。
5. 使用由 dbus提供的 `dbus-update-activation-environment --systemd --all` 命令。和`systemctl --user import-environment`有同樣的效果，但是會影響D-Bus會話。你可以把這個添加到shell初始化文件的末尾。
6. 對於用戶環境的「全局」環境變量，可以使用會被某些生成器解析的`environment.d` 目錄。 更多信息可以參考environment.d(5)和 systemd.generator(7)。
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 服務是 沒有 會話的， 它們在 logind 狀態之外運行， 所以不要在 lingering 中啟用自動登錄的功能，這會導致 會話中斷。

## 開發用戶單元

通用的 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

注意：UID 小於 1000 的用戶的日誌不會被 journald 記錄，需要的話可以 重定向 所有內容至系統日誌。

## 臨時文件

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

這篇文章的某些內容需要擴充。

原因：Cover `graphical-session.target`: systemd.special(7) § Special Passive User Units, [3]. (在 Talk:Systemd/用戶 中討論)

使用 systemd 單元來運行 Xorg 有好幾種方法，下面介紹其中兩種，一種是啟動一個新的用戶會話，在裡面運行 Xorg 服務，另外一種是用 systemd 用戶服務啟動 Xorg。

### 沒有顯示管理器的情況下自動登錄到 Xorg

本文或本章節的事實準確性存在爭議。

原因： 這樣設置會導致用戶有2條 D-Bus 總線，一條用於桌面，另一條用於 systemd。為什麼不共用 systemd 的那一條？（在 Talk:Systemd/用戶 中討論）

這種方法通過一個系統單元將用戶會話帶起來，並在用戶會話裡面啟動一個 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 的方法:

- 可以在無特權模式下運行，設備管理由 logind 代為管理(參考 Hans de Goede 的這個提交)。
- 可以實現通過 socket 激活服務 (參考這個提交)。

但非常不幸，xorg 的無特權模式需要在用戶會話裡面運行。所以，xorg 的用戶服務只能在 root 權限下運行(和 1.16 版本之前一樣)，而不能使用 1.16 版本提供的無特權模式。

注意：這並不是 logind 強加的限制，而是 xorg 需要知道它將要接管的是哪個會話，而現在它通過調用 logind's `GetSessionByPID` 來獲取這個信息(使用 xorg 自身的 pid 作為參數)。參見這個話題 和 xorg 源碼. 看上去如果 xorg 通過其依附的 tty 來獲取會話信息的話，這個問題將得到解決。
警告：在 xorg 1.18 上 socket 激活存在問題，客戶端會觸發激活死鎖。具體查閱上游報告 [4]。臨時策略是啟動一個不帶 socket 激活的 xorg 伺服器，並在伺服器啟動一段時間後客戶端再連接，確保連接時伺服器已經啟動。目前來看沒有一個可靠機制來判斷 X 伺服器是否啟動完畢。

下面是從用戶服務運行 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

注意：xorg應該在用戶登錄的虛擬終端上運行，否則 logind 會認為會話沒有激活。

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`。

警告：當前，通過用戶服務啟動窗口管理器意味著它是在會話之外運行的，這將帶來以下問題: break the session. 但是，systemd 的開發者看上去更傾向於這樣(?)。參見 [5] 和 [6]

## 一些用例

### 窗口管理器

以一個 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` 來使它在登錄時啟動。推薦採用啟用服務的方法來代替手動創建軟連結。

### 終端復用器持久化

比起默認登入一個窗口管理器，你可能更想要自動在後台運行一個終端復用器（比如 GNU 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

啟用 `tmux.service`、`multiplexer.target` 和由 `cruft.target` 運行的服務，像往常一樣啟動 `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 服務來修復此問題。

## 參閱

- KaiSforza's Bitbucket wiki
- Zoqaeski's units on GitHub
- Arch forum thread about changes in systemd 206 user instances

分類：​

- 系統管理

隱藏分類：​

- 使用「模板:準確性」標記的頁面或章節

systemd/用户
新增話題