go语言调度器源代码情景分析之八:系统调用

本文介绍了系统调用的基本概念,它是用户代码调用操作系统功能的桥梁。虽然系统调用在概念上类似于函数调用,但实际上由于权限级别的差异,需要通过特定的指令进入内核执行。文章以AMD64Linux平台为例,展示了如何使用汇编代码进行系统调用,并以Go语言代码为例说明了系统调用在日常编程中的隐蔽使用。
摘要由CSDN通过智能技术生成

以下内容转载自 https://mp.weixin.qq.com/s/CXhc71UZtbdbAo15xbJ6_Q

[我的博客地址](https://shankusu2017.github.io/)

原创 爱写程序的阿波张 源码游记 2019-04-24

我们将在最后一章讨论有关系统调用方面的抢占调度,所以这里有必要对系统调用有个基本的了解。

系统调用是指使用类似函数调用的方式调用操作系统提供的API。

虽然从概念上来说系统调用和函数调用差不多,但本质上它们有很大的不同,操作系统的代码位于内核地址空间,而CPU在执行用户代码时特权等级很低,无权访问需要最高优先级才能访问的内核地址空间的代码和数据,所以不能通过简单的call指令直接调用操作系统提供的函数,而需要使用特殊的指令进入操作系统内核完成指定的功能。

另外,用户代码调用操作系统API也不是根据函数名直接调用,而是需要根据操作系统为每个API提供的一个整型编号来调用,AMD64 Linux平台约定在进行系统调用时使用rax寄存器存放系统调用编号,同时约定使用rdi, rsi, rdx, r10, r8和r9来传递前6个系统调用参数。

可能有读者会说,我们平时编程也没有用到系统调用啊?!其实并不是没有用到,而是我们没有感觉到它的存在,比如最简单的向屏幕输出字符串,打开文件,读写文件以及网络编程中的创建socket等等都使用了系统调用,我们没有感觉到系统调用的存在主要是因为我们使用的函数库或package把它们封装成了函数,我们只需要直接调用这些函数就可以了。比如有下面一段go代码:

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指令进入内核执行打开文件的功能。


最后,如果你觉得本文对你有帮助的话,麻烦帮忙点一下文末右下角的 在看 或转发到朋友圈,非常感谢!

图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值