分享Linux開機的詳細步驟


本文目錄:

14.1 按下電源和bios階段

14.2 MBR和各種bootloader階段

14.2.1 boot loader

14.2.2 分區(qū)表

14.2.3 采用VBR/EBR方式引導操作系統(tǒng)

14.3 grub階段

14.3.1 使用grub2時的啟動過程

14.3.2 使用傳統(tǒng)grub時的啟動過程

14.4 內核加載階段

14.4.1 加載init ramdisk

14.4.2 initrd

14.4.3 initramfs

14.5 操作系統(tǒng)初始化

14.5.1 運行級別

14.5.2 系統(tǒng)環(huán)境初始化

14.5.3 運行級別環(huán)境初始化

14.6 終端初始化和登錄系統(tǒng)

14.6.1 終端初始化

14.6.2 登錄過程


計算機啟動分為內核加載前、加載時和加載后3個大階段,這3個大階段又可以分為很多小階段,本文將非常細化分析每一個重要的小階段。

內核加載前的階段和操作系統(tǒng)無關,linux或Windows在這部分的順序是一樣的。由于使用anaconda安裝Linux時,默認的圖形界面是不支持GPT分區(qū)的,即使是目前最新的CentOS 7.3也仍然不支持,所以在本文中主要介紹傳統(tǒng)BIOS平臺(MBR方式)的啟動方式(其實是本人愚笨,看不懂uefi啟動方式)。

在內核加載時和加載后階段,由于CentOS 7采用的是systemd,和CentOS 5或CentOS 6的sysV風格的init大不相同,所以本文也只介紹sysV風格的init。

14.1 按下電源和bios階段

按下電源,計算機開始通電,最重要的是要接通cpu的電路,然后通過cpu的針腳讓cpu運行起來,只有cpu運行起來才能執(zhí)行相關代碼跳到bios。

bios是按下開機鍵后第一個運行的程序,它會讀取CMOS中的信息,以了解部分硬件的信息,比如硬件自檢(post)、硬件上的時間、硬盤大小和型號等。其實,手動進入bios界面看到的信息,都是在這一階段獲取到的,如下圖。對本文來說,最重要的還是獲取到了啟動設備以及它們的啟動順序(順序從上到下)信息。

分享Linux開機的詳細步驟

當硬件檢測和信息獲取完畢,開始初始化硬件,最后從排在第一位的啟動設備中讀取MBR,如果第一個啟動設備中沒有找到合理的MBR,則繼續(xù)從第二個啟動設備中查找,直到找到正確的MBR。

14.2 MBR和各種bootloader階段

這小節(jié)將介紹各種BR(boot record)和各種boot loader,但只是簡單介紹其基本作用。

MBR是主引導記錄,位于磁盤的第一個扇區(qū),和分區(qū)無關,和操作系統(tǒng)無關,bios一定會讀取MBR中的記錄。

在MBR中存儲了bootloader/分區(qū)表/BRID。bootloader占用446個字節(jié),用于引導加載;分區(qū)表占用64個字節(jié),每個主分區(qū)或擴展分區(qū)占用16個字節(jié),如果16個字節(jié)中的第一個字節(jié)為0x80,則表示該分區(qū)為激活的分區(qū)(活動分區(qū)),且只允許有一個激活的分區(qū);最后2個字節(jié)是BRID(boot record ID),它固定為0x55AA,用于標識該存儲設備的MBR是否是合理有效的MBR,如果bios讀取MBR發(fā)現(xiàn)最后兩個字節(jié)不是0x55AA,就會讀取下一個啟動設備。

14.2.1 boot loader

MBR中的bootloader只占用446字節(jié),所以可存儲的代碼有限,能加載引導的東西也有限,所以在磁盤的不同位置上設計了多種boot loader。下面將說明各種情況。

在創(chuàng)建文件系統(tǒng)時,是否還記得有些分區(qū)的第一個block是boot sector?這個啟動扇區(qū)中也放了boot loader,大小也很有限。如果是主分區(qū)上的boot sector,則該段boot loader所在扇區(qū)稱為VBR(volumn boot record),如果是邏輯分區(qū)上的boot sector,則該段boot loader所在扇區(qū)稱為EBR(Extended boot sector)。但很不幸,這兩種方式的boot loader都很少被使用上了,因為它們很不方便,加上后面出現(xiàn)了啟動管理器(LILO和GRUB),它們就被遺忘了。但即使如此,在分區(qū)中還是存在boot sector。

14.2.2 分區(qū)表

硬盤分區(qū)的好處之一就是可以在不同的分區(qū)中安裝不同的操作系統(tǒng),但boot loader必須要知道每個操作系統(tǒng)具體是在哪個分區(qū)。

分區(qū)表的長度只有64個字節(jié),里面又分成四項,每項16個字節(jié)。所以,一個硬盤最多只能分四個主分區(qū)。

每個主分區(qū)表項的16個字節(jié),都由6個部分組成:

(1).第1個字節(jié):只能為0或者0x80。0x80表示該主分區(qū)是激活分區(qū),0表示非激活分區(qū)。單磁盤只能有一個主分區(qū)是激活的。(2).第2-4個字節(jié):主分區(qū)第一個扇區(qū)的物理位置(柱面、磁頭、扇區(qū)號等等)。(3).第5個字節(jié):主分區(qū)類型。(4).第6-8個字節(jié):主分區(qū)最后一個扇區(qū)的物理位置。(5).第9-12字節(jié):該主分區(qū)第一個扇區(qū)的邏輯地址。(6).第13-16字節(jié):主分區(qū)的扇區(qū)總數(shù)。

