4.4【A】

  • 进程之间不能直接访问对方内存

  • 所以必须用 Socket + 共享内存 通信

  • 每个进程独立运行

  • 每个进程自己负责自己的连接

  • 网卡模拟器进程:监听 PCIe 连接

  • QEMU 进程:主动连接 PCIe

  • 它们通过 Socket 建立连接,交换自我介绍

  • 然后用共享内存高速通信

底层状态

初始化

  • BaseIfInit = 给接口填参数、做检查
  • BaseIfListen = 创建 Socket、绑定、监听、从共享内存里划分队列空间
  • BaseIfEstablish = 等待连接 + 握手(真正把链路打通)
  • in_num_entries /out_num_entries 是固定常数!不是动态变化的!

因为这是 "环形队列(Ring Buffer)"!长度固定!

你必须理解:

SimBricks 用的是 预分配固定长度环形队列,不是动态链表!

  • in_num_entries = 队列最大能存多少条消息

  • out_num_entries = 队列最大长度

  • 创建时就定死,永远不变!

就像:

  • 快递柜一共有 50 个格子

  • 这个数量 永远不变

  • 格子只会在 "空 / 满" 之间变化

  • 最大容量永远是 50

所以:

  • in_num_entries = 固定常数
  • out_num_entries = 固定常数
  • 创建时就确定,不会变!

SimbricksBaseIfInit

作用:初始化接口结构体、做参数检查

只干 3 件事:

  • 检查 延迟 ≥ 同步间隔(必须满足,否则同步出错)
  • 把接口内存清零
  • 把参数复制到接口里

它不创建 Socket、不分配内存、不监听!只是 "填表"!

blocking_conn = 是否使用阻塞连接

  • blocking_conn = true→ 阻塞模式→ 等待连接时,程序会卡住不动,直到有人连接

  • blocking_conn = false非阻塞模式(我们要设置的) → 等待连接时,程序不会卡住,可以去做别的事

判断 fcntl 获取属性是否失败

Linux 系统函数调用失败时会返回 -1。这里就是检查:**"我刚才读取文件状态成功了吗?"**失败就直接报错退出。

SimbricksBaseIfListen

作用:创建 Socket + 绑定 + 监听 + 从共享内存划分队列空间

干 6 件大事:

  1. 创建 Unix Socket
  2. 设置非阻塞(可选)
  3. bind 绑定路径
  4. listen 开始等待连接
  5. 从共享内存池里划出一段空间作为 入队列 (in_queue)
  6. 再划出一段空间作为 出队列 (out_queue)

这个函数执行完 → 接口开始等待别人来连接!

  • 创建 Socket(相当于装个电话)
  • 绑定地址(相当于装个电话号码)
  • 开始监听(相当于等着别人打电话)
  • 划分共享内存队列(切出收件箱、发件箱)
  • pool->base:共享内存起始地址
  • pool->pos:当前分配到的位置
  • 计算结果:从共享内存里切出一段作为收件箱

SimBricksBaseIfEstablish

作用:等待所有接口连接成功 + 握手交换信息

就是你启动时看到的:

plaintext

复制代码
waiting for pcie connection...
waiting for net connection...

干 3 件事:

  1. 循环检查所有接口(PCIe + ETH)是否连上
  2. 连上后发送 / 接收握手信息(intro)
  3. 所有接口全部就绪才返回

它是 "等待全部接通" 的函数!

调用 SimbricksBaseIfConnected 检查当前接口连好了吗

它会返回 3 种值:

  • ret < 0:连接失败(出错)
  • ret = 0:已经连接好
  • ret > 0还没好,需要继续等待
  1. 发送握手消息(告诉对方我是谁)
  2. 接收握手消息(读取对方是谁)
  3. 用 poll 等着事件发生(不浪费 CPU)

直到 PCIe 和网络都握手成功,函数才结束!

  • Handshake = 握手

  • Tx = Send 发送

  • Intro = 自我介绍(我是网卡、我的设备 ID...)

    如果当前状态是"等待发送自我介绍"
    那就调用函数把自我介绍发出去!
    如果发送失败 → 报错退出!

NicIf

队列大小 = 条目数量 × 单个条目大小

ests [2] = 存放 "要建立的连接信息" 的数组

最多同时建立 2 个连接:PCIe + 网络

所以数组大小是 2。

n_bifs = 实际要建立的接口数量

有网络 → n_bifs++

有 PCIe → n_bifs++

建立接口信息

