您的位置 首页 方案

arm Linux系统启动之start_kernel函数

head-common.S—具体做了哪些动作—跳转到init/main.c—bstart_kernel//关于start_kernel的强文深入理解linux内核,第八章main.casm

head-common.S

—详细做了哪些动作
—跳转到init/main.c
—b start_kernel
//关于start_kernel的强文深化了解linux内核,第八章
main.c
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
//来设置smp process id,当然现在看到的代码里边这儿是空的
smp_setup_processor_id();
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
//lockdep是linux内核的一个调试模块,用来查看内核互斥机制尤其是自旋锁潜在的死锁问题。
//自旋锁由所以查询方法等候,不开释处理器,比一般的互斥机制更简略死锁,
//故引进lockdep查看以下几种状况或许的死锁(lockdep将有专门的文章详细介绍,在此仅仅简略罗列):
//
//·同一个进程递归地加锁同一把锁;
//
//·一把锁既在中止(或中止下半部)使能的状况下履行过加锁操作,
// 又在中止(或中止下半部)里履行过加锁操作。这样该锁有或许在锁守时由于中止产生又企图在同一处理器上加锁;
//
//·加锁后导致依靠图产生成闭环,这是典型的死锁现象。
lockdep_init();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
//初始化stack_canary栈3
//stack_canary的是带避免栈溢出进犯维护的仓库。
// 当user space的程序通过int 0x80进入内核空间的时分,CPU主动完结一次仓库切换,
//从user space的stack切换到kernel space的stack。
// 在这个进程exit之前所产生的一切体系调用所运用的kernel stack都是同一个。
//kernel stack的巨细一般为4096/8192,
//内核仓库示意图协助咱们了解:
//
// 内存低址 内存高址
// | |<-----------------------------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主动完结 |
//http://hi.baidu.com/wzt85/blog/item/112a37132f6116c2f6039e44.html
boot_init_stack_canary();
// cgroup: 它的全称为control group.即一组进程的行为操控.
// 比方,咱们约束进程/bin/sh的CPU运用为20%.咱们就能够建一个cpu占用为20%的cgroup.
// 然后将/bin/sh进程添加到这个cgroup中.当然,一个cgroup能够有多个进程.
//http://blogold.chinaunix.net/u1/51562/showart_1736813.html
cgroup_init_early();
//更新kernel中的一切的当即数值,可是包含哪些需求再看?
core_imv_update();
//封闭当时CUP中止
local_irq_disable();
//修正符号early_boot_irqs_enabled;
//通过一个静态全局变量 early_boot_irqs_enabled来协助咱们调试代码,
//通过这个符号能够协助咱们知道是否在”early bootup code”,也能够通过这个标志正告是有无效的终端翻开
early_boot_irqs_off();
//每一个中止都有一个IRQ描绘符(struct irq_desc)来进行描绘。
//这个函数的首要效果是设置一切的 IRQ描绘符(struct irq_desc)的锁是一致的锁,
//仍是每一个IRQ描绘符(struct irq_desc)都有一个小锁。
early_init_irq_lock_class();
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
// 大内核锁(BKL–Big Kernel Lock)
//大内核锁本质上也是自旋锁,可是它又不同于自旋锁,自旋锁是不能够递归取得锁的,由于那样会导致死锁。
//但大内核锁能够递归取得锁。大内核锁用于维护整个内核,而自旋锁用于维护十分特定的某一共享资源。
//进程坚持大内核锁时能够产生调度,详细完结是:
//在履行schedule时,schedule将查看进程是否具有大内核锁,假如有,它将被开释,以致于其它的进程能够取得该锁,
//而当轮到该进程运转时,再让它从头取得大内核锁。注意在坚持自旋锁期间是不运转产生调度的。
//需求特别指出,整个内核只要一个大内核锁,其实不难了解,内核只要一个,而大内核锁是维护整个内核的,当然有且只要一个就足够了。
//还需求特别指出的是,大内核锁是前史留传,内核顶用的十分少,一般坚持该锁的时刻较长,因而不发起运用它。
//从2.6.11内核起,大内核锁能够通过装备内核使其变得可抢占(自旋锁是不行抢占的),这时它实质上是一个互斥锁,运用信号量完结。
//大内核锁的API包含:
//
//void lock_kernel(void);
//
//该函数用于得到大内核锁。它能够递归调用而不会导致死锁。
//
//void unlock_kernel(void);
//
//该函数用于开释大内核锁。当然有必要与lock_kernel配对运用,调用了多少次lock_kernel,就需求调用多少次unlock_kernel。
//大内核锁的API运用十分简略,依照以下方法运用就能够了:
//lock_kernel(); //对被维护的共享资源的拜访 … unlock_kernel();
//http://blog.csdn.net/universus/archive/2010/05/25/5623971.aspx
lock_kernel();
//初始化time ticket,时钟
tick_init();
//函数 tick_init() 很简略,调用 clockevents_register_notifier 函数向 clockevents_chain 告诉链注册元素:
// tick_notifier。这个元素的回调函数指明了当时钟作业设备信息产生变化(例如新参加一个时钟作业设备等等)时,
//应该履行的操作,该回调函数为 tick_notify
//http://blogold.chinaunix.net/u3/97642/showart_2050200.html
boot_cpu_init();
//初始化页地址,当然关于arm这儿是个空函数
//http://book.chinaunix.net/special/ebook/PrenticeHall/PrenticeHallPTRTheLinuxKernelPrimer/0131181637/ch08lev1sec5.html
page_address_init();
printk(KERN_NOTICE “%s”, linux_banner);
//系结构相关的内核初始化进程
//http://www.cublog.cn/u3/94690/showart_2238008.html
setup_arch(&command_line);
//初始化内存办理
mm_init_owner(&init_mm, &init_task);
//处理发动指令,这儿便是设置的cmd_line
setup_command_line(command_line);
//这个在界说了SMP的时分有效果,现在这儿为空函数;关于smp的运用,后边在看。。。
setup_nr_cpu_ids();
//假如没有界说CONFIG_SMP宏,则这个函数为空函数。
//假如界说了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,
//并复制.data.percpu段的数据。为体系中的每个CPU的per_cpu变量请求空间。
setup_per_cpu_areas();
//界说在include/asm-x86/smp.h。
//假如是SMP环境,则设置boot CPU的一些数据。在引导进程中运用的CPU称为boot CPU
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
//设置node 和 zone 数据结构
//内存办理的解说:http://blog.chinaunix.net/space.php?uid=361890&do=blog&cuid=2146541
build_all_zonelists(NULL);
//初始化page allocation相关结构
page_alloc_init();
printk(KERN_NOTICE “Kernel command line: %s/n”, boot_command_line);
//解析内核参数
//对内核参数的解析: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表,以便于从进程的PID取得对应的进程描绘指针,依照实践的物理内存初始化pid hash表
//这儿涉及到进程办理http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx
pidhash_init();
//初始化VFS的两个重要数据结构dcache和inode的缓存。
//http://blog.csdn.net/yunsongice/archive/2011/02/01/6171324.aspx
vfs_caches_init_early();
//把编译期间,kbuild设置的反常表,也便是__start___ex_table和__stop___ex_table之中的一切元素进行排序
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.
*/
//中心进程调度器初始化,调度器的初始化的优先级要高于任何中止的树立,
//而且初始化进程0,即idle进程,可是并没有设置idle进程的NEED_RESCHED标志,
//所以还会持续完结内核初始化剩下的作业。
//这儿仅仅为进程调度程序的履行做准备。
//它所做的详细作业是调用init_bh函数(kernel/softirq.c)把timer,tqueue,immediate三个人物行列参加下半部分的数组
sched_init();
/*
* Disable preemption – early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
//抢占计数器加1
preempt_disable();
//查看中止是否翻开
if (!irqs_disabled()) {
printk(KERN_WARNING “start_kernel(): bug: interrupts were “
“enabled *very* early, fixing it/n”);
local_irq_disable();
}
//Read-Copy-Update的初始化
//RCU机制是Linux2.6之后供给的一种数据一致性拜访的机制,
//从RCU(read-copy-update)的称号上看,咱们就能对他的完结机制有一个大约的了解,
//在修正数据的时分,首要需求读取数据,然后生成一个副本,对副本进行修正,
//修正完结之后再将老数据update成新的数据,此所谓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树来办理坐落文件体系缓冲区中的磁盘块,
//radix树是trie树的一种
//http://blog.csdn.net/walkland/archive/2009/03/19/4006121.aspx
radix_tree_init();
/* init some links before init_ISA_irqs() */
//early_irq_init 则对数组中每个成员结构进行初始化,
//例如, 初始每个中止源的中止号.其他的函数根本为空.
early_irq_init();
//初始化IRQ中止和终端描绘符。
//初始化体系中支撑的最大或许的中止描绘结构struct irqdesc变量数组irq_desc[NR_IRQS],
//把每个结构变量irq_desc[n]都初始化为预先界说好的坏中止描绘结构变量bad_irq_desc,
//并初始化该中止的链表表头成员结构变量pend
init_IRQ();
//prio-tree是一棵查找树,办理的是什么?
//http://blog.csdn.net/dog250/archive/2010/06/28/5700317.aspx
prio_tree_init();
//初始化守时器Timer相关的数据结构
//http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html
init_timers();
//对高精度时钟进行初始化
hrtimers_init();
//软中止初始化
//http://blogold.chinaunix.net/u1/51562/showart_494363.html
softirq_init();
//初始化时钟源
timekeeping_init();
//初始化体系时刻,
//查看体系守时器描绘结构struct sys_timer全局变量system_timer是否为空,
//假如为空将其指向dummy_gettimeoffset()函数。
//http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html
time_init();
//profile仅仅内核的一个调试功能的东西,
//这个能够通过menuconfig中的Instrumentation Support->profile翻开。
//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”);
//与开端的early_boot_irqs_off相对应
early_boot_irqs_on();
//与local_irq_disbale相对应,开中止
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.
*/
//初始化操控台以显现printk的内容,在此之前调用的printk,仅仅把数据存到缓冲区里,
//只要在这个函数调用后,才会在操控台打印出内容
//该函数履行后可调用printk()函数将log_buf中契合打印等级要求的体系信息打印到操控台上。
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()是一个空函数,不然履行锁自测
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
//页面初始化,能够参阅上面的cgroup机制
page_cgroup_init();
//页面分配debug启用
enable_debug_pagealloc();
//此处函数为空
kmemtrace_init();
//memory lead侦测初始化,怎么侦测???
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之后表明树立一个高速缓冲池,树立起SLAB_DEBUG_OBJECTS标志。???
debug_objects_mem_init();
//idr在linux内核中指的便是整数ID办理机制,
//从本质上来说,这便是一种将整数ID号和特定指针相关在一起的机制
//idr机制适用在那些需求把某个整数和特定指针相关在一起的当地。
//http://blogold.chinaunix.net/u3/93255/showart_2524027.html
idr_init_cache();
//是否是对SMP的支撑,单核是否需求??这个要剖析
setup_per_cpu_pageset();
//NUMA (Non Uniform Memory Access) policy
//详细是什么不明白
numa_policy_init();
if (late_time_init)
late_time_init();
//初始化调度时钟
sched_clock_init();
//calibrate_delay()函数能够核算出cpu在一秒钟内履行了多少次一个极短的循环,
//核算出来的值通过处理后得到BogoMIPS 值,
//Bogo是Bogus(伪)的意思,MIPS是millions of instructions per second(百万条指令每秒)的缩写。
//这样咱们就知道了其实这个函数是linux内核中一个cpu功能测验函数。
//http://blogold.chinaunix.net/u2/86768/showart_2196664.html
calibrate_delay();
//PID是process id的缩写
//http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx
pidmap_init();
//来自mm/rmap.c
//分配一个anon_vma_cachep作为anon_vma的slab缓存。
//这个技能是PFRA(页框收回算法)技能中的组成部分。
//这个技能为定位而生——快速的定位指向同一页框的一切页表项。
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
//创立thread_info缓存
thread_info_cache_init();
//请求了一个slab来寄存credentials??????怎么了解?
cred_init();
//依据物理内存巨细核算答应创立进程的数量
//http://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html
fork_init(totalram_pages);
//给进程的各种资源办理结构分配了相应的目标缓存区
//http://www.shangshuwu.cn/index.php/Linux内核的进程创立
proc_caches_init();
//创立 buffer_head SLAB 缓存
buffer_init();
//初始化key的management stuff
key_init();
//关于体系安全的初始化,首要是拜访操控
//http://blog.csdn.net/nhczp/archive/2008/04/29/2341194.aspx
security_init();
//与debug kernel相关
dbg_late_init();
//调用kmem_cache_create()函数来为VFS创立各种SLAB分配器缓存
//包含:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四个SLAB分配器缓存
vfs_caches_init(totalram_pages);
//创立信号行列
signals_init();
/* rootfs populating might need page-writeback */
//回写相关的初始化
//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添加进哈希数组css_set_table[ ]中.
//在上面的代码中css_set_hash()是css_set_table的哈希函数.
//它是css_set->subsys为哈希键值,到css_set_table[ ]中找到对应项.然后调用hlist_add_head()将init_css_set添加到抵触项中.
//然后,注册了cgroup文件体系.这个文件体系也是咱们在用户空间运用cgroup时有必要挂载的.
//最终,在proc的根目录下创立了一个名为cgroups的文件.用来从用户空间调查cgroup的状况.
//http://blogold.chinaunix.net/u1/51562/showart_1736813.html
cgroup_init();
//http://blogold.chinaunix.net/u1/51562/showart_1777937.html
cpuset_init();
////进程状况初始化,实践上便是分配了一个存储线程状况的高速缓存
taskstats_init_early();
delayacct_init();
//此处为一空函数
imv_init_complete();
//测验CPU的各种缺点,记载检测到的缺点,以便于内核的其他部分今后能够运用他们作业。
check_bugs();
//电源相关的初始化
//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 */
//创立1号进程,详细剖析之
rest_init();
}

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/fangan/277315.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部