Thursday, May 24, 2012

Boot to Gnome on Debian

以前剛準備要從 Windows 換到 GNU/Linux 在 Linux 連線板爬文時, 才知道有些事並不是那麼理所當然. 例如這些 Windows 上的常識

  • 系統升級要重灌
  • 系統壞掉要重灌
  • 換主機板要重灌
  • 換硬碟要重灌
  • 用久了要重灌 (那個時代)
  • 更新要重開機
  • 安裝很多軟體後要重開機
  • ...
在 GNU/Linux 上並不一定適用.

當時仿效 vgod 大神, 沒想太多就找了 Debian potato (Linux 2.2!) 來安裝. 用沒多久發現有些套件在 potato 上沒有或太舊, 很快就決定從 stable 升級到 testing; 再從 testing 升到 unstable. 除了最開始安裝 Debian potato 外, 後來都只用到 apt-get. 等到後來買了筆電第二次裝 Debian, 也已經是 7 年後的事了. 而這中間的更新除非有動到 kernel, 不然也大都也沒有重開機. 頂多只是重啟 X server. 即使換了 CPU 跟主機板, 還是舊硬碟的系統繼續用. 甚至換硬碟時也只用到 cp 跟 grub-install 來搬系統. 一切就像 Linux 板說地一樣美好.

期間當然有很多次更新後有東西壞掉或不能開機, 但總是幸運地能夠修好. 能夠做到這點很重要的因素就是對開機流程有基本認識

知道系統是怎麼從 bootloader 進到桌面環境, 系統更新後出了問題才不會束手無策. 雖然有時候這比重灌或重開機更花時間.

Bootloader: grub

在用傳統 BIOS 的環境, grub 由 boot.img 跟 core.img 兩部份組成. 因為 MBR 大小的限制, 只有 boot.img 會被寫到 MBR. 也就是說當 BIOS 把 MBR 讀到記憶體並跳過去執行時, 實際上是在跑 boot.img. 它做的事只是把 core.img 最前頭的部份讀到記憶體就再跳過去. 而這個最前頭的部份才是真正把 core.img 剩餘的部份讀完, 並接著執行後才秀出我們熟悉的開機選單. 換句話說, grub 的主體是 core.img. boot.img 只是為因應 MBR 大小限制, 被設計來載入 core.img 用的.

在系統中, grub-setup 可以用來把 boot.img 寫到 MBR. core.img 則可以用 grub-mkimage 產生. 之所以需要動態產生 core.img 是因為依硬碟的 partition table type 跟 filesystem 不同, 我們需要不同的 core.img. 不過一般只要執行 grub-install 就會幫我們都處理好.

系統更新後如果重開機不能進 grub 或 kernel, 通常是 grub 壞了. 這時得用 Debian 安裝光碟 或 USB 碟 (或其它像是 SystemRescueCd) 開機. 開好機後進 console 把原來的 root mount 起來重跑一次 grub-install 常常就能解決問題. 如果是因為新版 grub 本身的問題, 那要先找舊版的 grub (或許還在 /var/cache/apt/archives ), 裝好再跑 grub-install.

而在 EFI 的環境, grub 只會有 core.img. grub-install 會把 core.img 複製到 EFI system partition 並設定好 EFI boot manager. 要注意的是在執行 grub-install 前需要先把 EFI system partition mount 到 /boot/efi 並且載入 efivars kernel module.

Kernel & initramfs

bootloader 會負責把 kernel 跟 initramfs 載入到記憶體並執行. 詳細的作法規範在 Documentation/x86/boot.txt.

沒特別設定時 kernel image 本身也會帶一份空的 initramfs, 所以這裡的 initramfs 指的是 external initramfs. 它是獨立的一個檔案. bootloader 可以透過舊有的 initrd 機制把 external initramfs 指定給 kernel.

kernel 初始化好系統後會去執行第一個 user space 的程式: init. 在 initramfs 裡有 /init 這個執行檔時 kernel 會去執行它. 否則 kernel 會把 root mount 起來, 並執行 root 裡的 /sbin/init.

