linux内核启动第二阶段分析-2.6.36

时间:2021-09-09 16:45:58

 内核源码下载地址:http://www.cn.kernel.org/pub/linux/kernel/v2.6/ linux-2.6.36.tar.bz2


536 asmlinkage void __init start_kernel(void)
537 {
538         char * command_line;
539         extern const struct kernel_param __start___param[], __stop___param[];这两个外部变量,定义了核的参数数据结构
540
541         smp_setup_processor_id();/*设置SMP多核的CPU核的ID号,单核不进行任何操作,我们不关心。但是如果有多个CPU的时候那么它就 返回在启动的时候的那个CPU的号  */
542
543         /*
544          * Need to run as early as possible, to initialize the
545          * lockdep hash:
546          */
547         lockdep_init();/*初始化核依赖关系哈希表。*/
548         debug_objects_early_init();//初始化debug kernel相关
549
550         /*
551          * Set up the the initial canary ASAP:
552          */
553         boot_init_stack_canary();//stack_canary的是带防止栈溢出攻击保护的堆栈。详见http://www.cloud-sec.org/CC_STACKPROTECTOR_patch_Analysis
554
555         cgroup_init_early();//Cgroup初始化,Cgroup是近代linux kernel出现的.它为进程和其后续的子进程提供了一种性能控制机制,详见:http://linux.chinaunix.net/techdoc/net/2008/12/23/1054425.shtml
556
557         local_irq_disable();/*关闭当前CPU的中断*/
558         early_boot_irqs_off();    通过一个静态全局变量    early_boot_irqs_enabled来帮助我们调试代码,通过这个标记可以帮助我们知道是否在“early bootup code”,也可以通过这个标志警告是否有无效的中断打开。和 early_boot_irqs_on()函数配置使用,参考下面。
559         early_init_irq_lock_class(); 每一个中断都有一个IRQ描述符 (struct irq_desc)来进行描述。这个函数的主要作用是设置所有的IRQ描述符 (struct irq_desc)的锁是统一的锁,还是每一个    IRQ描述符 (struct irq_desc)都有一个小锁。
560
561 /*
562  * Interrupts are still disabled. Do necessary setups, then
563  * enable them
564  */
565         tick_init();如果没有定义   CONFIG_GENERIC_CLOCKEVENTS宏定义,则这个函数为空函数,如果定义了这个宏,这执行初始化  tick控制功能,注册clockevents的框架。
566         boot_cpu_init();对于  CPU核的系统来说,设置第一个CPU核为活跃CPU核。对于单CPU核系统来说,设置CPU核为活跃CPU核。
567         page_address_init(); //ARM部分,没有支持高端内存相关代码,空函数。或当定义了CONFIG_HIGHMEM 宏,并且没有定义WANT_PAGE_VIRTUAL宏时,非空函数。 其他情况为空函数。
568         printk(KERN_NOTICE "%s", linux_banner); 将linux_banner的内容打印到log_buf缓冲区中。
569         setup_arch(&command_line);//函数原型在arch/arm/kernel/setup.c中
根据处理器、硬件平台具体型号设置系统。解析Linux系统命令行,设置0号进程(swapper进程)的内存描述结构init_mm,系统内存管理初始化,统计并注册系统各种资源,其他项目的初始化。
570         mm_init_owner(&init_mm, &init_task);
571         setup_command_line(command_line);保存未改变的  comand_line 到字符数组  static_command_line[] 中。保存  boot_command_line到字符数组saved_command_line[中。
572         setup_nr_cpu_ids();
573         setup_per_cpu_areas(); //为系统中每个处理器的per_cpu变量申请空间。如果没有定义  CONFIG_SMP宏,则这个函数为空函数。如果定义了 CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,并拷贝   .data.percpu段的数据。
574         smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
575
576         build_all_zonelists(NULL); 建立系统内存页区(zone)链表。建立各个节点的管理区的  zonelist,便于分配内存的   fallback使用。这个链表的作用: 这个链表是为了在一个分配不能够满足时可以考察下一个管理区来设置了。在考察结束时,分配将从  ZONE_HIGHMEM回退到  ZONE_NORMAL,在分配时从  ZONE_NORMAL退回到  ZONE_DMA就不会回退了。
577         page_alloc_init(); 
578
579         printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
580         parse_early_param(); 解析早期格式内核参数。
581         parse_args("Booting kernel", static_command_line, __start___param,
582                    __stop___param - __start___param,
583                    &unknown_bootoption);解析新格式内核参数。
584         /*
585          * These use large bootmem allocations and must precede
586          * kmem_cache_init()
587          */
588         pidhash_init();设置系统中每种pid hash表中的hash链表数的移位值全局变量pidhash_shift,将pidhash_shift设置为min(12);分别为每种hash表的连续hash链表表头结构空间申请内存,把申请到的内存虚拟基址分别传给pid_hash[n](n=0~3),并将每种hash表中的每个hash链表表头结构struct hlist_head中的first成员指针设置成NULL
589         vfs_caches_init_early();
590         sort_main_extable(); 这个函数对内核建立的异常处理调用函数表(exception table) 根据异常的向量号进行堆排序。将放在__start__ex_table到__stop__ex_table之间的*(__ex_table)区中的struct exception_table_entry型全局结构变量按insn成员变量值从小到大排序,即将可能导致缺页异常的指令按其指令二进制代码值从小到大排序。
591         trap_init();把放在.Lcvectors处的系统8个意外的入口跳转指令搬到高端中断向量0xffff0000 处,再将从__stubs_start到__stubs_end之间的各种意外初始处理代码搬到0xffff0200处。刷新0xffff0000处1页范围的指令cache,将DOMAIN_USER的访问权限由DOMAIN_MANAGER权限改设置成DOMAIN_CLIENT权限。设置  CPU的异常处理函数,TLB重填,cache出错,还有通用异常处理表的初始化。
592         mm_init();该函数执行完后不能再用像alloc_bootmem()、alloc_bootmem_low()、alloc_bootmem_pages()等申请低端内存的函数来申请内存,也就不能申请大块的连续物理内存了。
593         /*
594          * Set up the scheduler prior starting any interrupts (such as the
595          * timer interrupt). Full topology setup happens at smp_init()
596          * time - but meanwhile we still have a functioning scheduler.
597          */
598         sched_init(); 核心进程调度器初始化,调度器的初始化优先于任何中断的建立(包括  timer中断)。并且初始化进程0,即idle进程,但是并没有设置idle进程的  NEED_RESCHED标志,以完成内核剩余的启动部分。初始化每个处理器的可运行进程队列,设置系统初始化进程即0号进程。
599         /*
600          * Disable preemption - early bootup scheduling is extremely
601          * fragile until we cpu_idle() for the first time.
602          */
603         preempt_disable(); 进制内核的抢占。使当前进程的   struct thread_info结构  preempt_count成员的值增加1。
604         if (!irqs_disabled()) {
605                 printk(KERN_WARNING "start_kernel(): bug: interrupts were "
606                                 "enabled *very* early, fixing it\n");
607                 local_irq_disable();
608         }
609         rcu_init();初始化当前CPU的读、复制、更新数据结构(struct rcu_data)全局变量per_cpu_rcu_data和per_cpu_rcu_bh_data。
610         radix_tree_init();
611         /* init some links before init_ISA_irqs() */
612         early_irq_init();
613         init_IRQ();初始化系统中支持的最大可能中断数的中断描述结构struct irqdesc变量数组irq_desc[NR_IRQS],把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量 bad_irq_desc,并初始化该中断的连表表头成员结构变量pend.
614         prio_tree_init();初始化无符号长整型全局数组index_bits_to_maxindex[BITS_PER_LONG]的每个组员,将每个组员 index_bits_to_maxindex[n]设置成-1,将最后的index_bits_to_maxindex[BITS_PER_LONG- 1]设置成~0UL。
615         init_timers();初始化当前出处理器的时间向量基本结构struct tvec_t_base_s全局变量per_cpu_tvec_bases,初始化per_cpu_tvec_bases的自旋锁成员变量lock。
616         hrtimers_init();
617         softirq_init();设置系统小任务软件中断行为函数描述结构变量softirq_vec[TASKLET_SOFTIRQ(=6)],将softirq_vec[6]的行动函数指针action指向tasklet_action()函数,参数指针设置为NULL.
618         timekeeping_init();
619         time_init();检查系统定时器描述结构struct sys_timer全局变量system_timer是否为空,如果是将其指向dummy_gettimeoffset()函数。
620         profile_init();对系统剖析作相关初始化,系统剖析用于系统调用。
621         if (!irqs_disabled())
622                 printk(KERN_CRIT "start_kernel(): bug: interrupts were "
623                                  "enabled early\n");
624         early_boot_irqs_on();
625         local_irq_enable();将处理器的当前系统状态寄存器CPSR的第7位清0,使能IRQ中断。
626
627         /* Interrupts are enabled now so all GFP allocations are safe. */
628         gfp_allowed_mask = __GFP_BITS_MASK;
629
630         kmem_cache_init_late();执行高速缓存内存管理即slab分配器相关初始化。
631
632         /*
633          * HACK ALERT! This is early. We're enabling the console before
634          * we've done PCI setups etc, and console_init() must be aware of
635          * this. But we do want output early, in case something goes wrong.
636          */
637         console_init();初始化系统的控制台结构,该函数执行后调用printk()函数将log_buf中符合打印级别要求的系统信息打印到控制台上。
638         if (panic_later)  如果这个panic_later字符串已经设置了,则停止系统的启动。
639                 panic(panic_later, panic_param);
640
641         lockdep_info();如果没有定义LOCKDEP这个宏,则这个lockdep_info() 函数就为空函数。
642
643         /*
644          * Need to run this when irqs are enabled, because it wants
645          * to self-test [hard/soft]-irqs on/off lock inversion bugs
646          * too:
647          */
648         locking_selftest();如果没有定义   CONFIG_DEBUG_LOCKING_API_SELFTESTS 这个宏,则这个  locking_selftest() 函数为空函数。
649
650 #ifdef CONFIG_BLK_DEV_INITRD
651         if (initrd_start && !initrd_below_start_ok &&
652             page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
653                 printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
654                     "disabling it.\n",
655                     page_to_pfn(virt_to_page((void *)initrd_start)),
656                     min_low_pfn);
657                 initrd_start = 0;
658         }
659 #endif
660         page_cgroup_init();
661         enable_debug_pagealloc();
662         kmemleak_init();
663         debug_objects_mem_init();
664         idr_init_cache();
665         setup_per_cpu_pageset();
666         numa_policy_init();
667         if (late_time_init)
668                 late_time_init();
669         sched_clock_init();
670         calibrate_delay();计算机系统的BogMIPS数值,即处理器每秒钟执行的指令数。
671         pidmap_init();
672         anon_vma_init(); 该函数调用kmem_cache_create()函数,为匿名虚拟内存区域链表结构struct anon_vma创建高速缓存内存描述结构kmem_cache_t变量,为该变量命名为“anon_vma",其对象的构造函数指针指向void anon_vma_ctor(void *data,kmem_cache_t *cachep,unsigned long flags)函数,析构函数指针空,将创建的kmem_cache_t结构变量地址传给全局指针anon_vma_chachep。
673 #ifdef CONFIG_X86
674         if (efi_enabled)
675                 efi_enter_virtual_mode();
676 #endif
677         thread_info_cache_init();
678         cred_init();
679         fork_init(totalram_pages);执行进程创建相关初始化。
680         proc_caches_init();
681         buffer_init();调用 kmem_cache_create("buffer_head", sizeof(struct buffer_head), 0, SLAB_PANIC, init_buffer_head, NULL)函数为缓冲区描述结构struct buffer_head创建高速缓存内存描述结构kmem_cache_t变量。
682         key_init();
683         security_init();打印”安全架构v1.0.0被初始化“。如果没有定义系统哑元安全操作函数组结构全局变量dummy_security_ops,打印错误信息,返回I/O错误。
684         dbg_late_init();
685         vfs_caches_init(totalram_pages);
686         signals_init();调用kmem_cache_create("sigqueue", sizeof(struct sigqueue), __alignof__(struct sigqueue), SLAB_PANIC, NULL, NULL)函数为信号队列结构struct sigqueue创建高速缓存内存描述结构kmem_cache_t变量,名字叫”sigqueue“,不要求其对象按处理器硬件cache line大小对齐,没有定义其对象的构造和析构函数,将创建号的kmem_cache_t结构变量的地址传给全局指针sigqueue_cachep。
687         /* rootfs populating might need page-writeback */
688         page_writeback_init();统计系统中所有内存节点的通用(NORMAL)内存页区中高页数水印值页数之外的额外内存总页数之和传给buffer_pages。
689 #ifdef CONFIG_PROC_FS
690         proc_root_init();只有在系统支持proc文件系统即配置了CONFIG_PROC_FS选项时才被调用。
691 #endif
692         cgroup_init();
693         cpuset_init();
694         taskstats_init_early();
695         delayacct_init();
696
697         check_bugs();
698
699         acpi_early_init(); /* before LAPIC and SMP init */
700         sfi_init_late();
701
702         ftrace_init();
703
704         /* Do the rest non-__init'ed, we're now alive */
705         rest_init();该函数创建init()内核进程即1号进程,然后是系统启动进程即0号进程空闲。
706 }