小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Linux2.6啟動(dòng)3--start_kernel篇

 zzjiwang 2016-06-23

Linux2.6啟動(dòng)3--start_kernel

當(dāng)內(nèi)核與體系架構(gòu)相關(guān)的匯編代碼執(zhí)行完畢,即跳入start_kernel。這個(gè)函數(shù)在kernel/init/main.c中。由于這部分涉及linux眾多數(shù)據(jù)結(jié)構(gòu)的初始化,包括內(nèi)核命令行解析,內(nèi)存緩沖區(qū)建立初始化,頁(yè)面分配和初始化,虛擬文件系統(tǒng)建立,根文件系統(tǒng)掛載,驅(qū)動(dòng)文件掛載,二進(jìn)制程序文件的執(zhí)行等,限于篇幅和理解水平,只能流程上的大致梳理,以上提及方面后期再做詳細(xì)分析。為保證準(zhǔn)確性,參考了一部分書(shū)籍和網(wǎng)上技術(shù)文檔,如有疑問(wèn)請(qǐng)及時(shí)提出,共同學(xué)習(xí)探討。

 

asmlinkage void __init start_kernel(void)

{

       char * command_line;

       extern struct kernel_param __start___param[], __stop___param[];

//這里引用兩個(gè)符號(hào),是內(nèi)核編譯腳本定位的內(nèi)核參數(shù)起始地址

       smp_setup_processor_id();//CPU架構(gòu)的初始化,目前我們的高通linux側(cè)是單核的,此多核不做分析

       unwind_init();//本架構(gòu)中沒(méi)有用

       lockdep_init();//本架構(gòu)為空

       debug_objects_early_init();

       cgroup_init_early();

 

       local_irq_disable();

       early_boot_irqs_off();

       early_init_irq_lock_class();

 

       lock_kernel();//本架構(gòu)為空函數(shù)

       tick_init();

//時(shí)鐘中斷初始化函數(shù),調(diào)用 clockevents_register_notifier 函數(shù)向 clockevents_chain 時(shí)鐘事件鏈注冊(cè)時(shí)鐘控制函數(shù) tick_notifier。這是個(gè)回調(diào)函數(shù),指明了當(dāng)時(shí)鐘事件發(fā)生變化時(shí)應(yīng)該執(zhí)行的哪些操作,比如時(shí)鐘的掛起操作等

       boot_cpu_init();//用于多核CPU的初始化

       page_address_init();//用于高地址內(nèi)存,我們都用32CPU,此函數(shù)為空

       printk(KERN_NOTICE);

       printk(linux_banner);

       setup_arch(&command_line);

//具體看一下這個(gè)架構(gòu)初始化函數(shù)完成哪些功能

void __init setup_arch(char **cmdline_p)

{

       struct tag *tags = (struct tag *)&init_tags;//定義了一個(gè)默認(rèn)的內(nèi)核參數(shù)列表

       struct machine_desc *mdesc;

       char *from = default_command_line;

 

       setup_processor();//匯編的CPU初始化部分已講過(guò),不再討論

       mdesc = setup_machine(machine_arch_type);

       machine_name = mdesc->name;

 

       if (mdesc->soft_reboot)

              reboot_setup("s");

 

       if (__atags_pointer)

              tags = phys_to_virt(__atags_pointer);

       else if (mdesc->boot_params)

              tags = phys_to_virt(mdesc->boot_params);

//由于MMU單元已打開(kāi),此處需要而boot_params是物理地址,需要轉(zhuǎn)換成虛擬地址才能訪問(wèn),因?yàn)榇藭r(shí)CPU訪問(wèn)的都是虛擬地址

       /*

        * If we have the old style parameters, convert them to

        * a tag list.

        */

//內(nèi)核參數(shù)列表第一項(xiàng)必須是ATAG_CORE類(lèi)型

       if (tags->hdr.tag != ATAG_CORE)//如果不是,則需要轉(zhuǎn)換成新的內(nèi)核參數(shù)類(lèi)型,新的內(nèi)核參數(shù)類(lèi)型用下面struct tag結(jié)構(gòu)表示

              convert_to_tag_list(tags);//此函數(shù)完成新舊參數(shù)結(jié)構(gòu)轉(zhuǎn)換

struct tag {

       struct tag_header hdr;

       union {

              struct tag_core             core;

              struct tag_mem32 mem;

              struct tag_videotext      videotext;

              struct tag_ramdisk       ramdisk;

              struct tag_initrd     initrd;

              struct tag_serialnr  serialnr;

              struct tag_revision revision;

              struct tag_videolfb       videolfb;

              struct tag_cmdline cmdline;

       } u;

};

//舊的內(nèi)核參數(shù)列表用下面結(jié)構(gòu)表示

struct param_struct {

    union {

       struct {

           unsigned long page_size;           /*  0 */

           unsigned long nr_pages;            /*  4 */

           unsigned long ramdisk_size;             /*  8 */

           unsigned long flags;            /* 12 */

。。。。。。。。。。。。//較長(zhǎng),省略

}

 

       if (tags->hdr.tag != ATAG_CORE)//如果沒(méi)有內(nèi)核參數(shù)

              tags = (struct tag *)&init_tags;//則選用默認(rèn)的內(nèi)核參數(shù)

 

       if (mdesc->fixup)

              mdesc->fixup(mdesc, tags, &from, &meminfo);//用內(nèi)核參數(shù)列表填充meminfo

 

       if (tags->hdr.tag == ATAG_CORE) {

              if (meminfo.nr_banks != 0)

                     squash_mem_tags(tags);

              save_atags(tags);

              parse_tags(tags);//解析內(nèi)核參數(shù)列表,然后調(diào)用內(nèi)核參數(shù)列表的處理函數(shù)對(duì)這些參數(shù)進(jìn)行處理。比如,如果列表為命令行,則最終會(huì)用parse_tag_cmdlin函數(shù)進(jìn)行解析,這個(gè)函數(shù)用_tagtable編譯連接到了內(nèi)核里

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);

       }

//下面是記錄內(nèi)核代碼的起始,結(jié)束虛擬地址

       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;

//下面是對(duì)命令行的處理,剛才在參數(shù)列表處理parse_tag_cmdline函數(shù)已把命令行拷貝到了from空間

       memcpy(boot_command_line, from, COMMAND_LINE_SIZE);

       boot_command_line[COMMAND_LINE_SIZE-1] = '/0';

       parse_cmdline(cmdline_p, from);//解析出命令行,命令行解析出以后,同樣會(huì)調(diào)用相關(guān)處理函數(shù)進(jìn)行處理。系統(tǒng)用__early_param宏在編譯階段把處理函數(shù)編譯進(jìn)內(nèi)核。

 

       paging_init(&meminfo, mdesc);

//這個(gè)函數(shù)完成頁(yè)表初始化,具體的方法為建立線性地址劃分后每個(gè)地址空間的標(biāo)志;清除在boot階段建立的內(nèi)核映射空間,也即把頁(yè)表項(xiàng)全部清零;調(diào)用bootmem_init,禁止無(wú)效的內(nèi)存節(jié)點(diǎn),由于我們的物理內(nèi)存都是連續(xù)的空間,因此,內(nèi)存節(jié)點(diǎn)為1個(gè)。接下來(lái)判斷INITRD映像是否存在,若存在則檢查其所在的地址是否在一個(gè)有效的地址內(nèi),然后返回此內(nèi)存節(jié)點(diǎn)號(hào)。

先看兩個(gè)數(shù)據(jù)結(jié)構(gòu)。

struct meminfo表示內(nèi)存的劃分情況。Linux的內(nèi)存劃分為bank。每個(gè)bank

struct membank表示,start表示起始地址,這里是物理地址,size表示大小,node表示此bank所在的節(jié)點(diǎn)號(hào),對(duì)于只有一個(gè)節(jié)點(diǎn)的內(nèi)存,所有bank節(jié)點(diǎn)都相等

struct membank {

       unsigned long start;

       unsigned long size;

       int           node;

};

 

struct meminfo {

       int nr_banks;

       struct membank bank[NR_BANKS];

};

 

//page_init函數(shù)中比較重要的是bootmem_init函數(shù),此函數(shù)在完成原來(lái)映射頁(yè)表的清除后,最終調(diào)用bootmem_init_node如下:

bootmem_init_node(int node, int initrd_node, struct meminfo *mi)

{

       unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];

       unsigned long start_pfn, end_pfn, boot_pfn;

       unsigned int boot_pages;

       pg_data_t *pgdat;// 每個(gè)節(jié)點(diǎn)用pg_data_t描述,這個(gè)結(jié)構(gòu)用在非一致性內(nèi)存中,我們的內(nèi)存只有一個(gè),地址是連續(xù)的

       int i;

 

       start_pfn = -1UL;

       end_pfn = 0;

       for_each_nodebank(i, mi, node) {

              struct membank *bank = &mi->bank[i];

              unsigned long start, end;

 

              start = bank->start >> PAGE_SHIFT;//計(jì)算出頁(yè)表號(hào),實(shí)際也表示第幾個(gè)物理頁(yè)號(hào)

              end = (bank->start + bank->size) >> PAGE_SHIFT;

 

              if (start_pfn > start)

                     start_pfn = start;

              if (end_pfn < end)

                     end_pfn = end;

 

              map_memory_bank(bank);//將每個(gè)節(jié)點(diǎn)的每個(gè)bank重新映射,比如重新映射內(nèi)核空間

       }

       if (end_pfn == 0)

              return end_pfn;

        //一個(gè)字節(jié)代表8個(gè)頁(yè),因此找到一個(gè)

        //可放置這些所有自己的頁(yè)面即可。用一個(gè)bit位表示一個(gè)頁(yè)是否已占用,那么一個(gè)字節(jié)為8個(gè)頁(yè),比如4096個(gè)頁(yè)需要4096/8=512字節(jié),容納這個(gè)位圖需要一個(gè)頁(yè)

       boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn);

       boot_pfn = find_bootmap_pfn(node, mi, boot_pages);//node節(jié)點(diǎn)內(nèi)存的bank中找到一個(gè)可以放置位圖的頁(yè)面的頁(yè)面序列,然后返回這個(gè)頁(yè)面序列的首個(gè)頁(yè)面號(hào)

       node_set_online(node);//設(shè)置本節(jié)點(diǎn)有效

       pgdat = NODE_DATA(node);//獲取節(jié)點(diǎn)描述符pgdat

       init_bootmem_node(pgdat, boot_pfn, start_pfn, end_pfn);//設(shè)置本節(jié)點(diǎn)內(nèi)所有映射頁(yè)的位圖,即每個(gè)字節(jié)全部置為0xff,表示已經(jīng)映射使用。然后填充pgdat結(jié)構(gòu)

 

       for_each_nodebank(i, mi, node)

              free_bootmem_node(pgdat, mi->bank[i].start, mi->bank[i].size);//設(shè)置每個(gè)映射的頁(yè)面空閑,實(shí)際是對(duì)位圖的操作,對(duì)每個(gè)bit清零

        

       reserve_bootmem_node(pgdat, boot_pfn << PAGE_SHIFT,

                          boot_pages << PAGE_SHIFT, BOOTMEM_DEFAULT);

//標(biāo)示位圖所占的頁(yè)面被占用

       if (node == 0)

              reserve_node_zero(pgdat);

 

#ifdef CONFIG_BLK_DEV_INITRD

       /*

        * If the initrd is in this node, reserve its memory.

        */

       if (node == initrd_node) {

              int res = reserve_bootmem_node(pgdat, phys_initrd_start,

                                 phys_initrd_size, BOOTMEM_EXCLUSIVE);

//INITRD映像占用的空間需要標(biāo)示占用,INITRD是虛擬根文件系統(tǒng),此時(shí)還未加載,因此掛載之前這個(gè)物理空間不能再被分配使用

              if (res == 0) {

                     initrd_start = __phys_to_virt(phys_initrd_start);

                     initrd_end = initrd_start + phys_initrd_size;

              } else {

                     printk(KERN_ERR

                            "INITRD: 0x%08lx+0x%08lx overlaps in-use "

                            "memory region - disabling initrd/n",

                            phys_initrd_start, phys_initrd_size);

              }

       }

#endif

 

       /*

        * initialise the zones within this node.

        */

       memset(zone_size, 0, sizeof(zone_size));

       memset(zhole_size, 0, sizeof(zhole_size));

 

       /*

        * The size of this node has already been determined.  If we need

        * to do anything fancy with the allocation of this memory to the

        * zones, now is the time to do it.

        */

       zone_size[0] = end_pfn - start_pfn;

       zhole_size[0] = zone_size[0];

       for_each_nodebank(i, mi, node)

              zhole_size[0] -= mi->bank[i].size >> PAGE_SHIFT;

//計(jì)算共有多少頁(yè)空洞,注意,有些bank的起始結(jié)束地址并不是剛好4K對(duì)齊的,因此,可能存在某些空白頁(yè)框。用節(jié)點(diǎn)總的物理頁(yè)框減去每個(gè)bank頁(yè)框,就得到頁(yè)空洞

       //這個(gè)函數(shù)里面主要完成zone區(qū)的初始化,linux內(nèi)存管理將內(nèi)存節(jié)點(diǎn)又分為ZONE區(qū)管理,比如ZONE_DMAZONE_NORMAL等,因此需要初始化。由于平臺(tái)只針對(duì)一致性內(nèi)存管理,即物理內(nèi)存空間只包含DDR部分,此處很多函數(shù)是空的,再次略過(guò)

       arch_adjust_zones(node, zone_size, zhole_size);

 

       free_area_init_node(node, zone_size, start_pfn, zhole_size);

 

       return end_pfn;

}

//page_init的最后完成devicemaps_init初始化,比如中斷向量的映射。映射的大致過(guò)程是,申請(qǐng)一個(gè)物理框,然后調(diào)用creat_map將此物理頁(yè)框映射到0xffff0000.最后再調(diào)用struct machine_descmap_io完成IO設(shè)備的映射

//在完成內(nèi)存頁(yè)映射后即進(jìn)入request_standard_resources,這個(gè)函數(shù)比較簡(jiǎn)單,主要完成從iomem_resource空間申請(qǐng)所需的內(nèi)存資源,比如內(nèi)核代碼和視頻所需的資源等

       request_standard_resources(&meminfo, mdesc);

 

#ifdef CONFIG_SMP

       smp_init_cpus();

#endif

 

       cpu_init();//此函數(shù)為空

       init_arch_irq = mdesc->init_irq;//初始化與硬件體系相關(guān)的指針

       system_timer = mdesc->timer;

       init_machine = mdesc->init_machine;

 

#ifdef CONFIG_VT

#if defined(CONFIG_VGA_CONSOLE)

       conswitchp = &vga_con;

#elif defined(CONFIG_DUMMY_CONSOLE)

       conswitchp = &dummy_con;

#endif

#endif

       early_trap_init();//重定位中斷向量,將中斷向量代碼拷貝到中斷向量頁(yè),并把信號(hào)處理代碼指令拷貝到向量頁(yè)中

}

       mm_init_owner(&init_mm, &init_task);//空函數(shù)

       setup_command_line(command_line);//保存命令行,以備后用,此保存空間需申請(qǐng)

