1. 程式人生 > >LINUX系統初始化

LINUX系統初始化

轉載地址:https://blog.csdn.net/runner668/article/details/79844423

說明BIOS是位於位於主機板flash rom(掉電不丟失0)中的程式,作業系統Boot Loader位於硬碟MBR中。BIOS在完成

硬體檢測和資源分配後。將硬碟MBR中的Boot Loader讀到系統的RAM中,然後將控制權交給作業系統Boot Loader。

Boot  Loader的主要任務就是將核心映像從硬碟讀到RAM中,然後跳轉到核心的入口點執行,即開始啟動作業系統。

一、對於PC機來說          

BIOS —> MBR —> Kernel —> init1、當電腦一開啟電源時電腦就會進入BIOS(BIOS的工作主要是檢測一些硬體裝置); 
2、檢測完後會進入MBR也就是boot loader(MBR位於硬碟的第一個扇區總共512bytes,其中前446bytes裡面的編碼是在選擇引導分割槽也就是決定要由哪個分割槽來引導); 
3、 載入系統的Kernel(核心),在Kernel裡主要是載入電腦裝置的驅動程式,以便可以控制電腦上的裝置,並且以只讀方式來掛載根目錄,也就是一開始 只能讀取到根目錄所對應的那個分割槽,所以/etc、/bin、/sbin、/dev、/lib這五個目錄必須同根目錄在一個分割槽中; 
4、最後啟動init這個程式,所以init這個程式的程序編號為1,是Linux中第一個執行的程序; 
init這個程式會根據 Run level來執行以下這些程式: 
·/etc/rc.d/rc.sysinit; 
·/etc/rc.d/rc 和 etc/rc.d/rc?.d/ 
·/etc/rc.d/rc.local 
·如果有適當的圖形介面管理程式 

二、BIOS初始化時主要的三個任務 
BIOS(Basic Input/Output System) 
1、電腦周邊裝置的檢測,加電自檢 POST (Power on self test); 
2、BIOS會選擇要由哪一個裝置來開機,例如:軟盤啟動、光碟啟動、網路啟動、最常見的從硬碟啟動; 
3、選擇好由哪個裝置開機後,就開始讀取這個裝置的 MBR 引導扇區; 

三、介紹Boot Loader中的主要工作

1、Boot Loader可以安裝在兩個地方: 

· 安裝在硬碟的MBR中; 
· 當有時候MBR中被其他開機管理程式佔用就可以將 Boot Loader 安裝在硬碟中的其中一個分割槽的引導扇區上,; 
2、Boot Loader的程式碼分為兩個階段: 
(1)Boot Loader第一階段的程式碼非常小,只有446bytes,可以存入在MBR或是某一個分割槽的引導扇區裡, 
(2)Boot Loader第一階段的程式碼是從 boot 分割槽來載入的,就是說 Boot Loader 第二階段程式碼存放在 /boot 這個分割槽中; 
二:嵌入式系統Boot Loader

     嵌入式系統通常沒有BIOS,整個系統的載入啟動任務完全由Boot Loader完成。不同系統的Boot Loader是不同的。在ARM

嵌入式系統中,系統復位後通常從地址Ox00000000處開始執行,Boot Loader就從這裡開始。

三:linux核心啟動過程

linux系統的啟動,指的是從系統加電到系統控制檯顯示“login:”登入提示符為止的系統執行階段,與這部分動作密切相關的程式碼主要是:

四個彙編程式:bootsect.S setup.S head.S entry.S
init目錄下的main.c檔案              //核心的入口函式

主要檢視init/main.c中的start_kernel函式: 


在系統啟動過程中,主要關注一下幾個方面:

中斷系統及排程系統
檔案系統的初始化
裝置管理系統的初始化
網路協議的初始化
在init/main.c中reset_init函式: 


在此函式中,有kernel_thread函式,這個函式建立了核心執行緒,原型如下:

int kernel_thread(int (*fn), void *arg, unsigned long flags)
1
此函式定義在arch/xxx/kernel/process.c中,它利用do_fork()函式建立一個新的核心態執行緒,linux的核心執行緒是沒有虛擬儲存空間的程序,它們執行在核心中,直接使用實體地址空間。 
kernel_thread建立的新的核心執行緒是init,然後返回,執行unlock_kernel(與start_kernel中的lock_kernel對應),接著執行cpu_idle(),這實際是執行初始化主執行緒的歸宿:它觀察自己是否處於TIF_NEED_RESCHED——在need_resched實現,如果不是,就讓自己睡眠,否者完成schedule()函式。TIF即Thread Information Flag的意思。

下一步轉入init函式中: 


init執行緒呼叫了許多函式,但與網路有關的在do_basic_setup中初始化的sock_init(),然後繼續執行do_initcalls函式,此函式定義在c檔案中,有兩個重要的變數__initcall_start和__initcall_end,它們的定義如下:

extern initcall_t __initcall_start, __initcall_end;
static void __init do_initcalls(void)
{
    initcall_t *call;
    int count = preempt_count();
    /*從start遍歷到end*/
    for(call = &__initcall_start; call < &__initcall_end; call++){
        char *mesg;
        ......
        (*call)();
 
        msg = NULL;
}
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
initcall_t類與__initcall_start,__initcall_end定義在include/linux目錄下的init.h檔案中。

/////////////////////////////以下摘自網路//////////////

我們執行程式只需要點選應用程式的圖示就可以了,但在這之前,我們必須啟動我們的系統。在一切之前,我們必須有某些程式去引導我們系統的核心,這些程式就是核心載入程式了,例如LILO、GRUB、U-Boot、RedBoot。而這些載入程式同樣需要被其他程式載入和執行,這樣說下去,茫茫人生何處才是盡頭啊?想必大家可以想到的----硬體!這麼長的過程複雜、崎嶇!正所謂萬事開頭難,但不怕,我們來一起走過去吧!

X86的引導過程如圖:

cpu自身的初始化:這是引導的第一步,如果在多處理器系統上,那麼每個cpu都要自身初始化。cpu初始化後,cpu從某個固定的位置(應該是0Xfffffff0)取指,這條指令是跳轉指令,目的地是BIOS的首部程式碼,但是cpu並不在乎BIOS是否存在,它僅僅只是執行這個地址的指令而已!

BIOS:BIOS是隻讀儲存器(ROM),被固化於主機板上。其工作主要有兩個,就是上圖的加電自檢即是POST(post on self  test)與載入核心載入程式。

那麼他們是具體完成什麼工作的呢?

1) 加電自檢:完成系統的硬體檢測,其中包括記憶體檢測、系統匯流排檢測等工作。

2) 載入核心載入程式:在POST完成後,就要載入核心載入程式了,那它儲存在哪裡呢?磁盤裡!哈哈,BIOS會讀取0磁頭,0磁軌,一扇區的512個位元組,這個扇區有叫做MBR(主引導記錄),MBR中儲存了核心載入程式的開始部分,BIOS將其裝入記憶體執行。512個位元組的MBR有些什麼呢?這裡有必要說說MBR!MBR分割槽表以80為起始,以55AA為結束,共64個位元組。具體的MBR知識自己百度!

MBR:1) 446個位元組的載入程式程式碼

2) 64個位元組的分割槽表,有多少個分割槽呢。。?這還真不知道!分為4個分割槽表,一個可啟動分割槽和三個不可啟動分割槽。

3) 2個位元組的0XAA55,用於檢查MBR是否有效。

需要注意的是,核心載入程式被載入完後,POST部分的程式碼會被從記憶體中清理,只留部分在記憶體中留給目標作業系統使用。

核心載入程式:核心載入程式分兩部分:主、次載入程式。主載入程式的主要工作就是收索,尋找活動的分割槽,將活動的分割槽引導記錄中的次載入程式載入到記憶體中並且執行。而這個次載入程式就是負責載入核心的並且將控制權交給核心。上面提過核心載入程式有LILO、GRUB、U-Boot、RedBoot。其中前面兩個為pc中的,而後面兩個是嵌入式的。

核心:核心以壓縮的形式存在,不是一個可執行的核心!所以核心階段首先要做的是自解壓核心映像。這裡說說編譯核心後形成的核心壓縮的映像vmlinuz。編譯生成vmlinux後,一般會對其進行壓縮為vmlinuz,使其成為zImage--小於512KB的小核心,或者成為bzImage--大於512KB的大核心。

vmlinuz結構如圖:

做了這麼多工作終於把linux的核心給引匯出來了。!!下面我們來初始化這個千呼萬喚始出來的linux核心!

核心初始化:核心會呼叫一系列的初始化函式去對所有的核心元件進行初始化,由start_kernel()---.....---> rest_init() ----..----> kernel_init() ----....--> init_post() ------到---> 第一個使用者init程序結束。

start_kernel():其完成大部分核心初始化的工作。相關的程式碼去查閱核心的原始碼吧!www.kernel.org

rest_init():start_kernel() 呼叫rest_init() 進行後面的初始化工作。

kernel_init():此函式主要完成裝置驅動程式的初始化,並且呼叫 init_post() 啟動使用者空間的init程序。

init_post():初始化的尾聲,第一個使用者空間的init 橫空出世!其PID始終為1。

