系统调用
API(application program interface)即应用系统对操作系统功能的调用,也可以称为系统调用(system call)。
今天我们就来实现系统调用的功能,我们先来思考一下WIN10系统是如何实现系统调用的呢?
下图是简化版的Windows系统架构图:
Windows计算机中处理器有两种模式,分别为用户模式(用户态、目态)和内核模式(核心态、内核态、管态、系统模式、管理模式)。在用户模式下,处理器运行用户进程,不能使用特权指令,其中特权指令是指具有特殊权限的指令,一般不直接给用户使用,比如开/关中断指令、内存清零指令、停机指令等。而在内核模式下,处理器运行内核代码,可以使用特权指令。
在WIN10系统中,用户应用程序是通过子系统DLL来调用本地Windows服务的。Windows为用户模式中的应用程序提供一个虚拟地址块,被称为应用程序的用户空间。而应用程序不能直接访问的其余大块的地址被称为系统空间(内核空间)。
实现系统调用,我们需要CPU从用户空间进入到系统空间执行。而使CPU进入系统空间执行,有三种方式:
1.中断:当有来自外部设备的中断请求到来时,CPU会自动转入系统空间执行。(被动)
2.异常:当有执行指令发生异常时,CPU会进入系统空间执行。(被动)
3.自陷(陷入、陷阱):CPU通过自陷指令进入系统空间执行,自陷指令的执行相当于子程序调用,系统调用一般都是通过自陷指令实现的。(主动)
这里我们也是使用类似自陷的方式,来实现系统调用。
显示单个字符的API
我们先来通过API显示单个字符,实现这个功能我们先把需要显示字符编码存入寄存器,然后再让应用程序能够调用cons_putchar函数。这里存在两个问题,一个问题是函数没法接收存在寄存器上的字符编码,再一个问题是我们不知道cons_putchar函数的地址。所以我们先写一个函数_asm_cons_putchar,将寄存器的值推入栈中,再在这个函数中调用cons_putchar函数。
调用结构图如下:
在bootpack.map文件中我们能够查找到cons_putchar函数的地址,将它填入到代码中。
bootpack.map文件:
我们将地址填入应用程序中,需要注意的是,当应用程序通过API执行CALL指令实现函数调用时,需要加上段号。这里操作系统的段为“2*8”,使用far-CALL,同时指定段和偏移量。
hlt.nas文件:
[BITS 32]
MOV AL,'A' ;这句就是API
CALL 2*8:0xbe3 ;还有这句
fin:
HLT
JMP fin
cons_putchar函数的地址保存在内存中,这里保存在BOOTINFO之前的0x0fec。
console.c节选:
void console_task(struct SHEET *sheet, unsigned int memtotal)
{
(略)
cons.sht = sheet;
cons.cur_x = 8;
cons.cur_y = 28;
cons.cur_c = -1;
*((int *) 0x0fec) = (int) <