TLB还可以单个刷新,利用invlpg命令(invalidate TLB Entry)。invlpg的格式为invlpg m32,当执行这条指令的时候,处理器会用给出的线性地址搜索TLB,找到那个条目,然后从内存中重新加载其内容到相应的TLB页表数据中。invlpg是特权指令,必须要在CPL为特权0级执行,该指令不影响任何标志位。
我们的内核进行刷新TLB的是在加载程序之前复制页目录的时候做的。但是我自己写的程序加载位置是可变的,其实不刷新也没什么关系。教材那个就一定要刷新。具体看代码。
5. 宏汇编技术(Macro)
所谓的宏汇编技术,其实和C的宏是一样的,就是一个字符串代替一堆东西而已,当然了也可以带参数。
1. 单行宏%define:
顾名思义这种宏只能定义单行的比如:
2. 多行宏%macro:
这种宏的后面都要带%endmacro作为指定宏结束的位置。而且多行宏可以指定参数个数
参数的个数直接定义在宏名称的后面,使用的时候宏内参数由%加对应数字引用参数,上面的例子已经说得很清楚了,如果没有参数,那么参数个数直接设为0。
★PART3:本章的程序
说实话本章的练习题没什么好写的,就把例程写一遍就好了,我自己写的时候自己写了一个很大的坑就是我的宏写错了,导致自己访问内存的时候一直显示页错误(其实是调试了很久才知道是页错误,访问了一个没有登记的页)。而且要注意的是,一些关键的过程,比如put_string,读硬盘和TCB的链接这些过程,一定要关中断,不然会引发系统严重错误。
教材上用的中断只是关闭了从片的中断,我改了一下只留时钟中断,而且是更新结束中断,然后程序可以停机然后给时间中断唤醒,这样感觉会更清晰一点。
1. 主引导程序MBR
1 ;========================保护模式主引导扇区代码======================== 2 core_phy_base: equ 0x00040000 ;内核加载地址 3 core_sector_address: equ 0x00000001 ;内核所在扇区 4 ;====================================================================== 5 SECTION mbr align=16 vstart=0x00007c00 ;注意起始地址已经变成了0x7c00了 6 mov ax,cs 7 mov ss,ax 8 mov sp,0x7c00 9 10 mov eax,[cs:pgdt_base+0x02] 11 xor edx,edx 12 mov ebx,0x10 13 div ebx 14 15 mov ds,eax ;让ds指向gdt位置进行操作 16 mov ebx,edx ;别忘了还有可能出现偏移地址 17 ;---------------------描述符#0--------------------- 18 mov dword [ebx+0x00],0x00000000 ;空描述符 19 mov dword [ebx+0x04],0x00000000 20 ;---------------------描述符#1--------------------- 21 mov dword [ebx+0x08],0x0000ffff ;4GB代码段,特权级为0 22 mov dword [ebx+0x0c],0x00cf9800 23 ;---------------------描述符#2--------------------- 24 mov dword [ebx+0x10],0x0000ffff ;4GB向上拓展数据段和栈段,特权级为0 25 mov dword [ebx+0x14],0x00cf9200 26 27 mov word[cs:pgdt_base],23 ;加载gdt 28 lgdt [cs:pgdt_base] 29 30 in al,0x92 ;快速开启A20 31 or al,0x02 ;是写入2,不要搞错了,写入1就是重启了 32 out 0x92,al 33 cli ;关掉中断 34 35 mov eax,cr0 36 or eax,0x01 ;设置PE位 37 mov cr0,eax 38 39 jmp dword 0x0008:flush ;进入保护模式 40 41 [bits 32] 42 flush: 43 mov eax,0x0010 44 mov ds,eax 45 mov es,eax 46 mov fs,eax 47 mov gs,eax 48 mov ss,eax ;栈段也是向上拓展的 49 mov esp,0x7000 50 51 ;接下来开始读取内核头部 52 mov esi,core_sector_address 53 mov edi,core_phy_base 54 call read_harddisk_0 55 56 mov eax,[core_phy_base] ;读取用户总长度 57 xor edx,edx 58 mov ebx,512 59 div ebx 60 61 cmp edx,0 62 jne @read_last_sector 63 dec eax 64 @read_last_sector: 65 cmp eax,0 66 je @setup 67 mov ecx,eax 68 .read_last: 69 inc esi 70 call read_harddisk_0 71 loop .read_last 72 @setup: ;下面准备开启页管理 73 mov ecx,1024 74 mov ebx,0x00020000 75 xor esi,esi 76 77 _flush_PDT: ;清空页表 78 mov dword[es:ebx+esi*4],0x00000000 79 inc esi 80 loop _flush_PDT 81 82 ;页目录的最后一个32字节是指向自己的页表(这个页表就是页目录) 83 mov dword[ebx+4092],0x00020003 ;属性:存在于物理内存,只允许内核自己访问 84 85 ;页目录的第一个页表指示最底下1MB内存的4KB页(内核代码,必须虚拟地址和物理地址一致) 86 mov edx,0x00021003 87 mov dword[ebx+0x000],edx ;低端映射(临时的,创建用户目录的时候就没了) 88 mov dword[ebx+0x800],edx ;高端映射 89 90 ;现在0x00020000的页是第0个页表(指示页目录),0x00021000是第一个页表(指示底下1MB的东西) 91 mov ebx,0x00021000 92 xor eax,eax 93 xor esi,esi 94 95 _make_page: 96 mov edx,eax 97 or edx,0x00000003 ;属性:存在于物理内存,只允许内核自己访问 98 mov [ebx+esi*4],edx 99 add eax,0x1000 100 inc esi 101 cmp esi,256 102 jl _make_page 103 104 mov eax,0x00020000 105 mov cr3,eax ;把页目录基地址放在cr3,准备开启页功能 106 107 sgdt [pgdt_base] 108 add dword[pgdt_base+2],0x80000000 ;设定GDT为高地址 109 lgdt [pgdt_base] 110 111 mov eax,cr0 112 or eax,0x80000000 113 mov cr0,eax ;置PG位,开启页功能 114 115 ;将堆栈映射到高端,这是非常容易被忽略的一件事。应当把内核的所有东西 116 ;都移到高端,否则,一定会和正在加载的用户任务局部空间里的内容冲突, 117 ;而且很难想到问题会出在这里。 118 add esp,0x80000000 ;因为已经处于平坦模式了,所以内核栈指针也要映射 119 120 jmp [core_phy_base+0x80000000+4] ;都在一个段上了,直接近转移,start在偏移量是4的地方 121 ;=============================函数部分================================= 122 read_harddisk_0: ;esi存了28位的硬盘号 123 push ecx 124 125 mov edx,0x1f2 ;读取一个扇区 126 mov al,0x01 127 out dx,al 128 129 mov eax,esi ;0~7位,0x1f3端口 130 inc edx 131 out dx,al 132 133 mov al,ah ;8~15位,0x1f4端口 134 inc edx 135 out dx,al 136 137 shr eax,16 ;16-23位,0x1f5端口 138 inc edx 139 out dx,al 140 141 mov al,ah ;24-28位,LBA模式主硬盘 142 inc edx 143 and al,0x0f 144 or al,0xe0 145 out dx,al 146 147 inc edx ;读命令,0x1f7端口 148 mov al,0x20 149 out dx,al 150 151 .wait: 152 in al,dx 153 and al,0x88 154 cmp al,0x08 155 jne .wait 156 157 mov dx,0x1f0 158 mov ecx,256 159 .read: 160 in ax,dx 161 mov [edi],ax 162 add edi,2 163 loop .read 164 165 pop ecx 166 167 ret 168 ;====================================================================== 169 pgdt_base dw 0 170 dd 0x00008000 ;GDT的物理地址 171 ;====================================================================== 172 times 510-($-$$) db 0 173 dw 0xaa55