Qt跨进程通信在交易系统中的应用:让策略引擎与风控模块在毫秒级握手

------从共享内存行情总线到本地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;
    }
};

关键设计决策

  1. 无锁读取 :利用序列号奇偶性实现seqlock模式,避免了QSharedMemory::lock()的系统调用开销
  2. 只读映射 :消费端使用QSharedMemory::ReadOnly,操作系统可优化为COW(Copy-on-Write)
  3. 紧凑布局#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选型和优化是进程隔离的前提。


《注:若有发现问题欢迎大家提出来纠正》

以上仅为技术分享参考,不构成投资建议

相关推荐
zhangrelay2 小时前
三分钟云课实践速通--大学物理--python 版
linux·开发语言·python·学习·ubuntu·lubuntu
MegaDataFlowers2 小时前
调用Service层操作数据
java·开发语言
asdzx672 小时前
使用 Python 读取 PDF: 提取文本和图片
开发语言·python·pdf
沐知全栈开发2 小时前
jQuery Mobile 表单选择
开发语言
MoonBit月兔2 小时前
MoonBit 大型软件合成挑战赛决赛暨 Meetup 0.9 版本专场回顾
大数据·开发语言·人工智能·moonbit
宣宣猪的小花园.3 小时前
C语言重难点全解析:指针到内存四区
c语言·开发语言
南宫萧幕3 小时前
HEV 智能能量管理实战:从 MPC/PPO 理论解析到 Python-Simulink 联合仿真闭环全流程
开发语言·python·算法·matlab·控制
码农的神经元3 小时前
Python 实现县域变电站智能巡检与抢修调度:地图、路径规划与恢复策略
开发语言·python
我命由我123453 小时前
Java 开发 - CountDownLatch 不需要手动关闭
android·java·开发语言·jvm·kotlin·android studio·android-studio