《操作系统真像还原》操作系统实现——系统调用
到这里,有了前面的铺垫,要说的东西不多,其实没必要单独写出来,但是系统调用毕竟还是比较重要的东西,所以这里还是单独说一下。
Linux 下的系统调用占用的是 0x80 号中断,通过 eax 来选择要调用的功能,我们维护一个函数指针数组,存储各个功能的函数指针,然后用一个简单的汇编函数就可以实现调用
extern syscall_table
section .text
global syscall_handler
syscall_handler:
push 0 ; syscall has no errcode, thus push 0
push ds
push es
push fs
push gs
pushad ; save the context
push 0x80 ; push the INT number (as IntExit poped this)
push edx
push ecx
push ebx
call [syscall_table + eax * 4]
add esp, 12
mov [esp + 8 * 4], eax ; update the eax in the backup
; so eax can be the ret val
jmp IntExit
然后只要完成对应的系统调用即可。为了使调用简单,对于需要不同参数个数的系统调用提供类似于如下的宏来进行调用
#define _syscall3(NUMBER, ARG1, ARG2, ARG3) \
({ \
int retval; \
__asm__ volatile \
( \
"int $0x80" : \
"=a" (retval) : \
"a" (NUMBER), "b" (ARG1), \
"c" (ARG2), "d" (ARG3) : \
"memory" \
); \
retval; \
}) \
对于一个简单的系统调用 getpid() 暴露给用户态的函数定义即为
size_t getpid()
{
return _syscall0(SYS_GETPID);
}
在函数指针数组即系统调用函数表中进行如下的初始化
syscall_table[SYS_GETPID] = sys_getpid;
并提供一个在内核函数 sys_getpid 来执行 getpid 的操作
size_t sys_getpid()
{
return GetCurrentThreadPCB()->pid;
}
然后就只完成需要提供的系统调用函数即可。