阅读分析Linux0.11 /boot/head.s
目录
- 初始化IDT、IDTR和GDT、GDTR
- 检查协处理器并设置CR0寄存器
- 初始化页表和CR3寄存器,开启分页
初始化IDT、IDTR和GDT、GDTR
startup_32:movl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gslss _stack_start,%espcall setup_idtcall setup_gdtmovl $0x10,%eax # reload all the segment registersmov %ax,%ds # after changing gdt. CS was alreadymov %ax,%es # reloaded in 'setup_gdt'mov %ax,%fsmov %ax,%gslss _stack_start,%espxorl %eax,%eax
1: incl %eax # check that A20 really IS enabledmovl %eax,0x000000 # loop forever if it isn'tcmpl %eax,0x100000je 1b
个人阅读上面代码的知识点:
- lss _stack_start,%esp 首先lss是远指针加载指令,不了解的查deepseek就懂了。“_stack_start”这个标号找了很久都找不到在哪儿定义的,搜索整个源码都没有。最后还是求助deepseek终于搞懂了(deepseek真自学神器)。要把“_stack_start”中下划线去掉,直接搜索“stack_start”,才知道这个标号是定义在kernel/sched.c中的结构体变量名。之所以要加下划线,是因为早期的编译器编译时会在C中变量名前面加,现在不加了 。由于对kernel代码不懂,所以没过多研究,知道是在初始化栈就行了。
call setup_idt 调用子程序初始化IDT表,通过指令lidt将IDT表的长度和地址加载到IDTR寄存器。还处在内核初始化阶段,所以只是简单的将IDT表中的描述符初始化为同一个,都指向ignore_int这个中断处理程序。ignore_int子程序就是本文件中,功能就是打印一段字符。- lidt指令加载的IDT表的地址是线性地址,初始化阶段,此时还没开启分页模式,线性地址等于物理地址。开启分页后,IDTR中的线性地址要经过MMU查页表转化成IDT表在内存中的物理地址。
- 需要对IDT表描述符了解,IDT表描述符总共8字节,小端法存入内存,先存低4字节,再高4字节,先低2字节,再高2字节。之所以提小端法是因为我一开始没注意,把描述符的选择子判断错了。
call setup_gdt 调用子程序初始化GDT表,通过指令lgdt将GDT表的长度和地址加载到GDTR寄存器。GDT表自己构造。- lgdt指令加载的GDT表的地址是线性地址,只不过初始化阶段,此时还没开启分页模式,线性地址等于物理地址。开启分页后,GDTR中的线性地址要经过MMU查页表转化成GDT表在内存中的物理地址。
- 构造GDT表时,GDT表中的描述符大小为8字节,第一个描述符为全0,后面描述符的基地址base=0X00000000,limit=最大值(0X000FFF),把内存管理就设置成了平坦模式, 现代操作系统支持这种模式,这样一个进程的
代码段、数据段、栈段就共享同一个线性地址空间了,方便虚拟内存管理。
- 保护模式下,指令执行过程中,查询IDT表、GDT表的工作是有硬件来实现的,不是软件模拟的,所以对IDT、GDT中描述符的结构也是硬件规定的。我们在构造这两个表时要按照硬件的规定来。
- movl %eax,0x000000;cmpl %eax,0x100000 这段是负责检查A20地址线是否打开,如果没打开那么0X100000地址就等于0X000000地址,cmpl比较的结果就是相等,那就卡在这个执行死循环。
- je 1b 这个条件跳转指令开始看了我也很蒙,还是靠deepseek,这是以前的写法“b”表示满足条件跳转到后面的标号,“f”表示满足条件跳转到前面的标号。b和f指示跳转的方向的。现在好像不用这种用法了。
检查协处理器并设置CR0寄存器
movl %cr0,%eax # check math chipandl $0x80000011,%eax # Save PG,PE,ET
/* "orl $0x10020,%eax" here for 486 might be good */orl $2,%eax # set MPmovl %eax,%cr0call check_x87jmp after_page_tables/** We depend on ET to be correct. This checks for 287/387.*/
check_x87:fninitfstsw %axcmpb $0,%alje 1f /* no coprocessor: have to set bits */movl %cr0,%eaxxorl $6,%eax /* reset MP, set EM */movl %eax,%cr0ret
个人阅读上面代码的知识点:
- 了解CR0寄存器,CR0寄存器中,PE位开启保护模式、PG位开启分页机制、WP位写保护位。还有其它涉及协处理器的位,我一知半解就不写了。上面这段代码主要就是检查协处理器,然后进行设置。 指令call check_x87就是调用子程序检查是否存在287/387协处理器。
- fninit fstsw这两个是协处理器指令,自己查deepseek能看懂,不赘述了。check_x87这个子程序功能就是检查协处理器的。
初始化页表和CR3寄存器,开启分页
after_page_tables:pushl $0 # These are the parameters to main :-)pushl $0pushl $0pushl $L6 # return address for main, if it decides to.pushl $mainjmp setup_paging
L6:jmp L6 # main should never return here, but# just in case, we know what happens.
-----------------------------------------------------------------------------------------
setup_paging:movl $1024*5,%ecx /* 5 pages - pg_dir+4 page tables */xorl %eax,%eaxxorl %edi,%edi /* pg_dir is at 0x000 */cld;rep;stoslmovl $pg0+7,pg_dir /* set present bit/user r/w */movl $pg1+7,pg_dir+4 /* --------- " " --------- */movl $pg2+7,pg_dir+8 /* --------- " " --------- */movl $pg3+7,pg_dir+12 /* --------- " " --------- */movl $pg3+4092,%edimovl $0xfff007,%eax /* 16Mb - 4096 + 7 (r/w user,p) */std
1: stosl /* fill pages backwards - more efficient :-) */subl $0x1000,%eaxjge 1bxorl %eax,%eax /* pg_dir is at 0x0000 */movl %eax,%cr3 /* cr3 - page directory start */movl %cr0,%eaxorl $0x80000000,%eaxmovl %eax,%cr0 /* set paging (PG) bit */ret /* this also flushes prefetch-queue */
个人阅读上面代码的知识点:
- jmp setup_paging 跳转到初始化页表的子程序。该指令前面的push指令是在压栈,pushl $main 目的是从setup_paging子程序返回时,ret指令能返回到main程序。 我还没看过main.c的代码,不赘述了。功能就是结束head.s的初始化任务。开启main.c
- 强化我自己的一个知识点,call和jmp都是跳转指令,区别就是call跳转前要将返回地址压栈,jmp无需压栈直接跳转无法返回。开始我还疑问为什么不用call指令,然后返回到main.c,仔细一想call指令是将下一条指令的地址压栈,只能返回到本源文件call的下一条指令。而我们需要的是结束head.s,返回到另一个源文件main.c。所以通过pushl $main压入返回地址,不用call调用,而用jmp直接跳转到setup_paging初始化页表子程序。
setup_paging子程序初始化页表、CR3寄存器,置CR0寄存器的PG为1开启分页。了解页表和页表项,即使有些汇编指令不熟,查一下就是看懂这段了。- stosl指令,我忽略了它执行时会自动增加EDI。stos、movs、cmps、scas都是字符串操作指令,可以顺便都了解一下。伟大的deepseek很好用的。
- CR3寄存器是用来存放顶级页表的物理地址的。MMU将线性地址转换位物理地址时需要通过CR3寄存器找到页表进行映射。CR3寄存器的改变,会引起TLB表的改变。CR3改变代表页表的切换,TLB相当于部分页表项的缓存,自然也要切换。
.org 0x1000
pg0:.org 0x2000
pg1:.org 0x3000
pg2:.org 0x4000
pg3:.org 0x5000
- 刚看见这段代码时,感觉整齐有规律,就是不知道作用是什么。即使知道了.org这个伪指令的功能,也不知道写这段代码的意义。直到最后看到初始化页表的子程序setup_paging。补充点因为这段代码学到的零碎知识点。
- (1).org(Origin)是汇编语言中的一条伪指令(Directive),用于指定程序或数据在内存中的起始地址。 在需要直接控制内存布局的场景(如裸机开发、嵌入式开发)中,.org 是一个简单有效的工具,但在高层编程中通常由链接器管理地址分配。
- (2) 这段代码其实就是在划分页表的页。它们之间的地址差是0X1000(4KB),从0地址开始到0X5000共20KB,分成5个页,每页4KB,第一个页是页目录表,其余4个页都是普通页表。setup_paging子程序的开始将5个页20KB用0填充,接着构造4个普通物理页的页表项填充到0地址的页目录表,最后构造一个普通物理页的页表项填充4个普通物理页。
- (3)你还可以观察到初始化IDT、IDTR和GDT、GDTR的代码,还有检查协处理器并设置CR0寄存器的代码都写在这段代码之前,因为那些初始化、检查的代码执行完就没用了,它们所在的内存空间可以被页表覆盖; 结束head.s跳转到main.c的代码、初始化页表的子程序setup_paging、IDT表GDT表的位置,包括中断处理程序ignore_int的代码位置都在“.org 0X5000”之后,就是防止误操作,初始化页表时覆盖比较重要的代码、表空间。
- (4) 内存中IDT表、GDT表,各有1个,所有应用程序和操作系统共用;页表有多个,每个应程序都有自己的页表,任务切换时,通过修改CR3寄存器切换页表,同时TLB表也要刷新。
相关文章:
阅读分析Linux0.11 /boot/head.s
目录 初始化IDT、IDTR和GDT、GDTR检查协处理器并设置CR0寄存器初始化页表和CR3寄存器,开启分页 初始化IDT、IDTR和GDT、GDTR startup_32:movl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gslss _stack_start,%espcall setup_idtcall setup_gdtmovl $0x1…...
android11 DevicePolicyManager浅析
目录 📘 简单定义 📘应用启用设备管理者 📂 文件位置 🧠 DevicePolicyManager 功能分类举例 🛡️ 1. 安全策略控制 📷 2. 控制硬件功能 🧰 3. 应用管理 🔒 4. 用户管理 &am…...
《前端性能优化秘籍:打造极致用户体验》
在当下,网站和应用的性能表现直接关乎用户去留。快速加载、流畅交互的页面能让用户沉浸其中,反之,缓慢的响应速度则会让他们毫不犹豫地离开。对于前端开发者而言,性能优化不仅是技术追求,更是提升用户体验、增强产品竞…...
微信小程序实现table样式,自带合并行合并列
微信小程序在代码编写过程好像不支持原生table的使用,在开发过程中偶尔又得需要拿table来展示。 1.table效果展示 1.wxml <view class"table-container"><view class"table"><view class"table-row"><view cla…...
学习笔记十二——Rust 高阶函数彻底入门(超详细过程解析 + 每步数值追踪)
💡 彻底搞懂 Rust 高阶函数!新手最容易卡住的语法 调用流程全讲透(含逐步拆解) Rust 函数式编程中有一个常见却经常让人懵的概念:高阶函数(Higher-Order Function) 一看到 fn(i32) -> i32、…...
电脑的品牌和配置
我的笔记本是2020年买的,之前的订单找不到了,就知道是联想,不清楚具体的配置。 本文来源:腾讯元宝 检查系统信息(Windows) 这通常是 联想(Lenovo) 的型号代码。 81XV 是联想…...
Redis面试——常用命令
一、String (1)设置值相关命令 1.1.1 SET 功能:设置一个键值对,如果键已存在则覆盖旧值语法: SET key value [EX seconds] [PX milliseconds] [NX|XX]EX seconds:设置键的过期时间为 seconds 秒 PX milli…...
Swin-Transformer-UNet改进:融合Global-Local Spatial Attention (GLSA) 模块详解
目录 1.模块概述 2.swinUNet网络 3. 完整代码 1.模块概述 Global-Local Spatial Attention (GLSA) 是一种先进的注意力机制模块,专为计算机视觉任务设计,能够同时捕捉全局上下文信息和局部细节特征。 该模块通过创新的双分支结构和自适应融合机制,显著提升了特征表示能…...
ubuntu 向右拖动窗口后消失了、找不到了
这是目前单显示器的设置,因为实际只有1个显示器,之前的设置如下图所示,有2个显示器,一个主显示器,一个23寸的显示器 ubuntu 22.04 系统 今天在操作窗口时,向右一滑,发现这个窗口再也不显示了、找…...
大语言模型(LLMs)中的强化学习(Reinforcement Learning, RL)
第一部分:强化学习基础回顾 在深入探讨LLMs中的强化学习之前,我们先快速回顾一下强化学习的核心概念,确保基础扎实。 1. 强化学习是什么? 强化学习是一种机器学习范式,目标是让智能体(Agent)…...
2025最新版微软GraphRAG 2.0.0本地部署教程:基于Ollama快速构建知识图谱
一、前言 微软近期发布了知识图谱工具 GraphRAG 2.0.0,支持基于本地大模型(Ollama)快速构建知识图谱,显著提升了RAG(检索增强生成)的效果。本文手把手教你如何从零部署,并附踩坑记录和性能实测…...
泛型算法——只读算法(一)
在 C 标准库中,泛型算法的“只读算法”指那些 不会改变它们所操作的容器中的元素,仅用于访问或获取信息的算法,例如查找、计数、遍历等操作。 accumulate std::accumulate()是 C 标准库**numeric**头文件中提供的算法,用于对序列…...
Redis的常见数据类型
Redis 提供了多种数据类型,以满足不同的应用场景。以下是 Redis 的主要数据类型及其应用场景: 字符串(String): 描述:最基本的数据类型,存储单个键值对,值可以是字符串、整数或浮点数…...
Mybatis中dao(mapper)层几种传参方式
一、SQL语句中接收参数的方式有两种: 1、 #{}预编译 (可防止sql注入) 2、${}非预编译(直接拼接sql,不能防止sql注入) #{}和${}的区别是什么? #{} 占位符,相当于?,sql预编译&…...
网络安全知识点2
1.虚拟专用网VPN:VPN用户在此虚拟网络中传输私网流量,在不改变网络现状的情况下实现安全,可靠的连接 2.VPN技术的基本原理是利用隧道技术,对传输报文进行封装,利用VPN骨干网建立专用数据传输通道,实现报文…...
libevent服务器附带qt界面开发(附带源码)
本章是入门章节,讲解如何实现一个附带界面的服务器,后续会完善与优化 使用qt编译libevent源码演示视频qt的一些知识 1.主要功能有登录界面 2.基于libevent实现的服务器的业务功能 使用qt编译libevent 下载这个,其他版本也可以 主要是github上…...
智能体数据分析
数据概览: 展示智能体的累计对话次数、累计对话用户数、对话满意度、累计曝光次数。数据分析: 统计对话分析、流量分析、用户分析、行为分析数据指标,帮助开发者完成精准的全面分析。 ps:数据T1更新,当日12点更新前一天…...
[特殊字符] UnionFS(联合文件系统)原理解析:容器背后的存储技术
🔍 UnionFS(联合文件系统)原理解析:容器背后的存储技术 💡 什么是 UnionFS? UnionFS(联合文件系统) 是一种可以将多个不同来源的文件系统“合并”在一起的技术。它的核心思想是&am…...
STM32(M4)入门: 概述、keil5安装与模板建立(价值 3w + 的嵌入式开发指南)
前言:本教程内容源自信盈达教培资料,价值3w,使用的是信盈达的405开发版,涵盖面很广,流程清晰,学完保证能从新手入门到小高手,软件方面可以无基础学习,硬件学习支持两种模式ÿ…...
采用若依vue 快速开发系统功能模块
文章目录 运行若依项目 科室管理科室查询-后端代码实现科室查询-前端代码实现科室名称状态搜索科室删除-后端代码实现科室删除-前端代码实现科室新增-后端代码实现科室新增-前端代码实现科室修改-后端代码实现前端代码实现角色权限实现 运行若依项目 运行redis 创建数据库 修改…...
HTML:表格数据展示区
<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>人员信息表</title><link rel"styl…...
WIN11运行游戏时出现“ms-gamingoverlay”弹框的问题
针对WIN11运行游戏时出现“ms-gamingoverlay”弹框的问题,以下是经过验证的多种解决方法,结合不同场景需求提供对应方案: 一、关闭系统内置的游戏录制功能 禁用Xbox Game Bar及游戏录制 • 进入系统设置(WinI)→ 左侧选…...
Oracle测试题目及笔记(单选)
所有题目来自于互联网搜索 当 Oracle 服务器启动时,下列哪种文件不是必须的(D)。 A.数据文件 B.控制文件 C.日志文件 D.归档日志文件 数据文件、日志文件-在数据库的打开阶段使用 控制文件-在数…...
Jmeter创建使用变量——能够递增递减的计数器
Jmeter创建使用变量——能够递增递减的计数器 如下图所示,创建一个 取值需限定为0 2 4这三个值内的变量。 Increment:每次迭代后 递增的值,给计数器增加的值 Maximum value:计数器的最大值,如果超过最大值࿰…...
【LeetCode基础算法】滑动窗口与双指针
定长滑动窗口 总结:入-更新-出。 入:下标为 i 的元素进入窗口,更新相关统计量。如果 i<k−1 则重复第一步。 更新:更新答案。一般是更新最大值/最小值。 出:下标为 i−k1 的元素离开窗口,更新相关统计量…...
数据结构之BFS广度优先算法(腐烂的苹果)
队列这个数据结构在很多场景下都有使用,比如在实现二叉树的层序遍历,floodfill问题(等等未完成)中,都需要借助队列的先进先出特性,下面给出这几个问题的解法 经典的二叉树的层序遍历 算法图示,以下图所示的二叉树为例…...
道可云人工智能每日资讯|首届世界人工智能电影节在法国尼斯举行
道可云元宇宙每日简报(2025年4月15日)讯,今日元宇宙新鲜事有: 杭州《西湖区打造元宇宙产业高地的扶持意见》发布 杭州西湖区人民政府印发《西湖区打造元宇宙产业高地的扶持意见》。该意见已于4月4日正式施行,有效期至…...
火车头采集动态加载Ajax数据(无分页瀑布流网站)
为了先填充好数据在上线,在本地搭建了一个网站,并用火车头采集数据填充到里面。 开始很上手,因为找的网站的分类中是有分页的。很快捷的找到页面标识。 但是问题来了,如今很多网站都是采用的Ajax加载数据,根本没有分…...
Android Jetpack是什么与原生android 有什么区别
Android Jetpack是什么 Android Jetpack是Google推出的一套开发组件工具集,旨在帮助开发者更高效地构建高质量的Android应用。它包含多个库和工具,被分为架构、用户界面、行为和基础四大类。以下是一些Android Jetpack的示例: 架构组件 ViewModel:用于以生命周期的方式管理…...
Android Retrofit 框架适配器模块深入源码分析(五)
Android Retrofit 框架适配器模块深入源码分析 一、引言 在 Android 开发中,网络请求是一个常见且重要的功能。Retrofit 作为一个强大的网络请求框架,以其简洁的 API 和高度的可定制性受到了广泛的欢迎。适配器模块(CallAdapter)…...
