Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1487178
  • 博文数量: 338
  • 博客积分: 2695
  • 博客等级: 少校
  • 技术积分: 3556
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-05 11:37
个人简介

小鱼儿游啊游啊。。。。

文章分类

全部博文(338)

文章存档

2019年(4)

2018年(8)

2017年(6)

2016年(10)

2015年(49)

2014年(48)

2013年(98)

2012年(115)

分类: Android平台

2013-01-24 11:00:02


本文参考了

Linux kernel启动的过程概览
init/main.c:start_kernel()
    |
   \|/
init/main.c:rest_init
{
……
kernel_thread(kernel_init, NULL, CLONES_FS | CLONE_SIGHAND)
……
cpu_idle()
}
    |
   \|/
init/main.c:kernel_init//从上面代码可以看出,kernel_init是一个内核线程 
    |
   \|/
init/main.c:init_post  //会在最后调用启动脚本
{
……
823         /*
824          * We try each of these until one succeeds.
825          *
826          * The Bourne shell can be used instead of init if we are
827          * trying to recover a really broken machine.
828          */
829         if (execute_command) {
830                 run_init_process(execute_command);
831                 printk(KERN_WARNING "Failed to execute %s.  Attempting "
832                                         "defaults...\n", execute_command);
833         }
834         run_init_process("/sbin/init");
835         run_init_process("/etc/init");
836         run_init_process("/bin/init");
837         run_init_process("/bin/sh");
838
839         panic("No init found.  Try passing init= option to kernel.");
……
}


我们再来看看内核启动多核的详细过程。

init/main.c:start_kernel()
    |
   \|/
init/main.c:rest_init
{
……
kernel_thread(kernel_init, NULL, CLONES_FS | CLONE_SIGHAND)
……
}
    |
   \|/
kernel_init    
    |
   \|/
/* called by boot processor to activate the rest */
init/main.c: smp_init()
{
……
for_each_present_cpu(cpu) {
          if (num_onlien_cpus() >= setup_max_cpus)
               break;
          if ( !cpu_online(cpu))     
               cpu_up(cpu);
}
/* Any cleanup work */
printk(KERN_INFO "Brought up %ld CPUs\n", (long)num_online_cpus());
smp_cpu_done(setup_max_cpus);
……
}
--------------------------------------------------------------
cpu_up = native_cpu_up是一个回调函数。
注册地方是在:arch/x86/kernel/smp.c

struct smp_ops smp_ops = {
   ……
  .cpu_up = native_cpu_up,
   ……
}
--------------------------------------------------------------
    |
   \|/
arch/x86/kernel/smpboot.c:native_cpu_up(unsigned int cpu)
    |
   \|/
arch/x86/kernel/smpboot.c: do_boot_cpu(int apicid, int cpu)
    |
   \|/
wakeup_secondary_cpu_via_init(apicid, start_ip)


在启动多核的过程中有两个bitmap很重要,一个是cpu_callin_mask,另一个是cpu_callout_mask。
cpu_callin_mask代表某个cpu是否已经启动,它的某个bit被与之对应的cpu在启动后置位,标记已经启动。
cpu_callout_mask在do_boot_cpu中被置位,并在检查到对应cpu已经启动后重新清零。

我们下面来详细看看do_boot_cpu(int apicid, int cpu)与wakeup_secondary_cpu_via_init(apicid, start_ip)

