Linux内核设计的艺术-设备环境初始化及激活进程0

      代码路径:init/main.c

...
#define DRIVE_INFO (*(struct drive_info *)0x90080)
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
...
struct drive_info 
{
  char dummy[32]; 
} drive_info;

void main(void)		
{
 	ROOT_DEV = ORIG_ROOT_DEV;
 	drive_info = DRIVE_INFO;
        ...
}
      代码路径:fs/super.c

/* this is initialized in init/main.c */
int ROOT_DEV = 0;

         参考此图,可知ROOT_DEV存的2个字节(unsigned short)的根设备号,定义在fs/super.c,29行,这个值为0。drive_info填充了32个字节的硬盘参数。

      



       预备知识

<<20 或 >>20 相当于乘或除以 1 MB,
<<12 或 >>12 相当于乘或除以 4 KB(联想到页),
<<10 或 >>10 相当于乘或除以 1 KB

       代码路径:init/main.c

...
#define EXT_MEM_K (*(unsigned short *)0x90002)  //从1MB开始的扩展内存(KB)数
...

void main(void)		
{
 	...
	memory_end = (1<<20) + (EXT_MEM_K<<10); //1MB+扩展内存(MB),即内存总数
	memory_end &= 0xfffff000;     按页的倍数取整,忽略内存末端不足一页的部分
	if (memory_end > 16*1024*1024)
		memory_end = 16*1024*1024; //执行到此,memory_end为16MB
	if (memory_end > 12*1024*1024) 
		buffer_memory_end = 4*1024*1024;//执行到此,buffer_memory_end为4MB
	else if (memory_end > 6*1024*1024)
		buffer_memory_end = 2*1024*1024;
	else
		buffer_memory_end = 1*1024*1024;
	main_memory_start = buffer_memory_end;//执行到此,main_memory_start为4MB
        ...
}
      如下图所示,主内存结束(memory_end)为0xFFFFFF,主内存开始(main_memory_start)此时为0x3FFFFF,高速缓冲区末端(buffer_memory_end)为0x3FFFFF。此时还没有虚拟盘。


       



       代码路径:init/main.c

void main(void)		
{
 	...
#ifdef RAMDISK
	main_memory_start += rd_init(main_memory_start, RAMDISK*1024);//主内存从0x5FFFFF开始
#endif
        ...
}
       代码路径:kernel/blk_drv/ll_rw_blk.c

...
struct blk_dev_struct blk_dev[NR_BLK_DEV]= {
    { NULL, NULL },              /* no_dev */
    { NULL, NULL },              /* dev mem */
    { NULL, NULL },              /* dev fd */
    { NULL, NULL },              /* dev hd */
    { NULL, NULL },              /* dev ttyx */
    { NULL, NULL },              /* dev tty */
    { NULL, NULL }               /* dev lp */
};
...
       代码路径:kernel/blk_drv/blk.h

...
#define NR_BLK_DEV 7
...
struct blk_dev_struct {
    void (*request_fn)(void);
    struct request * current_request;
};
...
#if (MAJOR_NR== 1)
...
#define DEVICE_REQUEST do_rd_request
...
       代码路径:kernel/blk_drv/ramdisk.c

...
#define MAJOR_NR 1       

...
char	*rd_start;
int	rd_length = 0;
...
long rd_init(long mem_start, int length)
{
	int	i;
	char	*cp;

	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;//内核能够通过调用 do_rd_request 函数处理与虚拟盘相关的请求项操作
	rd_start = (char *) mem_start;
	rd_length = length;
	cp = rd_start;   //从0x3FFFFF开始
	for (i=0; i < length; i++)//共2MB
		*cp++ = '\0';    //0x3FFFFF~0x5FFFFF都是虚拟盘
	return(length);
}
        如上图所示, 主内存结束(memory_end)为0xFFFFFF,主内存开始(main_memory_start)此时为0x5FFFFF,高速缓冲区末端(buffer_memory_end)为0x3FFFFF,虚拟盘从0x3FFFFF~0x5FFFFF。



      代码路径:init/main.c

void main(void)		
{
 	...
	mem_init(main_memory_start,memory_end);
        ...
}
      代码路径:mm/memory.c

