概述
说明:本文所述方法基于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 jpg_buff; bool ret = cv::imencode(“.jpg”, dst_mat, jpg_buff); //传递整形数据 int a; //传递数组 vectorarr; 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 arr(2); int a = 0; std::vector 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;
|