正点原子linux内核启动流程学习笔记
2021/4/13 7:25:30
本文主要是介绍正点原子linux内核启动流程学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1、Linux 内核入口 stext
在linux内核启动之前要求如下:
①、关闭 MMU。
②、关闭 D-cache。(数据缓存)
③、 I-Cache 无所谓。(指令缓存)
④、 r0=0。
⑤、 r1=machine nr(也就是机器 ID)。
⑥、 r2=atags 或者设备树(dtb)首地址
1.1 为什么需要关闭MMU和D-cache
1.1.1 cache的作用
cache 是高速缓冲存储器
cache是位于主存(即是内存)与CPU内部的寄存器之间的一个存储设施,用来加快cpu与内存之间
数据与指令的传输速率,从而加快处理的速度。
1.1.2 为什么要关闭D-Cache 而I-Cache无所谓
在设备上电之初,内存的初始化速度比cpu初始化慢,在内存没有准备好的情况下,就对内存进行数据读取,那么会造成数据读取异常.
至于为什么I-Cache无所谓.不知道
Volatile:
本质:是告诉编译器不要对我的代码进行优化,作用是让编写者感觉变量的变化情况。因为在优化时,会将常用的代码取出来放到Caches中,它没有从实际的物理地址去取,它直接从CPU的缓存中去取,但常用的代码就是为了检测一些常用变量的变化,如果正在取数据的时候发生跳变,那么就检测不到变量的变化了,所以在这种情况下要用Volatile关键字告诉编译器不要做优化,让cpu每次都从实际的物理地址中去取指令。其实这也是为什么要关闭数据缓存的原因,如果汇编指令读取的时候缓存中的数据,而实际物理地址的数据发生了变化,将导致cpu读取不到真实的最新的值。然而在C语言中是不会关闭Caches的,如果编写者要检测外界物理数据的变化,或变化太快,从Caches中取数据会有误差,就加一个关键字Volatile。
1.2.1 MMU
mmu可以实现虚拟内存和内存保护等功能,完成对内存的操作和管理。
1、没有对MMU进行初始化,且用不到mmu为了避免影响启动时的初始化,先关闭MMU
2 linux内核启动步骤
2.1
- safe_svcmode_maskall 确保CPU处于SVC模式,并且关闭所有中断
- 读取处理器ID
- __lookup_processor_type 检查当前系统是否支持此CPU,支持则获取procinfo信息
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 内核将每种处理器都抽象为一个 proc_info_list 结构体,每种处理器都对应一个
procinfo。
- __vet_atag 验证atags或则设备树(dtb是否有效
- __create_page_tables 创建页表
- __mmap_switched 的地址保存到 r13 寄存器中
- start_kernel
/* * Need to run as early as possible, to initialize the * lockdep hash: */ lockdep_init(); /* lockdep 是死锁检测模块,此函数会初始化 ,两个hash表。此函数要求尽可能的早执行*/ set_task_stack_end_magic(&init_task); /* 设置任务栈结束,用于栈溢出检测*/ smp_setup_processor_id(); /* 跟 SMP 有关(多核处理器),设置处理器 ID */ debug_objects_early_init(); /* 做一些和 debug 有关的初始化 */ /* * Set up the the initial canary ASAP: */ boot_init_stack_canary(); /* 栈溢出检测初始化 */ cgroup_init_early(); /* cgroup 初始化, cgroup 用于控制 Linux 系统资源*/ local_irq_disable(); /* 关闭当前 CPU 中断 */ early_boot_irqs_disabled = true; /* * Interrupts are still disabled. Do necessary setups, then * enable them */ boot_cpu_init(); /* 跟 CPU 有关的初始化 */ page_address_init(); /* 页地址相关的初始化 */ pr_notice("%s", linux_banner); /* 打印 Linux 版本号、编译时间等信息 */ setup_arch(&command_line);/* 架构相关的初始化,此函数会解析传递进来的 * ATAGS 或者设备树(DTB)文件。会根据设备树里面 * 的 model 和 compatible 这两个属性值来查找 * Linux 是否支持这个单板。此函数也会获取设备树 * 中 chosen 节点下的 bootargs 属性值来得到命令 * 行参数,也就是 uboot 中的 bootargs 环境变量的 * 值,获取到的命令行参数会保存到 *command_line 中。 */ mm_init_cpumask(&init_mm); setup_command_line(command_line); setup_nr_cpu_ids(); /* 如果只是 SMP(多核 CPU)的话,此函数用于获取 * CPU 核心数量, CPU 数量保存在变量 * nr_cpu_ids 中。 */ setup_per_cpu_areas(); /* 在 SMP 系统中有用,设置每个 CPU 的 per-cpu 数据 */ smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ build_all_zonelists(NULL, NULL); /* 建立系统内存页区(zone)链表 */ page_alloc_init(); /* 处理用于热插拔 CPU 的页 */ /* 打印命令行信息 */ pr_notice("Kernel command line: %s\n", boot_command_line); parse_early_param(); /* 解析命令行中的 console 参数 */ after_dashes = parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, &unknown_bootoption); if (!IS_ERR_OR_NULL(after_dashes)) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, set_init_arg); jump_label_init(); /* * These use large bootmem allocations and must precede * kmem_cache_init() */ setup_log_buf(0); /* 设置 log 使用的缓冲区*/ pidhash_init(); /* 构建 PID 哈希表, Linux 中每个进程都有一个 ID, * 这个 ID 叫做 PID。通过构建哈希表可以快速搜索进程 * 信息结构体。 */ vfs_caches_init_early();/* 预先初始化 vfs(虚拟文件系统)的目录项和 * 索引节点缓存 */ sort_main_extable(); /* 定义内核异常列表 */ trap_init(); /* 完成对系统保留中断向量的初始化 */ mm_init(); /* 内存管理初始化 */ sched_init(); /* 初始化调度器,主要是初始化一些结构体 */ preempt_disable(); /* 关闭优先级抢占 */ if (WARN(!irqs_disabled(), /* 检查中断是否关闭,如果没有的话就关闭中断 */ "Interrupts were enabled *very* early, fixing it\n")) local_irq_disable(); idr_init_cache(); /* IDR 初始化, IDR 是 Linux 内核的整数管理机 * 制,也就是将一个整数 ID 与一个指针关联起来。 */ rcu_init(); /* 初始化 RCU, RCU 全称为 Read Copy Update(读-拷贝修改) */ /* trace_printk() and trace points may be used after this */ trace_init(); /* 跟踪调试相关初始化 */ context_tracking_init(); radix_tree_init(); /* 基数树相关数据结构初始化 */ /* init some links before init_ISA_irqs() */ early_irq_init(); /* 初始中断相关初始化,主要是注册 irq_desc 结构体变 * 量,因为 Linux 内核使用 irq_desc 来描述一个中断。 */ init_IRQ(); /* 中断初始化 */ tick_init(); /* tick 初始化 */ rcu_init_nohz(); init_timers(); /* 初始化定时器 */ hrtimers_init(); /* 初始化高精度定时器 */ softirq_init(); /* 软中断初始化 */ timekeeping_init(); time_init(); /* 初始化系统时间 */ sched_clock_postinit(); perf_event_init(); profile_init(); call_function_init(); WARN(!irqs_disabled(), "Interrupts were enabled early\n"); early_boot_irqs_disabled = false; local_irq_enable(); /* 使能中断 */ kmem_cache_init_late(); /* slab 初始化, slab 是 Linux 内存分配器 */ /* * 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(); /* 初始化控制台,之前 printk 打印的信息都存放 * 缓冲区中,并没有打印出来。只有调用此函数 * 初始化控制台以后才能在控制台上打印信息。 */ if (panic_later) panic("Too many boot %s vars at `%s'", panic_later, panic_param); lockdep_info(); /* 如果定义了宏 CONFIG_LOCKDEP,那么此函数打印一些信息。 */ /* * 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_objects_mem_init(); kmemleak_init(); /* kmemleak 初始化, kmemleak 用于检查内存泄漏 */ setup_per_cpu_pageset(); numa_policy_init(); if (late_time_init) late_time_init(); sched_clock_init(); calibrate_delay(); /* 测定 BogoMIPS 值,可以通过 BogoMIPS 来判断 CPU 的性能 * BogoMIPS 设置越大,说明 CPU 性能越好。 */ pidmap_init(); /* PID 位图初始化 */ anon_vma_init(); /* 生成 anon_vma slab 缓存 */ 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 thread_info_cache_init(); cred_init(); /* 为对象的每个用于赋予资格(凭证) */ fork_init(); /* 初始化一些结构体以使用 fork 函数 */ proc_caches_init(); /* 给各种资源管理结构分配缓存 */ buffer_init(); /* 初始化缓冲缓存 */ key_init(); /* 初始化密钥 */ security_init(); /* 安全相关初始化 */ dbg_late_init(); vfs_caches_init(totalram_pages); /* 为 VFS 创建缓存 */ signals_init(); /* 初始化信号 */ /* rootfs populating might need page-writeback */ page_writeback_init(); /* 页回写初始化 */ proc_root_init(); /* 注册并挂载 proc 文件系统 */ nsfs_init(); cpuset_init(); /* 初始化 cpuset, cpuset 是将 CPU 和内存资源以逻辑性 * 和层次性集成的一种机制,是 cgroup 使用的子系统之一 */ cgroup_init(); /* 初始化 cgroup */ 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(); } ftrace_init(); /* Do the rest non-__init'ed, we're now alive */ rest_init(); /* rest_init 函数 */
rest_init();
static noinline void __init_refok rest_init(void) { int pid; rcu_scheduler_starting(); /* 启动 RCU 锁调度器 */ 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); /* 创建init 进程 PID =1 */ numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); /* 创建 kthreadd 内核进程,此内核进程的 PID 为 2*/ 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); /* 来进入 idle 进程 PID = 0*/ }
- _enable_mmu 函 数 使 能 MMU
这篇关于正点原子linux内核启动流程学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-18git仓库有更新,jenkins 自动触发拉代码怎么配置的?-icode9专业技术文章分享
- 2024-12-18Jenkins webhook 方式怎么配置指定的分支?-icode9专业技术文章分享
- 2024-12-13Linux C++项目实战入门教程
- 2024-12-13Linux C++编程项目实战入门教程
- 2024-12-11Linux部署Scrapy教程:新手入门指南
- 2024-12-11怎么将在本地创建的 Maven 仓库迁移到 Linux 服务器上?-icode9专业技术文章分享
- 2024-12-10Linux常用命令
- 2024-12-06谁看谁服! Linux 创始人对于进程和线程的理解是…
- 2024-12-04操作系统教程:新手入门及初级技巧详解
- 2024-12-04操作系统入门:新手必学指南