《自己动手写操作系统》源码解析——第三章pmtest5.asm

自己动手写操作系统+源码+工具 自己动手写操作系统pdf+源码+工具,整合到一起,免得大家到处找。 立即下载
在pmtest4中,我们已经看到,对于非一致性代码,如何从低特权级转移向高特权级。但是我们该怎样从高特权级别,转移到低特权级别呢?一开始我们在实模式,是ring 0,我们怎样从ring 0到ring3 ,这个通过call和jmp没法做到,我们该通过什么方式实现?本文,主要是为你解答这些疑问,展现实现这种特权级别转移的方法。通过本节,你将学会如何在特权级之间自由切换。

一、预备理论

1.关于堆栈

我们都知道,可以通过call和jmp实现长跳转或者短跳转(段内转移或者段间转移)。但是jmp和call毕竟是有分别的,其中call指令是会影响到堆栈的:对于短调用,call指令执行时,下一条指令的eip入栈,ret指令执行时候=,这个eip就会从堆栈中弹出。

短调用call指令执行前后堆栈的变化如下图所示(注意带有参数的ret指令):

短调用返回时候的示意图:


长调用的情况与此相似,不同的是call指令执行时,被压栈的还有cs。


2.特权级变换

注意到:如果我们采用call指令进行长跳转,而且特权级别发生了变换,这个时候就发生了堆栈切换,比如从A切换到了B。但是我们的数据保存在A上面,如何从B中返回呢?原来,intel提供了一种机制,能够将A上的一些内容copy到B堆栈中。同时,A堆栈要切换到B堆栈,但是它怎么知道B堆栈在哪里呢答案:原来这个内容保存在任务的TSS结构中,TSS是任务状态段。每个任务都最多有四个堆栈段,但是只有一个esp和ss,TSS就解决了堆栈切换方面的数据保存问题。TSS的相关介绍,可以参考:保护模式及其编程——任务切换http://blog.youkuaiyun.com/trochiluses/article/details/19768579。

示意图如下:



比如,我们目前是ring 3,需要转移到ring 1,那时,堆栈将自动切换到ss1和esp1。(由于只是从外层到内层切换,才从TSS中取得堆栈信息,所以,TSS中没有ring 3相关的堆栈信息。那么从内层向外层切换,如何获得目的地的信息呢?提前压入栈中,来获得返回信息)。

切换过程中有关堆栈的处理,可以参考:保护模式及其编程——保护的详尽意义:通过调用门转移特权级http://blog.youkuaiyun.com/trochiluses/article/details/19573651


二、代码分析

这一部分代码与pmtest4中的代码比较相似,我们仅仅用加粗部分标记改变和增加的代码,没有改变而且不是特别影响逻辑的代码我们直接省略,来进行分析:

; ==========================================


%include "pm.inc"; 常量, 宏, 以及一些说明

org 0100h
jmp LABEL_BEGIN

[SECTION .gdt]
; GDT
;                                         段基址,         段界限     , 属性
LABEL_GDT: Descriptor      0,                   0, 0 ; 空描述符
LABEL_DESC_CODE_RING3:Descriptor       0, SegCodeRing3Len - 1, DA_C + DA_32 + DA_DPL3; 非一致代码段, 32
LABEL_DESC_STACK: Descriptor       0,          TopOfStack, DA_DRWA + DA_32 ; Stack, 32 位;没有标记的stack,实际上是ring 0
LABEL_DESC_STACK3: Descriptor      0,         TopOfStack3, DA_DRWA + DA_32 + DA_DPL3; Stack, 32 位;ring 3
LABEL_DESC_LDT: Descriptor      0,          LDTLen - 1, DA_LDT ; LDT
LABEL_DESC_TSS: Descriptor      0,          TSSLen - 1, DA_386TSS ; TSS
LABEL_DESC_VIDEO: Descriptor0B8000h,              0ffffh, DA_DRW + DA_DPL3; 显存首地址,为了能在ring 3中读写显存,我们改变了显存段的特权级别


