新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > arm Linux系統(tǒng)啟動(dòng)之start_kernel函數(shù)

arm Linux系統(tǒng)啟動(dòng)之start_kernel函數(shù)

作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏
head-common.S
---具體做了哪些動(dòng)作
---跳轉(zhuǎn)到init/main.c
---b start_kernel
//關(guān)于start_kernel的強(qiáng)文深入理解linux內(nèi)核,第八章
main.c
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
//來(lái)設(shè)置smp process id,當(dāng)然目前看到的代碼里面這里是空的
smp_setup_processor_id();
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
//lockdep是linux內(nèi)核的一個(gè)調(diào)試模塊,用來(lái)檢查內(nèi)核互斥機(jī)制尤其是自旋鎖潛在的死鎖問(wèn)題。
//自旋鎖由于是查詢方式等待,不釋放處理器,比一般的互斥機(jī)制更容易死鎖,
//故引入lockdep檢查以下幾種情況可能的死鎖(lockdep將有專門(mén)的文章詳細(xì)介紹,在此只是簡(jiǎn)單列舉):
//
//·同一個(gè)進(jìn)程遞歸地加鎖同一把鎖;
//
//·一把鎖既在中斷(或中斷下半部)使能的情況下執(zhí)行過(guò)加鎖操作,
// 又在中斷(或中斷下半部)里執(zhí)行過(guò)加鎖操作。這樣該鎖有可能在鎖定時(shí)由于中斷發(fā)生又試圖在同一處理器上加鎖;
//
//·加鎖后導(dǎo)致依賴圖產(chǎn)生成閉環(huán),這是典型的死鎖現(xiàn)象。
lockdep_init();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
//初始化stack_canary棧3
//stack_canary的是帶防止棧溢出攻擊保護(hù)的堆棧。
// 當(dāng)user space的程序通過(guò)int 0x80進(jìn)入內(nèi)核空間的時(shí)候,CPU自動(dòng)完成一次堆棧切換,
//從user space的stack切換到kernel space的stack。
// 在這個(gè)進(jìn)程exit之前所發(fā)生的所有系統(tǒng)調(diào)用所使用的kernel stack都是同一個(gè)。
//kernel stack的大小一般為4096/8192,
//內(nèi)核堆棧示意圖幫助大家理解:
//
// 內(nèi)存低址 內(nèi)存高址
// | |<-----------------------------esp|
// +-----------------------------------4096-------------------------------+
// | 72 | 4 | x < 4016 | 4 |
// +------------------+-----------------+---------------------------------+
// |thread_info | | STACK_END_MAGIC | var/call chain |stack_canary |
// +------------------+-----------------+---------------------------------+
// | 28 | 44 | | |
// V | |
// restart_block V
//
//esp+0x0 +0x40
// +---------------------------------------------------------------------------+
// |ebx|ecx|edx|esi|edi|ebp|eax|ds|es|fs|gs|orig_eax|eip|cs|eflags|oldesp|oldss|
// +---------------------------------------------------------------------------+
// | kernel完成 | cpu自動(dòng)完成 |
//http://hi.baidu.com/wzt85/blog/item/112a37132f6116c2f6039e44.html
boot_init_stack_canary();
// cgroup: 它的全稱為control group.即一組進(jìn)程的行為控制.
// 比如,我們限制進(jìn)程/bin/sh的CPU使用為20%.我們就可以建一個(gè)cpu占用為20%的cgroup.
// 然后將/bin/sh進(jìn)程添加到這個(gè)cgroup中.當(dāng)然,一個(gè)cgroup可以有多個(gè)進(jìn)程.
//http://blogold.chinaunix.net/u1/51562/showart_1736813.html
cgroup_init_early();
//更新kernel中的所有的立即數(shù)值,但是包括哪些需要再看?
core_imv_update();
//關(guān)閉當(dāng)前CUP中斷
local_irq_disable();
//修改標(biāo)記early_boot_irqs_enabled;
//通過(guò)一個(gè)靜態(tài)全局變量 early_boot_irqs_enabled來(lái)幫助我們調(diào)試代碼,
//通過(guò)這個(gè)標(biāo)記可以幫助我們知道是否在”early bootup code”,也可以通過(guò)這個(gè)標(biāo)志警告是有無(wú)效的終端打開(kāi)
early_boot_irqs_off();
//每一個(gè)中斷都有一個(gè)IRQ描述符(struct irq_desc)來(lái)進(jìn)行描述。
//這個(gè)函數(shù)的主要作用是設(shè)置所有的 IRQ描述符(struct irq_desc)的鎖是統(tǒng)一的鎖,
//還是每一個(gè)IRQ描述符(struct irq_desc)都有一個(gè)小鎖。
early_init_irq_lock_class();
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
// 大內(nèi)核鎖(BKL--Big Kernel Lock)
//大內(nèi)核鎖本質(zhì)上也是自旋鎖,但是它又不同于自旋鎖,自旋鎖是不可以遞歸獲得鎖的,因?yàn)槟菢訒?huì)導(dǎo)致死鎖。
//但大內(nèi)核鎖可以遞歸獲得鎖。大內(nèi)核鎖用于保護(hù)整個(gè)內(nèi)核,而自旋鎖用于保護(hù)非常特定的某一共享資源。
//進(jìn)程保持大內(nèi)核鎖時(shí)可以發(fā)生調(diào)度,具體實(shí)現(xiàn)是:
//在執(zhí)行schedule時(shí),schedule將檢查進(jìn)程是否擁有大內(nèi)核鎖,如果有,它將被釋放,以致于其它的進(jìn)程能夠獲得該鎖,
//而當(dāng)輪到該進(jìn)程運(yùn)行時(shí),再讓它重新獲得大內(nèi)核鎖。注意在保持自旋鎖期間是不運(yùn)行發(fā)生調(diào)度的。
//需要特別指出,整個(gè)內(nèi)核只有一個(gè)大內(nèi)核鎖,其實(shí)不難理解,內(nèi)核只有一個(gè),而大內(nèi)核鎖是保護(hù)整個(gè)內(nèi)核的,當(dāng)然有且只有一個(gè)就足夠了。
//還需要特別指出的是,大內(nèi)核鎖是歷史遺留,內(nèi)核中用的非常少,一般保持該鎖的時(shí)間較長(zhǎng),因此不提倡使用它。
//從2.6.11內(nèi)核起,大內(nèi)核鎖可以通過(guò)配置內(nèi)核使其變得可搶占(自旋鎖是不可搶占的),這時(shí)它實(shí)質(zhì)上是一個(gè)互斥鎖,使用信號(hào)量實(shí)現(xiàn)。
//大內(nèi)核鎖的API包括:
//
//void lock_kernel(void);
//
//該函數(shù)用于得到大內(nèi)核鎖。它可以遞歸調(diào)用而不會(huì)導(dǎo)致死鎖。
//
//void unlock_kernel(void);
//
//該函數(shù)用于釋放大內(nèi)核鎖。當(dāng)然必須與lock_kernel配對(duì)使用,調(diào)用了多少次lock_kernel,就需要調(diào)用多少次unlock_kernel。
//大內(nèi)核鎖的API使用非常簡(jiǎn)單,按照以下方式使用就可以了:
//lock_kernel(); //對(duì)被保護(hù)的共享資源的訪問(wèn) … unlock_kernel();
//http://blog.csdn.net/universus/archive/2010/05/25/5623971.aspx
lock_kernel();
//初始化time ticket,時(shí)鐘
tick_init();
//函數(shù) tick_init() 很簡(jiǎn)單,調(diào)用 clockevents_register_notifier 函數(shù)向 clockevents_chain 通知鏈注冊(cè)元素:
// tick_notifier。這個(gè)元素的回調(diào)函數(shù)指明了當(dāng)時(shí)鐘事件設(shè)備信息發(fā)生變化(例如新加入一個(gè)時(shí)鐘事件設(shè)備等等)時(shí),
//應(yīng)該執(zhí)行的操作,該回調(diào)函數(shù)為 tick_notify
//http://blogold.chinaunix.net/u3/97642/showart_2050200.html
boot_cpu_init();
//初始化頁(yè)地址,當(dāng)然對(duì)于arm這里是個(gè)空函數(shù)
//http://book.chinaunix.net/special/ebook/PrenticeHall/PrenticeHallPTRTheLinuxKernelPrimer/0131181637/ch08lev1sec5.html
page_address_init();
printk(KERN_NOTICE "%s", linux_banner);
//系結(jié)構(gòu)相關(guān)的內(nèi)核初始化過(guò)程
//http://www.cublog.cn/u3/94690/showart_2238008.html
setup_arch(&command_line);
//初始化內(nèi)存管理
mm_init_owner(&init_mm, &init_task);
//處理啟動(dòng)命令,這里就是設(shè)置的cmd_line
setup_command_line(command_line);
//這個(gè)在定義了SMP的時(shí)候有作用,現(xiàn)在這里為空函數(shù);對(duì)于smp的使用,后面在看。。。
setup_nr_cpu_ids();
//如果沒(méi)有定義CONFIG_SMP宏,則這個(gè)函數(shù)為空函數(shù)。
//如果定義了CONFIG_SMP宏,則這個(gè)setup_per_cpu_areas()函數(shù)給每個(gè)CPU分配內(nèi)存,
//并拷貝.data.percpu段的數(shù)據(jù)。為系統(tǒng)中的每個(gè)CPU的per_cpu變量申請(qǐng)空間。
setup_per_cpu_areas();
//定義在include/asm-x86/smp.h。
//如果是SMP環(huán)境,則設(shè)置boot CPU的一些數(shù)據(jù)。在引導(dǎo)過(guò)程中使用的CPU稱為boot CPU
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
//設(shè)置node 和 zone 數(shù)據(jù)結(jié)構(gòu)
//內(nèi)存管理的講解:http://blog.chinaunix.net/space.php?uid=361890&do=blog&cuid=2146541
build_all_zonelists(NULL);
//初始化page allocation相關(guān)結(jié)構(gòu)
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);
//解析內(nèi)核參數(shù)
//對(duì)內(nèi)核參數(shù)的解析:http://hi.baidu.com/yuhuntero/blog/item/654a7411e45ce519b8127ba9.html
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
//初始化hash表,以便于從進(jìn)程的PID獲得對(duì)應(yīng)的進(jìn)程描述指針,按照實(shí)際的物理內(nèi)存初始化pid hash表
//這里涉及到進(jìn)程管理http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx
pidhash_init();
//初始化VFS的兩個(gè)重要數(shù)據(jù)結(jié)構(gòu)dcache和inode的緩存。
//http://blog.csdn.net/yunsongice/archive/2011/02/01/6171324.aspx
vfs_caches_init_early();
//把編譯期間,kbuild設(shè)置的異常表,也就是__start___ex_table和__stop___ex_table之中的所有元素進(jìn)行排序
sort_main_extable();
//初始化中斷向量表
//http://blog.csdn.net/yunsongice/archive/2011/02/01/6171325.aspx
trap_init();
//memory map初始化
//http://blog.csdn.net/huyugv_830913/archive/2010/09/15/5886970.aspx
mm_init();
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
//核心進(jìn)程調(diào)度器初始化,調(diào)度器的初始化的優(yōu)先級(jí)要高于任何中斷的建立,
//并且初始化進(jìn)程0,即idle進(jìn)程,但是并沒(méi)有設(shè)置idle進(jìn)程的NEED_RESCHED標(biāo)志,
//所以還會(huì)繼續(xù)完成內(nèi)核初始化剩下的事情。
//這里僅僅為進(jìn)程調(diào)度程序的執(zhí)行做準(zhǔn)備。
//它所做的具體工作是調(diào)用init_bh函數(shù)(kernel/softirq.c)把timer,tqueue,immediate三個(gè)人物隊(duì)列加入下半部分的數(shù)組
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
//搶占計(jì)數(shù)器加1
preempt_disable();
//檢查中斷是否打開(kāi)
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it/n");
local_irq_disable();
}
//Read-Copy-Update的初始化
//RCU機(jī)制是Linux2.6之后提供的一種數(shù)據(jù)一致性訪問(wèn)的機(jī)制,
//從RCU(read-copy-update)的名稱上看,我們就能對(duì)他的實(shí)現(xiàn)機(jī)制有一個(gè)大概的了解,
//在修改數(shù)據(jù)的時(shí)候,首先需要讀取數(shù)據(jù),然后生成一個(gè)副本,對(duì)副本進(jìn)行修改,
//修改完成之后再將老數(shù)據(jù)update成新的數(shù)據(jù),此所謂RCU。
//http://blog.ednchina.com/tiloog/193361/message.aspx
//http://blogold.chinaunix.net/u1/51562/showart_1341707.html
rcu_init();
//定義在lib/radix-tree.c。
//Linux使用radix樹(shù)來(lái)管理位于文件系統(tǒng)緩沖區(qū)中的磁盤(pán)塊,
//radix樹(shù)是trie樹(shù)的一種
//http://blog.csdn.net/walkland/archive/2009/03/19/4006121.aspx
radix_tree_init();
/* init some links before init_ISA_irqs() */
//early_irq_init 則對(duì)數(shù)組中每個(gè)成員結(jié)構(gòu)進(jìn)行初始化,
//例如, 初始每個(gè)中斷源的中斷號(hào).其他的函數(shù)基本為空.
early_irq_init();
//初始化IRQ中斷和終端描述符。
//初始化系統(tǒng)中支持的最大可能的中斷描述結(jié)構(gòu)struct irqdesc變量數(shù)組irq_desc[NR_IRQS],
//把每個(gè)結(jié)構(gòu)變量irq_desc[n]都初始化為預(yù)先定義好的壞中斷描述結(jié)構(gòu)變量bad_irq_desc,
//并初始化該中斷的鏈表表頭成員結(jié)構(gòu)變量pend
init_IRQ();
//prio-tree是一棵查找樹(shù),管理的是什么?
//http://blog.csdn.net/dog250/archive/2010/06/28/5700317.aspx
prio_tree_init();
//初始化定時(shí)器Timer相關(guān)的數(shù)據(jù)結(jié)構(gòu)
//http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html
init_timers();
//對(duì)高精度時(shí)鐘進(jìn)行初始化
hrtimers_init();
//軟中斷初始化
//http://blogold.chinaunix.net/u1/51562/showart_494363.html
softirq_init();
//初始化時(shí)鐘源
timekeeping_init();
//初始化系統(tǒng)時(shí)間,
//檢查系統(tǒng)定時(shí)器描述結(jié)構(gòu)struct sys_timer全局變量system_timer是否為空,
//如果為空將其指向dummy_gettimeoffset()函數(shù)。
//http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html
time_init();
//profile只是內(nèi)核的一個(gè)調(diào)試性能的工具,
//這個(gè)可以通過(guò)menuconfig中的Instrumentation Support->profile打開(kāi)。
//http://www.linuxdiyf.com/bbs//thread-71446-1-1.html
profile_init();
if (!irqs_disabled())
printk(KERN_CRIT "start_kernel(): bug: interrupts were "
"enabled early/n");
//與開(kāi)始的early_boot_irqs_off相對(duì)應(yīng)
early_boot_irqs_on();
//與local_irq_disbale相對(duì)應(yīng),開(kāi)中斷
local_irq_enable();
/* Interrupts are enabled now so all GFP allocations are safe. */
gfp_allowed_mask = __GFP_BITS_MASK;
//memory cache的初始化
//http://my.chinaunix.net/space.php?uid=7588746&do=blog&id=153184
kmem_cache_init_late();
/*
* HACK ALERT! This is early. Were enabling the console before
* weve done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
//初始化控制臺(tái)以顯示printk的內(nèi)容,在此之前調(diào)用的printk,只是把數(shù)據(jù)存到緩沖區(qū)里,
//只有在這個(gè)函數(shù)調(diào)用后,才會(huì)在控制臺(tái)打印出內(nèi)容
//該函數(shù)執(zhí)行后可調(diào)用printk()函數(shù)將log_buf中符合打印級(jí)別要求的系統(tǒng)信息打印到控制臺(tái)上。
console_init();
if (panic_later)
panic(panic_later, panic_param);
//如果定義了CONFIG_LOCKDEP宏,那么就打印鎖依賴信息,否則什么也不做
lockdep_info();
/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
//如果定義CONFIG_DEBUG_LOCKING_API_SELFTESTS宏
//則locking_selftest()是一個(gè)空函數(shù),否則執(zhí)行鎖自測(cè)
locking_selftest();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
"disabling it./n",
page_to_pfn(virt_to_page((void *)initrd_start)),
min_low_pfn);
initrd_start = 0;
}
#endif
//頁(yè)面初始化,可以參考上面的cgroup機(jī)制
page_cgroup_init();
//頁(yè)面分配debug啟用
enable_debug_pagealloc();
//此處函數(shù)為空
kmemtrace_init();
//memory lead偵測(cè)初始化,如何偵測(cè)???
kmemleak_init();
//
//Called after the kmem_caches are functional to setup a dedicated
//cache pool, which has the SLAB_DEBUG_OBJECTS flag set. This flag
//prevents that the debug code is called on kmem_cache_free() for the
//debug tracker objects to avoid recursive calls.
//在kmem_caches之后表示建立一個(gè)高速緩沖池,建立起SLAB_DEBUG_OBJECTS標(biāo)志。???
debug_objects_mem_init();
//idr在linux內(nèi)核中指的就是整數(shù)ID管理機(jī)制,
//從本質(zhì)上來(lái)說(shuō),這就是一種將整數(shù)ID號(hào)和特定指針關(guān)聯(lián)在一起的機(jī)制
//idr機(jī)制適用在那些需要把某個(gè)整數(shù)和特定指針關(guān)聯(lián)在一起的地方。
//http://blogold.chinaunix.net/u3/93255/showart_2524027.html
idr_init_cache();
//是否是對(duì)SMP的支持,單核是否需要??這個(gè)要分析
setup_per_cpu_pageset();
//NUMA (Non Uniform Memory Access) policy
//具體是什么不懂
numa_policy_init();
if (late_time_init)
late_time_init();
//初始化調(diào)度時(shí)鐘
sched_clock_init();
//calibrate_delay()函數(shù)可以計(jì)算出cpu在一秒鐘內(nèi)執(zhí)行了多少次一個(gè)極短的循環(huán),
//計(jì)算出來(lái)的值經(jīng)過(guò)處理后得到BogoMIPS 值,
//Bogo是Bogus(偽)的意思,MIPS是millions of instructions per second(百萬(wàn)條指令每秒)的縮寫(xiě)。
//這樣我們就知道了其實(shí)這個(gè)函數(shù)是linux內(nèi)核中一個(gè)cpu性能測(cè)試函數(shù)。
//http://blogold.chinaunix.net/u2/86768/showart_2196664.html
calibrate_delay();
//PID是process id的縮寫(xiě)
//http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx
pidmap_init();
//來(lái)自mm/rmap.c
//分配一個(gè)anon_vma_cachep作為anon_vma的slab緩存。
//這個(gè)技術(shù)是PFRA(頁(yè)框回收算法)技術(shù)中的組成部分。
//這個(gè)技術(shù)為定位而生——快速的定位指向同一頁(yè)框的所有頁(yè)表項(xiàng)。
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
//創(chuàng)建thread_info緩存
thread_info_cache_init();
//申請(qǐng)了一個(gè)slab來(lái)存放credentials??????如何理解?
cred_init();
//根據(jù)物理內(nèi)存大小計(jì)算允許創(chuàng)建進(jìn)程的數(shù)量
//http://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html
fork_init(totalram_pages);
//給進(jìn)程的各種資源管理結(jié)構(gòu)分配了相應(yīng)的對(duì)象緩存區(qū)
//http://www.shangshuwu.cn/index.php/Linux內(nèi)核的進(jìn)程創(chuàng)建
proc_caches_init();
//創(chuàng)建 buffer_head SLAB 緩存
buffer_init();
//初始化key的management stuff
key_init();
//關(guān)于系統(tǒng)安全的初始化,主要是訪問(wèn)控制
//http://blog.csdn.net/nhczp/archive/2008/04/29/2341194.aspx
security_init();
//與debug kernel相關(guān)
dbg_late_init();
//調(diào)用kmem_cache_create()函數(shù)來(lái)為VFS創(chuàng)建各種SLAB分配器緩存
//包括:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四個(gè)SLAB分配器緩存
vfs_caches_init(totalram_pages);
//創(chuàng)建信號(hào)隊(duì)列
signals_init();
/* rootfs populating might need page-writeback */
//回寫(xiě)相關(guān)的初始化
//http://blog.csdn.net/yangp01/archive/2010/04/06/5454822.aspx
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
//它將剩余的subsys初始化.然后將init_css_set添加進(jìn)哈希數(shù)組css_set_table[ ]中.
//在上面的代碼中css_set_hash()是css_set_table的哈希函數(shù).
//它是css_set->subsys為哈希鍵值,到css_set_table[ ]中找到對(duì)應(yīng)項(xiàng).然后調(diào)用hlist_add_head()將init_css_set添加到?jīng)_突項(xiàng)中.
//然后,注冊(cè)了cgroup文件系統(tǒng).這個(gè)文件系統(tǒng)也是我們?cè)谟脩艨臻g使用cgroup時(shí)必須掛載的.
//最后,在proc的根目錄下創(chuàng)建了一個(gè)名為cgroups的文件.用來(lái)從用戶空間觀察cgroup的狀態(tài).
//http://blogold.chinaunix.net/u1/51562/showart_1736813.html
cgroup_init();
//http://blogold.chinaunix.net/u1/51562/showart_1777937.html
cpuset_init();
////進(jìn)程狀態(tài)初始化,實(shí)際上就是分配了一個(gè)存儲(chǔ)線程狀態(tài)的高速緩存
taskstats_init_early();
delayacct_init();
//此處為一空函數(shù)
imv_init_complete();
//測(cè)試CPU的各種缺陷,記錄檢測(cè)到的缺陷,以便于內(nèi)核的其他部分以后可以使用他們工作。
check_bugs();
//電源相關(guān)的初始化
//http://blogold.chinaunix.net/u/548/showart.php?id=377952
acpi_early_init(); /* before LAPIC and SMP init */
//
sfi_init_late();
ftrace_init();
/* Do the rest non-__inited, were now alive */
//創(chuàng)建1號(hào)進(jìn)程,詳細(xì)分析之
rest_init();
}


評(píng)論


技術(shù)專區(qū)

關(guān)閉