第3階段——內核啟動分析之start_kernel初始化函數(5)


內核啟動分析之start_kernel初始化函數(init/main.c)

stext函數啟動內核后,就開始進入start_kernel初始化各個函數, 下面只是淺嘗輒止的描述一下函數的功能,很多函數真正理解需要對linux相關體系有很深的了解后才能明白

代碼如下:

 

asmlinkage void __init start_kernel(void) 

{ 
  char * command_line; 
  extern struct kernel_param __start___param[], __stop___param[];  

  smp_setup_processor_id();  //來設置smp process id,當然目前看到的代碼里面這里是空的
  
  unwind_init(); 

//lockdep是linux內核的一個調試模塊,用來檢查內核互斥機制尤其是自旋鎖潛在的死鎖問題。  
//自旋鎖由於是查詢方式等待,不釋放處理器,比一般的互斥機制更容易死鎖,  
//故引入lockdep檢查以下幾種情況可能的死鎖(lockdep將有專門的文章詳細介紹,在此只是簡單列舉):  
//  
//·同一個進程遞歸地加鎖同一把鎖;  
//  
//·一把鎖既在中斷(或中斷下半部)使能的情況下執行過加鎖操作,  
// 又在中斷(或中斷下半部)里執行過加鎖操作。這樣該鎖有可能在鎖定時由於中斷發生又試圖在同一處理器上加鎖;  
//  
//·加鎖后導致依賴圖產生成閉環,這是典型的死鎖現象。  
   lockdep_init(); 
   

//關閉當前CUP中斷  
local_irq_disable(); 

//修改標記early_boot_irqs_enabled;  
//通過一個靜態全局變量 early_boot_irqs_enabled來幫助我們調試代碼,  
//通過這個標記可以幫助我們知道是否在”early bootup code”,也可以通過這個標志警告是有無效的終端打開  
early_boot_irqs_off(); 

//每一個中斷都有一個IRQ描述符(struct irq_desc)來進行描述。  
//這個函數的主要作用是設置所有的 IRQ描述符(struct irq_desc)的鎖是統一的鎖,  
//還是每一個IRQ描述符(struct irq_desc)都有一個小鎖。  
early_init_irq_lock_class(); 

 

 

 

/*

 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */ 
// 大內核鎖(BKL--Big Kernel Lock)  
//大內核鎖本質上也是自旋鎖,但是它又不同於自旋鎖,自旋鎖是不可以遞歸獲得鎖的,因為那樣會導致死鎖。  
//但大內核鎖可以遞歸獲得鎖。大內核鎖用於保護整個內核,而自旋鎖用於保護非常特定的某一共享資源。  
//進程保持大內核鎖時可以發生調度,具體實現是:  
//在執行schedule時,schedule將檢查進程是否擁有大內核鎖,如果有,它將被釋放,以致於其它的進程能夠獲得該鎖,  
//而當輪到該進程運行時,再讓它重新獲得大內核鎖。注意在保持自旋鎖期間是不運行發生調度的。  
//需要特別指出,整個內核只有一個大內核鎖,其實不難理解,內核只有一個,而大內核鎖是保護整個內核的,當然有且只有一個就足夠了。  
//還需要特別指出的是,大內核鎖是歷史遺留,內核中用的非常少,一般保持該鎖的時間較長,因此不提倡使用它。  
//從2.6.11內核起,大內核鎖可以通過配置內核使其變得可搶占(自旋鎖是不可搶占的),這時它實質上是一個互斥鎖,使用信號量實現。  
//大內核鎖的API包括:  
//  
//void lock_kernel(void);  
//  
//該函數用於得到大內核鎖。它可以遞歸調用而不會導致死鎖。  
//  
//void unlock_kernel(void);  
//  
//該函數用於釋放大內核鎖。當然必須與lock_kernel配對使用,調用了多少次lock_kernel,就需要調用多少次unlock_kernel。  
//大內核鎖的API使用非常簡單,按照以下方式使用就可以了:  
//lock_kernel(); //對被保護的共享資源的訪問 … unlock_kernel();  
//http://blog.csdn.net/universus/archive/2010/05/25/5623971.aspx  
  lock_kernel(); 

 
//初始化time ticket,時鍾  
  tick_init(); 

 

//函數 tick_init() 很簡單,調用 clockevents_register_notifier 函數向 clockevents_chain 通知鏈注冊元素:  
// tick_notifier。這個元素的回調函數指明了當時鍾事件設備信息發生變化(例如新加入一個時鍾事件設備等等)時,  
//應該執行的操作,該回調函數為 tick_notify   
//http://blogold.chinaunix.net/u3/97642/showart_2050200.html  
  boot_cpu_init();

  

//初始化頁地址,當然對於arm這里是個空函數  
//http://book.chinaunix.net/special/ebook/PrenticeHall/PrenticeHallPTRTheLinuxKernelPrimer/0131181637/ch08lev1sec5.html  
  page_address_init(); 

  

/*打印KER_NOTICE,這里的KER_NOTICE是字符串<5>*/
  printk(KERN_NOTICE);

 

 

/*打印以下linux版本信息:       
“Linux version 2.6.22.6 (book@book-desktop) (gcc version 3.4.5) #1 Fri Jun 16 00:55:53 CST 2017” */
 printk(linux_banner);
      

//系結構相關的內核初始化過程,處理uboot傳遞進來的atag參數( setup_memory_tags()和setup_commandline _tags() )  
//http://www.cublog.cn/u3/94690/showart_2238008.html  
setup_arch(&command_line); 

  

//處理啟動命令,這里就是設置的cmd_line,
//保存未改變的comand_line到字符數組static_command_line[] 中。
//保存  boot_command_line到字符數組saved_command_line[]中  
setup_command_line(command_line); 

unwind_setup();

//如果沒有定義CONFIG_SMP宏,則這個函數為空函數。  
//如果定義了CONFIG_SMP宏,則這個setup_per_cpu_areas()函數給每個CPU分配內存,  
//並拷貝.data.percpu段的數據。為系統中的每個CPU的per_cpu變量申請空間。  
setup_per_cpu_areas();

//定義在include/asm-x86/smp.h。  
//如果是SMP環境,則設置boot CPU的一些數據。在引導過程中使用的CPU稱為boot CPU  
smp_prepare_boot_cpu(); 

/* arch-specific boot-cpu hooks */ /* 進程調度器初始化 */ sched_init(); /* 禁止內核搶占 */ preempt_disable(); //設置node 和 zone 數據結構 //內存管理的講解:http://blog.chinaunix.net/space.php?uid=361890&do=blog&cuid=2146541 build_all_zonelists(NULL); //初始化page allocation相關結構 page_alloc_init(); /* 打印Linux啟動命令行參數 */ printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line); //解析內核參數 //對內核參數的解析:http://hi.baidu.com/yuhuntero/blog/item/654a7411e45ce519b8127ba9.html parse_early_param(); parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, &unknown_bootoption); /* * These use large bootmem allocations and must precede * kmem_cache_init() */ //初始化hash表,以便於從進程的PID獲得對應的進程描述指針,按照實際的物理內存初始化pid hash表 //這里涉及到進程管理http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx pidhash_init(); //初始化VFS的兩個重要數據結構dcache和inode的緩存。 //http://blog.csdn.net/yunsongice/archive/2011/02/01/6171324.aspx vfs_caches_init_early(); //把編譯期間,kbuild設置的異常表,也就是__start___ex_table和__stop___ex_table之中的所有元素進行排序 sort_main_extable(); //初始化中斷向量表 //http://blog.csdn.net/yunsongice/archive/2011/02/01/6171325.aspx trap_init(); //memory map初始化 //http://blog.csdn.net/huyugv_830913/archive/2010/09/15/5886970.aspx mm_init(); /* * Set up the scheduler prior starting any interrupts (such as the * timer interrupt). Full topology setup happens at smp_init() * time - but meanwhile we still have a functioning scheduler. */ //核心進程調度器初始化,調度器的初始化的優先級要高於任何中斷的建立, //並且初始化進程0,即idle進程,但是並沒有設置idle進程的NEED_RESCHED標志, //所以還會繼續完成內核初始化剩下的事情。 //這里僅僅為進程調度程序的執行做准備。 //它所做的具體工作是調用init_bh函數(kernel/softirq.c)把timer,tqueue,immediate三個人物隊列加入下半部分的數組 sched_init(); /* * Disable preemption - early bootup scheduling is extremely * fragile until we cpu_idle() for the first time. */ //搶占計數器加1 preempt_disable(); //檢查中斷是否打開,如果已經打開,則關閉中斷 if (!irqs_disabled()) { printk(KERN_WARNING "start_kernel(): bug: interrupts were " "enabled *very* early, fixing it/n"); local_irq_disable(); } sort_main_extable();
/* * trap_init函數完成對系統保留中斷向量(異常、非屏蔽中斷以及系統調用) * 的初始化,init_IRQ函數則完成其余中斷向量的初始化 */ trap_init(); //Read-Copy-Update的初始化 //RCU機制是Linux2.6之后提供的一種數據一致性訪問的機制, //從RCU(read-copy-update)的名稱上看,我們就能對他的實現機制有一個大概的了解, //在修改數據的時候,首先需要讀取數據,然后生成一個副本,對副本進行修改, //修改完成之后再將老數據update成新的數據,此所謂RCU。 //http://blog.ednchina.com/tiloog/193361/message.aspx //http://blogold.chinaunix.net/u1/51562/showart_1341707.html rcu_init(); //初始化IRQ中斷和終端描述符。 //初始化系統中支持的最大可能的中斷描述結構struct irqdesc變量數組irq_desc[NR_IRQS], //把每個結構變量irq_desc[n]都初始化為預先定義好的壞中斷描述結構變量bad_irq_desc, //並初始化該中斷的鏈表表頭成員結構變量pend init_IRQ(); /* 初始化hash表,便於從進程的PID獲得對應的進程描述符指針 */ pidhash_init(); //初始化定時器Timer相關的數據結構 //http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html init_timers(); //對高精度時鍾進行初始化 hrtimers_init(); //軟中斷初始化 //http://blogold.chinaunix.net/u1/51562/showart_494363.html softirq_init(); //初始化時鍾源 timekeeping_init(); //初始化系統時間, //檢查系統定時器描述結構struct sys_timer全局變量system_timer是否為空, //如果為空將其指向dummy_gettimeoffset()函數。 //http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html time_init(); //profile只是內核的一個調試性能的工具, //這個可以通過menuconfig中的Instrumentation Support->profile打開。 //http://www.linuxdiyf.com/bbs//thread-71446-1-1.html profile_init(); /*if判斷中斷是否打開,如果已經打開,打印數據*/ if (!irqs_disabled()) printk(KERN_CRIT "start_kernel(): bug: interrupts were enabled early/n"); //與開始的early_boot_irqs_off相對應 early_boot_irqs_on(); //與local_irq_disbale相對應,開CPU中斷 local_irq_enable(); /*
* HACK ALERT! This is early. We're enabling the console before * we've done PCI setups etc, and console_init() must be aware of * this. But we do want output early, in case something goes wrong. */ //初始化控制台以顯示printk的內容,在此之前調用的printk,只是把數據存到緩沖區里, //只有在這個函數調用后,才會在控制台打印出內容 //該函數執行后可調用printk()函數將log_buf中符合打印級別要求的系統信息打印到控制台上。 console_init(); if (panic_later) panic(panic_later, panic_param); //如果定義了CONFIG_LOCKDEP宏,那么就打印鎖依賴信息,否則什么也不做 lockdep_info(); /* * Need to run this when irqs are enabled, because it wants * to self-test [hard/soft]-irqs on/off lock inversion bugs * too: */ //如果定義CONFIG_DEBUG_LOCKING_API_SELFTESTS宏 //則locking_selftest()是一個空函數,否則執行鎖自測 locking_selftest(); #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) { printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " "disabling it./n",
page_to_pfn(virt_to_page((
void *)initrd_start)), min_low_pfn); initrd_start = 0; } #endif /* 虛擬文件系統的初始化 */ vfs_caches_init_early(); cpuset_init_early(); mem_init(); /* slab初始化 */ kmem_cache_init(); //是否是對SMP的支持,單核是否需要??這個要分析 setup_per_cpu_pageset(); numa_policy_init(); if (late_time_init) late_time_init(); //calibrate_delay()函數可以計算出cpu在一秒鍾內執行了多少次一個極短的循環, //計算出來的值經過處理后得到BogoMIPS 值, //Bogo是Bogus(偽)的意思,MIPS是millions of instructions per second(百萬條指令每秒)的縮寫。 //這樣我們就知道了其實這個函數是linux內核中一個cpu性能測試函數。 //http://blogold.chinaunix.net/u2/86768/showart_2196664.html calibrate_delay(); //PID是process id的縮寫 //http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx pidmap_init(); /* 接下來的函數中,大多數都是為有關的管理機制建立專用的slab緩存 */ pgtable_cache_init(); /* 初始化優先級樹index_bits_to_maxindex數組 */ prio_tree_init(); //來自mm/rmap.c //分配一個anon_vma_cachep作為anon_vma的slab緩存。 //這個技術是PFRA(頁框回收算法)技術中的組成部分。 //這個技術為定位而生——快速的定位指向同一頁框的所有頁表項。 anon_vma_init(); #ifdef CONFIG_X86 if (efi_enabled) efi_enter_virtual_mode(); #endif //根據物理內存大小計算允許創建進程的數量 //http://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html fork_init(totalram_pages); //給進程的各種資源管理結構分配了相應的對象緩存區 //http://www.shangshuwu.cn/index.php/Linux內核的進程創建 proc_caches_init(); //創建 buffer_head SLAB 緩存 buffer_init(); unnamed_dev_init(); //初始化key的management stuff key_init(); //關於系統安全的初始化,主要是訪問控制 //http://blog.csdn.net/nhczp/archive/2008/04/29/2341194.aspx security_init(); //調用kmem_cache_create()函數來為VFS創建各種SLAB分配器緩存 //包括:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四個SLAB分配器緩存 vfs_caches_init(totalram_pages); radix_tree_init(); //創建信號隊列 signals_init(); /* rootfs populating might need page-writeback */ //回寫相關的初始化 //http://blog.csdn.net/yangp01/archive/2010/04/06/5454822.aspx \
page_writeback_init(); #ifdef CONFIG_PROC_FS proc_root_init(); #endif //http://blogold.chinaunix.net/u1/51562/showart_1777937.html cpuset_init(); ////進程狀態初始化,實際上就是分配了一個存儲線程狀態的高速緩存 taskstats_init_early(); delayacct_init(); //測試CPU的各種缺陷,記錄檢測到的缺陷,以便於內核的其他部分以后可以使用他們工作。 check_bugs(); //電源相關的初始化 //http://blogold.chinaunix.net/u/548/showart.php?id=377952 acpi_early_init(); /* before LAPIC and SMP init */ //接着進入rest_init()創建init進程,創建根文件系統,啟動應用程序 rest_init(); }

然后進入rest_init():

 

static void noinline __init_refok rest_init(void)
{
    int pid;
    /*創建init進程,然后進入kernel_init()*/
       kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
    ... ...
} 

 

最后會進入kernel_init()函數

通過prepare_namespace()函數來創建根文件系統

 

接下來開始分析prepare_namespace()如何創建根文件系統


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM