uClinux的執行過程
uCinux的啟動主要經曆三個階段。首先,必須完成CPU和儲存空間的硬體初始化,在系統RAM中建立程式堆棧和資料區段,建立程式的運行時的環境。初始化完成之後,uClinux核心就取得了CPU的控制權,開始作業系統自身的初始化,這包括建立RAM中斷向量表、載入裝置驅動程式、記憶體管理模組等等。這一切完成後,uClinux啟動一個最初的init線程,進入到第三階段,這時核心已經正常運行,外圍模組也都就緒,開始執行一些指令檔(如/etc/rc指令檔)。
一.kernel程式碼片段之前的系統初始化
1. uClinux-dist/linux-2.4.x/arch/armnommu/boot/compressed/head.S
開發板從上電開始,最開始執行的程式放在uClinux-dist/linux-2.4.x/arch/armnommu/boot/compressed/head.S中.
(1) 切換模式,關閉中斷. (line 96 )
(2) 首先程式要先給SYSCFG,EXTDBWTH,ROMCON0等一系列系統控制寄存器賦值,此時flash地址在 0X0,DRAM地址在0X1000000.(line 141 )
(3) 點亮I/O口的指示燈. (line 152 )
(4) 把在flash上的image複製到DRAM上.(line 161 )
(5) 執行remap,把flash地址映射為0X1000000,DRAM地址映射為0.(line 172 )
(6) 開啟cache和write buffer.(line 196 )
(7) 設定好64K堆棧.(line 204 )
(8) 跳轉到decompress_kernel函數(line 217 ),此處的跳轉為帶返回的跳轉,以便於執行完此函數跳回來.
2. uClinux-dist/linux-2.4.x/arch/armnommu/boot/compressed/misc.c
此時的函數decompress_kernel是用C語言寫的,line 297 .
(1) makecrc();進行crc校正.
(2) puts("Uncompressing Linux..."); 輸出linux起動後的第一句話.
(3) gunzip();解壓縮kernel.
(4) puts(" done, booting the kernel./n");
3. uClinux-dist/linux-2.4.x/arch/armnommu/boot/compressed/head.S
執行完decompress_kernel函數後,kernel又跳回head.S中,因為此時我們還要檢驗解壓縮之後的kernel起始地址是否緊接著kernel image,如果是,beq call_kernel(line 220),執行解壓後的kernel.
如果解壓縮之後的kernel起始地址不是緊接著kernel image,執行relocate(line 236),將其拷貝到緊接著kernel image的地方,然後跳轉,執行解壓後的kernel.
二.kernel執行
1.uClinux-dist/linux-2.4.x/init/main.c中的start_kernel() (line 352)
系統啟動過程到此,轉入體繫結構無關的通用C代碼中,start_kernel() 中調用了一系列初始化函數,以完成kernel本身的設定。這些動作有的是公用的,有的則是需要配置的才會執行的。
(1) 輸出Linux版本資訊(printk(linux_banner))
(2) 設定與體繫結構相關的環境(setup_arch())
(3) parse_options(command_line);解析command_line,將其轉化為環境變數.
(4) 初始化系統IRQ(init_IRQ())
(5) 核心進程調度器初始化(sched_init())
(6) 軟中段初始化softirq_init();
(7) 時間、定時器初始化(包括估測主頻、初始化定時器中斷等,time_init())
(8) 控制台初始化console_init();
(9) 核心CACHE初始化kmem_cache_init();
(10)延遲校準calibrate_delay();
(11)記憶體初始化(設定記憶體上下界和頁表項初始值,mem_init())
(12)檔案,目錄,塊裝置讀寫緩衝區初始化
(13)檢查體繫結構漏洞(check_bugs())
(14)啟動init過程(建立第一個核心線程,調用init()函數,原執行序列調用cpu_idle() 等待調度,init())
至此start_kernel()結束,基本的核心環境已經建立起來了。
2.uClinux-dist/linux-2.4.x/init/main.c中的init() (line 548)
現在我們進入核心引導第二部分,init()函數作為核心線程,首先鎖定核心(僅對SMP機器有效,我們為空白函數),然後調用 do_basic_setup() (line 551)完成外設及其驅動程式的載入初始化。
過程如下:
* 網路初始化(初始化網路資料結構,包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,將調用protocols結構中包含的所有協議的初始化過程,sock_init())
* 建立事件管理核心線程(start_context_thread()函數,這是系統建立的第二個核心線程,名叫“keventd”。其代碼context_thread()也在kernel/context.c中,)
啟動任何使用__initcall標識的函數(方便核心開發人員添加啟動函數,此時由do_initcalls()函數啟動)。
此時系統開始載入外部裝置的初始化程式,如:在linux-2.4.x/driver/block/genhd.c中的device_init()函數,在genhd.c中由__initcall(device_init)標識在此時調用,device_init()函數是所有外部裝置初始化的總入口,包括了塊裝置的初始化blk_dev_init,網路裝置的初始化net_dev_init()和atmdev_init()等。
至此do_basic_setup()函數返回init(),在釋放啟動記憶體段(free_initmem())並給核心解鎖以後,init()開啟/dev/console裝置,重新導向stdin、stdout和stderr到控制台,最後,搜尋檔案系統中的init程式(或者由init=命令列參數指定的程式),並使用 execve()系統調用載入執行init程式。(line 576) .
init()函數到此結束,核心的引導部分也到此結束了,
3. uClinux-dist/linux-2.4.x/init/main.c中的execve("/etc/init",argv_init,envp_init); (line 579)
init進程是系統所有進程的起點,核心在完成核內引導以後,即在本線程(進程)空間內載入init程式,它的進程號是1。
init程式需要讀取/vendors/SAMSUNG/4510B/inittab檔案作為其行為指標,然後執行.
4.系統執行rc指令碼.
hostname Samsung
/bin/expand /etc/ramfs.img /dev/ram0
/bin/expand /etc/ramfs2048.img /dev/ram1
mount -t proc proc /proc
mount -t ext2 /dev/ram0 /var
mount -t ext2 /dev/ram1 /ramdisk
chmod 777 /ramdisk
mkdir /var/config
mkdir /var/tmp
mkdir /var/log
mkdir /var/run
mkdir /var/lock
ifconfig lo 127.0.0.1
route add -net 127.0.0.0 netmask 255.255.255.0 lo
dhcpcd &
cat /etc/motd
rc程式執行完畢後,系統內容已經設定好了,下面就該使用者登入系統了。
5.運行Sash command shell