系统调用指的是用类似函数调用的方式调用操作系统提供的API。
概念上来说,系统调用和函数调用极为相似,本质上有很大不同。操作系统代码位于内核地址空间,而CPU在执行用户代码时,特权等级很低,无权访问需要最高优先级才能访问的内核地址空间的代码和数据,所以不能通过简单的call指令直接调用操作系统提供的函数,而需用特殊的指令进入操作系统内核完成指定的功能。
用户代码调用操作系统API也不是根据函数名直接调用,而是根据操作系统为每个API提供的一个整型编号来调用,AMD64 Linux平台约定在进行系统调用时使用rax存放系统调用编号,同时约定使用rdi、rsi、rdx、r10、r9、r8来传递前六个系统调用参数。
像最简单的Go向屏幕输出字符串操作,从打开文件、读写文件以及网络编程中创建socket等都使用了系统调用,平时编程中感觉没有用到系统调用,是因为使用的函数库或package将其封装成了函数,编程人员只需直接调用即可。
看下面这个例子:
package main
import (
"os"
)
func main() {
fd, err := os.Open("./syscall.go") // 将会使用系统调用打开文件
......
fd.Close() // 将会使用系统调用关闭文件
}
上述代码中的os.Open()和fd.Close()最终都是通过系统调用进入操作系统内核完成相应功能,如os.Open()最终会执行如下汇编代码通过openat系统调用打开文件:
mov 0x10(%rsp),%rdi #第1个参数
mov 0x18(%rsp),%rsi #第2个参数
mov 0x20(%rsp),%rdx #第3个参数
mov 0x28(%rsp),%r10 #第4个参数
mov 0x30(%rsp),%r8 #第5个参数
mov 0x38(%rsp),%r9 #第6个参数
mov 0x8(%rsp),%rax #系统调用编号 rax = 267,表示调用openat系统调用
syscall #系统调用指令,进入Linux内核
上述汇编代码首先将6个参数以及openat系统调用的编号267存入相应寄存器,然后通过syscall指令进入内核执行打开文件的功能。
好啦,到这里本文就结束了,喜欢的话就来个三连击吧。
扫码关注公众号,获取更多优质内容。