//這個(gè)函數(shù)調(diào)用完了,就開(kāi)始執(zhí)行下面初始化函數(shù)

       unwind_setup();//空函數(shù)

       setup_per_cpu_areas();//設(shè)置每個(gè)CPU信息,單核CPU為空函數(shù)

       setup_nr_cpu_ids();//空函數(shù)

       smp_prepare_boot_cpu();  //設(shè)置啟動(dòng)的CPU為在線狀態(tài).在多CPU架構(gòu)下

//第一個(gè)啟動(dòng)的cpu啟動(dòng)到一定階段后,開(kāi)始啟動(dòng)其它的cpu,它會(huì)為每個(gè)后來(lái)啟動(dòng)的cpu創(chuàng)建一個(gè)0號(hào)進(jìn)程,而這些0號(hào)進(jìn)程的堆棧的thread_info結(jié)構(gòu)中的cpu成員變量則依次被分配出來(lái)(利用alloc_cpu_id()函數(shù))并設(shè)置好,這樣當(dāng)這些cpu開(kāi)始運(yùn)行的時(shí)候就有了自己的邏輯cpu號(hào)。

       sched_init();//初始化調(diào)度器,對(duì)調(diào)度機(jī)制進(jìn)行初始化,對(duì)每個(gè)CPU的運(yùn)行隊(duì)列

      

       preempt_disable();//啟動(dòng)階段系統(tǒng)比較脆弱,禁止進(jìn)程調(diào)度

       build_all_zonelists();//建立內(nèi)存區(qū)域鏈表

       page_alloc_init();//內(nèi)存頁(yè)初始化,此處無(wú)執(zhí)行

       printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);

       parse_early_param();

       parse_args("Booting kernel", static_command_line, __start___param,

                 __stop___param - __start___param,

                 &unknown_bootoption);

       //執(zhí)行命令行解析,若參數(shù)不存在,則調(diào)用unknown_bootoption

       if (!irqs_disabled()) {

              printk(KERN_WARNING "start_kernel(): bug: interrupts were "

                            "enabled *very* early, fixing it/n");

              local_irq_disable();

       }

       sort_main_extable();//對(duì)異常處理函數(shù)進(jìn)行排序

       trap_init();//空函數(shù)

       rcu_init();//linux2.6的一種互斥訪問(wèn)機(jī)制

       init_IRQ();//中斷向量初始化

       pidhash_init();//進(jìn)程嘻哈表初始化

       init_timers();//定時(shí)器初始化

       hrtimers_init();//高精度時(shí)鐘初始化

       softirq_init();//軟中斷初始化

       timekeeping_init();//系統(tǒng)時(shí)間初始化

       time_init();

       sched_clock_init();

       profile_init();//空函數(shù)

       if (!irqs_disabled())

              printk("start_kernel(): bug: interrupts were enabled early/n");

       early_boot_irqs_on();

       local_irq_enable();

       console_init();//打印終端初始化

       if (panic_later)

              panic(panic_later, panic_param);

 

       lockdep_info();

       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();//建立節(jié)點(diǎn)嘻哈表和數(shù)據(jù)緩沖嘻哈表

       cpuset_init_early();//空函數(shù)

       mem_init();//對(duì)全局的物理頁(yè)變量初始化,對(duì)沒(méi)有分配的頁(yè)面初始化

       enable_debug_pagealloc();

       cpu_hotplug_init();//沒(méi)有熱插拔CPU,此函數(shù)為空

       kmem_cache_init();//內(nèi)核內(nèi)存緩沖區(qū)初始化

       debug_objects_mem_init();

       idr_init_cache();//創(chuàng)建idr緩沖區(qū)

       setup_per_cpu_pageset();//采用的是一致性內(nèi)存,此函數(shù)為空

       numa_policy_init();//采用的是一致性內(nèi)存,此函數(shù)為空

       if (late_time_init)

              late_time_init();

       calibrate_delay();//校準(zhǔn)延時(shí)函數(shù)的精確度,實(shí)際上是校準(zhǔn)loops_per_jiffy全局變量,即每個(gè)時(shí)鐘滴答內(nèi)CPU執(zhí)行的指令數(shù)

       pidmap_init();//進(jìn)程號(hào)位圖初始化,一般用一個(gè)page來(lái)指示所有的進(jìn)程PID占用情況

       pgtable_cache_init();//空函數(shù)

       prio_tree_init();//初始化優(yōu)先級(jí)數(shù)組

       anon_vma_init();//空函數(shù)