C代码  收藏代码
  1. /* 
  2.  * NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad 
  3.  * (ie clustered apic addressing mode), this is a LOGICAL apic ID. 
  4.  * Returns zero if CPU booted OK, else error code from 
  5.  * ->wakeup_secondary_cpu. 
  6.  */  
  7. static int __cpuinit do_boot_cpu(int apicid, int cpu)  
  8. {  
  9.     unsigned long boot_error = 0;  
  10.     unsigned long start_ip;  
  11.     int timeout;  
  12.     struct create_idle c_idle = {  
  13.         .cpu    = cpu,  
  14.         .done   = COMPLETION_INITIALIZER_ONSTACK(c_idle.done),  
  15.     };  
  16.   
  17.     INIT_WORK_ON_STACK(&c_idle.work, do_fork_idle);  
  18.   
  19.     alternatives_smp_switch(1);  
  20.   
  21.     c_idle.idle = get_idle_for_cpu(cpu);  
  22.   
  23.     /* 
  24.      * We can't use kernel_thread since we must avoid to 
  25.      * reschedule the child. 
  26.      */  
  27.     if (c_idle.idle) {  
  28.         c_idle.idle->thread.sp = (unsigned long) (((struct pt_regs *)  
  29.             (THREAD_SIZE +  task_stack_page(c_idle.idle))) - 1);  
  30.         init_idle(c_idle.idle, cpu);  
  31.         goto do_rest;  
  32.     }  
  33.   
  34.     if (!keventd_up() || current_is_keventd())  
  35.         c_idle.work.func(&c_idle.work);  
  36.     else {  
  37.         schedule_work(&c_idle.work);  
  38.         wait_for_completion(&c_idle.done);  
  39.     }  
  40.   
  41.     if (IS_ERR(c_idle.idle)) {  
  42.         printk("failed fork for CPU %d\n", cpu);  
  43.         destroy_work_on_stack(&c_idle.work);  
  44.         return PTR_ERR(c_idle.idle);  
  45.     }  
  46.   
  47.     set_idle_for_cpu(cpu, c_idle.idle);  
  48. do_rest:  
  49.     per_cpu(current_task, cpu) = c_idle.idle;  
  50. #ifdef CONFIG_X86_32  
  51.     /* Stack for startup_32 can be just as for start_secondary onwards */  
  52.     irq_ctx_init(cpu);  
  53. #else  
  54.     clear_tsk_thread_flag(c_idle.idle, TIF_FORK);  
  55.     initial_gs = per_cpu_offset(cpu);  
  56.     per_cpu(kernel_stack, cpu) =  
  57.         (unsigned long)task_stack_page(c_idle.idle) -  
  58.         KERNEL_STACK_OFFSET + THREAD_SIZE;  
  59. #endif  
  60.     early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu);  
  61.     initial_code = (unsigned long)start_secondary;  
  62.     stack_start.sp = (void *) c_idle.idle->thread.sp;  
  63.   
  64.     /* start_ip had better be page-aligned! */  
  65.     start_ip = setup_trampoline();  
  66.   
  67.     /* So we see what's up */  
  68.     announce_cpu(cpu, apicid);  
  69.   
  70.     /* 
  71.      * This grunge runs the startup process for 
  72.      * the targeted processor. 
  73.      */  
  74.   
  75.     atomic_set(&init_deasserted, 0);  
  76.   
  77.     if (get_uv_system_type() != UV_NON_UNIQUE_APIC) {  
  78.   
  79.         pr_debug("Setting warm reset code and vector.\n");  
  80.   
  81.         smpboot_setup_warm_reset_vector(start_ip);  
  82.         /* 
  83.          * Be paranoid about clearing APIC errors. 
  84.         */  
  85.         if (APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {  
  86.             apic_write(APIC_ESR, 0);  
  87.             apic_read(APIC_ESR);  
  88.         }  
  89.     }  
  90.   
  91.     /* 
  92.      * Kick the secondary CPU. Use the method in the APIC driver 
  93.      * if it's defined - or use an INIT boot APIC message otherwise: 
  94.      */  
  95.     if (apic->wakeup_secondary_cpu)  
  96.         boot_error = apic->wakeup_secondary_cpu(apicid, start_ip);  
  97.     else  
  98.         boot_error = wakeup_secondary_cpu_via_init(apicid, start_ip);  
  99.   
  100.     if (!boot_error) {  
  101.         /* 
  102.          * allow APs to start initializing. 
  103.          */  
  104.         pr_debug("Before Callout %d.\n", cpu);  
  105.         cpumask_set_cpu(cpu, cpu_callout_mask);  
  106.         pr_debug("After Callout %d.\n", cpu);  
  107.   
  108.         /* 
  109.          * Wait 5s total for a response 
  110.          */  
  111.         for (timeout = 0; timeout < 50000; timeout++) {  
  112.             if (cpumask_test_cpu(cpu, cpu_callin_mask))  
  113.                 break;  /* It has booted */  
  114.             udelay(100);  
  115.         }  
  116.   
  117.         if (cpumask_test_cpu(cpu, cpu_callin_mask))  
  118.             pr_debug("CPU%d: has booted.\n", cpu);  
  119.         else {  
  120.             boot_error = 1;  
  121.             if (*((volatile unsigned char *)trampoline_base)  
  122.                     == 0xA5)  
  123.                 /* trampoline started but...? */  
  124.                 pr_err("CPU%d: Stuck ??\n", cpu);  
  125.             else  
  126.                 /* trampoline code not run */  
  127.                 pr_err("CPU%d: Not responding.\n", cpu);  
  128.             if (apic->inquire_remote_apic)  
  129.                 apic->inquire_remote_apic(apicid);  
  130.         }  
  131.     }  
  132.   
  133.     if (boot_error) {  
  134.         /* Try to put things back the way they were before ... */  
  135.         numa_remove_cpu(cpu); /* was set by numa_add_cpu */  
  136.   
  137.         /* was set by do_boot_cpu() */  
  138.         cpumask_clear_cpu(cpu, cpu_callout_mask);  
  139.   
  140.         /* was set by cpu_init() */  
  141.         cpumask_clear_cpu(cpu, cpu_initialized_mask);  
  142.   
  143.         set_cpu_present(cpu, false);  
  144.         per_cpu(x86_cpu_to_apicid, cpu) = BAD_APICID;  
  145.     }  
  146.   
  147.     /* mark "stuck" area as not stuck */  
  148.     *((volatile unsigned long *)trampoline_base) = 0;  
  149.   
  150.     if (get_uv_system_type() != UV_NON_UNIQUE_APIC) {  
  151.         /* 
  152.          * Cleanup possible dangling ends... 
  153.          */  
  154.         smpboot_restore_warm_reset_vector();  
  155.     }  
  156.   
  157.     destroy_work_on_stack(&c_idle.work);  
  158.     return boot_error;  
  159. }  



