init系統簡介:
Linux 操作系統的啟動首先從 BIOS 開始,接下來進入 boot loader,由 bootloader 載入內核,進行內核初始化。內核初始化的最后一步就是啟動 pid 為 1 的 init 進程,這個進程是系統的第一個進程,它負責產生其他所有用戶進程。
init 的一些特點
- init是Linux系統操作中不可缺少的程序之一。
- 所謂的init進程,它是由內核啟動的第一個用戶級進程。內核自行啟動(已被裝入內存,開始運行,并已初始化所有的設備驅動程序和數據結構等)之后,就通過啟動一個用戶級程序init的方式,完成引導進程。所以,init始終是第一個進程(其進程編號始終為1)。
- 僅僅將內核運行起來是毫無實際用途的,必須由 init 系統將系統代入可操作狀態。比如啟動外殼 shell 后,便有了人機交互,這樣就可以讓計算機執行一些預訂程序完成有實際意義的任務(這其實就是就是后面所說的內核態到用戶態的轉變)。內核會在過去曾使用過init的幾個地方查找它,它的正確位置(對Linux系統來說)是/sbin/init。如果內核找不到init,它就會試著運行/bin/sh,如果運行失敗,系統的啟動也會失敗。
Linux下的三個特殊進程
Linux下有三個特殊的進程idle進程(PID=0),init進程(PID=1),和kthreadd(PID=2)
- idle進程由系統自動創建,運行在內核態。idle進程其pid=0,其前身是系統創建的第一個進程,也是唯一一個沒有通過fork或者kernel_thread產生的進程。完成加載系統后,演變為進程調度、交換。
- kthreadd進程由idle通過kernel_thread創建,并始終運行在內核空間,負責所有內核進程的調度和管理。它的任務就是管理和調度其他內核線程kernel_thread, 會循環執行一個kthread的函數,該函數的作用就是運行kthread_create_list全局鏈表中維護的kthread, 當我們調用kernel_thread創建的內核線程會被加入到此鏈表中,因此所有的內核線程都是直接或者間接的以kthreadd為父進程 。
- init進程由idle通過kernel_thread創建,在內核空間完成初始化后,加載init程序。在這里我們就主要了解下init進程,init進程由0進程創建,完成系統的初始化,是系統中所有其他用戶進程的祖先進程
Linux中的所有進程都是由init進程創建并運行的。首先Linux內核啟動,然后在用戶空間中啟動init進程,再啟動其他系統進程。在系統啟動完成后,init將變成為守護進程監視系統其他進程。(內核態轉變為用戶態)
大致過程為:0號進程->1號內核進程->1號用戶進程(init進程)->getty進程->shell進程
init進程完成從內核態向用戶態的轉變
一個進程先后兩種狀態
init進程剛開始運行的時候是內核態,它屬于一個內核線程,然后運行一個用戶態下面的程序后(如/sbin/init),把自己轉成用戶態(后面的進程需要工作在用戶態下)。
init進程完成了從內核態到用戶態的過渡,因此后續的其他進程都可以工作在用戶態。
init進程在內核態下的工作內容
主要是掛載根文件系統,并試圖找到用戶態下的那個init程序。(這句話看出,init進程是早于init程序運行的。)
init進程要把自己轉成用戶態就必須運行一個用戶態的應用程序,要運行這個應用程序就必須得找到這個應用程序,要找到這個應用程序就必須得掛載根文件系統,因為所有的應用程序都在文件系統中。
內核源代碼中的所有函數都處于內核態,執行其中任何一個都不能脫離內核態。而應用程序必須不屬于內核源代碼,這樣才能保證應用程序處于用戶態。這里執行的init程序和內核不在一起,由根文件系統另外提供。
init進程在用戶態下的工作內容
init進程大部分有意義的工作都是在用戶態下進行的。init進程對操作系統的意義在于,其他所有的用戶進程都直接或者間接派生自init進程。
init進程如何從內核態跳躍到用戶態 ?還能回來不?
init進程處于內核態時,通過函數do_execve來執行一個用戶空間編譯鏈接的應用程序就跳躍到用戶態了。
- 跳躍過程中進程號沒有改變,一直是進程1。
- 跳躍過程是單向的,一旦執行init程序轉到用戶態,整個操作系統就算真正運轉起來了,以后只能在用戶態下工作,用戶態下想要進入內核態只能通過調用API。
1、init進程掛載了根文件系統
(1)prepare_namespace函數掛載根文件系統。
(2)根文件系統在哪里?根文件系統的文件系統類型是什么?
uboot通過傳參來告訴內核這些信息。
uboot傳參中的root=/dev/mmcblk0p2 rw 這一句就是告訴內核根文件系統在哪里。
uboot傳參中的rootfstype=ext3這一句就是告訴內核rootfs的類型。
(3)掛載結果
如果內核掛載根文件系統成功,則會打印出:VFS: Mounted root (ext3 filesystem) on device 179:2。(也可能其他數字)
如果掛載根文件系統失敗,則會打印:No filesystem could mount root, tried: yaffs2
(4)如果內核啟動時掛載rootfs失敗,則后面無法執行。
內核中設置了啟動失敗休息5s自動重啟的機制,因此這里會自動重啟,所以有時候大家會看到反復重啟的情況。
(5)如果掛載rootfs失敗,可能的原因有
最常見的錯誤就是uboot的bootargs設置不對。
rootfs燒錄失敗(fastboot燒錄不容易出錯)。
rootfs本身制作失敗的。
2、init進程執行init程序完成內核態到用戶態的轉變
(1)一旦掛載rootfs成功,則進入rootfs中尋找應用程序的init程序(在init_post()函數中),找到后用run_init_process去執行。
(2)如果確定init程序是誰?
先從uboot傳參cmdline中看有沒有指定,如果有指定先執行cmdline中指定的程序。比如init=/linuxrc表示rootfs的根目錄下的linuxrc程序就是init程序。
如果uboot傳參cmdline中沒有init=xx或者cmdline中指定的這個xx執行失敗,還有備用方案。第一備用:/sbin/init,第二備用:/etc/init,第三備用:/bin/init,第四備用:/bin/sh。如果以上都不成功,則沒有辦法了。
init=/linuxrc一般指向busybox。
3、init進程構建了用戶交互界面
(1)init進程是其他用戶進程的祖先。
linux系統中一個進程的創建是通過其父進程創建出來的。根據這個理論只要有一個父進程就能生出一堆子孫進程了。
(2)init啟動了login進程(用戶登錄進程)、命令行進程(提供命令行環境)、shell進程(提供命令解釋和執行)。
(3)shell進程啟動了其他用戶進程。
命令行和shell一旦工作,用戶就可以在命令行下通過./xx的方式來執行其他應用程序,每一個應用程序的運行就是一個進程。
4、init進程打開了控制臺
(1)linux系統中每個進程都有自己的一個文件描述符表,表中存儲的是本進程打開的文件。
(2)linux系統中一切皆是文件,因此設備也是以文件的方式來訪問的。要訪問一個設備,就要打開此設備對應的文件描述符。譬如/dev/fb0這個設備文件就代表LCD顯示器設備,/dev/buzzer代表蜂鳴器設備,/dev/console代表控制臺設備。
(3)這里打開了/dev/console文件,并且復制了2次文件描述符,一共得到了3個文件描述符。這三個文件描述符分別是0、1、2,就是所謂的標準輸入、標準輸出、標準錯誤這3個文件描述符。
(4)進程1打開了這3個文件描述符,因此進程1衍生出來的所有的進程默認都具有這3個文件描述符。
運行級別
簡單的說,運行級就是操作系統當前正在運行的功能級別。這個級別有多種,以centos為例,6和7版本的設置方法不同,具體對應關系如下。
init level (centos 6 /etc/inittab) | systemctl target(centos 7) | 說明 |
---|---|---|
0 | poweroff.target | 停機(千萬不能把initdefault 設置為0 ) |
1 | rescure.target | 單用戶(救援)模式 |
2 | multi-user.target | 多用戶,沒有 NFS |
3 | multi-user.target | 完整的多用戶文本模式級別,登錄后進入到控制臺命令行模式 |
4 | 未使用 | |
5 | graphical.target | X11 (xwindow,能夠正常切換的前提是系統支持) |
6 | reboot.target | 重新啟動 |
[root@k8s-m1 ~]# ll /usr/lib/systemd/system/runlevel*target
lrwxrwxrwx 1 root root 15 Feb 27 16:14 /usr/lib/systemd/system/runlevel0.target -> poweroff.target
lrwxrwxrwx 1 root root 13 Feb 27 16:14 /usr/lib/systemd/system/runlevel1.target -> rescue.target
lrwxrwxrwx 1 root root 17 Feb 27 16:14 /usr/lib/systemd/system/runlevel2.target -> multi-user.target
lrwxrwxrwx 1 root root 17 Feb 27 16:14 /usr/lib/systemd/system/runlevel3.target -> multi-user.target
lrwxrwxrwx 1 root root 17 Feb 27 16:14 /usr/lib/systemd/system/runlevel4.target -> multi-user.target
lrwxrwxrwx 1 root root 16 Feb 27 16:14 /usr/lib/systemd/system/runlevel5.target -> graphical.target
lrwxrwxrwx 1 root root 13 Feb 27 16:14 /usr/lib/systemd/system/runlevel6.target -> reboot.target
運行級別的配置
centos6設置默認的啟動級別
# 查看運行級別: centos7 也可以使用該命令。[root@k8s-m1 ~]# runlevel N 3
# 修改運行級別:臨時修改: init level(對應的級別即可)永久修改:需要修改 /etc/inittab 文件。
在最下面的一行中的語句 id:5:initdefault 中的數字5改成需要的啟動程度就可以了,一般是命令行模式 3
centos7設置默認的啟動級別
#centos7修改設置默認的系統級別
[root@k8s-m1 ~]# systemctl set-default multi-user.target
Removed symlink /etc/systemd/system/default.target.
Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/multi-user.target.
#centos7查看默認的系統級別
[root@k8s-m1 ~]# systemctl get-default multi-user.target
參考:https://blog.csdn.net/m0_45406092/article/details/130660743
更多關于Linux的知識請前往博客主頁查看。