版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
linuxSMP啟動過程學(xué)習(xí)筆記
1.SMP硬件體系結(jié)構(gòu):
對于SMP最簡單可以理解為系統(tǒng)存在多個(gè)完全相同的CPU,所
有CPU共享總線,擁有自己的寄存器。對于內(nèi)存和外部設(shè)備訪問,由
于共享總線,所以是共享的。Linux操作系統(tǒng)多個(gè)CPU共享在系統(tǒng)空
間上映射相同,是完全又樗的。
由于系統(tǒng)中存在多個(gè)CPU,這是就引入一個(gè)問題,當(dāng)外部設(shè)備產(chǎn)
生中斷的時(shí)候,具體有哪一個(gè)CPU進(jìn)行處理?
為此,intel公司提出了10APCI和LOCALAPCI的體系結(jié)構(gòu)。
10APIC連接各個(gè)外部設(shè)備,并可以設(shè)置分發(fā)類型,根據(jù)設(shè)定的
分發(fā)類型,中斷信號發(fā)送的對應(yīng)CPU的LOCALAPIC上。
LOCALAPIC負(fù)責(zé)本地CPU的中斷處理,LOCALAPIC不僅可以
接受10APIC的中斷,也需要處理本地CPU產(chǎn)生的異常。同時(shí)
LOCALAPIC還提供了一個(gè)定時(shí)器。
如何確定那個(gè)CPU是引導(dǎo)CPU?
根據(jù)intel公司中的資料,系統(tǒng)上電后,會根據(jù)MPInitialization
Protocol隨機(jī)選擇一個(gè)CPU作為BSP,只有BSP會運(yùn)行BIOS程序,
其他AP都進(jìn)入等待狀態(tài),BSP發(fā)送IPI中斷觸發(fā)后才可以運(yùn)行。具
體的MPInitializationProtocol細(xì)節(jié),可以參考Intel?64andIA-32
ArchitecturesSoftwareDeveloper'sManualVolume3A:System
ProgrammingGuide,Part1第8章。
引導(dǎo)CPU如何控制其他CPU開始運(yùn)行?
BSP可以通過IPI消息控制AP從指定的起始地址運(yùn)行。CPU中
集成的LOCALAPIC提供了這個(gè)功能??梢酝ㄟ^寫LOCALAPIC中提
供的相關(guān)寄存器,發(fā)送IPI消息到指定的CPU上。
如何獲取系統(tǒng)硬件CPU信息的?
在系統(tǒng)初始化后,硬件會在內(nèi)存的規(guī)定位置提供關(guān)于CPU,總線,
等的信息,即在初始化的過程,會
10APICSMPMPtable0linux
讀取該位置,獲取系統(tǒng)相關(guān)的硬件信息。
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的引導(dǎo)代碼。
get_smp_config();//根據(jù)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();
針對每個(gè)調(diào)用
CPUdo_boot_cpu(apicidzcpu)
smpJnitQ;//每個(gè)CPU開始進(jìn)行調(diào)度
trampoline.SAP引導(dǎo)代碼,為16進(jìn)制代碼,啟用保護(hù)模式
head.s為AP創(chuàng)建分頁管理
initialize_secondary根據(jù)之前fork創(chuàng)建設(shè)置的信息,跳轉(zhuǎn)到
start_secondary處
start-secondary判斷BSP是否啟動,如果啟動AP進(jìn)行任務(wù)調(diào)
度。
3.代碼學(xué)習(xí)總結(jié)
find_smp_config();,查找MPtable在內(nèi)存中的位置。具體協(xié)議
可以參考MP協(xié)議的第4章。
這個(gè)表的作用在于描述系統(tǒng)CPU,總線,10APIC等的硬件信息。
相關(guān)的兩個(gè)全局變量:smp_found_config是否找到SMPMP
table,mpf_foundSMPMPtable的線性地址。
smp_alloc_memory()為啟動AP的啟動程序分配內(nèi)存空間。相
關(guān)全局變量trampoline.base,分配的啟動地址的線性地址。
get_smp_config()根據(jù)MPtable中提供的內(nèi)容,獲取硬件的信
息。
init_apic_mappings();獲取10APIC和LOCALAPIC的映射地
址。
z叩」ow_mappings();如果沒有定義SMP的話,清楚用戶空間
的地址映射。將sw叩per_pg_dir中表項(xiàng)清零。
harmlessforothers
mov%cs,%ax#Codeanddatainthesameplace
mov%ax,%ds
cli#Weshouldbesafeanyway
movl$0xA5A5A5A5,trampoline_data-r_base
這個(gè)是設(shè)置標(biāo)識,以便BSP知道AP運(yùn)行到這里了。
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)
啟動保護(hù)模式,翳陣專到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:
在這段代碼中,設(shè)置標(biāo)識,以便BSP知道該AP已經(jīng)運(yùn)行到這段
代碼,加載GDT和LDT表基址。
然后啟動保護(hù)模式,勵(lì)陣專到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設(shè)置為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執(zhí)行fork創(chuàng)建的進(jìn)程內(nèi)核堆棧部分,以便后續(xù)跳轉(zhuǎn)到
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啟動的話,就調(diào)用initialize_secondary函數(shù)。
void_devinitinitialize_secondary(void)
asmvolatile(
"movl%Oz%%esp\n\t"
"jmp*%1"
*
:"r"(current->thread.esp)/"r"(current->thread.eip));
)
設(shè)置堆棧為fork創(chuàng)建時(shí)的堆棧,ip為fork時(shí)的ip,這樣就卻桀
的了start_secondary。
start_secondary函數(shù)中處理如下:
while(!cpu_isset(smp_processorjd()/
smp_commenced_mask))
rep_nop();
進(jìn)行smp_commenced_mask判斷,是否啟動AP運(yùn)行。
smp_commenced_mask在smp_init()中設(shè)置。
cpu_idle();
如果啟動了,調(diào)用cpujdle進(jìn)行任務(wù)調(diào)度。
SMP(對稱多處理器)啟動流程--轉(zhuǎn)載
本文系本站原創(chuàng),歡迎轉(zhuǎn)載!
轉(zhuǎn)載請注明出處:
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
〃根據(jù)bx值指示是主cpu(bx=0)還是次cpu(bx=l)
〃然后會有不同的執(zhí)行路徑
/這里是其他次cpu執(zhí)行路徑
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的執(zhí)行路徑
#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
這個(gè)其實(shí)就是跳到start_kernel函數(shù)。
asmlinkagevoidstart_kernel(void)
(
char*commandline;
#ifdef_SMP_
staticintfirst_cpu=l;
〃這個(gè)不是函數(shù)局部變量,是函數(shù)靜態(tài)變量,主cpu執(zhí)行這個(gè)函
數(shù)時(shí)復(fù)位為1,其他cpu為0,因?yàn)橹鱟pu總是第一個(gè)執(zhí)行這個(gè)函數(shù)
的。
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();
〃這個(gè)等待主cpu給大家發(fā)送開始信號
cpujdle(NULL);
〃這個(gè)是ide進(jìn)程。
)
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);
〃這個(gè)可以看成是自旋鎖,等待主cpu發(fā)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發(fā)開始信號是在init函數(shù)中調(diào)用smp.begin
函數(shù):
staticvoidsmp_begin(){
smp_threads_ready=l;
smp_commence();
〃這個(gè)會通過IPI給各個(gè)次cpu發(fā)送相關(guān)中斷來通信
)
每個(gè)cpu有一個(gè)current指針。
剛開始的時(shí)候由主cpu賦值為init.task;
在主cpu調(diào)用schedjnit賦值。
voidsched_init(void)
(
intcpu=smp_processor_id();〃這個(gè)為0,因?yàn)槭侵鱟pu才調(diào)用。
#ifndef_SMP_
current_set[cpu]=&init_task;
#else
init_cessor=cpu;
〃這個(gè)是將init.task標(biāo)志為主cpu在運(yùn)行。
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);
)
同時(shí)這些還會在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);
〃這個(gè)其實(shí)就是創(chuàng)建線程然后這個(gè)線程體現(xiàn)在task[i]±T,因?yàn)?/p>
創(chuàng)建的時(shí)候的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();
)
)
上面執(zhí)行完后就給每個(gè)cpu加了一個(gè)idle任務(wù)。
然后kernel_thread(init/NULL,0)創(chuàng)建的init任務(wù)。
〃每個(gè)cpu在時(shí)間中斷時(shí)都可能調(diào)用這個(gè)共同的函數(shù)。
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;
〃獲取進(jìn)程雙向鏈表的一個(gè)節(jié)點(diǎn)。
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
〃當(dāng)回到init_task時(shí)說明已經(jīng)查找為所有的了。
intweight=goodness(p/prevzthis_cpu);
if(weight>c)
c=weight,next=p;
p=p->next_run;
)
〃這個(gè)是查找所有的task,找出最合適的task來調(diào)度。
if(!c){
for_each_task(p)
p->counter=(p->counter>>1)+p->priority;
)
#ifdef_SMP__
next->processor=this_cpu;
〃將這個(gè)將要被執(zhí)行的processor標(biāo)識為這個(gè)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變量,在單核中肯定就是一個(gè)變量,在
多核中肯定是各個(gè)cpu有自己的current:
其定義如下:
#definecurrent(0+current_set[smp_processor_id()]
在smp中current是current.set數(shù)組中的一個(gè)元素,是指具體
一個(gè)cpu的當(dāng)前進(jìn)程。
從上面可以看出一個(gè)cpu是從全局task找一個(gè)task來運(yùn)行,每
個(gè)cpu有一個(gè)idle.task,這個(gè)task的編號是固定的。
所有的task可以通過init_task來找到,因?yàn)閯?chuàng)建新進(jìn)程(內(nèi)核線
程)的時(shí)候,會將新建的掛到鏈表上。
而init_task是靜態(tài)掛在這上面的。
附上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
);
故這個(gè)p=init_task.next_run;
p可以獲取到所有在就緒狀態(tài)的task;
#linux#smp#多核#多cpu#調(diào)度#啟動#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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 深度解析(2026)GBT 19216.2-2021在火焰條件下電纜或光纜的線路完整性試驗(yàn) 第2部分:火焰溫度不低于830 ℃的供火并施加沖擊振動額定電壓0.61 kV及以下外徑不超過20 mm電纜的試驗(yàn)方法2026
- 工業(yè)地坪項(xiàng)目可行性分析報(bào)告范文(總投資18000萬元)
- 儲存IC項(xiàng)目可行性分析報(bào)告范文(總投資3000萬元)
- 項(xiàng)目副總監(jiān)面試題及答案
- 電信行業(yè)人事部經(jīng)理面試題庫及解析
- 安全巡視員考試題
- 深度解析(2026)《GBT 18850-2002工業(yè)用金屬絲篩網(wǎng) 技術(shù)要求和檢驗(yàn)》
- 個(gè)人理財(cái)規(guī)劃師證書考試復(fù)習(xí)資料及重點(diǎn)串講含答案
- 聲學(xué)計(jì)量儀器項(xiàng)目可行性分析報(bào)告范文
- 公路工程注冊建造師面試要點(diǎn)與答案
- 水利工程運(yùn)維投標(biāo)方案(堤防、閘站、泵站)(技術(shù)標(biāo))
- 鐵路工程道砟購銷
- 2024年廣東省廣州市中考?xì)v史真題(原卷版)
- 壯醫(yī)藥線療法
- 超星爾雅學(xué)習(xí)通《中國古代史(中央民族大學(xué))》2024章節(jié)測試答案
- 項(xiàng)目4任務(wù)1-斷路器開關(guān)特性試驗(yàn)
- (高清版)DZT 0215-2020 礦產(chǎn)地質(zhì)勘查規(guī)范 煤
- 高層建筑消防安全培訓(xùn)課件
- 實(shí)驗(yàn)診斷學(xué)病例分析【范本模板】
- 西安交大少年班真題
- JJF(石化)006-2018漆膜彈性測定器校準(zhǔn)規(guī)范
評論
0/150
提交評論