复制代码
if (netParams) {
    SimbricksBaseIfInit(netif, netParams);   // 初始化网络接口
    SimbricksBaseIfListen(netif, pool);      // 等待交换机连接

    // 把这个接口加入"待建立列表"
    ests[n_bifs].base_if = netif;
    ests[n_bifs].tx_intro = &net_intro;   // 我发给对方的信息
    ests[n_bifs].rx_intro = &net_intro;   // 对方发给我的信息
    n_bifs++;
}

Socket

  • sockaddr_un = Unix 域套接字地址结构体
  • _un = Unix 的缩写(表示本地进程间通信)
  • saun = sockaddr_un 的简写
  • sun_ = sockaddr_un 前缀(为了统一命名)
  • sun_family 是宏展开出来的,不是手动写的
  • socket() = 买了一部手机(得到手机号 / FD)
  • bind() = 去营业厅把手机号和你本人绑定

结构体地址问题

nicif_ 不是指针!它是一个实实在在的结构体变量!

结构体变量一旦创建,它的内部所有成员就已经存在内存里了!

不需要提前初始化,也不需要赋值,就能取地址!

即使里面的值是随机的、脏的,变量本身是存在的!

struct SimbricksBaseIf *netif = &nicif->net.base;

struct SimbricksBaseIf *pcieif = &nicif->pcie.base;

为什么这两行没问题?

