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 的连接也大概率超时。

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

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

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

  • 宏就是给一段代码 / 内容起个别名
  • 编译之前,编译器会把别名自动替换成真实内容
  • 纯文本替换,不理解语法,只是无脑替换!
相关推荐
kyle~1 小时前
Linux系统优化---PREEMPT_RT机器人开发方向
linux·运维·机器人
独隅2 小时前
在 Linux 上部署 TensorFlow 模型的全面指南
linux·运维·tensorflow
Strange_Head2 小时前
《Linux系统编程篇》Linux Socket 网络编程02 (Linux 进程间通信(IPC))——基础篇
linux·运维·网络
星河耀银海2 小时前
JAVA IO流:从基础原理到实战应用
java·服务器·开发语言
Joren的学习记录3 小时前
【Linux运维大神系列】Kubernetes详解7(k8s技术笔记3)
linux·运维·kubernetes
q_30238195563 小时前
告别kubectl命令地狱!MCP-K8s让AI成为你的智能运维助手
运维·人工智能·语言模型·chatgpt·kubernetes·文心一言·devops
chenqianghqu4 小时前
ubuntu 22.04环境中安装goland
linux·运维·ubuntu
gwjcloud4 小时前
Frp内网穿透
linux·运维·服务器
MwEUwQ3Gx4 小时前
常见Linux权限提升笔记
linux·运维·笔记