...
#define LOW_MEM 0x100000                             //1 MB
#define PAGING_MEMORY (15*1024*1024)
#define PAGING_PAGES (PAGING_MEMORY>>12)             //15 MB 的页数
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
#define USED 100
...
static long HIGH_MEMORY= 0;
...
static unsigned char mem_map [PAGING_PAGES]= {0,};
...
void mem_init(long start_mem, long end_mem)
{
    int i;
    HIGH_MEMORY= end_mem;
    for (i=0;i<PAGING_PAGES;i++)
         mem_map[i]= USED;               //所有的页都设置为USED    
    i= MAP_NR(start_mem);                //虚拟盘开始的页标
    end_mem -= start_mem;
    end_mem >>= 12;                      //虚拟盘后总共的页数
    while (end_mem-->0)
         mem_map[i++]=0;                 //虚拟盘后所有的页设置为空闲      
}

      之所以这样设置,是因为从内核结束到4MB之前,是缓冲区申请的页面,都标志位已经使用。从4MB到6MB是虚拟盘所占用,不用于申请页面。

       形成的结果,如下图所示:

   



      代码路径:init/main.c

void main(void)		
{
 	...
	trap_init();
        ...
}
      代码路径:kernel/traps.c

void trap_init(void)
{
    int i;
    set_trap_gate(0,÷_error);// 除零错误
    set_trap_gate(1,&debug);      // 单步调试
    set_trap_gate(2,&nmi);        // 不可屏蔽中断
    set_system_gate(3,&int3);     /* int3-5 can be called from all */
    set_system_gate(4,&overflow); // 溢出
    set_system_gate(5,&bounds); // 边界检查错误
    set_trap_gate(6,&invalid_op); // 无效指令
                                              第 2 章 设备环境初始化及激活进程 0 53
  set_trap_gate(7,&device_not_available);       // 无效设备
  set_trap_gate(8,&double_fault);               // 双故障
  set_trap_gate(9,&coprocessor_segment_overrun);// 协处理器段越界
  set_trap_gate(10,&invalid_TSS);               // 无效 TSS
  set_trap_gate(11,&segment_not_present);       // 段不存在
  set_trap_gate(12,&stack_segment);             // 栈异常
  set_trap_gate(13,&general_protection);        // 一般性保护异常
  set_trap_gate(14,&page_fault);                // 缺页
  set_trap_gate(15,&reserved);                  // 保留
  set_trap_gate(16,&coprocessor_error);         // 协处理器错误
  for (i=17;i<48;i++)                           // 都先挂接好,中断服务程序函数名初
                                                // 始化为保留
       set_trap_gate(i,&reserved);
  set_trap_gate(45,&irq13);                     // 协处理器
  outb_p(inb_p(0x21)&0xfb,0x21);                // 允许 IRQ2 中断请求
  outb(inb_p(0xA1)&0xdf,0xA1);                  // 允许 IRQ13 中断请求
  set_trap_gate(39,¶llel_interrupt);        // 并口
}

    代码路径:include\asm\system.h

    ...
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__("movw %%dx,%%ax\n\t" \                     // 将 edx 的低字赋值给 eax 的低字
         "movw %0,%%dx\n\t" \                      //%0 对应第二个冒号后的第 1 行的 "i"
         "movl %%eax,%1\n\t" \                     //%1 对应第二个冒号后的第 2 行的 "o"
         "movl %%edx,%2" \                         //%2 对应第二个冒号后的第 3 行的 "o"
         : \                                       // 这个冒号后面是输出,下面冒号后面
                                                   // 是输入
    : "i" ((short) (0x8000 + (dpl<<13) + (type<<8))), \ // 立即数
           "o" (*((char *) (gate_addr))), \              // 中断描述符前 4 个字节的地址
           "o" (*(4 + (char *) (gate_addr))), \          // 中断描述符后 4 个字节的地址
           "d" ((char *) (addr)),"a" (0x00080000))       //"d" 对应 edx,"a" 对应 eax
    ...
#define set_intr_gate(n,addr) \
	_set_gate(&idt[n],14,0,addr)

#define set_trap_gate(n,addr) \
	_set_gate(&idt[n],15,0,addr)

