操作系统开发系列—13.g.操作系统的系统调用 ●

在我们的操作系统中,已经存在的3个进程是运行在ring1上的,它们已经不能任意地使用某些指令,不能访问某些权限更高的内存区域,但如果一项任务需要这些使用指令或者内存区域时,只能通过系统调用来实现,它是应用程序和操作系统之间的桥梁。

所以,一件事情就可能是应用程序做一部分,操作系统做一部分。这样,问题就又涉及特权级变换。

很明显,这已经难不倒我们了,因为进程的切换就是不停地在重复这么一个特权级变换的过程。在那里,触发变换的是外部中断,我们把这个诱因换一下就可以了,变成"int nnn",一切就都解决了。

我们来实现一个叫做int get_ticks()的函数,用这个函数来得到当前总共发生了多少次时钟中断。设置一个全局变量ticks,每发生一次时钟中断,它就加1,进程可以随时通过get_ticks()这个系统调用来得到这个值。

见syscall.asm:

_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

这里将系统调用对应的中断号设为0x90,它只要不和原来的中断号重复即可。

马上来定义INT_VECTOR_SYS_CALL对应的中断门,protect.c:

PUBLIC void init_prot()
{
...
	init_idt_desc(INT_VECTOR_SYS_CALL,	DA_386IGate,
		      sys_call,			PRIVILEGE_USER);
...
}

 这样我们就将第INT_VECTOR_SYS_CALL号中断与sys_call对应起来了。这里的call [sys_call_table+eax*4](调用的是sys_call_table[eax]),与irq_table类似,sys_call_table是一个函数指针数组,每一个成员都指向一个函数,用以处理相应的系统调用。

extern	sys_call_table

global sys_call

sys_call:
        call    save

        sti

        call    [sys_call_table + eax * 4]
        mov     [esi + EAXREG - P_STACKBASE], eax

        cli

        ret

前面eax已被赋值为_NR_get_ticks(即0),而sys_call_table[0]已经初始化为sys_get_ticks,所以call [sys_call_table+eax*4]这一句调用的便是sys_get_ticks。

mov [esi+EAXREG-P_STACKBASE],eax是把函数sys_call_table[eax]的返回值放在进程表中eax的位置,以便进程P被恢复执行时eax中装的是正确的返回值。

现在可以在进程中添加调用get_ticks的代码了:

void TestA()
{
	int i = 0;
	while (1) {
		get_ticks();
		disp_str("A");
		disp_int(i++);
		disp_str(".");
		delay(1);
	}
}

别忘了在kernel.asm和syscall.asm中导入和导出相应符号。

修改

void TestA()
{
	int i = 0;
	while (1) {
		disp_str("A");
		disp_int(get_ticks());
		disp_str(".");
		delay(1);
	}
}

运行如下,第一个是A0x0,第二个是A0x27,两次之间的“#”恰好是39个,我们的get_ticks一切正常:

 

源码

posted @ 2016-05-13 19:35  是非猫  阅读(300)  评论(0编辑  收藏  举报