任务寄存器选择器存储的值:索引还是字节偏移?Linux 0.11内核疑问
关于Linux 0.11中任务寄存器选择器的疑问解答
首先明确结论:任务寄存器(TR)中存储的是段选择器,但Linux 0.11的宏计算出来的字节偏移值,刚好等于对应的选择器数值,这是由GDT结构和选择器的编码规则决定的。
拆解宏的含义
先看<sched.h>里的三个宏:
#define FIRST_TSS_ENTRY 4 #define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3)) #define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n)))
FIRST_TSS_ENTRY << 3:GDT中每个段描述符占8字节(2^3),第4个条目(索引4)的字节偏移是4*8=32,这是第一个任务TSS在GDT中的起始偏移。(n << 4):Linux 0.11中每个任务对应GDT里的两个条目(TSS+LDT),每个条目8字节,总共16字节(2^4),所以第n个任务的TSS相对于第一个TSS的偏移是n*16。_TSS(n)的结果就是第n个任务的TSS描述符在GDT中的字节偏移。
为什么字节偏移等于选择器数值?
根据Intel 386手册,段选择器的结构是:
- 高13位:段描述符在GDT/LDT中的索引
- 第2位:TI位(0=使用GDT,1=使用LDT)
- 低2位:RPL(请求特权级)
对于内核级的TSS选择器,TI=0(用GDT),RPL=0(内核特权级),所以选择器的低3位都是0。而GDT中描述符的字节偏移 = 索引 * 8,对应的选择器数值 = 索引 << 3 | 0 | 0 = 字节偏移。
也就是说,当选择器的低3位为0时,选择器的数值和描述符在GDT中的字节偏移是完全相等的。Linux 0.11的宏直接用字节偏移作为选择器加载到TR,本质上是利用了这个等价关系,简化了计算。
总结
TR中存储的确实是段选择器,不是字节偏移。但在Linux 0.11的场景下,由于选择器的TI和RPL都为0,选择器的数值刚好等于对应TSS描述符在GDT中的字节偏移,所以宏里直接计算字节偏移来代替选择器数值是可行的。
内容的提问来源于stack exchange,提问作者Li-Guangda
相关产品推荐
相关产品推荐

