《操作系统真像还原》操作系统实现——系统调用

Posted on Jun 14, 2021

到这里,有了前面的铺垫,要说的东西不多,其实没必要单独写出来,但是系统调用毕竟还是比较重要的东西,所以这里还是单独说一下。

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;
}

然后就只完成需要提供的系统调用函数即可。