Linux 2.6.28 – 内核启动分析(一)

Linux 2.6.28内核启动分析(一)

首先声明,我也是初学Linux内核,因为有这方面的基础,所以打算学习一下内核,学习Linux要讲究方法,坚持固然重要,但是也要讲究方法,我个人认为,学习内核是一个长期的过程,如果指望一两天或者一两个星期就行弄清内核(除了你很杰出+有学习内核的天赋),那么我想说,你不适合学习Linux内核,所谓方法,就是分清主次矛盾,分清主干和枝叶,这样才能快速的入门。

下面我就从Linux的“main函数”开始我的Linux的内核之旅,所谓Linux的“main函数”,就是指从这个函数开始进入了和硬件体系结构无关的内核部分,这也就意味着进入了内核的初始化过程,Linux的内核初始化过程由start_kernel开始,直到第一个用户进程init结束,调用了一系列的初始化函数,其中每一步都将我们带入更大量、更复杂的代码和结构中!如果我们不分清主干和枝叶的学习,那么我们根本无法理解内核的原理!即使你坚持下来了,那么你也只是好好好好好….. 好好好好好的学习了一下C语言!因此,我的学习主线是:

Start_kernel();  –> rest_ini();  -> kernel_init();  -> init_post();   ….. 接下来我们就开始我Linux内核分析之旅!

asmlinkage void __init start_kernel(void)

