本文目錄:
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界面看到的信息,都是在這一階段獲取到的,如下圖。對本文來說,最重要的還是獲取到了啟動設備以及它們的啟動順序(順序從上到下)信息。
當硬件檢測和信息獲取完畢,開始初始化硬件,最后從排在第一位的啟動設備中讀取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ū)。
再看下圖,是裝第三個操作系統(tǒng)時的詢問boot loader安裝位置的步驟。
裝第一個操作系統(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之間的跳轉關系如下圖。
使用這種方式的菜單管理操作系統(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文件的結合體。它們的關系如下圖:
這張圖解釋了開機過程中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)。
當選擇某個菜單項后,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之間的關系如下圖:
雖然絕大多數(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í)行的一個收集過程。
在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上,每次開機都必然會開啟所有支持的虛擬終端,如下圖。
這些虛擬終端是由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久久精品免费国产大片|