linux内核rest_init分析
代碼在start_kernel函數(shù)運行的最后到了rest_init()函數(shù)中
?
1:rest_init()函數(shù)分析
? (1)rest_init中調用kernel_thread函數(shù)啟動了2個內核線程,分別是:kernel_init和kthreadd
? (2)調用schedule函數(shù)開啟了內核的調度系統(tǒng),從此linux系統(tǒng)開始轉起來了。
? (3)rest_init最終調用cpu_idle函數(shù)結束了整個內核的啟動。也就是說linux內核最終結束了一個函數(shù)cpu_idle。這個函數(shù)里面肯定是死循環(huán)。
? (4)簡單來說,linux內核最終的狀態(tài)是:有事干的時候去執(zhí)行有意義的工作(執(zhí)行各個進程任務),實在沒活干的時候就去死循環(huán)(實際上死循環(huán)也可以看成是一個任務)。
? (5)之前已經啟動了內核調度系統(tǒng),調度系統(tǒng)會負責考評系統(tǒng)中所有的進程,這些進程里面只有有哪個需要被運行,調度系統(tǒng)就會終止cpu_idle死循環(huán)進程(空閑進程)轉而去執(zhí)行有意義的干活的進程。這樣操作系統(tǒng)就轉起來了。
?
2.1:什么是內核線程
? (1)進程和線程。簡單來理解,一個運行的程序就是一個進程。所以進程就是任務、進程就是一個獨立的程序。
獨立的意思就是這個程序和別的程序是分開的,這個程序可以被內核單獨調用執(zhí)行或者暫停。
? (2)在linux系統(tǒng)中,線程和進程非常相似,幾乎可以看成是一樣的。實際上我們當前講課用到的進程和線程的概念就是
一樣的。
? (3)進程/線程就是一個獨立的程序。應用層運行一個程序就構成一個用戶進程/線程,那么內核中運行
一個函數(shù)(函數(shù)其實就是一個程序)就構成了一個內核進程/線程。
? (4)所以我們kernel_thead函數(shù)運行一個函數(shù),其實就是把這個函數(shù)變成了一個內核線程去運行起來,然后他可以被內核調度系統(tǒng)去調度。說白了就是去調度器注冊了一下,以后人家調度的時候會考慮你。
?
2.2:進程0、進程1、進程2
?(1)操作系統(tǒng)是用一個數(shù)字來表示/記錄一個進程/線程的,這個數(shù)字就被稱為這個進程的進程號。這個號碼是從0開始分配的。因此這里涉及到的三個進程分別是linux系統(tǒng)的進程0、進程1、進程2.
?(2)在linux命令行下,使用ps命令可以查看當前l(fā)inux系統(tǒng)中運行的進程情況。
(4)我們在ubuntu下ps -aux可以看到當前系統(tǒng)運行的所有進程,可以看出進程號是從1開始的。為什么不從0開始,因為進程0不是一個用戶進程,而屬于內核進程。
? 進程0:進程0其實就是剛才講過的idle進程,叫空閑進程,也就是死循環(huán)。
? 進程1:kernel_init函數(shù)就是進程1,這個進程被稱為init進程。
? 進程2:kthreadd函數(shù)就是進程2,這個進程是linux內核的守護進程。它的作用是管理調度其他內核進程這個進程是用來保證linux內核自己本身能正常工作的。
?
3:init進程分析
? 需要注意的一點是這個進程剛開始運行的時候是內核態(tài),是屬于內核進程,然后它自己運行了一個用戶太下面的程序后把自己強行轉成了用戶態(tài),因為init進程自身完成了從內核態(tài)到用戶態(tài)的過渡,所以后續(xù)的其他進程都可以工作在用戶態(tài)下面了
?
3.1:init進程在內核態(tài)下做了什么
? 重要的點就掛載根文件系統(tǒng),并試圖找到用戶態(tài)下的那個init程序,原因是init進程要完成從內核態(tài)到用戶態(tài)的轉變就必須去運行一個用戶態(tài)的應用程序,而內核源代碼中的程序都是屬于內核態(tài)的,所以這個應用程序必須不屬于內核源代碼,這樣才能保證自己是用戶態(tài),所以這個應用程序就的是由另外一份文件提供,即根文件系統(tǒng)
3.2: init進程在用戶態(tài)下做了什么
? init進程大部分有意義的工作都是在用戶態(tài)下進行的,原因是用戶態(tài)下的所有進程都是直接或者間接由init進程生成的。
3.3:如何從內核態(tài)跳躍到用戶態(tài)?還能回來不?
? init進程在內核態(tài)下面時,通過調用kernel_execve函數(shù)來執(zhí)行一個用戶空間編譯鏈接的應用程序就跳躍到了用戶態(tài)下面了,需要注意的是,這個跳躍的過程進程號并沒有改變還是進程1,并且這個跳躍是單向的,以后要從用戶態(tài)回到內核態(tài)只有走API這一條路了
kernel_execve函數(shù)被調用的路徑start_kernel->rest_init->kernel_thread->kernel_init->init_post->run_init_process->kernel_execve
?
4:init進程在內核態(tài)下的分析(也就是kernel_init函數(shù))
4.1:打開控制臺,代碼如下:
| 1 2 3 4 5 | /*?Open?the?/dev/console?on?the?rootfs,?this?should?never?fail?*/ if?(sys_open((const?char?__user?*)?"/dev/console",?O_RDWR,?0)?<?0) printk(KERN_WARNING?"Warning:?unable?to?open?an?initial?console.\n"); (void)?sys_dup(0); (void)?sys_dup(0); |
? (1)linux系統(tǒng)中每個進程都有自己的一個文件描述符表,表中存儲的是本進程打開的文件。
? (2)linux系統(tǒng)中有一個設計理念:一切屆是文件。所以設備也是以文件的方式來訪問的。我們要訪問一個設備,就要去打開這個設備對應的文件描述符。譬如/dev/fb0這個設備文件就代表LCD顯示器設備,/dev/buzzer代表蜂鳴器設備,/dev/console代表控制臺設備。打開一個設備的文件就會得到這個設備的文件描述符(或者是文件描述符的編號),這個編號就代表這個設備,以后操作這個設備就用這個文件描述符來操作它
? (3)這里我們打開了/dev/console文件,并且復制了2次文件描述符,一共得到了3個文件描述符。這三個文件描述符分別是0、1、2.這三個文件描述符就是所謂的:標準輸入、標準輸出、標準錯誤。
? (4)進程1打開了三個標準輸出輸出錯誤文件,因此后續(xù)的進程1衍生出來的所有的進程默認都具有這3個三件描述符
?
4.2:掛載根文件系統(tǒng),代碼如下
| 1 2 3 4 | if?(sys_access((const?char?__user?*)?ramdisk_execute_command,?0)?!=?0)?{ ramdisk_execute_command?=?NULL; prepare_namespace(); } |
? (1)prepare_namespace函數(shù)中掛載根文件系統(tǒng)
? (2)根文件系統(tǒng)在哪里?根文件系統(tǒng)的文件系統(tǒng)類型是什么? uboot通過傳參來告訴內核這些信息。uboot傳參中的root=/dev/mmcblk0p2 rw 這一句就是告訴內核根文件系統(tǒng)在哪里uboot傳參中的rootfstype=ext3這一句就是告訴內核rootfs的類型。
? (3)如果內核掛載根文件系統(tǒng)成功,則會打印出:VFS: Mounted root (ext3 filesystem) on device 179:2.如果掛載根文件系統(tǒng)失敗,則會打印:No filesystem could mount root, tried: ?yaffs2
? (4)如果內核啟動時掛載rootfs失敗,則后面肯定沒法執(zhí)行了。內核中設置了啟動失敗休息5s自動重啟的機制,因此這里會自動重啟,所以有時候大家會看到反復重啟的情況。
? (5)如果掛載rootfs失敗,可能的原因有:最常見的錯誤就是uboot的bootargs設置不對。rootfs燒錄失敗(fastboot燒錄不容易出錯,以前是手工燒錄很容易出錯)rootfs本身制作失敗的。(尤其是自己做的rootfs,或者別人給的第一次用)
?
5:執(zhí)行用戶態(tài)下的進程1程序
? (1)上面一旦掛載rootfs成功,則進入rootfs中尋找應用程序的init程序,
這個程序就是用戶空間的進程1.找到后用run_init_process(里面的kernel_execve函數(shù))去執(zhí)行他
? (2)我們如果確定init程序是誰?方法是:先從uboot傳參cmdline中看有沒有指定,如果有指定先執(zhí)行cmdline中指定的程序。cmdline中的init=/linuxrc這個就是指定rootfs中哪個程序是init程序。這里的指定方式就表示我們rootfs的根目錄下面有個名字叫l(wèi)inuxrc的程序,這個程序就是init程序。如果uboot傳參cmdline中沒有init=xx或者cmdline中指定的這個xx執(zhí)行失敗,還有備用方案。
第一備用:/sbin/init,第二備用:/etc/init,第三備用:/bin/init,第四備用:/bin/sh。
如果以上都不成功,則kernel啟動失敗
總結
以上是生活随笔為你收集整理的linux内核rest_init分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据可视化及数据保存
- 下一篇: 嵌入式linux移植ssh,将SSH移植