<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > arm Linux系統(tǒng)啟動之start_kernel函數(shù)

          arm Linux系統(tǒng)啟動之start_kernel函數(shù)

          作者: 時間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
          head-common.S
          ---具體做了哪些動作
          ---跳轉(zhuǎn)到init/main.c
          ---b start_kernel
          //關(guān)于start_kernel的強文深入理解linux內(nèi)核,第八章
          main.c
          asmlinkage void __init start_kernel(void)
          {
          char * command_line;
          extern struct kernel_param __start___param[], __stop___param[];
          //來設(shè)置smp process id,當然目前看到的代碼里面這里是空的
          smp_setup_processor_id();
          /*
          * Need to run as early as possible, to initialize the
          * lockdep hash:
          */
          //lockdep是linux內(nèi)核的一個調(diào)試模塊,用來檢查內(nèi)核互斥機制尤其是自旋鎖潛在的死鎖問題。
          //自旋鎖由于是查詢方式等待,不釋放處理器,比一般的互斥機制更容易死鎖,
          //故引入lockdep檢查以下幾種情況可能的死鎖(lockdep將有專門的文章詳細介紹,在此只是簡單列舉):
          //
          //·同一個進程遞歸地加鎖同一把鎖;
          //
          //·一把鎖既在中斷(或中斷下半部)使能的情況下執(zhí)行過加鎖操作,
          // 又在中斷(或中斷下半部)里執(zhí)行過加鎖操作。這樣該鎖有可能在鎖定時由于中斷發(fā)生又試圖在同一處理器上加鎖;
          //
          //·加鎖后導(dǎo)致依賴圖產(chǎn)生成閉環(huán),這是典型的死鎖現(xiàn)象。
          lockdep_init();
          debug_objects_early_init();
          /*
          * Set up the the initial canary ASAP:
          */
          //初始化stack_canary棧3
          //stack_canary的是帶防止棧溢出攻擊保護的堆棧。
          // 當user space的程序通過int 0x80進入內(nèi)核空間的時候,CPU自動完成一次堆棧切換,
          //從user space的stack切換到kernel space的stack。
          // 在這個進程exit之前所發(fā)生的所有系統(tǒng)調(diào)用所使用的kernel stack都是同一個。
          //kernel stack的大小一般為4096/8192,
          //內(nèi)核堆棧示意圖幫助大家理解:
          //
          // 內(nèi)存低址 內(nèi)存高址
          // | |<-----------------------------esp|
          // +-----------------------------------4096-------------------------------+
          // | 72 | 4 | x < 4016 | 4 |
          // +------------------+-----------------+---------------------------------+
          // |thread_info | | STACK_END_MAGIC | var/call chain |stack_canary |
          // +------------------+-----------------+---------------------------------+
          // | 28 | 44 | | |
          // V | |
          // restart_block V
          //
          //esp+0x0 +0x40
          // +---------------------------------------------------------------------------+
          // |ebx|ecx|edx|esi|edi|ebp|eax|ds|es|fs|gs|orig_eax|eip|cs|eflags|oldesp|oldss|
          // +---------------------------------------------------------------------------+
          // | kernel完成 | cpu自動完成 |
          //http://hi.baidu.com/wzt85/blog/item/112a37132f6116c2f6039e44.html
          boot_init_stack_canary();
          // cgroup: 它的全稱為control group.即一組進程的行為控制.
          // 比如,我們限制進程/bin/sh的CPU使用為20%.我們就可以建一個cpu占用為20%的cgroup.
          // 然后將/bin/sh進程添加到這個cgroup中.當然,一個cgroup可以有多個進程.
          //http://blogold.chinaunix.net/u1/51562/showart_1736813.html
          cgroup_init_early();
          //更新kernel中的所有的立即數(shù)值,但是包括哪些需要再看?
          core_imv_update();
          //關(guān)閉當前CUP中斷
          local_irq_disable();
          //修改標記early_boot_irqs_enabled;
          //通過一個靜態(tài)全局變量 early_boot_irqs_enabled來幫助我們調(diào)試代碼,
          //通過這個標記可以幫助我們知道是否在”early bootup code”,也可以通過這個標志警告是有無效的終端打開
          early_boot_irqs_off();
          //每一個中斷都有一個IRQ描述符(struct irq_desc)來進行描述。
          //這個函數(shù)的主要作用是設(shè)置所有的 IRQ描述符(struct irq_desc)的鎖是統(tǒng)一的鎖,
          //還是每一個IRQ描述符(struct irq_desc)都有一個小鎖。
          early_init_irq_lock_class();
          /*
          * Interrupts are still disabled. Do necessary setups, then
          * enable them
          */
          // 大內(nèi)核鎖(BKL--Big Kernel Lock)
          //大內(nèi)核鎖本質(zhì)上也是自旋鎖,但是它又不同于自旋鎖,自旋鎖是不可以遞歸獲得鎖的,因為那樣會導(dǎo)致死鎖。
          //但大內(nèi)核鎖可以遞歸獲得鎖。大內(nèi)核鎖用于保護整個內(nèi)核,而自旋鎖用于保護非常特定的某一共享資源。
          //進程保持大內(nèi)核鎖時可以發(fā)生調(diào)度,具體實現(xiàn)是:
          //在執(zhí)行schedule時,schedule將檢查進程是否擁有大內(nèi)核鎖,如果有,它將被釋放,以致于其它的進程能夠獲得該鎖,
          //而當輪到該進程運行時,再讓它重新獲得大內(nèi)核鎖。注意在保持自旋鎖期間是不運行發(fā)生調(diào)度的。
          //需要特別指出,整個內(nèi)核只有一個大內(nèi)核鎖,其實不難理解,內(nèi)核只有一個,而大內(nèi)核鎖是保護整個內(nèi)核的,當然有且只有一個就足夠了。
          //還需要特別指出的是,大內(nèi)核鎖是歷史遺留,內(nèi)核中用的非常少,一般保持該鎖的時間較長,因此不提倡使用它。
          //從2.6.11內(nèi)核起,大內(nèi)核鎖可以通過配置內(nèi)核使其變得可搶占(自旋鎖是不可搶占的),這時它實質(zhì)上是一個互斥鎖,使用信號量實現(xiàn)。
          //大內(nèi)核鎖的API包括:
          //
          //void lock_kernel(void);
          //
          //該函數(shù)用于得到大內(nèi)核鎖。它可以遞歸調(diào)用而不會導(dǎo)致死鎖。
          //
          //void unlock_kernel(void);
          //
          //該函數(shù)用于釋放大內(nèi)核鎖。當然必須與lock_kernel配對使用,調(diào)用了多少次lock_kernel,就需要調(diào)用多少次unlock_kernel。
          //大內(nèi)核鎖的API使用非常簡單,按照以下方式使用就可以了:
          //lock_kernel(); //對被保護的共享資源的訪問 … unlock_kernel();
          //http://blog.csdn.net/universus/archive/2010/05/25/5623971.aspx
          lock_kernel();
          //初始化time ticket,時鐘
          tick_init();
          //函數(shù) tick_init() 很簡單,調(diào)用 clockevents_register_notifier 函數(shù)向 clockevents_chain 通知鏈注冊元素:
          // tick_notifier。這個元素的回調(diào)函數(shù)指明了當時鐘事件設(shè)備信息發(fā)生變化(例如新加入一個時鐘事件設(shè)備等等)時,
          //應(yīng)該執(zhí)行的操作,該回調(diào)函數(shù)為 tick_notify
          //http://blogold.chinaunix.net/u3/97642/showart_2050200.html
          boot_cpu_init();
          //初始化頁地址,當然對于arm這里是個空函數(shù)
          //http://book.chinaunix.net/special/ebook/PrenticeHall/PrenticeHallPTRTheLinuxKernelPrimer/0131181637/ch08lev1sec5.html
          page_address_init();
          printk(KERN_NOTICE "%s", linux_banner);
          //系結(jié)構(gòu)相關(guān)的內(nèi)核初始化過程
          //http://www.cublog.cn/u3/94690/showart_2238008.html
          setup_arch(&command_line);
          //初始化內(nèi)存管理
          mm_init_owner(&init_mm, &init_task);
          //處理啟動命令,這里就是設(shè)置的cmd_line
          setup_command_line(command_line);
          //這個在定義了SMP的時候有作用,現(xiàn)在這里為空函數(shù);對于smp的使用,后面在看。。。
          setup_nr_cpu_ids();
          //如果沒有定義CONFIG_SMP宏,則這個函數(shù)為空函數(shù)。
          //如果定義了CONFIG_SMP宏,則這個setup_per_cpu_areas()函數(shù)給每個CPU分配內(nèi)存,
          //并拷貝.data.percpu段的數(shù)據(jù)。為系統(tǒng)中的每個CPU的per_cpu變量申請空間。
          setup_per_cpu_areas();
          //定義在include/asm-x86/smp.h。
          //如果是SMP環(huán)境,則設(shè)置boot CPU的一些數(shù)據(jù)。在引導(dǎo)過程中使用的CPU稱為boot CPU
          smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
          //設(shè)置node 和 zone 數(shù)據(jù)結(jié)構(gòu)
          //內(nèi)存管理的講解:http://blog.chinaunix.net/space.php?uid=361890&do=blog&cuid=2146541
          build_all_zonelists(NULL);
          //初始化page allocation相關(guān)結(jié)構(gòu)
          page_alloc_init();
          printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);
          //解析內(nèi)核參數(shù)
          //對內(nèi)核參數(shù)的解析: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獲得對應(yīng)的進程描述指針,按照實際的物理內(nèi)存初始化pid hash表
          //這里涉及到進程管理http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx
          pidhash_init();
          //初始化VFS的兩個重要數(shù)據(jù)結(jié)構(gòu)dcache和inode的緩存。
          //http://blog.csdn.net/yunsongice/archive/2011/02/01/6171324.aspx
          vfs_caches_init_early();
          //把編譯期間,kbuild設(shè)置的異常表,也就是__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.
          */
          //核心進程調(diào)度器初始化,調(diào)度器的初始化的優(yōu)先級要高于任何中斷的建立,
          //并且初始化進程0,即idle進程,但是并沒有設(shè)置idle進程的NEED_RESCHED標志,
          //所以還會繼續(xù)完成內(nèi)核初始化剩下的事情。
          //這里僅僅為進程調(diào)度程序的執(zhí)行做準備。
          //它所做的具體工作是調(diào)用init_bh函數(shù)(kernel/softirq.c)把timer,tqueue,immediate三個人物隊列加入下半部分的數(shù)組
          sched_init();
          /*
          * Disable preemption - early bootup scheduling is extremely
          * fragile until we cpu_idle() for the first time.
          */
          //搶占計數(shù)器加1
          preempt_disable();
          //檢查中斷是否打開
          if (!irqs_disabled()) {
          printk(KERN_WARNING "start_kernel(): bug: interrupts were "
          "enabled *very* early, fixing it/n");
          local_irq_disable();
          }
          //Read-Copy-Update的初始化
          //RCU機制是Linux2.6之后提供的一種數(shù)據(jù)一致性訪問的機制,
          //從RCU(read-copy-update)的名稱上看,我們就能對他的實現(xiàn)機制有一個大概的了解,
          //在修改數(shù)據(jù)的時候,首先需要讀取數(shù)據(jù),然后生成一個副本,對副本進行修改,
          //修改完成之后再將老數(shù)據(jù)update成新的數(shù)據(jù),此所謂RCU。
          //http://blog.ednchina.com/tiloog/193361/message.aspx
          //http://blogold.chinaunix.net/u1/51562/showart_1341707.html
          rcu_init();
          //定義在lib/radix-tree.c。
          //Linux使用radix樹來管理位于文件系統(tǒng)緩沖區(qū)中的磁盤塊,
          //radix樹是trie樹的一種
          //http://blog.csdn.net/walkland/archive/2009/03/19/4006121.aspx
          radix_tree_init();
          /* init some links before init_ISA_irqs() */
          //early_irq_init 則對數(shù)組中每個成員結(jié)構(gòu)進行初始化,
          //例如, 初始每個中斷源的中斷號.其他的函數(shù)基本為空.
          early_irq_init();
          //初始化IRQ中斷和終端描述符。
          //初始化系統(tǒng)中支持的最大可能的中斷描述結(jié)構(gòu)struct irqdesc變量數(shù)組irq_desc[NR_IRQS],
          //把每個結(jié)構(gòu)變量irq_desc[n]都初始化為預(yù)先定義好的壞中斷描述結(jié)構(gòu)變量bad_irq_desc,
          //并初始化該中斷的鏈表表頭成員結(jié)構(gòu)變量pend
          init_IRQ();
          //prio-tree是一棵查找樹,管理的是什么?
          //http://blog.csdn.net/dog250/archive/2010/06/28/5700317.aspx
          prio_tree_init();
          //初始化定時器Timer相關(guān)的數(shù)據(jù)結(jié)構(gòu)
          //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();
          //初始化系統(tǒng)時間,
          //檢查系統(tǒng)定時器描述結(jié)構(gòu)struct sys_timer全局變量system_timer是否為空,
          //如果為空將其指向dummy_gettimeoffset()函數(shù)。
          //http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html
          time_init();
          //profile只是內(nèi)核的一個調(diào)試性能的工具,
          //這個可以通過menuconfig中的Instrumentation Support->profile打開。
          //http://www.linuxdiyf.com/bbs//thread-71446-1-1.html
          profile_init();
          if (!irqs_disabled())
          printk(KERN_CRIT "start_kernel(): bug: interrupts were "
          "enabled early/n");
          //與開始的early_boot_irqs_off相對應(yīng)
          early_boot_irqs_on();
          //與local_irq_disbale相對應(yīng),開中斷
          local_irq_enable();
          /* Interrupts are enabled now so all GFP allocations are safe. */
          gfp_allowed_mask = __GFP_BITS_MASK;
          //memory cache的初始化
          //http://my.chinaunix.net/space.php?uid=7588746&do=blog&id=153184
          kmem_cache_init_late();
          /*
          * HACK ALERT! This is early. Were enabling the console before
          * weve done PCI setups etc, and console_init() must be aware of
          * this. But we do want output early, in case something goes wrong.
          */
          //初始化控制臺以顯示printk的內(nèi)容,在此之前調(diào)用的printk,只是把數(shù)據(jù)存到緩沖區(qū)里,
          //只有在這個函數(shù)調(diào)用后,才會在控制臺打印出內(nèi)容
          //該函數(shù)執(zhí)行后可調(diào)用printk()函數(shù)將log_buf中符合打印級別要求的系統(tǒng)信息打印到控制臺上。
          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()是一個空函數(shù),否則執(zhí)行鎖自測
          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
          //頁面初始化,可以參考上面的cgroup機制
          page_cgroup_init();
          //頁面分配debug啟用
          enable_debug_pagealloc();
          //此處函數(shù)為空
          kmemtrace_init();
          //memory lead偵測初始化,如何偵測???
          kmemleak_init();
          //
          //Called after the kmem_caches are functional to setup a dedicated
          //cache pool, which has the SLAB_DEBUG_OBJECTS flag set. This flag
          //prevents that the debug code is called on kmem_cache_free() for the
          //debug tracker objects to avoid recursive calls.
          //在kmem_caches之后表示建立一個高速緩沖池,建立起SLAB_DEBUG_OBJECTS標志。???
          debug_objects_mem_init();
          //idr在linux內(nèi)核中指的就是整數(shù)ID管理機制,
          //從本質(zhì)上來說,這就是一種將整數(shù)ID號和特定指針關(guān)聯(lián)在一起的機制
          //idr機制適用在那些需要把某個整數(shù)和特定指針關(guān)聯(lián)在一起的地方。
          //http://blogold.chinaunix.net/u3/93255/showart_2524027.html
          idr_init_cache();
          //是否是對SMP的支持,單核是否需要??這個要分析
          setup_per_cpu_pageset();
          //NUMA (Non Uniform Memory Access) policy
          //具體是什么不懂
          numa_policy_init();
          if (late_time_init)
          late_time_init();
          //初始化調(diào)度時鐘
          sched_clock_init();
          //calibrate_delay()函數(shù)可以計算出cpu在一秒鐘內(nèi)執(zhí)行了多少次一個極短的循環(huán),
          //計算出來的值經(jīng)過處理后得到BogoMIPS 值,
          //Bogo是Bogus(偽)的意思,MIPS是millions of instructions per second(百萬條指令每秒)的縮寫。
          //這樣我們就知道了其實這個函數(shù)是linux內(nèi)核中一個cpu性能測試函數(shù)。
          //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();
          //來自mm/rmap.c
          //分配一個anon_vma_cachep作為anon_vma的slab緩存。
          //這個技術(shù)是PFRA(頁框回收算法)技術(shù)中的組成部分。
          //這個技術(shù)為定位而生——快速的定位指向同一頁框的所有頁表項。
          anon_vma_init();
          #ifdef CONFIG_X86
          if (efi_enabled)
          efi_enter_virtual_mode();
          #endif
          //創(chuàng)建thread_info緩存
          thread_info_cache_init();
          //申請了一個slab來存放credentials??????如何理解?
          cred_init();
          //根據(jù)物理內(nèi)存大小計算允許創(chuàng)建進程的數(shù)量
          //http://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html
          fork_init(totalram_pages);
          //給進程的各種資源管理結(jié)構(gòu)分配了相應(yīng)的對象緩存區(qū)
          //http://www.shangshuwu.cn/index.php/Linux內(nèi)核的進程創(chuàng)建
          proc_caches_init();
          //創(chuàng)建 buffer_head SLAB 緩存
          buffer_init();
          //初始化key的management stuff
          key_init();
          //關(guān)于系統(tǒng)安全的初始化,主要是訪問控制
          //http://blog.csdn.net/nhczp/archive/2008/04/29/2341194.aspx
          security_init();
          //與debug kernel相關(guān)
          dbg_late_init();
          //調(diào)用kmem_cache_create()函數(shù)來為VFS創(chuàng)建各種SLAB分配器緩存
          //包括:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四個SLAB分配器緩存
          vfs_caches_init(totalram_pages);
          //創(chuàng)建信號隊列
          signals_init();
          /* rootfs populating might need page-writeback */
          //回寫相關(guān)的初始化
          //http://blog.csdn.net/yangp01/archive/2010/04/06/5454822.aspx
          page_writeback_init();
          #ifdef CONFIG_PROC_FS
          proc_root_init();
          #endif
          //它將剩余的subsys初始化.然后將init_css_set添加進哈希數(shù)組css_set_table[ ]中.
          //在上面的代碼中css_set_hash()是css_set_table的哈希函數(shù).
          //它是css_set->subsys為哈希鍵值,到css_set_table[ ]中找到對應(yīng)項.然后調(diào)用hlist_add_head()將init_css_set添加到?jīng)_突項中.
          //然后,注冊了cgroup文件系統(tǒng).這個文件系統(tǒng)也是我們在用戶空間使用cgroup時必須掛載的.
          //最后,在proc的根目錄下創(chuàng)建了一個名為cgroups的文件.用來從用戶空間觀察cgroup的狀態(tài).
          //http://blogold.chinaunix.net/u1/51562/showart_1736813.html
          cgroup_init();
          //http://blogold.chinaunix.net/u1/51562/showart_1777937.html
          cpuset_init();
          ////進程狀態(tài)初始化,實際上就是分配了一個存儲線程狀態(tài)的高速緩存
          taskstats_init_early();
          delayacct_init();
          //此處為一空函數(shù)
          imv_init_complete();
          //測試CPU的各種缺陷,記錄檢測到的缺陷,以便于內(nèi)核的其他部分以后可以使用他們工作。
          check_bugs();
          //電源相關(guān)的初始化
          //http://blogold.chinaunix.net/u/548/showart.php?id=377952
          acpi_early_init(); /* before LAPIC and SMP init */
          //
          sfi_init_late();
          ftrace_init();
          /* Do the rest non-__inited, were now alive */
          //創(chuàng)建1號進程,詳細分析之
          rest_init();
          }


          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();