• 沒有找到結果。

附錄 B 啟動程序詳細過程 啟動程序詳細過程 啟動程序詳細過程 啟動程序詳細過程

一 一

一、、、、 Bootloader

 執行步驟:

1. 讀取 MBR 的 Kernel Loader (亦即是 lilo、grub、spfdisk 等等)開機資 訊

2. 載入 Kernel 的作業系統核心資訊

 詳細內容:

由於個人電腦的系統在讀完 BIOS 之後,會先去讀取第一個開機硬碟的第 一個磁區,就是 master boot record(MBR),MBR 主要就是在記錄開機的資 訊,也就是儲存 lilo 及 grub 的地方。讓硬體認識核心是 kernel loader 的主 要功能。

「arch/i386/boot」下就是製作 Linux Bootloader 的文件。「head.S」文件提供 了對 OSF PAL/1 的調用入口,它將被編譯後置於引導扇區(Sector)(註:ARC 的分區首扇區或 SRM 的磁盤 0 扇區),得到控制後初始化一些數據結構,再 將控制轉給「main.c」中的 start_kernel( ),start_kernel( )向控制台輸出一些 提示,調用 pal_init( )初始化 PAL 代碼,調用 openboot( ) 打開引導設備(通 過讀取 Firmware 環境),調用 load( )將核心程式碼加載到

START_ADDR(「include/asm-i386/system.h」),再將 Firmware 中的核心引 導參數加載到 ZERO_PAGE(0)中,最後調用 runkernel( )將控制轉給 0x100000 的 kernel,bootloader 部分結束。

i386 系統中一般都有 BIOS 做最初的引導工作,那就是將四個主分區表中的 第一個可引導分區的第一個扇區加載到實模式地址 0x7c00 上,然後將控制 轉交給它。在「arch/i386/boot」目錄下,bootsect.S 是生成引導扇區的彙編 源碼,它首先將自己拷貝到 0x90000 上,然後將緊接其後的 setup 部分(第二

附錄 附錄 附錄

附錄 B 啟動程序詳細過程 啟動程序詳細過程 啟動程序詳細過程 啟動程序詳細過程

扇區)拷貝到 0x90200,將真正的內核代碼拷貝到 0x100000。

以上這些拷貝動作都是以 bootsect.S、setup.S 以及 vmlinux 在磁盤上連續存 放為前提的,也就是說,我們的 bzImage 文件或者 zImage 文件是按照 bootsect、setup、vmlinux 這樣的順序組織,並存放於始於引導分區的首扇區 的連續磁盤扇區之中。

bootsect.S 完成加載動作後,就直接跳轉到 0x90200,這裡正是 setup.S 的程 序入口,setup.S 的主要功能就是將系統參數(包括記憶體、磁盤等,由 BIOS 返回)拷貝到 0x90000-0x901FF 記憶體中,這個地方正是 bootsect.S 存放的 地方,這時它將被系統參數覆蓋。以後這些參數將由保護模式下的代碼來讀 取。除此之外,setup.S 還將 video.S 中的代碼包含進來,檢測和設置顯示器 和顯示模式。最後,setup.S 將系統轉換到保護模式,並跳轉到 0x100000(對 於 bzImage 格式的大內核是 0x100000,對於 zImage 格式的是 0x1000)的內 核引導代碼,Bootloader 過程結束。

二二

二二、、、、 Kernel:::

 執行步驟:kernel 執行 init 程式並取得 run-level 資訊。

 詳細說明:在 Linux 的系統下,通常開機的核心都擺在/boot 底下,因 此,這個時候的 boot loader 就會到/boot 去尋找相關的核心。核心載入之後,

由核心執行的第一個程式/sbin/init,而第一個目標當然就是確定主機是要以 怎樣的情況登入,這時就必須要以/sbin/init 來載入/etc/inittab 的資訊。

1. Kernel

引導入口引導入口引導入口引導入口

在 i386 系統中,當內核以 bzImage 的形式壓縮,即大內核方式 (__BIG_KERNEL__)壓縮時就需要預先處理 bootsect.S 和 setup.S,

按照大核模式使用$(CPP)處理生成 bootsect.S 和 setup.S,然後再編

譯生成相應的.o 文件,並使用 "arch/i386/boot/compressed/build.c"

生成的 build 工具,將實際的 Kernel(未壓縮的,含 kernel 中的 head.S 代碼)與"arch/i386/boot/compressed"下的 head.S 和 misc.c 合成到一 起,其中的 head.S 代替了"arch/i386/kernel/head.S"的位置,由 Bootloader 引導執行(startup_32 入口),然後它呼叫 misc.c 中定義的 decompress_kernel( )函數,使用 "lib/inflate.c"中定義的 gunzip( )將 內核解壓到 0x100000,再轉到其上執行"arch/i386/kernel/head.S"中 的 startup_32 程式碼。

2.

核心數據結構初始化核心數據結構初始化核心數據結構初始化核心數據結構初始化

--Kernel

引導第一部分引導第一部分引導第一部分引導第一部分

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