{

       char * command_line;

---------------------------------------------------------------------------------------------------------------------------------

这两个外部变量,是内核编译剧本定位的内核参数肇端地址

       extern struct kernel_param __start___param[], __stop___param[];

---------------------------------------------------------------------------------------------------------------------------------

指定当前的cpu的逻辑号,这个函数对应于对称多处理器的设置,当系统中只有一个cpu的情况,此函数为空,什么也不做

       smp_setup_processor_id();

---------------------------------------------------------------------------------------------------------------------------------

unwind_init();   

---------------------------------------------------------------------------------------------------------------------------------

初始化内核依赖的关系表。一些体系结构拥有自己的start_kernel()代码回去调用lockdep_init(),与此同时也会从start_kernel()中调用lockdep_init()仅仅是为了初始化hash

lockdep_init();        

---------------------------------------------------------------------------------------------------------------------------------

       debug_objects_early_init();

       cgroup_init_early();

---------------------------------------------------------------------------------------------------------------------------------

关闭当前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();

---------------------------------------------------------------------------------------------------------------------------------

获得大内核锁,用于锁定整个内核,大内核锁的上锁其实就是对当前进程的lock_depth1,并判断加1后,如果等于0则为第一次上锁,需对当前结构的thread_infopreempt_count也加1阻止抢占,大内核锁的特点之一就是可以递归调用,也即可以重复上锁,只要解锁次数与上锁次数相同即可。实现方式就是lock_depth值累计上锁次数,但preempt_count值并不需累计,所以同一进程的第二次上锁时只对lock_depth累计而不对preempt_count累计。

       lock_kernel();

---------------------------------------------------------------------------------------------------------------------------------

如果没有定义   CONFIG_GENERIC_CLOCKEVENTS宏定义,则这个函数为空函数,如果定义了这个宏,这执行初始化 tick控制功能,注册clockevents的框架。

       tick_init();

---------------------------------------------------------------------------------------------------------------------------------

对于CPU核的系统来说,设置第一个CPU核为活跃  CPU核。对于单CPU核系统来说,设置CPU核为活跃  CPU

       boot_cpu_init();

---------------------------------------------------------------------------------------------------------------------------------

当定义了CONFIG_HIGHMEM 宏,并且没有定义   WANT_PAGE_VIRTUAL 宏时,非空函数。其他情况为空函数。注意:ARM9不支持高端地址(大于896M),一般的嵌入式产品也不会用高端地址,所以,在ARM体系结构下,此函数为空!

       page_address_init();

---------------------------------------------------------------------------------------------------------------------------------

输出打印版本信息

       printk(KERN_NOTICE);

       printk(linux_banner);

---------------------------------------------------------------------------------------------------------------------------------

每种体系结构都有自己的  setup_arch()函数,这些是体系结构相关的。如何确定编译那个体系结构的setup_arch()函数呢?

主要由linux源码树顶层MakefileARCH变量来决定的。 

  例如: ARM体系结构的。

  SUBARCH :=arm   

  ARCH        ?= $(SUBARCH)

该函数在所在的路劲为 /arm/kernel/setup.c,其中那个command_line就是有bootloader传过来的!这个函数是个重量级的函数,大家不可忽视!该函数完成体系结构相关的初始化,内核移植的过程一般也就到此函数为止了,其余的就只是一些相关的外设驱动。

       setup_arch(&command_line);

---------------------------------------------------------------------------------------------------------------------------------

       mm_init_owner(&init_mm, &init_task);

---------------------------------------------------------------------------------------------------------------------------------

保存未改变的comand_line到字符数组static_command_line[] 中。保存  boot_command_line到字符数组saved_command_line[]中

       setup_command_line(command_line);

---------------------------------------------------------------------------------------------------------------------------------

空函数

       unwind_setup();

---------------------------------------------------------------------------------------------------------------------------------

如果没有定义CONFIG_SMP宏,则这个函数为空函数。如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPUper_cpu变量申请空间。

       setup_per_cpu_areas();

---------------------------------------------------------------------------------------------------------------------------------

       setup_nr_cpu_ids();

---------------------------------------------------------------------------------------------------------------------------------

       smp_prepare_boot_cpu();   /* arch-specific boot-cpu hooks */

       /*

        * 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标志,所以还会继续完成内核初始化剩下的事情。

sched_init();

---------------------------------------------------------------------------------------------------------------------------------

进制内核的抢占。使当前进程的   struct thread_info结构  preempt_count成员的值增加1

       preempt_disable();

---------------------------------------------------------------------------------------------------------------------------------

建立系统内存页区(zone)链表

       build_all_zonelists();

--------------------------------------------------------------------------------------------------------------------------------

       page_alloc_init();

--------------------------------------------------------------------------------------------------------------------------------

打印Linux启动命令行参数

printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);

--------------------------------------------------------------------------------------------------------------------------------

解析早期格式的内核参数

parse_early_param();

--------------------------------------------------------------------------------------------------------------------------------

该函数对Linux启动命令行参数进行在分析和处理,__stop___param - __start___param这两个参数所定义的位置在 /arch/arm/kernel/vmlinux.lds中定义,最后的那参数的作用是,当不能够识别前面的命令时,所调用的函数。

parse_args("Booting kernel", static_command_line, __start___param,

                 __stop___param - __start___param,

                 &unknown_bootoption);

--------------------------------------------------------------------------------------------------------------------------------

先检查中断是否已经打开,若打开,输出信息后则关闭中断。

if (!irqs_disabled()) {

              printk(KERN_WARNING "start_kernel(): bug: interrupts were "

                            "enabled *very* early, fixing it/n");

              local_irq_disable();

       }

--------------------------------------------------------------------------------------------------------------------------------对内核建立的异常处理向量表进行堆排序。

sort_main_extable();

--------------------------------------------------------------------------------------------------------------------------------

设置CPU的异常处理函数。

trap_init();

--------------------------------------------------------------------------------------------------------------------------------

       rcu_init();

--------------------------------------------------------------------------------------------------------------------------------

初始化IRQ中断和终端描述符。初始化系统中支持的最大可能的中断描述结构struct irqdesc变量数组irq_desc[NR_IRQS],把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量bad_irq_desc,并初始化该中断的链表表头成员结构变量pend.

init_IRQ();

--------------------------------------------------------------------------------------------------------------------------------

初始化hash表,以便于从进程的PID获得对应的进程描述指针,按照开发办上的物理内存初始化pid hash

pidhash_init();

--------------------------------------------------------------------------------------------------------------------------------

初始化定时器Timer相关的数据结构。

init_timers();

--------------------------------------------------------------------------------------------------------------------------------

对高精度时钟进行初始化。

hrtimers_init();

--------------------------------------------------------------------------------------------------------------------------------

初始化软中断。

softirq_init();

--------------------------------------------------------------------------------------------------------------------------------

初始化资源和普通计时器

timekeeping_init();

--------------------------------------------------------------------------------------------------------------------------------

初始化系统时间,检查系统定时器描述结构struct sys_timer全局变量system_timer是否为空,如果为空将其指向dummy_gettimeoffset()函数。

time_init();

--------------------------------------------------------------------------------------------------------------------------------

       sched_clock_init();

--------------------------------------------------------------------------------------------------------------------------------

对内核的一个性能测试工具profile进行初始化。

profile_init();

--------------------------------------------------------------------------------------------------------------------------------

       if (!irqs_disabled())

              printk("start_kernel(): bug: interrupts were enabled early/n");

--------------------------------------------------------------------------------------------------------------------------------

       early_boot_irqs_on();

--------------------------------------------------------------------------------------------------------------------------------

使能IRQ中断

local_irq_enable();

--------------------------------------------------------------------------------------------------------------------------------

初始化控制台以显示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:

        */

上面的黑立体字是内核的作者的注释,很重要!

       locking_selftest();

 

转载的

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值