【C++】Windows下共享内存加信号量实现进程间同步通信

目录

一,函数清单

1.CreateFileMapping 方法

2.OpenFileMapping 方法

3.MapViewOfFile 方法

4.UnmapViewOfFile 方法

5.CreateSemaphore 方法

6. OpenSemaphore 方法

7.WaitForSingleObject 方法

8.ReleaseSemaphore 方法

9.CloseHandle 方法

10.GetLastError 方法

二,单共享内存单信号量-进程间单向通信

共享内存管理文件实现 

A进程调用接口实现写操作

B进程调用接口实现读操作

三,单共享内存双信号量-进程间单向通信

共享内存管理文件实现

A进程调用接口实现写操作

B进程调用接口实现读操作

四,双共享内存双信号量-进程间双向通信


一,函数清单

1.CreateFileMapping 方法

HANDLE CreateFileMapping(
  [in]           HANDLE                hFile,
  [in, optional] LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  [in]           DWORD                 flProtect,
  [in]           DWORD                 dwMaximumSizeHigh,
  [in]           DWORD                 dwMaximumSizeLow,
  [in, optional] LPCWSTR               lpName
);

功能

  • 创建或打开命名或未命名的共享内存对象。

参数

  • hFile:创建共享内存则设置为 INVALID_HANDLE_VALUE,并且还必须为 dwMaximumSizeHigh 和 dwMaximumSizeLow 参数中的文件映射对象指定大小
  • lpFileMappingAttributes:共享内存对象的安全属性,通常设置为 NULL,则子进程无法继承句柄,并且共享内存对象获取默认的安全描述符。
  • flProtect:指定共享内存对象的访问方式。通常设置为 PAGE_EXECUTE_READWRITE,表示可进行只读、写入复制、读/写或执行访问。
  • dwMaximumSizeHigh:高位字节大小,通常设置为0。
  • dwMaximumSizeLow:地位字节大小,设置共享内存段的大小。
  • lpName:共享内存对象的标识名称。

返回值

  • 如果函数成功,则返回值是新创建的共享内存对象的句柄。
  • 如果该对象在函数调用之前存在,则函数将返回现有对象的句柄 (其当前大小,而不是指定的大小) , GetLastError 返回 ERROR_ALREADY_EXISTS
  • 如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。

2.OpenFileMapping 方法

HANDLE OpenFileMapping(
  [in] DWORD   dwDesiredAccess,
  [in] BOOL    bInheritHandle,
  [in] LPCWSTR lpName
);

功能

  • 打开命名共享内存对象。

参数

  • dwDesiredAccess:对共享内存对象的访问方式。用于共享内存段的保护,通常设置为 FILE_MAP_ALL_ACCESS,可执行所有访问权限。
  • bInheritHandle:如果此参数为 TRUE, 则 CreateProcess 函数创建的进程可以继承句柄;否则,无法继承句柄。
  • lpName:要打开的共享内存对象的标识名称。

返回值

  • 如果函数成功,则返回值是指定共享内存对象的打开句柄。
  • 如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。

3.MapViewOfFile 方法

LPVOID MapViewOfFile(
  [in] HANDLE hFileMappingObject,
  [in] DWORD  dwDesiredAccess,
  [in] DWORD  dwFileOffsetHigh,
  [in] DWORD  dwFileOffsetLow,
  [in] SIZE_T dwNumberOfBytesToMap
);

功能

  • 将共享内存映射到调用进程的地址空间。

参数

  •  hFileMappingObject:共享内存对象的句柄。 CreateFileMapping 和 OpenFileMapping 函数返回此句柄。
  • dwDesiredAccess:对共享内存对象的访问方式,通常指定为FILE_MAP_ALL_ACCESS,表示可读可写。
  • dwFileOffsetHigh:高位字节大小,通常设置为0。
  • dwFileOffsetLow:地位字节大小,通常设置为0。
  • dwNumberOfBytesToMap:要映射到共享内存段的字节数。 

返回值

  • 如果函数成功,则返回值为共享内存段的起始地址。
  • 如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。

4.UnmapViewOfFile 方法

BOOL UnmapViewOfFile(
  [in] LPCVOID lpBaseAddress
);

功能

  • 从调用进程的地址空间取消映射共享内存段。

参数

  • lpBaseAddress:指向要取消映射的共享内存指针。此值必须与上一次调用MapViewOfFile 函数返回的值相同。

