分析linux启动内核源码

news2025/7/30 9:57:48

内核的启动时从main.c这个文件里面的start_kernel函数开始的,这个文件在linux源码里面的init文件夹下面

下面我们来看看这个函数 这个函数很长,可以看个大概过去

asmlinkage __visible void __init start_kernel(void)
{
    char *command_line;
    char *after_dashes;

    set_task_stack_end_magic(&init_task);
    smp_setup_processor_id();
    debug_objects_early_init();

    cgroup_init_early();

    local_irq_disable();
    early_boot_irqs_disabled = true;

    /*
     * Interrupts are still disabled. Do necessary setups, then
     * enable them.
     */
    boot_cpu_init();
    page_address_init();
    pr_notice("%s", linux_banner);
    setup_arch(&command_line);
    /*
     * Set up the the initial canary and entropy after arch
     * and after adding latent and command line entropy.
     */
    add_latent_entropy();
    add_device_randomness(command_line, strlen(command_line));
    boot_init_stack_canary();
    mm_init_cpumask(&init_mm);
    setup_command_line(command_line);
    setup_nr_cpu_ids();
    setup_per_cpu_areas();
    smp_prepare_boot_cpu();    /* arch-specific boot-cpu hooks */
    boot_cpu_hotplug_init();

    build_all_zonelists(NULL);
    page_alloc_init();

    pr_notice("Kernel command line: %s\n", boot_command_line);
    parse_early_param();
    after_dashes = parse_args("Booting kernel",
                  static_command_line, __start___param,
                  __stop___param - __start___param,
                  -1, -1, NULL, &unknown_bootoption);
    if (!IS_ERR_OR_NULL(after_dashes))
        parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
               NULL, set_init_arg);

    jump_label_init();

    /*
     * These use large bootmem allocations and must precede
     * kmem_cache_init()
     */
    setup_log_buf(0);
    vfs_caches_init_early();
    sort_main_extable();
    trap_init();
    mm_init();

    ftrace_init();

    /* trace_printk can be enabled here */
    early_trace_init();

    /*
     * 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();
    radix_tree_init();

    /*
     * Set up housekeeping before setting up workqueues to allow the unbound
     * workqueue to take non-housekeeping into account.
     */
    housekeeping_init();

    /*
     * Allow workqueue creation and work item queueing/cancelling
     * early.  Work item execution depends on kthreads and starts after
     * workqueue_init().
     */
    workqueue_init_early();

    rcu_init();

    /* Trace events are available after this */
    trace_init();

    if (initcall_debug)
        initcall_debug_enable();

    context_tracking_init();
    /* init some links before init_ISA_irqs() */
    early_irq_init();
    init_IRQ();
    tick_init();
    rcu_init_nohz();
    init_timers();
    hrtimers_init();
    softirq_init();
    timekeeping_init();
    time_init();
    printk_safe_init();
    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();

    /*
     * 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();
    if (panic_later)
        panic("Too many boot %s vars at `%s'", panic_later,
              panic_param);

    lockdep_init();

    /*
     * 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();

    /*
     * This needs to be called before any devices perform DMA
     * operations that might use the SWIOTLB bounce buffers. It will
     * mark the bounce buffers as decrypted so that their usage will
     * not cause "plain-text" data to be decrypted when accessed.
     */
    mem_encrypt_init();

#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
    kmemleak_init();
    setup_per_cpu_pageset();
    numa_policy_init();
    acpi_early_init();
    if (late_time_init)
        late_time_init();
    sched_clock_init();
    calibrate_delay();
    pid_idr_init();
    anon_vma_init();
#ifdef CONFIG_X86
    if (efi_enabled(EFI_RUNTIME_SERVICES))
        efi_enter_virtual_mode();
#endif
    thread_stack_cache_init();
    cred_init();
    fork_init();
    proc_caches_init();
    uts_ns_init();
    buffer_init();
    key_init();
    security_init();
    dbg_late_init();
    vfs_caches_init();
    pagecache_init();
    signals_init();
    seq_file_init();
    proc_root_init();
    nsfs_init();
    cpuset_init();
    cgroup_init();
    taskstats_init_early();
    delayacct_init();

    check_bugs();

    acpi_subsystem_init();
    arch_post_acpi_subsys_init();
    sfi_init_late();

    /* Do the rest non-__init'ed, we're now alive */
    arch_call_rest_init();
}
【文章福利】小编推荐自己的Linux内核技术交流群: 【977878001】整理一些个人觉得比较好得学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100进群领取,额外赠送一份 价值699的内核资料包(含视频教程、电子书、实战项目及代码)