#define set_system_gate(n,addr) \
	_set_gate(&idt[n],15,3,addr)
       首先看下图

 

        Selector为0x0008,Offset为中断函数的偏移。set_trap_gate,P为1,DPL为00,TYPE为F。set_intr_gate,P为1,DPL为00,TYPE为E。set_system_gate,P为1,DPL为11,TYPE为F。



       代码路径:init/main.c

void main(void)		
{
 	...
	blk_dev_init();
        ...
}
       代码路径:kernel/blk_dev/blk.h

...
#define NR_REQUEST 32
struct request {
    int dev;       /* -1 if no request */
    int cmd;       /* READ or WRITE */
    int errors;
    unsigned long sector;
    unsigned long nr_sectors;
    char * buffer;
    struct task_struct * waiting;
    struct buffer_head * bh;
    struct request * next;        // 说明 request 可以构成链表
};
...
       代码路径:kernel/blk_dev/ll_rw_block.c

...
struct request request[NR_REQUEST]; // 数组链表
...
void blk_dev_init(void)
{
    int i;
    for (i=0;i<NR_REQUEST;i++) {
         request[i].dev= -1;        // 设置为空闲
         request[i].next= NULL;     // 互不挂接
    }
}



       代码路径:init/main.c

void main(void)		
{
 	...
	tty_init();
        ...
}
       代码路径:kernel/chr_dev/tty_io.c

void tty_init(void)
{
    rs_init();
    con_init()
}
      代码路径:kernel/chr_dev/serial.c

void rs_init(void)
{
    set_intr_gate(0x24,rs1_interrupt); // 设置串行口 1 中断,参看上图
    set_intr_gate(0x23,rs2_interrupt); // 设置串行口 2 中断
    init(tty_table[1].read_q.data);    // 初始化串行口 1
    init(tty_table[2].read_q.data);    // 初始化串行口 2
    outb(inb_p(0x21)&0xE7,0x21);       // 允许 IRQ3,IRQ4
}
      代码路径:kernel/chr_dev/console.c

...
void con_init(void)
{
  ...
  set_trap_gate(0x21,&keyboard_interrupt);// 设置键盘中断,参看 2.5 节
  outb_p(inb_p(0x21)&0xfd,0x21);// 允许 IRQ1
  a=inb_p(0x61);
  outb_p(a|0x80,0x61); // 禁止键盘工作
  outb(a,0x61);         // 再允许键盘工作
}
     省略了很多初始化显示设备的工作,这部分主要是通过读0x90000~0x9000C的内容来初始化显示器,初始化完成后,原来的0x90000~0x901FE就没有用了,下面会被用作高速缓冲区。



     代码路径:init/main.c

void main(void)		
{
 	...
	time_init();
        ...
}

     time_init()的代码省略,从机器中读取开机时间。



     代码路径:init/main.c

void main(void)		
{
 	...
	sched_init();
        ...
}

     代码路径:init/linux/sched.h

...
struct tss_struct {
	long	back_link;	/* 16 high bits zero */
	long	esp0;
	long	ss0;		/* 16 high bits zero */
	long	esp1;
	long	ss1;		/* 16 high bits zero */
	long	esp2;
	long	ss2;		/* 16 high bits zero */
	long	cr3;
	long	eip;
	long	eflags;
	long	eax,ecx,edx,ebx;
	long	esp;
	long	ebp;
	long	esi;
	long	edi;
	long	es;		/* 16 high bits zero */
	long	cs;		/* 16 high bits zero */
	long	ss;		/* 16 high bits zero */
	long	ds;		/* 16 high bits zero */
	long	fs;		/* 16 high bits zero */
	long	gs;		/* 16 high bits zero */
	long	ldt;		/* 16 high bits zero */
	long	trace_bitmap;	/* bits: trace 0, bitmap 16-31 */
	struct i387_struct i387;
};

struct task_struct {
/* these are hardcoded - don't touch */
	long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	long counter;
	long priority;
	long signal;
	struct sigaction sigaction[32];
	long blocked;	/* bitmap of masked signals */
/* various fields */
	int exit_code;
	unsigned long start_code,end_code,end_data,brk,start_stack;
	long pid,father,pgrp,session,leader;
	unsigned short uid,euid,suid;
	unsigned short gid,egid,sgid;
	long alarm;
	long utime,stime,cutime,cstime,start_time;
	unsigned short used_math;
/* file system info */
	int tty;		/* -1 if no tty, so it must be signed */
	unsigned short umask;
	struct m_inode * pwd;
	struct m_inode * root;
	struct m_inode * executable;
	unsigned long close_on_exec;
	struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
	struct desc_struct ldt[3];
/* tss for this task */
	struct tss_struct tss;
};

