歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核啟動流程詳細分析

Linux內核啟動流程詳細分析

日期:2017/2/28 14:27:07   编辑:Linux內核

Linux內核啟動流程

  arch/arm/kernel/head-armv.S

  該文件是內核最先執行的一個文件,包括內核入口ENTRY(stext)到start_kernel間的初始化代碼,

  主要作用是檢查CPU ID, Architecture Type,初始化BSS等操作,並跳到start_kernel函數。在執行前,處理器應滿足以下狀態:

r0 - should be 0
r1 - unique architecture number
MMU - off
I-cache - on or off
D-cache – off

 1 /* 部分源代碼分析 */ 
 2 /* 內核入口點 */ 
 3 ENTRY(stext) 
 4 /* 程序狀態,禁止FIQ、IRQ,設定SVC模式 */ 
 5 mov r0, #F_BIT | I_BIT | MODE_SVC@ make sure svc mode 
 6 /* 置當前程序狀態寄存器 */ 
 7 msr cpsr_c, r0 @ and all irqs disabled 
 8 /* 判斷CPU類型,查找運行的CPU ID值與Linux編譯支持的ID值是否支持 */ 
 9 bl __lookup_processor_type 
10 /* 跳到__error */ 
11 teq r10, #0 @ invalid processor? 
12 moveq r0, #'p' @ yes, error 'p' 
13 beq __error 
14 /* 判斷體系類型,查看R1寄存器的Architecture Type值是否支持 */ 
15 bl __lookup_architecture_type 
16 /* 不支持,跳到出錯 */ 
17 teq r7, #0 @ invalid architecture? 
18 moveq r0, #'a' @ yes, error 'a' 
19 beq __error 
20 /* 創建核心頁表 */ 
21 bl __create_page_tables 
22 adr lr, __ret @ return address 
23 add pc, r10, #12 @ initialise processor 
24 /* 跳轉到start_kernel函數 */ 
25 b start_kernel 

1. start_kernel()函數分析

  下面對start_kernel()函數及其相關函數進行分析。

1.1 lock_kernel()

 1 /* Getting the big kernel lock. 
 2 * This cannot happen asynchronously, 
 3 * so we only need to worry about other 
 4 * CPU's. 
 5 */ 
 6 extern __inline__ void lock_kernel(void) 
 7 { 
 8     if (!++current->lock_depth) 
 9     spin_lock(&kernel_flag); 
10 } 

  kernel_flag 是一個內核大自旋鎖,所有進程都通過這個大鎖來實現向內核態的遷移。

  只有獲得這個大自旋鎖的處理器可以進入內核,如中斷處理程序等。在任何一對 lock_kernel/unlock_kernel函數裡至多可以有一個程序占用CPU。

  進程的lock_depth成員初始化為-1,在 kerenl/fork.c文件中設置。在它小於0時 (恆為 -1),進程不擁有內核鎖;當大於或等於0時,進程得到內核鎖。

1.2 setup_arch()

  setup_arch()函數做體系相關的初始化工作,函數的定義在arch/arm/kernel/setup.c文件中,主要涉及下列主要函數及代碼。

  setup_processor()
  該函數主要通過

for (list = &__proc_info_begin; list < &__proc_info_end ; list++) 
if ((processor_id & list->cpu_mask) == list->cpu_val) 
break; 

  這樣一個循環來在.proc.info段中尋找匹配的processor_id,processor_id在head_armv.S文件中設置。

1.2.2 setup_architecture(machine_arch_type)

  該函數獲得體系結構的信息,返回mach-xxx/arch.c 文件中定義的machine結構體的指針,包含以下內容

