最近在看linux0.11源码,对其中的进程调度查了一些资料,《80386汇编语言精要》这本书写的非常好,对理解帮助很大,建议大家看一下,主要是两方面的知识:

  1. GDT\IDT与运行有什么关系?
  2. 80386有三种运行模式:实模式,保护模式,兼容8086模式
  3. linux0.11中具体是如何实现的

GDT\IDT与运行有什么关系?

先弄清楚以下这几个寄存器的作用(作用就是为了寻址)。

  • GDT:该表地址存在GDTR寄存器里

全局描述符表 GDT ( Global Descriptor Table) , 除了任务门、中断门和陷井门描述符外 , 包含着系统中所有任务都可用的那些描述符。它的第一个 8 字节位置 没有使用。其中可以存放如下几种不同的描述符:

  • 门:为什么task切换要使用软中断

门是用来控制访问在目标码段的入口点。有调用门、任务门、中断门和陷井门四种。所谓中断就是用于处理外部发生的突发事件, 陷井是一种特殊的中断, 用户定的软件中断其实就是陷井。调用门主要用于将程序控制转换到一个更高的特权级(数字低) , 任务门用于切换任务, 它只能涉及任务状态段。中断门和陷井门用于中断处理, 其中的地址是指向中断或陷井处理子程序起点的指针, 中断门禁止中断( IF = 0) , 而陷井门并不禁止。

  • IDT:该表地址存在IDTR寄存器里

中断描述符表 IDT ( Interrupt Descriptor Table) , 可以包含 256 个描述符 , 每个描述符为 8 个字节。IDT 中只能包含任务门、中断门和陷井门描述符 , 虽然它最长也可以为 64K 字节 , 但只能存取 2K 字节以内的描述符 , 即 256 个。它最少为 256 个字节 ,因为 Intel 公司保留了 32 个中断描述符供自己使用。规定这些数字都是为了和早期的机器兼容。

  • LDT:

局部描述符表 LDT ( Local Descriptor Table) , 包含了与一个给定任务有关的描述符 , 每一个任务都有一个各自的 LDT。有了 LDT, 就可以使给定任务的码、数据与别的任务相隔离。 LDT 中的描述符都在 GDT 中 , 所以 , 不同的任务可以有相同的描述符 , 这样就可以共享全局数据和代码。段描述符:就是存在GDT中的项

  • TSS:在 GDT

所谓任务状态段 TSS ( Task State Segment) , 就是一个特殊的固定格式的段 , 它包含了一个任务和与之相链接的允许嵌套的任务的所有状态信息!这个段在task切换中非常重要,我后面再分析。当任务状态段描述符所描述的任务正在执行时, 它就是类型B, 否则就置成类型9TSS 就由任务状态寄存器TR 来标识,TR有16位可见+64位不可见,16位可见就是段选择子,相当于GDT中的索引,TR通过可见的16位找到TSS。执行IRET 时, 控制返回到原来的任务, 当前的任务状态则被保存到TSS 中, 并从原有任务的TSS 中恢复原有任务的状态。

  • 段选择子:(放在段寄存器里,80386中有6个(即CS,SS,DS,ES,FS,GS)16位的段寄存器)

在实模式下 , 段寄存器存储的是真实的段地址 , 在保护模式下 , 16 位的段寄存器无法放下 32 位段地址 , 因此 , 它们被称为选择器 , 即起选择描符述表中的描述符的作用。这样 , 就把描述符中的 32 位段地址做为实际的段地址。

80386有三种运行模式:实模式,保护模式,兼容8086模式。

兼容8086模式很好理解!每种模式下寄存器的内容均有不同的意义,实模式与保护模式主要为了通过硬件的设计添加错误检查。你可以设计的程序只运行在实模式(实模式是16位,保护模式是32位)所有的地址都是硬件地址,随便一个指针错误都会造成死机,当然结果就是系统很容易崩溃!linux中setup.s最后几句就是进入保护模式。

# Well, now's the time to actually move into protected mode. To make
# things as simple as possible, we do no register set-up or anything,
# we let the gnu-compiled 32-bit programs do that. We just jump to
# absolute address 0x00000, in 32-bit protected mode.
    #mov    $0x0001, %ax    # protected mode (PE) bit
    #lmsw    %ax        # This is it!
    mov    %cr0, %eax    # get machine status(cr0|MSW)    
    bts    $0, %eax    # turn on the PE-bit 
    mov    %eax, %cr0    # protection enabled