/*
 *  INIT_TASK is used to set up the first task table, touch at
 * your own risk!. Base=0, limit=0x9ffff (=640kB)
 */
#define INIT_TASK \
/* state etc */	{ 0,15,15, \       //就绪态,15个时间片
/* signals */	0,{{},},0, \
/* ec,brk... */	0,0,0,0,0,0, \
/* pid etc.. */	0,-1,0,0,0, \      //进程0
/* uid etc */	0,0,0,0,0,0, \
/* alarm */	0,0,0,0,0,0, \
/* math */	0, \
/* fs info */	-1,0022,NULL,NULL,NULL,0, \
/* filp */	{NULL,}, \
	{ \
		{0,0}, \
/* ldt */	{0x9f,0xc0fa00}, \
		{0x9f,0xc0f200}, \
	}, \
/*tss*/	{0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\
	 0,0,0,0,0,0,0,0, \
	 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \
	 _LDT(0),0x80000000, \
		{} \
	}, \
}
...
#define FIRST_TSS_ENTRY 4
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n)))
#define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n)))
...
       代码路径:include/asm/system.h

...
#define _set_tssldt_desc(n,addr,type) \ // 嵌入汇编参看 trap_init 的注释
__asm__ ("movw $104,%1\n\t" \          // 将 104,即 1101000 存入描述符的第 1、2 字节
          "movw %%ax,%2\n\t" \         // 将 tss 或 ldt 基地址的低 16 位存入描述符的第
                                       //3、4 字节
          "rorl $16,%%eax\n\t" \       // 循环右移 16 位,即高、低字互换
          "movb %%al,%3\n\t" \         // 将互换完的第 1 字节,即地址的第 3 字节存入第 5 字节
          "movb $" type ",%4\n\t" \    // 将 0x89 或 0x82 存入第 6 字节
          "movb $0x00,%5\n\t" \        // 将 0x00 存入第 7 字节
                                             
           "movb %%ah,%6\n\t" \    // 将互换完的第 2 字节,即地址的第 4 字节存入第 8 字节
           "rorl $16,%%eax" \      // 复原 eax
           ::"a" (addr), "m" (*(n)), "m" (*(n + 2)), "m" (*(n + 4)), \
           "m" (*(n + 5)), "m" (*(n + 6)), "m" (*(n + 7)) \
          //"m" (*(n)) 是 gdt 第 n 项描述符的地址开始的内存单元
          //"m" (*(n + 2)) 是 gdt 第 n 项描述符的地址向上第 3 字节开始的内存单元
          // 其余依此类推
           )
//n :gdt 的项值,addr :  tss 或 ldt 的地址,0x89 对应 tss,0x82 对应 ldt
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82")
...

      代码路径:/linux/include/linux/head.h

...
typedef struct desc_struct {
        unsigned long a,b;
} desc_table[256];
... 

      代码路径:kernel/sched.c

...
#define LATCH (1193180/HZ)        // 每个时间片的振荡次数
...
union task_union {               // task_struct 与内核栈的共用体
    struct task_struct task;
    char stack[PAGE_SIZE];       // PAGE_SIZE 是 4 KB
};
static union task_union init_task= {INIT_TASK,};// 进程 0 的 task_struct
...
// 初始化进程槽 task[NR_TASKS] 的第一项为进程 0,即 task[0] 为进程 0 占用
struct task_struct * task[NR_TASKS]= {&(init_task.task), };