返回值

  • 如果该函数成功,则返回值为非零值。
  • 如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

5.CreateSemaphore 方法

HANDLE CreateSemaphore(
  [in, optional] LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  [in]           LONG                  lInitialCount,
  [in]           LONG                  lMaximumCount,
  [in, optional] LPCWSTR               lpName
);

功能

  • 创建或打开命名或未命名的信号量对象。

参数

  • lpSemaphoreAttributes:信号量安全特征。通常指定为 NULL,但子进程无法继承句柄。
  • lInitialCount:信号量对象的初始可使用资源数目。 此值必须>=0,并且<=lMaximumCount。如果设置为0,信号量为未触发状态。
  • lMaximumCount:信号量对象的最大资源计数。 此值必须大于零。
  • lpName:信号量对象的名称。各进程实现同步会用到该名称。

返回值

  • 如果函数成功,则返回值是信号量对象的句柄。
  • 如果在函数调用之前存在相同命名的信号量对象,则函数将返回现有对象的句柄,GetLastError 返回 ERROR_ALREADY_EXISTS
  • 如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。

6. OpenSemaphore 方法

HANDLE OpenSemaphore(
  [in] DWORD   dwDesiredAccess,
  [in] BOOL    bInheritHandle,
  [in] LPCWSTR lpName
);

功能

  • 打开现有的命名信号量对象。

参数

  • dwDesiredAccess:对信号量对象的访问权限,通常指定为 SEMAPHORE_ALL_ACCESS
  • bInheritHandle:如果此值为 TRUE,则此过程创建的进程将继承句柄。 否则,进程不会继承此句柄。
  • lpName:打开已有的信号量名称。

返回值

  • 如果函数成功,则返回值是信号灯对象的句柄。
  • 如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。

7.WaitForSingleObject 方法

DWORD WaitForSingleObject(
  [in] HANDLE hHandle,
  [in] DWORD  dwMilliseconds
);

功能

  • 阻塞等待指定对象处于信号状态或超时间隔已过。

参数

  • hHandle:创建的信号量对象的句柄。
  • dwMilliseconds:超时间隔(以毫秒为单位)。通常指定为 INFINITE,表示仅当发出对象信号时,该函数才会返回。

返回值

  • 如果函数成功,则返回值指示导致函数返回的事件。可以是下列值之一
返回代码/值描述

WAIT_ABANDONED

指定的对象是一个互斥对象,该对象不是由拥有互斥对象的线程在拥有线程终止之前释放的。 互斥对象所有权授予调用线程,互斥状态设置为非对齐状态。如果互斥体保护持久状态信息,则应检查该信息是否一致性。

WAIT_OBJECT_0

指定对象的状态已发出信号。

WAIT_TIMEOUT

超时间隔已过,对象的状态未对齐。

WAIT_FAILED

函数失败。 要获得更多的错误信息,请调用 GetLastError。

8.ReleaseSemaphore 方法

BOOL ReleaseSemaphore(
  [in]            HANDLE hSemaphore,
  [in]            LONG   lReleaseCount,
  [out, optional] LPLONG lpPreviousCount
);

功能

  • 按指定量增加指定的信号量对象的资源计数。

参数

  • hSemaphore:设置为 CreateSemaphore 或 OpenSemaphore 函数返回的信号量对象句柄。
  • lReleaseCount:信号量对象的当前资源计数将增加的量。该值必须>0且<=最大资源数量,通常设置为1。
  • lpPreviousCount:指向一个变量的指针,用于接收信号量的先前资源计数。如果不需要先前资源计数,此参数可以为 NULL 

返回值

  • 如果该函数成功,则返回值为非零值。
  • 如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

9.CloseHandle 方法

BOOL CloseHandle(
  [in] HANDLE hObject
);

功能

  • 关闭打开的对象句柄。

参数

  • hObject:打开对象的有效句柄。

返回值

  • 如果该函数成功,则返回值为非零值。
  • 如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

10.GetLastError 方法

_Post_equals_last_error_ DWORD GetLastError();

功能

  • 检索调用线程的最后错误代码值。 最后一个错误代码是按线程维护的。 多个线程不会覆盖彼此的最后错误代码。

返回值

  • 返回值是调用线程的最后错误代码。 

二,单共享内存单信号量-进程间单向通信

