------从共享内存行情总线到本地Socket指令通道,构建零拷贝交易通信架构
一、开篇:为什么交易系统必须用跨进程通信?
在量化交易系统中,一个致命的架构错误是把策略引擎、行情接收、风控检查、订单发送全部塞进同一个进程。当策略逻辑因为一个bug崩溃时,风控模块也随之停摆------未撤回的订单在市场里裸奔,这才是真正的灾难。
正确的做法是进程隔离:每个关键模块独立运行在各自进程中,通过高效的跨进程通信(IPC)机制协同工作。但交易系统对延迟极度敏感,普通的IPC机制(如HTTP、REST)根本无法满足微秒级通信要求。Qt提供了多种IPC原语,本文将深入源码层面,解析如何在交易场景中选择和优化这些机制。
二、Qt IPC机制全景图与选型决策
┌──────────────────────────────────────────────────────┐
│ Qt 跨进程通信机制 │
├───────────────┬──────────────┬───────────────────────┤
│ 共享内存 │ 本地Socket │ D-Bus │
│ QSharedMemory│ QLocalSocket │ QDBusConnection │
│ QSystemSemaphore │ │
│ QSystemLock │ │
├───────────────┼──────────────┼───────────────────────┤
│ 延迟:<1μs │ 延迟:5-20μs │ 延迟:50-200μs │
│ 吞吐:极高 │ 吞吐:高 │ 吞吐:中 │
│ 复杂度:高 │ 复杂度:中 │ 复杂度:低 │
│ 场景:行情广播│ 场景:指令传递│ 场景:配置/控制 │
└───────────────┴──────────────┴───────────────────────┘
交易系统选型原则:
- 行情数据广播 → QSharedMemory(零拷贝,最低延迟)
- 交易指令传递 → QLocalSocket/QLocalServer(可靠有序,双向通信)
- 系统控制与管理 → QDBus(标准接口,便于第三方集成)
三、QSharedMemory源码解析------行情零拷贝广播
3.1 底层实现机制
QSharedMemory 在不同平台使用不同的系统API:
cpp
// qtbase/src/corelib/kernel/qsharedmemory.cpp
// Windows: CreateFileMapping + MapViewOfFile
// Linux: shmget + shmat (POSIX shared memory)
// macOS: mmap with MAP_SHARED
// Windows实现 (qtbase/src/corelib/kernel/qsharedmemory_p.h)
class QSharedMemoryPrivate
{
public:
HANDLE handle() const { return m_handle; }
// Windows特定
HANDLE m_handle = nullptr; // CreateFileMapping返回的句柄
void *m_memory = nullptr; // MapViewOfFile映射的地址
QString m_key; // 共享内存名称
qsizetype m_size = 0; // 共享内存大小
};
3.2 create与attach的源码追踪
cpp
// qtbase/src/corelib/kernel/qsharedmemory_win.cpp
bool QSharedMemoryPrivate::create(qsizetype size)
{
// 1. 将Qt key转换为Windows name
const QString nativeKey = makePlatformSafeKey(m_key);
// 2. 创建文件映射对象
m_handle = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr,
PAGE_READWRITE, 0, size,
reinterpret_cast<LPCWSTR>(nativeKey.utf16()));
if (!m_handle) {
setErrorString(QLatin1String("QSharedMemory::create"));
return false;
}
// 3. 映射视图
m_memory = MapViewOfFile(m_handle, FILE_MAP_ALL_ACCESS, 0, 0, size);
if (!m_memory) {
CloseHandle(m_handle);
m_handle = nullptr;
return false;
}
// 4. 初始化为0
memset(m_memory, 0, size);
m_size = size;
return true;
}
3.3 交易系统实战:行情共享内存总线
cpp
// ========== 行情共享内存定义 ==========
#pragma pack(push, 1) // 紧凑对齐,减少内存占用
struct MarketDataHeader
{
quint32 magic; // 魔数校验: 0x4D4B5444 ("MKTD")
quint32 version; // 数据版本号
quint64 sequence; // 行情序列号(单调递增)
quint64 timestamp; // 纳秒级时间戳
quint32 symbolCount; // 标的物数量
quint32 dataOffset; // 行情数据偏移量
quint32 checksum; // CRC32校验和
};
struct SymbolTick
{
char symbol[16]; // 标的代码
double lastPrice; // 最新价
double bidPrice[5]; // 买一到买五价
double askPrice[5]; // 卖一到卖五价
qint32 bidVolume[5]; // 买一到买五量
qint32 askVolume[5]; // 卖一到卖五量
double open, high, low, close;
qint64 volume; // 成交量
double turnover; // 成交额
quint64 updateTime; // 更新时间
};
#pragma pack(pop)
// 共享内存总大小 = header + N个tick
constexpr qsizetype SHM_SIZE = sizeof(MarketDataHeader) + 5000 * sizeof(SymbolTick);
// ========== 行情发布端(行情进程) ==========
class MarketDataPublisher : public QObject
{
Q_OBJECT
public:
explicit MarketDataPublisher(QObject *parent = nullptr)
: QObject(parent)
{
m_sharedMemory.setKey("TradingSystem_MarketData_Bus");
if (!m_sharedMemory.create(SHM_SIZE)) {
if (m_sharedMemory.error() == QSharedMemory::AlreadyExists) {
if (!m_sharedMemory.attach()) {
qCritical() << "Attach shared memory failed:" << m_sharedMemory.errorString();
}
}
}
m_header = static_cast<MarketDataHeader*>(m_sharedMemory.data());
m_ticks = reinterpret_cast<SymbolTick*>(
static_cast<char*>(m_sharedMemory.data()) + sizeof(MarketDataHeader));
}
void publishTick(const QString &symbol, const SymbolTick &tick)
{
// 关键:使用系统信号量而非QSharedMemory::lock()
// QSharedMemory::lock() 内部使用QSystemLock,在Windows上是Mutex
// 我们用更轻量的SpinLock + 序列号方案避免锁开销
// 1. 递增序列号(偶数表示正在写入)
quint64 oldSeq = m_header->sequence;
m_header->sequence = oldSeq + 1; // 奇数→写入中
// 2. 写入发布内存屏障(确保序列号先于数据可见)
// 在x86上,store有release语义,但为跨平台安全:
#if defined(Q_OS_X86) || defined(Q_OS_X86_64)
__asm__ __volatile__("" ::: "memory"); // 编译器屏障
#endif
// 3. 查找或添加symbol slot
quint32 idx = findOrCreateSymbol(symbol);
m_ticks[idx] = tick;
memcpy(m_ticks[idx].symbol, symbol.toUtf8().constData(),
qMin(symbol.size(), 15));
// 4. 更新header
m_header->timestamp = getCurrentNanos();
m_header->checksum = computeCRC32(m_ticks, m_header->symbolCount);
// 5. 递增序列号(偶数→写入完成)
m_header->sequence = oldSeq + 2; // 偶数→可读
emit tickPublished(symbol);
}
private:
QSharedMemory m_sharedMemory;
MarketDataHeader *m_header = nullptr;
SymbolTick *m_ticks = nullptr;
QHash<QString, quint32> m_symbolIndex;
quint32 findOrCreateSymbol(const QString &symbol)
{
if (m_symbolIndex.contains(symbol)) {
return m_symbolIndex[symbol];
}
quint32 idx = m_header->symbolCount++;
m_symbolIndex[symbol] = idx;
return idx;
}
quint64 getCurrentNanos()
{
// Windows: QueryPerformanceCounter
LARGE_INTEGER freq, counter;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&counter);
return (quint64)counter.QuadPart * 1000000000ULL / (quint64)freq.QuadPart;
}
};
// ========== 行情消费端(策略进程) ==========
class MarketDataConsumer : public QObject
{
Q_OBJECT
public:
explicit MarketDataConsumer(QObject *parent = nullptr)
: QObject(parent)
{
m_sharedMemory.setKey("TradingSystem_MarketData_Bus");
if (!m_sharedMemory.attach(QSharedMemory::ReadOnly)) {
qCritical() << "Attach failed:" << m_sharedMemory.errorString();
}
m_header = static_cast<const MarketDataHeader*>(m_sharedMemory.constData());
m_ticks = reinterpret_cast<const SymbolTick*>(
static_cast<const char*>(m_sharedMemory.constData()) + sizeof(MarketDataHeader));
}
// 无锁读取:利用序列号的奇偶性判断数据一致性
bool readLatestTick(const QString &symbol, SymbolTick &outTick)
{
quint64 seq1, seq2;
do {
seq1 = m_header->sequence;
if (seq1 & 1) {
// 正在写入,自旋等待
QThread::usleep(1);
continue;
}
// 读取数据到本地
quint32 idx = findSymbol(symbol);
if (idx == UINT32_MAX) return false;
outTick = m_ticks[idx]; // 结构体拷贝
// 内存屏障
#if defined(Q_OS_X86) || defined(Q_OS_X86_64)
__asm__ __volatile__("" ::: "memory");
#endif
seq2 = m_header->sequence;
} while (seq1 != seq2); // 如果序列号变了,说明写入过程中被修改,重试
return true;
}
private:
QSharedMemory m_sharedMemory;
const MarketDataHeader *m_header = nullptr;
const SymbolTick *m_ticks = nullptr;
quint32 findSymbol(const QString &symbol)
{
// 简单线性查找(实际工程中可用更高效的索引)
for (quint32 i = 0; i < m_header->symbolCount; ++i) {
if (QString::fromUtf8(m_ticks[i].symbol) == symbol) {
return i;
}
}
return UINT32_MAX;
}
};
关键设计决策:
- 无锁读取 :利用序列号奇偶性实现seqlock模式,避免了
QSharedMemory::lock()的系统调用开销 - 只读映射 :消费端使用
QSharedMemory::ReadOnly,操作系统可优化为COW(Copy-on-Write) - 紧凑布局 :
#pragma pack(push, 1)消除padding,确保跨进程内存布局一致
四、QLocalSocket源码解析------交易指令可靠通道
4.1 平台适配层
cpp
// qtbase/src/corelib/socket/qlocalsocket.cpp
// QLocalSocket在不同平台的实现:
// Windows: Named Pipe (CreateNamedPipe + ConnectNamedPipe)
// Linux: Unix Domain Socket (AF_UNIX)
// macOS: Unix Domain Socket (AF_UNIX)
// Windows实现 (qtbase/src/corelib/socket/qlocalsocket_win.cpp)
class QLocalSocketPrivate
{
// Windows Named Pipe
HANDLE m_handle = INVALID_HANDLE_VALUE;
HANDLE m_eventHandle = nullptr;
OVERLAPPED m_overlapped;
// 异步读写缓冲
QByteArray m_readBuffer;
QByteArray m_writeBuffer;
};
4.2 交易指令通道实现
cpp
// ========== 指令协议定义 ==========
enum class CommandType : quint32
{
PlaceOrder = 0x01,
CancelOrder = 0x02,
ModifyOrder = 0x03,
QueryPosition = 0x04,
RiskCheck = 0x05,
Heartbeat = 0xFF
};
#pragma pack(push, 1)
struct CommandHeader
{
quint32 magic; // 0x544E4D43 ("TNMC")
CommandType cmdType;
quint32 payloadLen; // 负载长度
quint64 requestId; // 请求ID(用于匹配响应)
quint64 timestamp; // 时间戳
};
struct PlaceOrderPayload
{
char symbol[16];
quint8 direction; // 0=买, 1=卖
quint8 orderType; // 0=限价, 1=市价
double price;
qint64 quantity;
char accountId[32];
};
struct CommandResponse
{
quint64 requestId;
quint32 resultCode; // 0=成功
char message[128];
};
#pragma pack(pop)
// ========== 指令服务端(风控进程) ==========
class CommandServer : public QObject
{
Q_OBJECT
public:
explicit CommandServer(QObject *parent = nullptr)
: QObject(parent)
{
m_server = new QLocalServer(this);
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
connect(m_server, &QLocalServer::newConnection, this, [this]() {
QLocalSocket *client = m_server->nextPendingConnection();
connect(client, &QLocalSocket::readyRead, this, [this, client]() {
handleCommand(client);
});
connect(client, &QLocalSocket::disconnected, client, &QLocalSocket::deleteLater);
m_clients.append(client);
});
}
bool start(const QString &serverName)
{
// 清理可能残留的旧socket文件(Linux)或Named Pipe(Windows)
QLocalServer::removeServer(serverName);
return m_server->listen(serverName);
}
private:
void handleCommand(QLocalSocket *client)
{
// 流式读取:处理TCP类似的粘包问题
m_buffer.append(client->readAll());
while (m_buffer.size() >= (int)sizeof(CommandHeader)) {
const CommandHeader *header =
reinterpret_cast<const CommandHeader*>(m_buffer.constData());
// 校验magic
if (header->magic != 0x544E4D43) {
m_buffer.clear();
qWarning() << "Invalid command magic";
return;
}
quint32 totalLen = sizeof(CommandHeader) + header->payloadLen;
if (m_buffer.size() < (int)totalLen) {
break; // 等待更多数据
}
// 提取完整命令
QByteArray cmdData = m_buffer.left(totalLen);
m_buffer.remove(0, totalLen);
// 分发处理
processCommand(client, header, cmdData.mid(sizeof(CommandHeader)));
}
}
void processCommand(QLocalSocket *client, const CommandHeader *header,
const QByteArray &payload)
{
CommandResponse response;
response.requestId = header->requestId;
response.resultCode = 0;
switch (header->cmdType) {
case CommandType::PlaceOrder: {
if (payload.size() != sizeof(PlaceOrderPayload)) {
response.resultCode = 1001;
strncpy(response.message, "Invalid payload size", sizeof(response.message));
break;
}
const PlaceOrderPayload *order =
reinterpret_cast<const PlaceOrderPayload*>(payload.constData());
// 风控检查
if (!riskCheck(order)) {
response.resultCode = 2001;
strncpy(response.message, "Risk limit exceeded", sizeof(response.message));
break;
}
// 转发到交易所
emit orderReceived(*order);
strncpy(response.message, "Order accepted", sizeof(response.message));
break;
}
case CommandType::CancelOrder:
// ...处理撤单
break;
default:
response.resultCode = 9999;
strncpy(response.message, "Unknown command", sizeof(response.message));
}
// 发送响应
client->write(reinterpret_cast<const char*>(&response), sizeof(response));
client->flush();
}
QLocalServer *m_server;
QList<QLocalSocket*> m_clients;
QByteArray m_buffer;
};
// ========== 指令客户端(策略进程) ==========
class CommandClient : public QObject
{
Q_OBJECT
public:
explicit CommandClient(QObject *parent = nullptr)
: QObject(parent)
{
m_socket = new QLocalSocket(this);
connect(m_socket, &QLocalSocket::readyRead, this, &CommandClient::onReadyRead);
connect(m_socket, &QLocalSocket::disconnected, this, [this]() {
qWarning() << "Disconnected from command server, reconnecting...";
QTimer::singleShot(1000, this, &CommandClient::reconnect);
});
}
void connectToServer(const QString &serverName)
{
m_serverName = serverName;
m_socket->connectToServer(serverName);
}
quint64 placeOrder(const QString &symbol, quint8 direction,
double price, qint64 quantity, const QString &accountId)
{
// 构造命令
CommandHeader header;
header.magic = 0x544E4D43;
header.cmdType = CommandType::PlaceOrder;
header.payloadLen = sizeof(PlaceOrderPayload);
header.requestId = ++m_requestCounter;
header.timestamp = getCurrentNanos();
PlaceOrderPayload payload;
memset(&payload, 0, sizeof(payload));
strncpy(payload.symbol, symbol.toUtf8().constData(), 15);
payload.direction = direction;
payload.orderType = 0; // 限价
payload.price = price;
payload.quantity = quantity;
strncpy(payload.accountId, accountId.toUtf8().constData(), 31);
// 发送
QByteArray data;
data.append(reinterpret_cast<const char*>(&header), sizeof(header));
data.append(reinterpret_cast<const char*>(&payload), sizeof(payload));
m_socket->write(data);
m_socket->flush();
return header.requestId;
}
private:
void onReadyRead()
{
m_responseBuffer.append(m_socket->readAll());
while (m_responseBuffer.size() >= (int)sizeof(CommandResponse)) {
const CommandResponse *resp =
reinterpret_cast<const CommandResponse*>(m_responseBuffer.constData());
emit commandResponse(resp->requestId, resp->resultCode,
QString::fromUtf8(resp->message));
m_responseBuffer.remove(0, sizeof(CommandResponse));
}
}
void reconnect()
{
m_socket->connectToServer(m_serverName);
}
QLocalSocket *m_socket;
QString m_serverName;
quint64 m_requestCounter = 0;
QByteArray m_responseBuffer;
signals:
void commandResponse(quint64 requestId, quint32 resultCode, const QString &message);
};
五、QSystemSemaphore与QSystemLock------进程间同步原语
5.1 行情更新通知机制
共享内存只能解决数据传递问题,但消费端如何知道有新数据?轮询会浪费CPU,我们需要通知机制:
cpp
// 发布端:写入数据后通知
class NotifiedPublisher : public MarketDataPublisher
{
public:
NotifiedPublisher(QObject *parent = nullptr)
: MarketDataPublisher(parent)
, m_semaphore("TradingSystem_MarketData_Sem", 0) // 初始值0
{
}
void publishTick(const QString &symbol, const SymbolTick &tick) override
{
MarketDataPublisher::publishTick(symbol, tick);
// 通知所有等待的消费者
// QSystemSemaphore::release() 在Windows上是ReleaseSemaphore
m_semaphore.release(1); // 释放一个信号量
}
private:
QSystemSemaphore m_semaphore;
};
// 消费端:等待通知后读取
class NotifiedConsumer : public MarketDataConsumer
{
public:
NotifiedConsumer(QObject *parent = nullptr)
: MarketDataConsumer(parent)
, m_semaphore("TradingSystem_MarketData_Sem", 0)
{
}
void startListening()
{
// 在专用线程中等待信号量
auto thread = QThread::create([this]() {
while (!QThread::currentThread()->isInterruptionRequested()) {
// acquire在Windows上是WaitForSingleObject
// 超时100ms,允许检查中断请求
if (m_semaphore.acquire()) {
// 有新数据,读取并发射信号
SymbolTick tick;
// 读取所有待处理的tick
while (readLatestTick(m_focusSymbol, tick)) {
emit tickReceived(tick);
}
}
}
});
thread->start();
}
private:
QSystemSemaphore m_semaphore;
QString m_focusSymbol;
signals:
void tickReceived(const SymbolTick &tick);
};
5.2 QSystemLock vs QSharedMemory::lock()
cpp
// QSharedMemory::lock() 源码 (qtbase/src/corelib/kernel/qsharedmemory.cpp)
bool QSharedMemory::lock()
{
Q_D(QSharedMemory);
if (!d->lock()) {
d->setError(QSharedMemory::LockError);
return false;
}
return true;
}
// 内部实现 (qsharedmemory_p.h)
bool QSharedMemoryPrivate::lock()
{
if (!m_systemLock) {
m_systemLock = new QSystemLock(m_key);
}
return m_systemLock->lock();
}
性能对比:
| 机制 | Windows API | 平均延迟 | 适用场景 |
|---|---|---|---|
| QSharedMemory::lock() | Mutex | 1-5μs | 通用共享内存保护 |
| QSystemLock | Mutex | 1-5μs | 同上(QSharedMemory内部使用) |
| QSystemSemaphore | Semaphore | 0.5-2μs | 生产者-消费者通知 |
| SeqLock(自实现) | 原子操作 | <0.1μs | 读多写少的行情广播 |
交易系统推荐:行情广播用自实现的SeqLock(无锁),指令通道用QLocalSocket(自带有序保证),配置同步用QSharedMemory::lock()(简单可靠)。
六、完整架构:毫秒级交易IPC系统
┌──────────────────┐ QSharedMemory ┌──────────────────┐
│ 行情接收进程 │─────(零拷贝广播)───────→│ 策略引擎进程 │
│ MarketDataFeed │ SeqLock无锁 │ StrategyEngine │
└──────────────────┘ └────────┬─────────┘
│
QLocalSocket
(指令通道)
│
▼
┌──────────────────┐
│ 风控进程 │
│ RiskController │
└────────┬─────────┘
│
QLocalSocket
(审核后的订单)
│
▼
┌──────────────────┐
│ 交易网关进程 │
│ TradingGateway │
└──────────────────┘
6.1 进程间心跳监控
cpp
class ProcessWatchdog : public QObject
{
Q_OBJECT
public:
void registerProcess(const QString &name, QLocalSocket *socket)
{
m_processes[name] = {socket, QDateTime::currentMSecsSinceEpoch(), 0};
// 每5秒检查一次心跳
auto timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this, name]() {
auto &info = m_processes[name];
qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - info.lastHeartbeat;
if (elapsed > 10000) { // 10秒无心跳
qCritical() << name << "heartbeat timeout!" << elapsed << "ms";
emit processTimeout(name);
}
});
timer->start(5000);
}
void updateHeartbeat(const QString &name)
{
if (m_processes.contains(name)) {
m_processes[name].lastHeartbeat = QDateTime::currentMSecsSinceEpoch();
}
}
private:
struct ProcessInfo {
QLocalSocket *socket;
qint64 lastHeartbeat;
quint32 missedCount;
};
QHash<QString, ProcessInfo> m_processes;
signals:
void processTimeout(const QString &processName);
};
七、性能优化:极致低延迟技巧
7.1 QLocalSocket的批量写入
cpp
// ❌ 每个指令单独write + flush
for (const auto &order : orders) {
client->placeOrder(order.symbol, order.direction, order.price, order.quantity, order.accountId);
client->flush(); // 每次flush都触发系统调用
}
// ✅ 批量写入后一次性flush
for (const auto &order : orders) {
client->placeOrder(order.symbol, order.direction, order.price, order.quantity, order.accountId);
// 不flush,数据暂存在内核缓冲区
}
client->flush(); // 一次性发送
7.2 QSharedMemory的Huge Pages优化
cpp
// Windows: 使用大页内存减少TLB miss
// 需要SeLockedMemoryPrivilege权限
HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr,
SEC_COMMIT | SEC_LARGE_PAGES, 0, SHM_SIZE, L"TradingSystem_MarketData");
// Linux: 使用hugetlbfs
// mount -t hugetlbfs none /dev/hugepages
// int fd = open("/dev/hugepages/trading_shm", O_CREAT | O_RDWR, 0666);
7.3 CPU亲和性绑定
cpp
// 将关键进程绑定到专用CPU核心,避免上下文切换
#ifdef Q_OS_LINUX
#include <sched.h>
void setCpuAffinity(int cpuCore)
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpuCore, &cpuset);
sched_setaffinity(0, sizeof(cpuset), &cpuset);
}
#endif
#ifdef Q_OS_WINDOWS
void setCpuAffinity(int cpuCore)
{
SetThreadAffinityMask(GetCurrentThread(), 1ULL << cpuCore);
}
#endif
八、进程崩溃恢复策略
cpp
// 监控进程:检测子进程崩溃并自动重启
class ProcessSupervisor : public QObject
{
Q_OBJECT
public:
void startProcess(const QString &name, const QString &program,
const QStringList &args)
{
auto process = new QProcess(this);
process->setProgram(program);
process->setArguments(args);
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this, name, process](int exitCode, QProcess::ExitStatus status) {
if (status == QProcess::CrashExit) {
qCritical() << name << "crashed with exit code" << exitCode;
// 延迟3秒后重启
QTimer::singleShot(3000, this, [this, name]() {
restartProcess(name);
});
}
});
m_processes[name] = process;
process->start();
}
private:
void restartProcess(const QString &name)
{
if (m_processes.contains(name)) {
m_processes[name]->start();
qInfo() << name << "restarted";
}
}
QHash<QString, QProcess*> m_processes;
};
九、常见陷阱与排错经验
9.1 QSharedMemory的key冲突
cpp
// ❌ 不同系统实例使用相同key
m_sharedMemory.setKey("MarketData"); // 如果同时跑多个交易系统实例,冲突!
// ✅ 加入实例标识
QString key = QString("MarketData_%1").arg(instanceId);
m_sharedMemory.setKey(key);
9.2 QLocalSocket的粘包处理
cpp
// QLocalSocket保证有序但不保证消息边界
// 必须在应用层实现消息分帧(见上文handleCommand的流式读取)
// 切记:一次write不等于对端一次readyRead!
9.3 进程异常退出后的共享内存残留
cpp
// 进程崩溃后QSharedMemory不会自动detach
// 新进程attach前应检查数据完整性(通过header中的magic和checksum)
// 如果检测到残留的脏数据:
if (header->magic == 0x4D4B5444 && header->checksum != computeCRC32(...)) {
qWarning() << "Stale shared memory detected, reinitializing";
memset(m_sharedMemory.data(), 0, SHM_SIZE);
initHeader();
}
十、总结:交易系统IPC选型速查表
| 通信需求 | Qt机制 | 延迟量级 | 关键优化点 |
|---|---|---|---|
| 行情广播(1→N) | QSharedMemory + SeqLock | <1μs | 无锁、紧凑布局、Huge Pages |
| 交易指令(1→1) | QLocalSocket | 5-20μs | 批量写入、自定义协议分帧 |
| 进程通知 | QSystemSemaphore | <1μs | 避免轮询,信号量驱动 |
| 状态同步 | QSharedMemory + QSystemLock | 1-5μs | 短临界区、减少lock持续时间 |
| 进程管理 | QProcess + QLocalSocket心跳 | - | 崩溃检测、自动重启 |
| 配置/控制 | QDBus | 50-200μs | 仅用于非延迟敏感的管理操作 |
核心原则:行情走共享内存无锁读取,指令走本地Socket保证可靠,心跳走定时器+Socket,管理走D-Bus。进程隔离是交易系统可靠性的基石,而正确的IPC选型和优化是进程隔离的前提。
《注:若有发现问题欢迎大家提出来纠正》
以上仅为技术分享参考,不构成投资建议