现在的位置: 首页 > 综合 > 正文

关于段及相关概念的一些自己的阐述

2013年09月05日 ⁄ 综合 ⁄ 共 2804字 ⁄ 字号 评论关闭

1.
void sched_init(void)
{...
00392    

set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));

00393    

set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
...}
gdt:全局描述符,一共定义256个描述符,GDT本身不是一个段而是线形空间

的一个数据结构。GDT的基地址和长度需要加载进GDTR寄存器。GDT的基地

址要以8个字节对齐获得最佳的运行效率。因为GDT描述符号以8字节为单位


2.结构相关解释
typedef struct desc_struct {
     unsigned long a,b;
 } desc_table;
这里使用两个标准long四字节的存放gdt和ldt。
首先程序运行在虚拟地址上的(实际程序都如此在保护模式下)虚拟地址是

由两部分组成 段选择符号(16)+偏梁量(有效地址32位);通过段选择符号来

访问相应的段描述符号,由段描述符中提供的段基地址,限长,属性及虚拟地

址另外的偏移来构造线形地址。
a.段选择符:是虚拟地址组成部分见上面描述。其中前16位是段的唯一标志

,并且提供了段描述符表GDT中的一个字段的偏移量。具体的组成如下面表

示:
|15-3描述符索引index|2是TI|1-0是RPL|
index:索引值用于寻找对应的描述符表中的具体段
TI: 表示是GDT还是LDT
RPL:类似权限的东西
Remark:段选择符总共提供6个,CS DS SS ES FS GS,其实段描述符信息在访

问的时候由cpu自动通过对16位段选择符的解码来加载到一个所谓的影子寄

存器中,这样速度会非常快。可以直接使用当然对于我们用户可能是看不到

这个寄存器的存在和操作。
b.段描述符表,如同gdt上面的也属于段描述符表的一种,用于提供段选择符

号的最终访问获得存储的段信息。
c:最终的线形地址确立是由段描述符中提供的段基址+虚拟地址的段偏移。

然后再根据分页的方法获得最终的物理地址。

3.描述格式
//这个两个低层调用及最后一个实现宏用于调用设置TSS,LDT段描述孵内容


//n:为描述符的地址,即后面会往n开始到n+7位置中写入一个完整的8个字

节的段描述符。
//add:基地址,即段信息中所保存的段基址
//0x8X是属性描述表示是何种类型的段TSS or LDT or the other
#define  set_tss_desc(n, addr)   _set_tssldt_desc(((char *)

(n)),((int)(addr)),"0x89")
#define  set_ldt_desc(n, addr)   _set_tssldt_desc(((char *)

(n)),((int)(addr)),"0x82")
段描述符的格式
高32位中
|31-24:基地址

31..24|23:G|22:D/B|21:0|20:AVL|19-16:Limit19..16|15:P|14-13:DPL|1

2:S|11-8:TYPE|7-0:基地址23..16|
低32位中
|31-16:base address 15..0|15-0:Segment Limit|

4.
//看具体实现加载段描述符的过程
#define _set_tssldt_desc  (  n,  addr,  type)
//104是段限长,TSS最长104个字节放到低2个字节 15-0:Segment Limit
__asm__ ("movw $104,%1/n/t" /
//ax段基地址的低16位放入 31-16:base address 15..0
    "movw %%ax,%2/n/t" /
//右移16位 ax中变成高16位地址
    "rorl $16,%%eax/n/t" /
//将高16位地址中的低8位放入 7-0:基地址23..16
    "movb %%al,%3/n/t" /
//将type放入|15:P|14-13:DPL|12:S|11-8:TYPE
    "movb $" type ",%4/n/t" /
//|23:G|22:D/B|21:0|20:AVL|19-16:Limit19..16|全部置零
    "movb $0x00,%5/n/t" /
//高基地址8位放入最高位
    "movb %%ah,%6/n/t" /
    "rorl $16,%%eax" /
    //输入规则%0指定eax存放addr段基址,%1~%6均为指定m内存地址分别

为n,n+2,n+4...地址内容
    ::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), /
     "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) /
    )
5.加载任务TSS,LDT等段选择符和段描述符加载到任务寄存器中的实现
n:第n号任务或局部表的段偏移
//ltr或者lldt命令加载%%ax的段选择符,此段选择可以看到是由低层的一

个宏_TSS和_LDT来返回来实现。
#define ltr  (n)     __asm__("ltr %%ax"::"a" (_TSS(n)))
#define lldt (n)     __asm__("lldt %%ax"::"a" (_LDT(n)))
//_TSS _LDT将返回一个当前任务信息或者局部描述符的对应段偏移是相对

于GDT中的偏移,这里使用一个简单的计算公式 n*(8+8)+起始偏移数目*8,

这里我们要看下这些TSS和LDT在GDT中位置。
#define _TSS  (n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
#define _LDT  (n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
6.GDT具体分布
0 |NULL   |  不可以使用的GDT(0)
1 |系统代码段CS |  操作系统的自己使用
2 |系统数据段DS |  操作系统的数据段
3 |系统调用 |  一些调用的信息符
4 |TSS0  |  这里很容易看到FIRST_TSS_ENTRY=4
5 |LDT0  |  这里很容易看到FIRST_LDT_ENTRY=5
....
每个域都是8个字节所以第n个TSS地址 = TSS0的基址+ (n*LSSi-LSSi-1)
= FIRST_TSS_ENTRY*8 + n*(8+8)
= FIRST_TSS_ENTRY<<3+n<<4
同理可以推导出LDT的地址进行加载

至此基本清楚段选择符 描述符 段一些关系和如何设置的操作
#define str  (n)  
__asm__("str %%ax/n/t" /
    "subl %2,%%eax/n/t" /
    "shrl $4,%%eax" /
    :"=a" (n) /
    :"a" (0),"i" (FIRST_TSS_ENTRY<<3))

 

抱歉!评论已关闭.