标题:
ARM多核引导过程(4)
[打印本页]
作者:
yuyang911220
时间:
2014-12-22 16:26
标题:
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);
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0