一、鏈接腳本 vmlinux.lds
Linux 內(nèi)核的鏈接腳本文件 arch/arm/kernel/vmlinux.lds 中有如下代碼:
ENTRY 指明了了 Linux 內(nèi)核入口,入口為 stext,stext 定義在文件 arch/arm/kernel/head.S 中。
二、Linux 內(nèi)核啟動(dòng)流程分析
1、Linux 內(nèi)核入口 stext
stext 是 Linux 內(nèi)核的入口地址,在文件 arch/arm/kernel/head.S 中有如下所示提示內(nèi)容:
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags or dtb pointer.
.....
Linux 內(nèi)核啟動(dòng)之前要求如下:
①、關(guān)閉 MMU。
②、關(guān)閉 D-cache。
③、I-Cache 無(wú)所謂。
④、r0=0。
⑤、r1=machine nr(也就是機(jī)器 ID)。
⑥、r2=atags 或者設(shè)備樹(shù)(dtb)首地址。
Linux 內(nèi)核的入口點(diǎn) stext 其實(shí)相當(dāng)于內(nèi)核的入口函數(shù),stext 函數(shù)內(nèi)容如下:
80 ENTRY(stext)
......
91 @ ensure svc mode and all interrupts masked
92 safe_svcmode_maskall r9
93
94 mrc p15, 0, r9, c0, c0 @ get processor id
95 bl __lookup_processor_type @ r5=procinfo r9=cpuid
96 movs r10, r5 @ invalid processor (r5=0)?
97 THUMB( it eq ) @ force fixup-able long branch encoding
98 beq __error_p @ yes, error 'p'
99
......
107
108 #ifndef CONFIG_XIP_KERNEL
......
113 #else
114 ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case
115 #endif
116
117 /*
118 * r1 = machine no, r2 = atags or dtb,
119 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
120 */
121 bl __vet_atags
......
128 bl __create_page_tables
129
130 /*
131 * The following calls CPU specific code in a position independent
132 * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
133 * xxx_proc_info structure selected by __lookup_processor_type
134 * above. On return, the CPU will be ready for the MMU to be
135 * turned on, and r0 will hold the CPU control register value.
136 */
137 ldr r13, =__mmap_switched @ address to jump to after
138 @ mmu has been enabled
139 adr lr, BSYM(1f) @ return (PIC) address
140 mov r8, r4 @ set TTBR1 to swapper_pg_dir
141 ldr r12, [r10, #PROCINFO_INITFUNC]
142 add r12, r12, r10
143 ret r12
144 1: b __enable_mmu
145 ENDPROC(stext)
第 92 行:調(diào)用函數(shù) safe_svcmode_maskall 確保 CPU 處于 SVC 模式,并關(guān)閉所有中斷,定義arch/arm/include/asm/assembler.h 中。
第 94 行:讀處理器 ID,ID 值保存在 r9 寄存器中。
第 95 行:調(diào)用函數(shù)__lookup_processor_type 檢查當(dāng)前系統(tǒng)是否支持此 CPU,如果支持就獲取 procinfo 信息。 procinfo 是 proc_info_list 類型的結(jié)構(gòu)體,在 arch/arm/include/asm/procinfo.h 中的定義如下:
struct proc_info_list {
unsigned int
cpu_val;
unsigned int cpu_mask;
unsigned long __cpu_mm_mmu_flags; /* used by head.S */
unsigned long __cpu_io_mmu_flags; /* used by head.S */
unsigned long __cpu_flush; /* used by head.S */
const char
*arch_name;
const char
*elf_name;
unsigned int elf_hwcap;
const char
*cpu_name;
struct processor *proc;
struct cpu_tlb_fns *tlb;
struct cpu_user_fns *user;
struct cpu_cache_fns *cache;
};
Linux 內(nèi)核將每種處理器都抽象為一個(gè) proc_info_list 結(jié)構(gòu)體,每種處理器都對(duì)應(yīng)一個(gè)procinfo。因此可以通過(guò)處理器 ID 來(lái)找到對(duì)應(yīng)的 procinfo 結(jié)構(gòu),__lookup_processor_type 函數(shù)找到對(duì)應(yīng)處理器的 procinfo 以后會(huì)將其保存到 r5 寄存器中。
第 121 行:調(diào)用函數(shù)__vet_atags 驗(yàn)證 atags 或設(shè)備樹(shù)(dtb)的合法性。函數(shù)__vet_atags 定義在文件 arch/arm/kernel/head-common.S 中。
第 128 行:調(diào)用函數(shù)__create_page_tables 創(chuàng)建頁(yè)表。
第 137 行:將函數(shù)__mmap_switched 的地址保存到 r13 寄存器中。定義在文件 arch/arm/kernel/head-common.S中,函數(shù)__mmap_switched 最終會(huì)調(diào)用 start_kernel 函數(shù)。
第 144 行:調(diào)用__enable_mmu函數(shù)使能 MMU ,__enable_mmu 定義在文件 arch/arm/kernel/head.S 中。__enable_mmu 會(huì)通過(guò)調(diào)用__turn_mmu_on來(lái)打開(kāi) MMU,__turn_mmu_on 最后會(huì)執(zhí)行 r13 里面保存的__mmap_switched 函數(shù)。
2、__mmap_switched 函數(shù)
__mmap_switched 函數(shù)定義在文件 arch/arm/kernel/head-common.S 中,最終調(diào)用 start_kernel 來(lái)啟動(dòng) Linux 內(nèi)核。
3、start_kernel 函數(shù)
start_kernel 函數(shù)定義在文件 init/main.c 中。start_kernel 里面調(diào)用了大量的函數(shù)來(lái)啟動(dòng) Linux 內(nèi)核,最后調(diào)用了 rest_init。
4、rest_init 函數(shù)
rest_init 函數(shù)定義在文件 init/main.c 中,函數(shù)內(nèi)容如下:
383 static noinline void __init_refok rest_init(void)
384 {
385 int pid;
386
387 rcu_scheduler_starting();
388 smpboot_thread_init();
389 /*
390 * We need to spawn init first so that it obtains pid 1, however
391 * the init task will end up wanting to create kthreads, which,
392 * if we schedule it before we create kthreadd, will OOPS.
393 */
394 kernel_thread(kernel_init, NULL, CLONE_FS);
395 numa_default_policy();
396 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
397 rcu_read_lock();
398 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
399 rcu_read_unlock();
400 complete(&kthreadd_done);
401
402 /*
403 * The boot idle thread must execute schedule()
404 * at least once to get things moving:
405 */
406 init_idle_bootup_task(current);
407 schedule_preempt_disabled();
408 /* Call into cpu_idle with preempt disabled */
409 cpu_startup_entry(CPUHP_ONLINE);
410 }
第 387 行:調(diào)用函數(shù) rcu_scheduler_starting,啟動(dòng) RCU 鎖調(diào)度器
第 394 行:調(diào)用函數(shù) kernel_thread 創(chuàng)建 kernel_init 進(jìn)程,也就是 init 內(nèi)核進(jìn)程。init 進(jìn)程的 PID 為 1。
第 396 行:調(diào)用函數(shù) kernel_thread 創(chuàng)建 kthreadd 內(nèi)核進(jìn)程,此內(nèi)核進(jìn)程的 PID 為 2。kthreadd 進(jìn)程負(fù)責(zé)所有內(nèi)核進(jìn)程的調(diào)度和管理。
第 409 行:最后調(diào)用函數(shù) cpu_startup_entry 來(lái)進(jìn)入 idle 進(jìn)程。
在 Linux 終端中輸入“ps -A”就可以打印出當(dāng)前系統(tǒng)中的所有進(jìn)程
5、init 進(jìn)程
kernel_init 函數(shù)就是 init 進(jìn)程具體做的工作,定義在文件 init/main.c 中,函數(shù)內(nèi)容如下:
928 static int __ref kernel_init(void *unused)
929 {
930 int ret;
931
932 kernel_init_freeable(); /* init 進(jìn)程的一些其他初始化工作 */
933 /* need to finish all async __init code before freeing the memory */
934 async_synchronize_full(); /* 等待所有的異步調(diào)用執(zhí)行完成 */
935 free_initmem(); /* 釋放 init 段內(nèi)存 */
936 mark_rodata_ro();
937 system_state = SYSTEM_RUNNING; /* 標(biāo)記系統(tǒng)正在運(yùn)行 */
938 numa_default_policy();
939
940 flush_delayed_fput();
941
942 if (ramdisk_execute_command) {
943 ret = run_init_process(ramdisk_execute_command);
944 if (!ret)
945 return 0;
946 pr_err('Failed to execute %s (error %d)n',
947 ramdisk_execute_command, ret);
948 }
949
950 /*
951 * We try each of these until one succeeds.
952 *
953 * The Bourne shell can be used instead of init if we are
954 * trying to recover a really broken machine.
955 */
956 if (execute_command) {
957 ret = run_init_process(execute_command);
958 if (!ret)
959 return 0;
960 panic('Requested init %s failed (error %d).',
961 execute_command, ret);
962 }
963 if (!try_to_run_init_process('/sbin/init') ||
964 !try_to_run_init_process('/etc/init') ||
965 !try_to_run_init_process('/bin/init') ||
966 !try_to_run_init_process('/bin/sh'))
967 return 0;
968
969 panic('No working init found. Try passing init= option to kernel. '
970 'See Linux Documentation/init.txt for guidance.');
971 }
第 932 行:kernel_init_freeable 用于完成 init 進(jìn)程的一些初始化工作。
第 940 行:ramdisk_execute_command 是一個(gè)全局的 char 指針變量,此變量值為“/init”,也就是根目錄下的 init 程序。ramdisk_execute_command 也可以通過(guò) uboot 傳遞,在 bootargs 中使用“rdinit=xxx”即可,xxx 為具體的 init 程序名字。
第 943 行:如果存在“/init”程序就通過(guò)函數(shù) run_init_process 運(yùn)行此程序。
第 956 行:如果 ramdisk_execute_command 為空就看 execute_command 是否為空,不管如何一定要在根文件系統(tǒng)中找到一個(gè) init 程序。execute_command 的值是通過(guò) uboot 傳遞,在 bootargs 中用“init=xxxx”就可以了,比如 “init=/linuxrc” 表示根文件系統(tǒng)中的 linuxrc 為要執(zhí)行的用戶空間init程序。
第 963~966 行:如果 ramdisk_execute_command 和 execute_command 都為空,那么就依次查找“/sbin/init”、“/etc/init”、“/bin/init”和“/bin/sh”,這四個(gè)相當(dāng)于備用 init 程序,如果這四個(gè)也不存在,那么 Linux 啟動(dòng)失??!
第 969 行:如果以上步驟都沒(méi)有找到用戶空間的 init 程序,那么就提示錯(cuò)誤發(fā)生!
kernel_init_freeable 定義在文件 init/main.c 中,縮減后內(nèi)容如下:
973 static noinline void __init kernel_init_freeable(void)
974 {
975 /*
976 * Wait until kthreadd is all set-up.
977 */
978 wait_for_completion(&kthreadd_done);/* 等待 kthreadd 進(jìn)程準(zhǔn)備就緒 */
......
998
999 smp_init(); /* SMP 初始化 */
1000 sched_init_smp(); /* 多核(SMP)調(diào)度初始化 */
1001
1002 do_basic_setup(); /* 設(shè)備初始化都在此函數(shù)中完成 */
1003
1004 /* Open the /dev/console on the rootfs, this should never fail */
1005 if (sys_open((const char __user *) '/dev/console', O_RDWR, 0) < 0)
1006 pr_err('Warning: unable to open an initial console.n');
1007
1008 (void) sys_dup(0);
1009 (void) sys_dup(0);
1010 /*
1011 * check if there is an early userspace init. If yes, let it do
1012 * all the work
1013 */
1014
1015 if (!ramdisk_execute_command)
1016 ramdisk_execute_command = '/init';
1017
1018 if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
1019 ramdisk_execute_command = NULL;
1020 prepare_namespace();
1021 }
1022
1023 /*
1024 * Ok, we have completed the initial bootup, and
1025 * we're essentially up and running. Get rid of the
上一篇:【IMX6ULL學(xué)習(xí)筆記】九、Linux內(nèi)核移植
下一篇:【IMX6ULL學(xué)習(xí)筆記】七、Linux 頂層Makefile
推薦閱讀最新更新時(shí)間:2025-07-02 12:07



設(shè)計(jì)資源 培訓(xùn) 開(kāi)發(fā)板 精華推薦
- Microchip 升級(jí)數(shù)字信號(hào)控制器(DSC)產(chǎn)品線 推出PWM 分辨率和 ADC 速度業(yè)界領(lǐng)先的新器件
- 意法半導(dǎo)體STM32MP23x:突破成本限制的工業(yè)AI應(yīng)用核心
- 意法半導(dǎo)體推出用于匹配遠(yuǎn)距離無(wú)線微控制器STM32WL33的集成的匹配濾波芯片
- ESP32開(kāi)發(fā)板連接TFT顯示屏ST7789跳坑記
- 如何讓ESP32支持analogWrite函數(shù)
- LGVL配合FreeType為可變字體設(shè)置字重-ESP32篇
- 使用樹(shù)莓派進(jìn)行 ESP32 Jtag 調(diào)試
- ESP32怎么在SPIFFS里面存儲(chǔ)html,css,js文件,以及網(wǎng)頁(yè)和arduino的通訊
- ESP32 freeRTOS使用測(cè)試
- LTC2194IUKG、16 位、105Msps 低功耗雙通道 ADC 的典型應(yīng)用
- LT3088HST 并聯(lián)穩(wěn)壓器的典型應(yīng)用
- 具有外部控制信號(hào)、AVIN 或 AGND 的 EP53A7LQI 1A 同步降壓穩(wěn)壓器的典型應(yīng)用電路
- LTC4367HDD-1 用于限制浪涌電流的過(guò)壓電源保護(hù)控制器的典型應(yīng)用
- 基于ST1S03的1.5 A / 3.3 V降壓DC-DC轉(zhuǎn)換器演示板
- 具有基于STAP08DP05和STM8AF的汽車應(yīng)用診斷的高亮度LED陣列驅(qū)動(dòng)器
- L7808C 高輸入輸出穩(wěn)壓器的典型應(yīng)用
- LT6657BHMS8-2.5 低壓差基準(zhǔn)電壓源的典型應(yīng)用電路
- 使用 Analog Devices 的 LTC3423EMS 的參考設(shè)計(jì)
- AD9261-10EBZ,AD9261 評(píng)估板,16 位連續(xù)時(shí)間 Sigma-Delta ADC
- 蘋(píng)果被判侵犯3G專利,需向西班牙公司TOT賠償1.1億美元
- 從設(shè)計(jì)概念到 FPGA 原型僅需數(shù)分鐘,印度 InCore 完成 SoC Generator 平臺(tái)硅驗(yàn)證
- 消息稱因難尋客戶,三星推遲美國(guó)芯片工廠的完工時(shí)間
- BOE(京東方)聯(lián)合榮耀打造榮耀Magic V5 以領(lǐng)先LTPO技術(shù)打造行業(yè)新標(biāo)桿
- 華為ADS 4發(fā)布:多傳感器融合,提升自動(dòng)駕駛安全性
- 曉鶯說(shuō):線控制動(dòng)變革風(fēng)云
- 大眾商用車推出AirConsole 將其信息娛樂(lè)系統(tǒng)擴(kuò)展為游戲機(jī)
- 福州大學(xué)發(fā)明新機(jī)器視覺(jué)傳感器 可使機(jī)器人對(duì)極端光照做出超快反應(yīng)
- 蘋(píng)果獲沉浸式虛擬顯示器相關(guān)的專利
- 英特爾汽車“折戟”,十年布局一夜歸零
- 重磅!節(jié)卡發(fā)布多款新品協(xié)作機(jī)器人!
- ABB CEO出席全球首席執(zhí)行官委員會(huì)圓桌峰會(huì),看好中國(guó)市場(chǎng)前景
- 博眾機(jī)器人邀您一起探索5G空間站
- 博通收購(gòu)賽門鐵克的戰(zhàn)略布局是什么?
- 存儲(chǔ)芯片出貨量下降,三星Q2季度利潤(rùn)再跌60%
- 世界機(jī)器人強(qiáng)國(guó)排行榜: 第一名居然是它
- 汽車技術(shù)加速成長(zhǎng) 電子模塊迎來(lái)機(jī)遇
- 歐洲實(shí)現(xiàn)首個(gè)5G數(shù)據(jù)連接 德國(guó)電信在柏林部署5G天線
- 特斯拉虧損擴(kuò)大 未來(lái)利潤(rùn)或受美關(guān)稅影響
- 暗流涌動(dòng)的自動(dòng)駕駛勢(shì)力 也許將成為未來(lái)出行領(lǐng)域的王者
- 看到圖片我~~~~~~~~~~··
- 在SOPC中自定義外設(shè),該怎么寫(xiě)hdl文件?——急——謝謝
- 開(kāi)關(guān)電源的設(shè)計(jì)資料
- 電子變壓器在電源技術(shù)中的作用
- 這個(gè)狗咋不聽(tīng)話呢
- 串口的問(wèn)題已經(jīng)搞定 給一些還在困惑中的朋友一點(diǎn)光明
- msp430f5438用I2C讀TMP275沒(méi)有反應(yīng)!
- 一封來(lái)自大四學(xué)生的信,虛心請(qǐng)教
- 【NUCLEO-L452RE測(cè)評(píng)】Low-power run mode 1MHz 25 °C 130uA
- TIVA C Launchpad周計(jì)劃——第二周(收集到的各種資料)