《ORANGE‘s 一个操作系统的实现》--保护模式进阶
保护模式进阶
大内存读写
GDT段
;GDT
[SECTION .gdt]
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C+DA_32; 非一致代码段, 32
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ; Data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32; Stack, 32 位
LABEL_DESC_TEST: Descriptor 0500000h, 0ffffh, DA_DRW
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
; GDT 结束GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限dd 0 ; GDT基地址; GDT 选择子
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorData equ LABEL_DESC_DATA - LABEL_GDT
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorTest equ LABEL_DESC_TEST - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt]
与认识保护模式当中的代码类似,这里也是定义了GDT的描述符、DT表的属性、选择子等内容
其中LABEL_DESC_TEST的段基址被设定为了0500000h远远超过实模式的寻址上限
LABEL_DESC_VIDEO指向了显存的首地址,用于将特定字符显示在屏幕上
数据段
;数据段
[SECTION .data1]
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
; 字符串
PMMessage: db "In Protect Mode now. ^-^", 0 ; 在保护模式中显示
OffsetPMMessage equ PMMessage - $$
StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest equ StrTest - $$
DataLen equ $ - LABEL_DATA
; END of [SECTION .data1]
OffsetPMMessage定义为PMMessage相对于$$(即LABEL_DATA)的偏移地址
OffsetStrTest定义为StrTest相对于$$(即LABEL_DATA)的偏移地址
DataLen定义了数据段的长度
堆栈段
; 全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:times 512 db 0TopOfStack equ $ - LABEL_STACK - 1; END of [SECTION .gs]
这段代码定义了一个512个字节的堆栈段LABEL_STACK和一个栈顶指针TopOfStack
实模式->保护模式代码段
LABEL_BEGIN:mov ax, csmov ds, axmov es, axmov ss, axmov sp, 0100hmov [LABEL_GO_BACK_TO_REAL+3], axmov [SPValueInRealMode], sp; 初始化 16 位代码段描述符mov ax, csmovzx eax, axshl eax, 4add eax, LABEL_SEG_CODE16mov word [LABEL_DESC_CODE16 + 2], axshr eax, 16mov byte [LABEL_DESC_CODE16 + 4], almov byte [LABEL_DESC_CODE16 + 7], ah; 初始化 32 位代码段描述符xor eax, eaxmov ax, csshl eax, 4add eax, LABEL_SEG_CODE32mov word [LABEL_DESC_CODE32 + 2], axshr eax, 16mov byte [LABEL_DESC_CODE32 + 4], almov byte [LABEL_DESC_CODE32 + 7], ah; 初始化数据段描述符xor eax, eaxmov ax, dsshl eax, 4add eax, LABEL_DATAmov word [LABEL_DESC_DATA + 2], axshr eax, 16mov byte [LABEL_DESC_DATA + 4], almov byte [LABEL_DESC_DATA + 7], ah; 初始化堆栈段描述符xor eax, eaxmov ax, dsshl eax, 4add eax, LABEL_STACKmov word [LABEL_DESC_STACK + 2], axshr eax, 16mov byte [LABEL_DESC_STACK + 4], almov byte [LABEL_DESC_STACK + 7], ah
实模式代码段首先将各个段段首的真实地址写入描述符的段基址当中
注意这两行代码:
mov [LABEL_GO_BACK_TO_REAL+3], axmov [SPValueInRealMode], sp
这两行代码将实模式下的cs赋值给LABEL_GO_BACK_TO_REAL标签往后数第三、四个字节,将实模式下的sp赋值给SPValueInRealMode
其中LABEL_GO_BACK_TO_REAL+3实际上是jmp 0:LABEL_REAL_ENTRY中的0,观察下图jmp指令的内存占用不难发现,byte4和byte5指向了jmp指令的段基址,因此这里是修改jmp指令的段基址到实模式下的cs