最后的四個字節(jié)”主分區(qū)的扇區(qū)總數(shù)”,決定了這個主分區(qū)的長度。也就是說,一個主分區(qū)的扇區(qū)總數(shù)最多不超過2的32次方。如果每個扇區(qū)為512個字節(jié),就意味著單個分區(qū)最大不超過2TB。

14.2.3 采用VBR/EBR方式引導操作系統(tǒng)

暫且先不討論grub如何管理啟動操作系統(tǒng)的,以VBR和EBR引導操作系統(tǒng)為例。

當bios讀取到MBR中的boot loader后,會繼續(xù)讀取分區(qū)表。分兩種情況:

(1)如果查找分區(qū)表時發(fā)現(xiàn)某個主分區(qū)表的第一個字節(jié)是0x80,也就是激活的分區(qū),那么說明操作系統(tǒng)裝在了該主分區(qū),然后執(zhí)行已載入的MBR中的boot loader代碼,加載該激活主分區(qū)的VBR中的boot loader,至此,控制權就交給了VBR的boot loader了;

(2)如果操作系統(tǒng)不是裝在主分區(qū),那么肯定是裝在邏輯分區(qū)中,所以查找完主分區(qū)表后會繼續(xù)查找擴展分區(qū)表,直到找到EBR所在的分區(qū),然后MBR中的boot loader將控制權交給該EBR的boot loader。

也就是說,如果一塊硬盤上裝了多個操作系統(tǒng),那么boot loader會分布在多個地方,可能是VBR,也可能是EBR,但MBR是一定有的,這是被bios給”綁定”了的。在裝LINUX操作系統(tǒng)時,其中有一個步驟就是詢問你MBR裝在哪里的,但這個MBR并非一定真的是MBR,可能是MBR,也可能是VBR,還可能是EBR,并且想要單磁盤多系統(tǒng)共存,則MBR一定不能被覆蓋(此處不考慮grub)。

如下圖,是我測試單磁盤裝3個操作系統(tǒng)時的分區(qū)結構。其中/dev/sda{1,2,3}是第一個CentOS 6系統(tǒng),/dev/sda{5,6,7}是第二個CentOS 7系統(tǒng),/dev/sda{8,9,10}是第三個CentOS 6系統(tǒng),每一個操作系統(tǒng)的分區(qū)序號從前向后都是/boot分區(qū)、根分區(qū)、swap分區(qū)。

分享Linux開機的詳細步驟

再看下圖,是裝第三個操作系統(tǒng)時的詢問boot loader安裝位置的步驟。

分享Linux開機的詳細步驟

裝第一個操作系統(tǒng)時,boot loader可以裝在/dev/sda上,也可以選擇裝在/dev/sda1上,這時裝的是MBR和VBR,任選一個都會將另一個也裝上,從第二個操作系統(tǒng)開始,裝的是EBR而非MBR,且應該指定boot loader位置(如/dev/sda5和/dev/sda8),否則默認選項是裝在/dev/sda上,但這會覆蓋原有的MBR。

另外,在指定boot loader安裝路徑的下方,還有一個方框是操作系統(tǒng)列表,這就是操作系統(tǒng)菜單,其中可以指定默認的操作系統(tǒng),這里的默認指的是MBR默認跳轉到哪個VBR或EBR上。

所以,MBR/VBR和EBR之間的跳轉關系如下圖。

分享Linux開機的詳細步驟

使用這種方式的菜單管理操作系統(tǒng)啟動,無需什么stage1,stage1.5和stage2的概念,只要跳轉到了分區(qū)上的VBR或EBR,那么直接就可以加載引導該分區(qū)上的操作系統(tǒng)。

但是,這種管理操作系統(tǒng)啟動的菜單已經沒有意義了,現(xiàn)在都是使用grub來管理,所以裝第二個操作系統(tǒng)或第n個操作系統(tǒng)時不手動指定boot loader安裝位置,覆蓋掉MBR也無所謂,想要實現(xiàn)單磁盤多系統(tǒng)共存所需要做的,僅僅只是修改grub的配置文件而已。

使用grub管理引導菜單時,VBR/EBR就毫無用處了,具體的見下文。

14.3 grub階段

使用grub管理啟動,則MBR中的boot loader是由grub程序安裝的,此外還會安裝其他的boot loader。CentOS 6使用的是傳統(tǒng)的grub,而CentOS 7使用的是grub2。

如果使用的是傳統(tǒng)的grub,則安裝的boot loader為stage1、stage1_5和stage2,如果使用的是grub2,則安裝的是boot.img和core.img。傳統(tǒng)grub和grub2的區(qū)別還是挺大的,所以下面分開解釋,如果對于grub有不理解之處,見我的另一篇文章grub2詳解

14.3.1 使用grub2時的啟動過程

grub2程序安裝grub后,會在/boot/grub2/i386-pc/目錄下生成boot.img和core.img文件,另外還有一些模塊文件,其中包括文件系統(tǒng)類的模塊。

[root@xuexi ~]# find /boot/grub2/i386-pc/ -name '*.img' -o -name "*fs.mod" -o -name "*ext[0-9].mod"  /boot/grub2/i386-pc/affs.mod/boot/grub2/i386-pc/afs.mod/boot/grub2/i386-pc/bfs.mod/boot/grub2/i386-pc/btrfs.mod/boot/grub2/i386-pc/cbfs.mod/boot/grub2/i386-pc/ext2.mod   # ext2、ext3和ext4都使用該模塊/boot/grub2/i386-pc/hfs.mod/boot/grub2/i386-pc/jfs.mod/boot/grub2/i386-pc/ntfs.mod/boot/grub2/i386-pc/procfs.mod/boot/grub2/i386-pc/reiserfs.mod/boot/grub2/i386-pc/romfs.mod/boot/grub2/i386-pc/sfs.mod/boot/grub2/i386-pc/xfs.mod/boot/grub2/i386-pc/zfs.mod/boot/grub2/i386-pc/core.img  /boot/grub2/i386-pc/boot.img

