概述
说明:本文所述方法基于windows操作系统实现多进程通信,实现进程间同步、传递图像数据。
知识点:C++、信号量、共享内存、多进程。
进程间通信
管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;Linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
代码实现
使用C++实现两个进程之间通信:
1:使用共享内存实现两个进程之间传递数据
2:使用信号量实现两个进程同步
共享内存传递int、vector、图像
思路:父进程将需要发送的数据拷贝到申请的共享内存中,子进程从共享内存中把数据拷贝到本地。
注意:函数memcpy(lpBase, &arr[0], 8);在执行时,参数定义如下:
1:目标地址
2:源起始地址
3:拷贝的字节数
因此在传递vector变量时我们需要传入的第二个变量是&arr[0],因为这才是动态数组的起始地址,而&arr是vector对象的地址,并不是数组的起始地址
拷贝的字节数,因为int占4个字节,因此我们把从&arr[0]开始的8个字节的内容复制到共享内存中。
在传递图像数据时,我这里采取的方法是,父进程对图像编码,存入共享内存,子进程读出数据后解码。
子进程在接收数据时,需要提前申请空间
vector arr(2);
std::vector jpg_buff(15676);
BUF_SIZE, 为文件大小,所传递的数据量不能大于BUF_SIZE。
父进程
// 定义共享数据
//联合Opencv传递图像数据
cv::Mat std;
cv::Mat hel;
std = cv::imread("hello.jpg");
cv::Mat dst_mat = std(cv::Rect(0, 0, 250, 250));
std::vector<uchar> jpg_buff;
bool ret = cv::imencode(".jpg", dst_mat, jpg_buff);
//传递整形数据
int a;
//传递数组
vector<int>arr;
arr.pushback(211);
arr.pushback(311);
///*hel=cv::imdecode(jpg_buff,0);
//cv::imwrite("000.jpg", hel);*/
// 创建共享文件句柄
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // 物理文件句柄
NULL, // 默认安全级别
PAGE_READWRITE, // 可读可写
0, // 高位文件大小
BUF_SIZE, // 定义文件大小
L"Sharememory" // 共享内存名称
);
// 映射缓存区视图 , 得到指向共享内存的指针
LPVOID lpBase = MapViewOfFile(
hMapFile, // 共享内存的句柄
FILE_MAP_ALL_ACCESS, // 可读写许可
0,
0,
BUF_SIZE//定义文件大小
);
// 将数据拷贝到共享内存
//传递整形数据
memcpy(lpBase, &a, 4);
//传递数组
memcpy(lpBase, &arr[0], 8);
//传递图片
memcpy(lpBase, &jpg_buff[0], 15676);
线程挂起等其他线程读取数据
Sleep(15000);
解除文件映射
UnmapViewOfFile(lpBase);
关闭内存映射文件对象句柄
CloseHandle(hMapFile);
子进程:
// 打开共享的文件对象
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, NULL, L"Sharememory");
//申请变量准备接收数据
vector<int> arr(2);
int a = 0;
std::vector<uchar> jpg_buff(15676);
if (hMapFile) {
LPVOID lpBase= MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
cv::Mat hel;
//以下三种数据和父进程发送的数据要一一对应
memcpy(&jpg_buff[0], lpBase, 15676);
memcpy(&a, lpBase, 4);
memcpy(&arr[0], lpBase, 4);
//图像解码
hel = cv::imdecode(jpg_buff, 0);
cv::imwrite("009.jpg", hel);
// 解除文件映射
UnmapViewOfFile(lpBase);
// 关闭内存映射文件对象句柄
CloseHandle(hMapFile);
}
else {
// 打开共享内存句柄失败
cout << "打开共享失败!" << endl;
}
信号量实现进程同步
信号量通信较为简单,使用时应注意以下几点:
1:ReleaseSemaphore(myHSemaphore, 1, NULL);相当于发送一次消息
2:WaitForSingleObject(myHSemaphore, INFINITE);相当于消耗一次消息
父进程:
//信号量
HANDLE myHSemaphore;
myHSemaphore = CreateSemaphore(NULL, FALSE, TRUE, TEXT("TOCHILD"));
if (myHSemaphore == NULL)
{
AfxMessageBox(TEXT("ERROR"));
return;
}
Sleep(5000);
ReleaseSemaphore(myHSemaphore, 1, NULL);
Sleep(2000);
WaitForSingleObject(myHSemaphore, INFINITE);
CloseHandle(myHSemaphore);
AfxMessageBox(TEXT("OK"));
子进程:
HANDLE myHSemaphore;
myHSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, TEXT("TOCHILD"));
if (myHSemaphore == NULL)
{
return FALSE;
}
std::cout << "waiting " << std::endl;
WaitForSingleObject(myHSemaphore, INFINITE);
std::cout << "had waited " << std::endl;
ReleaseSemaphore(myHSemaphore, 1, NULL);
Sleep(500);
CloseHandle(myHSemaphore);
return TRUE;