feat(syscall): 添加syscall table的实现 (#1164)

* feat(syscall): 添加syscall table的实现

- 实现syscall table
- 为syscall table适配write/writev、read和readv系统调用

---------

Signed-off-by: longjin <longjin@DragonOS.org>
This commit is contained in:
LoGin 2025-05-13 18:59:18 +08:00 committed by GitHub
parent 545bc2c346
commit b322121dd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 874 additions and 227 deletions

View File

@ -17,7 +17,7 @@ import os
# -- Project information -----------------------------------------------------
project = 'DragonOS'
copyright = '2022-2024, DragonOS Community'
copyright = '2022-2025, DragonOS Community'
author = 'longjin'
github_org = 'DragonOS-Community'
github_repo = 'DragonOS'
@ -31,7 +31,12 @@ release = 'dev'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['myst_parser', 'sphinx_multiversion']
extensions = [
'myst_parser',
'sphinx_multiversion',
'sphinxcontrib.mermaid',
'sphinx.ext.extlinks',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

View File

@ -33,6 +33,7 @@
kernel/container/index
kernel/libs/index
kernel/trace/index
kernel/syscall/index
@ -42,11 +43,6 @@
userland/appdev/index
.. toctree::
:maxdepth: 1
:caption: 系统调用api文档
syscall_api/index
.. toctree::
:maxdepth: 1

View File

@ -67,7 +67,7 @@ Syscall: │ sys_open, sys_read, sys_write, sys_close, │
- `sys_fchmod`:修改文件权限(未实现)
- 其他系统调用接口(未实现)
&emsp;&emsp;关于接口的具体含义,可以参考 [DragonOS系统调用接口](../../syscall_api/index.rst)
&emsp;&emsp;关于接口的具体含义,可以参考Linux的相关文档
## 虚拟文件系统VFS

View File

@ -1,10 +1,10 @@
.. _syscall_api:
.. _syscall:
系统调用API
系统调用
====================================
.. toctree::
:maxdepth: 1
:caption: 目录
intro
syscall_table

View File

@ -0,0 +1,120 @@
系统调用表实现方案
====================
.. note::
Author: longjin <longjin@dragonos.org>
Date: 2025/05/13
概述
----
.. mermaid::
:align: center
:caption: 系统调用表架构
classDiagram
class Syscall {
<<trait>>
+num_args() usize
+handle(args, from_user) Result<usize, SystemError>
+entry_format(args) Vec<FormattedSyscallParam>
}
class SyscallHandle {
+nr: usize
+inner_handle: &dyn Syscall
}
class SyscallTable {
-entries: [Option<&SyscallHandle>; 512]
+get(nr) Option<&dyn Syscall>
}
Syscall <|.. SysXXXXXXHandle
SyscallHandle "1" *-- "1" Syscall
SyscallTable "1" *-- "512" SyscallHandle
相比于将原本集中在一个大match中的系统调用分发本方案采用基于trait和系统调用表的实现。主要优势包括
- 降低栈内存使用:避免单个大函数占用过多栈空间
- 支持参数打印:通过统一的参数格式化接口
- 更好的扩展性:新增系统调用无需修改分发逻辑
核心设计
--------
Syscall Trait
~~~~~~~~~~~~~
所有系统调用处理函数都需要实现 `Syscall` trait
.. code-block:: rust
pub trait Syscall: Send + Sync + 'static {
fn num_args(&self) -> usize;
fn handle(&self, args: &[usize], from_user: bool) -> Result<usize, SystemError>;
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam>;
}
- `num_args()`: 返回该系统调用需要的参数数量
- `handle()`: 实际执行系统调用处理
- `entry_format()`: 格式化参数用于调试打印
SyscallHandle
~~~~~~~~~~~~~
`SyscallHandle` 结构体将系统调用号与处理函数关联:
.. code-block:: rust
pub struct SyscallHandle {
pub nr: usize, // 系统调用号
pub inner_handle: &'static dyn Syscall, // 处理函数
pub name: &'static str,
}
SyscallTable
~~~~~~~~~~~~
`SyscallTable` 管理所有系统调用:
- 固定大小512项
- 编译时初始化
- 通过系统调用号快速查找处理函数
使用方式
--------
实现系统调用
~~~~~~~~~~~~
1. 定义实现``Syscall`` trait的结构体
2. 实现``handle()````entry_format()``方法
3. 使用``declare_syscall!``宏注册
参考实现:`sys_write.rs <sys_write_>`_
.. _sys_write:
https://github.com/DragonOS-Community/DragonOS/blob/master/kernel/src/filesystem/vfs/syscall/sys_write.rs
注册系统调用
~~~~~~~~~~~~
使用``declare_syscall!``宏注册系统调用:
.. code-block:: rust
syscall_table_macros::declare_syscall!(SYS_WRITE, SysWriteHandle);
参数说明:
1. 系统调用名称(用于生成符号)
2. 实现``Syscall`` trait的结构体
初始化流程
----------
1. 内核启动时调用``syscall_table_init()``
2. 从链接器符号``_syscall_table``加载所有注册的系统调用
3. 填充系统调用表

View File

@ -1,4 +1,5 @@
sphinx==5.0.2
myst-parser==0.18.0
sphinx-rtd-theme
sphinxcontrib-mermaid==1.0.0
git+https://git.mirrors.dragonos.org.cn/DragonOS-Community/sphinx-multiversion.git@5858b75#egg=sphinx-multiversion

View File

@ -1 +0,0 @@
# 简介

5
kernel/Cargo.lock generated
View File

@ -531,6 +531,7 @@ dependencies = [
"slabmalloc",
"smoltcp",
"static-keys",
"syscall_table_macros",
"system_error",
"uefi",
"uefi-raw",
@ -1665,6 +1666,10 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syscall_table_macros"
version = "0.1.0"
[[package]]
name = "system_error"
version = "0.1.0"

View File

@ -60,6 +60,7 @@ smoltcp = { version = "=0.11.0", default-features = false, features = [
"proto-ipv4",
"proto-ipv6",
] }
syscall_table_macros = { path = "crates/syscall_table_macros" }
system_error = { path = "crates/system_error" }
uefi = { version = "=0.26.0", features = ["alloc"] }
uefi-raw = "=0.5.0"

View File

@ -0,0 +1,7 @@
[package]
name = "syscall_table_macros"
version = "0.1.0"
edition = "2021"
[dependencies]

View File

@ -0,0 +1,19 @@
#![no_std]
#![deny(clippy::all)]
#[macro_export]
#[allow(clippy::crate_in_macro_def)]
macro_rules! declare_syscall {
($nr:ident, $inner_handle:ident) => {
paste::paste! {
#[allow(non_upper_case_globals)]
#[link_section = ".syscall_table"]
#[used]
pub static [<HANDLE_ $nr>]: crate::syscall::table::SyscallHandle = crate::syscall::table::SyscallHandle {
nr: $nr,
inner_handle: &$inner_handle,
name: stringify!($nr),
};
}
};
}

View File

@ -74,8 +74,18 @@ SECTIONS
*(.tracepoint.*)
_etracepoint = .;
}
. = ALIGN(32768);
. = ALIGN(4096);
syscall_table_start_pa = .;
.syscall_table (syscall_table_start_pa):
{
_syscall_table = .;
*(.syscall_table)
*(.syscall_table.*)
_esyscall_table = .;
}
. = ALIGN(32768);
init_proc_union_start_pa = .;
.data.init_proc_union (init_proc_union_start_pa):
{ *(.data.init_proc_union) }

View File

@ -76,8 +76,19 @@ SECTIONS
*(.tracepoint.*)
_etracepoint = .;
}
. = ALIGN(32768);
. = ALIGN(4096);
syscall_table_start_pa = .;
.syscall_table (syscall_table_start_pa): AT(syscall_table_start_pa - KERNEL_VMA)
{
_syscall_table = .;
*(.syscall_table)
*(.syscall_table.*)
_esyscall_table = .;
}
. = ALIGN(32768);
init_proc_union_start_pa = .;
.data.init_proc_union (init_proc_union_start_pa): AT(init_proc_union_start_pa - KERNEL_VMA)
{ *(.data.init_proc_union) }

