linux内核启动分析(二)
文章目录
- 1. set_task_stack_end_magic
- 2.smp_setup_processor_id
- 3. debug_objects_early_init
- 4. cgroup_init_early
- 4.1 init_cgroup_root
- 4.1.1 init_cgroup_housekeeping
- 4.2 cgroup_init_subsys
- 5. local_irq_disable
- 5.1 raw_irqs_disabled
- 5.2 raw_local_irq_disable
- 5.3 trace_hardirqs_off
- 6. boot_cpu_init
- 7. page_address_init
- 8. early_security_init
我们前面看了start_kernel前的汇编进行的初始化工作,现在开始看start_kernel函数了:
1. set_task_stack_end_magic
void set_task_stack_end_magic(struct task_struct *tsk)
{unsigned long *stackend;stackend = end_of_stack(tsk);//获取内核栈末尾地址*stackend = STACK_END_MAGIC;//设置为魔数,//在每次栈空间被使用时,内核都会检查该位置的值是否保持不变//如果发现变化,就会发出栈溢出的警报。#define task_stack_end_corrupted(task) \(*(end_of_stack(task)) != STACK_END_MAGIC)
}
set_task_stack_end_magic函数就做了一件事,获取内核栈的末尾地址,然后给这个地址赋值STACK_END_MAGIC。然后linux内核是使用task_stack_end_corrupted这个宏判断内核是否栈溢出的。linux内核会在cpu睡眠的时候检查当前栈是否发生溢出。如果开启了schedule debug则会在schedule的时候检查上一个进程的栈是否发生溢出。如果我们真的担心这个问题,最好的做法是在内核的启动参数,一般是grub中添加stacktrace,那么开启内核的函数调用堆栈追踪功能,同事会检查堆栈是否溢出。
2.smp_setup_processor_id
void __init smp_setup_processor_id(void)
{//读取MPIDR_EL1寄存器,并且只保留4个等级表示该core属于哪个cluse等信息u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;set_cpu_logical_map(0, mpidr);//把core信息写入__cpu_logical_map/** clear __my_cpu_offset on boot CPU to avoid hang caused by* using percpu variable early, for example, lockdep will* access percpu variable inside lock_release*///tpidr_el1存放当前cpu的线程IDset_my_cpu_offset(0);//初始化0到对应cpu的tpidr_el1和tpidr_el2寄存器pr_info("Booting Linux on physical CPU 0x%010lx [0x%08x]\n",(unsigned long)mpidr, read_cpuid_id());
}
smp_setup_processor_id函数主要做了两个事情:
- 读取MPIDR_EL1,把该寄存器的4个等级的aff提取出来,写入该cpu号为下标的__cpu_logical_map数组中。这4组aff值在每个core中是唯一的,加起来可以表示core在整个soc的位置。aff0表示一个core中的第几个线程,因为有可能使用超线程技术,一个core中有两个线程;aff1表示core在cluster中的第几个core;aff2表示这是在第几个cluster中。这个值是soc厂商写死的,用于表示core的信息。
- 初始化tpidr_el1和tpidr_el2寄存器为0。
以前我一直以为它表示当前cpu处于0号进程,当时认为这个寄存器是用来存放当前cpu正在处理的进程的进程号的,加速cpu找到current这个task_struct结构体。现在看了代码才知道,这个寄存器是存放每cpu的offs值的,用于快速找到每cpu变量所在的内存。
3. debug_objects_early_init
我们没有开启CONFIG_DEBUG_OBJECTS,是个空函数。
4. cgroup_init_early
int __init cgroup_init_early(void)
{static struct cgroup_fs_context __initdata ctx;struct cgroup_subsys *ss;int i;ctx.root = &cgrp_dfl_root;init_cgroup_root(&ctx);//注册和初始化根控制组cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;//初始化init_task.cgroups,这个RCU指针结构体,看看他的定义就懂了//struct css_set __rcu *cgroups;RCU_INIT_POINTER(init_task.cgroups, &init_css_set);//组装好基本的控制组框架for_each_subsys(ss, i) {//遍历所有启用的cgroup子系统,他们存放在cgroup_subsys数组中WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id,"invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p id:name=%d:%s\n",i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free,ss->id, ss->name);WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN,"cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]);ss->id = i;//设置IDss->name = cgroup_subsys_name[i];//设置cgroup名字if (!ss->legacy_name)ss->legacy_name = cgroup_subsys_name[i];//设置legacy_nameif (ss->early_init)cgroup_init_subsys(ss, true);//cgroup的某一个子系统}return 0;
}
我们cgroup开启了cpuset、cpu、cpuacct、io、memory、devices、perf_event、hugetlb、pids这几个子系统。
cgroup_init_early函数主要做了几件:
- 调用函数init_cgroup_root注册和初始化根控制组;
- 初始化init_task.cgroups,这个RCU类型的指针;
- 遍历所有启用的cgroup子系统,初始化他们,主要是使用cgroup_init_subsys函数初始化子系统。
4.1 init_cgroup_root
void init_cgroup_root(struct cgroup_fs_context *ctx)
{struct cgroup_root *root = ctx->root;struct cgroup *cgrp = &root->cgrp;INIT_LIST_HEAD(&root->root_list);//初始化cgroup_root的链表atomic_set(&root->nr_cgrps, 1);//设置cgroup的数量为1cgrp->root = root;init_cgroup_housekeeping(cgrp);//初始化根cgroup的后勤工作//初始化root的flags、release_agent_path、name、cgrp。root->flags = ctx->flags;if (ctx->release_agent)strscpy(root->release_agent_path, ctx->release_agent, PATH_MAX);if (ctx->name)strscpy(root->name, ctx->name, MAX_CGROUP_ROOT_NAMELEN);if (ctx->cpuset_clone_children)set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
}
init_cgroup_root函数主要做了一下工作:
- 初始化cgroup_root的链表,设置cgroup的数量为1;
- 调用函数init_cgroup_housekeeping初始化根cgroup的后勤工作;
- 初始化root的flags、release_agent_path、name、cgrp。
4.1.1 init_cgroup_housekeeping
static void init_cgroup_housekeeping(struct cgroup *cgrp)
{struct cgroup_subsys *ss;int ssid;INIT_LIST_HEAD(&cgrp->self.sibling);//初始化存放兄弟cgroup的链表INIT_LIST_HEAD(&cgrp->self.children);//初始化存放子cgroup的链表INIT_LIST_HEAD(&cgrp->cset_links);//初始化指向cgrp_cset_links的列表INIT_LIST_HEAD(&cgrp->pidlists);//初始化存放进程pid的链表mutex_init(&cgrp->pidlist_mutex);//初始化保护pidlists的互斥锁//初始化cgrp的其他成员参数cgrp->self.cgroup = cgrp;cgrp->self.flags |= CSS_ONLINE;cgrp->dom_cgrp = cgrp;cgrp->max_descendants = INT_MAX;cgrp->max_depth = INT_MAX;INIT_LIST_HEAD(&cgrp->rstat_css_list);prev_cputime_init(&cgrp->prev_cputime);//空函数for_each_subsys(ss, ssid)INIT_LIST_HEAD(&cgrp->e_csets[ssid]);init_waitqueue_head(&cgrp->offline_waitq);//初始化工作队列,用于release的时候调用cgroup1_release_agent函数启动一个用户态程序INIT_WORK(&cgrp->release_agent_work, cgroup1_release_agent);
}
init_cgroup_housekeeping主要是做了以下工作:
- 初始化存放兄弟cgroup的链表self.sibling
- 初始化存放子cgroup的链表self.children
- 初始化指向cgrp_cset_links的列表cgrp->cset_links
- 初始化存放进程pid的链表cgrp->pidlists
- 初始化保护pidlists的互斥锁cgrp->pidlist_mutex
- 初始化cgrp的其他成员参数
4.2 cgroup_init_subsys
因为启动阶段,ss->early_init为0,cgroup_init_subsys没有执行,先不看。
5. local_irq_disable
#define local_irq_disable() \do { \bool was_disabled = raw_irqs_disabled();\raw_local_irq_disable(); \if (!was_disabled) \trace_hardirqs_off(); \} while (0)
local_irq_disable这个宏主要做了3个工作:
- 调用函数raw_irqs_disabled读取irq的状态;
- 调用函数raw_local_irq_disable关中断;
- 判断步骤一获取到的状态是否关闭,如果还没有关闭,就执行trace_hardirqs_off函数进行trace。
5.1 raw_irqs_disabled
#define raw_irqs_disabled() (arch_irqs_disabled())static inline int arch_irqs_disabled(void)
{return arch_irqs_disabled_flags(arch_local_save_flags());
}static inline unsigned long arch_local_save_flags(void)
{unsigned long flags;asm volatile(ALTERNATIVE("mrs %0, daif",__mrs_s("%0", SYS_ICC_PMR_EL1),ARM64_HAS_IRQ_PRIO_MASKING): "=&r" (flags):: "memory");return flags;
}static inline int arch_irqs_disabled_flags(unsigned long flags)
{int res;asm volatile(ALTERNATIVE("and %w0, %w1, #" __stringify(PSR_I_BIT),"eor %w0, %w1, #" __stringify(GIC_PRIO_IRQON),ARM64_HAS_IRQ_PRIO_MASKING): "=&r" (res): "r" ((int) flags): "memory");return res;
}
raw_irqs_disabled主要做了:
- 调用函数arch_local_save_flags获取daif,其中表示了cpu的irq情况,
- 调用函数arch_irqs_disabled_flags对步骤一的返回值进行处理,只保留irq的bit。
5.2 raw_local_irq_disable
#define raw_local_irq_disable() arch_local_irq_disable()static inline void arch_local_irq_disable(void)
{if (system_has_prio_mask_debugging()) {u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);}asm volatile(ALTERNATIVE("msr daifset, #2 // arch_local_irq_disable",__msr_s(SYS_ICC_PMR_EL1, "%0"),ARM64_HAS_IRQ_PRIO_MASKING):: "r" ((unsigned long) GIC_PRIO_IRQOFF): "memory");
}
raw_local_irq_disable主要是执行了汇编 “msr daifset, #2” ,从而达到关闭irq。
5.3 trace_hardirqs_off
void trace_hardirqs_off(void)
{lockdep_hardirqs_off(CALLER_ADDR0);if (!this_cpu_read(tracing_irq_cpu)) {this_cpu_write(tracing_irq_cpu, 1);tracer_hardirqs_off(CALLER_ADDR0, CALLER_ADDR1);if (!in_nmi())trace_irq_disable_rcuidle(CALLER_ADDR0, CALLER_ADDR1);}
}
这个函数追进去看不太懂,只知道这是ftrace的东西。
6. boot_cpu_init
void __init boot_cpu_init(void)
{int cpu = smp_processor_id();/* Mark the boot cpu "present", "online" etc for SMP and UP case */set_cpu_online(cpu, true);//记录当前cpu为onlineset_cpu_active(cpu, true);//记录当前cpu为activeset_cpu_present(cpu, true);//记录当前cpu为presentset_cpu_possible(cpu, true);//记录当前cpu为possible#ifdef CONFIG_SMP__boot_cpu_id = cpu;//记录引导的cpu ID
#endif
}
boot_cpu_init主要是设置当前cpu的状态,这些状态保存在全局变量__cpu_online_mask、__cpu_active_mask、__cpu_present_mask、__cpu_possible_mask中。
7. page_address_init
void __init page_address_init(void)
{int i;for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {INIT_LIST_HEAD(&page_address_htable[i].lh);spin_lock_init(&page_address_htable[i].lock);}
}static struct page_address_slot {struct list_head lh; /* List of page_address_maps */spinlock_t lock; /* Protect this bucket's list */
} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];struct page_address_map {struct page *page;void *virtual;struct list_head list;
};static struct page_address_map page_address_maps[LAST_PKMAP];
page_address_init函数只有一个作用就是初始化page_address_htable数组,这个数组是一个哈希桶,这个列表存放的是struct page_address_map结构体,看到这个结构体我么就知道这是一个page到虚拟地址的映射关系,也就是说我们经常挺熟的反向映射。
8. early_security_init
不太能看懂,后续补,先学习一下。
相关文章:
linux内核启动分析(二)
文章目录1. set_task_stack_end_magic2.smp_setup_processor_id3. debug_objects_early_init4. cgroup_init_early4.1 init_cgroup_root4.1.1 init_cgroup_housekeeping4.2 cgroup_init_subsys5. local_irq_disable5.1 raw_irqs_disabled5.2 raw_local_irq_disable5.3 trace_ha…...
『EasyNotice』.NET开源消息通知组件——快速实现邮件/钉钉告警通知
📣读完这篇文章里你能收获到 傻瓜式扩展方法直接使用如何通过EasyNotice快速实现钉钉/邮件的通知发送感谢点赞收藏,避免下次找不到~ 文章目录一、EasyNotice1. 功能介绍2. 源码地址二、项目接入1. 发送邮件通知Step 1 : 安装包,通过Nuget安装…...
JVM垃圾回收算法
垃圾标记阶段 对象存活判断:在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为己经死亡的对象,GC才会在执行垃圾回收时,…...
怎么看待ChatGPT封号这件事呢?
最近的ChatGPT大量封号,刷爆了全网,我的两个个人账号被封禁了,不知道大家最近有没有遇到相关的报错信息,要么就是检查你当前的浏览器配置,最后来一个access denied,要么直接就给你来一个当前的国家不支持。…...
八、交换技术原理
(一)交换机 1、交换机介绍 一种用于电(光)信号转发的网络设备,可以为接入交换机的任意两个网络节点提供独享的电信号通路。 工作于第二层的叫交换机,工作于第三层的叫第三层交换机,最常见的是…...
什么是DHCP?DHCP有什么用?(中科三方)
在IP网络中,每一个连接的设备都需要分配一个唯一的IP地址,才能实现和Internet上其他设备的互联。在一些终端规模较大的网络中,需要为每一个主机手工配置IP地址,以避免IP地址的重复,如果主机发生变更,还要去…...
算法设计-二分
一、有序和单调 二分本质上是一种更加智能的搜索状态空间的方式,他需要状态空间的状态呈现一种“有序的一维数组”的形式,然后再进行搜索。所以一开始的排序是无法避免的。 因为二分的写法问题,所以应当怎样排序也是有一定讲究的&…...
隧道技术基础
隧道技术基础基本概念端口转发应用层代理基本概念 攻击者通过边界主机进入内网,往往会利用它当跳板进行横向渗透,但现在的内部网络大多部署了很多安全设备,网络结构错综复杂,对于某些系统的访问会受到各种阻挠,这就需…...
卡尔曼滤波浅析
文章目录前言任务状态预测外部影响因素外部不确定性状态更新利用测量进一步修正状态合并两个高斯分布公式汇总图形化解释总结(readme)references前言 Kalman Filter算法,是一种递推预测滤波算法,算法中涉及到滤波,也涉…...
Eolink Apikit 创建/生成 API 文档
在 API 研发管理产品中,几乎所有的协作工作都是围绕着 API 文档进行的。 我们在接触了大量的客户后发现,采用 文档驱动 的协作模式会比先开发、后维护文档的方式更好,团队协作效率和产品质量都能得到提高。因此我们建议您尝试基于文档来进行工…...
2023年上半年系统分析师备考法则
截止3月30日,上海、北京等地都开始报名,部分省市已经截止报名,大家如果还没报名成功的赶紧报名,千万别错过了,另外就是别忘了缴费,缴费成功才是报名成功。 报名网址:https://bm.ruankao.org.cn…...
【人工智能】—约束传播、弧约束、问题结果与问题分解、局部搜索CSP
【人工智能】—约束传播、弧约束、问题结果与问题分解、局部搜索CSP约束传播弧约束弧相容算法AC-3问题结构化简约束图-树结构CSP问题的局部搜索CSP的迭代算法举例:4-Queens加速:模拟退火法加速:最小最大优化(约束加权法)小结约束传播 前向检…...
Java设计模式面试专题
1.请列举出在 JDK 中几个常用的设计模式? 单例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些类中。工厂模式(Factory pattern)被用于各种不可变的类如 Boolean,像 Boolean.value…...
文件(下)——“C”
各位CSDN的uu们你们好呀,今天,小雅兰的内容是文件的知识点,下面,就让我们进入文件的世界吧 文件的顺序读写 文件的随机读写 fseek ftell rewind 文本文件和二进制文件 文件读取结束的判定 文件缓冲区 在上篇博客中,…...
bugku 渗透靶场3
前言 本题一共八个flag,主要是为了练习内网渗透的思路。 解题思路 首先给了一个站长之家-模拟蜘蛛爬取,这个以前见到过,存在sstf漏洞,直接读取文件。 file:///flag既然是要内网渗透,那肯定要看/etc/hosts。 file:…...
NER 任务以及联合提槽任务
KBERT 论文:《K-BERT: Enabling Language Representation with Knowledge Graph》 论文地址:https://arxiv.org/pdf/1909.07606v1 git地址:https://github.com/autoliuweijie/K-BERT SoftLexicon 出自ACL 2020的Simplify the Usage of Lexic…...
scala函数式编程
目录 不同范式对比: 1.面向对象编程 2.函数式编程 2.1函数基本语法 2.2函数和方法的区别 核心概念: 2.3函数定义 2.4函数参数 2.5 函数至简原则 2.6.高阶函数 三.偏函数 四.柯里化函数 五.递归函数 递归函数注意点: 六.控制抽象 1…...
网吧2023:热闹回来了,电竞战歌起
【潮汐商业评论/原创】 大年初四下午,人民公园附近尚未恢复往日热闹,上海网鱼电竞负责人崔潇瀚驱车前往位于人广世贸商场的网鱼电竞。 与广场上三两路人行色匆匆相比,门店显得忙碌异常,前台的服务叫单声响个不停,员工…...
代码随想录算法训练营第五十九天|503.下一个更大元素II、42. 接雨水
LeetCode 503 下一个更大元素II 题目链接:https://leetcode.cn/problems/next-greater-element-ii/ 思路: 方法一:两个for循环遍历单调栈 第一个for循环确定数组中的某个值在右边有最大的数,第二个for循环是为了可以使数组变成循环数…...
9、简单功能分析
文章目录1、静态资源访问1.1、静态资源目录1.2、如果静态资源与controller资源重名1.3、改变默认的静态资源路径1.4、修改静态资源访问前缀1.5、webjar2、欢迎页支持3、自定义 Favicon4、静态资源配置原理4.1、与Web开发有关的相关自动配置类4.2、WebMvcAutoConfiguration 注解…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
