1.用户态与内核态:
*将内核程序与用户程序隔离!
内核态能访问任何数据,用户态不能访问内核数据。
当前程序执行在什么态?因CS:IP是当前指令,故用CS的最低两位来表示:0是内核态(00),3是用户态(11)
2.中断指令:int
Intel x86中,中断指令:int
将使CS中的CPL改为0,进入内核。这是用户程序(CPL=3,DPL=0)发起的调用内核代码的唯一方式
*系统调用:
(1)用户程序中包含一段包含int指令的代码
(2)操作系统写中断处理,获取想调程序的编号
(3)操作系统根据编号执行相应代码
过程:
应用程序:
调用printf(xxx)
c函数库:
库函数printf(xxx)
库函数write(xxx)
os内核:
系统调用write(xxx)
linux/lib/write.c
#include <unistd.h>
_syscall3(int, write, int, fd, const char *buf, off_t, count)
linux/include/unistd.h
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;
}
#define __NR_write 4 /* __NR_write 是系统调用号,放在eax中;同时eax也存放返回值。ebx,ecx,edx存放3个参数*/
void sched_init(void) {
set_system_gate(0x80,&system_call);
}
用于设置0x80的中断处理
linux/include/asm/system.h
#define set_system_gate(n, addr)
_set_gate(&ide[n],15,3,addr); idt是中断向量表基址
#difine _set_gate(gate_addr, type, dp1, addr)
__asm__("movw %%ax\n\t" "movw %0,%%dx\n\t" "mov1 %%eax,%1\n\t" "mov1 %%edx,%2"::"i"((short)(0x8000+(dpl<<13)+type<<8))),
"o"(*((char *)(gate_addr))), "o"(*(4+(char *)(gate_addr))),"d"((char *)(addr),"a"(0x00080000))
linux/kernel/system_call.s
nr_system_calls=72
.globl _system_call /* 中断处理程序 */
_system_call: cmpl $nr_system_calls-1,%eax /* %eax存放系统调用号 */
ja bad_sys_call
push %ds push %es push %fs
push1 %edx push1 %ecx push1 %ebx /* 调用的参数 */
mov1 $0x10,%edx mov %dx,%ds mov %ds,%es /* 内核数据 */
mov1 $0x17,%edx mov %dx,%fs /* fs找到用户数据 */
call _system_call_table(,%eax,4) /* ex:a(,%eax,4)=a+4*eax */
push1 %eax /* 返回值压栈,被ret_from_sys_call使用 */
...
ret_from_sys_call: popl %eax,(其他的pop),iret
_sys_call_table+4*%eax 即为相应系统调用处理函数的入口
include/linux/sys.h
fn_ptr _sys_call_table[]= /* _system_call_table是一个全局函数数组 */
{
sys_setup, sys_exit, sys_fork, sys_read, sys_write,...
};
include/linux/sched.h
typedef int (fn_ptr *)();
call sys_table(,%eax,4)即为 call sys_write
用户态 | 内核态 | |||||||||
printf 用户调用 | -> | printf 展成int 0x80 | -> | system_call 中断处理 | -> | sys_call_table 查表 | -> | __NR_write=4 | -> | 调用 sys_write |