; 门                                            目标选择子,       偏移, DCount, 属性
LABEL_CALL_GATE_TEST: Gate SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3;门描述符的特权级别也是ring 3,不然没法访问
; GDT 结束

GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限
dd 0; GDT基地址

; GDT 选择子
SelectorNormal equLABEL_DESC_NORMAL - LABEL_GDT
SelectorCodeRing3equ LABEL_DESC_CODE_RING3- LABEL_GDT + SA_RPL3
SelectorStack3equ LABEL_DESC_STACK3- LABEL_GDT + SA_RPL3
SelectorTSSequ LABEL_DESC_TSS- LABEL_GDT
SelectorCallGateTestequ LABEL_CALL_GATE_TEST- LABEL_GDT + SA_RPL3
; END of [SECTION .gdt]
;========================================================================================================

[SECTION .data1] ; 数据段
SPValueInRealMode dw 0
; 字符串
PMMessage: db "In Protect Mode now. ^-^", 0 ; 进入保护模式后显示此字符串
;*******************************
DataLen equ $ - LABEL_DATA
; END of [SECTION .data1]

===============================================================================================
; 全局堆栈段
[SECTION .gs]
LABEL_STACK:
times 512 db 0
TopOfStack equ$ - LABEL_STACK - 1
; END of [SECTION .gs]
================================================================================================

; 堆栈段ring3
[SECTION .s3]
ALIGN 32
[BITS 32]
LABEL_STACK3:
times 512 db 0
TopOfStack3 equ$ - LABEL_STACK3 - 1
; END of [SECTION .s3]

=================================================================================================
; TSS ---------------------------------------------------------------------------------------------
[SECTION .tss]
ALIGN 32
[BITS 32]
LABEL_TSS:
DD 0; Back
DD TopOfStack; 0 级堆栈
DD SelectorStack; 
DD 0; 1 级堆栈
DD 0; 
; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
========================================================================================================

