原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
系统调用的意义:
操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用
2.极大的提高了系统的安全性
3.使用户程序具有可移植性
API和系统调用的关系:
(很多人认为这是同一个概念,其实不然)
API只是一个函数定义系统调用通过软中断向内核发出一个明确的请求(unix/linux系统是这样,windows内核不开源但也是软中断),不是每个API都对应一个特定的系统调用比如:c标准库的一些数学函数abs(),div()... ...
一个单独的API可能调用几个系统调用
比如:printf()调用 fprintf(stdout...)
文件的操作也调用 fprintf(pFile..)
又比如:
scanf()调用 fscanf(stdin)
malloc、calloc、relloc...
这样都是很常用的
当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。
在Linux中是通过执行int $0x80来执行系统调用的,这条汇编指令产生向量为128的编程异常(陷阱门),学过汇编都知道中断后会跳转到中断向量表去执行中断处理程序,从而完成系统调用。
内核实现了很多不同的系统调用,进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数
x86下
使用eax寄存器
#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
#define __NR_exit (__NR_SYSCALL_BASE+ 1)
#define __NR_fork (__NR_SYSCALL_BASE+ 2)
#define __NR_read (__NR_SYSCALL_BASE+ 3)
#define __NR_write (__NR_SYSCALL_BASE+ 4)
#define __NR_open (__NR_SYSCALL_BASE+ 5)
.................
#define __NR_pipe2 (__NR_SYSCALL_BASE+359)
#define __NR_inotify_init1 (__NR_SYSCALL_BASE+360)
#define __NR_preadv (__NR_SYSCALL_BASE+361)
#define __NR_pwritev (__NR_SYSCALL_BASE+362)
#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
#define __NR_perf_event_open (__NR_SYSCALL_BASE+364)
#define __NR_recvmmsg (__NR_SYSCALL_BASE+365)
寄存器传递参数具有如下限制:
1、每个参数的长度不能超过寄存器的长度,即32位2、在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp)
3、如果超过6个就将某个寄存器作为指针传递地址来访问多个内存空间
内嵌汇编含义:
mov $0,%ebx ;对应time(NULL)的第一个参数,传0x00
mov $0xd,%eax ;传递系统调用号(#define __NR_time 13)
int $0x80 ;中断
mov %eax,%0 ;将返回值赋值给%0对应的出口参数即:tt
Linux中,在用户态和内核态运行的进程使用的栈是不同的,分别叫做用户栈和内核栈,两者各自负责相应特权级别状态下的函数调用。当进行系统调用时,进程不仅要从用户态切换到内核态,同时也要完成栈切换,这样处于内核态的系统调用才能在内核栈上完成调用。系统调用返回时,还要切换回用户栈,继续完成用户态下的函数调用。