#ifdef CONFIG_X86

       if (efi_enabled)

              efi_enter_virtual_mode();

#endif

       thread_info_cache_init();//空函數(shù)

       fork_init(num_physpages);//初始化kernelfork()環(huán)境。Linux下應(yīng)用程序執(zhí)行是靠系統(tǒng)調(diào)用fork()完成,fork_init所完成的工作就是確定可以fork()的線程的數(shù)量,然后是初始化init_task進(jìn)程

       proc_caches_init();//proc文件系統(tǒng)創(chuàng)建高速緩存

       buffer_init();//空函數(shù)

       unnamed_dev_init();//初始化一個(gè)虛擬文件系統(tǒng)使用的啞文件

       key_init();//沒(méi)有鍵盤(pán)則為空,如果有鍵盤(pán),則為鍵盤(pán)分配一個(gè)高速緩存

       security_init();//空函數(shù)

       vfs_caches_init(num_physpages);//虛擬文件系統(tǒng)掛載,這個(gè)函數(shù)的詳細(xì)說(shuō)明如下

 

void __init vfs_caches_init(unsigned long mempages)//參數(shù)說(shuō)明系統(tǒng)內(nèi)存的物理頁(yè)數(shù)

{

       unsigned long reserve;

 

       reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);

       mempages -= reserve;