C代码  收藏代码
  1. /* 
  2.  * Currently trivial. Write the real->protected mode 
  3.  * bootstrap into the page concerned. The caller 
  4.  * has made sure it's suitably aligned. 
  5.  */  
  6. unsigned long __trampinit setup_trampoline(void)  
  7. {  
  8.         memcpy(trampoline_base, trampoline_data, TRAMPOLINE_SIZE);  
  9.         return virt_to_phys(trampoline_base);  
  10. }  


可以从上面代码中看出do_boot_cpu会为编号为apicid的AP设定好它将要使用的stack以及它将要执行的代码start_eip,在完成这些后,通过发送IPI序列来启动AP,
并会将cpu_callout_mask的代表相应AP的位清零。


C代码  收藏代码
  1. static int __cpuinit  
  2. wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip)  
  3. {  
  4.     unsigned long send_status, accept_status = 0;  
  5.     int maxlvt, num_starts, j;  
  6.   
  7.     maxlvt = lapic_get_maxlvt();  
  8.   
  9.     /* 
  10.      * Be paranoid about clearing APIC errors. 
  11.      */  
  12.     if (APIC_INTEGRATED(apic_version[phys_apicid])) {  
  13.         if (maxlvt > 3)      /* Due to the Pentium erratum 3AP.  */  
  14.             apic_write(APIC_ESR, 0);  
  15.         apic_read(APIC_ESR);  
  16.     }  
  17.   
  18.     pr_debug("Asserting INIT.\n");  
  19.   
  20.     /* 
  21.      * Turn INIT on target chip 
  22.      */  
  23.     /* 
  24.      * Send IPI 
  25.      */  
  26.     apic_icr_write(APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT,  
  27.                phys_apicid);  
  28.   
  29.     pr_debug("Waiting for send to finish...\n");  
  30.     send_status = safe_apic_wait_icr_idle();  
  31.   
  32.     mdelay(10);  
  33.   
  34.     pr_debug("Deasserting INIT.\n");  
  35.   
  36.     /* Target chip */  
  37.     /* Send IPI */  
  38.     apic_icr_write(APIC_INT_LEVELTRIG | APIC_DM_INIT, phys_apicid);  
  39.   
  40.     pr_debug("Waiting for send to finish...\n");  
  41.     send_status = safe_apic_wait_icr_idle();  
  42.   
  43.     mb();  
  44.     atomic_set(&init_deasserted, 1);  
  45.   
  46.     /* 
  47.      * Should we send STARTUP IPIs ? 
  48.      * 
  49.      * Determine this based on the APIC version. 
  50.      * If we don't have an integrated APIC, don't send the STARTUP IPIs. 
  51.      */  
  52.     if (APIC_INTEGRATED(apic_version[phys_apicid]))  
  53.         num_starts = 2;  
  54.     else  
  55.         num_starts = 0;  
  56.   
  57.     /* 
  58.      * Paravirt / VMI wants a startup IPI hook here to set up the 
  59.      * target processor state. 
  60.      */  
  61.     startup_ipi_hook(phys_apicid, (unsigned long) start_secondary,  
  62.              (unsigned long)stack_start.sp);  
  63.   
  64.     /* 
  65.      * Run STARTUP IPI loop. 
  66.      */  
  67.     pr_debug("#startup loops: %d.\n", num_starts);  
  68.   
  69.     for (j = 1; j <= num_starts; j++) {  
  70.         pr_debug("Sending STARTUP #%d.\n", j);  
  71.         if (maxlvt > 3)      /* Due to the Pentium erratum 3AP.  */  
  72.             apic_write(APIC_ESR, 0);  
  73.         apic_read(APIC_ESR);  
  74.         pr_debug("After apic_write.\n");  
  75.   
  76.         /* 
  77.          * STARTUP IPI 
  78.          */  
  79.   
  80.         /* Target chip */  
  81.         /* Boot on the stack */  
  82.         /* Kick the second */  
  83.         apic_icr_write(APIC_DM_STARTUP | (start_eip >> 12),  
  84.                    phys_apicid);  
  85.   
  86.         /* 
  87.          * Give the other CPU some time to accept the IPI. 
  88.          */  
  89.         udelay(300);  
  90.   
  91.         pr_debug("Startup point 1.\n");  
  92.   
  93.         pr_debug("Waiting for send to finish...\n");  
  94.         send_status = safe_apic_wait_icr_idle();  
  95.   
  96.         /* 
  97.          * Give the other CPU some time to accept the IPI. 
  98.          */  
  99.         udelay(200);  
  100.         if (maxlvt > 3)      /* Due to the Pentium erratum 3AP.  */  
  101.             apic_write(APIC_ESR, 0);  
  102.         accept_status = (apic_read(APIC_ESR) & 0xEF);  
  103.         if (send_status || accept_status)  
  104.             break;  
  105.     }  
  106.     pr_debug("After Startup.\n");  
  107.   
  108.     if (send_status)  
  109.         printk(KERN_ERR "APIC never delivered???\n");  
  110.     if (accept_status)  
  111.         printk(KERN_ERR "APIC delivery error (%lx).\n", accept_status);  
  112.   
  113.     return (send_status | accept_status);  
  114. }  



