WinixJ---kernel/process.c文件详解(下)

WinixJ---kernel/process.c文件详解(下)
初始化进程控制块数组的操作是在init_proc_list()内完成的,它只被cbegin()函数调用,其实它的工作很简单,就是将进程控制块中的各个项初始化成初始值。并且将相应进程的LDT在GDT中的描述符初始化。这样当启动一个进程的时候,只需要在进程自己的LDT和TSS中填入适当的值即可,不需要理会LDT和TSS在GDT中的描述符内容。

  1  // 初始化init和sys进程
  2  // 主要包括进程pid、进程名、ldt、tss以及init进程在gdt中对应的项的初始化
  3  static   void  init_proc01()
  4  {
  5       struct  seg_struct ldt_temp[ 3 =  {{ 0x0000 0x0000 0x00 0x00 0x00 0x00 }, 
  6                                       { 0xffff 0x0000 0x00 0xfa 0xcf 0x00 }, 
  7                                       { 0xffff 0x0000 0x00 0xf2 0xcf 0x00 }};
  8 
  9       // pid为0
 10       // 进程名为"init"
 11      proc_list[ 0 ].pid                 =   0 ;
 12      proc_list[ 0 ].ppid                 =   - 1 ;
 13      proc_list[ 0 ].state                 =  PROC_RUNNING;
 14      proc_list[ 0 ].priority             =   15 ;
 15      proc_list[ 0 ].time_slices         =  proc_list[ 0 ].priority;
 16      proc_list[ 0 ].running_time         =   0 ;
 17      strcpy(proc_list[ 0 ].name,  " init " );
 18       // ldt共包括三项,0->空,1->cs,2->ds&ss
 19      proc_list[ 0 ].ldt[ 0 ]                 =  ldt_temp[ 0 ];
 20      proc_list[ 0 ].ldt[ 1 ]                 =  ldt_temp[ 1 ];
 21      proc_list[ 0 ].ldt[ 2 ]                 =  ldt_temp[ 2 ];
 22      proc_list[ 0 ].tss.back_link         =   0 ;
 23      proc_list[ 0 ].tss.esp0             =  KSTACKTOP( 0 );  // 内核态堆栈顶
 24      proc_list[ 0 ].tss.ss0             =  KERNEL_SS_SELECTOR;
 25      proc_list[ 0 ].tss.esp1             =   0 ;
 26      proc_list[ 0 ].tss.ss1             =   0 ;
 27      proc_list[ 0 ].tss.esp2             =   0 ;
 28      proc_list[ 0 ].tss.ss2             =   0 ;
 29      proc_list[ 0 ].tss.cr3             =  (uint32)page_dir;  // 页目录表地址
 30      proc_list[ 0 ].tss.eip             =   0 ;
 31      proc_list[ 0 ].tss.eflags             =   0 ;
 32      proc_list[ 0 ].tss.eax             =   0 ;
 33      proc_list[ 0 ].tss.ecx             =   0 ;
 34      proc_list[ 0 ].tss.edx             =   0 ;
 35      proc_list[ 0 ].tss.ebx             =   0 ;
 36      proc_list[ 0 ].tss.esp             =   0 ;
 37      proc_list[ 0 ].tss.ebp             =   0 ;
 38      proc_list[ 0 ].tss.esi             =   0 ;
 39      proc_list[ 0 ].tss.edi             =   0 ;
 40      proc_list[ 0 ].tss.es                 =   0x17 // 指向ldt中的选择子
 41      proc_list[ 0 ].tss.cs                 =   0x0f // 指向ldt中的选择子
 42      proc_list[ 0 ].tss.ss                 =   0x17 // 指向ldt中的选择子
 43      proc_list[ 0 ].tss.ds                 =   0x17 // 指向ldt中的选择子
 44      proc_list[ 0 ].tss.fs                 =   0x17 // 指向ldt中的选择子
 45      proc_list[ 0 ].tss.gs                 =   0x17 // 指向ldt中的选择子
 46      proc_list[ 0 ].tss.ldt             =  LDT_SELECTOR( 0 );
 47      proc_list[ 0 ].tss.trace_bitmap     =   0x80000000 ;
 48 
 49      set_tss_seg(FIRST_TSS_INDEX,  & (proc_list[ 0 ].tss));
 50      set_ldt_seg(FIRST_LDT_INDEX, proc_list[ 0 ].ldt);
 51 
 52       // pid为1
 53       // 进程名为"sys"
 54      proc_list[ 1 ].pid                 =   1 ;
 55      proc_list[ 1 ].ppid                 =   0 ;
 56      proc_list[ 1 ].state                 =  PROC_RUNNING;
 57      proc_list[ 1 ].priority             =   15 ;
 58      proc_list[ 1 ].time_slices         =  proc_list[ 1 ].priority;
 59      proc_list[ 1 ].running_time         =   0 ;
 60      strcpy(proc_list[ 1 ].name,  " sys " );
 61       // ldt共包括三项,0->空,1->cs,2->ds&ss
 62      proc_list[ 1 ].ldt[ 0 ]                 =  ldt_temp[ 0 ];
 63      proc_list[ 1 ].ldt[ 1 ]                 =  ldt_temp[ 1 ];
 64      proc_list[ 1 ].ldt[ 2 ]                 =  ldt_temp[ 2 ];
 65      proc_list[ 1 ].tss.back_link         =   0 ;
 66      proc_list[ 1 ].tss.esp0             =  KSTACKTOP( 1 );  // 内核态堆栈顶
 67      proc_list[ 1 ].tss.ss0             =  KERNEL_SS_SELECTOR;
 68      proc_list[ 1 ].tss.esp1             =   0 ;
 69      proc_list[ 1 ].tss.ss1             =   0 ;
 70      proc_list[ 1 ].tss.esp2             =   0 ;
 71      proc_list[ 1 ].tss.ss2             =   0 ;
 72      proc_list[ 1 ].tss.cr3             =  (uint32)page_dir;  // 页目录表地址
 73      proc_list[ 1 ].tss.eip             =  (uint32)sys;
 74      proc_list[ 1 ].tss.eflags             =   0x1202 ;
 75      proc_list[ 1 ].tss.eax             =   0 ;
 76      proc_list[ 1 ].tss.ecx             =   0 ;
 77      proc_list[ 1 ].tss.edx             =   0 ;
 78      proc_list[ 1 ].tss.ebx             =   0 ;
 79      proc_list[ 1 ].tss.esp             =  USTACKTOP( 1 );
 80      proc_list[ 1 ].tss.ebp             =  USTACKTOP( 1 );
 81      proc_list[ 1 ].tss.esi             =   0 ;
 82      proc_list[ 1 ].tss.edi             =   0 ;
 83      proc_list[ 1 ].tss.es                 =   0x17 // 指向ldt中的选择子
 84      proc_list[ 1 ].tss.cs                 =   0x0f // 指向ldt中的选择子
 85      proc_list[ 1 ].tss.ss                 =   0x17 // 指向ldt中的选择子
 86      proc_list[ 1 ].tss.ds                 =   0x17 // 指向ldt中的选择子
 87      proc_list[ 1 ].tss.fs                 =   0x17 // 指向ldt中的选择子
 88      proc_list[ 1 ].tss.gs                 =   0x17 // 指向ldt中的选择子
 89      proc_list[ 1 ].tss.ldt             =  LDT_SELECTOR( 1 );
 90      proc_list[ 1 ].tss.trace_bitmap     =   0x80000000 ;
 91 
 92      set_tss_seg(FIRST_TSS_INDEX  +   2 & (proc_list[ 1 ].tss));
 93      set_ldt_seg(FIRST_LDT_INDEX  +   2 , proc_list[ 1 ].ldt);
 94  }
 95 
 96  void  init_proc_list()
 97  {
 98       int  i;
 99       for  (i  =   0 ; i  <  NR_PROCS;  ++ i)
100      {
101          proc_list[i].pid             =   - 1 ;
102          proc_list[i].ppid             =   - 1 ;
103          proc_list[i].state             =  PROC_STOPPED;
104          proc_list[i].priority         =   0 ;
105          proc_list[i].time_slices     =   0 ;
106          proc_list[i].running_time     =   0 ;
107          memset(proc_list[i].name,  0 , PROC_NAME_LEN);
108          memset(proc_list[i].ldt,  0 3   *   sizeof ( struct  seg_struct));
109          memset( & (proc_list[i].tss),  0 sizeof (TSS));
110          set_tss_seg(FIRST_TSS_INDEX  +  i  *   2 & (proc_list[i].tss));
111          set_ldt_seg(FIRST_LDT_INDEX  +  i  *   2 , proc_list[i].ldt);
112      }
113 
114       // 启动的第一个进程是进程链表proc_list中第一个进程,为init进程
115      current     =  proc_list;
116  }
117 

注意到在process.c文件中有一个current变量,它是proc_struct类型指针,是全局变量,这从linux借鉴过来,用于指向当前正在运行的进程。
下面着重看init_proc01()函数。
它的作用是在proc_list[0]和proc_list[1]中填入适当的值,以为启动进程init(0号进程)和sys(1号进程)做准备。
我们只研究init进程控制块的填入过程:
进程id为0,父进程id为-1(因为它是所有进程的父进程,它没有父进程),优先级为15,因此初始时间片为15。仔细研究ldt_temp[]发现ldt中的cs段描述符属性为:地址空间为0-4G,DPL=3,说明是用户级别进程代码段;ds段描述符属性为:地址空间为0-4G,DPL=3,说明是用户级别数据段。再看tss中各字段的填入过程,重要的几个字段分别是:esp0、ss0、cr3、eip、eflags、esp、ebp、以及es、cs、ss、ds、fs、gs以及ldt,对于init进程,由于我们会在后面手工启动,所以有些字段可以不填,但是对于其他进程的初始化,这些字段都是必须填写合适并完整的。

最后看手工启动进程init的函数,该函数非常重要,启动init之后我们的操作系统便有了质的飞跃。

 1  void  start_proc0()
 2  {
 3       // 初始化init进程
 4      init_proc01();
 5 
 6      uint32 ss         =   0x17 ;
 7      uint32 esp         =  USTACKTOP( 0 );
 8      uint32 eflags     =   0x1202 // init进程可以使用I/O命令,同时要求init进程允许中断
 9      uint32 cs         =   0x0f ;
10      uint32 eip         =  (uint32)init;
11 
12       /* *********************************************************
13       * 下面调试了很久!!!!!!!!!!!!!!!!!
14       * 不能将enable_hwint(CLOCK_IV);语句放到紧挨sti()的前面
15       * 因为enable_hwint(CLOCK_IV)是c语句宏,展开之后会
16       * 用到堆栈,就会把刚push进去的ss、esp、eflags、cs、eip
17       * 给覆盖掉!!!!!!!!!!!!!!!!!!!!!!
18       * 将enable_hwint()放到init_clock()里面
19       ********************************************************* */
20 
21       // 加载init进程对应的tss
22       // 加载init进程对应的ldt
23      ltr(TSS_SELECTOR( 0 ));
24      lldt(LDT_SELECTOR( 0 ));
25      push(ss);                 // push ss
26      push(esp);                 // push esp
27      push(eflags);             // push eflags
28      push(cs);                 // push cs
29      push(eip);                 // push eip
30      init_segr();             // 初始化ds、es、gs、fs四个段寄存器, 使其指向ldt中的数据段描述符
31      sti();                     // 打开中断,从现在开始内核态将允许中断
32      iretd();                 // iretd这条指令之后第一个进程init进程便开始运行了
33  }
34 

手工启动进程的过程我们采用minix的做法,在内核栈中我们伪造一个现场,使得其看似是正在运行进程init然后发生时钟中断后的现场,这样的现场应该是这样的:
进程init的ss、esp、eflags、cs、eip应该已经被压入栈中,寄存器tr应该存有进程init在GDT中的tss选择子,寄存器ldtr应该存有进程init在GDT中的ldt选择子,各个段寄存器应该是指向ldt中的选择子。如果这些都准备好了那就和时钟中断处理程序返回前没什么两样了,这样就可以打开中断,然后使用一条iret指令就可以了。iret指令会依次从堆栈中弹出eip、cs、eflags、esp、ss,然后切换到进程init的用户态继续执行代码,这样我们的第一个init进程就启动起来了!!!

你可能感兴趣的:(WinixJ---kernel/process.c文件详解(下))