内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

这个函数里面我们会看到有很多的各种init,也就是初始化,我们只说几个重点操作

首先来看下这个函数set_task_stack_end_magic(&init_task);

在linux里面所有的进程都是由父进程创建而来,所以说在启动内核的时候需要有个祖先进程,这个进程是系统创建的

第一个进程,我们称为0号进程,它是唯一一个没有通过fork或者kernel_thread的进程

然后就是初始化系统调用,对应的函数就是trap_init();这里面设置了很多中断门,用于处理各种中断

系统调用也是通过发送中断的方式进行的。

接下来就是内存管理模块的初始化,对应的函数是mm_init();

然后就是初始化任务调度,对应的函数就是sched_init();

这个任务调度是干嘛用的呢?就是操作系统协调进程和cpu,比如说分配哪个进程在cpu上运行呀,

在比如说你这个进程在cpu上运行时间过长了,然后操作系统就会把你踢下去,换另一个进程在cpu上运行。

到了这个preempt_disable();函数,这个函数的意思就是在这个函数运行以后就禁止被中断

也就是说在这个函数运行后面,如果没有主动让出cpu,那么其他进程是无法抢占他的。

然后看下这个tick_init();这个函数是时钟初始化,这个时钟的概念是什么意思呢?

计算机会每隔一段时间周期通知操作系统,就像时钟一样,滴答滴答,每滴答一下就是一个时间周期过去了,

通知操作系统后,操作系统会看下当前在cpu上运行的进程运行时间是否过长,如果过长就标识该进程为可抢占

然后在某些时机下会切掉该进程,换下一个进程。

最后start_kernel()调用的是rest_init()用来初始化其他方面,这里面做了好多事情

noinline void __ref rest_init(void)
{
    struct task_struct *tsk;
    int pid;

    rcu_scheduler_starting();
    /*
     * 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.
     */
    pid = kernel_thread(kernel_init, NULL, CLONE_FS);
    /*
     * Pin init on the boot CPU. Task migration is not properly working
     * until sched_init_smp() has been run. It will set the allowed
     * CPUs for init to the non isolated CPUs.
     */
    rcu_read_lock();
    tsk = find_task_by_pid_ns(pid, &init_pid_ns);
    set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id()));
    rcu_read_unlock();

    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();

    /*
     * Enable might_sleep() and smp_processor_id() checks.
     * They cannot be enabled earlier because with CONFIG_PREEMPT=y
     * kernel_thread() would trigger might_sleep() splats. With
     * CONFIG_PREEMPT_VOLUNTARY=y the init task might have scheduled
     * already, but it's stuck on the kthreadd_done completion.
     */
    system_state = SYSTEM_SCHEDULING;

    complete(&kthreadd_done);

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

首先调用kernel_thread()函数,用来创建用户态的第一个进程,这个进程是所有用户态进程的祖先进程,我们称为1号进程

这个一号进程进入用户态以后,开枝散叶,创建了很多子进程,子进程又创建子进程,就形成了一颗进程树。

一旦有了用户进程,就需要划分资源了,比如说用户态的进程要想使用网卡发送数据,这个时候不能直接让用户态进程调用网卡

而是通过操作系统提供的系统调用函数,给进程发送数据,发送成功以后在返回到用户态进程,通知进程处理结果,也就是封装了

底层实现,用户态进程想要实现什么功能,直接调用系统调用就可以了,在用户态进程进行系统调用时,操作系统会把当前该进程的

参数都保存到寄存器里面,如果有对寄存器不懂的,就把寄存器想象成变量,变量是编程语言存放数据的,那么寄存器就是cpu用来存放数据的东西,

等到系统调用从内核态返回到用户态的时候,会恢复当时保存的寄存器里面的数据,继续运行。

这个过程就是这样的,用户态-》系统调用-》保存寄存器-》内核态执行系统调用-》恢复寄存器-》返回用户态 接着运行

然后接着说这个一号进程启动过程,现在这个进程还是在内核态的,那么要怎么把它搞到用户态里面的,

一般都是从用户态到内核态在返回到用户态,很少见过直接从内核态开始然后到用户态的

看下下面这个代码

void
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
{
set_user_gs(regs, 0);
regs->fs    = 0;
regs->ds    = __USER_DS;
regs->es    = __USER_DS;
regs->ss    = __USER_DS;
regs->cs    = __USER_CS;
regs->ip    = new_ip;
regs->sp    = new_sp;
regs->flags    = X86_EFLAGS_IF;
force_iret();
}
EXPORT_SYMBOL_GPL(start_thread);

创建进程的这函数最后会有这么一个函数也就是start_thread(),这里面把各个寄存器都设置为了_USER,啥意思呢,里面将用户态的代码段CS设置为_USER_CS,将用户态的数据段DS设置为_USER_DS,

以及指令指针寄存器IP,栈顶指针SP,最后的force_iret();是用来恢复寄存器的,按理来说应该恢复在系统调用的时候保存的寄存器,这里面恢复的其实就是上面设置的寄存器。CS和指令指针寄存器IP恢复了,

指向用户态下一个要执行的语句,DS和函数栈指针SP也被恢复了,指向用户态函数栈的栈顶,所以,下一条指令就从用户态开始了。

用户态的祖先进程创建完了,那么内核态有没有一个祖先进程呢?

有的,rest_init第二大事情就是第三个进程,也就是2号进程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/35178.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MCE | 靶向相分离 小分子药物研发

细胞内的各种组分如何在正确的时间、地点上聚集并执行其相应的功能&#xff0c;是生命科学领域内的一大问题。近些年来&#xff0c;细胞内一些没有细胞膜结构包被的“细胞器” (Membrane-less organelles/condensates)——又称生物分子凝聚体 (Biomolecular condensates) 逐渐引…

Analyzing User-Level Privacy Attack Against Federated Learning

Analyzing User-Level Privacy Attack Against Federated Learning IEEE JSAC CCF-A期刊 宋梦凯&#xff08;武汉大学网络安全实验室&#xff09; Summary 提出了针对FL用户级隐私的基于GAN的攻击&#xff08;mGAN-AI&#xff09;&#xff0c;主要是从每个client的更新中计算…

UniPro助力半导体企业之低代码平台篇:高效协同快速响应

在《UniPro助力半导体企业之特色篇&#xff1a;缺陷管理覆盖全流程》中&#xff0c;我们介绍了UniPro如何帮助半导体企业完成在研发过程中的Bug管理&#xff0c;然而缺陷管理也并非UniPro的全部&#xff0c;除此之外&#xff0c;UniPro有着完整的项目管理体系&#xff0c;涵盖了…

相控阵天线(二):非规则直线阵列天线(稀布阵列、稀疏阵列、平方率分布阵列)

目录非规则线阵概述不均匀递变间距阵列稀布阵列稀疏阵列不均匀相位递变阵列不均匀幅度激励阵列代码示例非规则线阵概述 非规则线阵主要包括以下情况&#xff1a; 1. 不均匀间距阵列&#xff1a; a&#xff09;不均匀间距递变阵列&#xff1a;单元间距按照一定的系数递增&#…

傻白入门芯片设计,IP, MCM, SiP, SoC 和 Chiplet的区别(二)

一、IP&#xff1a; 早期的复制电路都是全定制&#xff0c;比如Intel的4004cpu&#xff0c;这种设计非常耗时。考虑到cpu的很多模块有相似的地方&#xff0c;能不能把这些东西模块化&#xff1f;于是就有了IP核的概念&#xff0c;Intelligent Property&#xff0c;即知识产权核…

智慧运维解决方案-最新全套文件

智慧运维解决方案-最新全套文件一、智能运维的必然性二、建设思路三、建设方案1、IT资产和配置管理2、自动化运维管理3、一体化运维平台四、获取 - 智慧运维全套最新解决方案合集一、智能运维的必然性 运维场景多样化。随着IT业务持续增长&#xff0c;为保证业务连续性&#xf…

相控阵天线(八):圆环阵列天线和球面阵列天线

目录圆环阵圆环阵方向图函数均匀圆环阵示例圆环阵层间距的影响非均匀圆环阵示例球面阵列球面阵方向图函数球面阵示例圆环阵 多个单元分布在一个圆环上的阵列称为圆环阵列。这是一种有实际意义的阵列结构&#xff0c;可应用于无线电测向、导航、地下探测等系统中。 圆环阵方向…

微服务介绍

目录一、系统架构演变单体应用架构垂直应用架构分布式架构SOA架构微服务架构二、微服务架构介绍微服务架构常见问题微服务架构常见概念服务治理服务调用服务网关服务容错链路追踪微服务架构常见问题解决方案ServiceComdServiceCloudServiceCloud AlibabaSpringCloud Alibaba介绍…

[论文评析-CV]MediaPipe: A Framework for Building Perception Pipelines, ArXiv,2019