initramfs 的 /init 如果存在時, 它在做的通常也是把 root mount 起來, 並執行 root 裡 的 /sbin/init. 之所以會需要 initramfs 是因為有些環境, 例如 root 在遠端時, kernel 做這些事比較麻煩.

kernel 沒辦法進到 user space 常常是因為有缺 driver. 最常見的應該是 SATA 或 filesystem driver 沒有被編進 kernel 或者沒有在 initramfs 裡.

init: sysvinit

sysvinit 之後可能會被 systemd 取代, 不過在這之前還是得熟悉 sysvinit.

sysvinit 的設定檔在 /etc/inittab. 在 Debian 裡它主要會去執行 /etc/rcS.d 跟 /etc/rc2.d 裡所有的 script, 並讓使用者可以從 tty1 到 tty6 登入系統. 這些 script 在做的不外乎是 mount filesystem, 對網路, 系統時間等做基本設定, 還有啟動系統服務. 很多 script 會到 /etc/default 底下讀設定, 所以可以透過修改 /etc/default 裡的檔案來改變這些 script 的行為.

如果開機過程在這個部份出問題時, 可以試著在 kernel command line 加上 single 進入單人模式或 init=/bin/sh 讓 shell 當 init process. 因為這邊都是 script, 有問題時也比較容易解決.

一個多重開機到 Windows 後再回來可能會出現的問題是系統時間差了 8 小時. 這是因為 Debian 在把系統時間寫到 RTC 時會用 UTC 時間, 而 Windows 會把 RTC 的時間當成是 UTC+8. 如果遇到這個問題可以到 /etc/default/rcS 把 UTC 設定 no.

Display Manager: gdm

gdm 會把 X server 跑起來, 並顯示我們熟悉的圖形登入畫面. 它會掃描

/usr/share/gdm/BuiltInSessions 跟
/usr/share/xsessions
裡面的 .desktop 檔找出系統提供的 session. 當我們選好 session 並成功登入時, 它會執行
/etc/gdm/Xsession session-name
/etc/gdm/Xsession 這個 script 會讀入 /etc/X11/Xsession.d 底下所有的 script. 它們在最後通常會執行類似這樣的命令
ssh-agent dbus-launch some-session-manager
讓 session manager 帶出桌面環境來.

如果想要額外設定別的環境變數的話, 可以寫在 ~/.xsessionrc. 或者如果不想跑系統現有的 session 的話, 也可以自己寫 shell script 存成 ~/.xsession. 如此 /etc/gdm/Xsession 最後會改執行 ~/.xsession 而不是 some-session-manager. 這些細節可以在 /etc/X11/Xsession.d 裡看到.

在 Debian 裡輸入法現在是透過 im-config 在管理. 它也是利用 Xsession.d 的機制在登入的過程中被跑起來.

Session Manager: gnome-session

登入後的桌面環境是由 session manager 帶出來的. 一般情況 gnome-session 會參照 /usr/share/gnome-session/sessions/gnome.session 執行 gnome-shell 與 gnome-settings-daemon. 它同時也會執行

~/.config/autostart
/usr/share/gnome/autostart
/etc/xdg/autostart
裡所有沒被 disable 的程式. 後者可以利用 gnome-session-properties 做增減.

gnome-shell 就是眼睛看得到的部分, 這邊不提. gnome-settings-daemon 的功能之一是它會監看某些 GSettings key 並做回應. 例如在 terminal 執行

$ gsettings set org.gnome.desktop.background picture-uri file://<path-to-image>
gnome-settings-daemon 會收到這項改變, 並且把桌面的背景圖片換成新指定的圖片. 或者執行
$ gsettings set org.gnome.desktop.interface gtk-theme <theme-name>
gnome-settings-daemon 會把新的 GTK+ theme name 廣播出去. 所有 GTK+-based 的程式也會自動換用新的 theme.

No comments: