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
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
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进程就启动起来了!!!