其中boot.img就是安裝在MBR中的boot loader。當然,它們的內容是不一樣的,安裝boot loader時,grub2-install會將boot.img轉換為合適的代碼寫入MBR中的boot loader部分。

core.img是第二段Boot loader段,grub2-install會將core.img轉換為合適的代碼寫入到緊跟在MBR后面的空間,這段空間是MBR之后、第一個分區(qū)之前的空閑空間,被稱為MBR gap,這段空間最小31KB,但一般都會是1MB左右。

實際上,core.img是多個img文件的結合體。它們的關系如下圖:

分享Linux開機的詳細步驟

這張圖解釋了開機過程中grub2階段的所有過程,boot.img段的boot loader只有一個作用,就是跳轉到core.img對應的boot loader的第一個扇區(qū),對于從硬盤啟動的系統(tǒng)來說,該扇區(qū)是diskboot.img的內容,diskboot.img的作用是加載core.img中剩余的內容。

由于diskboot.img所在的位置是以硬編碼的方式寫入到boot.img中的,所以boot.img總能找到core.img中diskboot.img的位置并跳轉到它身上,隨后控制權交給diskboot.img。隨后diskboot.img加載壓縮后的kernel.img(注意,是grub的kernel不是操作系統(tǒng)的kernel)以初始化grub運行時的各種環(huán)境,控制權交給kernel.img。

但直到目前為止,core.img都還不識別/boot所在分區(qū)的文件系統(tǒng),所以kernel.img初始化grub環(huán)境的過程就包括了加載模塊,嚴格地說不是加載,因為在安裝grub時,文件系統(tǒng)類的模塊已經嵌入到了core.img中,例如ext類的文件系統(tǒng)模塊ext2.mod。

加載了模塊后,kernel.img就能識別/boot分區(qū)的文件系統(tǒng),也就能找到grub的配置文件/boot/grub2/grub.cfg,有了grub.cfg就能顯示啟動菜單,我們就能自由的選擇要啟動的操作系統(tǒng)。

分享Linux開機的詳細步驟

當選擇某個菜單項后,kernel.img會根據(jù)grub.cfg中的配置加載對應的操作系統(tǒng)內核(/boot目錄下vmlinuz開頭的文件),并向操作系統(tǒng)內核傳遞啟動時參數(shù),包括根文件系統(tǒng)所在的分區(qū),init ramdisk(即initrd或initramfs)的路徑。例如下面是某個菜單項的配置:

menuentry 'CentOS 6' --unrestricted {          search --no-floppy --fs-uuid --set=root f5d8939c-4a04-4f47-a1bc-1b8cbabc4d32          linux16 /vmlinuz-2.6.32-504.el6.x86_64 root=UUID=edb1bf15-9590-4195-aa11-6dac45c7f6f3 ro quiet          initrd16 /initramfs-2.6.32-504.el6.x86_64.img  }

加載完操作系統(tǒng)內核后grub2就將控制權交給操作系統(tǒng)內核。

總結下,從MBR開始后的過程是這樣的:

1.執(zhí)行MBR中的boot loader(即boot.img)跳轉到diskboot.img。

2.執(zhí)行diskboot.img,加載core.img剩余的部分,并跳轉到kernel.img。

3.kernel.img讀取/boot/grub2/grub2.cfg,并顯示啟動管理菜單。

4.選中某菜單后,kernel.img加載該菜單項配置的操作系統(tǒng)內核/boot/vmlinux-XXX,并傳遞內核啟動參數(shù),包括根文件系統(tǒng)所在分區(qū)和init ramdisk的路徑。

5.控制權交給操作系統(tǒng)內核。

14.3.2 使用傳統(tǒng)grub時的啟動過程

傳統(tǒng)grub對應的boot loader是stage1和stage2,從stage1跳轉到stage2大多數(shù)情況下還會用到stage1_5對應的boot loader。

與grub2相比,stage1和boot.img的作用是類似的,都在MBR中。當該段boot loader執(zhí)行后,它的目的是跳轉到stage1_5的第一個扇區(qū)上,然后由該扇區(qū)的代碼加載剩余的內容,并跳轉到stage2的第一個扇區(qū)上。

stage1_5存在的理由是因為stage2功能較多,導致其文件體積較大(一般至少都有100多K),所以并沒有像core.img一樣嵌入到磁盤上,而是簡單地將其放在了boot分區(qū)上,但stage1并不識別boot分區(qū)的文件系統(tǒng)類型,所以借助中間的輔助boot loader即stage1_5來跳轉。

stage1_5的目的之一是識別文件系統(tǒng),但文件系統(tǒng)的類型有很多,所以對應的stage1_5也有很多種。

[root@xuexi ~]# ls -C /boot/grub/*stage1_5*/boot/grub/e2fs_stage1_5     /boot/grub/jfs_stage1_5       /boot/grub/vstafs_stage1_5  /boot/grub/fat_stage1_5      /boot/grub/minix_stage1_5     /boot/grub/xfs_stage1_5  /boot/grub/ffs_stage1_5      /boot/grub/reiserfs_stage1_5  /boot/grub/iso9660_stage1_5  /boot/grub/ufs2_stage1_5