...
void sched_init(void)
{
    int i;
    struct desc_struct * p;
         ...
         set_tss_desc(gdt + FIRST_TSS_ENTRY,&(init_task.task.tss));// 设置 TSS0
         set_ldt_desc(gdt + FIRST_LDT_ENTRY,&(init_task.task.ldt));// 设置 LDT0
         p= gdt + 2+FIRST_TSS_ENTRY; // 从 GDT 的 6 项,即 TSS1 开始向上全部清零,并且将进程槽从
         for(i=1;i<NR_TASKS;i++) {     //1 往后的项清空。0 项为进程 0 所用  NR_TASKS=64
              task[i]= NULL;
              p->a=p->b=0;
              p++;
              p->a=p->b=0;
              p++;
         }
    /* Clear NT, so that we won't have troubles with that later on */
         ...
         ltr(0);       // 重要!将 TSS 挂接到 TR 寄存器
         lldt(0);      // 重要!将 LDT 挂接到 LDTR 寄存器
	outb_p(0x36,0x43);    /* binary, mode 3, LSB/MSB, ch 0 */// 设置定时器
	outb_p(LATCH & 0xff , 0x40); /* LSB */      // 每 10 毫秒一次时钟中断
	outb(LATCH >> 8 , 0x40);      /* MSB */
	set_intr_gate(0x20,&timer_interrupt);       // 重要!设置时钟中断,进程调度的基础
	outb(inb_p(0x21)& ~ 0x01,0x21);             // 允许时钟中断
	set_system_gate(0x80,&system_call);         // 重要!设置系统调用总入口

}
...

      LDT如下图所示:


        {0x9f,0xc0fa00}被分解为上面的格式,那就是

        00               c0                    fa           00                 00               00               00              9f

       第一个LDT数据全为0

    第二个LDT数据如下:

    Segment Base为0x0

    Segment Limit为159*4K=636K

    G=1 表示界限粒度为4K 字节

    D=1 表示是32位

    P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中

    DPL为11,表示用户态

    DT=1,存储段描述符

    TYPE为a,存储段,执行/读

    第三个LDT数据只是TYPE为2,数据段,读/写

         

       

         set_tss_desc(gdt + FIRST_TSS_ENTRY,&(init_task.task.tss));// 设置 TSS0

 

      Segment Base:tss0的基地址

      Segment Limit为104

      G=0   D=0  AVL = 0   P=1   DPL=00   

      DT=0 为系统段描述符

      TYPE为9,可用386TSS

      set_ldt_desc(gdt + FIRST_LDT_ENTRY,&(init_task.task.ldt));// 设置 LDT0

      和上面的主要区别是

      TYPE为2,LDT

      ltr(0)

      

      LDTR选择器为4<<3=0x20,TI=0 RPL=00

      TR选择器为5<<3=0x28,TI=0  RPL=00

      高速缓存寄存器中存的就是上面刚刚说的系统段描述符

      其中,

              p->a=p->b=0;//清TSS
              p++;
              p->a=p->b=0;//清LDT
              p++;
     此段代码的意思是把TSS1,LDT1,TSS2,LDT2.........TSS63,LDT63全部清零



      代码路径:init/main.c

void main(void)		
{
 	...
	buffer_init(buffer_memory_end);//0x3FFFFF
        ...
}

       代码路径:linux/include/linux/fs.h

...
struct buffer_head {
        char * b_data;                  /* pointer to data block (1024 bytes) */
        unsigned long b_blocknr;        /* block number */
        unsigned short b_dev;           /* device (0 = free) */
        unsigned char b_uptodate;
        unsigned char b_dirt;           /* 0-clean,1-dirty */
        unsigned char b_count;          /* users using this block */
        unsigned char b_lock;           /* 0 - ok, 1 -locked */
        struct task_struct * b_wait;
        struct buffer_head * b_prev;
        struct buffer_head * b_next;
        struct buffer_head * b_prev_free;
        struct buffer_head * b_next_free;
};
...

       代码路径:fs/buffer.c