在 start_kernel( )函數中:

 輸出 Linux 版本信息[ printk(linux_banner) ]

 設置與體系結構相關的環境[ setup_arch( ) ]

 頁表結構初始化[ paging_init( ) ]

 使用"arch/i386/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( )]

 建立外設初始化--內核引導第二部分 init( )函數作為核心 thread,首先鎖定內核(僅對 SMP 機器有效),然後調用

do_basic_setup( )完成外設及其驅動程序的加載和初始化。過程如 下:

(1) 總線初始化[ 比如 pci_init( ) ]

(2) 網絡初始化[初始化網絡數據結構,包括 sk_init( )、

skb_init( )和 proto_init( )三部分,在 proto_init( )中,將調用 protocols 結構中包含的所有協議的初始化過程,sock_init( )]

(3) 建立 bdflush 核心 thread[ bdflush( )過程常駐核心空間,由 核心喚醒來清理被寫過的記憶體緩衝區,當 bdflush( )由 kernel_thread( )啟動後,它將自己命名為 kflushd]

(4) 建立 kupdate 核心 thread[ kupdate( )過程常駐核心空間,由 核心按時調度執行,將記憶體緩衝區中的信息更新到磁盤 中,更新的內容包括超級塊和 inode 表]

(5) 設置並啟動核心調頁 threadkswapd[ 為了防止 kswapd 啟 動時將版本信息輸出到其他信息中間,核心線調用

kswapd_setup( )設置 kswapd 運行所要求的環境,然後再建立 kswapd 核心 thread ]

(6) 建立事件管理核心 thread[ start_context_thread( )函數啟動

context_thread( )過程,並重命名為 keventd]

(7) 設備初始化[ 包括並口 parport_init( )、字元設備 chr_dev_init( )、塊設備 blk_dev_init( )、SCSI 設備

scsi_dev_init( )、網絡設備 net_dev_init( )、磁盤初始化及分區 檢查等等,device_setup( ) ]

(8) 執行文件格式設置[ binfmt_setup( ) ]

(9) 啟動任何使用__initcall 標識的函數[ 方便核心開發者添 加啟動函數,do_initcalls( ) ]

(10) 文件系統初始化[ filesystem_setup( ) ] (11) 安裝 root 文件系統[ mount_root( ) ]

至此 do_basic_setup( )函數返回 init( ),在釋放啟動記憶體段

[ free_initmem( ) ]並給內核解鎖以後,init( )打開/dev/console 設備,

重定向 stdin、stdout 和 stderr 到控制台,最後,搜索文件系統中的 init 程序[ 或者由 init=命令行參數指定的程序 ],並使用 execve( ) 系統調用加載執行 init 程序。

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

(1) start_kernel( )本身所在的執行體,這其實是一個"手工

"建立的 thread,它在建立了 init( ) thread 以後就進入 cpu_idle( )循環了,它不會在進程(thread)列表中出現 (2) initthread,由 start_kernel( )建立,當前處於用戶態,

加載了 init 程序

(3) kflushd 核心 thread,由 initthread 建立,在核心態運 行 bdflush( )函數

(4) kupdate 核心 thread,由 initthread 建立,在核心態運 行 kupdate( )函數

(5) kswapd 核心 thread,由 initthread 建立,在核心態運 行 kswapd( )函數

(6) keventd 核心 thread,由 initthread 建立,在核心態運 行 context_thread( )函數和設置內部及通用

cache[ "slab_cache",kmem_cache_sizes_init( ) ] (12) 建立 uid taskcount SLAB cache[ "uid_cache",

uidcache_init( )]

(13) 建立文件 cache[ "files_cache",filescache_init( )]

(14) 建立目錄 cache[ "dentry_cache",dcache_init( ) ]

(15) 建立與虛存相關的 cache[ "vm_area_struct","mm_struct",

vma_init( ) ]

(16) 塊設備讀寫緩衝區初始化[ 同時建立"buffer_head"cache 用戶加速訪問,buffer_init( ) ]

(17) 建立頁 cache[記憶體頁 hash 表初始化,page_cache_init( )]

(18) 建立信號隊列 cache[ "signal_queue",signals_init( ) ] (19) 初始化記憶體 inode 表[ node_init( ) ]

(20) 建立記憶體文件描述符表[ "filp_cache",file_table_init( ) ] (21) 檢查體系結構漏洞[對於 alpha,此函數為空,check_bugs( )]

(22) SMP 機器其餘 CPU(除當前引導 CPU)初始化[ 對於沒有 配置 SMP 的內核,此函數為空,smp_init( ) ]

(23) 啟動 init 過程[ 建立第一個核心 thread,調用 init( )函數,

原執行序列調用 cpu_idle( )等待調度,init( ) ]

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

3.

外設初始化外設初始化外設初始化外設初始化

--Kernel

引導第二部分引導第二部分引導第二部分引導第二部分

init( )函數作為核心 thread,首先鎖定內核(僅對 SMP 機器有效),

然後調用 do_basic_setup( )完成外設及其驅動程序的加載和初始 化。過程如下:

