![]() |
即使您的工作不要求您每天多次重新啟動(dòng)您的 Linux 機(jī)器,等待系統(tǒng)啟動(dòng)也實(shí)在是一件枯燥的事情。因而有了 kexec。本質(zhì)上講,kexec是一個(gè)讓您可以重新啟動(dòng)到一個(gè)新 Linux 內(nèi)核的快速重新引導(dǎo)功能部件 --不必通過(guò)引導(dǎo)裝載程序。更快速的重新啟動(dòng)即使對(duì)于正常運(yùn)行時(shí)間并不是至關(guān)重要時(shí)也是有益的 --對(duì)那些每天都要多次重新啟動(dòng)機(jī)器的內(nèi)核和系統(tǒng)軟件開(kāi)發(fā)者來(lái)說(shuō)更可謂是救生者。kexec 當(dāng)前只能用在 x86 32 位平臺(tái)上。
隨著計(jì)算機(jī)系統(tǒng)變得更快更好,系統(tǒng)重新啟動(dòng)時(shí)間也需要跟上發(fā)展。實(shí)際上,隨著系統(tǒng)的處理器速度、存儲(chǔ)容量和資源性能變得更加先進(jìn)和復(fù)雜,重新啟動(dòng)的時(shí)間竟變長(zhǎng)了。雖然更長(zhǎng)的重新啟動(dòng)時(shí)間對(duì)每個(gè)人來(lái)說(shuō)只是一種刺激,但它對(duì)生產(chǎn)系統(tǒng)的影響是至關(guān)重要的,因?yàn)楦L(zhǎng)的重新啟動(dòng)時(shí)間意味著正常運(yùn)行時(shí)間的減少。除了影響系統(tǒng)對(duì)其用戶的可用性之外,更長(zhǎng)的重新啟動(dòng)時(shí)間對(duì)內(nèi)核和系統(tǒng)軟件開(kāi)發(fā)者來(lái)說(shuō)是一個(gè)主要的瓶頸,因?yàn)樗麄兠刻於家啻沃匦聠?dòng)他們的機(jī)器。
當(dāng)系統(tǒng)有很多松散分布的 SCSI 總線或者 ECC 校驗(yàn)的物理內(nèi)存時(shí),重新啟動(dòng)時(shí)間特別長(zhǎng)。測(cè)試結(jié)果顯示,重新啟動(dòng)過(guò)程中大部分時(shí)間消耗在固件(firmware)階段,在此階段,連接到系統(tǒng)的設(shè)備被識(shí)別出來(lái)并被初始化(要深入了解,請(qǐng)查看本文的參考資料部分)。自然,大部分試圖減少重新啟動(dòng)時(shí)間的努力都瞄準(zhǔn)重新啟動(dòng)過(guò)程的這個(gè)階段。其中一項(xiàng)努力帶來(lái)了 kexec 的發(fā)展,kexec 是一個(gè)可用于x86 平臺(tái)上 Linux 內(nèi)核的功能部件。使用kexec,您可以直接重新啟動(dòng)到另一個(gè)內(nèi)核,不再必須通過(guò)固件和引導(dǎo)裝載程序階段。跳過(guò)序列中最長(zhǎng)的部分大大減少了重新啟動(dòng)時(shí)間。
要理解 kexec,需要先具備 Linux 中引導(dǎo)過(guò)程的知識(shí)。Linux 中的引導(dǎo)過(guò)程有兩個(gè)階段:引導(dǎo)裝載程序階段和內(nèi)核階段。
引導(dǎo)裝載程序階段主要包括硬件階段、固件階段、第一級(jí)引導(dǎo)裝載程序和第二級(jí)引導(dǎo)裝載程序。引導(dǎo)過(guò)程從硬件加電啟動(dòng)開(kāi)始。一些初始化工作完成后,控制轉(zhuǎn)到固件。固件在一些體系結(jié)構(gòu)中也稱為“BIOS”,它去檢測(cè)系統(tǒng)上的各種設(shè)備,包括內(nèi)存控制器、存儲(chǔ)設(shè)備、總線橋和其他硬件。固件基于設(shè)置將控制移交給一個(gè)最小化的引導(dǎo)裝載程序,即大家所知的主引導(dǎo)記錄(master bootrecord),這個(gè)主引導(dǎo)記錄可能在磁盤(pán)驅(qū)動(dòng)器上,或者在可移動(dòng)媒體上,或者在網(wǎng)絡(luò)上。將控制移交給操作系統(tǒng)的實(shí)際工作由第二階段引導(dǎo)裝載程序(通常被簡(jiǎn)單地認(rèn)為是“引導(dǎo)裝載器(boot loader)”)執(zhí)行。這個(gè)引導(dǎo)裝載程序讓用戶可以選擇要裝載的內(nèi)核,將內(nèi)核和相關(guān)參數(shù)裝載到內(nèi)存,初始化內(nèi)核,設(shè)置需要的環(huán)境變量,并最終“運(yùn)行”內(nèi)核。
引導(dǎo)的下一個(gè)階段是內(nèi)核階段,此時(shí)內(nèi)核已經(jīng)獲得控制權(quán)。它設(shè)置需要的數(shù)據(jù)結(jié)構(gòu),檢測(cè)當(dāng)前在系統(tǒng)上的設(shè)備,裝載需要的設(shè)備驅(qū)動(dòng)程序,并初始化設(shè)備。引導(dǎo)過(guò)程的最后階段包括用戶級(jí)初始化。在這個(gè)階段,內(nèi)核檢查文件系統(tǒng)的完整性,掛載文件系統(tǒng),設(shè)置交換分區(qū)(或者交換文件),啟動(dòng)系統(tǒng)服務(wù),設(shè)置系統(tǒng)終端,并完成所有其他設(shè)置。
在系統(tǒng)重新啟動(dòng)時(shí),引導(dǎo)裝載階段之前要先關(guān)閉先前正在運(yùn)行的系統(tǒng)。這涉及到停止運(yùn)行的進(jìn)程,將高速緩沖存儲(chǔ)器內(nèi)容寫(xiě)回到磁盤(pán),?載文件系統(tǒng),然后執(zhí)行硬件的重啟。在本文的參考資料部分,您可以找到對(duì) Linux 中引導(dǎo)過(guò)程以及常見(jiàn)的引導(dǎo)相關(guān)概念的極好的描述。
kexec是 Linux 內(nèi)核的一個(gè)補(bǔ)丁,讓您可以從當(dāng)前正在運(yùn)行的內(nèi)核直接引導(dǎo)到一個(gè)新內(nèi)核。在上面描述的引導(dǎo)序列中,kexec跳過(guò)了整個(gè)引導(dǎo)裝載程序階段(第一部分)并直接跳轉(zhuǎn)到我們希望引導(dǎo)到的內(nèi)核。不再有硬件的重啟,不再有固件操作,不再涉及引導(dǎo)裝載程序。完全避開(kāi)了引導(dǎo)序列中最弱的一環(huán) -- 固件。這一功能部件帶來(lái)的最大益處在于,系統(tǒng)現(xiàn)在可以極其快速地重新啟動(dòng)。對(duì)企業(yè)級(jí)系統(tǒng)而言,kexec大大減少了重新啟動(dòng)引起的系統(tǒng)宕機(jī)時(shí)間。對(duì)內(nèi)核和系統(tǒng)軟件開(kāi)發(fā)者而言,kexec幫助您在開(kāi)發(fā)和測(cè)試成果時(shí)可以迅速重新啟動(dòng)系統(tǒng),而不必每次都要再經(jīng)歷耗時(shí)的固件階段。
kexec 補(bǔ)丁是 Eric Biederman 的作品,這個(gè)項(xiàng)目仍處在積極的開(kāi)發(fā)之中(查看參考資料部分以深入了解此項(xiàng)目以及如何對(duì)它做出貢獻(xiàn))。
顯然,由于這個(gè)功能部件涉及到操作系統(tǒng)如此多的敏感部分,需要特別細(xì)心以使其始終正確工作。對(duì) kexec 來(lái)說(shuō),最大的挑戰(zhàn)在于,在 Linux中,要重新引導(dǎo)到的新內(nèi)核需要位于內(nèi)存中與當(dāng)前正在運(yùn)行的內(nèi)核相同的位置。仍然在當(dāng)前內(nèi)核的上下文中運(yùn)行時(shí),用新內(nèi)核去替換內(nèi)存中現(xiàn)有的內(nèi)核,這是件困難的工作。另一個(gè)大問(wèn)題是,系統(tǒng)中設(shè)備的狀態(tài)。固件總是將設(shè)備初始化(或重啟)到已知的“sane”狀態(tài)。kexec繞過(guò)固件階段的事實(shí),意味著設(shè)備的狀態(tài)是不可靠的。
本文接下來(lái)的幾節(jié)將向您展示如何征服這些挑戰(zhàn),并展示如何實(shí)現(xiàn)到新內(nèi)核的直接引導(dǎo)。注意,當(dāng)前 kexec 只能用于 x86 32 位平臺(tái)。盡管將 kexec移植到其他平臺(tái)的工作正在進(jìn)行,但是還沒(méi)有可以用的代碼版本。因此,后面幾節(jié)中的所有技術(shù)細(xì)節(jié)都只特定于 x86 平臺(tái)。
Kexec有兩個(gè)組件。第一個(gè)是用戶空間組件,叫做“kexec-tools”。第二個(gè)是真正的內(nèi)核補(bǔ)丁。這兩部分實(shí)現(xiàn) kexec的兩個(gè)主要操作:將新內(nèi)核裝載到內(nèi)存并重新啟動(dòng)到它??梢匀菀椎孬@得一個(gè)啟用 kexec 的內(nèi)核。只需要下載 kexec-tools包和特定內(nèi)核的補(bǔ)?。ㄒ?jiàn) 參考資料部分的鏈接),編譯 kexec-tools 包以得到kexec工具,并將特定內(nèi)核的補(bǔ)丁加入到內(nèi)核樹(shù)中并重裝啟動(dòng)到它。當(dāng)然,您在編譯內(nèi)核時(shí)要確保選中CONFIG_KEXEC
選項(xiàng)。
如上所述,kexec 的使用包括(1)將重新啟動(dòng)到的內(nèi)核裝載到內(nèi)核中,然后(2)真正重新啟動(dòng)到它。裝載內(nèi)核的語(yǔ)法如下:
kexec -l <kernel-image> --append="<command-line-options>"
在這里,<kernel-image>
是您想要重新啟動(dòng)后的內(nèi)核文件,<command-line-options>
容納的是需要傳遞到新內(nèi)核的命令行參數(shù)。由于錯(cuò)誤的命令行選項(xiàng)可能會(huì)在重新啟動(dòng)時(shí)引發(fā)問(wèn)題,所以,確保合法值傳遞到重新啟動(dòng)的內(nèi)核的安全方法是傳遞/proc/cmdline
的內(nèi)容。
例如,如果您希望重新啟動(dòng)的內(nèi)核映像是 /boot/bzImage,/proc/cmdline
的內(nèi)容是"root=/dev/hda1"
,那么裝載內(nèi)核的命令將是:
kexec -l /boot/bzImage -append="root=/dev/hda1"
然后,為了真正重新啟動(dòng)已裝載的內(nèi)核,只需要輸入:
kexec -e
系統(tǒng)將立即重新啟動(dòng)。不同于正常的重新啟動(dòng)過(guò)程,在重新啟動(dòng)之前,kexec 不去執(zhí)行徹底停止系統(tǒng)。需要您在嘗試進(jìn)行 kexec 重新啟動(dòng)之前去殺死所有應(yīng)用程序并?載文件系統(tǒng)。
![]() |
的魔力
在 kexec 的發(fā)展過(guò)程中,最大的挑戰(zhàn)之一來(lái)自于這樣一個(gè)事實(shí):Linux 內(nèi)核要從內(nèi)存中固定的地址運(yùn)行。也就是說(shuō),新內(nèi)核需要安放于當(dāng)前內(nèi)核正在運(yùn)行的位置。在 x86 系統(tǒng)上,內(nèi)核位于物理地址 0x100000(虛擬地址為 0xc0000000,也叫做PAGE_OFFSET
)。用新內(nèi)核覆蓋舊內(nèi)核的工作分三個(gè)階段完成:
前兩個(gè)階段在內(nèi)核的“裝載”期間完成。第一個(gè)任務(wù)是解釋內(nèi)核映像文件的內(nèi)容。Kexec-tools已經(jīng)被編譯,因此,理論上您可以裝載并引導(dǎo)任何(甚至是非-Linux 的)內(nèi)核。當(dāng)前,只能引導(dǎo)到 elf32格式的內(nèi)核映像。這個(gè)文件被解析,內(nèi)核“段(segments)”被裝載到緩存中。這些段根據(jù)代碼的自然類型進(jìn)行分類。例如,在使用常見(jiàn)的“bzImage”內(nèi)核文件格式的情況下,典型的段是針對(duì) 16 位內(nèi)核代碼、32 位內(nèi)核代碼和初始 RAM磁盤(pán)代碼的段。用于追蹤這些段的結(jié)構(gòu)體被稱為 kexec_segment
,是一個(gè)相當(dāng)簡(jiǎn)單的結(jié)構(gòu)體:
struct kexec_segment { void *buf; size_t bufsz; void *mem; size_t memsz; }; |
結(jié)構(gòu)體的前兩個(gè)元素指向用戶空間緩存和它的大小,接下來(lái)兩個(gè)元素指明了段的最終目標(biāo)位置和它的大小。
一旦特定內(nèi)核文件格式的(kernel-file format-specific)模塊將映像裝載到用戶內(nèi)存,映像就會(huì)被sys_kexec
系統(tǒng)調(diào)用轉(zhuǎn)移到動(dòng)態(tài)內(nèi)核內(nèi)存。這個(gè)系統(tǒng)調(diào)用給每個(gè)從用戶空間傳遞而來(lái)的段分配動(dòng)態(tài)內(nèi)核頁(yè),并將段拷貝到這些內(nèi)核頁(yè)上。
kexec 還分配了一個(gè)用來(lái)存儲(chǔ)匯編代碼的小存根(small stub)的內(nèi)核頁(yè),稱為reboot_code_buffer
。這個(gè)存根完成用將要重新啟動(dòng)到的內(nèi)核來(lái)覆蓋當(dāng)前內(nèi)核并跳轉(zhuǎn)到它的實(shí)際工作。reboot_code_buffer
是惟一的存留在其最終存放位置的緩存。換句話說(shuō),它從它最初裝載到的位置開(kāi)始執(zhí)行。為此,在啟用了 MMU 的系統(tǒng)上,駐留這些代碼的頁(yè)被一致映射(identity mapped)。簡(jiǎn)單說(shuō),這需要用相同的物理和虛擬地址在init_mm
(內(nèi)核的頁(yè)表結(jié)構(gòu)體)中創(chuàng)建一個(gè)頁(yè)表?xiàng)l目。必須這樣做才能在重新啟動(dòng)操作中訪問(wèn)這一代碼段,如接下來(lái)將要論述的。
關(guān)于reboot_code_buffer
、各種段以及其他細(xì)節(jié)的信息通過(guò)使用kimage
結(jié)構(gòu)來(lái)保持:
struct kimage { kimage_entry_t head; kimage_entry_t *entry; kimage_entry_t *last_entry; unsigned long destination; unsigned long offset; unsigned long start; struct page *reboot_code_pages; unsigned long nr_segments; struct kexec_segment segment[KEXEC_SEGMENT_MAX+1]; struct list_head dest_pages; struct list_head unuseable_pages; }; |
當(dāng)前,這個(gè)結(jié)構(gòu)體中最重要的部分是指向容納映像的內(nèi)核內(nèi)存中緩存的segment[KEXEC_SEGMENT_MAX+1]
元素,和指向重新啟動(dòng)過(guò)程中使用的匯編存根的reboot_code_pages
。
一旦內(nèi)核映像被裝載,系統(tǒng)就可以重新啟動(dòng)到它。使用kexec -e
命令來(lái)開(kāi)始真正重新啟動(dòng)到新內(nèi)核。這個(gè)命令實(shí)際上是用sys_reboot
系統(tǒng)調(diào)用來(lái)通知內(nèi)核執(zhí)行重新啟動(dòng),但是使用了一個(gè)特別的- LINUX_REBOOT_CMD_KEXEC
標(biāo)志。
重新啟動(dòng)的系統(tǒng)調(diào)用遇到這個(gè)特別的標(biāo)志后,將控制權(quán)移交給machine_kexec()
函數(shù)。machine_kexec()
執(zhí)行的動(dòng)作完全針對(duì)特定體系結(jié)構(gòu)。在當(dāng)前的 x86 實(shí)現(xiàn)中,動(dòng)作的序列如下:
mm struct
切換到使用內(nèi)核的 init_mm
結(jié)構(gòu)體,以訪問(wèn)一致映射的 reboot_code_buffer
。 reboot_code_buffer
中。此匯編代碼可以在 relocate_new_kernel
例程中找到。 __KERNEL_DS
)值裝載到段寄存器,并使用 GDT 和 IDT 無(wú)效。 reboot_code_buffer
中的代碼,將一些重要的信息以參數(shù)的形式傳遞給新內(nèi)核,比如容納有內(nèi)核映像的源/目的地址的間接頁(yè),新內(nèi)核的起始地址, reboot_code_buffer
頁(yè)的地址,以及一個(gè)標(biāo)明系統(tǒng)是否啟用了物理地址擴(kuò)展(physical address extension,PAE)的標(biāo)志。 匯編存根代碼執(zhí)行下面的操作:
這一系列工作完成后,新內(nèi)核獲得控制權(quán),然后系統(tǒng)正常引導(dǎo)起來(lái)。
![]() |
要求高可用性的系統(tǒng),以及需要不斷重新啟動(dòng)系統(tǒng)的內(nèi)核開(kāi)發(fā)人員,都將受益于 kexec。因?yàn)?kexec 跳過(guò)了系統(tǒng)重新啟動(dòng)過(guò)程中最耗時(shí)的部分(也就是固件階段),所以重新啟動(dòng)變得非常快,可用性得到了提高。
kexec 在宕機(jī)轉(zhuǎn)儲(chǔ)(crash dump)工具中也得到了令人關(guān)注的應(yīng)用。Linux Kernel Crash Dumps(LKCD)項(xiàng)目(查看參考資料中的鏈接)使用 kexec開(kāi)發(fā)了一種不同的轉(zhuǎn)儲(chǔ)機(jī)制。在系統(tǒng)出錯(cuò)或者用戶轉(zhuǎn)儲(chǔ)開(kāi)始時(shí),系統(tǒng)內(nèi)存映像被壓縮并轉(zhuǎn)儲(chǔ)到可用的空閑內(nèi)存頁(yè)中。接下來(lái),系統(tǒng)使用 kexec重新啟動(dòng)到另一個(gè)內(nèi)核。新內(nèi)核會(huì)被告知轉(zhuǎn)儲(chǔ)存儲(chǔ)在何處,并防止任何進(jìn)程使用那些內(nèi)存區(qū)域。隨后,內(nèi)存轉(zhuǎn)儲(chǔ)可以寫(xiě)出到磁盤(pán)分區(qū)或者通過(guò)網(wǎng)絡(luò)寫(xiě)到另一臺(tái)機(jī)器。
這一設(shè)計(jì)的關(guān)鍵在于這樣一個(gè)事實(shí),通過(guò)避開(kāi)重新啟動(dòng)過(guò)程中的固件階段,LKCD可以防止物理內(nèi)存內(nèi)容被固件清除掉。在宕機(jī)的時(shí)候,LKCD也不需要依賴于一個(gè)不可靠的磁盤(pán)或者網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)器來(lái)將內(nèi)存映像寫(xiě)出到目標(biāo)位置。當(dāng)重新啟動(dòng)執(zhí)行,系統(tǒng)處于可靠狀態(tài)后,轉(zhuǎn)儲(chǔ)通過(guò)正常的系統(tǒng)設(shè)備驅(qū)動(dòng)器寫(xiě)出到目標(biāo)位置。
![]() |
Kexec 當(dāng)前只能用于 x86 32 位平臺(tái)。使其可以應(yīng)用于 PPC 64 和 AMD 64 等其他體系結(jié)構(gòu)的平臺(tái)將會(huì)有所幫助。而且,更好地與關(guān)機(jī)(shutdown)接口集成,以方便地終止進(jìn)程、停止設(shè)備和?載文件系統(tǒng),將使它更便于普通用戶使用。
您可以為 kexec 的開(kāi)發(fā)做出貢獻(xiàn)。開(kāi)始時(shí)請(qǐng)?jiān)谝粋€(gè)測(cè)試用的系統(tǒng)上嘗試 kexec。您還可以加入到“fastboot”郵件列表中,所有關(guān)于項(xiàng)目的技術(shù)討論都在那里進(jìn)行(查看參考資料中的鏈接)。
聯(lián)系客服