[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
mov   [LABEL_GO_BACK_TO_REAL+3], ax
mov [SPValueInRealMode], sp

; 初始化 16 位代码段描述符
mov  ax, cs
;********************
mov  byte [LABEL_DESC_CODE16 + 7], ah

; 初始化 32 位代码段描述符
; 初始化测试调用门的代码段描述符
; 初始化数据段描述符
; 初始化堆栈段描述符
; 初始化堆栈段描述符(ring3)
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK3
mov word [LABEL_DESC_STACK3 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK3 + 4], al
mov byte [LABEL_DESC_STACK3 + 7], ah

; 初始化 LDT 在 GDT 中的描述符
; 初始化 LDT 中的描述符
; 初始化Ring3描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_CODE_RING3
mov word [LABEL_DESC_CODE_RING3 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE_RING3 + 4], al
mov byte [LABEL_DESC_CODE_RING3 + 7], ah


; 初始化 TSS 描述符
xoreax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_TSS
mov word [LABEL_DESC_TSS + 2], ax
shr eax, 16
mov byte [LABEL_DESC_TSS + 4], al
mov byte [LABEL_DESC_TSS + 7], ah


; 为加载 GDTR 作准备
; 加载 GDTR
; 关中断
; 打开地址线A20
; 准备切换到保护模式
; 真正进入保护模式
jmp dword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
mov ax, cs
关闭 A20 地址线
; 开中断

; ┛回到 DOS
; END of [SECTION .s16]
==============================================================================================

[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS
32]
LABEL_SEG_CODE32:
movax, SelectorData
movds, ax ; 数据段选择子
; 视频段选择子
         ; 堆栈段选择子
movesp, TopOfStack

; 下面显示一个字符串
; 显示完毕
call DispReturn
; Load TSS
movax, SelectorTSS

ltrax ; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。


pushSelectorStack3 ;ss3
push TopOfStack3 ;sp3
push SelectorCodeRing3 ;cs3
push 0 ;Eip
retf ; Ring0 -> Ring3,历史性转移!将打印数字 '3'。

注意:这一个关键的retf指令,它弹出两个值,一个给EIP,另一个给ECS;通过这种方式,转移到ring 3;这里要特别关注retf指令,它用于长返回,而且可以用于带有特权级别变换的长返回。上面之所以要push 4个参数,是因为retf要用到这四个参数。之所以要loadTSS,是因为待会还要从ring3回到ring 0.


另外,需要特别注意的是此处的TSS;如果没有TSS,我们只能从高特权级别转向低特权级;没法从低特权级别转向高特权级别。也就是说,如果涉及到堆栈切换,那么必须要用到TSS。我们可以将ltr ax这句注释掉,然后查看结果(见最后一副图)。


; ------------------------------------------------------------------------

SegCode32Len equ$ - LABEL_SEG_CODE32
; END of [SECTION .s32]


;=======================================================================================================

[SECTION .sdest]; 调用门目标段
[BITS 32]

LABEL_SEG_CODE_DEST:
mov ax, SelectorVideo
mov gs, ax; 视频段选择子(目的)
movedi, (80 * 12 + 0) * 2 ; 屏幕第 12 行, 第 0 列。
mov ah, 0Ch; 0000: 黑底    1100: 红字
mov al, 'C'
mov [gs:edi], ax
; Load LDT
mov ax, SelectorLDT
lldt ax
jmpSelectorLDTCodeA:0 ; 跳入局部任务,将打印字母 'L'。
;retf
SegCodeDestLenequ $ - LABEL_SEG_CODE_DEST
; END of [SECTION .sdest]
======================================================================================

; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
; 跳回实模式:
mov ax, SelectorNormal
;******************************************
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置成正确的值

Code16Len equ $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]
=============================================================================
; LDT
[SECTION .ldt]
ALIGN 32
LABEL_LDT:
;                                         段基址       段界限     ,   属性
LABEL_LDT_DESC_CODEA: Descriptor      0,     CodeALen - 1,   DA_C + DA_32 ; Code, 32 位

LDTLen equ $ - LABEL_LDT

; LDT 选择子
SelectorLDTCodeA equLABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
; END of [SECTION .ldt]