MACHINE_START (xxx, “xxx”) 
MAINTAINER ("xxx" 
BOOT_MEM (xxx, xxx, xxx) 
FIXUP (xxx) 
MAPIO (xxx) 
INITIRQ (xxx) 
MACHINE_END 

1.2.3內存設置代碼

if (meminfo.nr_banks == 0) 
{ 
    meminfo.nr_banks = 1; 
    meminfo.bank[0].start = PHYS_OFFSET; 
    meminfo.bank[0].size = MEM_SIZE; 
} 
  meminfo結構表明內存情況,是對物理內存結構meminfo的默認初始化。

  nr_banks指定內存塊的數量,bank指定每塊內存的范圍,PHYS _OFFSET指定某塊內存塊的開始地址,MEM_SIZE指定某塊內存塊長度。 PHYS _OFFSET和MEM_SIZE都定義在include/asm-armnommu/arch-XXX/memory.h文件中,其中 PHYS _OFFSET是內存的開始地址,MEM_SIZE就是內存的結束地址。

  這個結構在接下來內存的初始化代碼中起重要作用。

1.2.4 內核內存空間管理

init_mm.start_code = (unsigned long) &_text; 內核代碼段開始 
init_mm.end_code = (unsigned long) &_etext; 內核代碼段結束 
init_mm.end_data = (unsigned long) &_edata; 內核數據段開始 
init_mm.brk = (unsigned long) &_end; 內核數據段結束 

  每一個任務都有一個mm_struct結構管理其內存空間,init_mm 是內核的mm_struct。

  其中設置成員變量* mmap指向自己, 意味著內核只有一個內存管理結構,設置 pgd=swapper_pg_dir,swapper_pg_dir是內核的頁目錄,ARM體系結構的內核頁目錄大小定義為16k。init_mm定義了整個內核的內存空間,內核線程屬於內核代碼,同樣使用內核空間,其訪問內存空間的權限與內核一樣。

1.2.5 內存結構初始化

  bootmem_init (&meminfo)函數根據meminfo進行內存結構初始化。

  bootmem_init(&meminfo)函數中調用reserve_node_zero(bootmap_pfn, bootmap_pages) 函數,這個函數的作用是保留一部分內存使之不能被動態分配。

  這些內存塊包括:

reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); /*內核所占用地址空間*/ 
reserve_bootmem_node(pgdat, bootmap_pfn<<PAGE_SHIFT, bootmap_pages<<PAGE_SHIFT) /*bootmem結構所占用地址空間*/

1.2.6 paging_init(&meminfo, mdesc)

  創建內核頁表,映射所有物理內存和IO空間,對於不同的處理器,該函數差別比較大。

  下面簡單描述一下ARM體系結構的存儲系統及MMU相關的概念。
  在ARM存儲系統中,使用內存管理單元(MMU)實現虛擬地址到實際物理地址的映射。

  利用MMU,可把SDRAM的地址完全映射到0x0起始的一片連續地址空間,而把原來占據這片空間的FLASH或者ROM映射到其他不相沖突的存儲空間位置。

  例如,FLASH的地址從0x0000 0000~0x00FFFFFF,而SDRAM的地址范圍是0x3000 0000~0x3lFFFFFF,則可把SDRAM地址映射為0x0000 0000~0xlFFFFFF,而FLASH的地址可以映射到0x9000 0000~0x90FFFFFF(此處地址空間為空閒,未被占用)。映射完成後,如果處理器發生異常,假設依然為IRQ中斷,PC指針指向0xl8處的地址,而這個時候PC實際上是從位於物理地址的0x3000 0018處讀取指令。

  通過MMU的映射,則可實現程序完全運行在SDRAM之中。在實際的應用中.可能會把兩片不連續的物理地址空間分配給SDRAM。而在操作系統中,習慣於把SDRAM的空間連續起來,方便內存管理,且應用程序申請大塊的內存時,操作系統內核也可方便地分配。通過MMU可實現不連續的物理地址空間映射為連續的虛擬地址空間。操作系統內核或者一些比較關鍵的代碼,一般是不希望被用戶應用程序訪問。通過MMU可以控制地址空間的訪問權限,從而保護這些代碼不被破壞。

  MMU的實現過程,實際上就是一個查表映射的過程。建立頁表是實現MMU功能不可缺少的一步。頁表位於系統的內存中,頁表的每一項對應於一個虛擬地址到物理地址的映射。每一項的長度即是一個字的長度(在ARM中,一個字的長度被定義為4Bytes)。頁表項除完成虛擬地址到物理地址的映射功能之外,還定義了訪問權限和緩沖特性等。
  MMU的映射分為兩種,一級頁表的變換和二級頁表變換。兩者的不同之處就是實現的變換地址空間大小不同。
  一級頁表變換支持1 M大小的存儲空間的映射,而二級可以支持64 kB,4 kB和1 kB大小地址空間的映射。

動態表(頁表)的大小=表項數*每個表項所需的位數,即為整個內存空間建立索引表時,需要多大空間存放索引表本身。
表項數=虛擬地址空間/每頁大小
每個表項所需的位數=Log(實際頁表數)+適當控制位數
實際頁表數 =物理地址空間/每頁大小

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-10/108034p2.htm

Copyright © Linux教程網 All Rights Reserved