RISC-V Syscall 系列1:什么是 Syscall ?
本帖最后由 塞巴斯蒂安 于 2022-8-31 14:19 编辑Author:envestcc chen1233216@hotmail.com
Date: 2022/06/14
Revisor: walimis、Falcon
Project: RISC-V Linux 内核剖析
Sponsor: PLCT Lab, ISCAS
该系列活动推荐统一采用泰晓科技技术社区自研的 Linux Lab 开源实验环境,也可直接采用社区自研的免安装即插即跑 Linux Lab Disk(https://tinylab.org/linux-lab-disk),又称泰晓 Linux 实验盘,某宝检索“泰晓 Linux”可找到。
RISC-V Syscall 系列1:什么是 Syscall ?
一、什么是 Syscall ?
(图片源自 wikipedia)
Syscall 又称为系统调用,它是操作系统内核给用户态程序提供的一组 API,可以用来访问系统资源和内核提供的服务。比如用户态程序申请内存、读写文件等都需要通过 Syscall 完成。
通过 Linux 源码里可以看到(include/linux/syscalls.h),大约有 400 多个 Syscall。其中一部分是兼容 POSIX 标准,另一些是 Linux 特有的。
二、如何调用 Syscall ?
应用程序想要调用 Syscall 有两种方式,分别是直接调用和使用 C 标准库。
直接调用
下面我们通过一段汇编代码来看看如何直接调用 Syscall。
.data
msg:
.ascii "Hello, world!\n"
.text
.global _start
_start:
li a7, 64 # linux write syscall
li a0, 1 # stdout
la a1, msg # address of string
li a2, 14 # length of string
ecall # call linux syscall
li a7, 93 # linux exit syscall
li a0, 0 # return value
ecall # call linux syscall上面的代码的功能是通过系统调用往标准输出上打印一串字符。
$ cat test.S
.data
msg:
.ascii "Hello, world!\n"
.text
.global _start
_start:
li a7, 64
li a0, 1
la a1, msg
li a2, 14
ecall
li a7, 93
li a0, 0
ecall
$ riscv64-linux-gnu-gcc -c test.S
$ riscv64-linux-gnu-ld -o test test.o
$ qemu-riscv64 test
Hello, world!RISC-V 中通过 ecall 指令进行 Syscall 的调用。ecall 指令会将 CPU 从用户态转换到内核态,并跳转到 Syscall 的入口处。通过 a7 寄存器来标识是哪个 Syscall。至于调用 Syscall 要传递的参数则可以依次使用 a0-a5 这 6 个寄存器来存储。ecall 指令之前叫 scall,包括现在 Linux 源码里都用的是 scall,后来改为了 ecall。原因是该指令不仅可以用来进行系统调用,还可以提供更通用化的功能。
write 的系统调用号为 64,所以上述代码里将 64 存储到 a7 中。write 系统调用的参数有 3 个,第一个是文件描述符,第二个是要打印的字符串地址,第三个是字符串的长度,上述代码中将这三个参数分别存入到 a0、a1、a2 这三个寄存器中。
系统调用号列表可以在 Linux 源码中进行查看:include/uapi/asm-generic/unistd.h。
#define __NR_write 64
#define __NR_exit 93系统调用函数声明源码位置:include/linux/syscalls.h
asmlinkage long sys_write(unsigned int fd, const char __user *buf, size_t count);
asmlinkage long sys_exit(int error_code);
C 标准库
直接使用汇编调用 Syscall 比较繁琐也不安全,C 标准库提供了对 Syscall 的封装。
(图片源自 wikipedia)
下面用一段 C 代码例子看看如何使用 Syscall ,这种方式大家都比较熟悉。
#include <unistd.h>
int main() {
write(1, "Hello, world!\n", 14);
return 0;
}使用下面的命令进行测试即可输出结果。
$ cat testc.c
#include <unistd.h>
int main() {
write(1, "Hello, world!\n", 14);
return 0;
}
$ riscv64-linux-gnu-gcc -static testc.c -o testc
$ qemu-riscv64 testc
Hello, world!
三、总结
本篇文章主要从 Syscall 使用者的角度,阐述了什么是 Syscall。然后以实际代码为例,展示了在 RISC-V 架构下应用程序如何使用汇编代码和 C 标准库两种方式调用 Syscall 。
系列文章预告:RISC-V Syscall 系列2:Syscall 过程分析
参考资料
System call
syscall(2) — Linux manual page
Linux kernel interfaces
RISC-V Assembly Programmer's Manual
RISC-V架构下利用QEMU进行GDB调试
Risc-V Assembly Language Hello World
System Interface & Headers Reference
Misunderstanding RISC-V ecalls and syscalls
完
页:
[1]