[计算机操作系统] —— system call

492 阅读1分钟

硬件提供了进入内核态的方式,对于x86,使用中断指令int,将CPL置0,这也是用户程序发起的调用内核代码的唯一方式。

系统调用的核心:①用户程序中包含一段带有int指令的代码;②操作系统写中断处理,获取想要调用的程序的编号;③操作系统根据编号执行相应代码。

#define __NR_write 4	// 系统调用号,函数表索引

// "=a"(__res) 	   输出,"=a"表示eax,eax值会交给__res
// ""(__NR_##name) 输入,""空表示同样是eax,__NR_write值会交给eax
// 后面就是把三个参数交给ebx,ecx,edx
// int 0x80表示软中断,也就是系统调用

# define _syscall3(type name, atype a, btype b, ctype c) \
type name(atype a, btype b, ctype c) { \
	long __res; \
    __asm__ volatile("int 0x80":"=a"(__res):""(__NR_##name),“b”((long)(a)),"c"((long)(b)),"d"((long)(c)); \
    if(__res >= 0) return (type)__res; \ 
    errno = -__res; \
    return -1; \
}

_syscall3(int, write, int, fd, const char *buf, off_t, count)

上面代码的意思就是把代表write函数的系统调用号给到eax,然后执行int 0x80指令。

int 0x80中断是如何处理的?

void sched_init(void) {
	set_system_gate(0x80, &system_call);
}

# define set_system_gate(n, addr) _set_gate(&idt[n], 15, 3, addr);
# define set_gate(gate_addr, type, dpl, addr) __asm__(...)

// 上面这一行先置DPL为3,这样CPL=3可以进来,然后内部置CPL为0,进入内核态

set_system_gate设置了0x80的中断处理门,使用system_call这个函数来处理0x80中断,操作系统在IDT中找到该函数入口并执行,执行结果记录在eax并交给__res,继续回到用户程序执行。

// system_call.s
...
call _sys_call_table(,%eax,4)

// sys.h
fn_ptr sys_call_table[] = {
	sys_setup, sys_exit, sys_fork, sys_read, sys_write...
};

// sched.h
typedef int (fn_ptr*)();

system_call中根据系统调用号,决定具体调用哪个函数来处理,至于sys_write是如何实现的,会在学习IO驱动之后了解到。