init: 核心會在過去曾使用過init的幾個地方查詢它,它的正確位置(對Linux系統來說)是/sbin/init。如果核心找不到init,它就會試著執行/bin/sh,如果執行失敗,系統的啟動也會失敗。找到/sbin/init 後init會根據/etc/inittab (網上的資料都這樣說的,我在Ubuntu3.8的核心裡找不到,但在Fedora中可以找到!是發行版本不同吧?(求指教)這裡附上圖片!)檔案完成其他一些工作,例如:getty程序接受使用者的登入,設定網路等。這裡詳細說說吧。

fedora19的etc有inittab檔案:

系統中所有的程序形成樹型結構,而這棵樹的根就是在核心態形成的,系統自動構造的0號程序,它是所有的程序的祖先。大致是在vmlinux的入口 startup_32(head.S)中為pid號為0的原始程序設定了執行環境,然後原是程序開始執行start_kernel()完成Linux核心的初始化工作。包括初始化頁表,初始化中斷向量表,初始化系統時間等。繼而呼叫 fork(),建立第核心init程序:

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);  //    引數CLONE_FS  | CLONE_SIGHAND表示0號執行緒和1號執行緒分別共享檔案系統(CLONE_FS)、開啟的檔案(CLONE_FILES)和訊號處理程式 (CLONE_SIGHAND)。

這個程序就是著名的pid為1的init程序(核心態的),它會繼續完成剩下的初始化工作比且建立若干個用於快取記憶體和虛擬主存管理的核心執行緒,如kswapd和bdflush等,然後execve(/sbin/init)(生成使用者態的init程序pid=1,因為沒有呼叫fork(),所以pid還是1!), 成為系統中的其他所有程序的祖先。回過頭來看pid=0的程序,在建立了init程序後(核心態的),pid=0的程序呼叫 cpu_idle()在主cpu中演變成了idle程序。而核心態pid=1的init程序同樣會在各個從cpu上生成idel程序。 init在演變成/sbin/init之前,會執行一部分初始化工作,其中一個就是smp_prepare_cpus(),初始化SMP處理器,在這過程中會在處理每個從處理器時呼叫

task =copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL, NULL, 0);  init_idle(task, cpu);

即從init中複製出一個程序,並把它初始化為idle程序(pid仍然為0)。從處理器上的idle程序會進行一Activate工作,然後執行cpu_idle()。

執行/sbin/init,這樣從核心太過度到使用者態,按照配置檔案/etc/inittab 要求完成啟動的工作,並且建立若干個不編號為1,2,3...號的終端註冊程序getty,其作用就是設定其程序組的標識號,監視配置到系統終端的介面電路,當有訊號來到的時候,getty會執行execve()生成註冊程序login,使用者可以註冊登入了,如果登入成功,則login會演化為shell程序,若login不成功則關閉開啟的終端線路,使用者1號程序會建立新的getty。到這裡init的流程基本完成了。奉上大圖一張!

init的過程:

init 的流程講完了,這裡粗略說說init還做了什麼事。先來認識下執行級別。

執行級別:    linux可以在不同的場合啟動不同的開機啟動程式,這就叫做執行級別。根據不同的執行級別啟動不同的程式。例如在用作伺服器的時候要開啟Apache,而桌面就不需要。

linux預先設定了7種執行級別(0--6)。ubuntu有8種(0--6、S),這裡主要以ubuntu來說。0:關閉系統,1:系統進入單使用者模式,S:單使用者恢復模式,文字登入介面,只執行少數的系統服務。2:多使用者模式(系統預設的級別),圖形登入介面,執行所有預定的系統服務。3--5:多使用者模式,圖形登入介面,執行所有預定的系統服務(對於系統定製而言,執行級別2-5的作用等同),6:重啟系統。

對於每個執行級別,在/etc/都有對應的子錄目---/etc/rcN.d 用來指定要載入的程式。

細心看的話可以發現,除README外其他的檔案都是“S開頭+兩位數字+程式名”的形式。這代表什麼呢?S代表Start啟動。如果是K的話則代表關閉kill,如果從其他的執行級別切換過來的話則要關閉程式。之後的數字為處理的順序,越小則越早執行,如果數字相同,按字母的順序啟動。

上圖可以看出這裡的檔案都是連結檔案,為什麼呢?上面說過各種執行級別有各自的一個錄目用來存放各自的開機程式,如果有多個執行級別要啟動同一個程式,那麼這個程式的指令碼會被拷貝到每一個錄目裡,這樣做的話,如果要修改啟動指令碼就要修改每一個錄目,這樣不科學啊!!!!所以這些檔案都是連結檔案指向/etc/init.d。啟動時就是執行這些指令碼的。

子系統的初始化:

核心選項:linux 允許使用者傳遞核心配置選項給核心,核心在初始化的過程中呼叫parse_args()函式對這些選項進行解析,之後呼叫相應的函式進行處理。對於parse_args()函式,其能夠解析形如“變數名=值”的字串,在模組載入的時候也會被呼叫來解析模組的引數。

