zoukankan      html  css  js  c++  java
  • 使用PSCI机制的SMP启动分析

     

     

    其他core的入口

     

    文件:arch/arm64/kernel/head.S

     

     

     

    secondary_entry

    在从bl31切到EL1上的Linux Kernel后:

    595行,在el2_setup中设置EL1EL0为小端模式,然后将w0设置为BOOT_CPU_MODE_EL1,并返回

    596行,记录cpuX的启动模式到__boot_cpu_mode,目前是BOOT_CPU_MODE_EL1

     

    secondary_startup

    604行,__cpu_setup:

    1、无效本地tlb

    2、使能FP/ASIMD

    3、设置mdscr_el1,在EL0访问Debug Communication Channel寄存器时,陷入EL1

    4、操作daif,使能debug中断

    5、设置pmuserenr_el0,当EL0访问PMU寄存器时会陷入EL1

    6、填充mair_el1,设置后面要用到的内存属性索引,目前用到了6中内存属性:

     

    7、读取sctlr_el1,修改后存入x0,后面配置mmu时会用到x0的值:x0= (sctrl_el1 & ~0xfcffffff)|0x34d5d91d,即将控制大小端的bit保留(因为之前在el2_setup里设置过了),其他位清零,然后设置新值,新值的含义如下:

     

    从低位到高位依次说明:

    0

    M

    1表示开启EL1EL0stage1地址转换机制,目前因为用不到虚拟化,所有只有stage1

    1

    A

    0:关闭地址对齐检查,但是load/store exclusiveload-acquire/store-release除外

    2

    C

    1EL0/1data cache的控制不再由sctrl_el1控制,如果HCR_EL2.DC1,那么EL0/1data cache开启。(所以,不开MMU,数据cache也可以开?)

    3

    SA

    1  EL1上栈指针对齐检查,需要16字节对齐

    4

    SA0

    1EL0上栈指针对齐检查,需要16字节对齐

    5

    CP15BEN

    0EL0运行在AArch32时,不能使用CP15DMBCP15DSB以及CP15ISB

    7

    ITD

    0EL0运行在AArch32模式时,仍旧可以使用IT指令(IF-THENhttp://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cjabicci.html

    8

    SED

    1EL0运行在AArch32时,不能使用SEDEND指令。这个指令是在ARMv6引入的,用于切换当前cpu的大小端,请参考:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cjacabbf.html

    9

    UMA

    0:在EL0运行在AArch64上通过MRSMSR指令访问DAIF时会陷入EL1

    12

    I

    1EL0/1的指令cache不再由sctrl_el1控制。如果HCR_EL2.DC1,那么EL0/1的指令 cache开启

    14

    DZE

    1EL0运行AArch64时,如果执行DC ZVA指令(清除指令虚拟地址的data cache),会陷入EL1.

    15

    UCT

    1EL0运行AArch64时,访问CTR_EL0时不会陷入EL1. 这个寄存器用于获取当前系统的cache的架构信息

    16

    nTWI

    1: EL0上执行WFI时不会陷入EL1

    18

    nTWE

    1: EL0上执行WFE时不会陷入EL1

    19

    WXN

    0: EL1&0的内存访问权限没有影响。如果是1的话,在EL1&0下,如果某块内存具备可写权限,那么这块内存就是XNExecute Never

    24

    E0E

    控制EL0数据访问的大小端控制,0小端,1大端

    25

    EE

    控制EL1以及EL1/0table walk时的大小端,0小端,1大端

    26

    UCI

    1EL0运行在AArch64时,如果执行DC CVAU/CIVAC/CVACIC IVAU时不会陷入EL1

     

    8、设置TCR_EL1,用于建立1:1映射,目前配置的VA时48bit的:

     

    [5:0] T0SZ 64-48=16
    [21:16] T1SZ 64-48=16
    [9:8] IRGN0, 使用TTBR0_EL1进行内存访问时的Inner cacheability 1:Normal memory, Inner Write-Back Write-Allocate Cacheable
    [25:24] IRGN1, 使用TTBR1_EL1进行内存访问时的Inner cacheability 1:Normal memory, Inner Write-Back Write-Allocate Cacheable
    [11:10] ORGN0, 使用TTBR0_EL1进行内存访问时的Outer cacheability 1:Normal memory, Outer Write-Back Write-Allocate Cacheable
    [27:26] ORGN1, 使用TTBR1_EL1进行内存访问时的Outer cacheability 1:Normal memory, Outer Write-Back Write-Allocate Cacheable
    [13:12] SH0, 使用TTBR0_EL1进行内存访问时的Shareability attribute 3: Inner Shareable
    [29:28] SH1, 使用TTBR1_EL1进行内存访问时的Shareability attribute 3: Inner Shareable
    [15:14] TG0, 使用TTBR0_EL1时的页大小 0:4KB
    [31:30] TG1, 使用TTBR1_EL1时的页大小 0:4KB
    [36] AS, 表示ASID的大小 1:16bit
    [37] TBI0, 在使用TTBR0_EL1做地址翻译时,是否忽略虚拟地址的高字节,如果不忽略的话,会被用来当tagged地址 1:忽略高字节
    [22] A1, 选择通过TTBR0_EL1或者TTBR1_EL1来定义ASID 1:使用TTBR1_EL1.ASID来定义ASID
    [34:32] IPS,物理地址大小 通过读取ID_AA64MMFR0_EL1的获取当前系统的Physical Address range,然后将值写到这里,目前是48
     

    第605行,__enable_mmu:

    1、首先判断当前系统硬件是否支持4KB的粒度,是的话继续执行,否则stuck住

    2、更新boot cpu的状态信息,即将__early_cpu_boot_status的值设置为0

    3、将idmap_pg_dir赋值给ttbr0_el1

    4、将swapper_pg_dir赋值给ttbr1_el1

    5、将之前填充好的x0的值赋值给sctlr_el1

    6、将icache无效

    此时,mmu已经开启了,上面用到的两个页目录的内容在cpu0启动时已经填写好了,其他的core直接用。

     

    __secondary_switched:

     

    第611行,设置CPUx的异常向量表基地址为vectors

    第615~619,设置CPUx的栈指针的值,这里用到的secondary_data的内容是在CPU0启动CPUx时赋值的。sp指向了idle task的栈顶地址,sp_el0指向idle task的task_struct首地址

    第622,跳转到secondary_start_kernel

     

    secondary_start_kernel:

     

    第218,获取init_mm,后面会赋值给current->active_mm
    第222,获取当前current任务所处的cpu编号,如果是cpu1的话,这里就返回1
    第223,将cpuX在数组__per_cpu_offset中的对应项的地址&__per_cpu_offset[X]赋值给tpidr_el1
    第229,增加init_mm的引用计数mm_count,因为所有的内核线程共享同一个mm_struct
    第236,将ttbr0_el1赋值为empty_zero_page
    第238,关闭当前CPU正在执行的task的抢占,这样当前cpu就只能被中断打断,不会切到其他进程
    第254,获取当前cpu的各种硬件信息,比如arch_timer的频率、cache类型等等,将信息存放到一个per cpu的全局变量cpu_data中,此时会看到
                启动log里输出"Detected %s I-cache on CPU%d"接着会将这些信息跟cpu0的进行比较,如果发现不合理的地方,
                会输出“Unsupported CPU feature variation detected.”
    第259,通知之前注册的回调函数,会使能gic和timer。此时cpuX的中断还没有使能,并且cpu0还在__cpu_up()中等待,没有返回
                首先获取当前cpu的cpuhp_state,然后调用从(CPUHP_BRINGUP_CPU+1)到CPUHP_AP_ONLINE之间注册的bringup回调函数,这个结合后面cpu0唤醒cpuX的逻辑分析。
     
    CPUHP_AP_IDLE_DEAD    
    CPUHP_AP_OFFLINE    
    CPUHP_AP_SCHED_STARTING sched_cpu_starting  
    CPUHP_AP_RCUTREE_DYING    
    CPUHP_AP_IRQ_GIC_STARTING gic_starting_cpu 动态注册的
    CPUHP_AP_ARM_VFP_STARTING    
    CPUHP_AP_ARM64_DEBUG_MONITORS_STARTING    
    CPUHP_AP_PERF_ARM_HW_BREAKPOINT_STARTING    
    CPUHP_AP_PERF_ARM_STARTING    
    CPUHP_AP_ARM_ARCH_TIMER_STARTING arch_timer_starting_cpu 动态注册
    CPUHP_AP_ARM_GLOBAL_TIMER_STARTING    
    CPUHP_AP_DUMMY_TIMER_STARTING    
    CPUHP_AP_ARM_CORESIGHT_STARTING    
    CPUHP_AP_ARM64_ISNDEP_STARTING    
    CPUHP_AP_SMPCFD_DYING    
    CPUHP_AP_ONLINE    

     

    第261,读取mpdir_el1,如果是多核的话,获取当前cpu的thread id、core id以及cluster id。如果使能了DEBUG,那么会输出“CPU%u: cluster %d core %d thread %d mpidr %#016llx”

    第270,设置secondary_data.status为CPU_BOOT_SUCCESS,后面cpu0醒来后,会根据这个标志判断cpuX是否启动正常

    第271,设置__cpu_online_mask中当前cpu对应的bit,后面cpu0醒来后,会根据这个标志判断cpuX是否启动正常

    第272,通知cpu0,可以继续往下运行了,此时cpu0在archarm64kernelsmp.c中调用完boot_secondary后,紧接着wait_for_completion_timeout(&cpu_running, msecs_to_jiffies(1000))

    第274,开启本地中断

    第275,使能SERROR中断

    第280,执行cpu_startup_entry:

     

    第371,arm没有实现
    第372,将当前cpu的cpuhp_state的state设置为CPUHP_AP_ONLINE_IDLE,然后对done_up完成变量执行complete操作。这个是跟cpu0通信,cpu0在执行到bringup_cpu最后,完成唤醒操作后,会执行bringup_wait_for_ap(cpu)操作,它里面会调用wait_for_ap_thread(st, true)-->wait_for_completion(done)

    第374,执行do_idle

     

     

    CPU0启动以及PSCI初始化

    设备树

    psci:

     

    cpu:
     

    start_kernel:

    1、smp_setup_processor_id(); 读取mpdir,获取当前cpu id,填写到cpu_logical_map(0),输出"Booting Linux on physical CPU 0x%lx "
    2、boot_cpu_init():读取mpdir,填写set_cpu_online(0,true)/set_cpu_active(0, true)/set_cpu_present(0, true)/set_cpu_possible(0, true),记录cpu号到__boot_cpu_id
    3、setup_arch -> psci_dt_init: 调用psci_0_2_init,其中会跟bl31通信,并给psci_ops赋值
    4、setup_arch -> cpu_read_bootcpu_ops(): 设置cpu_ops[0]为dt_supported_cpu_ops[1],即cpu_psci_ops,其中[0]用于spin-table启动方式,目前我们用的是psci方式
    其中cpu_psci_ops的定义如下:
    5、setup_arch -> smp_init_cpus(),这个函数会接着调用of_parse_and_init_cpus,解析设备树的cpu节点,将解析出来的reg属性的值分
        别跟cpu_logical_map(0)/cpu_logical_map(1)/cpu_logical_map(2)/cpu_logical_map(3)映射起来,在解析到cpu0时,还会将bootcpu_valid设置为true。
                    如果设备树中还指定了“numa-node-id”属性,那么还会属性的值跟cpu_to_node_map[cpu]映射起来,最后cpu_count记录实际解析到的cpu的个数。接着,smp_init_cpu对非boot cpu,依次调用smp_cpu_setup,在其中会设置cpu_ops[cpu]为cpu_psci_ops,然后调用cpu_ops[cpu]->cpu_init(cpu),也就是上面的cpu_psci_cpu_init,在arm64平台上这是个空函数,最后设置set_cpu_possible(cpu, true)。此时其他core都还没有上电
    6、setup_arch -> smp_build_mpidr_hash() 填充mpidr_hash结构,如果开了debug,那么会输出"MPIDR hash: aff0[%u] aff1[%u] aff2[%u] aff3[%u] mask[%#llx] bits[%u] "
    7、smp_prepare_boot_cpu():首先设置tpidr_el1为__per_cpu_offset[0],接着调用jump_label_init,这个暂不分析。接着调用cpuinfo_store_boot_cpu,获取cpu0的
        各种硬件信息,存放到per_cpu变量cpu_data中(对于cpu0,还会将boot_cpu_data指向cpu0的cpu_data),这个在其他core起来后也会执行,
        但是boot cpu(即cpu0)的不同之处是,除了获取硬件信息之外,还会将这些信息设置到arm64_ftr_regs结构中,将来其他core起来后,会进行对比。最后,如果
        使能了VHE,那么还会根据kernel是否处于EL2,来设置boot_cpu_hyp_mode为true或者false
    8、boot_cpu_hotplug_init:设置cpu0的cpuhp_state.booted_once为true,同时将cpu0的cpuhp_state.state设置为CPUHP_ONLINE
    9、init_IRQ():设置中断栈,调用of_irq_init初始化中断控制器,其中会注册CPUHP_AP_IRQ_GIC_STARTING的回调函数gic_starting_cpu
    10、time_init():会调用到driversclocksourcearm_arch_timer.c中的arch_timer_of_init,注册CPUHP_AP_ARM_ARCH_TIMER_STARTING的回调函数arch_timer_starting_cpu

     

    下面进入rest_init:

    在这个函数里先后创建个内核线程kernel_init和kthreadd,然后在kernel_init中会wait_for_completion(&kthreadd_done),当主流程准备就绪后,设置system_state为SYSTEM_SCHEDULING,

    然后主流程会complete(&kthreadd_done),在主流程的最后,会执行cpu_startup_entry(CPUHP_ONLINE),在前面非boot cpu的启动分析中,最后也会执行cpu_startup_entry,不过它传递的cpu状态是CPUHP_AP_ONLINE_IDLE

    下面重点分析kernel_init:

    在执行这个内核线程是,使用的仍然是cpu0,因为当前系统中也只有cpu0.

     

    第1058,smp_prepare_cpus,首先调用init_cpu_topology,解析当前/cpus节点以及下面的cpu-map子节点,填充cpu_topology[cpu],得到系统cpu的拓扑关系,然后

                调用set_sched_topology设置sched_domain_topology为arm64_topology。如果没有从设备树里cpu-map获得拓扑关系,smp_prepare_cpus会调用store_cpu_topology来

                设置cpu_topology[0]的内容。最后,这个函数会遍历当前所有cpu,设置per_cpu变量cpu_number为cpu编号,然后cpu_ops[cpu]->cpu_prepare(cpu),如果成功的话,

                 设置set_cpu_present(cpu, true)

    第1064,do_pre_smp_initcalls:执行__initcall_start到__initcall0_start之间的回调函数

    第1067,smp_init:这个函数就是cpu0唤醒其他core的地方,重点分析

     

    smp_init:

     

    第568,idle_threads_init,为每个非boot cpu都各fork一个idle task,将获得的task_struct记录到per_cpu变量idle_threads中
    第569,cpuhp_threads_init,为每个core都创建一个"cpuhp/%u"内核线程,结果记录在per_cpu变量cpuhp_state.thread中,然后启动当前cpu的"cpuhp/%u"线程:"cpuhp/0"

    第574~579,遍历cpu,如果cpu没有online(cpu_online_mask),那么调用cpu_on(cpu)

     

    下面重点分析cpu_on:

     

    上面第二个参数表示target status,表示需要将第一个参数表示的cpu,唤醒,并达到指定的状态,目前cpu0就是处于CPUHP_ONLINE,这是cpu正常工作时的状态
    这个函数会继续调用_cpu_up(cpu, 0, target):
    1、检查cpu_present(cpu),正常情况下,为1
    2、如果待唤醒的cpu的cpuhp_state.state大于target表示的状态,那么直接返回。数值越大,表示cpu越接近CPUHP_ONLINE,既然是bring up,那么当然是将state变大为目标
    下面是cpu状态切换的状态图:
     
    上图左边是cpu up时的状态切换:OFFLINE -> BRINGUP_CPU -> AP_OFFLINE -> AP_ONLINE  -> AP_ACTIVE
    3、如果待处理cpu的当前状态是CPUHP_OFFLINE,那么要先获取该cpu的idle task结构,由于该cpu目前还没有上电,自然属于这种
    4、调用cpuhp_set_state(st, target),设置待唤醒cpu的目标状态为CPUHP_ONLINE,如果当前状态小于target,将bringup设置为1,表示要执行up操作,返回的是当前状态
     

    4、如果待处理cpu的当前状态大于CPUHP_BRINGUP_CPU,那么会执行cpuhp_kick_ap_work(cpu)。

    5、下面的唤醒步骤分两个阶段,首先从OFFLINE到CPUHP_BRINGUP_CPU,然后剩下从CPUHP_BRINGUP_CPU到CPUHP_ONLINE则通过AP hotplug thread进行,这个线程就是上面创建的"cpuhp/0",

            它的task_struct存放在per_cpu变量cpuhp_state.thread中

     

    下面是从OFFLINE->CPUHP_BRINGUP_CPU:

    下面分析cpuhp_up_callbacks:

    在从OFFLINE->CPUHP_BRINGUP_CPU的切换过程中,每一步切换,都会调用cpuhp_invoke_callback完成:
    在该函数中,首先通过调用cpuhp_get_step(state)获取state对应的cpuhp_step结构,内核在kernelcpu.c里提供了两个cpuhp_state数组:cpuhp_ap_states和cpuhp_bp_states,内核在这两个数组中内置了一些初始的回调函数,也为动态注册留出了位置,可以使用includelinuxcpuhotplug.h中提供的API进行注册。如果当前状态小于CPUHP_BRINGUP_CPU并且不等于CPUHP_TEARDOWN_CPU时,使用的是cpuhp_bp_states,否则使用cpuhp_ap_states。所以,对OFFLINE->CPUHP_BRINGUP_CPU,使用的是cpuhp_bp_states。

    在获得cpuhp_step结构后,先判断该step是否支持multi_instance,如果不支持的话,对于bringup操作,会回调step->startup.single(cpu),否则回调step->teardown.single(cpu).对于支持multi_instance的step,会遍历step->list,对于bringup,回调step->startup.multi(cpu,node),其中node就是从step->list遍历得到的,否则回调step->teardown.multi(cpu,node)。

     

    下面是cpuhp_bp_states中涉及到的回调:

     

    CPUHP_CREATE_THREADS smpboot_create_threads 遍历hotplug_threads,对没有创建task的
    CPUHP_PERF_PREPARE perf_event_init_cpu  
    CPUHP_WORKQUEUE_PREP workqueue_prepare_cpu  
    CPUHP_HRTIMERS_PREPARE hrtimers_prepare_cpu  
    CPUHP_SMPCFD_PREPARE smpcfd_prepare_cpu  
    CPUHP_RELAY_PREPARE relay_prepare_cpu  
    CPUHP_SLAB_PREPARE slab_prepare_cpu  
    CPUHP_RCUTREE_PREP rcutree_prepare_cpu  
    CPUHP_TIMERS_PREPARE timers_prepare_cpu  
    CPUHP_BRINGUP_CPU bringup_cpu  

    bringup_cpu:

     

    第518,获取指定cpu的idle task进程描述符
    第529,调用__cpu_up(cpu, idle),这里的cpu表示要唤醒的cpu,idle用于为刚唤醒的cpu提供栈空间。具体过程如下:
        1、将idle赋值给secondary_data.task
        2、将task_stack_page(idle) + THREAD_SIZE赋值给secondary_data.stack,cpuX唤醒后会使用这个栈,参考之前的分析
        3、将CPU_MMU_OFF赋值给secondary_data.status,在cpuX唤醒后,会修改这个值,然后cpu0会根据这个值判断cpuX是否正常
        4、调用cpu_ops[cpu]->cpu_boot(cpu),这里的cpu_ops是cpu_psci_cpu_init,cpu_boot是cpu_psci_cpu_boot,这个函数会执行
    psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry))

               cpu_on是在driversfirmwarepsci.c赋值的:

            上面会通过smc调用陷入bl31,然后cpuX在从EL3回到EL1时,就会从__pa_symbol(secondary_entry)开始运行,不过此时cpuX的MMU还没有开启,用的是物理地址

                5、如果上面返回成功,那么cpu0调用wait_for_completion_timeout(&cpu_running,msecs_to_jiffies(1000));  根据前面的分析,当cpuX醒来之后,会complete这里的cpu_running

                6、调用cpu_online(cpu)检查cpuX是否成功online

                7、读取READ_ONCE(secondary_data.status),然后判断cpuX是否正常

    第533,bringup_wait_for_ap(cpu):

                1、调用wait_for_ap_thread(st, true),因为是bringup,所以会wait_for_completion(&st->done_up)。在cpuX唤醒后,执行idle任务之前会complete该变量

                2、stop_machine_unpark(cpu);

                3、kthread_unpark(st->thread),这里的st->thread是"cpuhp/0",是在cpuhp_threads_init中初始化的

                4、如果st->target小于等于CPUHP_AP_ONLINE_IDLE,那么这个函数的任务就完成了,但是对于这里,target的值是CPUHP_ONLINE

                5、调用cpuhp_kick_ap(st, st->target),这个函数进一步调用__cpuhp_kick_ap(st):

     

                6、设置st->shoule_run为true
                7、在__cpuhp_kick_ap中会唤醒st->thread,此时smpboot_thread_fn开始在cpuX上开始运行,并检查st->shoule_run为true,这样cpuhp_thread_fun被
                      smpboot_thread_fn回调,这个线程负责cpuX第二个阶段的状态切换,在cpuX唤醒后,会进入CPUHP_AP_ONLINE_IDLE,所以完成从CPUHP_AP_ONLINE_IDLE->CPUHP_ONLINE
                8、wait_for_completion(&st->done_up),在st->thread中会complete该变量

    cpuhp_thread_fun:

     

    1、比较cpuX的当前状态和目标状态,如果小于目标状态,那么设置st->should为true,这样这个函数返回后,还会被smpboot_thread_fn继续回调

    2、调用st->result = cpuhp_invoke_callback(cpu, state, bringup, st->node, &st->last);

    3、如果上个函数返回失败,设置st->should_run = false

    4、如果st->should_run为false,那么调用complete_ap_thread(st, bringup),如果是bringup,这个函数会complete(&st->done_up)。即:在正常情况下,

            如果当前状态达不到target状态,那么st->should_run一直为true,只有达到target,才会complete(&st->done_up)。别忘了,此时cpu0还在wait呢

     

    第二阶段从CPUHP_AP_ONLINE_IDLE->CPUHP_ONLINE,下面的函数会被调用,这些函数位于cpuhp_ap_states中:

    CPUHP_AP_SMPBOOT_THREADS smpboot_unpark_threads  
    CPUHP_AP_IRQ_AFFINITY_ONLINE irq_affinity_online_cpu  
    CPUHP_AP_PERF_ONLINE perf_event_init_cpu  
    CPUHP_AP_WORKQUEUE_ONLINE workqueue_online_cpu  
    CPUHP_AP_RCUTREE_ONLINE rcutree_online_cpu  
    CPUHP_AP_ACTIVE sched_cpu_activate  
    CPUHP_ONLINE NULL  
     
     

    SMP启动log

     

    下面是多核启动时的log,有助于我们理解上面的分析过程:(log中<cpu_id 进程name:进程id>)

     

    [    0.117936] <0  swapper/0:1> smp: Bringing up secondary CPUs ...
    [    0.118893] <0  swapper/0:1> smpboot_create_threads, cpu: 1
    [    0.140551] <0  swapper/0:1> perf_event_init_cpu, cpu: 1
    [    0.140958] <0  swapper/0:1> workqueue_prepare_cpu, cpu: 1
    [    0.149409] <0  swapper/0:1> hrtimers_prepare_cpu, cpu: 1
    [    0.149555] <0  swapper/0:1> smpcfd_prepare_cpu, cpu: 1
    [    0.149780] <0  swapper/0:1> rcutree_prepare_cpu, cpu: 1
    [    0.150696] <0  swapper/0:1> timers_prepare_cpu, cpu: 1
    [    0.150999] <0  swapper/0:1> bringup_cpu, cpu: 1
    [    0.152518] <1  swapper/1:0> Detected PIPT I-cache on CPU1
    [    0.153478] <1  swapper/1:0> sched_cpu_starting, cpu: 1
    [    0.153575] <1  swapper/1:0> GICv3: CPU1: found redistributor 1 region 0:0x00000000080c0000
    [    0.153849] <1  swapper/1:0> CPU1: cluster 0 core 1 thread -1 mpidr 0x00000080000001
    [    0.153955] <1  swapper/1:0> CPU1: Booted secondary processor [410fd083]
    [    0.157031] <1    cpuhp/1:13> smpboot_unpark_threads, cpu: 1
    [    0.158684] <1    cpuhp/1:13> irq_affinity_online_cpu, cpu: 1
    [    0.158999] <1    cpuhp/1:13> perf_event_init_cpu, cpu: 1
    [    0.159236] <1    cpuhp/1:13> workqueue_online_cpu, cpu: 1
    [    0.160831] <1    cpuhp/1:13> rcutree_online_cpu, cpu: 1
    [    0.161322] <1    cpuhp/1:13> sched_cpu_activate, cpu: 1
    [    0.162781] <0  swapper/0:1> smpboot_create_threads, cpu: 2
    [    0.185357] <0  swapper/0:1> perf_event_init_cpu, cpu: 2
    [    0.185528] <0  swapper/0:1> workqueue_prepare_cpu, cpu: 2
    [    0.193720] <0  swapper/0:1> hrtimers_prepare_cpu, cpu: 2
    [    0.193829] <0  swapper/0:1> smpcfd_prepare_cpu, cpu: 2
    [    0.193951] <0  swapper/0:1> rcutree_prepare_cpu, cpu: 2
    [    0.194451] <0  swapper/0:1> timers_prepare_cpu, cpu: 2
    [    0.194583] <0  swapper/0:1> bringup_cpu, cpu: 2
    [    0.194853] <2  swapper/2:0> Detected PIPT I-cache on CPU2
    [    0.194970] <2  swapper/2:0> sched_cpu_starting, cpu: 2
    [    0.195027] <2  swapper/2:0> GICv3: CPU2: found redistributor 2 region 0:0x00000000080e0000
    [    0.195198] <2  swapper/2:0> CPU2: cluster 0 core 2 thread -1 mpidr 0x00000080000002
    [    0.195269] <2  swapper/2:0> CPU2: Booted secondary processor [410fd083]
    [    0.195899] <2    cpuhp/2:18> smpboot_unpark_threads, cpu: 2
    [    0.196294] <2    cpuhp/2:18> irq_affinity_online_cpu, cpu: 2
    [    0.196426] <2    cpuhp/2:18> perf_event_init_cpu, cpu: 2
    [    0.196594] <2    cpuhp/2:18> workqueue_online_cpu, cpu: 2
    [    0.197099] <2    cpuhp/2:18> rcutree_online_cpu, cpu: 2
    [    0.197325] <2    cpuhp/2:18> sched_cpu_activate, cpu: 2
    [    0.197811] <0  swapper/0:1> smpboot_create_threads, cpu: 3
    [    0.220870] <0  swapper/0:1> perf_event_init_cpu, cpu: 3
    [    0.221065] <0  swapper/0:1> workqueue_prepare_cpu, cpu: 3
    [    0.229439] <0  swapper/0:1> hrtimers_prepare_cpu, cpu: 3
    [    0.229550] <0  swapper/0:1> smpcfd_prepare_cpu, cpu: 3
    [    0.229675] <0  swapper/0:1> rcutree_prepare_cpu, cpu: 3
    [    0.230062] <0  swapper/0:1> timers_prepare_cpu, cpu: 3
    [    0.230195] <0  swapper/0:1> bringup_cpu, cpu: 3
    [    0.230439] <3  swapper/3:0> Detected PIPT I-cache on CPU3
    [    0.230549] <3  swapper/3:0> sched_cpu_starting, cpu: 3
    [    0.230604] <3  swapper/3:0> GICv3: CPU3: found redistributor 3 region 0:0x0000000008100000
    [    0.230767] <3  swapper/3:0> CPU3: cluster 0 core 3 thread -1 mpidr 0x00000080000003
    [    0.230907] <3  swapper/3:0> CPU3: Booted secondary processor [410fd083]
    [    0.231723] <3    cpuhp/3:23> smpboot_unpark_threads, cpu: 3
    [    0.232132] <3    cpuhp/3:23> irq_affinity_online_cpu, cpu: 3
    [    0.232266] <3    cpuhp/3:23> perf_event_init_cpu, cpu: 3
    [    0.232439] <3    cpuhp/3:23> workqueue_online_cpu, cpu: 3
    [    0.232941] <3    cpuhp/3:23> rcutree_online_cpu, cpu: 3
    [    0.233179] <3    cpuhp/3:23> sched_cpu_activate, cpu: 3
    [    0.233572] <0  swapper/0:1> smp: Brought up 1 node, 4 CPUs
    [    0.233680] <0  swapper/0:1> SMP: Total of 4 processors activated.
     
    完。
  • 相关阅读:
    开发金额处理BigDecimal
    Strtuts2.3.15 bug ,可以远程执行命令
    java 获取properies资源信息
    获取程序运行所在ip
    JVM虚拟机内存和String 类型深入
    数据库表结构上下文
    properties配置文件实现DBManager
    查询功能的包装类
    通过反射生成类的utils
    jdbc反射操作
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/11925299.html
Copyright © 2011-2022 走看看