本文目錄:
14.1 按下電源和bios階段
14.2 MBR和各種bootloader階段
14.2.1 boot loader
14.2.2 分區表
14.2.3 采用VBR/EBR方式引導操作系統
14.3 grub階段
14.3.1 使用grub2時的啟動過程
14.3.2 使用傳統grub時的啟動過程
14.4 內核加載階段
14.4.1 加載init ramdisk
14.4.2 initrd
14.4.3 initramfs
14.5 操作系統初始化
14.5.1 運行級別
14.5.2 系統環境初始化
14.5.3 運行級別環境初始化
14.6 終端初始化和登錄系統
14.6.1 終端初始化
14.6.2 登錄過程
計算機啟動分為內核加載前、加載時和加載后3個大階段,這3個大階段又可以分為很多小階段,本文將非常細化分析每一個重要的小階段。
內核加載前的階段和操作系統無關,linux或Windows在這部分的順序是一樣的。由于使用anaconda安裝Linux時,默認的圖形界面是不支持GPT分區的,即使是目前最新的CentOS 7.3也仍然不支持,所以在本文中主要介紹傳統BIOS平臺(MBR方式)的啟動方式(其實是本人愚笨,看不懂uefi啟動方式)。
在內核加載時和加載后階段,由于CentOS 7采用的是systemd,和CentOS 5或CentOS 6的sysV風格的init大不相同,所以本文也只介紹sysV風格的init。
14.1 按下電源和bios階段
按下電源,計算機開始通電,最重要的是要接通cpu的電路,然后通過cpu的針腳讓cpu運行起來,只有cpu運行起來才能執行相關代碼跳到bios。
bios是按下開機鍵后第一個運行的程序,它會讀取CMOS中的信息,以了解部分硬件的信息,比如硬件自檢(post)、硬件上的時間、硬盤大小和型號等。其實,手動進入bios界面看到的信息,都是在這一階段獲取到的,如下圖。對本文來說,最重要的還是獲取到了啟動設備以及它們的啟動順序(順序從上到下)信息。
當硬件檢測和信息獲取完畢,開始初始化硬件,最后從排在第一位的啟動設備中讀取MBR,如果第一個啟動設備中沒有找到合理的MBR,則繼續從第二個啟動設備中查找,直到找到正確的MBR。
14.2 MBR和各種bootloader階段
這小節將介紹各種BR(boot record)和各種boot loader,但只是簡單介紹其基本作用。
MBR是主引導記錄,位于磁盤的第一個扇區,和分區無關,和操作系統無關,bios一定會讀取MBR中的記錄。
在MBR中存儲了bootloader/分區表/BRID。bootloader占用446個字節,用于引導加載;分區表占用64個字節,每個主分區或擴展分區占用16個字節,如果16個字節中的第一個字節為0x80,則表示該分區為激活的分區(活動分區),且只允許有一個激活的分區;最后2個字節是BRID(boot record ID),它固定為0x55AA,用于標識該存儲設備的MBR是否是合理有效的MBR,如果bios讀取MBR發現最后兩個字節不是0x55AA,就會讀取下一個啟動設備。
14.2.1 boot loader
MBR中的bootloader只占用446字節,所以可存儲的代碼有限,能加載引導的東西也有限,所以在磁盤的不同位置上設計了多種boot loader。下面將說明各種情況。
在創建文件系統時,是否還記得有些分區的第一個block是boot sector?這個啟動扇區中也放了boot loader,大小也很有限。如果是主分區上的boot sector,則該段boot loader所在扇區稱為VBR(volumn boot record),如果是邏輯分區上的boot sector,則該段boot loader所在扇區稱為EBR(Extended boot sector)。但很不幸,這兩種方式的boot loader都很少被使用上了,因為它們很不方便,加上后面出現了啟動管理器(LILO和GRUB),它們就被遺忘了。但即使如此,在分區中還是存在boot sector。
14.2.2 分區表
硬盤分區的好處之一就是可以在不同的分區中安裝不同的操作系統,但boot loader必須要知道每個操作系統具體是在哪個分區。
分區表的長度只有64個字節,里面又分成四項,每項16個字節。所以,一個硬盤最多只能分四個主分區。
每個主分區表項的16個字節,都由6個部分組成:
(1).第1個字節:只能為0或者0x80。0x80表示該主分區是激活分區,0表示非激活分區。單磁盤只能有一個主分區是激活的。(2).第2-4個字節:主分區第一個扇區的物理位置(柱面、磁頭、扇區號等等)。(3).第5個字節:主分區類型。(4).第6-8個字節:主分區最后一個扇區的物理位置。(5).第9-12字節:該主分區第一個扇區的邏輯地址。(6).第13-16字節:主分區的扇區總數。
最后的四個字節”主分區的扇區總數”,決定了這個主分區的長度。也就是說,一個主分區的扇區總數最多不超過2的32次方。如果每個扇區為512個字節,就意味著單個分區最大不超過2TB。
14.2.3 采用VBR/EBR方式引導操作系統
暫且先不討論grub如何管理啟動操作系統的,以VBR和EBR引導操作系統為例。
當bios讀取到MBR中的boot loader后,會繼續讀取分區表。分兩種情況:
(1)如果查找分區表時發現某個主分區表的第一個字節是0x80,也就是激活的分區,那么說明操作系統裝在了該主分區,然后執行已載入的MBR中的boot loader代碼,加載該激活主分區的VBR中的boot loader,至此,控制權就交給了VBR的boot loader了;
(2)如果操作系統不是裝在主分區,那么肯定是裝在邏輯分區中,所以查找完主分區表后會繼續查找擴展分區表,直到找到EBR所在的分區,然后MBR中的boot loader將控制權交給該EBR的boot loader。
也就是說,如果一塊硬盤上裝了多個操作系統,那么boot loader會分布在多個地方,可能是VBR,也可能是EBR,但MBR是一定有的,這是被bios給”綁定”了的。在裝LINUX操作系統時,其中有一個步驟就是詢問你MBR裝在哪里的,但這個MBR并非一定真的是MBR,可能是MBR,也可能是VBR,還可能是EBR,并且想要單磁盤多系統共存,則MBR一定不能被覆蓋(此處不考慮grub)。
如下圖,是我測試單磁盤裝3個操作系統時的分區結構。其中/dev/sda{1,2,3}是第一個CentOS 6系統,/dev/sda{5,6,7}是第二個CentOS 7系統,/dev/sda{8,9,10}是第三個CentOS 6系統,每一個操作系統的分區序號從前向后都是/boot分區、根分區、swap分區。
再看下圖,是裝第三個操作系統時的詢問boot loader安裝位置的步驟。
裝第一個操作系統時,boot loader可以裝在/dev/sda上,也可以選擇裝在/dev/sda1上,這時裝的是MBR和VBR,任選一個都會將另一個也裝上,從第二個操作系統開始,裝的是EBR而非MBR,且應該指定boot loader位置(如/dev/sda5和/dev/sda8),否則默認選項是裝在/dev/sda上,但這會覆蓋原有的MBR。
另外,在指定boot loader安裝路徑的下方,還有一個方框是操作系統列表,這就是操作系統菜單,其中可以指定默認的操作系統,這里的默認指的是MBR默認跳轉到哪個VBR或EBR上。
所以,MBR/VBR和EBR之間的跳轉關系如下圖。
使用這種方式的菜單管理操作系統啟動,無需什么stage1,stage1.5和stage2的概念,只要跳轉到了分區上的VBR或EBR,那么直接就可以加載引導該分區上的操作系統。
但是,這種管理操作系統啟動的菜單已經沒有意義了,現在都是使用grub來管理,所以裝第二個操作系統或第n個操作系統時不手動指定boot loader安裝位置,覆蓋掉MBR也無所謂,想要實現單磁盤多系統共存所需要做的,僅僅只是修改grub的配置文件而已。
使用grub管理引導菜單時,VBR/EBR就毫無用處了,具體的見下文。
14.3 grub階段
使用grub管理啟動,則MBR中的boot loader是由grub程序安裝的,此外還會安裝其他的boot loader。CentOS 6使用的是傳統的grub,而CentOS 7使用的是grub2。
如果使用的是傳統的grub,則安裝的boot loader為stage1、stage1_5和stage2,如果使用的是grub2,則安裝的是boot.img和core.img。傳統grub和grub2的區別還是挺大的,所以下面分開解釋,如果對于grub有不理解之處,見我的另一篇文章grub2詳解。
14.3.1 使用grub2時的啟動過程
grub2程序安裝grub后,會在/boot/grub2/i386-pc/目錄下生成boot.img和core.img文件,另外還有一些模塊文件,其中包括文件系統類的模塊。
[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之后、第一個分區之前的空閑空間,被稱為MBR gap,這段空間最小31KB,但一般都會是1MB左右。
實際上,core.img是多個img文件的結合體。它們的關系如下圖:
這張圖解釋了開機過程中grub2階段的所有過程,boot.img段的boot loader只有一個作用,就是跳轉到core.img對應的boot loader的第一個扇區,對于從硬盤啟動的系統來說,該扇區是diskboot.img的內容,diskboot.img的作用是加載core.img中剩余的內容。
由于diskboot.img所在的位置是以硬編碼的方式寫入到boot.img中的,所以boot.img總能找到core.img中diskboot.img的位置并跳轉到它身上,隨后控制權交給diskboot.img。隨后diskboot.img加載壓縮后的kernel.img(注意,是grub的kernel不是操作系統的kernel)以初始化grub運行時的各種環境,控制權交給kernel.img。
但直到目前為止,core.img都還不識別/boot所在分區的文件系統,所以kernel.img初始化grub環境的過程就包括了加載模塊,嚴格地說不是加載,因為在安裝grub時,文件系統類的模塊已經嵌入到了core.img中,例如ext類的文件系統模塊ext2.mod。
加載了模塊后,kernel.img就能識別/boot分區的文件系統,也就能找到grub的配置文件/boot/grub2/grub.cfg,有了grub.cfg就能顯示啟動菜單,我們就能自由的選擇要啟動的操作系統。
當選擇某個菜單項后,kernel.img會根據grub.cfg中的配置加載對應的操作系統內核(/boot目錄下vmlinuz開頭的文件),并向操作系統內核傳遞啟動時參數,包括根文件系統所在的分區,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 }
加載完操作系統內核后grub2就將控制權交給操作系統內核。
總結下,從MBR開始后的過程是這樣的:
1.執行MBR中的boot loader(即boot.img)跳轉到diskboot.img。
2.執行diskboot.img,加載core.img剩余的部分,并跳轉到kernel.img。
3.kernel.img讀取/boot/grub2/grub2.cfg,并顯示啟動管理菜單。
4.選中某菜單后,kernel.img加載該菜單項配置的操作系統內核/boot/vmlinux-XXX,并傳遞內核啟動參數,包括根文件系統所在分區和init ramdisk的路徑。
5.控制權交給操作系統內核。
14.3.2 使用傳統grub時的啟動過程
傳統grub對應的boot loader是stage1和stage2,從stage1跳轉到stage2大多數情況下還會用到stage1_5對應的boot loader。
與grub2相比,stage1和boot.img的作用是類似的,都在MBR中。當該段boot loader執行后,它的目的是跳轉到stage1_5的第一個扇區上,然后由該扇區的代碼加載剩余的內容,并跳轉到stage2的第一個扇區上。
stage1_5存在的理由是因為stage2功能較多,導致其文件體積較大(一般至少都有100多K),所以并沒有像core.img一樣嵌入到磁盤上,而是簡單地將其放在了boot分區上,但stage1并不識別boot分區的文件系統類型,所以借助中間的輔助boot loader即stage1_5來跳轉。
stage1_5的目的之一是識別文件系統,但文件系統的類型有很多,所以對應的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分區也只能對應一種stage1_5。這個stage1_5對應的boot loader一般會被嵌入到MBR后、第一個分區前的中間那段空間(即MBR gap)。
當執行了stage1_5對應的boot loader后,stage1_5就能識別出boot所在的分區,并找到stage2文件的第一個扇區,然后跳轉過去。
當控制權交給了stage2,stage2就能加載grub的配置文件/boot/grub/grub.conf并顯示菜單并初始化grub的運行時環境,當選中操作系統后,stage2將和kernel.img一樣加載操作系統內核,傳遞內核啟動參數,并將控制權交給操作系統內核。
所以,stage1、stage1_5和stage2之間的關系如下圖:
雖然絕大多數都提供了stage1_5,但它不是必須的,它的作用僅僅只是識別boot分區的文件系統類型,對于一個會編程的人來說,可以將固定boot分區的文件系統識別代碼嵌入到stage1中,這樣stage1自身就能識別boot分區,就不需要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到磁盤上,該文件系統類型的stage1_5占用了15個扇區,最后安裝stage1,并告知stage1 stage1_5的位置是第1到第15個扇區,之所以先嵌入stage1_5再嵌入stage1就是為了讓stage1知道stage1_5的位置,最后還告知了stage1 stage2和配置文件menu.lst(它是grub.conf的軟鏈接)的路徑。
14.4 內核加載階段
提前說明,下文所述均為sysV init系統啟動風格,systemd的啟動管理方式大不相同,所以不要將systemd管理的啟動方式與此做比較。
到目前為止,內核已經被加載到內存掌握了控制權,且收到了boot loader最后傳遞的內核啟動參數以及init ramdisk的路徑。
所有的內核都是以bzImage方式壓縮過的,壓縮后CentOS 6的內核大小大約為4M,CentOS 7的內核大小大約為5M。內核要能正常運作下去,它需要進行解壓釋放。
解壓釋放之后,將創建pid為0的idle進程,該進程非常重要,后續內核所有的進程都是通過fork它創建的,且很多cpu降溫工具就是強制執行idle進程來實現的。
然后創建pid=1和pid=2的內核進程。pid=1的進程也就是init進程,pid=2的進程是kthread內核線程,它的作用是在真正調用init程序之前完成內核環境初始化和設置工作,例如根據grub傳遞的內核啟動參數找到init ramdisk并加載。
14.4.1 加載init ramdisk
在前面,已經創建了pid=1的init進程和pid=2的kthread進程,但注意,它們都是內核線程,全稱是kernel_init和kernel_kthread,而真正能被ps捕獲到的pid=1的init進程是由kernel_init調用init程序后形成的。
要加載/sbin/init程序,首先要找到根分區,根分區是有文件系統的,所以內核需要先識別文件系統并加載文件系統的驅動,但文件系統的驅動又是放在根分區的,這就出現了先有雞還是先有蛋的矛盾。
解決的方法之一是像grub2識別boot分區的文件系統一樣,將根文件系統驅動模塊嵌入到內核中,但文件系統的種類太多,而且會升級,這樣就導致內核不斷的嵌入新的文件系統驅動模塊,內核不斷增大,這顯然是不合適的。
解決方法之二則像傳統grub借助中間過渡引導段stage1_5一樣,將根文件系統的驅動模塊放入一個中間過渡文件,在加載根文件系統之前先加載這個過渡文件,再由過渡文件跳轉到根文件系統。
方法二正是現在采用的,其采用的中間過渡文件稱為init ramdisk,它是在安裝完操作系統時生成的,這樣它會收集到當前操作系統的根文件系統是什么類型的文件系統,也就能只嵌入一個對應的文件系統驅動模塊使其變得足夠小。如下圖,它是安裝操作系統時安裝完所有軟件包后執行的一個收集過程。
在CentOS 5上采用的init ramdisk稱為initrd,而CentOS 6和CentOS 7采用的則是initramfs,它們的目的是一樣的,但在實現上卻大有不同。但它們都存放在/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的作用肯定不僅僅只是找到根文件系統,它還會做其他工作。具體還做什么工作,請繼續閱讀下文。
14.4.2 initrd
initrd其實是一個鏡像文件系統,是在內存中劃分一片區域模擬磁盤分區,在該文件中包含了找到根文件系統的腳本和驅動。
既然是文件系統,那么內核也必須要帶有對應文件系統的驅動,另外文件系統要使用就必須有根”/”,這個根是內存中的”虛根”。由于內核加載到這里已經初始化一些運行環境了,所以內核的運行狀態等參數也要保存下來,保存的位置就是內存中虛根下的/proc和/sys,此外還有收集到的硬件設備信息以及設備的運行環境也要保存下來,保存的位置是/dev。到此為止,pid=2的內核線程kernel_kthread就完成了基本工作,開始轉到kernel_init進程上了。
再之后就是kernel_init掛載真正的根文件系統并從虛根切換到實根,最后kernel_init將調用init程序,也就是真正的能被我們看見的pid=1的init進程,然后將控制權交給init,所以從現在開始,將切換到用戶空間,后續剩余的事情都將由用戶空間的程序完成。
以下是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必須是一個文件系統,是在內存中模擬出磁盤分區的,所以內核必須要帶有它的文件系統驅動,而initramfs則僅僅只是一個鏡像壓縮文件而非文件系統,所以它不需要帶文件系統驅動,在加載時,內核會將其解壓的內容裝入到一個tmpfs 中。
initramfs和initrd最大的區別在于init進程的區別對待。initramfs為了盡早進入用戶空間,它將init程序集成到了initramfs鏡像文件中,這樣就可以在initramfs裝入tmpfs時直接運行init進程,而不用去找根文件系統下的/sbin/init,由此掛載根文件系統的工作將由init來完成,而不再是內核線程kernel_init完成。最后從虛根切換到實根。
那根分區下的/sbin/init是干嘛的呢?可以認為是init ramdisk中init的一個備份,如果ramdisk中找不到init就會去找/sbin/init。另外,在正常運行的操作系統環境下,/sbin/init還經常用來完成其他工作,如發送信號。
其實initramfs完成了很多工作,解開它的鏡像文件就能發現它的目錄結構和真實環境下的目錄結構類似。以下是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目錄下發現init程序。
[root@xuexi tmp]# ll sbin/init lrwxrwxrwx 1 root root 22 Jun 29 23:28 sbin/init -> ../lib/systemd/systemd
14.5 操作系統初始化
下文解釋的是sysV風格的系統環境,與systemd初始化大不相同。
當init進程掌握控制權后,意味著已經進入了用戶空間,后續的事情也將以用戶空間為主導來完成。
init的名稱是initialize的縮寫,是初始化的意思,所以它的作用也就是初始化的作用。在內核加載階段,也有初始化動作,初始化的環境是內核的環境,是由kernel_init、kernel_thread等內核線程完成的。而init掌握控制權后,已經可以和用戶空間交互,意味著真正的開始進入操作系統,所以它初始化的是操作系統的環境。
操作系統初始化涉及了不少過程,大致如下:讀取運行級別;初始化系統類的環境;根據運行級別初始化用戶類的環境;執行rc.local文件完成用戶自定義開機要執行的命令;加載終端;
14.5.1 運行級別
在sysV風格的系統下,使用了運行級別的概念,不同運行級別初始化不同的系統類環境,你可以認為windows的安全模式也是使用運行級別的一種產物。
在Linux系統中定義了7個運行級別,使用0-6的數字表示。
0:halt,即關機
1:單用戶模式
2:不帶NFS的多用戶模式
3:完整多用戶模式
4:保留未使用的級別
5:X11,即圖形界面模式
6:reboot,即重啟
實際上,執行關機或重啟命令的本質就是向init進程傳遞0或6這兩個運行級別。
sysV的init程序讀取/etc/inittab文件來獲取默認的運行級別,并根據此文件所指定的配置執行默認運行級別對應的操作。注意,systemd管理的系統是沒有/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:
該文件告訴我們,系統初始化過程由/etc/init/rcS.conf完成,運行級別類的初始化過程由/etc/init.conf來完成,按下CTRL+ALT+DEL鍵要執行的過程由/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來完成系統環境類初始化動作,再讀取/etc/init/rc.conf來完成運行級別類動作。
14.5.2 系統環境初始化
先看看/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”這一行就表示要執行/etc/rc.d/rc.sysinit文件,該文件定義了系統初始化(system initialization)的內容,包括:
(1).確認主機名。
(2).掛載/proc和/sys等特殊文件系統,使得內核參數和狀態可與人進行交互。是否還記得在內核加載階段時的/proc和/sys?
(3).啟動udev,也就是啟動類似windows中的設備管理器。
(4)初始化硬件參數,如加載某些驅動,設置時鐘等。
(5).設置主機名。
(6).執行fsck檢測磁盤是否健康。
(7).掛載/etc/fstab中除/proc和NFS的文件系統。
(8).激活swap。
(9).將所有執行的操作寫入到/var/log/dmesg文件中。
14.5.3 運行級別環境初始化
執行完系統初始化后,接下來就是執行運行級別的初始化。先看看/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這個腳本來初始化指定運行級別的環境。Linux采用了將各運行級別初始化內容分開管理的方式,將0-6這7個運行級別要執行的初始化腳本分別放入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系統中是沒有/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加一個數字開頭,其后才是腳本名稱,且它們都是/etc/rc.d/init.d中文件的鏈接。S開頭表示進入該運行級別時要運行的程序,S字母后的數值表示啟動順序,數字越大,啟動的越晚;K開頭的表示退出該運行級別時要殺掉的程序,數值表示關閉的順序。
所有這些文件都是由/etc/rc.d/rc這個程序調用的,K開頭的則傳給rc一個stop參數,S開頭的則傳給rc一個start參數。
打開rc0.d和rc6.d這兩個目錄,你會發現在這兩個目錄中除了”S00killall”和”S01reboot”,其余都是K開頭的文件。
而在rc[2-5].d這幾個目錄中,都有一個S99local文件,且它們都是指向/etc/rc.d/rc.local的軟鏈接。S99表示最后啟動的一個程序,所以rc.local中的程序是2345這4個運行級別初始化過程中最后運行的一個腳本。這是Linux提供給我們定義自己想要在開機時(嚴格地說是進入運行級別)就執行的命令的文件。
當初始化完運行級別環境后,將要準備登錄系統了。
14.6 終端初始化和登錄系統
Linux是多任務多用戶的操作系統,它允許多人同時在線工作。但每個人都必須要輸入用戶名和密碼才能驗證身份并最終登錄。但登陸時是以圖形界面的方式給用戶使用,還是以純命令行模式給用戶使用呢?這是終端決定的,也就是說在登錄前需要先加載終端。至于什么是終端,見我的另一篇文章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
細心一點會發現,有的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進程監視,一發現被殺掉了init會立即重啟它。所以,只要getty進程一結束,init會立即監視到而重啟該進程。因此,用戶登錄成功后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 久久精品亚洲一区二区三区浴池| 婷婷久久综合九色综合98| 精品久久久久久中文字幕大豆网| 亚洲午夜久久久久久噜噜噜| 国产精品亚洲综合专区片高清久久久 | 国内精品久久久久久中文字幕 | 亚洲äv永久无码精品天堂久久| 欧美一区二区久久精品| 国产精品18久久久久久vr| 亚洲国产精品久久久天堂| 欧美一区二区久久精品| 99久久99久久| 伊人热热久久原色播放www| 久久99精品国产麻豆婷婷| 亚洲愉拍99热成人精品热久久| 久久成人影院精品777| 久久精品国产一区| 久久人人爽人人爽人人片AV东京热| 久久精品国产一区| 久久久久女人精品毛片| 久久婷婷是五月综合色狠狠| 99久久www免费人成精品 | 久久99精品免费一区二区| 99久久婷婷国产综合亚洲| 精品熟女少妇av免费久久| 亚洲国产成人久久综合区| 久久黄视频| 一本色道久久88精品综合| 亚洲婷婷国产精品电影人久久| 国产亚洲色婷婷久久99精品91| 欧美综合天天夜夜久久| 国产亚洲精久久久久久无码 | 久久美女人爽女人爽| 久久精品国产亚洲av影院| 久久久精品2019免费观看| 欧美一区二区三区久久综合| 国产成人综合久久综合| 久久国产精品成人片免费| 99久久婷婷国产综合亚洲| 久久99国产亚洲高清观看首页| 久久久中文字幕|