当前位置: 首页 > news >正文

丹阳网站建设案例/网页设计参考网站

丹阳网站建设案例,网页设计参考网站,网站弹窗无法显示,上海百度优化1.A20地址线:A20地址线是指第21根地址线(因为地址线是从A0开始的)。我们前边提到过,在支持32位保护模式的计算机中运行实模式时,为了适应实模式的寻址,我们的高12位地址线是默认输出0的。举个例子&#xff…

7989d8668ef43427b4e68e7d634d1312.png

1.A20地址线:

A20地址线是指第21根地址线(因为地址线是从A0开始的)。

我们前边提到过,在支持32位保护模式的计算机中运行实模式时,为了适应实模式的寻址,我们的高12位地址线是默认输出0的。

举个例子:对于实模式下的0xFFFFF地址,实际上传入内存控制器的地址是0x000FFFFF,使用这种默认高位置0的方式可以增强寻址的兼容性,这样,不管是20位地址还是32位地址,我们只需要内存控制器能够识别32位地址即可。

a61ada38ae73de0d2798938390ac4fad.png

但是,这样也会带来一个问题,我们如果在实模式下使用段:偏移的方式寻址,可能会产生21位的物理地址。如果我们使用0xFFFF:0x0010地址,当这个地址传入MMU时,MMU会产生21位地址100000000000000000000B,当A20打开时,就会寻址到1M以上的空间,这在实模式下是不允许的,所以在计算机开机时,A20是默认关闭的。这个时候,20位地址的部分就会被舍弃,超过1M的空间就会由于这个舍去的地址位而顺序地映射回0开始的物理地址中。

22bb64d574f828538868e4ab5e8eac1f.png

所以,在保护模式中,由于我们需要使用到第21个地址位,进入保护模式的第一步就是打开A20地址线。

2.加载GDT

在保护模式中,我们仍然需要地址分段,但是我们以后用到更多的内存管理是建立在分页上的。分页是可选的,但分段是必须的!

在保护模式分段中,由于我们要控制段的一些权限以及特权级问题(主要还是突出了“保护”二字,这方面后边会讲解),我们需要更多的数据来描述段的信息,所以我们使用了段描述符结构来存放这些信息,并且使用类似于指针的选择子来执行这些结构,我们只需要在段寄存器中存放选择子即可。当我们访问一个段时,计算机会取出段寄存器中的“指针”,获取到指向的段描述符就可以得到段的基址以及一些特权信息了。

为了方便管理,我们将段描述符放在一起,形成一张段描述符表,用的最多的段描述符表就是GDT(LDT我们不使用)。

(1)selector结构

b4dcdb478b9944d424dd0cad3c0cadea.png
selector结构

selector的高13位是本描述符在GDT中的索引值,RPL是请求特权级(0~3 越小特权级别越高)TI指本描述符在LDT还是GDT中,我们默认设置为0(在GDT中)

我们重点关注的是描述符索引,由于每个描述符的大小位8字节,所以访问某个描述符地址为:GDT基址+8*描述符索引。

(2)段描述符

297db2d1e89193525ab786ce1920bdd4.png

下边来简要概述下每个字段(如果想深入了解,可以去查看inter手册)

段界限基址被截断成了3个部分(主要还是为了兼容80286的非32位保护模式),拼接成了32位的段基址。

S,TYPE两个段共同表示了段的一些属性,比如读写执行属性以及段的类型(区分代码段 堆栈段等等)。

DPL表示段的目标权限等级

P表示段是否存在于内存中,我们暂不考虑,设为1即可

AVL段表示保留位,没啥用,预设为0就好了

段界限段和G共同表示了段的长度,G为0时,段长度=(段界限+1)*1-1 Byte

G为1时,段长度=(段界限+1)*4K+1 Byte,我们预设段界限为0xFFFFF,G为1

这样我们就可以访问 (0xFFFFF+1)*4K-1=0xFFFFFFFF的空间了,一共4个GB

L表示是否为64位段,我们编写的是32位系统,所以预设为0即可

D/B段表示EIP ESP IP ESP的选择,这个字段是为了兼容性考虑的,我们预设为1即可

(3)补充注意

在GDT中的第一个段描述符是不可用的,这是为了防止进入保护模式后,开发人员忘记装载新的段选择子,导致段寄存器中的值还是0,这样就会访问到第零个描述符造成错误的指令执行,所以我们首先要把GDT中的第一个描述符置0,我们使用第1个开始的描述符即可

(4)装载GDTR

我们的selector中存放的是段的索引,所以我们还需要一个寄存器来存放GDT的基址,这就是GDTR寄存器,这样我们就可以使用GDTR中的基址+段选择符索引*8即可访问对应的段描述符,在汇编中提供了 lgdt指令来装载GDTR寄存器。

