最近在阅读Linux内存管理时,突然对GDT(Glocal Descriptor Table)和LDT(Local Descriptor Table)产生了一些疑问。他们在内存是怎样存放的呢?程序在运行时又是如何对GDT和LDT产生了影响?
以下配图均来自《Linux内核完全注释(1.9.5版)》,所有的内容也都是基于Linux内核0.11版。其他Linux版本则需查看相应的代码进行分析
在Linux内核0.11中,对GDT和LDT的声明和配置如下:
从上图可以看到LDTR指向的是GDT中的一部分,确切的说是指向了GDT的第五个描述符,而从这个描述符开始,包含了LDT。
从相应的内核代码中,我们看到在task_struct的声明中,有如下内容:
struct task_struct {
...
struct desc_struct ldt[3];
struct tss_struct tss;
};
这说明ldt是和每个task有关。每当需要创建新的process时,就需要在内存中把一块相应的区域划分给这个process的LDT:
int copy_process(int nr, ...)
{
struct task_struct *p;
p=(struct task_struct *)get_free_page();
...
if (copy_mem(nr,p)) {
//copy memory error will comes, here, free memory
free_page((long) p);
return -EAGAIN;
}
...
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY, &(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, &(p->ldt));
p->state=TASK_RUNNING;
return last_pid;
}
在上述程序中set_ldt_desc函数的作用就是把p->ldt的地址赋值给(gdt+(nr<<1)+FIRST_LDT_ENTRY)这个内存,也即让(gdt+(nr<<1)+FIRST_LDT_ENTRY)的内容指向p->ldt,这样就完成了GDT对LDT的索引。
nr是程序分配的新建任务的任务号,而return的是新建任务的进程号,这两个是不一样的。
变量gdt表示GDT在内存中的地址,在文件head.s中定义的。
FIRST_LDT_ENTRY是一个宏定义,表示数字5。从本文开头的图中可以看到,GDT的开始4项已经被占用。从第五项开始,保存着TSS(任务状态段)描述符和LDT(局部描述符表)描述符,所以任务nr的LDT描述符的地址相对于GDT地址是nr乘以2加上最初的偏移值,也即(nr<<1)+FIRST_LDT_ENTRY