MediaPipe: A Framework for Building Perception Pipelines文章信息前言框架介绍MediaPipe用于目标检测(1)Detection branch:(2)Tracking branch:MediaPipe框架重要的概念调度其他References文章信息 论文题目&#xff1a;MediaPipe: A Framework for Building Perception Pi…

mysql误删数据后 快速恢复的办法

手抖不小心把表里的数据删除或修改错误怎么办&#xff1f;该如何快速恢复呢&#xff1f;遇到这样的问题怎么办&#xff1f;希望下面这篇文章能够帮助到你&#xff01; 第一步&#xff1a;保证mysql已经开启binlog&#xff0c;查看命令&#xff1a; 查看binklog是否开启 show…

运动耳机品牌排行榜前十名有哪些,2022年六款运动耳机值得入手

近几年来&#xff0c;运动健身潮流一直都非常火热&#xff0c;但一个人运动难免会感到枯燥&#xff0c;这个时候最需要的就是音乐的陪伴了&#xff0c;佩戴着运动耳机听音乐&#xff0c;运动的时间也会过得越来越快&#xff0c;不过在选购运动耳机的过程会比挑选普通蓝牙耳机还…

pycharm社区版不能使用conda

修改成cmd 本质是conda init 问题 专业版的直接在终端改成cmd就行了

(DS90UB3702TRURRQ1) LT8640SHV-2低噪声降压稳压器QFN

LT8640/LT8640-1降压稳压器采用Silent Switcher架构&#xff0c;设计用于最大限度地降低EMI/EMC辐射并在高达3MHz的频率下提供高效率。由于具有2.5μA的超低静态电流&#xff08;当输出处于全面调节状态时&#xff09;&#xff0c;因此适用于要求在非常小负载电流条件下获得极高…

ICP算法加速优化--多线程和GPU

LZ之前的文章ICP算法实现&#xff08;C&#xff09; 用C实现了基础的ICP算法&#xff0c;由于该算法是一种迭代的优化算法&#xff0c;里面含有大量循环操作以及矩阵运算&#xff0c;可以通过使用多线程或者GPU硬件来进行加速&#xff0c;具体分别可以通过OpenMP和CUDA编程实现…

六、【React基础】组件实例三大核心属性之三 refs + 事件处理

文章目录1、字符串形式的ref&#xff08;过时/不推荐&#xff09;2、回调形式的ref&#xff08;推荐&#xff01;&#xff01;&#xff01;&#xff09;● 回调ref中回调次数的问题3、createRef创建ref容器&#xff08;最新最推荐&#xff09;4、事件处理理解&#xff1a;组件内…

Web3D应用开发在线IDE【中文版】

nunuStudio 是一个Web 3D应用程序的集成开发环境&#xff0c;它提供用于在 3D 世界中创建和编辑对象的工具&#xff0c;支持JavaScript和Python对3D场景进行二次开发。nunuStudio中文版 由 BimAnt 提供。 如果你曾经使用过其他类似的框架&#xff08;unity、playcanvas、godot …

Spring Boot 3.0 正式发布了!一个超重要的版本!!

首发于 JavaGuide (「Java学习面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试&#xff0c;首选 JavaGuide&#xff01;) 紧跟着 Spring Framework 6.0 的正式发布&#xff0c;就在昨天&#xff0c;Spring Boot 3.0 也正式发布了&#xff01; 这是一…

供应多臂PEG衍生物4-Arm PEG-Azide,4-Arm PEG-N3,四臂-聚乙二醇-叠氮

1、名称 英文&#xff1a;4-Arm PEG-Azide&#xff0c;4-Arm PEG-N3 中文&#xff1a;四臂-聚乙二醇-叠氮 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Azide PEG Multi-arm PEGs 4、分子量&#xff1a;可定制&#xff0c;四臂-PEG 2000-叠氮、4-Arm PEG-N3 20000、…

深入分析序列化和反序列化原理,终于知道serialVersionUID到底有什么用了

一个问题引发的思考 下面是一个简单的socket通信demo。 通信数据类&#xff1a; package com.zwx.serialize.demo; public class SocketUser { public SocketUser(String id, String name) {this.id id;this.name name; }private String id; private String name;public St…

ADAU1860调试心得(2)硬件和软件的详细说明

硬件 一台64位的PC&#xff0c;一块ADAU1860EVB开发板&#xff0c;一个LARK-1860专用仿真器&#xff0c;音频线若干&#xff0c;mini USB线一根&#xff0c;一个到三个输入音源&#xff0c;可以是PC或者手机或者其他模拟音频输入设备&#xff0c; 一个输出音源设备&#xff0c;…