Kernel中的哪些虚函数实现原理里实现

linux2.3.22.6内核启动第二阶段(start_kernel函数主要流程) - ARM技术论坛 -
中国电子技术论坛 -
最好最受欢迎电子论坛!
后使用快捷导航没有帐号?
linux2.3.22.6内核启动第二阶段(start_kernel函数主要流程)
17:16:46  
linux2.3.22.6内核启动第二阶段(start_kernel函数主要流程)[size=12.0000pt]一、前言UBOOT在特定内存处( 0x)以TAG格式设置好的参数传给了内核,内核当然是要去处理的。之前对于怎么处理一点概念也没有,以至于碰到的许多问题不知道去哪里解决,看了韦东山老师视频关于内核的讲解之后,柳暗花明又一村啊。对内核关于参数的处理,有了很明确的概念与方向,下面及时整理出一点心得,语言表达的不好,勿喷哈。下面是韦东山老师在课程中对于内核启动的目的,很形象的描述1、内核启动的终极目的就是运行应用程序,Windows的应用程序一般放在C盘、D盘中,而我们LIUNUX的应用程序放在根文件系统中,所以内核想运行应用程序就得首先挂载根文件系统。[size=14.0000pt]2、之前U-boot辛辛苦苦的设置了一些参数,例如机器ID和以TAG格式存储在地址bi_boot_params开始处,并且以setup_start_tag (bd);开始,以setup_end_tag (bd)结束的参数,(主要就是这两块了)。UBOOT将这些参数传递给内核,内核除了挂载跟文件系统外首先得处理这些参数吧(要不然多对不起U-BOOT啊)。二、内核2大任务的处理下面开始正题首先宏观上大体说一下内核是处理UBOOT传递过来的参数的,以便对处理过程有个整体的“鸟瞰式”的理解。内核对于参数的处理个人理解为:整体上分为2大块:[size=14.0000pt](1)机器ID,这个参数的处理主要在Head.s阶段中的__lookup_machine_type函数中处理。[size=14.0000pt](2)对于TAG式参数的处理。这一块又分为两步处理。首先是对参数中非bootargs参数的处理及对bootargs参数的处理。也就是说内核对于存放在0x中的参数处理时,bootargs这部分参数是单独拿出来处理的。下面详细记录内核的C语言部分启动的主要流程,重点记录,内核处理参数的2大块,及挂载根文件系统、挂载根文件系统后启动的第一个应用程序。start_kernel是内核启动后运行的第一个C语言程序,在这里面调用了许多函数,其实大多函数我们没有必要去了解(因为内核太庞大了,等到我们需要时再仔细分析)这里主要针对任务1和2记录内核流程。smp_setup_processor_id();//这个函数现在是空的;lockdep_init();//Runtime&&locking correctness validator, see Documentation/lockdep_design.txtdebug_objects_early_init();cgroup_init_early();//Control group, read Documentation/cgroup.txtlocal_irq_disable();early_boot_irqs_off();early_init_irq_lock_class();/* 基本上面几个函数就是初始化lockdep和cgroup,然后禁止IRQ,为后续的运行创造条件 */lock_kernel(); /* 看这个函数的之前我们首先来了解一段知识,linux kernel默认是支持 preemption(抢占)的。在SMP环境下为了实现kernel的锁定,kernel使用了一个 BKL(big kernel lock)的概念,在初始化的过程中先锁定这个BKL,然后再继续 进行其他启动或者初始化过程,这样就可以防止启动过程中被中断,执行到 res_init以后,kernel会释放这个锁,这样就确保了整个start_kernel过程都 不会被抢占或者中断。由此我们可以看到这主要是针对多处理器而言的,实际 上如果只有一个处理器的话它也不会被中断或者被抢占。 */ tick_init();& && && &//和时钟相关的初始化,好像是注册notify事件,没有仔细研究过&&boot_cpu_init();& && && &//这个实际上是在SMP环境下选择CPU,这里直接CPUID选择的是0号cpupage_address_init();& && && &//初始化high memory,在arm环境下实际上这个函数是空的,也就是说arm& && && &不支持high memory& && && &printk(KERN_NOTICE);& && && &printk(linux_banner);& && && &//这里的KER_NOTICE是字符串&5&,不太明白它的意思。。。& && && &后面的linux_banner定义在kernel/init/version.c里面,& && && &这里的printk是门高深的学问,(根据韦老师所讲,这个函数不直接打印& && && &输出,而是首先把他放到缓存里免去,等到初始化完console_init后& && && &再打印输出) setup_arch(&command_line); 重量级函数,贴出代码一一分析 void __init setup_arch(char **cmdline_p){& & & & struct tag *tags = (struct tag *)&init_& & & & struct machine_desc *& & & & char *from = default_command_ & & & & setup_processor();//该函数其实就是调用第一阶段head.s中的函数& & & & //lookup_processor_type& & & & mdesc = setup_machine(machine_arch_type);& & & & //该函数其实就是调用第一阶段head.s中的函数lookup_machine_type& & & & //将查找到的对应的机器型号的信息保存在结构体mdesc中,mdesc结构体代码此处也贴出来:struct machine_desc {& & & & /*& & & &&&* Note! The first four elements are used& & & &&&* by assembler code in head-armv.S& & & &&&*/& & & & unsigned int& & & & & & & && & & & & & & & /* architecture number& & & & */& & & & unsigned int& & & & & & & & phys_& & & & /* start of physical io& & & & */& & & & unsigned int& & & & & & & & io_pg_& & & & /* byte offset for io & & & & & & & & & & & & & & & & & & & & & & & &&&* page tabe entry& & & & */ & & & & const char& & & & & & & & *& & & & & & & & /* architecture name& & & & */& & & & unsigned long& & & & & & & & boot_& & & & /* tagged list& & & & & & & & */ & & & & unsigned int& & & & & & & & video_& & & & /* start of video RAM& & & & */& & & & unsigned int& & & & & & & & video_& & & & /* end of video RAM& & & & */ & & & & unsigned int& & & & & & & & reserve_lp0 :1;& & & & /* never has lp0& & & & */& & & & unsigned int& & & & & & & & reserve_lp1 :1;& & & & /* never has lp1& & & & */& & & & unsigned int& & & & & & & & reserve_lp2 :1;& & & & /* never has lp2& & & & */& & & & unsigned int& & & & & & & & soft_reboot :1;& & & & /* soft reboot& & & & & & & & */& & & & void& & & & & & & & & & & & (*fixup)(struct machine_desc *,& & & & & & & & & & & & & & & & & & & &&&struct tag *, char **,& & & & & & & & & & & & & & & & & & & &&&struct meminfo *);& & & & void& & & & & & & & & & & & (*map_io)(void);/* IO mapping function& & & & */& & & & void& & & & & & & & & & & & (*init_irq)(void);& & & & struct sys_timer& & & & *& & & & & & & & /* system tick timer& & & & */& & & & void& & & & & & & & & & & & (*init_machine)(void);};看到了吧,其中就有成员unsigned long& & & & & & & & boot_& & & & /* tagged list& & & & & & & & */ 这里面方的就是存放参数的地址啊!!! & & & & & & & & machine_name = mdesc-&& &//给全局变量machine_name赋值 & & & & if (mdesc-&soft_reboot)& &//设置reboot方式,默认是硬启动& & & & & & & & reboot_setup(&s&); & & & & if (mdesc-&boot_params)& & & & & & & & tags = phys_to_virt(mdesc-&boot_params);//将参数的物理地址转化为虚拟地址 & & & & /*& & & &&&* If we have the old style parameters, convert them to& & & &&&* a tag list.& & & &&&*/& & & & if (tags-&hdr.tag != ATAG_CORE)& & & & & & & & convert_to_tag_list(tags);& & & & if (tags-&hdr.tag != ATAG_CORE)& & & & & & & & tags = (struct tag *)&init_//首先判断是不是正确的atag格式,如果是以前老版本的param_struct格式会首先将其转换成tag格式,如果转换以后还是不对,则使用默认的init_tags,这里判断的过程都是根据结构体第一个值是不是ATAG_CORE. & & & & if (mdesc-&fixup)& & & & & & & & mdesc-&fixup(mdesc, tags, &from, &meminfo); & & & & if (tags-&hdr.tag == ATAG_CORE) {& & & & & & & & if (meminfo.nr_banks != 0)& & & & & & & & & & & & squash_mem_tags(tags);& & & & & & & & parse_tags(tags);//这里要说明一下这里的tags实际上是一个tag的数组或者说队列,里面有多个tag结构体.贴出tag结构体定义如下:struct tag {& & & & struct tag_& && && && && && && && & struct tag_header {& & & & & && && && && && && && && && && && && && && & __u32& & & & & && && && && && && && && && && && && && && && &__u32& && && && && && && && && && && && && && && && && && &};& & & & union {& & & & & & & & struct tag_core& & & & & & & & & & & & & & & & struct tag_mem32& & & & & & & & & & & & struct tag_videotext& & & & & & & & & & & & struct tag_ramdisk& & & & & & & & & & & & struct tag_initrd& & & & & & & & & & & & struct tag_serialnr& & & & & & & & & & & & struct tag_revision& & & & //这些东西不就是U-BOOT中启动内核前设置的参数标记么& & & & & & & & struct tag_videolfb& & & & & & & & & & & & struct tag_cmdline& & & &
& & & & & & & & /*& & & & & & & &
* Acorn specific& & & & & & & &
*/& & & & & & & & struct tag_acorn& & & &
& & & & & & & & /*& & & & & & & &
* DC21285 specific& & & & & & & &
*/& & & & & & & & struct tag_memclk& & & & & & & & }}; & & & & }//这里首先判断fixup函数指针,这里一般为空,如果不为空就会地用fixup来重新修改memory map,meminfo这个结构体定义在arch/arm/include/asm/setup.h里面,描述了内存块的信息,内存块的个数,每个内存块的起始地址和大小,如果修改了memory map则需要从atag参数里面去掉bootloader传过来的的memory map信息,然后是保存一下atag,这个保存函数在这里实际上是空的,没有做任何操作,最后是对atag参数进行解析。进入parse_tags(tags);函数:static void __init parse_tags(const struct tag *t){& & & & for (; t-&hdr. t = tag_next(t))& & & & & & & & if (!parse_tag(t))& & & & & & & & & & & & printk(KERN_WARNING& & & & & & & & & & & & & & & & &Ignoring unrecognised tag 0x%08x\n&,& & & & & & & & & & & & & & & & t-&hdr.tag);}对tag数组中的每一个tag依次调用parse_tag(t)函数进行解析,进入parse_tag(t)函数:static int __init parse_tag(const struct tag *tag){& & & & extern struct tagtable __tagtable_begin, __tagtable_& & & & struct tagtable *t; & & & & for (t = &__tagtable_ t & &__tagtable_ t++)& & & & & & & & if (tag-&hdr.tag == t-&tag) {& & & & & & & & & & & & t-&parse(tag);& & & & & & & & & & & && & & & & & & & } & & & & return t & &__tagtable_}parse_tag(t)函数就是在段(此段在连接文件中规划)& & __tagtable_begin = .;& & & & & & & & & & & & *(.taglist.init)& & & & & & & & __tagtable_end = .;中依次比较,看看与段中的那个标记相同,如果找到就去调用标记对应的函数。在内核中搜索“*(.taglist.init)”找到#define __tag __used __attribute__((__section__(&.taglist.init&)))#define __tagtable(tag, fn) \static struct tagtable __tagtable_##fn __tag = { tag, fn }在内核中搜索__tagtable找到__tagtable(ATAG_MEM, parse_tag_mem32);__tagtable(ATAG_CMDLINE, parse_tag_cmdline);。。。。。。。。。。。。等定义,我们组要关心列出的这两个,ATAG_MEM与ATAG_CMDLINE这两个不正是UBOOT中定义的两个标记么。通过以上分析其实解析参数的过程就是这个样子的:通过宏__tagtable()将各种标记及处理该标记的函数对应起来,通过附加强制属性将其放到段& &__tagtable_begin = .;& & & & & & & & & & & & *(.taglist.init)& & & & & & & & __tagtable_end = .中,解析函数时就是通过标记好在该段中查找,如若找到标记号,就通过该标记号对应的函数做进一步处理。ATAG_MEM,对应的函数为:parse_tag_mem32,该函数实现功能就是:根据内存tag定义的内存起始地址、长度。在全局结构变量meminfo中增加内存的描述信息,以后内核就可以通过meminfo结构了解开发板的内存信息。ATAG_CMDLINE对应的函数为parse_tag_cmdline这个函数实现的功能主要是:简单的将命令行tag的内容复制到字符串default_command_line(set_arch函数开头定义了这个变量)中。 接着往下分析
& & & & init_mm.start_code = (unsigned long) &_& & & & init_mm.end_code& &= (unsigned long) &_& & & & init_mm.end_data& &= (unsigned long) &_& & & & init_mm.brk& & & && & = (unsigned long) &_// 这就就是对init_mm结构体进行赋值,具体不了解这些东西咋用的,但是就是将text和data段给赋值了& & & & memcpy(boot_command_line, from, COMMAND_LINE_SIZE);//将命令行信息复制给变量boot_command_line& & & & boot_command_line[COMMAND_LINE_SIZE-1] = '\0';& & & & parse_cmdline(cmdline_p, from);//扫描命令行参数,对其中的一些参数做先期处理,这些参数用__early_param定义,__early_param(&mem=&, early_mem);__early_param(&initrd=&, early_initrd);放到了段:& && &&&__early_begin = .;& & & & & & & & & & & & *(.early_param.init)& & & & & & & & __early_end = .;& && && && && &( 脚本文件中规划好的)中。(注意是命令行内部的参数处理,例如命令行参数中若含有“men=”字符串就会调用函数early_mem)& & & & paging_init(&meminfo, mdesc);//根据前面解析内存tag(arse_tag_mem32,该函数在全局结构变量meminfo中增加内存的描述信息)及setup_machine函数返回的mdesc。& & & & request_standard_resources(&meminfo, mdesc);好了,到此为止下面的就不分析了,对我们理解参数解析作用不大了,现在来总结一下内核解析参数,总的来说分两块,第一解析除命令行意外的标记,如内存标记等,命令行参数标记是单独拿出来解析的,而且还进行了先期处理。 #ifdef CONFIG_SMP& & & & smp_init_cpus();#endif & & & & cpu_init(); & & & & /*& & & &&&* Set up various architecture-specific pointers& & & &&&*/& & & & init_arch_irq = mdesc-&init_& & & & system_timer = mdesc-&& & & & init_machine = mdesc-&init_ #ifdef CONFIG_VT#if defined(CONFIG_VGA_CONSOLE)& & & & conswitchp = &vga_#elif defined(CONFIG_DUMMY_CONSOLE)& & & & conswitchp = &dummy_#endif#endif #if& & & & defined(CONFIG_KGDB)& & & & extern void __init early_trap_init(void);& & & & early_trap_init();#endif}回到函数start_kernel中接着往下看,下面还有两个与参数解析相关的函数。函数:setup_command_line(command_line);里面将boot_command_line内容复制给了saved_command_line将command_line内容复制给了static_command_line接着在函数start_kernel内往下看:函数parse_early_param();函数parse_args(&Booting kernel&, static_command_line, __start___param,& & & & & & & && & __stop___param - __start___param,& & & & & & & && & &unknown_bootoption);函数parse_args调用函数unknown_bootoption对命令行参数进行一一解析。怎么解析呢,跟进去,进入函数unknown_bootoption,调用了函数obsolete_checksetup,进入函数obsolete_checksetup,函数obsolete_checksetup实现的功能就是,将命令行中的参数在段& && &&&__setup_start = .;& & & & & & & & & & & & *(.init.setup)& & & & & & & & __setup_end = .;(在连接文件中已规划好)中进行一一比对,若找到了就调用参数所指定的函数进行处理。具体实现见如下分析:在内核中搜索.init.setup找到了:#define __setup_param(str, unique_id, fn, early)& & & & & & & & & & & & \& & & & static char __setup_str_##unique_id[] __initdata =& & & & \& & & & static struct obs_kernel_param __setup_##unique_id& & & & \& & & & & & & & __attribute_used__& & & & & & & & & & & & & & & & \& & & & & & & & __attribute__((__section__(&.init.setup&)))& & & & \& & & & & & & & __attribute__((aligned((sizeof(long)))))& & & & \& & & & & & & & = { __setup_str_##unique_id, fn, early }接着再内核中搜索“ __setup_param”找到了:如下的宏: #define __setup_null_param(str, unique_id)& & & & & & & & & & & & \& & & & __setup_param(str, unique_id, NULL, 0) #define __setup(str, fn)& & & & & & & & & & & & & & & & & & & & \& & & & __setup_param(str, fn, fn, 0)#define early_param(str, fn)& & & & & & & & & & & & & & & & & & & & \& & & & __setup_param(str, fn, fn, 1)继续跟进搜索“__setup”你会找到诸如一下这样的一系列的宏定义:__setup(&root=&, root_dev_setup);__setup(&rootflags=&, root_data_setup);__setup(&rootfstype=&, fs_names_setup);__setup(&rootdelay=&, root_delay_setup);__setup(&console=&, console_setup);绿色部分不正是我们命令行中的字符串么!!!!! 执行完函数parse_args后,命令行参数就解析完了,至此UBOOT传递过来的所有参数也就都解析完毕了。回到start_kernel函数中找到函数console_init();这也是个比较重要的函数,具体分析见韦东山老师编写的《嵌入式Linux应用开发完全手册》:P325页。start_kernel函数最后调用到了rest_init();函数,rest_init();函数又调用到了函数kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);相当于执行函数:kernel_init,函数kernel_init又调用到了函数prepare_namespace();,函数prepare_namespace();最终调用到了mount_root();挂载根文件系统,挂载成功后返回到函数prepare_namespace()中,又调用到了init_post函数,在函数init_post中调用run_init_process(&/sbin/init&);& & & & run_init_process(&/etc/init&);& & & & run_init_process(&/bin/init&);& & & & run_init_process(&/bin/sh&);开始执行应用程序。
Powered by
供应链服务
商务及广告合作
Jeffery Guo
关注我们的微信
供应链服务 PCB/IC/PCBA
下载发烧友APP
版权所有 (C) 深圳华强聚丰电子科技有限公司理解start_kernel中函数语句的作用 - CSDN博客
理解start_kernel中函数语句的作用
asmlinkage void __init start_kernel(void)
&& &char * command_
&& &extern const struct kernel_param __start___param[], __stop___param[];
&& &/*这两个变量为地址指针,指向内核启动参数处理相关结构体段在内存中的位置(虚拟地址)。
&&& 声明传入参数的外部参数对于ARM平台,位于 include\asm-generic\vmlinux.lds.h*/ &
&&&&&&& * Need to run as early as possible, to initialize the
&&&&&&& * lockdep hash:
&&&&&&&&& lockdep是一个内核调试模块,用来检查内核互斥机制(尤其是自旋锁)潜在的死锁问题。
&&&&&&& */
&& &lockdep_init();//初始化内核依赖的关系表,初始化hash表 &
&& &smp_setup_processor_id();//获取当前CPU,单处理器为空
&& &debug_objects_early_init();//对调试对象进行早期的初始化,其实就是HASH锁和静态对象池进行初始化 &
&& &&&& /*
&&& &&& &&&&& * Set up the the initial canary ASAP:
&&&&&&&&&&&&&& 初始化栈canary值
&&&&&&&&&&&&&& canary值的是用于防止栈溢出攻击的堆栈的保护字 。
&&&&&&&&&&& */
&& &boot_init_stack_canary();
&& & /*1.cgroup: 它的全称为control group.即一组进程的行为控制. &
&&&&&&&&&& 2.该函数主要是做数据结构和其中链表的初始化 &
&&&&&&&&&& 3.参考资料: Linux cgroup机制分析之框架分析
&&&&&&&& */ &
&& &cgroup_init_early();
&& &local_irq_disable();//关闭系统总中断(底层调用汇编指令)
&& &early_boot_irqs_disabled =
&* Interrupts are still disabled. Do necessary setups, then
&* enable them
&&& &boot_cpu_init();//1.激活当前CPU(在内核全局变量中将当前CPU的状态设为激活状态) &
&& &&& &page_address_init();//高端内存相关,未定义高端内存的话为空函数 &
&&&&&&& pr_notice(&%s&, linux_banner);
&& &&& &/*1.内核构架相关初始化函数,可以说是非常重要的一个初始化步骤。
&& &&& &其中包含了处理器相关参数的初始化、内核启动参数(tagged list)的获取和前期处理、
&& &&& &内存子系统的早期的初始化(bootmem分配器)。 主要完成了4个方面的工作,一个就是取得MACHINE和PROCESSOR的信息然或将他们赋值
&& &&& &给kernel相应的全局变量,然后呢是对boot_command_line和tags接行解析,再然后呢就是
&& &&& &memory、cach的初始化,最后是为kernel的后续运行请求资源″**/ &
&& &&& &setup_arch(&command_line); &
&& &&& &/*1.初始化代表内核本身内
&& &&& &存使用的管理结构体init_mm。 &
&& &&& &2.ps:每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核的mm_struct,其中: &
&& &&& &3.设置成员变量* mmap指向自己,意味着内核只有一个内存管理结构; &
&& &&& &4.设置* pgd=swapper_pg_dir,swapper_pg_dir是内核的页目录(在arm体系结构有16k, 所以init_mm定义了整个kernel的内存空间)。 &
&& &&& &5.这些内容涉及到内存管理子系统*/ &
&& &&& &mm_init_owner(&init_mm, &init_task); &
&& &&& &mm_init_cpumask(&init_mm);//初始化CPU屏蔽字 &
&& &&& &/*1.对cmdline进行备份和保存:保存未改变的comand_line到字符数组static_command_line[] 中。保存& boot_command_line到字符数组saved_command_line[]中
&& &&& &setup_command_line(command_line);
&& &&& &/*如果没有定义CONFIG_SMP宏,则这个函数为空函数。如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间。
&& &&& &*/ &
&& &&& &/*下面三段1.针对SMP处理器的内存初始化函数,如果不是SMP系统则都为空函数。 (arm为空) &
&& &&& &2.他们的目的是给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间并为boot CPU设置一些数据。 &
&& &&& &3.在SMP系统中,在引导过程中使用的CPU称为boot CPU*/
&& &setup_nr_cpu_ids();
&& &setup_per_cpu_areas();
&& &smp_prepare_boot_cpu();&& &/* arch-specific boot-cpu hooks */
&& &build_all_zonelists(NULL, NULL);//& 建立系统内存页区(zone)链表
&& &page_alloc_init();//内存页初始化
&& &pr_notice(&Kernel command line: %s\n&, boot_command_line);
&& &parse_early_param();//& 解析早期格式的内核参数 &
&& &&& &/*函数对Linux启动命令行参数进行在分析和处理,
&& &&& &当不能够识别前面的命令时,所调用的函数。*/ &
&& &parse_args(&Booting kernel&, static_command_line, __start___param,
&& &&& &&& __stop___param - __start___param,
&& &&& &&& -1, -1, &unknown_bootoption);
&& &jump_label_init();
&& & * These use large bootmem allocations and must precede
&& & * kmem_cache_init()
&& &setup_log_buf(0);
&& & /*初始化hash表,以便于从进程的PID获得对应的进程描述指针,按照开发办上的物理内存初始化pid hash表
&& &&& &*/
&& &pidhash_init();
&& &vfs_caches_init_early();//建立节点哈希表和数据缓冲哈希表
&& &sort_main_extable();//对异常处理函数进行排序
&& &trap_init();//初始化硬件中断
&& &mm_init();//&&& Set up kernel memory allocators&&&& 建立了内核的内存分配器& &
&& & * 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.
&& &sched_init();//核心进程调度器初始化
&& & * Disable preemption - early bootup scheduling is extremely
&& & * fragile until we cpu_idle() for the first time.
&& &preempt_disable();//禁止调度
&& & //& 先检查中断是否已经打开,若打开,输出信息后则关闭中断。
&& &if (WARN(!irqs_disabled(), &Interrupts were enabled *very* early, fixing it\n&))
&& &&& &local_irq_disable();
&& &idr_init_cache();//创建idr缓冲区 &
&& &rcu_init();//互斥访问机制
&& &tick_nohz_init();
&& &context_tracking_init();
&& &radix_tree_init();
&& &/* init some links before init_ISA_irqs() */
&& &early_irq_init();
&& &init_IRQ();//使用alpha_mv结构和entry.S入口初始化系统IRQ
&& &tick_init();
&& &init_timers();//定时器初始化
&& &hrtimers_init();//高精度时钟初始化
&& &softirq_init();//软中断初始化
&& &timekeeping_init();//&& 初始化资源和普通计时器
&& &time_init();//时间、定时器初始化(包括读取CMOS时钟、估测主频、初始化定时器中断等)
&& &sched_clock_postinit();
&& &perf_event_init();
&& &profile_init();//&& 对内核的一个性能测试工具profile进行初始化。
&& &call_function_init();
&& &WARN(!irqs_disabled(), &Interrupts were enabled early\n&);
&& &early_boot_irqs_disabled =
&& &local_irq_enable();//使能中断
&& &kmem_cache_init_late();//kmem_cache_init_late的目的就在于完善slab分配器的缓存机制.
&& & * 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.
&& &console_init();//初始化控制台以显示printk的内容 &
&& &if (panic_later)
&& &&& &panic(&Too many boot %s vars at `%s'&, panic_later,
&& &&& &&&&&& panic_param);
&& &lockdep_info();//&& 如果定义了CONFIG_LOCKDEP宏,那么就打印锁依赖信息,否则什么也不做
&& & * Need to run this when irqs are enabled, because it wants
&& & * to self-test [hard/soft]-irqs on/off lock inversion bugs
&& & * too:
&& &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) {
&& &&& &pr_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;
&& &page_cgroup_init();
&& &debug_objects_mem_init();
&& &kmemleak_init();
&& &setup_per_cpu_pageset();
&& &numa_policy_init();
&& &if (late_time_init)
&& &&& &late_time_init();
&& &sched_clock_init();
&& &calibrate_delay();//延迟校准(获得时钟jiffies与CPU主频ticks的延迟)
&& &pidmap_init();//进程号位图初始化,一般用一个錺age来表示所有进程的錺id占用情况 &
&& &anon_vma_init();//& 匿名虚拟内存域( anonymous VMA)初始化 &
&& &acpi_early_init();
#ifdef CONFIG_X86
&& &if (efi_enabled(EFI_RUNTIME_SERVICES))
&& &&& &efi_enter_virtual_mode();
#ifdef CONFIG_X86_ESPFIX64
&& &/* Should be run before the first non-init thread is created */
&& &init_espfix_bsp();
&& &thread_info_cache_init();//获取thread_info缓存空间,大部分构架为空函数(包括ARM &
&& &cred_init();//任务信用系统初始化。详见:Documentation/credentials.txt &
&& &fork_init(totalram_pages);//进程创建机制初始化。为内核&task_struct&分配空间,计算最大任务数。 &
&& &proc_caches_init();//初始化进程创建机制所需的其他数据结构,为其申请空间。
&& &buffer_init();//块设备读写缓冲区初始化(同时创建&buffer_head&cache用户加速访问)
&& &key_init();//内核密钥管理系统初始化
&& &security_init();//内核安全框架初始化
&& &dbg_late_init();
&& &vfs_caches_init(totalram_pages);//虚拟文件系统(VFS)缓存初始化 &
&& &signals_init();//信号管理系统初始化
&& &/* rootfs populating might need page-writeback */
&& &page_writeback_init();//页写回机制初始化
#ifdef CONFIG_PROC_FS
&& &proc_root_init();//proc文件系统初始化
&& &cgroup_init();//control group正式初始化 &
&& &cpuset_init();//CPUSET初始化。 参考资料:《多核心計算環境—NUMA與CPUSET簡介》
&& &taskstats_init_early();//任务状态早期初始化函数:为结构体获取高速缓存,并初始化互斥机制。
&& &delayacct_init();//任务延迟初始化
&& &check_bugs();//检查CPU BUG的函数,通过软件规避BUG
&& &sfi_init_late();//功能跟踪调试机制初始化,ftrace 是 function trace 的简称
&& &if (efi_enabled(EFI_RUNTIME_SERVICES)) {
&& &&& &efi_late_init();
&& &&& &efi_free_boot_services();
&& &ftrace_init();
&& &/* Do the rest non-__init'ed, we're now alive */
&& &rest_init();// 虽然从名字上来说是剩余的初始化。但是这个函数中的初始化包含了很多的内容 &
本文已收录于以下专栏:
相关文章推荐
从某种意义上,函数start_kernel就好像一般可执行程序中的主函数main,系统进入这个函数之前已经进行了一些最低限度的初始化,再往前研究就涉及很多硬件相关及编程语言了,这里是较高层次的初始化,...
parse_args(&Bootingkernel&, static_command_line, __start___param,
__stop___param - __start___param...
在分析这段代码之前,我们必须先找到汇编代码的入口位置,也就是Bootloader启动linux时所跳转到的地址。内核一般被压缩后保存到FLASH上的,在Bootloader启动Linux之前必须先解压...
/** HACK ALERT! This is early. We're enabling the console before* we've done PCI setups etc, and con...
linux启动流程(从start_kernel中的rest_init函数到init进程(1))
    在init/main.c文件中有个函数叫start_kernel,它是用来启动内核的主函数,我...
Linux内核课第三周作业。本文在云课堂中实验楼完成。
唐国泽 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc./course/USTC-...
asmlinkage void __init start_kernel(void)
char * command_
extern struct kernel_param __star...
《Linux内核分析》MOOC课程http://mooc./course/USTC- 
内核init目录下的main.c文件...
numa_policy_init();这个函数是初始化NUMA的内存访问策略。所谓NUMA,它是NonUniform Memory AccessAchitecture的缩写,主要用来提高多个CPU访问...
紧跟参数后面的,就是调用函数smp_setup_processor_id()了,这个函数主要作用是获取当前正在执行初始化的处理器ID。如果仔细地阅读完初始化函数start_kernel,就会发现里面还...
他的最新文章
讲师:吴岸城
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 虚函数的实现 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信