歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux SMP 啟動過程學習筆記

Linux SMP 啟動過程學習筆記

日期:2017/2/28 16:05:39   编辑:Linux教程

1. SMP硬件體系結構:

對於SMP最簡單可以理解為系統存在多個完全相同的CPU,所有CPU共享總線,擁有自己的寄存器。對於內存和外部設備訪問,由於共享總線,所以是共享的。Linux操作系統多個CPU共享在系統空間上映射相同,是完全對等的。

由於系統中存在多個CPU,這是就引入一個問題,當外部設備產生中斷的時候,具體有哪一個CPU進行處理?

為此,intel公司提出了IO APCI和LOCAL APCI的體系結構。

IO APIC連接各個外部設備,並可以設置分發類型,根據設定的分發類型,中斷信號發送的對應CPU的LOCAL APIC上。

LOCAL APIC負責本地CPU的中斷處理,LOCAL APIC不僅可以接受IO APIC的中斷,也需要處理本地CPU產生的異常。同時LOCAL APIC還提供了一個定時器。

如何確定那個CPU是引導CPU?

根據intel公司中的資料,系統上電後,會根據MP Initialization Protocol隨機選擇一個CPU作為BSP,只有BSP會運行BIOS程序,其他AP都進入等待狀態,BSP發送IPI中斷觸發後才可以運行。具體的MP Initialization Protocol細節,可以參考Intel? 64 and IA-32 Architectures Software Developer’s Manual Volume 3A: System Programming Guide, Part 1 第8章。

引導CPU如何控制其他CPU開始運行?

BSP可以通過IPI消息控制AP從指定的起始地址運行。CPU中集成的LOCAL APIC提供了這個功能。可以通過寫LOCAL APIC中提供的相關寄存器,發送IPI消息到指定的CPU上。

如何獲取系統硬件CPU信息的?

在系統初始化後,硬件會在內存的規定位置提供關於CPU,總線, IO APIC等的信息,即SMP MP table。在linux初始化的過程,會讀取該位置,獲取系統相關的硬件信息。

2. linux SMP啟動過程流程簡介

setup_arch()

setup_memory();

reserve_bootmem(PAGE_SIZE, PAGE_SIZE);

find_smp_config(); //查找smp mp table的位置

smp_alloc_memory();

trampoline_base = (void *) alloc_bootmem_low_pages(PAGE_SIZE); //分配trampoline,用於啟動AP的引導代碼。

get_smp_config(); //根據smp mp table,獲取具體的硬件信息

trap_init()

init_apic_mappings();

mem_init()

zap_low_mappings(); 如果沒有定義SMP的話,清楚用戶空間的地址映射。

rest_init();

kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);

init();

set_cpus_allowed(current, CPU_MASK_ALL);

smp_prepare_cpus(max_cpus);

smp_boot_cpus(max_cpus);

connect_bsp_APIC();

setup_local_APIC(); //初始化 BSP的 LOCAL APCI。

map_cpu_to_logical_apicid();

針對每個CPU調用do_boot_cpu(apicid, cpu)

smp_init(); //每個CPU開始進行調度

trampoline.S AP引導代碼,為16進制代碼,啟用保護模式

head.s 為AP創建分頁管理

initialize_secondary 根據之前fork創建設置的信息,跳轉到start_secondary處

start_secondary 判斷BSP是否啟動,如果啟動AP進行任務調度。

3. 代碼學習總結

find_smp_config();,查找MP table在內存中的位置。具體協議可以參考MP協議的第4章。

這個表的作用在於描述系統CPU,總線,IO APIC等的硬件信息。

相關的兩個全局變量:smp_found_config是否找到SMP MP table,mpf_found SMP MP table的線性地址。

smp_alloc_memory() 為啟動AP的啟動程序分配內存空間。相關全局變量trampoline_base,分配的啟動地址的線性地址。

get_smp_config() 根據MP table中提供的內容,獲取硬件的信息。

init_apic_mappings();獲取IO APIC和LOCAL APIC的映射地址。

zap_low_mappings();如果沒有定義SMP的話,清楚用戶空間的地址映射。將swapper_pg_dir中表項清零。

setup_local_APIC(); 初始化 BSP的 LOCAL APCI。

do_boot_cpu(apicid, cpu)

idle = alloc_idle_task(cpu);

task = copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL, NULL, 0);

init_idle(task, cpu);

將init進程使用copy_process復制,並且調用init_idle函數,設置可以運行的CPU。

idle->thread.eip = (unsigned long) start_secondary;

修改task_struct中的thread.eip,使得AP初始化完成後,就運行start_secondary函數。

start_eip = setup_trampoline();

調用setup_trampoline()函數,復制trampoline_data到trampoline_end之間的代碼到trampoline_base處,trampoline_base就是之前在setup_arch處申請的內存。start_eip返回值是trampoline_base對應的物理地址。

smpboot_setup_warm_reset_vector(start_eip);設置內存40:67h處為start_eip為啟動地址。

wakeup_secondary_cpu(apicid, start_eip);在這個函數中通過操作APIC_ICR寄存器,BSP向目標AP發送IPI消息,觸發目標AP從start_eip地址處,從實模式開始運行。

trampoline.S

ENTRY(trampoline_data)

r_base = .

wbinvd # Needed for NUMA-Q should be harmless for others

mov %cs, %ax # Code and data in the same place

mov %ax, %ds

cli # We should be safe anyway

movl $0xA5A5A5A5, trampoline_data - r_base

這個是設置標識,以便BSP知道AP運行到這裡了。

lidtl boot_idt - r_base # load idt with 0, 0

lgdtl boot_gdt - r_base # load gdt with whatever is appropriate

加載ldt和gdt

xor %ax, %ax

inc %ax # protected mode (PE) bit

lmsw %ax # into protected mode

# flush prefetch and jump to startup_32_smp in arch/i386/kernel/head.S

ljmpl $__BOOT_CS, $(startup_32_smp-__PAGE_OFFSET)

啟動保護模式,跳轉到startup_32_smp 處

# These need to be in the same 64K segment as the above;

# hence we don't use the boot_gdt_descr defined in head.S

boot_gdt:

.word __BOOT_DS + 7 # gdt limit

.long boot_gdt_table-__PAGE_OFFSET # gdt base

boot_idt:

.word 0 # idt limit = 0

.long 0 # idt base = 0L

.globl trampoline_end

trampoline_end:

在這段代碼中,設置標識,以便BSP知道該AP已經運行到這段代碼,加載GDT和LDT表基址。

然後啟動保護模式,跳轉到startup_32_smp 處。

Copyright © Linux教程網 All Rights Reserved