子系統的初始化:在完成核心選項的解析後,就進入初始化的函式呼叫。在kernel_init()函式中呼叫do_basic_setup()函式再去呼叫do_initcalls()函式來完成。各個函式具體實現請查閱原始碼!

登入:登入有三種方式。

1) 命令列登入

init建立getty,等使用者輸入使用者名稱和密碼,輸入完成後呼叫login程式進行核對密碼,如果正確就讀取/etc/passwd檔案,讀取這個使用者的指定的shell並啟動它。

2) ssh登入

系統呼叫sshd程式,取代getty和login,之後啟動shell。

3) 圖形介面登入

init程序呼叫顯示管理器,Gnome圖形介面對應為gdm,然後輸入使用者名稱、密碼,如果密碼正確就啟動使用者會話。

到這裡系統就啟動起來了!說了這麼多,現在我們來玩點好玩的!!!!!!簡單熟悉一下linux的啟動!!!!!!!!!!!首先要準備一個ubuntu系統最好13.04或者12.04都得,可以是虛擬機器(最好是在虛擬機器上操作!因為我因為這個小實驗而重灌了一遍真機系統!當時不懂啊!慘。。。。。。。。。)。

1:進入系統,在主資料夾(方便)新建一個c語言檔案並且命名為init.c,輸入---->最簡單的c語言程式helloworld,不這是最偉大的c語言程式!

main(){

printf("helloworld!\n");

}

之後不用說就是編譯啦。開啟終端(ctrl + alt + t)執行這條指令:gcc --static -o init init.c    這樣init檔案就準備好了!猜到我想做什麼了嗎。。?哈哈我們繼續!!

2:將上文提到過的/sbin/init 檔案備份執行這條指令:sudo cp /sbin/init /sbin/init.bak  備份成init.bak 檔案

將原來的init 檔案刪除,執行指令:sudo rm /sbin/init

好了,下一步就將我們的helloworld  init複製到/sbin/錄目下!執行指令: sudo mv init /sbin/  這條指令之前要注意你終端當前的路徑與init的路徑是不是相同,要不不能成功的!!

3:這樣就做好了!!!我們果斷重啟!這樣在啟動中我們看到了我們的 helloworld!唉。。它停哪裡了!!那是肯定的,上文介紹了init的作用,你換了,不能啟動是正常的啦。。思考下,到這裡核心處於什麼狀態。?這裡其實核心基本已經初始化完成了!就剩下程序的生成與子系統的初始化了。在init_post()函式的最尾會查詢init的路徑,如果找不到就崩潰。而且會試圖建立一個互動式的shell(/bin/sh)來代替找不到的init,讓使用者可以修復這種錯誤、重新啟動。

4:現在我們來恢復我們的系統!剛才在 2 中的步驟不會讓你白做的!重新啟動系統,並在一開始就按著左Shift (我以前的系統是不用的,不知道以前對grub.cfg做了什麼壞事!!),系統會自動檢測到這個訊號的,這樣會出現下面圖中的介面。(建議去看看grub2的特性!):

按下e,進行編輯,按鍵盤右下角  向下的按鍵,在linux 這一行的最尾(quiet)前面加入---->  init=/sbin/init.bak  ,按下 ctrl + x 啟動系統。如圖:

如果一切沒有錯,那麼你肯定能成功地重新啟動系統!如果看到登入介面,那麼恭喜你,你已經在漫漫人生路上走了一趟了!

好了,到這裡本文要講的知識已經講完了。下面我們一起來思考一下我在這其中遇到的沒有解決的問題,請大神們指教!

問題:  首先我的實驗是在虛擬機器上做的,原本我以為init是跟核心的壓縮檔案有關,所以用3.8.0-19 核心進去系統將init刪了,這樣3.8.0-19核心肯定啟動不了核心。我用3.8.0-27核心進去系統,同樣不能進去。。還是出現偉大的helloworld!這樣看 init跟vmlinux等 在boot裡的檔案無關了!那麼init關機後會被儲存在哪裡!?還有一個最重要的!我重新裝了一遍系統(虛擬機器的),這是沒有3.8.0-27核心的,我將我真機的3.8.0-27的核心檔案(/boot 裡的vmlinuz檔案等)放到虛擬機器的/boot/資料夾裡,重新啟動系統,發現解析度變了,游標不靈活(可能是解析度的關係!),螢幕大小感覺是原來的兩倍。這是為什麼呢?
--------------------- 
作者:runner668 
來源:CSDN 
原文:https://blog.csdn.net/runner668/article/details/79844423 
版權宣告:本文為博主原創文章,轉載請附上博文連結!