==================================================================================
; CodeA (LDT, 32 位代码段)
[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_A:
mov ax, SelectorVideo
mov gs, ax; 视频段选择子(目的)
movedi, (80 * 13 + 0) * 2 ; 屏幕第 13 行, 第 0 列。
mov ah, 0Ch; 0000: 黑底    1100: 红字
mov al, 'L'
mov [gs:edi], ax
; 准备经由16位代码段跳回实模式
jmp SelectorCode16:0
CodeALen equ $ - LABEL_CODE_A
; END of [SECTION .la]

=======================================================================================

; CodeRing3
[SECTION .ring3]
ALIGN 32
[BITS 32]
LABEL_CODE_RING3:
mov ax, SelectorVideo
mov gs, ax; 视频段选择子(目的)
movedi, (80 * 14 + 0) * 2 ; 屏幕第 14 行, 第 0 列。
mov ah, 0Ch; 0000: 黑底    1100: 红字
mov al, '3'
mov [gs:edi], ax

callSelectorCallGateTest:0 ; 测试调用门(有特权级变换),将打印字母 'C'。

jmp$ ;注意:程序将停在这里


SegCodeRing3Lenequ $ - LABEL_CODE_RING3
; END of [SECTION .ring3]


程序的大致执行过程如下:

保护模式下进入16b的代码段,ring=0,初始化相关段描述符、门描述符、ldt描述符、数据段描述符、堆栈段描述符、ldt中的描述符、ring 3的代码段描述符TSS描述符;加载gdt,通过长jmp,进入保护模式,进入32b的代码段,ring =0:初始化数据段、堆栈段、视频段的选择子寄存器;显示一个字符串;load TSS;利用retf指令,从ring 0转移到ring 3的代码段:在代码段中显示‘3’,然后通过调用门进入ring 0的代码段:打印字母C,然后通过jmp跳转到局部代码段:打印字母‘L’;程序回到ring 3,成为循环。


查看运行结果:我们第一次进入了不同的特权级。


下图:注释TSS的结果,可以看到可以从ring 0进入ring 3;但是无法从ring 3进入ring 0.


自己动手写操作系统(含源代码) 自己动手写操作系统(含源代码)。很不错的资源。 立即下载

相关推荐

《自己动手写操作系统》源码(0币)

《自己动手写操作系统》源码(0币)

pmtest5.asm

; ==============带特权级变换的调用门==============; pmtest5.asm; 编译方法:nasm pmtest5.asm -o pmtest5.com;; 执行过程:                  门;    16位实模式---->CODE32(ring0)---->CODE_RING3(ring3)----------->CODE_DEST(ring0)---

世界上有两种人,一种是改变世界的人,另一种是被世界同化的人! 709

Git系列(一)、Git基础教程【建议收藏】

Git一、Git简介1.1、什么是Git? 一、Git简介 1.1、什么是Git? Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或大或小的项目。 Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源代码的版本控制软件。 Git与常用的版本控制工具CVS、Subversion等不同,它采用了分布式版本库的方式,不用服务器端软件支持。 .........

一宿君 4万+

《自己动手写操作系统》——制作一个简单的操作系统

将一段显示字符串的汇编代码编译成二进制,并制作成镜像文件;安装虚拟机软件,加载镜像文件,输出字符串不符合预期;通过反汇编发现,目前的10H中断需要额外指定cx寄存器的值;org 07c00h的作用是告诉汇编器这段代码将会加载在段内偏移地址为7c00处的内存中,因此需要将代码中所有地址都进行这个偏移。要正确反汇编一个’DOS.COM’文件,反汇编器必须知道文件中的第一条指令是被装载到内存何处,ndisasm缺省地认为你给它的每一个文件都是装载到0处的,所以你必须告诉它这一点。'-o'

UCAS_HMM的博客 2510

UReport2 报表设计器 在线表格

UReport2官网 一、UReport2报表设计器 UReport2是一个开源的可视化报表设计器,功能强大,操作简单,可以实现复杂的报表统计,有各种各样的导出和打印功能,支持导入Excel表格! 1、报表设计器 2、设计一个简单的用户列表展示 二、可编辑表格 扩展了layui的表格,支持多元化数据编辑(单选,多些、日期、下拉联动),开发简单、高效、便捷。有了它就可以轻松开发类似进销存系统的表格录入功能了,用户拿来即用! 三、主从表录......

ejinxian的专栏 2万+

centos-stream-9上安装nvidia驱动和cuda-toolkit

centos-stream-9上安装nvidia驱动和cuda-toolkit

qq_19072921的博客 3021

[自己动手写操作系统]的学习实践【第三章】

因为第二章主要是讲系统的一些配置,所以我们在这里就不准备怎么费多少笔墨描述了,第三章 保护模式第一节  认识保护模式本章我们要学习计算机如何由实模式转入保护模式。实模式向保护模式的转变需要更改一系列的参数。实质是通过一个数据结构来使得16位寄存器访问了4GB的空间。我们下面的程序就显示了这个例子。我们对此的分析将以旁注的方式表现。; ========================

“秋天的叶子”的专栏 896

自绘CListCtrl时发现ON_WM_MEASUREITEM_REFLECT和DrawItem(LPDRAWITEMSTRUCT lpMeasureItemStruct) 无响应

自绘CListCtrl时发现ON_WM_MEASUREITEM_REFLECT和DrawItem(LPDRAWITEMSTRUCT lpMeasureItemStruct) 无响应 一般情况下,我们都是在 dialog 里面 放一个 list 控件,然后 用自己的类绑定自己的自绘对象,如同下面: 1.  在 dialog 里面 加一个 list 列表 2. 然后 绑定这个列表 哈哈...

mfreesky的专栏 2171

http://blog.csdn.net/zgh1988/article/details/7098981

[置顶]全面剖析《自己动手写操作系统》第三章---保护模式                分类:            操作系统2011-12-24 00:30649人阅读评论(16)收藏举报 《自己动手写操作系统》读后感                http://blog.csdn.net/zgh1988/article/details/7059936 全面剖析《自己动手写操

yu_chenglong的专栏 1095

概率论基础-严士健 第二版 习题与补充1.6答案

概率论基础-严士健 第二版 习题与补充1.6答案 1.不独立。 设有一均匀正八面体,其第1,2,3,4面染红色,第1,2,3,5面染白色,第1,6,7,8面染黑色,现在以A,B,C分别表示投一次正八面体出现红、白、黑的事件,则 P(A)=P(B)=P(C)=12.P(A) = P(B) = P(C) = \frac{1}{2}.P(A)=P(B)=P(C)=21​. P(ABC)=18=P(A)P...

weixin_42341993的博客 2515

肖特基二极管选型参数,结构原理,工艺与注意问题总结

肖特基二极管(Schottky Diode)是一种特殊的二极管,也被称为肖特基势垒二极管。它是利用金属与半导体接触形成的金属-半导体结原理制成的。与传统的PN结二极管相比,肖特基二极管具有更低的正向压降、更高的开关速度、更高的反向恢复时间以及更好的温度稳定性等特点。本文对肖特基二极管的工作原理,结构特点,工艺流程,选型参数及设计注意事项进行总结。

fydar的博客 813

《自己动手写操作系统》第三章 pmtest1——从实模式到保护模式

摘要:本节,我们将学习如何从实模式进入保护模式,剖析pmtest1.asm源码。详细讲解如何从保护模式进入实模式。 1.头文件编写: 这一部分,你需要把一些关键物理器件和数据结构的布局记在脑子里 GDT描述符:32b段基地址(B2、B3、B4、B7)+20b段限长(B0、B1、B6的低4位)+12b段属性(B5、B6的高4位),详细布局见下图(针对代码段和数据段): 所

河西无名式 3738

芒果YOLO11改进12:即插即用 | 独家原创改进最新PWConv核心结构|来自最新CVPR2023顶会,

即插即用 | 独家原创改进最新PWConv核心结构|来自最新CVPR2023顶会,

包括YOLOv5、YOLOv7、YOLOv8等模型改进 560

《自己动手写操作系统》第三章 pmtest5源码解析——特权级变换与堆栈切换实例详解

摘要:在pmtest4中,我们已经看到,对于非一致性代码,如何从低特权级转移向高特权级。但是我们该怎样从高特权级别,转移到低特权级别呢?本文,主要是为你解答这些疑问,展现实现这种特权级别转移的方法。另外,我们将在实践中探讨,为什么要设定RPL,是不是相对与DPL多此一举呢?在调试环节,我们将通过改动代码,排查相关错误来深入理解特权级检测机制。 一、预备理论1.关于堆栈 我们都知道,可以通过cal

河西无名式 2204

使用 MATLAB/Simulink 构建一个多时间尺度下的微电网经济运行优化策略模型

本文介绍了基于Simulink的多能互补微电网系统建模方法,重点关注多时间尺度下的经济运行优化策略。内容包括:系统背景与目标、微电网结构设计(光伏、风电、储能、柴油发电机等模块)、多时间尺度能量管理架构(日前优化、日内滚动优化、实时控制)的实现步骤,以及仿真运行与结果分析。教程详细说明了如何在Simulink中调用优化算法,设置仿真参数,并提供了进阶研究方向和资源推荐。该模型为研究微电网的经济运行和智能调度提供了完整的仿真平台解决方案。

amy_mhd的博客 172

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

读《自己动手写操作系统》

  用一个月左右的时间读了《自己动手写操作系统》,这是一本让人读着很过瘾,却也特别累的一本书。http://ike.126.com  对操作系统的兴趣由来已久,只是一直未能找到入门之径。操作系统教材是个令人生畏的东西,它可以告诉人有什么,却不能告诉人为什么,从那里了解的操作系统有如盲人摸到的象,得到各个部分,却不能拥有整体,加之缺乏实践的支持,理论显得苍白空洞。如Linux般的开源操作系统

净心编程 9647

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

通过调用门进行有特权级变换的转移,详细注解 对pmtest5.asm解释很详细.

http://www.myexception.cn/operating-system/484288.html http://www.myexception.cn/operating-system/445331.html http://www.myexception.cn/operating-system/445078.html 代码总体过程如下: 1.进入保护模式,显示字符In Pr...

weixin_33989780的博客 215

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

pmtest4.asm

;====================================== ;pmtest4.asm ;编译方法:nasm pmtest4.asm -o pmtest4.bin ;====================================== %include "pm.inc"; 常量,宏 以及一些说明 ;org 07c00h org 0100h jmp LABEL_B...

lhm的博客 533

orange's学习--第三章:e pmtest5.asm设置TSS通过调用门进行低特权级到高特权级的转移

由低特权级到高特权级切换时新堆栈会从TSS中取得,由高特权级到低特权级切换时,需要从当前堆栈中弹出调用者的ss和sp。 除了0级堆栈之外,其他各个字段我们都没做任何初始化。因为在本例中,我们只用到这一部分。 TSS作为一个段存在,需要设置GDT内容和段选择子,然后进行加载 LABEL_DESC_TSS: Descriptor 0, TSSLen-1, DA...

交换ab:a=a^b;b=a^b;a=a^b; 373

pmtest5

Code;==========================================;pmtest5.asm;编译方法:nasmpmtest5.asm-opmtest5.com;==========================================%include"pm.inc";常量,宏,以及一些说明org0100hjm...

weixin_34152820的博客 213

【学习】【保护模式编程、四】

【LDT描述符&&特权级&&门】一、LDT(局部描述符)GDT是全局描述符,是整个系统的描述符,描述符着所有的段!!!在前几章我们已经熟悉了GDT的一些基本功能,与运作机制。对GDT描述符的定义与使用也就那么几项固定的步骤,接下来再了解LDT.LDT是局部描述符。看字面LDT与GDT很相似.它们都是描述符。只不过GDT是全局描述符、而LDT是局部描述符。那么LDT该如何

zhusongdong的专栏 2449

操作系统实践(5)——关于LDT

LDT与GDT类似,为局部描述符表。使用LDT有几个步骤: 1.在GDT中加一个表项,并增加一个描述符,假设为SelectorLDT; 2.像配置GDT表那样,配置好LDT表自己的描述符,并设置好相应的ldt的选择子,选择子有一位标识是ldt还是gdt; 3.加载LDT,使用lldt指令; 4.直接jmp ldt里面的选择子。跳转到LDT里的代码段,跟用GDT跳转代码段一样,都是通过 j

最帅惋红曲 1606

自己动手写操作系统(高清图书+源代码)分享

很喜欢《自己动手写操作系统》这本书,但现在这本书已经绝版了。在这里分享一下这本书的高清电子版和源代码,感兴趣的人可以下载一下。 链接:https://pan.baidu.com/s/1lPXg-Airu2NFjSaTwX67jQ 提取码:sdfr 复制这段内容后打开百度网盘手机App,操作更方便哦 ...

qq_42884482的博客 9698
上一篇: 自己动手写操作系统源码剖析——第三章 pmtest4.asm
下一篇: int 15h中断获取内存信息
谁不小心的
博客等级 码龄15年 462粉丝 246原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值