一段wakeup_secondary_cpu_via_init执行的log
C代码  收藏代码
  1. 656 CPU17: has booted.  
  2. 657 WP output: cpu :18  
  3. 658 ------native_cpu_up cpu:18, apicid:18----------  
  4. 659 ------------in 3 do_boot_cpu------- #18  
  5. 660 Asserting INIT.  
  6. 661 Waiting for send to finish...  
  7. 662 Deasserting INIT.  
  8. 663 Waiting for send to finish...  
  9. 664 #startup loops: 2.  
  10. 665 Sending STARTUP #1.  
  11. 666 After apic_write.  
  12. 667 Startup point 1.  
  13. 668 Waiting for send to finish...  
  14. 669 Sending STARTUP #2.  
  15. 670 After apic_write.  
  16. 671 Startup point 1.  
  17. 672 Waiting for send to finish...  
  18. 673 in the cpu_init())  
  19. 674 After Startup.  
  20. 675 Before Callout 18.  
  21. 676 After Callout 18.  
  22. 677 cpu is: 12  
  23. 678 in the enable_x2apic()  
  24. 679 ------in x2apic_phys_get_apic_id-----  
  25. 680 CPU#18 (phys ID: 18) waiting for CALLOUT  
  26. 681 CALLIN, before setup_local_APIC().  
  27. 682 ------3------  
  28. 683 Stack at about ffff88021f953f44  
  29. 684 ------in x2apic_phys_get_apic_id-----  
  30. 685 CPU18: has booted.  