另一个值的注意的是,每次这样访问地址时都访问一次GDT来寻找段描述符是很浪费时间的,所以,计算机会将段的描述信息放入高速缓存中,这样访问地址前可以直接通过缓存得到段基址。所以我们在修改GDT的时候一定要清空缓存,不然就会访问到更新前的段选择符信息。而清空缓存的方法就是再次使用lgdt来重新加载GDT。

3.动手实践吧

首先要清楚,我们使用的是平坦寻址模式,即每个段都是由0x00000000开始的,并且都能访问到4GB的空间,之所以这样做,是因为这样可以方便编写程序,以及兼容ELF文件,而对于内存管理等问题,我们交给分页即可。

boot.inc
;--------------   gdt描述符属性  -----------
DESC_G_4K   equ	  1_00000000000000000000000b   
DESC_D_32   equ	   1_0000000000000000000000b
DESC_L	    equ	    0_000000000000000000000b	;  64位代码标记,此处标记为0便可。
DESC_AVL    equ	     0_00000000000000000000b	;  cpu不用此位,暂置为0  
DESC_LIMIT_CODE2  equ 1111_0000000000000000b
DESC_LIMIT_DATA2  equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2  equ 0000_000000000000000b
DESC_P	    equ		  1_000000000000000b
DESC_DPL_0  equ		   00_0000000000000b
DESC_DPL_1  equ		   01_0000000000000b
DESC_DPL_2  equ		   10_0000000000000b
DESC_DPL_3  equ		   11_0000000000000b
DESC_S_CODE equ		     1_000000000000b
DESC_S_DATA equ	  DESC_S_CODE
DESC_S_sys  equ		     0_000000000000b
DESC_TYPE_CODE  equ	      1000_00000000b	;x=1,c=0,r=0,a=0代码段是可执行的,非依从的,不可读的,已访问位a清0.  
DESC_TYPE_DATA  equ	      0010_00000000b	;x=0,e=0,w=1,a=0数据段是不可执行的,向上扩展的,可写的,已访问位a清0.DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00
DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00
DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b;--------------   选择子属性  ---------------
RPL0  equ   00b
RPL1  equ   01b
RPL2  equ   10b
RPL3  equ   11b
TI_GDT	 equ   000b
TI_LDT	 equ   100b
这是一个配置文件,描述了一些段的字段信息

下边是我们GDT的分配以及加载GDT的过程

GDT_BASE:   dd    0x00000000 dd    0x00000000CODE_DESC:  dd    0x0000FFFF dd    DESC_CODE_HIGH4DATA_STACK_DESC:  dd    0x0000FFFFdd    DESC_DATA_HIGH4VIDEO_DESC: dd    0x80000007        ; limit=(0xbffff-0xb8000)/4k=0x7dd    DESC_VIDEO_HIGH4  ; 此时dpl为0GDT_SIZE   equ   $ - GDT_BASEGDT_LIMIT   equ   GDT_SIZE - 1 times 30 dq 0                     ; 此处预留30个描述符的空位SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0     ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0     ; 同上SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0    ; 同上 total_mem_bytes dd 0                  ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址gdt_ptr  dw  GDT_LIMIT dd  GDT_BASE
;boot开始!
boot_start:cli                        ;关闭外中断        mov [mutiboot_addr32], ebx ; GRUB加载内核后会将mutiboot信息地址存放在ebx中;-----------------   准备进入保护模式   -------------------
;1 打开A20
;2 加载gdt
;3 将cr0的pe位置1;-----------------  打开A20  ----------------in al,0x92or al,0000_0010Bout 0x92,al;-----------------  加载GDT  ----------------lgdt [gdt_ptr];-----------------  cr0第0位置1  ----------------mov eax, cr0or eax, 0x00000001mov cr0, eaxjmp dword SELECTOR_CODE:far_jmp_target      ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,;初始化段寄存器以及栈结构far_jmp_target:mov ax,SELECTOR_DATAmov ss,axmov ds,axmov ax,SELECTOR_VIDEOmov gs,axmov esp, STACK_TOP      and esp, 0xFFFFFFF0  ;16字节对齐mov ebp, 0
我们定义了三个段描述符,并且按照一定的顺序加载了GDT。但是CS段选择子的加载,我们只能采用远跳转的方法,直接跳转到SELECTOR_CODE段执行,并且可以清空流水线(我们之前提到过)。最后再手动加载SS以及DS段

4.使用GRUB加载内核文件

我之前本来使用的是自己编写加载器来加载内核,但是后来由于安装的bochs工具的一些问题,导致每次只能加载两个扇区,调试了几天无果后,我打算使用现有的加载器来加载内核,同时,这样做我们可以避开ELF文件头的分析过程

