linux -- 啓動分析及耗時分析

入口:
start_kernel(kernel/init/main.c) 
{

        char *command_line;
        char *after_dashes;

    lockdep_init() /* ? 初始化lockdep hash 表 or 系統哈希表chainhash_table ? */
    set_task_stack_end_magic()

    smp_setup_processor_id(kernel/arch/arm/kernel/setup.c) /*當只有一個CPU的時候這個函數就什麼都不做,但是如果有多個CPU的時候那麼它就 返回在啓動的時候的那個CPU的號  */
        [    0.000000] Booting Linux on physical CPU 0x500

    debug_objects_early_init() //初始化debug kernel相關
    boot_init_stack_canary() //初始化棧canary值,canary值防止棧溢出攻擊, stack_canary是帶防止棧溢出攻擊保護的堆棧。

    cgroup_init_early(kernel/kernel/cgroup.c) //Cgroup初始化,將一組任務在一個或多個子系統中與一組參數關聯.Cgroup是近代linux kernel出現的.它爲進程和其後續的子進程提供了一種性能控制機制
        --> cgroup_init_subsys
            [    0.000000] Initializing cgroup subsys cpuset
            [    0.000000] Initializing cgroup subsys cpu
            [    0.000000] Initializing cgroup subsys cpuacct

    local_irq_disable()  /*關閉當前CPU的中斷*/
    boot_cpu_init() //對於CPU核的系統來說,設置第一個CPU核爲活躍CPU核。對於單CPU核系統來說,設置CPU核爲活躍CPU核

    pr_notice("%s", linux_banner);
        [    0.000000] Linux version 4.4.41 (feizhitao@feizhitao-All-Series) (gcc version 4.8.4 (Ubuntu/Linaro 4.8.4-2ubuntu1~14.04.1) ) #5 SMP Thu May 11 13:54:33 CST 2017

        /*每種體系結構都有自己的setup_arch()函數,是體系結構相關的,具體編譯哪個 體系結構的setup_arch()函數,由源碼樹頂層目錄下的Makefile中的ARCH變量決定 
          例如: ARM體系結構的。
          SUBARCH :=arm  
          ARCH  ?= $(SUBARCH)
          該函數在所在的路勁爲 /arm/kernel/setup.c,其中那個command_line就是有bootloader傳過來的!這個函數是個重量級的函數,大家不可忽視!該函數完成體系結構相關的初始化,
          內核移植的過程一般也就到此函數爲止了,其餘的就只是一些相關的外設驅動。*/
    setup_arch(kernel/arch/arm/kernel/setup.c)
        --> setup_processor()
            [    0.000000] CPU: ARMv7 Processor [410fc0d1] revision 1 (ARMv7), cr=10c5387d
            --> cacheid_init()
                [    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache

        --> setup_machine_fdt(kernel/arch/arm/kernel/devtree.c)
            --> of_flat_dt_match_machine(kernel/drivers/of/fdt.c)
                [    0.000000] Machine model: rockchip,rk3288-evb-act8846

        --> paging_init(kernel/arch/arm/mm/mmu.c)
            -->build_mem_type_table()
                [    0.000000] Memory policy: Data cache writealloc
            --> bootmem_init(kernel/arch/arm/mm/init.c)-->zone_sizes_init()-->free_area_init_node
                -->free_area_init_node(kernel/mm/page_alloc.c) /* ? free_area_init_nodes() x, ? free_area_init() x*/
                    --> calculate_node_totalpages()
                        [    0.000000] On node 0 totalpages: 524288

                    [    0.000000] free_area_init_node: node 0, pgdat c1095340, node_mem_map eeffa000

                    --> free_area_init_core()
                        [    0.000000]   Normal zone: 1536 pages used for memmap
                        [    0.000000]   Normal zone: 0 pages reserved
                        [    0.000000]   Normal zone: 196608 pages, LIFO batch:31
                        [    0.000000]   HighMem zone: 327680 pages, LIFO batch:31

    static inline void  mm_init_cpumask(kernel/include/linux/mm_types.h) /* cpu屏蔽位清零 */
    setup_command_line() /*將命令行參數保存到字符數組static_command_line*/
    setup_nr_cpu_ids()

    setup_per_cpu_areas(kernel/mm/percpu.c) /* 每個CPU分配pre-cpu結構內存並複製.data.percpu段的數據 */ 
        --> pcpu_embed_first_chunk()
            --> pr_info("PERCPU: Embedded %zu pages/cpu @%p s%zu r%zu d%zu u%zu\n",
            --> pcpu_setup_first_chunk()
                --> pcpu_dump_alloc_info() 
                    [    0.000000] pcpu-alloc: s24448 r8192 d20608 u53248 alloc=13*4096
                    [    0.000000] pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3 

    smp_prepare_boot_cpu();  /* arch-specific boot-cpu hooks */
    /*? numa_zonelist_order_handler(kernel/mm/page_alloc.c) */
        /*-->*/ 
    build_all_zonelists(kernel/mm/page_alloc.c) /* 建立系統內存頁區(zone)鏈表 */
            [    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 522752

    page_alloc_init(kernel/mm/page_alloc.c)
        --> hotcpu_notifier(?) /* no print*/
    pr_notice("Kernel command line: %s\n", boot_command_line);
        [    0.000000] Kernel command line: earlyprintk console=tty1 console=ttyS2,115200n8 rw root=/dev/mmcblk2p7 rootfstype=ext4 init=/sbin/init

    parse_early_param(); /* 解析啓動命令行參數 */
    after_dashes = parse_args("Booting kernel",  static_command_line, __start___param, __stop___param - __start___param, -1, -1, NULL, &unknown_bootoption);
    jump_label_init();

    /*
    * These use large bootmem allocations and must precede
    * kmem_cache_init()
    */
/* 設置 log輸出緩衝buf */
    setup_log_buf(0);

/* 初始化和分配pid散列表 */
    pidhash_init(kernel/kernel/pid.c);
        [    0.000000] PID hash table entries: 4096 (order: 2, 16384 bytes)

/* 創建虛擬文件系統(vfs)需要各種數據結構的緩存 */
    vfs_caches_init_early(kernel/fs/dcache.c) 
        --> dcache_init_early() 
            [    0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
        --> inode_init_early()
            [    0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)

/* 內核異常表排序 */
    sort_main_extable();

/* 異常捕獲設置初始化,跟體系結構相關,arm架構的實現是空函數 */
    trap_init();

/* 內核內存分配器初始化,初始化slab機制分配器和vmalloc機制 */
    mm_init() --> mem_init(kernel/arch/arm/mm/init.c) --> mem_init_print_info(kernel/mm/page_alloc.c)
        [    0.000000] Memory: 2062916K/2097152K available (10240K kernel code, 628K rwdata, 2608K rodata, 1024K init, 548K bss, 34236K reserved, 0K cma-reserved, 1310720K highmem)
        [    0.000000] Virtual kernel memory layout:
                   vector  : 0xffff0000 - 0xffff1000   (   4 kB)
                   fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
                   vmalloc : 0xf0800000 - 0xff800000   ( 240 MB)
                   lowmem  : 0xc0000000 - 0xf0000000   ( 768 MB)
                   pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
                   modules : 0xbf000000 - 0xbfe00000   (  14 MB)
                     .text : 0xc0008000 - 0xc0b00000   (11232 kB)
                     .init : 0xc0f00000 - 0xc1000000   (1024 kB)
                     .data : 0xc1000000 - 0xc109d2a0   ( 629 kB)
                      .bss : 0xc109f000 - 0xc11282ac   ( 549 kB)

        /*
         * 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.
         */
/* 調度器數據結構初始化*/
        sched_init();

        /*
         * Disable preemption - early bootup scheduling is extremely
         * fragile until we cpu_idle() for the first time.
         */
/* 關搶斷和中斷,啓動期間不允許調度和中斷 */
        preempt_disable();
        if (WARN(!irqs_disabled(),
                 "Interrupts were enabled *very* early, fixing it\n"))
                local_irq_disable();

/* 爲 idr(一種將一個整數ID號和一個指針關聯在一起的機制) 機制創建cache */
        idr_init_cache();

/* rcu(read-copy-update, 內核鎖機制一類)機制初始化 */
        rcu_init();

        /* trace_printk() and trace points may be used after this */
        trace_init();

/* 上下文tracking機制 ? 該機制被 CONFIG_CONTEXT_TRACKING_FORCE 包住了 */
        context_tracking_init();

/* 爲內核基數樹算法分配內存,運用於內存頁查找 */
        radix_tree_init();

        /* init some links before init_ISA_irqs() */
/* 初始化體系結構相關irq,創建irq描述符,插入到基數屬鏈表 irq_desc_tree 中管理*/
        early_irq_init();
        init_IRQ();

/* 時鐘相關初始化*/
        tick_init();
        rcu_init_nohz();
        init_timers();
        hrtimers_init();
        softirq_init();
        timekeeping_init();
        time_init();

/* 進程調度時鐘初始化 */
        sched_clock_postinit();

/* cpu 性能相關monitor */
        perf_event_init();

/* gdb等debug工具設置相關 */
        profile_init();

/* smp下跨cpu的函數傳遞初始化 */
        call_function_init();
        WARN(!irqs_disabled(), "Interrupts were enabled early\n");
        early_boot_irqs_disabled = false;

/* 使能中斷 */
        local_irq_enable();

/* slab 分配器後期初始化 */
        kmem_cache_init_late();

        /*
         * 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.
         */
/* 終端控制檯輸出初始化 */
        console_init(); register_console

/* 檢查異常記錄信息,如果存在異常,走內核panic流程 */
        if (panic_later)
                panic("Too many boot %s vars at `%s'", panic_later,
                      panic_param);

/* printk 輸出相關信息 */
        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:
         */
/* 打印測試信息 */
        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) {
                pr_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
        page_ext_init();

/* debug 相關*/
        debug_objects_mem_init();

/* 內存leak監視 */
        kmemleak_init();

        setup_per_cpu_pageset();
        numa_policy_init();
        if (late_time_init)
                late_time_init();
        sched_clock_init();
        calibrate_delay();

/* 進程pid 映射表初始化 */
        pidmap_init();
        anon_vma_init();
        acpi_early_init();

#ifdef CONFIG_X86
        if (efi_enabled(EFI_RUNTIME_SERVICES))
                efi_enter_virtual_mode();
#endif
#ifdef CONFIG_X86_ESPFIX64
        /* Should be run before the first non-init thread is created */
        init_espfix_bsp();
#endif

/* 創建內核進程分配的cache */
        thread_info_cache_init(); 
        cred_init();

/* fork 機制初始化 */
        fork_init(); 

/* 創建進程需要的slab緩存 */
        proc_caches_init(); 
        buffer_init();

/* 內核安全架構初始化 */
        key_init(); 
        security_init();

/* kgdb 在線調試相關支持*/
        dbg_late_init(); 

/* vfs所需要的slab緩存 */
        vfs_caches_init();

/* 爲 sigqueue_cachep 創建slab緩存 */
        signals_init(); 
        /* rootfs populating might need page-writeback */

/* 內存頁寫回機制初始化 */
        page_writeback_init(); 

/* proc 文件系統 */
        proc_root_init(); 


        nsfs_init();
        cpuset_init();
        cgroup_init();
        taskstats_init_early();
        delayacct_init();

        check_bugs();

        acpi_subsystem_init();
        sfi_init_late();

        if (efi_enabled(EFI_RUNTIME_SERVICES)) {
                efi_late_init();
                efi_free_boot_services();
        }

/* trace 初始化 */
        ftrace_init();

        /* Do the rest non-__init'ed, we're now alive */
        rest_init();


}



static noinline void __init_refok rest_init(void)                                                     
{
        int pid;

        rcu_scheduler_starting();
        smpboot_thread_init();                                                                        
        /*                                                                                            
         * We need to spawn init first so that it obtains pid 1, however                              
         * the init task will end up wanting to create kthreads, which, if                            
         * we schedule it before we create kthreadd, will OOPS.                                       
         */
        kernel_thread(kernel_init, NULL, CLONE_FS);                                                   
        numa_default_policy();                                                                        
        pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);                                  
        rcu_read_lock();                                                                              
        kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);                                       
        rcu_read_unlock();
        complete(&kthreadd_done);                                                                     

        /*
         * The boot idle thread must execute schedule()                                               
         * at least once to get things moving:                                                        
         */
        init_idle_bootup_task(current);                                                               
        schedule_preempt_disabled();
        /* Call into cpu_idle with preempt disabled */                                                
        cpu_startup_entry(CPUHP_ONLINE);                                                              
}

小結:
1、首先0號進程fork出1號init進程,執行設備驅動初始化和拉起第一個用戶空間程序init;
2、接着0號進程fork出2號內核線程(kthreadd)作爲內核的守護進程;
3、然後0號進程進入idle循環.

耗時點1:

    ? uart_add_one_port(kernel/drivers/tty/serial/serial_core.c) --> uart_configure_port() --> uart_report_port()
        [    0.405601] ff180000.serial: ttyS0 at MMIO 0xff180000 (irq = 34, base_baud = 1500000) is a 16550A
        [    0.406749] ff190000.serial: ttyS1 at MMIO 0xff190000 (irq = 35, base_baud = 1500000) is a 16550A

    unregister_console(kernel/kernel/printk/printk.c)
        [    0.407936] console [ttyS2] disabled

    ? uart_add_one_port(kernel/drivers/tty/serial/serial_core.c) --> uart_configure_port() --> uart_report_port()
        [    0.408031] ff690000.serial: ttyS2 at MMIO 0xff690000 (irq = 36, base_baud = 1500000) is a 16550A

    register_console(kernel/kernel/printk/printk.c)
        [    1.216754] console [ttyS2] enabled
uart0: serial@ff180000
uart1: serial@ff190000 
uart2: serial@ff690000
uart3: serial@ff1b0000
uart4: serial@ff1c0000
    [    1.220905] rockchip-pinctrl pinctrl: pin gpio7-8 already requested by vcc-3g-regulator; cannot claim for ff1b0000.serial
    [    1.231938] rockchip-pinctrl pinctrl: pin-224 (ff1b0000.serial) status -22
    [    1.238868] rockchip-pinctrl pinctrl: could not request pin 224 (gpio7-8) from group uart3-xfer  on device rockchip-pinctrl

耗時點2gmac: ethernet@ff290000

    [    1.400192] tun: Universal TUN/TAP device driver, 1.6
    [    1.405307] tun: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
    [    1.413431] rk_gmac-dwmac ff290000.ethernet: clock input or output? (input).
    [    1.420514] rk_gmac-dwmac ff290000.ethernet: TX delay(0x30).
    [    1.426229] rk_gmac-dwmac ff290000.ethernet: RX delay(0x10).
    [    1.432138] rk_gmac-dwmac ff290000.ethernet: clock input from PHY
    [    1.438278] rk_gmac-dwmac ff290000.ethernet: init for RGMII
    [    1.448987] stmmac - user ID: 0x10, Synopsys ID: 0x35
    [    1.454082]  Ring mode enabled
    [    1.457155]  DMA HW capability register supported
    [    1.461687]  Normal descriptors
    [    1.465060]  RX Checksum Offload Engine supported (type 2)
    [    1.470560]  TX Checksum insertion supported
    [    1.474859]  Wake-Up On Lan supported
    [    1.478609]  Enable RX Mitigation via HW Watchdog Timer
of_get_named_gpio(kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c)
    [    1.484840] of_get_named_gpiod_flags: parsed 'snps,reset-gpio' property of node '/ethernet@ff290000[0]' - status (0)
    [    2.517745] libphy: stmmac: probed
    [    2.521180] eth0: PHY ID 001cc915 at 0 IRQ POLL (stmmac-0:00) active
    [    2.527611] eth0: PHY ID 001cc915 at 1 IRQ POLL (stmmac-0:01)
    [    2.534313] PPP generic driver version 2.4.2
    [    2.539048] usbcore: registered new interface driver rndis_wlan
    [    2.545350] usbcore: registered new interface driver rt2800usb
    [    2.551316] Rockchip WiFi SYS interface (V1.00) ... 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章