雖然有很多種stage1_5,但每個boot分區(qū)也只能對應一種stage1_5。這個stage1_5對應的boot loader一般會被嵌入到MBR后、第一個分區(qū)前的中間那段空間(即MBR gap)。

當執(zhí)行了stage1_5對應的boot loader后,stage1_5就能識別出boot所在的分區(qū),并找到stage2文件的第一個扇區(qū),然后跳轉過去。

當控制權交給了stage2,stage2就能加載grub的配置文件/boot/grub/grub.conf并顯示菜單并初始化grub的運行時環(huán)境,當選中操作系統(tǒng)后,stage2將和kernel.img一樣加載操作系統(tǒng)內核,傳遞內核啟動參數(shù),并將控制權交給操作系統(tǒng)內核。

所以,stage1、stage1_5和stage2之間的關系如下圖:

分享Linux開機的詳細步驟

雖然絕大多數(shù)都提供了stage1_5,但它不是必須的,它的作用僅僅只是識別boot分區(qū)的文件系統(tǒng)類型,對于一個會編程的人來說,可以將固定boot分區(qū)的文件系統(tǒng)識別代碼嵌入到stage1中,這樣stage1自身就能識別boot分區(qū),就不需要stage1_5了。

看看安裝grub時,grub到底做了些什么工作。

grub> setup (hd0)   Checking if "/boot/grub/stage1" exists... yes   Checking if "/boot/grub/stage2" exists... yes   Checking if "/boot/grub/e2fs_stage1_5" exists... yes   Running "embed /boot/grub/e2fs_stage1_5 (hd0)"...  15 sectors are embedded.  succeeded   Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/menu.lst"... succeeded  Done.

首先檢測各stage文件是否存在于/boot/grub目錄下,隨后嵌入stage1_5到磁盤上,該文件系統(tǒng)類型的stage1_5占用了15個扇區(qū),最后安裝stage1,并告知stage1 stage1_5的位置是第1到第15個扇區(qū),之所以先嵌入stage1_5再嵌入stage1就是為了讓stage1知道stage1_5的位置,最后還告知了stage1 stage2和配置文件menu.lst(它是grub.conf的軟鏈接)的路徑。

14.4 內核加載階段

提前說明,下文所述均為sysV init系統(tǒng)啟動風格,systemd的啟動管理方式大不相同,所以不要將systemd管理的啟動方式與此做比較。

到目前為止,內核已經被加載到內存掌握了控制權,且收到了boot loader最后傳遞的內核啟動參數(shù)以及init ramdisk的路徑。

所有的內核都是以bzImage方式壓縮過的,壓縮后CentOS 6的內核大小大約為4M,CentOS 7的內核大小大約為5M。內核要能正常運作下去,它需要進行解壓釋放。

解壓釋放之后,將創(chuàng)建pid為0的idle進程,該進程非常重要,后續(xù)內核所有的進程都是通過fork它創(chuàng)建的,且很多cpu降溫工具就是強制執(zhí)行idle進程來實現(xiàn)的。

然后創(chuàng)建pid=1和pid=2的內核進程。pid=1的進程也就是init進程,pid=2的進程是kthread內核線程,它的作用是在真正調用init程序之前完成內核環(huán)境初始化和設置工作,例如根據(jù)grub傳遞的內核啟動參數(shù)找到init ramdisk并加載。

14.4.1 加載init ramdisk

在前面,已經創(chuàng)建了pid=1的init進程和pid=2的kthread進程,但注意,它們都是內核線程,全稱是kernel_init和kernel_kthread,而真正能被ps捕獲到的pid=1的init進程是由kernel_init調用init程序后形成的。

要加載/sbin/init程序,首先要找到根分區(qū),根分區(qū)是有文件系統(tǒng)的,所以內核需要先識別文件系統(tǒng)并加載文件系統(tǒng)的驅動,但文件系統(tǒng)的驅動又是放在根分區(qū)的,這就出現(xiàn)了先有雞還是先有蛋的矛盾。

解決的方法之一是像grub2識別boot分區(qū)的文件系統(tǒng)一樣,將根文件系統(tǒng)驅動模塊嵌入到內核中,但文件系統(tǒng)的種類太多,而且會升級,這樣就導致內核不斷的嵌入新的文件系統(tǒng)驅動模塊,內核不斷增大,這顯然是不合適的。

解決方法之二則像傳統(tǒng)grub借助中間過渡引導段stage1_5一樣,將根文件系統(tǒng)的驅動模塊放入一個中間過渡文件,在加載根文件系統(tǒng)之前先加載這個過渡文件,再由過渡文件跳轉到根文件系統(tǒng)。

方法二正是現(xiàn)在采用的,其采用的中間過渡文件稱為init ramdisk,它是在安裝完操作系統(tǒng)時生成的,這樣它會收集到當前操作系統(tǒng)的根文件系統(tǒng)是什么類型的文件系統(tǒng),也就能只嵌入一個對應的文件系統(tǒng)驅動模塊使其變得足夠小。如下圖,它是安裝操作系統(tǒng)時安裝完所有軟件包后執(zhí)行的一個收集過程。

分享Linux開機的詳細步驟

在CentOS 5上采用的init ramdisk稱為initrd,而CentOS 6和CentOS 7采用的則是initramfs,它們的目的是一樣的,但在實現(xiàn)上卻大有不同。但它們都存放在/boot目錄下。

[root@xuexi ~]# ll -h /boot/init*  -rw-------. 1 root root 19M Feb 25 11:53 /boot/initramfs-2.6.32-504.el6.x86_64.img