为了方便大家的学习,我是配置好了一个1.44M的软盘来作为启动盘

https://pan.baidu.com/s/1AtixGuVUt1nm9qNE1OLhIQ​pan.baidu.com
提取码jjj6

但是如果想自己制作软盘启动器的同学,看以下链接

CSDN-专业IT技术社区-登录​blog.csdn.net

GRUB会加载配置文件中指定的内核文件,我们要把软盘文件放在开发主目录下。

对于内核编译以及链接过程,我提供了以下两个文件:

Makefile
#Makefile for BHOS
#edit:2020/1/26
#by 不吃香菜的大头怪C_SOURCES = $(shell find . -name "*.c")        #.c源文件
C_OBJECTS = $(patsubst %.c, %.o, $(C_SOURCES)) #.c生成.o文件
S_SOURCES = $(shell find . -name "*.s")        #.s源文件
S_OBJECTS = $(patsubst %.s, %.o, $(S_SOURCES)) #.s生成.o文件#编译器与链接器参数
nasm_pars = -f elf -g -F stabs
gcc_pars = -c -Wall -m32 -ggdb -gstabs+ -nostdinc -fno-builtin -fno-stack-protector -I include
ld_pars = -T kernel.ld -m elf_i386 -nostdlib#目标软盘
target_floppy = floppy.img#过程
all: $(S_OBJECTS) $(C_OBJECTS) link copykern.c.o:gcc $(gcc_pars) $< -o $@.s.o:nasm $(nasm_pars) $<.PHONY:link
link:ld $(ld_pars) $(S_OBJECTS) $(C_OBJECTS) -o kernel.elf.PHONY:copykern
copykern:sudo mount $(target_floppy) /mnt/kernel/sudo cp kernel.elf /mnt/kernel/sudo umount /mnt/kernel/.PHONY:mt
mt:sudo mount $(target_floppy) /mnt/kernel.PHONY:umt
umt:sudo umount /mnt/kernel.PHONY:run
run:qemu -fda $(target_floppy) -boot a.PHONY:clean
clean:rm $(S_OBJECTS) $(C_OBJECTS) kernel.elf

由于我们再Makefile中使用了链接器脚本参数来控制ld链接器,提供以下脚本

kernel.ld
ENTRY(boot_start)
SECTIONS
{/*内核是加载到1 M空间之上的*/. = 0x100000;.text :{*(.text). = ALIGN(4096);}.data :{*(.data). = ALIGN(4096);}.rodata :{*(.rodata). = ALIGN(4096);    }.bss :{*(.bss). = ALIGN(4096);}.stab :{*(.stab). = ALIGN(4096);}.stabstr :{*(.stabstr). = ALIGN(4096);}/DISCARD/ : { *(.comment) *(.eh_frame) }
}
放在主目录下,主要内容是将内核各个section紧挨着链接,并且链接的虚拟起始地址为0x100000,即1M开始处,并且定义了内核的起始执行位置,即boot_start

最后,我们来编写GRUB加载内核后执行的启动内容文件

boot.s
;/boot/boot.s
;edit:2020/1/26
;by:不吃香菜的大头怪%include "./boot/boot.inc" 
;这三个双字为GRUB加载器识别MagicNumber 以及配置信息(可以不用理解)
dd 0x1badb002  
dd 0x3   	
dd -(0x1badb002+0x3)      [BITS 32]   ;由于GRUB在加载内核前进入保护模式,所以要32位编译   
section .text
[GLOBAL boot_start]    
[GLOBAL mutiboot_addr32]  
[EXTERN kern_entry]GDT_BASE:   dd    0x00000000 dd    0x00000000CODE_DESC:  dd    0x0000FFFF dd    DESC_CODE_HIGH4DATA_STACK_DESC:  dd    0x0000FFFFdd    DESC_DATA_HIGH4VIDEO_DESC: dd    0x80000007        ; limit=(0xbffff-0xb8000)/4k=0x7dd    DESC_VIDEO_HIGH4  ; 此时dpl为0GDT_SIZE   equ   $ - GDT_BASEGDT_LIMIT   equ   GDT_SIZE - 1 times 30 dq 0                     ; 此处预留30个描述符的空位SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0     ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0     ; 同上SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0    ; 同上 total_mem_bytes dd 0                  ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址gdt_ptr  dw  GDT_LIMIT dd  GDT_BASE
;boot开始!
boot_start:cli                        ;关闭外中断        mov [mutiboot_addr32], ebx ; GRUB加载内核后会将mutiboot信息地址存放在ebx中;-----------------   准备进入保护模式   -------------------
;1 打开A20
;2 加载gdt
;3 将cr0的pe位置1;-----------------  打开A20  ----------------in al,0x92or al,0000_0010Bout 0x92,al;-----------------  加载GDT  ----------------lgdt [gdt_ptr];-----------------  cr0第0位置1  ----------------mov eax, cr0or eax, 0x00000001mov cr0, eaxjmp dword SELECTOR_CODE:far_jmp_target      ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,;初始化段寄存器以及栈结构far_jmp_target:mov ax,SELECTOR_DATAmov ss,axmov ds,axmov ax,SELECTOR_VIDEOmov gs,axmov esp, STACK_TOP      and esp, 0xFFFFFFF0  ;16字节对齐mov ebp, 0           
;进入内核主函数    call kern_entry                    jmp dword $          ;防止意外退出内核section .data
mutiboot_addr32:        dd 0x0        section .bss             ; 未初始化的数据段从这里开始
stack:resb 0x100000        ; 1M的内核栈 (应该够了吧,不够自己改)
STACK_TOP equ $-1      

