随笔-80  评论-24  文章-0  trackbacks-0
初始化进程控制块数组的操作是在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= {{0x00000x00000x000x000x000x00}, 
  6                                      {0xffff0x00000x000xfa0xcf0x00}, 
  7                                      {0xffff0x00000x000xf20xcf0x00}};
  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, 03 * sizeof(struct seg_struct));
109         memset(&(proc_list[i].tss), 0sizeof(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进程就启动起来了!!!
posted on 2012-02-14 19:34 myjfm 阅读(331) 评论(0)  编辑 收藏 引用 所属分类: 操作系统

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理