可以看到,它們的大小有十多兆,由此也可知道init ramdisk的作用肯定不僅僅只是找到根文件系統(tǒng),它還會做其他工作。具體還做什么工作,請繼續(xù)閱讀下文。

14.4.2 initrd

initrd其實是一個鏡像文件系統(tǒng),是在內存中劃分一片區(qū)域模擬磁盤分區(qū),在該文件中包含了找到根文件系統(tǒng)的腳本和驅動。

既然是文件系統(tǒng),那么內核也必須要帶有對應文件系統(tǒng)的驅動,另外文件系統(tǒng)要使用就必須有根”/”,這個根是內存中的”虛根”。由于內核加載到這里已經初始化一些運行環(huán)境了,所以內核的運行狀態(tài)等參數(shù)也要保存下來,保存的位置就是內存中虛根下的/proc和/sys,此外還有收集到的硬件設備信息以及設備的運行環(huán)境也要保存下來,保存的位置是/dev。到此為止,pid=2的內核線程kernel_kthread就完成了基本工作,開始轉到kernel_init進程上了。

再之后就是kernel_init掛載真正的根文件系統(tǒng)并從虛根切換到實根,最后kernel_init將調用init程序,也就是真正的能被我們看見的pid=1的init進程,然后將控制權交給init,所以從現(xiàn)在開始,將切換到用戶空間,后續(xù)剩余的事情都將由用戶空間的程序完成。

以下是CentOS 5.8中initrd文件的解壓過程和捷報后的目錄結構。

[root@localhost ~]# cp /boot/initrd-2.6.18-308.el5.img /tmp/initrd.gz  [root@localhost tmp]# gunzip initrd.gz  [root@localhost tmp]# cpio -id < initrd     [root@localhost tmp]# lsbin  dev  etc  init  initrd  lib  proc  sbin  sys  sysroot

14.4.3 initramfs

initramfs比initrd先進了一些,initrd必須是一個文件系統(tǒng),是在內存中模擬出磁盤分區(qū)的,所以內核必須要帶有它的文件系統(tǒng)驅動,而initramfs則僅僅只是一個鏡像壓縮文件而非文件系統(tǒng),所以它不需要帶文件系統(tǒng)驅動,在加載時,內核會將其解壓的內容裝入到一個tmpfs 中。

initramfs和initrd最大的區(qū)別在于init進程的區(qū)別對待。initramfs為了盡早進入用戶空間,它將init程序集成到了initramfs鏡像文件中,這樣就可以在initramfs裝入tmpfs時直接運行init進程,而不用去找根文件系統(tǒng)下的/sbin/init,由此掛載根文件系統(tǒng)的工作將由init來完成,而不再是內核線程kernel_init完成。最后從虛根切換到實根。

那根分區(qū)下的/sbin/init是干嘛的呢?可以認為是init ramdisk中init的一個備份,如果ramdisk中找不到init就會去找/sbin/init。另外,在正常運行的操作系統(tǒng)環(huán)境下,/sbin/init還經常用來完成其他工作,如發(fā)送信號。

其實initramfs完成了很多工作,解開它的鏡像文件就能發(fā)現(xiàn)它的目錄結構和真實環(huán)境下的目錄結構類似。以下是CentOS 7上initramfs-3.10.0-327.el7.x86_64解包過程和解包后的目錄結構。

[root@xuexi ~]# cp /boot/initramfs-3.10.0-327.el7.x86_64.img /tmp/initramfs.gz  [root@xuexi ~]# cd /tmp; gunzip /tmp/initramfs.gz  [root@xuexi tmp]# cpio -id < initramfs    [root@xuexi tmp]# ls -l  total 8lrwxrwxrwx  1 root root    7 Jun 29 23:28 bin -> usr/bin  drwxr-xr-x  2 root root   42 Jun 29 23:28 dev  drwxr-xr-x 11 root root 4096 Jun 29 23:28 etc  lrwxrwxrwx  1 root root   23 Jun 29 23:28 init -> usr/lib/systemd/systemd  lrwxrwxrwx  1 root root    7 Jun 29 23:28 lib -> usr/lib  lrwxrwxrwx  1 root root    9 Jun 29 23:28 lib64 -> usr/lib64  drwxr-xr-x  2 root root    6 Jun 29 23:28 proc  drwxr-xr-x  2 root root    6 Jun 29 23:28 root  drwxr-xr-x  2 root root    6 Jun 29 23:28 run  lrwxrwxrwx  1 root root    8 Jun 29 23:28 sbin -> usr/sbin-rwxr-xr-x  1 root root 3041 Jun 29 23:28 shutdown  drwxr-xr-x  2 root root    6 Jun 29 23:28 sys  drwxr-xr-x  2 root root    6 Jun 29 23:28 sysroot  drwxr-xr-x  2 root root    6 Jun 29 23:28 tmp  drwxr-xr-x  7 root root   61 Jun 29 23:28 usr  drwxr-xr-x  2 root root   27 Jun 29 23:28 var

另外,還可以在其sbin目錄下發(fā)現(xiàn)init程序。

[root@xuexi tmp]# ll sbin/init  lrwxrwxrwx 1 root root 22 Jun 29 23:28 sbin/init -> ../lib/systemd/systemd

14.5 操作系統(tǒng)初始化

下文解釋的是sysV風格的系統(tǒng)環(huán)境,與systemd初始化大不相同。

當init進程掌握控制權后,意味著已經進入了用戶空間,后續(xù)的事情也將以用戶空間為主導來完成。