进入保护模式后,软件使用逻辑地址访问时,硬件可以通过段描述符检查是否溢出。

例如:若给定一个逻辑地址是 a:b ,根据逻辑地址的a(段选择符)的T1位确定是选择GDT还是LDT。

  1. a若是T1位选择GDT,根据GDTR找到GDT的基址,根据a的 3~15位确定它的段描述符X在GDT中的位置(GDTR即基址+a的3-15bit即相对位置):确定段描述符X,再根据段描述符提取出其中包含的段基址信息,段基址+b(段内偏移),最终确定线性地址。

  2. a若是T1位选择LDT,根据GDTR找到GDT的基址,根据LDTR的高13位确定它的LDTX描述符在GDT中的位置(GDTR基址+LDTR13bit即相对位置):确定LDTX描述符。LDTX描述符可以确定LDT的基址(LDTX描述符确定LDT表在内存中的起始位置),再根据段选择符a确定的相对位置,可以确定LDT中的私有段描述符Y。接下来同上面的:再根据段描述符提取出其中包含的段基址信息,段基址+b(段内偏移),最终确定线性地址。

linux0.11中具体是如何实现的?

在setup.s中有加载idt与gdt,指令如下:

lidt    idt_48        # load idt with 0,0
    lgdt    gdt_48        # load gdt with whatever appropriate

lidt与lgdt操作数是48位,6 字节操作数装入全局描述符表寄存器IDTR与GDTR。

setup.s代码中的定义如下:需要注意的是此时系统还处于实模式16位操作。

gdt:
    .word    0,0,0,0        # dummy

.word    0x07FF        # 8Mb - limit=2047 (2048*4096=8Mb)
    .word    0x0000        # base address=0
    .word    0x9A00        # code read/exec
    .word    0x00C0        # granularity=4096, 386

.word    0x07FF        # 8Mb - limit=2047 (2048*4096=8Mb)
    .word    0x0000        # base address=0
    .word    0x9200        # data read/write
    .word    0x00C0        # granularity=4096, 386

idt_48:
    .word    0            # idt limit=0
    .word    0,0            # idt base=0L

gdt_48:
    .word    0x800            # gdt limit=2048, 256 GDT entries
    .word   512+gdt, 0x9        # gdt base = 0X9xxxx,

然后通过以下指令进入保护模式并跳转。跳转指令是ljmp。

    #mov    $0x0001, %ax    # protected mode (PE) bit
    #lmsw    %ax        # This is it!
    mov    %cr0, %eax    # get machine status(cr0|MSW)    
    bts    $0, %eax    # turn on the PE-bit 
    mov    %eax, %cr0    # protection enabled
                
                # segment-descriptor        (INDEX:TI:RPL)
    .equ    sel_cs0, 0x0008 # select for code segment 0 (  001:0 :00) 
    ljmp    $sel_cs0, $0    # jmp offset 0 of code segment 0 in gdt

进入保护模式之后第一件事是什么,当然是设置堆栈。这个时候再回头看head.s的开头就更清楚了,之前的boot分析其实还不是很懂这里为什么要lss两次。如果第一次lss不设置,那么这个堆栈寄存器esp根本就没有初始化。

startup_32:
    movl $0x10,%eax
    mov %ax,%ds
    mov %ax,%es
    mov %ax,%fs
    mov %ax,%gs
    lss stack_start,%esp
    call setup_idt
    call setup_gdt
    movl $0x10,%eax        # reload all the segment registers
    mov %ax,%ds        # after changing gdt. CS was already
    mov %ax,%es        # reloaded in 'setup_gdt'
    mov %ax,%fs
    mov %ax,%gs
    lss stack_start,%esp
    xorl %eax,%eax
1:    incl %eax        # check that A20 really IS enabled
    movl %eax,0x000000    # loop forever if it isn't
    cmpl %eax,0x100000
    je 1b

