2010年2月15日 星期一

grub 速記

GNU GRUB(簡稱grub,stands for GRand Unified Bootloader)已經成為大部分linux distributions的預設開機管理程式了。 從前我們可能會討論grub跟lilo應用上的差異,現在幾乎只能看見關於grub跟grub2的討論了。 他的彈性與強大眾所皆知,記得在數年前Solaris OS剛釋出免費的x86版本,以及隨後幾周發布的opensolaris都是用grub來當作預設的開機管理程式。 本文就是簡單對於grub以及其設定檔作非常簡單的筆記。

一個最陽春、不包含註解又能成功開機的grub,其menu.lst(RH系的linux為grub.conf,為menu.lst的一個軟連結),大致如下:

default 0
timeout 8

title Linux Mint 8 Helena
root (hd0,1)
kernel /boot/vmlinuz root=LABEL=ROOT ro
quiet

簡單的說,timeout是倒數秒數;default是倒數結束後預設啟動第幾個entry,不用多說,跟硬碟的代號一樣,從0開始起算。這個範例可以開機的情況相當侷限:
首先,/boot跟/必須在同一個partition。
第二,核心的名稱必須為vmlinuz且位於/boot底下。
第三,/boot必須是ext2/3的檔案系統。

事實上,vmlinuz可以是一個連結。以debian系統來說,你每安裝一個新的kernel之後,她會自動幫你為最新的kernel建一個名稱為vmlinuz的軟連結, 好方便你忘記落落長的核心名稱時,敲個vmlinuz就可以指到正確的核心。就我遇到過的linux而言,似乎沒有一套linux像debian一樣貼心哩。
kernel後面的root並不是指作業系統的root,而是指核心的位置。如同在fstab定義的方式,root可以有三種方式去定義:device ID/UUID/Label。 例如我的/跟/boot在同一個分割區,他的uuid是fa2028ac-0d01-4251-bcd7-07a3df26530d,label是ROOT,裝置代號是/dev/sda2,此時root可以有三種表示方法:

  1. root=/dev/sda2
  2. root=LABEL=ROOT
  3. root=uuid=fa2028ac-0d01-4251-bcd7-07a3df26530d
我個人比較偏好使用Label,因為我經常使用usb外接硬碟開機,啟動時有些機器未必符合我的device.map的設定;uuid雖然是不太可能重複的一組id,但是長度太長,整個檔案顯得很吵雜; label則是一個折衷的好辦法,一個便於自己記憶辨認,又不至於重複性太高,也不會讓設定檔變得難以閱讀。例如台灣人最愛使用的fedora,就是使用LABEL=/1當成指定kernel所在分割區的方法。

但是,對於非ext2/3的filesystems,核心沒有外加模組可是無法辨認這些檔案系統。沒辦法辨認檔案系統,就無法從硬碟裡載入kernel並進行開機程序。因此需要動用到Initial Ram Disk(簡稱initrd,一個在記憶體上運行的虛擬的檔案系統,) 來幫助我們開機。initrd很小,約莫8MB左右,裡面包含核心啟動所需要的硬體模組,將這些modules載入完畢之後會執行真正的init,並根據/etc/inittab的設定展開了一系列的開機流程。也就是說,假如你討厭使用initrd, 你就必須手動編譯你的核心,把這台電腦上所有的硬體驅動全部都編譯到核心裡(當然,最重要的檔案系統一定要記得編譯進去!),這樣就可以不用使用到initrd。如果你不想自己重編你的kernel,你就必須指定initrd的位置:

initrd /boot/initrd.img

initrd.img的名稱是配合kernel名稱的,例如有一個vmlinuz-2.6.31-19-generic的kernel就會有一個initrd.img-2.6.31-19-generic的img。

windows的開機很簡單,不過grub不支援ntfs,所以很自然的利用chianload的方式來做:

title Windows 95/98/NT/2000
root (hd0,0)
makeactive
chainloader +1
其中,root可以用rootnoverify代替,表示讀取此分割區時不校驗。
事實上,chainloader這個grub指令非常好用,不光是用在啟動windows,當年還沒有虛擬機器時,想要用一台電腦玩很多套linux時,每一套linux都會預設把boot loader寫到mbr裡,導致最後你的grub清單會異常複雜, 因為所有找得到的kernel都被寫成兩串。如果你可以把每一套linux的開機管理程式都裝在其boot sector(每個分割區的前512bytes),在透過chianloader的引導,你就可以開到正確的linux,不用擔心某套linux核心更新之後,你要自己手動修改kernel版本號碼。
當然這已經不再是很值得被記憶的好方法,因為用虛擬機器實在是太過方便了。

幾個跟grub有關的commands:

  1. update-grub
  2. 產生一個新的menu.lst。如果檔案已經存在,將會被覆寫過去。

  3. update-initramfs
  4. 利用-c產生一個新的initrd,-u更新一個initrd,-d移除之。
    另外,你也可以用makeinitramfs來產生一個新的initrd;redhat系列則是mkinitrd。

  5. grub-install
  6. 安裝grub到某個位置,例如 grub-install /dev/sdb或是 grub-install '(hd1)'

  7. grub-mkconfig
  8. 產生對應kernel版本的config檔。

  9. grub-md5-crypt
  10. 將grub設定md5加密過的密碼。

  11. grub-mkdevicemap
  12. 產生一個對應當前裝置的device.map檔。