View File

@ -40,7 +40,7 @@ SECTIONS
_etext = .;
__etext = .;
}
. = ALIGN(32768);
. = ALIGN(4096);
data_start_pa = .;
.data (data_start_pa): AT(data_start_pa - KERNEL_VMA)
{
@ -51,7 +51,7 @@ SECTIONS
_edata = .;
}
. = ALIGN(32768);
. = ALIGN(4096);
rodata_start_pa = .;
.rodata (rodata_start_pa): AT(rodata_start_pa - KERNEL_VMA)
@ -65,7 +65,7 @@ SECTIONS
_erodata = .;
}
. = ALIGN(32768);
. = ALIGN(4096);
trace_point_start_pa = .;
.tracepoint (trace_point_start_pa): AT(trace_point_start_pa - KERNEL_VMA)
@ -75,8 +75,18 @@ SECTIONS
*(.tracepoint.*)
_etracepoint = .;
}
. = ALIGN(32768);
. = ALIGN(4096);
syscall_table_start_pa = .;
.syscall_table (syscall_table_start_pa): AT(syscall_table_start_pa - KERNEL_VMA)
{
_syscall_table = .;
*(.syscall_table)
*(.syscall_table.*)
_esyscall_table = .;
}
. = ALIGN(32768);
init_proc_union_start_pa = .;
.data.init_proc_union (init_proc_union_start_pa): AT(init_proc_union_start_pa - KERNEL_VMA)
{ *(.data.init_proc_union) }

View File

