Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2151598
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-09-26 12:03:39

1.进程0的创建 
1.1 构造进程0的ldt与tss --> 在include/linux/sched.h中
  1. 152 /*
  2. 153 * INIT_TASK is used to set up the first task table, touch at
  3. 154 * your own Base=0, limit=0x9ffff (=640kB)
  4. 155 */
  5. 156 #define INIT_TASK 
  6. 157     /* state etc */ { 0,15,15, 
  7. 158     /* signals */ 0,{{},},0, 
  8. 159     /* ec,brk... */ 0,0,0,0,0,0, 
  9. 160     /* pid etc.. */ 0,0,0,0, 
  10. 161     /* suppl grps*/ {NOGROUP,}, 
  11. 162     /* proc links*/ &init_task.task,0,0,0, 
  12. 163     /* uid etc */ 0,0,0,0,0,0, 
  13. 164     /* timeout */ 0,0,0,0,0,0,0, 
  14. 165     /* rlimits */ { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, 
  15. 166      {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, 
  16. 167      {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}}, 
  17. 168     /* flags */ 0, 
  18. 169     /* math */ 0, 
  19. 170     /* fs info */ -1,0022,NULL,NULL,NULL,NULL,0, 
  20. 171     /* filp */ {NULL,}, 
  21. 172      {        -->ldt
  22. 173          {0,0},               -->selector=0x07
  23. 174          {0x9f,0xc0fa00},     -->selector=0x0F,进程0的代码段,[0-640K],DPL=3,TYPE=0x0a,(0x9F+1)*4096/1024=(636+4)K
  24. 175          {0x9f,0xc0f200},     -->selector=0x17,进程0的数据段,[0-640K],DPL=3,TYPE=0x02
  25. 176      }, 
  26. 177      {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,  --> tss
  27. 178       0,0,0,0,0,0,0,0, 
  28. 179       0,0,0x17,0x17,0x17,0x17,0x17,0x17, 
  29. 180       _LDT(0),0x80000000,      -->gtd[5]是进程0的ldt
  30. 181       {} 
  31. 182      }, 
  32. 183 }

上图出自《Linux内核完全注释V3.0.pdf》P122
1.2 把上面的数组放进数组中 --> 在kernel/sched.c中
  1. 67 static union task_union init_task = {INIT_TASK,}
1.3 挂接进程0的tss与ldt -->在kernel/sched.c中
  1. 418 void sched_init(void)
  2. 419 {
  3. 420      int i;
  4. 421      struct desc_struct * p;
  5. 422
  6. 423      if (sizeof(struct sigaction) != 16)
  7. 424           panic("Struct sigaction MUST be 16 bytes");
  8. 425      set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));    -->将进程0的tss与gdt[4]连接
  9. 426      set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));    -->将进程0的ldt与gdt[5]连接
  10. 427      p = gdt+2+FIRST_TSS_ENTRY;
  11. 428      for(i=1;i<NR_TASKS;i++) {
  12. 429           task[i] = NULL;
  13. 430           p->a=p->b=0;
  14. 431           p++;
  15. 432           p->a=p->b=0;
  16. 433           p++;
  17. 434      }
  18. 435     /* Clear NT, so that we won't have troubles with that later on */
  19. 436      __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");      -->保证iret切换任务时EFLAGE的N位必须为1
  20. 437      ltr(0);        -->加载tr,注意这儿lldt与ltr都是加载selector
  21. 438      lldt(0);       -->加载ldt,然后根据selector找到相应的内容去加载真正的地址
  22. 439      outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
  23. 440      outb_p(LATCH & 0xff , 0x40); /* LSB */
  24. 441      outb(LATCH >> 8 , 0x40); /* MSB */
  25. 442      set_intr_gate(0x20,&timer_interrupt);
  26. 443      outb(inb_p(0x21)&~0x01,0x21);
  27. 444      set_system_gate(0x80,&system_call);
  28. 445 }
1.4 set_ldt_desc --> 在include/asm/system.h中
在gdt[n+常数]处嵌入ldt,将参数addr填到基地址中
  1. #define _set_tssldt_desc(n,addr,0x82)
  2. __asm__ ("movw $104,%1"   -->BYTE[0-1]:TSS与LDT段限长=104
  3.     "movw %%ax,%2"        -->BYTE[2-3]:段基址[0-15]
  4.     "rorl $16,%%eax"      -->将eax的高16位与低16位交换
  5.     "movb %%al,%3"        -->BYTE[4]: al是段基址的[16-23]
  6.     "movb $0x82,%4"       -->BYTE[5]: P=1 DPL=0 S=TYPE=2(数据段) TYPE=9(代码段)
  7.     "movb $0x00,%5"       -->BYTE[6]: 置0 G=0,DB=0,AVL=0
  8.     "movb %%ah,%6"        -->BYTE[7]: ah是段基址的[24-31]
  9.     "rorl $16,%%eax"      -->将eax的高16位与低16位再交换一次,即把eax恢复成原值
  10.     ::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), 
  11.      "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7))
  12.     )
2. 进程0的启动 --> 在include/asm/system.h中
由特权级0 切换到特权级3
  1. #define move_to_user_mode()
  2. __asm__ ("movl %%esp,%%eax"
  3.     "pushl $0x17"          -->SS=等于任务0的LDT数据段选择子=0x17,index=2,Ti=1(LDT),RPL=3
  4.     "pushl %%eax"          -->ESP=任务0的堆栈
  5.     "pushfl"               -->EFLAGS=任务0的标志寄存器
  6.     "pushl $0x0f"          -->CS=任务0的LDT代码段选择子=0xF,index=1, TI=1(LDT),RPL=3
  7.     "pushl $1f"            -->EIP=任务0的代码指针,就是下面的标号1
  8.     "iret"                 -->iret之后就切换到任务0中去了,就是跳到下面的标号1去执行了
  9.     "1: movl $0x17,%%eax"
  10.     "mov %%ax,%%ds"
  11.     "mov %%ax,%%es"        -->这几行就是把ds,es,fs,gs指向进程0的数据段
  12.     "mov %%ax,%%fs"
  13.     "mov %%ax,%%gs"
  14.     :::"ax")
a. 关于iret
intel手册中IRET只会将EIP,CS,EFLAGS弹出,但是当有特权级的切换时,SS:ESP也被弹出
iret之后的出栈顺序是固定的,如下:
     EIP --> CS --> EFLAGS --> ESP --> SS        
b. 这儿只是改变了SS堆栈特权级,ESP的内容没有改变


附录1. 关于段限长
以下出自:[笔记] 当段限长是0的时候意味着什么
%B6%CE%CF%DE%B3%A4
问题: 当代码段描述符中,段限长为0的时候,代码段多长?
答:当段限长为0的时候,代码段的长度并不是零,而是1个长度单位。长度单位取决于颗粒度。
当颗粒度为0的时候,段的长度单位是字节,限长0代表段的长度是1字节,即段中可以存储1个字节的内容
当颗粒度为1的时候,段的长度单位是4KB,限长0代表段的长度是4KB,即段中可以存储4KB的内容
抽象出,当段限长为n的时候,段的长度是(n+1)个长度单位。

例如,4.9节中的例子,代码段的限长是0x7FF,颗粒度是1。
则,段的长度是(0x7FF + 1) = (0x800) = 2^11个长度单位 =2^11 * 4KB = 8 * (2^10) KB = 8 MB

颗粒度表示用什么方式来计算长度。
颗粒度为0,表示用字节来计算。
颗粒度为1,表示用页数来计算,因为每页大小为4KB
阅读(1065) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~