//創(chuàng)建一個(gè)高速緩存

       names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,

                     SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);

 

       filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,

                     SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);

 

       dcache_init();//在高速緩存中分配一個(gè)目錄項(xiàng),并初始化

       inode_init();//在高速緩存中分配一個(gè)inode節(jié)點(diǎn),并初始化

       files_init(mempages);//初始化文件描述符,初始化全局文件狀態(tài)變量

       mnt_init();

       bdev_cache_init();//如果編譯階段設(shè)置了塊設(shè)備,則注冊(cè)一個(gè)塊設(shè)備文件系統(tǒng)

       chrdev_init();//初始化字符設(shè)備管理數(shù)組cdev_map

}

// mnt_init()是創(chuàng)建根文件系統(tǒng)的關(guān)鍵,解釋如下

void __init mnt_init(void)

{

       unsigned u;

       int err;

 

       init_rwsem(&namespace_sem);

//創(chuàng)建一個(gè)虛擬文件系統(tǒng)的vfsmount結(jié)構(gòu)緩存。每個(gè)掛載的文件系統(tǒng)都有一個(gè)

struct vfsmoun結(jié)構(gòu)

       mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),

                     0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

//創(chuàng)建文件系統(tǒng)掛載嘻哈表

       mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);

 

       if (!mount_hashtable)

              panic("Failed to allocate mount hash table/n");

 

       printk("Mount-cache hash table entries: %lu/n", HASH_SIZE);

