主要内容
实现简单的系统调用
实现进程调度
一
系统调用类似于Windows的API,是应用程序与操作系统之间的桥梁,对于一个程序无法访问的区域,就需要用到系统调用。
二
1.用寄存器eax来实现消息的传递
%include "sconst.inc"
_NR_get_ticks equ 0 ; 要跟 global.c 中 sys_call_table 的定义相对应!
INT_VECTOR_SYS_CALL equ 0x90
global get_ticks ; 导出符号
bits 32
[section .text]
get_ticks:
mov eax, _NR_get_ticks
int INT_VECTOR_SYS_CALL
ret
2.初始化系统调用的中断门
将NT_VECTOR_SYS_CALL号中断与sys_call对应
init_idt_desc(INT_VECTOR_SYS_CALL, DA_386IGate,
sys_call, PRIVILEGE_USER);
3.实现sys_call
sys_call:
call save
sti
call [sys_call_table + eax * 4] //调用sys_call_table[eax],每一个数组指向一个函数
mov [esi + EAXREG - P_STACKBASE], eax //将sys_call_table[eax]返回值放入进程表的eax位置,保证进程被恢复后eax中是正确的值
cli
ret
PUBLIC system_call sys_call_table[NR_SYS_CALL] = {sys_get_ticks};//目前只有一个成员
save:
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx
mov esi, esp ;esi = 进程表起始地址
inc dword [k_reenter] ;k_reenter++;
cmp dword [k_reenter], 0 ;if(k_reenter ==0)
jne .1 ;{
mov esp, StackTop ; mov esp, StackTop <--切换到内核栈
push restart ; push restart
jmp [esi + RETADR - P_STACKBASE]; return;
.1: ;} else { 已经在内核栈,不需要再切换
push restart_reenter ; push restart_reenter
jmp [esi + RETADR - P_STACKBASE]; return;
;}
PUBLIC int sys_get_ticks()
{
disp_str("+");
return 0;
}
4.在进程中调用系统调用
三
运用建立的系统调用来实现判断时间的函数
1.关于时钟中断
他是由一个被称为PIT的芯片触发的,在IBM XT中这个芯片是intel 8253,8253有三个16位计数器,各有不同作用。
Counter0:输出到IRQ0,以便每隔一段时间产生一次时钟中断
Counter1:通常被设为18,以便每隔约15us做一次RAM刷新
Counter2:连接PC喇叭
计数器有一个输入频率,在PC上是1193180Hz,每一个时钟周期,计数器的值减一,当为0时,会触发一个输出,因为这里的计数器为16位,最大值为65535,所以默认的时钟中断频率为18.2Hz,而我们可以通过编程控制8253,改变计数器的计数值。
2.改变计数值
通过相应端口的写操作来实现,Counter0对应操作端口40h
在写操作之前,需要先通过43h端口写8253模式控制寄存器
所以7,6位为00,5,4位为11,这里使用模式2,第3,2,1位为010,
设置8253
out_byte(TIMER_MODE, RATE_GENERATOR);
out_byte(TIMER0, (u8) (TIMER_FREQ/HZ) );
out_byte(TIMER0, (u8) ((TIMER_FREQ/HZ) >> 8));
宏定义
#define TIMER0 0x40 /* I/O port for timer channel 0 */
#define TIMER_MODE 0x43 /* I/O port for timer mode control */
#define RATE_GENERATOR 0x34 /* 00-11-010-0 :
* Counter0 - LSB then MSB - rate generator - binary
*/
#define TIMER_FREQ 1193182L/* clock frequency for timer in PC and AT */
#define HZ 100 /* clock freq (software settable on IBM-PC) */
精确到10ms的延迟函数
PUBLIC void milli_delay(int milli_sec)
{
int t = get_ticks();
while(((get_ticks() - t) * 1000 / HZ) < milli_sec) {}
}
四
1.实现进程的优先级
为每一个进程添加一个变量,进程每获得一个运行周期,这个变量减一,当为0时就不再执行,直到所有进程的变量都变为0时。
typedef struct s_proc {
STACK_FRAME regs; /* process registers saved in stack frame */
u16 ldt_sel; /* gdt selector giving ldt base and limit */
DESCRIPTOR ldts[LDT_SIZE]; /* local descriptors for code and data */
int ticks; //ticks是递减的,当所有进程都变为0时再把ticks赋值初始值priority
int priority; //记录ticks的初值
u32 pid; /* process id passed in from MM */
char p_name[16]; /* name of the process */
}PROCESS;
为进程赋不同的初值决定优先级
proc_table[0].ticks = proc_table[0].priority = 150;
proc_table[1].ticks = proc_table[1].priority = 50;
proc_table[2].ticks = proc_table[2].priority = 30;
2.进程调度
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;
}
}
}
}
修改时钟中断后运行
PUBLIC void clock_handler(int irq)
{
ticks++;
p_proc_ready->ticks--;
if (k_reenter != 0) {
return;
}
schedule();
}