(1) 總線初始化[ 比如 pci_init( ) ]

(2) 網絡初始化[ 初始化網絡數據結構,包括 sk_init( )、

skb_init( )和 proto_init( )三部分,在 proto_init( )中,將調用 protocols 結構中包含的所有協議的初始化過程,sock_init( ) ] (3) 建立 bdflush 核心 thread[ bdflush( )過程常駐核心空間,由 核心喚醒來清理被寫過的記憶體緩衝區,當 bdflush( )由 kernel_thread( )啟動後,它將自己命名為 kflushd ]

(4) 建立 kupdate 核心 thread[ kupdate( )過程常駐核心空間,由 核心按時調度執行,將記憶體緩衝區中的信息更新到磁盤中 ,更新的內容包括超級塊和 inode 表 ]

(5) 設置並啟動核心調頁 threadkswapd[ 為了防止 kswapd 啟 動時將版本信息輸出到其他信息中間,核心線調用

kswapd_setup( )設置 kswapd 運行所要求的環境,然後再建立 kswapd 核心 thread ]

(6) 建立事件管理核心 thread[ start_context_thread( )函數啟動 context_thread( )過程,並重命名為 keventd ]

(7) 設備初始化[ 包括並口 parport_init( )、字元設備 chr_dev_init( )、塊設備 blk_dev_init( )、SCSI 設備

scsi_dev_init( )、網絡設備 net_dev_init( )、磁盤初始化及分區 檢查等等,device_setup( ) ]

(8) 執行文件格式設置[ binfmt_setup( ) ]

(9) 啟動任何使用__initcall 標識的函數[ 方便核心開發者添

加啟動函數,do_initcalls( ) ]

(10) 文件系統初始化[ filesystem_setup( ) ] (11) 安裝 root 文件系統[ mount_root( ) ]

至此 do_basic_setup( )函數返回 init( ),在釋放啟動記憶體段

[ free_initmem( ) ]並給內核解鎖以後,init( )打開/dev/console 設備,

重定向 stdin、stdout 和 stderr 到控制台,最後搜索文件系統中的 init 程序(或者由 init=命令行參數指定的程序),並使用 execve( )系統調 用加載執行 init 程序。

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

1. start_kernel( )本身所在的執行體,這其實是一個"手工"建 立的 thread,它在建立了 init( )thread 以後就進入 cpu_idle( ) 循環了,它不會在進程[thread]列表中出現

2. initthread,由 start_kernel( )建立,當前處於用戶態,加載 了 init 程序

3. kflushd 核心 thread,由 initthread 建立,在核心態運行 bdflush( )函數

4. kupdate 核心 thread,由 initthread 建立,在核心態運行 kupdate( )函數

5. kswapd 核心 thread,由 initthread 建立,在核心態運行 kswapd( )函數

6. keventd 核心 thread,由 initthread 建立,在核心態運行 context_thread( )函數

三、 Init:

 執行步驟:

1. init 執行 /etc/rc.d/rc.sysinit 檔案

2. 啟動核心的外掛式模組 (/etc/modules.conf) 3. init 執行 run-level 的各個批次檔( Scripts ) 4. init 執行 /etc/rc.d/rc.local 檔案

5. 執行 /bin/login 程式

6. 登入之後開始以 Shell 控管主機

 詳細說明:

1. init 的第一個執行內容 /etc/rc.d/rc.sysinit,內容包括:設定預設路徑

( PATH )、設定主機名稱、執行 /etc/sysconfig/network 所記錄的 網路資訊、掛載 /proc 這個保存在記憶體當中的主機基本訊息、以 及其他幾個 Linux 作業系統最基本的幾個資訊。

2. 啟動核心的外掛式模組 (/etc/modules.conf)可以選擇使用模組的型 態來進行驅動程式的載入,如果系統原本找不到的模組就可以在 /etc/modules.conf 寫入。

3. init 執行 run-level 的各個 scripts,由於不同的 run-level 所需要載 入的模組並不相同,所以系統為不同的 run-level 設定了一些批次檔 ( scripts ),而 run-level 早就在前面的時候以 /etc/inittab 當中取得。

4. init 執行 /etc/rc.d/rc.local,cdrom 必須要載入兩個模組之後才能使 用,分別是 modprobe cdrom, modprobe ide-cd 加入到

/etc/rc.d/rc.local 中。

#

# inittab This file describes how the INIT process should set up

# the system in a certain run-level.

#

# Author Miquel van Smoorenburg,

# Modified for RHS Linux by Marc Ewing and Donnie Barnes

#

# Default runlevel. The runlevels used by RHS 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 havenetworking)

# 3 - Full multiuser mode

# 4 - unused

# 5 - X11

# 6 - reboot (Do NOT set initdefault to this)

#

##表示當前預設執行層級為 5(initdefault);

id:5:initdefault:

##啟動時自動執行/etc/rc.d/rc.sysinit 腳本(sysinit)

# System initialization.

si::sysinit:/etc/rc.d/rc.sysinit