kernel 啟動過程之三, start_kernel()函數 概敘!init/main.c

來源:互聯網
上載者:User

 

核心資料結構初始化--核心引導第一部分

 

start_kernel()中調用了一系列初始化函數,以完成kernel本身的設定。
這些動作有的是公用的,有的則是需要配置的才會執行的。

在start_kernel()函數中,

 

  • 輸出Linux版本資訊(printk(linux_banner))
  • 設定與體繫結構相關的環境(setup_arch())
  • 頁表結構初始化(paging_init())
  • 使用"arch/alpha/kernel/entry.S"中的進入點設定系統自陷入口(trap_init())
  • 使用alpha_mv結構和entry.S入口初始化系統IRQ(init_IRQ())
  • 核心進程調度器初始化(包括初始化幾個預設的Bottom-half,sched_init())
  • 時間、定時器初始化(包括讀取CMOS時鐘、估測主頻、初始化定時器中斷等,time_init())
  • 提取並分析核心啟動參數(從環境變數中讀取參數,設定相應標誌位等待處理,(parse_options())
  • 控制台初始化(為輸出資訊而先於PCI初始化,console_init())
  • 剖析器資料結構初始化(prof_buffer和prof_len變數)
  • 核心Cache初始化(描述Cache資訊的Cache,kmem_cache_init())
  • 延遲校準(獲得時鐘jiffies與CPU主頻ticks的延遲,calibrate_delay())
  • 記憶體初始化(設定記憶體上下界和頁表項初始值,mem_init())
  • 建立和設定內部及通用cache("slab_cache",kmem_cache_sizes_init())
  • 建立uid taskcount SLAB
    cache("uid_cache",uidcache_init())
  • 建立檔案cache("files_cache",filescache_init())
  • 建立目錄cache("dentry_cache",dcache_init())
  • 建立與虛存相關的cache("vm_area_struct","mm_struct",vma_init())
  • 塊裝置讀寫緩衝區初始化(同時建立"buffer_head"cache使用者加速訪問,buffer_init())
  • 建立頁cache(記憶體頁hash表初始化,page_cache_init())
  • 建立訊號隊列cache("signal_queue",signals_init())
  • 初始化記憶體inode表(inode_init())
  • 建立記憶體檔案描述符表("filp_cache",file_table_init())
  • 檢查體繫結構漏洞(對於alpha,此函數為空白,check_bugs())
  • SMP機器其餘CPU(除當前引導CPU)初始化(對於沒有配置SMP的核心,此函數為空白,smp_init())
  • 啟動init過程(建立第一個核心線程,調用init()函數,原執行序列調用cpu_idle()
    等待調度,init())

至此start_kernel()結束,基本的核心環境已經建立起來了。

對於I386平台

i386平台上的核心啟動過程與此基本相同,所不同的主要是實現方式。

對於2.4.x版核心

2.4.x中變化比較大,但基本過程沒變,變動的是各個資料結構的具體實現,比如Cache。

 



回頁首

 

外設初始化--核心引導第二部分

init()函數作為核心線程,首先鎖定核心(僅對SMP機器有效),然後調用
do_basic_setup()完成外設及其驅動程式的載入和初始化。過程如下:

 

  • 匯流排初始化(比如pci_init())
  • 網路初始化(初始化網路資料結構,包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,將調用protocols結構中包含的所有協議的初始化過程,sock_init())
  • 建立bdflush核心線程(bdflush()過程常駐核心空間,由核心喚醒來清理被寫過的記憶體緩衝區,當bdflush()由kernel_thread()啟動後,它將自己命名為kflushd)
  • 建立kupdate核心線程(kupdate()過程常駐核心空間,由核心按時調度執行,將記憶體緩衝區中的資訊更新到磁碟中,更新的內容包括超級塊和inode表)
  • 設定並啟動核心調頁線程kswapd(為了防止kswapd啟動時將版本資訊輸出到其他資訊中間,核心線調用kswapd_setup()設定kswapd運行所要求的環境,然後再建立
    kswapd核心線程)
  • 建立事件管理核心線程(start_context_thread()函數啟動context_thread()過程,並重新命名為keventd)
  • 裝置初始化(包括並口parport_init()、字元裝置chr_dev_init()、塊裝置
    blk_dev_init()、SCSI裝置scsi_dev_init()、網路裝置net_dev_init()、磁碟初始化及分區檢查等等,device_setup())
  • 執行檔案格式設定(binfmt_setup())
  • 啟動任何使用__initcall標識的函數(方便核心開發人員添加啟動函數,do_initcalls())
  • 檔案系統初始化(filesystem_setup())
  • 安裝root檔案系統(mount_root())

至此do_basic_setup()函數返回init(),在釋放啟動記憶體段(free_initmem())並給核心解鎖以後,init()開啟
/dev/console裝置,重新導向stdin、stdout和stderr到控制台,最後,搜尋檔案系統中的init程式(或者由init=命令列參
數指定的程式),並使用 execve()系統調用載入執行init程式。

init()函數到此結束,核心的引導部分也到此結束了,這個由start_kernel()建立的第一個線程已經成為一個使用者模式下的進程了。此時系統中存在著六個運行實體:

  • start_kernel()本身所在的執行體,這其實是一個"手工"建立的線程,它在建立了init()線程以後就進入cpu_idle()迴圈了,它不會在進程(線程)列表中出現
  • init線程,由start_kernel()建立,當前處於使用者態,載入了init程式
  • kflushd核心線程,由init線程建立,在核心態運行bdflush()函數
  • kupdate核心線程,由init線程建立,在核心態運行kupdate()函數
  • kswapd核心線程,由init線程建立,在核心態運行kswapd()函數
  • keventd核心線程,由init線程建立,在核心態運行context_thread()函數

對於I386平台

基本相同。

對於2.4.x版核心

這一部分的啟動過程在2.4.x核心中簡化了不少,預設的獨立初始化過程只剩下網路
(sock_init())和建立事件管理核心線程,而其他所需要的初始化都使用__initcall()宏
包含在do_initcalls()函數中啟動執行。

 



回頁首

 

init進程和inittab引導指令

init進程是系統所有進程的起點,核心在完成核內引導以後,即在本線程(進程)空
間內載入init程式,它的進程號是1。

init程式需要讀取/etc/inittab檔案作為其行為指標,inittab是以行為單位的描述性(非執行性)文本,每一個指令行都具有以下格式:

id:runlevel:action:process其中id為入口標識符,runlevel為運行層級,action為動作代號,process為具體的執行程式。

id一般要求4個字元以內,對於getty或其他login程式項,要求id與tty的編號相同,否則getty程式將不能正常工作。

runlevel是init所處於的運行層級的標識,一般使用0-6以及S或s。0、1、6運行層級被系統保留,0作為shutdown動作,1作為重啟
至單一使用者模式,6為重啟;S和s意義相同,表示單一使用者模式,且無需inittab檔案,因此也不在inittab中出現,實際上,進入單一使用者模式時,
init直接在控制台(/dev/console)上運行/sbin/sulogin。

在一般的系統實現中,都使用了2、3、4、5幾個層級,在Redhat系統中,2表示無NFS支援的多使用者模式,3表示完全多使用者模式(也是最常用的級
別),4保留給使用者自訂,5表示XDM圖形登入方式。7-9層級也是可以使用的,傳統的Unix系統沒有定義這幾個層級。runlevel可以是並列的
多個值,以匹配多個運行層級,對大多數action來說,僅當runlevel與當前運行層級匹配成功才會執行。

initdefault是一個特殊的action值,用於標識預設的啟動層級;當init由核心啟用
以後,它將讀取inittab中的initdefault項,取得其中的runlevel,並作為當前的運行級
別。如果沒有inittab檔案,或者其中沒有initdefault項,init將在控制台上請求輸入
runlevel。

sysinit、boot、bootwait等action將在系統啟動時無條件運行,而忽略其中的runlevel,其餘的action(不含initdefault)都與某個runlevel相關。各個action的定義在inittab的man手冊中有詳細的描述。

在Redhat系統中,一般情況下inittab都會有如下幾項:

id:3:initdefault:
#表示當前預設運行層級為3--完全多任務模式;
si::sysinit:/etc/rc.d/rc.sysinit
#啟動時自動執行/etc/rc.d/rc.sysinit指令碼
l3:3:wait:/etc/rc.d/rc 3
#當運行層級為3時,以3為參數運行/etc/rc.d/rc指令碼,init將等待其返回
0:12345:respawn:/sbin/mingetty tty0
#在1-5各個層級上以tty0為參數執行/sbin/mingetty程式,開啟tty0終端用於
#使用者登入,如果進程退出則再次運行mingetty程式
x:5:respawn:/usr/bin/X11/xdm -nodaemon
#在5層級上運行xdm程式,提供xdm圖形方式登入介面,並在退出時重新執行

 



回頁首

 

rc啟動指令碼

上一節已經提到init進程將啟動運行rc指令碼,這一節將介紹rc指令碼具體的工作。

一般情況下,rc啟動指令碼都位於/etc/rc.d目錄下,rc.sysinit中最常見的動作就是啟用交換分區,檢查磁碟,載入硬體模組,這些動作無論
哪個運行層級都是需要優先執行的。僅當rc.sysinit執行完以後init才會執行其他的boot或bootwait動作。

如果沒有其他boot、bootwait動作,在運行層級3下,/etc/rc.d/rc將會得到執行,命令列參數為3,即執行
/etc/rc.d/rc3.d/目錄下的所有檔案。rc3.d下的檔案都是指向/etc/rc.d/init.d/目錄下各個Shell指令碼的符號連
接,而這些指令碼一般能接受start、stop、restart、status等參數。rc指令碼以start參數啟動所有以S開頭的指令碼,在此之前,如果
相應的指令碼也存在K打頭的連結,而且已經處於運行態了(以/var/lock/subsys/下的檔案作為標誌),則將首先啟動K開頭的指令碼,以stop
作為參數停止這些已經啟動了的服務,然後再重新運行。顯然,這樣做的直接目的就是當init改變運行層級時,所有相關的服務都將重啟,即使是同一個層級。

rc程式執行完畢後,系統內容已經設定好了,下面就該使用者登入系統了。

 



回頁首

 

getty和login

在rc返回後,init將得到控制,並啟動mingetty(見第五節)。mingetty是getty的簡化,不能處理串口操作。getty的功能一般包括:

  • 開啟終端線,並設定模式
  • 輸出登入介面及提示,接受使用者名稱的輸入
  • 以該使用者名稱作為login的參數,載入login程式

註:用於遠程登入的提示資訊位於/etc/issue.net中。

login程式在getty的同一個進程空間中運行,接受getty傳來的使用者名稱參數作為登入
的使用者名稱。

如果使用者名稱不是root,且存在/etc/nologin檔案,login將輸出nologin檔案的內容,
然後退出。這通常用來系統維護時防止非root使用者登入。

只有/etc/securetty中登記了的終端才允許root使用者登入,如果不存在這個檔案,
則root可以在任何終端上登入。/etc/usertty檔案用於對使用者作出附加訪問限制,如果
不存在這個檔案,則沒有其他限制。

當使用者登入通過了這些檢查後,login將搜尋/etc/passwd檔案(必要時搜尋
/etc/shadow檔案)用於匹配密碼、設定主目錄和載入shell。如果沒有指定主目錄,將
預設為根目錄;如果沒有指定shell,將預設為/bin/sh。在將控制轉交給shell以前,
getty將輸出/var/log/lastlog中記錄的上次登入系統的資訊,然後檢查使用者是否有新
郵件(/usr/spool/mail/{username})。在設定好shell的uid、gid,以及TERM,PATH
等環境變數以後,進程載入shell,login的任務也就完成了。

 



回頁首

 

bash

運行層級3下的使用者login以後,將啟動一個使用者指定的shell,以下以/bin/bash為例繼續我們的啟動過程。

bash是Bourne
Shell的GNU擴充,除了繼承了sh的所有特點以外,還增加了很多特
性和功能。由login啟動的bash是作為一個登入shell啟動的,它繼承了getty設定的TERM、PATH等環境變數,其中PATH對於普通使用者為"/bin:/usr/bin:/usr/local/bin",對於root
為"/sbin:/bin:/usr/sbin:/usr/bin"。作為登入shell,它將首先尋找/etc/profile
指令檔,並執行它;然後如果存在~/.bash_profile,則執行它,否則執行
~/.bash_login,如果該檔案也不存在,則執行~/.profile檔案。然後bash將作為一個
互動式shell執行~/.bashrc檔案(如果存在的話),很多系統中,~/.bashrc都將啟動
/etc/bashrc作為系統範圍內的設定檔。

當顯示出命令列提示符的時候,整個啟動過程就結束了。此時的系統,運行著核心,
運行著幾個核心線程,運行著init進程,運行著一批由rc啟動指令碼啟用的守護進程(如
inetd等),運行著一個bash作為使用者的命令直譯器。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.