如何设计网络与 Socket
把网卡收到的包变成用户程序可读写、可阻塞、可限额的 fd。
定位:这页不实现完整 TCP/IP。它只讲 ToyOS 第一版网络接口:应用不直接碰网卡,而是通过 socket fd 收发字节流或数据报。
失败:网卡中断带来数据包。如果用户程序直接读网卡队列,就会绕过端口权限、破坏其他连接、抢占缓冲区。网络必须先进入内核协议和 socket 账本。
| 问题 | 直接做会怎样坏 | 被迫设计 |
| 包属于哪个程序 | 应用自己抢包,端口归属混乱 | 端口表、socket 表 |
| 到达速度和读取速度不同 | 包丢失或无限占内存 | 接收缓冲区和背压 |
| 连接有状态 | 应用自己处理状态,错误不可控 | socket state machine |
| 网络也是 fd | read/write/poll 无法统一 | socket fd 接入文件表 |
| 对象 | 字段 | 负责什么 |
Socket | state、local_addr、remote_addr、rx_buffer、tx_buffer | 一条网络端点的状态和缓冲 |
PortTable | port → socket | 把到达端口的数据交给对应 socket |
Packet | src、dst、payload、protocol | 内核处理的网络数据单元 |
AcceptQueue | pending_connections | 保存等待 accept 的连接 |
NetworkBuffer | data、len、capacity、limit | 吸收网卡和应用速度差 |
| 系统调用 | 职责 | 必须检查 |
socket() | 创建 socket 对象并返回 fd | fd 上限、内存上限 |
bind(fd, addr) | 绑定本地地址和端口 | 端口是否被占用,是否有权限 |
listen(fd) | 进入监听状态 | socket 状态是否正确 |
accept(fd) | 取出一个待处理连接 | 队列为空时阻塞或返回错误 |
connect(fd, addr) | 主动连接远端 | 地址合法、路由可达、状态转换合法 |
read/write | 收发 socket 数据 | 缓冲区、连接状态、用户指针 |
network_interrupt:
packet = nic.read_packet()
socket = port_table.lookup(packet.dst_port)
if socket == null:
drop(packet)
else if socket.rx_buffer.full:
apply_backpressure_or_drop(packet)
else:
socket.rx_buffer.push(packet.payload)
wakeup(socket.read_wait_queue)
| 阶段 | 内核动作 | 失败处理 |
| 网卡中断 | 驱动取包 | 坏包丢弃 |
| 协议解析 | 识别目标地址、端口、协议 | 未知协议丢弃或返回错误 |
| 路由到 socket | 查端口表和连接表 | 无监听者丢弃或拒绝 |
| 进入缓冲区 | 放入 rx_buffer | 满了就背压或丢弃 |
| 唤醒进程 | read 阻塞者变 ready | 唤醒条件必须精确 |
| 实验 | 通过标准 | 证明了什么 |
| 监听实验 | 服务进程 bind/listen 后,端口表可查 | 端口归属成立 |
| 连接实验 | 客户端 connect 后,服务端 accept 返回新 fd | 连接状态机成立 |
| 收包实验 | 网卡包进入对应 socket rx_buffer | 中断到 socket 路径成立 |
| 阻塞实验 | rx_buffer 空时 read 阻塞,数据到达后唤醒 | 网络和调度器联动成立 |
| 限额实验 | 连接数或缓冲区超过上限时返回错误或丢弃 | 网络不能无限吃内存 |
一句话:socket 的价值不是“能联网”,而是把网络连接收成一个受权限、缓冲、状态机和调度器管理的 fd。