系统调用指的是用类似函数调用的方式调用操作系统提供的API。
概念上来说,系统调用和函数调用极为相似,本质上有很大不同。操作系统代码位于内核地址空间,而CPU在执行用户代码时,特权等级很低,无权访问需要最高优先级才能访问的内核地址空间的代码和数据,所以不能通过简单的call指令直接调用操作系统提供的函数,而需用特殊的指令进入操作系统内核完成指定的功能。
用户代码调用操作系统API也不是根据函数名直接调用,而是根据操作系统为每个API提供的一个整型编号来调用,AMD64 Linux平台约定在进行系统调用时使用rax存放系统调用编号,同时约定使用rdi、rsi、rdx、r10、r9、r8来传递前六个系统调用参数。
像最简单的Go向屏幕输出字符串操作,从打开文件、读写文件以及网络编程中创建socket等都使用了系统调用,平时编程中感觉没有用到系统调用,是因为使用的函数库或package将其封装成了函数,编程人员只需直接调用即可。
看下面这个例子:
package mainimport ("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指令进入内核执行打开文件的功能。
好啦,到这里本文就结束了,喜欢的话就来个三连击吧。
扫码关注公众号,获取更多优质内容。

本文详细介绍了系统调用的概念,它与普通函数调用的不同之处在于,系统调用需要进入内核空间执行。通过示例展示了在Go语言中如何调用系统调用来打开和关闭文件,并提供了相应的汇编代码。文章强调,虽然程序员日常编程中可能感觉不到系统调用,但实际上很多底层操作如文件操作和网络编程都离不开系统调用。
816

被折叠的 条评论
为什么被折叠?