init的名稱是initialize的縮寫,是初始化的意思,所以它的作用也就是初始化的作用。在內核加載階段,也有初始化動作,初始化的環(huán)境是內核的環(huán)境,是由kernel_init、kernel_thread等內核線程完成的。而init掌握控制權后,已經可以和用戶空間交互,意味著真正的開始進入操作系統(tǒng),所以它初始化的是操作系統(tǒng)的環(huán)境。

操作系統(tǒng)初始化涉及了不少過程,大致如下:讀取運行級別;初始化系統(tǒng)類的環(huán)境;根據(jù)運行級別初始化用戶類的環(huán)境;執(zhí)行rc.local文件完成用戶自定義開機要執(zhí)行的命令;加載終端;

14.5.1 運行級別

在sysV風格的系統(tǒng)下,使用了運行級別的概念,不同運行級別初始化不同的系統(tǒng)類環(huán)境,你可以認為windows的安全模式也是使用運行級別的一種產物。

在Linux系統(tǒng)中定義了7個運行級別,使用0-6的數(shù)字表示。

0:halt,即關機

1:單用戶模式

2:不帶NFS的多用戶模式

3:完整多用戶模式

4:保留未使用的級別

5:X11,即圖形界面模式

6:reboot,即重啟

實際上,執(zhí)行關機或重啟命令的本質就是向init進程傳遞0或6這兩個運行級別。

sysV的init程序讀取/etc/inittab文件來獲取默認的運行級別,并根據(jù)此文件所指定的配置執(zhí)行默認運行級別對應的操作。注意,systemd管理的系統(tǒng)是沒有/etc/inittab文件的,即使有也僅僅只是出于提醒的目的,因為systemd沒有了運行級別的概念,說實話,systemd管的真的太多了。

CentOS 6.6上該文件內容如下:

[root@xuexi ~]# cat /etc/inittab  # inittab is only used by upstart for the default runlevel.  #  # ADDING OTHER CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.  ## System initialization is started by /etc/init/rcS.conf  #  # Individual runlevels are started by /etc/init/rc.conf  #  # Ctrl-Alt-Delete is handled by /etc/init/control-alt-delete.conf  #  # Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,  # with configuration in /etc/sysconfig/init.#  # For information on how to write upstart event handlers, or how  # upstart works, see init(5), init(8), and initctl(8).  #  # Default runlevel. The runlevels used are:  #   0 - halt (Do NOT set initdefault to this)  #   1 - Single user mode  #   2 - Multiuser, without NFS (The same as 3, if you do not have networking)  #   3 - Full multiuser mode  #   4 - unused  #   5 - X11  #   6 - reboot (Do NOT set initdefault to this)  #id:3:initdefault:

該文件告訴我們,系統(tǒng)初始化過程由/etc/init/rcS.conf完成,運行級別類的初始化過程由/etc/init.conf來完成,按下CTRL+ALT+DEL鍵要執(zhí)行的過程由/etc/init/control-alt-delete.conf來完成,終端加載的過程由/etc/init/tty.conf和/etc/init/serial.conf讀取配置文件/etc/sysconfig/init來完成再文件最后,還有一行”id:3:initdefault”,表示默認的運行級別為3,即完整的多用戶模式。

確認了要進入的運行級別后,init將先讀取/etc/init/rcS.conf來完成系統(tǒng)環(huán)境類初始化動作,再讀取/etc/init/rc.conf來完成運行級別類動作。

14.5.2 系統(tǒng)環(huán)境初始化

先看看/etc/init/rcS.conf文件的內容。

[root@xuexi ~]# cat /etc/init/rcS.conf  # rcS - runlevel compatibility  #  # This task runs the old sysv-rc startup scripts.  #  # Do not edit this file directly. If you want to change the behaviour,  # please create a file rcS.override and put your changes there.     start on startup     stop on runlevel     task     # Note: there can be no previous runlevel here, if we have one it's bad# information (we enter rc1 not rcS for maintenance).  Run /etc/rc.d/rc  # without information so that it defaults to previous=N runlevel=S.  console output  pre-start scriptfor t in $(cat /proc/cmdline); docase $t inemergency)                                  start rcS-emergency                                  break                          ;;esacdoneend scriptexec /etc/rc.d/rc.sysinitpost-stop scriptif [ "$UPSTART_EVENTS" = "startup" ]; then[ -f /etc/inittab ] && runlevel=$(/bin/awk -F ':' '$3 == "initdefault" && $1 !~ "^#" { print $2 }' /etc/inittab)                  [ -z "$runlevel" ] && runlevel="3"for t in $(cat /proc/cmdline); docase $t in-s|single|S|s) runlevel="S" ;;                                  [1-9])       runlevel="$t" ;;esacdoneexec telinit $runlevelfiend script

其中”exec /etc/rc.d/rc.sysinit”這一行就表示要執(zhí)行/etc/rc.d/rc.sysinit文件,該文件定義了系統(tǒng)初始化(system initialization)的內容,包括:

(1).確認主機名。

(2).掛載/proc和/sys等特殊文件系統(tǒng),使得內核參數(shù)和狀態(tài)可與人進行交互。是否還記得在內核加載階段時的/proc和/sys?

(3).啟動udev,也就是啟動類似windows中的設備管理器。

(4)初始化硬件參數(shù),如加載某些驅動,設置時鐘等。

(5).設置主機名。

(6).執(zhí)行fsck檢測磁盤是否健康。

(7).掛載/etc/fstab中除/proc和NFS的文件系統(tǒng)。

(8).激活swap。

(9).將所有執(zhí)行的操作寫入到/var/log/dmesg文件中。

14.5.3 運行級別環(huán)境初始化

