进程间通信(IPC)-c++实现
信号,管道,信号量,共享内存,socket
进程是系统资源调度分配的一个独立单位;线程是CPU调度和分配的基本单位
进程间通信
1.信号通信(signal)
即父进程创建一个有名事件,子进程发送事件信号,然后父进程获取到事件信号后,执行相应的代码。信号通信是最古老的进程间通信的方法之一,系统内核中内置了一些常见的有名信号,不过不同的系统和硬件具体的内置信号不太一样。
//捕获信号
#include <iostream>
#include <csignal>
#include <unistd.h>
using namespace std;
void signalHandler( int signum )
{
cout << "Interrupt signal (" << signum << ") received.\n";
exit(signum);
}
int main ()
{
signal(SIGINT, signalHandler); // 注册信号 SIGINT 和信号处理程序
while(1){
cout << "Going to sleep...." << endl;
sleep(1);
}
return 0;
}
//生成信号
#include <iostream>
#include <csignal>
#include <unistd.h>
using namespace std;
void signalHandler( int signum )
{
cout << "Interrupt signal (" << signum << ") received.\n";
exit(signum);
}
int main ()
{
int i = 0;
signal(SIGINT, signalHandler); // 注册信号 SIGINT 和信号处理程序
while(++i){
cout << "wait for 1s...." << endl;
if( i == 3 ){
raise( SIGINT);
}
sleep(1);
}
return 0;
}
2.管道通信
管道通信在系统中,是以文件的方式进行读写的,匿名管道在物理上由文件系统的高速缓冲区构成,而命名管道则可在系统的临时文件中找到具体的文件,相当于通过外部文件来交流信息。父子进程间以比特流、字符流的方式传送信息。管道属于半双工通信,在父子进程中同时创建一对管道,然后利用其中一端(0端)来读数据,另一端(1端)来写数据。
//匿名管道
#include<stdio.h>
#include<unistd.h>
int main()
{
int fd[2];
pipe(fd);
int pid=fork();
if(pid>0)
{
close(fd[0]);
write(fd[1],"hello world",20);
close(fd[1]);
}
if(pid==0)
{
close(fd[1]);
char ch;
while(read(fd[0],&ch,1)>0)
printf("%c",ch);
close(fd[0]);
}
}
//命名管道
#include <iostream>
#include <Windows.h>
/*
*
* 進程間通信 命名管道 server
*
*
*
*/
int main()
{
char buff[256];
DWORD len = 0;
HANDLE h_Pipe = CreateNamedPipe(
TEXT("\\\\.\\Pipe\\mypipe"), //管道名字
PIPE_ACCESS_DUPLEX, //管道類型
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, //管道參數
PIPE_UNLIMITED_INSTANCES, //管道能創建的最大實例數量
0, //輸出緩衝區的長度 0表示默認
0, //輸入緩衝區的長度 0表示默認
NMPWAIT_WAIT_FOREVER, //超時時間
NULL);
if (h_Pipe == INVALID_HANDLE_VALUE)
{
std::cout << "Failed to CreateNamedPipe!"<<std::endl;
return -1;
}
std::cout << "CreateNamedPipe success!"<<std::endl;
std::cout << "waiting client connect..."<<std::endl;
if (ConnectNamedPipe(h_Pipe, NULL) == NULL) //阻塞客戶端來連接
{
std::cout << "Failed to Connect!" << std::endl;
}
else
{
std::cout << "Connect success!" << std::endl;
}
while (true)
{
if (ReadFile(h_Pipe, buff, 256, &len, NULL) == FALSE) //接收客戶端發送的內容
{
std::cout << "Failed to read data!" << std::endl;
break;
}
else
{
std::cout << "read data:" << buff << ",data size:" << len <<std::endl;
char d[256] = "i am server ,hello client";
DWORD len_ = 0;
WriteFile(h_Pipe, d, sizeof(d), &len_, 0); //向客戶端發送內容
std::cout << "send data:" << d << ",data size:" << len_ << std::endl;
Sleep(1000);
}
}
CloseHandle(h_Pipe); //關閉管道釋放資源
system("pause");
}
#include <iostream>
#include <Windows.h>
/*
*
* 進程間通信 命名管道 client
*
*
*
*/
int main()
{
DWORD len = 0;
bool bRet = WaitNamedPipe(TEXT("\\\\.\\Pipe\\mypipe"), NMPWAIT_WAIT_FOREVER);
if (!bRet)
{
std::cout << "Failed to connect pipeline!" << std::endl;
return -1;
}
HANDLE h_Pipe = CreateFile( //管道屬於一種特殊的文件
TEXT("\\\\.\\Pipe\\mypipe"), //文件名字
GENERIC_READ | GENERIC_WRITE, //文件模式
0, //是否共享
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, //文件屬性(只讀,默認...)NORMAL 爲默認屬性
NULL);
if (h_Pipe == INVALID_HANDLE_VALUE)
{
std::cout << "Failed to CreateFile pipeline file!" << std::endl;
}
else
{
while (true)
{
char buff[256] = "i am client ,hello server!";
if (WriteFile(h_Pipe, buff, sizeof(buff), &len, 0) == FALSE) //向服務器發數據
{
std::cout << "write faile to server failed!" << std::endl;
break;
}
else
{
std::cout << "send data to server:" << buff << ",data size:" << len << std::endl;
char recvData[256] = "";
DWORD rlen = 0;
ReadFile(h_Pipe, recvData, sizeof(recvData), &rlen, 0); //接收服務器內容
std::cout << "receive data form server:" << recvData << ",data size:" << rlen << std::endl;
}
Sleep(1000);
}
}
CloseHandle(h_Pipe);
system("pause");
}
3.共享内存
基本原理:以页面为单位,将一个普通文件映射到内存中,达到共享内存和节约内存的目的,通常在需要对文件进行频繁读写时使用,这样用内存读写取代I/O读写,以获得较高的性能;
windows下,进程间的内存地址,逻辑上是互相隔离的,物理上却是相互重叠的,所谓重叠,就是一块内存可以被多个进程使用;当调用createFileMapping函数时,创建了一个文件映射对象,返回文件映射对象句柄hMap,winddows即申请一块指定大小的内存空间;为了能够访问这个文件映射对象,需要调用MapViewOfFile函数,windows将该内存空间映射到进程的地址空间中;当其他进程访问这块内存时,需要调用OpenFileMapping **函数获取句柄hMap,并调用MapViewOfFile函数得到此内存空间的一个映射;当需要将内容保存到文件时,使用FlushViewOfFile函数进行写入;当需要解除文件映射时,使用UnmapViewOfFile函数;当需要关闭文件俺映射对象句柄时,使用CloseHandle**
//进程 A 将数据写入到共享内存 :
#include <windows.h>
using namespace std;
#define BUF_SIZE 4096
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
// 定义共享数据
char szBuffer[] = "Hello Shared Memory";
// 创建共享文件句柄
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
);
// 将数据拷贝到共享内存
strcpy((char*)lpBase,szBuffer);
// 线程挂起等其他线程读取数据
Sleep(20000);
// 解除文件映射
UnmapViewOfFile(lpBase);
// 关闭内存映射文件对象句柄
CloseHandle(hMapFile);
return 0;
}
//进程 B 获取共享内存中的数据 :
#include <iostream>
#include <windows.h>
using namespace std;
#define BUF_SIZE 4096
int main()
{
// 打开共享的文件对象
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,NULL,L"ShareMemory");
if (hMapFile)
{
LPVOID lpBase = MapViewOfFile(hMapFile,FILE_MAP_ALL_ACCESS,0,0,0);
// 将共享内存数据拷贝出来
char szBuffer[BUF_SIZE] = {0};
strcpy(szBuffer,(char*)lpBase);
printf("%s",szBuffer);
// 解除文件映射
UnmapViewOfFile(lpBase);
// 关闭内存映射文件对象句柄
CloseHandle(hMapFile);
}
else
{
// 打开共享内存句柄失败
printf("OpenMapping Error");
}
return 0;
}
linux下是使用mmap,具体实现在这里
4.信号量
以下是两个进程使用同一个信号量的通信
#include <stdio.h>
#include <windows.h>
int main(void)
{
HANDLE hMutex = CreateSemaphore(NULL, 1, 5, TEXT("125096"));
if (hMutex)
{
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
printf("有一个实例存在");
HANDLE hOpen = OpenSemaphore(SEMAPHORE_MODIFY_STATE, FALSE, TEXT("125096")); //SEMAPHORE_ALL_ACCESS SEMAPHORE_MODIFY_STATE
if (hOpen)
{
if (ReleaseSemaphore(hOpen, 1, NULL))
{
printf("OK\n");
}
else
{
int a = GetLastError();
//a=288
//企图释放并非呼叫方所拥有的多用户终端运行程序。
printf("NO\n");
}
}
}
}
else
{
printf("创建失败\n");
}
getchar();
CloseHandle(hMutex);
return 0;
}
socket套接字
消息队列
并发和并行
用进程并发和并行来举例,并发是在单核的处理器中,同时存在几个程序同时运行,但是这个“同时”指的是快速切换,也就是说,在同一个时间点里,只能存在一个程序;但是并行不一样,实现真正的同时,多个程序在不同的cpu里运行,那么这些程序互不干扰和等待,他们是并行