; 为加载 GDTR 作准备xor eax, eaxmov ax, dsshl eax, 4add eax, LABEL_GDT ; eax <- gdt 基地址mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址; 加载 GDTRlgdt [GdtPtr]; 关中断cli; 打开地址线A20in al, 92hor al, 00000010bout 92h, al; 准备切换到保护模式mov eax, cr0or eax, 1mov cr0, eax; 真正进入保护模式jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处
然后加载GDTR,在关中断后打开地址线A20切换到保护模式
保护模式代码段
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]LABEL_SEG_CODE32:mov ax, SelectorDatamov ds, ax ; 数据段选择子mov ax, SelectorTestmov es, ax ; 测试段选择子mov ax, SelectorVideomov gs, ax ; 视频段选择子mov ax, SelectorStackmov ss, ax ; 堆栈段选择子mov esp, TopOfStack
保护模式开始先将各个数据段赋给对应的寄存器
; 下面显示一个字符串,数据段的基址就是LABLE_DATA的物理地址mov ah, 0Ch ; 0000: 黑底 1100: 红字xor esi, esixor edi, edimov esi, OffsetPMMessage ; 源数据偏移(相对于LABEL_DESC_DATA的偏移量)mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕每行有 80 个字符,每个字符占用两个字节(一个用于字符,一个用于属性)cld
.1: ;刷新标志寄存器lodsb ; 从 esi 指向的内存地址加载一个字节到 al 寄存器,并递增 esitest al, al ; 测试 al 寄存器的值是否为零jz .2 ; 为0则是终止符,表示显示完毕mov [gs:edi], ax ; add edi, 2jmp .1
.2: ; 显示完毕
然后将ah置为0ch,设置屏幕属性为黑底红字,将esi和edi清零[,然后将OffsetPMMessage(即PMMessage相对于LABEL_DATA的偏移地址)送到esi,设定目标数据的偏移地址并刷新标志寄存器
然后进入循环,从esi指向的内存地址加载一个字节到al寄存器,并递增esi,然后判断al寄存器是否为0('\0'),如果不是终止字符,则向段基址为gs(即SelectorVideo),偏移地址为edi的显存地址中写入数据ax(ah为显示属性,al为显示字符)
.2: ; 显示完毕call DispReturncall TestReadcall TestWritecall TestRead; 到此停止jmp SelectorCode16:0
显示完毕后依次调用
DispReturn:模拟回车显示TestRead:从内存中读内容到显存TestWrite:向内存中写内容
从先后两次调用TestRead得到的不同的显示结果可以判断是否成功进行了大内存读写
TestRead
TestRead:xor esi, esimov ecx, 8
.loop:mov al, [es:esi]call DispALinc esiloop .loopcall DispReturnret
函数首先设定ecx为8,表示读取8个字节的数据
随后进入循环,从es:esi(即SelectorTest:esi)中读取一个字节的内容到al,并调用DispAL函数将它以16进制的方式打印出来
循环结束后模拟打印一个回车并结束函数
TestWrite
TestWrite:push esipush edixor esi, esixor edi, edimov esi, OffsetStrTest ; 源数据偏移cld
.1:lodsbtest al, aljz .2mov [es:edi], alinc edijmp .1
.2:pop edipop esiret
函数将esi赋值为OffsetStrTest(即StrTest的偏移地址)
然后进入循环,将整个字符串写入es(即SelectorTest)中
保护模式->实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:; 跳回实模式:mov ax, SelectorNormalmov ds, axmov es, axmov fs, axmov gs, axmov ss, axmov eax, cr0and al, 11111110bmov cr0, eaxLABEL_GO_BACK_TO_REAL:jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值Code16Len equ $ - LABEL_SEG_CODE16
程序首先将实模式的选择子赋给ax,并初始化其他寄存器,然后置cr0寄存器PE标志位为0,设定程序处于实模式
jmp 0:LABEL_REAL_ENTRY指令已经在前面修改为jmp 实模式下cs对应的地址:LABEL_REAL_ENTRY,因此程序再次跳转到实模式(即[SECTION .s16]代码段)下LABEL_REAL_ENTRY标签处
LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里mov ax, csmov ds, axmov es, axmov ss, axmov sp, [SPValueInRealMode]in al, 92h ; `.and al, 11111101b ; | 关闭 A20 地址线out 92h, al ; /sti ; 开中断mov ax, 4c00h ; `.int 21h ; / 回到 DOS
; END of [SECTION .s16]
最后关闭A20地址线并开中断,正式回到实模式
LDT(Local Descriptor Table)
本节内容仅仅介绍pmtest3.asm相对于pmtest2.asm做的改变,并省略初始化描述符代码
GDT中添加LDT描述符
[SECTION .gdt]
; GDT
;......
; 段基址, 段界限 , 属性
LABEL_DESC_LDT: Descriptor 0, LDTLen - 1, DA_LDT ; LDT; GDT 结束
GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限dd 0 ; GDT基地址; GDT 选择子
;......
SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT
;......
; END of [SECTION .gdt]
这部分完成了在GDT中定义LDT描述符的过程,其中LABEL_DESC_LDT的段基址被填充为标签LABEL_LDT的真实地址
问:为什么要在GDT中定义LDT?
答:全局描述符表(GDT)是用于存储段描述符的表,而局部描述符表(LDT)是特定于某个进程的描述符表。LDT允许进程拥有自己的段描述符,这有助于实现内存保护和隔离。
LDT段
; 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 equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
; END of [SECTION .ldt]
LABEL_LDT_DESC_CODEA的基址被填充为标签LABEL_CODE_A的真实地址
注意SelectorLDTCodeA(即选择子)的定义中多了一项SA_TIL,其定义为SA_TIL EQU 4,即将选择子的TIL标志位设置为1
TIL标志位用于区分GDT选择子和LDT选择子,如果TIL为1,则系统会从当前LDT中寻找相应的描述符
LDT代码段
; CodeA (LDT, 32 位代码段)
[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_A:mov ax, SelectorVideomov gs, ax ; 视频段选择子(目的)mov edi, (80 * 12 + 0) * 2 ; 屏幕第 10 行, 第 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]
LDT代码段的实现与32位代码段大致相同,就是在屏幕上以黑底红字打印字母L并模拟回车显示,并在代码段的末尾调转到SelectorCode16选择子指向的代码段LABEL_SEG_CODE16
LDT和GDT区别
-
作用范围:
- GDT(Global Descriptor Table):全局描述符表是系统级别的数据结构,它为整个操作系统定义段描述符。所有的进程和线程都共享同一个GDT。
- LDT(Local Descriptor Table):局部描述符表是特定于进程的数据结构,每个进程可以有自己的LDT,用于定义该进程特有的段描述符。
-
权限和隔离:
- GDT:由于GDT是全局的,它通常包含操作系统核心代码和数据的段描述符,这些描述符通常具有较高的权限级别。
- LDT:LDT允许进程拥有自己的段描述符,这有助于实现进程间的内存隔离。每个进程的LDT可以有不同的权限设置,从而提供更细粒度的访问控制。
-
内容:
- GDT:GDT通常包含代码段、数据段、任务状态段(TSS)、门描述符等。
- LDT:LDT通常包含该进程特定的代码段、数据段、资源段等。
-
使用方式:
- GDT:操作系统在启动时初始化GDT,并在进程切换时使用GDT中的描述符来加载新的段寄存器。
- LDT:进程在创建时可以创建自己的LDT,并通过特定的系统调用(如
set_thread_area或modify_ldt)来加载和切换LDT。
-
性能影响:
- GDT:由于所有进程共享GDT,频繁的GDT更新可能会影响系统性能。
- LDT:每个进程有自己的LDT,因此LDT的更新不会影响到其他进程,这可以在一定程度上减少系统开销。
-
安全性:
- GDT:由于GDT是全局的,对GDT的不当修改可能会影响整个系统的稳定性和安全性。
- LDT:LDT提供了额外的隔离层,即使一个进程的LDT被破坏,也不会影响到其他进程。
-
操作系统支持:
- GDT:几乎所有的x86操作系统都支持GDT。
- LDT:现代操作系统对LDT的支持有所减少,因为现代操作系统更多地依赖于扁平的内存模型和页式内存管理,而不是传统的段式内存管理。
特权级
在IA32的分段机制下,操作系统总共有4个特权级,从高到低分别是0、1、2、3。数字越小表示的特权级越大。
CPL
CPL是当前执行的程序或任务的特权级。
- 通常情况下:
CPL等于代码所在的段的特权级。当程序转移到不同特权级的代码段时,处理器将改变CPL。 - 遇到一致代码段:一致代码段可以被相同或者更低特权级的代码访问,当处理器访问一个与CPL特权级不同的一致代码段时,
CPL不会被改变。
DPL
DPL表示段或者门的特权级
- 数据段:
DPL规定了可以访问此段的最低特权级 - 非一致代码段(无调用门):
DPL规定访问此段的特权级 - 调用门:
DPL规定了当前执行的程序或任务可以访问此调用门的最低特权级 - 一致代码段和通过调用门访问的非一致代码段:
DPL规定了访问此段的最高特权级 TSS:DPL规定了可以访问此TSS的最低特权级
RPL
对于非一致代码段,处理器通过检查RPL和CPL来确认一个访问请求是否合法
RPL>CPL:比较目标段的DPL和当前RPLRPL<CPL:比较目标段的DPL和当前CPL
特权级转移
jmp和call实现转移
-
规则:
-
非一致代码段:
CPL必须等于目标段的DPL,同时要求RPL小于等于DPL -
一致代码段:则要求
CPL大于或者等于目标段的DPL,RPL不做检查,转移后CPL不会发生变化
-
-
缺点:
- 对于非一致代码段,只能在相同特权级代码段之间转移
- 遇到一致代码段也最多能从低到高
调用门
调用门结构
调用门作用
笔者认为,调用门实际上充当一个中间人作用,使得低特权级的代码段可以访问高特权级代码
假设现在需要由A代码段使用call指令经调用门G访问B代码段,设如下几个标记:
CPL RPL_A DPL_B DPL_G
- B代码段为一致代码段:
A代码段访问调用门G:- 对于
调用门G,DPL_G规定了访问此调用门的最低特权级 CPL≤ \le ≤DPL_G且RPL_A≤ \le ≤DPL_G
- 对于
调用门G访问B代码段:- 对于一致代码段,
DPL_B规定了访问此段的最高特权级 DPL_B≤ \le ≤CPL
- 对于一致代码段,
- B代码段为非一致代码段:
A代码段访问调用门G:- 对于
调用门G,DPL_G规定了访问此调用门的最低特权级 CPL≤ \le ≤DPL_G且RPL_A≤ \le ≤DPL_G
- 对于
调用门G访问B代码段:- 对于一致代码段使用
call指令时,DPL_B规定了访问此段的最高特权级 DPL_B≤ \le ≤CPL(如果使用jmp指令则DPL_B= = =CPL)
- 对于一致代码段使用
如图所示
有特权级变化时堆栈的变换
TSS
由于x86架构下,各个特权级之间不共享堆栈,因此程序共需要四个堆栈.这些信息被存储在TSS数据结构中:
可以看到,TSS中一共存储了3套ss和esp,分别对应0特权级到2特权级(由低特权级到高特权级切换时新堆栈才会从TSS中取得,所以TSS中没有位于最外层的堆栈信息)
call过程堆栈变化
- 根据目标代码段的
DPL(新的CPL)从TSS中选择应该切换至哪个ss和esp - 从TSS中读取新的
ss和esp。在这过程中如果发现ss、esp或者TSS界限错误都会导致异常 - 对
ss描述符进行检验,如果发生错误,同样产生异常 - 暂时性地保存当前
ss和esp的值 - 加载新的
ss和esp(用新的ss和esp去替换旧的) - 将刚刚保存起来的
ss和esp的值压入新栈 - 从调用者堆栈(原堆栈)中将参数复制到被调用者堆栈(新堆栈)中,复制参数的数目由调用门中
Param Count一项来决定。如果ParamCount是零的话,将不会复制参数 - 将当前的
cs和eip压栈 - 加载调用门中指定的新的
cs和eip,开始执行被调用者过程。
ret过程堆栈变化
- 检查保存的
cs中的RPL以判断返回时是否要变换特权级 - 弹出并加载被调用者堆栈上的
cs和eip(指向call语句的下一条命令),并会进行代码段描述符和选择子类型和特权级检验 - 如果
ret指令含有参数,则增加esp的值以跳过参数,然后esp将指向被保存过的调用者ss和esp。 - 弹出并加载
ss和esp,切换到调用者堆栈,被调用者的ss和esp被丢弃。在这里将会进行ss描述符、esp以及ss段描述符的检验 - 如果
ret指令含有参数,增加esp的值以跳过参数(此时已经在调用者堆栈中) - 检查
ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(此规则不适用于一致代码段),那么一个空描述符会被加载到该寄存器。

相关文章:
《ORANGE‘s 一个操作系统的实现》--保护模式进阶
保护模式进阶 大内存读写 GDT段 ;GDT [SECTION .gdt] ; 段基址, 段界限 , 属性 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符 LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描…...
【可变参模板】可变参类模板
可变参类模板也和可变参函数模板一样,允许模板定义含有0到多个(任意个)模板参数。可变参类模板参数包的展开方式有多种,以下介绍几种常见的方法。 一、递归继承展开 1.1类型模板参数包的展开 首先先看下面的代码: /…...
Linux 递归删除大量的文件
一般情况下 在 Ubuntu 中,递归删除大量文件和文件夹可以通过以下几种方式快速完成。常用的方法是使用 rm 命令,配合一些适当的选项来提高删除速度和效率。 1. 使用 rm 命令递归删除 最常见的方式是使用 rm 命令的递归选项 -r 来删除目录及其所有内容。…...
设计一个算法,找出由str1和str2所指向两个链表共同后缀的起始位置
假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,则可共享相同的后缀存储空间,例如,’loading’和’being’的存储映像如下图所示。 设str1和str2分别指向两个单词所在单链表的头结点,链表结点结构为 data…...
Python中如何判断一个变量是否为None
在Python中,判断一个变量是否为None是一个常见的需求,特别是在处理可选值、默认值或者是在函数返回结果可能不存在时。虽然这个操作本身相对简单,但围绕它的讨论可以扩展到Python的哲学、类型系统、以及如何在不同场景下优雅地处理None值。 …...
表观遗传系列1:DNA 甲基化以及组蛋白修饰
1. 表观遗传 表观遗传信息很多为化学修饰,包括 DNA 甲基化以及组蛋白修饰,即DNA或蛋白可以通过化学修饰添加附加信息。 DNA位于染色质(可视为微环境)中,并不是裸露的,因此DNA分子研究需要跟所处环境结合起…...
Android 跳转至各大应用商店应用详情页
测试通过机型品牌: 华为、小米、红米、OPPO、一加、Realme、VIVO、IQOO、荣耀、魅族、三星 import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import …...
Pywinauto鼠标操作指南
Pywinauto是一个强大的Python库,用于自动化Windows桌面应用程序的测试。它提供了一系列工具和API来模拟用户输入,包括键盘、鼠标事件,以及与各种窗口控件交互的能力。本文将详细介绍如何使用Pywinauto来执行鼠标操作,并通过一些示…...
VRAY云渲染动画怎么都是图片?
动画实际上是由一系列连续的静态图像(帧)组成的,当这些帧快速连续播放时,就形成了动画效果。每一帧都是一个单独的图片,需要单独渲染。 云渲染农场的工作方式: 1、用户将3D场景文件和动画设置上传到云渲染…...
共享内存(C语言)
目录 一、引言 二、共享内存概述 1.什么是共享内存 2.共享内存的优势 三、共享内存的实现 1.创建共享内存 2.关联共享内存 3.访问共享内存 4.解除共享内存关联 5.删除共享内存 四、共享内存应用实例 五、总结 本文将深入探讨C语言中的共享内存技术,介绍其原理、…...
《JavaEE进阶》----16.<Mybatis简介、操作步骤、相关配置>
本篇博客讲记录: 1.回顾MySQL的JDBC操作 2..Mybatis简介、Mybatis操作数据库的步骤 3.Mybatis 相关日志的配置(日志的配置、驼峰自动转换的配置) 前言 之前学习应用分层时我们知道Web应用程序一般分为三层,Controller、Service、D…...
HuggingFists算子能力扩展-PythonScript
HuggingFists作为一个低代码平台,很多朋友会关心如何扩展平台算子能力。扩展平台尚不支持的算子功能。本文就介绍一种通过脚本算子扩展算子能力的解决方案。 HuggingFists支持Python和Javascript两种脚语言的算子。两种语言的使用方式相同,使用者可以任选…...
WInform记录的添加和显示
1、程序 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;namespace ComboBoxApp {public part…...
★ C++基础篇 ★ string类的实现
Ciallo~(∠・ω< )⌒☆ ~ 今天,我将继续和大家一起学习C基础篇第五章下篇----string类的模拟实现 ~ 上篇:★ C基础篇 ★ string类-CSDN博客 C基础篇专栏:★ C基础篇 ★_椎名澄嵐的博客-CSDN博客 目录 一 基础结构 二 迭代器 …...
rman compress
级别 初始 备完 耗时 low 1804 3572 0:10 High 1812 3176 2:00 MEDIUM 1820 3288 0:13 BASIC 1828 3444 0:56 ---不如MEDIUM,时间还长 NO COMPRESS 1820 5924 0:5 R…...
创建一个Oracle版本的JDK的Docker镜像
背景说明 OpenJDK 和Oracle JDK 一般情况下我们选择OpenJDK,两者针对大部分场景都可以满足,有些地方例如反射技术获得某些包路径下的类对象等,有时候选择OpenJDK会导致空指针异常。 两者在底层实现方面有部分区别。 创建镜像 这里是Linux…...
Harmony OS DevEco Studio 如何导入第三方库(以lottie为例)?-- HarmonyOS自学2
在做鸿蒙开发时,离不开第三方库的引入 一.有哪些支持的Harmony OS的 第三方库? 第三方库下载地址: 1 tpc_resource: 三方组件资源汇总 2 OpenHarmony三方库中心仓 二. 如何加入到DevEco Studio工程 以 lottie为例 OpenHarmony-TPC/lot…...
JAVA数据导出为Excel
目录 一、导入依赖 二、使用的相关类 1、XSSFWorkbook 构造方法 创建表 操作表 保存表 样式和格式 日期处理 密码保护 其他 2、XSSFSheet 获取属性和信息 行操作 列操作 表的属性 合并单元格 保护表 页眉和页脚 注释 其它 3、XSSFRow 获取属性和信息 单…...
【数据结构与算法 | 灵神题单 | 快慢指针(链表)篇】力扣876, 2095, 234
1. 力扣876:链表的中间节点 1.1 题目: 给你单链表的头结点 head ,请你找出并返回链表的中间结点。 如果有两个中间结点,则返回第二个中间结点。 示例 1: 输入:head [1,2,3,4,5] 输出:[3,4,…...
第十五届蓝桥杯图形化省赛题目及解析
第十五届蓝桥杯图形化省赛题目及解析 一. 单选题 1. 运行以下程序,角色会说( )? A、29 B、31 C、33 D、35 正确答案:C 答案解析: 重复执行直到m>n不成立,即重复执行直到m<n。所有当m小于或者 等于n时&…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