//初始化嘻哈表

       for (u = 0; u < HASH_SIZE; u++)

              INIT_LIST_HEAD(&mount_hashtable[u]);

 

       err = sysfs_init();//創(chuàng)建一個(gè)sysfs虛擬文件系統(tǒng),并掛載為根文件系統(tǒng)。如果系統(tǒng)不指定sysfs,則此函數(shù)為空

       if (err)

              printk(KERN_WARNING "%s: sysfs_init error: %d/n",

                     __func__, err);

       fs_kobj = kobject_create_and_add("fs", NULL);//創(chuàng)建一個(gè)對(duì)象文件,加到文件系統(tǒng)中

       if (!fs_kobj)

              printk(KERN_WARNING "%s: kobj create error/n", __func__);

       init_rootfs();//注冊(cè)一個(gè)rootfs文件系統(tǒng)

       init_mount_tree();//將上面創(chuàng)建的rootfs文件系統(tǒng)掛載為根文件系統(tǒng)。這只是個(gè)虛擬的文件系統(tǒng),就好比只是創(chuàng)建了一個(gè)/目錄。最后,這個(gè)函數(shù)會(huì)為系統(tǒng)最開(kāi)始的進(jìn)程( init_task 進(jìn)程)準(zhǔn)備他的進(jìn)程數(shù)據(jù)塊中的namespace 域,主要目的是將 do_kern_mount() 函數(shù)中建立的 mnt dentry 信息記錄在了 init_task 進(jìn)程的進(jìn)程數(shù)據(jù)塊中,這樣任何以后從 init_task 進(jìn)程 fork 出來(lái)的進(jìn)程也都先天地繼承了這一信息。

