梁越

进程间通信(IPC)-c++实现

0 人看过

信号,管道,信号量,共享内存,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里运行,那么这些程序互不干扰和等待,他们是并行