场景描述:实现进程间单向通信,只允许一块进程往该共享内存写入数据,另一个进程负责读数据,并使用信号量实现互斥操作,当有一个进程在进行写或读数据时,对该共享内存独占访问,其它进程必须阻塞等待该共享内存的信号量资源释放。

  • 开辟线程对共享内存进行管理。
  • 只有一个信号量,该信号量初始化只有一个可用资源。

共享内存管理文件实现 

头文件

#ifndef SHAREMEMORYMANAGER_H
#define SHAREMEMORYMANAGER_H

#include <QObject>
#include <QThread>
#include <Windows.h>

#include <QDebug>
#define cout qDebug() <<"["<<__FUNCTION__<<"]["<<__LINE__<<"]:"

class ShareMemoryManager : public QObject
{
    Q_OBJECT
public:
    explicit ShareMemoryManager(int shareMemorySize,const QString &shareMemoryKey
                                ,const QString &semaphoreKey,QObject *parent = nullptr);
    ~ShareMemoryManager();

    void startManager();    //开辟线程管理
    void quitManager();     //退出线程管理

private:
    void initShareMemory(const int &shareMemorySize,const QString &shareMemoryKey);
    void initSemaphore(const QString &semaphoreKey);

signals:
    void sig_readMessageFromShareMemory(QString message);

public slots:
    void slot_writeMessageToShareMemory(const QString &message);    //A进程进行写操作调用该接口
    void slot_readMessageFromShareMemory();                         //B进程进行读操作调用该接口

private:
    QThread *m_pThread;

    HANDLE m_pShareMemoryHandle;    //共享内存对象句柄
    LPVOID m_pShareMemoryBuffer;    //共享内存映射到本地

    HANDLE m_pSemaphoreForShareMem;     //共享内存信号量

    int m_shareMemorySize;      //共享内存段大小

    char *m_pReadBuf{nullptr};       //读缓冲区,用于读取共享内存的数据
};

#endif // SHAREMEMORYMANAGER_H

源文件

#include "ShareMemoryManager.h"

ShareMemoryManager::ShareMemoryManager(int shareMemorySize, const QString &shareMemoryKey
                                       , const QString &semaphoreKey, QObject *parent):QObject(parent)
{
    m_pThread = nullptr;
    startManager();
    initShareMemory(shareMemorySize,shareMemoryKey);
    initSemaphore(semaphoreKey);
}

ShareMemoryManager::~ShareMemoryManager()
{
    //释放信号量资源
    if (m_pSemaphoreForShareMem != nullptr) {
        ReleaseSemaphore(m_pSemaphoreForShareMem,1,NULL);
        CloseHandle(m_pSemaphoreForShareMem);
        m_pSemaphoreForShareMem = nullptr;
    }

    //释放共享内存资源:先解除文件映射,再关闭共享内存句柄
    if (m_pShareMemoryBuffer != nullptr) {
        UnmapViewOfFile(m_pShareMemoryBuffer);
        m_pShareMemoryBuffer = nullptr;
    }
    if (m_pShareMemoryHandle != nullptr) {
        CloseHandle(m_pShareMemoryHandle);
        m_pShareMemoryHandle = nullptr;
    }

    //释放读缓冲区指针
    if(m_pReadBuf != nullptr){
        free(m_pReadBuf);
        m_pReadBuf = nullptr;
    }

    quitManager();
}

void ShareMemoryManager::startManager()
{
    if(m_pThread == nullptr){
        m_pThread = new QThread;
        this->moveToThread(m_pThread);
        m_pThread->start();
    }
}

void ShareMemoryManager::quitManager()
{
    if(m_pThread != nullptr){
        m_pThread->requestInterruption();
        m_pThread->quit();
        m_pThread->wait();
        m_pThread->deleteLater();
        m_pThread = nullptr;
    }
}

void ShareMemoryManager::initShareMemory(const int &shareMemorySize, const QString &shareMemoryKey)
{
    m_shareMemorySize = shareMemorySize;
    m_pShareMemoryHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_EXECUTE_READWRITE,0,shareMemorySize,shareMemoryKey.toStdWString().c_str());
    if (m_pShareMemoryHandle == NULL)
    {
        cout << "Could not create file mapping B object:" << GetLastError();
        return;
    }

    m_pShareMemoryBuffer = MapViewOfFile(m_pShareMemoryHandle, FILE_MAP_ALL_ACCESS, 0, 0, shareMemorySize);
    if (m_pShareMemoryBuffer == NULL)
    {
        cout << "Could not map view of file B:" << GetLastError();
        CloseHandle(m_pShareMemoryHandle);
        return;
    }
}

