在DragonOS实现一个系统调用
author:樊宙(BrahmaMantra)
系统调用是操作系统提供的一组接口,允许用户空间的应用程序与内核进行交互。通过系统调用,应用程序可以请求操作系统执行特定的服务,如文件操作、进程管理、内存管理和网络通信等。系统调用是用户态和内核之间的桥梁,确保应用程序能够安全、受控地访问系统资源。
相关思路
观察SYS_OPEN实现
我们在用户态使用open函数时,会切换到内核态去调用SYS_OPEN系统调用,
SYS_OPEN => {
let path = args[0] as *const u8;
let flags = args[1] as u32;
let mode = args[2] as u32;
Self::open(path, flags, mode, true)
}
- 简单来说,用户程序发起系统调用请求,内核通过syscall_handle捕获到系统调用后,我们需要提供一个系统调用号,以及对应的处理逻辑,返回一个结果
具体实现
在Dragonos添加系统调用号
kernel/src/syscall/mod.rs
SYS_ADD => {
// 这里应该对传入的参数进行合法性检查
// 此处仅供演示:
let a = args[0];
let b = args[1];
Ok(a + b)
}
编写测试程序
cd user/apps && cargo new test_SYS_ADD
编写源码,参考如下:
extern crate libc;
use std::ffi::CString;
use libc::{syscall, SYS_exit};
fn main() {
// 假设系统调用号 10086 接受两个整数参数并返回它们的和
let num1 = 5;
let num2 = 7;
unsafe {
// 使用 syscall 来调用系统调用 10086
let result: i32 = syscall(10086, num1, num2) as i32;
println!("syscall(SYS_ADD): ADD {} + {} = {}", num1, num2,result);
// 使用 assert 来验证返回结果
assert_eq!(result, num1 + num2, "System call returned incorrect result!");
println!("System call returned correct result: {}", result);
}
}
相关配置
可以参考别的测试程序下的配置进行相应处理,大多测试程序的配置都很相近
通过DADK编译与安装测试程序
DADK是DragonOS应用程序开发工具包,方便用户通过简单的配置文件,建立用户程序与内核之间的联系
touch user/dadk/config/test_SYS_ADD.toml
# 用户程序名称
name = "test_SYS_ADD"
# 版本号
version = "0.1.0"
# 用户程序描述信息
description = "测试SYS_ADD"
# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果
build-once = false
# (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装
install-once = false
# 目标架构
# 可选值:"x86_64", "aarch64", "riscv64"
target-arch = ["x86_64"]
# 任务源
[task-source]
# 构建类型
# 可选值:"build-from_source", "install-from-prebuilt"
type = "build-from-source"
# 构建来源
# "build_from_source" 可选值:"git", "local", "archive"
# "install_from_prebuilt" 可选值:"local", "archive"
source = "local"
# 路径或URL
source-path = "user/apps/test_SYS_ADD"
# 构建相关信息
[build]
# (可选)构建命令
build-command = "make install"
# 安装相关信息
[install]
# (可选)安装到DragonOS的路径
in-dragonos-path = "/"
# 清除相关信息
[clean]
# (可选)清除命令
clean-command = "make clean"
# (可选)依赖项
# 注意:如果没有依赖项,忽略此项,不允许只留一个[[depends]]
# 由于原文件中依赖项为空,此处省略[[depends]]部分
# (可选)环境变量
# 由于原文件中没有环境变量,此处省略[[envs]]部分
为测试程序写一个Makefile
在测试程序的路径下:
TOOLCHAIN="+nightly-2024-11-05-x86_64-unknown-linux-gnu"
RUSTFLAGS+=""
ifdef DADK_CURRENT_BUILD_DIR
# 如果是在dadk中编译,那么安装到dadk的安装目录中
INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
else
# 如果是在本地编译,那么安装到当前目录下的install目录中
INSTALL_DIR = ./install
endif
ifeq ($(ARCH), x86_64)
export RUST_TARGET=x86_64-unknown-linux-musl
else ifeq ($(ARCH), riscv64)
export RUST_TARGET=riscv64gc-unknown-linux-gnu
else
# 默认为x86_86,用于本地编译
export RUST_TARGET=x86_64-unknown-linux-musl
endif
run:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET)
build:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET)
clean:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET)
test:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET)
doc:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET)
fmt:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt
fmt-check:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check
run-release:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release
build-release:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release
clean-release:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release
test-release:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release
.PHONY: install
install:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force
测试效果如下:
root@DragonOS:/$ test_SYS_ADD
[ WARN ] (src/syscall/mod.rs:876) SYS_POLL has not yet been implemented
[ WARN ] (src/syscall/mod.rs:899) SYS_SIGALTSTACK has not yet been implemented
syscall(SYS_ADD): ADD 5 + 7 = 12
System call returned correct result: 12
[ WARN ] (src/syscall/mod.rs:904) SYS_EXIT_GROUP has not yet been implemented
root@DragonOS:/$
最后
欢迎同学们在认真思考的基础之上,互相之间进行交流,在论坛上发布自己的心得,如有疑问可咨询2033552517@qq.com或私信与我交流
- 附带内容:如何使用GDB调试内核