@ -4,7 +4,6 @@ use alloc::{
sync::{Arc, Weak},
vec::Vec,
};
use log::debug;
use system_error::SystemError;
use crate::{
@ -106,7 +105,7 @@ impl MbrDiskPartionTable {
table.dpte[i].starting_lba = cursor.read_u32()?;
table.dpte[i].total_sectors = cursor.read_u32()?;
debug!("dpte[{i}] = {:?}", table.dpte[i]);
// debug!("dpte[{i}] = {:?}", table.dpte[i]);
}
table.bs_trailsig = cursor.read_u16()?;
// debug!("bs_trailsig = {}", unsafe {

View File

@ -0,0 +1,149 @@
use alloc::vec::Vec;
use system_error::SystemError;
use crate::syscall::user_access::{UserBufferReader, UserBufferWriter};
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct IoVec {
/// 缓冲区的起始地址
pub iov_base: *mut u8,
/// 缓冲区的长度
pub iov_len: usize,
}
/// 用于存储多个来自用户空间的IoVec
///
/// 由于目前内核中的文件系统还不支持分散读写所以暂时只支持将用户空间的IoVec聚合成一个缓冲区然后进行操作。
/// TODO支持分散读写
#[derive(Debug)]
pub struct IoVecs(Vec<IoVec>);
impl IoVecs {
/// 获取IoVecs中所有缓冲区的总长度
#[inline(never)]
pub fn total_len(&self) -> usize {
self.0.iter().map(|x| x.iov_len).sum()
}
/// Constructs `IoVecs` from an array of `IoVec` in userspace.
///
/// # Arguments
///
/// * `iov` - Pointer to the array of `IoVec` in userspace
/// * `iovcnt` - Number of `IoVec` elements in the array
/// * `readv` - Whether this is for the `readv` syscall (currently unused)
///
/// # Returns
///
/// Returns `Ok(IoVecs)` on success, or `Err(SystemError)` if any error occurs.
///
/// # Safety
///
/// This function is unsafe because it operates on raw pointers from userspace.
/// The caller must ensure:
/// - The pointer `iov` is valid and points to at least `iovcnt` valid `IoVec` structures
/// - The userspace memory is not modified during this operation
#[inline(never)]
pub unsafe fn from_user(
iov: *const IoVec,
iovcnt: usize,
_readv: bool,
) -> Result<Self, SystemError> {
let iovs_reader = UserBufferReader::new(iov, iovcnt * core::mem::size_of::<IoVec>(), true)?;
// 将用户空间的IoVec转换为引用注意这里的引用是静态的因为用户空间的IoVec不会被释放
let iovs = iovs_reader.buffer::<IoVec>(0)?;
let mut slices: Vec<IoVec> = Vec::with_capacity(iovs.len());
for iov in iovs.iter() {
if iov.iov_len == 0 {
continue;
}
let _ = UserBufferWriter::new(iov.iov_base, iov.iov_len, true)?;
slices.push(*iov);
}
return Ok(Self(slices));
}
/// Aggregates data from all IoVecs into a single buffer.
///
/// This function reads data from each IoVec in sequence and combines them into
/// a single contiguous buffer.
///
/// # Returns
///
/// Returns a [`Vec<u8>`] containing all the data from the IoVecs.
///
/// # Examples
///
/// ```rust
/// let iovecs = IoVecs::from_user(/* ... */)?;
/// let buffer = iovecs.gather();
/// ```
pub fn gather(&self) -> Vec<u8> {
let mut buf = Vec::new();
for slice in self.0.iter() {
let buf_reader = UserBufferReader::new(slice.iov_base, slice.iov_len, true).unwrap();
let slice = buf_reader.buffer::<u8>(0).unwrap();
buf.extend_from_slice(slice);
}
return buf;
}
/// Scatters the given data into the IoVecs.
///
/// This function writes data sequentially to each IoVec, splitting the input data
/// across multiple buffers as needed. If the input data is smaller than the total
/// capacity of the IoVecs, only the required amount of data will be written.
/// If the input data is larger than the total capacity, the excess data will be ignored.
///
/// # Arguments
///
/// * `data` - The data to be scattered across the IoVecs
///
/// # Examples
///
/// ```rust
/// let iovecs = IoVecs::from_user(/* ... */)?;
/// iovecs.scatter(&[1, 2, 3, 4, 5]);
/// ```
pub fn scatter(&self, data: &[u8]) {
let mut data: &[u8] = data;
for slice in self.0.iter() {
let len = core::cmp::min(slice.iov_len, data.len());
if len == 0 {
continue;
}
let mut buf_writer =
UserBufferWriter::new(slice.iov_base, slice.iov_len, true).unwrap();
let slice = buf_writer.buffer::<u8>(0).unwrap();
slice[..len].copy_from_slice(&data[..len]);
data = &data[len..];
}
}
/// Creates a buffer with capacity equal to the total length of all IoVecs.
///
/// # Arguments
///
/// * `set_len` - If true, sets the length of the returned Vec to the total length of all IoVecs.
/// If false, the returned Vec will have length 0 but capacity equal to the total length.
///
/// # Returns
///
/// A new [`Vec<u8>`] with capacity (and potentially length) equal to the total length of all IoVecs.
pub fn new_buf(&self, set_len: bool) -> Vec<u8> {
let total_len = self.total_len();
let mut buf: Vec<u8> = Vec::with_capacity(total_len);
if set_len {
buf.resize(total_len, 0);
}
return buf;
}
}

View File

@ -1,5 +1,6 @@
pub mod fcntl;
pub mod file;
pub mod iov;
pub mod mount;
pub mod open;
pub mod stat;

View File

@ -12,7 +12,7 @@ use crate::{
driver::base::{block::SeekFrom, device::device_number::DeviceNumber},
filesystem::vfs::{file::FileDescriptorVec, vcore as Vcore},
libs::rwlock::RwLockWriteGuard,
mm::{verify_area, VirtAddr},
mm::VirtAddr,
process::ProcessManager,
syscall::{
user_access::{self, check_and_clone_cstr, UserBufferWriter},
@ -35,6 +35,11 @@ use super::{
VFS_MAX_FOLLOW_SYMLINK_TIMES,
};
mod sys_read;
mod sys_readv;
mod sys_write;
mod sys_writev;
pub const SEEK_SET: u32 = 0;
pub const SEEK_CUR: u32 = 1;
pub const SEEK_END: u32 = 2;
@ -497,48 +502,6 @@ impl Syscall {
return r;
}
/// @brief 根据文件描述符读取文件数据。尝试读取的数据长度与buf的长度相同。
///
/// @param fd 文件描述符编号
/// @param buf 输出缓冲区
///
/// @return Ok(usize) 成功读取的数据的字节数
/// @return Err(SystemError) 读取失败返回posix错误码
pub fn read(fd: i32, buf: &mut [u8]) -> Result<usize, SystemError> {
let binding = ProcessManager::current_pcb().fd_table();
let fd_table_guard = binding.read();
let file = fd_table_guard.get_file_by_fd(fd);
if file.is_none() {
return Err(SystemError::EBADF);
}
// drop guard 以避免无法调度的问题
drop(fd_table_guard);
let file = file.unwrap();
return file.read(buf.len(), buf);
}
/// @brief 根据文件描述符向文件写入数据。尝试写入的数据长度与buf的长度相同。
///
/// @param fd 文件描述符编号
/// @param buf 输入缓冲区
///
/// @return Ok(usize) 成功写入的数据的字节数
/// @return Err(SystemError) 写入失败返回posix错误码
pub fn write(fd: i32, buf: &[u8]) -> Result<usize, SystemError> {
let binding = ProcessManager::current_pcb().fd_table();
let fd_table_guard = binding.read();
let file = fd_table_guard
.get_file_by_fd(fd)
.ok_or(SystemError::EBADF)?;
// drop guard 以避免无法调度的问题
drop(fd_table_guard);
return file.write(buf.len(), buf);
}
/// @brief 调整文件操作指针的位置
///
/// @param fd 文件描述符编号
@ -1421,28 +1384,6 @@ impl Syscall {
return Ok(0);
}
pub fn writev(fd: i32, iov: usize, count: usize) -> Result<usize, SystemError> {
// IoVecs会进行用户态检验
let iovecs = unsafe { IoVecs::from_user(iov as *const IoVec, count, false) }?;
let data = iovecs.gather();
Self::write(fd, &data)
}
pub fn readv(fd: i32, iov: usize, count: usize) -> Result<usize, SystemError> {
// IoVecs会进行用户态检验
let mut iovecs = unsafe { IoVecs::from_user(iov as *const IoVec, count, true) }?;
let mut data = vec![0; iovecs.0.iter().map(|x| x.len()).sum()];
let len = Self::read(fd, &mut data)?;
iovecs.scatter(&data[..len]);
return Ok(len);
}
pub fn readlink_at(
dirfd: i32,
path: *const u8,
@ -1675,104 +1616,3 @@ impl Syscall {
do_utimes(&pathname, times)
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct IoVec {
/// 缓冲区的起始地址
pub iov_base: *mut u8,
/// 缓冲区的长度
pub iov_len: usize,
}
/// 用于存储多个来自用户空间的IoVec
///
/// 由于目前内核中的文件系统还不支持分散读写所以暂时只支持将用户空间的IoVec聚合成一个缓冲区然后进行操作。
/// TODO支持分散读写
#[derive(Debug)]
pub struct IoVecs(Vec<&'static mut [u8]>);
impl IoVecs {
/// 从用户空间的IoVec中构造IoVecs
///
/// @param iov 用户空间的IoVec
/// @param iovcnt 用户空间的IoVec的数量
/// @param readv 是否为readv系统调用
///
/// @return 构造成功返回IoVecs否则返回错误码
pub unsafe fn from_user(
iov: *const IoVec,
iovcnt: usize,
_readv: bool,
) -> Result<Self, SystemError> {
// 检查iov指针所在空间是否合法
verify_area(
VirtAddr::new(iov as usize),
iovcnt * core::mem::size_of::<IoVec>(),
)
.map_err(|_| SystemError::EFAULT)?;
// 将用户空间的IoVec转换为引用注意这里的引用是静态的因为用户空间的IoVec不会被释放
let iovs: &[IoVec] = core::slice::from_raw_parts(iov, iovcnt);
let mut slices: Vec<&mut [u8]> = Vec::with_capacity(iovs.len());
for iov in iovs.iter() {
if iov.iov_len == 0 {
continue;
}
verify_area(
VirtAddr::new(iov.iov_base as usize),
iovcnt * core::mem::size_of::<IoVec>(),
)
.map_err(|_| SystemError::EFAULT)?;
slices.push(core::slice::from_raw_parts_mut(iov.iov_base, iov.iov_len));
}
return Ok(Self(slices));
}
/// @brief 将IoVecs中的数据聚合到一个缓冲区中
///
/// @return 返回聚合后的缓冲区
pub fn gather(&self) -> Vec<u8> {
let mut buf = Vec::new();
for slice in self.0.iter() {
buf.extend_from_slice(slice);
}
return buf;
}
/// @brief 将给定的数据分散写入到IoVecs中
pub fn scatter(&mut self, data: &[u8]) {
let mut data: &[u8] = data;
for slice in self.0.iter_mut() {
let len = core::cmp::min(slice.len(), data.len());
if len == 0 {
continue;
}
slice[..len].copy_from_slice(&data[..len]);
data = &data[len..];
}
}
/// @brief 创建与IoVecs等长的缓冲区
///
/// @param set_len 是否设置返回的Vec的len。
/// 如果为true则返回的Vec的len为所有IoVec的长度之和;
/// 否则返回的Vec的len为0capacity为所有IoVec的长度之和.
///
/// @return 返回创建的缓冲区
pub fn new_buf(&self, set_len: bool) -> Vec<u8> {
let total_len: usize = self.0.iter().map(|slice| slice.len()).sum();
let mut buf: Vec<u8> = Vec::with_capacity(total_len);
if set_len {
buf.resize(total_len, 0);
}
return buf;
}
}

View File

@ -0,0 +1,105 @@
use system_error::SystemError;
use crate::arch::syscall::nr::SYS_READ;
use crate::process::ProcessManager;
use crate::syscall::table::FormattedSyscallParam;
use crate::syscall::table::Syscall;
use crate::syscall::user_access::UserBufferWriter;
use alloc::string::ToString;
use alloc::vec::Vec;
/// System call handler for the `read` syscall
///
/// This handler implements the `Syscall` trait to provide functionality for reading data from a file descriptor.
pub struct SysReadHandle;
impl Syscall for SysReadHandle {
/// Returns the number of arguments expected by the `read` syscall
fn num_args(&self) -> usize {
3
}
/// Handles the `read` system call
///
/// Reads data from the specified file descriptor into a user buffer.
///
/// # Arguments
/// * `args` - Array containing:
/// - args[0]: File descriptor (i32)
/// - args[1]: Pointer to user buffer (*mut u8)
/// - args[2]: Length of data to read (usize)
/// * `from_user` - Indicates if the call originates from user space
///
/// # Returns
/// * `Ok(usize)` - Number of bytes successfully read
/// * `Err(SystemError)` - Error code if operation fails
fn handle(&self, args: &[usize], from_user: bool) -> Result<usize, SystemError> {
let fd = Self::fd(args);
let buf_vaddr = Self::buf(args);
let len = Self::len(args);
let mut user_buffer_writer = UserBufferWriter::new(buf_vaddr, len, from_user)?;
let user_buf = user_buffer_writer.buffer(0)?;
do_read(fd, user_buf)
}
/// Formats the syscall parameters for display/debug purposes
///
/// # Arguments
/// * `args` - The raw syscall arguments
///
/// # Returns
/// Vector of formatted parameters with descriptive names
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
vec![
FormattedSyscallParam::new("fd", Self::fd(args).to_string()),
FormattedSyscallParam::new("buf", format!("{:#x}", Self::buf(args) as usize)),
FormattedSyscallParam::new("len", Self::len(args).to_string()),
]
}
}
impl SysReadHandle {
/// Extracts the file descriptor from syscall arguments
fn fd(args: &[usize]) -> i32 {
args[0] as i32
}
/// Extracts the buffer pointer from syscall arguments
fn buf(args: &[usize]) -> *mut u8 {
args[1] as *mut u8
}
/// Extracts the buffer length from syscall arguments
fn len(args: &[usize]) -> usize {
args[2]
}
}
syscall_table_macros::declare_syscall!(SYS_READ, SysReadHandle);
/// Internal implementation of the read operation
///
/// # Arguments
/// * `fd` - File descriptor to read from
/// * `buf` - Buffer to store read data
///
/// # Returns
/// * `Ok(usize)` - Number of bytes successfully read
/// * `Err(SystemError)` - Error code if operation fails
pub(super) fn do_read(fd: i32, buf: &mut [u8]) -> Result<usize, SystemError> {
let binding = ProcessManager::current_pcb().fd_table();
let fd_table_guard = binding.read();
let file = fd_table_guard.get_file_by_fd(fd);
if file.is_none() {
return Err(SystemError::EBADF);
}
// drop guard 以避免无法调度的问题
drop(fd_table_guard);
let file = file.unwrap();
return file.read(buf.len(), buf);
}

View File

@ -0,0 +1,65 @@
use system_error::SystemError;
use crate::arch::syscall::nr::SYS_READV;
use crate::filesystem::vfs::iov::IoVec;
use crate::filesystem::vfs::iov::IoVecs;
use crate::syscall::table::FormattedSyscallParam;
use crate::syscall::table::Syscall;
use alloc::string::ToString;
use alloc::vec::Vec;
use super::sys_read::do_read;
/// System call handler for `readv` operation
///
/// The `readv` system call reads data into multiple buffers from a file descriptor.
/// It is equivalent to multiple `read` calls but is more efficient.
pub struct SysReadVHandle;
impl Syscall for SysReadVHandle {
fn num_args(&self) -> usize {
3
}
fn handle(&self, args: &[usize], _from_user: bool) -> Result<usize, SystemError> {
let fd = Self::fd(args);
let iov = Self::iov(args);
let count = Self::count(args);
// IoVecs会进行用户态检验
let iovecs = unsafe { IoVecs::from_user(iov, count, true) }?;
let mut data = vec![0; iovecs.total_len()];
let len = do_read(fd, &mut data)?;
iovecs.scatter(&data[..len]);
return Ok(len);
}
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
vec![
FormattedSyscallParam::new("fd", Self::fd(args).to_string()),
FormattedSyscallParam::new("iov", format!("{:#x}", Self::iov(args) as usize)),
FormattedSyscallParam::new("count", Self::count(args).to_string()),
]
}
}
impl SysReadVHandle {
fn fd(args: &[usize]) -> i32 {
args[0] as i32
}
fn iov(args: &[usize]) -> *const IoVec {
args[1] as *const IoVec
}
fn count(args: &[usize]) -> usize {
args[2]
}
}
syscall_table_macros::declare_syscall!(SYS_READV, SysReadVHandle);

View File

@ -0,0 +1,104 @@
use system_error::SystemError;
use crate::arch::syscall::nr::SYS_WRITE;
use crate::process::ProcessManager;
use crate::syscall::table::FormattedSyscallParam;
use crate::syscall::table::Syscall;
use crate::syscall::user_access::UserBufferReader;
use alloc::string::ToString;
use alloc::vec::Vec;
/// System call handler for the `write` syscall
///
/// This handler implements the `Syscall` trait to provide functionality for writing data to a file descriptor.
pub struct SysWriteHandle;
impl Syscall for SysWriteHandle {
/// Returns the number of arguments expected by the `write` syscall
fn num_args(&self) -> usize {
3
}
/// Handles the `write` system call
///
/// Writes data from a user buffer to the specified file descriptor.
///
/// # Arguments
/// * `args` - Array containing:
/// - args[0]: File descriptor (i32)
/// - args[1]: Pointer to user buffer (*const u8)
/// - args[2]: Length of data to write (usize)
/// * `from_user` - Indicates if the call originates from user space
///
/// # Returns
/// * `Ok(usize)` - Number of bytes successfully written
/// * `Err(SystemError)` - Error code if operation fails
fn handle(&self, args: &[usize], from_user: bool) -> Result<usize, SystemError> {
let fd = Self::fd(args);
let buf_vaddr = Self::buf(args);
let len = Self::len(args);
let user_buffer_reader = UserBufferReader::new(buf_vaddr, len, from_user)?;
let user_buf = user_buffer_reader.read_from_user(0)?;
do_write(fd, user_buf)
}
/// Formats the syscall parameters for display/debug purposes
///
/// # Arguments
/// * `args` - The raw syscall arguments
///
/// # Returns
/// Vector of formatted parameters with descriptive names
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
vec![
FormattedSyscallParam::new("fd", Self::fd(args).to_string()),
FormattedSyscallParam::new("buf", format!("{:#x}", Self::buf(args) as usize)),
FormattedSyscallParam::new("len", Self::len(args).to_string()),
]
}
}
impl SysWriteHandle {
/// Extracts the file descriptor from syscall arguments
fn fd(args: &[usize]) -> i32 {
args[0] as i32
}
/// Extracts the buffer pointer from syscall arguments
fn buf(args: &[usize]) -> *const u8 {
args[1] as *const u8
}
/// Extracts the buffer length from syscall arguments
fn len(args: &[usize]) -> usize {
args[2]
}
}
syscall_table_macros::declare_syscall!(SYS_WRITE, SysWriteHandle);
/// Internal implementation of the write operation
///
/// # Arguments
/// * `fd` - File descriptor to write to
/// * `buf` - Buffer containing data to write
///
/// # Returns
/// * `Ok(usize)` - Number of bytes successfully written
/// * `Err(SystemError)` - Error code if operation fails
pub(super) fn do_write(fd: i32, buf: &[u8]) -> Result<usize, SystemError> {
let binding = ProcessManager::current_pcb().fd_table();
let fd_table_guard = binding.read();
let file = fd_table_guard
.get_file_by_fd(fd)
.ok_or(SystemError::EBADF)?;
// drop guard 以避免无法调度的问题
drop(fd_table_guard);
return file.write(buf.len(), buf);
}

View File

@ -0,0 +1,85 @@
use system_error::SystemError;
use crate::arch::syscall::nr::SYS_WRITEV;
use crate::filesystem::vfs::iov::IoVec;
use crate::filesystem::vfs::iov::IoVecs;
use crate::syscall::table::FormattedSyscallParam;
use crate::syscall::table::Syscall;
use alloc::string::ToString;
use alloc::vec::Vec;
use super::sys_write::do_write;
/// System call handler for `writev` operation
///
/// The `writev` system call writes data from multiple buffers to a file descriptor.
/// It is equivalent to multiple `write` calls but is more efficient.
pub struct SysWriteVHandle;
impl Syscall for SysWriteVHandle {
/// Returns the number of arguments required by the `writev` system call
fn num_args(&self) -> usize {
3
}
/// Handles the `writev` system call
///
/// # Arguments
/// * `args` - System call arguments containing:
/// * `fd`: File descriptor to write to
/// * `iov`: Pointer to array of I/O vectors
/// * `count`: Number of elements in the I/O vector array
/// * `_from_user` - Flag indicating if the call originated from user space
///
/// # Returns
/// * `Ok(usize)` - Number of bytes written
/// * `Err(SystemError)` - Error that occurred during operation
///
/// # Safety
/// The caller must ensure the `iov` pointer is valid and points to properly initialized memory.
fn handle(&self, args: &[usize], _from_user: bool) -> Result<usize, SystemError> {
let fd = Self::fd(args);
let iov = Self::iov(args);
let count = Self::count(args);
// IoVecs会进行用户态检验
let iovecs = unsafe { IoVecs::from_user(iov, count, false) }?;
let data = iovecs.gather();
do_write(fd, &data)
}
/// Formats the system call parameters for display/debug purposes
///
/// # Arguments
/// * `args` - System call arguments to format
///
/// # Returns
/// Vector of formatted parameters with their names and values
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
vec![
FormattedSyscallParam::new("fd", Self::fd(args).to_string()),
FormattedSyscallParam::new("iov", format!("{:#x}", Self::iov(args) as usize)),
FormattedSyscallParam::new("count", Self::count(args).to_string()),
]
}
}
impl SysWriteVHandle {
/// Extracts the file descriptor from system call arguments
fn fd(args: &[usize]) -> i32 {
args[0] as i32
}
/// Extracts the I/O vector pointer from system call arguments
fn iov(args: &[usize]) -> *const IoVec {
args[1] as *const IoVec
}
/// Extracts the I/O vector count from system call arguments
fn count(args: &[usize]) -> usize {
args[2]
}
}
syscall_table_macros::declare_syscall!(SYS_WRITEV, SysWriteVHandle);

View File

@ -23,7 +23,7 @@ use crate::{
process::{kthread::kthread_init, process_init, ProcessManager},
sched::SchedArch,
smp::{early_smp_init, SMPArch},
syscall::Syscall,
syscall::{syscall_init, Syscall},
time::{
clocksource::clocksource_boot_finish, timekeeping::timekeeping_init, timer::timer_init,
},
@ -69,6 +69,8 @@ fn do_start_kernel() {
init_intertrait();
syscall_init().expect("syscall init failed");
vfs_init().expect("vfs init failed");
driver_init().expect("driver init failed");

View File

@ -8,7 +8,7 @@ use system_error::SystemError;
use crate::{
filesystem::vfs::{
file::{File, FileMode},
syscall::{IoVec, IoVecs},
iov::{IoVec, IoVecs},
FileType,
},
libs::spinlock::SpinLockGuard,
@ -295,7 +295,7 @@ impl Syscall {
/// @return 成功返回接收的字节数,失败返回错误码
pub fn recvmsg(fd: usize, msg: &mut MsgHdr, _flags: u32) -> Result<usize, SystemError> {
// 检查每个缓冲区地址是否合法生成iovecs
let mut iovs = unsafe { IoVecs::from_user(msg.msg_iov, msg.msg_iovlen, true)? };
let iovs = unsafe { IoVecs::from_user(msg.msg_iov, msg.msg_iovlen, true)? };
let socket: Arc<SocketInode> = ProcessManager::current_pcb()
.get_socket(fd as i32)

View File

@ -23,6 +23,7 @@ use crate::{
use log::{info, warn};
use num_traits::FromPrimitive;
use system_error::SystemError;
use table::{syscall_table, syscall_table_init};
use crate::{
arch::{interrupt::TrapFrame, MMArch},
@ -49,6 +50,7 @@ use self::{
};
pub mod misc;
pub mod table;
pub mod user_access;
// 与linux不一致的调用在linux基础上累加
@ -99,6 +101,18 @@ impl Syscall {
args: &[usize],
frame: &mut TrapFrame,
) -> Result<usize, SystemError> {
// 首先尝试从syscall_table获取处理函数
if let Some(handler) = syscall_table().get(syscall_num) {
// 使用以下代码可以打印系统调用号和参数,方便调试
// log::debug!(
// "Syscall {} called with args {}",
// handler.name,
// handler.args_string(args)
// );
return handler.inner_handle.handle(args, frame.is_from_user());
}
// 如果找不到fallback到原有逻辑
let r = match syscall_num {
SYS_PUT_STRING => {
Self::put_string(args[0] as *const u8, args[1] as u32, args[2] as u32)
@ -155,28 +169,6 @@ impl Syscall {
let fd = args[0];
Self::close(fd)
}
SYS_READ => {
let fd = args[0] as i32;
let buf_vaddr = args[1];
let len = args[2];
let from_user = frame.is_from_user();
let mut user_buffer_writer =
UserBufferWriter::new(buf_vaddr as *mut u8, len, from_user)?;
let user_buf = user_buffer_writer.buffer(0)?;
Self::read(fd, user_buf)
}
SYS_WRITE => {
let fd = args[0] as i32;
let buf_vaddr = args[1];
let len = args[2];
let from_user = frame.is_from_user();
let user_buffer_reader =
UserBufferReader::new(buf_vaddr as *const u8, len, from_user)?;
let user_buf = user_buffer_reader.read_from_user(0)?;
Self::write(fd, user_buf)
}
SYS_LSEEK => {
let fd = args[0] as i32;
@ -781,9 +773,6 @@ impl Syscall {
return ret;
}
SYS_READV => Self::readv(args[0] as i32, args[1], args[2]),
SYS_WRITEV => Self::writev(args[0] as i32, args[1], args[2]),
SYS_SET_TID_ADDRESS => Self::set_tid_address(args[0]),
#[cfg(target_arch = "x86_64")]
@ -1267,3 +1256,9 @@ impl Syscall {
return Ok(s.len());
}
}
#[inline(never)]
pub fn syscall_init() -> Result<(), SystemError> {
syscall_table_init()?;
Ok(())
}

129
kernel/src/syscall/table.rs Normal file
View File

@ -0,0 +1,129 @@
#![allow(unused)]
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::cell::OnceCell;
use core::fmt::Display;
use crate::libs::once::Once;
use crate::syscall::SystemError;
/// 定义Syscall trait
pub trait Syscall: Send + Sync + 'static {
/// 系统调用参数数量
fn num_args(&self) -> usize;
fn handle(&self, args: &[usize], from_user: bool) -> Result<usize, SystemError>;
/// Formats the system call parameters for display/debug purposes
///
/// # Arguments
/// * `args` - System call arguments to format
///
/// # Returns
/// Vector of formatted parameters with their names and values
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam>;
}
pub struct FormattedSyscallParam {
pub name: &'static str,
pub value: String,
}
impl FormattedSyscallParam {
pub fn new(name: &'static str, value: String) -> Self {
Self { name, value }
}
}
impl Display for FormattedSyscallParam {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}: {}", self.name, self.value)
}
}
/// 系统调用处理句柄
#[repr(C)]
pub struct SyscallHandle {
pub nr: usize,
pub inner_handle: &'static dyn Syscall,
pub name: &'static str,
}
impl SyscallHandle {
#[inline(never)]
pub fn args_string(&self, args: &[usize]) -> String {
let args_slice = self.inner_handle.entry_format(args);
args_slice
.iter()
.map(|p| format!("{}", p))
.collect::<Vec<String>>()
.join(", ")
}
}
/// 系统调用表类型
#[repr(C)]
pub struct SyscallTable {
entries: [Option<&'static SyscallHandle>; Self::ENTRIES],
}
impl SyscallTable {
pub const ENTRIES: usize = 512;
/// 获取系统调用处理函数
pub fn get(&self, nr: usize) -> Option<&'static SyscallHandle> {
*self.entries.get(nr)?
}
}
// 声明外部链接的syscall_table符号
extern "C" {
fn _syscall_table();
fn _esyscall_table();
}
/// 全局系统调用表实例
#[used]
#[link_section = ".data"]
static mut SYS_CALL_TABLE: SyscallTable = SyscallTable {
entries: [None; SyscallTable::ENTRIES],
};
#[inline]
pub(super) fn syscall_table() -> &'static SyscallTable {
unsafe { &SYS_CALL_TABLE }
}
/// 初始化系统调用表
#[inline(never)]
pub(super) fn syscall_table_init() -> Result<(), SystemError> {
static INIT: Once = Once::new();
INIT.call_once(|| {
log::debug!("Initializing syscall table...");
// 初始化系统调用表
unsafe {
let start = _syscall_table as usize;
let end = _esyscall_table as usize;
let size = end - start;
let count = size / core::mem::size_of::<SyscallHandle>();
if size % core::mem::size_of::<SyscallHandle>() != 0 {
panic!("Invalid syscall table size: {}", size);
}
let handles =
core::slice::from_raw_parts(_syscall_table as *const SyscallHandle, count);
for handle in handles {
if handle.nr < SyscallTable::ENTRIES {
SYS_CALL_TABLE.entries[handle.nr] = Some(handle);
} else {
panic!("Invalid syscall number: {}", handle.nr);
}
}
log::debug!("Syscall table (count: {count}) initialized successfully.")
}
});
Ok(())
}

View File

@ -8,14 +8,12 @@ CC=$(CROSS_COMPILE)gcc
.PHONY: all
all: main.c
# $(CC) -static -o init main.c
all: $(riscv_rust_init)
$(MAKE) -C riscv_rust_init ARCH=$(ARCH) install
.PHONY: install clean
install: all
$(MAKE) -C riscv_rust_init ARCH=$(ARCH) install
# mv init $(DADK_CURRENT_BUILD_DIR)/init
clean:

View File

@ -1,9 +0,0 @@
#include <stdio.h>
int main() {
while(1){
printf("\033[43;37mHello, World!\033[0m\n");
sleep(1);
}
return 0;
}

View File

@ -29,7 +29,7 @@ build-command = "make install"
# 安装相关信息
[install]
# 可选安装到DragonOS的路径
in-dragonos-path = "/bin"
in-dragonos-path = "/"
# 清除相关信息
[clean]
# (可选)清除命令