幾個在grub shell裡面的commands:

  1. setup (hdx,y)
  2. 把grub安裝在該partition/disk裡。

  3. find /boot/grub/menu.lst
  4. 藉由find指令,你可以得知kernel或是其他跟grub有關的檔案放的位置,幫助你取得正確的kernel root位置。

  5. map (hd0) (hd1)
    map (hd1) (hd0)
    此map的指令比較像是mirror的味道,在某些很舊的系統(例如DOS)必須要是第一顆硬碟才能開機,此時就可以用map騙過她來啟動系統。

  6. hide (hd0,1)
    隱藏這個partition。通常在擁有多個windows系統的情形下,先安裝的人先取到C drive,後安裝的windows其系統磁區卻只能拿到D,很多軟體跑起來會有問題。
    利用hide指令,可以讓windows看不到其他windows的系統磁區。

  7. lock
    如果某個entry裡面寫了lock,就表示需要密碼才能開機進入。

  8. md5crypt
    效果如同grub-md5-crypt,只是一個是外部命令,一個是grub shell內建命令。

我們用一個簡單的case來試著撰寫一個menu.lst。假設你需要安裝四套作業系統,其中一套windows xp,另一套windows vista,這兩套分別裝在第二顆硬碟的第一、二個主要分割區; Debian跟openSuSE裝在第一顆硬碟的第四、五個分割區。把Debian的grub安裝在MBR,/boot跟/在同個分割區;OpenSuSE的grub則安裝在他自己的boot sector, 但是他的/boot則是位於第六個分割區。假設第二顆硬碟每個分割區都有被指定來使用,沒有跳號問題,預設用Debian開機,倒數5秒並且grub用密碼保護,若要開進任何一個windows都需要輸入密碼才能開機; 此時我們的menu.lst該如何撰寫呢?

default 0
timeout 5
password --md5 $1$qQ7aR/$BAJyOwZW961kWL7vVGAzm

title Debian GNU/Linux, kernel 2.6.26-1-686-bigmem
root (hd0,3)
kernel /boot/vmlinuz-2.6.26-1-686-bigmem root=LABEL=DEBROOT ro quiet
initrd /boot/initrd.img-2.6.26-1-686-bigmem

title Debian GNU/Linux, kernel 2.6.26-1-686-bigmem (single-user mode)
root (hd0,3)
kernel /boot/vmlinuz-2.6.26-1-686-bigmem root=LABEL=DEBROOT ro single
initrd /boot/initrd.img-2.6.26-1-686-bigmem

title Chainload to OpenSuSE 11.2
root (hd0,5)
chainloader +1

title OpenSuSE 11.2 2.6.31.8-0.1-default
root (hd0,4)
kernel /boot/vmlinuz-2.6.31.8-0.1-default root=/dev/sda6 resume=/dev/sda5 splash=silent quiet showopts vga=0x317
initrd /boot/initrd-2.6.31.8-0.1-default

title Windows XP
lock
rootnoverify (hd0,0)
makeactive
hide (hd0,1)
chainloader +1

title Windows vista/Windows 7
lock
rootnoverify (hd0,1)
makeactive
hide (hd0,0)
chainloader +16

這個範例其實很簡單,要開進其他的linux也可以用chainload的方式,並跳進另一套grub或是其開機管理程式。唯一要注意的就是,如果/boot分割區是獨立於/之外,需注意root後面的參數以及kernel裡面的root意義大不相同。

關於這個速記,您可以從鳥哥的網站查到更多關於grub以及開機流程較詳盡的認識。

2 則留言:

匿名 提到...

大大您好:

小弟是剛要學習linux的菜鳥,
手上有一台老筆電想說剛好可以安裝centos當練習機,不過因為光碟機挑片很嚴重在加上只支援USB FDD開機,所以在網路找到grub4dos的相關資料,決定利用它來安裝CENTOS,不過遇到一些問題想請教您,

設備基本狀況:
硬碟分割成三個磁碟,C、D、未格式化
作業系統:windows xp
C磁區為FAT32格式、D磁區為NTFS,

準備動作:
1、C磁區存放需要的檔案,
  解壓縮後的centos、grub.exe、
menu.lst、vmlinuz、initrd.img。
2、menu.lst的撰寫多增加以下內容:

title install centos
root (hd0,0)
kernel /vmlinuz
initrd /initrd.img

基本上進入grub的選單後有出現install centos,但光棒無法移到該位置讓我選擇,

想請問大大是否了解問題可能出在哪裡,
那如果直接打指令應該如何執行呢?

一個剛要進入linux的菜鳥

maxsolar 提到...

使用Unetbootin,可以把抓下來的centos iso檔安裝到USB裡面,你就可以使用usb開機了。
我已經近八年沒碰windows系統了,以後也不會在碰,很抱歉我不懂grub4dos這類東西^__^