//下面是進(jìn)行radix樹(shù)初始化。這個(gè)是linux2.6引入的為各種頁(yè)面操作的重要結(jié)構(gòu)之一struct  radix_tree_node。這種數(shù)據(jù)結(jié)構(gòu)將指針與一個(gè)long型鍵值關(guān)聯(lián)起來(lái),提供高效快速查找。具體不再分析

       radix_tree_init();

       signals_init();//創(chuàng)建并初始化信號(hào)隊(duì)列

       /* rootfs populating might need page-writeback */

       page_writeback_init();//CPU在內(nèi)存中開(kāi)辟高速緩存,CPU直接訪問(wèn)高速緩存提以高速度。當(dāng)cpu更新了高速緩存的數(shù)據(jù)后,需要定期將高速緩存的數(shù)據(jù)寫(xiě)回到存儲(chǔ)介質(zhì)中,比如磁盤(pán)和flash等。這個(gè)函數(shù)初始化寫(xiě)回的周期

#ifdef CONFIG_PROC_FS

       proc_root_init();//如果配置了proc文件系統(tǒng),則需初始化并加載proc文件系統(tǒng)。在根目錄的proc文件夾就是proc文件系統(tǒng),這個(gè)文件系統(tǒng)是ram類(lèi)型的,記錄系統(tǒng)的臨時(shí)數(shù)據(jù),系統(tǒng)關(guān)機(jī)后不會(huì)寫(xiě)回到flash

#endif

       cgroup_init();//沒(méi)有配置cgroup,此函數(shù)為空

       cpuset_init();//CPU,此函數(shù)為空

       taskstats_init_early();//進(jìn)程狀態(tài)初始化,實(shí)際上就是分配了一個(gè)存儲(chǔ)線程狀態(tài)的高速緩存

       delayacct_init();//空函數(shù)

 

       check_bugs();//空函數(shù)

 

       acpi_early_init();//空函數(shù)

rest_init();//start_kernel啟動(dòng)的最后一個(gè)函數(shù),進(jìn)入這個(gè)函數(shù),完成剩余啟動(dòng)的初始化

}

 

// rest_init()大致解釋如下:

static void noinline __init_refok rest_init(void)

       __releases(kernel_lock)

{

       int pid;

//創(chuàng)建內(nèi)核線程。Kernel_thread運(yùn)用系統(tǒng)調(diào)用do_fork()產(chǎn)生新的子線程,子線程就調(diào)用傳入的調(diào)用函數(shù)執(zhí)行之,此處函數(shù)就是kernel_init. kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

       numa_default_policy();//空函數(shù)

       pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

       kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);

       unlock_kernel();

 

       /*

        * The boot idle thread must execute schedule()

        * at least once to get things moving:

        */

       init_idle_bootup_task(current);

       preempt_enable_no_resched();

       schedule();

       preempt_disable();

 

       /* Call into cpu_idle with preempt disabled */

       cpu_idle();

}

// kernel_init通過(guò)調(diào)用do_basic_setup完成編譯階段注冊(cè)的設(shè)備驅(qū)動(dòng)程序初始化。

//這個(gè)函數(shù)又調(diào)用了一個(gè)很重要的初始化函數(shù)Do_initcalls()。它用來(lái)啟動(dòng)所有在__initcall_start__initcall_end段的函數(shù),而靜態(tài)編譯進(jìn)內(nèi)核的modules也會(huì)將其入口放置在這段區(qū)間里。和根文件系統(tǒng)相關(guān)的初始化函數(shù)都會(huì)由rootfs_initcall()所引用。rootfs_initcall(populate_rootfs);

也就是說(shuō)會(huì)在系統(tǒng)初始化的時(shí)候會(huì)調(diào)用populate_rootfs進(jìn)行初始化。代碼如下:

 

static int __init populate_rootfs(void)

