300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > arm Linux系统启动之----start_kernel函数

arm Linux系统启动之----start_kernel函数

时间:2020-07-29 13:36:06

相关推荐

arm Linux系统启动之----start_kernel函数

head-common.S---具体做了哪些动作---跳转到init/main.c ---b start_kernel//关于start_kernel的强文深入理解linux内核,第八章main.casmlinkage 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自动完成 |///wzt85/blog/item/112a37132f6116c2f6039e44.htmlboot_init_stack_canary(); //cgroup: 它的全称为control group.即一组进程的行为控制.//比如,我们限制进程/bin/sh的CPU使用为20%.我们就可以建一个cpu占用为20%的cgroup.//然后将/bin/sh进程添加到这个cgroup中.当然,一个cgroup可以有多个进程.///u1/51562/showart_1736813.htmlcgroup_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();///universus/archive//05/25/5623971.aspxlock_kernel();//初始化time ticket,时钟tick_init();//函数 tick_init() 很简单,调用 clockevents_register_notifier 函数向 clockevents_chain 通知链注册元素:// tick_notifier。这个元素的回调函数指明了当时钟事件设备信息发生变化(例如新加入一个时钟事件设备等等)时,//应该执行的操作,该回调函数为 tick_notify ///u3/97642/showart_2050200.htmlboot_cpu_init();//初始化页地址,当然对于arm这里是个空函数///special/ebook/PrenticeHall/PrenticeHallPTRTheLinuxKernelPrimer/0131181637/ch08lev1sec5.htmlpage_address_init();printk(KERN_NOTICE "%s", linux_banner);//系结构相关的内核初始化过程///u3/94690/showart_2238008.htmlsetup_arch(&command_line);//初始化内存管理mm_init_owner(&init_mm, &init_task);//处理启动命令,这里就是设置的cmd_linesetup_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 CPUsmp_prepare_boot_cpu();/* arch-specific boot-cpu hooks *///设置node 和 zone 数据结构//内存管理的讲解:/space.php?uid=361890&do=blog&cuid=2146541build_all_zonelists(NULL);//初始化page allocation相关结构page_alloc_init();printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);//解析内核参数//对内核参数的解析:/yuhuntero/blog/item/654a7411e45ce519b8127ba9.htmlparse_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表//这里涉及到进程管理/satanwxd/archive//03/27/5422053.aspxpidhash_init();//初始化VFS的两个重要数据结构dcache和inode的缓存。///yunsongice/archive//02/01/6171324.aspxvfs_caches_init_early();//把编译期间,kbuild设置的异常表,也就是__start___ex_table和__stop___ex_table之中的所有元素进行排序sort_main_extable();//初始化中断向量表///yunsongice/archive//02/01/6171325.aspxtrap_init();//memory map初始化///huyugv_830913/archive//09/15/5886970.aspxmm_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。///tiloog/193361/message.aspx///u1/51562/showart_1341707.htmlrcu_init();//定义在lib/radix-tree.c。//Linux使用radix树来管理位于文件系统缓冲区中的磁盘块,//radix树是trie树的一种///walkland/archive//03/19/4006121.aspxradix_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,//并初始化该中断的链表表头成员结构变量pendinit_IRQ();//prio-tree是一棵查找树,管理的是什么?///dog250/archive//06/28/5700317.aspxprio_tree_init();//初始化定时器Timer相关的数据结构///developerworks/cn/linux/l-cn-clocks/index.htmlinit_timers();//对高精度时钟进行初始化hrtimers_init();//软中断初始化///u1/51562/showart_494363.htmlsoftirq_init();//初始化时钟源timekeeping_init();//初始化系统时间,//检查系统定时器描述结构struct sys_timer全局变量system_timer是否为空,//如果为空将其指向dummy_gettimeoffset()函数。///developerworks/cn/linux/l-cn-clocks/index.htmltime_init();//profile只是内核的一个调试性能的工具,//这个可以通过menuconfig中的Instrumentation Support->profile打开。///bbs//thread-71446-1-1.htmlprofile_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的初始化///space.php?uid=7588746&do=blog&id=153184kmem_cache_init_late();/** HACK ALERT! This is early. We're enabling the console before* we've 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_INITRDif (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机制适用在那些需要把某个整数和特定指针关联在一起的地方。///u3/93255/showart_2524027.htmlidr_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性能测试函数。///u2/86768/showart_2196664.htmlcalibrate_delay();//PID是process id的缩写///satanwxd/archive//03/27/5422053.aspxpidmap_init();//来自mm/rmap.c//分配一个anon_vma_cachep作为anon_vma的slab缓存。//这个技术是PFRA(页框回收算法)技术中的组成部分。//这个技术为定位而生——快速的定位指向同一页框的所有页表项。anon_vma_init();#ifdef CONFIG_X86if (efi_enabled)efi_enter_virtual_mode();#endif//创建thread_info缓存thread_info_cache_init();//申请了一个slab来存放credentials??????如何理解?cred_init();//根据物理内存大小计算允许创建进程的数量///blog//11/jollen_linux_3_fork_init.htmlfork_init(totalram_pages);//给进程的各种资源管理结构分配了相应的对象缓存区///index.php/Linux内核的进程创建proc_caches_init();//创建 buffer_head SLAB 缓存buffer_init();//初始化key的management stuffkey_init();//关于系统安全的初始化,主要是访问控制///nhczp/archive//04/29/2341194.aspxsecurity_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 *///回写相关的初始化///yangp01/archive//04/06/5454822.aspxpage_writeback_init();#ifdef CONFIG_PROC_FSproc_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的状态.///u1/51562/showart_1736813.htmlcgroup_init();///u1/51562/showart_1777937.htmlcpuset_init();进程状态初始化,实际上就是分配了一个存储线程状态的高速缓存taskstats_init_early();delayacct_init();//此处为一空函数imv_init_complete();//测试CPU的各种缺陷,记录检测到的缺陷,以便于内核的其他部分以后可以使用他们工作。check_bugs();//电源相关的初始化///u/548/showart.php?id=377952acpi_early_init(); /* before LAPIC and SMP init *///sfi_init_late();ftrace_init();/* Do the rest non-__init'ed, we're now alive *///创建1号进程,详细分析之rest_init();}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。