首先,我们再文件开头填充了三个Magic Number,并且我们再Makefile中把boot.s连接到内核文件头部,这样GRUB识别内核头部的这三个数后会将内核文件识别为系统所在文件。

我们再此定义了boot_start符号,并且使用global将这个符号声明成全局的,这样GRUB就可以访问这个符号,并且我们在ld脚本中定义了boot_start为内核入口函数,GRUB在配置完成后就会jmp到boot_start执行

在boot_start中,我们首先把ebx存放在一个指定地址中,ebx中存放的是GRUB启动中的一些重要数据,以后会使用到。

最后,我们使用call kern_entry执行了内核的入口函数(我们在boot.s中也声明extern这个外部符号,类似于c语言中的函数声明)

下边,我们来编写kern_entry函数所在的c主文件

entry.c
void kern_entry(){char *input = (uint8_t *)0xB8000;char color = (0 << 4) | (15 & 0x0F);*input++ = 'H'; *input++ = color;*input++ = 'e'; *input++ = color;*input++ = 'l'; *input++ = color;*input++ = 'l'; *input++ = color;*input++ = 'o'; *input++ = color;*input++ = ' '; *input++ = color;*input++ = 'B'; *input++ = color;*input++ = 'H'; *input++ = color;*input++ = 'O'; *input++ = color;*input++ = 'S'; *input++ = color;*input++ = '!'; *input++ = color;while(1)asm volatile ("hlt");
}
我们在内核中直接访问VGA显存映射来控制屏幕输出

最后,cd到项目主目录中,使用make编译后再使用make run打开qemu虚拟机运行

7d934aae97d4ee8abce613253b450dd5.png
GRUB界面

a9a7c0114eb56d79efdba95aff3aafa5.png

最后,我们成功地加载了内核,并且有了屏幕输出

下一节,我们将编写string函数库以及printk等内核级打印函数

http://www.jmfq.cn/news/5133601.html

相关文章:

  • 深圳市网站建设公司/海南seo排名优化公司
  • 做画找图网站/南京百度seo代理
  • 做网站dw怎么用/seo岗位工作内容
  • 在云服务器打建网站/项目网站
  • 地方性小网站的建设/百度竞价点击软件奔奔
  • 广州天河区房价2022年最新房价/seo外链推广平台
  • 宁波网页/高明搜索seo
  • 2018年网站建设/企业网站建设论文
  • 查找网站建设虚拟目录/怀来网站seo
  • 制作b2c网站多少钱/网站制作公司官网
  • 成都网站建设scwbo/google play 安卓下载
  • 湘潭公司做网站/外国黄冈网站推广平台
  • 做木质的网站/淘宝关键词怎么选取
  • 武汉可信网站建设公司/长春网站优化体验
  • 建站多少钱一个/市场调研方法
  • 三项措施做好门户网站建设/网站排行查询
  • 做化工回收的 做那个网站/北京seo网站优化公司
  • 云南省交通投资建设集团有限公司网站/哪个推广平台推广最靠谱
  • .top和网站/搜索引擎推广渠道
  • 哪有做企业网站/seo点击排名源码
  • 北京网站建设的价格低/网络营销广告案例
  • 加强网站信息怎么做/全国疫情实时资讯
  • 规划设计公司毛利/柏乡seo快排优化
  • 给你一个新的网站怎么做/seo建站教学
  • 内容管理网站/杭州关键词优化测试
  • 怎样在设计网站做图赚钱/东莞网站建设推广技巧
  • 可视化信息 网站/南昌seo网站排名
  • 网站浮动广告怎么做/windows优化大师会员
  • 深圳龙岗淘宝网站建设公司有哪些/seoshanghai net
  • 贵港市建设局网站/洛阳seo外包公司费用