void ShareMemoryManager::initSemaphore(const QString &semaphoreKey)
{
    m_pSemaphoreForShareMem = CreateSemaphore(NULL, 1, 1, semaphoreKey.toStdWString().c_str());
    if (m_pSemaphoreForShareMem == NULL)
    {
        cout << "CreateSemaphore writeSemName error: %d" << GetLastError();
        return;
    }
}

void ShareMemoryManager::slot_writeMessageToShareMemory(const QString &message)
{
    DWORD semRet = WaitForSingleObject(m_pSemaphoreForShareMem,INFINITE);
    if(semRet == WAIT_OBJECT_0){
        strcpy_s((char*)m_pShareMemoryBuffer,m_shareMemorySize,message.toStdString().c_str());
        ReleaseSemaphore(m_pSemaphoreForShareMem,1,NULL);
    }
}

void ShareMemoryManager::slot_readMessageFromShareMemory()
{
    if(m_pReadBuf == nullptr){
        m_pReadBuf = (char*)malloc(m_shareMemorySize*sizeof(char));
    }
    memset(m_pReadBuf,0,m_shareMemorySize);

    DWORD semRet = WaitForSingleObject(m_pSemaphoreForShareMem,INFINITE);
    if (semRet == WAIT_OBJECT_0) {
        strcpy_s(m_pReadBuf,m_shareMemorySize,(char*)m_pShareMemoryBuffer);
        cout << QString(m_pReadBuf);    //打印读出来的数据
        emit sig_readMessageFromShareMemory(QString(m_pReadBuf));
        ReleaseSemaphore(m_pSemaphoreForShareMem,1,NULL);
    }
}

A进程调用接口实现写操作

头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "ShareMemoryManager.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void sig_sendMessage(const QString &data);

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;

    ShareMemoryManager *m_pSMM;
};

#endif // MAINWINDOW_H

源文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_pSMM = new ShareMemoryManager(250,"shareMemory","Semaphore");
    connect(this,SIGNAL(sig_sendMessage(QString)),m_pSMM,SLOT(slot_writeMessageToShareMemory(QString)),Qt::ConnectionType::QueuedConnection);
}

MainWindow::~MainWindow()
{
    if(m_pSMM != nullptr){
        m_pSMM->deleteLater();
        m_pSMM = nullptr;
    }
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QString data = ui->lineEdit->text();
    emit sig_sendMessage(data);
}

ui布局

B进程调用接口实现读操作

头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "ShareMemoryManager.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void slot_readMessageToShow(QString message);   //显示从共享内存读取的数据

private:
    Ui::MainWindow *ui;

    ShareMemoryManager *m_pSMM;
};

#endif // MAINWINDOW_H

源文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_pSMM = new ShareMemoryManager(250,"shareMemory","Semaphore");
    connect(ui->pushButton,SIGNAL(clicked()),m_pSMM,SLOT(slot_readMessageFromShareMemory()),Qt::ConnectionType::QueuedConnection);
    connect(m_pSMM,SIGNAL(sig_readMessageFromShareMemory(QString)),this,SLOT(slot_readMessageToShow(QString)),Qt::ConnectionType::QueuedConnection);
}

MainWindow::~MainWindow()
{
    if(m_pSMM != nullptr){
        m_pSMM->deleteLater();
        m_pSMM = nullptr;
    }
    delete ui;
}

void MainWindow::slot_readMessageToShow(QString message)
{
    ui->plainTextEdit->appendPlainText(message);
}

ui布局

 

三,单共享内存双信号量-进程间单向通信

场景描述:实现进程间单向通信,只允许一块进程往该共享内存写入数据,另一个进程负责读数据,并使用信号量实现互斥操作,A进程进行写操作完后会释放一个读信号量资源通知B进程进行读操作,A进程并阻塞在写信号量上。B进程进行读操作会消耗一个读信号量资源并释放一个写信号量资源,通知A进程进行下一轮写操作。

  • 开辟线程对共享内存进行管理。
  • 定义读写两个信号量,信号量初始化可用资源数都为0(所以在先调用WaitForSingleObject后调用ReleaseSemaphore会阻塞进程)。

共享内存管理文件实现

头文件

#ifndef SHAREMEMORYMANAGER_H
#define SHAREMEMORYMANAGER_H

#include <QObject>
#include <QThread>
#include <Windows.h>

#include <QDebug>
#define cout qDebug() <<"["<<__FUNCTION__<<"]["<<__LINE__<<"]:"

class ShareMemoryManager : public QObject
{
    Q_OBJECT
public:
    explicit ShareMemoryManager(int shareMemorySize,const QString &shareMemoryKey
                                ,const QString &readSemaphoreKey,const QString &writeSemaphoreKey
                                ,QObject *parent = nullptr);
    ~ShareMemoryManager();

    void startManager();    //开辟线程管理
    void quitManager();     //退出线程管理

private:
    void initShareMemory(const int &shareMemorySize,const QString &shareMemoryKey);
    void initSemaphore(const QString &readSemaphoreKey,const QString &writeSemaphoreKey);

signals:
    void sig_readMessageFromShareMemory(QString message);

public slots:
    void slot_writeMessageToShareMemory(const QString &message);    //A进程进行写操作调用该接口
    void slot_readMessageFromShareMemory();                         //B进程进行读操作调用该接口

private:
    QThread *m_pThread;

    HANDLE m_pShareMemoryHandle;    //共享内存对象句柄
    LPVOID m_pShareMemoryBuffer;    //共享内存映射到本地

    HANDLE m_pReadSemaphoreForShareMem;     //共享内存读信号量
    HANDLE m_pWriteSemaphoreForShareMem;    //共享内存写信号量

    int m_shareMemorySize;      //共享内存段大小

    char *m_pReadBuf{nullptr};  //读缓冲区,用于读取共享内存的数据
};

#endif // SHAREMEMORYMANAGER_H

源文件

#include "ShareMemoryManager.h"

ShareMemoryManager::ShareMemoryManager(int shareMemorySize, const QString &shareMemoryKey
                                       , const QString &readSemaphoreKey, const QString &writeSemaphoreKey, QObject *parent):QObject(parent)
{
    m_pThread = nullptr;
    startManager();
    initShareMemory(shareMemorySize,shareMemoryKey);
    initSemaphore(readSemaphoreKey,writeSemaphoreKey);
}

ShareMemoryManager::~ShareMemoryManager()
{
    //释放信号量资源
    if (m_pReadSemaphoreForShareMem != nullptr) {
        ReleaseSemaphore(m_pReadSemaphoreForShareMem,1,NULL);
        CloseHandle(m_pReadSemaphoreForShareMem);
        m_pReadSemaphoreForShareMem = nullptr;
    }
    if (m_pWriteSemaphoreForShareMem != nullptr) {
        ReleaseSemaphore(m_pWriteSemaphoreForShareMem,1,NULL);
        CloseHandle(m_pWriteSemaphoreForShareMem);
        m_pWriteSemaphoreForShareMem = nullptr;
    }

    //释放共享内存资源:先解除文件映射,再关闭共享内存句柄
    if (m_pShareMemoryBuffer != nullptr) {
        UnmapViewOfFile(m_pShareMemoryBuffer);
        m_pShareMemoryBuffer = nullptr;
    }
    if (m_pShareMemoryHandle != nullptr) {
        CloseHandle(m_pShareMemoryHandle);
        m_pShareMemoryHandle = nullptr;
    }

    //释放读缓冲区指针
    if(m_pReadBuf != nullptr){
        free(m_pReadBuf);
        m_pReadBuf = nullptr;
    }

    quitManager();
}

void ShareMemoryManager::startManager()
{
    if(m_pThread == nullptr){
        m_pThread = new QThread;
        this->moveToThread(m_pThread);
        m_pThread->start();
    }
}

void ShareMemoryManager::quitManager()
{
    if(m_pThread != nullptr){
        m_pThread->requestInterruption();
        m_pThread->quit();
        m_pThread->wait();
        m_pThread->deleteLater();
        m_pThread = nullptr;
    }
}

void ShareMemoryManager::initShareMemory(const int &shareMemorySize, const QString &shareMemoryKey)
{
    m_shareMemorySize = shareMemorySize;
    m_pShareMemoryHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_EXECUTE_READWRITE,0,shareMemorySize,shareMemoryKey.toStdWString().c_str());
    if (m_pShareMemoryHandle == NULL)
    {
        cout << "Could not create file mapping B object:" << GetLastError();
        return;
    }

    m_pShareMemoryBuffer = MapViewOfFile(m_pShareMemoryHandle, FILE_MAP_ALL_ACCESS, 0, 0, shareMemorySize);
    if (m_pShareMemoryBuffer == NULL)
    {
        cout << "Could not map view of file B:" << GetLastError();
        CloseHandle(m_pShareMemoryHandle);
        return;
    }
}