linux0.11 80386段相关推荐

  1. 一站式linux0.11内核head.s代码段图表详解

    阅读本文章需要的基础: 计算机组成原理:针对8086,80386CPU架构的计算机硬件体系要有清楚的认知,我们都知道操作系统是用来管理硬件的,那我们就要对本版本的操作系统所依赖的硬件体系有系统的了解, ...

  2. setup.s 解读——Linux-0.11 剖析笔记(三)

    题目:setup.s 解读--Linux-0.11 剖析笔记(三) 更新记录 版本 时间 修订内容 1.0 2018-4-14 增加了"获取显示模式"这一节,AL取值的表格 2.0 ...

  3. setup.s 分析—— Linux-0.11 学习笔记(二)

    更新记录 版本 时间 修订内容 1.0 2018-4-14 增加了"获取显示模式"这一节,AL取值的表格 标题: setup.s 分析-- Linux-0.11 学习笔记(二) 老 ...

  4. Linux0.11内核剖析--内核体系结构

    一个完整可用的操作系统主要由 4 部分组成:硬件.操作系统内核.操作系统服务和用户应用程序,如下图所示: 用户应用程序是指那些字处理程序. Internet 浏览器程序或用户自行编制的各种应用程序: ...

  5. linux0.11磁盘映像制作及其剩余程序阅读注释笔记

    [ 1] linux0.11引导程序阅读注释. [ 2] linux0.11由实模式进入保护模式程序阅读注释 . [ 3] linux0.11护模式初始化程序阅读注释. [ 4] linux0.11主 ...

  6. Linux0.11内核引导启动过程概述

    Linux0.11仅支持x86架构.它的内核引导启动程序在文件夹boot内,共有三个汇编代码文件.按照启动流程依次是: (1)bootsect.s.boot是启动引导的意思,sect即sector,是 ...

  7. c 取地址 虚拟地址 物理地址_通过linux0.11源码理解进程的虚拟地址、线性地址、物理地址...

    进程的地址有三种,分别是虚拟地址(逻辑地址).线性地址.物理地址.在分析之前先讲一下进程执行的时候,地址的解析过程.在保护模式下,段寄存器保存的是段选择子,当进程被系统选中执行时,会把tss和ldt等 ...

  8. Linux0.11小结

    第一部分 基础内容 1.操作系统基础     操作系统是计算机硬件系统与用户程序间重要环节,理解操作系统的原理是编写优秀代码的基础.教课书中阐述的操作系统一般由5部分组成. 一个最简单的操作系统,可以 ...

  9. Linux0.11启动过程

    从开机加电,到执行main函数之前的过程 好吧,这里应该是有执行3个汇编的文件,但是我不太了解.囧 从main函数,到启动OK(即可以响应用户操作了) 这个步骤做了3件事情: 创建进程0,使之具备在主 ...

  10. Linux0.11进程切换和TSS结构

    TSS 全称为task state segment,是指在操作系统进程管理的过程中,进程切换时的任务现场信息.       X86体系从硬件上支持任务间的切换.为此目的,它增设了一个新段:任务状态段( ...

最新文章

  1. 目前研制量子计算机,18个量子比特纠缠究竟是什么水平? 量子计算机离我们还有多远?...
  2. 新入行程序员考虑自己是否明白以下这8件事情
  3. WPF:数据绑定--PropertyChangeNotification属性更改通知
  4. 排序方法整理Java - 冒泡排序、选择排序、插入排序、快速排序
  5. Python基础教程:列表解析
  6. [ios2]苹果iOS 5限制应用本地存储问题 【转】
  7. python实现随机乱序/洗牌
  8. vue选中点击的元素_vue中v-for循环选中点击的元素并对该元素添加样式操作
  9. 【算法】希尔排序 推导方法
  10. python读取txt文件每一行_Python3基础 file for+list 读取txt文本 并 一行一行的输出(低效率)...
  11. 比Magic Leap快一步,HoloLamp做到了裸眼观看全息图
  12. keil MDK uVision 5最新版本下载(含有注册机)
  13. cad2016中选择全图字体怎么操作_在学习CAD的过程中,经常会遇到的10个问题,你遇到过吗...
  14. 2019年微信养号攻略
  15. pandas 常见写法
  16. Derek Sivers:砍掉一切没有惊讶感的内容(译)
  17. React的箭头函数详解
  18. 今天是一位朋友的生日,送给她最美丽的祝福.
  19. 小米盒子打开adb调试模式
  20. 高端加密IC开发常见问题

热门文章

  1. Eclipse学习笔记
  2. Gradle简要教程
  3. Padavan固件添加adbyby去广告功能
  4. 如何在Mac电脑上更改地区或国家?
  5. mysql主从配置(超简单)
  6. 使用c语言打印九九乘法表
  7. 虚拟服务器开启打印端口号,打印机服务器虚拟端口设置方法
  8. 前端对页面中的 checked 选中状态的展示
  9. 软件项目管理流程小结
  10. PHP搭建留言板,用PHP制作留言板_php