Linux0.11的地址有3类地址需要区分清楚:1.程序(进程)的虚拟地址和逻辑地址虚拟地址(virtualaddress)指程序产生的有段选择符和段内偏移两部分组成的地址。一个程序的虚拟地址空间有GDT映射的全地址空间和LDT映射的局部地址空间组成。逻辑地址(logicaladdress)指程序产生的段内偏移地址。应用程序只与逻辑地址打交道,分段分页对应用程序来说是透明的。也就是说C语言中的&,汇编语言中的符号地址,C中嵌入式汇编的”m”对应的都是逻辑地址。2.CPU的线性地址线性地址(linearaddress)是逻辑地址到物理地址变换的中间层,是处理器可寻址空间的地址。程序代码产生的逻辑地址加上段基地址就产生了线性地址。3.实际物理内存地址物理地址(physicaladdress)是CPU外部地址总线上的寻址信号,是地址变换的最终结果,一个物理地址始终对应实际内存中的一个存储单元。对80386保护模式来说,如果开启分页机制,线性地址经过页变换产生物理地址。如果没有开启分页机制,线性地址直接对应物理地址。页目录表项、页表项对应都是物理地址。Linux0.11的内核数据段,内核代码段基地址都是0,所以对内核来说,逻辑地址就是线性地址。又因为1个页目录表和4个页表完全映射16M物理内存,所以线性地址也就是物理地址。故对linux0.11内核来说,逻辑地址,线性地址,物理地址重合。与80386段相关的宏定义set_seg_desc/*****************************************************************************//*功能:设置存储段描述符,把指定属性的段描述符放入gate_addr处*//*参数:gate_addr段描述符的目的地址*//*type描述符中类型域,具体见80386基础一节中的表格*//*dpl描述符中特权级*//*base段基地址,这是线性地址*//*limit段限长*//*返回:(无)*//*****************************************************************************/#define_set_seg_desc(gate_addr,type,dpl,base,limit){\07152331限长19..16段限长(LIMIT)15..0AVL0DG段基地址(BASE)15..0段基地址(BASE)31..24段基地址(BASE)23..16typeP1DPL3239475564//把段描述符的第4-7字节放入gate_addr处*((gate_addr)+1)=((base)&0xff000000)|\//base的31..24位放入gate_addr的31..24位(((base)&0x00ff0000)16)|\//base的23..16位放入gate_addr的7..0位((limit)&0xf0000)|\//limit的19..16位放入gate_addr的19..16位((dpl)13)|\//dpl放入gate_addr的14..13位(0x00408000)|\//把P位和D位设置位1,G置为0((type)8);\//type放入gate_addr的11..8位//把段描述符的第0-3字节放入gate_addr+1处*(gate_addr)=(((base)&0x0000ffff)16)|\//base的15..0放入gate+1的31..15位((limit)&0x0ffff);}//limit的15..0位放入gate+1的15..0位set_tssldt_desc/*****************************************************************************//*功能:设置系统段描述符,把指定属性的段描述符放入GDT中*//*表项n对应的地址处,*//*参数:nGDT中表项n对应的地址*//*addr系统段的基地址,这是一个线性地址*//*type描述符中类型域,具体见80386基础一节中的表格*//*0x89表示386TSS段描述符,0x82表示LDT段*//*这里8是为了设置P位为1*//*返回:(无)*//*****************************************************************************///%0寄存器eaxaddr//%1-%6物理地址符号项n地址-n+7的地址#define_set_tssldt_desc(n,addr,type)\__asm__(movw$104,%1\n\t\//把TSS的限长104字节放入n地址处,//这样ldt的限长也定为104,这没有关系,因为linux0.11//中一个任务的ldt只有3个表项movw%%ax,%2\n\t\//把addr的15..0位(在ax中)放入n+2处rorl$16,%%eax\n\t\//把addr的高16位(eax中)放入ax中movb%%al,%3\n\t\//addr的23..16位放入n+4中movb$type,%4\n\t\//把type字段放入n+5中movb$0x00,%5\n\t\//把G置为0,说明粒度是字节。//因为限长定死为104,所以高位肯定是007152331限长19..16段限长(LIMIT)15..0AVL0XG段基地址(BASE)15..0段基地址(BASE)31..24段基地址(BASE)23..16typeP0DPL3239475564movb%%ah,%6\n\t\//把addr的31..24位放入n+7中rorl$16,%%eax\//eax清0::a(addr),m(*(n)),m(*(n+2)),m(*(n+4)),\m(*(n+5)),m(*(n+6)),m(*(n+7))\)#defineset_tss_desc(n,addr)_set_tssldt_desc(((char*)(n)),addr,0x89)#defineset_ldt_desc(n,addr)_set_tssldt_desc(((char*)(n)),addr,0x82)对n,addr地址的说明set_tss_desc()和set_ldt_desc()在两个地方被调用:sched_init()和copy_process()中。在sched_init()中:set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));在copy_process()中set_tss_desc(gdt+(nr1)+FIRST_TSS_ENTRY,&(p-tss));set_ldt_desc(gdt+(nr1)+FIRST_LDT_ENTRY,&(p-ldt))这里传入的地址都是逻辑地址,但是因为3种地址重合,所以也没问题。内核空间、用户空间之间的数据传输内核空间数据段的选择符为0x10,用户空间数据段选择符为0x17。内核空间、用户空间之间的数据传输,是段间数据传输。C语言中的赋值语句编译成汇编后,“=”两边的变量默认段选择符都是DS,因此只能用于同一段内数据传输。在segment.h中定义了一系列用于内核空间和用户空间传输数据的函数。从用户空间取得数据的函数中,mov指令的源操作数段寄存器都明确指出是fs,向用户空间写数据的函数中,mov指令的目的操作数段寄存器都是fs。当系统调用发生时,int0x80处理函数会把fs设成用户数据段选择符(0x17),参见中断异常处理和系统调用一章。下面分析一组对byte操作的函数,其他的对word和long操作的函数与之类似。get_fs_byte()//功能:从用户空间中addr地址处取出一个字节//参数:addr用户空间中的逻辑地址//返回:fs:[addr]处的一个字节内容externinlineunsignedcharget_fs_byte(constchar*addr){unsignedregisterchar_v;//addr是逻辑地址,也就是用户数据段内的偏移。//而当前数据段为内核数据段,所以要写成fs:[addr],这是虚拟地址__asm__(movb%%fs:%1,%0:=r(_v):m(*addr));return_v;}put_fs_byte()//功能:向用户空间中addr地址处写一个字节的内容//参数:val要写入的数据//addr用户空间中的逻辑地址//返回:(无)externinlinevoidput_fs_byte(charval,char*addr){//addr是相对于用户数据段的偏移,而当前数据段为内核数据段//所以要写成fs:[addr]的形式__asm__(movb%0,%%fs:%1::r(val),m(*addr));}Linux0.11相关进程数据结构主要有4个数据结构task_union(sched.c第53行)//这实际上是一页内存,页面低端头部放的是task_struct(进程控制块)结构,页面//其他部分当作进程的内核态堆栈使用uniontask_union{structtask_structtask;charstack[PAGE_SIZE];};task[NR_TASKS](sched.c第65行)//task_struct指针数组,每个进程的task_struct指针都保存在这个数组中。虽然指针类型是//task_struct*,但实际上指向的是一页内存,其中包括了进程的内核态堆栈。//task[0]以及被手工初始化成init_taskstructtask_struct*task[NR_TASKS]={&(init_task.task),};Task_struct内核态堆栈pagePage+4Ktss_struct(sched.h第53行)//任务段数据,与80386的tss结构对应structtss_struct{longback_link;/*16highbitszero*/longesp0;longss0;/*16highbitszero*/longesp1;longss1;/*16highbitszero*/longesp2;longss2;/*16highbitszero*/longcr3;longeip;longeflags;longeax,ecx,edx,ebx;longesp;longebp;longesi;longedi;longes;/*16highbitszero*/longcs;/*16highbitszero*/longss;/*16highbitszero*/longds;/*16highbitszero*/longfs;/*16highbitszero*/longgs;/*16highbitszero*/longldt;/*16highbitszero*/longtrace_bitmap;/*bits:trace0,bitmap16-31*/structi387_structi387;};task_struct(sched.c第80行)//进程控制块structtask_struct{/*-----------------------thesearehardcoded-don'ttouch-----------------------*/longstate;//进程运行状态(-1不可运行,0可运行,0以停止)longcounter;//任务运行时间片,递减到0是说明时间片用完longpriority;//任务运行优先数,刚开始是counter=prioritylongsignal;//任务的信号位图,信号值=偏移+1stru