...
struct buffer_head * start_buffer= (struct buffer_head *) &end;
struct buffer_head * hash_table[NR_HASH];
static struct buffer_head * free_list;
...
void buffer_init(long buffer_end)
{
    struct buffer_head * h= start_buffer;
    void * b;
    int i;
    if (buffer_end== 1<<20)
         b= (void *) (640*1024);
    else
         b= (void *) buffer_end;
//h、b 分别从缓冲区的低地址端和高地址端开始,每次对进 buffer_head、缓冲块各一个
// 忽略剩余不足一对 buffer_head、缓冲块的空间
    while ( (b -= BLOCK_SIZE) >= ((void *) (h + 1)) ) {
         h->b_dev= 0;
         h->b_dirt= 0;
         h->b_count= 0;
         h->b_lock= 0;
         h->b_uptodate= 0;
         h->b_wait= NULL;
         h->b_next= NULL;         // 这两项初始化为空,后续的使用将与 hash_table 挂接
         h->b_prev= NULL;
         h->b_data= (char *) b; // 每个 buffer_head 关联一个缓冲块
         h->b_prev_free= h-1;     // 这两项使 buffer_head 分别与前、
         h->b_next_free= h + 1; // 后 buffer_head 挂接,形成双向链表,因为地址本身就放在那,虽然还没有后一项,也能提前链接上
         h++;
         NR_BUFFERS++;
         if (b== (void *) 0x100000)      // 避开 ROMBIOS&VGA
               b= (void *) 0xA0000;
    }
    h--;//多加了一个h,要减去
    free_list= start_buffer;             // free_list 指向第一个 buffer_head
    free_list->b_prev_free= h;           // 使 buffer_head 双向链表,第一个h的前一个原来没有地址
    h->b_next_free= free_list;           // 形成双向环链表,最后一个h的后一个没有地址
    for (i=0;i<NR_HASH;i++)              // 清空 hash_table[307]
         hash_table[i]=NULL;
}

先看下图:



       形成双向链表


         start_buffer是内核system模块结束的位置,buffer_end为0x3FFFFF,从内核的末端及buffer_end同时开始,方向相对增长、配对 地做出 buffer_head、缓冲块,直到不足一对 buffer_head、缓冲块



       代码路径:init/main.c

...
#define MAJOR_NR 3
...
void main(void)		
{
 	...
	hd_init();
        ...
}
      代码路径: kernel/blk_drv/hd.c

void hd_init(void)
{
    blk_dev[MAJOR_NR].request_fn= DEVICE_REQUEST;// 挂接 do_hd_request()
    set_intr_gate(0x2E,&hd_interrupt);   // 设置硬盘中断
    outb_p(inb_p(0x21)&0xfb,0x21);       // 允许 8259A 发出中断请求
    outb(inb_p(0xA1)&0xbf,0xA1);         // 允许IRQ14
}


       

       代码路径:init/main.c

...
#define MAJOR_NR 2
...
void main(void)		
{
 	...
	floppy_init();
        ...
}
      代码路径: kernel/floppy.c

    ...
void floppy_init(void)
{
    blk_dev[MAJOR_NR].request_fn= DEVICE_REQUEST; // 挂接 do_fd_request()
    set_trap_gate(0x26,&floppy_interrupt);         // 设置软盘中断
    outb(inb_p(0x21)& ~ 0x40,0x21);               // 允许IRQ6
}


      代码路径:init/main.c   开中断

void main(void)
{
    ...
    sti();
    ...
}
   

      代码路径:init/main.c

void main(void)		
{
 	...
	move_to_user_mode();
        ...
}
      代码路径:include/system.h

#define move_to_user_mode() \      // 模仿中断硬件压栈,顺序是 ss、esp、eflags、cs、eip
__asm__("movl %%esp,%%eax\n\t" \
         "pushl $0x17\n\t" \      //SS 进栈,0x17 即二进制的 10111(3 特权级、LDT、数据段)
         "pushl %%eax\n\t" \      //ESP 进栈
         "pushfl\n\t" \            //EFLAGS 进栈
         "pushl $0x0f\n\t" \      //CS 进栈,0x0f 即 1111(3 特权级、LDT、代码段)
         "pushl $1f\n\t" \        //EIP 进栈
         "iret\n" \               // 出栈恢复现场、翻转特权级从 0 到 3
         "1:\tmovl $0x17,%%eax\n\t" \    // 下面的代码使 ds、es、fs、gs 与 ss 一致
               "movw %%ax,%%ds\n\t" \
               "movw %%ax,%%es\n\t" \
               "movw %%ax,%%fs\n\t" \
               "movw %%ax,%%gs" \
                  :::»ax»)

         看下图:


        和0特权级本质的变化的就是这些选择器和高速缓存寄存器不同了,现在是3特权级,而且从LDT取得描述符。

    

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值