From 2d93e1dbcaf486f8e1ae56f51971e764430c48c0 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 19:25:58 +0800 Subject: [PATCH 01/11] =?UTF-8?q?chore:=20=E5=BF=BD=E7=95=A5=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E4=BB=A3=E7=90=86=E4=B8=8E=E8=8D=89=E7=A8=BF=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index af57e980..645f8b11 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,7 @@ os/loongarch.log # Local notes os/可能的改进.md + +# Local agent guidance +**/AGENTS.md +docs/ From 16b161f7aeb0e15ac58175ddb0d05b5d367faf2c Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 19:26:13 +0800 Subject: [PATCH 02/11] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E9=87=8D=E6=9E=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/README.md | 14 + doc/arch/README.md | 14 + doc/arch/loongarch/README.md | 8 + doc/arch/loongarch/network_rearchitecture.md | 9 + doc/arch/riscv/README.md | 8 + doc/arch/riscv/network_rearchitecture.md | 9 + doc/net/README.md | 25 + doc/net/network_implementation_guide.md | 1290 +++++++++++++++++ doc/net/network_rearchitecture.md | 20 + doc/net/rearchitecture_device.md | 49 + doc/net/rearchitecture_execution_plan.md | 19 + doc/net/rearchitecture_interface.md | 64 + doc/net/rearchitecture_loopback_poll.md | 81 ++ doc/net/rearchitecture_socket_syscall.md | 76 + doc/net/rearchitecture_stack_runtime.md | 86 ++ document/SUMMARY.md | 9 +- document/net/README.md | 21 + document/net/architecture.md | 34 + document/net/device_and_interface.md | 43 + document/net/loopback_poll.md | 23 + document/net/network_implementation_guide.md | 1308 +----------------- document/net/socket_syscall.md | 35 + document/net/stack_runtime.md | 41 + document/net/testing.md | 24 + 24 files changed, 2033 insertions(+), 1277 deletions(-) create mode 100644 doc/README.md create mode 100644 doc/arch/README.md create mode 100644 doc/arch/loongarch/README.md create mode 100644 doc/arch/loongarch/network_rearchitecture.md create mode 100644 doc/arch/riscv/README.md create mode 100644 doc/arch/riscv/network_rearchitecture.md create mode 100644 doc/net/README.md create mode 100644 doc/net/network_implementation_guide.md create mode 100644 doc/net/network_rearchitecture.md create mode 100644 doc/net/rearchitecture_device.md create mode 100644 doc/net/rearchitecture_execution_plan.md create mode 100644 doc/net/rearchitecture_interface.md create mode 100644 doc/net/rearchitecture_loopback_poll.md create mode 100644 doc/net/rearchitecture_socket_syscall.md create mode 100644 doc/net/rearchitecture_stack_runtime.md create mode 100644 document/net/README.md create mode 100644 document/net/architecture.md create mode 100644 document/net/device_and_interface.md create mode 100644 document/net/loopback_poll.md create mode 100644 document/net/socket_syscall.md create mode 100644 document/net/stack_runtime.md create mode 100644 document/net/testing.md diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 00000000..292353e8 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,14 @@ +# Codex 施工文档 + +本目录保存面向维护者和 Codex 的构建、重构、迁移、排查类文档。 + +与 `document/` 的分工: + +- `doc/`:怎么执行、怎么迁移、怎么检查、历史施工记录。 +- `document/`:当前系统怎么工作、模块职责是什么、接口语义是什么。 + +## 当前目录 + +- `net/`:网络模块重构施工文档。 +- `arch/`:架构相关施工差异文档。 + diff --git a/doc/arch/README.md b/doc/arch/README.md new file mode 100644 index 00000000..4ee267d2 --- /dev/null +++ b/doc/arch/README.md @@ -0,0 +1,14 @@ +# 架构相关施工文档 + +本目录保存和具体架构相关的构建、迁移、重构差异文档。它只记录执行重构时需要区分 RISC-V、LoongArch64 等架构的部分。 + +与 `document/arch/` 的分工: + +- `doc/arch/`:架构相关施工差异、迁移边界、兼容检查点。 +- `document/arch/`:当前架构实现说明、寄存器/陷入/启动等解释文档。 + +## 当前目录 + +- `riscv/`:RISC-V 相关施工差异。 +- `loongarch/`:LoongArch64 相关施工差异。 + diff --git a/doc/arch/loongarch/README.md b/doc/arch/loongarch/README.md new file mode 100644 index 00000000..6ea8d0eb --- /dev/null +++ b/doc/arch/loongarch/README.md @@ -0,0 +1,8 @@ +# LoongArch64 施工差异 + +本目录保存 LoongArch64 相关的重构施工差异。 + +## 文件说明 + +- `network_rearchitecture.md`:网络重构中涉及 LoongArch64 syscall 入口、编号和 VirtIO 接入的差异。 + diff --git a/doc/arch/loongarch/network_rearchitecture.md b/doc/arch/loongarch/network_rearchitecture.md new file mode 100644 index 00000000..2fd2022b --- /dev/null +++ b/doc/arch/loongarch/network_rearchitecture.md @@ -0,0 +1,9 @@ +# LoongArch 网络重构说明 + +LoongArch 网络重构不需要在网络核心中引入架构差异。架构层只负责 syscall 分发、trap 上下文和用户指针访问前置条件。 + +## 规则 + +- syscall 号和参数寄存器差异只放在 `os/src/arch/loongarch/`。 +- `os/src/kernel/syscall/network.rs` 接收架构无关的参数值。 +- 网络核心不得根据 LoongArch 条件编译分叉。 diff --git a/doc/arch/riscv/README.md b/doc/arch/riscv/README.md new file mode 100644 index 00000000..bfd2030f --- /dev/null +++ b/doc/arch/riscv/README.md @@ -0,0 +1,8 @@ +# RISC-V 施工差异 + +本目录保存 RISC-V 相关的重构施工差异。 + +## 文件说明 + +- `network_rearchitecture.md`:网络重构中涉及 RISC-V syscall 入口、编号和 VirtIO 接入的差异。 + diff --git a/doc/arch/riscv/network_rearchitecture.md b/doc/arch/riscv/network_rearchitecture.md new file mode 100644 index 00000000..f4b84506 --- /dev/null +++ b/doc/arch/riscv/network_rearchitecture.md @@ -0,0 +1,9 @@ +# RISC-V 网络重构说明 + +RISC-V 网络重构不需要在网络核心中引入架构差异。架构层只负责 syscall 分发、trap 上下文和用户指针访问前置条件。 + +## 规则 + +- syscall 号和参数寄存器差异只放在 `os/src/arch/riscv/`。 +- `os/src/kernel/syscall/network.rs` 接收架构无关的参数值。 +- 网络核心不得根据 RISC-V 条件编译分叉。 diff --git a/doc/net/README.md b/doc/net/README.md new file mode 100644 index 00000000..279351d9 --- /dev/null +++ b/doc/net/README.md @@ -0,0 +1,25 @@ +# 网络重构施工文档 + +本目录保存 Codex 使用的网络模块重构、迁移和执行类文档。它们用于指导补丁顺序、检查跨层边界和记录兼容路径,不作为当前实现的主要解释文档。 + +当前网络模块的解释说明请阅读: + +- `document/net/README.md` +- `document/net/architecture.md` +- `document/net/device_and_interface.md` +- `document/net/stack_runtime.md` +- `document/net/socket_syscall.md` +- `document/net/loopback_poll.md` +- `document/net/testing.md` + +## 文件说明 + +- `network_implementation_guide.md`:历史实现指南和问题记录。 +- `network_rearchitecture.md`:网络子系统重新分层蓝图。 +- `rearchitecture_execution_plan.md`:重构执行阶段、补丁边界和验收矩阵。 +- `rearchitecture_device.md`:设备层迁移说明。 +- `rearchitecture_interface.md`:接口控制面迁移说明。 +- `rearchitecture_stack_runtime.md`:协议栈运行时迁移说明。 +- `rearchitecture_socket_syscall.md`:SocketFile 与 syscall 迁移说明。 +- `rearchitecture_loopback_poll.md`:loopback、poll、RX/TX 迁移说明。 + diff --git a/doc/net/network_implementation_guide.md b/doc/net/network_implementation_guide.md new file mode 100644 index 00000000..4089cfb1 --- /dev/null +++ b/doc/net/network_implementation_guide.md @@ -0,0 +1,1290 @@ +# 网络子系统实现指南 + +## 文档概述 + +本文档提供了实现完整网络功能的详细指南,包括修复当前问题和实现真正的 POSIX 网络系统调用。 + +**创建日期**: 2025-11-24 +**状态**: 设计文档 / 实现待完成 + +--- + +## 1. 当前状态与问题总结 + +### 1.1 已修复的问题 ✓ + +#### 问题 1: `create_smoltcp_interface` 内存安全问题 +**严重程度**: 🔴 严重 - 未定义行为 + +**问题描述**: +```rust +// src/device/net/interface.rs (旧代码) +pub fn create_smoltcp_interface(&self) -> Interface { + let mut device_adapter = NetDeviceAdapter::new(self.device.clone()); + let iface = Interface::new(config, &mut device_adapter, timestamp); + iface // ❌ 返回持有悬垂指针的 Interface! +} +``` + +`device_adapter` 在栈上创建,函数返回后被销毁,导致返回的 `Interface` 持有悬垂指针。 + +**解决方案**: +创建 `SmoltcpInterface` 包装器,确保 Device 和 Interface 有相同的生命周期: + +```rust +pub struct SmoltcpInterface { + device_adapter: NetDeviceAdapter, // 拥有 Device + iface: Interface, // Interface 借用 device_adapter +} + +impl SmoltcpInterface { + fn new(device: Arc, mac_address: EthernetAddress) -> Self { + let mut device_adapter = NetDeviceAdapter::new(device); + let iface = Interface::new(config, &mut device_adapter, timestamp); + Self { device_adapter, iface } + } + + pub fn poll(&mut self, timestamp: Instant, sockets: &mut SocketSet) -> PollResult { + self.iface.poll(timestamp, &mut self.device_adapter, sockets) + } + + pub fn interface_mut(&mut self) -> &mut Interface { &mut self.iface } + pub fn interface(&self) -> &Interface { &self.iface } +} + +// 现在返回包装器而不是裸 Interface +pub fn create_smoltcp_interface(&self) -> SmoltcpInterface { + let mut smoltcp_iface = SmoltcpInterface::new(self.device.clone(), self.mac_address()); + // ... 配置 IP 和路由 + smoltcp_iface +} +``` + +**文件位置**: `os/src/device/net/interface.rs:47-99` + +--- + +#### 问题 2: 子网掩码解析功能有限 +**严重程度**: 🟡 中等 - 功能受限 + +**问题描述**: +`set_interface_config` 函数使用硬编码的 match 语句解析子网掩码,只支持 9 种常见掩码。 + +```rust +// 旧代码 +let prefix_length = match mask { + "255.255.255.0" => 24, + "255.255.0.0" => 16, + // ... 仅支持少数几种 + _ => return Err(NetworkConfigError::InvalidSubnet), +}; +``` + +**解决方案**: +实现通用的子网掩码解析函数,支持任意有效掩码: + +```rust +/// 解析点分十进制子网掩码并计算前缀长度 +/// +/// # 算法 +/// 1. 解析为 4 字节并转换为 u32 +/// 2. 计算前导 1 的个数(前缀长度) +/// 3. 验证掩码有效性:所有 1 必须连续 +/// - 有效: 11111111111111111111111100000000 (0xFFFFFF00) +/// - 无效: 11111111111111110000000011111111 (0xFFFF00FF) +/// +/// # 示例 +/// - "255.255.255.0" → Ok(24) +/// - "255.255.255.128" → Ok(25) +/// - "255.255.255.3" → Err (不连续) +fn parse_subnet_mask(mask: &str) -> Result { + // 解析为 4 字节 + let octets: Result, _> = mask.split('.').map(|s| s.parse()).collect(); + let octets = octets.map_err(|_| NetworkConfigError::InvalidSubnet)?; + + if octets.len() != 4 { + return Err(NetworkConfigError::InvalidSubnet); + } + + // 转换为 u32 + let mask_u32 = ((octets[0] as u32) << 24) + | ((octets[1] as u32) << 16) + | ((octets[2] as u32) << 8) + | (octets[3] as u32); + + // 计算前缀长度 + let prefix_length = mask_u32.leading_ones() as u8; + + // 验证掩码连续性 + if prefix_length == 0 { + if mask_u32 == 0 { Ok(0) } else { Err(NetworkConfigError::InvalidSubnet) } + } else if prefix_length == 32 { + if mask_u32 == 0xFFFFFFFF { Ok(32) } else { Err(NetworkConfigError::InvalidSubnet) } + } else { + let expected_mask = 0xFFFFFFFFu32 << (32 - prefix_length); + if mask_u32 == expected_mask { + Ok(prefix_length) + } else { + Err(NetworkConfigError::InvalidSubnet) + } + } +} + +// 使用 +let prefix_length = Self::parse_subnet_mask(mask)?; +``` + +**文件位置**: `os/src/device/net/config.rs:20-87, 240` + +--- + +### 1.2 未修复的问题 - 网络系统调用存根 + +#### 问题 3: 所有网络系统调用只是存根实现 +**严重程度**: 🔴 严重 - 核心功能缺失 + +**问题描述**: +所有网络系统调用 (`socket`, `bind`, `listen`, `accept`, `connect`, `send`, `recv` 等) 都只返回虚拟值,没有实现真正的网络功能。 + +**当前实现** (`os/src/kernel/syscall/net_syscall.rs`): +```rust +pub fn socket(domain: i32, socket_type: i32, protocol: i32) -> isize { + // TODO: 实现套接字创建 + 3 // ❌ 返回虚拟 FD +} + +pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { + // TODO: 实现绑定逻辑 + 0 // ❌ 假装成功 +} + +pub fn send(sockfd: i32, buf: *const u8, len: usize, flags: i32) -> isize { + // TODO: 实现发送逻辑 + len as isize // ❌ 假装发送了所有数据 +} + +pub fn recv(sockfd: i32, buf: *mut u8, len: usize, flags: i32) -> isize { + // TODO: 实现接收逻辑 + 0 // ❌ 总是返回没有数据 +} +``` + +**影响**: +- 用户程序无法使用网络功能 +- 与 POSIX 标准不兼容 +- 无法运行真实的网络应用 + +--- + +## 2. 完整网络功能架构设计 + +### 2.1 整体架构图 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 用户空间 │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ TCP 应用 │ │ UDP 应用 │ │ 原始套接字 │ │ +│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ +└─────────┼─────────────────┼─────────────────┼───────────────┘ + │ │ │ + │ POSIX Socket API (syscall) │ + │ │ │ +┌─────────┼─────────────────┼─────────────────┼───────────────┐ +│ │ 内核空间 │ │ +│ ┌──────▼──────────────────▼─────────────▼──────┐ │ +│ │ 网络系统调用层 │ │ +│ │ socket/bind/listen/accept/connect/send/recv │ │ +│ └──────┬──────────────────────────────────┬─────┘ │ +│ │ │ │ +│ ┌──────▼───────────────────────────────────▼─────┐ │ +│ │ Socket 文件抽象层 │ │ +│ │ SocketFile (实现 File trait) │ │ +│ │ - TcpSocketFile │ │ +│ │ - UdpSocketFile │ │ +│ └──────┬──────────────────────────────────┬──────┘ │ +│ │ │ │ +│ ┌──────▼───────────────────────────────────▼──────┐ │ +│ │ 网络协议栈管理器 │ │ +│ │ NetworkStack │ │ +│ │ - SmoltcpInterface (设备 + 接口) │ │ +│ │ - SocketSet (所有 socket 的集合) │ │ +│ │ - Socket 元数据表 │ │ +│ └──────┬────────────────────────────────────────┘ │ +│ │ │ +│ ┌──────▼────────────────────────────────────────┐ │ +│ │ smoltcp 协议栈 │ │ +│ │ - TCP/UDP/IP/ICMP 协议实现 │ │ +│ │ - Socket 管理 │ │ +│ └──────┬─────────────────────────────────────────┘ │ +│ │ │ +│ ┌──────▼────────────────────────────────────────┐ │ +│ │ NetDeviceAdapter (设备适配器) │ │ +│ └──────┬─────────────────────────────────────────┘ │ +│ │ │ +│ ┌──────▼────────────────────────────────────────┐ │ +│ │ VirtIO 网络驱动 │ │ +│ │ (VirtioNet) │ │ +│ └────────────────────────────────────────────────┘ │ +│ │ +└───────────────────────────────────────────────────────────────┘ + │ + ┌────────▼────────┐ + │ 网络硬件 (QEMU) │ + └─────────────────┘ +``` + +### 2.2 关键组件说明 + +#### 2.2.1 Socket 文件抽象 (`os/src/vfs/socket.rs` - 需要创建) + +**目的**: 将 socket 集成到 VFS 中,使其像文件一样可以通过 FD 访问。 + +```rust +use crate::vfs::File; +use alloc::sync::Arc; +use smoltcp::socket::{TcpSocket, UdpSocket}; +use smoltcp::wire::{IpEndpoint, IpAddress}; + +/// Socket 类型 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SocketType { + Stream, // TCP (SOCK_STREAM) + Datagram, // UDP (SOCK_DGRAM) + Raw, // 原始套接字 (SOCK_RAW) +} + +/// Socket 地址 +#[derive(Debug, Clone, Copy)] +pub struct SocketAddr { + pub ip: IpAddress, + pub port: u16, +} + +impl SocketAddr { + pub fn to_endpoint(&self) -> IpEndpoint { + IpEndpoint::new(self.ip, self.port) + } +} + +/// Socket 状态 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SocketState { + Closed, + Listening, + Connecting, + Connected, + FinWait, +} + +/// TCP Socket 文件 +pub struct TcpSocketFile { + /// smoltcp socket handle + socket_handle: SocketHandle, + + /// Socket 状态 + state: SpinLock, + + /// 本地绑定地址 + local_addr: SpinLock>, + + /// 远程连接地址 + remote_addr: SpinLock>, + + /// 等待队列(用于 accept 的连接队列) + pending_connections: SpinLock>, + + /// 最大等待连接数(backlog) + backlog: usize, +} + +impl TcpSocketFile { + pub fn new(socket_handle: SocketHandle) -> Self { + Self { + socket_handle, + state: SpinLock::new(SocketState::Closed), + local_addr: SpinLock::new(None), + remote_addr: SpinLock::new(None), + pending_connections: SpinLock::new(VecDeque::new()), + backlog: 0, + } + } + + /// 绑定到本地地址 + pub fn bind(&self, addr: SocketAddr) -> Result<(), NetworkError> { + // 实现绑定逻辑 + todo!() + } + + /// 监听连接 + pub fn listen(&self, backlog: usize) -> Result<(), NetworkError> { + // 实现监听逻辑 + todo!() + } + + /// 接受连接 + pub fn accept(&self) -> Result<(Arc, SocketAddr), NetworkError> { + // 实现 accept 逻辑 + todo!() + } + + /// 连接到远程地址 + pub fn connect(&self, addr: SocketAddr) -> Result<(), NetworkError> { + // 实现连接逻辑 + todo!() + } +} + +/// 为 TcpSocketFile 实现 File trait +impl File for TcpSocketFile { + fn read(&self, buf: &mut [u8]) -> Result { + // 从 TCP socket 读取数据 + // 需要访问全局 NetworkStack 来操作 socket + todo!() + } + + fn write(&self, buf: &[u8]) -> Result { + // 向 TCP socket 写入数据 + todo!() + } + + fn seek(&self, _pos: SeekFrom) -> Result { + // Socket 不支持 seek + Err(FileError::NotSupported) + } + + fn is_seekable(&self) -> bool { + false + } + + // ... 其他 File trait 方法 +} + +/// UDP Socket 文件 +pub struct UdpSocketFile { + socket_handle: SocketHandle, + local_addr: SpinLock>, + remote_addr: SpinLock>, +} + +impl UdpSocketFile { + pub fn new(socket_handle: SocketHandle) -> Self { + Self { + socket_handle, + local_addr: SpinLock::new(None), + remote_addr: SpinLock::new(None), + } + } + + pub fn bind(&self, addr: SocketAddr) -> Result<(), NetworkError> { + todo!() + } + + pub fn sendto(&self, buf: &[u8], addr: SocketAddr) -> Result { + todo!() + } + + pub fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr), NetworkError> { + todo!() + } +} + +impl File for UdpSocketFile { + // 实现类似 TcpSocketFile 的方法 + // ... +} + +/// 网络错误 +#[derive(Debug)] +pub enum NetworkError { + InvalidAddress, + InvalidSocket, + NotConnected, + AlreadyConnected, + ConnectionRefused, + WouldBlock, + Timeout, + // ... 其他错误 +} +``` + +#### 2.2.2 网络协议栈管理器 (`os/src/net/stack.rs` - 需要创建) + +**目的**: 管理全局的 smoltcp 协议栈实例、socket 集合和元数据。 + +```rust +use crate::device::net::interface::SmoltcpInterface; +use crate::sync::SpinLock; +use alloc::collections::BTreeMap; +use alloc::sync::Arc; +use lazy_static::lazy_static; +use smoltcp::socket::{SocketHandle, SocketSet, TcpSocket, UdpSocket}; +use smoltcp::time::Instant; + +/// Socket 元数据 +pub struct SocketMetadata { + pub socket_type: SocketType, + pub local_addr: Option, + pub remote_addr: Option, + pub state: SocketState, +} + +/// 全局网络协议栈 +pub struct NetworkStack { + /// smoltcp 接口(包含 Device 和 Interface) + smoltcp_iface: SpinLock, + + /// Socket 集合(所有 socket 的容器) + socket_set: SpinLock>, + + /// Socket 元数据映射表 + /// SocketHandle -> SocketMetadata + socket_metadata: SpinLock>, + + /// 当前时间(用于 smoltcp) + current_time: SpinLock, +} + +impl NetworkStack { + pub fn new(smoltcp_iface: SmoltcpInterface) -> Self { + Self { + smoltcp_iface: SpinLock::new(smoltcp_iface), + socket_set: SpinLock::new(SocketSet::new(Vec::new())), + socket_metadata: SpinLock::new(BTreeMap::new()), + current_time: SpinLock::new(Instant::from_millis(0)), + } + } + + /// 创建新的 TCP socket + pub fn create_tcp_socket(&self) -> Result { + let mut socket_set = self.socket_set.lock(); + + // 创建 TCP socket 缓冲区 + let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 4096]); + let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 4096]); + let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); + + // 添加到 socket 集合 + let socket_handle = socket_set.add(tcp_socket); + + // 记录元数据 + let mut metadata = self.socket_metadata.lock(); + metadata.insert(socket_handle, SocketMetadata { + socket_type: SocketType::Stream, + local_addr: None, + remote_addr: None, + state: SocketState::Closed, + }); + + Ok(socket_handle) + } + + /// 创建新的 UDP socket + pub fn create_udp_socket(&self) -> Result { + let mut socket_set = self.socket_set.lock(); + + // 创建 UDP socket 缓冲区 + let udp_rx_buffer = UdpSocketBuffer::new( + vec![UdpPacketMetadata::EMPTY; 16], + vec![0; 4096] + ); + let udp_tx_buffer = UdpSocketBuffer::new( + vec![UdpPacketMetadata::EMPTY; 16], + vec![0; 4096] + ); + let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); + + let socket_handle = socket_set.add(udp_socket); + + let mut metadata = self.socket_metadata.lock(); + metadata.insert(socket_handle, SocketMetadata { + socket_type: SocketType::Datagram, + local_addr: None, + remote_addr: None, + state: SocketState::Closed, + }); + + Ok(socket_handle) + } + + /// 删除 socket + pub fn remove_socket(&self, handle: SocketHandle) { + let mut socket_set = self.socket_set.lock(); + socket_set.remove(handle); + + let mut metadata = self.socket_metadata.lock(); + metadata.remove(&handle); + } + + /// 绑定 TCP socket 到本地地址 + pub fn tcp_bind(&self, handle: SocketHandle, addr: SocketAddr) -> Result<(), NetworkError> { + let mut socket_set = self.socket_set.lock(); + let socket = socket_set.get_mut::(handle); + + // 调用 smoltcp 的 listen + socket.listen(addr.port) + .map_err(|_| NetworkError::InvalidAddress)?; + + // 更新元数据 + let mut metadata = self.socket_metadata.lock(); + if let Some(meta) = metadata.get_mut(&handle) { + meta.local_addr = Some(addr); + } + + Ok(()) + } + + /// TCP connect + pub fn tcp_connect( + &self, + handle: SocketHandle, + remote_addr: SocketAddr, + local_port: u16, + ) -> Result<(), NetworkError> { + let mut socket_set = self.socket_set.lock(); + let socket = socket_set.get_mut::(handle); + + socket.connect( + self.smoltcp_iface.lock().interface().context(), + remote_addr.to_endpoint(), + local_port, + ).map_err(|_| NetworkError::ConnectionRefused)?; + + let mut metadata = self.socket_metadata.lock(); + if let Some(meta) = metadata.get_mut(&handle) { + meta.remote_addr = Some(remote_addr); + meta.state = SocketState::Connecting; + } + + Ok(()) + } + + /// TCP send + pub fn tcp_send(&self, handle: SocketHandle, data: &[u8]) -> Result { + let mut socket_set = self.socket_set.lock(); + let socket = socket_set.get_mut::(handle); + + if !socket.can_send() { + return Err(NetworkError::WouldBlock); + } + + socket.send_slice(data) + .map_err(|_| NetworkError::WouldBlock) + } + + /// TCP recv + pub fn tcp_recv(&self, handle: SocketHandle, buffer: &mut [u8]) -> Result { + let mut socket_set = self.socket_set.lock(); + let socket = socket_set.get_mut::(handle); + + if !socket.can_recv() { + return Err(NetworkError::WouldBlock); + } + + socket.recv_slice(buffer) + .map_err(|_| NetworkError::WouldBlock) + } + + /// UDP sendto + pub fn udp_sendto( + &self, + handle: SocketHandle, + data: &[u8], + remote_addr: SocketAddr, + ) -> Result<(), NetworkError> { + let mut socket_set = self.socket_set.lock(); + let socket = socket_set.get_mut::(handle); + + socket.send_slice(data, remote_addr.to_endpoint()) + .map_err(|_| NetworkError::WouldBlock) + } + + /// UDP recvfrom + pub fn udp_recvfrom( + &self, + handle: SocketHandle, + buffer: &mut [u8], + ) -> Result<(usize, SocketAddr), NetworkError> { + let mut socket_set = self.socket_set.lock(); + let socket = socket_set.get_mut::(handle); + + match socket.recv_slice(buffer) { + Ok((size, endpoint)) => { + let addr = SocketAddr { + ip: endpoint.addr, + port: endpoint.port, + }; + Ok((size, addr)) + } + Err(_) => Err(NetworkError::WouldBlock), + } + } + + /// 轮询网络栈(处理所有网络事件) + /// 应该在定时器中断或专门的网络线程中定期调用 + pub fn poll(&self) { + // 更新时间 + let mut current_time = self.current_time.lock(); + *current_time = Instant::from_millis(current_time.total_millis() + 10); + + // 轮询接口 + let mut socket_set = self.socket_set.lock(); + let mut smoltcp_iface = self.smoltcp_iface.lock(); + + smoltcp_iface.poll(*current_time, &mut socket_set); + } +} + +lazy_static! { + /// 全局网络协议栈实例 + pub static ref NETWORK_STACK: SpinLock>> = + SpinLock::new(None); +} + +/// 初始化网络协议栈 +pub fn init_network_stack(smoltcp_iface: SmoltcpInterface) { + let stack = Arc::new(NetworkStack::new(smoltcp_iface)); + *NETWORK_STACK.lock() = Some(stack); +} + +/// 获取全局网络协议栈 +pub fn get_network_stack() -> Option> { + NETWORK_STACK.lock().clone() +} +``` + +#### 2.2.3 网络系统调用实现 (`os/src/kernel/syscall/net_syscall.rs` - 需要重写) + +**目的**: 实现真正的 POSIX socket 系统调用。 + +```rust +use crate::net::stack::{get_network_stack, NetworkStack}; +use crate::net::socket::{SocketAddr, TcpSocketFile, UdpSocketFile}; +use crate::kernel::task::current_task; +use crate::vfs::File; +use alloc::sync::Arc; +use core::ffi::c_void; + +/// sys_socket - 创建套接字 +/// +/// # 参数 +/// - domain: AF_INET (2) / AF_INET6 (10) +/// - type: SOCK_STREAM (1) / SOCK_DGRAM (2) / SOCK_RAW (3) +/// - protocol: 0 (自动选择) +/// +/// # 返回值 +/// - 成功: 文件描述符 +/// - 失败: 负数错误码 +pub fn sys_socket(domain: i32, socket_type: i32, protocol: i32) -> isize { + // 验证参数 + if domain != 2 { // AF_INET + return -97; // -EAFNOSUPPORT + } + + let stack = match get_network_stack() { + Some(s) => s, + None => return -19, // -ENODEV (网络栈未初始化) + }; + + // 根据 socket 类型创建不同的 socket + let socket_file: Arc = match socket_type { + 1 => { // SOCK_STREAM (TCP) + let handle = match stack.create_tcp_socket() { + Ok(h) => h, + Err(_) => return -12, // -ENOMEM + }; + Arc::new(TcpSocketFile::new(handle)) + } + 2 => { // SOCK_DGRAM (UDP) + let handle = match stack.create_udp_socket() { + Ok(h) => h, + Err(_) => return -12, // -ENOMEM + }; + Arc::new(UdpSocketFile::new(handle)) + } + _ => return -93, // -EPROTONOSUPPORT + }; + + // 将 socket 添加到当前任务的文件描述符表 + let task = current_task().unwrap(); + let fd = match task.add_file(socket_file) { + Ok(fd) => fd, + Err(_) => return -24, // -EMFILE (太多打开的文件) + }; + + fd as isize +} + +/// sys_bind - 绑定套接字到地址 +/// +/// # 参数 +/// - sockfd: socket 文件描述符 +/// - addr: sockaddr 结构指针 +/// - addrlen: 地址长度 +/// +/// # 返回值 +/// - 成功: 0 +/// - 失败: 负数错误码 +pub fn sys_bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { + if sockfd < 0 || addr.is_null() { + return -14; // -EFAULT + } + + // 解析 sockaddr_in 结构 + let socket_addr = unsafe { + if addrlen < 16 { // sizeof(sockaddr_in) + return -22; // -EINVAL + } + + // sockaddr_in 结构: + // - u16 sin_family + // - u16 sin_port (网络字节序) + // - u32 sin_addr (网络字节序) + let port = u16::from_be((addr.add(2) as *const u16).read()); + let ip_bytes = core::slice::from_raw_parts(addr.add(4), 4); + let ip = IpAddress::v4(ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3]); + + SocketAddr { ip, port } + }; + + // 获取 socket 文件 + let task = current_task().unwrap(); + let socket_file = match task.get_file(sockfd as usize) { + Some(f) => f, + None => return -9, // -EBADF + }; + + // 尝试向下转型为 TcpSocketFile 或 UdpSocketFile + if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { + match tcp_socket.bind(socket_addr) { + Ok(_) => 0, + Err(_) => -98, // -EADDRINUSE + } + } else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { + match udp_socket.bind(socket_addr) { + Ok(_) => 0, + Err(_) => -98, // -EADDRINUSE + } + } else { + -88 // -ENOTSOCK + } +} + +/// sys_listen - 监听连接 +pub fn sys_listen(sockfd: i32, backlog: i32) -> isize { + if sockfd < 0 { + return -9; // -EBADF + } + + let task = current_task().unwrap(); + let socket_file = match task.get_file(sockfd as usize) { + Some(f) => f, + None => return -9, + }; + + // 只有 TCP socket 支持 listen + if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { + match tcp_socket.listen(backlog as usize) { + Ok(_) => 0, + Err(_) => -22, // -EINVAL + } + } else { + -95 // -EOPNOTSUPP (操作不支持) + } +} + +/// sys_accept - 接受连接 +pub fn sys_accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { + if sockfd < 0 { + return -9; + } + + let task = current_task().unwrap(); + let socket_file = match task.get_file(sockfd as usize) { + Some(f) => f, + None => return -9, + }; + + let tcp_socket = match socket_file.as_any().downcast_ref::() { + Some(s) => s, + None => return -88, // -ENOTSOCK + }; + + // 接受连接 + let (new_socket_file, remote_addr) = match tcp_socket.accept() { + Ok(result) => result, + Err(_) => return -11, // -EAGAIN (没有连接可接受) + }; + + // 填充地址信息 + if !addr.is_null() && !addrlen.is_null() { + unsafe { + let available_len = *addrlen as usize; + if available_len >= 16 { + // 填充 sockaddr_in + // ... (类似 bind 的逆操作) + } + } + } + + // 为新连接创建 FD + match task.add_file(new_socket_file) { + Ok(fd) => fd as isize, + Err(_) => -24, // -EMFILE + } +} + +/// sys_connect - 连接到远程地址 +pub fn sys_connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { + if sockfd < 0 || addr.is_null() { + return -14; + } + + // 解析地址(类似 bind) + let socket_addr = unsafe { + // ... 解析 sockaddr_in + }; + + let task = current_task().unwrap(); + let socket_file = match task.get_file(sockfd as usize) { + Some(f) => f, + None => return -9, + }; + + let tcp_socket = match socket_file.as_any().downcast_ref::() { + Some(s) => s, + None => return -88, + }; + + match tcp_socket.connect(socket_addr) { + Ok(_) => 0, + Err(_) => -111, // -ECONNREFUSED + } +} + +/// sys_send / sys_sendto - 发送数据 +pub fn sys_sendto( + sockfd: i32, + buf: *const u8, + len: usize, + flags: i32, + dest_addr: *const u8, + addrlen: u32, +) -> isize { + if sockfd < 0 || buf.is_null() || len == 0 { + return -14; + } + + let task = current_task().unwrap(); + let socket_file = match task.get_file(sockfd as usize) { + Some(f) => f, + None => return -9, + }; + + // 从用户空间拷贝数据 + let data = unsafe { core::slice::from_raw_parts(buf, len) }; + + // TCP socket + if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { + match socket_file.write(data) { + Ok(written) => written as isize, + Err(_) => -11, // -EAGAIN + } + } + // UDP socket + else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { + if dest_addr.is_null() { + return -89; // -EDESTADDRREQ + } + + let remote_addr = unsafe { + // 解析 dest_addr + }; + + match udp_socket.sendto(data, remote_addr) { + Ok(sent) => sent as isize, + Err(_) => -11, + } + } else { + -88 // -ENOTSOCK + } +} + +/// sys_recv / sys_recvfrom - 接收数据 +pub fn sys_recvfrom( + sockfd: i32, + buf: *mut u8, + len: usize, + flags: i32, + src_addr: *mut u8, + addrlen: *mut u32, +) -> isize { + if sockfd < 0 || buf.is_null() || len == 0 { + return -14; + } + + let task = current_task().unwrap(); + let socket_file = match task.get_file(sockfd as usize) { + Some(f) => f, + None => return -9, + }; + + let buffer = unsafe { core::slice::from_raw_parts_mut(buf, len) }; + + // TCP socket + if let Some(_tcp_socket) = socket_file.as_any().downcast_ref::() { + match socket_file.read(buffer) { + Ok(read) => read as isize, + Err(_) => 0, // 没有数据可读 + } + } + // UDP socket + else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { + match udp_socket.recvfrom(buffer) { + Ok((size, remote_addr)) => { + // 填充源地址 + if !src_addr.is_null() && !addrlen.is_null() { + unsafe { + // 填充 sockaddr_in + } + } + size as isize + } + Err(_) => 0, + } + } else { + -88 + } +} + +// ... 其他系统调用 (close, shutdown, getsockopt, setsockopt 等) +``` + +--- + +## 3. 实现步骤 + +### 第 1 步: 创建 Socket 文件抽象 +**文件**: `os/src/vfs/socket.rs` + +1. 定义 `SocketType`, `SocketAddr`, `SocketState` 等基础类型 +2. 实现 `TcpSocketFile` 结构体及其方法 +3. 实现 `UdpSocketFile` 结构体及其方法 +4. 为两者实现 `File` trait +5. 在 `os/src/vfs/mod.rs` 中导出 + +**测试**: 编译通过,类型检查正确 + +--- + +### 第 2 步: 创建网络协议栈管理器 +**文件**: `os/src/net/stack.rs` (需要先创建 `os/src/net/` 目录) + +1. 定义 `NetworkStack` 结构体 +2. 实现 socket 创建/删除方法 +3. 实现 TCP/UDP 操作方法 (bind, connect, send, recv 等) +4. 实现 `poll()` 方法 +5. 创建全局实例和初始化函数 + +**测试**: 编译通过,可以创建 NetworkStack 实例 + +--- + +### 第 3 步: 修改 NetworkInterface 初始化 +**文件**: `os/src/device/net/interface.rs`, `os/src/main.rs` 或初始化代码 + +1. 在网络设备初始化时创建 `SmoltcpInterface` +2. 使用 `SmoltcpInterface` 初始化 `NetworkStack` +3. 设置全局 `NETWORK_STACK` + +**代码示例**: +```rust +// 在网络初始化函数中 +pub fn init_network() { + // ... 初始化网络接口 + let manager = NETWORK_INTERFACE_MANAGER.lock(); + if let Some(iface) = manager.get_interfaces().first() { + let smoltcp_iface = iface.create_smoltcp_interface(); + init_network_stack(smoltcp_iface); + } +} +``` + +--- + +### 第 4 步: 实现网络系统调用 +**文件**: `os/src/kernel/syscall/net_syscall.rs` + +1. 重写 `sys_socket` +2. 重写 `sys_bind` +3. 重写 `sys_listen` +4. 重写 `sys_accept` +5. 重写 `sys_connect` +6. 重写 `sys_sendto` / `sys_send` +7. 重写 `sys_recvfrom` / `sys_recv` +8. 实现其他系统调用 (close, shutdown, getsockopt, setsockopt) + +**测试**: 逐个测试每个系统调用 + +--- + +### 第 5 步: 实现网络轮询机制 +**选项 A: 定时器中断轮询** +```rust +// 在定时器中断处理函数中 +pub fn timer_interrupt_handler() { + // ... 其他定时器逻辑 + + if let Some(stack) = get_network_stack() { + stack.poll(); // 轮询网络栈 + } + + // ... 调度等 +} +``` + +**选项 B: 专门的网络线程** (推荐) +```rust +// 创建内核线程 +pub fn network_polling_thread() { + loop { + if let Some(stack) = get_network_stack() { + stack.poll(); + } + + // 休眠一小段时间(如 10ms) + sleep_ms(10); + } +} +``` + +--- + +### 第 6 步: 处理阻塞和非阻塞 +1. 为 socket 添加阻塞/非阻塞标志 +2. 阻塞模式下,recv/send 应该等待数据或空间可用 +3. 实现等待队列,让任务在 socket 上等待 +4. 网络事件到达时唤醒等待的任务 + +--- + +### 第 7 步: 测试 +1. **基础测试**: 创建 socket, bind, close +2. **UDP 测试**: 发送和接收 UDP 数据包 +3. **TCP 客户端测试**: 连接到外部服务器,发送/接收数据 +4. **TCP 服务器测试**: 监听端口,接受连接 +5. **并发测试**: 多个连接同时工作 +6. **错误处理测试**: 各种错误情况 + +**测试用户程序示例**: +```c +// user/src/test_tcp_client.c +int main() { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + printf("socket() failed\n"); + return 1; + } + + struct sockaddr_in server_addr = { + .sin_family = AF_INET, + .sin_port = htons(80), + .sin_addr.s_addr = inet_addr("192.168.1.1"), + }; + + if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { + printf("connect() failed\n"); + return 1; + } + + const char* request = "GET / HTTP/1.0\r\n\r\n"; + send(sockfd, request, strlen(request), 0); + + char buffer[1024]; + int n = recv(sockfd, buffer, sizeof(buffer), 0); + if (n > 0) { + printf("Received: %.*s\n", n, buffer); + } + + close(sockfd); + return 0; +} +``` + +--- + +## 4. 关键注意事项 + +### 4.1 线程安全 +- 所有全局状态都必须用锁保护 +- 避免死锁:定义锁的获取顺序 +- 注意:不能在中断处理程序中调用可能阻塞的操作 + +### 4.2 内存管理 +- smoltcp 的 socket 缓冲区需要静态生命周期 +- 使用 `Vec` 或 `Box` 分配堆内存 +- 注意避免内存泄漏(socket 关闭时释放资源) + +### 4.3 地址字节序 +- 网络字节序是大端 (big-endian) +- RISC-V 通常是小端 (little-endian) +- 使用 `u16::from_be()` / `u16::to_be()` 转换 + +### 4.4 用户空间内存访问 +- 所有用户指针都必须验证 +- 使用 `copy_from_user` / `copy_to_user` 安全拷贝 +- 防止用户传入内核地址 + +### 4.5 错误处理 +- 使用标准的 POSIX 错误码 (errno) +- 常见错误码: + - `-EBADF` (9): 错误的文件描述符 + - `-EINVAL` (22): 无效参数 + - `-EAGAIN` (11): 资源暂时不可用 + - `-ECONNREFUSED` (111): 连接被拒绝 + - `-EADDRINUSE` (98): 地址已被使用 + +--- + +## 5. 依赖和模块关系 + +### 5.1 新增模块依赖图 +``` +vfs/socket.rs + ├─> sync::SpinLock + ├─> vfs::File (trait) + ├─> net::stack::NetworkStack + └─> smoltcp::socket::{TcpSocket, UdpSocket} + +net/stack.rs + ├─> sync::SpinLock + ├─> device::net::interface::SmoltcpInterface + ├─> smoltcp::socket::SocketSet + └─> smoltcp::time::Instant + +kernel/syscall/net_syscall.rs + ├─> vfs::socket::{TcpSocketFile, UdpSocketFile} + ├─> net::stack::{get_network_stack, NetworkStack} + ├─> kernel::task::current_task + └─> 用户内存访问函数 +``` + +### 5.2 遵循模块分层 +根据 CLAUDE.md 中的模块层次规则: +``` +arch → mm → sync → kernel → {ipc, vfs, fs, net} +``` + +- `net/` 模块位于最上层,可以使用所有下层模块 +- 不能让下层模块依赖 `net/` +- Socket 作为 VFS 的一部分,位于 `vfs/socket.rs` + +--- + +## 6. 性能优化建议(后续) + +1. **零拷贝**: 使用 DMA 直接在网卡和用户空间之间传输 +2. **批量处理**: 一次轮询处理多个数据包 +3. **中断合并**: 减少中断频率 +4. **Socket 缓冲区调优**: 根据应用调整缓冲区大小 +5. **连接复用**: 支持 SO_REUSEADDR 和 SO_REUSEPORT + +--- + +## 7. 未来扩展 + +### 7.1 IPv6 支持 +- 扩展 `SocketAddr` 支持 IPv6 +- 处理 `AF_INET6` domain + +### 7.2 原始套接字 (SOCK_RAW) +- 允许直接访问 IP/ICMP 层 +- 需要权限检查 + +### 7.3 Unix Domain Socket +- 进程间通信 +- 文件系统路径作为地址 + +### 7.4 高级功能 +- `poll()` / `epoll()` 系统调用 +- `sendmsg()` / `recvmsg()` (scatter-gather I/O) +- `sendfile()` (零拷贝文件传输) +- Socket 选项 (SO_KEEPALIVE, TCP_NODELAY 等) + +--- + +## 8. 参考资料 + +### 8.1 文档 +- [smoltcp 文档](https://docs.rs/smoltcp/) +- [POSIX Socket API](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html) +- [Linux Socket Man Pages](https://man7.org/linux/man-pages/man2/socket.2.html) + +### 8.2 代码示例 +- [Redox OS 网络实现](https://gitlab.redox-os.org/redox-os/netstack) +- [rCore 网络模块](https://github.com/rcore-os/rCore/tree/master/kernel/src/net) + +--- + +## 9. 检查清单 + +实现完成后,确保: + +- [ ] 所有 TODO 注释已删除 +- [ ] 所有系统调用都返回正确的错误码 +- [ ] 内存安全(无悬垂指针、无内存泄漏) +- [ ] 线程安全(所有共享状态都有锁保护) +- [ ] 用户输入验证(防止越界、空指针等) +- [ ] 编写了测试用户程序 +- [ ] 通过了所有测试 +- [ ] 更新了相关文档 +- [ ] 代码符合项目风格规范(`cargo fmt`, `make quick_check_style`) + +--- + +## 附录 A: smoltcp Socket 缓冲区大小建议 + +```rust +// TCP +let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 8192]); // 8KB 接收缓冲区 +let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 8192]); // 8KB 发送缓冲区 + +// UDP +let udp_rx_buffer = UdpSocketBuffer::new( + vec![UdpPacketMetadata::EMPTY; 16], // 最多 16 个数据包 + vec![0; 4096] // 4KB 总缓冲区 +); +let udp_tx_buffer = UdpSocketBuffer::new( + vec![UdpPacketMetadata::EMPTY; 16], + vec![0; 4096] +); +``` + +根据应用需求调整: +- **Web 服务器**: 增大 TCP 缓冲区 (16KB - 64KB) +- **音视频流**: 增大 UDP 缓冲区 +- **嵌入式/内存受限**: 减小缓冲区 (1KB - 2KB) + +--- + +## 附录 B: 常见错误码对照表 + +| 错误名 | 值 | 含义 | 返回时机 | +|-----------------|-----|------------------------|-----------------------------| +| EBADF | 9 | 错误的文件描述符 | FD 无效或不是 socket | +| EAGAIN/EWOULDBLOCK | 11 | 资源暂时不可用 | 非阻塞操作会阻塞 | +| ENOMEM | 12 | 内存不足 | 无法分配 socket | +| EFAULT | 14 | 错误的地址 | 用户指针无效 | +| EINVAL | 22 | 无效参数 | 参数检查失败 | +| EMFILE | 24 | 打开文件过多 | FD 表已满 | +| ENOTSOCK | 88 | 不是 socket | 对非 socket FD 操作 | +| EDESTADDRREQ | 89 | 需要目标地址 | UDP sendto 未提供地址 | +| EPROTONOSUPPORT | 93 | 不支持的协议 | 协议参数错误 | +| EOPNOTSUPP | 95 | 不支持的操作 | 对 UDP socket 调用 listen | +| EAFNOSUPPORT | 97 | 不支持的地址族 | domain 不是 AF_INET | +| EADDRINUSE | 98 | 地址已被使用 | bind 到已占用端口 | +| ECONNREFUSED | 111 | 连接被拒绝 | connect 失败 | + +--- + +**文档维护**: 实现过程中遇到问题或有新发现时,请更新此文档。 diff --git a/doc/net/network_rearchitecture.md b/doc/net/network_rearchitecture.md new file mode 100644 index 00000000..d3d58bb5 --- /dev/null +++ b/doc/net/network_rearchitecture.md @@ -0,0 +1,20 @@ +# 网络重构施工记录 + +本文档记录 `new-main` 上网络模块重构的执行顺序。当前事实应写入 `document/net/`,本目录只保留施工上下文。 + +## 执行顺序 + +1. 更新 `AGENTS.md`,固定网络重构边界。 +2. 修正 `document/net/`,把旧“待实现指南”改成当前架构事实。 +3. 引入 `NetworkStack`,把 smoltcp runtime 和 socket 操作收口到 `net::stack`。 +4. 拆分设备注册和接口控制面,VirtIO 只注册 `NetDevice`。 +5. 显式建模 `lo` 和 loopback link。 +6. 清理 syscall 层,移除裸 `SOCKET_SET`、`NET_IFACE` 和 smoltcp socket 类型依赖。 +7. 执行 `cargo fmt && cargo check`。 + +## 验收边界 + +- `os/src/kernel/syscall/network.rs` 不直接访问协议栈内部状态。 +- `NetworkInterface` 不实现 `Driver`,中断兼容通过 `NetDriverHandle`。 +- `NullNetDevice` 不承担 loopback 语义。 +- `document/net/` 的说明与代码一致。 diff --git a/doc/net/rearchitecture_device.md b/doc/net/rearchitecture_device.md new file mode 100644 index 00000000..16ed524e --- /dev/null +++ b/doc/net/rearchitecture_device.md @@ -0,0 +1,49 @@ +# 网络设备层重构说明 + +本文档只覆盖 `device::net`。设备层的任务是提供 NIC 收发能力,不承载接口配置、协议栈状态或用户 socket 语义。 + +## 当前代码 + +- `os/src/device/net/net_device.rs` 定义 `NetDevice` 和 `VirtioNetDevice`。 +- `os/src/device/net/virtio_net.rs` 初始化 VirtIO 网卡,同时创建 `NetworkInterface` 并注册为 `Driver`。 +- `os/src/device/net/null_net.rs` 提供 `NullNetDevice`,当前被 loopback 兼容路径间接依赖。 +- `os/src/device/net/mod.rs` 维护 `NETWORK_DEVICES`。 + +## 目标边界 + +`NetDevice` 只允许表达: + +- 二层帧 `send()` / `receive()` +- `device_id()` +- `mtu()` +- `name()` +- `mac_address()` +- 链路状态和设备能力查询 + +`NetDevice` 禁止表达: + +- IP 地址、网关、路由 +- socket 或 fd 状态 +- `smoltcp` 类型 +- loopback 特判 + +## 必做改造 + +1. 为 `NetDevice` 增加最小能力描述类型,例如 `NetDeviceCaps` 和 `LinkState`。 +2. 把 `VirtioNetDevice` 的职责限制为 VirtIO 队列收发、MAC、MTU、设备状态。 +3. 新增网络子系统设备接入口,例如 `net::register_net_device(Arc)`。 +4. `virtio_net::init()` 和 `virtio_net::init_pci()` 只注册设备,不直接创建接口对象。 +5. 如果中断注册仍需要 `Driver`,新增独立的 net driver shim,避免 `NetworkInterface` 继续实现 `Driver`。 + +## 迁移兼容 + +- 第一阶段可保留 `NETWORK_DEVICES`,但它只能是设备表,不能成为接口表。 +- 第一阶段可保留 `NullNetDevice`,但文档和注释必须标明它不是 loopback。 +- 若必须保留旧 `virtio_net::init()` 行为,使用薄包装调用新的网络子系统注册入口。 + +## 验收点 + +- 替换 `VirtioNetDevice` 实现不需要修改 `os/src/kernel/syscall/network.rs`。 +- 设备层文件不 import `smoltcp`。 +- 设备层文件不 import `SocketFile`、`SOCKET_SET`、`NET_IFACE`。 +- `NetDeviceAdapter` 不在 `device::net` 公开 API 中出现。 diff --git a/doc/net/rearchitecture_execution_plan.md b/doc/net/rearchitecture_execution_plan.md new file mode 100644 index 00000000..3a06236b --- /dev/null +++ b/doc/net/rearchitecture_execution_plan.md @@ -0,0 +1,19 @@ +# 网络重构执行计划 + +本计划用于将旧 `main` 中已经验证的网络重构重新落实到 `new-main`。 + +## 阶段 + +1. `AGENTS.md`:补齐各目录边界规则,尤其是 `net`、`device/net`、`kernel/syscall`。 +2. 文档:直接修正 `document/net/` 中的旧文档,拆分为架构、设备接口、栈运行时、socket syscall、loopback poll、测试。 +3. 协议栈:新增 `os/src/net/stack.rs`,让 `NetworkStack` 持有 runtime 状态并提供门面 API。 +4. Socket/syscall:`SocketFile` 和网络 syscall 经 `NetworkStack` 操作协议栈。 +5. 设备接口:新增 `register_net_device()`,VirtIO 只交出 `NetDevice`;接口注册由网络子系统负责。 +6. Loopback:新增 `LoopbackNetDevice`,默认配置确保 `lo` 存在。 +7. 验证:格式化、编译检查、grep 边界。 + +## 保留兼容 + +- `SocketHandle` 暂时仍包装 smoltcp handle。 +- 当前仍是单 active smoltcp interface runtime。 +- `NetworkInterface::create_smoltcp_interface()` 暂时保留为初始化工厂。 diff --git a/doc/net/rearchitecture_interface.md b/doc/net/rearchitecture_interface.md new file mode 100644 index 00000000..bc849f5f --- /dev/null +++ b/doc/net/rearchitecture_interface.md @@ -0,0 +1,64 @@ +# 网络接口层重构说明 + +本文档覆盖 `net::if`,也就是内核控制面中的网络接口对象。接口层描述 `lo`、`eth0` 这类接口,而不是协议栈运行时。 + +## 当前代码 + +- `os/src/net/interface.rs` 中的 `NetworkInterface` 同时保存接口配置、持有设备、创建 `SmoltcpInterface`、实现 `Driver`。 +- `NETWORK_INTERFACE_MANAGER` 当前维护 `Vec>`。 +- `getifaddrs` 和网络配置路径依赖接口枚举与 IP 配置。 + +## 目标对象 + +建议拆出: + +- `NetIf`:接口身份和只读查询入口。 +- `NetIfConfig`:MAC、IP 地址列表、gateway、MTU、flags。 +- `NetIfState`:up/running/link/loopback 等运行状态。 +- `InterfaceRegistry`:接口注册、枚举、按名查找。 + +接口对象可以持有 `Arc` 或 loopback 后端引用,但不能持有 `smoltcp::Interface` 或 `SocketSet`。 + +## 必做改造 + +1. 把 `NetworkInterface::create_smoltcp_interface()` 从接口对象中移除或改成仅供兼容层内部调用。 +2. 把 `NetworkInterface` 实现 `Driver` 的逻辑迁出到设备/中断适配对象。 +3. 将 `interrupt_enabled`、`last_interrupt_time` 这类中断推进状态从接口配置对象中剥离。 +4. `NETWORK_INTERFACE_MANAGER` 改名或收缩为 `InterfaceRegistry`,只保留注册、枚举、查找。 +5. `NetworkConfigManager::set_interface_config()` 只修改接口配置,并通过 `NetworkStack` 通知协议栈刷新地址和路由。 + +## 接口 flags + +至少应稳定表达: + +- `UP` +- `RUNNING` +- `LOOPBACK` +- `BROADCAST` +- `MULTICAST` + +`lo` 必须显式带 `LOOPBACK`;真实网卡不能因无 loopback 技巧而伪装成 `lo`。 + +## 迁移兼容 + +- 类型名 `NetworkInterface` 可暂时保留,但语义要逐步收缩。 +- 旧的 `NETWORK_INTERFACE_MANAGER` 可作为 `InterfaceRegistry` 的别名保留一个阶段。 +- `getifaddrs` 可先继续读取旧接口对象,但新增字段必须来自接口层而不是协议栈运行时。 + +## 验收点 + +- 接口层不 import `smoltcp::iface::Interface`。 +- 接口层不 import `SocketSet`。 +- 接口枚举能区分 `lo` 和 `ethN`。 +- IP/gateway 修改有单一入口,并能触发协议栈配置刷新。 + +## 当前执行状态 + +- `NetworkInterface` 已从 `Driver` trait 实现中拆出;兼容中断注册由 `NetDriverHandle` 负责。 +- `virtio_net` 不再直接创建或注册接口对象,而是调用网络子系统的 `register_net_device(device)`。 +- 默认网络初始化会确保 `lo` 接口存在;真实 `ethN` 与 `lo` 的枚举身份已分开。 + +保留的兼容点: + +- `NetworkInterface::create_smoltcp_interface()` 仍作为单 active runtime 的兼容工厂存在,后续可继续下沉到 `NetworkStack` 初始化路径。 +- `interrupt_enabled` 和 `last_interrupt_time` 暂留在 `NetworkInterface`,由 `NetDriverHandle` 通过接口对象访问。 diff --git a/doc/net/rearchitecture_loopback_poll.md b/doc/net/rearchitecture_loopback_poll.md new file mode 100644 index 00000000..1ff7d852 --- /dev/null +++ b/doc/net/rearchitecture_loopback_poll.md @@ -0,0 +1,81 @@ +# Loopback 与 poll 路径重构说明 + +本文档覆盖 loopback、RX/TX、poll/select 唤醒与中断协作。这里是当前网络实现最容易“补丁堆补丁”的区域,必须先固定模型。 + +## 当前代码 + +- `NetDeviceAdapter` 内部有 `loopback_queue`。 +- `NetTxToken::consume()` 根据 `device.name() == "null-net"` 或 127 地址判断是否回灌队列。 +- `NetIfaceWrapper::poll()` 为 loopback 做额外 bounded poll。 +- `poll_until_empty()` 为本地回环和 netperf 场景主动 drain。 +- `NetworkInterface::try_handle_interrupt()` 直接从设备收包并唤醒 poll waiters。 + +## 目标模型 + +- `lo` 是显式接口,不是 `NullNetDevice` 或 `NetDeviceAdapter` 的隐藏行为。 +- 真实 NIC 的 RX/TX 只来自设备队列。 +- loopback 的 RX/TX 走独立 loopback 后端或 `NetworkStack` 内部虚拟链路。 +- 中断路径只标记事件并调度网络 poll,不直接维护另一份协议栈推进逻辑。 +- `NetworkStack::poll()` 是唯一入口。 + +## Loopback 建模选择 + +优先方案: + +- 新增 `LoopbackDevice`,实现与 `NetDevice` 相近的二层帧收发接口。 +- 网络子系统启动时创建 `lo`,配置 `127.0.0.1/8`。 +- loopback 发送帧进入自己的 RX 队列,再由 `NetworkStack::poll()` 消费。 + +备选方案: + +- 不创建完整设备对象,只在 `NetworkStack` 内部维护 `LoopbackLink`。 +- `lo` 仍必须作为 `NetIf` 出现在接口注册表中。 + +无论使用哪个方案,都不能继续把 loopback 写在普通 `NetDeviceAdapter` 的特判里。 + +## poll 改造 + +`NetworkStack::poll()` 应按固定顺序执行: + +1. 采样当前时间。 +2. 从真实设备和 loopback 后端接收帧。 +3. 调用 smoltcp poll。 +4. 处理 TCP close reap。 +5. 处理 UDP dispatch。 +6. 计算 socket 可读/可写变化。 +7. 统一唤醒 poll/select waiters。 + +## 中断协作 + +- 设备中断处理不直接调用 smoltcp poll。 +- 中断处理只记录对应设备或接口有 RX/TX 事件。 +- 如果当前内核没有独立网络线程,可在中断返回前轻量唤醒 waiters,由进程上下文中的 poll 路径推进。 +- 不得在中断上下文执行会分配内存的 UDP dispatch。 + +## 迁移步骤 + +1. 给当前额外 poll 逻辑加清晰注释,标记为兼容层。 +2. 新增显式 `lo` 接口并让接口枚举能看到它。 +3. 将 `NullNetDevice` 与 loopback 语义拆开。 +4. 将 `loopback_queue` 从 `NetDeviceAdapter` 移到 loopback 后端。 +5. 删除 `device.name() == "null-net"` 特判。 +6. 将 `poll_until_empty()` 降级为测试/兼容包装,主路径只调用 `NetworkStack::poll()`。 + +## 验收点 + +- 没有真实网卡时,`lo` 仍能服务 TCP/UDP 127.0.0.1。 +- 有真实网卡时,127.0.0.1 不经真实 NIC。 +- UDP poll/select 可读事件不依赖 ad-hoc 额外轮询补丁。 +- 中断路径和主动 poll 不形成双重状态源。 + +## 当前执行状态 + +- 新增 `LoopbackNetDevice`,无真实 NIC 时由默认配置创建显式 `lo`。 +- 默认网络初始化会先确保 `lo` 存在,再选择真实 `ethN` 或 `lo` 作为当前单 active smoltcp runtime。 +- `NetTxToken` 不再检查 `device.name() == "null-net"`;`NullNetDevice` 不再表示 loopback。 +- 兼容 loopback link 已归 `net::stack` runtime,普通 `NetDeviceAdapter` 不再持有自己的 loopback 队列字段。 + +保留的兼容点: + +- 当前仍是单 active interface runtime;有真实 NIC 时 `lo` 主要作为接口枚举和 127/8 runtime link 语义存在,还不是独立 smoltcp interface。 +- `poll_until_empty()` 仍作为 loopback/netperf 兼容包装保留,主路径继续通过 `NetworkStack::poll()` 推进。 diff --git a/doc/net/rearchitecture_socket_syscall.md b/doc/net/rearchitecture_socket_syscall.md new file mode 100644 index 00000000..e26ce55d --- /dev/null +++ b/doc/net/rearchitecture_socket_syscall.md @@ -0,0 +1,76 @@ +# Socket 与 syscall 重构说明 + +本文档覆盖 `SocketFile`、fd 映射和 `kernel::syscall::network`。目标是让 syscall 层只处理用户 ABI,底层网络行为全部转发给 `NetworkStack`。 + +## 当前代码 + +- `os/src/kernel/syscall/network.rs` 直接 import `SOCKET_SET`、`SocketHandle`、`smoltcp::socket::{tcp, udp}`。 +- `SocketFile` 在 `os/src/net/socket.rs` 中直接读取 `SOCKET_SET`。 +- `FD_SOCKET_MAP` 使用 `(tid, fd) -> SocketHandle` 记录 fd 与 smoltcp handle 的关系。 +- `set_network_interface_config()` 仍返回 `-1/-2/-3` 这类私有错误码。 + +## 目标边界 + +syscall 层只负责: + +- 提取架构层传入的 syscall 参数。 +- 使用 `SumGuard` 访问用户指针。 +- sockaddr 与用户缓冲区的 ABI 编解码。 +- fd table 查询和 `SocketFile` 创建。 +- 将请求转发到 `NetworkStack`。 +- 返回标准 Linux errno。 + +syscall 层禁止: + +- 直接访问 `smoltcp::SocketSet`。 +- 直接匹配 `tcp::State` 或 `udp::Socket`。 +- 直接推进设备 poll。 +- 直接读取或修改 `NET_IFACE`。 + +## SocketFile 职责 + +`SocketFile` 保留为 VFS 文件对象,但只保存逻辑状态: + +- `StackSocketId` +- local endpoint +- remote endpoint +- flags/options +- listener backlog +- shutdown 状态 +- UDP per-fd 接收队列 + +`SocketFile::read()`、`write()`、`readable()`、`writable()` 调用 `NetworkStack` 方法,不直接锁 `SocketSet`。 + +## 必做改造 + +1. 定义 `StackSocketId`,替代公开传递的 `SocketHandle`。 +2. 将 `FD_SOCKET_MAP` 的 value 从 smoltcp handle 改为 `StackSocketId`;更理想的是让 fd 的 `SocketFile` 成为唯一映射源。 +3. 将 `create_tcp_socket()`、`create_udp_socket()` 改为 `NetworkStack::create_socket()`。 +4. 将 `tcp_connect()`、`socket_sendto()`、`udp_attach_fd_to_port()` 等函数改为 `NetworkStack` 方法。 +5. `kernel::syscall::network.rs` 中的 `SOCKET_SET` 和 `smoltcp` import 必须消失。 +6. 所有网络 syscall 统一使用 `uapi::errno`,不再返回私有负数。 + +## 阻塞与非阻塞语义 + +- 非阻塞 fd 遇到暂不可读/写返回 `EAGAIN`。 +- 阻塞路径可以循环调用 `NetworkStack::poll()`、`yield_task()`、信号中断检查。 +- poll/select 的可读可写判断必须依赖 `SocketFile` 的公开方法,而这些方法内部转发到 `NetworkStack` 查询。 + +## 验收点 + +- `socket()` 只创建 `SocketFile` 和网络栈 socket,不分配虚假 fd。 +- `bind/listen/connect/accept/send/recv/sendto/recvfrom` 都经 `NetworkStack`。 +- `getsockname/getpeername` 不直接读取 smoltcp socket。 +- 用户指针访问不发生在持有网络栈内部锁期间。 + +## 当前执行状态 + +- `kernel::syscall::network` 已不再 import `SOCKET_SET`、`NET_IFACE` 或 `smoltcp::socket::{tcp, udp}`。 +- `listen/accept/connect/shutdown/getsockname/getpeername` 通过 `NetworkStack` 查询或修改协议栈状态。 +- `SocketFile::read/write/readable/writable/recvfrom/drop` 已改为调用 `NetworkStack` 文件级 API。 +- `set_network_interface_config()` 已从私有 `-1/-2/-3/-4/-5` 返回值改为 `uapi::errno`。 + +保留的兼容点: + +- `FD_SOCKET_MAP` 的 value 仍是 `SocketHandle`,尚未替换为真正独立的 `StackSocketId`。 +- `socket.rs` 仍包含 socket runtime 的 smoltcp 具体操作,但这些操作已位于 `NetworkStack` 调用边界之后。 diff --git a/doc/net/rearchitecture_stack_runtime.md b/doc/net/rearchitecture_stack_runtime.md new file mode 100644 index 00000000..4a529c9d --- /dev/null +++ b/doc/net/rearchitecture_stack_runtime.md @@ -0,0 +1,86 @@ +# 协议栈运行时重构说明 + +本文档覆盖 `net::stack`。该层是 `smoltcp` 的唯一宿主,负责统一持有协议栈状态、socket 集合、设备适配器和 poll 推进逻辑。 + +## 当前代码 + +- `os/src/net/socket.rs` 定义 `SOCKET_SET`、`NET_IFACE`、`FD_SOCKET_MAP`、`NetIfaceWrapper`。 +- `os/src/net/interface.rs` 定义 `SmoltcpInterface` 和 `NetDeviceAdapter`。 +- `poll_network_interfaces()`、`poll_network_and_dispatch()`、`poll_until_empty()` 分散承担协议栈推进。 +- UDP per-port dispatcher 与 TCP pending close 也在 `socket.rs` 内部。 + +## 目标对象 + +建议引入: + +- `NetworkStack`:网络协议栈对外门面。 +- `StackRuntime`:单接口或多接口的 smoltcp runtime。 +- `StackSocketId`:网络子系统内部 socket id,不直接等同于 smoltcp handle。 +- `StackPollResult`:poll 后对 VFS/poll waiters 发布的事件摘要。 + +`NetworkStack` 内部独占持有: + +- `smoltcp::iface::Interface` +- `smoltcp::iface::SocketSet` +- `NetDeviceAdapter` +- UDP dispatch 表 +- TCP pending close 表 +- waiters 唤醒所需事件摘要 + +## 对外 API + +第一阶段 API 可以保持小而稳定: + +```rust +impl NetworkStack { + pub fn create_socket(&self, kind: SocketKind) -> Result; + pub fn bind(&self, id: StackSocketId, endpoint: NetEndpoint) -> Result<(), NetError>; + pub fn listen(&self, id: StackSocketId, backlog: usize) -> Result<(), NetError>; + pub fn accept(&self, id: StackSocketId) -> Result; + pub fn connect(&self, id: StackSocketId, remote: NetEndpoint, local: NetEndpoint) -> Result<(), NetError>; + pub fn send(&self, id: StackSocketId, data: &[u8]) -> Result; + pub fn recv(&self, id: StackSocketId, data: &mut [u8]) -> Result; + pub fn poll(&self) -> StackPollResult; +} +``` + +这里的 `NetEndpoint`、`NetError` 应是网络子系统自有类型,避免 syscall 层直接依赖 smoltcp 类型。 + +## 必做改造 + +1. 把 `SmoltcpInterface` 下沉到 `net::stack`,不再从接口层公开返回。 +2. 把 `NetDeviceAdapter` 移到 `net::stack` 内部。 +3. 把 `SOCKET_SET` 和 `NET_IFACE` 收进 `NetworkStack`,旧全局符号改成兼容包装。 +4. 把 UDP dispatch、TCP pending close、poll waiters 唤醒统一放入 `NetworkStack::poll()`。 +5. 所有 socket 操作完成后如需推进协议栈,只调用 `NetworkStack::poll()`。 + +## 锁顺序 + +推荐固定为: + +`NetworkStack -> InterfaceRuntime -> SocketSet -> SocketFile local state` + +禁止: + +- syscall 层先锁 `SocketFile` 后抓裸 `SOCKET_SET`。 +- 中断路径直接抓 `SocketSet` 并执行 smoltcp poll。 +- 持有网络栈锁时复制用户缓冲区。 + +## 验收点 + +- `smoltcp` import 集中在 `net::stack` 和少量兼容转换模块。 +- `SOCKET_SET` 不再作为裸全局暴露给 syscall。 +- `NetworkStack::poll()` 是唯一协议栈推进入口。 +- UDP 可读事件、TCP close reaping、waiter wakeup 都在 poll 结束阶段集中发布。 + +## 当前执行状态 + +- `SOCKET_SET` 与 `NET_IFACE` 已移入 `net::stack`,`socket.rs` 仅作为 runtime 实现模块以 `pub(crate)` 方式引用。 +- `NetworkStack` 已覆盖 socket 创建、TCP connect/listen/state/close/endpoints、UDP dispatch/attach、poll、SocketFile read/write/readable/writable/drop/sendto/recvfrom。 +- UDP dispatch、TCP pending close reaping 和 poll waiter 唤醒仍在统一 poll 路径中执行。 +- loopback 兼容队列已从 `NetDeviceAdapter` 字段移入 `net::stack` runtime 的 loopback link。 + +保留的兼容点: + +- `NetIfaceWrapper` 仍定义在 `socket.rs`,作为旧单接口 runtime 的实现细节;后续可整体搬入 `stack.rs` 或 `stack/runtime.rs`。 +- `SocketHandle` 仍等同于 smoltcp handle 包装,暂未引入多 runtime 安全的 socket id。 diff --git a/document/SUMMARY.md b/document/SUMMARY.md index e1fd6fed..8d8daf12 100644 --- a/document/SUMMARY.md +++ b/document/SUMMARY.md @@ -24,7 +24,14 @@ # 网络 -- [网络实现指南](net/network_implementation_guide.md) +- [网络模块概览](net/README.md) + - [整体架构](net/architecture.md) + - [设备与接口](net/device_and_interface.md) + - [协议栈运行时](net/stack_runtime.md) + - [Socket 与 syscall](net/socket_syscall.md) + - [Loopback 与 poll](net/loopback_poll.md) + - [测试与排查](net/testing.md) + - [网络实现指南](net/network_implementation_guide.md) - [netperf / netserver 测试说明](net/netperf.md) # 同步原语 diff --git a/document/net/README.md b/document/net/README.md new file mode 100644 index 00000000..b0d3ed7c --- /dev/null +++ b/document/net/README.md @@ -0,0 +1,21 @@ +# 网络模块概览 + +网络模块负责把内核网卡设备、接口配置、smoltcp 协议栈、VFS socket 文件和网络 syscall 连接起来。 + +## 源码入口 + +- `os/src/device/net/`:网卡设备抽象和 VirtIO/loopback/null 设备。 +- `os/src/net/interface.rs`:接口注册表、接口配置和 smoltcp interface 兼容工厂。 +- `os/src/net/stack.rs`:`NetworkStack` 门面和协议栈运行时状态。 +- `os/src/net/socket.rs`:`SocketFile`、fd/socket 映射、UDP per-fd 队列和兼容实现。 +- `os/src/kernel/syscall/network.rs`:网络 syscall ABI 层。 + +## 文档导航 + +- `architecture.md`:整体分层和依赖方向。 +- `device_and_interface.md`:设备注册、接口对象和中断兼容桥。 +- `stack_runtime.md`:smoltcp runtime、poll、socket set 和 loopback link。 +- `socket_syscall.md`:SocketFile、fd 映射、syscall errno 边界。 +- `loopback_poll.md`:显式 loopback 和 poll/select 唤醒。 +- `testing.md`:验证矩阵和常见排查。 +- `network_implementation_guide.md`:维护指南。 diff --git a/document/net/architecture.md b/document/net/architecture.md new file mode 100644 index 00000000..775139a5 --- /dev/null +++ b/document/net/architecture.md @@ -0,0 +1,34 @@ +# 网络架构 + +## 目标模型 + +```text +device/net NetDevice + | + v +net::register_net_device() + | + v +NetworkInterface registry + | + v +NetworkStack runtime + | + v +SocketFile / syscall ABI +``` + +依赖方向只能自上而下或通过明确门面调用。设备层不理解 socket,syscall 层不理解 smoltcp 的内部 socket set。 + +## 当前边界 + +- `NetworkStack` 是协议栈状态宿主,持有 smoltcp socket set、active interface runtime、loopback link,并提供 TCP/UDP/socket 文件级 API。 +- `NetworkInterface` 是控制面接口对象,保存名称、MAC、IP 地址、网关和兼容中断状态。 +- `SocketFile` 是 VFS 文件对象,保存 fd 相关逻辑状态,如 local/remote endpoint、flags、shutdown 状态、UDP per-fd 接收队列。 +- `kernel::syscall::network` 只负责用户参数、fd table、sockaddr 编解码和 errno。 + +## 兼容点 + +- 当前仍是单 active smoltcp runtime,多接口 runtime 需要后续引入真正的 `StackSocketId` 和接口路由。 +- `SocketHandle` 仍包装 smoltcp handle,作为旧路径兼容类型。 +- `NetworkInterface::create_smoltcp_interface()` 暂时保留为 runtime 初始化工厂。 diff --git a/document/net/device_and_interface.md b/document/net/device_and_interface.md new file mode 100644 index 00000000..5a029e4c --- /dev/null +++ b/document/net/device_and_interface.md @@ -0,0 +1,43 @@ +# 设备与接口 + +## 设备层 + +`os/src/device/net/` 只负责网卡数据面: + +- `net_device.rs` 定义 `NetDevice`、`NetDeviceError` 和 VirtIO net 设备实现。 +- `virtio_net.rs` 初始化 VirtIO net 设备,并调用网络子系统注册入口。 +- `loopback.rs` 提供显式 loopback 设备。 +- `null_net.rs` 仅作为空设备/占位实现,不表示 loopback。 + +设备层不得直接依赖 IP、网关、socket、syscall 或 smoltcp socket set。 + +## 注册路径 + +真实网卡初始化后调用: + +```rust +crate::net::register_net_device(device) +``` + +该入口负责: + +- 注册底层 `NetDevice`。 +- 创建 `NetworkInterface`。 +- 把接口加入 `NETWORK_INTERFACE_MANAGER`。 +- 通过 `NetDriverHandle` 注册中断兼容桥。 + +## 接口层 + +`NetworkInterface` 保存控制面状态: + +- interface name,如 `eth0`、`lo`。 +- MAC address。 +- IP CIDR 列表。 +- IPv4 gateway。 +- 兼容中断开关和最后中断时间。 + +`NetworkInterface` 不应直接实现 `Driver`。中断体系仍需要 `Driver` 时,使用 `NetDriverHandle`。 + +## Loopback + +默认网络初始化会确保 `lo` 存在。无真实 NIC 时,`lo` 作为 active runtime 接口,配置 `127.0.0.1/8` 且不配置默认网关。 diff --git a/document/net/loopback_poll.md b/document/net/loopback_poll.md new file mode 100644 index 00000000..6ea23e2b --- /dev/null +++ b/document/net/loopback_poll.md @@ -0,0 +1,23 @@ +# Loopback 与 poll + +## Loopback 模型 + +`lo` 是显式接口,不再由 `NullNetDevice` 隐式承担。无真实 NIC 时,默认配置创建 `LoopbackNetDevice`,接口名为 `lo`,地址为 `127.0.0.1/8`。 + +当前仍是单 active smoltcp runtime,因此有真实 NIC 时,127/8 frame 通过 `net::stack` 内部 loopback link 消费;后续多 runtime 可以把 `lo` 升级为独立 smoltcp interface。 + +## Poll 模型 + +协议栈推进集中在 `NetworkStack::poll()`: + +- smoltcp poll。 +- loopback link bounded drain。 +- UDP per-port dispatch 到 per-fd queue。 +- TCP pending close reaping。 +- poll/select waiter wakeup。 + +`poll_until_empty()` 仅作为 loopback/netperf 兼容包装保留,不应成为新的主路径。 + +## 中断协作 + +网络中断兼容桥只记录事件和唤醒 waiter,不直接维护另一份协议栈状态。需要处理收包时,由进程上下文中的 poll/read/write/connect 等路径推进 `NetworkStack`。 diff --git a/document/net/network_implementation_guide.md b/document/net/network_implementation_guide.md index 4089cfb1..da045803 100644 --- a/document/net/network_implementation_guide.md +++ b/document/net/network_implementation_guide.md @@ -1,1290 +1,46 @@ # 网络子系统实现指南 -## 文档概述 +本文档描述 `new-main` 上网络模块的当前实现边界和维护方式。历史上的“待实现 POSIX 网络栈”说明已经合并为当前事实:网络模块以 `NetDevice -> NetworkInterface -> NetworkStack -> SocketFile/syscall` 为主线,保留少量兼容壳体以降低重构风险。 -本文档提供了实现完整网络功能的详细指南,包括修复当前问题和实现真正的 POSIX 网络系统调用。 +## 当前分层 -**创建日期**: 2025-11-24 -**状态**: 设计文档 / 实现待完成 +- 设备层:`os/src/device/net/` 定义和实现网卡数据面,核心抽象是 `NetDevice`。 +- 接口层:`os/src/net/interface.rs` 维护接口名称、MAC、IP、网关和接口枚举。 +- 栈运行时:`os/src/net/stack.rs` 持有 smoltcp runtime、socket set、loopback link 和 poll 推进入口。 +- Socket 层:`os/src/net/socket.rs` 保留 `SocketFile`、fd 映射和 UDP per-fd 队列,协议栈操作通过 `NetworkStack` 门面执行。 +- Syscall 层:`os/src/kernel/syscall/network.rs` 只处理用户 ABI、fd table、sockaddr 编解码和 errno 映射。 ---- +## 关键规则 -## 1. 当前状态与问题总结 +- 设备驱动只注册 `NetDevice`,不直接创建接口、配置 IP 或操作 socket。 +- `SOCKET_SET`、`NET_IFACE`、smoltcp socket 类型不得暴露给 syscall 层。 +- `NetworkStack::poll()` 是协议栈推进和 poll/select 唤醒的统一入口。 +- `lo` 是显式 loopback 语义;不要用 `NullNetDevice` 伪装 loopback。 +- 架构差异只放在 `document/arch/*` 和 `os/src/arch/*`,不要进入网络核心实现。 -### 1.1 已修复的问题 ✓ +## 维护入口 -#### 问题 1: `create_smoltcp_interface` 内存安全问题 -**严重程度**: 🔴 严重 - 未定义行为 +- 总体架构见 `document/net/architecture.md`。 +- 设备和接口见 `document/net/device_and_interface.md`。 +- 栈运行时见 `document/net/stack_runtime.md`。 +- Socket 和 syscall 见 `document/net/socket_syscall.md`。 +- Loopback 与 poll 见 `document/net/loopback_poll.md`。 +- 测试和排查见 `document/net/testing.md`、`document/net/netperf.md`。 -**问题描述**: -```rust -// src/device/net/interface.rs (旧代码) -pub fn create_smoltcp_interface(&self) -> Interface { - let mut device_adapter = NetDeviceAdapter::new(self.device.clone()); - let iface = Interface::new(config, &mut device_adapter, timestamp); - iface // ❌ 返回持有悬垂指针的 Interface! -} -``` - -`device_adapter` 在栈上创建,函数返回后被销毁,导致返回的 `Interface` 持有悬垂指针。 - -**解决方案**: -创建 `SmoltcpInterface` 包装器,确保 Device 和 Interface 有相同的生命周期: - -```rust -pub struct SmoltcpInterface { - device_adapter: NetDeviceAdapter, // 拥有 Device - iface: Interface, // Interface 借用 device_adapter -} - -impl SmoltcpInterface { - fn new(device: Arc, mac_address: EthernetAddress) -> Self { - let mut device_adapter = NetDeviceAdapter::new(device); - let iface = Interface::new(config, &mut device_adapter, timestamp); - Self { device_adapter, iface } - } - - pub fn poll(&mut self, timestamp: Instant, sockets: &mut SocketSet) -> PollResult { - self.iface.poll(timestamp, &mut self.device_adapter, sockets) - } - - pub fn interface_mut(&mut self) -> &mut Interface { &mut self.iface } - pub fn interface(&self) -> &Interface { &self.iface } -} - -// 现在返回包装器而不是裸 Interface -pub fn create_smoltcp_interface(&self) -> SmoltcpInterface { - let mut smoltcp_iface = SmoltcpInterface::new(self.device.clone(), self.mac_address()); - // ... 配置 IP 和路由 - smoltcp_iface -} -``` - -**文件位置**: `os/src/device/net/interface.rs:47-99` - ---- - -#### 问题 2: 子网掩码解析功能有限 -**严重程度**: 🟡 中等 - 功能受限 - -**问题描述**: -`set_interface_config` 函数使用硬编码的 match 语句解析子网掩码,只支持 9 种常见掩码。 - -```rust -// 旧代码 -let prefix_length = match mask { - "255.255.255.0" => 24, - "255.255.0.0" => 16, - // ... 仅支持少数几种 - _ => return Err(NetworkConfigError::InvalidSubnet), -}; -``` - -**解决方案**: -实现通用的子网掩码解析函数,支持任意有效掩码: - -```rust -/// 解析点分十进制子网掩码并计算前缀长度 -/// -/// # 算法 -/// 1. 解析为 4 字节并转换为 u32 -/// 2. 计算前导 1 的个数(前缀长度) -/// 3. 验证掩码有效性:所有 1 必须连续 -/// - 有效: 11111111111111111111111100000000 (0xFFFFFF00) -/// - 无效: 11111111111111110000000011111111 (0xFFFF00FF) -/// -/// # 示例 -/// - "255.255.255.0" → Ok(24) -/// - "255.255.255.128" → Ok(25) -/// - "255.255.255.3" → Err (不连续) -fn parse_subnet_mask(mask: &str) -> Result { - // 解析为 4 字节 - let octets: Result, _> = mask.split('.').map(|s| s.parse()).collect(); - let octets = octets.map_err(|_| NetworkConfigError::InvalidSubnet)?; - - if octets.len() != 4 { - return Err(NetworkConfigError::InvalidSubnet); - } - - // 转换为 u32 - let mask_u32 = ((octets[0] as u32) << 24) - | ((octets[1] as u32) << 16) - | ((octets[2] as u32) << 8) - | (octets[3] as u32); - - // 计算前缀长度 - let prefix_length = mask_u32.leading_ones() as u8; - - // 验证掩码连续性 - if prefix_length == 0 { - if mask_u32 == 0 { Ok(0) } else { Err(NetworkConfigError::InvalidSubnet) } - } else if prefix_length == 32 { - if mask_u32 == 0xFFFFFFFF { Ok(32) } else { Err(NetworkConfigError::InvalidSubnet) } - } else { - let expected_mask = 0xFFFFFFFFu32 << (32 - prefix_length); - if mask_u32 == expected_mask { - Ok(prefix_length) - } else { - Err(NetworkConfigError::InvalidSubnet) - } - } -} - -// 使用 -let prefix_length = Self::parse_subnet_mask(mask)?; -``` - -**文件位置**: `os/src/device/net/config.rs:20-87, 240` - ---- - -### 1.2 未修复的问题 - 网络系统调用存根 - -#### 问题 3: 所有网络系统调用只是存根实现 -**严重程度**: 🔴 严重 - 核心功能缺失 - -**问题描述**: -所有网络系统调用 (`socket`, `bind`, `listen`, `accept`, `connect`, `send`, `recv` 等) 都只返回虚拟值,没有实现真正的网络功能。 - -**当前实现** (`os/src/kernel/syscall/net_syscall.rs`): -```rust -pub fn socket(domain: i32, socket_type: i32, protocol: i32) -> isize { - // TODO: 实现套接字创建 - 3 // ❌ 返回虚拟 FD -} - -pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { - // TODO: 实现绑定逻辑 - 0 // ❌ 假装成功 -} - -pub fn send(sockfd: i32, buf: *const u8, len: usize, flags: i32) -> isize { - // TODO: 实现发送逻辑 - len as isize // ❌ 假装发送了所有数据 -} - -pub fn recv(sockfd: i32, buf: *mut u8, len: usize, flags: i32) -> isize { - // TODO: 实现接收逻辑 - 0 // ❌ 总是返回没有数据 -} -``` - -**影响**: -- 用户程序无法使用网络功能 -- 与 POSIX 标准不兼容 -- 无法运行真实的网络应用 - ---- - -## 2. 完整网络功能架构设计 - -### 2.1 整体架构图 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 用户空间 │ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│ │ TCP 应用 │ │ UDP 应用 │ │ 原始套接字 │ │ -│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ -└─────────┼─────────────────┼─────────────────┼───────────────┘ - │ │ │ - │ POSIX Socket API (syscall) │ - │ │ │ -┌─────────┼─────────────────┼─────────────────┼───────────────┐ -│ │ 内核空间 │ │ -│ ┌──────▼──────────────────▼─────────────▼──────┐ │ -│ │ 网络系统调用层 │ │ -│ │ socket/bind/listen/accept/connect/send/recv │ │ -│ └──────┬──────────────────────────────────┬─────┘ │ -│ │ │ │ -│ ┌──────▼───────────────────────────────────▼─────┐ │ -│ │ Socket 文件抽象层 │ │ -│ │ SocketFile (实现 File trait) │ │ -│ │ - TcpSocketFile │ │ -│ │ - UdpSocketFile │ │ -│ └──────┬──────────────────────────────────┬──────┘ │ -│ │ │ │ -│ ┌──────▼───────────────────────────────────▼──────┐ │ -│ │ 网络协议栈管理器 │ │ -│ │ NetworkStack │ │ -│ │ - SmoltcpInterface (设备 + 接口) │ │ -│ │ - SocketSet (所有 socket 的集合) │ │ -│ │ - Socket 元数据表 │ │ -│ └──────┬────────────────────────────────────────┘ │ -│ │ │ -│ ┌──────▼────────────────────────────────────────┐ │ -│ │ smoltcp 协议栈 │ │ -│ │ - TCP/UDP/IP/ICMP 协议实现 │ │ -│ │ - Socket 管理 │ │ -│ └──────┬─────────────────────────────────────────┘ │ -│ │ │ -│ ┌──────▼────────────────────────────────────────┐ │ -│ │ NetDeviceAdapter (设备适配器) │ │ -│ └──────┬─────────────────────────────────────────┘ │ -│ │ │ -│ ┌──────▼────────────────────────────────────────┐ │ -│ │ VirtIO 网络驱动 │ │ -│ │ (VirtioNet) │ │ -│ └────────────────────────────────────────────────┘ │ -│ │ -└───────────────────────────────────────────────────────────────┘ - │ - ┌────────▼────────┐ - │ 网络硬件 (QEMU) │ - └─────────────────┘ -``` - -### 2.2 关键组件说明 - -#### 2.2.1 Socket 文件抽象 (`os/src/vfs/socket.rs` - 需要创建) - -**目的**: 将 socket 集成到 VFS 中,使其像文件一样可以通过 FD 访问。 - -```rust -use crate::vfs::File; -use alloc::sync::Arc; -use smoltcp::socket::{TcpSocket, UdpSocket}; -use smoltcp::wire::{IpEndpoint, IpAddress}; - -/// Socket 类型 -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SocketType { - Stream, // TCP (SOCK_STREAM) - Datagram, // UDP (SOCK_DGRAM) - Raw, // 原始套接字 (SOCK_RAW) -} - -/// Socket 地址 -#[derive(Debug, Clone, Copy)] -pub struct SocketAddr { - pub ip: IpAddress, - pub port: u16, -} - -impl SocketAddr { - pub fn to_endpoint(&self) -> IpEndpoint { - IpEndpoint::new(self.ip, self.port) - } -} - -/// Socket 状态 -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SocketState { - Closed, - Listening, - Connecting, - Connected, - FinWait, -} - -/// TCP Socket 文件 -pub struct TcpSocketFile { - /// smoltcp socket handle - socket_handle: SocketHandle, - - /// Socket 状态 - state: SpinLock, - - /// 本地绑定地址 - local_addr: SpinLock>, - - /// 远程连接地址 - remote_addr: SpinLock>, - - /// 等待队列(用于 accept 的连接队列) - pending_connections: SpinLock>, - - /// 最大等待连接数(backlog) - backlog: usize, -} - -impl TcpSocketFile { - pub fn new(socket_handle: SocketHandle) -> Self { - Self { - socket_handle, - state: SpinLock::new(SocketState::Closed), - local_addr: SpinLock::new(None), - remote_addr: SpinLock::new(None), - pending_connections: SpinLock::new(VecDeque::new()), - backlog: 0, - } - } - - /// 绑定到本地地址 - pub fn bind(&self, addr: SocketAddr) -> Result<(), NetworkError> { - // 实现绑定逻辑 - todo!() - } - - /// 监听连接 - pub fn listen(&self, backlog: usize) -> Result<(), NetworkError> { - // 实现监听逻辑 - todo!() - } - - /// 接受连接 - pub fn accept(&self) -> Result<(Arc, SocketAddr), NetworkError> { - // 实现 accept 逻辑 - todo!() - } - - /// 连接到远程地址 - pub fn connect(&self, addr: SocketAddr) -> Result<(), NetworkError> { - // 实现连接逻辑 - todo!() - } -} - -/// 为 TcpSocketFile 实现 File trait -impl File for TcpSocketFile { - fn read(&self, buf: &mut [u8]) -> Result { - // 从 TCP socket 读取数据 - // 需要访问全局 NetworkStack 来操作 socket - todo!() - } - - fn write(&self, buf: &[u8]) -> Result { - // 向 TCP socket 写入数据 - todo!() - } - - fn seek(&self, _pos: SeekFrom) -> Result { - // Socket 不支持 seek - Err(FileError::NotSupported) - } - - fn is_seekable(&self) -> bool { - false - } - - // ... 其他 File trait 方法 -} - -/// UDP Socket 文件 -pub struct UdpSocketFile { - socket_handle: SocketHandle, - local_addr: SpinLock>, - remote_addr: SpinLock>, -} - -impl UdpSocketFile { - pub fn new(socket_handle: SocketHandle) -> Self { - Self { - socket_handle, - local_addr: SpinLock::new(None), - remote_addr: SpinLock::new(None), - } - } - - pub fn bind(&self, addr: SocketAddr) -> Result<(), NetworkError> { - todo!() - } - - pub fn sendto(&self, buf: &[u8], addr: SocketAddr) -> Result { - todo!() - } - - pub fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr), NetworkError> { - todo!() - } -} - -impl File for UdpSocketFile { - // 实现类似 TcpSocketFile 的方法 - // ... -} - -/// 网络错误 -#[derive(Debug)] -pub enum NetworkError { - InvalidAddress, - InvalidSocket, - NotConnected, - AlreadyConnected, - ConnectionRefused, - WouldBlock, - Timeout, - // ... 其他错误 -} -``` - -#### 2.2.2 网络协议栈管理器 (`os/src/net/stack.rs` - 需要创建) - -**目的**: 管理全局的 smoltcp 协议栈实例、socket 集合和元数据。 - -```rust -use crate::device::net::interface::SmoltcpInterface; -use crate::sync::SpinLock; -use alloc::collections::BTreeMap; -use alloc::sync::Arc; -use lazy_static::lazy_static; -use smoltcp::socket::{SocketHandle, SocketSet, TcpSocket, UdpSocket}; -use smoltcp::time::Instant; - -/// Socket 元数据 -pub struct SocketMetadata { - pub socket_type: SocketType, - pub local_addr: Option, - pub remote_addr: Option, - pub state: SocketState, -} - -/// 全局网络协议栈 -pub struct NetworkStack { - /// smoltcp 接口(包含 Device 和 Interface) - smoltcp_iface: SpinLock, - - /// Socket 集合(所有 socket 的容器) - socket_set: SpinLock>, - - /// Socket 元数据映射表 - /// SocketHandle -> SocketMetadata - socket_metadata: SpinLock>, - - /// 当前时间(用于 smoltcp) - current_time: SpinLock, -} - -impl NetworkStack { - pub fn new(smoltcp_iface: SmoltcpInterface) -> Self { - Self { - smoltcp_iface: SpinLock::new(smoltcp_iface), - socket_set: SpinLock::new(SocketSet::new(Vec::new())), - socket_metadata: SpinLock::new(BTreeMap::new()), - current_time: SpinLock::new(Instant::from_millis(0)), - } - } - - /// 创建新的 TCP socket - pub fn create_tcp_socket(&self) -> Result { - let mut socket_set = self.socket_set.lock(); - - // 创建 TCP socket 缓冲区 - let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 4096]); - let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 4096]); - let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - - // 添加到 socket 集合 - let socket_handle = socket_set.add(tcp_socket); - - // 记录元数据 - let mut metadata = self.socket_metadata.lock(); - metadata.insert(socket_handle, SocketMetadata { - socket_type: SocketType::Stream, - local_addr: None, - remote_addr: None, - state: SocketState::Closed, - }); - - Ok(socket_handle) - } - - /// 创建新的 UDP socket - pub fn create_udp_socket(&self) -> Result { - let mut socket_set = self.socket_set.lock(); - - // 创建 UDP socket 缓冲区 - let udp_rx_buffer = UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; 16], - vec![0; 4096] - ); - let udp_tx_buffer = UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; 16], - vec![0; 4096] - ); - let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); - - let socket_handle = socket_set.add(udp_socket); - - let mut metadata = self.socket_metadata.lock(); - metadata.insert(socket_handle, SocketMetadata { - socket_type: SocketType::Datagram, - local_addr: None, - remote_addr: None, - state: SocketState::Closed, - }); - - Ok(socket_handle) - } - - /// 删除 socket - pub fn remove_socket(&self, handle: SocketHandle) { - let mut socket_set = self.socket_set.lock(); - socket_set.remove(handle); - - let mut metadata = self.socket_metadata.lock(); - metadata.remove(&handle); - } - - /// 绑定 TCP socket 到本地地址 - pub fn tcp_bind(&self, handle: SocketHandle, addr: SocketAddr) -> Result<(), NetworkError> { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - // 调用 smoltcp 的 listen - socket.listen(addr.port) - .map_err(|_| NetworkError::InvalidAddress)?; - - // 更新元数据 - let mut metadata = self.socket_metadata.lock(); - if let Some(meta) = metadata.get_mut(&handle) { - meta.local_addr = Some(addr); - } - - Ok(()) - } - - /// TCP connect - pub fn tcp_connect( - &self, - handle: SocketHandle, - remote_addr: SocketAddr, - local_port: u16, - ) -> Result<(), NetworkError> { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - socket.connect( - self.smoltcp_iface.lock().interface().context(), - remote_addr.to_endpoint(), - local_port, - ).map_err(|_| NetworkError::ConnectionRefused)?; - - let mut metadata = self.socket_metadata.lock(); - if let Some(meta) = metadata.get_mut(&handle) { - meta.remote_addr = Some(remote_addr); - meta.state = SocketState::Connecting; - } - - Ok(()) - } - - /// TCP send - pub fn tcp_send(&self, handle: SocketHandle, data: &[u8]) -> Result { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - if !socket.can_send() { - return Err(NetworkError::WouldBlock); - } - - socket.send_slice(data) - .map_err(|_| NetworkError::WouldBlock) - } - - /// TCP recv - pub fn tcp_recv(&self, handle: SocketHandle, buffer: &mut [u8]) -> Result { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - if !socket.can_recv() { - return Err(NetworkError::WouldBlock); - } - - socket.recv_slice(buffer) - .map_err(|_| NetworkError::WouldBlock) - } - - /// UDP sendto - pub fn udp_sendto( - &self, - handle: SocketHandle, - data: &[u8], - remote_addr: SocketAddr, - ) -> Result<(), NetworkError> { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - socket.send_slice(data, remote_addr.to_endpoint()) - .map_err(|_| NetworkError::WouldBlock) - } - - /// UDP recvfrom - pub fn udp_recvfrom( - &self, - handle: SocketHandle, - buffer: &mut [u8], - ) -> Result<(usize, SocketAddr), NetworkError> { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - match socket.recv_slice(buffer) { - Ok((size, endpoint)) => { - let addr = SocketAddr { - ip: endpoint.addr, - port: endpoint.port, - }; - Ok((size, addr)) - } - Err(_) => Err(NetworkError::WouldBlock), - } - } - - /// 轮询网络栈(处理所有网络事件) - /// 应该在定时器中断或专门的网络线程中定期调用 - pub fn poll(&self) { - // 更新时间 - let mut current_time = self.current_time.lock(); - *current_time = Instant::from_millis(current_time.total_millis() + 10); - - // 轮询接口 - let mut socket_set = self.socket_set.lock(); - let mut smoltcp_iface = self.smoltcp_iface.lock(); - - smoltcp_iface.poll(*current_time, &mut socket_set); - } -} - -lazy_static! { - /// 全局网络协议栈实例 - pub static ref NETWORK_STACK: SpinLock>> = - SpinLock::new(None); -} - -/// 初始化网络协议栈 -pub fn init_network_stack(smoltcp_iface: SmoltcpInterface) { - let stack = Arc::new(NetworkStack::new(smoltcp_iface)); - *NETWORK_STACK.lock() = Some(stack); -} - -/// 获取全局网络协议栈 -pub fn get_network_stack() -> Option> { - NETWORK_STACK.lock().clone() -} -``` +## 回归要求 -#### 2.2.3 网络系统调用实现 (`os/src/kernel/syscall/net_syscall.rs` - 需要重写) +每次修改网络边界后至少执行: -**目的**: 实现真正的 POSIX socket 系统调用。 - -```rust -use crate::net::stack::{get_network_stack, NetworkStack}; -use crate::net::socket::{SocketAddr, TcpSocketFile, UdpSocketFile}; -use crate::kernel::task::current_task; -use crate::vfs::File; -use alloc::sync::Arc; -use core::ffi::c_void; - -/// sys_socket - 创建套接字 -/// -/// # 参数 -/// - domain: AF_INET (2) / AF_INET6 (10) -/// - type: SOCK_STREAM (1) / SOCK_DGRAM (2) / SOCK_RAW (3) -/// - protocol: 0 (自动选择) -/// -/// # 返回值 -/// - 成功: 文件描述符 -/// - 失败: 负数错误码 -pub fn sys_socket(domain: i32, socket_type: i32, protocol: i32) -> isize { - // 验证参数 - if domain != 2 { // AF_INET - return -97; // -EAFNOSUPPORT - } - - let stack = match get_network_stack() { - Some(s) => s, - None => return -19, // -ENODEV (网络栈未初始化) - }; - - // 根据 socket 类型创建不同的 socket - let socket_file: Arc = match socket_type { - 1 => { // SOCK_STREAM (TCP) - let handle = match stack.create_tcp_socket() { - Ok(h) => h, - Err(_) => return -12, // -ENOMEM - }; - Arc::new(TcpSocketFile::new(handle)) - } - 2 => { // SOCK_DGRAM (UDP) - let handle = match stack.create_udp_socket() { - Ok(h) => h, - Err(_) => return -12, // -ENOMEM - }; - Arc::new(UdpSocketFile::new(handle)) - } - _ => return -93, // -EPROTONOSUPPORT - }; - - // 将 socket 添加到当前任务的文件描述符表 - let task = current_task().unwrap(); - let fd = match task.add_file(socket_file) { - Ok(fd) => fd, - Err(_) => return -24, // -EMFILE (太多打开的文件) - }; - - fd as isize -} - -/// sys_bind - 绑定套接字到地址 -/// -/// # 参数 -/// - sockfd: socket 文件描述符 -/// - addr: sockaddr 结构指针 -/// - addrlen: 地址长度 -/// -/// # 返回值 -/// - 成功: 0 -/// - 失败: 负数错误码 -pub fn sys_bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { - if sockfd < 0 || addr.is_null() { - return -14; // -EFAULT - } - - // 解析 sockaddr_in 结构 - let socket_addr = unsafe { - if addrlen < 16 { // sizeof(sockaddr_in) - return -22; // -EINVAL - } - - // sockaddr_in 结构: - // - u16 sin_family - // - u16 sin_port (网络字节序) - // - u32 sin_addr (网络字节序) - let port = u16::from_be((addr.add(2) as *const u16).read()); - let ip_bytes = core::slice::from_raw_parts(addr.add(4), 4); - let ip = IpAddress::v4(ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3]); - - SocketAddr { ip, port } - }; - - // 获取 socket 文件 - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, // -EBADF - }; - - // 尝试向下转型为 TcpSocketFile 或 UdpSocketFile - if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { - match tcp_socket.bind(socket_addr) { - Ok(_) => 0, - Err(_) => -98, // -EADDRINUSE - } - } else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { - match udp_socket.bind(socket_addr) { - Ok(_) => 0, - Err(_) => -98, // -EADDRINUSE - } - } else { - -88 // -ENOTSOCK - } -} - -/// sys_listen - 监听连接 -pub fn sys_listen(sockfd: i32, backlog: i32) -> isize { - if sockfd < 0 { - return -9; // -EBADF - } - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - // 只有 TCP socket 支持 listen - if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { - match tcp_socket.listen(backlog as usize) { - Ok(_) => 0, - Err(_) => -22, // -EINVAL - } - } else { - -95 // -EOPNOTSUPP (操作不支持) - } -} - -/// sys_accept - 接受连接 -pub fn sys_accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { - if sockfd < 0 { - return -9; - } - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - let tcp_socket = match socket_file.as_any().downcast_ref::() { - Some(s) => s, - None => return -88, // -ENOTSOCK - }; - - // 接受连接 - let (new_socket_file, remote_addr) = match tcp_socket.accept() { - Ok(result) => result, - Err(_) => return -11, // -EAGAIN (没有连接可接受) - }; - - // 填充地址信息 - if !addr.is_null() && !addrlen.is_null() { - unsafe { - let available_len = *addrlen as usize; - if available_len >= 16 { - // 填充 sockaddr_in - // ... (类似 bind 的逆操作) - } - } - } - - // 为新连接创建 FD - match task.add_file(new_socket_file) { - Ok(fd) => fd as isize, - Err(_) => -24, // -EMFILE - } -} - -/// sys_connect - 连接到远程地址 -pub fn sys_connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { - if sockfd < 0 || addr.is_null() { - return -14; - } - - // 解析地址(类似 bind) - let socket_addr = unsafe { - // ... 解析 sockaddr_in - }; - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - let tcp_socket = match socket_file.as_any().downcast_ref::() { - Some(s) => s, - None => return -88, - }; - - match tcp_socket.connect(socket_addr) { - Ok(_) => 0, - Err(_) => -111, // -ECONNREFUSED - } -} - -/// sys_send / sys_sendto - 发送数据 -pub fn sys_sendto( - sockfd: i32, - buf: *const u8, - len: usize, - flags: i32, - dest_addr: *const u8, - addrlen: u32, -) -> isize { - if sockfd < 0 || buf.is_null() || len == 0 { - return -14; - } - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - // 从用户空间拷贝数据 - let data = unsafe { core::slice::from_raw_parts(buf, len) }; - - // TCP socket - if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { - match socket_file.write(data) { - Ok(written) => written as isize, - Err(_) => -11, // -EAGAIN - } - } - // UDP socket - else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { - if dest_addr.is_null() { - return -89; // -EDESTADDRREQ - } - - let remote_addr = unsafe { - // 解析 dest_addr - }; - - match udp_socket.sendto(data, remote_addr) { - Ok(sent) => sent as isize, - Err(_) => -11, - } - } else { - -88 // -ENOTSOCK - } -} - -/// sys_recv / sys_recvfrom - 接收数据 -pub fn sys_recvfrom( - sockfd: i32, - buf: *mut u8, - len: usize, - flags: i32, - src_addr: *mut u8, - addrlen: *mut u32, -) -> isize { - if sockfd < 0 || buf.is_null() || len == 0 { - return -14; - } - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - let buffer = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - - // TCP socket - if let Some(_tcp_socket) = socket_file.as_any().downcast_ref::() { - match socket_file.read(buffer) { - Ok(read) => read as isize, - Err(_) => 0, // 没有数据可读 - } - } - // UDP socket - else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { - match udp_socket.recvfrom(buffer) { - Ok((size, remote_addr)) => { - // 填充源地址 - if !src_addr.is_null() && !addrlen.is_null() { - unsafe { - // 填充 sockaddr_in - } - } - size as isize - } - Err(_) => 0, - } - } else { - -88 - } -} - -// ... 其他系统调用 (close, shutdown, getsockopt, setsockopt 等) -``` - ---- - -## 3. 实现步骤 - -### 第 1 步: 创建 Socket 文件抽象 -**文件**: `os/src/vfs/socket.rs` - -1. 定义 `SocketType`, `SocketAddr`, `SocketState` 等基础类型 -2. 实现 `TcpSocketFile` 结构体及其方法 -3. 实现 `UdpSocketFile` 结构体及其方法 -4. 为两者实现 `File` trait -5. 在 `os/src/vfs/mod.rs` 中导出 - -**测试**: 编译通过,类型检查正确 - ---- - -### 第 2 步: 创建网络协议栈管理器 -**文件**: `os/src/net/stack.rs` (需要先创建 `os/src/net/` 目录) - -1. 定义 `NetworkStack` 结构体 -2. 实现 socket 创建/删除方法 -3. 实现 TCP/UDP 操作方法 (bind, connect, send, recv 等) -4. 实现 `poll()` 方法 -5. 创建全局实例和初始化函数 - -**测试**: 编译通过,可以创建 NetworkStack 实例 - ---- - -### 第 3 步: 修改 NetworkInterface 初始化 -**文件**: `os/src/device/net/interface.rs`, `os/src/main.rs` 或初始化代码 - -1. 在网络设备初始化时创建 `SmoltcpInterface` -2. 使用 `SmoltcpInterface` 初始化 `NetworkStack` -3. 设置全局 `NETWORK_STACK` - -**代码示例**: -```rust -// 在网络初始化函数中 -pub fn init_network() { - // ... 初始化网络接口 - let manager = NETWORK_INTERFACE_MANAGER.lock(); - if let Some(iface) = manager.get_interfaces().first() { - let smoltcp_iface = iface.create_smoltcp_interface(); - init_network_stack(smoltcp_iface); - } -} -``` - ---- - -### 第 4 步: 实现网络系统调用 -**文件**: `os/src/kernel/syscall/net_syscall.rs` - -1. 重写 `sys_socket` -2. 重写 `sys_bind` -3. 重写 `sys_listen` -4. 重写 `sys_accept` -5. 重写 `sys_connect` -6. 重写 `sys_sendto` / `sys_send` -7. 重写 `sys_recvfrom` / `sys_recv` -8. 实现其他系统调用 (close, shutdown, getsockopt, setsockopt) - -**测试**: 逐个测试每个系统调用 - ---- - -### 第 5 步: 实现网络轮询机制 -**选项 A: 定时器中断轮询** -```rust -// 在定时器中断处理函数中 -pub fn timer_interrupt_handler() { - // ... 其他定时器逻辑 - - if let Some(stack) = get_network_stack() { - stack.poll(); // 轮询网络栈 - } - - // ... 调度等 -} -``` - -**选项 B: 专门的网络线程** (推荐) -```rust -// 创建内核线程 -pub fn network_polling_thread() { - loop { - if let Some(stack) = get_network_stack() { - stack.poll(); - } - - // 休眠一小段时间(如 10ms) - sleep_ms(10); - } -} -``` - ---- - -### 第 6 步: 处理阻塞和非阻塞 -1. 为 socket 添加阻塞/非阻塞标志 -2. 阻塞模式下,recv/send 应该等待数据或空间可用 -3. 实现等待队列,让任务在 socket 上等待 -4. 网络事件到达时唤醒等待的任务 - ---- - -### 第 7 步: 测试 -1. **基础测试**: 创建 socket, bind, close -2. **UDP 测试**: 发送和接收 UDP 数据包 -3. **TCP 客户端测试**: 连接到外部服务器,发送/接收数据 -4. **TCP 服务器测试**: 监听端口,接受连接 -5. **并发测试**: 多个连接同时工作 -6. **错误处理测试**: 各种错误情况 - -**测试用户程序示例**: -```c -// user/src/test_tcp_client.c -int main() { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) { - printf("socket() failed\n"); - return 1; - } - - struct sockaddr_in server_addr = { - .sin_family = AF_INET, - .sin_port = htons(80), - .sin_addr.s_addr = inet_addr("192.168.1.1"), - }; - - if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { - printf("connect() failed\n"); - return 1; - } - - const char* request = "GET / HTTP/1.0\r\n\r\n"; - send(sockfd, request, strlen(request), 0); - - char buffer[1024]; - int n = recv(sockfd, buffer, sizeof(buffer), 0); - if (n > 0) { - printf("Received: %.*s\n", n, buffer); - } - - close(sockfd); - return 0; -} +```bash +cd os +cargo fmt +cargo check ``` ---- - -## 4. 关键注意事项 - -### 4.1 线程安全 -- 所有全局状态都必须用锁保护 -- 避免死锁:定义锁的获取顺序 -- 注意:不能在中断处理程序中调用可能阻塞的操作 - -### 4.2 内存管理 -- smoltcp 的 socket 缓冲区需要静态生命周期 -- 使用 `Vec` 或 `Box` 分配堆内存 -- 注意避免内存泄漏(socket 关闭时释放资源) - -### 4.3 地址字节序 -- 网络字节序是大端 (big-endian) -- RISC-V 通常是小端 (little-endian) -- 使用 `u16::from_be()` / `u16::to_be()` 转换 - -### 4.4 用户空间内存访问 -- 所有用户指针都必须验证 -- 使用 `copy_from_user` / `copy_to_user` 安全拷贝 -- 防止用户传入内核地址 - -### 4.5 错误处理 -- 使用标准的 POSIX 错误码 (errno) -- 常见错误码: - - `-EBADF` (9): 错误的文件描述符 - - `-EINVAL` (22): 无效参数 - - `-EAGAIN` (11): 资源暂时不可用 - - `-ECONNREFUSED` (111): 连接被拒绝 - - `-EADDRINUSE` (98): 地址已被使用 - ---- - -## 5. 依赖和模块关系 - -### 5.1 新增模块依赖图 -``` -vfs/socket.rs - ├─> sync::SpinLock - ├─> vfs::File (trait) - ├─> net::stack::NetworkStack - └─> smoltcp::socket::{TcpSocket, UdpSocket} - -net/stack.rs - ├─> sync::SpinLock - ├─> device::net::interface::SmoltcpInterface - ├─> smoltcp::socket::SocketSet - └─> smoltcp::time::Instant - -kernel/syscall/net_syscall.rs - ├─> vfs::socket::{TcpSocketFile, UdpSocketFile} - ├─> net::stack::{get_network_stack, NetworkStack} - ├─> kernel::task::current_task - └─> 用户内存访问函数 -``` - -### 5.2 遵循模块分层 -根据 CLAUDE.md 中的模块层次规则: -``` -arch → mm → sync → kernel → {ipc, vfs, fs, net} -``` - -- `net/` 模块位于最上层,可以使用所有下层模块 -- 不能让下层模块依赖 `net/` -- Socket 作为 VFS 的一部分,位于 `vfs/socket.rs` - ---- - -## 6. 性能优化建议(后续) - -1. **零拷贝**: 使用 DMA 直接在网卡和用户空间之间传输 -2. **批量处理**: 一次轮询处理多个数据包 -3. **中断合并**: 减少中断频率 -4. **Socket 缓冲区调优**: 根据应用调整缓冲区大小 -5. **连接复用**: 支持 SO_REUSEADDR 和 SO_REUSEPORT - ---- - -## 7. 未来扩展 - -### 7.1 IPv6 支持 -- 扩展 `SocketAddr` 支持 IPv6 -- 处理 `AF_INET6` domain - -### 7.2 原始套接字 (SOCK_RAW) -- 允许直接访问 IP/ICMP 层 -- 需要权限检查 - -### 7.3 Unix Domain Socket -- 进程间通信 -- 文件系统路径作为地址 - -### 7.4 高级功能 -- `poll()` / `epoll()` 系统调用 -- `sendmsg()` / `recvmsg()` (scatter-gather I/O) -- `sendfile()` (零拷贝文件传输) -- Socket 选项 (SO_KEEPALIVE, TCP_NODELAY 等) - ---- - -## 8. 参考资料 - -### 8.1 文档 -- [smoltcp 文档](https://docs.rs/smoltcp/) -- [POSIX Socket API](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html) -- [Linux Socket Man Pages](https://man7.org/linux/man-pages/man2/socket.2.html) - -### 8.2 代码示例 -- [Redox OS 网络实现](https://gitlab.redox-os.org/redox-os/netstack) -- [rCore 网络模块](https://github.com/rcore-os/rCore/tree/master/kernel/src/net) - ---- - -## 9. 检查清单 - -实现完成后,确保: - -- [ ] 所有 TODO 注释已删除 -- [ ] 所有系统调用都返回正确的错误码 -- [ ] 内存安全(无悬垂指针、无内存泄漏) -- [ ] 线程安全(所有共享状态都有锁保护) -- [ ] 用户输入验证(防止越界、空指针等) -- [ ] 编写了测试用户程序 -- [ ] 通过了所有测试 -- [ ] 更新了相关文档 -- [ ] 代码符合项目风格规范(`cargo fmt`, `make quick_check_style`) - ---- - -## 附录 A: smoltcp Socket 缓冲区大小建议 - -```rust -// TCP -let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 8192]); // 8KB 接收缓冲区 -let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 8192]); // 8KB 发送缓冲区 - -// UDP -let udp_rx_buffer = UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; 16], // 最多 16 个数据包 - vec![0; 4096] // 4KB 总缓冲区 -); -let udp_tx_buffer = UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; 16], - vec![0; 4096] -); -``` - -根据应用需求调整: -- **Web 服务器**: 增大 TCP 缓冲区 (16KB - 64KB) -- **音视频流**: 增大 UDP 缓冲区 -- **嵌入式/内存受限**: 减小缓冲区 (1KB - 2KB) - ---- - -## 附录 B: 常见错误码对照表 - -| 错误名 | 值 | 含义 | 返回时机 | -|-----------------|-----|------------------------|-----------------------------| -| EBADF | 9 | 错误的文件描述符 | FD 无效或不是 socket | -| EAGAIN/EWOULDBLOCK | 11 | 资源暂时不可用 | 非阻塞操作会阻塞 | -| ENOMEM | 12 | 内存不足 | 无法分配 socket | -| EFAULT | 14 | 错误的地址 | 用户指针无效 | -| EINVAL | 22 | 无效参数 | 参数检查失败 | -| EMFILE | 24 | 打开文件过多 | FD 表已满 | -| ENOTSOCK | 88 | 不是 socket | 对非 socket FD 操作 | -| EDESTADDRREQ | 89 | 需要目标地址 | UDP sendto 未提供地址 | -| EPROTONOSUPPORT | 93 | 不支持的协议 | 协议参数错误 | -| EOPNOTSUPP | 95 | 不支持的操作 | 对 UDP socket 调用 listen | -| EAFNOSUPPORT | 97 | 不支持的地址族 | domain 不是 AF_INET | -| EADDRINUSE | 98 | 地址已被使用 | bind 到已占用端口 | -| ECONNREFUSED | 111 | 连接被拒绝 | connect 失败 | - ---- +涉及行为修改时,还需要覆盖: -**文档维护**: 实现过程中遇到问题或有新发现时,请更新此文档。 +- 接口枚举能看到 `lo`,有真实网卡时还能看到 `ethN`。 +- `set_network_interface_config()` 能正确设置和查询 IP、mask、gateway。 +- `socket/bind/listen/accept/connect/send/recv/sendto/recvfrom` 基础路径可用。 +- `ppoll/select` 能观察 TCP accept、TCP recv、UDP recv 可读事件。 +- netperf/netserver 已知 EINTR 行为不要误判为网络重构失败。 diff --git a/document/net/socket_syscall.md b/document/net/socket_syscall.md new file mode 100644 index 00000000..7832e23a --- /dev/null +++ b/document/net/socket_syscall.md @@ -0,0 +1,35 @@ +# Socket 与 syscall + +## SocketFile + +`SocketFile` 是 VFS 文件对象,保存 fd 相关逻辑状态: + +- stack socket handle。 +- listener backlog 和 accept 队列。 +- local/remote endpoint。 +- UDP per-fd receive queue。 +- shutdown、flags、socket options。 + +`SocketFile::read()`、`write()`、`readable()`、`writable()`、`recvfrom()` 和 drop 路径通过 `NetworkStack` 方法进入协议栈。 + +## Syscall 边界 + +`os/src/kernel/syscall/network.rs` 负责: + +- 解析 syscall 参数。 +- 使用 `SumGuard` 访问用户指针。 +- 编解码 sockaddr。 +- 操作 fd table。 +- 将网络行为转发给 `NetworkStack` 或 socket 文件对象。 +- 返回 Linux errno。 + +syscall 层禁止: + +- import `SOCKET_SET` 或 `NET_IFACE`。 +- import `smoltcp::socket::{tcp, udp}`。 +- 匹配 smoltcp TCP state。 +- 持有协议栈锁时复制用户缓冲区。 + +## Errno + +网络 syscall 应使用 `uapi::errno` 或 `FsError::to_errno()`。`set_network_interface_config()` 这类接口配置 syscall 不返回私有 `-1/-2/-3` 错误码。 diff --git a/document/net/stack_runtime.md b/document/net/stack_runtime.md new file mode 100644 index 00000000..25c3887c --- /dev/null +++ b/document/net/stack_runtime.md @@ -0,0 +1,41 @@ +# 协议栈运行时 + +`os/src/net/stack.rs` 是 smoltcp runtime 的归属层。 + +## NetworkStack + +`NetworkStack` 对外提供稳定门面: + +- 创建 TCP/UDP socket。 +- TCP connect/listen/state/close/endpoints。 +- UDP dispatch 和 per-port attach。 +- SocketFile read/write/readable/writable/drop/sendto/recvfrom。 +- poll 和 bounded loopback drain。 + +调用者不应直接访问 smoltcp socket set。 + +## 内部状态 + +`net::stack` 持有: + +- `SOCKET_SET`:smoltcp socket set。 +- `NET_IFACE`:当前 active interface runtime。 +- loopback link:当前单 runtime 下的 loopback frame queue。 + +这些符号仅限 `pub(crate)` 兼容实现使用,不对 syscall 层开放。 + +## Poll 顺序 + +`NetworkStack::poll()` 推进协议栈,并集中处理: + +- smoltcp interface poll。 +- bounded loopback extra poll。 +- UDP datagram dispatch。 +- TCP graceful close reaping。 +- poll/select waiter wakeup。 + +锁顺序保持为: + +```text +NetworkStack -> interface runtime -> SocketSet -> SocketFile local state +``` diff --git a/document/net/testing.md b/document/net/testing.md new file mode 100644 index 00000000..bb726097 --- /dev/null +++ b/document/net/testing.md @@ -0,0 +1,24 @@ +# 网络测试与排查 + +## 基础检查 + +```bash +cd os +cargo fmt +cargo check +``` + +## 功能矩阵 + +- 启动后接口枚举能看到 `lo`。 +- 有真实 VirtIO NIC 时接口枚举能看到 `ethN`。 +- loopback-only 场景能使用 `127.0.0.1/8`。 +- `socket/bind/listen/accept/connect/send/recv/sendto/recvfrom` 基础路径可用。 +- `ppoll/select` 能观察 TCP accept、TCP recv 和 UDP recv 可读事件。 +- netperf/netserver 结果与 `netperf.md` 中记录的 EINTR 现象一致。 + +## 常见问题 + +- 如果 syscall 层出现 `SOCKET_SET`、`NET_IFACE` 或 `smoltcp::socket` import,说明边界回退了。 +- 如果有真实 NIC 时 127.0.0.1 流量尝试走设备发送,检查 `NetTxToken` 的 loopback frame 判定和 stack loopback link。 +- 如果 UDP poll/select 不醒,检查 `udp_dispatch_drain_locked()` 是否在 poll 路径执行,并确认 per-fd queue 是否收到 datagram。 From 11ca65115db7272e2dab08393855e7ec4825a2bc Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 19:26:21 +0800 Subject: [PATCH 03/11] =?UTF-8?q?fix:=20=E5=BF=BD=E7=95=A5=E6=9C=AA?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/build.rs | 2 ++ os/src/main.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/os/build.rs b/os/build.rs index aa42771e..28b8d5ab 100644 --- a/os/build.rs +++ b/os/build.rs @@ -5,6 +5,8 @@ //! 2. Packs them into an init_simple_fs image //! 3. Embeds the image into the kernel binary +#![allow(dead_code)] + use std::env; use std::fs; use std::path::{Path, PathBuf}; diff --git a/os/src/main.rs b/os/src/main.rs index 326ef6d6..2bca27b6 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -9,6 +9,17 @@ #![feature(custom_test_frameworks)] #![test_runner(test_runner)] #![reexport_test_harness_main = "test_main"] +#![allow( + dead_code, + unreachable_code, + unreachable_patterns, + unused_assignments, + unused_doc_comments, + unused_imports, + unused_mut, + unused_unsafe, + unused_variables +)] extern crate alloc; From 37cc37e76ac613e8bad2d4fcce820f53916c70a2 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 19:26:31 +0800 Subject: [PATCH 04/11] =?UTF-8?q?fix:=20=E6=94=B6=E5=8F=A3=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E5=8D=8F=E8=AE=AE=E6=A0=88=E8=BF=90=E8=A1=8C=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/src/kernel/syscall/network.rs | 203 ++++---- os/src/net/socket.rs | 852 +++++++++++++++++++------------ os/src/net/stack.rs | 444 ++++++++++++++++ 3 files changed, 1063 insertions(+), 436 deletions(-) create mode 100644 os/src/net/stack.rs diff --git a/os/src/kernel/syscall/network.rs b/os/src/kernel/syscall/network.rs index 3a6c54e4..838e0afb 100644 --- a/os/src/kernel/syscall/network.rs +++ b/os/src/kernel/syscall/network.rs @@ -97,22 +97,22 @@ use crate::{ arch::trap::SumGuard, kernel::current_task, net::{ - config::NetworkConfigManager, + config::{NetworkConfigError, NetworkConfigManager}, interface::NETWORK_INTERFACE_MANAGER, socket::{ - SOCKET_SET, SocketFile, SocketHandle, create_tcp_socket, create_udp_socket, - get_socket_handle, parse_sockaddr_in, register_socket_fd, unregister_socket_fd, - write_sockaddr_in, + SocketFile, SocketHandle, create_tcp_socket, create_udp_socket, get_socket_handle, + parse_sockaddr_in, register_socket_fd, unregister_socket_fd, write_sockaddr_in, }, + stack::{TcpConnectionState, TcpListenState, network_stack}, }, pr_debug, pr_info, println, uapi::{ + errno, fcntl::{FdFlags, OpenFlags}, socket::{SOCK_CLOEXEC, SOCK_DGRAM, SOCK_NONBLOCK, SOCK_STREAM, SOCK_TYPE_MASK}, }, }; use alloc::sync::Arc; -use smoltcp::socket::{tcp, udp}; use smoltcp::wire::{IpAddress, IpEndpoint, Ipv4Address}; /// 获取网络接口列表 @@ -134,28 +134,28 @@ pub fn set_network_interface_config( let ifname_str = match get_c_str_safe(ifname) { Some(s) => s, None => { - return -1; + return -(errno::EFAULT as isize); } }; let ip_str = match get_c_str_safe(ip) { Some(s) => s, None => { - return -2; + return -(errno::EFAULT as isize); } }; let gateway_str = match get_c_str_safe(gateway) { Some(s) => s, None => { - return -3; + return -(errno::EFAULT as isize); } }; let mask_str = match get_c_str_safe(mask) { Some(s) => s, None => { - return -4; + return -(errno::EFAULT as isize); } }; @@ -165,7 +165,14 @@ pub fn set_network_interface_config( Ok(_) => 0, Err(e) => { println!("Network config error: {:?}", e); - -5 + let errno = match e { + NetworkConfigError::InterfaceNotFound => errno::ENODEV, + NetworkConfigError::InvalidAddress + | NetworkConfigError::InvalidSubnet + | NetworkConfigError::InvalidGateway => errno::EINVAL, + NetworkConfigError::ConfigFailed => errno::EIO, + }; + -(errno as isize) } } } @@ -423,10 +430,11 @@ pub fn listen(sockfd: i32, backlog: i32) -> isize { listen_endpoint.port ); - let mut sockets = SOCKET_SET.lock(); - let socket = sockets.get_mut::(h); - if socket.listen(listen_endpoint).is_err() { - drop(sockets); + if network_stack() + .lock() + .tcp_listen(h, listen_endpoint) + .is_err() + { attempts_left = attempts_left.saturating_sub(1); if attempts_left == 0 { return -98; // EADDRINUSE @@ -434,7 +442,6 @@ pub fn listen(sockfd: i32, backlog: i32) -> isize { endpoint.port = 0; continue; } - drop(sockets); break; } @@ -497,26 +504,29 @@ pub fn accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { None => return -88, // ENOTSOCK }; - let (state, listen_endpoint) = { - let sockets = SOCKET_SET.lock(); - let s = sockets.get::(listen_handle); - (s.state(), s.listen_endpoint()) + let (state, listen_endpoint) = match network_stack() + .lock() + .tcp_listener_state_endpoint(listen_handle) + { + Some(v) => v, + None => return -88, // ENOTSOCK }; - if state != tcp::State::Listen && socket_file.listen_sockets_len() < backlog { + if state != TcpListenState::Listen && socket_file.listen_sockets_len() < backlog { // detach current listen socket immediately let new_listen_handle = match create_tcp_socket() { Ok(SocketHandle::Tcp(h)) => h, _ => return -12, // ENOMEM }; - let mut sockets = SOCKET_SET.lock(); - let new_listen_socket = sockets.get_mut::(new_listen_handle); - if new_listen_socket.listen(listen_endpoint).is_err() { - sockets.remove(new_listen_handle); + if network_stack() + .lock() + .tcp_listen(new_listen_handle, listen_endpoint) + .is_err() + { + network_stack().lock().remove_tcp_socket(new_listen_handle); return -12; // ENOMEM or other error } - drop(sockets); use crate::net::socket::{update_socket_file_handle, update_socket_handle}; update_socket_handle( @@ -527,7 +537,10 @@ pub fn accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { update_socket_file_handle(&file, SocketHandle::Tcp(new_listen_handle)).unwrap(); // Established / CloseWait: this handle is ready to return right away. - if matches!(state, tcp::State::Established | tcp::State::CloseWait) { + if matches!( + state, + TcpListenState::Established | TcpListenState::CloseWait + ) { return accept_return_conn( task.clone(), tid as usize, @@ -561,12 +574,11 @@ fn accept_return_conn( ) -> isize { use crate::net::socket::SocketFile; - let sockets = SOCKET_SET.lock(); - let conn_socket = sockets.get::(conn_handle); - let remote_endpoint = match conn_socket.remote_endpoint() { - Some(ep) => ep, - None => return -11, // EAGAIN - }; + let (remote_endpoint, local_endpoint) = + match network_stack().lock().tcp_accept_endpoints(conn_handle) { + Some(v) => v, + None => return -11, // EAGAIN + }; if !addr.is_null() && !addrlen.is_null() { let _guard = SumGuard::new(); @@ -577,11 +589,10 @@ fn accept_return_conn( } let conn = Arc::new(SocketFile::new(SocketHandle::Tcp(conn_handle))); - if let Some(local_ep) = conn_socket.local_endpoint() { + if let Some(local_ep) = local_endpoint { conn.set_local_endpoint(local_ep); } conn.set_remote_endpoint(remote_endpoint); - drop(sockets); match task.lock().fd_table.alloc(conn) { Ok(fd) => { @@ -630,14 +641,12 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { match handle { SocketHandle::Tcp(h) => { - let sockets = SOCKET_SET.lock(); - let socket = sockets.get::(h); - pr_debug!( - "connect: socket state={:?}, is_open={}", - socket.state(), - socket.is_open() - ); - if socket.is_open() { + let (_state, is_open) = network_stack() + .lock() + .tcp_debug_state(h) + .unwrap_or((TcpConnectionState::Closed, false)); + pr_debug!("connect: socket state={:?}, is_open={}", _state, is_open); + if is_open { return -106; // EISCONN } @@ -649,31 +658,33 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { _ => false, }; - let mut local_endpoint = socket.local_endpoint().unwrap_or_else(|| { - // Choose local address based on remote address - use smoltcp::wire::IpAddress; - let local_addr = match endpoint.addr { - IpAddress::Ipv4(_) => { - if is_loopback { - IpAddress::Ipv4(Ipv4Address::LOCALHOST) - } else { - IpAddress::Ipv4(Ipv4Address::new(10, 0, 2, 15)) + let mut local_endpoint = network_stack() + .lock() + .socket_local_endpoint(handle) + .unwrap_or_else(|| { + // Choose local address based on remote address. + use smoltcp::wire::IpAddress; + let local_addr = match endpoint.addr { + IpAddress::Ipv4(_) => { + if is_loopback { + IpAddress::Ipv4(Ipv4Address::LOCALHOST) + } else { + IpAddress::Ipv4(Ipv4Address::new(10, 0, 2, 15)) + } } - } - #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(_) => { - use smoltcp::wire::Ipv6Address; - if is_loopback { - IpAddress::Ipv6(Ipv6Address::LOOPBACK) - } else { - // Use unspecified address, let the interface choose - IpAddress::Ipv6(Ipv6Address::UNSPECIFIED) + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(_) => { + use smoltcp::wire::Ipv6Address; + if is_loopback { + IpAddress::Ipv6(Ipv6Address::LOOPBACK) + } else { + IpAddress::Ipv6(Ipv6Address::UNSPECIFIED) + } } - } - _ => IpAddress::Ipv4(Ipv4Address::new(10, 0, 2, 15)), - }; - IpEndpoint::new(local_addr, 0) - }); + _ => IpAddress::Ipv4(Ipv4Address::new(10, 0, 2, 15)), + }; + IpEndpoint::new(local_addr, 0) + }); // Allocate ephemeral port if needed if local_endpoint.port == 0 { @@ -689,8 +700,6 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { { sf.set_local_endpoint(local_endpoint); } - drop(sockets); - use crate::net::socket::tcp_connect; if let Err(e) = tcp_connect(h, endpoint, local_endpoint) { pr_debug!("connect: tcp_connect failed: {:?}", e); @@ -699,7 +708,6 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { // For blocking sockets, wait until connection is established if !is_nonblock { - use crate::net::socket::SOCKET_SET; pr_debug!("connect: handle={:?}, entering wait loop", h); loop { // Poll until all loopback packets are processed @@ -707,17 +715,17 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { crate::net::socket::poll_until_empty(); } - let sockets = SOCKET_SET.lock(); - let socket = sockets.get::(h); - let state = socket.state(); + let state = network_stack() + .lock() + .tcp_connection_state(h) + .unwrap_or(TcpConnectionState::Closed); pr_debug!("connect: loop, handle={:?}, state={:?}", h, state); - drop(sockets); - if state == smoltcp::socket::tcp::State::Established { + if state == TcpConnectionState::Established { pr_debug!("connect: established"); break; } - if state == smoltcp::socket::tcp::State::Closed { + if state == TcpConnectionState::Closed { pr_debug!("connect: socket closed, returning ECONNREFUSED"); return -111; // ECONNREFUSED } @@ -811,11 +819,15 @@ pub fn send(sockfd: i32, buf: *const u8, len: usize, _flags: i32) -> isize { let (_tid, file) = { let task_lock = task.lock(); let tid = task_lock.tid; + if sockfd < 0 { + pr_debug!("send: EBADF tid={}, sockfd={}", tid, sockfd); + return -(crate::uapi::errno::EBADF as isize); + } let file = match task_lock.fd_table.get(sockfd as usize) { Ok(f) => f, Err(_) => { - crate::pr_warn!("send: EBADF tid={}, sockfd={}", tid, sockfd); - return -9; + pr_debug!("send: EBADF tid={}, sockfd={}", tid, sockfd); + return -(crate::uapi::errno::EBADF as isize); } }; (tid, file) @@ -860,11 +872,15 @@ pub fn recv(sockfd: i32, buf: *mut u8, len: usize, _flags: i32) -> isize { let (_tid, file) = { let task_lock = task.lock(); let tid = task_lock.tid; + if sockfd < 0 { + pr_debug!("recv: EBADF tid={}, sockfd={}", tid, sockfd); + return -(crate::uapi::errno::EBADF as isize); + } let file = match task_lock.fd_table.get(sockfd as usize) { Ok(f) => f, Err(_) => { - crate::pr_warn!("recv: EBADF tid={}, sockfd={}", tid, sockfd); - return -9; + pr_debug!("recv: EBADF tid={}, sockfd={}", tid, sockfd); + return -(crate::uapi::errno::EBADF as isize); } }; (tid, file) @@ -1726,9 +1742,7 @@ pub fn shutdown(sockfd: i32, how: i32) -> isize { if should_close_tcp { if let SocketHandle::Tcp(h) = handle { - let mut sockets = SOCKET_SET.lock(); - let socket = sockets.get_mut::(h); - socket.close(); + network_stack().lock().tcp_close(h); } } @@ -1751,25 +1765,7 @@ pub fn getsockname(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { }; drop(task_lock); - let sockets = SOCKET_SET.lock(); - let local_endpoint = match handle { - SocketHandle::Tcp(h) => { - let socket = sockets.get::(h); - socket.local_endpoint() - } - SocketHandle::Udp(h) => { - let socket = sockets.get::(h); - let listen_ep = socket.endpoint(); - Some(IpEndpoint::new( - listen_ep - .addr - .unwrap_or(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED)), - listen_ep.port, - )) - } - }; - - drop(sockets); + let local_endpoint = network_stack().lock().socket_local_endpoint(handle); // Linux behavior: getsockname() on an unbound socket typically returns success and // fills a sockaddr with AF_INET and port 0. @@ -1801,15 +1797,10 @@ pub fn getpeername(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { None => return -88, // ENOTSOCK }; - let sockets = SOCKET_SET.lock(); let remote_endpoint = match handle { - SocketHandle::Tcp(h) => { - let socket = sockets.get::(h); - socket.remote_endpoint() - } + SocketHandle::Tcp(_) => network_stack().lock().socket_remote_endpoint(handle), SocketHandle::Udp(_) => { // UDP doesn't have a peer, use stored endpoint - drop(sockets); let file = match task.lock().fd_table.get(sockfd as usize) { Ok(f) => f, Err(_) => return -9, // EBADF diff --git a/os/src/net/socket.rs b/os/src/net/socket.rs index 3a2c8d08..483b7dd2 100644 --- a/os/src/net/socket.rs +++ b/os/src/net/socket.rs @@ -19,7 +19,7 @@ use alloc::collections::BTreeMap; use alloc::sync::Arc; pub struct NetIfaceWrapper { - device: SpinLock, + device: SpinLock, interface: SpinLock, } @@ -103,9 +103,7 @@ impl NetIfaceWrapper { } lazy_static! { - pub static ref SOCKET_SET: SpinLock> = SpinLock::new(SocketSet::new(vec![])); pub static ref FD_SOCKET_MAP: SpinLock> = SpinLock::new(BTreeMap::new()); - pub static ref NET_IFACE: SpinLock> = SpinLock::new(None); // UDP port dispatcher: one smoltcp UDP socket per local port, plus multiple "logical" sockets // (per fd) that receive datagrams based on their connected remote endpoint. static ref UDP_PORTS: SpinLock> = SpinLock::new(BTreeMap::new()); @@ -117,6 +115,8 @@ lazy_static! { SpinLock::new(alloc::vec::Vec::new()); } +use crate::net::stack::{NET_IFACE, SOCKET_SET}; + use crate::uapi::fcntl::OpenFlags; use crate::uapi::socket::SocketOptions; @@ -232,32 +232,9 @@ impl SocketFile { /// This is used to provide a minimal accept/backlog behavior on top of smoltcp's /// single-socket listen model. pub fn take_established_from_listen_queue(&self) -> Option { - let sockets = SOCKET_SET.lock(); - let mut q = self.listen_sockets.lock(); - let mut i = 0; - while i < q.len() { - match q[i] { - SocketHandle::Tcp(h) => { - let s = sockets.get::(h); - match s.state() { - tcp::State::Established | tcp::State::CloseWait => { - return Some(q.remove(i)); - } - tcp::State::Closed => { - q.remove(i); - continue; - } - _ => {} - } - } - SocketHandle::Udp(_) => { - q.remove(i); - continue; - } - } - i += 1; - } - None + crate::net::stack::network_stack() + .lock() + .take_established_from_listen_queue(self) } pub fn set_handle(&self, new_handle: SocketHandle) { @@ -314,68 +291,17 @@ impl SocketFile { } pub fn is_closed(&self) -> bool { - let sockets = SOCKET_SET.lock(); - match self.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get::(*h); - socket.state() == tcp::State::Closed - } - _ => false, - } + crate::net::stack::network_stack() + .lock() + .socket_is_closed(self) } } impl Drop for SocketFile { fn drop(&mut self) { - let mut sockets = SOCKET_SET.lock(); - if let Some(handle) = *self.handle.lock() { - match handle { - SocketHandle::Tcp(h) => { - let socket = sockets.get_mut::(h); - let state = socket.state(); - crate::pr_debug!("[Socket] Drop: handle={:?}, state={:?}", h, state); - // Check if we need to close the socket. - // - // For active connections, do NOT remove from SocketSet immediately after close(), - // otherwise the peer may observe an abortive close and user programs (iperf3) - // can treat it as "unexpectedly closed". - match state { - tcp::State::Closed | tcp::State::TimeWait | tcp::State::Listen => { - // Fully closed, safe to remove now. - sockets.remove(h); - } - _ => { - // Initiate/continue graceful close, and defer removal until the stack - // transitions to Closed/TimeWait (requires polling). - crate::pr_debug!("[Socket] Drop: closing socket handle={:?}", h); - socket.close(); - PENDING_TCP_CLOSE.lock().push(h); - } - } - } - SocketHandle::Udp(h) => { - // UDP sockets may be managed by the per-port dispatcher (shared smoltcp socket). - // Do not remove from SocketSet here; stale logical sockets are cleaned up in the - // dispatcher, which will remove the shared socket when no logical sockets remain. - let ports = UDP_PORTS.lock(); - let is_shared = ports.values().any(|e| e.handle == h); - drop(ports); - if !is_shared { - sockets.remove(h); - } - } - } - } - for handle in self.listen_sockets.lock().iter() { - match handle { - SocketHandle::Tcp(h) => { - sockets.remove(*h); - } - SocketHandle::Udp(h) => { - sockets.remove(*h); - } - } - } + crate::net::stack::network_stack() + .lock() + .drop_socket_file(self); } } @@ -474,11 +400,152 @@ pub fn socket_sendto( handle: SocketHandle, buf: &[u8], endpoint: IpEndpoint, +) -> Result { + crate::net::stack::network_stack() + .lock() + .socket_sendto(handle, buf, endpoint) +} + +impl File for SocketFile { + fn readable(&self) -> bool { + crate::net::stack::network_stack() + .lock() + .socket_readable(self) + } + fn writable(&self) -> bool { + crate::net::stack::network_stack() + .lock() + .socket_writable(self) + } + + fn read(&self, buf: &mut [u8]) -> Result { + crate::net::stack::network_stack() + .lock() + .socket_read(self, buf) + } + + fn write(&self, buf: &[u8]) -> Result { + crate::net::stack::network_stack() + .lock() + .socket_write(self, buf) + } + + fn metadata(&self) -> Result { + Err(FsError::NotSupported) + } + + fn as_any(&self) -> &dyn core::any::Any { + self + } + + fn flags(&self) -> OpenFlags { + *self.flags.lock() + } + + fn set_status_flags(&self, new_flags: OpenFlags) -> Result<(), FsError> { + *self.flags.lock() = new_flags; + Ok(()) + } + + fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, Option>), FsError> { + crate::net::stack::network_stack() + .lock() + .socket_recvfrom(self, buf) + } +} + +pub(crate) fn take_established_from_listen_queue_impl(file: &SocketFile) -> Option { + let sockets = SOCKET_SET.lock(); + let mut q = file.listen_sockets.lock(); + let mut i = 0; + while i < q.len() { + match q[i] { + SocketHandle::Tcp(h) => { + let s = sockets.get::(h); + match s.state() { + tcp::State::Established | tcp::State::CloseWait => { + return Some(q.remove(i)); + } + tcp::State::Closed => { + q.remove(i); + continue; + } + _ => {} + } + } + SocketHandle::Udp(_) => { + q.remove(i); + continue; + } + } + i += 1; + } + None +} + +pub(crate) fn socket_is_closed_impl(file: &SocketFile) -> bool { + let sockets = SOCKET_SET.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get::(*h); + socket.state() == tcp::State::Closed + } + _ => false, + } +} + +pub(crate) fn drop_socket_file_impl(file: &SocketFile) { + let mut sockets = SOCKET_SET.lock(); + if let Some(handle) = *file.handle.lock() { + match handle { + SocketHandle::Tcp(h) => { + let socket = sockets.get_mut::(h); + let state = socket.state(); + crate::pr_debug!("[Socket] Drop: handle={:?}, state={:?}", h, state); + // Active connections are closed gracefully and removed by the poll reaper. + match state { + tcp::State::Closed | tcp::State::TimeWait | tcp::State::Listen => { + sockets.remove(h); + } + _ => { + crate::pr_debug!("[Socket] Drop: closing socket handle={:?}", h); + socket.close(); + PENDING_TCP_CLOSE.lock().push(h); + } + } + } + SocketHandle::Udp(h) => { + // UDP sockets may be shared by the per-port dispatcher. + let ports = UDP_PORTS.lock(); + let is_shared = ports.values().any(|e| e.handle == h); + drop(ports); + if !is_shared { + sockets.remove(h); + } + } + } + } + for handle in file.listen_sockets.lock().iter() { + match handle { + SocketHandle::Tcp(h) => { + sockets.remove(*h); + } + SocketHandle::Udp(h) => { + sockets.remove(*h); + } + } + } +} + +pub(crate) fn socket_sendto_impl( + handle: SocketHandle, + buf: &[u8], + endpoint: IpEndpoint, ) -> Result { let result = { let mut sockets = SOCKET_SET.lock(); match handle { - SocketHandle::Tcp(_) => Err(FsError::NotSupported), // TCP doesn't support sendto + SocketHandle::Tcp(_) => Err(FsError::NotSupported), SocketHandle::Udp(h) => { let socket = sockets.get_mut::(h); socket @@ -489,277 +556,244 @@ pub fn socket_sendto( } }; if result.is_ok() { - poll_network_interfaces(); + poll_network_interfaces_impl(); crate::kernel::syscall::io::wake_poll_waiters(); } result } -impl File for SocketFile { - fn readable(&self) -> bool { - // Listener socket: only readable when a connection is ready to accept. - if *self.is_listener.lock() { - let sockets = SOCKET_SET.lock(); - - for handle in self.listen_sockets.lock().iter() { - if let SocketHandle::Tcp(h) = handle { - let s = sockets.get::(*h); - if matches!(s.state(), tcp::State::Established | tcp::State::CloseWait) { - return true; - } +pub(crate) fn socket_readable_impl(file: &SocketFile) -> bool { + // Listener socket: only readable when a connection is ready to accept. + if *file.is_listener.lock() { + let sockets = SOCKET_SET.lock(); + + for handle in file.listen_sockets.lock().iter() { + if let SocketHandle::Tcp(h) = handle { + let s = sockets.get::(*h); + if matches!(s.state(), tcp::State::Established | tcp::State::CloseWait) { + return true; } } + } - if let Some(SocketHandle::Tcp(h)) = *self.handle.lock() { - let s = sockets.get::(h); - return matches!(s.state(), tcp::State::Established | tcp::State::CloseWait); - } - return false; + if let Some(SocketHandle::Tcp(h)) = *file.handle.lock() { + let s = sockets.get::(h); + return matches!(s.state(), tcp::State::Established | tcp::State::CloseWait); } + return false; + } - let sockets = SOCKET_SET.lock(); - match self.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get::(*h); - let can_recv = socket.can_recv(); - let state = socket.state(); - crate::pr_debug!( - "[Socket] readable: handle={:?}, state={:?}, can_recv={}", - h, - state, - can_recv - ); - // Linux-like semantics: sockets become readable on FIN (EOF). - // smoltcp reports this as CloseWait when the peer has closed. - can_recv || matches!(state, tcp::State::Closed | tcp::State::CloseWait) - } - Some(SocketHandle::Udp(h)) => { - drop(sockets); - self.udp_queue_len() > 0 - } - None => false, + let sockets = SOCKET_SET.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get::(*h); + let can_recv = socket.can_recv(); + let state = socket.state(); + crate::pr_debug!( + "[Socket] readable: handle={:?}, state={:?}, can_recv={}", + h, + state, + can_recv + ); + can_recv || matches!(state, tcp::State::Closed | tcp::State::CloseWait) + } + Some(SocketHandle::Udp(_)) => { + drop(sockets); + file.udp_queue_len() > 0 } + None => false, } - fn writable(&self) -> bool { - let sockets = SOCKET_SET.lock(); - let result = match self.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get::(*h); - let can_send = socket.can_send(); - let state = socket.state(); - crate::pr_debug!( - "[Socket] writable: handle={:?}, state={:?}, can_send={}", - h, - state, - can_send - ); +} + +pub(crate) fn socket_writable_impl(file: &SocketFile) -> bool { + let sockets = SOCKET_SET.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get::(*h); + let can_send = socket.can_send(); + let state = socket.state(); + crate::pr_debug!( + "[Socket] writable: handle={:?}, state={:?}, can_send={}", + h, + state, can_send - } - Some(SocketHandle::Udp(h)) => { - let socket = sockets.get::(*h); - socket.can_send() - } - None => false, - }; - result + ); + can_send + } + Some(SocketHandle::Udp(h)) => { + let socket = sockets.get::(*h); + socket.can_send() + } + None => false, } +} - fn read(&self, buf: &mut [u8]) -> Result { - if self.is_shutdown_read() { - return Ok(0); // EOF - } +pub(crate) fn socket_read_impl(file: &SocketFile, buf: &mut [u8]) -> Result { + if file.is_shutdown_read() { + return Ok(0); + } - let mut sockets = SOCKET_SET.lock(); - let result = match self.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get_mut::(*h); - let state = socket.state(); - let recv_queue = socket.recv_queue(); - crate::pr_debug!( - "[Socket] read: handle={:?}, state={:?}, recv_queue={}, buf.len()={}", - h, - state, - recv_queue, - buf.len() - ); + let mut sockets = SOCKET_SET.lock(); + let result = match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get_mut::(*h); + let state = socket.state(); + let recv_queue = socket.recv_queue(); + crate::pr_debug!( + "[Socket] read: handle={:?}, state={:?}, recv_queue={}, buf.len()={}", + h, + state, + recv_queue, + buf.len() + ); - // Closed socket returns EOF (0 bytes) - if socket.state() == tcp::State::Closed { - return Ok(0); - } + if socket.state() == tcp::State::Closed { + return Ok(0); + } - // Linux-like EOF semantics for TCP: - // once peer has closed (CloseWait) and we have no pending data, read must return 0. - if state == tcp::State::CloseWait && recv_queue == 0 { - return Ok(0); - } + if state == tcp::State::CloseWait && recv_queue == 0 { + return Ok(0); + } - let result = socket.recv_slice(buf).map_err(|_| FsError::WouldBlock); - - // CRITICAL FIX: smoltcp's recv_slice() returns Ok(0) when no data is available - // but the socket is still connected. We need to distinguish between: - // 1. No data available (should return EAGAIN for non-blocking, or block for blocking) - // 2. Connection closed (should return 0 = EOF) - if let Ok(0) = result { - // CloseWait indicates FIN received. Treat 0-length read as EOF. - if state == tcp::State::CloseWait { - crate::pr_debug!( - "[Socket] read: recv_slice returned 0 and state=CloseWait, returning EOF" - ); - Ok(0) - } else - // recv_slice returned 0 bytes - check if this is EOF or just no data - if socket.may_recv() { - // Socket can still receive data, so this is not EOF - // Return EAGAIN to indicate no data available - crate::pr_debug!( - "[Socket] read: recv_slice returned 0 but may_recv=true, returning EAGAIN" - ); - Err(FsError::WouldBlock) - } else { - // Socket cannot receive anymore, this is EOF - crate::pr_debug!( - "[Socket] read: recv_slice returned 0 and may_recv=false, returning EOF" - ); - Ok(0) - } + let result = socket.recv_slice(buf).map_err(|_| FsError::WouldBlock); + + if let Ok(0) = result { + if state == tcp::State::CloseWait { + crate::pr_debug!( + "[Socket] read: recv_slice returned 0 and state=CloseWait, returning EOF" + ); + Ok(0) + } else if socket.may_recv() { + crate::pr_debug!( + "[Socket] read: recv_slice returned 0 but may_recv=true, returning EAGAIN" + ); + Err(FsError::WouldBlock) } else { - if let Ok(n) = result { - crate::pr_debug!("[Socket] read: received {} bytes", n); - } - result + crate::pr_debug!( + "[Socket] read: recv_slice returned 0 and may_recv=false, returning EOF" + ); + Ok(0) } + } else { + if let Ok(n) = result { + crate::pr_debug!("[Socket] read: received {} bytes", n); + } + result } - Some(SocketHandle::Udp(_h)) => { - drop(sockets); - let Some(d) = self.udp_pop() else { - return Err(FsError::WouldBlock); - }; - let n = core::cmp::min(buf.len(), d.len); - buf[..n].copy_from_slice(&d.data[..n]); - Ok(n) - } - None => Err(FsError::InvalidArgument), - }; - if result.is_ok() { - crate::kernel::syscall::io::wake_poll_waiters(); } - result + Some(SocketHandle::Udp(_)) => { + drop(sockets); + let Some(d) = file.udp_pop() else { + return Err(FsError::WouldBlock); + }; + let n = core::cmp::min(buf.len(), d.len); + buf[..n].copy_from_slice(&d.data[..n]); + Ok(n) + } + None => Err(FsError::InvalidArgument), + }; + if result.is_ok() { + crate::kernel::syscall::io::wake_poll_waiters(); } + result +} - fn write(&self, buf: &[u8]) -> Result { - if self.is_shutdown_write() { - return Err(FsError::BrokenPipe); - } +pub(crate) fn socket_write_impl(file: &SocketFile, buf: &[u8]) -> Result { + if file.is_shutdown_write() { + return Err(FsError::BrokenPipe); + } - let result = { - let mut sockets = SOCKET_SET.lock(); - match self.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get_mut::(*h); - let result = socket.send_slice(buf).map_err(|_| FsError::WouldBlock); - - // Similar to recv_slice(), smoltcp may return Ok(0) when it cannot currently - // accept more data, even though the connection is still alive. - if !buf.is_empty() { - if let Ok(0) = result { - if socket.may_send() { - return Err(FsError::WouldBlock); - } else { - return Err(FsError::BrokenPipe); - } + let result = { + let mut sockets = SOCKET_SET.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get_mut::(*h); + let result = socket.send_slice(buf).map_err(|_| FsError::WouldBlock); + + if !buf.is_empty() { + if let Ok(0) = result { + if socket.may_send() { + return Err(FsError::WouldBlock); + } else { + return Err(FsError::BrokenPipe); } } - - result } - Some(SocketHandle::Udp(h)) => { - let endpoint = match self.get_remote_endpoint() { - Some(ep) => ep, - None => return Err(FsError::NotConnected), - }; - let socket = sockets.get_mut::(*h); - socket - .send_slice(buf, endpoint) - .map_err(|_| FsError::WouldBlock)?; - Ok(buf.len()) - } - None => Err(FsError::InvalidArgument), + + result } - }; - if result.is_ok() { - poll_network_interfaces(); - crate::kernel::syscall::io::wake_poll_waiters(); + Some(SocketHandle::Udp(h)) => { + let endpoint = match file.get_remote_endpoint() { + Some(ep) => ep, + None => return Err(FsError::NotConnected), + }; + let socket = sockets.get_mut::(*h); + socket + .send_slice(buf, endpoint) + .map_err(|_| FsError::WouldBlock)?; + Ok(buf.len()) + } + None => Err(FsError::InvalidArgument), } - result - } - - fn metadata(&self) -> Result { - Err(FsError::NotSupported) - } - - fn as_any(&self) -> &dyn core::any::Any { - self - } - - fn flags(&self) -> OpenFlags { - *self.flags.lock() + }; + if result.is_ok() { + poll_network_interfaces_impl(); + crate::kernel::syscall::io::wake_poll_waiters(); } + result +} - fn set_status_flags(&self, new_flags: OpenFlags) -> Result<(), FsError> { - *self.flags.lock() = new_flags; - Ok(()) +pub(crate) fn socket_recvfrom_impl( + file: &SocketFile, + buf: &mut [u8], +) -> Result<(usize, Option>), FsError> { + if file.is_shutdown_read() { + return Ok((0, None)); } - fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, Option>), FsError> { - if self.is_shutdown_read() { - return Ok((0, None)); - } - - let mut sockets = SOCKET_SET.lock(); - match self.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get_mut::(*h); - let state = socket.state(); - if state == tcp::State::Closed { - return Ok((0, None)); - } - if state == tcp::State::CloseWait && socket.recv_queue() == 0 { - return Ok((0, None)); - } - - let result = socket.recv_slice(buf).map_err(|_| FsError::WouldBlock); - let n = if let Ok(0) = result { - if state == tcp::State::CloseWait { - 0 - } else if socket.may_recv() { - return Err(FsError::WouldBlock); - } else { - 0 - } - } else { - result? - }; - let remote = socket.remote_endpoint().map(|ep| { - let mut addr_buf = alloc::vec![0u8; 16]; - let _ = write_sockaddr_in_to_buf(&mut addr_buf, ep); - addr_buf - }); - Ok((n, remote)) + let mut sockets = SOCKET_SET.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get_mut::(*h); + let state = socket.state(); + if state == tcp::State::Closed { + return Ok((0, None)); + } + if state == tcp::State::CloseWait && socket.recv_queue() == 0 { + return Ok((0, None)); } - Some(SocketHandle::Udp(_h)) => { - drop(sockets); - let Some(d) = self.udp_pop() else { + + let result = socket.recv_slice(buf).map_err(|_| FsError::WouldBlock); + let n = if let Ok(0) = result { + if state == tcp::State::CloseWait { + 0 + } else if socket.may_recv() { return Err(FsError::WouldBlock); - }; - let n = core::cmp::min(buf.len(), d.len); - buf[..n].copy_from_slice(&d.data[..n]); + } else { + 0 + } + } else { + result? + }; + let remote = socket.remote_endpoint().map(|ep| { let mut addr_buf = alloc::vec![0u8; 16]; - let _ = write_sockaddr_in_to_buf(&mut addr_buf, d.src); - Ok((n, Some(addr_buf))) - } - None => Err(FsError::InvalidArgument), + let _ = write_sockaddr_in_to_buf(&mut addr_buf, ep); + addr_buf + }); + Ok((n, remote)) + } + Some(SocketHandle::Udp(_)) => { + drop(sockets); + let Some(d) = file.udp_pop() else { + return Err(FsError::WouldBlock); + }; + let n = core::cmp::min(buf.len(), d.len); + buf[..n].copy_from_slice(&d.data[..n]); + let mut addr_buf = alloc::vec![0u8; 16]; + let _ = write_sockaddr_in_to_buf(&mut addr_buf, d.src); + Ok((n, Some(addr_buf))) } + None => Err(FsError::InvalidArgument), } } @@ -788,7 +822,7 @@ fn create_udp_socket_in_set(sockets: &mut SocketSet<'static>) -> Result Result { +pub(crate) fn create_tcp_socket_impl() -> Result { let mut rx_vec = alloc::vec::Vec::new(); rx_vec.try_reserve(4096).map_err(|_| ())?; rx_vec.resize(4096, 0); @@ -805,7 +839,7 @@ pub fn create_tcp_socket() -> Result { Ok(SocketHandle::Tcp(handle)) } -pub fn create_udp_socket() -> Result { +pub(crate) fn create_udp_socket_impl() -> Result { let mut sockets = SOCKET_SET.lock(); let handle = create_udp_socket_in_set(&mut *sockets)?; Ok(SocketHandle::Udp(handle)) @@ -895,7 +929,7 @@ pub fn write_sockaddr_in(addr: *mut u8, addrlen: *mut u32, endpoint: IpEndpoint) } /// Initialize network interface (should be called during network setup) -pub fn init_network(mut smoltcp_iface: crate::net::interface::SmoltcpInterface) { +pub(crate) fn init_network_impl(mut smoltcp_iface: crate::net::interface::SmoltcpInterface) { let wrapper = NetIfaceWrapper { device: SpinLock::new(smoltcp_iface.device_adapter_mut().clone()), interface: SpinLock::new(smoltcp_iface.into_interface()), @@ -904,7 +938,11 @@ pub fn init_network(mut smoltcp_iface: crate::net::interface::SmoltcpInterface) } /// Perform TCP connect with Context -pub fn tcp_connect(handle: SmoltcpHandle, remote: IpEndpoint, local: IpEndpoint) -> Result<(), ()> { +pub(crate) fn tcp_connect_impl( + handle: SmoltcpHandle, + remote: IpEndpoint, + local: IpEndpoint, +) -> Result<(), ()> { crate::pr_debug!("tcp_connect: start, handle={:?}", handle); let iface_guard = NET_IFACE.lock(); @@ -938,7 +976,7 @@ pub fn tcp_connect(handle: SmoltcpHandle, remote: IpEndpoint, local: IpEndpoint) } /// Poll network interfaces to process packets -pub fn poll_network_interfaces() { +pub(crate) fn poll_network_interfaces_impl() { if let Some(ref wrapper) = *NET_IFACE.lock() { crate::pr_debug!("poll_network_interfaces: calling poll"); wrapper.poll(&SOCKET_SET); @@ -949,9 +987,9 @@ pub fn poll_network_interfaces() { /// /// IMPORTANT: this may allocate (copies UDP payloads) and therefore must not be called from /// interrupt context. -pub fn poll_network_and_dispatch() { - poll_network_interfaces(); - if udp_dispatch() { +pub(crate) fn poll_network_and_dispatch_impl() { + poll_network_interfaces_impl(); + if udp_dispatch_impl() { crate::kernel::syscall::io::wake_poll_waiters(); } } @@ -959,7 +997,7 @@ pub fn poll_network_and_dispatch() { /// Drain UDP datagrams from shared per-port sockets and deliver them to per-fd queues. /// /// Returns whether any datagram was delivered. -pub fn udp_dispatch() -> bool { +pub(crate) fn udp_dispatch_impl() -> bool { let mut sockets = SOCKET_SET.lock(); udp_dispatch_drain_locked(&mut *sockets) } @@ -968,7 +1006,7 @@ pub fn udp_dispatch() -> bool { /// for datagram dispatching. /// /// Lock order: SOCKET_SET -> UDP_PORTS (must match NetIfaceWrapper::poll path). -pub fn udp_attach_fd_to_port( +pub(crate) fn udp_attach_fd_to_port_impl( tid: usize, fd: usize, file: &Arc, @@ -1129,7 +1167,7 @@ fn udp_dispatch_drain_locked(sockets: &mut SocketSet<'static>) -> bool { } /// Poll until loopback queue is empty -pub fn poll_until_empty() { +pub(crate) fn poll_until_empty_impl() { if let Some(ref wrapper) = *NET_IFACE.lock() { // Always poll at least once to process socket state changes wrapper.poll(&SOCKET_SET); @@ -1147,3 +1185,157 @@ pub fn poll_until_empty() { } } } + +pub fn create_tcp_socket() -> Result { + crate::net::stack::network_stack() + .lock() + .create_tcp_socket() +} + +pub fn create_udp_socket() -> Result { + crate::net::stack::network_stack() + .lock() + .create_udp_socket() +} + +/// Initialize network interface through the stack facade. +pub fn init_network(smoltcp_iface: crate::net::interface::SmoltcpInterface) { + crate::net::stack::network_stack() + .lock() + .init_network(smoltcp_iface); +} + +pub fn tcp_connect(handle: SmoltcpHandle, remote: IpEndpoint, local: IpEndpoint) -> Result<(), ()> { + crate::net::stack::network_stack() + .lock() + .tcp_connect(handle, remote, local) +} + +pub fn tcp_connection_state_impl( + handle: SmoltcpHandle, +) -> Option { + let sockets = SOCKET_SET.lock(); + let socket = sockets.get::(handle); + Some(match socket.state() { + tcp::State::Established => crate::net::stack::TcpConnectionState::Established, + tcp::State::Closed => crate::net::stack::TcpConnectionState::Closed, + _ => crate::net::stack::TcpConnectionState::Other, + }) +} + +pub(crate) fn tcp_listen_impl( + handle: SmoltcpHandle, + endpoint: smoltcp::wire::IpListenEndpoint, +) -> Result<(), ()> { + let mut sockets = SOCKET_SET.lock(); + sockets + .get_mut::(handle) + .listen(endpoint) + .map_err(|_| ()) +} + +pub(crate) fn tcp_listener_state_endpoint_impl( + handle: SmoltcpHandle, +) -> Option<( + crate::net::stack::TcpListenState, + smoltcp::wire::IpListenEndpoint, +)> { + let sockets = SOCKET_SET.lock(); + let socket = sockets.get::(handle); + let state = match socket.state() { + tcp::State::Listen => crate::net::stack::TcpListenState::Listen, + tcp::State::Established => crate::net::stack::TcpListenState::Established, + tcp::State::CloseWait => crate::net::stack::TcpListenState::CloseWait, + _ => crate::net::stack::TcpListenState::Other, + }; + Some((state, socket.listen_endpoint())) +} + +pub(crate) fn remove_tcp_socket_impl(handle: SmoltcpHandle) { + SOCKET_SET.lock().remove(handle); +} + +pub(crate) fn tcp_accept_endpoints_impl( + handle: SmoltcpHandle, +) -> Option<(IpEndpoint, Option)> { + let sockets = SOCKET_SET.lock(); + let socket = sockets.get::(handle); + let remote = socket.remote_endpoint()?; + Some((remote, socket.local_endpoint())) +} + +pub(crate) fn tcp_debug_state_impl( + handle: SmoltcpHandle, +) -> Option<(crate::net::stack::TcpConnectionState, bool)> { + let sockets = SOCKET_SET.lock(); + let socket = sockets.get::(handle); + let state = match socket.state() { + tcp::State::Established => crate::net::stack::TcpConnectionState::Established, + tcp::State::Closed => crate::net::stack::TcpConnectionState::Closed, + _ => crate::net::stack::TcpConnectionState::Other, + }; + Some((state, socket.is_open())) +} + +pub(crate) fn tcp_close_impl(handle: SmoltcpHandle) { + SOCKET_SET.lock().get_mut::(handle).close(); +} + +pub(crate) fn socket_local_endpoint_impl(handle: SocketHandle) -> Option { + let sockets = SOCKET_SET.lock(); + match handle { + SocketHandle::Tcp(h) => sockets.get::(h).local_endpoint(), + SocketHandle::Udp(h) => { + let listen_ep = sockets.get::(h).endpoint(); + Some(IpEndpoint::new( + listen_ep + .addr + .unwrap_or(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED)), + listen_ep.port, + )) + } + } +} + +pub(crate) fn socket_remote_endpoint_impl(handle: SocketHandle) -> Option { + let sockets = SOCKET_SET.lock(); + match handle { + SocketHandle::Tcp(h) => sockets.get::(h).remote_endpoint(), + SocketHandle::Udp(_) => None, + } +} + +/// Poll network interfaces to process packets. +pub fn poll_network_interfaces() { + crate::net::stack::network_stack().lock().poll(); +} + +/// Poll smoltcp + dispatch UDP datagrams to per-fd queues. +pub fn poll_network_and_dispatch() { + crate::net::stack::network_stack() + .lock() + .poll_and_dispatch(); +} + +/// Drain UDP datagrams from shared per-port sockets and deliver them to per-fd queues. +pub fn udp_dispatch() -> bool { + crate::net::stack::network_stack().lock().udp_dispatch() +} + +pub fn udp_attach_fd_to_port( + tid: usize, + fd: usize, + file: &Arc, + old_handle: SmoltcpHandle, + port: u16, + bind_addr: Option, +) -> Result { + crate::net::stack::network_stack() + .lock() + .udp_attach_fd_to_port(tid, fd, file, old_handle, port, bind_addr) +} + +/// Poll until loopback queue is empty. +pub fn poll_until_empty() { + crate::net::stack::network_stack().lock().poll_until_empty(); +} diff --git a/os/src/net/stack.rs b/os/src/net/stack.rs new file mode 100644 index 00000000..3bc2a73a --- /dev/null +++ b/os/src/net/stack.rs @@ -0,0 +1,444 @@ +//! Network stack runtime facade. +//! +//! This module is the migration host for protocol-stack state. The first +//! refactor step keeps the existing implementation in `socket.rs`, while new +//! call sites start depending on this facade instead of reaching for raw +//! globals such as `SOCKET_SET` or `NET_IFACE`. + +use crate::sync::SpinLock; +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use alloc::vec::Vec; +use lazy_static::lazy_static; +use smoltcp::iface::{Interface, SocketHandle as SmoltcpHandle, SocketSet}; +use smoltcp::time::Instant; +use smoltcp::wire::{EthernetAddress, IpEndpoint, IpListenEndpoint}; + +use super::socket::{NetIfaceWrapper, SocketHandle}; + +lazy_static! { + /// Stack-owned smoltcp socket set. + pub(crate) static ref SOCKET_SET: SpinLock> = + SpinLock::new(SocketSet::new(alloc::vec![])); + + /// Stack-owned active interface runtime. + pub(crate) static ref NET_IFACE: SpinLock> = SpinLock::new(None); + + /// Runtime loopback link used while the stack is still single-interface. + static ref LOOPBACK_LINK: SpinLock>> = SpinLock::new(VecDeque::new()); +} + +pub(crate) fn enqueue_loopback_frame(frame: Vec) { + LOOPBACK_LINK.lock().push_back(frame); +} + +fn dequeue_loopback_frame() -> Option> { + LOOPBACK_LINK.lock().pop_front() +} + +pub(crate) fn loopback_link_len() -> usize { + LOOPBACK_LINK.lock().len() +} + +/// Smoltcp interface wrapper that owns the adapter borrowed by `Interface`. +pub struct SmoltcpInterface { + device_adapter: NetDeviceAdapter, + iface: Interface, +} + +impl SmoltcpInterface { + /// Create a smoltcp interface wrapper for a net device. + pub(crate) fn new( + device: Arc, + mac_address: EthernetAddress, + ) -> Self { + let mut device_adapter = NetDeviceAdapter::new(device); + let config = + smoltcp::iface::Config::new(smoltcp::wire::HardwareAddress::Ethernet(mac_address)); + let current_time = crate::arch::timer::get_time_ms() as i64; + let iface = Interface::new( + config, + &mut device_adapter, + Instant::from_millis(current_time), + ); + + Self { + device_adapter, + iface, + } + } + + /// Poll the interface and sockets. + pub fn poll( + &mut self, + timestamp: Instant, + sockets: &mut smoltcp::iface::SocketSet, + ) -> smoltcp::iface::PollResult { + self.iface + .poll(timestamp, &mut self.device_adapter, sockets) + } + + /// Mutable access for compatibility during the migration. + pub fn interface_mut(&mut self) -> &mut Interface { + &mut self.iface + } + + /// Immutable access for compatibility during the migration. + pub fn interface(&self) -> &Interface { + &self.iface + } + + /// Mutable adapter access for compatibility during the migration. + pub fn device_adapter_mut(&mut self) -> &mut NetDeviceAdapter { + &mut self.device_adapter + } + + /// Consume the wrapper and return the smoltcp interface. + pub fn into_interface(self) -> Interface { + self.iface + } +} + +/// Adapter from Comix `NetDevice` to smoltcp's `Device` trait. +#[derive(Clone)] +pub struct NetDeviceAdapter { + device: Arc, + rx_buffer: [u8; 2048], +} + +impl NetDeviceAdapter { + /// Create a new adapter. + pub fn new(device: Arc) -> Self { + Self { + device, + rx_buffer: [0; 2048], + } + } + + /// Compatibility hook for the old bounded loopback drain path. + pub fn loopback_queue_len(&self) -> usize { + loopback_link_len() + } +} + +impl smoltcp::phy::Device for NetDeviceAdapter { + type RxToken<'a> = NetRxToken<'a>; + type TxToken<'a> = NetTxToken<'a>; + + fn receive( + &mut self, + _timestamp: smoltcp::time::Instant, + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if let Some(packet) = dequeue_loopback_frame() { + if packet.len() > self.rx_buffer.len() { + return None; + } + self.rx_buffer[..packet.len()].copy_from_slice(&packet); + return Some(( + NetRxToken { + buffer: &self.rx_buffer[..packet.len()], + }, + NetTxToken { + device: &self.device, + }, + )); + } + + match self.device.receive(&mut self.rx_buffer) { + Ok(size) if size > 0 => Some(( + NetRxToken { + buffer: &self.rx_buffer[..size], + }, + NetTxToken { + device: &self.device, + }, + )), + _ => None, + } + } + + fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { + Some(NetTxToken { + device: &self.device, + }) + } + + fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { + let mut caps = smoltcp::phy::DeviceCapabilities::default(); + caps.max_transmission_unit = + self.device.mtu() + smoltcp::wire::EthernetFrame::<&[u8]>::header_len(); + caps.medium = smoltcp::phy::Medium::Ethernet; + caps.max_burst_size = Some(64); + caps + } +} + +/// Receive token. +pub struct NetRxToken<'a> { + buffer: &'a [u8], +} + +impl smoltcp::phy::RxToken for NetRxToken<'_> { + fn consume(self, f: F) -> R + where + F: FnOnce(&[u8]) -> R, + { + f(self.buffer) + } + + fn meta(&self) -> smoltcp::phy::PacketMeta { + smoltcp::phy::PacketMeta::default() + } +} + +/// Transmit token. +pub struct NetTxToken<'a> { + device: &'a Arc, +} + +impl smoltcp::phy::TxToken for NetTxToken<'_> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut buffer = alloc::vec![0; len]; + let result = f(&mut buffer); + + let is_loopback = if buffer.len() >= 14 { + let ethertype = u16::from_be_bytes([buffer[12], buffer[13]]); + match ethertype { + 0x0800 if buffer.len() >= 34 => buffer[26] == 127 || buffer[30] == 127, + 0x0806 if buffer.len() >= 42 => buffer[28] == 127 || buffer[38] == 127, + _ => false, + } + } else { + false + }; + + if is_loopback { + enqueue_loopback_frame(buffer); + } else { + let _ = self.device.send(&buffer); + } + + result + } + + fn set_meta(&mut self, _meta: smoltcp::phy::PacketMeta) {} +} + +/// Coarse TCP state exposed to syscall code during the migration. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TcpConnectionState { + /// The connection handshake completed. + Established, + /// The socket reached smoltcp's closed state. + Closed, + /// Any other in-progress state. + Other, +} + +/// Coarse state for a listening TCP socket. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TcpListenState { + /// The socket is still in listen state. + Listen, + /// The socket has an established connection ready to accept. + Established, + /// The peer has closed after establishment, still acceptable by userspace. + CloseWait, + /// Any other transient state. + Other, +} + +/// Stable facade for network protocol-stack operations. +pub struct NetworkStack; + +impl NetworkStack { + const fn new() -> Self { + Self + } + + /// Initialize the active smoltcp interface runtime. + pub fn init_network(&self, smoltcp_iface: crate::net::interface::SmoltcpInterface) { + super::socket::init_network_impl(smoltcp_iface); + } + + /// Create a TCP socket in the stack runtime. + pub fn create_tcp_socket(&self) -> Result { + super::socket::create_tcp_socket_impl() + } + + /// Create a UDP socket in the stack runtime. + pub fn create_udp_socket(&self) -> Result { + super::socket::create_udp_socket_impl() + } + + /// Connect a TCP socket using the active interface context. + pub fn tcp_connect( + &self, + handle: SmoltcpHandle, + remote: IpEndpoint, + local: IpEndpoint, + ) -> Result<(), ()> { + super::socket::tcp_connect_impl(handle, remote, local) + } + + /// Query the coarse TCP state without exposing `SOCKET_SET` to callers. + pub fn tcp_connection_state(&self, handle: SmoltcpHandle) -> Option { + super::socket::tcp_connection_state_impl(handle) + } + + /// Start listening on a TCP socket. + pub fn tcp_listen(&self, handle: SmoltcpHandle, endpoint: IpListenEndpoint) -> Result<(), ()> { + super::socket::tcp_listen_impl(handle, endpoint) + } + + /// Query listener state and endpoint without exposing `SocketSet`. + pub fn tcp_listener_state_endpoint( + &self, + handle: SmoltcpHandle, + ) -> Option<(TcpListenState, IpListenEndpoint)> { + super::socket::tcp_listener_state_endpoint_impl(handle) + } + + /// Remove a TCP socket handle from the runtime. + pub fn remove_tcp_socket(&self, handle: SmoltcpHandle) { + super::socket::remove_tcp_socket_impl(handle); + } + + /// Return remote and local endpoints for an accepted TCP socket. + pub fn tcp_accept_endpoints( + &self, + handle: SmoltcpHandle, + ) -> Option<(IpEndpoint, Option)> { + super::socket::tcp_accept_endpoints_impl(handle) + } + + /// Return lightweight TCP debug state for syscall logging. + pub fn tcp_debug_state(&self, handle: SmoltcpHandle) -> Option<(TcpConnectionState, bool)> { + super::socket::tcp_debug_state_impl(handle) + } + + /// Close a TCP socket. + pub fn tcp_close(&self, handle: SmoltcpHandle) { + super::socket::tcp_close_impl(handle); + } + + /// Query a socket's local endpoint. + pub fn socket_local_endpoint(&self, handle: SocketHandle) -> Option { + super::socket::socket_local_endpoint_impl(handle) + } + + /// Query a socket's remote endpoint. + pub fn socket_remote_endpoint(&self, handle: SocketHandle) -> Option { + super::socket::socket_remote_endpoint_impl(handle) + } + + /// Poll the network stack once. + pub fn poll(&self) { + super::socket::poll_network_interfaces_impl(); + } + + /// Poll smoltcp and dispatch queued datagrams. + pub fn poll_and_dispatch(&self) { + super::socket::poll_network_and_dispatch_impl(); + } + + /// Drain UDP datagrams from shared per-port sockets. + pub fn udp_dispatch(&self) -> bool { + super::socket::udp_dispatch_impl() + } + + /// Attach a UDP fd to the shared per-port socket. + pub fn udp_attach_fd_to_port( + &self, + tid: usize, + fd: usize, + file: &Arc, + old_handle: SmoltcpHandle, + port: u16, + bind_addr: Option, + ) -> Result { + super::socket::udp_attach_fd_to_port_impl(tid, fd, file, old_handle, port, bind_addr) + } + + /// Poll until loopback compatibility queues are boundedly drained. + pub fn poll_until_empty(&self) { + super::socket::poll_until_empty_impl(); + } + + /// Pop an established child socket from a listener's accept queue. + pub fn take_established_from_listen_queue( + &self, + file: &super::socket::SocketFile, + ) -> Option { + super::socket::take_established_from_listen_queue_impl(file) + } + + /// Query whether a socket has reached the closed TCP state. + pub fn socket_is_closed(&self, file: &super::socket::SocketFile) -> bool { + super::socket::socket_is_closed_impl(file) + } + + /// Release all stack resources owned by a SocketFile. + pub fn drop_socket_file(&self, file: &super::socket::SocketFile) { + super::socket::drop_socket_file_impl(file); + } + + /// Send a datagram to a specific endpoint. + pub fn socket_sendto( + &self, + handle: SocketHandle, + buf: &[u8], + endpoint: IpEndpoint, + ) -> Result { + super::socket::socket_sendto_impl(handle, buf, endpoint) + } + + /// Query VFS readability for a socket file. + pub fn socket_readable(&self, file: &super::socket::SocketFile) -> bool { + super::socket::socket_readable_impl(file) + } + + /// Query VFS writability for a socket file. + pub fn socket_writable(&self, file: &super::socket::SocketFile) -> bool { + super::socket::socket_writable_impl(file) + } + + /// Read stream data or a queued UDP datagram payload. + pub fn socket_read( + &self, + file: &super::socket::SocketFile, + buf: &mut [u8], + ) -> Result { + super::socket::socket_read_impl(file, buf) + } + + /// Write stream data or a connected UDP datagram payload. + pub fn socket_write( + &self, + file: &super::socket::SocketFile, + buf: &[u8], + ) -> Result { + super::socket::socket_write_impl(file, buf) + } + + /// Receive data and, when available, the peer sockaddr buffer. + pub fn socket_recvfrom( + &self, + file: &super::socket::SocketFile, + buf: &mut [u8], + ) -> Result<(usize, Option>), crate::vfs::FsError> { + super::socket::socket_recvfrom_impl(file, buf) + } +} + +lazy_static! { + static ref NETWORK_STACK: SpinLock = SpinLock::new(NetworkStack::new()); +} + +/// Borrow the global network stack facade. +pub fn network_stack() -> &'static SpinLock { + &NETWORK_STACK +} From 4ad26a9a2242c9c01d8b0ffba931256ca73ae0aa Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 19:26:41 +0800 Subject: [PATCH 05/11] =?UTF-8?q?fix:=20=E6=8B=86=E5=88=86=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E8=AE=BE=E5=A4=87=E4=B8=8E=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/src/device/net/loopback.rs | 61 +++++++++ os/src/device/net/mod.rs | 1 + os/src/device/net/virtio_net.rs | 45 ++----- os/src/net/config.rs | 47 +++++-- os/src/net/interface.rs | 231 +++----------------------------- os/src/net/mod.rs | 27 ++++ 6 files changed, 156 insertions(+), 256 deletions(-) create mode 100644 os/src/device/net/loopback.rs diff --git a/os/src/device/net/loopback.rs b/os/src/device/net/loopback.rs new file mode 100644 index 00000000..fe87c4a3 --- /dev/null +++ b/os/src/device/net/loopback.rs @@ -0,0 +1,61 @@ +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use crate::sync::SpinLock; + +use super::net_device::{NetDevice, NetDeviceError}; + +/// Explicit loopback network device. +/// +/// Frames sent to this device are queued and returned by `receive()`, which +/// makes loopback a real device model instead of a `NullNetDevice` side effect. +pub struct LoopbackNetDevice { + device_id: usize, + rx_queue: SpinLock>>, + mac: [u8; 6], + mtu: usize, +} + +impl LoopbackNetDevice { + pub fn new(device_id: usize) -> Arc { + Arc::new(Self { + device_id, + rx_queue: SpinLock::new(VecDeque::new()), + mac: [0x02, 0x00, 0x00, 0x00, 0x00, 0x7f], + mtu: 65535, + }) + } +} + +impl NetDevice for LoopbackNetDevice { + fn send(&self, packet: &[u8]) -> Result<(), NetDeviceError> { + self.rx_queue.lock().push_back(packet.to_vec()); + Ok(()) + } + + fn receive(&self, buf: &mut [u8]) -> Result { + let Some(packet) = self.rx_queue.lock().pop_front() else { + return Err(NetDeviceError::QueueEmpty); + }; + let len = core::cmp::min(packet.len(), buf.len()); + buf[..len].copy_from_slice(&packet[..len]); + Ok(len) + } + + fn device_id(&self) -> usize { + self.device_id + } + + fn mtu(&self) -> usize { + self.mtu + } + + fn name(&self) -> &str { + "loopback" + } + + fn mac_address(&self) -> [u8; 6] { + self.mac + } +} diff --git a/os/src/device/net/mod.rs b/os/src/device/net/mod.rs index cd49e053..32e34a8c 100644 --- a/os/src/device/net/mod.rs +++ b/os/src/device/net/mod.rs @@ -6,6 +6,7 @@ use crate::device::NetDevice; use crate::sync::SpinLock; use alloc::{sync::Arc, vec::Vec}; +pub mod loopback; pub mod net_device; pub mod null_net; pub mod virtio_net; diff --git a/os/src/device/net/virtio_net.rs b/os/src/device/net/virtio_net.rs index 7525f816..ade46330 100644 --- a/os/src/device/net/virtio_net.rs +++ b/os/src/device/net/virtio_net.rs @@ -1,15 +1,10 @@ use virtio_drivers::transport::{mmio::MmioTransport, pci::PciTransport}; use crate::{ - device::{ - Driver, - net::{add_network_device, net_device::VirtioNetDevice}, - }, - net::interface::NetworkInterface, - pr_info, pr_warn, println, + device::net::net_device::{NetDeviceError, VirtioNetDevice}, + pr_info, pr_warn, sync::SpinLock, }; -use alloc::{format, sync::Arc}; use lazy_static::lazy_static; lazy_static! { @@ -32,28 +27,16 @@ pub fn init(transport: MmioTransport<'static>) { match VirtioNetDevice::new(transport, device_id) { Ok(virtio_device) => { pr_info!("[Device] VirtioNetDevice created with ID: {}", device_id); - - // 创建网络接口 - let interface_name = format!("eth{}", device_id); - let network_interface = - Arc::new(NetworkInterface::new(interface_name, virtio_device.clone())); - - // 将设备添加到全局设备列表 - add_network_device(virtio_device); - - // 将接口添加到全局接口管理器 - crate::net::interface::NETWORK_INTERFACE_MANAGER - .lock() - .add_interface(network_interface.clone()); - - // 注册设备驱动 - crate::device::register_driver(network_interface.clone() as Arc); + let network_interface = crate::net::register_net_device(virtio_device); pr_info!( "[Device] Network interface {} initialized successfully", network_interface.name() ); } + Err(NetDeviceError::DeviceNotReady) => { + pr_info!("[Device] VirtioNetDevice not ready; skipping"); + } Err(e) => { pr_warn!("[Device] Failed to initialize VirtioNetDevice: {:?}", e); } @@ -74,24 +57,16 @@ pub fn init_pci(transport: PciTransport) { match VirtioNetDevice::new(transport, device_id) { Ok(virtio_device) => { pr_info!("[Device] VirtioNetDevice created with ID: {}", device_id); - - let interface_name = format!("eth{}", device_id); - let network_interface = - Arc::new(NetworkInterface::new(interface_name, virtio_device.clone())); - - add_network_device(virtio_device); - - crate::net::interface::NETWORK_INTERFACE_MANAGER - .lock() - .add_interface(network_interface.clone()); - - crate::device::register_driver(network_interface.clone() as Arc); + let network_interface = crate::net::register_net_device(virtio_device); pr_info!( "[Device] Network interface {} initialized successfully", network_interface.name() ); } + Err(NetDeviceError::DeviceNotReady) => { + pr_info!("[Device] VirtioNetDevice not ready; skipping"); + } Err(e) => { pr_warn!("[Device] Failed to initialize VirtioNetDevice: {:?}", e); } diff --git a/os/src/net/config.rs b/os/src/net/config.rs index 63db3783..0e618572 100644 --- a/os/src/net/config.rs +++ b/os/src/net/config.rs @@ -19,6 +19,27 @@ pub enum NetworkConfigError { pub struct NetworkConfigManager; impl NetworkConfigManager { + fn ensure_loopback_interface() -> alloc::sync::Arc { + if let Some(existing) = NETWORK_INTERFACE_MANAGER + .lock() + .find_interface_by_name("lo") + .cloned() + { + return existing; + } + + use crate::device::net::loopback::LoopbackNetDevice; + use alloc::sync::Arc; + + let dev = LoopbackNetDevice::new(0); + let iface = Arc::new(NetworkInterface::new(String::from("lo"), dev)); + iface.add_ip_address(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)); + NETWORK_INTERFACE_MANAGER + .lock() + .add_interface(iface.clone()); + iface + } + /// 解析点分十进制子网掩码并计算前缀长度 /// /// # 参数 @@ -90,25 +111,33 @@ impl NetworkConfigManager { pub fn init_default_interface() -> Result<(), NetworkConfigError> { earlyprintln!("Initializing default network configuration..."); + let loopback_interface = Self::ensure_loopback_interface(); + earlyprintln!("Ensured loopback interface: {}", loopback_interface.name()); + // 先获取接口的Arc,然后释放全局锁 // 避免在持有 NETWORK_INTERFACE_MANAGER 锁时操作接口字段锁 let interface = { let binding = NETWORK_INTERFACE_MANAGER.lock(); - binding.get_interfaces().first().cloned() + binding + .get_interfaces() + .iter() + .find(|iface| iface.name() != "lo") + .cloned() + .or_else(|| binding.find_interface_by_name("lo").cloned()) }; // NETWORK_INTERFACE_MANAGER锁已释放 let (interface, is_null_loopback_only) = if let Some(interface) = interface { - (interface, false) + let loopback_only = interface.name() == "lo"; + (interface, loopback_only) } else { - // 没有任何真实网卡(例如 QEMU 没挂 virtio-net)时,创建一个“空设备接口”, - // 让 smoltcp/套接字栈至少能在 loopback(127.0.0.1) 场景工作。 - use crate::device::net::null_net::NullNetDevice; + // 没有任何真实网卡(例如 QEMU 没挂 virtio-net)时,创建显式 loopback 设备。 + use crate::device::net::loopback::LoopbackNetDevice; use alloc::sync::Arc; - earlyprintln!("No net interfaces found; creating null loopback-only interface"); + earlyprintln!("No net interfaces found; creating explicit loopback interface"); - let dev = NullNetDevice::new(0); - let iface = Arc::new(NetworkInterface::new(String::from("lo0"), dev)); + let dev = LoopbackNetDevice::new(0); + let iface = Arc::new(NetworkInterface::new(String::from("lo"), dev)); NETWORK_INTERFACE_MANAGER .lock() .add_interface(iface.clone()); @@ -119,7 +148,7 @@ impl NetworkConfigManager { earlyprintln!("Configuring interface: {}", interface.name()); if is_null_loopback_only { - // NullNetDevice 只有 loopback 场景可用:不要配置非 127/8 的地址和网关, + // Loopback-only 场景不要配置非 127/8 的地址和网关, // 否则 smoltcp 可能会为 127.0.0.1 选择错误的源地址/路由,导致 UDP/TCP 行为异常。 let loopback_cidr = IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8); interface.add_ip_address(loopback_cidr); diff --git a/os/src/net/interface.rs b/os/src/net/interface.rs index cd472f3b..41b10d06 100644 --- a/os/src/net/interface.rs +++ b/os/src/net/interface.rs @@ -1,13 +1,14 @@ use crate::device::DeviceType; use crate::device::net::net_device::NetDevice; use crate::sync::SpinLock; -use alloc::string::{String, ToString}; +use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; use lazy_static::lazy_static; -use smoltcp::iface::Interface; use smoltcp::time::Instant; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; +use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address}; + +pub use crate::net::stack::{NetDeviceAdapter, SmoltcpInterface}; /// 网络接口管理器 pub struct NetworkInterfaceManager { @@ -44,70 +45,6 @@ lazy_static! { SpinLock::new(NetworkInterfaceManager::new()); } -/// Smoltcp 接口包装器,确保 Device 和 Interface 有相同的生命周期 -pub struct SmoltcpInterface { - device_adapter: NetDeviceAdapter, - iface: Interface, -} - -impl SmoltcpInterface { - /// 创建新的 smoltcp 接口包装器 - fn new(device: Arc, mac_address: EthernetAddress) -> Self { - let mut device_adapter = NetDeviceAdapter::new(device); - - let config = - smoltcp::iface::Config::new(smoltcp::wire::HardwareAddress::Ethernet(mac_address)); - let current_time = crate::arch::timer::get_time_ms() as i64; - let iface = Interface::new( - config, - &mut device_adapter, - smoltcp::time::Instant::from_millis(current_time), - ); - - Self { - device_adapter, - iface, - } - } - - /// 轮询网络接口,处理接收和发送 - /// - /// # 参数 - /// * `timestamp` - 当前时间戳 - /// * `sockets` - Socket 集合,用于处理网络协议栈的 socket 操作 - /// - /// # 返回值 - /// 返回轮询结果,指示是否有事件被处理 - pub fn poll( - &mut self, - timestamp: Instant, - sockets: &mut smoltcp::iface::SocketSet, - ) -> smoltcp::iface::PollResult { - self.iface - .poll(timestamp, &mut self.device_adapter, sockets) - } - - /// 获取可变的 smoltcp Interface 引用 - pub fn interface_mut(&mut self) -> &mut Interface { - &mut self.iface - } - - /// 获取不可变的 smoltcp Interface 引用 - pub fn interface(&self) -> &Interface { - &self.iface - } - - /// 获取可变的 device adapter 引用 - pub fn device_adapter_mut(&mut self) -> &mut NetDeviceAdapter { - &mut self.device_adapter - } - - /// 消费包装器,返回内部的Interface - pub fn into_interface(self) -> Interface { - self.iface - } -} - /// 网络接口 pub struct NetworkInterface { name: String, @@ -225,160 +162,30 @@ impl NetworkInterface { } } -/// 网络设备适配器,用于将NetDevice适配到smoltcp需要的Device trait -#[derive(Clone)] -pub struct NetDeviceAdapter { - device: Arc, - rx_buffer: [u8; 2048], - loopback_queue: Arc>>>, -} - -impl NetDeviceAdapter { - /// 创建新的网络设备适配器 - pub fn new(device: Arc) -> Self { - Self { - device, - rx_buffer: [0; 2048], - loopback_queue: Arc::new(SpinLock::new(alloc::collections::VecDeque::new())), - } - } - - pub fn loopback_queue_len(&self) -> usize { - self.loopback_queue.lock().len() - } -} - -// 实现smoltcp 0.12.0的Device trait -impl smoltcp::phy::Device for NetDeviceAdapter { - type RxToken<'a> = NetRxToken<'a>; - type TxToken<'a> = NetTxToken<'a>; - - fn receive( - &mut self, - _timestamp: smoltcp::time::Instant, - ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - // 先检查 loopback 队列 - if let Some(packet) = self.loopback_queue.lock().pop_front() { - if packet.len() > self.rx_buffer.len() { - // Drop oversized loopback frames to avoid panicking on buffer copy. - return None; - } - self.rx_buffer[..packet.len()].copy_from_slice(&packet); - return Some(( - NetRxToken { - buffer: &self.rx_buffer[..packet.len()], - }, - NetTxToken { - device: &self.device, - loopback_queue: self.loopback_queue.clone(), - }, - )); - } - - // 尝试从物理设备接收 - match self.device.receive(&mut self.rx_buffer) { - Ok(size) if size > 0 => Some(( - NetRxToken { - buffer: &self.rx_buffer[..size], - }, - NetTxToken { - device: &self.device, - loopback_queue: self.loopback_queue.clone(), - }, - )), - _ => None, - } - } - - fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { - Some(NetTxToken { - device: &self.device, - loopback_queue: self.loopback_queue.clone(), - }) - } - - fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { - let mut caps = smoltcp::phy::DeviceCapabilities::default(); - // NOTE: smoltcp expects Ethernet MTU (incl. 14-byte Ethernet header, excl. 4-byte FCS). - // Our NetDevice::mtu() follows the Linux convention (IP MTU), so we must add Ethernet header. - caps.max_transmission_unit = - self.device.mtu() as usize + smoltcp::wire::EthernetFrame::<&[u8]>::header_len(); - caps.medium = smoltcp::phy::Medium::Ethernet; - // Allow the stack to process more loopback packets per poll. - // This significantly reduces busy-wait time for high-rate workloads (e.g. iperf3 UDP). - caps.max_burst_size = Some(64); - caps - } -} -/// 接收令牌 -pub struct NetRxToken<'a> { - buffer: &'a [u8], +/// 实现Driver trait +/// Compatibility driver handle for net-device interrupt registration. +/// +/// Interface configuration stays in `NetworkInterface`; this shim keeps the +/// legacy device registry working while the network subsystem owns interfaces. +pub struct NetDriverHandle { + interface: Arc, } -impl smoltcp::phy::RxToken for NetRxToken<'_> { - fn consume(self, f: F) -> R - where - F: FnOnce(&[u8]) -> R, - { - f(self.buffer) - } - - fn meta(&self) -> smoltcp::phy::PacketMeta { - smoltcp::phy::PacketMeta::default() +impl NetDriverHandle { + pub fn new(interface: Arc) -> Self { + Self { interface } } } -/// 发送令牌 -pub struct NetTxToken<'a> { - device: &'a Arc, - loopback_queue: Arc>>>, -} +impl core::ops::Deref for NetDriverHandle { + type Target = NetworkInterface; -impl smoltcp::phy::TxToken for NetTxToken<'_> { - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - let mut buffer = alloc::vec![0; len]; - let result = f(&mut buffer); - - // 检查是否是 loopback 数据包。 - // - // 特殊处理:在 NullNetDevice 上没有真实链路,发送只会被丢弃。 - // 因此将所有 Tx 都回环到 loopback_queue,确保本机协议栈自洽(iperf3 UDP/TCP)。 - let is_loopback = if self.device.name() == "null-net" { - true - } else if buffer.len() >= 14 { - let ethertype = u16::from_be_bytes([buffer[12], buffer[13]]); - match ethertype { - 0x0800 if buffer.len() >= 34 => { - // IP: check both source and destination IP (offset 26 and 30) - buffer[26] == 127 || buffer[30] == 127 - } - 0x0806 if buffer.len() >= 42 => { - // ARP: check both sender and target IP (offset 28 and 38) - buffer[28] == 127 || buffer[38] == 127 - } - _ => false, - } - } else { - false - }; - - if is_loopback { - self.loopback_queue.lock().push_back(buffer); - } else { - let _ = self.device.send(&buffer); - } - - result + fn deref(&self) -> &Self::Target { + &self.interface } - - fn set_meta(&mut self, _meta: smoltcp::phy::PacketMeta) {} } -/// 实现Driver trait -impl crate::device::Driver for NetworkInterface { +impl crate::device::Driver for NetDriverHandle { fn try_handle_interrupt(&self, irq: Option) -> bool { // 检查中断是否启用 if !self.is_interrupt_enabled() { diff --git a/os/src/net/mod.rs b/os/src/net/mod.rs index 7814aba0..97a83c30 100644 --- a/os/src/net/mod.rs +++ b/os/src/net/mod.rs @@ -2,6 +2,33 @@ //! //! 提供网络接口管理、协议栈适配和网络配置功能 +use alloc::{format, sync::Arc}; + pub mod config; pub mod interface; pub mod socket; +pub mod stack; + +/// Register a net device and create its current compatibility interface. +/// +/// This is the migration boundary between device drivers and the network +/// subsystem. Interrupt compatibility is registered through `NetDriverHandle` +/// so `NetworkInterface` remains interface control-plane state. +pub fn register_net_device( + device: Arc, +) -> Arc { + let interface_name = format!("eth{}", device.device_id()); + let network_interface = Arc::new(interface::NetworkInterface::new( + interface_name, + device.clone(), + )); + + crate::device::net::add_network_device(device); + interface::NETWORK_INTERFACE_MANAGER + .lock() + .add_interface(network_interface.clone()); + let driver = Arc::new(interface::NetDriverHandle::new(network_interface.clone())); + crate::device::register_driver(driver as Arc); + + network_interface +} From e1281aed4cf102a6626b4c8ea388401ac4a93b44 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 19:27:11 +0800 Subject: [PATCH 06/11] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E7=A9=BA=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/README.md | 1 - doc/arch/README.md | 1 - doc/arch/loongarch/README.md | 1 - doc/arch/riscv/README.md | 1 - doc/net/README.md | 1 - 5 files changed, 5 deletions(-) diff --git a/doc/README.md b/doc/README.md index 292353e8..a132411c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -11,4 +11,3 @@ - `net/`:网络模块重构施工文档。 - `arch/`:架构相关施工差异文档。 - diff --git a/doc/arch/README.md b/doc/arch/README.md index 4ee267d2..fa2ad7eb 100644 --- a/doc/arch/README.md +++ b/doc/arch/README.md @@ -11,4 +11,3 @@ - `riscv/`:RISC-V 相关施工差异。 - `loongarch/`:LoongArch64 相关施工差异。 - diff --git a/doc/arch/loongarch/README.md b/doc/arch/loongarch/README.md index 6ea8d0eb..6fb9ad29 100644 --- a/doc/arch/loongarch/README.md +++ b/doc/arch/loongarch/README.md @@ -5,4 +5,3 @@ ## 文件说明 - `network_rearchitecture.md`:网络重构中涉及 LoongArch64 syscall 入口、编号和 VirtIO 接入的差异。 - diff --git a/doc/arch/riscv/README.md b/doc/arch/riscv/README.md index bfd2030f..4c7de382 100644 --- a/doc/arch/riscv/README.md +++ b/doc/arch/riscv/README.md @@ -5,4 +5,3 @@ ## 文件说明 - `network_rearchitecture.md`:网络重构中涉及 RISC-V syscall 入口、编号和 VirtIO 接入的差异。 - diff --git a/doc/net/README.md b/doc/net/README.md index 279351d9..1abd9e0b 100644 --- a/doc/net/README.md +++ b/doc/net/README.md @@ -22,4 +22,3 @@ - `rearchitecture_stack_runtime.md`:协议栈运行时迁移说明。 - `rearchitecture_socket_syscall.md`:SocketFile 与 syscall 迁移说明。 - `rearchitecture_loopback_poll.md`:loopback、poll、RX/TX 迁移说明。 - From 2f50599f9bc7523da17d11c5b2ce4e60b877e30f Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 21:27:55 +0800 Subject: [PATCH 07/11] =?UTF-8?q?docs:=20=E4=BF=AE=E6=AD=A3=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- doc/README.md | 13 - doc/arch/README.md | 13 - doc/arch/loongarch/README.md | 7 - doc/arch/riscv/README.md | 7 - doc/net/README.md | 24 - doc/net/network_implementation_guide.md | 1290 ----------------- .../network_rearchitecture_new_main.md | 25 + document/SUMMARY.md | 2 + .../arch/loongarch/network_rearchitecture.md | 0 .../arch/riscv/network_rearchitecture.md | 0 .../net/network_rearchitecture.md | 2 +- .../net/rearchitecture_device.md | 0 .../net/rearchitecture_execution_plan.md | 0 .../net/rearchitecture_interface.md | 0 .../net/rearchitecture_loopback_poll.md | 0 .../net/rearchitecture_socket_syscall.md | 0 .../net/rearchitecture_stack_runtime.md | 0 18 files changed, 29 insertions(+), 1356 deletions(-) delete mode 100644 doc/README.md delete mode 100644 doc/arch/README.md delete mode 100644 doc/arch/loongarch/README.md delete mode 100644 doc/arch/riscv/README.md delete mode 100644 doc/net/README.md delete mode 100644 doc/net/network_implementation_guide.md create mode 100644 docs/pr_recorde/network_rearchitecture_new_main.md rename {doc => document}/arch/loongarch/network_rearchitecture.md (100%) rename {doc => document}/arch/riscv/network_rearchitecture.md (100%) rename {doc => document}/net/network_rearchitecture.md (91%) rename {doc => document}/net/rearchitecture_device.md (100%) rename {doc => document}/net/rearchitecture_execution_plan.md (100%) rename {doc => document}/net/rearchitecture_interface.md (100%) rename {doc => document}/net/rearchitecture_loopback_poll.md (100%) rename {doc => document}/net/rearchitecture_socket_syscall.md (100%) rename {doc => document}/net/rearchitecture_stack_runtime.md (100%) diff --git a/.gitignore b/.gitignore index 645f8b11..62d65db1 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,4 @@ os/可能的改进.md # Local agent guidance **/AGENTS.md -docs/ +docs/superpowers/ diff --git a/doc/README.md b/doc/README.md deleted file mode 100644 index a132411c..00000000 --- a/doc/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Codex 施工文档 - -本目录保存面向维护者和 Codex 的构建、重构、迁移、排查类文档。 - -与 `document/` 的分工: - -- `doc/`:怎么执行、怎么迁移、怎么检查、历史施工记录。 -- `document/`:当前系统怎么工作、模块职责是什么、接口语义是什么。 - -## 当前目录 - -- `net/`:网络模块重构施工文档。 -- `arch/`:架构相关施工差异文档。 diff --git a/doc/arch/README.md b/doc/arch/README.md deleted file mode 100644 index fa2ad7eb..00000000 --- a/doc/arch/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# 架构相关施工文档 - -本目录保存和具体架构相关的构建、迁移、重构差异文档。它只记录执行重构时需要区分 RISC-V、LoongArch64 等架构的部分。 - -与 `document/arch/` 的分工: - -- `doc/arch/`:架构相关施工差异、迁移边界、兼容检查点。 -- `document/arch/`:当前架构实现说明、寄存器/陷入/启动等解释文档。 - -## 当前目录 - -- `riscv/`:RISC-V 相关施工差异。 -- `loongarch/`:LoongArch64 相关施工差异。 diff --git a/doc/arch/loongarch/README.md b/doc/arch/loongarch/README.md deleted file mode 100644 index 6fb9ad29..00000000 --- a/doc/arch/loongarch/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# LoongArch64 施工差异 - -本目录保存 LoongArch64 相关的重构施工差异。 - -## 文件说明 - -- `network_rearchitecture.md`:网络重构中涉及 LoongArch64 syscall 入口、编号和 VirtIO 接入的差异。 diff --git a/doc/arch/riscv/README.md b/doc/arch/riscv/README.md deleted file mode 100644 index 4c7de382..00000000 --- a/doc/arch/riscv/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# RISC-V 施工差异 - -本目录保存 RISC-V 相关的重构施工差异。 - -## 文件说明 - -- `network_rearchitecture.md`:网络重构中涉及 RISC-V syscall 入口、编号和 VirtIO 接入的差异。 diff --git a/doc/net/README.md b/doc/net/README.md deleted file mode 100644 index 1abd9e0b..00000000 --- a/doc/net/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# 网络重构施工文档 - -本目录保存 Codex 使用的网络模块重构、迁移和执行类文档。它们用于指导补丁顺序、检查跨层边界和记录兼容路径,不作为当前实现的主要解释文档。 - -当前网络模块的解释说明请阅读: - -- `document/net/README.md` -- `document/net/architecture.md` -- `document/net/device_and_interface.md` -- `document/net/stack_runtime.md` -- `document/net/socket_syscall.md` -- `document/net/loopback_poll.md` -- `document/net/testing.md` - -## 文件说明 - -- `network_implementation_guide.md`:历史实现指南和问题记录。 -- `network_rearchitecture.md`:网络子系统重新分层蓝图。 -- `rearchitecture_execution_plan.md`:重构执行阶段、补丁边界和验收矩阵。 -- `rearchitecture_device.md`:设备层迁移说明。 -- `rearchitecture_interface.md`:接口控制面迁移说明。 -- `rearchitecture_stack_runtime.md`:协议栈运行时迁移说明。 -- `rearchitecture_socket_syscall.md`:SocketFile 与 syscall 迁移说明。 -- `rearchitecture_loopback_poll.md`:loopback、poll、RX/TX 迁移说明。 diff --git a/doc/net/network_implementation_guide.md b/doc/net/network_implementation_guide.md deleted file mode 100644 index 4089cfb1..00000000 --- a/doc/net/network_implementation_guide.md +++ /dev/null @@ -1,1290 +0,0 @@ -# 网络子系统实现指南 - -## 文档概述 - -本文档提供了实现完整网络功能的详细指南,包括修复当前问题和实现真正的 POSIX 网络系统调用。 - -**创建日期**: 2025-11-24 -**状态**: 设计文档 / 实现待完成 - ---- - -## 1. 当前状态与问题总结 - -### 1.1 已修复的问题 ✓ - -#### 问题 1: `create_smoltcp_interface` 内存安全问题 -**严重程度**: 🔴 严重 - 未定义行为 - -**问题描述**: -```rust -// src/device/net/interface.rs (旧代码) -pub fn create_smoltcp_interface(&self) -> Interface { - let mut device_adapter = NetDeviceAdapter::new(self.device.clone()); - let iface = Interface::new(config, &mut device_adapter, timestamp); - iface // ❌ 返回持有悬垂指针的 Interface! -} -``` - -`device_adapter` 在栈上创建,函数返回后被销毁,导致返回的 `Interface` 持有悬垂指针。 - -**解决方案**: -创建 `SmoltcpInterface` 包装器,确保 Device 和 Interface 有相同的生命周期: - -```rust -pub struct SmoltcpInterface { - device_adapter: NetDeviceAdapter, // 拥有 Device - iface: Interface, // Interface 借用 device_adapter -} - -impl SmoltcpInterface { - fn new(device: Arc, mac_address: EthernetAddress) -> Self { - let mut device_adapter = NetDeviceAdapter::new(device); - let iface = Interface::new(config, &mut device_adapter, timestamp); - Self { device_adapter, iface } - } - - pub fn poll(&mut self, timestamp: Instant, sockets: &mut SocketSet) -> PollResult { - self.iface.poll(timestamp, &mut self.device_adapter, sockets) - } - - pub fn interface_mut(&mut self) -> &mut Interface { &mut self.iface } - pub fn interface(&self) -> &Interface { &self.iface } -} - -// 现在返回包装器而不是裸 Interface -pub fn create_smoltcp_interface(&self) -> SmoltcpInterface { - let mut smoltcp_iface = SmoltcpInterface::new(self.device.clone(), self.mac_address()); - // ... 配置 IP 和路由 - smoltcp_iface -} -``` - -**文件位置**: `os/src/device/net/interface.rs:47-99` - ---- - -#### 问题 2: 子网掩码解析功能有限 -**严重程度**: 🟡 中等 - 功能受限 - -**问题描述**: -`set_interface_config` 函数使用硬编码的 match 语句解析子网掩码,只支持 9 种常见掩码。 - -```rust -// 旧代码 -let prefix_length = match mask { - "255.255.255.0" => 24, - "255.255.0.0" => 16, - // ... 仅支持少数几种 - _ => return Err(NetworkConfigError::InvalidSubnet), -}; -``` - -**解决方案**: -实现通用的子网掩码解析函数,支持任意有效掩码: - -```rust -/// 解析点分十进制子网掩码并计算前缀长度 -/// -/// # 算法 -/// 1. 解析为 4 字节并转换为 u32 -/// 2. 计算前导 1 的个数(前缀长度) -/// 3. 验证掩码有效性:所有 1 必须连续 -/// - 有效: 11111111111111111111111100000000 (0xFFFFFF00) -/// - 无效: 11111111111111110000000011111111 (0xFFFF00FF) -/// -/// # 示例 -/// - "255.255.255.0" → Ok(24) -/// - "255.255.255.128" → Ok(25) -/// - "255.255.255.3" → Err (不连续) -fn parse_subnet_mask(mask: &str) -> Result { - // 解析为 4 字节 - let octets: Result, _> = mask.split('.').map(|s| s.parse()).collect(); - let octets = octets.map_err(|_| NetworkConfigError::InvalidSubnet)?; - - if octets.len() != 4 { - return Err(NetworkConfigError::InvalidSubnet); - } - - // 转换为 u32 - let mask_u32 = ((octets[0] as u32) << 24) - | ((octets[1] as u32) << 16) - | ((octets[2] as u32) << 8) - | (octets[3] as u32); - - // 计算前缀长度 - let prefix_length = mask_u32.leading_ones() as u8; - - // 验证掩码连续性 - if prefix_length == 0 { - if mask_u32 == 0 { Ok(0) } else { Err(NetworkConfigError::InvalidSubnet) } - } else if prefix_length == 32 { - if mask_u32 == 0xFFFFFFFF { Ok(32) } else { Err(NetworkConfigError::InvalidSubnet) } - } else { - let expected_mask = 0xFFFFFFFFu32 << (32 - prefix_length); - if mask_u32 == expected_mask { - Ok(prefix_length) - } else { - Err(NetworkConfigError::InvalidSubnet) - } - } -} - -// 使用 -let prefix_length = Self::parse_subnet_mask(mask)?; -``` - -**文件位置**: `os/src/device/net/config.rs:20-87, 240` - ---- - -### 1.2 未修复的问题 - 网络系统调用存根 - -#### 问题 3: 所有网络系统调用只是存根实现 -**严重程度**: 🔴 严重 - 核心功能缺失 - -**问题描述**: -所有网络系统调用 (`socket`, `bind`, `listen`, `accept`, `connect`, `send`, `recv` 等) 都只返回虚拟值,没有实现真正的网络功能。 - -**当前实现** (`os/src/kernel/syscall/net_syscall.rs`): -```rust -pub fn socket(domain: i32, socket_type: i32, protocol: i32) -> isize { - // TODO: 实现套接字创建 - 3 // ❌ 返回虚拟 FD -} - -pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { - // TODO: 实现绑定逻辑 - 0 // ❌ 假装成功 -} - -pub fn send(sockfd: i32, buf: *const u8, len: usize, flags: i32) -> isize { - // TODO: 实现发送逻辑 - len as isize // ❌ 假装发送了所有数据 -} - -pub fn recv(sockfd: i32, buf: *mut u8, len: usize, flags: i32) -> isize { - // TODO: 实现接收逻辑 - 0 // ❌ 总是返回没有数据 -} -``` - -**影响**: -- 用户程序无法使用网络功能 -- 与 POSIX 标准不兼容 -- 无法运行真实的网络应用 - ---- - -## 2. 完整网络功能架构设计 - -### 2.1 整体架构图 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 用户空间 │ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│ │ TCP 应用 │ │ UDP 应用 │ │ 原始套接字 │ │ -│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ -└─────────┼─────────────────┼─────────────────┼───────────────┘ - │ │ │ - │ POSIX Socket API (syscall) │ - │ │ │ -┌─────────┼─────────────────┼─────────────────┼───────────────┐ -│ │ 内核空间 │ │ -│ ┌──────▼──────────────────▼─────────────▼──────┐ │ -│ │ 网络系统调用层 │ │ -│ │ socket/bind/listen/accept/connect/send/recv │ │ -│ └──────┬──────────────────────────────────┬─────┘ │ -│ │ │ │ -│ ┌──────▼───────────────────────────────────▼─────┐ │ -│ │ Socket 文件抽象层 │ │ -│ │ SocketFile (实现 File trait) │ │ -│ │ - TcpSocketFile │ │ -│ │ - UdpSocketFile │ │ -│ └──────┬──────────────────────────────────┬──────┘ │ -│ │ │ │ -│ ┌──────▼───────────────────────────────────▼──────┐ │ -│ │ 网络协议栈管理器 │ │ -│ │ NetworkStack │ │ -│ │ - SmoltcpInterface (设备 + 接口) │ │ -│ │ - SocketSet (所有 socket 的集合) │ │ -│ │ - Socket 元数据表 │ │ -│ └──────┬────────────────────────────────────────┘ │ -│ │ │ -│ ┌──────▼────────────────────────────────────────┐ │ -│ │ smoltcp 协议栈 │ │ -│ │ - TCP/UDP/IP/ICMP 协议实现 │ │ -│ │ - Socket 管理 │ │ -│ └──────┬─────────────────────────────────────────┘ │ -│ │ │ -│ ┌──────▼────────────────────────────────────────┐ │ -│ │ NetDeviceAdapter (设备适配器) │ │ -│ └──────┬─────────────────────────────────────────┘ │ -│ │ │ -│ ┌──────▼────────────────────────────────────────┐ │ -│ │ VirtIO 网络驱动 │ │ -│ │ (VirtioNet) │ │ -│ └────────────────────────────────────────────────┘ │ -│ │ -└───────────────────────────────────────────────────────────────┘ - │ - ┌────────▼────────┐ - │ 网络硬件 (QEMU) │ - └─────────────────┘ -``` - -### 2.2 关键组件说明 - -#### 2.2.1 Socket 文件抽象 (`os/src/vfs/socket.rs` - 需要创建) - -**目的**: 将 socket 集成到 VFS 中,使其像文件一样可以通过 FD 访问。 - -```rust -use crate::vfs::File; -use alloc::sync::Arc; -use smoltcp::socket::{TcpSocket, UdpSocket}; -use smoltcp::wire::{IpEndpoint, IpAddress}; - -/// Socket 类型 -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SocketType { - Stream, // TCP (SOCK_STREAM) - Datagram, // UDP (SOCK_DGRAM) - Raw, // 原始套接字 (SOCK_RAW) -} - -/// Socket 地址 -#[derive(Debug, Clone, Copy)] -pub struct SocketAddr { - pub ip: IpAddress, - pub port: u16, -} - -impl SocketAddr { - pub fn to_endpoint(&self) -> IpEndpoint { - IpEndpoint::new(self.ip, self.port) - } -} - -/// Socket 状态 -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SocketState { - Closed, - Listening, - Connecting, - Connected, - FinWait, -} - -/// TCP Socket 文件 -pub struct TcpSocketFile { - /// smoltcp socket handle - socket_handle: SocketHandle, - - /// Socket 状态 - state: SpinLock, - - /// 本地绑定地址 - local_addr: SpinLock>, - - /// 远程连接地址 - remote_addr: SpinLock>, - - /// 等待队列(用于 accept 的连接队列) - pending_connections: SpinLock>, - - /// 最大等待连接数(backlog) - backlog: usize, -} - -impl TcpSocketFile { - pub fn new(socket_handle: SocketHandle) -> Self { - Self { - socket_handle, - state: SpinLock::new(SocketState::Closed), - local_addr: SpinLock::new(None), - remote_addr: SpinLock::new(None), - pending_connections: SpinLock::new(VecDeque::new()), - backlog: 0, - } - } - - /// 绑定到本地地址 - pub fn bind(&self, addr: SocketAddr) -> Result<(), NetworkError> { - // 实现绑定逻辑 - todo!() - } - - /// 监听连接 - pub fn listen(&self, backlog: usize) -> Result<(), NetworkError> { - // 实现监听逻辑 - todo!() - } - - /// 接受连接 - pub fn accept(&self) -> Result<(Arc, SocketAddr), NetworkError> { - // 实现 accept 逻辑 - todo!() - } - - /// 连接到远程地址 - pub fn connect(&self, addr: SocketAddr) -> Result<(), NetworkError> { - // 实现连接逻辑 - todo!() - } -} - -/// 为 TcpSocketFile 实现 File trait -impl File for TcpSocketFile { - fn read(&self, buf: &mut [u8]) -> Result { - // 从 TCP socket 读取数据 - // 需要访问全局 NetworkStack 来操作 socket - todo!() - } - - fn write(&self, buf: &[u8]) -> Result { - // 向 TCP socket 写入数据 - todo!() - } - - fn seek(&self, _pos: SeekFrom) -> Result { - // Socket 不支持 seek - Err(FileError::NotSupported) - } - - fn is_seekable(&self) -> bool { - false - } - - // ... 其他 File trait 方法 -} - -/// UDP Socket 文件 -pub struct UdpSocketFile { - socket_handle: SocketHandle, - local_addr: SpinLock>, - remote_addr: SpinLock>, -} - -impl UdpSocketFile { - pub fn new(socket_handle: SocketHandle) -> Self { - Self { - socket_handle, - local_addr: SpinLock::new(None), - remote_addr: SpinLock::new(None), - } - } - - pub fn bind(&self, addr: SocketAddr) -> Result<(), NetworkError> { - todo!() - } - - pub fn sendto(&self, buf: &[u8], addr: SocketAddr) -> Result { - todo!() - } - - pub fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr), NetworkError> { - todo!() - } -} - -impl File for UdpSocketFile { - // 实现类似 TcpSocketFile 的方法 - // ... -} - -/// 网络错误 -#[derive(Debug)] -pub enum NetworkError { - InvalidAddress, - InvalidSocket, - NotConnected, - AlreadyConnected, - ConnectionRefused, - WouldBlock, - Timeout, - // ... 其他错误 -} -``` - -#### 2.2.2 网络协议栈管理器 (`os/src/net/stack.rs` - 需要创建) - -**目的**: 管理全局的 smoltcp 协议栈实例、socket 集合和元数据。 - -```rust -use crate::device::net::interface::SmoltcpInterface; -use crate::sync::SpinLock; -use alloc::collections::BTreeMap; -use alloc::sync::Arc; -use lazy_static::lazy_static; -use smoltcp::socket::{SocketHandle, SocketSet, TcpSocket, UdpSocket}; -use smoltcp::time::Instant; - -/// Socket 元数据 -pub struct SocketMetadata { - pub socket_type: SocketType, - pub local_addr: Option, - pub remote_addr: Option, - pub state: SocketState, -} - -/// 全局网络协议栈 -pub struct NetworkStack { - /// smoltcp 接口(包含 Device 和 Interface) - smoltcp_iface: SpinLock, - - /// Socket 集合(所有 socket 的容器) - socket_set: SpinLock>, - - /// Socket 元数据映射表 - /// SocketHandle -> SocketMetadata - socket_metadata: SpinLock>, - - /// 当前时间(用于 smoltcp) - current_time: SpinLock, -} - -impl NetworkStack { - pub fn new(smoltcp_iface: SmoltcpInterface) -> Self { - Self { - smoltcp_iface: SpinLock::new(smoltcp_iface), - socket_set: SpinLock::new(SocketSet::new(Vec::new())), - socket_metadata: SpinLock::new(BTreeMap::new()), - current_time: SpinLock::new(Instant::from_millis(0)), - } - } - - /// 创建新的 TCP socket - pub fn create_tcp_socket(&self) -> Result { - let mut socket_set = self.socket_set.lock(); - - // 创建 TCP socket 缓冲区 - let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 4096]); - let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 4096]); - let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - - // 添加到 socket 集合 - let socket_handle = socket_set.add(tcp_socket); - - // 记录元数据 - let mut metadata = self.socket_metadata.lock(); - metadata.insert(socket_handle, SocketMetadata { - socket_type: SocketType::Stream, - local_addr: None, - remote_addr: None, - state: SocketState::Closed, - }); - - Ok(socket_handle) - } - - /// 创建新的 UDP socket - pub fn create_udp_socket(&self) -> Result { - let mut socket_set = self.socket_set.lock(); - - // 创建 UDP socket 缓冲区 - let udp_rx_buffer = UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; 16], - vec![0; 4096] - ); - let udp_tx_buffer = UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; 16], - vec![0; 4096] - ); - let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); - - let socket_handle = socket_set.add(udp_socket); - - let mut metadata = self.socket_metadata.lock(); - metadata.insert(socket_handle, SocketMetadata { - socket_type: SocketType::Datagram, - local_addr: None, - remote_addr: None, - state: SocketState::Closed, - }); - - Ok(socket_handle) - } - - /// 删除 socket - pub fn remove_socket(&self, handle: SocketHandle) { - let mut socket_set = self.socket_set.lock(); - socket_set.remove(handle); - - let mut metadata = self.socket_metadata.lock(); - metadata.remove(&handle); - } - - /// 绑定 TCP socket 到本地地址 - pub fn tcp_bind(&self, handle: SocketHandle, addr: SocketAddr) -> Result<(), NetworkError> { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - // 调用 smoltcp 的 listen - socket.listen(addr.port) - .map_err(|_| NetworkError::InvalidAddress)?; - - // 更新元数据 - let mut metadata = self.socket_metadata.lock(); - if let Some(meta) = metadata.get_mut(&handle) { - meta.local_addr = Some(addr); - } - - Ok(()) - } - - /// TCP connect - pub fn tcp_connect( - &self, - handle: SocketHandle, - remote_addr: SocketAddr, - local_port: u16, - ) -> Result<(), NetworkError> { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - socket.connect( - self.smoltcp_iface.lock().interface().context(), - remote_addr.to_endpoint(), - local_port, - ).map_err(|_| NetworkError::ConnectionRefused)?; - - let mut metadata = self.socket_metadata.lock(); - if let Some(meta) = metadata.get_mut(&handle) { - meta.remote_addr = Some(remote_addr); - meta.state = SocketState::Connecting; - } - - Ok(()) - } - - /// TCP send - pub fn tcp_send(&self, handle: SocketHandle, data: &[u8]) -> Result { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - if !socket.can_send() { - return Err(NetworkError::WouldBlock); - } - - socket.send_slice(data) - .map_err(|_| NetworkError::WouldBlock) - } - - /// TCP recv - pub fn tcp_recv(&self, handle: SocketHandle, buffer: &mut [u8]) -> Result { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - if !socket.can_recv() { - return Err(NetworkError::WouldBlock); - } - - socket.recv_slice(buffer) - .map_err(|_| NetworkError::WouldBlock) - } - - /// UDP sendto - pub fn udp_sendto( - &self, - handle: SocketHandle, - data: &[u8], - remote_addr: SocketAddr, - ) -> Result<(), NetworkError> { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - socket.send_slice(data, remote_addr.to_endpoint()) - .map_err(|_| NetworkError::WouldBlock) - } - - /// UDP recvfrom - pub fn udp_recvfrom( - &self, - handle: SocketHandle, - buffer: &mut [u8], - ) -> Result<(usize, SocketAddr), NetworkError> { - let mut socket_set = self.socket_set.lock(); - let socket = socket_set.get_mut::(handle); - - match socket.recv_slice(buffer) { - Ok((size, endpoint)) => { - let addr = SocketAddr { - ip: endpoint.addr, - port: endpoint.port, - }; - Ok((size, addr)) - } - Err(_) => Err(NetworkError::WouldBlock), - } - } - - /// 轮询网络栈(处理所有网络事件) - /// 应该在定时器中断或专门的网络线程中定期调用 - pub fn poll(&self) { - // 更新时间 - let mut current_time = self.current_time.lock(); - *current_time = Instant::from_millis(current_time.total_millis() + 10); - - // 轮询接口 - let mut socket_set = self.socket_set.lock(); - let mut smoltcp_iface = self.smoltcp_iface.lock(); - - smoltcp_iface.poll(*current_time, &mut socket_set); - } -} - -lazy_static! { - /// 全局网络协议栈实例 - pub static ref NETWORK_STACK: SpinLock>> = - SpinLock::new(None); -} - -/// 初始化网络协议栈 -pub fn init_network_stack(smoltcp_iface: SmoltcpInterface) { - let stack = Arc::new(NetworkStack::new(smoltcp_iface)); - *NETWORK_STACK.lock() = Some(stack); -} - -/// 获取全局网络协议栈 -pub fn get_network_stack() -> Option> { - NETWORK_STACK.lock().clone() -} -``` - -#### 2.2.3 网络系统调用实现 (`os/src/kernel/syscall/net_syscall.rs` - 需要重写) - -**目的**: 实现真正的 POSIX socket 系统调用。 - -```rust -use crate::net::stack::{get_network_stack, NetworkStack}; -use crate::net::socket::{SocketAddr, TcpSocketFile, UdpSocketFile}; -use crate::kernel::task::current_task; -use crate::vfs::File; -use alloc::sync::Arc; -use core::ffi::c_void; - -/// sys_socket - 创建套接字 -/// -/// # 参数 -/// - domain: AF_INET (2) / AF_INET6 (10) -/// - type: SOCK_STREAM (1) / SOCK_DGRAM (2) / SOCK_RAW (3) -/// - protocol: 0 (自动选择) -/// -/// # 返回值 -/// - 成功: 文件描述符 -/// - 失败: 负数错误码 -pub fn sys_socket(domain: i32, socket_type: i32, protocol: i32) -> isize { - // 验证参数 - if domain != 2 { // AF_INET - return -97; // -EAFNOSUPPORT - } - - let stack = match get_network_stack() { - Some(s) => s, - None => return -19, // -ENODEV (网络栈未初始化) - }; - - // 根据 socket 类型创建不同的 socket - let socket_file: Arc = match socket_type { - 1 => { // SOCK_STREAM (TCP) - let handle = match stack.create_tcp_socket() { - Ok(h) => h, - Err(_) => return -12, // -ENOMEM - }; - Arc::new(TcpSocketFile::new(handle)) - } - 2 => { // SOCK_DGRAM (UDP) - let handle = match stack.create_udp_socket() { - Ok(h) => h, - Err(_) => return -12, // -ENOMEM - }; - Arc::new(UdpSocketFile::new(handle)) - } - _ => return -93, // -EPROTONOSUPPORT - }; - - // 将 socket 添加到当前任务的文件描述符表 - let task = current_task().unwrap(); - let fd = match task.add_file(socket_file) { - Ok(fd) => fd, - Err(_) => return -24, // -EMFILE (太多打开的文件) - }; - - fd as isize -} - -/// sys_bind - 绑定套接字到地址 -/// -/// # 参数 -/// - sockfd: socket 文件描述符 -/// - addr: sockaddr 结构指针 -/// - addrlen: 地址长度 -/// -/// # 返回值 -/// - 成功: 0 -/// - 失败: 负数错误码 -pub fn sys_bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { - if sockfd < 0 || addr.is_null() { - return -14; // -EFAULT - } - - // 解析 sockaddr_in 结构 - let socket_addr = unsafe { - if addrlen < 16 { // sizeof(sockaddr_in) - return -22; // -EINVAL - } - - // sockaddr_in 结构: - // - u16 sin_family - // - u16 sin_port (网络字节序) - // - u32 sin_addr (网络字节序) - let port = u16::from_be((addr.add(2) as *const u16).read()); - let ip_bytes = core::slice::from_raw_parts(addr.add(4), 4); - let ip = IpAddress::v4(ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3]); - - SocketAddr { ip, port } - }; - - // 获取 socket 文件 - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, // -EBADF - }; - - // 尝试向下转型为 TcpSocketFile 或 UdpSocketFile - if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { - match tcp_socket.bind(socket_addr) { - Ok(_) => 0, - Err(_) => -98, // -EADDRINUSE - } - } else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { - match udp_socket.bind(socket_addr) { - Ok(_) => 0, - Err(_) => -98, // -EADDRINUSE - } - } else { - -88 // -ENOTSOCK - } -} - -/// sys_listen - 监听连接 -pub fn sys_listen(sockfd: i32, backlog: i32) -> isize { - if sockfd < 0 { - return -9; // -EBADF - } - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - // 只有 TCP socket 支持 listen - if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { - match tcp_socket.listen(backlog as usize) { - Ok(_) => 0, - Err(_) => -22, // -EINVAL - } - } else { - -95 // -EOPNOTSUPP (操作不支持) - } -} - -/// sys_accept - 接受连接 -pub fn sys_accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { - if sockfd < 0 { - return -9; - } - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - let tcp_socket = match socket_file.as_any().downcast_ref::() { - Some(s) => s, - None => return -88, // -ENOTSOCK - }; - - // 接受连接 - let (new_socket_file, remote_addr) = match tcp_socket.accept() { - Ok(result) => result, - Err(_) => return -11, // -EAGAIN (没有连接可接受) - }; - - // 填充地址信息 - if !addr.is_null() && !addrlen.is_null() { - unsafe { - let available_len = *addrlen as usize; - if available_len >= 16 { - // 填充 sockaddr_in - // ... (类似 bind 的逆操作) - } - } - } - - // 为新连接创建 FD - match task.add_file(new_socket_file) { - Ok(fd) => fd as isize, - Err(_) => -24, // -EMFILE - } -} - -/// sys_connect - 连接到远程地址 -pub fn sys_connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { - if sockfd < 0 || addr.is_null() { - return -14; - } - - // 解析地址(类似 bind) - let socket_addr = unsafe { - // ... 解析 sockaddr_in - }; - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - let tcp_socket = match socket_file.as_any().downcast_ref::() { - Some(s) => s, - None => return -88, - }; - - match tcp_socket.connect(socket_addr) { - Ok(_) => 0, - Err(_) => -111, // -ECONNREFUSED - } -} - -/// sys_send / sys_sendto - 发送数据 -pub fn sys_sendto( - sockfd: i32, - buf: *const u8, - len: usize, - flags: i32, - dest_addr: *const u8, - addrlen: u32, -) -> isize { - if sockfd < 0 || buf.is_null() || len == 0 { - return -14; - } - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - // 从用户空间拷贝数据 - let data = unsafe { core::slice::from_raw_parts(buf, len) }; - - // TCP socket - if let Some(tcp_socket) = socket_file.as_any().downcast_ref::() { - match socket_file.write(data) { - Ok(written) => written as isize, - Err(_) => -11, // -EAGAIN - } - } - // UDP socket - else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { - if dest_addr.is_null() { - return -89; // -EDESTADDRREQ - } - - let remote_addr = unsafe { - // 解析 dest_addr - }; - - match udp_socket.sendto(data, remote_addr) { - Ok(sent) => sent as isize, - Err(_) => -11, - } - } else { - -88 // -ENOTSOCK - } -} - -/// sys_recv / sys_recvfrom - 接收数据 -pub fn sys_recvfrom( - sockfd: i32, - buf: *mut u8, - len: usize, - flags: i32, - src_addr: *mut u8, - addrlen: *mut u32, -) -> isize { - if sockfd < 0 || buf.is_null() || len == 0 { - return -14; - } - - let task = current_task().unwrap(); - let socket_file = match task.get_file(sockfd as usize) { - Some(f) => f, - None => return -9, - }; - - let buffer = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - - // TCP socket - if let Some(_tcp_socket) = socket_file.as_any().downcast_ref::() { - match socket_file.read(buffer) { - Ok(read) => read as isize, - Err(_) => 0, // 没有数据可读 - } - } - // UDP socket - else if let Some(udp_socket) = socket_file.as_any().downcast_ref::() { - match udp_socket.recvfrom(buffer) { - Ok((size, remote_addr)) => { - // 填充源地址 - if !src_addr.is_null() && !addrlen.is_null() { - unsafe { - // 填充 sockaddr_in - } - } - size as isize - } - Err(_) => 0, - } - } else { - -88 - } -} - -// ... 其他系统调用 (close, shutdown, getsockopt, setsockopt 等) -``` - ---- - -## 3. 实现步骤 - -### 第 1 步: 创建 Socket 文件抽象 -**文件**: `os/src/vfs/socket.rs` - -1. 定义 `SocketType`, `SocketAddr`, `SocketState` 等基础类型 -2. 实现 `TcpSocketFile` 结构体及其方法 -3. 实现 `UdpSocketFile` 结构体及其方法 -4. 为两者实现 `File` trait -5. 在 `os/src/vfs/mod.rs` 中导出 - -**测试**: 编译通过,类型检查正确 - ---- - -### 第 2 步: 创建网络协议栈管理器 -**文件**: `os/src/net/stack.rs` (需要先创建 `os/src/net/` 目录) - -1. 定义 `NetworkStack` 结构体 -2. 实现 socket 创建/删除方法 -3. 实现 TCP/UDP 操作方法 (bind, connect, send, recv 等) -4. 实现 `poll()` 方法 -5. 创建全局实例和初始化函数 - -**测试**: 编译通过,可以创建 NetworkStack 实例 - ---- - -### 第 3 步: 修改 NetworkInterface 初始化 -**文件**: `os/src/device/net/interface.rs`, `os/src/main.rs` 或初始化代码 - -1. 在网络设备初始化时创建 `SmoltcpInterface` -2. 使用 `SmoltcpInterface` 初始化 `NetworkStack` -3. 设置全局 `NETWORK_STACK` - -**代码示例**: -```rust -// 在网络初始化函数中 -pub fn init_network() { - // ... 初始化网络接口 - let manager = NETWORK_INTERFACE_MANAGER.lock(); - if let Some(iface) = manager.get_interfaces().first() { - let smoltcp_iface = iface.create_smoltcp_interface(); - init_network_stack(smoltcp_iface); - } -} -``` - ---- - -### 第 4 步: 实现网络系统调用 -**文件**: `os/src/kernel/syscall/net_syscall.rs` - -1. 重写 `sys_socket` -2. 重写 `sys_bind` -3. 重写 `sys_listen` -4. 重写 `sys_accept` -5. 重写 `sys_connect` -6. 重写 `sys_sendto` / `sys_send` -7. 重写 `sys_recvfrom` / `sys_recv` -8. 实现其他系统调用 (close, shutdown, getsockopt, setsockopt) - -**测试**: 逐个测试每个系统调用 - ---- - -### 第 5 步: 实现网络轮询机制 -**选项 A: 定时器中断轮询** -```rust -// 在定时器中断处理函数中 -pub fn timer_interrupt_handler() { - // ... 其他定时器逻辑 - - if let Some(stack) = get_network_stack() { - stack.poll(); // 轮询网络栈 - } - - // ... 调度等 -} -``` - -**选项 B: 专门的网络线程** (推荐) -```rust -// 创建内核线程 -pub fn network_polling_thread() { - loop { - if let Some(stack) = get_network_stack() { - stack.poll(); - } - - // 休眠一小段时间(如 10ms) - sleep_ms(10); - } -} -``` - ---- - -### 第 6 步: 处理阻塞和非阻塞 -1. 为 socket 添加阻塞/非阻塞标志 -2. 阻塞模式下,recv/send 应该等待数据或空间可用 -3. 实现等待队列,让任务在 socket 上等待 -4. 网络事件到达时唤醒等待的任务 - ---- - -### 第 7 步: 测试 -1. **基础测试**: 创建 socket, bind, close -2. **UDP 测试**: 发送和接收 UDP 数据包 -3. **TCP 客户端测试**: 连接到外部服务器,发送/接收数据 -4. **TCP 服务器测试**: 监听端口,接受连接 -5. **并发测试**: 多个连接同时工作 -6. **错误处理测试**: 各种错误情况 - -**测试用户程序示例**: -```c -// user/src/test_tcp_client.c -int main() { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) { - printf("socket() failed\n"); - return 1; - } - - struct sockaddr_in server_addr = { - .sin_family = AF_INET, - .sin_port = htons(80), - .sin_addr.s_addr = inet_addr("192.168.1.1"), - }; - - if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { - printf("connect() failed\n"); - return 1; - } - - const char* request = "GET / HTTP/1.0\r\n\r\n"; - send(sockfd, request, strlen(request), 0); - - char buffer[1024]; - int n = recv(sockfd, buffer, sizeof(buffer), 0); - if (n > 0) { - printf("Received: %.*s\n", n, buffer); - } - - close(sockfd); - return 0; -} -``` - ---- - -## 4. 关键注意事项 - -### 4.1 线程安全 -- 所有全局状态都必须用锁保护 -- 避免死锁:定义锁的获取顺序 -- 注意:不能在中断处理程序中调用可能阻塞的操作 - -### 4.2 内存管理 -- smoltcp 的 socket 缓冲区需要静态生命周期 -- 使用 `Vec` 或 `Box` 分配堆内存 -- 注意避免内存泄漏(socket 关闭时释放资源) - -### 4.3 地址字节序 -- 网络字节序是大端 (big-endian) -- RISC-V 通常是小端 (little-endian) -- 使用 `u16::from_be()` / `u16::to_be()` 转换 - -### 4.4 用户空间内存访问 -- 所有用户指针都必须验证 -- 使用 `copy_from_user` / `copy_to_user` 安全拷贝 -- 防止用户传入内核地址 - -### 4.5 错误处理 -- 使用标准的 POSIX 错误码 (errno) -- 常见错误码: - - `-EBADF` (9): 错误的文件描述符 - - `-EINVAL` (22): 无效参数 - - `-EAGAIN` (11): 资源暂时不可用 - - `-ECONNREFUSED` (111): 连接被拒绝 - - `-EADDRINUSE` (98): 地址已被使用 - ---- - -## 5. 依赖和模块关系 - -### 5.1 新增模块依赖图 -``` -vfs/socket.rs - ├─> sync::SpinLock - ├─> vfs::File (trait) - ├─> net::stack::NetworkStack - └─> smoltcp::socket::{TcpSocket, UdpSocket} - -net/stack.rs - ├─> sync::SpinLock - ├─> device::net::interface::SmoltcpInterface - ├─> smoltcp::socket::SocketSet - └─> smoltcp::time::Instant - -kernel/syscall/net_syscall.rs - ├─> vfs::socket::{TcpSocketFile, UdpSocketFile} - ├─> net::stack::{get_network_stack, NetworkStack} - ├─> kernel::task::current_task - └─> 用户内存访问函数 -``` - -### 5.2 遵循模块分层 -根据 CLAUDE.md 中的模块层次规则: -``` -arch → mm → sync → kernel → {ipc, vfs, fs, net} -``` - -- `net/` 模块位于最上层,可以使用所有下层模块 -- 不能让下层模块依赖 `net/` -- Socket 作为 VFS 的一部分,位于 `vfs/socket.rs` - ---- - -## 6. 性能优化建议(后续) - -1. **零拷贝**: 使用 DMA 直接在网卡和用户空间之间传输 -2. **批量处理**: 一次轮询处理多个数据包 -3. **中断合并**: 减少中断频率 -4. **Socket 缓冲区调优**: 根据应用调整缓冲区大小 -5. **连接复用**: 支持 SO_REUSEADDR 和 SO_REUSEPORT - ---- - -## 7. 未来扩展 - -### 7.1 IPv6 支持 -- 扩展 `SocketAddr` 支持 IPv6 -- 处理 `AF_INET6` domain - -### 7.2 原始套接字 (SOCK_RAW) -- 允许直接访问 IP/ICMP 层 -- 需要权限检查 - -### 7.3 Unix Domain Socket -- 进程间通信 -- 文件系统路径作为地址 - -### 7.4 高级功能 -- `poll()` / `epoll()` 系统调用 -- `sendmsg()` / `recvmsg()` (scatter-gather I/O) -- `sendfile()` (零拷贝文件传输) -- Socket 选项 (SO_KEEPALIVE, TCP_NODELAY 等) - ---- - -## 8. 参考资料 - -### 8.1 文档 -- [smoltcp 文档](https://docs.rs/smoltcp/) -- [POSIX Socket API](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html) -- [Linux Socket Man Pages](https://man7.org/linux/man-pages/man2/socket.2.html) - -### 8.2 代码示例 -- [Redox OS 网络实现](https://gitlab.redox-os.org/redox-os/netstack) -- [rCore 网络模块](https://github.com/rcore-os/rCore/tree/master/kernel/src/net) - ---- - -## 9. 检查清单 - -实现完成后,确保: - -- [ ] 所有 TODO 注释已删除 -- [ ] 所有系统调用都返回正确的错误码 -- [ ] 内存安全(无悬垂指针、无内存泄漏) -- [ ] 线程安全(所有共享状态都有锁保护) -- [ ] 用户输入验证(防止越界、空指针等) -- [ ] 编写了测试用户程序 -- [ ] 通过了所有测试 -- [ ] 更新了相关文档 -- [ ] 代码符合项目风格规范(`cargo fmt`, `make quick_check_style`) - ---- - -## 附录 A: smoltcp Socket 缓冲区大小建议 - -```rust -// TCP -let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 8192]); // 8KB 接收缓冲区 -let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 8192]); // 8KB 发送缓冲区 - -// UDP -let udp_rx_buffer = UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; 16], // 最多 16 个数据包 - vec![0; 4096] // 4KB 总缓冲区 -); -let udp_tx_buffer = UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; 16], - vec![0; 4096] -); -``` - -根据应用需求调整: -- **Web 服务器**: 增大 TCP 缓冲区 (16KB - 64KB) -- **音视频流**: 增大 UDP 缓冲区 -- **嵌入式/内存受限**: 减小缓冲区 (1KB - 2KB) - ---- - -## 附录 B: 常见错误码对照表 - -| 错误名 | 值 | 含义 | 返回时机 | -|-----------------|-----|------------------------|-----------------------------| -| EBADF | 9 | 错误的文件描述符 | FD 无效或不是 socket | -| EAGAIN/EWOULDBLOCK | 11 | 资源暂时不可用 | 非阻塞操作会阻塞 | -| ENOMEM | 12 | 内存不足 | 无法分配 socket | -| EFAULT | 14 | 错误的地址 | 用户指针无效 | -| EINVAL | 22 | 无效参数 | 参数检查失败 | -| EMFILE | 24 | 打开文件过多 | FD 表已满 | -| ENOTSOCK | 88 | 不是 socket | 对非 socket FD 操作 | -| EDESTADDRREQ | 89 | 需要目标地址 | UDP sendto 未提供地址 | -| EPROTONOSUPPORT | 93 | 不支持的协议 | 协议参数错误 | -| EOPNOTSUPP | 95 | 不支持的操作 | 对 UDP socket 调用 listen | -| EAFNOSUPPORT | 97 | 不支持的地址族 | domain 不是 AF_INET | -| EADDRINUSE | 98 | 地址已被使用 | bind 到已占用端口 | -| ECONNREFUSED | 111 | 连接被拒绝 | connect 失败 | - ---- - -**文档维护**: 实现过程中遇到问题或有新发现时,请更新此文档。 diff --git a/docs/pr_recorde/network_rearchitecture_new_main.md b/docs/pr_recorde/network_rearchitecture_new_main.md new file mode 100644 index 00000000..5941fe66 --- /dev/null +++ b/docs/pr_recorde/network_rearchitecture_new_main.md @@ -0,0 +1,25 @@ +# ✨ Feature / 新功能 + +## 🚀 描述 (Description) + +本次拉取请求在 `new-main` 上落实网络模块重构,主要改进如下: + +- 收口网络协议栈运行时:新增 `NetworkStack` 门面,将 smoltcp socket set、active interface runtime、loopback link 和 poll 推进统一放到 `os/src/net/stack.rs`。 +- 清理 socket/syscall 边界:`SocketFile` 的读写、可读可写、recvfrom、sendto、drop 等路径改为经 `NetworkStack` 操作协议栈;网络 syscall 不再直接访问 `SOCKET_SET`、`NET_IFACE` 或 `smoltcp::socket::{tcp, udp}`。 +- 拆分设备与接口职责:VirtIO net 初始化只注册 `NetDevice`,接口创建统一由网络子系统入口负责;`NetworkInterface` 不再直接实现 `Driver`,改由 `NetDriverHandle` 作为兼容桥。 +- 显式建模 loopback:新增 `LoopbackNetDevice`,默认网络初始化确保 `lo` 存在;`NullNetDevice` 不再承担 loopback 语义。 +- 更新网络文档:补齐 `document/net/` 面向读者的模块文档,并恢复/补充 `document/net/`、`document/arch/*/network_rearchitecture.md` 中的施工记录。 +- 清理本地协作文件:`.gitignore` 忽略各处 `AGENTS.md` 和 `docs/superpowers/` 草稿目录;补充 unused 类告警忽略标签,避免重构期间被旧未使用告警阻塞。 + +本记录基于以下提交: + +- `2d93e1d chore: 忽略本地代理与草稿文档` +- `16b161f docs: 更新网络重构文档` +- `11ca651 fix: 忽略未使用告警` +- `37cc37e fix: 收口网络协议栈运行时` +- `4ad26a9 fix: 拆分网络设备与接口` +- `e1281ae fix: 修正文档空行` + +## 🔗 关联 Issue + +暂无关联 Issue。 diff --git a/document/SUMMARY.md b/document/SUMMARY.md index 8d8daf12..c25dca2f 100644 --- a/document/SUMMARY.md +++ b/document/SUMMARY.md @@ -108,11 +108,13 @@ - [用户栈布局](arch/riscv/stack_layout.md) - [多核启动](arch/riscv/smp_boot.md) - [核间中断 (IPI)](arch/riscv/ipi.md) +- [网络重构说明](arch/riscv/network_rearchitecture.md) ## LoongArch64 - [LoongArch64](arch/loongarch/README.md) - [启动与用户态运行修复总结(comix-1 当前分支)](arch/loongarch/bringup_userland.md) + - [网络重构说明](arch/loongarch/network_rearchitecture.md) --- diff --git a/doc/arch/loongarch/network_rearchitecture.md b/document/arch/loongarch/network_rearchitecture.md similarity index 100% rename from doc/arch/loongarch/network_rearchitecture.md rename to document/arch/loongarch/network_rearchitecture.md diff --git a/doc/arch/riscv/network_rearchitecture.md b/document/arch/riscv/network_rearchitecture.md similarity index 100% rename from doc/arch/riscv/network_rearchitecture.md rename to document/arch/riscv/network_rearchitecture.md diff --git a/doc/net/network_rearchitecture.md b/document/net/network_rearchitecture.md similarity index 91% rename from doc/net/network_rearchitecture.md rename to document/net/network_rearchitecture.md index d3d58bb5..2bc7db3e 100644 --- a/doc/net/network_rearchitecture.md +++ b/document/net/network_rearchitecture.md @@ -1,6 +1,6 @@ # 网络重构施工记录 -本文档记录 `new-main` 上网络模块重构的执行顺序。当前事实应写入 `document/net/`,本目录只保留施工上下文。 +本文档记录 `new-main` 上网络模块重构的执行顺序。 ## 执行顺序 diff --git a/doc/net/rearchitecture_device.md b/document/net/rearchitecture_device.md similarity index 100% rename from doc/net/rearchitecture_device.md rename to document/net/rearchitecture_device.md diff --git a/doc/net/rearchitecture_execution_plan.md b/document/net/rearchitecture_execution_plan.md similarity index 100% rename from doc/net/rearchitecture_execution_plan.md rename to document/net/rearchitecture_execution_plan.md diff --git a/doc/net/rearchitecture_interface.md b/document/net/rearchitecture_interface.md similarity index 100% rename from doc/net/rearchitecture_interface.md rename to document/net/rearchitecture_interface.md diff --git a/doc/net/rearchitecture_loopback_poll.md b/document/net/rearchitecture_loopback_poll.md similarity index 100% rename from doc/net/rearchitecture_loopback_poll.md rename to document/net/rearchitecture_loopback_poll.md diff --git a/doc/net/rearchitecture_socket_syscall.md b/document/net/rearchitecture_socket_syscall.md similarity index 100% rename from doc/net/rearchitecture_socket_syscall.md rename to document/net/rearchitecture_socket_syscall.md diff --git a/doc/net/rearchitecture_stack_runtime.md b/document/net/rearchitecture_stack_runtime.md similarity index 100% rename from doc/net/rearchitecture_stack_runtime.md rename to document/net/rearchitecture_stack_runtime.md From e554fdca2f1841bee81c46d0ad95c04615d3f4c7 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 23:33:29 +0800 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20=E6=94=B6=E5=8F=A3=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E6=A0=88=E7=8A=B6=E6=80=81=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/src/arch/riscv/boot/mod.rs | 2 +- os/src/arch/riscv/intr/mod.rs | 4 +- os/src/arch/riscv/syscall/mod.rs | 9 - os/src/kernel/syscall/network.rs | 64 +-- os/src/kernel/syscall/task.rs | 2 +- os/src/main.rs | 11 - os/src/net/socket.rs | 938 ++----------------------------- os/src/net/stack.rs | 786 ++++++++++++++++++++++++-- os/src/util/user_buffer.rs | 8 +- 9 files changed, 793 insertions(+), 1031 deletions(-) diff --git a/os/src/arch/riscv/boot/mod.rs b/os/src/arch/riscv/boot/mod.rs index 9681b5cd..af44cc4d 100644 --- a/os/src/arch/riscv/boot/mod.rs +++ b/os/src/arch/riscv/boot/mod.rs @@ -152,7 +152,7 @@ fn init() { pr_info!("[Init] Continuing without filesystem..."); } - // 初始化默认网络配置(eth0 + 127.0.0.1 loopback + 全局 NET_IFACE) + // 初始化默认网络配置(eth0 + 127.0.0.1 loopback + NetworkStack runtime) if let Err(e) = crate::net::config::NetworkConfigManager::init_default_interface() { pr_warn!( "[Init] Warning: Failed to init default network interface: {:?}", diff --git a/os/src/arch/riscv/intr/mod.rs b/os/src/arch/riscv/intr/mod.rs index 7854690f..91a1aae6 100644 --- a/os/src/arch/riscv/intr/mod.rs +++ b/os/src/arch/riscv/intr/mod.rs @@ -36,7 +36,7 @@ pub unsafe fn disable_timer_interrupt() { /// /// 该函数直接操作 CPU 寄存器,调用者必须确保在适当的上下文中调用 pub unsafe fn enable_software_interrupt() { - sie::set_ssoft() + unsafe { sie::set_ssoft() } } /// 禁用软件中断 @@ -45,7 +45,7 @@ pub unsafe fn enable_software_interrupt() { /// /// 该函数直接操作 CPU 寄存器,调用者必须确保在适当的上下文中调用 pub unsafe fn disable_software_interrupt() { - sie::clear_ssoft() + unsafe { sie::clear_ssoft() } } /// 启用中断 diff --git a/os/src/arch/riscv/syscall/mod.rs b/os/src/arch/riscv/syscall/mod.rs index 8f7e002f..428365a4 100644 --- a/os/src/arch/riscv/syscall/mod.rs +++ b/os/src/arch/riscv/syscall/mod.rs @@ -182,15 +182,6 @@ pub fn dispatch_syscall(frame: &mut super::trap::TrapFrame) { // 扩展系统调用 (Extended/Legacy) // (send/recv 等已经通过更通用的接口实现,不需要单独分发) - - // 系统信息 (补充) - syscall_number::SYS_SYSINFO => sys_sysinfo(frame), - - // POSIX 定时器 (补充) - syscall_number::SYS_CLOCK_GETTIME => sys_clock_gettime(frame), - syscall_number::SYS_CLOCK_SETTIME => sys_clock_settime(frame), - syscall_number::SYS_CLOCK_GETRES => sys_clock_getres(frame), - _ => { // 未知的系统调用 frame.x10_a0 = (-(ENOSYS as isize)) as usize; diff --git a/os/src/kernel/syscall/network.rs b/os/src/kernel/syscall/network.rs index 838e0afb..5ea615f6 100644 --- a/os/src/kernel/syscall/network.rs +++ b/os/src/kernel/syscall/network.rs @@ -242,7 +242,7 @@ pub fn socket(domain: i32, socket_type: i32, _protocol: i32) -> isize { /// 绑定套接字 pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { - let endpoint = unsafe { + let endpoint = { let _guard = SumGuard::new(); let ep = parse_sockaddr_in(addr, addrlen); match ep { @@ -430,11 +430,7 @@ pub fn listen(sockfd: i32, backlog: i32) -> isize { listen_endpoint.port ); - if network_stack() - .lock() - .tcp_listen(h, listen_endpoint) - .is_err() - { + if network_stack().tcp_listen(h, listen_endpoint).is_err() { attempts_left = attempts_left.saturating_sub(1); if attempts_left == 0 { return -98; // EADDRINUSE @@ -504,13 +500,11 @@ pub fn accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { None => return -88, // ENOTSOCK }; - let (state, listen_endpoint) = match network_stack() - .lock() - .tcp_listener_state_endpoint(listen_handle) - { - Some(v) => v, - None => return -88, // ENOTSOCK - }; + let (state, listen_endpoint) = + match network_stack().tcp_listener_state_endpoint(listen_handle) { + Some(v) => v, + None => return -88, // ENOTSOCK + }; if state != TcpListenState::Listen && socket_file.listen_sockets_len() < backlog { // detach current listen socket immediately @@ -520,11 +514,10 @@ pub fn accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { }; if network_stack() - .lock() .tcp_listen(new_listen_handle, listen_endpoint) .is_err() { - network_stack().lock().remove_tcp_socket(new_listen_handle); + network_stack().remove_tcp_socket(new_listen_handle); return -12; // ENOMEM or other error } @@ -574,18 +567,16 @@ fn accept_return_conn( ) -> isize { use crate::net::socket::SocketFile; - let (remote_endpoint, local_endpoint) = - match network_stack().lock().tcp_accept_endpoints(conn_handle) { - Some(v) => v, - None => return -11, // EAGAIN - }; + let (remote_endpoint, local_endpoint) = match network_stack().tcp_accept_endpoints(conn_handle) + { + Some(v) => v, + None => return -11, // EAGAIN + }; if !addr.is_null() && !addrlen.is_null() { let _guard = SumGuard::new(); - unsafe { - // accept(): Linux truncates if addrlen is too small; our helper implements that. - let _ = write_sockaddr_in(addr, addrlen, remote_endpoint); - } + // accept(): Linux truncates if addrlen is too small; our helper implements that. + let _ = write_sockaddr_in(addr, addrlen, remote_endpoint); } let conn = Arc::new(SocketFile::new(SocketHandle::Tcp(conn_handle))); @@ -605,7 +596,7 @@ fn accept_return_conn( /// 连接到远程地址 pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { - let endpoint = unsafe { + let endpoint = { let _guard = SumGuard::new(); let ep = parse_sockaddr_in(addr, addrlen); match ep { @@ -642,7 +633,6 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { match handle { SocketHandle::Tcp(h) => { let (_state, is_open) = network_stack() - .lock() .tcp_debug_state(h) .unwrap_or((TcpConnectionState::Closed, false)); pr_debug!("connect: socket state={:?}, is_open={}", _state, is_open); @@ -659,7 +649,6 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { }; let mut local_endpoint = network_stack() - .lock() .socket_local_endpoint(handle) .unwrap_or_else(|| { // Choose local address based on remote address. @@ -716,7 +705,6 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { } let state = network_stack() - .lock() .tcp_connection_state(h) .unwrap_or(TcpConnectionState::Closed); pr_debug!("connect: loop, handle={:?}, state={:?}", h, state); @@ -1576,7 +1564,7 @@ pub fn sendto( return send(sockfd, buf, len, 0); } - let endpoint = unsafe { + let endpoint = { let _guard = SumGuard::new(); let ep = parse_sockaddr_in(dest_addr, addrlen); match ep { @@ -1742,7 +1730,7 @@ pub fn shutdown(sockfd: i32, how: i32) -> isize { if should_close_tcp { if let SocketHandle::Tcp(h) = handle { - network_stack().lock().tcp_close(h); + network_stack().tcp_close(h); } } @@ -1765,7 +1753,7 @@ pub fn getsockname(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { }; drop(task_lock); - let local_endpoint = network_stack().lock().socket_local_endpoint(handle); + let local_endpoint = network_stack().socket_local_endpoint(handle); // Linux behavior: getsockname() on an unbound socket typically returns success and // fills a sockaddr with AF_INET and port 0. @@ -1779,10 +1767,8 @@ pub fn getsockname(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { }; let _guard = SumGuard::new(); - unsafe { - if write_sockaddr_in(addr, addrlen, ep).is_err() { - return -22; // EINVAL - } + if write_sockaddr_in(addr, addrlen, ep).is_err() { + return -22; // EINVAL } 0 } @@ -1798,7 +1784,7 @@ pub fn getpeername(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { }; let remote_endpoint = match handle { - SocketHandle::Tcp(_) => network_stack().lock().socket_remote_endpoint(handle), + SocketHandle::Tcp(_) => network_stack().socket_remote_endpoint(handle), SocketHandle::Udp(_) => { // UDP doesn't have a peer, use stored endpoint let file = match task.lock().fd_table.get(sockfd as usize) { @@ -1813,10 +1799,8 @@ pub fn getpeername(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { if let Some(ep) = remote_endpoint { { let _guard = SumGuard::new(); - unsafe { - if write_sockaddr_in(addr, addrlen, ep).is_err() { - return -22; // EINVAL - } + if write_sockaddr_in(addr, addrlen, ep).is_err() { + return -22; // EINVAL } } 0 diff --git a/os/src/kernel/syscall/task.rs b/os/src/kernel/syscall/task.rs index c5d22bca..ad09bdeb 100644 --- a/os/src/kernel/syscall/task.rs +++ b/os/src/kernel/syscall/task.rs @@ -357,7 +357,7 @@ pub fn execve( envp: *const *const c_char, ) -> c_int { // 使用 SumGuard 来安全访问用户空间路径和参数 - let (path_str, argv_strings, envp_strings) = unsafe { + let (path_str, argv_strings, envp_strings) = { let _guard = SumGuard::new(); let path_str = match get_path_safe(path) { Ok(s) => s.to_string(), diff --git a/os/src/main.rs b/os/src/main.rs index 2bca27b6..326ef6d6 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -9,17 +9,6 @@ #![feature(custom_test_frameworks)] #![test_runner(test_runner)] #![reexport_test_harness_main = "test_main"] -#![allow( - dead_code, - unreachable_code, - unreachable_patterns, - unused_assignments, - unused_doc_comments, - unused_imports, - unused_mut, - unused_unsafe, - unused_variables -)] extern crate alloc; diff --git a/os/src/net/socket.rs b/os/src/net/socket.rs index 483b7dd2..84cce86e 100644 --- a/os/src/net/socket.rs +++ b/os/src/net/socket.rs @@ -3,10 +3,8 @@ use crate::sync::SpinLock; use crate::vfs::{File, FsError, InodeMetadata}; use alloc::collections::VecDeque; -use alloc::vec; use lazy_static::lazy_static; -use smoltcp::iface::{Interface, SocketHandle as SmoltcpHandle, SocketSet}; -use smoltcp::socket::{tcp, udp}; +use smoltcp::iface::SocketHandle as SmoltcpHandle; use smoltcp::wire::{IpAddress, IpEndpoint, Ipv4Address}; #[derive(Clone, Copy, Debug)] @@ -18,127 +16,27 @@ pub enum SocketHandle { use alloc::collections::BTreeMap; use alloc::sync::Arc; -pub struct NetIfaceWrapper { - device: SpinLock, - interface: SpinLock, -} - -impl NetIfaceWrapper { - pub fn poll(&self, sockets: &SpinLock>) -> bool { - let timestamp = - smoltcp::time::Instant::from_millis(crate::arch::timer::get_time_ms() as i64); - let mut dev = self.device.lock(); - - // 检查队列长度 - let queue_len = dev.loopback_queue_len(); - if queue_len > 0 { - crate::pr_debug!("poll: loopback queue has {} packets", queue_len); - } - - let mut iface = self.interface.lock(); - let mut sockets = sockets.lock(); - - crate::pr_debug!("poll: before iface.poll"); - let result = iface.poll(timestamp, &mut *dev, &mut *sockets); - crate::pr_debug!("poll: result={:?}", result); - - // NOTE: For loopback traffic, frames produced by Tx are enqueued into `loopback_queue` - // during this poll, and therefore won't be received until a subsequent poll. Do a small, - // bounded extra poll to consume newly enqueued frames, otherwise UDP workloads like - // iperf3 can appear to "stall" (server sees only the first datagram). - if dev.loopback_queue_len() > 0 { - const MAX_EXTRA_POLLS: usize = 2; - for _ in 0..MAX_EXTRA_POLLS { - if dev.loopback_queue_len() == 0 { - break; - } - let _ = iface.poll(timestamp, &mut *dev, &mut *sockets); - } - } - - // Drain UDP datagrams from shared per-port sockets and deliver them to per-fd queues. - // - // This must happen as part of the global poll path; otherwise, programs that wait in - // select()/poll() (e.g. iperf3 UDP server) never observe readability because our - // SocketFile::readable() checks the per-fd queue, not the smoltcp socket buffer. - // - // Lock order: SocketSet -> UDP_PORTS (see udp_attach_fd_to_port). - let delivered_udp = udp_dispatch_drain_locked(&mut *sockets); - - // Reap TCP sockets that have finished a graceful close. - let mut pending = PENDING_TCP_CLOSE.lock(); - pending.retain(|h| { - let state = sockets.get::(*h).state(); - if matches!(state, tcp::State::Closed | tcp::State::TimeWait) { - crate::pr_debug!( - "[Socket] reap: removing closed tcp handle={:?}, state={:?}", - h, - state - ); - sockets.remove(*h); - false - } else { - true - } - }); - - let changed = result != smoltcp::iface::PollResult::None || delivered_udp; - if changed { - crate::kernel::syscall::io::wake_poll_waiters(); - } - changed - } - - pub fn loopback_queue_len(&self) -> usize { - self.device.lock().loopback_queue_len() - } - - pub fn with_context(&self, f: F) -> R - where - F: FnOnce(&mut smoltcp::iface::Context) -> R, - { - let mut iface = self.interface.lock(); - f(iface.context()) - } -} - lazy_static! { - pub static ref FD_SOCKET_MAP: SpinLock> = SpinLock::new(BTreeMap::new()); - // UDP port dispatcher: one smoltcp UDP socket per local port, plus multiple "logical" sockets - // (per fd) that receive datagrams based on their connected remote endpoint. - static ref UDP_PORTS: SpinLock> = SpinLock::new(BTreeMap::new()); - // TCP sockets that initiated a graceful close on Drop, and should be removed - // from SocketSet once the close handshake completes. - // - // Lock order invariant: SocketSet -> PENDING_TCP_CLOSE (matches Drop path). - static ref PENDING_TCP_CLOSE: SpinLock> = - SpinLock::new(alloc::vec::Vec::new()); + pub static ref FD_SOCKET_MAP: SpinLock> = + SpinLock::new(BTreeMap::new()); } -use crate::net::stack::{NET_IFACE, SOCKET_SET}; - use crate::uapi::fcntl::OpenFlags; use crate::uapi::socket::SocketOptions; const UDP_RXQ_CAP: usize = 64; -const UDP_DGRAM_MAX: usize = 2048; +pub(crate) const UDP_DGRAM_MAX: usize = 2048; #[derive(Debug)] -struct UdpDatagram { - src: IpEndpoint, - len: usize, - data: [u8; UDP_DGRAM_MAX], -} - -#[derive(Debug)] -struct UdpPortEntry { - handle: SmoltcpHandle, - sockets: alloc::vec::Vec>, +pub(crate) struct UdpDatagram { + pub(crate) src: IpEndpoint, + pub(crate) len: usize, + pub(crate) data: [u8; UDP_DGRAM_MAX], } pub struct SocketFile { - handle: SpinLock>, - listen_sockets: SpinLock>, + pub(crate) handle: SpinLock>, + pub(crate) listen_sockets: SpinLock>, listen_backlog: SpinLock, local_endpoint: SpinLock>, remote_endpoint: SpinLock>, @@ -147,7 +45,7 @@ pub struct SocketFile { shutdown_wr: SpinLock, flags: SpinLock, options: SpinLock, - is_listener: SpinLock, + pub(crate) is_listener: SpinLock, } impl SocketFile { @@ -232,9 +130,7 @@ impl SocketFile { /// This is used to provide a minimal accept/backlog behavior on top of smoltcp's /// single-socket listen model. pub fn take_established_from_listen_queue(&self) -> Option { - crate::net::stack::network_stack() - .lock() - .take_established_from_listen_queue(self) + crate::net::stack::network_stack().take_established_from_listen_queue(self) } pub fn set_handle(&self, new_handle: SocketHandle) { @@ -257,11 +153,11 @@ impl SocketFile { *self.remote_endpoint.lock() } - fn udp_queue_len(&self) -> usize { + pub(crate) fn udp_queue_len(&self) -> usize { self.udp_rx_queue.lock().len() } - fn udp_push(&self, d: UdpDatagram) -> bool { + pub(crate) fn udp_push(&self, d: UdpDatagram) -> bool { let mut q = self.udp_rx_queue.lock(); if q.len() == q.capacity() { return false; @@ -270,7 +166,7 @@ impl SocketFile { true } - fn udp_pop(&self) -> Option { + pub(crate) fn udp_pop(&self) -> Option { self.udp_rx_queue.lock().pop_front() } @@ -291,17 +187,13 @@ impl SocketFile { } pub fn is_closed(&self) -> bool { - crate::net::stack::network_stack() - .lock() - .socket_is_closed(self) + crate::net::stack::network_stack().socket_is_closed(self) } } impl Drop for SocketFile { fn drop(&mut self) { - crate::net::stack::network_stack() - .lock() - .drop_socket_file(self); + crate::net::stack::network_stack().drop_socket_file(self); } } @@ -401,33 +293,23 @@ pub fn socket_sendto( buf: &[u8], endpoint: IpEndpoint, ) -> Result { - crate::net::stack::network_stack() - .lock() - .socket_sendto(handle, buf, endpoint) + crate::net::stack::network_stack().socket_sendto(handle, buf, endpoint) } impl File for SocketFile { fn readable(&self) -> bool { - crate::net::stack::network_stack() - .lock() - .socket_readable(self) + crate::net::stack::network_stack().socket_readable(self) } fn writable(&self) -> bool { - crate::net::stack::network_stack() - .lock() - .socket_writable(self) + crate::net::stack::network_stack().socket_writable(self) } fn read(&self, buf: &mut [u8]) -> Result { - crate::net::stack::network_stack() - .lock() - .socket_read(self, buf) + crate::net::stack::network_stack().socket_read(self, buf) } fn write(&self, buf: &[u8]) -> Result { - crate::net::stack::network_stack() - .lock() - .socket_write(self, buf) + crate::net::stack::network_stack().socket_write(self, buf) } fn metadata(&self) -> Result { @@ -448,403 +330,10 @@ impl File for SocketFile { } fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, Option>), FsError> { - crate::net::stack::network_stack() - .lock() - .socket_recvfrom(self, buf) - } -} - -pub(crate) fn take_established_from_listen_queue_impl(file: &SocketFile) -> Option { - let sockets = SOCKET_SET.lock(); - let mut q = file.listen_sockets.lock(); - let mut i = 0; - while i < q.len() { - match q[i] { - SocketHandle::Tcp(h) => { - let s = sockets.get::(h); - match s.state() { - tcp::State::Established | tcp::State::CloseWait => { - return Some(q.remove(i)); - } - tcp::State::Closed => { - q.remove(i); - continue; - } - _ => {} - } - } - SocketHandle::Udp(_) => { - q.remove(i); - continue; - } - } - i += 1; - } - None -} - -pub(crate) fn socket_is_closed_impl(file: &SocketFile) -> bool { - let sockets = SOCKET_SET.lock(); - match file.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get::(*h); - socket.state() == tcp::State::Closed - } - _ => false, - } -} - -pub(crate) fn drop_socket_file_impl(file: &SocketFile) { - let mut sockets = SOCKET_SET.lock(); - if let Some(handle) = *file.handle.lock() { - match handle { - SocketHandle::Tcp(h) => { - let socket = sockets.get_mut::(h); - let state = socket.state(); - crate::pr_debug!("[Socket] Drop: handle={:?}, state={:?}", h, state); - // Active connections are closed gracefully and removed by the poll reaper. - match state { - tcp::State::Closed | tcp::State::TimeWait | tcp::State::Listen => { - sockets.remove(h); - } - _ => { - crate::pr_debug!("[Socket] Drop: closing socket handle={:?}", h); - socket.close(); - PENDING_TCP_CLOSE.lock().push(h); - } - } - } - SocketHandle::Udp(h) => { - // UDP sockets may be shared by the per-port dispatcher. - let ports = UDP_PORTS.lock(); - let is_shared = ports.values().any(|e| e.handle == h); - drop(ports); - if !is_shared { - sockets.remove(h); - } - } - } - } - for handle in file.listen_sockets.lock().iter() { - match handle { - SocketHandle::Tcp(h) => { - sockets.remove(*h); - } - SocketHandle::Udp(h) => { - sockets.remove(*h); - } - } - } -} - -pub(crate) fn socket_sendto_impl( - handle: SocketHandle, - buf: &[u8], - endpoint: IpEndpoint, -) -> Result { - let result = { - let mut sockets = SOCKET_SET.lock(); - match handle { - SocketHandle::Tcp(_) => Err(FsError::NotSupported), - SocketHandle::Udp(h) => { - let socket = sockets.get_mut::(h); - socket - .send_slice(buf, endpoint) - .map_err(|_| FsError::WouldBlock)?; - Ok(buf.len()) - } - } - }; - if result.is_ok() { - poll_network_interfaces_impl(); - crate::kernel::syscall::io::wake_poll_waiters(); - } - result -} - -pub(crate) fn socket_readable_impl(file: &SocketFile) -> bool { - // Listener socket: only readable when a connection is ready to accept. - if *file.is_listener.lock() { - let sockets = SOCKET_SET.lock(); - - for handle in file.listen_sockets.lock().iter() { - if let SocketHandle::Tcp(h) = handle { - let s = sockets.get::(*h); - if matches!(s.state(), tcp::State::Established | tcp::State::CloseWait) { - return true; - } - } - } - - if let Some(SocketHandle::Tcp(h)) = *file.handle.lock() { - let s = sockets.get::(h); - return matches!(s.state(), tcp::State::Established | tcp::State::CloseWait); - } - return false; - } - - let sockets = SOCKET_SET.lock(); - match file.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get::(*h); - let can_recv = socket.can_recv(); - let state = socket.state(); - crate::pr_debug!( - "[Socket] readable: handle={:?}, state={:?}, can_recv={}", - h, - state, - can_recv - ); - can_recv || matches!(state, tcp::State::Closed | tcp::State::CloseWait) - } - Some(SocketHandle::Udp(_)) => { - drop(sockets); - file.udp_queue_len() > 0 - } - None => false, + crate::net::stack::network_stack().socket_recvfrom(self, buf) } } -pub(crate) fn socket_writable_impl(file: &SocketFile) -> bool { - let sockets = SOCKET_SET.lock(); - match file.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get::(*h); - let can_send = socket.can_send(); - let state = socket.state(); - crate::pr_debug!( - "[Socket] writable: handle={:?}, state={:?}, can_send={}", - h, - state, - can_send - ); - can_send - } - Some(SocketHandle::Udp(h)) => { - let socket = sockets.get::(*h); - socket.can_send() - } - None => false, - } -} - -pub(crate) fn socket_read_impl(file: &SocketFile, buf: &mut [u8]) -> Result { - if file.is_shutdown_read() { - return Ok(0); - } - - let mut sockets = SOCKET_SET.lock(); - let result = match file.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get_mut::(*h); - let state = socket.state(); - let recv_queue = socket.recv_queue(); - crate::pr_debug!( - "[Socket] read: handle={:?}, state={:?}, recv_queue={}, buf.len()={}", - h, - state, - recv_queue, - buf.len() - ); - - if socket.state() == tcp::State::Closed { - return Ok(0); - } - - if state == tcp::State::CloseWait && recv_queue == 0 { - return Ok(0); - } - - let result = socket.recv_slice(buf).map_err(|_| FsError::WouldBlock); - - if let Ok(0) = result { - if state == tcp::State::CloseWait { - crate::pr_debug!( - "[Socket] read: recv_slice returned 0 and state=CloseWait, returning EOF" - ); - Ok(0) - } else if socket.may_recv() { - crate::pr_debug!( - "[Socket] read: recv_slice returned 0 but may_recv=true, returning EAGAIN" - ); - Err(FsError::WouldBlock) - } else { - crate::pr_debug!( - "[Socket] read: recv_slice returned 0 and may_recv=false, returning EOF" - ); - Ok(0) - } - } else { - if let Ok(n) = result { - crate::pr_debug!("[Socket] read: received {} bytes", n); - } - result - } - } - Some(SocketHandle::Udp(_)) => { - drop(sockets); - let Some(d) = file.udp_pop() else { - return Err(FsError::WouldBlock); - }; - let n = core::cmp::min(buf.len(), d.len); - buf[..n].copy_from_slice(&d.data[..n]); - Ok(n) - } - None => Err(FsError::InvalidArgument), - }; - if result.is_ok() { - crate::kernel::syscall::io::wake_poll_waiters(); - } - result -} - -pub(crate) fn socket_write_impl(file: &SocketFile, buf: &[u8]) -> Result { - if file.is_shutdown_write() { - return Err(FsError::BrokenPipe); - } - - let result = { - let mut sockets = SOCKET_SET.lock(); - match file.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get_mut::(*h); - let result = socket.send_slice(buf).map_err(|_| FsError::WouldBlock); - - if !buf.is_empty() { - if let Ok(0) = result { - if socket.may_send() { - return Err(FsError::WouldBlock); - } else { - return Err(FsError::BrokenPipe); - } - } - } - - result - } - Some(SocketHandle::Udp(h)) => { - let endpoint = match file.get_remote_endpoint() { - Some(ep) => ep, - None => return Err(FsError::NotConnected), - }; - let socket = sockets.get_mut::(*h); - socket - .send_slice(buf, endpoint) - .map_err(|_| FsError::WouldBlock)?; - Ok(buf.len()) - } - None => Err(FsError::InvalidArgument), - } - }; - if result.is_ok() { - poll_network_interfaces_impl(); - crate::kernel::syscall::io::wake_poll_waiters(); - } - result -} - -pub(crate) fn socket_recvfrom_impl( - file: &SocketFile, - buf: &mut [u8], -) -> Result<(usize, Option>), FsError> { - if file.is_shutdown_read() { - return Ok((0, None)); - } - - let mut sockets = SOCKET_SET.lock(); - match file.handle.lock().as_ref() { - Some(SocketHandle::Tcp(h)) => { - let socket = sockets.get_mut::(*h); - let state = socket.state(); - if state == tcp::State::Closed { - return Ok((0, None)); - } - if state == tcp::State::CloseWait && socket.recv_queue() == 0 { - return Ok((0, None)); - } - - let result = socket.recv_slice(buf).map_err(|_| FsError::WouldBlock); - let n = if let Ok(0) = result { - if state == tcp::State::CloseWait { - 0 - } else if socket.may_recv() { - return Err(FsError::WouldBlock); - } else { - 0 - } - } else { - result? - }; - let remote = socket.remote_endpoint().map(|ep| { - let mut addr_buf = alloc::vec![0u8; 16]; - let _ = write_sockaddr_in_to_buf(&mut addr_buf, ep); - addr_buf - }); - Ok((n, remote)) - } - Some(SocketHandle::Udp(_)) => { - drop(sockets); - let Some(d) = file.udp_pop() else { - return Err(FsError::WouldBlock); - }; - let n = core::cmp::min(buf.len(), d.len); - buf[..n].copy_from_slice(&d.data[..n]); - let mut addr_buf = alloc::vec![0u8; 16]; - let _ = write_sockaddr_in_to_buf(&mut addr_buf, d.src); - Ok((n, Some(addr_buf))) - } - None => Err(FsError::InvalidArgument), - } -} - -fn create_udp_socket_in_set(sockets: &mut SocketSet<'static>) -> Result { - // Allocate metadata buffers - let mut rx_meta_vec = alloc::vec::Vec::new(); - rx_meta_vec.try_reserve(4).map_err(|_| ())?; - rx_meta_vec.resize(4, udp::PacketMetadata::EMPTY); - - let mut tx_meta_vec = alloc::vec::Vec::new(); - tx_meta_vec.try_reserve(4).map_err(|_| ())?; - tx_meta_vec.resize(4, udp::PacketMetadata::EMPTY); - - // Allocate data buffers - let mut rx_data_vec = alloc::vec::Vec::new(); - rx_data_vec.try_reserve(4096).map_err(|_| ())?; - rx_data_vec.resize(4096, 0); - - let mut tx_data_vec = alloc::vec::Vec::new(); - tx_data_vec.try_reserve(4096).map_err(|_| ())?; - tx_data_vec.resize(4096, 0); - - let rx_buffer = udp::PacketBuffer::new(rx_meta_vec, rx_data_vec); - let tx_buffer = udp::PacketBuffer::new(tx_meta_vec, tx_data_vec); - let socket = udp::Socket::new(rx_buffer, tx_buffer); - Ok(sockets.add(socket)) -} - -pub(crate) fn create_tcp_socket_impl() -> Result { - let mut rx_vec = alloc::vec::Vec::new(); - rx_vec.try_reserve(4096).map_err(|_| ())?; - rx_vec.resize(4096, 0); - - let mut tx_vec = alloc::vec::Vec::new(); - tx_vec.try_reserve(4096).map_err(|_| ())?; - tx_vec.resize(4096, 0); - - let rx_buffer = tcp::SocketBuffer::new(rx_vec); - let tx_buffer = tcp::SocketBuffer::new(tx_vec); - let socket = tcp::Socket::new(rx_buffer, tx_buffer); - - let handle = SOCKET_SET.lock().add(socket); - Ok(SocketHandle::Tcp(handle)) -} - -pub(crate) fn create_udp_socket_impl() -> Result { - let mut sockets = SOCKET_SET.lock(); - let handle = create_udp_socket_in_set(&mut *sockets)?; - Ok(SocketHandle::Udp(handle)) -} - const AF_INET: u16 = 2; const SOCKADDR_IN_SIZE: usize = 16; @@ -872,7 +361,7 @@ pub fn parse_sockaddr_in(addr: *const u8, addrlen: u32) -> Result Result<(), ()> { +pub(crate) fn write_sockaddr_in_to_buf(buf: &mut [u8], endpoint: IpEndpoint) -> Result<(), ()> { if buf.len() < SOCKADDR_IN_SIZE { return Err(()); } @@ -928,398 +417,36 @@ pub fn write_sockaddr_in(addr: *mut u8, addrlen: *mut u32, endpoint: IpEndpoint) Ok(()) } -/// Initialize network interface (should be called during network setup) -pub(crate) fn init_network_impl(mut smoltcp_iface: crate::net::interface::SmoltcpInterface) { - let wrapper = NetIfaceWrapper { - device: SpinLock::new(smoltcp_iface.device_adapter_mut().clone()), - interface: SpinLock::new(smoltcp_iface.into_interface()), - }; - *NET_IFACE.lock() = Some(wrapper); -} - -/// Perform TCP connect with Context -pub(crate) fn tcp_connect_impl( - handle: SmoltcpHandle, - remote: IpEndpoint, - local: IpEndpoint, -) -> Result<(), ()> { - crate::pr_debug!("tcp_connect: start, handle={:?}", handle); - - let iface_guard = NET_IFACE.lock(); - crate::pr_debug!("tcp_connect: got NET_IFACE lock"); - - let wrapper = iface_guard.as_ref().ok_or(())?; - - let result = wrapper.with_context(|context| { - crate::pr_debug!("tcp_connect: in with_context"); - let mut sockets = SOCKET_SET.lock(); - crate::pr_debug!("tcp_connect: got SOCKET_SET lock"); - let socket = sockets.get_mut::(handle); - crate::pr_debug!("tcp_connect: calling socket.connect"); - let r = socket.connect(context, remote, local).map_err(|e| { - crate::pr_debug!("tcp_connect error: {:?}", e); - () - }); - crate::pr_debug!("tcp_connect: socket.connect returned {:?}", r); - r - }); - - // Poll immediately after connect to trigger SYN packet - if result.is_ok() { - crate::pr_debug!("tcp_connect: polling to send SYN"); - wrapper.poll(&SOCKET_SET); - } - - drop(iface_guard); - crate::pr_debug!("tcp_connect: done, result={:?}", result); - result -} - -/// Poll network interfaces to process packets -pub(crate) fn poll_network_interfaces_impl() { - if let Some(ref wrapper) = *NET_IFACE.lock() { - crate::pr_debug!("poll_network_interfaces: calling poll"); - wrapper.poll(&SOCKET_SET); - } -} - -/// Poll smoltcp + dispatch UDP datagrams to per-fd queues. -/// -/// IMPORTANT: this may allocate (copies UDP payloads) and therefore must not be called from -/// interrupt context. -pub(crate) fn poll_network_and_dispatch_impl() { - poll_network_interfaces_impl(); - if udp_dispatch_impl() { - crate::kernel::syscall::io::wake_poll_waiters(); - } -} - -/// Drain UDP datagrams from shared per-port sockets and deliver them to per-fd queues. -/// -/// Returns whether any datagram was delivered. -pub(crate) fn udp_dispatch_impl() -> bool { - let mut sockets = SOCKET_SET.lock(); - udp_dispatch_drain_locked(&mut *sockets) -} - -/// Attach an existing UDP fd to the shared per-port UDP socket, and register it as a logical socket -/// for datagram dispatching. -/// -/// Lock order: SOCKET_SET -> UDP_PORTS (must match NetIfaceWrapper::poll path). -pub(crate) fn udp_attach_fd_to_port_impl( - tid: usize, - fd: usize, - file: &Arc, - old_handle: SmoltcpHandle, - port: u16, - bind_addr: Option, -) -> Result { - // 1) Ensure shared per-port smoltcp socket exists and is bound. - let shared_handle = { - let mut sockets = SOCKET_SET.lock(); - let mut ports = UDP_PORTS.lock(); - if let Some(e) = ports.get(&port) { - e.handle - } else { - let h = create_udp_socket_in_set(&mut *sockets)?; - use smoltcp::wire::IpListenEndpoint; - let listen = IpListenEndpoint { - addr: bind_addr, - port, - }; - if sockets.get_mut::(h).bind(listen).is_err() { - sockets.remove(h); - return Err(()); - } - ports.insert( - port, - UdpPortEntry { - handle: h, - sockets: alloc::vec::Vec::new(), - }, - ); - h - } - }; - - // 2) Atomically switch fd mapping to the shared handle first (avoid stale-handle panics). - update_socket_handle(tid, fd, SocketHandle::Udp(shared_handle)); - if let Some(sf) = file.as_any().downcast_ref::() { - sf.set_handle(SocketHandle::Udp(shared_handle)); - } - - // 3) Register this logical socket for dispatch. - { - let mut ports = UDP_PORTS.lock(); - if let Some(e) = ports.get_mut(&port) { - let already = e - .sockets - .iter() - .filter_map(|w| w.upgrade()) - .any(|f| alloc::sync::Arc::ptr_eq(&f, file)); - if !already { - e.sockets.push(alloc::sync::Arc::downgrade(file)); - } - } - } - - // 4) Finally remove the old per-fd smoltcp socket handle (it is unbound and should not receive). - if old_handle != shared_handle { - // Never remove a handle that is currently used as a shared per-port socket. - // (This can happen if user space calls bind/connect in an unexpected order.) - let mut sockets = SOCKET_SET.lock(); - let ports = UDP_PORTS.lock(); - let old_is_shared = ports.values().any(|e| e.handle == old_handle); - drop(ports); - if !old_is_shared { - sockets.remove(old_handle); - } - } - - Ok(shared_handle) -} - -/// Drain UDP datagrams from shared per-port sockets and deliver them to per-fd queues. -/// -/// This function must be called with `SOCKET_SET` already locked. -fn udp_dispatch_drain_locked(sockets: &mut SocketSet<'static>) -> bool { - let mut delivered_any = false; - let mut ports = UDP_PORTS.lock(); - let mut to_remove: alloc::vec::Vec<(u16, SmoltcpHandle)> = alloc::vec::Vec::new(); - - for (port, entry) in ports.iter_mut() { - let socket = sockets.get_mut::(entry.handle); - - while socket.can_recv() { - let (payload, meta) = match socket.recv() { - Ok(v) => v, - Err(_) => break, - }; - - let src = meta.endpoint; - let mut data = [0u8; UDP_DGRAM_MAX]; - let copy_len = core::cmp::min(payload.len(), UDP_DGRAM_MAX); - data[..copy_len].copy_from_slice(&payload[..copy_len]); - let d = UdpDatagram { - src, - len: copy_len, - data, - }; - - // Prefer a connected socket that matches the remote endpoint. Otherwise, deliver to the - // first unconnected socket registered for this port. - let mut target: Option> = None; - let mut fallback: Option> = None; - - entry.sockets.retain(|w| w.strong_count() > 0); - - for w in entry.sockets.iter() { - let Some(f) = w.upgrade() else { continue }; - let Some(sf) = f.as_any().downcast_ref::() else { - continue; - }; - - let Some(local_ep) = sf.get_local_endpoint() else { - continue; - }; - if local_ep.port != *port { - continue; - } - - match sf.get_remote_endpoint() { - Some(remote) => { - if remote.addr == src.addr && remote.port == src.port { - target = Some(f.clone()); - break; - } - } - None => { - if fallback.is_none() { - fallback = Some(f.clone()); - } - } - } - } - - let target = target.or(fallback); - if let Some(f) = target { - if let Some(sf) = f.as_any().downcast_ref::() { - if sf.udp_push(d) { - delivered_any = true; - } - } - } - } - - // Prune dead logical sockets; if none remain, remove the shared smoltcp socket. - entry.sockets.retain(|w| w.strong_count() > 0); - if entry.sockets.is_empty() { - to_remove.push((*port, entry.handle)); - } - } - - for (port, handle) in to_remove { - ports.remove(&port); - sockets.remove(handle); - } - - delivered_any -} - -/// Poll until loopback queue is empty -pub(crate) fn poll_until_empty_impl() { - if let Some(ref wrapper) = *NET_IFACE.lock() { - // Always poll at least once to process socket state changes - wrapper.poll(&SOCKET_SET); - - // Then drain loopback queue, but do it in bounded steps. - // - // Draining until empty can take unbounded time when user programs (e.g. iperf3 UDP) - // generate packets faster than the stack can consume them, causing apparent "hangs". - const MAX_DRAIN_POLLS: usize = 256; - for _ in 0..MAX_DRAIN_POLLS { - if wrapper.loopback_queue_len() == 0 { - break; - } - wrapper.poll(&SOCKET_SET); - } - } -} - pub fn create_tcp_socket() -> Result { - crate::net::stack::network_stack() - .lock() - .create_tcp_socket() + crate::net::stack::network_stack().create_tcp_socket() } pub fn create_udp_socket() -> Result { - crate::net::stack::network_stack() - .lock() - .create_udp_socket() + crate::net::stack::network_stack().create_udp_socket() } /// Initialize network interface through the stack facade. pub fn init_network(smoltcp_iface: crate::net::interface::SmoltcpInterface) { - crate::net::stack::network_stack() - .lock() - .init_network(smoltcp_iface); + crate::net::stack::network_stack().init_network(smoltcp_iface); } pub fn tcp_connect(handle: SmoltcpHandle, remote: IpEndpoint, local: IpEndpoint) -> Result<(), ()> { - crate::net::stack::network_stack() - .lock() - .tcp_connect(handle, remote, local) -} - -pub fn tcp_connection_state_impl( - handle: SmoltcpHandle, -) -> Option { - let sockets = SOCKET_SET.lock(); - let socket = sockets.get::(handle); - Some(match socket.state() { - tcp::State::Established => crate::net::stack::TcpConnectionState::Established, - tcp::State::Closed => crate::net::stack::TcpConnectionState::Closed, - _ => crate::net::stack::TcpConnectionState::Other, - }) -} - -pub(crate) fn tcp_listen_impl( - handle: SmoltcpHandle, - endpoint: smoltcp::wire::IpListenEndpoint, -) -> Result<(), ()> { - let mut sockets = SOCKET_SET.lock(); - sockets - .get_mut::(handle) - .listen(endpoint) - .map_err(|_| ()) -} - -pub(crate) fn tcp_listener_state_endpoint_impl( - handle: SmoltcpHandle, -) -> Option<( - crate::net::stack::TcpListenState, - smoltcp::wire::IpListenEndpoint, -)> { - let sockets = SOCKET_SET.lock(); - let socket = sockets.get::(handle); - let state = match socket.state() { - tcp::State::Listen => crate::net::stack::TcpListenState::Listen, - tcp::State::Established => crate::net::stack::TcpListenState::Established, - tcp::State::CloseWait => crate::net::stack::TcpListenState::CloseWait, - _ => crate::net::stack::TcpListenState::Other, - }; - Some((state, socket.listen_endpoint())) -} - -pub(crate) fn remove_tcp_socket_impl(handle: SmoltcpHandle) { - SOCKET_SET.lock().remove(handle); -} - -pub(crate) fn tcp_accept_endpoints_impl( - handle: SmoltcpHandle, -) -> Option<(IpEndpoint, Option)> { - let sockets = SOCKET_SET.lock(); - let socket = sockets.get::(handle); - let remote = socket.remote_endpoint()?; - Some((remote, socket.local_endpoint())) -} - -pub(crate) fn tcp_debug_state_impl( - handle: SmoltcpHandle, -) -> Option<(crate::net::stack::TcpConnectionState, bool)> { - let sockets = SOCKET_SET.lock(); - let socket = sockets.get::(handle); - let state = match socket.state() { - tcp::State::Established => crate::net::stack::TcpConnectionState::Established, - tcp::State::Closed => crate::net::stack::TcpConnectionState::Closed, - _ => crate::net::stack::TcpConnectionState::Other, - }; - Some((state, socket.is_open())) -} - -pub(crate) fn tcp_close_impl(handle: SmoltcpHandle) { - SOCKET_SET.lock().get_mut::(handle).close(); -} - -pub(crate) fn socket_local_endpoint_impl(handle: SocketHandle) -> Option { - let sockets = SOCKET_SET.lock(); - match handle { - SocketHandle::Tcp(h) => sockets.get::(h).local_endpoint(), - SocketHandle::Udp(h) => { - let listen_ep = sockets.get::(h).endpoint(); - Some(IpEndpoint::new( - listen_ep - .addr - .unwrap_or(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED)), - listen_ep.port, - )) - } - } -} - -pub(crate) fn socket_remote_endpoint_impl(handle: SocketHandle) -> Option { - let sockets = SOCKET_SET.lock(); - match handle { - SocketHandle::Tcp(h) => sockets.get::(h).remote_endpoint(), - SocketHandle::Udp(_) => None, - } + crate::net::stack::network_stack().tcp_connect(handle, remote, local) } /// Poll network interfaces to process packets. pub fn poll_network_interfaces() { - crate::net::stack::network_stack().lock().poll(); + crate::net::stack::network_stack().poll(); } /// Poll smoltcp + dispatch UDP datagrams to per-fd queues. pub fn poll_network_and_dispatch() { - crate::net::stack::network_stack() - .lock() - .poll_and_dispatch(); + crate::net::stack::network_stack().poll_and_dispatch(); } /// Drain UDP datagrams from shared per-port sockets and deliver them to per-fd queues. pub fn udp_dispatch() -> bool { - crate::net::stack::network_stack().lock().udp_dispatch() + crate::net::stack::network_stack().udp_dispatch() } pub fn udp_attach_fd_to_port( @@ -1331,11 +458,10 @@ pub fn udp_attach_fd_to_port( bind_addr: Option, ) -> Result { crate::net::stack::network_stack() - .lock() .udp_attach_fd_to_port(tid, fd, file, old_handle, port, bind_addr) } /// Poll until loopback queue is empty. pub fn poll_until_empty() { - crate::net::stack::network_stack().lock().poll_until_empty(); + crate::net::stack::network_stack().poll_until_empty(); } diff --git a/os/src/net/stack.rs b/os/src/net/stack.rs index 3bc2a73a..9c8e5232 100644 --- a/os/src/net/stack.rs +++ b/os/src/net/stack.rs @@ -1,43 +1,85 @@ //! Network stack runtime facade. //! -//! This module is the migration host for protocol-stack state. The first -//! refactor step keeps the existing implementation in `socket.rs`, while new -//! call sites start depending on this facade instead of reaching for raw -//! globals such as `SOCKET_SET` or `NET_IFACE`. +//! This module owns the protocol-stack runtime state and operations. use crate::sync::SpinLock; -use alloc::collections::VecDeque; +use alloc::collections::{BTreeMap, VecDeque}; use alloc::sync::Arc; use alloc::vec::Vec; use lazy_static::lazy_static; use smoltcp::iface::{Interface, SocketHandle as SmoltcpHandle, SocketSet}; +use smoltcp::socket::{tcp, udp}; use smoltcp::time::Instant; -use smoltcp::wire::{EthernetAddress, IpEndpoint, IpListenEndpoint}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpEndpoint, IpListenEndpoint, Ipv4Address}; -use super::socket::{NetIfaceWrapper, SocketHandle}; +use super::socket::{self, SocketFile, SocketHandle, UdpDatagram}; -lazy_static! { - /// Stack-owned smoltcp socket set. - pub(crate) static ref SOCKET_SET: SpinLock> = - SpinLock::new(SocketSet::new(alloc::vec![])); +pub(crate) fn enqueue_loopback_frame(frame: Vec) { + network_stack().enqueue_loopback_frame(frame); +} - /// Stack-owned active interface runtime. - pub(crate) static ref NET_IFACE: SpinLock> = SpinLock::new(None); +fn dequeue_loopback_frame() -> Option> { + network_stack().dequeue_loopback_frame() +} - /// Runtime loopback link used while the stack is still single-interface. - static ref LOOPBACK_LINK: SpinLock>> = SpinLock::new(VecDeque::new()); +pub(crate) fn loopback_link_len() -> usize { + network_stack().loopback_link_len() } -pub(crate) fn enqueue_loopback_frame(frame: Vec) { - LOOPBACK_LINK.lock().push_back(frame); +/// Active smoltcp interface runtime. +pub struct NetIfaceWrapper { + device: SpinLock, + interface: SpinLock, } -fn dequeue_loopback_frame() -> Option> { - LOOPBACK_LINK.lock().pop_front() +impl NetIfaceWrapper { + fn poll_smoltcp(&self, sockets: &SpinLock>) -> bool { + let timestamp = + smoltcp::time::Instant::from_millis(crate::arch::timer::get_time_ms() as i64); + let mut dev = self.device.lock(); + + let queue_len = dev.loopback_queue_len(); + if queue_len > 0 { + crate::pr_debug!("poll: loopback queue has {} packets", queue_len); + } + + let mut iface = self.interface.lock(); + let mut sockets = sockets.lock(); + + crate::pr_debug!("poll: before iface.poll"); + let result = iface.poll(timestamp, &mut *dev, &mut *sockets); + crate::pr_debug!("poll: result={:?}", result); + + // Frames produced by loopback Tx are visible to Rx on a later poll. + if dev.loopback_queue_len() > 0 { + const MAX_EXTRA_POLLS: usize = 2; + for _ in 0..MAX_EXTRA_POLLS { + if dev.loopback_queue_len() == 0 { + break; + } + let _ = iface.poll(timestamp, &mut *dev, &mut *sockets); + } + } + + result != smoltcp::iface::PollResult::None + } + + fn loopback_queue_len(&self) -> usize { + self.device.lock().loopback_queue_len() + } + + fn with_context(&self, f: F) -> R + where + F: FnOnce(&mut smoltcp::iface::Context) -> R, + { + let mut iface = self.interface.lock(); + f(iface.context()) + } } -pub(crate) fn loopback_link_len() -> usize { - LOOPBACK_LINK.lock().len() +struct UdpPortEntry { + handle: SmoltcpHandle, + sockets: alloc::vec::Vec>, } /// Smoltcp interface wrapper that owns the adapter borrowed by `Interface`. @@ -251,27 +293,70 @@ pub enum TcpListenState { Other, } -/// Stable facade for network protocol-stack operations. -pub struct NetworkStack; +/// Stateful network protocol-stack runtime. +pub struct NetworkStack { + socket_set: SpinLock>, + net_iface: SpinLock>, + loopback_link: SpinLock>>, + udp_ports: SpinLock>, + pending_tcp_close: SpinLock>, +} impl NetworkStack { - const fn new() -> Self { - Self + fn new() -> Self { + Self { + socket_set: SpinLock::new(SocketSet::new(alloc::vec![])), + net_iface: SpinLock::new(None), + loopback_link: SpinLock::new(VecDeque::new()), + udp_ports: SpinLock::new(BTreeMap::new()), + pending_tcp_close: SpinLock::new(alloc::vec::Vec::new()), + } + } + + fn enqueue_loopback_frame(&self, frame: Vec) { + self.loopback_link.lock().push_back(frame); + } + + fn dequeue_loopback_frame(&self) -> Option> { + self.loopback_link.lock().pop_front() + } + + fn loopback_link_len(&self) -> usize { + self.loopback_link.lock().len() } /// Initialize the active smoltcp interface runtime. - pub fn init_network(&self, smoltcp_iface: crate::net::interface::SmoltcpInterface) { - super::socket::init_network_impl(smoltcp_iface); + pub fn init_network(&self, mut smoltcp_iface: crate::net::interface::SmoltcpInterface) { + let wrapper = NetIfaceWrapper { + device: SpinLock::new(smoltcp_iface.device_adapter_mut().clone()), + interface: SpinLock::new(smoltcp_iface.into_interface()), + }; + *self.net_iface.lock() = Some(wrapper); } /// Create a TCP socket in the stack runtime. pub fn create_tcp_socket(&self) -> Result { - super::socket::create_tcp_socket_impl() + let mut rx_vec = alloc::vec::Vec::new(); + rx_vec.try_reserve(4096).map_err(|_| ())?; + rx_vec.resize(4096, 0); + + let mut tx_vec = alloc::vec::Vec::new(); + tx_vec.try_reserve(4096).map_err(|_| ())?; + tx_vec.resize(4096, 0); + + let rx_buffer = tcp::SocketBuffer::new(rx_vec); + let tx_buffer = tcp::SocketBuffer::new(tx_vec); + let socket = tcp::Socket::new(rx_buffer, tx_buffer); + + let handle = self.socket_set.lock().add(socket); + Ok(SocketHandle::Tcp(handle)) } /// Create a UDP socket in the stack runtime. pub fn create_udp_socket(&self) -> Result { - super::socket::create_udp_socket_impl() + let mut sockets = self.socket_set.lock(); + let handle = self.create_udp_socket_in_set(&mut *sockets)?; + Ok(SocketHandle::Udp(handle)) } /// Connect a TCP socket using the active interface context. @@ -281,17 +366,54 @@ impl NetworkStack { remote: IpEndpoint, local: IpEndpoint, ) -> Result<(), ()> { - super::socket::tcp_connect_impl(handle, remote, local) + crate::pr_debug!("tcp_connect: start, handle={:?}", handle); + + let iface_guard = self.net_iface.lock(); + crate::pr_debug!("tcp_connect: got net_iface lock"); + let wrapper = iface_guard.as_ref().ok_or(())?; + + let result = wrapper.with_context(|context| { + crate::pr_debug!("tcp_connect: in with_context"); + let mut sockets = self.socket_set.lock(); + crate::pr_debug!("tcp_connect: got socket_set lock"); + let socket = sockets.get_mut::(handle); + crate::pr_debug!("tcp_connect: calling socket.connect"); + let r = socket.connect(context, remote, local).map_err(|e| { + crate::pr_debug!("tcp_connect error: {:?}", e); + () + }); + crate::pr_debug!("tcp_connect: socket.connect returned {:?}", r); + r + }); + + if result.is_ok() { + crate::pr_debug!("tcp_connect: polling to send SYN"); + wrapper.poll_smoltcp(&self.socket_set); + } + + drop(iface_guard); + crate::pr_debug!("tcp_connect: done, result={:?}", result); + result } - /// Query the coarse TCP state without exposing `SOCKET_SET` to callers. + /// Query the coarse TCP state without exposing the smoltcp socket set to callers. pub fn tcp_connection_state(&self, handle: SmoltcpHandle) -> Option { - super::socket::tcp_connection_state_impl(handle) + let sockets = self.socket_set.lock(); + let socket = sockets.get::(handle); + Some(match socket.state() { + tcp::State::Established => TcpConnectionState::Established, + tcp::State::Closed => TcpConnectionState::Closed, + _ => TcpConnectionState::Other, + }) } /// Start listening on a TCP socket. pub fn tcp_listen(&self, handle: SmoltcpHandle, endpoint: IpListenEndpoint) -> Result<(), ()> { - super::socket::tcp_listen_impl(handle, endpoint) + let mut sockets = self.socket_set.lock(); + sockets + .get_mut::(handle) + .listen(endpoint) + .map_err(|_| ()) } /// Query listener state and endpoint without exposing `SocketSet`. @@ -299,12 +421,20 @@ impl NetworkStack { &self, handle: SmoltcpHandle, ) -> Option<(TcpListenState, IpListenEndpoint)> { - super::socket::tcp_listener_state_endpoint_impl(handle) + let sockets = self.socket_set.lock(); + let socket = sockets.get::(handle); + let state = match socket.state() { + tcp::State::Listen => TcpListenState::Listen, + tcp::State::Established => TcpListenState::Established, + tcp::State::CloseWait => TcpListenState::CloseWait, + _ => TcpListenState::Other, + }; + Some((state, socket.listen_endpoint())) } /// Remove a TCP socket handle from the runtime. pub fn remove_tcp_socket(&self, handle: SmoltcpHandle) { - super::socket::remove_tcp_socket_impl(handle); + self.socket_set.lock().remove(handle); } /// Return remote and local endpoints for an accepted TCP socket. @@ -312,42 +442,83 @@ impl NetworkStack { &self, handle: SmoltcpHandle, ) -> Option<(IpEndpoint, Option)> { - super::socket::tcp_accept_endpoints_impl(handle) + let sockets = self.socket_set.lock(); + let socket = sockets.get::(handle); + let remote = socket.remote_endpoint()?; + Some((remote, socket.local_endpoint())) } /// Return lightweight TCP debug state for syscall logging. pub fn tcp_debug_state(&self, handle: SmoltcpHandle) -> Option<(TcpConnectionState, bool)> { - super::socket::tcp_debug_state_impl(handle) + let sockets = self.socket_set.lock(); + let socket = sockets.get::(handle); + let state = match socket.state() { + tcp::State::Established => TcpConnectionState::Established, + tcp::State::Closed => TcpConnectionState::Closed, + _ => TcpConnectionState::Other, + }; + Some((state, socket.is_open())) } /// Close a TCP socket. pub fn tcp_close(&self, handle: SmoltcpHandle) { - super::socket::tcp_close_impl(handle); + self.socket_set + .lock() + .get_mut::(handle) + .close(); } /// Query a socket's local endpoint. pub fn socket_local_endpoint(&self, handle: SocketHandle) -> Option { - super::socket::socket_local_endpoint_impl(handle) + let sockets = self.socket_set.lock(); + match handle { + SocketHandle::Tcp(h) => sockets.get::(h).local_endpoint(), + SocketHandle::Udp(h) => { + let listen_ep = sockets.get::(h).endpoint(); + Some(IpEndpoint::new( + listen_ep + .addr + .unwrap_or(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED)), + listen_ep.port, + )) + } + } } /// Query a socket's remote endpoint. pub fn socket_remote_endpoint(&self, handle: SocketHandle) -> Option { - super::socket::socket_remote_endpoint_impl(handle) + let sockets = self.socket_set.lock(); + match handle { + SocketHandle::Tcp(h) => sockets.get::(h).remote_endpoint(), + SocketHandle::Udp(_) => None, + } } /// Poll the network stack once. pub fn poll(&self) { - super::socket::poll_network_interfaces_impl(); + if let Some(ref wrapper) = *self.net_iface.lock() { + crate::pr_debug!("poll_network_interfaces: calling poll"); + let smoltcp_changed = wrapper.poll_smoltcp(&self.socket_set); + let udp_changed = { + let mut sockets = self.socket_set.lock(); + self.udp_dispatch_drain_locked(&mut *sockets) + }; + self.reap_pending_tcp_close(); + if smoltcp_changed || udp_changed { + crate::kernel::syscall::io::wake_poll_waiters(); + } + } } /// Poll smoltcp and dispatch queued datagrams. pub fn poll_and_dispatch(&self) { - super::socket::poll_network_and_dispatch_impl(); + self.poll(); } /// Drain UDP datagrams from shared per-port sockets. pub fn udp_dispatch(&self) -> bool { - super::socket::udp_dispatch_impl() + let mut sockets = self.socket_set.lock(); + self.udp_dispatch_drain_locked(&mut *sockets) } /// Attach a UDP fd to the shared per-port socket. @@ -360,12 +531,84 @@ impl NetworkStack { port: u16, bind_addr: Option, ) -> Result { - super::socket::udp_attach_fd_to_port_impl(tid, fd, file, old_handle, port, bind_addr) + let shared_handle = { + let mut sockets = self.socket_set.lock(); + let mut ports = self.udp_ports.lock(); + if let Some(e) = ports.get(&port) { + e.handle + } else { + let h = self.create_udp_socket_in_set(&mut *sockets)?; + let listen = IpListenEndpoint { + addr: bind_addr, + port, + }; + if sockets.get_mut::(h).bind(listen).is_err() { + sockets.remove(h); + return Err(()); + } + ports.insert( + port, + UdpPortEntry { + handle: h, + sockets: alloc::vec::Vec::new(), + }, + ); + h + } + }; + + socket::update_socket_handle(tid, fd, SocketHandle::Udp(shared_handle)); + if let Some(sf) = file.as_any().downcast_ref::() { + sf.set_handle(SocketHandle::Udp(shared_handle)); + } + + { + let mut ports = self.udp_ports.lock(); + if let Some(e) = ports.get_mut(&port) { + let already = e + .sockets + .iter() + .filter_map(|w| w.upgrade()) + .any(|f| alloc::sync::Arc::ptr_eq(&f, file)); + if !already { + e.sockets.push(alloc::sync::Arc::downgrade(file)); + } + } + } + + if old_handle != shared_handle { + let mut sockets = self.socket_set.lock(); + let ports = self.udp_ports.lock(); + let old_is_shared = ports.values().any(|e| e.handle == old_handle); + drop(ports); + if !old_is_shared { + sockets.remove(old_handle); + } + } + + Ok(shared_handle) } /// Poll until loopback compatibility queues are boundedly drained. pub fn poll_until_empty(&self) { - super::socket::poll_until_empty_impl(); + if let Some(ref wrapper) = *self.net_iface.lock() { + wrapper.poll_smoltcp(&self.socket_set); + { + let mut sockets = self.socket_set.lock(); + self.udp_dispatch_drain_locked(&mut *sockets); + } + self.reap_pending_tcp_close(); + + const MAX_DRAIN_POLLS: usize = 256; + for _ in 0..MAX_DRAIN_POLLS { + if wrapper.loopback_queue_len() == 0 { + break; + } + wrapper.poll_smoltcp(&self.socket_set); + let mut sockets = self.socket_set.lock(); + self.udp_dispatch_drain_locked(&mut *sockets); + } + } } /// Pop an established child socket from a listener's accept queue. @@ -373,17 +616,86 @@ impl NetworkStack { &self, file: &super::socket::SocketFile, ) -> Option { - super::socket::take_established_from_listen_queue_impl(file) + let sockets = self.socket_set.lock(); + let mut q = file.listen_sockets.lock(); + let mut i = 0; + while i < q.len() { + match q[i] { + SocketHandle::Tcp(h) => { + let s = sockets.get::(h); + match s.state() { + tcp::State::Established | tcp::State::CloseWait => { + return Some(q.remove(i)); + } + tcp::State::Closed => { + q.remove(i); + continue; + } + _ => {} + } + } + SocketHandle::Udp(_) => { + q.remove(i); + continue; + } + } + i += 1; + } + None } /// Query whether a socket has reached the closed TCP state. pub fn socket_is_closed(&self, file: &super::socket::SocketFile) -> bool { - super::socket::socket_is_closed_impl(file) + let sockets = self.socket_set.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get::(*h); + socket.state() == tcp::State::Closed + } + _ => false, + } } /// Release all stack resources owned by a SocketFile. pub fn drop_socket_file(&self, file: &super::socket::SocketFile) { - super::socket::drop_socket_file_impl(file); + let mut sockets = self.socket_set.lock(); + if let Some(handle) = *file.handle.lock() { + match handle { + SocketHandle::Tcp(h) => { + let socket = sockets.get_mut::(h); + let state = socket.state(); + crate::pr_debug!("[Socket] Drop: handle={:?}, state={:?}", h, state); + match state { + tcp::State::Closed | tcp::State::TimeWait | tcp::State::Listen => { + sockets.remove(h); + } + _ => { + crate::pr_debug!("[Socket] Drop: closing socket handle={:?}", h); + socket.close(); + self.pending_tcp_close.lock().push(h); + } + } + } + SocketHandle::Udp(h) => { + let ports = self.udp_ports.lock(); + let is_shared = ports.values().any(|e| e.handle == h); + drop(ports); + if !is_shared { + sockets.remove(h); + } + } + } + } + for handle in file.listen_sockets.lock().iter() { + match handle { + SocketHandle::Tcp(h) => { + sockets.remove(*h); + } + SocketHandle::Udp(h) => { + sockets.remove(*h); + } + } + } } /// Send a datagram to a specific endpoint. @@ -393,17 +705,91 @@ impl NetworkStack { buf: &[u8], endpoint: IpEndpoint, ) -> Result { - super::socket::socket_sendto_impl(handle, buf, endpoint) + let result = { + let mut sockets = self.socket_set.lock(); + match handle { + SocketHandle::Tcp(_) => Err(crate::vfs::FsError::NotSupported), + SocketHandle::Udp(h) => { + let socket = sockets.get_mut::(h); + socket + .send_slice(buf, endpoint) + .map_err(|_| crate::vfs::FsError::WouldBlock)?; + Ok(buf.len()) + } + } + }; + if result.is_ok() { + self.poll(); + crate::kernel::syscall::io::wake_poll_waiters(); + } + result } /// Query VFS readability for a socket file. pub fn socket_readable(&self, file: &super::socket::SocketFile) -> bool { - super::socket::socket_readable_impl(file) + if *file.is_listener.lock() { + let sockets = self.socket_set.lock(); + + for handle in file.listen_sockets.lock().iter() { + if let SocketHandle::Tcp(h) = handle { + let s = sockets.get::(*h); + if matches!(s.state(), tcp::State::Established | tcp::State::CloseWait) { + return true; + } + } + } + + if let Some(SocketHandle::Tcp(h)) = *file.handle.lock() { + let s = sockets.get::(h); + return matches!(s.state(), tcp::State::Established | tcp::State::CloseWait); + } + return false; + } + + let sockets = self.socket_set.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get::(*h); + let can_recv = socket.can_recv(); + let state = socket.state(); + crate::pr_debug!( + "[Socket] readable: handle={:?}, state={:?}, can_recv={}", + h, + state, + can_recv + ); + can_recv || matches!(state, tcp::State::Closed | tcp::State::CloseWait) + } + Some(SocketHandle::Udp(_)) => { + drop(sockets); + file.udp_queue_len() > 0 + } + None => false, + } } /// Query VFS writability for a socket file. pub fn socket_writable(&self, file: &super::socket::SocketFile) -> bool { - super::socket::socket_writable_impl(file) + let sockets = self.socket_set.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get::(*h); + let can_send = socket.can_send(); + let state = socket.state(); + crate::pr_debug!( + "[Socket] writable: handle={:?}, state={:?}, can_send={}", + h, + state, + can_send + ); + can_send + } + Some(SocketHandle::Udp(h)) => { + let socket = sockets.get::(*h); + socket.can_send() + } + None => false, + } } /// Read stream data or a queued UDP datagram payload. @@ -412,7 +798,75 @@ impl NetworkStack { file: &super::socket::SocketFile, buf: &mut [u8], ) -> Result { - super::socket::socket_read_impl(file, buf) + if file.is_shutdown_read() { + return Ok(0); + } + + let mut sockets = self.socket_set.lock(); + let result = match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get_mut::(*h); + let state = socket.state(); + let recv_queue = socket.recv_queue(); + crate::pr_debug!( + "[Socket] read: handle={:?}, state={:?}, recv_queue={}, buf.len()={}", + h, + state, + recv_queue, + buf.len() + ); + + if socket.state() == tcp::State::Closed { + return Ok(0); + } + + if state == tcp::State::CloseWait && recv_queue == 0 { + return Ok(0); + } + + let result = socket + .recv_slice(buf) + .map_err(|_| crate::vfs::FsError::WouldBlock); + + if let Ok(0) = result { + if state == tcp::State::CloseWait { + crate::pr_debug!( + "[Socket] read: recv_slice returned 0 and state=CloseWait, returning EOF" + ); + Ok(0) + } else if socket.may_recv() { + crate::pr_debug!( + "[Socket] read: recv_slice returned 0 but may_recv=true, returning EAGAIN" + ); + Err(crate::vfs::FsError::WouldBlock) + } else { + crate::pr_debug!( + "[Socket] read: recv_slice returned 0 and may_recv=false, returning EOF" + ); + Ok(0) + } + } else { + if let Ok(n) = result { + crate::pr_debug!("[Socket] read: received {} bytes", n); + } + result + } + } + Some(SocketHandle::Udp(_)) => { + drop(sockets); + let Some(d) = file.udp_pop() else { + return Err(crate::vfs::FsError::WouldBlock); + }; + let n = core::cmp::min(buf.len(), d.len); + buf[..n].copy_from_slice(&d.data[..n]); + Ok(n) + } + None => Err(crate::vfs::FsError::InvalidArgument), + }; + if result.is_ok() { + crate::kernel::syscall::io::wake_poll_waiters(); + } + result } /// Write stream data or a connected UDP datagram payload. @@ -421,7 +875,50 @@ impl NetworkStack { file: &super::socket::SocketFile, buf: &[u8], ) -> Result { - super::socket::socket_write_impl(file, buf) + if file.is_shutdown_write() { + return Err(crate::vfs::FsError::BrokenPipe); + } + + let result = { + let mut sockets = self.socket_set.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get_mut::(*h); + let result = socket + .send_slice(buf) + .map_err(|_| crate::vfs::FsError::WouldBlock); + + if !buf.is_empty() { + if let Ok(0) = result { + if socket.may_send() { + return Err(crate::vfs::FsError::WouldBlock); + } else { + return Err(crate::vfs::FsError::BrokenPipe); + } + } + } + + result + } + Some(SocketHandle::Udp(h)) => { + let endpoint = match file.get_remote_endpoint() { + Some(ep) => ep, + None => return Err(crate::vfs::FsError::NotConnected), + }; + let socket = sockets.get_mut::(*h); + socket + .send_slice(buf, endpoint) + .map_err(|_| crate::vfs::FsError::WouldBlock)?; + Ok(buf.len()) + } + None => Err(crate::vfs::FsError::InvalidArgument), + } + }; + if result.is_ok() { + self.poll(); + crate::kernel::syscall::io::wake_poll_waiters(); + } + result } /// Receive data and, when available, the peer sockaddr buffer. @@ -430,15 +927,190 @@ impl NetworkStack { file: &super::socket::SocketFile, buf: &mut [u8], ) -> Result<(usize, Option>), crate::vfs::FsError> { - super::socket::socket_recvfrom_impl(file, buf) + if file.is_shutdown_read() { + return Ok((0, None)); + } + + let mut sockets = self.socket_set.lock(); + match file.handle.lock().as_ref() { + Some(SocketHandle::Tcp(h)) => { + let socket = sockets.get_mut::(*h); + let state = socket.state(); + if state == tcp::State::Closed { + return Ok((0, None)); + } + if state == tcp::State::CloseWait && socket.recv_queue() == 0 { + return Ok((0, None)); + } + + let result = socket + .recv_slice(buf) + .map_err(|_| crate::vfs::FsError::WouldBlock); + let n = if let Ok(0) = result { + if state == tcp::State::CloseWait { + 0 + } else if socket.may_recv() { + return Err(crate::vfs::FsError::WouldBlock); + } else { + 0 + } + } else { + result? + }; + let remote = socket.remote_endpoint().map(|ep| { + let mut addr_buf = alloc::vec![0u8; 16]; + let _ = socket::write_sockaddr_in_to_buf(&mut addr_buf, ep); + addr_buf + }); + Ok((n, remote)) + } + Some(SocketHandle::Udp(_)) => { + drop(sockets); + let Some(d) = file.udp_pop() else { + return Err(crate::vfs::FsError::WouldBlock); + }; + let n = core::cmp::min(buf.len(), d.len); + buf[..n].copy_from_slice(&d.data[..n]); + let mut addr_buf = alloc::vec![0u8; 16]; + let _ = socket::write_sockaddr_in_to_buf(&mut addr_buf, d.src); + Ok((n, Some(addr_buf))) + } + None => Err(crate::vfs::FsError::InvalidArgument), + } + } + + fn create_udp_socket_in_set( + &self, + sockets: &mut SocketSet<'static>, + ) -> Result { + let mut rx_meta_vec = alloc::vec::Vec::new(); + rx_meta_vec.try_reserve(4).map_err(|_| ())?; + rx_meta_vec.resize(4, udp::PacketMetadata::EMPTY); + + let mut tx_meta_vec = alloc::vec::Vec::new(); + tx_meta_vec.try_reserve(4).map_err(|_| ())?; + tx_meta_vec.resize(4, udp::PacketMetadata::EMPTY); + + let mut rx_data_vec = alloc::vec::Vec::new(); + rx_data_vec.try_reserve(4096).map_err(|_| ())?; + rx_data_vec.resize(4096, 0); + + let mut tx_data_vec = alloc::vec::Vec::new(); + tx_data_vec.try_reserve(4096).map_err(|_| ())?; + tx_data_vec.resize(4096, 0); + + let rx_buffer = udp::PacketBuffer::new(rx_meta_vec, rx_data_vec); + let tx_buffer = udp::PacketBuffer::new(tx_meta_vec, tx_data_vec); + let socket = udp::Socket::new(rx_buffer, tx_buffer); + Ok(sockets.add(socket)) + } + + fn udp_dispatch_drain_locked(&self, sockets: &mut SocketSet<'static>) -> bool { + let mut delivered_any = false; + let mut ports = self.udp_ports.lock(); + let mut to_remove: alloc::vec::Vec<(u16, SmoltcpHandle)> = alloc::vec::Vec::new(); + + for (port, entry) in ports.iter_mut() { + let socket = sockets.get_mut::(entry.handle); + + while socket.can_recv() { + let (payload, meta) = match socket.recv() { + Ok(v) => v, + Err(_) => break, + }; + + let src = meta.endpoint; + let mut data = [0u8; socket::UDP_DGRAM_MAX]; + let copy_len = core::cmp::min(payload.len(), socket::UDP_DGRAM_MAX); + data[..copy_len].copy_from_slice(&payload[..copy_len]); + let d = UdpDatagram { + src, + len: copy_len, + data, + }; + + let mut target: Option> = None; + let mut fallback: Option> = None; + + entry.sockets.retain(|w| w.strong_count() > 0); + + for w in entry.sockets.iter() { + let Some(f) = w.upgrade() else { continue }; + let Some(sf) = f.as_any().downcast_ref::() else { + continue; + }; + + let Some(local_ep) = sf.get_local_endpoint() else { + continue; + }; + if local_ep.port != *port { + continue; + } + + match sf.get_remote_endpoint() { + Some(remote) => { + if remote.addr == src.addr && remote.port == src.port { + target = Some(f.clone()); + break; + } + } + None => { + if fallback.is_none() { + fallback = Some(f.clone()); + } + } + } + } + + let target = target.or(fallback); + if let Some(f) = target { + if let Some(sf) = f.as_any().downcast_ref::() { + if sf.udp_push(d) { + delivered_any = true; + } + } + } + } + + entry.sockets.retain(|w| w.strong_count() > 0); + if entry.sockets.is_empty() { + to_remove.push((*port, entry.handle)); + } + } + + for (port, handle) in to_remove { + ports.remove(&port); + sockets.remove(handle); + } + + delivered_any + } + + fn reap_pending_tcp_close(&self) { + let mut sockets = self.socket_set.lock(); + let mut pending = self.pending_tcp_close.lock(); + pending.retain(|h| { + let state = sockets.get::(*h).state(); + if matches!(state, tcp::State::Closed | tcp::State::TimeWait) { + crate::pr_debug!( + "[Socket] reap: removing closed tcp handle={:?}, state={:?}", + h, + state + ); + sockets.remove(*h); + false + } else { + true + } + }); } } lazy_static! { - static ref NETWORK_STACK: SpinLock = SpinLock::new(NetworkStack::new()); + static ref NETWORK_STACK: NetworkStack = NetworkStack::new(); } -/// Borrow the global network stack facade. -pub fn network_stack() -> &'static SpinLock { +/// Borrow the global network stack runtime. +pub fn network_stack() -> &'static NetworkStack { &NETWORK_STACK } diff --git a/os/src/util/user_buffer.rs b/os/src/util/user_buffer.rs index 44196de6..c54c223b 100644 --- a/os/src/util/user_buffer.rs +++ b/os/src/util/user_buffer.rs @@ -90,11 +90,11 @@ impl UserBuffer { /// TODO: 运行时做一次“粗略”范围校验(不保证已映射,仅做地址区间与溢出检查) /// 建议在 syscall 层或结合 MemorySpace 做页表级校验。 pub fn range_sane(&self) -> bool { - unimplemented!(); let start = self.data as usize; - let end = start.checked_add(self.len).unwrap_or(usize::MAX); - // start < USER_BASE && end <= USER_TOP; - true + let Some(end) = start.checked_add(self.len) else { + return false; + }; + start >= USER_BASE && end <= USER_TOP } /// 返回用户缓冲区长度 From bfda031f87af35f1e519ee109cd1c176201aac63 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 4 May 2026 23:33:43 +0800 Subject: [PATCH 09/11] =?UTF-8?q?docs:=20=E5=90=8C=E6=AD=A5=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E6=A0=88=E7=8A=B6=E6=80=81=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- document/net/README.md | 4 ++-- document/net/rearchitecture_stack_runtime.md | 16 ++++++++-------- document/net/stack_runtime.md | 12 +++++++----- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/document/net/README.md b/document/net/README.md index b0d3ed7c..08c2ffc6 100644 --- a/document/net/README.md +++ b/document/net/README.md @@ -6,8 +6,8 @@ - `os/src/device/net/`:网卡设备抽象和 VirtIO/loopback/null 设备。 - `os/src/net/interface.rs`:接口注册表、接口配置和 smoltcp interface 兼容工厂。 -- `os/src/net/stack.rs`:`NetworkStack` 门面和协议栈运行时状态。 -- `os/src/net/socket.rs`:`SocketFile`、fd/socket 映射、UDP per-fd 队列和兼容实现。 +- `os/src/net/stack.rs`:`NetworkStack` 状态对象和协议栈运行时实现。 +- `os/src/net/socket.rs`:`SocketFile`、fd/socket 映射、UDP per-fd 队列和公开 socket 包装 API。 - `os/src/kernel/syscall/network.rs`:网络 syscall ABI 层。 ## 文档导航 diff --git a/document/net/rearchitecture_stack_runtime.md b/document/net/rearchitecture_stack_runtime.md index 4a529c9d..3a3e69f2 100644 --- a/document/net/rearchitecture_stack_runtime.md +++ b/document/net/rearchitecture_stack_runtime.md @@ -4,10 +4,10 @@ ## 当前代码 -- `os/src/net/socket.rs` 定义 `SOCKET_SET`、`NET_IFACE`、`FD_SOCKET_MAP`、`NetIfaceWrapper`。 -- `os/src/net/interface.rs` 定义 `SmoltcpInterface` 和 `NetDeviceAdapter`。 -- `poll_network_interfaces()`、`poll_network_and_dispatch()`、`poll_until_empty()` 分散承担协议栈推进。 -- UDP per-port dispatcher 与 TCP pending close 也在 `socket.rs` 内部。 +- `os/src/net/stack.rs` 定义 `NetworkStack`,持有 smoltcp socket set、active interface runtime、loopback link、UDP per-port dispatcher 和 TCP pending close。 +- `os/src/net/socket.rs` 保留 `SocketFile`、fd/socket 映射、UDP per-fd queue 和公开 socket 包装 API。 +- `os/src/net/interface.rs` 定义接口控制面,`SmoltcpInterface` 和 `NetDeviceAdapter` 由 `net::stack` 提供。 +- `poll_network_interfaces()`、`poll_network_and_dispatch()`、`poll_until_empty()` 均委托到 `NetworkStack`。 ## 目标对象 @@ -50,7 +50,7 @@ impl NetworkStack { 1. 把 `SmoltcpInterface` 下沉到 `net::stack`,不再从接口层公开返回。 2. 把 `NetDeviceAdapter` 移到 `net::stack` 内部。 -3. 把 `SOCKET_SET` 和 `NET_IFACE` 收进 `NetworkStack`,旧全局符号改成兼容包装。 +3. 把 smoltcp socket set 和 active interface runtime 收进 `NetworkStack`,不再保留裸全局状态。 4. 把 UDP dispatch、TCP pending close、poll waiters 唤醒统一放入 `NetworkStack::poll()`。 5. 所有 socket 操作完成后如需推进协议栈,只调用 `NetworkStack::poll()`。 @@ -75,12 +75,12 @@ impl NetworkStack { ## 当前执行状态 -- `SOCKET_SET` 与 `NET_IFACE` 已移入 `net::stack`,`socket.rs` 仅作为 runtime 实现模块以 `pub(crate)` 方式引用。 +- smoltcp socket set、active interface runtime、loopback link、UDP dispatch 表和 TCP pending close 表已成为 `NetworkStack` 字段。 - `NetworkStack` 已覆盖 socket 创建、TCP connect/listen/state/close/endpoints、UDP dispatch/attach、poll、SocketFile read/write/readable/writable/drop/sendto/recvfrom。 - UDP dispatch、TCP pending close reaping 和 poll waiter 唤醒仍在统一 poll 路径中执行。 -- loopback 兼容队列已从 `NetDeviceAdapter` 字段移入 `net::stack` runtime 的 loopback link。 +- `NetIfaceWrapper` 已搬入 `net::stack`,`socket.rs` 不再承载协议栈 runtime 实现。 +- loopback 兼容队列已从 `NetDeviceAdapter` 字段移入 `NetworkStack` 的 loopback link。 保留的兼容点: -- `NetIfaceWrapper` 仍定义在 `socket.rs`,作为旧单接口 runtime 的实现细节;后续可整体搬入 `stack.rs` 或 `stack/runtime.rs`。 - `SocketHandle` 仍等同于 smoltcp handle 包装,暂未引入多 runtime 安全的 socket id。 diff --git a/document/net/stack_runtime.md b/document/net/stack_runtime.md index 25c3887c..2ec16ef5 100644 --- a/document/net/stack_runtime.md +++ b/document/net/stack_runtime.md @@ -4,7 +4,7 @@ ## NetworkStack -`NetworkStack` 对外提供稳定门面: +`NetworkStack` 对外提供稳定状态对象: - 创建 TCP/UDP socket。 - TCP connect/listen/state/close/endpoints。 @@ -18,11 +18,13 @@ `net::stack` 持有: -- `SOCKET_SET`:smoltcp socket set。 -- `NET_IFACE`:当前 active interface runtime。 -- loopback link:当前单 runtime 下的 loopback frame queue。 +- `socket_set`:smoltcp socket set。 +- `net_iface`:当前 active interface runtime。 +- `loopback_link`:当前单 runtime 下的 loopback frame queue。 +- `udp_ports`:UDP per-port dispatcher。 +- `pending_tcp_close`:等待 graceful close 回收的 TCP socket。 -这些符号仅限 `pub(crate)` 兼容实现使用,不对 syscall 层开放。 +这些状态都是 `NetworkStack` 字段,不作为裸全局符号暴露给 syscall 层或 socket 层。 ## Poll 顺序 From 58e91e964d03fe55797c3a94668e9ec8f9b54993 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Tue, 5 May 2026 17:16:12 +0800 Subject: [PATCH 10/11] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E4=B8=80=E9=83=A8?= =?UTF-8?q?=E5=88=86cargo=20clippy=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/Cargo.toml | 4 + os/build.rs | 2 +- os/src/arch/riscv/boot/mod.rs | 4 +- os/src/arch/riscv/intr/softirq.rs | 3 + os/src/arch/riscv/ipi.rs | 4 +- os/src/console.rs | 10 +- os/src/device/block/virtio_blk.rs | 18 ++-- os/src/device/bus/pcie.rs | 52 +++++----- os/src/device/bus/virtio_mmio.rs | 2 +- os/src/device/device_tree.rs | 30 +++--- os/src/device/gpu/virtio_gpu.rs | 2 +- os/src/device/input/virtio_input.rs | 2 +- os/src/device/irq/mod.rs | 12 +-- os/src/device/rtc/rtc_goldfish.rs | 2 +- os/src/device/serial/uart16550.rs | 11 +-- os/src/device/virtio_hal.rs | 7 +- os/src/fs/ext4/adpaters.rs | 8 +- os/src/fs/ext4/inode.rs | 8 +- os/src/fs/ext4/mod.rs | 4 +- os/src/fs/mod.rs | 9 +- os/src/fs/proc/generators/process/mod.rs | 2 +- os/src/fs/proc/inode.rs | 12 +-- os/src/fs/proc/mod.rs | 2 +- os/src/fs/simple_fs.rs | 8 +- os/src/fs/sysfs/inode.rs | 2 +- os/src/fs/sysfs/mod.rs | 2 +- os/src/fs/tests/ext4/ext4_directory.rs | 2 +- os/src/fs/tests/proc/proc_directory.rs | 2 +- os/src/fs/tests/tmpfs/tmpfs_sparse.rs | 16 +-- os/src/fs/tmpfs/inode.rs | 12 +-- os/src/fs/tmpfs/mod.rs | 1 - os/src/ipc/pipe.rs | 2 +- os/src/ipc/signal.rs | 10 +- os/src/kernel/cpu.rs | 2 +- os/src/kernel/scheduler/mod.rs | 12 +-- os/src/kernel/scheduler/task_queue.rs | 2 +- os/src/kernel/scheduler/wait_queue.rs | 2 +- os/src/kernel/syscall/fcntl.rs | 2 +- os/src/kernel/syscall/fs.rs | 41 ++++---- os/src/kernel/syscall/io.rs | 86 ++++++++-------- os/src/kernel/syscall/ioctl.rs | 2 +- os/src/kernel/syscall/ipc.rs | 11 +-- os/src/kernel/syscall/mm.rs | 38 +++---- os/src/kernel/syscall/network.rs | 120 +++++++++++------------ os/src/kernel/syscall/signal.rs | 10 +- os/src/kernel/syscall/task.rs | 16 ++- os/src/kernel/syscall/util.rs | 2 +- os/src/kernel/task/task_manager.rs | 4 +- os/src/kernel/timer.rs | 16 +-- os/src/log/buffer.rs | 15 ++- os/src/log/tests/format.rs | 2 +- os/src/mm/frame_allocator/mod.rs | 2 +- os/src/mm/memory_space/mapping_area.rs | 34 +++---- os/src/mm/memory_space/memory_space.rs | 4 +- os/src/net/config.rs | 10 +- os/src/net/interface.rs | 2 +- os/src/net/socket.rs | 1 + os/src/net/stack.rs | 42 ++++---- os/src/security/entropy_pool.rs | 4 +- os/src/sync/mod.rs | 4 +- os/src/sync/mutex.rs | 2 +- os/src/sync/preempt.rs | 6 +- os/src/sync/raw_spin_lock.rs | 11 +-- os/src/test/guard.rs | 6 +- os/src/test/macros.rs | 35 +++---- os/src/test/mod.rs | 6 +- os/src/test/net_test.rs | 29 +++--- os/src/uapi/ioctl.rs | 2 +- os/src/uapi/resource.rs | 2 +- os/src/uapi/select.rs | 2 +- os/src/util/stdio.rs | 10 +- os/src/util/str.rs | 4 +- os/src/util/user_buffer.rs | 6 +- os/src/vfs/adapter.rs | 4 +- os/src/vfs/devno.rs | 4 +- os/src/vfs/file_lock.rs | 2 +- os/src/vfs/file_system.rs | 1 - os/src/vfs/impls/blk_dev_file.rs | 10 +- os/src/vfs/impls/char_dev_file.rs | 84 ++++++++-------- os/src/vfs/impls/pipe_file.rs | 22 ++--- os/src/vfs/mod.rs | 8 +- os/src/vfs/mount.rs | 4 +- os/src/vfs/path.rs | 14 +-- os/src/vfs/tests/dentry.rs | 2 +- os/src/vfs/tests/fd_table.rs | 2 +- os/src/vfs/tests/mount.rs | 4 +- os/src/vfs/tests/path.rs | 4 +- 87 files changed, 494 insertions(+), 545 deletions(-) diff --git a/os/Cargo.toml b/os/Cargo.toml index f7e59a6c..b6e37279 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -22,6 +22,10 @@ chrono = { version = "0.4", default-features = false, features = ["alloc"] } uart_16550 = "0.4.0" hashbrown = "0.16.1" +[features] +default = [] +proto-ipv6 = [] + # RISC-V 架构特定依赖 [target.'cfg(target_arch = "riscv64")'.dependencies] sbi-rt = { version = "0.0.2", features = ["legacy"] } diff --git a/os/build.rs b/os/build.rs index 28b8d5ab..fc5bc7a9 100644 --- a/os/build.rs +++ b/os/build.rs @@ -127,7 +127,7 @@ fn main() { // 我们创建一个空的伪文件来满足 include_bytes! 的需求 let dummy_img = PathBuf::from(&out_dir).join("ext4_test_dummy.img"); if !dummy_img.exists() { - let _ = fs::write(&dummy_img, &[]); + let _ = fs::write(&dummy_img, []); } println!( "cargo:warning=[build.rs] Skipping real test image creation (using dummy for IDE)" diff --git a/os/src/arch/riscv/boot/mod.rs b/os/src/arch/riscv/boot/mod.rs index af44cc4d..2761b448 100644 --- a/os/src/arch/riscv/boot/mod.rs +++ b/os/src/arch/riscv/boot/mod.rs @@ -238,7 +238,7 @@ pub fn main(hartid: usize) { // 必须在任何可能调用 cpu_id() 的代码之前完成 { use crate::kernel::CPUS; - let cpu_ptr = &*CPUS.get_of(0) as *const _ as usize; + let cpu_ptr = CPUS.get_of(0) as *const _ as usize; unsafe { core::arch::asm!("mv tp, {}", in(reg) cpu_ptr); } @@ -448,7 +448,7 @@ pub extern "C" fn secondary_start(hartid: usize) -> ! { // 设置 tp 指向对应的 Cpu 结构体 { use crate::kernel::CPUS; - let cpu_ptr = &*CPUS.get_of(hartid) as *const _ as usize; + let cpu_ptr = CPUS.get_of(hartid) as *const _ as usize; unsafe { core::arch::asm!("mv tp, {}", in(reg) cpu_ptr); } diff --git a/os/src/arch/riscv/intr/softirq.rs b/os/src/arch/riscv/intr/softirq.rs index 6e952286..7a03fb34 100644 --- a/os/src/arch/riscv/intr/softirq.rs +++ b/os/src/arch/riscv/intr/softirq.rs @@ -21,6 +21,9 @@ pub enum Softirq { /// 触发软中断 /// 参数: /// * `softirq` - 要触发的软中断类型 +/// +/// # 安全性 +/// /// 安全性: 该函数涉及底层中断处理机制,可能会引发竞态条件或系统不稳定。 /// 调用者必须确保在适当的上下文中调用此函数,以避免潜在的问题。 #[unsafe(no_mangle)] diff --git a/os/src/arch/riscv/ipi.rs b/os/src/arch/riscv/ipi.rs index 5d09dfa6..ce4b1d3c 100644 --- a/os/src/arch/riscv/ipi.rs +++ b/os/src/arch/riscv/ipi.rs @@ -81,9 +81,9 @@ pub fn send_ipi_many(hart_mask: usize, ipi_type: IpiType) { let num_cpu = unsafe { crate::kernel::NUM_CPU }; // 设置所有目标 CPU 的待处理标志 - for cpu in 0..num_cpu { + for (cpu, pending) in IPI_PENDING.iter().enumerate().take(num_cpu) { if (hart_mask & (1 << cpu)) != 0 { - IPI_PENDING[cpu].fetch_or(ipi_type as u32, Ordering::Release); + pending.fetch_or(ipi_type as u32, Ordering::Release); } } diff --git a/os/src/console.rs b/os/src/console.rs index bd29e8ab..d878a383 100644 --- a/os/src/console.rs +++ b/os/src/console.rs @@ -22,11 +22,11 @@ pub fn init() { #[inline] fn write_str_unlocked(s: &str) { - if CONSOLE_RUNTIME.load(Ordering::Acquire) { - if let Some(console) = crate::device::console::MAIN_CONSOLE.read().as_ref() { - console.write_str(s); - return; - } + if CONSOLE_RUNTIME.load(Ordering::Acquire) + && let Some(console) = crate::device::console::MAIN_CONSOLE.read().as_ref() + { + console.write_str(s); + return; } for b in s.bytes() { diff --git a/os/src/device/block/virtio_blk.rs b/os/src/device/block/virtio_blk.rs index 1d4731fe..76d5f287 100644 --- a/os/src/device/block/virtio_blk.rs +++ b/os/src/device/block/virtio_blk.rs @@ -1,5 +1,4 @@ -use alloc::sync::Arc; -use alloc::{format, string::String}; +use alloc::{string::String, sync::Arc}; use virtio_drivers::device::blk::VirtIOBlk; use virtio_drivers::transport::InterruptStatus; use virtio_drivers::transport::{mmio::MmioTransport, pci::PciTransport}; @@ -18,6 +17,10 @@ use super::{ /// VirtIO 块设备驱动结构体 pub struct VirtIOBlkDriver(Mutex>>); +impl VirtIOBlkDriver { + const ID: &'static str = "virtio_block"; +} + impl Driver for VirtIOBlkDriver { fn try_handle_interrupt(&self, _irq: Option) -> bool { let status = self.0.lock().ack_interrupt(); @@ -29,7 +32,7 @@ impl Driver for VirtIOBlkDriver { } fn get_id(&self) -> String { - format!("virtio_block") + String::from(Self::ID) } fn as_block(&self) -> Option<&dyn BlockDriver> { @@ -85,7 +88,7 @@ impl Driver for VirtIOBlkPciDriver { } fn get_id(&self) -> String { - format!("virtio_block_pci") + String::from("virtio_block_pci") } fn as_block(&self) -> Option<&dyn BlockDriver> { @@ -204,7 +207,7 @@ mod tests { test_case!(test_virtioblk_basic_metadata, { // 无法直接构造 VirtIOBlkDriver(需要真实 MmioTransport), // 因此这里只做字符串与常量行为的直接校验。 - kassert!("virtio_block".to_string() == "virtio_block"); + kassert!(VirtIOBlkDriver::ID == "virtio_block"); }); // 中断处理逻辑的可调用性测试(只验证调用路径,不验证硬件副作用) @@ -227,9 +230,8 @@ mod tests { check_common_driver_behavior(drv.as_ref()); let block_iface = drv.as_block().unwrap(); // 尝试测试第 0 号块(实际系统中可根据分配策略选择安全块号) - let ok = try_block_roundtrip(block_iface, 0); - // 如果失败(比如环境不支持实际 I/O),则跳过,不判为失败 - kassert!(ok || !ok); // 始终为真,占位避免 panic;可替换为日志。 + // 这个测试只覆盖调用路径;没有真实 I/O 能力的环境允许失败。 + let _ = try_block_roundtrip(block_iface, 0); } else { // 未初始化驱动,跳过 kassert!(true); diff --git a/os/src/device/bus/pcie.rs b/os/src/device/bus/pcie.rs index 88c6cc49..861d29a9 100644 --- a/os/src/device/bus/pcie.rs +++ b/os/src/device/bus/pcie.rs @@ -91,7 +91,7 @@ impl PcieHost { }) .unwrap_or(3); // PCI 默认 3 - let child_size_cells = node + let _child_size_cells = node .property("#size-cells") .and_then(|p| { if p.value.len() >= 4 { @@ -151,9 +151,8 @@ impl PcieHost { // child 地址(第一 cell 为 flags) let mut child_cells_vals: [u32; 8] = [0; 8]; - for c in 0..child_addr_cells { - child_cells_vals[c] = - u32::from_be_bytes(prop.value[idx..idx + 4].try_into().unwrap()); + for cell in child_cells_vals.iter_mut().take(child_addr_cells) { + *cell = u32::from_be_bytes(prop.value[idx..idx + 4].try_into().unwrap()); idx += 4; } let flags = child_cells_vals[0]; @@ -163,8 +162,8 @@ impl PcieHost { let is_mem = space_code == 0x0100_0000 || space_code == 0x0200_0000; // 组合 child 基址(后两个 cells) let mut child_addr = 0usize; - for c in 1..child_addr_cells { - child_addr = (child_addr << 32) | child_cells_vals[c] as usize; + for cell in child_cells_vals.iter().take(child_addr_cells).skip(1) { + child_addr = (child_addr << 32) | *cell as usize; } // parent 地址 @@ -206,21 +205,21 @@ impl PcieHost { let mut mmio_base = mmio_base; let mut mmio_size = mmio_size; - if ecam_size == 0 { - if let Some((def_base, def_size)) = mmio_of(VirtDevice::VirtPcieEcam) { - ecam_base = def_base; - ecam_size = def_size; - ecam_from_fdt = false; - pr_warn!("[PCIe] FDT ECAM missing, using platform defaults"); - } + if ecam_size == 0 + && let Some((def_base, def_size)) = mmio_of(VirtDevice::VirtPcieEcam) + { + ecam_base = def_base; + ecam_size = def_size; + ecam_from_fdt = false; + pr_warn!("[PCIe] FDT ECAM missing, using platform defaults"); } - if mmio_size < 0x100000 { - if let Some((def_base, def_size)) = mmio_of(VirtDevice::VirtPcieMmio) { - mmio_base = def_base; - mmio_size = def_size; - pr_warn!("[PCIe] FDT PCIe ranges too small, overriding MMIO window"); - } + if mmio_size < 0x100000 + && let Some((def_base, def_size)) = mmio_of(VirtDevice::VirtPcieMmio) + { + mmio_base = def_base; + mmio_size = def_size; + pr_warn!("[PCIe] FDT PCIe ranges too small, overriding MMIO window"); } let ecam_end = ecam_base.saturating_add(ecam_size); @@ -360,7 +359,7 @@ impl PcieHost { /// 初始化并枚举 PCIe 设备 pub fn init_and_enumerate() { - if let Some(host) = PcieHost::from_fdt().or_else(|| PcieHost::from_platform_defaults()) { + if let Some(host) = PcieHost::from_fdt().or_else(PcieHost::from_platform_defaults) { host.enumerate(); } else { pr_info!("[PCIe] no host found from platform defaults"); @@ -369,13 +368,12 @@ pub fn init_and_enumerate() { /// 初始化 PCIe 并枚举 VirtIO PCI 设备 pub fn init_virtio_pci() { - let host = - if let Some(host) = PcieHost::from_fdt().or_else(|| PcieHost::from_platform_defaults()) { - host - } else { - pr_info!("[PCIe] no host found from platform defaults"); - return; - }; + let host = if let Some(host) = PcieHost::from_fdt().or_else(PcieHost::from_platform_defaults) { + host + } else { + pr_info!("[PCIe] no host found from platform defaults"); + return; + }; let ecam_vaddr = match current_memory_space() .lock() diff --git a/os/src/device/bus/virtio_mmio.rs b/os/src/device/bus/virtio_mmio.rs index 170f5dbe..c5f15bca 100644 --- a/os/src/device/bus/virtio_mmio.rs +++ b/os/src/device/bus/virtio_mmio.rs @@ -18,7 +18,7 @@ use crate::{ }, kernel::current_memory_space, mm::address::{Paddr, UsizeConvert}, - pr_info, pr_warn, + pr_warn, }; pub fn driver_init() { diff --git a/os/src/device/device_tree.rs b/os/src/device/device_tree.rs index 0dfe298b..8e320b80 100644 --- a/os/src/device/device_tree.rs +++ b/os/src/device/device_tree.rs @@ -85,15 +85,15 @@ pub fn init() { pr_info!( "[Device] Memory Region: Start = {:#X}, Size = {:#X}", region.starting_address as usize, - region.size.unwrap() as usize + { region.size.unwrap() } ); }); - if let Some(bootargs) = FDT.chosen().bootargs() { - if !bootargs.is_empty() { - pr_info!("Kernel cmdline: {}", bootargs); - *CMDLINE.write() = String::from(bootargs); - } + if let Some(bootargs) = FDT.chosen().bootargs() + && !bootargs.is_empty() + { + pr_info!("Kernel cmdline: {}", bootargs); + *CMDLINE.write() = String::from(bootargs); } // 首先初始化中断控制器 @@ -106,14 +106,14 @@ pub fn init() { /// * `fdt` - 设备树对象 fn walk_dt(fdt: &Fdt, intc_only: bool) { for node in fdt.all_nodes() { - if let Some(compatible) = node.compatible() { - if node.property("interrupt-controller").is_some() == intc_only { - pr_info!("[Device] Found device: {}", node.name); - let registry = DEVICE_TREE_REGISTRY.read(); - for c in compatible.all() { - if let Some(f) = registry.get(c) { - f(&node); - } + if let Some(compatible) = node.compatible() + && node.property("interrupt-controller").is_some() == intc_only + { + pr_info!("[Device] Found device: {}", node.name); + let registry = DEVICE_TREE_REGISTRY.read(); + for c in compatible.all() { + if let Some(f) = registry.get(c) { + f(&node); } } } @@ -129,7 +129,7 @@ pub fn dram_info() -> Option<(usize, usize)> { for region in FDT.memory().regions() { let s = region.starting_address as usize; - let size = region.size.unwrap_or(0) as usize; + let size = region.size.unwrap_or(0); let e = s.saturating_add(size); if size == 0 { continue; diff --git a/os/src/device/gpu/virtio_gpu.rs b/os/src/device/gpu/virtio_gpu.rs index 613b935e..36952205 100644 --- a/os/src/device/gpu/virtio_gpu.rs +++ b/os/src/device/gpu/virtio_gpu.rs @@ -2,6 +2,6 @@ use virtio_drivers::transport::mmio::MmioTransport; use crate::pr_info; -pub fn init(transport: MmioTransport<'static>) { +pub fn init(_transport: MmioTransport<'static>) { pr_info!("[Device] GPU driver (virtio-gpu) is initialized"); } diff --git a/os/src/device/input/virtio_input.rs b/os/src/device/input/virtio_input.rs index 5d8e18a7..3ca81736 100644 --- a/os/src/device/input/virtio_input.rs +++ b/os/src/device/input/virtio_input.rs @@ -2,6 +2,6 @@ use virtio_drivers::transport::mmio::MmioTransport; use crate::pr_info; -pub fn init(transport: MmioTransport<'static>) { +pub fn init(_transport: MmioTransport<'static>) { pr_info!("[Device] Input driver (virtio-input) is initialized"); } diff --git a/os/src/device/irq/mod.rs b/os/src/device/irq/mod.rs index 85fdf229..fee4f4cb 100644 --- a/os/src/device/irq/mod.rs +++ b/os/src/device/irq/mod.rs @@ -102,12 +102,12 @@ impl IrqManager { /// # 返回值: /// 如果中断被处理则返回 true,否则返回 false pub fn try_handle_interrupt(&self, irq_opt: Option) -> bool { - if let Some(irq) = irq_opt { - if let Some(e) = self.mapping.get(&irq) { - for dri in e.iter() { - if dri.try_handle_interrupt(Some(irq)) { - return true; - } + if let Some(irq) = irq_opt + && let Some(e) = self.mapping.get(&irq) + { + for dri in e.iter() { + if dri.try_handle_interrupt(Some(irq)) { + return true; } } } diff --git a/os/src/device/rtc/rtc_goldfish.rs b/os/src/device/rtc/rtc_goldfish.rs index af257da1..8a1d9165 100644 --- a/os/src/device/rtc/rtc_goldfish.rs +++ b/os/src/device/rtc/rtc_goldfish.rs @@ -19,7 +19,7 @@ pub struct RtcGoldfish { } impl Driver for RtcGoldfish { - fn try_handle_interrupt(&self, irq: Option) -> bool { + fn try_handle_interrupt(&self, _irq: Option) -> bool { false } diff --git a/os/src/device/serial/uart16550.rs b/os/src/device/serial/uart16550.rs index 5850c04c..9ad8fbf9 100644 --- a/os/src/device/serial/uart16550.rs +++ b/os/src/device/serial/uart16550.rs @@ -1,6 +1,6 @@ //! 16550 UART 串行端口驱动程序模块 -use alloc::{format, sync::Arc}; +use alloc::sync::Arc; use fdt::node::FdtNode; use uart_16550::MmioSerialPort; @@ -21,7 +21,7 @@ pub struct Uart16550 { } impl Driver for Uart16550 { - fn try_handle_interrupt(&self, irq: Option) -> bool { + fn try_handle_interrupt(&self, _irq: Option) -> bool { todo!() } @@ -30,7 +30,7 @@ impl Driver for Uart16550 { } fn get_id(&self) -> alloc::string::String { - format!("ns16550a") + alloc::string::String::from("ns16550a") } fn as_serial(&self) -> Option<&dyn SerialDriver> { @@ -51,10 +51,7 @@ impl SerialDriver for Uart16550 { } fn try_read(&self) -> Option { - match self.serial_port.lock().try_receive() { - Ok(byte) => Some(byte), - Err(_) => None, - } + self.serial_port.lock().try_receive().ok() } } diff --git a/os/src/device/virtio_hal.rs b/os/src/device/virtio_hal.rs index 42b063b9..1a3849d3 100644 --- a/os/src/device/virtio_hal.rs +++ b/os/src/device/virtio_hal.rs @@ -82,8 +82,8 @@ unsafe impl Hal for VirtIOHal { let virt = paddr_to_vaddr(phys_addr); // 验证虚拟地址的合法性 - let ptr = NonNull::new(virt as *mut u8).expect("mmio_phys_to_virt returned null pointer"); - ptr + + NonNull::new(virt as *mut u8).expect("mmio_phys_to_virt returned null pointer") } /// 共享内存区域给设备,并返回设备可访问的物理地址 @@ -91,8 +91,7 @@ unsafe impl Hal for VirtIOHal { let vaddr = buffer.as_ptr() as *const u8 as usize; let paddr = unsafe { vaddr_to_paddr(vaddr) }; - let result = PhysAddr::from(paddr as u64); - result + PhysAddr::from(paddr as u64) } /// 取消共享内存区域,并在必要时将数据复制回原始缓冲区 diff --git a/os/src/fs/ext4/adpaters.rs b/os/src/fs/ext4/adpaters.rs index 4963d5c5..cb825c2c 100644 --- a/os/src/fs/ext4/adpaters.rs +++ b/os/src/fs/ext4/adpaters.rs @@ -26,7 +26,7 @@ impl BlockDeviceAdapter { // 确保块大小是扇区大小的整数倍 assert!( - block_size % sector_size == 0, + block_size.is_multiple_of(sector_size), "Block size must be a multiple of sector size" ); @@ -49,8 +49,7 @@ impl ext4_rs::BlockDevice for BlockDeviceAdapter { // 计算需要读取多少个扇区才能获得 block_size 字节的数据 let bytes_needed = self.block_size; - let sectors_needed = - (sector_offset + bytes_needed + self.sector_size - 1) / self.sector_size; + let sectors_needed = (sector_offset + bytes_needed).div_ceil(self.sector_size); // 读取所有需要的扇区 let mut buffer = alloc::vec![0u8; sectors_needed * self.sector_size]; @@ -83,8 +82,7 @@ impl ext4_rs::BlockDevice for BlockDeviceAdapter { // 计算需要写入多少个扇区 let bytes_to_write = data.len(); - let sectors_needed = - (sector_offset + bytes_to_write + self.sector_size - 1) / self.sector_size; + let sectors_needed = (sector_offset + bytes_to_write).div_ceil(self.sector_size); // 读取-修改-写入 let mut buffer = alloc::vec![0u8; sectors_needed * self.sector_size]; diff --git a/os/src/fs/ext4/inode.rs b/os/src/fs/ext4/inode.rs index 63fe7049..1ca710c2 100644 --- a/os/src/fs/ext4/inode.rs +++ b/os/src/fs/ext4/inode.rs @@ -170,7 +170,7 @@ impl Inode for Ext4Inode { // 类似 create,lookup 也应该使用相对路径 // 直接在当前目录下查找指定名称的文件 - let mut fs = self.fs.lock(); + let fs = self.fs.lock(); let mut parent = self.ino; let mut name_off = 0; @@ -654,7 +654,7 @@ impl Inode for Ext4Inode { } fn set_times(&self, atime: Option, mtime: Option) -> Result<(), FsError> { - let mut fs = self.fs.lock(); + let fs = self.fs.lock(); // 获取 inode 引用(可变) let mut inode_ref = fs.get_inode_ref(self.ino); @@ -686,7 +686,7 @@ impl Inode for Ext4Inode { } fn chown(&self, uid: u32, gid: u32) -> Result<(), FsError> { - let mut fs = self.fs.lock(); + let fs = self.fs.lock(); // 获取 inode 引用(可变) let mut inode_ref = fs.get_inode_ref(self.ino); @@ -714,7 +714,7 @@ impl Inode for Ext4Inode { } fn chmod(&self, mode: FileMode) -> Result<(), FsError> { - let mut fs = self.fs.lock(); + let fs = self.fs.lock(); // 获取 inode 引用(可变) let mut inode_ref = fs.get_inode_ref(self.ino); diff --git a/os/src/fs/ext4/mod.rs b/os/src/fs/ext4/mod.rs index e7a5650c..ea48a0d8 100644 --- a/os/src/fs/ext4/mod.rs +++ b/os/src/fs/ext4/mod.rs @@ -115,9 +115,7 @@ impl Ext4FileSystem { let adapter = Arc::new(BlockDeviceAdapter::new(device.clone(), block_size)); // 预检 superblock,避免 ext4_rs 在坏镜像/错误读取时 panic - if let Err(e) = validate_superblock(&adapter) { - return Err(e); - } + validate_superblock(&adapter)?; // 使用 ext4_rs 打开文件系统 // 注意:ext4_rs::Ext4::open 直接返回 Ext4,不返回 Result diff --git a/os/src/fs/mod.rs b/os/src/fs/mod.rs index 1ed7505d..375cf818 100644 --- a/os/src/fs/mod.rs +++ b/os/src/fs/mod.rs @@ -151,7 +151,7 @@ pub fn init_simple_fs() -> Result<(), crate::vfs::FsError> { /// /// 尝试从第一个可用的块设备创建 Ext4 文件系统,并挂载为根文件系统 pub fn init_ext4_from_block_device() -> Result<(), crate::vfs::FsError> { - use crate::config::{EXT4_BLOCK_SIZE, FS_IMAGE_SIZE, VIRTIO_BLK_SECTOR_SIZE}; + use crate::config::{EXT4_BLOCK_SIZE, FS_IMAGE_SIZE}; use crate::vfs::FsError; pr_info!("[Ext4] Initializing Ext4 filesystem from block device"); @@ -220,7 +220,6 @@ pub fn init_ext4_from_block_device() -> Result<(), crate::vfs::FsError> { /// - `mount_point`: 挂载点路径(如 "/tmp") /// - `max_size_mb`: 最大容量(MB),0 表示无限制 pub fn mount_tmpfs(mount_point: &str, max_size_mb: usize) -> Result<(), crate::vfs::FsError> { - use crate::vfs::FsError; use alloc::string::ToString; pr_info!( @@ -249,9 +248,7 @@ pub fn mount_tmpfs(mount_point: &str, max_size_mb: usize) -> Result<(), crate::v } pub fn init_dev() -> Result<(), FsError> { - if let Err(e) = vfs_lookup("/dev") { - return Err(e); - } + vfs_lookup("/dev")?; create_devices()?; @@ -311,7 +308,6 @@ fn create_devices() -> Result<(), FsError> { pub fn init_procfs() -> Result<(), crate::vfs::FsError> { use crate::fs::proc::ProcFS; use crate::vfs::MountFlags; - use alloc::string::ToString; pr_info!("[ProcFS] Initializing procfs"); @@ -338,7 +334,6 @@ pub fn init_procfs() -> Result<(), crate::vfs::FsError> { pub fn init_sysfs() -> Result<(), crate::vfs::FsError> { use crate::fs::sysfs::SysFS; use crate::vfs::MountFlags; - use alloc::string::ToString; pr_info!("[SysFS] Initializing sysfs"); diff --git a/os/src/fs/proc/generators/process/mod.rs b/os/src/fs/proc/generators/process/mod.rs index 6e72d00f..f09618ae 100644 --- a/os/src/fs/proc/generators/process/mod.rs +++ b/os/src/fs/proc/generators/process/mod.rs @@ -6,6 +6,6 @@ pub mod status; pub use cmdline::CmdlineGenerator; pub use maps::MapsGenerator; -pub use memory::{ProcMemStats, collect_user_vm_stats}; +pub use memory::collect_user_vm_stats; pub use stat::StatGenerator; pub use status::StatusGenerator; diff --git a/os/src/fs/proc/inode.rs b/os/src/fs/proc/inode.rs index 7cf341f3..63ee830b 100644 --- a/os/src/fs/proc/inode.rs +++ b/os/src/fs/proc/inode.rs @@ -353,12 +353,12 @@ impl Inode for ProcInode { } // 仅对 /proc 根目录:检查是否为进程目录(数字命名) - if self.kind == ProcInodeKind::Root { - if let Ok(pid) = name.parse::() { - // 动态创建进程目录(不缓存,避免 stale PID 与 dentry 缓存问题) - if let Some(proc_dir) = self.create_process_dir(pid) { - return Ok(proc_dir as Arc); - } + if self.kind == ProcInodeKind::Root + && let Ok(pid) = name.parse::() + { + // 动态创建进程目录(不缓存,避免 stale PID 与 dentry 缓存问题) + if let Some(proc_dir) = self.create_process_dir(pid) { + return Ok(proc_dir as Arc); } } diff --git a/os/src/fs/proc/mod.rs b/os/src/fs/proc/mod.rs index 11281199..a2bc4473 100644 --- a/os/src/fs/proc/mod.rs +++ b/os/src/fs/proc/mod.rs @@ -60,5 +60,5 @@ pub mod generators; pub mod inode; pub mod proc; -pub use inode::{ContentGenerator, ProcInode, ProcInodeContent}; +pub use inode::{ContentGenerator, ProcInode}; pub use proc::ProcFS; diff --git a/os/src/fs/simple_fs.rs b/os/src/fs/simple_fs.rs index 9a01528d..1536705a 100644 --- a/os/src/fs/simple_fs.rs +++ b/os/src/fs/simple_fs.rs @@ -170,14 +170,14 @@ impl SimpleFs { let mut cur_offset = offset + 32; // 读取文件名 - let name_aligned = (name_len + 3) / 4 * 4; + let name_aligned = name_len.div_ceil(4) * 4; let mut name_buf = vec![0u8; name_aligned]; Self::read_at_offset(device.clone(), cur_offset, &mut name_buf)?; let name = String::from(String::from_utf8_lossy(&name_buf[..name_len])); cur_offset += name_aligned; // 读取文件数据 - let data_aligned = (data_len + 511) / 512 * 512; + let data_aligned = data_len.div_ceil(512) * 512; let mut data_buf = vec![0u8; data_len]; if data_len > 0 { Self::read_at_offset(device.clone(), cur_offset, &mut data_buf)?; @@ -338,7 +338,7 @@ impl Inode for SimpleFsInode { Ok(InodeMetadata { inode_no: self.inode_no, inode_type: self.inode_type, - mode: self.mode.clone(), + mode: self.mode, uid: 0, gid: 0, size: data.len(), @@ -346,7 +346,7 @@ impl Inode for SimpleFsInode { mtime: TimeSpec::now(), ctime: TimeSpec::now(), nlinks: 1, - blocks: (data.len() + 511) / 512, + blocks: data.len().div_ceil(512), rdev: 0, }) } diff --git a/os/src/fs/sysfs/inode.rs b/os/src/fs/sysfs/inode.rs index 5f952442..3f8fa0e8 100644 --- a/os/src/fs/sysfs/inode.rs +++ b/os/src/fs/sysfs/inode.rs @@ -81,7 +81,7 @@ impl SysfsInode { /// 创建属性文件 inode pub fn new_attribute(attr: SysfsAttr) -> Arc { let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed); - let mode = attr.mode.clone(); + let mode = attr.mode; let now = TimeSpec::now(); Arc::new(Self { inode_no, diff --git a/os/src/fs/sysfs/mod.rs b/os/src/fs/sysfs/mod.rs index 0aa09736..865e3dee 100644 --- a/os/src/fs/sysfs/mod.rs +++ b/os/src/fs/sysfs/mod.rs @@ -78,5 +78,5 @@ mod device_registry; mod inode; mod sysfs; -pub use device_registry::{find_block_device, find_net_device}; +pub use device_registry::find_block_device; pub use sysfs::SysFS; diff --git a/os/src/fs/tests/ext4/ext4_directory.rs b/os/src/fs/tests/ext4/ext4_directory.rs index faf09ccf..2d8093da 100644 --- a/os/src/fs/tests/ext4/ext4_directory.rs +++ b/os/src/fs/tests/ext4/ext4_directory.rs @@ -31,7 +31,7 @@ test_case!(test_ext4_create_directory, { } kassert!(result.is_ok()); - if let Ok(_) = result { + if result.is_ok() { // 验证目录存在 let lookup_result = root.lookup("testdir"); kassert!(lookup_result.is_ok()); diff --git a/os/src/fs/tests/proc/proc_directory.rs b/os/src/fs/tests/proc/proc_directory.rs index b7bd0cb2..f97d8a1c 100644 --- a/os/src/fs/tests/proc/proc_directory.rs +++ b/os/src/fs/tests/proc/proc_directory.rs @@ -108,7 +108,7 @@ test_case!(test_procfs_readdir_entry_types, { ); // 验证名称非空 - kassert!(entry.name.len() > 0); + kassert!(!entry.name.is_empty()); // 验证 inode 号非零 kassert!(entry.inode_no > 0); diff --git a/os/src/fs/tests/tmpfs/tmpfs_sparse.rs b/os/src/fs/tests/tmpfs/tmpfs_sparse.rs index 069aa545..0e81f34f 100644 --- a/os/src/fs/tests/tmpfs/tmpfs_sparse.rs +++ b/os/src/fs/tests/tmpfs/tmpfs_sparse.rs @@ -28,7 +28,7 @@ test_case!(test_tmpfs_sparse_hole, { // 读取开始部分 let mut buf = vec![0u8; 5]; kassert!(file.read_at(0, &mut buf).unwrap() == 5); - kassert!(&buf == data1); + kassert!(buf == data1); // 读取空洞部分(应该返回0) let mut hole_buf = vec![0xFF; 1024]; @@ -42,7 +42,7 @@ test_case!(test_tmpfs_sparse_hole, { // 读取结束部分 let mut buf2 = vec![0u8; 3]; kassert!(file.read_at(offset, &mut buf2).unwrap() == 3); - kassert!(&buf2 == data2); + kassert!(buf2 == data2); }); test_case!(test_tmpfs_sparse_multiple_holes, { @@ -66,16 +66,16 @@ test_case!(test_tmpfs_sparse_multiple_holes, { // 验证各个数据块 let mut buf = vec![0u8; 5]; kassert!(file.read_at(0, &mut buf).unwrap() == 5); - kassert!(&buf == data); + kassert!(buf == data); kassert!(file.read_at(4096, &mut buf).unwrap() == 5); - kassert!(&buf == data); + kassert!(buf == data); kassert!(file.read_at(8192, &mut buf).unwrap() == 5); - kassert!(&buf == data); + kassert!(buf == data); kassert!(file.read_at(16384, &mut buf).unwrap() == 5); - kassert!(&buf == data); + kassert!(buf == data); }); test_case!(test_tmpfs_sparse_truncate_extend, { @@ -99,7 +99,7 @@ test_case!(test_tmpfs_sparse_truncate_extend, { // 读取初始数据 let mut buf = vec![0u8; 7]; kassert!(file.read_at(0, &mut buf).unwrap() == 7); - kassert!(&buf == data); + kassert!(buf == data); // 读取扩展部分(应该是0) let mut hole = vec![0xFF; 1024]; @@ -135,7 +135,7 @@ test_case!(test_tmpfs_sparse_write_beyond_eof, { // 读取实际数据 let mut buf = vec![0u8; 6]; kassert!(file.read_at(offset, &mut buf).unwrap() == 6); - kassert!(&buf == data); + kassert!(buf == data); }); test_case!(test_tmpfs_sparse_fill_hole, { diff --git a/os/src/fs/tmpfs/inode.rs b/os/src/fs/tmpfs/inode.rs index 1a793d17..2261777f 100644 --- a/os/src/fs/tmpfs/inode.rs +++ b/os/src/fs/tmpfs/inode.rs @@ -304,7 +304,7 @@ impl Inode for TmpfsInode { // 更新文件大小和时间 let mut meta = self.metadata.lock(); meta.size = meta.size.max(offset + bytes_written); - meta.blocks = (meta.size + 511) / 512; // 以 512B 为单位 + meta.blocks = meta.size.div_ceil(512); // 以 512B 为单位 drop(meta); self.update_mtime(); @@ -558,8 +558,8 @@ impl Inode for TmpfsInode { if new_size < old_size { // 缩小:释放多余的页 - let new_page_count = (new_size + PAGE_SIZE - 1) / PAGE_SIZE; - let old_page_count = (old_size + PAGE_SIZE - 1) / PAGE_SIZE; + let new_page_count = new_size.div_ceil(PAGE_SIZE); + let old_page_count = old_size.div_ceil(PAGE_SIZE); let mut data = self.data.lock(); @@ -576,7 +576,7 @@ impl Inode for TmpfsInode { } meta.size = new_size; - meta.blocks = (new_size + 511) / 512; + meta.blocks = new_size.div_ceil(512); drop(meta); self.update_mtime(); @@ -624,9 +624,7 @@ impl Inode for TmpfsInode { // 将目标路径写入符号链接文件的数据中 let target_bytes = target.as_bytes(); - if let Err(e) = symlink_inode.write_at(0, target_bytes) { - return Err(e); - } + symlink_inode.write_at(0, target_bytes)?; // 添加到父目录 self.children diff --git a/os/src/fs/tmpfs/mod.rs b/os/src/fs/tmpfs/mod.rs index 2c479389..dbc39761 100644 --- a/os/src/fs/tmpfs/mod.rs +++ b/os/src/fs/tmpfs/mod.rs @@ -42,5 +42,4 @@ mod inode; mod tmpfs; -pub use inode::TmpfsInode; pub use tmpfs::TmpFs; diff --git a/os/src/ipc/pipe.rs b/os/src/ipc/pipe.rs index aa358abd..b00d991a 100644 --- a/os/src/ipc/pipe.rs +++ b/os/src/ipc/pipe.rs @@ -135,6 +135,6 @@ impl PipeRingBuffer { pub fn all_write_ends_dropped(&self) -> bool { self.write_end .as_ref() - .map_or(true, |weak| weak.upgrade().is_none()) + .is_none_or(|weak| weak.upgrade().is_none()) } } diff --git a/os/src/ipc/signal.rs b/os/src/ipc/signal.rs index 78cc08b3..69045d72 100644 --- a/os/src/ipc/signal.rs +++ b/os/src/ipc/signal.rs @@ -114,7 +114,7 @@ pub fn check_signal() { let mut t = task.lock(); let pending_copy = t.pending.clone(); let shared_pending_copy = t.shared_pending.lock().clone(); - let blocked_copy = t.blocked.clone(); + let blocked_copy = t.blocked; if let Some(flag) = first_deliverable_signal(pending_copy.signals, blocked_copy) { let num = signal_from_flag(flag).unwrap(); let action = { @@ -180,9 +180,9 @@ fn install_user_signal_trap_frame( let siginfo = create_siginfo_for_signal(SignalFlags::from_signal_num(sig_num).unwrap()); let sa_flags = SaFlags::from_bits_truncate(action.sa_flags as u32); let uc = UContextT::new( - 0, // TODO: flags未实现 - 0 as *mut _, // TODO: link未实现 - t.signal_stack.lock().clone(), + 0, // TODO: flags未实现 + core::ptr::null_mut(), // TODO: link未实现 + *t.signal_stack.lock(), t.blocked.to_sigset_t(), MContextT::from_trap_frame(tf), ); @@ -313,7 +313,7 @@ impl SignalPending { /// # 参数: /// * `blocked`: 当前阻塞的信号集合 pub fn has_deliverable_signal(&self, blocked: SignalFlags) -> bool { - !first_deliverable_signal(self.signals, blocked).is_none() + first_deliverable_signal(self.signals, blocked).is_some() } /// 获取第一个可投递的信号 diff --git a/os/src/kernel/cpu.rs b/os/src/kernel/cpu.rs index e274d9bb..380866ec 100644 --- a/os/src/kernel/cpu.rs +++ b/os/src/kernel/cpu.rs @@ -23,7 +23,7 @@ lazy_static! { /// 注意:使用 MAX_CPU_COUNT 而不是 NUM_CPU,避免 lazy_static 初始化时机问题 pub static ref CPUS: PerCpu = { use crate::config::MAX_CPU_COUNT; - PerCpu::new_with_id_and_count(MAX_CPU_COUNT, |cpu_id| Cpu::new_with_id(cpu_id)) + PerCpu::new_with_id_and_count(MAX_CPU_COUNT, Cpu::new_with_id) }; } diff --git a/os/src/kernel/scheduler/mod.rs b/os/src/kernel/scheduler/mod.rs index b166b34d..9b1df459 100644 --- a/os/src/kernel/scheduler/mod.rs +++ b/os/src/kernel/scheduler/mod.rs @@ -153,8 +153,7 @@ pub fn yield_task() { pub fn sleep_task_with_block(task: SharedTask, receive_signal: bool) { let cpu_id = { let t = task.lock(); - t.on_cpu - .unwrap_or_else(|| crate::arch::kernel::cpu::cpu_id()) + t.on_cpu.unwrap_or_else(crate::arch::kernel::cpu::cpu_id) }; scheduler_of(cpu_id).lock().sleep_task(task, receive_signal); } @@ -171,7 +170,7 @@ pub fn wake_up_with_block(task: SharedTask) { // 关键:多核下 wake 可能被重复触发(不同 CPU/不同事件源),必须做到“全局幂等”: // - 若任务已经是 Running(正在跑/已入队),则不要再次入队到其他 CPU 的运行队列 // 否则同一任务可能被两个 CPU 同时调度运行,导致 TrapFrame/上下文被并发破坏(海森堡 panic/挂起)。 - let mut should_ipi = false; + let should_ipi; { let mut sched = scheduler_of(target_cpu).lock(); @@ -216,8 +215,7 @@ pub fn wake_up_with_block(task: SharedTask) { pub fn exit_task_with_block(task: SharedTask) { let cpu_id = { let t = task.lock(); - t.on_cpu - .unwrap_or_else(|| crate::arch::kernel::cpu::cpu_id()) + t.on_cpu.unwrap_or_else(crate::arch::kernel::cpu::cpu_id) }; scheduler_of(cpu_id).lock().exit_task(task); } @@ -235,9 +233,7 @@ pub fn sleep_task_with_guard_and_block( stask: SharedTask, receive_signal: bool, ) { - let cpu_id = task - .on_cpu - .unwrap_or_else(|| crate::arch::kernel::cpu::cpu_id()); + let cpu_id = task.on_cpu.unwrap_or_else(crate::arch::kernel::cpu::cpu_id); scheduler_of(cpu_id) .lock() .sleep_task_with_guard(task, stask, receive_signal); diff --git a/os/src/kernel/scheduler/task_queue.rs b/os/src/kernel/scheduler/task_queue.rs index 466929f5..49a63841 100644 --- a/os/src/kernel/scheduler/task_queue.rs +++ b/os/src/kernel/scheduler/task_queue.rs @@ -147,7 +147,7 @@ mod tests { let mut q = TaskQueue::new(); kassert!(q.is_empty()); let t = mk_task(40); - q.add_task(t.clone()); + q.add_task(t); kassert!(!q.is_empty()); let _ = q.pop_task(); kassert!(q.is_empty()); diff --git a/os/src/kernel/scheduler/wait_queue.rs b/os/src/kernel/scheduler/wait_queue.rs index 9c2a9cae..3818fda8 100644 --- a/os/src/kernel/scheduler/wait_queue.rs +++ b/os/src/kernel/scheduler/wait_queue.rs @@ -2,7 +2,7 @@ //! //! 定义了等待队列结构体及其相关操作 use crate::kernel::task::SharedTask; -use crate::kernel::{TaskQueue, sleep_task_with_block, wake_up_with_block, yield_task}; +use crate::kernel::{TaskQueue, sleep_task_with_block, wake_up_with_block}; use crate::sync::RawSpinLock; use alloc::vec::Vec; diff --git a/os/src/kernel/syscall/fcntl.rs b/os/src/kernel/syscall/fcntl.rs index 33ef2ef3..bb50f23b 100644 --- a/os/src/kernel/syscall/fcntl.rs +++ b/os/src/kernel/syscall/fcntl.rs @@ -1,7 +1,7 @@ //! fcntl 系统调用实现 use crate::arch::trap::SumGuard; -use crate::kernel::{current_cpu, current_task}; +use crate::kernel::current_task; use crate::uapi::errno::EINVAL; use crate::uapi::fcntl::{FcntlCmd, FdFlags, FileStatusFlags, Flock, LockType}; use crate::vfs::{FsError, OpenFlags, file_lock_manager}; diff --git a/os/src/kernel/syscall/fs.rs b/os/src/kernel/syscall/fs.rs index 82de8705..af195b7b 100644 --- a/os/src/kernel/syscall/fs.rs +++ b/os/src/kernel/syscall/fs.rs @@ -2,12 +2,12 @@ use core::ffi::c_char; -use alloc::{string::ToString, sync::Arc}; +use alloc::string::ToString; use crate::{ arch::trap::SumGuard, kernel::{ - current_cpu, current_task, + current_task, syscall::util::{ create_file_at, create_file_from_dentry, get_path_safe, resolve_at_path, resolve_at_path_with_flags, @@ -19,8 +19,8 @@ use crate::{ time::TimeSpec, }, vfs::{ - DENTRY_CACHE, Dentry, FileMode, FsError, InodeType, OpenFlags, RegFile, SeekWhence, Stat, - Statx, split_path, vfs_lookup, + DENTRY_CACHE, Dentry, FileMode, FsError, InodeType, OpenFlags, SeekWhence, Stat, Statx, + split_path, vfs_lookup, }, }; @@ -31,19 +31,18 @@ pub const O_CLOEXEC: u32 = 0o2000000; pub fn close(fd: usize) -> isize { let task = current_task(); - let mut task_lock = task.lock(); + let task_lock = task.lock(); let tid = task_lock.tid as usize; // If this fd is a socket, also remove the (tid, fd) -> socket handle mapping. // Otherwise, fd reuse can accidentally refer to a stale socket handle. - if let Ok(file) = task_lock.fd_table.get(fd) { - if file + if let Ok(file) = task_lock.fd_table.get(fd) + && file .as_any() .downcast_ref::() .is_some() - { - crate::net::socket::unregister_socket_fd(tid, fd); - } + { + crate::net::socket::unregister_socket_fd(tid, fd); } match task_lock.fd_table.close(fd) { @@ -158,19 +157,17 @@ pub fn openat(dirfd: i32, pathname: *const c_char, flags: u32, mode: u32) -> isi }; // 检查 O_DIRECTORY (必须是目录) - if open_flags.contains(OpenFlags::O_DIRECTORY) { - if meta.inode_type != InodeType::Directory { - return FsError::NotDirectory.to_errno(); - } + if open_flags.contains(OpenFlags::O_DIRECTORY) && meta.inode_type != InodeType::Directory { + return FsError::NotDirectory.to_errno(); } // 处理 O_TRUNC (截断文件) - if open_flags.contains(OpenFlags::O_TRUNC) && open_flags.writable() { - if meta.inode_type == InodeType::File { - if let Err(e) = dentry.inode.truncate(0) { - return e.to_errno(); - } - } + if open_flags.contains(OpenFlags::O_TRUNC) + && open_flags.writable() + && meta.inode_type == InodeType::File + && let Err(e) = dentry.inode.truncate(0) + { + return e.to_errno(); } // 创建 File 对象 @@ -413,7 +410,7 @@ pub fn getdents64(fd: usize, dirp: *mut u8, count: usize) -> isize { unsafe { let _guard = SumGuard::new(); - for (_, entry) in entries.iter().skip(start_index).enumerate() { + for entry in entries.iter().skip(start_index) { // 计算这个 dirent 需要的空间 let dirent_len = LinuxDirent64::total_len(&entry.name); @@ -648,7 +645,7 @@ pub fn readlinkat(dirfd: i32, pathname: *const c_char, buf: *mut u8, bufsiz: usi Ok(s) => s, Err(e) => return e.to_errno(), }; - let bytes_read = core::cmp::min(target.as_bytes().len(), bufsiz); + let bytes_read = core::cmp::min(target.len(), bufsiz); // 复制到用户空间(注意:readlink 不添加 null 终止符) { diff --git a/os/src/kernel/syscall/io.rs b/os/src/kernel/syscall/io.rs index e910f629..1da2a6d7 100644 --- a/os/src/kernel/syscall/io.rs +++ b/os/src/kernel/syscall/io.rs @@ -35,17 +35,16 @@ pub fn write(fd: usize, buf: *const u8, count: usize) -> isize { // 对 blocking socket:EAGAIN 时主动 poll + yield 重试(驱动网络前进) if result == -11 { use crate::net::socket::SocketFile; - if let Some(socket_file) = file.as_any().downcast_ref::() { - if !socket_file + if let Some(socket_file) = file.as_any().downcast_ref::() + && !socket_file .flags() .contains(crate::uapi::fcntl::OpenFlags::O_NONBLOCK) - { - drop(file); - drop(task); - crate::net::socket::poll_network_and_dispatch(); - crate::kernel::yield_task(); - continue; - } + { + drop(file); + drop(task); + crate::net::socket::poll_network_and_dispatch(); + crate::kernel::yield_task(); + continue; } } @@ -80,17 +79,16 @@ pub fn read(fd: usize, buf: *mut u8, count: usize) -> isize { // 对 blocking socket:EAGAIN 时主动 poll + yield 重试(驱动网络前进) if result == -11 { use crate::net::socket::SocketFile; - if let Some(socket_file) = file.as_any().downcast_ref::() { - if !socket_file + if let Some(socket_file) = file.as_any().downcast_ref::() + && !socket_file .flags() .contains(crate::uapi::fcntl::OpenFlags::O_NONBLOCK) - { - drop(file); - drop(task); - crate::net::socket::poll_network_and_dispatch(); - crate::kernel::yield_task(); - continue; - } + { + drop(file); + drop(task); + crate::net::socket::poll_network_and_dispatch(); + crate::kernel::yield_task(); + continue; } } @@ -237,16 +235,14 @@ pub fn pread64(fd: usize, buf: *mut u8, count: usize, offset: i64) -> isize { Err(e) => return e.to_errno(), }; - let result = { + { let _guard = SumGuard::new(); let buffer = unsafe { core::slice::from_raw_parts_mut(buf, count) }; match file.read_at(offset as usize, buffer) { Ok(n) => n as isize, Err(e) => e.to_errno(), } - }; - - result + } } /// 位置写入:向指定位置写入数据,不改变文件偏移量 @@ -266,16 +262,14 @@ pub fn pwrite64(fd: usize, buf: *const u8, count: usize, offset: i64) -> isize { Err(e) => return e.to_errno(), }; - let result = { + { let _guard = SumGuard::new(); let buffer = unsafe { core::slice::from_raw_parts(buf, count) }; match file.write_at(offset as usize, buffer) { Ok(n) => n as isize, Err(e) => e.to_errno(), } - }; - - result + } } /// 向量化位置读取:从指定位置读取数据到多个缓冲区,不改变文件偏移量 @@ -626,10 +620,10 @@ fn poll_with_timeout( crate::net::socket::poll_network_and_dispatch(); // Check if woken by timeout - if let Some(trigger) = timeout_trigger { - if get_time() >= trigger { - return 0; - } + if let Some(trigger) = timeout_trigger + && get_time() >= trigger + { + return 0; } } } @@ -778,13 +772,13 @@ fn select_common( let mut ready_count = 0; let mut read_set = input_read.as_ref().map(|_| FdSet::new()); let mut write_set = input_write.as_ref().map(|_| FdSet::new()); - let mut except_set = input_except.as_ref().map(|_| FdSet::new()); + let except_set = input_except.as_ref().map(|_| FdSet::new()); let task_lock = task.lock(); for fd in 0..nfds { - let check_read = input_read.as_ref().map_or(false, |s| s.is_set(fd)); - let check_write = input_write.as_ref().map_or(false, |s| s.is_set(fd)); - let check_except = input_except.as_ref().map_or(false, |s| s.is_set(fd)); + let check_read = input_read.as_ref().is_some_and(|s| s.is_set(fd)); + let check_write = input_write.as_ref().is_some_and(|s| s.is_set(fd)); + let check_except = input_except.as_ref().is_some_and(|s| s.is_set(fd)); if !check_read && !check_write && !check_except { continue; @@ -806,17 +800,19 @@ fn select_common( }; let mut fd_ready = false; - if check_read && file.readable() { - if let Some(ref mut set) = read_set { - set.set(fd); - fd_ready = true; - } - } - if check_write && file.writable() { - if let Some(ref mut set) = write_set { - set.set(fd); - fd_ready = true; - } + if check_read + && file.readable() + && let Some(ref mut set) = read_set + { + set.set(fd); + fd_ready = true; + } + if check_write + && file.writable() + && let Some(ref mut set) = write_set + { + set.set(fd); + fd_ready = true; } // exceptfds: OOB data, errors (not implemented yet) if fd_ready { diff --git a/os/src/kernel/syscall/ioctl.rs b/os/src/kernel/syscall/ioctl.rs index aab4b123..7f79e8f6 100644 --- a/os/src/kernel/syscall/ioctl.rs +++ b/os/src/kernel/syscall/ioctl.rs @@ -206,7 +206,7 @@ fn handle_fionread(file: &alloc::sync::Arc, arg: usize) -> } /// FIOASYNC - 设置/清除异步 I/O 通知 -fn handle_fioasync(file: &alloc::sync::Arc, arg: usize) -> isize { +fn handle_fioasync(_file: &alloc::sync::Arc, arg: usize) -> isize { unsafe { let _guard = SumGuard::new(); let value_ptr = arg as *const i32; diff --git a/os/src/kernel/syscall/ipc.rs b/os/src/kernel/syscall/ipc.rs index bda300ee..e69d688b 100644 --- a/os/src/kernel/syscall/ipc.rs +++ b/os/src/kernel/syscall/ipc.rs @@ -4,7 +4,7 @@ use alloc::sync::Arc; use crate::{ arch::trap::SumGuard, - kernel::{current_cpu, current_task}, + kernel::current_task, vfs::{FdFlags, File, FsError, OpenFlags, PipeFile}, }; @@ -53,11 +53,10 @@ pub fn pipe2(pipefd: *mut i32, flags: u32) -> isize { let fd_table = current_task().lock().fd_table.clone(); // 分配文件描述符 - let read_fd = - match fd_table.alloc_with_flags(Arc::new(pipe_read) as Arc, fd_flags.clone()) { - Ok(fd) => fd, - Err(e) => return e.to_errno(), - }; + let read_fd = match fd_table.alloc_with_flags(Arc::new(pipe_read) as Arc, fd_flags) { + Ok(fd) => fd, + Err(e) => return e.to_errno(), + }; let write_fd = match fd_table.alloc_with_flags(Arc::new(pipe_write) as Arc, fd_flags) { diff --git a/os/src/kernel/syscall/mm.rs b/os/src/kernel/syscall/mm.rs index 2eea7a35..9a06c53e 100644 --- a/os/src/kernel/syscall/mm.rs +++ b/os/src/kernel/syscall/mm.rs @@ -6,7 +6,7 @@ use crate::mm::address::{PageNum, UsizeConvert, Vaddr, Vpn, VpnRange}; use crate::mm::memory_space::MmapFile; use crate::mm::memory_space::mapping_area::AreaType; use crate::mm::page_table::UniversalPTEFlag; -use crate::uapi::errno::{EACCES, EBADF, EEXIST, EINVAL, EIO, ENOMEM, EOPNOTSUPP}; +use crate::uapi::errno::{EACCES, EBADF, EEXIST, EINVAL, EIO, ENOMEM}; use crate::uapi::mm::{MAP_FAILED, MapFlags, ProtFlags}; use crate::{pr_err, pr_warn}; @@ -123,7 +123,7 @@ pub fn mmap(addr: *mut c_void, len: usize, prot: i32, flags: i32, fd: i32, offse // 创建 MmapFile(如果是文件映射) let mmap_file = if !map_flags.contains(MapFlags::ANONYMOUS) { // 文件映射:验证文件描述符和偏移量 - if offset < 0 || (offset as usize) % PAGE_SIZE != 0 { + if offset < 0 || !(offset as usize).is_multiple_of(PAGE_SIZE) { pr_err!("mmap: file offset must be non-negative and page-aligned"); return -EINVAL as isize; } @@ -290,24 +290,24 @@ pub fn mmap(addr: *mut c_void, len: usize, prot: i32, flags: i32, fd: i32, offse } // 如果是文件映射,立即加载数据 - if let Some(area) = space.areas_mut().last_mut() { - if let Err(e) = area.load_from_file() { - pr_err!( - "mmap failed to load file data: {:?}, addr=0x{:x}, len=0x{:x}, fd={}", - e, - start_addr, - len, - fd + if let Some(area) = space.areas_mut().last_mut() + && let Err(e) = area.load_from_file() + { + pr_err!( + "mmap failed to load file data: {:?}, addr=0x{:x}, len=0x{:x}, fd={}", + e, + start_addr, + len, + fd + ); + // 加载失败,清理已创建的映射 + if let Err(unmap_err) = space.munmap(start_addr, len) { + pr_warn!( + "mmap: failed to clean up mapping on load error: {:?}", + unmap_err ); - // 加载失败,清理已创建的映射 - if let Err(unmap_err) = space.munmap(start_addr, len) { - pr_warn!( - "mmap: failed to clean up mapping on load error: {:?}", - unmap_err - ); - } - return -EIO as isize; } + return -EIO as isize; } start_addr as isize @@ -384,7 +384,7 @@ pub fn mprotect(addr: *mut c_void, len: usize, prot: i32) -> isize { } // 检查地址对齐 - if start % PAGE_SIZE != 0 { + if !start.is_multiple_of(PAGE_SIZE) { pr_err!("mprotect: address not page-aligned: 0x{:x}", start); return -EINVAL as isize; } diff --git a/os/src/kernel/syscall/network.rs b/os/src/kernel/syscall/network.rs index 5ea615f6..ddf75873 100644 --- a/os/src/kernel/syscall/network.rs +++ b/os/src/kernel/syscall/network.rs @@ -217,7 +217,7 @@ pub fn socket(domain: i32, socket_type: i32, _protocol: i32) -> isize { let socket_file = Arc::new(SocketFile::new_with_flags(handle, open_flags)); let task = current_task(); - let mut task_lock = task.lock(); + let task_lock = task.lock(); let tid = task_lock.tid; match task_lock.fd_table.alloc_with_flags(socket_file, fd_flags) { Ok(fd) => { @@ -286,12 +286,11 @@ pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { }; // Linux behavior: binding an already-bound TCP socket is invalid. - if let Some(sf) = file.as_any().downcast_ref::() { - if let Some(old) = sf.get_local_endpoint() { - if old.port != 0 { - return -22; // EINVAL - } - } + if let Some(sf) = file.as_any().downcast_ref::() + && let Some(old) = sf.get_local_endpoint() + && old.port != 0 + { + return -22; // EINVAL } if set_socket_local_endpoint(&file, endpoint).is_err() { @@ -304,12 +303,11 @@ pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { // one smoltcp UDP socket per local port, and per-fd queues filtered by remote endpoint. // Linux behavior: binding an already-bound UDP socket is invalid. - if let Some(sf) = file.as_any().downcast_ref::() { - if let Some(old) = sf.get_local_endpoint() { - if old.port != 0 { - return -22; // EINVAL - } - } + if let Some(sf) = file.as_any().downcast_ref::() + && let Some(old) = sf.get_local_endpoint() + && old.port != 0 + { + return -22; // EINVAL } // Bind port 0 => allocate an ephemeral port. @@ -331,6 +329,7 @@ pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { IpAddress::Ipv6(a) if a.is_unspecified() => None, #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(_) => Some(endpoint.addr), + #[cfg(not(feature = "proto-ipv6"))] _ => None, }; @@ -444,7 +443,7 @@ pub fn listen(sockfd: i32, backlog: i32) -> isize { socket_file.set_listener(true); socket_file.clear_listen_sockets(); // iperf 会传入非常大的 backlog(甚至 INT_MAX),这里做一个上限避免内存/逻辑风险 - let backlog = (backlog as usize).max(1).min(128); + let backlog = (backlog as usize).clamp(1, 128); socket_file.set_listen_backlog(backlog); 0 } @@ -478,7 +477,7 @@ pub fn accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { let is_nonblock = socket_file .flags() .contains(crate::uapi::fcntl::OpenFlags::O_NONBLOCK); - let backlog = socket_file.listen_backlog().max(1).min(128); + let backlog = socket_file.listen_backlog().clamp(1, 128); loop { // 推进 loopback + 网络状态机 @@ -645,6 +644,7 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { IpAddress::Ipv4(addr) => addr.octets()[0] == 127, #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(addr) => addr.is_loopback(), + #[cfg(not(feature = "proto-ipv6"))] _ => false, }; @@ -665,11 +665,12 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { IpAddress::Ipv6(_) => { use smoltcp::wire::Ipv6Address; if is_loopback { - IpAddress::Ipv6(Ipv6Address::LOOPBACK) + IpAddress::Ipv6(Ipv6Address::LOCALHOST) } else { IpAddress::Ipv6(Ipv6Address::UNSPECIFIED) } } + #[cfg(not(feature = "proto-ipv6"))] _ => IpAddress::Ipv4(Ipv4Address::new(10, 0, 2, 15)), }; IpEndpoint::new(local_addr, 0) @@ -761,7 +762,7 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(a) if a.is_loopback() => { use smoltcp::wire::Ipv6Address; - IpAddress::Ipv6(Ipv6Address::LOOPBACK) + IpAddress::Ipv6(Ipv6Address::LOCALHOST) } _ => IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), }; @@ -775,7 +776,7 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(a) if a.is_loopback() => { use smoltcp::wire::Ipv6Address; - Some(IpAddress::Ipv6(Ipv6Address::LOOPBACK)) + Some(IpAddress::Ipv6(Ipv6Address::LOCALHOST)) } _ => None, }; @@ -834,18 +835,17 @@ pub fn send(sockfd: i32, buf: *const u8, len: usize, _flags: i32) -> isize { } Err(e) => { pr_debug!("send: sockfd={}, len={} -> error={:?}", sockfd, len, e); - if e == crate::vfs::FsError::WouldBlock { - if let Some(socket_file) = file.as_any().downcast_ref::() { - if !socket_file.flags().contains(OpenFlags::O_NONBLOCK) { - drop(file); - crate::net::socket::poll_network_and_dispatch(); - crate::kernel::yield_task(); - if crate::ipc::signal_interrupts_syscall(&task) { - return -(crate::uapi::errno::EINTR as isize); - } - continue; - } + if e == crate::vfs::FsError::WouldBlock + && let Some(socket_file) = file.as_any().downcast_ref::() + && !socket_file.flags().contains(OpenFlags::O_NONBLOCK) + { + drop(file); + crate::net::socket::poll_network_and_dispatch(); + crate::kernel::yield_task(); + if crate::ipc::signal_interrupts_syscall(&task) { + return -(crate::uapi::errno::EINTR as isize); } + continue; } return e.to_errno(); } @@ -887,18 +887,17 @@ pub fn recv(sockfd: i32, buf: *mut u8, len: usize, _flags: i32) -> isize { } Err(e) => { pr_debug!("recv: sockfd={}, len={} -> error={:?}", sockfd, len, e); - if e == crate::vfs::FsError::WouldBlock { - if let Some(socket_file) = file.as_any().downcast_ref::() { - if !socket_file.flags().contains(OpenFlags::O_NONBLOCK) { - drop(file); - crate::net::socket::poll_network_and_dispatch(); - crate::kernel::yield_task(); - if crate::ipc::signal_interrupts_syscall(&task) { - return -(crate::uapi::errno::EINTR as isize); - } - continue; - } + if e == crate::vfs::FsError::WouldBlock + && let Some(socket_file) = file.as_any().downcast_ref::() + && !socket_file.flags().contains(OpenFlags::O_NONBLOCK) + { + drop(file); + crate::net::socket::poll_network_and_dispatch(); + crate::kernel::yield_task(); + if crate::ipc::signal_interrupts_syscall(&task) { + return -(crate::uapi::errno::EINTR as isize); } + continue; } return e.to_errno(); } @@ -909,7 +908,7 @@ pub fn recv(sockfd: i32, buf: *mut u8, len: usize, _flags: i32) -> isize { /// 关闭套接字 pub fn close_sock(sockfd: i32) -> isize { let task = current_task(); - let mut task_lock = task.lock(); + let task_lock = task.lock(); let tid = task_lock.tid; unregister_socket_fd(tid as usize, sockfd as usize); @@ -926,10 +925,7 @@ unsafe fn get_c_str_safe(ptr: *const c_char) -> Option<&'static str> { return None; } - match unsafe { CStr::from_ptr(ptr) }.to_str() { - Ok(s) => Some(s), - Err(_) => None, - } + unsafe { CStr::from_ptr(ptr) }.to_str().ok() } /// 获取网络接口统计信息 @@ -1093,7 +1089,7 @@ pub fn getifaddrs(ifap: *mut *mut u8) -> isize { 0, ); - if addr < 0 || addr == 0 { + if addr <= 0 { return -(ENOMEM as isize); } @@ -1246,6 +1242,7 @@ unsafe fn fill_sockaddr_from_ip(addr: *mut SockAddrIn, ip: smoltcp::wire::IpAddr // IPv6 需要不同的结构体,这里暂不支持 sockaddr.sin_addr = [0; 4]; } + #[cfg(not(feature = "proto-ipv6"))] _ => { sockaddr.sin_addr = [0; 4]; } @@ -1352,7 +1349,7 @@ pub fn freeifaddrs(ifa: *mut u8) -> isize { // 设置网络接口配置 pub fn setsockopt(sockfd: i32, level: i32, optname: i32, optval: *const u8, optlen: u32) -> isize { use crate::arch::trap::SumGuard; - use crate::kernel::current_cpu; + use crate::uapi::errno::{EBADF, EINVAL, ENOPROTOOPT, ENOTSOCK}; use crate::uapi::socket::*; @@ -1529,7 +1526,7 @@ pub fn accept4(sockfd: i32, addr: *mut u8, addrlen: *mut u32, flags: i32) -> isi let fd_usize = fd as usize; if flags & SOCK_CLOEXEC != 0 { - let mut task_lock = task.lock(); + let task_lock = task.lock(); if let Ok(old_flags) = task_lock.fd_table.get_fd_flags(fd_usize) { let _ = task_lock .fd_table @@ -1665,19 +1662,18 @@ pub fn recvfrom( pr_debug!("recvfrom: sockfd={}, len={} -> error={:?}", sockfd, len, e); if e == crate::vfs::FsError::WouldBlock { use crate::net::socket::SocketFile; - if let Some(socket_file) = file.as_any().downcast_ref::() { - if !socket_file + if let Some(socket_file) = file.as_any().downcast_ref::() + && !socket_file .flags() .contains(crate::uapi::fcntl::OpenFlags::O_NONBLOCK) - { - drop(file); - crate::net::socket::poll_network_and_dispatch(); - crate::kernel::yield_task(); - if crate::ipc::signal_interrupts_syscall(&task) { - return -(crate::uapi::errno::EINTR as isize); - } - continue; + { + drop(file); + crate::net::socket::poll_network_and_dispatch(); + crate::kernel::yield_task(); + if crate::ipc::signal_interrupts_syscall(&task) { + return -(crate::uapi::errno::EINTR as isize); } + continue; } } return e.to_errno(); @@ -1692,7 +1688,7 @@ pub fn shutdown(sockfd: i32, how: i32) -> isize { const SHUT_WR: i32 = 1; const SHUT_RDWR: i32 = 2; - if how < 0 || how > 2 { + if !(0..=2).contains(&how) { return -22; // EINVAL } @@ -1728,10 +1724,8 @@ pub fn shutdown(sockfd: i32, how: i32) -> isize { _ => unreachable!(), // 这里是不可到达的到达即意味着有问题 }; - if should_close_tcp { - if let SocketHandle::Tcp(h) = handle { - network_stack().tcp_close(h); - } + if should_close_tcp && let SocketHandle::Tcp(h) = handle { + network_stack().tcp_close(h); } 0 diff --git a/os/src/kernel/syscall/signal.rs b/os/src/kernel/syscall/signal.rs index 95f95bb4..b54b5bb3 100644 --- a/os/src/kernel/syscall/signal.rs +++ b/os/src/kernel/syscall/signal.rs @@ -113,7 +113,7 @@ pub fn rt_sigaction(signum: c_int, act: *const SignalAction, oldact: *mut Signal let t = task.lock(); if !oldact.is_null() { - let current_action = t.signal_handlers.lock().actions[signum as usize].clone(); + let current_action = t.signal_handlers.lock().actions[signum as usize]; unsafe { write_to_user(oldact, current_action); } @@ -257,7 +257,7 @@ pub fn signal_stack(uss: *const StackT, uoss: *mut StackT) -> c_int { let mut t = task.lock(); if !uoss.is_null() { - let old_ss = t.signal_stack.lock().clone(); + let old_ss = *t.signal_stack.lock(); unsafe { write_to_user(uoss, old_ss); } @@ -411,7 +411,7 @@ fn wait_for_signal( .unwrap(); let sig_num = flag.to_signal_number(); t.pending.signals.remove(flag); - return Ok((sig_num, create_siginfo_for_signal(flag))); + Ok((sig_num, create_siginfo_for_signal(flag))) } else { Err(-EAGAIN) } @@ -441,7 +441,7 @@ fn wait_for_signal( .unwrap(); let sig_num = flag.to_signal_number(); t.pending.signals.remove(flag); - return Ok((sig_num, create_siginfo_for_signal(flag))); + Ok((sig_num, create_siginfo_for_signal(flag))) } } else { // 阻塞等待 @@ -463,6 +463,6 @@ fn wait_for_signal( .unwrap(); let sig_num = flag.to_signal_number(); t.pending.signals.remove(flag); - return Ok((sig_num, create_siginfo_for_signal(flag))); + Ok((sig_num, create_siginfo_for_signal(flag))) } } diff --git a/os/src/kernel/syscall/task.rs b/os/src/kernel/syscall/task.rs index ad09bdeb..7903f7ef 100644 --- a/os/src/kernel/syscall/task.rs +++ b/os/src/kernel/syscall/task.rs @@ -540,10 +540,8 @@ pub fn wait4(pid: c_int, wstatus: *mut c_int, options: c_int, _rusage: *mut Rusa if let Some(res) = t.check_child(cond, !opt.contains(WaitFlags::NOWAIT)) { crate::pr_debug!("wait4: found child pid={}", res.lock().pid); break res; - } else { - if opt.contains(WaitFlags::NOHANG) { - return 0; - } + } else if opt.contains(WaitFlags::NOHANG) { + return 0; } { let mut wc = t.wait_child.lock(); @@ -653,12 +651,12 @@ pub fn get_pgid(pid: c_int) -> c_int { /// 设置进程组 ID pub fn set_pgid(pid: c_int, pgid: c_int) -> c_int { - use crate::uapi::errno::{EACCES, EINVAL, EPERM, ESRCH}; + use crate::uapi::errno::{EINVAL, EPERM, ESRCH}; let current = current_task(); let current_locked = current.lock(); let current_pid = current_locked.tid as c_int; - let current_ppid = current_locked.ppid as c_int; + let _current_ppid = current_locked.ppid as c_int; drop(current_locked); let target_pid = if pid == 0 { current_pid } else { pid }; @@ -921,7 +919,7 @@ pub fn getitimer(which: c_int, curr_value: *mut Itimerval) -> c_int { let mut val = Itimerval::zero(); if let Some(timer) = TIMER.lock().find_entry(&owner, sig) { let now = get_time(); - let remaining = if *timer.0 > now { *timer.0 - now } else { 0 }; + let remaining = (*timer.0).saturating_sub(now); let it_value = TimeSpec::from_freq(remaining, clock_freq()).to_timeval(); let it_interval = timer.1.it_interval.to_timeval(); val = Itimerval { @@ -969,7 +967,7 @@ pub fn setitimer(which: c_int, new_value: *const Itimerval, old_value: *mut Itim let mut old = Itimerval::zero(); if let Some(timer) = binding.find_entry(&owner, sig) { let now = get_time(); - let remaining = if *timer.0 > now { *timer.0 - now } else { 0 }; + let remaining = (*timer.0).saturating_sub(now); let it_value = TimeSpec::from_freq(remaining, clock_freq()).to_timeval(); let it_interval = timer.1.it_interval.to_timeval(); old = Itimerval { @@ -1042,7 +1040,7 @@ pub fn futex( } else { return -EFAULT; }; - if user_val != val as u32 { + if user_val != val { return -EAGAIN; } diff --git a/os/src/kernel/syscall/util.rs b/os/src/kernel/syscall/util.rs index c91c8175..483bab05 100644 --- a/os/src/kernel/syscall/util.rs +++ b/os/src/kernel/syscall/util.rs @@ -183,7 +183,7 @@ pub fn validate_syslog_args(action: SyslogAction, bufp: *mut u8, len: i32) -> Re SyslogAction::ConsoleLevel => { // Linux 要求 console_loglevel 在 1-8 范围内 // 参考:kernel/printk/printk.c - if len < 1 || len > 8 { + if !(1..=8).contains(&len) { return Err(EINVAL); } } diff --git a/os/src/kernel/task/task_manager.rs b/os/src/kernel/task/task_manager.rs index 85d8820d..fb22e572 100644 --- a/os/src/kernel/task/task_manager.rs +++ b/os/src/kernel/task/task_manager.rs @@ -130,7 +130,7 @@ impl TaskManagerTrait for TaskManager { fn exit_task(&mut self, task: SharedTask, code: i32) { { let mut task = task.lock(); - task.exit_code = Some(code as i32); + task.exit_code = Some(code); // Linux 语义:线程退出时应释放其对用户地址空间的引用。 // 线程组共享的地址空间由 Arc 计数管理:最后一个线程退出时自动释放。 task.memory_space = None; @@ -266,7 +266,7 @@ mod tests { let g = exited_task.lock(); // 验证任务管理器设置了返回值 (新的责任) - kassert!(g.exit_code == Some(EXIT_CODE as i32)); + kassert!(g.exit_code == Some(EXIT_CODE)); // 验证调度器设置了状态 (调度器的责任) kassert!(g.state == TaskState::Zombie); diff --git a/os/src/kernel/timer.rs b/os/src/kernel/timer.rs index 0fd713fd..34e9c91a 100644 --- a/os/src/kernel/timer.rs +++ b/os/src/kernel/timer.rs @@ -48,10 +48,10 @@ impl TimerQueue { /// # 返回值: /// - 已到期的任务(如果有) pub fn pop_due_task(&mut self, current_time: usize) -> Option { - if let Some((&trigger_time, _)) = self.queue.iter().next() { - if trigger_time <= current_time { - return self.queue.remove(&trigger_time); - } + if let Some((&trigger_time, _)) = self.queue.iter().next() + && trigger_time <= current_time + { + return self.queue.remove(&trigger_time); } None } @@ -124,10 +124,10 @@ impl TimerEntries { /// # 返回值: /// - 已到期的定时器条目(如果有) pub fn pop_due_entry(&mut self, current_time: usize) -> Option { - if let Some((&trigger_time, _)) = self.entries.iter().next() { - if trigger_time <= current_time { - return self.entries.remove(&trigger_time); - } + if let Some((&trigger_time, _)) = self.entries.iter().next() + && trigger_time <= current_time + { + return self.entries.remove(&trigger_time); } None } diff --git a/os/src/log/buffer.rs b/os/src/log/buffer.rs index 8268cc76..11b72aad 100644 --- a/os/src/log/buffer.rs +++ b/os/src/log/buffer.rs @@ -187,7 +187,6 @@ struct ReaderData { impl GlobalLogBuffer { /// 在编译时创建一个新的全局日志缓冲区 pub(super) const fn new() -> Self { - const EMPTY: LogEntry = LogEntry::empty(); Self { writer_data: CachePadded64 { inner: WriterData { @@ -200,7 +199,7 @@ impl GlobalLogBuffer { dropped: AtomicUsize::new(0), }, }, - buffer: [EMPTY; MAX_LOG_ENTRIES], + buffer: [const { LogEntry::empty() }; MAX_LOG_ENTRIES], unread_bytes: AtomicUsize::new(0), } } @@ -288,11 +287,11 @@ impl GlobalLogBuffer { let read_seq = self.reader_data.read_seq.load(Ordering::Acquire); let slot = read_seq % MAX_LOG_ENTRIES; - let slot_ptr = unsafe { self.buffer.as_ptr().add(slot) as *const LogEntry }; + let slot_ptr = unsafe { self.buffer.as_ptr().add(slot) }; - const EMPTY: LogEntry = LogEntry::empty(); + let empty = LogEntry::empty(); unsafe { - if !EMPTY.is_ready(slot_ptr, read_seq) { + if !empty.is_ready(slot_ptr, read_seq) { return None; } } @@ -364,12 +363,12 @@ impl GlobalLogBuffer { // 计算缓冲区索引 let slot = index % MAX_LOG_ENTRIES; - let slot_ptr = unsafe { self.buffer.as_ptr().add(slot) as *const LogEntry }; + let slot_ptr = unsafe { self.buffer.as_ptr().add(slot) }; // 检查序列号是否匹配(确保数据有效) - const EMPTY: LogEntry = LogEntry::empty(); + let empty = LogEntry::empty(); unsafe { - if !EMPTY.is_ready(slot_ptr, index) { + if !empty.is_ready(slot_ptr, index) { return None; } } diff --git a/os/src/log/tests/format.rs b/os/src/log/tests/format.rs index ca8289b0..b7ec5356 100644 --- a/os/src/log/tests/format.rs +++ b/os/src/log/tests/format.rs @@ -6,7 +6,7 @@ test_case!(test_message_truncation, { let log = LogCore::new(LogLevel::Debug, LogLevel::Warning); // Create a long message (>256 bytes) - let long_msg = alloc::format!("{}", "a".repeat(300)); + let long_msg = "a".repeat(300); test_log!(log, LogLevel::Info, "{}", long_msg); let entry = log._read_log().unwrap(); diff --git a/os/src/mm/frame_allocator/mod.rs b/os/src/mm/frame_allocator/mod.rs index 2c6f0a91..ff6f8d38 100644 --- a/os/src/mm/frame_allocator/mod.rs +++ b/os/src/mm/frame_allocator/mod.rs @@ -202,7 +202,7 @@ mod frame_allocator_tests { let ppn = frames.range().start().as_usize(); // 验证对齐 - kassert!(ppn % 16 == 0); + kassert!(ppn.is_multiple_of(16)); }); // 6. 大量分配测试 diff --git a/os/src/mm/memory_space/mapping_area.rs b/os/src/mm/memory_space/mapping_area.rs index 28733b16..e854375c 100644 --- a/os/src/mm/memory_space/mapping_area.rs +++ b/os/src/mm/memory_space/mapping_area.rs @@ -80,7 +80,7 @@ impl MappingArea { } pub fn permission(&self) -> UniversalPTEFlag { - self.permission.clone() + self.permission } pub fn set_permission(&mut self, perm: UniversalPTEFlag) { @@ -178,7 +178,7 @@ impl MappingArea { } }; - page_table.map_with_batch(vpn, ppn, PageSize::Size4K, self.permission.clone(), batch)?; + page_table.map_with_batch(vpn, ppn, PageSize::Size4K, self.permission, batch)?; Ok(()) } @@ -284,7 +284,7 @@ impl MappingArea { vpn_range: self.vpn_range, area_type: self.area_type, map_type: self.map_type, - permission: self.permission.clone(), + permission: self.permission, frames: BTreeMap::new(), // 不克隆帧 // fork 时复制文件映射信息(MAP_SHARED 和 MAP_PRIVATE 都需要) file: self.file.as_ref().map(|f| MmapFile { @@ -339,7 +339,7 @@ impl MappingArea { *vpn, new_ppn, PageSize::Size4K, - self.permission.clone(), + self.permission, Some(batch), )?; @@ -374,7 +374,7 @@ impl MappingArea { *vpn, new_ppn, PageSize::Size4K, - self.permission.clone(), + self.permission, Some(batch), )?; @@ -448,7 +448,7 @@ impl MappingArea { /// - 只支持 Framed 映射类型 pub fn split_at( mut self, - page_table: &mut ActivePageTableInner, + _page_table: &mut ActivePageTableInner, split_vpn: Vpn, ) -> Result<(Self, Self), page_table::PagingError> { // 验证拆分点 @@ -493,7 +493,7 @@ impl MappingArea { left_range, self.area_type, self.map_type, - self.permission.clone(), + self.permission, left_file, ); @@ -501,7 +501,7 @@ impl MappingArea { right_range, self.area_type, self.map_type, - self.permission.clone(), + self.permission, right_file, ); @@ -601,7 +601,7 @@ impl MappingArea { left_range, self.area_type, self.map_type, - self.permission.clone(), + self.permission, left_file, )) } else { @@ -625,7 +625,7 @@ impl MappingArea { right_range, self.area_type, self.map_type, - self.permission.clone(), + self.permission, right_file, )) } else { @@ -691,7 +691,7 @@ impl MappingArea { vpn, ppn, PageSize::Size4K, - middle_area.permission.clone(), + middle_area.permission, Some(batch), )?; } @@ -760,15 +760,15 @@ impl MappingArea { // 根据解除映射的位置,决定返回什么 if unmap_start == area_start && unmap_end == area_end { // 情况 1: 整个区域被解除映射 - return Ok(None); + Ok(None) } else if unmap_start == area_start { // 情况 2: 解除映射了前半部分,保留 [unmap_end, area_end) self.vpn_range = VpnRange::new(unmap_end, area_end); - return Ok(Some((self, None))); + Ok(Some((self, None))) } else if unmap_end == area_end { // 情况 3: 解除映射了后半部分,保留 [area_start, unmap_start) self.vpn_range = VpnRange::new(area_start, unmap_start); - return Ok(Some((self, None))); + Ok(Some((self, None))) } else { // 情况 4: 解除映射了中间部分,需要拆分为两个区域 // 保留 [area_start, unmap_start) 和 [unmap_end, area_end) @@ -800,7 +800,7 @@ impl MappingArea { left_range, self.area_type, self.map_type, - self.permission.clone(), + self.permission, left_file, ); @@ -808,7 +808,7 @@ impl MappingArea { right_range, self.area_type, self.map_type, - self.permission.clone(), + self.permission, right_file, ); @@ -825,7 +825,7 @@ impl MappingArea { } } - return Ok(Some((left_area, Some(right_area)))); + Ok(Some((left_area, Some(right_area)))) } } diff --git a/os/src/mm/memory_space/memory_space.rs b/os/src/mm/memory_space/memory_space.rs index b091fe1f..3198341b 100644 --- a/os/src/mm/memory_space/memory_space.rs +++ b/os/src/mm/memory_space/memory_space.rs @@ -744,7 +744,7 @@ impl MemorySpace { Std(&'a [xmas_elf::symbol_table::Entry64]), } - let mut write_usize_at = |va: usize, value: usize| -> Result<(), PagingError> { + let write_usize_at = |va: usize, value: usize| -> Result<(), PagingError> { let paddr = space .page_table .translate(Vaddr::from_usize(va)) @@ -1258,7 +1258,7 @@ impl MemorySpace { } // 检查地址对齐 - if start % PAGE_SIZE != 0 { + if !start.is_multiple_of(PAGE_SIZE) { return Err(PagingError::InvalidAddress); } diff --git a/os/src/net/config.rs b/os/src/net/config.rs index 0e618572..d15053db 100644 --- a/os/src/net/config.rs +++ b/os/src/net/config.rs @@ -86,16 +86,16 @@ impl NetworkConfigManager { if prefix_length == 0 { // 特殊情况:掩码为 0.0.0.0 if mask_u32 == 0 { - return Ok(0); + Ok(0) } else { - return Err(NetworkConfigError::InvalidSubnet); + Err(NetworkConfigError::InvalidSubnet) } } else if prefix_length == 32 { // 特殊情况:掩码为 255.255.255.255 if mask_u32 == 0xFFFFFFFF { - return Ok(32); + Ok(32) } else { - return Err(NetworkConfigError::InvalidSubnet); + Err(NetworkConfigError::InvalidSubnet) } } else { // 一般情况:验证掩码格式 @@ -103,7 +103,7 @@ impl NetworkConfigManager { if mask_u32 != expected_mask { return Err(NetworkConfigError::InvalidSubnet); } - return Ok(prefix_length); + Ok(prefix_length) } } diff --git a/os/src/net/interface.rs b/os/src/net/interface.rs index 41b10d06..2dc72169 100644 --- a/os/src/net/interface.rs +++ b/os/src/net/interface.rs @@ -8,7 +8,7 @@ use lazy_static::lazy_static; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address}; -pub use crate::net::stack::{NetDeviceAdapter, SmoltcpInterface}; +pub use crate::net::stack::SmoltcpInterface; /// 网络接口管理器 pub struct NetworkInterfaceManager { diff --git a/os/src/net/socket.rs b/os/src/net/socket.rs index 84cce86e..e392ef03 100644 --- a/os/src/net/socket.rs +++ b/os/src/net/socket.rs @@ -381,6 +381,7 @@ pub(crate) fn write_sockaddr_in_to_buf(buf: &mut [u8], endpoint: IpEndpoint) -> IpAddress::Ipv6(_) => { return Err(()); // IPv6 not supported in AF_INET } + #[cfg(not(feature = "proto-ipv6"))] _ => { return Err(()); // Unknown address type } diff --git a/os/src/net/stack.rs b/os/src/net/stack.rs index 9c8e5232..86f70ea1 100644 --- a/os/src/net/stack.rs +++ b/os/src/net/stack.rs @@ -47,7 +47,7 @@ impl NetIfaceWrapper { let mut sockets = sockets.lock(); crate::pr_debug!("poll: before iface.poll"); - let result = iface.poll(timestamp, &mut *dev, &mut *sockets); + let result = iface.poll(timestamp, &mut *dev, &mut sockets); crate::pr_debug!("poll: result={:?}", result); // Frames produced by loopback Tx are visible to Rx on a later poll. @@ -57,7 +57,7 @@ impl NetIfaceWrapper { if dev.loopback_queue_len() == 0 { break; } - let _ = iface.poll(timestamp, &mut *dev, &mut *sockets); + let _ = iface.poll(timestamp, &mut *dev, &mut sockets); } } @@ -355,7 +355,7 @@ impl NetworkStack { /// Create a UDP socket in the stack runtime. pub fn create_udp_socket(&self) -> Result { let mut sockets = self.socket_set.lock(); - let handle = self.create_udp_socket_in_set(&mut *sockets)?; + let handle = self.create_udp_socket_in_set(&mut sockets)?; Ok(SocketHandle::Udp(handle)) } @@ -380,7 +380,6 @@ impl NetworkStack { crate::pr_debug!("tcp_connect: calling socket.connect"); let r = socket.connect(context, remote, local).map_err(|e| { crate::pr_debug!("tcp_connect error: {:?}", e); - () }); crate::pr_debug!("tcp_connect: socket.connect returned {:?}", r); r @@ -501,7 +500,7 @@ impl NetworkStack { let smoltcp_changed = wrapper.poll_smoltcp(&self.socket_set); let udp_changed = { let mut sockets = self.socket_set.lock(); - self.udp_dispatch_drain_locked(&mut *sockets) + self.udp_dispatch_drain_locked(&mut sockets) }; self.reap_pending_tcp_close(); if smoltcp_changed || udp_changed { @@ -518,7 +517,7 @@ impl NetworkStack { /// Drain UDP datagrams from shared per-port sockets. pub fn udp_dispatch(&self) -> bool { let mut sockets = self.socket_set.lock(); - self.udp_dispatch_drain_locked(&mut *sockets) + self.udp_dispatch_drain_locked(&mut sockets) } /// Attach a UDP fd to the shared per-port socket. @@ -537,7 +536,7 @@ impl NetworkStack { if let Some(e) = ports.get(&port) { e.handle } else { - let h = self.create_udp_socket_in_set(&mut *sockets)?; + let h = self.create_udp_socket_in_set(&mut sockets)?; let listen = IpListenEndpoint { addr: bind_addr, port, @@ -595,7 +594,7 @@ impl NetworkStack { wrapper.poll_smoltcp(&self.socket_set); { let mut sockets = self.socket_set.lock(); - self.udp_dispatch_drain_locked(&mut *sockets); + self.udp_dispatch_drain_locked(&mut sockets); } self.reap_pending_tcp_close(); @@ -606,7 +605,7 @@ impl NetworkStack { } wrapper.poll_smoltcp(&self.socket_set); let mut sockets = self.socket_set.lock(); - self.udp_dispatch_drain_locked(&mut *sockets); + self.udp_dispatch_drain_locked(&mut sockets); } } } @@ -888,13 +887,13 @@ impl NetworkStack { .send_slice(buf) .map_err(|_| crate::vfs::FsError::WouldBlock); - if !buf.is_empty() { - if let Ok(0) = result { - if socket.may_send() { - return Err(crate::vfs::FsError::WouldBlock); - } else { - return Err(crate::vfs::FsError::BrokenPipe); - } + if !buf.is_empty() + && let Ok(0) = result + { + if socket.may_send() { + return Err(crate::vfs::FsError::WouldBlock); + } else { + return Err(crate::vfs::FsError::BrokenPipe); } } @@ -1063,12 +1062,11 @@ impl NetworkStack { } let target = target.or(fallback); - if let Some(f) = target { - if let Some(sf) = f.as_any().downcast_ref::() { - if sf.udp_push(d) { - delivered_any = true; - } - } + if let Some(f) = target + && let Some(sf) = f.as_any().downcast_ref::() + && sf.udp_push(d) + { + delivered_any = true; } } diff --git a/os/src/security/entropy_pool.rs b/os/src/security/entropy_pool.rs index 0f32773e..5af1a76c 100644 --- a/os/src/security/entropy_pool.rs +++ b/os/src/security/entropy_pool.rs @@ -72,8 +72,8 @@ impl EntropyPool for BiogasPoll { } fn try_fill(&mut self, dest: &mut [u8]) -> Result { - for i in 0..dest.len() { - dest[i] = (self.biogas & 0xFF) as u8; + for byte in dest.iter_mut() { + *byte = (self.biogas & 0xFF) as u8; self.biogas = self .biogas .wrapping_mul(6364136223846793005) diff --git a/os/src/sync/mod.rs b/os/src/sync/mod.rs index 25ed2783..f0c29672 100644 --- a/os/src/sync/mod.rs +++ b/os/src/sync/mod.rs @@ -12,12 +12,10 @@ mod rwlock; mod spin_lock; mod ticket_lock; -pub use intr_guard::*; pub use mutex::*; pub use per_cpu::PerCpu; -pub use preempt::{PreemptGuard, preempt_disable, preempt_enable}; +pub use preempt::PreemptGuard; pub use raw_spin_lock::*; pub use raw_spin_lock_without_guard::*; pub use rwlock::*; pub use spin_lock::*; -pub use ticket_lock::*; diff --git a/os/src/sync/mutex.rs b/os/src/sync/mutex.rs index 6eabd268..7b243419 100644 --- a/os/src/sync/mutex.rs +++ b/os/src/sync/mutex.rs @@ -2,7 +2,7 @@ use core::cell::UnsafeCell; use core::sync::atomic::{AtomicBool, Ordering}; -use crate::kernel::{WaitQueue, current_cpu, current_task, yield_task}; +use crate::kernel::{WaitQueue, current_task, yield_task}; use crate::sync::SpinLock; use crate::sync::{raw_spin_lock::RawSpinLock, raw_spin_lock::RawSpinLockGuard}; diff --git a/os/src/sync/preempt.rs b/os/src/sync/preempt.rs index 69112f73..e790b3f5 100644 --- a/os/src/sync/preempt.rs +++ b/os/src/sync/preempt.rs @@ -21,10 +21,8 @@ impl CacheAlignedAtomic { /// Per-CPU 抢占计数器 /// /// 每个 CPU 维护一个计数器,> 0 表示抢占已禁用。 -static PREEMPT_COUNT: [CacheAlignedAtomic; MAX_CPU_COUNT] = { - const INIT: CacheAlignedAtomic = CacheAlignedAtomic::new(); - [INIT; MAX_CPU_COUNT] -}; +static PREEMPT_COUNT: [CacheAlignedAtomic; MAX_CPU_COUNT] = + [const { CacheAlignedAtomic::new() }; MAX_CPU_COUNT]; /// 禁用抢占 /// diff --git a/os/src/sync/raw_spin_lock.rs b/os/src/sync/raw_spin_lock.rs index 8b9d2415..257eff49 100644 --- a/os/src/sync/raw_spin_lock.rs +++ b/os/src/sync/raw_spin_lock.rs @@ -145,19 +145,16 @@ mod tests { // NOTE: 在实际运行环境中,第二次调用会死循环,测试环境通常需要模拟并发 // 在这里我们依赖测试框架的单线程执行来简单检查 is_locked 状态 - // 模拟多线程获取失败的场景: - let second_lock_failed; - // 临时释放,让第二次获取成功 drop(guard1); let guard2 = lock.lock(); - if lock.is_locked() { + let second_lock_failed = if lock.is_locked() { // 第二次获取成功 - second_lock_failed = false; + false } else { - second_lock_failed = true; - } + true + }; kassert!(!second_lock_failed); drop(guard2); diff --git a/os/src/test/guard.rs b/os/src/test/guard.rs index f84859eb..ee085f95 100644 --- a/os/src/test/guard.rs +++ b/os/src/test/guard.rs @@ -25,17 +25,17 @@ impl TestEnvGuard { None } else { // 将裸指针转换回函数指针 fn() 以便保存 - Some(unsafe { core::mem::transmute(current_handler_ptr) }) + Some(unsafe { core::mem::transmute::<*mut (), fn()>(current_handler_ptr) }) }; - let mut guard = TestEnvGuard { + let guard = TestEnvGuard { prev_flags: unsafe { read_flags() }, prev_handler, // 保存旧的 handler }; match env { TestEnvironment::None => {} - TestEnvironment::Interrupt(handler) => unsafe { + TestEnvironment::Interrupt(_handler) => unsafe { crate::arch::intr::enable_interrupts(); }, } diff --git a/os/src/test/macros.rs b/os/src/test/macros.rs index dc3feac3..12fefd3a 100644 --- a/os/src/test/macros.rs +++ b/os/src/test/macros.rs @@ -1,12 +1,6 @@ #![allow(dead_code)] -use crate::{ - arch::intr::{ - are_interrupts_enabled, disable_interrupts, enable_interrupts, read_and_enable_interrupts, - restore_interrupts, - }, - println, -}; -use core::sync::atomic::{AtomicUsize, Ordering}; +use crate::println; +use core::sync::atomic::AtomicUsize; #[derive(Copy, Clone, Debug)] pub struct FailedAssertion { @@ -81,9 +75,9 @@ macro_rules! early_test { #[allow(dead_code)] // 函数不是直接调用的,所以允许未使用 fn []() { // 打印测试开始信息,此时 console 应该已经可以工作 - crate::println!("\x1b[36m[early_test] Running: {}\x1b[0m\n", stringify!($func_name)); + $crate::println!("\x1b[36m[early_test] Running: {}\x1b[0m\n", stringify!($func_name)); $body - crate::println!("\x1b[36m[early_test] Passed: {}\x1b[0m\n", stringify!($func_name)); + $crate::println!("\x1b[36m[early_test] Passed: {}\x1b[0m\n", stringify!($func_name)); } // 将函数指针放入自定义的链接器段 @@ -194,13 +188,16 @@ fn run_test(test_name: &str, env_name: Option<&str>, test_fn: impl FnOnce()) { let failed_count = failed_after - failed_before; unsafe { - for i in failed_before..FAILED_INDEX { - if let Some(fail) = &FAILED_LIST[i] { - println!( - "\x1b[31mFailed assertion: {} at {}:{}\x1b[0m", - fail.cond, fail.file, fail.line - ); - } + let failed_limit = FAILED_INDEX; + let failed_list = core::ptr::addr_of!(FAILED_LIST).cast::>(); + for i in failed_before..failed_limit { + let Some(fail) = failed_list.add(i).read() else { + continue; + }; + println!( + "\x1b[31mFailed assertion: {} at {}:{}\x1b[0m", + fail.cond, fail.file, fail.line + ); } } @@ -225,8 +222,8 @@ pub fn run_early_tests() { // 创建一个指向函数指针的切片 // 安全性:我们假设链接器脚本正确创建了这些符号,并且它们对齐了函数指针。 // 段中的每个项都是一个 `fn()` 类型的指针。 - let start = unsafe { &__early_test_start as *const _ as *const extern "C" fn() }; - let end = unsafe { &__early_test_end as *const _ as *const extern "C" fn() }; + let start: *const extern "C" fn() = unsafe { &__early_test_start as *const _ }; + let end: *const extern "C" fn() = unsafe { &__early_test_end as *const _ }; // 计算测试数量 let count = unsafe { end.offset_from(start) } as usize; diff --git a/os/src/test/mod.rs b/os/src/test/mod.rs index 81558472..1a217cb7 100644 --- a/os/src/test/mod.rs +++ b/os/src/test/mod.rs @@ -49,8 +49,8 @@ pub fn run_early_tests() { // 创建一个指向函数指针的切片 // 安全性:我们假设链接器脚本正确创建了这些符号,并且它们对齐了函数指针。 // 段中的每个项都是一个 `fn()` 类型的指针。 - let start = unsafe { &__early_test_start as *const _ as *const extern "C" fn() }; - let end = unsafe { &__early_test_end as *const _ as *const extern "C" fn() }; + let start: *const extern "C" fn() = unsafe { &__early_test_start as *const _ }; + let end: *const extern "C" fn() = unsafe { &__early_test_end as *const _ }; // 计算测试数量 let count = unsafe { end.offset_from(start) } as usize; @@ -125,7 +125,7 @@ mod tests { }); early_test!(exampe_early_test, { - kassert!(1 == 1); + kassert!(true); }); // 测试 `test_case!` 宏的 `(Interrupts)` 环境是否能正确地 diff --git a/os/src/test/net_test.rs b/os/src/test/net_test.rs index 10e7e2b3..ec1f4ffe 100644 --- a/os/src/test/net_test.rs +++ b/os/src/test/net_test.rs @@ -1,4 +1,17 @@ -use crate::{println, test_case}; +use crate::println; + +/// 运行所有网络相关测试 +/// +/// 此函数用于在系统启动时手动运行网络测试。 +pub fn run_network_tests() { + println!("\n--- 运行网络系统调用测试 ---"); + + // 这里可以添加更多测试执行代码 + println!("网络测试框架已初始化"); + println!("可以在合适的时机通过 test_case! 宏定义的测试函数执行具体测试"); + + println!("--- 网络系统调用测试结束 ---"); +} /// 网络系统调用测试 /// @@ -8,6 +21,7 @@ use crate::{println, test_case}; #[cfg(test)] mod net_tests { use super::*; + use crate::test_case; /// 测试获取网络接口列表 test_case!(test_get_network_interfaces, { @@ -62,16 +76,3 @@ mod net_tests { println!("预期行为: 返回错误码,不会导致系统崩溃"); }); } - -/// 运行所有网络相关测试 -/// -/// 此函数用于在系统启动时手动运行网络测试。 -pub fn run_network_tests() { - println!("\n--- 运行网络系统调用测试 ---"); - - // 这里可以添加更多测试执行代码 - println!("网络测试框架已初始化"); - println!("可以在合适的时机通过 test_case! 宏定义的测试函数执行具体测试"); - - println!("--- 网络系统调用测试结束 ---"); -} diff --git a/os/src/uapi/ioctl.rs b/os/src/uapi/ioctl.rs index 55a103a4..bd6bb550 100644 --- a/os/src/uapi/ioctl.rs +++ b/os/src/uapi/ioctl.rs @@ -247,7 +247,7 @@ pub const NCCS: usize = 19; /// - offset 17-35: c_cc\[19\] (19 * u8 = 19 bytes) /// - offset 36-39: c_ispeed (u32 = 4 bytes, 对齐到4字节边界) /// - offset 40-43: c_ospeed (u32 = 4 bytes) -/// 总大小:44字节 +/// 总大小:44字节 #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct Termios { diff --git a/os/src/uapi/resource.rs b/os/src/uapi/resource.rs index 4710eccb..680c37c2 100644 --- a/os/src/uapi/resource.rs +++ b/os/src/uapi/resource.rs @@ -1,6 +1,6 @@ //! 资源限制相关的常量和类型定义。 -use core::{ffi::c_long, usize}; +use core::ffi::c_long; #[repr(C)] #[derive(Debug, Clone, Copy)] diff --git a/os/src/uapi/select.rs b/os/src/uapi/select.rs index 08bcc3ff..8bb5b98a 100644 --- a/os/src/uapi/select.rs +++ b/os/src/uapi/select.rs @@ -7,7 +7,7 @@ pub const FD_SETSIZE: usize = 1024; /// Number of longs needed to hold FD_SETSIZE bits const NFDBITS: usize = 8 * mem::size_of::(); -const FD_SET_LONGS: usize = (FD_SETSIZE + NFDBITS - 1) / NFDBITS; +const FD_SET_LONGS: usize = FD_SETSIZE.div_ceil(NFDBITS); /// File descriptor set for select() #[repr(C)] diff --git a/os/src/util/stdio.rs b/os/src/util/stdio.rs index 92ca94db..0bde015f 100644 --- a/os/src/util/stdio.rs +++ b/os/src/util/stdio.rs @@ -32,15 +32,11 @@ impl Stdin { pub fn read_line(&mut self, buf: &mut String) { let mut bytes = Vec::new(); - loop { - if let Some(c) = crate::console::getchar() { - if c == b'\n' || c == b'\r' { - break; - } - bytes.push(c); - } else { + while let Some(c) = crate::console::getchar() { + if c == b'\n' || c == b'\r' { break; } + bytes.push(c); } buf.push_str(&String::from_utf8_lossy(&bytes)); } diff --git a/os/src/util/str.rs b/os/src/util/str.rs index 512e5e7b..2c34666d 100644 --- a/os/src/util/str.rs +++ b/os/src/util/str.rs @@ -114,9 +114,9 @@ pub unsafe fn cstr_equal(s1: *const u8, s2: *const u8) -> bool { /// * `dest` - 目标缓冲区切片 /// * `len` - 最大拷贝长度 pub fn cstr_copy(src: *const u8, dest: &mut [u8], len: usize) { - for i in 0..len { + for (i, byte) in dest.iter_mut().enumerate().take(len) { let b = unsafe { core::ptr::read(src.add(i)) }; - dest[i] = b; + *byte = b; if b == 0 { break; } diff --git a/os/src/util/user_buffer.rs b/os/src/util/user_buffer.rs index c54c223b..308c6b50 100644 --- a/os/src/util/user_buffer.rs +++ b/os/src/util/user_buffer.rs @@ -10,7 +10,7 @@ use alloc::vec::Vec; use core::ptr; -use crate::arch::constant::{USER_BASE, USER_TOP}; +use crate::arch::constant::USER_TOP; use crate::arch::trap::SumGuard; /// 向用户空间写入数据 @@ -94,7 +94,7 @@ impl UserBuffer { let Some(end) = start.checked_add(self.len) else { return false; }; - start >= USER_BASE && end <= USER_TOP + end <= USER_TOP } /// 返回用户缓冲区长度 @@ -114,7 +114,7 @@ impl UserBuffer { /// /// 检查指针是否: /// 1. 非空 -/// 2. 指向用户空间地址范围 [USER_BASE, USER_TOP] +/// 2. 指向用户空间地址范围 [0, USER_TOP] /// 3. 指针指向的内存区域不溢出用户空间 /// /// # 参数 diff --git a/os/src/vfs/adapter.rs b/os/src/vfs/adapter.rs index 98319bfe..8acc143f 100644 --- a/os/src/vfs/adapter.rs +++ b/os/src/vfs/adapter.rs @@ -3,7 +3,7 @@ //! 用于处理数据结构之间的转换 use super::{InodeMetadata, InodeType}; -use crate::uapi::fs::{LinuxDirent64, STATX_BASIC_STATS, Stat, Statx, StatxTimestamp}; +use crate::uapi::fs::{STATX_BASIC_STATS, Stat, Statx, StatxTimestamp}; /// Stat 结构适配方法 impl Stat { @@ -38,7 +38,7 @@ impl Statx { /// 从 InodeMetadata 创建 Statx 结构 pub fn from_metadata(meta: &InodeMetadata) -> Self { let ts = |t: crate::uapi::time::TimeSpec| StatxTimestamp { - tv_sec: t.tv_sec as i64, + tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as u32, __reserved: 0, }; diff --git a/os/src/vfs/devno.rs b/os/src/vfs/devno.rs index 991d665f..d6204b99 100644 --- a/os/src/vfs/devno.rs +++ b/os/src/vfs/devno.rs @@ -2,7 +2,7 @@ //! //! 冷插拔系统的简化实现:所有设备号到驱动的映射都通过硬编码规则完成。 -use crate::device::{BLK_DRIVERS, Driver, RTC_DRIVERS, SERIAL_DRIVERS}; +use crate::device::{Driver, RTC_DRIVERS, SERIAL_DRIVERS}; use crate::vfs::dev::{major, minor}; use alloc::sync::Arc; @@ -47,7 +47,7 @@ pub fn get_chrdev_driver(dev: u64) -> Option> { } chrdev_major::TTY => { // TTY 设备 - if min >= 64 && min < 128 { + if (64..128).contains(&min) { // 串口设备:ttyS0-ttyS63 (minor 64-127) let idx = (min - 64) as usize; SERIAL_DRIVERS diff --git a/os/src/vfs/file_lock.rs b/os/src/vfs/file_lock.rs index 6bb9c9ef..5d544ae7 100644 --- a/os/src/vfs/file_lock.rs +++ b/os/src/vfs/file_lock.rs @@ -184,7 +184,7 @@ impl FileLockManager { } LockType::Read | LockType::Write => { // 检查是否有冲突 - let file_locks = locks.entry(file_id).or_insert_with(Vec::new); + let file_locks = locks.entry(file_id).or_default(); let new_lock = FileLockEntry { lock_type, diff --git a/os/src/vfs/file_system.rs b/os/src/vfs/file_system.rs index ac99c966..0d43b1b0 100644 --- a/os/src/vfs/file_system.rs +++ b/os/src/vfs/file_system.rs @@ -1,5 +1,4 @@ use crate::vfs::{FsError, Inode}; -use alloc::string::String; use alloc::sync::Arc; /// 文件系统 trait diff --git a/os/src/vfs/impls/blk_dev_file.rs b/os/src/vfs/impls/blk_dev_file.rs index 2eb4cef5..23726026 100644 --- a/os/src/vfs/impls/blk_dev_file.rs +++ b/os/src/vfs/impls/blk_dev_file.rs @@ -133,10 +133,10 @@ impl File for BlockDeviceFile { let mut sector_buf = [0u8; 512]; // 如果不是完整扇区写入,需要先读取 - if offset_in_sector != 0 || to_write != Self::BLOCK_SIZE { - if !driver.read_block(sector_idx, &mut sector_buf) { - return Err(FsError::IoError); - } + if (offset_in_sector != 0 || to_write != Self::BLOCK_SIZE) + && !driver.read_block(sector_idx, &mut sector_buf) + { + return Err(FsError::IoError); } // 修改数据 @@ -189,7 +189,7 @@ impl File for BlockDeviceFile { } fn flags(&self) -> OpenFlags { - self.flags.clone() + self.flags } fn inode(&self) -> Result, FsError> { diff --git a/os/src/vfs/impls/char_dev_file.rs b/os/src/vfs/impls/char_dev_file.rs index d11ee714..d910a096 100644 --- a/os/src/vfs/impls/char_dev_file.rs +++ b/os/src/vfs/impls/char_dev_file.rs @@ -31,10 +31,10 @@ impl CharDeviceFile { #[inline] fn echo_byte(&self, ch: u8) { - if let Some(ref driver) = self.driver { - if let Some(serial) = driver.as_serial() { - serial.write(&[ch]); - } + if let Some(ref driver) = self.driver + && let Some(serial) = driver.as_serial() + { + serial.write(&[ch]); } } } @@ -265,7 +265,7 @@ impl File for CharDeviceFile { if post && onlcr { for &ch in buf { if ch == b'\n' { - serial.write(&[b'\r', b'\n']); + serial.write(b"\r\n"); } else { serial.write(&[ch]); } @@ -286,7 +286,7 @@ impl File for CharDeviceFile { self.inode.metadata() } - fn lseek(&self, offset: isize, whence: SeekWhence) -> Result { + fn lseek(&self, _offset: isize, _whence: SeekWhence) -> Result { // 大多数字符设备不支持 seek // 但某些设备(如 /dev/mem)可能需要 Err(FsError::NotSupported) @@ -297,7 +297,7 @@ impl File for CharDeviceFile { } fn flags(&self) -> OpenFlags { - self.flags.clone() + self.flags } fn inode(&self) -> Result, FsError> { @@ -309,10 +309,6 @@ impl File for CharDeviceFile { } fn ioctl(&self, request: u32, arg: usize) -> Result { - use crate::arch::trap::SumGuard; - use crate::uapi::errno::{EINVAL, ENOTTY}; - use crate::uapi::ioctl::*; - let maj = major(self.dev); // 根据设备类型分发 ioctl @@ -458,41 +454,41 @@ impl CharDeviceFile { } // 通过驱动获取时间 - if let Some(ref driver) = self.driver { - if let Some(rtc) = driver.as_rtc() { - let dt = rtc.read_datetime(); - - unsafe { - let _guard = SumGuard::new(); - let rtc_time_ptr = arg as *mut RtcTime; - if rtc_time_ptr.is_null() { - return Ok(-EINVAL as isize); - } - - // 清零结构体 - core::ptr::write_bytes( - rtc_time_ptr as *mut u8, - 0, - core::mem::size_of::(), - ); - - // 填充时间结构体 - let rtc_time = RtcTime { - tm_sec: dt.second as i32, - tm_min: dt.minute as i32, - tm_hour: dt.hour as i32, - tm_mday: dt.day as i32, - tm_mon: (dt.month - 1) as i32, // Linux 月份是 0-based - tm_year: (dt.year - 1900) as i32, - tm_wday: 0, // 未计算 - tm_yday: 0, // 未计算 - tm_isdst: 0, - }; - - core::ptr::write_volatile(rtc_time_ptr, rtc_time); + if let Some(ref driver) = self.driver + && let Some(rtc) = driver.as_rtc() + { + let dt = rtc.read_datetime(); + + unsafe { + let _guard = SumGuard::new(); + let rtc_time_ptr = arg as *mut RtcTime; + if rtc_time_ptr.is_null() { + return Ok(-EINVAL as isize); } - return Ok(0); + + // 清零结构体 + core::ptr::write_bytes( + rtc_time_ptr as *mut u8, + 0, + core::mem::size_of::(), + ); + + // 填充时间结构体 + let rtc_time = RtcTime { + tm_sec: dt.second as i32, + tm_min: dt.minute as i32, + tm_hour: dt.hour as i32, + tm_mday: dt.day as i32, + tm_mon: (dt.month - 1) as i32, // Linux 月份是 0-based + tm_year: (dt.year - 1900), + tm_wday: 0, // 未计算 + tm_yday: 0, // 未计算 + tm_isdst: 0, + }; + + core::ptr::write_volatile(rtc_time_ptr, rtc_time); } + return Ok(0); } Err(FsError::NoDevice) } diff --git a/os/src/vfs/impls/pipe_file.rs b/os/src/vfs/impls/pipe_file.rs index 2df55227..0a215ad4 100644 --- a/os/src/vfs/impls/pipe_file.rs +++ b/os/src/vfs/impls/pipe_file.rs @@ -42,7 +42,7 @@ impl PipeRingBuffer { /// 设置管道容量 fn set_capacity(&mut self, new_capacity: usize) -> Result<(), FsError> { - if new_capacity < Self::MIN_CAPACITY || new_capacity > Self::MAX_CAPACITY { + if !(Self::MIN_CAPACITY..=Self::MAX_CAPACITY).contains(&new_capacity) { return Err(FsError::InvalidArgument); } @@ -67,8 +67,8 @@ impl PipeRingBuffer { } let nread = buf.len().min(self.buffer.len()); - for i in 0..nread { - buf[i] = self.buffer.pop_front().unwrap(); + for byte in buf.iter_mut().take(nread) { + *byte = self.buffer.pop_front().unwrap(); } Ok(nread) @@ -200,10 +200,10 @@ impl File for PipeFile { let mut ring_buf = self.buffer.lock(); let result = ring_buf.read(buf); // Only wake up writers if we actually freed buffer space - if let Ok(bytes_read) = result { - if bytes_read > 0 { - crate::kernel::syscall::io::wake_poll_waiters(); - } + if let Ok(bytes_read) = result + && bytes_read > 0 + { + crate::kernel::syscall::io::wake_poll_waiters(); } result } @@ -216,10 +216,10 @@ impl File for PipeFile { let mut ring_buf = self.buffer.lock(); let result = ring_buf.write(buf); // Only wake up readers if we actually wrote data - if let Ok(bytes_written) = result { - if bytes_written > 0 { - crate::kernel::syscall::io::wake_poll_waiters(); - } + if let Ok(bytes_written) = result + && bytes_written > 0 + { + crate::kernel::syscall::io::wake_poll_waiters(); } result } diff --git a/os/src/vfs/mod.rs b/os/src/vfs/mod.rs index 83ce2a9a..909939ff 100644 --- a/os/src/vfs/mod.rs +++ b/os/src/vfs/mod.rs @@ -163,17 +163,15 @@ pub mod mount; pub mod path; pub use adapter::inode_type_to_d_type; -pub use dentry::{DENTRY_CACHE, Dentry, DentryCache}; -pub use dev::{major, makedev, minor}; -pub use devno::{get_blkdev_index, get_chrdev_driver}; +pub use dentry::{DENTRY_CACHE, Dentry}; pub use error::FsError; pub use fd_table::FDTable; pub use file::File; pub use file_lock::file_lock_manager; pub use file_system::{FileSystem, StatFs}; -pub use impls::{PipeFile, RegFile, StderrFile, StdinFile, StdoutFile, create_stdio_files}; +pub use impls::{PipeFile, RegFile, create_stdio_files}; pub use inode::{DirEntry, FileMode, Inode, InodeMetadata, InodeType}; -pub use mount::{MOUNT_TABLE, MountFlags, MountPoint, MountTable, get_root_dentry}; +pub use mount::{MOUNT_TABLE, MountFlags, get_root_dentry}; pub use path::{ normalize_path, parse_path, split_path, vfs_lookup, vfs_lookup_from, vfs_lookup_no_follow, vfs_lookup_no_follow_from, diff --git a/os/src/vfs/mount.rs b/os/src/vfs/mount.rs index 0441a773..6ef872bc 100644 --- a/os/src/vfs/mount.rs +++ b/os/src/vfs/mount.rs @@ -226,7 +226,7 @@ //! ``` use crate::sync::SpinLock; -use crate::vfs::{Dentry, FileMode, FileSystem, FsError}; +use crate::vfs::{Dentry, FileSystem, FsError}; use alloc::collections::BTreeMap; use alloc::string::String; use alloc::sync::Arc; @@ -324,7 +324,7 @@ impl MountTable { let mut mounts = self.mounts.lock(); mounts .entry(normalized_path.clone()) - .or_insert_with(Vec::new) + .or_default() .push(mount_point.clone()); // 如果挂载点的 dentry 已经存在于缓存中,更新其挂载信息 diff --git a/os/src/vfs/path.rs b/os/src/vfs/path.rs index 3bf0427b..070e713f 100644 --- a/os/src/vfs/path.rs +++ b/os/src/vfs/path.rs @@ -301,7 +301,7 @@ pub fn split_path(path: &str) -> Result<(String, String), FsError> { Ok((dir, filename)) } else { // 相对路径,使用当前目录 - Ok((String::from("."), String::from(normalized))) + Ok((String::from("."), normalized)) } } @@ -447,12 +447,12 @@ fn check_mount_point(dentry: Arc) -> Result, FsError> { // 慢速路径:查找挂载表(首次访问或缓存失效) let full_path = dentry.full_path(); - if let Some(mount_point) = crate::vfs::MOUNT_TABLE.find_mount(&full_path) { - if mount_point.mount_path == full_path { - // 更新 dentry 的挂载缓存 - dentry.set_mount(&mount_point.root); - return Ok(mount_point.root.clone()); - } + if let Some(mount_point) = crate::vfs::MOUNT_TABLE.find_mount(&full_path) + && mount_point.mount_path == full_path + { + // 更新 dentry 的挂载缓存 + dentry.set_mount(&mount_point.root); + return Ok(mount_point.root.clone()); } Ok(dentry) diff --git a/os/src/vfs/tests/dentry.rs b/os/src/vfs/tests/dentry.rs index ff71c5c0..d583272c 100644 --- a/os/src/vfs/tests/dentry.rs +++ b/os/src/vfs/tests/dentry.rs @@ -109,7 +109,7 @@ test_case!(test_dentry_overwrite_child, { let child1 = Dentry::new("child".to_string(), root_inode.clone()); let child2 = Dentry::new("child".to_string(), root_inode.clone()); - parent.add_child(child1.clone()); + parent.add_child(child1); parent.add_child(child2.clone()); let found = parent.lookup_child("child"); diff --git a/os/src/vfs/tests/fd_table.rs b/os/src/vfs/tests/fd_table.rs index fe075923..5074d9a7 100644 --- a/os/src/vfs/tests/fd_table.rs +++ b/os/src/vfs/tests/fd_table.rs @@ -21,7 +21,7 @@ test_case!(test_fdtable_alloc, { // 分配 FD let fd = fd_table.alloc(file).unwrap(); - kassert!(fd >= 0); + kassert!(fd_table.get(fd).is_ok()); }); test_case!(test_fdtable_get, { diff --git a/os/src/vfs/tests/mount.rs b/os/src/vfs/tests/mount.rs index bfb90130..a0ceece6 100644 --- a/os/src/vfs/tests/mount.rs +++ b/os/src/vfs/tests/mount.rs @@ -11,7 +11,7 @@ test_case!(test_mount_fs, { // 挂载到 /test let result = MOUNT_TABLE.mount( - fs.clone(), + fs, "/test", MountFlags::empty(), Some(String::from("testfs")), @@ -34,7 +34,7 @@ test_case!(test_mount_list, { // 列出挂载点 let mounts = MOUNT_TABLE.list_mounts(); // 至少应该有根文件系统 - kassert!(mounts.len() >= 1); + kassert!(!mounts.is_empty()); }); // P2 边界和错误处理测试 diff --git a/os/src/vfs/tests/path.rs b/os/src/vfs/tests/path.rs index bd738e11..93914f57 100644 --- a/os/src/vfs/tests/path.rs +++ b/os/src/vfs/tests/path.rs @@ -1,5 +1,5 @@ use super::super::*; -use crate::vfs::path::PathComponent; +use crate::vfs::path::{PathComponent, parse_path}; use crate::{kassert, test_case}; use alloc::string::ToString; @@ -194,7 +194,7 @@ test_case!(test_dentry_mount_cache, { let fs = create_test_simplefs(); // 挂载到 /cache_test - let mount_result = MOUNT_TABLE.mount(fs.clone(), "/cache_test", MountFlags::empty(), None); + let mount_result = MOUNT_TABLE.mount(fs, "/cache_test", MountFlags::empty(), None); kassert!(mount_result.is_ok()); // 获取挂载点 From 63340a1c1611fd99ec827d08100ee904aedf8876 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Tue, 5 May 2026 20:01:26 +0800 Subject: [PATCH 11/11] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- .../network_rearchitecture_new_main.md | 25 ------ document/SUMMARY.md | 2 - .../arch/loongarch/network_rearchitecture.md | 9 -- document/arch/riscv/network_rearchitecture.md | 9 -- document/net/network_implementation_guide.md | 4 +- document/net/network_rearchitecture.md | 20 ----- document/net/rearchitecture_device.md | 49 ----------- document/net/rearchitecture_execution_plan.md | 19 ---- document/net/rearchitecture_interface.md | 64 -------------- document/net/rearchitecture_loopback_poll.md | 81 ----------------- document/net/rearchitecture_socket_syscall.md | 76 ---------------- document/net/rearchitecture_stack_runtime.md | 86 ------------------- 13 files changed, 5 insertions(+), 443 deletions(-) delete mode 100644 docs/pr_recorde/network_rearchitecture_new_main.md delete mode 100644 document/arch/loongarch/network_rearchitecture.md delete mode 100644 document/arch/riscv/network_rearchitecture.md delete mode 100644 document/net/network_rearchitecture.md delete mode 100644 document/net/rearchitecture_device.md delete mode 100644 document/net/rearchitecture_execution_plan.md delete mode 100644 document/net/rearchitecture_interface.md delete mode 100644 document/net/rearchitecture_loopback_poll.md delete mode 100644 document/net/rearchitecture_socket_syscall.md delete mode 100644 document/net/rearchitecture_stack_runtime.md diff --git a/.gitignore b/.gitignore index 62d65db1..01707549 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,6 @@ os/可能的改进.md # Local agent guidance **/AGENTS.md -docs/superpowers/ + +# Local planning/refactor notes +docs/ diff --git a/docs/pr_recorde/network_rearchitecture_new_main.md b/docs/pr_recorde/network_rearchitecture_new_main.md deleted file mode 100644 index 5941fe66..00000000 --- a/docs/pr_recorde/network_rearchitecture_new_main.md +++ /dev/null @@ -1,25 +0,0 @@ -# ✨ Feature / 新功能 - -## 🚀 描述 (Description) - -本次拉取请求在 `new-main` 上落实网络模块重构,主要改进如下: - -- 收口网络协议栈运行时:新增 `NetworkStack` 门面,将 smoltcp socket set、active interface runtime、loopback link 和 poll 推进统一放到 `os/src/net/stack.rs`。 -- 清理 socket/syscall 边界:`SocketFile` 的读写、可读可写、recvfrom、sendto、drop 等路径改为经 `NetworkStack` 操作协议栈;网络 syscall 不再直接访问 `SOCKET_SET`、`NET_IFACE` 或 `smoltcp::socket::{tcp, udp}`。 -- 拆分设备与接口职责:VirtIO net 初始化只注册 `NetDevice`,接口创建统一由网络子系统入口负责;`NetworkInterface` 不再直接实现 `Driver`,改由 `NetDriverHandle` 作为兼容桥。 -- 显式建模 loopback:新增 `LoopbackNetDevice`,默认网络初始化确保 `lo` 存在;`NullNetDevice` 不再承担 loopback 语义。 -- 更新网络文档:补齐 `document/net/` 面向读者的模块文档,并恢复/补充 `document/net/`、`document/arch/*/network_rearchitecture.md` 中的施工记录。 -- 清理本地协作文件:`.gitignore` 忽略各处 `AGENTS.md` 和 `docs/superpowers/` 草稿目录;补充 unused 类告警忽略标签,避免重构期间被旧未使用告警阻塞。 - -本记录基于以下提交: - -- `2d93e1d chore: 忽略本地代理与草稿文档` -- `16b161f docs: 更新网络重构文档` -- `11ca651 fix: 忽略未使用告警` -- `37cc37e fix: 收口网络协议栈运行时` -- `4ad26a9 fix: 拆分网络设备与接口` -- `e1281ae fix: 修正文档空行` - -## 🔗 关联 Issue - -暂无关联 Issue。 diff --git a/document/SUMMARY.md b/document/SUMMARY.md index c25dca2f..8d8daf12 100644 --- a/document/SUMMARY.md +++ b/document/SUMMARY.md @@ -108,13 +108,11 @@ - [用户栈布局](arch/riscv/stack_layout.md) - [多核启动](arch/riscv/smp_boot.md) - [核间中断 (IPI)](arch/riscv/ipi.md) -- [网络重构说明](arch/riscv/network_rearchitecture.md) ## LoongArch64 - [LoongArch64](arch/loongarch/README.md) - [启动与用户态运行修复总结(comix-1 当前分支)](arch/loongarch/bringup_userland.md) - - [网络重构说明](arch/loongarch/network_rearchitecture.md) --- diff --git a/document/arch/loongarch/network_rearchitecture.md b/document/arch/loongarch/network_rearchitecture.md deleted file mode 100644 index 2fd2022b..00000000 --- a/document/arch/loongarch/network_rearchitecture.md +++ /dev/null @@ -1,9 +0,0 @@ -# LoongArch 网络重构说明 - -LoongArch 网络重构不需要在网络核心中引入架构差异。架构层只负责 syscall 分发、trap 上下文和用户指针访问前置条件。 - -## 规则 - -- syscall 号和参数寄存器差异只放在 `os/src/arch/loongarch/`。 -- `os/src/kernel/syscall/network.rs` 接收架构无关的参数值。 -- 网络核心不得根据 LoongArch 条件编译分叉。 diff --git a/document/arch/riscv/network_rearchitecture.md b/document/arch/riscv/network_rearchitecture.md deleted file mode 100644 index f4b84506..00000000 --- a/document/arch/riscv/network_rearchitecture.md +++ /dev/null @@ -1,9 +0,0 @@ -# RISC-V 网络重构说明 - -RISC-V 网络重构不需要在网络核心中引入架构差异。架构层只负责 syscall 分发、trap 上下文和用户指针访问前置条件。 - -## 规则 - -- syscall 号和参数寄存器差异只放在 `os/src/arch/riscv/`。 -- `os/src/kernel/syscall/network.rs` 接收架构无关的参数值。 -- 网络核心不得根据 RISC-V 条件编译分叉。 diff --git a/document/net/network_implementation_guide.md b/document/net/network_implementation_guide.md index da045803..602f1278 100644 --- a/document/net/network_implementation_guide.md +++ b/document/net/network_implementation_guide.md @@ -1,6 +1,6 @@ # 网络子系统实现指南 -本文档描述 `new-main` 上网络模块的当前实现边界和维护方式。历史上的“待实现 POSIX 网络栈”说明已经合并为当前事实:网络模块以 `NetDevice -> NetworkInterface -> NetworkStack -> SocketFile/syscall` 为主线,保留少量兼容壳体以降低重构风险。 +本文档描述 `new-main` 上网络模块的当前实现边界和维护方式。网络模块以 `NetDevice -> NetworkInterface -> NetworkStack -> SocketFile/syscall` 为主线,保留少量兼容壳体以维持现有接口稳定。 ## 当前分层 @@ -43,4 +43,4 @@ cargo check - `set_network_interface_config()` 能正确设置和查询 IP、mask、gateway。 - `socket/bind/listen/accept/connect/send/recv/sendto/recvfrom` 基础路径可用。 - `ppoll/select` 能观察 TCP accept、TCP recv、UDP recv 可读事件。 -- netperf/netserver 已知 EINTR 行为不要误判为网络重构失败。 +- netperf/netserver 已知 EINTR 行为不要误判为网络功能不可用。 diff --git a/document/net/network_rearchitecture.md b/document/net/network_rearchitecture.md deleted file mode 100644 index 2bc7db3e..00000000 --- a/document/net/network_rearchitecture.md +++ /dev/null @@ -1,20 +0,0 @@ -# 网络重构施工记录 - -本文档记录 `new-main` 上网络模块重构的执行顺序。 - -## 执行顺序 - -1. 更新 `AGENTS.md`,固定网络重构边界。 -2. 修正 `document/net/`,把旧“待实现指南”改成当前架构事实。 -3. 引入 `NetworkStack`,把 smoltcp runtime 和 socket 操作收口到 `net::stack`。 -4. 拆分设备注册和接口控制面,VirtIO 只注册 `NetDevice`。 -5. 显式建模 `lo` 和 loopback link。 -6. 清理 syscall 层,移除裸 `SOCKET_SET`、`NET_IFACE` 和 smoltcp socket 类型依赖。 -7. 执行 `cargo fmt && cargo check`。 - -## 验收边界 - -- `os/src/kernel/syscall/network.rs` 不直接访问协议栈内部状态。 -- `NetworkInterface` 不实现 `Driver`,中断兼容通过 `NetDriverHandle`。 -- `NullNetDevice` 不承担 loopback 语义。 -- `document/net/` 的说明与代码一致。 diff --git a/document/net/rearchitecture_device.md b/document/net/rearchitecture_device.md deleted file mode 100644 index 16ed524e..00000000 --- a/document/net/rearchitecture_device.md +++ /dev/null @@ -1,49 +0,0 @@ -# 网络设备层重构说明 - -本文档只覆盖 `device::net`。设备层的任务是提供 NIC 收发能力,不承载接口配置、协议栈状态或用户 socket 语义。 - -## 当前代码 - -- `os/src/device/net/net_device.rs` 定义 `NetDevice` 和 `VirtioNetDevice`。 -- `os/src/device/net/virtio_net.rs` 初始化 VirtIO 网卡,同时创建 `NetworkInterface` 并注册为 `Driver`。 -- `os/src/device/net/null_net.rs` 提供 `NullNetDevice`,当前被 loopback 兼容路径间接依赖。 -- `os/src/device/net/mod.rs` 维护 `NETWORK_DEVICES`。 - -## 目标边界 - -`NetDevice` 只允许表达: - -- 二层帧 `send()` / `receive()` -- `device_id()` -- `mtu()` -- `name()` -- `mac_address()` -- 链路状态和设备能力查询 - -`NetDevice` 禁止表达: - -- IP 地址、网关、路由 -- socket 或 fd 状态 -- `smoltcp` 类型 -- loopback 特判 - -## 必做改造 - -1. 为 `NetDevice` 增加最小能力描述类型,例如 `NetDeviceCaps` 和 `LinkState`。 -2. 把 `VirtioNetDevice` 的职责限制为 VirtIO 队列收发、MAC、MTU、设备状态。 -3. 新增网络子系统设备接入口,例如 `net::register_net_device(Arc)`。 -4. `virtio_net::init()` 和 `virtio_net::init_pci()` 只注册设备,不直接创建接口对象。 -5. 如果中断注册仍需要 `Driver`,新增独立的 net driver shim,避免 `NetworkInterface` 继续实现 `Driver`。 - -## 迁移兼容 - -- 第一阶段可保留 `NETWORK_DEVICES`,但它只能是设备表,不能成为接口表。 -- 第一阶段可保留 `NullNetDevice`,但文档和注释必须标明它不是 loopback。 -- 若必须保留旧 `virtio_net::init()` 行为,使用薄包装调用新的网络子系统注册入口。 - -## 验收点 - -- 替换 `VirtioNetDevice` 实现不需要修改 `os/src/kernel/syscall/network.rs`。 -- 设备层文件不 import `smoltcp`。 -- 设备层文件不 import `SocketFile`、`SOCKET_SET`、`NET_IFACE`。 -- `NetDeviceAdapter` 不在 `device::net` 公开 API 中出现。 diff --git a/document/net/rearchitecture_execution_plan.md b/document/net/rearchitecture_execution_plan.md deleted file mode 100644 index 3a06236b..00000000 --- a/document/net/rearchitecture_execution_plan.md +++ /dev/null @@ -1,19 +0,0 @@ -# 网络重构执行计划 - -本计划用于将旧 `main` 中已经验证的网络重构重新落实到 `new-main`。 - -## 阶段 - -1. `AGENTS.md`:补齐各目录边界规则,尤其是 `net`、`device/net`、`kernel/syscall`。 -2. 文档:直接修正 `document/net/` 中的旧文档,拆分为架构、设备接口、栈运行时、socket syscall、loopback poll、测试。 -3. 协议栈:新增 `os/src/net/stack.rs`,让 `NetworkStack` 持有 runtime 状态并提供门面 API。 -4. Socket/syscall:`SocketFile` 和网络 syscall 经 `NetworkStack` 操作协议栈。 -5. 设备接口:新增 `register_net_device()`,VirtIO 只交出 `NetDevice`;接口注册由网络子系统负责。 -6. Loopback:新增 `LoopbackNetDevice`,默认配置确保 `lo` 存在。 -7. 验证:格式化、编译检查、grep 边界。 - -## 保留兼容 - -- `SocketHandle` 暂时仍包装 smoltcp handle。 -- 当前仍是单 active smoltcp interface runtime。 -- `NetworkInterface::create_smoltcp_interface()` 暂时保留为初始化工厂。 diff --git a/document/net/rearchitecture_interface.md b/document/net/rearchitecture_interface.md deleted file mode 100644 index bc849f5f..00000000 --- a/document/net/rearchitecture_interface.md +++ /dev/null @@ -1,64 +0,0 @@ -# 网络接口层重构说明 - -本文档覆盖 `net::if`,也就是内核控制面中的网络接口对象。接口层描述 `lo`、`eth0` 这类接口,而不是协议栈运行时。 - -## 当前代码 - -- `os/src/net/interface.rs` 中的 `NetworkInterface` 同时保存接口配置、持有设备、创建 `SmoltcpInterface`、实现 `Driver`。 -- `NETWORK_INTERFACE_MANAGER` 当前维护 `Vec>`。 -- `getifaddrs` 和网络配置路径依赖接口枚举与 IP 配置。 - -## 目标对象 - -建议拆出: - -- `NetIf`:接口身份和只读查询入口。 -- `NetIfConfig`:MAC、IP 地址列表、gateway、MTU、flags。 -- `NetIfState`:up/running/link/loopback 等运行状态。 -- `InterfaceRegistry`:接口注册、枚举、按名查找。 - -接口对象可以持有 `Arc` 或 loopback 后端引用,但不能持有 `smoltcp::Interface` 或 `SocketSet`。 - -## 必做改造 - -1. 把 `NetworkInterface::create_smoltcp_interface()` 从接口对象中移除或改成仅供兼容层内部调用。 -2. 把 `NetworkInterface` 实现 `Driver` 的逻辑迁出到设备/中断适配对象。 -3. 将 `interrupt_enabled`、`last_interrupt_time` 这类中断推进状态从接口配置对象中剥离。 -4. `NETWORK_INTERFACE_MANAGER` 改名或收缩为 `InterfaceRegistry`,只保留注册、枚举、查找。 -5. `NetworkConfigManager::set_interface_config()` 只修改接口配置,并通过 `NetworkStack` 通知协议栈刷新地址和路由。 - -## 接口 flags - -至少应稳定表达: - -- `UP` -- `RUNNING` -- `LOOPBACK` -- `BROADCAST` -- `MULTICAST` - -`lo` 必须显式带 `LOOPBACK`;真实网卡不能因无 loopback 技巧而伪装成 `lo`。 - -## 迁移兼容 - -- 类型名 `NetworkInterface` 可暂时保留,但语义要逐步收缩。 -- 旧的 `NETWORK_INTERFACE_MANAGER` 可作为 `InterfaceRegistry` 的别名保留一个阶段。 -- `getifaddrs` 可先继续读取旧接口对象,但新增字段必须来自接口层而不是协议栈运行时。 - -## 验收点 - -- 接口层不 import `smoltcp::iface::Interface`。 -- 接口层不 import `SocketSet`。 -- 接口枚举能区分 `lo` 和 `ethN`。 -- IP/gateway 修改有单一入口,并能触发协议栈配置刷新。 - -## 当前执行状态 - -- `NetworkInterface` 已从 `Driver` trait 实现中拆出;兼容中断注册由 `NetDriverHandle` 负责。 -- `virtio_net` 不再直接创建或注册接口对象,而是调用网络子系统的 `register_net_device(device)`。 -- 默认网络初始化会确保 `lo` 接口存在;真实 `ethN` 与 `lo` 的枚举身份已分开。 - -保留的兼容点: - -- `NetworkInterface::create_smoltcp_interface()` 仍作为单 active runtime 的兼容工厂存在,后续可继续下沉到 `NetworkStack` 初始化路径。 -- `interrupt_enabled` 和 `last_interrupt_time` 暂留在 `NetworkInterface`,由 `NetDriverHandle` 通过接口对象访问。 diff --git a/document/net/rearchitecture_loopback_poll.md b/document/net/rearchitecture_loopback_poll.md deleted file mode 100644 index 1ff7d852..00000000 --- a/document/net/rearchitecture_loopback_poll.md +++ /dev/null @@ -1,81 +0,0 @@ -# Loopback 与 poll 路径重构说明 - -本文档覆盖 loopback、RX/TX、poll/select 唤醒与中断协作。这里是当前网络实现最容易“补丁堆补丁”的区域,必须先固定模型。 - -## 当前代码 - -- `NetDeviceAdapter` 内部有 `loopback_queue`。 -- `NetTxToken::consume()` 根据 `device.name() == "null-net"` 或 127 地址判断是否回灌队列。 -- `NetIfaceWrapper::poll()` 为 loopback 做额外 bounded poll。 -- `poll_until_empty()` 为本地回环和 netperf 场景主动 drain。 -- `NetworkInterface::try_handle_interrupt()` 直接从设备收包并唤醒 poll waiters。 - -## 目标模型 - -- `lo` 是显式接口,不是 `NullNetDevice` 或 `NetDeviceAdapter` 的隐藏行为。 -- 真实 NIC 的 RX/TX 只来自设备队列。 -- loopback 的 RX/TX 走独立 loopback 后端或 `NetworkStack` 内部虚拟链路。 -- 中断路径只标记事件并调度网络 poll,不直接维护另一份协议栈推进逻辑。 -- `NetworkStack::poll()` 是唯一入口。 - -## Loopback 建模选择 - -优先方案: - -- 新增 `LoopbackDevice`,实现与 `NetDevice` 相近的二层帧收发接口。 -- 网络子系统启动时创建 `lo`,配置 `127.0.0.1/8`。 -- loopback 发送帧进入自己的 RX 队列,再由 `NetworkStack::poll()` 消费。 - -备选方案: - -- 不创建完整设备对象,只在 `NetworkStack` 内部维护 `LoopbackLink`。 -- `lo` 仍必须作为 `NetIf` 出现在接口注册表中。 - -无论使用哪个方案,都不能继续把 loopback 写在普通 `NetDeviceAdapter` 的特判里。 - -## poll 改造 - -`NetworkStack::poll()` 应按固定顺序执行: - -1. 采样当前时间。 -2. 从真实设备和 loopback 后端接收帧。 -3. 调用 smoltcp poll。 -4. 处理 TCP close reap。 -5. 处理 UDP dispatch。 -6. 计算 socket 可读/可写变化。 -7. 统一唤醒 poll/select waiters。 - -## 中断协作 - -- 设备中断处理不直接调用 smoltcp poll。 -- 中断处理只记录对应设备或接口有 RX/TX 事件。 -- 如果当前内核没有独立网络线程,可在中断返回前轻量唤醒 waiters,由进程上下文中的 poll 路径推进。 -- 不得在中断上下文执行会分配内存的 UDP dispatch。 - -## 迁移步骤 - -1. 给当前额外 poll 逻辑加清晰注释,标记为兼容层。 -2. 新增显式 `lo` 接口并让接口枚举能看到它。 -3. 将 `NullNetDevice` 与 loopback 语义拆开。 -4. 将 `loopback_queue` 从 `NetDeviceAdapter` 移到 loopback 后端。 -5. 删除 `device.name() == "null-net"` 特判。 -6. 将 `poll_until_empty()` 降级为测试/兼容包装,主路径只调用 `NetworkStack::poll()`。 - -## 验收点 - -- 没有真实网卡时,`lo` 仍能服务 TCP/UDP 127.0.0.1。 -- 有真实网卡时,127.0.0.1 不经真实 NIC。 -- UDP poll/select 可读事件不依赖 ad-hoc 额外轮询补丁。 -- 中断路径和主动 poll 不形成双重状态源。 - -## 当前执行状态 - -- 新增 `LoopbackNetDevice`,无真实 NIC 时由默认配置创建显式 `lo`。 -- 默认网络初始化会先确保 `lo` 存在,再选择真实 `ethN` 或 `lo` 作为当前单 active smoltcp runtime。 -- `NetTxToken` 不再检查 `device.name() == "null-net"`;`NullNetDevice` 不再表示 loopback。 -- 兼容 loopback link 已归 `net::stack` runtime,普通 `NetDeviceAdapter` 不再持有自己的 loopback 队列字段。 - -保留的兼容点: - -- 当前仍是单 active interface runtime;有真实 NIC 时 `lo` 主要作为接口枚举和 127/8 runtime link 语义存在,还不是独立 smoltcp interface。 -- `poll_until_empty()` 仍作为 loopback/netperf 兼容包装保留,主路径继续通过 `NetworkStack::poll()` 推进。 diff --git a/document/net/rearchitecture_socket_syscall.md b/document/net/rearchitecture_socket_syscall.md deleted file mode 100644 index e26ce55d..00000000 --- a/document/net/rearchitecture_socket_syscall.md +++ /dev/null @@ -1,76 +0,0 @@ -# Socket 与 syscall 重构说明 - -本文档覆盖 `SocketFile`、fd 映射和 `kernel::syscall::network`。目标是让 syscall 层只处理用户 ABI,底层网络行为全部转发给 `NetworkStack`。 - -## 当前代码 - -- `os/src/kernel/syscall/network.rs` 直接 import `SOCKET_SET`、`SocketHandle`、`smoltcp::socket::{tcp, udp}`。 -- `SocketFile` 在 `os/src/net/socket.rs` 中直接读取 `SOCKET_SET`。 -- `FD_SOCKET_MAP` 使用 `(tid, fd) -> SocketHandle` 记录 fd 与 smoltcp handle 的关系。 -- `set_network_interface_config()` 仍返回 `-1/-2/-3` 这类私有错误码。 - -## 目标边界 - -syscall 层只负责: - -- 提取架构层传入的 syscall 参数。 -- 使用 `SumGuard` 访问用户指针。 -- sockaddr 与用户缓冲区的 ABI 编解码。 -- fd table 查询和 `SocketFile` 创建。 -- 将请求转发到 `NetworkStack`。 -- 返回标准 Linux errno。 - -syscall 层禁止: - -- 直接访问 `smoltcp::SocketSet`。 -- 直接匹配 `tcp::State` 或 `udp::Socket`。 -- 直接推进设备 poll。 -- 直接读取或修改 `NET_IFACE`。 - -## SocketFile 职责 - -`SocketFile` 保留为 VFS 文件对象,但只保存逻辑状态: - -- `StackSocketId` -- local endpoint -- remote endpoint -- flags/options -- listener backlog -- shutdown 状态 -- UDP per-fd 接收队列 - -`SocketFile::read()`、`write()`、`readable()`、`writable()` 调用 `NetworkStack` 方法,不直接锁 `SocketSet`。 - -## 必做改造 - -1. 定义 `StackSocketId`,替代公开传递的 `SocketHandle`。 -2. 将 `FD_SOCKET_MAP` 的 value 从 smoltcp handle 改为 `StackSocketId`;更理想的是让 fd 的 `SocketFile` 成为唯一映射源。 -3. 将 `create_tcp_socket()`、`create_udp_socket()` 改为 `NetworkStack::create_socket()`。 -4. 将 `tcp_connect()`、`socket_sendto()`、`udp_attach_fd_to_port()` 等函数改为 `NetworkStack` 方法。 -5. `kernel::syscall::network.rs` 中的 `SOCKET_SET` 和 `smoltcp` import 必须消失。 -6. 所有网络 syscall 统一使用 `uapi::errno`,不再返回私有负数。 - -## 阻塞与非阻塞语义 - -- 非阻塞 fd 遇到暂不可读/写返回 `EAGAIN`。 -- 阻塞路径可以循环调用 `NetworkStack::poll()`、`yield_task()`、信号中断检查。 -- poll/select 的可读可写判断必须依赖 `SocketFile` 的公开方法,而这些方法内部转发到 `NetworkStack` 查询。 - -## 验收点 - -- `socket()` 只创建 `SocketFile` 和网络栈 socket,不分配虚假 fd。 -- `bind/listen/connect/accept/send/recv/sendto/recvfrom` 都经 `NetworkStack`。 -- `getsockname/getpeername` 不直接读取 smoltcp socket。 -- 用户指针访问不发生在持有网络栈内部锁期间。 - -## 当前执行状态 - -- `kernel::syscall::network` 已不再 import `SOCKET_SET`、`NET_IFACE` 或 `smoltcp::socket::{tcp, udp}`。 -- `listen/accept/connect/shutdown/getsockname/getpeername` 通过 `NetworkStack` 查询或修改协议栈状态。 -- `SocketFile::read/write/readable/writable/recvfrom/drop` 已改为调用 `NetworkStack` 文件级 API。 -- `set_network_interface_config()` 已从私有 `-1/-2/-3/-4/-5` 返回值改为 `uapi::errno`。 - -保留的兼容点: - -- `FD_SOCKET_MAP` 的 value 仍是 `SocketHandle`,尚未替换为真正独立的 `StackSocketId`。 -- `socket.rs` 仍包含 socket runtime 的 smoltcp 具体操作,但这些操作已位于 `NetworkStack` 调用边界之后。 diff --git a/document/net/rearchitecture_stack_runtime.md b/document/net/rearchitecture_stack_runtime.md deleted file mode 100644 index 3a3e69f2..00000000 --- a/document/net/rearchitecture_stack_runtime.md +++ /dev/null @@ -1,86 +0,0 @@ -# 协议栈运行时重构说明 - -本文档覆盖 `net::stack`。该层是 `smoltcp` 的唯一宿主,负责统一持有协议栈状态、socket 集合、设备适配器和 poll 推进逻辑。 - -## 当前代码 - -- `os/src/net/stack.rs` 定义 `NetworkStack`,持有 smoltcp socket set、active interface runtime、loopback link、UDP per-port dispatcher 和 TCP pending close。 -- `os/src/net/socket.rs` 保留 `SocketFile`、fd/socket 映射、UDP per-fd queue 和公开 socket 包装 API。 -- `os/src/net/interface.rs` 定义接口控制面,`SmoltcpInterface` 和 `NetDeviceAdapter` 由 `net::stack` 提供。 -- `poll_network_interfaces()`、`poll_network_and_dispatch()`、`poll_until_empty()` 均委托到 `NetworkStack`。 - -## 目标对象 - -建议引入: - -- `NetworkStack`:网络协议栈对外门面。 -- `StackRuntime`:单接口或多接口的 smoltcp runtime。 -- `StackSocketId`:网络子系统内部 socket id,不直接等同于 smoltcp handle。 -- `StackPollResult`:poll 后对 VFS/poll waiters 发布的事件摘要。 - -`NetworkStack` 内部独占持有: - -- `smoltcp::iface::Interface` -- `smoltcp::iface::SocketSet` -- `NetDeviceAdapter` -- UDP dispatch 表 -- TCP pending close 表 -- waiters 唤醒所需事件摘要 - -## 对外 API - -第一阶段 API 可以保持小而稳定: - -```rust -impl NetworkStack { - pub fn create_socket(&self, kind: SocketKind) -> Result; - pub fn bind(&self, id: StackSocketId, endpoint: NetEndpoint) -> Result<(), NetError>; - pub fn listen(&self, id: StackSocketId, backlog: usize) -> Result<(), NetError>; - pub fn accept(&self, id: StackSocketId) -> Result; - pub fn connect(&self, id: StackSocketId, remote: NetEndpoint, local: NetEndpoint) -> Result<(), NetError>; - pub fn send(&self, id: StackSocketId, data: &[u8]) -> Result; - pub fn recv(&self, id: StackSocketId, data: &mut [u8]) -> Result; - pub fn poll(&self) -> StackPollResult; -} -``` - -这里的 `NetEndpoint`、`NetError` 应是网络子系统自有类型,避免 syscall 层直接依赖 smoltcp 类型。 - -## 必做改造 - -1. 把 `SmoltcpInterface` 下沉到 `net::stack`,不再从接口层公开返回。 -2. 把 `NetDeviceAdapter` 移到 `net::stack` 内部。 -3. 把 smoltcp socket set 和 active interface runtime 收进 `NetworkStack`,不再保留裸全局状态。 -4. 把 UDP dispatch、TCP pending close、poll waiters 唤醒统一放入 `NetworkStack::poll()`。 -5. 所有 socket 操作完成后如需推进协议栈,只调用 `NetworkStack::poll()`。 - -## 锁顺序 - -推荐固定为: - -`NetworkStack -> InterfaceRuntime -> SocketSet -> SocketFile local state` - -禁止: - -- syscall 层先锁 `SocketFile` 后抓裸 `SOCKET_SET`。 -- 中断路径直接抓 `SocketSet` 并执行 smoltcp poll。 -- 持有网络栈锁时复制用户缓冲区。 - -## 验收点 - -- `smoltcp` import 集中在 `net::stack` 和少量兼容转换模块。 -- `SOCKET_SET` 不再作为裸全局暴露给 syscall。 -- `NetworkStack::poll()` 是唯一协议栈推进入口。 -- UDP 可读事件、TCP close reaping、waiter wakeup 都在 poll 结束阶段集中发布。 - -## 当前执行状态 - -- smoltcp socket set、active interface runtime、loopback link、UDP dispatch 表和 TCP pending close 表已成为 `NetworkStack` 字段。 -- `NetworkStack` 已覆盖 socket 创建、TCP connect/listen/state/close/endpoints、UDP dispatch/attach、poll、SocketFile read/write/readable/writable/drop/sendto/recvfrom。 -- UDP dispatch、TCP pending close reaping 和 poll waiter 唤醒仍在统一 poll 路径中执行。 -- `NetIfaceWrapper` 已搬入 `net::stack`,`socket.rs` 不再承载协议栈 runtime 实现。 -- loopback 兼容队列已从 `NetDeviceAdapter` 字段移入 `NetworkStack` 的 loopback link。 - -保留的兼容点: - -- `SocketHandle` 仍等同于 smoltcp handle 包装,暂未引入多 runtime 安全的 socket id。