版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
linuxSMP启动过程学习笔记
1.SMP硬件体系结构:
对于SMP最简单可以理解为系统存在多个完全相同的CPU,所
有CPU共享总线,拥有自己的寄存器。对于内存和外部设备访问,由
于共享总线,所以是共享的。Linux操作系统多个CPU共享在系统空
间上映射相同,是完全又樗的。
由于系统中存在多个CPU,这是就引入一个问题,当外部设备产
生中断的时候,具体有哪一个CPU进行处理?
为此,intel公司提出了10APCI和LOCALAPCI的体系结构。
10APIC连接各个外部设备,并可以设置分发类型,根据设定的
分发类型,中断信号发送的对应CPU的LOCALAPIC上。
LOCALAPIC负责本地CPU的中断处理,LOCALAPIC不仅可以
接受10APIC的中断,也需要处理本地CPU产生的异常。同时
LOCALAPIC还提供了一个定时器。
如何确定那个CPU是引导CPU?
根据intel公司中的资料,系统上电后,会根据MPInitialization
Protocol随机选择一个CPU作为BSP,只有BSP会运行BIOS程序,
其他AP都进入等待状态,BSP发送IPI中断触发后才可以运行。具
体的MPInitializationProtocol细节,可以参考Intel?64andIA-32
ArchitecturesSoftwareDeveloper'sManualVolume3A:System
ProgrammingGuide,Part1第8章。
引导CPU如何控制其他CPU开始运行?
BSP可以通过IPI消息控制AP从指定的起始地址运行。CPU中
集成的LOCALAPIC提供了这个功能。可以通过写LOCALAPIC中提
供的相关寄存器,发送IPI消息到指定的CPU上。
如何获取系统硬件CPU信息的?
在系统初始化后,硬件会在内存的规定位置提供关于CPU,总线,
等的信息,即在初始化的过程,会
10APICSMPMPtable0linux
读取该位置,获取系统相关的硬件信息。
2.linuxSMP启动过程流程简介
setup_arch()
setup_memory();
reserve_bootmem(PAGE_SIZEzPAGE_SIZE);
find_smp_config();//查找smpmptable的位置
smp_alloc_memory();
trampoline_base=(void*)
alloc_bootmem_low_pages(PAGE_SIZE);〃分酉己trampoline,用于
启动AP的引导代码。
get_smp_config();//根据smpmptable,获取具体的硬件信
息
trap_init()
init_apic_mappings();
mem_init()
zap_low_mappings();如果没有定义SMP的话,清楚用户空间
的地址映射。
rest_init();
kernel_thread(initNULL,CLONE_FS|CLONE_SIGHAND);
init();
set_cpus_a11owed(current,CPU_MASK_ALL);
smp_prepare_cpus(max_cpus);
smp_boot_cpus(max_cpus);
connect_bsp_APIC();
//初始化的
setup_local_APIC();BSPLOCALAPCIo
map_cpu_to_logical_apicid();
针对每个调用
CPUdo_boot_cpu(apicidzcpu)
smpJnitQ;//每个CPU开始进行调度
trampoline.SAP引导代码,为16进制代码,启用保护模式
head.s为AP创建分页管理
initialize_secondary根据之前fork创建设置的信息,跳转到
start_secondary处
start-secondary判断BSP是否启动,如果启动AP进行任务调
度。
3.代码学习总结
find_smp_config();,查找MPtable在内存中的位置。具体协议
可以参考MP协议的第4章。
这个表的作用在于描述系统CPU,总线,10APIC等的硬件信息。
相关的两个全局变量:smp_found_config是否找到SMPMP
table,mpf_foundSMPMPtable的线性地址。
smp_alloc_memory()为启动AP的启动程序分配内存空间。相
关全局变量trampoline.base,分配的启动地址的线性地址。
get_smp_config()根据MPtable中提供的内容,获取硬件的信
息。
init_apic_mappings();获取10APIC和LOCALAPIC的映射地
址。
z叩」ow_mappings();如果没有定义SMP的话,清楚用户空间
的地址映射。将sw叩per_pg_dir中表项清零。
harmlessforothers
mov%cs,%ax#Codeanddatainthesameplace
mov%ax,%ds
cli#Weshouldbesafeanyway
movl$0xA5A5A5A5,trampoline_data-r_base
这个是设置标识,以便BSP知道AP运行到这里了。
lidtlbootjdt-r_base#loadidtwith0,0
Igdtlboot_gdt-r_base#loadgdtwithwhateveris
appropriate
加载Idt和gdt
xor%ax,%ax
inc%ax#protectedmode(PE)bit
Imsw%ax#intoprotectedmode
#flushprefetchandjumptostartup_32_smpin
arch/i386/kernel/head.S
Ijmpl$_BOOT_CSf$(startup_32_smp-_PAGE_OFFSET)
启动保护模式,翳阵专到startup_32_smp处
#Theseneedtobeinthesame64Ksegmentastheabove;
#hencewedon'tusetheboot_gdt_descrdefinedinhead.S
boot_gdt:
.word_BOOT_DS+7#gdtlimit
Jongboot_gdt_table-_PAGE_OFFSET#gdtbase
bootjdt:
.word0#idtlimit=0
Jong0#idtbase=OL
.globltrampoline_end
trampoline_end:
在这段代码中,设置标识,以便BSP知道该AP已经运行到这段
代码,加载GDT和LDT表基址。
然后启动保护模式,励阵专到startup_32_smp处。
Head.s部分代码:
ENTRY(startup_32_smp)
cld
movl$(_BOOT_DS),%eax
movl%eax,%ds
movl%eaxz%es
movl%eaxz%fs
movl%eaxf%gs
xorl%ebxz%ebx
incl%ebx
如果是AP的话,将bx设置为1
movl$swapper_pg_dir-_PAGE_OFFSET,%eax
movl%eaxz%cr3
movl%crOz%eax
orl$0x80000000,%eax
movl%eaxf%crO
Ijmp$_BOOT_CS,$lf
启用分页,
Issstack_start%esp
使esp执行fork创建的进程内核堆栈部分,以便后续跳转到
start_secondary
#ifdefCONFIG_SMP
movbready,%cl
movb$1,ready
cmpb$0,%cl
jeIf#thefirstCPUcallsstart_kernel
#allotherCPUscallinitialize_secondary
callinitialize_secondary
jmpL6
1:
#endif
callstart_kernel
如果是AP启动的话,就调用initialize_secondary函数。
void_devinitinitialize_secondary(void)
asmvolatile(
"movl%Oz%%esp\n\t"
"jmp*%1"
*
:"r"(current->thread.esp)/"r"(current->thread.eip));
)
设置堆栈为fork创建时的堆栈,ip为fork时的ip,这样就却桀
的了start_secondary。
start_secondary函数中处理如下:
while(!cpu_isset(smp_processorjd()/
smp_commenced_mask))
rep_nop();
进行smp_commenced_mask判断,是否启动AP运行。
smp_commenced_mask在smp_init()中设置。
cpu_idle();
如果启动了,调用cpujdle进行任务调度。
SMP(对称多处理器)启动流程--转载
本文系本站原创,欢迎转载!
转载请注明出处:
startup_32:
cld
cli
movl$(KERNEL_DS)z%eax
mov%axz%ds
mov%axz%es
mov%axz%fs
mov%axz%gs
#ifdef_SMP_
orw%bxz%bx#WhatstateareweinBX=1forSMP
#0forboot
jz2f#Initialboot
〃根据bx值指示是主cpu(bx=0)还是次cpu(bx=l)
〃然后会有不同的执行路径
/这里是其他次cpu执行路径
mov%ax,%ss
xorl%eax,%eax#Backto0
mov%cx,%ax#SPlow16bits
movl%eax/%esp
pushl0#ClearNT
popfl
Ijmp$(KERNEL_CS)Z$0x100000#IntoCandsanity
2:〃这里是主cpu的执行路径
#endif
IssSYMBOL_NAME(stack_start)z%esp
xorl%eax,%eax
1:incl%eax#checkthatA20reallyISenabled
movl%eax/0x000000#loopforeverifitisn't
cmpl%eax/0xl00000
jelb
pushl$0
popfl
xorl%eaxz%eax
movl$SYMBOL_NAME(_edata)z%edi
movl$SYMBOL_NAME(_end),%ecx
subl%edi,%ecx
cld
rep
stosb
subl$16z%esp#placeforstructureonthestack
pushl%esp#addressofstructureasfirstarg
callSYMBOL_NAME(decompress_kernel)
orl%eaxz%eax
jnz3f
xorl%ebxz%ebx
Ijmp$(KERNEL_CS),$0x100000
Ijmp$(KERNEL_CS),$0x100000
这个其实就是跳到start_kernel函数。
asmlinkagevoidstart_kernel(void)
(
char*commandline;
#ifdef_SMP_
staticintfirst_cpu=l;
〃这个不是函数局部变量,是函数静态变量,主cpu执行这个函
数时复位为1,其他cpu为0,因为主cpu总是第一个执行这个函数
的。
if(!first_cpu)
start_secondary();
〃对于
first_cpu=O;
#endif
setup_arch(&command_line,&memory_start/
&memory_end);
memory_start=paging_init(memory_start/memory_end);
trapjnit();
initJRQO;
sched_init();
time_init();
parse_options(command_line);
#ifdefCONFIG_MODULES
init_modules();
#endif
#ifdefCONFIG_PROFILE
if(!prof_shift)
#ifdefCONFIG_PROFILE_SHIFT
prof_shift=CONFIG_PROFILE_SHIFT;
#else
prof_shift=2;
#endif
#endif
if(prof_shift){
prof_buffer=(unsignedint*)memory_start;
prof_len=(unsignedlong)&_etext-(unsignedlong)&_stext;
prof_len>>=prof_shift;
memory_start+=prof_len*sizeof(unsignedint);
)
memory_start=console_init(memory_startzmemory_end);
#ifdefCONFIG_PCI
memory_start=pci_init(memory_start,memory_end);
#endif
memory_start=kmalloc_init(memory_start,memory_end);
sti();
calibrate_delay();
memory_start=inode_init(memory_start/memory_end);
memory_start=file_table_init(memory_startzmemory_end);
memory_start=
name_cachejnit(memory_start/memory_end);
#ifdefCONFIG_BLK_DEVJNITRD
if(initrd_start&&initrd_start<memory_start){
printk(KERN_CRIT"initrdoverwritten(0x%08lx<0x%08lx)-
II
"disablingit.\rT,initrd_start,memory_start);
initrd_start=0;
)
#endif
mem_init(memory_start/memory_end);
bufferJnitO;
sock_init();
#ifdefined(CONFIG_SYSVIPC)||defined(CONFIG_KERNELD)
ipc_init();
#endif
dquot_init();
arch_syms_export();
sti();
check_bugs();
printk(linux_banner);
#ifdef_SMP_
smp_init();
#endif
sysctlJnitQ;
kernel_thread(init,NULL,0);
cpu_idle(NULL);
)
asmlinkagevoidstart_secondary(void)
(
trap_init();
init」RQ();
〃初始化自己的irq
smp_callin();
〃这个等待主cpu给大家发送开始信号
cpujdle(NULL);
〃这个是ide进程。
)
voidsmp_callin(void)
(
externvoidcalibrate_delay(void);
intcpuid=GET_APIC_ID(apic_read(APICJD));
unsignedlongI;
SMP_PRINTK(("CALLIN%d\n"/smp_processor_id()));
I=apic_read(APIC_SPIV);
l|=d<<8);
apic_write(APIC_SPIVJ);
sti();
calibrate_delay();
smp_store_cpu_info(cpuid);
set_bit(cpuidz(unsignedlong*)&cpu_callin_map[O]);
load_ldt(0);
local_flush_tlb();
while(!smp_commenced);
〃这个可以看成是自旋锁,等待主cpu发smp_commenced信号
即开始信号。
if(cpu_number_map[cpuid]==-1)
while(l);
local_flush_tlb();
SMP_PRINTK((”Commenced..\n"));
load_TR(cpu_number_map[cpuid]);
)
intcpu_idle(void*unused)
(
for(;;)
idle。;
)
主cpu给各次cpu发开始信号是在init函数中调用smp.begin
函数:
staticvoidsmp_begin(){
smp_threads_ready=l;
smp_commence();
〃这个会通过IPI给各个次cpu发送相关中断来通信
)
每个cpu有一个current指针。
刚开始的时候由主cpu赋值为init.task;
在主cpu调用schedjnit赋值。
voidsched_init(void)
(
intcpu=smp_processor_id();〃这个为0,因为是主cpu才调用。
#ifndef_SMP_
current_set[cpu]=&init_task;
#else
init_cessor=cpu;
〃这个是将init.task标志为主cpu在运行。
for(cpu=0;cpu<NR_CPUS;cpu++)
current_set[cpu]=&init_task;
#endif
init_bh(TIMER_BH,timer_bh);
init_bh(TQUEUE_BH,tqueue_bh);
init_bh(IMMEDIATE_BH/immediate_bh);
)
同时这些还会在smp_init丰富。
staticvoidsmpjnit(void)
(
inti,j;
smp_boot_cpus();
for(i=l;i<smp_num_cpus;i++)
structtask_struct*n,*p;
j=cpu_logical_map[i];
kerneLthread^puJdle,NULL,CLONE_PID);
〃这个其实就是创建线程然后这个线程体现在task[i]±T,因为
创建的时候的task_struct就是从task[i]取的。
current_set[j]=task[i];
current_set[j]->processor=j;
cli();
n=task[i]->next_run;
p=task[i]->prev_run;
nr_running—;
n->prev_run=p;
p->next_run=n;
task[i]->next_run=task[i]->prev_run=task[i];
sti();
)
)
上面执行完后就给每个cpu加了一个idle任务。
然后kernel_thread(init/NULL,0)创建的init任务。
〃每个cpu在时间中断时都可能调用这个共同的函数。
asmlinkagevoidschedule(void)
(
intc;
structtask_struct*p;
structtask_struct*prev,*next;
unsignedlongtimeout=0;
intthis_cpu=smp_processor_id();
〃获取cpu.id;
if(intr_count)
gotoscheduling_in_interrupt;
if(bh_active&bh_mask){
intr_count=1;
do_bottom_half();
intr_count=0;
)
run_task_queue(&tq_scheduler);
need_resched=0;
prev=current;
cli();
if(!prev->counter&&prev->policy==SCHED_RR){
prev->counter=prev->priority;
move_last_runqueue(prev);
)
switch(prev->state){
caseTASKJNTERRUPTIBLE:
if(prev->signal&-prev->blocked)
gotomakerunnable;
timeout=prev->timeout;
if(timeout&&(timeout<=jiffies)){
prev->timeout=0;
timeout=0;
makerunnable:
prev->state=TASK_RUNNING;
break;
)
default:
del_from_runqueue(prev);
caseTASK_RUNNING:
)
p=init_task.next_run;
〃获取进程双向链表的一个节点。
sti();
#ifdef_SMP_
prev->processor=NO_PROC_ID;
#defineidle_task(task[cpu_number_map[this_cpu]])
#else
#defineidle_task(&init_task)
#endif
c=-1000;
next=idle_task;
while(p!=&init_task){
//p初始值为init_task.next_run
〃当回到init_task时说明已经查找为所有的了。
intweight=goodness(p/prevzthis_cpu);
if(weight>c)
c=weight,next=p;
p=p->next_run;
)
〃这个是查找所有的task,找出最合适的task来调度。
if(!c){
for_each_task(p)
p->counter=(p->counter>>1)+p->priority;
)
#ifdef_SMP__
next->processor=this_cpu;
〃将这个将要被执行的processor标识为这个cpu
next->last_processor=this_cpu;
#endif
#ifdef_SMP_PROF_
if(0==next->pid)
set_bit(this_cpuz&smp_idle_map);
else
cleajb什(this_cpu,&smp_idle_map);
#endif
if(prev!=next){
structtimer_listtimer;
kstat.context_swtch++;
if(timeout){
init_timer(&timer);
timer.expires=timeout;
timer.data=(unsignedlong)prev;
timer.function=process_timeout;
add_timer(&timer);
)
get_mmu_context(next);
switch_to(prev/next);
if(timeout)
del_timer(&timer);
)
return;
scheduling_in_interrupt:
printk("Aiee:schedulingininterrupt%p\n'\
_builtin_return_address(O));
)
上面需要注意的是current变量,在单核中肯定就是一个变量,在
多核中肯定是各个cpu有自己的current:
其定义如下:
#definecurrent(0+current_set[smp_processor_id()]
在smp中current是current.set数组中的一个元素,是指具体
一个cpu的当前进程。
从上面可以看出一个cpu是从全局task找一个task来运行,每
个cpu有一个idle.task,这个task的编号是固定的。
所有的task可以通过init_task来找到,因为创建新进程(内核线
程)的时候,会将新建的挂到链表上。
而init_task是静态挂在这上面的。
附上task_struct:
structtask_struct{
volatilelongstate;
longcounter;
longpriority;
unsignedlongsignal;
unsignedlongblocked;
unsignedlongflags;
interrno;
longdebugreg[8];
structexec_domain*exec_domain;
structlinux_binfmt*binfmt;
structtask_struct*next_taskz*prev_task;
structtask_struct*next_run,*prev_run;
unsignedlongsaved_kernel_stack;
unsignedlongkernel_stack_page;
intexit_code,exit.signal;
unsignedlongpersonality;
intdumpablel;
intdid_exec:l;
intpid;
intpgrp;
inttty_old_pgrp;
intsession;
intleader;
intgroups[NGROUPS];
structtask_struct*p_opptrz*p_pptrz*p_cptrz*p_ysptr;
*p_osptr;
structwait_queue*wait_chldexit;
unsignedshortuid,euid,suid,fsuid;
unsignedshortgid,egid,sgidjsgid;
unsignedlongtimeout,policy,rt_priority;
unsignedlongit_real_valuezit_prof_valuezit_virt_value;
unsignedlongit_real_incrzit_prof_incr;it_virt_incr;
structtimer_listreal_timer;
longutime,stimezcutime,cstime,start_time;
unsignedlongmin_fltzmaj_fltznswap,cmin_flt,cmaj_fltz
cnswap;
intswappable:l;
unsignedlongswap_address;
unsignedlongold_maj_flt;
unsignedlongdec_flt;
unsignedlongswap_cnt;
structrlimitrlim[RLIM_NLIMITS];
unsignedshortused_math;
charcomm[16];
intlink_count;
structtty_struct*tty;
structsem_undo*semundo;
structsem_queue*semsleeping;
structdesc_struct*ldt;
structthread_structtss;
structfs_struct*fs;
structfiles_struct*files;
structmm_struct*mm;
structsignal_struct*sig;
#ifdef_SMP__
intprocessor;
intlast_processor;
intlock_depth;
#endif
);
故这个p=init_task.next_run;
p可以获取到所有在就绪状态的task;
#linux#smp#多核#多cpu#调度#启动#it
3年前
SMP(对称多处理器)的启动流程
ThereareafewSMPrelatedmacros,likeCONFIG_SMR
CONFIG_X86_LOCAL_APICZCONFIG_X86_IO_APICZ
CONFIG_MULTIQUADandCONFIG_VISWS.Iwillignorecodethat
requiresCONFIG_MULTIQUADorCONFIG_VISWSZwhichmost
peopledon'tcare(ifnotusingIBMhigh-endmultiprocessor
serverorSGIVisualWorkstation).
BSPexecutesstart_kernel()->smp_init()->smp_bootcpus()
->do_boot_cpu()->wakeup_secondary_via_INIT()totriggerAPs.
CheckMultiProcessorSpecificationandIA-32ManualVol.3(Ch.7.
Multile-ProcessorManagement,andCh.8.Advanced
ProgrammableInterruptController)fortechnicaldetails.
8.1.Beforesmp_init()
Beforecallingsmp_init()rstart_kernel()didsomethingto
setupSMPenvironment:
start_kernel()
|—setup_arch()
||—parse_cmdline_early();//SMPlooksfor"nohtMand
"acpismp=force"
II-
||if(!memcmp(from,"noht",4)){
||disable_x86_ht=1;
||set_bit(X86_FEATURE_HTzdisabled_x86_caps);
II}
II
n
elseif(!niemcmp(from/"acpismp=force,13))
enable_acpi_smp_table=1;
|—setup_memory();//reservememoryforMP
configurationtable
|||—reserve_bootmem(PAGE_SIZEzPAGE_SIZE);
||find_smp_config();
find_intel_smp();
||smp_scan_config();
|—setflagsmp_found_config
|—setMPfloatingpointermpf_found
reserve_bootmem(mpf_found,PAGE_SIZE);
|—if(disable_x86_ht){//ifHyperThreadingfeature
disabled
||clear_bit(X86_FEATURE_HTz
&boot_cpu_data.x86_capability[0]);
||set_bit(X86_FEATURE_HTzdisabled_x86_caps);
||enable_acpi_smp_table=0;
II}
||-if(test_bit(X86_FEATURE_HT/
&boot_cpu_data.x86_capability[0]))
enable_acpi_smp_table=1;
|smp_alloc_memory();
II-
||trampoline_base=(void*)
alloc_bootmem_low_pages(PAGE_SIZE);
|get_smp_config();
|—config_acpi_tables();
||—memset(&acpi_boot_ops/0,sizeof(acpi_boot_ops));
|||—acpi_boot_ops[ACPI_APIC]=acpi_parse_madt;
II
if(enable_acpi_smp_table&&!acpi_tables_init())
have_acpi_tables=1;
|—setpic_mode
II
|—savelocalAPICaddressinmp_lapic_addr
scanforMPconfigurationtableentries,like
」
|MP_PROCESSORZMP_BUSZMPOAPIC,MPJNTSRC
andMP_LINTSRC.
|-trap_init();
init_apic_mappings();//setupPTEforAPIC
if(!smp_found_config&&detect_init_APIC()){
apic_phys=(unsignedlong)
alloc_bootmem_pages(PAGE_SIZE);
II叩ijphys=_pa(apic_phys);
||}else
||apic_phys=mp_lapic_addr;
叩
set_fixmap_nocache(FIX_APIC_BASE,ijphys);
|if(boot_cpu_physical_apicid==-1U)
boot_cpu_physical_apicid=
GET_APIC」D(apic_read(APIC」D));
//mapIOAPICaddresstouncacheablelinearaddress
set_fixmap_nocache(idxzioapic_phys);
//NowwecanuselinearaddresstoaccessAPICspace.
I-init」RQ();
I|-init_ISA_irqs();
III--
IIIinit_bsp_APIC();
||init_8259A(auto_eoi=0);
|setupSMP/APICinterrupthandlers,esp.IPL
mem_init();
、
IPI(InterProcessorInterrupt),CPU-to-CPUinterruptthrough
localAPIC,isthemechanismusedbyBSPtotriggerAPs.
Beawarethat"onelocalAPICperCPUisrequired"inanMP-
compliantsystem.ProcessorsdonotshareAPIClocalunits
addressspace(physicaladdressOxFEEOOOOO-OxFEEFFFFF),but
willshareAPICI/Ounits(OxFECOOOOO-OxFECFFFFF).Both
addressspacesareuncacheable.
8.2.smp_init()
BSPcallsstart_kernel()->smp_init()->smp_boot_cpus()to
setupdatastructuresforeachCPUandactivatetherestAPs.
///////////////I///////////////////////I///////////////////////I///
////////////
staticvoid_initsmp_init(void)
(
smp_boot_cpus();
wait_init_idle=cpu_online_map;
clear_bit(current->processor,&wait_init_idle);
smp_threads_ready=l;
smp_commence(){
Dprintk("Settingcommenced=l/gogogo\n");
wmb();
atomic_set(&smp_commencedzl);
)
printk("Waitingonwait_init_idle(map=Ox%lx)\nn,
wait_init_idle);
while(wait_init_idle){
cpu_relax();//i.e."rep;nop"
barrier();
)
printk("AIIprocessorshavedoneinit_idle\n");
)
〃〃〃〃//〃〃〃/〃〃〃〃〃/〃〃〃/〃/〃〃〃〃〃〃〃〃〃〃〃〃/〃〃
////////////
void_initsmp_boot_cpus(void)
(
〃...somethingnotveryinteresting:-)
prof_counter[O..NR_CPUS-l]=0;
prof_old_multiplier[O..NR_CPUS-l]=0;
prof_multiplier[O..NR_CPUS-l]=0;
init_cpu_to_apicid(){
physical_apicid_2_cpu[0..MAX_APICID-l]=-1;
logical_apicid_2_cpu[0..MAX_APICID-l]=-1;
cpu_2_physical_apicid[0..NR_CPUS-l]=0;
cpu_2_logical_apicid[0..NR_CPUS-l]=0;
)
smp_store_cpu_info(0);
printk(nCPU%d:",0);
print_cpu_info(&cpu_data[0]);
set_bit(0,&cpu_online_map);
boot_cpu_logical_apicid=logical_smp_processor_id(){
GET_APIC_LOGICALJD(*(unsignedlong
*)(APIC_BASE+APIC_LDR));
)
map_cpu_to_boot_apicid(0/boot_cpu_apicid){
physical_apicid_2_cpu[boot_cpu_apicid]=0;
cpu_2_physical_apicid[0]=boot_cpu_apicid;
)
global_irq_holder=0;
current->processor=0;
init_idle();//willclearcorrespondingbitinwait_init_idle
smp_tune_scheduling();
//...someconditionschecked
connect_bsp_APIC();//enableAPICmodeifusedtobePIC
mode
setup_local_APIC();
if(GET_APIC_ID(apic_read(APIC_ID))!=
boot_cpu_physical_apicid)
BUG();
Dprintk("CPUpresentmap:%lx\n”,phys_cpu_present_map);
for(bit=0;bit<NR_CPUS;bit++){
apicid=cpu_present_to_apicid(bit);
if(apicid==boot_cpu_apicid)
continue;
if(!(phys_cpu_present_map&(1<<bit)))
continue;
if((max_cpus>=0)&&(max_cpus<=cpucount+1))
continue;
do_boot_cpu(apicid);
if((boot_apicid_to_cpu(apicid)==-1)&&
(phys_cpu_present_map&(1<<bit)))
printk("CPU#%dnotresponding-cannotuseit.\n"z
apicid);
)
//...SMPBogoMIPS
//...Bsteppingprocessorwarning
//...HyperThreadinghandling
setup_APIC_clocks();
if(cpu_has_tsc&&cpucount)
synchronize_tsc_bp();
smp_done:
zap_low_mappings();
)
〃〃〃〃〃/〃〃〃〃/〃/〃〃/〃〃〃〃〃〃〃/〃〃/〃/〃〃〃//〃〃/〃/
////////////
staticvoid_initdo_boot_cpu(intapicid)
cpu=++cpucount;
//1.prepare"idleprocess"taskstructfornextAP
if(fork_by_hand()<0)
panic("failedforkforCPU%d",cpu);
idle=init_task.prev_task;
if(!idle)
panic(HNoidleprocessforCPU%dn,cpu);
idle->processor=cpu;
idle->cpus_runnable=1<<cpu;〃onlyonthisAP!
map_cpu_to_boot_apicid(cpu,apicid){
physical_apicid_2_cpu[apicid]=cpu;
cpu_2_physical_apicid[cpu]=apicid;
)
idle->thread.eip=(unsignedlong)start_secondary;
del_from_runqueue(idle);
unhash_process(idle);
init_tasks[cpu]=idle;
//2.preparestackandcode(CS:IP)fornextAP
start_eip=setup_trampoline(){
memcpy(trampoline_base,trampoline_data,
trampoline_end-trampoline_data);
returnvirt_to_phys(trampoline_base);
)
nn
printk(Bootingprocessor%d/%deip%lx\n#cpu,apicid,
start_eip);
stack_start.esp=(void*)(1024+PAGE_SIZE+(char*)idle);
atomic_set(&init_deassertedz0);
Dprintk("Settingwarmresetcodeandvector.\n");
CMOS.WRITECOxa,Oxf);
local_flush_tlb();
Dprintk("l.\nn);
*((volatileunsignedshort*)TRAMPOLINE_HIGH)=
start_eip>>4;
Dprintk("2.\nn);
*((volatileunsignedshort*)TRAMPOLINE_LOW)=start_eip
&Oxf;
Dprintk("3.\nn);
//wehavesetup0:467tostart_eip(trampoline_base)
//3.kickAPtorun(APgetsCS:IPfrom0:467)
//StartingactualIPIsequence...
boot_error=wakeup_secondary_via_INIT(apicid/start_eip);
if(!boot_error){//looksOK
set_bit(cpuz&cpu_callout_map);
//bitcpuincpu_callin_mapissetbyAPinsmp_callin()
if(test_bit(cpu,&cpu_callin_map)){
print_cpu_info(&cpu_data[cpu]);
}else{
boot_error=1;
//marker0xA5setbyAPintrampoline_data()
if(*((volatileunsignedchar*)phys_to_virt(8192))
==0xA5)
printk(nStuck??\n");
else
printk("Notresponding.\nn);
)
)
if(boot_error){
unmap_cpu_to_boot_apicid(cpu,apicid);
clear_bit(cpu#&cpu_callout_map);
clear_bit(cpu,&cpu_initialized);
clear_bit(cpu,&cpu_online_map);
cpucount--;
)
*((volatileunsignedlong*)phys_to_virt(8192))=0;
)
Don'tconfusestart_secondary()withtrampoline_data().The
formerisAP"idle'processtaskstructEIPvalue,andthelatteris
thereal-modecodethatAPrunsafterBSPkicksit(using
wakeup_secondary_via_INIT()).
8.3.linux/arch/i386/kernel/trampoline.S
Thisfilecontainsthe16-bitreal-modeAPstartupcode.BSP
reservedmemoryspacetrampoline_baseinstart_kernel()->
setup_arch()->smp_alloc_memory().BeforeBSPtriggersARit
copiesthetrampolinecode,betweentrampoline_dataand
trampoline_end,totrampoline_base(indo_boot_cpu()->
setup_trampolineO).BSPsetsup0:
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年湛江市公安局霞山分局第三次招聘警务辅助人员的备考题库完整答案详解
- 贵阳市公安机关2025年面向社会公开招聘第三批警务辅助人员备考题库及一套答案详解
- 2025 九年级语文上册《创造宣言》不能 与 能 对比课件
- 2025长寿革命:迎接新现实研究报告 The longevity revolution Preparing for a new reality
- 2025湖北恩施州宣恩县供销集团有限公司招聘1人模拟笔试试题及答案解析
- 2025年文山州富宁县田蓬镇第二卫生院招聘编外专业技术人员(3人)备考考试试题及答案解析
- 2025四川大学华西三亚医院(考核)招聘事业编和员额制卫生专业技术人员招聘36人(第1号)备考考试试题及答案解析
- 2025锦州市教育局所属学校赴高校(辽宁师范大学同层次及以上)现场公开招聘工作人员(教师)119人笔试考试备考试题及答案解析
- 2025中国人民财产保险股份有限公司双河支公司招聘1人模拟笔试试题及答案解析
- 2025广西贵港桂平市大洋镇中心卫生院公开招聘编外工作人员4人备考笔试试题及答案解析
- 水利工程运维投标方案(堤防、闸站、泵站)(技术标)
- 铁路工程道砟购销
- 2024年广东省广州市中考历史真题(原卷版)
- 壮医药线疗法
- 超星尔雅学习通《中国古代史(中央民族大学)》2024章节测试答案
- 项目4任务1-断路器开关特性试验
- (高清版)DZT 0215-2020 矿产地质勘查规范 煤
- 高层建筑消防安全培训课件
- 实验诊断学病例分析【范本模板】
- 西安交大少年班真题
- JJF(石化)006-2018漆膜弹性测定器校准规范
评论
0/150
提交评论