一、各个程序的作用
f程序,进程主体
g加入系统调用
h加入时间
i进程调度
三、下面分析f程序
1.关键结构体
[1]任务结构体
堆栈大小,任务名,任务函数
typedef void (*t_pf_task) ();
typedef struct s_task{
t_pf_task initial_eip; //TestA,TestB,TestC
int stacksize; //0x8000,0x8000,0x8000
char name[32]; //"TestA","TestB","TestC"
}TASK;
[2]每个进程的结构体
每个进程有TSS,ldt选择子,2个LDT,pid和pidname
typedef struct s_proc{
STACK_FRAME regs;
t_16 ldt_sel;
DESCRIPTOR ldts[LDT_SIZE]; //LDT_SIZE=2,DESCRIPTOR是8字节的描述符
t_32 pid;
char p_name[16];
}PROCESS;
typedef structs_stackframe { /* proc_ptrpoints here ↑ Low */
t_32 gs; /*┓ │ */+0
t_32 fs; /*┃ │ */+4
t_32 es; /*┃ │ */+8
t_32 ds; /*┃ │ */+12
t_32 edi; /*┃ │ */+16
t_32 esi; /*┣ pushed by save() │ */+20
t_32 ebp; /* ┃ │ */+24
t_32 kernel_esp; /* <-'popad' will ignore it │ */+28
t_32 ebx; /* ┃ ↑栈从高地址往低地址增长*/+32
t_32 edx; /* ┃ │ */+36
t_32 ecx; /* ┃ │ */+40
t_32 eax; /* ┛ │ */+44
t_32 retaddr; /*return address for assembly code save() │ */+48
t_32 eip; /* ┓ │ */+52
t_32 cs; /* ┃ │ */+56
t_32 eflags; /* ┣ these are pushed by CPU during interrupt │ */+60
t_32 esp; /* ┃ │ */+64
t_32 ss; /* ┛ ┷High */+68
}STACK_FRAME;
/* 存储段描述符/系统段描述符 */
typedef structs_descriptor /* 共 8 个字节 */
{
t_16 limit_low; /*Limit */
t_16 base_low; /*Base */
t_8 base_mid; /*Base */
t_8 attr1; /*P(1) DPL(2) DT(1) TYPE(4) */
t_8 limit_high_attr2; /*G(1) D(1) 0(1) AVL(1) LimitHigh(4) */
t_8 base_high; /*Base */
}DESCRIPTOR;
三个进程PROCESS结构内容如下
| TestA | save: | TestB | TestC |
| ||
0x0004b5c0-0x4b624 共101个字节 |
|
|
|
|
| ||
STACK_FRAME regs; |
|
|
|
|
| ||
+0 gs | 0x19 | 0x19 |
|
|
| ||
+4 fs | 0xd | 0xd |
|
|
| ||
+8 es | 0xd | 0xd |
|
|
| ||
+12 ds | 0xd | 0xd |
|
|
| ||
+16 edi | 0x0 | 0xb4a |
|
|
| ||
+20 esi | 0x0后来设置成 | 0x31543 |
|
|
|
|
|
+24 ebp | 0x0 | 0x4a89c |
|
|
|
|
|
+28 kernel_esp | 0x0 | 0x4b5f0 |
|
|
|
|
|
+32 ebx | 0x0 | 0x0 |
|
|
|
|
|
+36 edx | 0x0 | 0x0 |
|
|
|
|
|
+40 ecx | 0x0 | 0x0 |
|
|
|
|
|
+44 eax | 0x0 | 0x4a894 |
|
|
|
|
|
+48 retaddr | 0x0 | restart或者restart_reenter |
|
|
|
|
|
+52 eip | 0x000309e7 TestA函数 | 0x31438 |
|
|
|
|
|
+56 cs | 0x5 | 0x5 |
|
|
|
|
|
+60 eflags | 0x1202 | 0x1203 |
|
|
|
|
|
+64 esp | 0x4a8c0 |
|
|
|
|
|
|
+68 ss | 0xd |
|
|
|
|
|
|
+72 t_16 ldt_sel | 0x28 |
|
|
|
|
|
|
DESCRIPTOR ldts[LDT_SIZE]; |
|
|
|
|
|
|
|
+74 第一个LDT | 0x00cfb800 0x0000ffff |
|
|
|
|
|
|
+82 第二个LDT | 0x00cfb200 0x0000ffff |
|
|
|
|
|
|
+90 t_32 pid; | 0x00000000 |
|
|
|
|
|
|
+94 char p_name[16]; | 00 00 54 65 73 74 41也就是TestA |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.TSS
typedef struct s_tss {
t_32 backlink;
t_32 esp0; /*stack pointer to use during interrupt */
t_32 ss0; /* " segment " " " " */
t_32 esp1;
x
t_32 cr3;
t_32 eip;
t_32 flags;
t_32 eax;
t_32 ecx;
t_32 edx;
t_32 ebx;
t_32 esp;
t_32 ebp;
t_32 esi;
t_32 edi;
t_32 es;
t_32 cs;
t_32 ss;
t_32 ds;
t_32 fs;
t_32 gs;
t_32 ldt;
t_16 trap;
t_16 iobase; /*I/O位图基址大于或等于TSS段界限,就表示没有I/O许可位图 */
/*t_8 iomap[2];*/
}TSS;
4.添加一个任务的步骤总结
下面我在f程序的基础上,增加一个进程D
(1)在添加一个进程体
Kernel\main.c
void TestD()
{
int i = 0x3000;
while(1){
disp_str("D");
disp_int(i++);
disp_str(".");
delay(1);
}
}
(2)在task_table中添加一项进程
Kernel\global.c
PUBLIC TASK task_table[NR_TASKS] = {{TestA,STACK_SIZE_TESTA, "TestA"},
{TestB,STACK_SIZE_TESTB, "TestB"},
{TestC,STACK_SIZE_TESTC, "TestC"},
{TestD,STACK_SIZE_TESTD, "TestD"}};
(3)让NR_TASKS加1
Include\proc.h
#define NR_TASKS 4
(4)定义任务堆栈和修改STACK_SIZE_TOTAL
Include\proc.h
#define STACK_SIZE_TESTA 0x8000
#define STACK_SIZE_TESTB 0x8000
#define STACK_SIZE_TESTC 0x8000
#define STACK_SIZE_TESTD 0x8000
#define STACK_SIZE_TOTAL (STACK_SIZE_TESTA+ \
STACK_SIZE_TESTB+ \
STACK_SIZE_TESTC+ \
STACK_SIZE_TESTD)
(5)添加新任务执行体函数声明
Include\proto.h
void TestD();
四、分析g加入系统调用
在f程序上增加的代码
1.在kernel/syscall.asm
get_ticks:
mov eax, _NR_get_ticks ; _NR_get_ticks
int INT_VECTOR_SYS_CALL ; INT_VECTOR_SYS_CALL equ 0x90
ret
2.在 kernel/protect.c中
//init_idt_desc_3102C(0x90, 0x8E,sys_call_307C6, 3);
init_idt_desc(INT_VECTOR_SYS_CALL, DA_386IGate, sys_call, PRIVILEGE_USER);
3.当调用get_tichs就会调用sys_call函数,
Kernel/kernel.asm中
sys_call:
call save
sti ;开中断
call [sys_call_table + eax * 4] ;call off_32098[eax*4] ,,off_32098存放的是sys_get_ticks函数
mov [esi + EAXREG - P_STACKBASE], eax ;mov [esi+2Ch], eax
cli ;关中断
ret
4.上面实际调用了call [sys_call_table],也就是调用sys_call_table[0]
在kernel/global.c中有
PUBLIC t_sys_call sys_call_table[NR_SYS_CALL] ={sys_get_ticks};
实际调用函数sys_get_ticks
5.在kernel/proc.c中
PUBLIC int sys_get_ticks()
{
returnticks;
}
所以get_ticks最终调用的是sys_get_ticks函数
6.使用kernel/ main.c
void TestA()
{
while(1){
disp_str("A");
disp_int(get_ticks());
disp_str(".");
delay(1);
}
}
五、分析h加入时间
1.设置8253将时钟中断间隔改成10ms
Kernel\main.c
out_byte(TIMER_MODE,RATE_GENERATOR);
out_byte(TIMER0,(t_8) (TIMER_FREQ/HZ) );
out_byte(TIMER0,(t_8) ((TIMER_FREQ/HZ) >> 8));
2. 延迟函数
Kernel\ clock.c
PUBLIC void milli_delay(int milli_sec)
{
intt = get_ticks();
while(((get_ticks()- t) * 1000 / HZ) < milli_sec) {}
}
3.使用
void TestA()
{
while(1){
disp_str("A");
disp_int(get_ticks());
disp_str(".");
milli_delay(1000); //延迟了1s
}
}
4.注意
由于多进程切换和打印那些#字符导致不准确,去掉之后就好多了,还有bochs也不准确,换virtual pc就准确了,virtual pc更加像真机
5.为了测试我们改成一个进程
Kernel\global.c
PUBLIC TASK task_table[NR_TASKS] = {{TestA,STACK_SIZE_TESTA, "TestA"}};
Include\proc.h
#define NR_TASKS 1
#define STACK_SIZE_TESTA 0x8000
#define STACK_SIZE_TOTAL (STACK_SIZE_TESTA)
显示结果,果然每次打印相隔1s,数值每次差别是100
六、分析i程序进程调度
根据延长的时间长短实现进程调度
在进程表中加入一个变量,
1.为进程表添加新的成员
Include/proc.h
Typedef struct s_proc{
……
Int ticks;从某个值开始递减
Int priority;prority=ticks永久不变
2.初始化
Kernel/main.c
proc_table[0].ticks =proc_table[0].priority = 15;
proc_table[1].ticks =proc_table[1].priority = 5;
proc_table[2].ticks =proc_table[2].priority = 3;
3进程调度函数
Kernel/proc.c
PUBLIC void schedule()
{
PROCESS* p;
int greatest_ticks = 0;
while(!greatest_ticks) {
for(p=proc_table; p<proc_table+NR_TASKS; p++) {
if(p->ticks > greatest_ticks) {
greatest_ticks= p->ticks;
p_proc_ready= p;
}
}
if(!greatest_ticks) {
for (p=proc_table;p<proc_table+NR_TASKS; p++) {
p->ticks= p->priority;
}
}
}
}
4修改时钟中断处理函数
Kernel/clock.c
PUBLIC void clock_handler(int irq)
{
p_proc_read->ticks--;//新加入
schedule();//新加入
}