執(zhí)行完系統(tǒng)初始化后,接下來就是執(zhí)行運行級別的初始化。先看看/etc/init/rc.conf的內容。

[root@xuexi ~]# cat /etc/init/rc.conf  # rc - System V runlevel compatibility  #  # This task runs the old sysv-rc runlevel scripts.  It  # is usually started by the telinit compatibility wrapper.  #  # Do not edit this file directly. If you want to change the behaviour,  # please create a file rc.override and put your changes there.     start on runlevel [0123456]     stop on runlevel [!$RUNLEVEL]     task     export RUNLEVEL  console outputexec /etc/rc.d/rc $RUNLEVEL

最后一行”exec /etc/rc.d/rc $RUNLEVEL”說明調用/etc/rc.d/rc這個腳本來初始化指定運行級別的環(huán)境。Linux采用了將各運行級別初始化內容分開管理的方式,將0-6這7個運行級別要執(zhí)行的初始化腳本分別放入rc[0-6].d這7個目錄中。

[root@xuexi ~]# ls -l /etc/rc.d/total 60drwxr-xr-x. 2 root root  4096 Jun 11 02:42 init.d-rwxr-xr-x. 1 root root  2617 Oct 16  2014 rc  drwxr-xr-x. 2 root root  4096 Jun 11 02:42 rc0.d  drwxr-xr-x. 2 root root  4096 Jun 11 02:42 rc1.d  drwxr-xr-x. 2 root root  4096 Jun 11 02:42 rc2.d  drwxr-xr-x. 2 root root  4096 Jun 11 02:42 rc3.d  drwxr-xr-x. 2 root root  4096 Jun 11 02:42 rc4.d  drwxr-xr-x. 2 root root  4096 Jun 11 02:42 rc5.d  drwxr-xr-x. 2 root root  4096 Jun 11 02:42 rc6.d-rwxr-xr-x. 1 root root   220 Oct 16  2014 rc.local-rwxr-xr-x. 1 root root 19914 Oct 16  2014 rc.sysinit

實際上/etc/init.d/下的腳本才是真正的腳本,放入rcN.d目錄中的文件只不過是/etc/init.d/目錄下腳本的軟鏈接。注意,/etc/init.d是Linux耍的一個小把戲,它是/etc/rc.d/init.d的一個符號鏈接,在有些類unix系統(tǒng)中是沒有/etc/init.d的,都是直接使用/etc/rc.d/init.d。

以/etc/rc.d/rc3.d為例。

[root@xuexi ~]# ll /etc/rc.d/rc3.d/ | headtotal 0lrwxrwxrwx. 1 root root 16 Feb 25 11:52 K01smartd -> ../init.d/smartd  lrwxrwxrwx. 1 root root 16 Feb 25 11:52 K10psacct -> ../init.d/psacct  lrwxrwxrwx. 1 root root 19 Feb 25 11:51 K10saslauthd -> ../init.d/saslauthd  lrwxrwxrwx  1 root root 22 Jun 10 08:59 K15htcacheclean -> ../init.d/htcacheclean  lrwxrwxrwx  1 root root 15 Jun 10 08:59 K15httpd -> ../init.d/httpd  lrwxrwxrwx  1 root root 15 Jun 11 02:42 K15nginx -> ../init.d/nginx  lrwxrwxrwx. 1 root root 18 Feb 25 11:52 K15svnserve -> ../init.d/svnserve  lrwxrwxrwx. 1 root root 20 Feb 25 11:51 K50netconsole -> ../init.d/netconsole  lrwxrwxrwx  1 root root 17 Jun 10 00:50 K73winbind -> ../init.d/winbind

可見,rcN.d中的文件都以K或S加一個數(shù)字開頭,其后才是腳本名稱,且它們都是/etc/rc.d/init.d中文件的鏈接。S開頭表示進入該運行級別時要運行的程序,S字母后的數(shù)值表示啟動順序,數(shù)字越大,啟動的越晚;K開頭的表示退出該運行級別時要殺掉的程序,數(shù)值表示關閉的順序。

所有這些文件都是由/etc/rc.d/rc這個程序調用的,K開頭的則傳給rc一個stop參數(shù),S開頭的則傳給rc一個start參數(shù)。

打開rc0.d和rc6.d這兩個目錄,你會發(fā)現(xiàn)在這兩個目錄中除了”S00killall”和”S01reboot”,其余都是K開頭的文件。

在rc[2-5].d這幾個目錄中,都有一個S99local文件,且它們都是指向/etc/rc.d/rc.local的軟鏈接。S99表示最后啟動的一個程序,所以rc.local中的程序是2345這4個運行級別初始化過程中最后運行的一個腳本。這是Linux提供給我們定義自己想要在開機時(嚴格地說是進入運行級別)就執(zhí)行的命令的文件。

當初始化完運行級別環(huán)境后,將要準備登錄系統(tǒng)了。

14.6 終端初始化和登錄系統(tǒng)

Linux是多任務多用戶的操作系統(tǒng),它允許多人同時在線工作。但每個人都必須要輸入用戶名和密碼才能驗證身份并最終登錄。但登陸時是以圖形界面的方式給用戶使用,還是以純命令行模式給用戶使用呢?這是終端決定的,也就是說在登錄前需要先加載終端。至于什么是終端,見我的另一篇文章Linux終端類型

14.6.1 終端初始化

在Linux上,每次開機都必然會開啟所有支持的虛擬終端,如下圖。

分享Linux開機的詳細步驟

這些虛擬終端是由getty命令(get tty)來完成的,getty命令有很多變種,有mingetty、agetty、rungettty等,在CentOS 5和CentOS 6都使用mingetty,在CentOS 7上使用agetty。getty命令的作用之一是調用登錄程序/bin/login。

