首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

ARM多核引导过程(4)

ARM多核引导过程(4)

h、用函数setup_per_cpu_areas()设置多处理器环境,这个函数确定单个CPU所需要的内存大小,并分配和初始化4个核分别所需要的内存,这样一来,每一个CPU有         了自己的区域放置数据;(init/main.c)     i、用函数smp_prepare_boot_cpu()来允许正在引导的处理器(CPU0)访问自己的初始化过的数据;(arch/arm/kernel/smp.c);      j、用函数sched_init() (kernel/sched.c)设置Linux调度器;         1)为每一个cpu相应的数据初始化一个运行队列;         2)用函数init_idle(current, smp_processor_id())为cpu0 fork一个idle线程;     k、用函数build_all_zonelists() (mm/page_alloc.c)初始化内存区域:包括DMA, normal, high三个区;     l、用函数 parse_early_param() (init/main.c) 和函数 parse_args() (kernel/params.c)解析传递过来的命令行参数列表;     m、初始化中断表、GIC、异常处理向量表(用函数init_IRQ() (arch/arm/kernel/irq.c) 和函数 trap_init() (arch/arm/kernel/traps.c)),并为每一个        中断分配CPU亲和力值;     n、用函数softirq_init() (kernel/softirq.c)引导CPU(这里是CPU0)能接受由tasklet传来的通知;     o、初始化并运行系统timer,用函数time_init() (arch/arm/kernel/time.c);     p、使能CPU0的本地中断,用函数local_irq_enable() (include/linux/irqflags.h);     q、初始化显示终端,用函数console_init() (drivers/char/tty_io.c);     r、找出所有内存区域的free的内存页总数,用函数mem_init() (arch/arm/mm/init.c);     s、初始化内存分配器,用函数kmem_cache_init() (mm/slab.c);     t、确定CPU的时钟的速度,相当于BogoMips的值,用函数calibrate_delay() (init/calibrate.c);     u、初始化内核内部组件,如page tables, SLAB caches, VFS, buffers, signals queues, 线程和进程最大值等;     v、初始化proc/文件系统,用函数proc_root_init() (fs/proc/root.c);     w、调用函数 rest_init()建立进程1;    第二步、函数rest_init(): (init/main.c)      a、建立 “init process”,这个进程又叫进程1,所用函数kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);      b、建立内核守护进程,又叫进程2,所用函数是:pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES) (kernel/kthread.c) ,它是所有内         核线程的父亲;      c、释放内核锁kernel lock,它是在start_kernel() 里锁上的。所用函数unlock_kernel()(include/linux/smp-lock.h);      d、执行schedule(),开始运行调度器;(文件kernel/sched.c);      e、运行cpu0上的的idle线程,所用函数cpu_idle(),这个线程使CPU0成为调度器,当没有其它未执行的进程运行在CPU0上时候,它将返回。CPU的idle线程负责节         省电力,并保持低耗电状态;(arch/arm/kernel/process.c)   第三步、函数kernel_init(): (init/main.c) <进程1>           A、通过调用函数smp_prepare_cpus()开始准备SMP环境(arch/arm/mach-realview/platsmp.c);          1、使能CPU0上的本地timer,所用函数local_timer_setup(cpu) (arch/arm/mach-realview/localtimer.c);          2、移动CPU0相应的数据信息到它自己的存储区,所用函数smp_store_cpu_info(cpu) (arch/arm/kernel/smp.c) ;          3、初始化当前使用的CPU情况,描述了CPU的设置,所用函数cpu_set(i,cpu_present_map)。这将告诉内核,有4个cpu;          4、初始化Snoop控制器,所用函数scu_enable() (arch/arm/mach-realview/platsmp.c);          5、调用函数poke_milo(),它关心正在启动的次要处理器;(arch/arm/mach-realview/platsmp.c)           a、在函数poke_milo()中,它通过清除SYS_FLAGSCLR寄存器的低2位,来触发其它CPU执行realview_secondary_startup例程,并把                realview_secondary_startup例程的起始地址写入SYS_FLAGSSET (arch/arm/mach-realview/headsmp.S);             b、在realview_secondary例程中,次要CPUs都等待一个同步信号,这个信号将从运行在CPU0上的内核发出,意思是他们都已经准备好被初始化,当所有的                处理器都已经ready,他们将被初始化,所用函数secondary_startup()(arch/arm/mach-realview/headsmp.S) ;             c、secondary_startup例程,使次要CPUs做了和CPU0类似的初始化过程;(arch/arm/mach-realview/headsmp.S)                1)切换到超级模式,屏蔽所有中断;                 2)找处理器类型,用函数__lookup_processor_type(),这个函数返回一个指针,指向proc_info_list列表,(ARM11多核架构定义在文件                    arch/arm/mm/proc-v6.S中);                 3)每一个CPU都使用__cpu_up()函数提供的页表,__cpu_up下面有讲;                 4)跳到__v6_setup例程,(arch/arm/mm/proc-v6.S) 初始化对应于每一个CPU的TLB, cache 和MMU;                 5)用__enable_mmu 例程使能MMU,它设置一些配置位,然后调用__turn_mmu_on (arch/arm/kernel/head.S);                 6)在__turn_mmu_on函数中,会设置一些控制寄存器,然后跳到__secondary_data,这里会执行__secondary_switched例程(arch/arm/kernel/head.S);                 7)__secondary_switched例程,会跳到secondary_start_kernel例程( arch/arm/kernel/smp.c),这个例程设置一些栈指针到线程结构里,                    线程结构是通过运行在CPU0上的cpu_up函数分配的;                 8)secondary_start_kernel (arch/arm/kernel/smp.c) 是次要处理器的官方起始点,它被看作是一个运行在对应的CPU上的内核线程,                    在这个线程里,下面的步骤是进一步的初始化动作:                       一、用函数cpu_init()初始化CPU,它复制cache信息, 初始化SMP特定的信息, 并建立每个cpu栈(arch/arm/kernel/setup.c);                       二、用函数platform_secondary_init(cpu),来和CPU0上的引导线程同步,使能一些对应CPU上的分发中断控制器接口,如timer、irq;                           (arch/arm/mach-realview/platsmp.c)                       三、用函数local_irq_enable() 和 local_fiq_enable() (include/linux/irqflags.h)使能本地中断;                       四、建立对应CPU上的本地timer,所用函数:local_timer_setup(cpu) (arch/arm/mach-realview/localtimer.c);                       五、确定CPU 时钟的 BogoMips,所用函数: calibrate_delay() (init/calibrate.c);                       六、移动对应CPU的数据到它自己的存储区,所用函数smp_store_cpu_info(cpu) (arch/arm/kernel/smp.c);                       七、在二级CPU上执行idle线程,也可以叫0号线程,所用函数cpu_idle()。这个函数当没有其他等待进程运行在CPUx上时候,返回。                           (arch/arm/kernel/process.c)      B、调用函数smp_init() (init/main.c) <在CPU0上>         1、引导每一个离线CPU(CPU1,CPU2 and CPU3),所用函数cpu_up(cpu): (arch/arm/kernel/smp.c);              a、用函数fork_idle(cpu)手动建立新的idle线程,并指派它到相应的CPU的数据结构;              b、分配并初始化内存页表,并允许二级CPU安全地使能MMU,所用函数pgd_alloc();              c、通知二级CPU到哪里去找它的栈、和页表;              d、引导二级CPU,所用函数boot_secondary(cpu,idle): (arch/arm/mach-realview/platsmp.c);                 1)用锁机制spin_lock(&boot_lock)同步CPU0和二级CPU上的引导进程;                 2)通知二级处理器,它可以开始引导内核它自己的部分;                 3)用函数smp_cross_call(mask_cpu)发一个软件中断,唤醒二级核起来 (include/asm-arm/mach-realview/smp.h);                 4)等待二级处理器完成各自的引导和校准,所用函数secondary_start_kernel(),这个函数前面已经讲过了;               e、在每一个CPU上重复这个过程;           2、在终端上显示内核信息:“SMP: Total of 4 processors activated (334.02 BogoMIPS)“,所用函数smp_cpus_done(max_cpus)             (arch/arm/kernel/smp.c);
继承事业,薪火相传
返回列表