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选型和优化是进程隔离的前提。


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

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

相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner11 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00614 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术14 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript