歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> 關於Unix >> 探索Linux啟動過程

探索Linux啟動過程

日期:2017/3/6 15:26:14   编辑:關於Unix
參閱CU上的很多關於rc的帖子,於是非常想弄清楚Linux/ Unix 到底是如何啟動的?rc腳本有是如何起作用的? 幸虧goole什麼都知道。 羅列一篇,方便類我等菜鳥來溫習和查閱,以Solaris為例 按下電源,首先是BIOS取得系統控制權,BIOS進行最初的引導工作,然後交控

參閱CU上的很多關於rc的帖子,於是非常想弄清楚Linux/Unix到底是如何啟動的?rc腳本有是如何起作用的?

幸虧goole什麼都知道。

羅列一篇,方便類我等菜鳥來溫習和查閱,以Solaris為例

按下電源,首先是BIOS取得系統控制權,BIOS進行最初的引導工作,然後交控制權交給引導分區,由引導分區加載內核並調用start_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()結束,基本的核心環境已經建立起來了。

start_kernel最後一項是啟動了init函數,接著由它來完成外設的初始化

  • 總線初始化(比如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())

這些步驟結束後,init()搜索文件系統中的init程序,並創建它,也就是我們通常所說的init進程,它是系統所有進程的起點,進程ID=1。

在啟動了的Solaris下,利用 "$ps -p 1" 可以查看該進程,輸出如下:

PID TTY TIME CMD
1 ? 0:01 init

接下來init進程讀取/etc/inittab文件,來決定下一步如何做。

inittab是以行為單位的描述性(非執行性)文本,每一個指令行都具有以下格式:

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

id一般要求4個字符以內,runlevelinit所處於的運行級別的標識,一般使用0-6以及S或s(S或s表示單用戶模式)。

action字段則告訴init進程,如何對待process字段指定的進程:當inittab中各行的runlevel值與當前運行級別匹配時,指定的action才被執行。

但有幾個特殊的action:

initdefault是一個特殊的action值,用於標識缺省的啟動級別;當init由核心激活以後,它將首先讀取inittab中的initdefault項,取得其中的runlevel,並作為當前的運行級別。

sysinit、boot、bootwait等action將在系統啟動時無條件運行,而忽略其中的runlevel,即不管當前運行級別是什麼,它都執行,並且是優先執行。其余的action(不含initdefault)都與某個runlevel相關。

我的Solaris9中的/etc/inittab如下

ap::sysinit:/sbin/autopush -f /etc/iu.ap #action=sysinit, 該行不管在什麼運行級別下,都運行
ap::sysinit:/sbin/soconfig -f /etc/sock2path #同上
fs::sysinit:/sbin/rcS sysinit >/dev/msglog 2<>/dev/msglog
is:3:initdefault: #該行action=initdefault,表明系統的默認運行級別是3
p3:s1234:powerfail:/usr/sbin/shutdown -y -i5 -g0 >/dev/msglog 2<>/dev/msglog
sS:s:wait:/sbin/rcS >/dev/msglog 2<>/dev/msglog
s0:0:wait:/sbin/rc0 >/dev/msglog 2<>/dev/msglog
s1:1:respawn:/sbin/rc1 >/dev/msglog 2<>/dev/msglog
s2:23:wait:/sbin/rc2 >/dev/msglog 2<>/dev/msglog
s3:3:wait:/sbin/rc3 >/dev/msglog 2<>/dev/msglog
s5:5:wait:/sbin/rc5 >/dev/msglog 2<>/dev/msglog
s6:6:wait:/sbin/rc6 >/dev/msglog 2<>/dev/msglog
fw:0:wait:/sbin/uadmin 2 0 >/dev/msglog 2<>/dev/msglog
of:5:wait:/sbin/uadmin 2 6 >/dev/msglog 2<>/dev/msglog
rb:6:wait:/sbin/uadmin 2 1 >/dev/msglog 2<>/dev/msglog
sc:234:respawn:/usr/lib/saf/sac -t 300 #在2,3,4運行級別下都執行
co:234:respawn:/usr/lib/saf/ttymon -g -h -p "`uname -n` console login: " -T sun -d /dev/console -l console -m ldterm,ttcompat

去man inittab吧,什麼都講了 :)

接著看我的inittab文件,當action=sysinit的行執行完之後(前三行),將執行runlevel=3的行,即“ s3:3:wait:/sbin/rc3 ”。查找了一下,/sbin/rc3是一個shell腳本,用於初始化在運行級別3的系統。因此/etc/inittab中已經定義好了在運行級別X下,就運行 /sbin/rcX,那/sbin/rcX到底是什麼?

cat一下/sbin/rc3,看看,重要的幾行代碼如下:

[ $_INIT_PREV_LEVEL = 2 -o $_INIT_PREV_LEVEL = 4 ] && \
echo 'Changing to state 3.' #如果以前運行級別是2或4,則打印信息“切換到狀態三”

#如果運行級別!=4而且存在/etc/rc3.d這個目錄,則停掉所有以K開始的腳本中的服務或程序,啟動所有以S開始的腳本中的服務或程序

if [ $_INIT_PREV_LEVEL != 4 -a -d /etc/rc3.d ]; then
for f in /etc/rc3.d/K*; do
if [ -s $f ]; then
case $f in
*.sh) . $f ;;
*) /sbin/sh $f stop ;;
esac
fi
done

for f in /etc/rc3.d/S*; do
if [ -s $f ]; then
case $f in
*.sh) . $f ;;
*) /sbin/sh $f start ;;
esac
fi
done
fi


那就再追蹤到/etc/rc3.d下面去,好累啊 :(

#cd /etc/rc3.d

K42amserver S13kdc.master S15nfs.server S34dhcp S50apache S52imq S77dmi S81volmgt S89sshd
README S14kdc S16boot.server S42amserver S50san_driverchk S76snmpdx S80mipagent S84appserv S90samba

哦,都是些程序或進程的啟動腳本,S開頭是啟動腳本 K開頭是停止腳本。這正是/sbin/rc3這個shell腳本中設定的執行方式。

例如:S90samba 代表一個啟動samba服務的腳本,90表示啟動順序編號。 K42amserver代表結束服務的腳本。

rc程序執行完畢後,系統環境已經設置好了,下面就該用戶登錄系統了,終於結束了。

最後看張圖,畫的很清楚


參考:

http://chinaunix.net/jh/4/31269.html

http://www-128.ibm.com/developerworks/cn/linux/kernel/startup/index.html#2

http://www.yesky.com/SoftChannel/72350098490654720/20040208/1766282.shtml


Copyright © Linux教程網 All Rights Reserved