系统调用是受控的内核入口,借助于这一机制,进程可以请求内核以自己的名义去执行某些动作。Linux 内核以 C 语言语法 API 接口形式(头文件),提供有一系列服务供程序访问。可以通过 man 2 syscall 查看系统调用信息。
关于系统调用,需要注意以下几点: 1、系统调用将处理器从用户态切换到核心态,以便 CPU 访问受到保护的内核内存; 2、系统调用的组成是固定的,每个系统调用都由一个唯一的数字来标识; 3、每个系统调用可辅之以一套参数,对用户控件(进程虚拟地址控件)与内核空间之间(相互)传递的信息加以规范;
以C语言为例,执行系统调用时,幕后会历经诸多步骤。以 x86-32 平台为例,按时间发生顺序对这些步骤加以分析: 1、应用程序通过 C 语言函数库中的外壳(wrapper)函数,来发起系统调用; 2、对系统调用中断处理例程来说,外壳函数必须保证所有的系统调用参数可用。通过堆栈,这些参数传入外壳函数,但内核却希望将这些参数置入特定寄存器。因此,外壳函数会将上述参数复制到寄存器; 3、由于所有系统调用进入内核的方式相同,内核需要设法区分每个系统调用。为此,外壳函数会将系统调用编号复制到一个特殊的 CPU 寄存器 (%eax) 中; 4、外壳函数执行一条中断机器指令(int 0×80),引发处理器从用户态切换到核心态,并执行系统中断 0×80(十进制128)的中断矢量所之指向的代码;(2.6内核 和 glibc 2.3.2 以后的版本支持 sysenter 指令,进入内核速度更快); 5、为响应中断 0×80,内核会调用 system_call 例程(内核源码中 arch/i386/entry.S)来处理这次中断; 6、若系统调用服务例程的返回值表明调用有误,外壳函数会使用该值来设置全局变量 errno,然后外壳函数会返回到调用程序,并同时返回一个整数值,以表明系统调用是否成功;
二、Go 语言封装的系统调用
Go 语言调用系统调用,并没有使用系统提供的 C 语言函数形式,而是自己封装了系统调用。以 AMD64 为例,Go 语言提供了如下调用系统调用的方式:
复制代码
其中,Syscall 和 RawSyscall 的区别如下:(以6结尾的一样) 从源码可以看出,Syscall 开始和结束,分别调用了 runtime 中的进入系统调用和退出系统调用的函数,这就说明,系统调用被 runtime 运行时(调度器)管理,系统调用可以在任何 goroutine 中执行;而 RawSyscall 并没有,因此它可能会阻塞,导致整个程序阻塞。我们应该总是使用 Syscall,RawSyscall 存在的意义是为那些永远不会阻塞的系统调用准备的,比如 Getpid。我们自己的程序需要时,应该用 Syscall。 Go 中 Syscall 的实现,在汇编文件 syscall/asm_linux_amd64.s 中:
// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
// Trap # in AX, args in DI SI DX R10 R8 R9, return in AX DX
// Note that this differs from "standard" ABI convention, which
// would pass 4th arg in CX, not R10.
TEXT ·Syscall(SB),NOSPLIT,$0-56
CALL runtime·entersyscall(SB)
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
MOVQ $0, R10
MOVQ $0, R8
MOVQ $0, R9
MOVQ trap+0(FP), AX // syscall entry
SYSCALL // 进入内核,执行内核处理例程
// 0xfffffffffffff001 是 linux MAX_ERRNO 取反 转无符号,http://lxr.free-electrons.com/source/include/linux/err.h#L17
CMPQ AX, $0xfffffffffffff001 // 发生错误,r1=-1
JLS ok
MOVQ $-1, r1+32(FP)
MOVQ $0, r2+40(FP)
NEGQ AX // 错误码,因为错误码是负数,这里转为正数
MOVQ AX, err+48(FP)
CALL runtime·exitsyscall(SB)
RET
ok:
MOVQ AX, r1+32(FP)
MOVQ DX, r2+40(FP)
MOVQ $0, err+48(FP)
CALL runtime·exitsyscall(SB)
RET
相关参考文献 https://groups.google.com/forum/#!searchin/golang-nuts/Syscall$20RawSyscall/golang-nuts/y9lT_1loJj4/g4ZrYB2_80YJ 《The Linux Programming Interface —A Linux and UNIX System Programming Handbook》