{

       char *err = unpack_to_rootfs(__initramfs_start,

                      __initramfs_end - __initramfs_start, 0);

       if (err)

              panic(err);

       if (initrd_start) {

#ifdef CONFIG_BLK_DEV_RAM

              int fd;

              printk(KERN_INFO "checking if image is initramfs...");

              err = unpack_to_rootfs((char *)initrd_start,

                     initrd_end - initrd_start, 1);

              if (!err) {

                     printk(" it is/n");

                     unpack_to_rootfs((char *)initrd_start,

                            initrd_end - initrd_start, 0);

                     free_initrd();

                     return 0;

              }

              printk("it isn't (%s); looks like an initrd/n", err);

              fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);

              if (fd >= 0) {

                     sys_write(fd, (char *)initrd_start,

                                   initrd_end - initrd_start);

                     sys_close(fd);

                     free_initrd();

              }

#else

              printk(KERN_INFO "Unpacking initramfs...");

              err = unpack_to_rootfs((char *)initrd_start,

                     initrd_end - initrd_start, 0);

              if (err)

                     panic(err);

              printk(" done/n");

              free_initrd();

#endif

       }

       return 0;

}

//unpack_to_rootfs就是解壓包,所解得包就是usr/initramfs_data.cpio.gz下的文件系統(tǒng)。然后將其釋放至上面創(chuàng)建的rootfs。注意這個(gè)文件系統(tǒng)已經(jīng)在編譯的時(shí)候用build_in.O的方式一種是跟kernel融為一體了所在的段就是__initramfs_start__initramfs_end的區(qū)域。這種情況下,直接調(diào)用unpack_to_rootfs將其釋放到根目錄.如果不是屬于這種形式的。也就是由內(nèi)核參數(shù)指定的文件系統(tǒng),即image-initrd文件系統(tǒng)。如果配制CONFIG_BLK_DEV_RAM才會(huì)支持image-initrd。否則全當(dāng)成cpio-initrd的形式處理。

對(duì)于是cpio-initrd的情況。直接將其釋放到根目錄。對(duì)于是image-initrd的情況。在根目錄下建立/initrd.image文件,然后將INITRD寫(xiě)到這個(gè)文件中,并釋放INITRD占用的空間。。

接下來(lái),就開(kāi)始具體的掛載操作,在kernel_init函數(shù)中完成

 

static int __init kernel_init(void * unused)

{

       lock_kernel();

       /*

        * init can run on any cpu.

        */

       set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR);

       /*

        * Tell the world that we're going to be the grim

        * reaper of innocent orphaned children.

        *

        * We don't want people to have to make incorrect

        * assumptions about where in the task array this

        * can be found.

        */

       init_pid_ns.child_reaper = current;

 

       cad_pid = task_pid(current);

 

       smp_prepare_cpus(setup_max_cpus);

 

       do_pre_smp_initcalls();

 

       smp_init();

       sched_init_smp();

 

       cpuset_init_smp();

 

       do_basic_setup();

 

       if (!ramdisk_execute_command)

              ramdisk_execute_command = "/init";

//如果存在指定的INITRD命令行參數(shù),則執(zhí)行命令行參數(shù)指定的init文件,如果不存在,則制定執(zhí)行的命令為根目錄下的init文件。如果用戶指定的文件系統(tǒng)存在,則調(diào)用prepare_namespace();進(jìn)行文件系統(tǒng)掛載的預(yù)操作

       if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

              ramdisk_execute_command = NULL;

//prepare_namespace()中首先會(huì)輪訓(xùn)檢測(cè)塊設(shè)備,若檢測(cè)到則創(chuàng)建一個(gè)設(shè)備節(jié)點(diǎn),然后分配一個(gè)設(shè)備號(hào)。如果saved_root_name不為空,則說(shuō)明內(nèi)核有指定設(shè)備作為根文件系統(tǒng),則通過(guò)mount_block_root掛載根文件系統(tǒng),然后退出即可。

比如有時(shí)指定了內(nèi)核參數(shù)root=/dev/ram,則直接從這個(gè)位置進(jìn)行掛載。

如果沒(méi)有指定的塊設(shè)備作為根文件系統(tǒng),而是指明了INITRD映像,則調(diào)用initrd_load 函數(shù)掛載initram文件系統(tǒng)。這個(gè)函數(shù)首先創(chuàng)建/dev/ram設(shè)備節(jié)點(diǎn),然后把映像拷貝到這個(gè)設(shè)備文件中,接著調(diào)用handle_initrd對(duì)INITRD進(jìn)行處理。

prepare_namespace()執(zhí)行最后調(diào)用mount_root();將指定的文件系統(tǒng)掛接到/root下,然后切換當(dāng)前目錄到root下。再者,還需調(diào)用sys_mount(".", "/", NULL, MS_MOVE, NULL);將當(dāng)前目錄掛接為/根目錄。

 

 

              prepare_namespace();

       }

       init_post();

       return 0;

}

 

static void __init handle_initrd(void)