例如,在CentOS 6下,捕獲tty終端情況。

[root@xuexi ~]# ps -elf | grep tt[y]4 S root       1412      1  0  80   0 -  1016 n_tty_ Jun21 tty2     00:00:00 /sbin/mingetty /dev/tty24 S root       1414      1  0  80   0 -  1016 n_tty_ Jun21 tty3     00:00:00 /sbin/mingetty /dev/tty34 S root       1417      1  0  80   0 -  1016 n_tty_ Jun21 tty4     00:00:00 /sbin/mingetty /dev/tty44 S root       1419      1  0  80   0 -  1016 n_tty_ Jun21 tty5     00:00:00 /sbin/mingetty /dev/tty54 S root       1421      1  0  80   0 -  1016 n_tty_ Jun21 tty6     00:00:00 /sbin/mingetty /dev/tty64 S root       1492   1410  0  80   0 - 27118 n_tty_ Jun21 tty1     00:00:00 -bash

在CentOS 7下,捕獲tty終端情況。

[root@xuexi tmp]# ps -elf | grep tt[y]4 S root       8258      1  0  80   0 - 27507 n_tty_ 04:17 tty2     00:00:00 /sbin/agetty --noclear tty2 linux4 S root       8259      1  0  80   0 - 27507 n_tty_ 04:17 tty3     00:00:00 /sbin/agetty --noclear tty3 linux4 S root       8260      1  0  80   0 - 27507 n_tty_ 04:17 tty4     00:00:00 /sbin/agetty --noclear tty4 linux4 S root       8262    915  0  80   0 - 29109 n_tty_ 04:17 tty1     00:00:00 -bash4 S root       8307   8305  0  80   0 - 29109 n_tty_ 04:17 tty5     00:00:00 -bash4 S root       8348   8346  0  80   0 - 29136 n_tty_ 04:17 tty6     00:00:00 -bash

細心一點會發(fā)現(xiàn),有的tty終端仍然以/sbin/mingetty進程或/sbin/agetty進程顯示,有些卻以bash進程顯示。這是因為getty進程在調用/bin/login后,如果輸入用戶名和密碼成功登錄了某個虛擬終端,那么gettty程序會融合到bash(假設bash是默認的shell)進程,這樣getty進程就不會再顯示了。

雖然getty不顯示了,但并不代表它消失了,它仍以特殊的方式存在著。是否還記得/etc/inittab文件?此文件中提示了終端加載的過程由/etc/init/tty.conf讀取配置文件/etc/sysconfig/init來完成。

[root@xuexi ~]# grep tty -A 1 /etc/inittab  # Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,  # with configuration in /etc/sysconfig/init.

那么就看看/etc/init/tty.conf文件。

[root@xuexi ~]# cat /etc/init/tty.conf  # tty - getty  #  # This service maintains a getty on the specified device.  #  # Do not edit this file directly. If you want to change the behaviour,  # please create a file tty.override and put your changes there.     stop on runlevel [S016]   respawninstance $TTYexec /sbin/mingetty $TTYusage 'tty TTY=/dev/ttyX  - where X is console id'

此文件中的respawn表示進程由init進程監(jiān)視,一發(fā)現(xiàn)被殺掉了init會立即重啟它。所以,只要getty進程一結束,init會立即監(jiān)視到而重啟該進程。因此,用戶登錄成功后getty只是融合到了bash進程中,并非退出,否則init會立即重啟它,而它會調用login程序讓你再次輸入用戶和密碼。

再看看/etc/sysconfig/init文件。

[root@xuexi ~]# cat /etc/sysconfig/init  # color => new RH6.0 bootup  # verbose => old-style bootup  # anything else => new style bootup without ANSI colors or positioning  BOOTUP=color  # column to start "[  OK  ]" label inRES_COL=60# terminal sequence to move to that column. You could change this  # to something like "tput hpa ${RES_COL}" if your terminal supports it  MOVE_TO_COL="echo -en 

9999国产精品欧美久久久久久|
无码人妻久久一区二区三区蜜桃|
亚洲人成无码久久电影网站|
久久久久久综合一区中文字幕|
久久婷婷五月综合色奶水99啪|
久久伊人亚洲AV无码网站|
99久久国产免费福利|
97久久天天综合色天天综合色hd|
久久99国产综合精品免费|
久久久精品人妻一区二区三区蜜桃|
久久综合日本熟妇|
三级韩国一区久久二区综合|
久久综合狠狠综合久久97色|
久久久久亚洲精品无码网址|
久久国产精品免费一区|
久久久久香蕉视频|
成人综合久久精品色婷婷|
久久天天躁狠狠躁夜夜躁2014|
久久婷婷五月综合97色直播|
久久精品卫校国产小美女|
91麻豆国产精品91久久久|
久久无码人妻一区二区三区午夜|
99久久这里只有精品|
久久精品一区二区影院|
欧美亚洲国产精品久久高清|
一本色道久久99一综合|
久久久无码人妻精品无码|
中文字幕一区二区三区久久网站
|
91精品国产综合久久四虎久久无码一级
|
久久久久久久久66精品片|
精品综合久久久久久97|
国产精品久久久久久福利69堂|
久久91精品综合国产首页|
久久AV无码精品人妻糸列|
国产成人综合久久久久久|
一本色道久久HEZYO无码|
91久久精品国产成人久久|
国产精品久久久久久久久久影院|
99久久免费国产特黄|
久久久久久久久久久|
99久久精品免费国产大片|