初始化进程控制块数组的操作是在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进程就启动起来了!!!
posted on 2012-02-14 19:34
myjfm 阅读(331)
评论(0) 编辑 收藏 引用 所属分类:
操作系统