4. 目录协议基础实现

任务目标

实现一个基于目录的缓存一致性协议核心逻辑,使用位向量记录每个缓存块在各核心中的共享状态,处理读写请求并维护一致性状态机。本案例聚焦目录协议中最核心的状态跟踪与请求转发逻辑,不涉及总线仲裁、存储层次或具体缓存替换策略。

核心设计

目录表以缓存块地址为索引,每个条目包含:

  • 状态:UNCACHED(无缓存)、SHARED(共享)、EXCLUSIVE(独占/已修改)
  • 共享者位向量:每bit对应一个核心,置1表示该核心持有此块副本
  • 拥有者:当状态为EXCLUSIVE时,记录当前持有写权限的核心ID

完整实现

#include <cstdint>
#include <unordered_map>
#include <cassert>
#include <cstdio>

constexpr int NUM_CORES = 4;
constexpr uint32_t INVALID_OWNER = 0xFFFFFFFF;

enum class DirState { UNCACHED, SHARED, EXCLUSIVE };

struct DirEntry {
    DirState state = DirState::UNCACHED;
    uint32_t sharers = 0;      // 位向量,bit i 表示 core i 持有副本
    uint32_t owner = INVALID_OWNER; // EXCLUSIVE 时的写权限持有者
};

class DirectoryProtocol {
public:
    // 核心发起读请求,返回是否命中(无需总线传输)
    bool handleRead(uint32_t core_id, uint32_t addr) {
        auto &e = dir[addr];
        if (e.state == DirState::UNCACHED) {
            // 从内存取数,标记共享
            e.state = DirState::SHARED;
            e.sharers = (1u << core_id);
            return false; // miss,需从内存加载
        }
        if (e.state == DirState::EXCLUSIVE) {
            // 若请求者不是拥有者,需要先写回内存再降级为共享
            if (e.owner != core_id) {
                // 通知原拥有者写回(此处仅状态更新)
                e.state = DirState::SHARED;
                e.sharers = (1u << core_id) | (1u << e.owner);
                e.owner = INVALID_OWNER;
                return false; // 需要从拥有者处获取数据
            }
            return true; // 拥有者自身读命中
        }
        // SHARED 状态
        e.sharers |= (1u << core_id);
        return true; // 共享读命中
    }

    // 核心发起写请求,返回是否需要失效其他副本
    bool handleWrite(uint32_t core_id, uint32_t addr) {
        auto &e = dir[addr];
        if (e.state == DirState::UNCACHED) {
            e.state = DirState::EXCLUSIVE;
            e.owner = core_id;
            e.sharers = (1u << core_id);
            return false; // 无其他副本需失效
        }
        if (e.state == DirState::SHARED) {
            uint32_t others = e.sharers & ~(1u << core_id);
            if (others != 0) {
                // 需要向其他共享者发送失效消息
                e.sharers = (1u << core_id);
                e.state = DirState::EXCLUSIVE;
                e.owner = core_id;
                return true; // 有失效需要广播
            }
            // 无其他共享者,直接升级
            e.state = DirState::EXCLUSIVE;
            e.owner = core_id;
            return false;
        }
        // EXCLUSIVE 状态
        if (e.owner == core_id) {
            return false; // 自身已持有写权限
        }
        // 其他核心独占,需先写回再转移所有权
        e.owner = core_id;
        e.sharers = (1u << core_id);
        return true; // 需要通知原拥有者写回
    }

    // 处理核心的失效确认(收到失效消息后回复)
    void handleAck(uint32_t core_id, uint32_t addr) {
        auto &e = dir[addr];
        e.sharers &= ~(1u << core_id);
        if (e.sharers == 0 && e.state == DirState::SHARED) {
            e.state = DirState::UNCACHED;
        }
    }

    // 打印目录状态(调试用)
    void dump(uint32_t addr) const {
        auto it = dir.find(addr);
        if (it == dir.end()) {
            printf("addr 0x%x: UNCACHED\n", addr);
            return;
        }
        const auto &e = it->second;
        const char *st = e.state == DirState::UNCACHED ? "UNCACHED" :
                         e.state == DirState::SHARED ? "SHARED" : "EXCLUSIVE";
        printf("addr 0x%x: state=%s sharers=0x%x owner=%u\n",
               addr, st, e.sharers, e.owner);
    }

private:
    std::unordered_map<uint32_t, DirEntry> dir;
};

// 最小验证入口
int main() {
    DirectoryProtocol proto;
    const uint32_t ADDR = 0x1000;

    // 场景1:core0 读 miss,core1 读共享
    assert(proto.handleRead(0, ADDR) == false);
    assert(proto.handleRead(1, ADDR) == true);
    proto.dump(ADDR); // 预期: SHARED sharers=0x3

    // 场景2:core2 写,需要失效 core0/core1
    assert(proto.handleWrite(2, ADDR) == true);
    proto.dump(ADDR); // 预期: EXCLUSIVE owner=2 sharers=0x4

    // 场景3:core0 读,需要从 core2 获取数据并降级为共享
    assert(proto.handleRead(0, ADDR) == false);
    proto.dump(ADDR); // 预期: SHARED sharers=0x5

    // 场景4:core2 写回确认(模拟收到失效响应)
    proto.handleAck(2, ADDR);
    proto.dump(ADDR); // 预期: SHARED sharers=0x1 (仅core0)

    printf("All directory protocol tests passed.\n");
    return 0;
}

运行与验证

  1. 将代码保存为 directory_protocol.cpp
  2. 编译:g++ -std=c++11 -o dir_proto directory_protocol.cpp
  3. 运行:./dir_proto

通过条件:程序输出4行目录状态(分别对应注释中的预期状态),最后打印 All directory protocol tests passed. 且无断言失败。四个场景覆盖了:读miss→共享、共享→独占(需失效)、独占→共享(需写回)、失效确认后的状态清理。

关键说明

  • 位向量 sharers 的bit i对应core i,0x3表示core0和core1持有副本
  • 返回 true 表示需要总线事务(失效广播或写回请求),false 表示本地命中或仅需内存访问
  • 实际系统中失效消息的发送与确认是异步的,本实现用 handleAck 模拟确认到达后的状态更新
  • 目录条目按需创建,UNCACHED 状态不显式存储,通过 unordered_map 缺省行为隐式表示