void ShareMemoryManager::initSemaphore(const QString &readSemaphoreKey, const QString &writeSemaphoreKey)
{
    m_pReadSemaphoreForShareMem = CreateSemaphore(NULL, 0, 1, readSemaphoreKey.toStdWString().c_str());
    if (m_pReadSemaphoreForShareMem == NULL)
    {
        cout << "CreateSemaphore readSemName error: %d" << GetLastError();
        return;
    }

    m_pWriteSemaphoreForShareMem = CreateSemaphore(NULL, 0, 1, writeSemaphoreKey.toStdWString().c_str());
    if (m_pWriteSemaphoreForShareMem == NULL)
    {
        cout << "CreateSemaphore writeSemName error: %d" << GetLastError();
        return;
    }
}

void ShareMemoryManager::slot_writeMessageToShareMemory(const QString &message)
{
    strcpy_s((char*)m_pShareMemoryBuffer,m_shareMemorySize,message.toStdString().c_str());
    
    //释放共享内存的可读信号,通知其它进程可以读取内容
    ReleaseSemaphore(m_pReadSemaphoreForShareMem,1,NULL);
    //阻塞等待其它进程释放共享内存的可读信号,然后写进程进行下一轮写操作
    WaitForSingleObject(m_pWriteSemaphoreForShareMem,INFINITE);
}

void ShareMemoryManager::slot_readMessageFromShareMemory()
{
    if(m_pReadBuf == nullptr){
        m_pReadBuf = (char*)malloc(m_shareMemorySize*sizeof(char));
    }
    memset(m_pReadBuf,0,m_shareMemorySize);

    //阻塞等待写进程释放共享内存的可读信号
    DWORD semRet = WaitForSingleObject(m_pReadSemaphoreForShareMem,INFINITE);
    if (semRet == WAIT_OBJECT_0) {
        strcpy_s(m_pReadBuf,m_shareMemorySize,(char*)m_pShareMemoryBuffer);
        sig_readMessageFromShareMemory(QString(m_pReadBuf));
        //释放共享内存的写信号,通知写进程进行写操作
        ReleaseSemaphore(m_pWriteSemaphoreForShareMem,1,NULL);
    }
}

A进程调用接口实现写操作

头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "ShareMemoryManager.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void sig_sendMessage(const QString &data);

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;

    ShareMemoryManager *m_pSMM;
};

#endif // MAINWINDOW_H

源文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_pSMM = new ShareMemoryManager(250,"shareMemory","readSemaphore","writeSemaphore");
    connect(this,SIGNAL(sig_sendMessage(QString)),m_pSMM,SLOT(slot_writeMessageToShareMemory(QString)),Qt::ConnectionType::QueuedConnection);
}

MainWindow::~MainWindow()
{
    if(m_pSMM != nullptr){
        m_pSMM->deleteLater();
        m_pSMM = nullptr;
    }
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QString data = ui->lineEdit->text();
    emit sig_sendMessage(data);
}

UI文件同单共享内存单信号量

B进程调用接口实现读操作

头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "ShareMemoryManager.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void slot_readMessageToShow(QString message);   //显示从共享内存读取的数据

private:
    Ui::MainWindow *ui;

    ShareMemoryManager *m_pSMM;
};

#endif // MAINWINDOW_H

源文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

m_pSMM = new ShareMemoryManager(250,"shareMemory","readSemaphore","writeSemaphore");
    connect(ui->pushButton,SIGNAL(clicked()),m_pSMM,SLOT(slot_readMessageFromShareMemory()));
    connect(m_pSMM,SIGNAL(sig_readMessageFromShareMemory(QString)),this,SLOT(slot_readMessageToShow(QString)));
}

MainWindow::~MainWindow()
{
    if(m_pSMM != nullptr){
        m_pSMM->deleteLater();
        m_pSMM = nullptr;
    }
    delete ui;
}

void MainWindow::slot_readMessageToShow(QString message)
{
    ui->plainTextEdit->appendPlainText(message);
}

UI文件同单共享内存单信号量

四,双共享内存双信号量-进程间双向通信

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
教程非常不错,价值280元,绝对是干货 Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元(MTU)/路径MTU 以太网帧格式 IC MP ARP RARP 03TCPIP基础(三) IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06socket编程(一) 什么是socket IPv4套接口地址结构 网络字节序 字节序转换函数 地址转换函数 套接字类型 07socket编程(二) TCP客户/服务器模型 回射客户/服务器 socket、bind、listen、accept、connect 08socket编程(三) SO_REUSEADDR 处理多客户连接(process-per-conection) 点对点聊天程序实现 09socket编程(四) 流协议与粘包 粘包产生的原因 粘包处理方案 readn writen 回射客户/服务器 10socket编程(五) read、write与recv、send readline实现 用readline实现回射客户/服务器 getsockname、getpeername gethostname、gethostbyname、gethostbyaddr 11socket编程(六) TCP回射客户/服务器 TCP是个流协议 僵进程与SIGCHLD信号 12socket编程(七) TCP 11种状态 连接建立三次握手、连接终止四次握手 TIME_WAIT与SO_REUSEADDR SIGPIPE 13socket编程(八) 五种I/O模型 select 用select改进回射客户端程序 14socket编程(九) select 读、写、异常事件发生条件 用select改进回射服务器程序。 15socket编程(十) 用select改进第八章点对点聊天程序 16socket编程(十一) 套接字I/O超时设置方法 用select实现超时 read_timeout函数封装 write_timeout函数封装 accept_timeout函数封装 connect_timeout函数封装 17socket编程(十二) select限制 poll 18socket编程(十三) epoll使用 epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址结构 UNIX域字节流回射客户/服务 UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程通信篇 23进程通信介绍(一) 进程同步进程互斥 进程通信目的 进程通信发展 进程通信分类 进程共享信息的三种方式 IPC对象的持续性 24进程通信介绍(二) 死锁 信号量 PV原语 用PV原语解决司机与售票员问题 用PV原语解决民航售票问题 用PV原语解决汽车租赁问题 25System V消息队列(一) 消息队列 IPC对象数据结构 消息队列结构 消息队列在内核中的表示 消息队列函数 26System V消息队列(二) msgsnd函数 msgrcv函数 27System V消息队列(三) 消息队列实现回射客户/服务器 28共享内存介绍 共享内存 共享内存示意图 管道、消息队列与共享内存传递数据对比 mmap函数 munmap函数 msync函数 29System V共享内存 共享内存数据结构 共享内存函数 共享内存示例 30System V信号量(一) 信号量 信号量
Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元(MTU)/路径MTU 以太网帧格式 ICMP ARP RARP 03TCPIP基础(三) IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06socket编程(一) 什么是socket IPv4套接口地址结构 网络字节序 字节序转换函数 地址转换函数 套接字类型 07socket编程(二) TCP客户/服务器模型 回射客户 /服务器 socket、bind、listen、accept、connect 08socket编程(三) SO_REUSEADDR 处理多客户连接(process-per-conection) 点对点聊天程序实现 09socket编程(四) 流协议与粘包 粘包产生的原因 粘包处理方案 readn writen 回射客户/服务器 10socket编程(五) read、write与recv、send readline实现 用readline实现回射客户/服务器 getsockname、getpeername gethostname、gethostbyname、gethostbyaddr 11socket编程(六) TCP回射客户/服务器 TCP是个流协议 僵进程与SIGCHLD信号 12socket编程(七) TCP 11种状态 连接建立三次握手、连接终止四次握手 TIME_WAIT与SO_REUSEADDR SIGPIPE 13socket编程(八) 五种I/O模型 select 用select改进回射客户端程序 14socket编程(九) select 读、写、异常事件发生条件 用select改进回射服务器程序。 15socket编程(十) 用select改进第八章点对点聊天程序 16socket编程(十一) 套接字I/O超时设置方法 用select实现超时 read_timeout函数封装 write_timeout函数封装 accept_timeout函数封装 connect_timeout函数封装 17socket编程(十二) select限制 poll 18socket编程(十三) epoll使用 epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址结构 UNIX域字节流回射客户/服务 UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程通信篇 23进程通信介绍(一) 进程同步进程互斥 进程通信目的 进程通信发展 进程通信分类 进程共享信息的三种方式 IPC对象的持续性 24进程通信介绍(二) 死锁 信号量 PV原语 用PV原语解决司机与售票员问题 用PV原语解决民航售票问题 用PV原语解决汽车租赁问题 25System V消息队列(一) 消息队列 IPC对象数据结构 消息队列结构 消息队列在内核中的表示 消息队列函数 26System V消息队列(二) msgsnd函数 msgrcv函数 27System V消息队列(三) 消息队列实现回射客户/服务器 28共享内存介绍 共享内存 共享内存示意图 管道、消息队列与共享内存传递数据对比 mmap函数 munmap函数 msync函数 29System V共享内存 共享内存数据结构 共享内存函数 共享内存示例 30System V信号量(一) 信
### 回答1: 共享内存是一种进程通信的方式。它允许多个进程访问相同的内存区域,从而实现数据的共享和传输。 在使用共享内存进行进程通信时,首先需要申请一块共享内存区域,并将其映射到各个进程的地址空中。这样,所有进程就可以通过读写该内存区域来进行数据的传递和共享。由于共享内存操作的是实际的内存地址,相比其他进程通信方式,如管道或消息队列,共享内存具有更高的传输效率。 共享内存通信的一个重要问题是同步与互斥。多个进程同时对共享内存进行读写操作时,需要通过互斥手段来避免竞态条件和数据不一致的问题。常用的同步机制包括信号量、互斥锁等。通过使用这些同步机制,进程可以获得对共享内存的独占访问,避免数据冲突。 共享内存通信在某些场景下非常有用,例如多个进程需要共享大量数据、频繁进行数据交换的场合。通过共享内存,可以避免数据复制和编码解码等操作,有效提高系统的性能。 然而,共享内存通信也存在一些问题。首先,由于多个进程可以直接访问该内存区域,因此必须确保进程的协调和同步。另外,共享内存具有共享性,一旦出现错误或者异常行为,会影响到所有依赖于该内存区域的进程。 综上所述,共享内存是一种高效的进程通信方式,可以提供快速的数据传输和共享功能。然而,在使用共享内存通信时,需要注意协调和同步措施,以确保数据的一致性和正确性。 ### 回答2: 共享内存是一种进程通信的方法,它允许多个进程同时访问同一块内存区域。在使用共享内存进行进程通信时,多个进程可以通过读写同一块内存来交换数据,从而实现进程的数据共享。 共享内存实现通常借助于操作系统提供的相关API,例如Linux系统提供了shmget、shmat、shmdt和shmctl等函数,用于创建和控制共享内存区域。 使用共享内存进行进程通信的优势在于可以实现高效的数据传输,因为数据在内存中的复制效率比较高。而且,由于多个进程可以同时访问同一块内存区域,这种方式也能够更好地支持并发操作。 然而,共享内存也存在一些潜在的问题。首先,由于多个进程可以同时访问共享内存,所以在使用时需要注意对共享资源的互斥保护,以避免数据的竞争和冲突。其次,共享内存在使用过程中需要确保数据的一致性和同步,否则可能会导致数据错误或者进程的死锁。最后,共享内存的使用需要仔细考虑安全性问题,以避免恶意进程的非法访问和篡改。 总之,共享内存是一种高效的进程通信方式,能够实现进程的数据共享。但在使用时需要注意互斥保护、数据一致性和安全性等问题,确保进程通信的稳定和可靠性。 ### 回答3: 共享内存是一种用于进程通信的机制。它可以使多个进程在同一时访问相同的内存区域,从而实现数据的共享和传递。 在使用共享内存进行进程通信时,首先需要创建一个共享内存段。多个进程可以通过系统调用(如shmget)来获取这个共享内存段的标识符,以便能够访问它。通过访问这个标识符,进程可以将共享内存映射到自己的地址空,从而可以对内存进行读写操作。 进程可以通过访问共享内存中的数据来实现通信。可以在共享内存中定义一些共享数据结构,进程可以通过读取和修改这些数据结构来进行通信。由于多个进程可以同时访问共享内存,因此需要对共享内存的访问进行同步和互斥操作,以避免数据竞争等问题。 共享内存的使用具有一定的优点和缺点。它的优点是速度快,因为进程可以直接访问内存,无需经过复制和传输等额外开销。同时,由于数据是直接在内存中共享,所以多个进程可以实现高效的数据交换和传递。然而,共享内存也有一些缺点,比如需要手动进行同步和互斥操作,否则可能会导致数据不一致等问题。此外,共享内存的使用需要进程具有一定的协作性,否则可能会导致竞争和死锁等问题。 总的来说,共享内存是一种高效的进程通信机制,可以在多个进程共享数据,从而实现数据的传递和交换。但是,需要注意同步和互斥操作,以及进程的协作性问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉伊卜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值