{

       int error;

       int pid;

 

       real_root_dev = new_encode_dev(ROOT_DEV);//真正的根文件節(jié)點(diǎn)

       create_dev("/dev/root.old", Root_RAM0);//創(chuàng)建一個(gè)設(shè)備節(jié)點(diǎn),設(shè)備號(hào)是Root_RAM0,因此這個(gè)節(jié)點(diǎn)對(duì)應(yīng)是/dev/ramINITRD

       /* mount initrd on rootfs' /root */

//將此設(shè)備掛接到/root

       mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);

       sys_mkdir("/old", 0700);

       root_fd = sys_open("/", 0, 0);//記錄根目錄的文件描述符

       old_fd = sys_open("/old", 0, 0);//記錄old目錄的文件描述符

       /* move initrd over / and chdir/chroot in initrd root */

       sys_chdir("/root");//切換至root目錄,剛才已經(jīng)掛載了/dev/root.old

       sys_mount(".", "/", NULL, MS_MOVE, NULL);//當(dāng)前目錄掛載為根文件系統(tǒng),也就是/dev/root.old變成現(xiàn)在的根文件系統(tǒng)

       sys_chroot(".");//切換到當(dāng)前文件系統(tǒng)的根目錄

       current->flags |= PF_FREEZER_SKIP;

//創(chuàng)建一個(gè)進(jìn)程,運(yùn)行目前文件系統(tǒng)下的/linuxrc文件

       pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);

       if (pid > 0)

              while (pid != sys_wait4(-1, NULL, 0, NULL))

                     yield();

 

       current->flags &= ~PF_FREEZER_SKIP;

 

       /* move initrd to rootfs' /old */

       sys_fchdir(old_fd);

       sys_mount("/", ".", NULL, MS_MOVE, NULL);

//處理完上面的文件,則initrd處理完之后,重新chroot進(jìn)入rootfs

       /* switch root and cwd back to / of rootfs */

       sys_fchdir(root_fd);

       sys_chroot(".");

       sys_close(old_fd);

       sys_close(root_fd);

//如果real_root_dev linuxrc中重新設(shè)成Root_RAM0,則initrd就是最終的realfs了,改變當(dāng)前目錄到initrd中,不作后續(xù)處理直接返回。

       if (new_decode_dev(real_root_dev) == Root_RAM0) {

              sys_chdir("/old");

              return;

       }

//否則需要重新掛載上面linuxRC文件執(zhí)行時(shí)指定的根文件系統(tǒng)

       ROOT_DEV = new_decode_dev(real_root_dev);

       mount_root();

 

       printk(KERN_NOTICE "Trying to move old root to /initrd ... ");

       error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);

       if (!error)

              printk("okay/n");

       else {

              int fd = sys_open("/dev/root.old", O_RDWR, 0);

              if (error == -ENOENT)

                     printk("/initrd does not exist. Ignored./n");

              else

                     printk("failed/n");

              printk(KERN_NOTICE "Unmounting old root/n");

              sys_umount("/old", MNT_DETACH);

              printk(KERN_NOTICE "Trying to free ramdisk memory ... ");

              if (fd < 0) {

                     error = fd;

              } else {

                     error = sys_ioctl(fd, BLKFLSBUF, 0);

                     sys_close(fd);

              }

              printk(!error ? "okay/n" : "failed/n");

       }

}

static int noinline init_post(void)

{

       free_initmem();

       unlock_kernel();

       mark_rodata_ro();

       system_state = SYSTEM_RUNNING;

       numa_default_policy();

 

       if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)

              printk(KERN_WARNING "Warning: unable to open an initial console./n");

 

       (void) sys_dup(0);

       (void) sys_dup(0);

 

       current->signal->flags |= SIGNAL_UNKILLABLE;

//剛才上面已經(jīng)初始化了ramdisk_execute_command,此處可直接運(yùn)行之。

如果不存在則運(yùn)行下面程序,如果都不存在,則退出。

下面的/sbin/init就是上述掛載的根文件系統(tǒng)下的文件。

       if (ramdisk_execute_command) {

              run_init_process(ramdisk_execute_command);

              printk(KERN_WARNING "Failed to execute %s/n",

                            ramdisk_execute_command);

       }

       if (execute_command) {

              run_init_process(execute_command);

              printk(KERN_WARNING "Failed to execute %s.  Attempting "

                                   "defaults.../n", execute_command);

       }

       run_init_process("/sbin/init");

       run_init_process("/etc/init");

       run_init_process("/bin/init");

       run_init_process("/bin/sh");

 

       panic("No init found.  Try passing init= option to kernel.");

}

//當(dāng)沒(méi)有找到init程序后,則退出,進(jìn)行進(jìn)程調(diào)度,進(jìn)入cpu_idle()進(jìn)程

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多