因为:

  1. nicif 是一个有效指针 (你传的是 &nicif_

  2. nicif->net 已经存在(结构体成员)

  3. nicif->net.base 也已经存在

  4. 所以 &nicif->net.base合法地址

这里仅仅是取地址!没有读里面的值!

CXL

CXLCache

CXL Type3

4.1 Type 3(内存扩展器)Classic 模式完整链条

复制代码
用户运行:
  gem5.opt x86-cxl-type3-with-classic.py
             │
             ▼
  [Python 初始化阶段]
  1. 创建 SimpleBoard / X86Board
  2. 配置 PrivateL1PrivateL2SharedL3CacheHierarchy
     (L1 32K/32K + L2 512K + L3 96M, MESI)
  3. 创建 DIMM_DDR5_4400 × 2
     - memory[0]: 3GB 主机内存  @ 0x00000000
     - memory[1]: 8GB CXL 内存  @ 0xC0000000 (CXL 地址空间)
  4. 注入 CXLBridge(位于 L3 缓存与 CXL DRAM 控制器之间)
  5. set_kernel_disk_workload(vmlinux, disk_img, cmd)
  6. simulator.run()
             │
             ▼
  [gem5 事件驱动模拟启动]
  7. KVM CPU 快速启动 Linux 内核(跳过 Boot 细节)
  8. Linux 内核通过 ACPI/SRAT 发现 CXL NUMA 节点
  9. m5 exit 事件触发 → 切换到 TIMING/O3 CPU
             │
             ▼
  [基准测试执行阶段]
  10. 用户程序发起内存访问(例如 numactl --membind=1 ./test)
             │
             ▼
  [CPU 访问 CXL 内存的周期级模拟路径]

  CPU 核心(O3/TIMING)
    → L1D Cache(MESI 查找)
    → L2 Cache(MESI 查找)
    → L3 Cache(共享,MESI 查找)
    → Cache Miss → 发送 MemReq 包到内存总线
             │
             ▼
  CoherentXBar(一致性总线)
    → 地址解码:命中 CXL 地址范围?
    → Yes → 路由到 CXLBridge
             │
             ▼
  CXLBridge::BridgeResponsePort::recvTimingReq()
    → 检查地址是否在 cxl_range 内
    → 计算 total_delay = bridge_lat + proto_proc_lat
    → 设置 pkt->cxl_cmd = M2SReq(读)/ M2SRwD(写)
    → 将包推入 reqQueue,调度 schedTimingReq 事件
             │
  [等待 total_delay 周期]
             │
             ▼
  CXLBridge::BridgeRequestPort 发送请求
    → CXL 设备侧 MemCtrl::recvTimingReq()
    → DRAM Interface 计算 DRAM 时序(tCL, tRCD, tRP...)
    → 生成响应包,pkt->cxl_cmd = S2MDRS(含数据)
             │
  [等待 DRAM 访问延迟]
             │
             ▼
  CXLBridge::BridgeResponsePort 接收响应
    → 推入 respQueue
    → 调度 schedTimingResp 事件
             │
  [等待 bridge_lat 返回延迟]
             │
             ▼
  CoherentXBar → L3 Cache 填充
    → L2 Cache 填充
    → L1D Cache 填充
    → CPU 寄存器文件获得数据
             │
             ▼
  [周期计数更新,统计延迟/带宽指标]

CXLBridge

m5

m5 是 gem5 模拟器的核心 Python 模块,是用户与 gem5 底层 C++ 仿真引擎交互的入口,也是 SimCXL(基于 gem5 扩展)中控制仿真全生命周期的核心接口。

核心定位:

m5 模块由 gem5 构建系统通过 pybind11 自动生成(绑定 C++ 代码)+ 手动编写的 Python 封装组成,是 Python 层与 C++ 层的 "桥梁"。

所有仿真操作(实例化系统、启动仿真、读取统计信息、控制仿真流程)都通过 m5 模块完成,比如:

m5.instantiate():将 Python 定义的系统配置转化为 C++ 仿真对象;

m5.simulate():启动事件驱动的仿真循环;

m5.stats.dump():导出仿真统计数据;

m5.exit():终止仿真。

这是 SimCXL/gem5 中从高层易用的 Python 组件封装,到底层 C++ 绑定的调用链路,核心是 "把用户写的简洁 Python 配置,转化为底层 C++ 仿真对象",

C语言

  1. socket()

创建一个通信端点(电话)

  1. bind()

给 Socket 绑定一个路径地址(装电话号码)

  1. listen()

开始等待别人连接(等电话)

  1. fcntl()

修改文件描述符属性(设置非阻塞)

  1. close()

关闭 Socket,释放资源(挂电话)

strlen(字符串)

  • 作用:计算字符串长度
  • 这里检查:Socket 路径太长会超出系统限制,无法绑定

listen 监听函数

  • 作用:开始等待别人来连接
  • 参数 5:等待连接的队列长度(最多等 5 个)

socket(地址族, 类型, 协议)

  • AF_UNIX:本地进程间通信(机器内)
  • SOCK_STREAM:可靠字节流(TCP 模式)
  • 返回值 listen_fd
    • 文件描述符(相当于电话的 ID
    • 后面所有操作都靠它

bind 绑定函数(非常重要)

  • 作用:把 Socket 和文件路径绑定在一起
  • 相当于:给电话装上号码
  • 别人通过这个路径就能找到你

strncpy 字符串拷贝

  • 安全复制字符串,防止超长溢出

代理

  1. DNS 解析失败 --- Clash 配置了使用 Cloudflare 的 DoH(DNS over HTTPS)dns.cloudflare.com/dns-query,但这个 DNS 服务器本身就被墙了,所以连 DNS 都查不了。

  2. 模式是"直连" --- 当前是 DIRECT 直连模式,没有走代理,所以即使 DNS 解析出来,GitHub 的连接也大概率超时。

去左侧点「代理 」,把模式从「直连」改成「规则 」或「全局」:

  • 规则模式 --- 按规则分流,推荐
  • 全局模式 --- 所有流量走代理,用于测试

宏 = 代码替换工具(文本替换)

  • 宏就是给一段代码 / 内容起个别名
  • 编译之前,编译器会把别名自动替换成真实内容
  • 纯文本替换,不理解语法,只是无脑替换!
相关推荐
子琦啊1 天前
【算法复习】字符串 | 两个底层直觉,吃透高频题
linux·运维·算法
跨境数据猎手1 天前
跨境独立站系统技术拆解(附带源码)
服务器·前端·php
AOwhisky1 天前
Kubernetes 学习笔记:集群管理、命名空间与 Pod 基础
linux·运维·笔记·学习·云原生·kubernetes
小龙在慢慢变强..1 天前
目录结构(FHS 标准)
linux·运维·服务器
刘延林.1 天前
win11系统下通过 WSL2 安装Ubuntu 24.04 使用RTX 5080 GPU
linux·运维·ubuntu
星恒讯工业路由器1 天前
星恒讯工业生产自动化解决方案
运维·物联网·自动化·智能路由器·信息与通信
a8a3021 天前
Laravel9.x新特性全解析
运维·spring boot·nginx
beyond阿亮1 天前
IEC104 Client Simulator - IEC104 主站/客户端模拟器 仿真器免费使用教程
运维·服务器·网络
(Charon)1 天前
【C++/Qt】Qt 封装 TCP 客户端底层 Network 类:连接、收发、自动测试与错误处理
服务器·网络·qt·tcp/ip