wakeup_secondary_cpu_via_init是与硬件相关的代码,它的主要作用是通过发送INIT-INIT-Startup IPI序列来将AP从halted的状态唤醒并让它开始执行代码start_eip所指向的代码。
Startup IPI会有一个域来指定需要执行代码的地址:apic_icr_write(APIC_DM_STARTUP | (start_eip >> 12), phys_apicid);
如果想彻底搞清楚一段代码,请去看Intel文档。


start_secondary是AP会执行的代码,这段代码通过smp_callin来将设定cpu_callin_mask来告诉BSP它已经启动。start_secondary最后是idle循环。
C代码  收藏代码
  1. /* 
  2.  * Activate a secondary processor. 
  3.  */  
  4. notrace static void __cpuinit start_secondary(void *unused)  
  5. {  
  6.     /* 
  7.      * Don't put *anything* before cpu_init(), SMP booting is too 
  8.      * fragile that we want to limit the things done here to the 
  9.      * most necessary things. 
  10.      */  
  11.     vmi_bringup();  
  12.     cpu_init();  
  13.     preempt_disable();  
  14.     smp_callin();  
  15.   
  16.     /* otherwise gcc will move up smp_processor_id before the cpu_init */  
  17.     barrier();  
  18.     /* 
  19.      * Check TSC synchronization with the BP: 
  20.      */  
  21.     check_tsc_sync_target();  
  22.   
  23.     if (nmi_watchdog == NMI_IO_APIC) {  
  24.         disable_8259A_irq(0);  
  25.         enable_NMI_through_LVT0();  
  26.         enable_8259A_irq(0);  
  27.     }  
  28.   
  29. #ifdef CONFIG_X86_32  
  30.     while (low_mappings)  
  31.         cpu_relax();  
  32.     __flush_tlb_all();  
  33. #endif  
  34.   
  35.     /* This must be done before setting cpu_online_mask */  
  36.     set_cpu_sibling_map(raw_smp_processor_id());  
  37.     wmb();  
  38.   
  39.     /* 
  40.      * We need to hold call_lock, so there is no inconsistency 
  41.      * between the time smp_call_function() determines number of 
  42.      * IPI recipients, and the time when the determination is made 
  43.      * for which cpus receive the IPI. Holding this 
  44.      * lock helps us to not include this cpu in a currently in progress 
  45.      * smp_call_function(). 
  46.      * 
  47.      * We need to hold vector_lock so there the set of online cpus 
  48.      * does not change while we are assigning vectors to cpus.  Holding 
  49.      * this lock ensures we don't half assign or remove an irq from a cpu. 
  50.      */  
  51.     ipi_call_lock();  
  52.     lock_vector_lock();  
  53.     __setup_vector_irq(smp_processor_id());  
  54.     set_cpu_online(smp_processor_id(), true);  
  55.     unlock_vector_lock();  
  56.     ipi_call_unlock();  
  57.     per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;  
  58.   
  59.     /* enable local interrupts */  
  60.     local_irq_enable();  
  61.   
  62.     x86_cpuinit.setup_percpu_clockev();  
  63.   
  64.     wmb();  
  65.     cpu_idle();  


阅读(10324) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

my0929my2015-02-05 12:01:11

能解释一下 IPI的缩写?

my0929my2015-02-05 12:01:04

能解释一下 IPI的缩写?