入口:
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
耗時點2:
gmac: 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) ...
linux -- 啓動分析及耗時分析
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.