C++进程间通信的多种方式及实现

1、管道(Pipe):是一种半双工的通信方式,适用于具有亲缘关系的两个进程之间的通信。管道分为匿名管道和命名管道。

//下面是C++使用管道(Pipe)进行进程间通信的一个例子:
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main() {
    int fd[2];
    pid_t pid;
    char buffer[256];

    // 创建管道
    if (pipe(fd) < 0) {
        cerr << "Failed to create pipe." << endl;
        return 1;
    }
    
    // 创建子进程
    if ((pid = fork()) < 0) {
        cerr << "Failed to create child process." << endl;
        return 1;
    } else if (pid > 0) { // 父进程
        close(fd[0]); // 关闭读端
    
        string message = "Hello, child process.";
        write(fd[1], message.c_str(), message.length()); // 向管道写入数据
    
        wait(NULL); // 等待子进程结束
    } else { // 子进程
        close(fd[1]); // 关闭写端
    
        read(fd[0], buffer, sizeof(buffer)); // 从管道读取数据
        cout << "Message from parent process: " << buffer << endl;
    }
    
    return 0;

}

上述代码中,先调用pipe()函数创建了一个管道,并在fork()函数中创建了一个子进程。父进程向管道中写入一条消息,子进程从管道中读取该消息并输出到屏幕上。

需要注意的是,管道是半双工的通信方式,在本例中父进程和子进程各自拥有管道的一端,因此需要分别关闭对方不需要使用的端口,以避免出现死锁等问题。另外,当进程间通信结束后需要及时关闭管道,并在父进程中等待子进程结束。

2 、共享内存(Shared Memory):是一种高效的进程间通信方式,可以在多个进程之间共享同一块内存区域,需要使用信号量等同步机制保证数据的正确性。

下面是C++使用共享内存(Shared Memory)进行进程间通信的一个例子:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>

using namespace std;

int main() {
    pid_t pid;
    int* shared_memory;
    const int SIZE = 4096; // 共享内存的大小

    // 创建共享内存
    int fd = shm_open("/my_shared_memory", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    ftruncate(fd, SIZE);
    
    // 映射共享内存到进程地址空间
    shared_memory = (int*)mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
    // 创建子进程
    if ((pid = fork()) < 0) {
        cerr << "Failed to create child process." << endl;
        return 1;
    } else if (pid > 0) { // 父进程
        sleep(1); // 等待子进程执行
    
        cout << "Message from child process: " << shared_memory[0] << endl;
        shared_memory[1] = 2021; // 向共享内存中写入一条消息
    
        wait(NULL); // 等待子进程结束
    
        // 删除共享内存
        munmap(shared_memory, SIZE);
        shm_unlink("/my_shared_memory");
    } else { // 子进程
        shared_memory[0] = 1024; // 向共享内存中写入一条消息
    }
    
    return 0;

}

上述代码中,先调用shm_open()函数创建了一个共享内存对象,并调用ftruncate()设置其大小。然后使用mmap()映射了该共享内存到进程地址空间中。父进程向共享内存写入一条消息,子进程读取该消息并向共享内存中写入另一条消息。

需要注意的是,共享内存可以被多个进程同时访问,因此需要使用信号量等同步机制来保证数据的正确性。另外,在进程间通信结束后需要及时解除共享内存的映射,并删除该共享内存对象。

3、消息队列(Message Queue):是一种可靠的进程间通信方式,允许一个进程向消息队列发送消息,另一个进程从该队列接收消息。

下面是C++使用消息队列(Message Queue)进行进程间通信的一个例子:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>

using namespace std;

struct msgbuf {
    long type;
    char message[256];
};

int main() {
    pid_t pid;
    int msqid;
    key_t key = 1234;
    struct msgbuf message;

    // 创建消息队列
    if ((msqid = msgget(key, IPC_CREAT | 0666)) < 0) {
        cerr << "Failed to create message queue." << endl;
        return 1;
    }
    
    // 创建子进程
    if ((pid = fork()) < 0) {
        cerr << "Failed to create child process." << endl;
        return 1;
    } else if (pid > 0) { // 父进程
        sleep(1); // 等待子进程执行
    
        msgrcv(msqid, &message, sizeof(message), 2, 0); // 接收来自子进程的消息
        cout << "Message from child process: " << message.message << endl;
    
        message.type = 3;
        strcpy(message.message, "Hello, child process.");
        msgsnd(msqid, &message, sizeof(message), IPC_NOWAIT); // 向消息队列中发送消息
    
        wait(NULL); // 等待子进程结束
    
        // 删除消息队列
        msgctl(msqid, IPC_RMID, NULL);
    } else { // 子进程
        message.type = 2;
        strcpy(message.message, "Hello, parent process.");
        msgsnd(msqid, &message, sizeof(message), IPC_NOWAIT); // 向消息队列中发送消息
    
        msgrcv(msqid, &message, sizeof(message), 3, 0); // 接收来自父进程的消息
        cout << "Message from parent process: " << message.message << endl;
    }
    
    return 0;

}

上述代码中,先调用msgget()函数创建了一个消息队列,并使用key指定其唯一标识符。然后在fork()函数中创建了一个子进程。父进程向消息队列中发送一条消息,子进程读取该消息并向消息队列中发送另一条消息。

需要注意的是,消息队列是可靠的通信方式,在多个进程之间传递数据时不会出现丢失或错位等情况。另外,在进程间通信结束后需要及时删除消息队列。

4、信号量(Semaphore):是一种特殊的变量,主要用于多个进程对共享资源的访问控制问题,以保证程序正常运行。

下面是C++使用信号量(Semaphore)进行进程间通信的一个例子:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sem.h>

using namespace std;

int main() {
    pid_t pid;
    int semid;
    key_t key = 1234;

    // 创建信号量
    if ((semid = semget(key, 1, IPC_CREAT | 0666)) < 0) {
        cerr << "Failed to create semaphore." << endl;
        return 1;
    }
    
    // 初始化信号量值为0
    union semun arg;
    arg.val = 0;
    semctl(semid, 0, SETVAL, arg);
    
    // 创建子进程
    if ((pid = fork()) < 0) {
        cerr << "Failed to create child process." << endl;
        return 1;
    } else if (pid > 0) { // 父进程
        cout << "Parent process is waiting for child process..." << endl;
    
        // 等待子进程释放信号量
        struct sembuf sb;
        sb.sem_num = 0;
        sb.sem_op = -1;
        sb.sem_flg = 0;
        semop(semid, &sb, 1);
    
        cout << "Child process released the semaphore." << endl;
    
        wait(NULL); // 等待子进程结束
    
        // 删除信号量
        semctl(semid, 0, IPC_RMID);
    } else { // 子进程
        cout << "Child process is releasing the semaphore..." << endl;
    
        // 释放信号量
        struct sembuf sb;
        sb.sem_num = 0;
        sb.sem_op = 1;
        sb.sem_flg = 0;
        semop(semid, &sb, 1);
    
        cout << "Semaphore released." << endl;
    }
    
    return 0;

}

上述代码中,先调用semget()函数创建了一个信号量,并使用key指定其唯一标识符。然后初始化信号量值为0。父进程在等待子进程释放信号量之前一直处于阻塞状态,子进程释放信号量后即可继续执行。需要注意的是,多个进程间对同一个信号量的操作会产生竞争关系,因此需要使用信号量来控制访问该共享资源的顺序。

另外,在进程间通信结束后需要及时删除信号量。

5、套接字(Socket):是一种网络编程技术,但也可用于实现进程间通信,支持不同主机上的进程进行通信。

下面是C++使用套接字(Socket)进行进程间通信的一个例子:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>

using namespace std;

int main() {
    pid_t pid;
    int sockfd, newsockfd, portno;
    socklen_t clilen;
    char buffer[256];
    struct sockaddr_in serv_addr, cli_addr;

    // 创建套接字
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        cerr << "Failed to create socket." << endl;
        return 1;
    }
    
    // 设置服务器地址结构体
    bzero((char*)&serv_addr, sizeof(serv_addr));
    portno = 8888;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);
    
    // 绑定套接字到端口号
    if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        cerr << "Failed to bind socket." << endl;
        return 1;
    }
    
    // 监听套接字
    listen(sockfd, 5);
    
    // 创建子进程
    if ((pid = fork()) < 0) {
        cerr << "Failed to create child process." << endl;
        return 1;
    } else if (pid > 0) { // 父进程
        cout << "Parent process is waiting for child process..." << endl;
    
        clilen = sizeof(cli_addr);
        newsockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &clilen); // 等待客户端连接
    
        bzero(buffer, 256);
        read(newsockfd, buffer, 255); // 从套接字中读取消息
        cout << "Message from child process: " << buffer << endl;
    
        string message = "Hello, child process.";
        write(newsockfd, message.c_str(), message.length()); // 向套接字中写入消息
    
        close(newsockfd); // 关闭新的套接字
        wait(NULL); // 等待子进程结束
    } else { // 子进程
        sleep(1); // 等待父进程执行
    
        // 连接服务器
        if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
            cerr << "Failed to connect to server." << endl;
            return 1;
        }
    
        string message = "Hello, parent process.";
        write(sockfd, message.c_str(), message.length()); // 向套接字中写入消息
    
        bzero(buffer, 256);
        read(sockfd, buffer, 255); // 从套接字中读取消息
        cout << "Message from parent process: " << buffer << endl;
    
        close(sockfd); // 关闭套接字
    }
    
    return 0;

}

上述代码中,先调用socket()函数创建了一个套接字。然后设置服务器地址结构体,并使用bind()函数将套接字绑定到指定的端口号上。调用listen()函数开始监听套接字,等待客户端连接。父进程在等待子进程连接之前一直处于阻塞状态,子进程连接上服务器后即可继续执行。需要注意的是,在多个进程间使用套接字进行通信时,需要指定不同的端口号来区分各个进程之间的连接。

另外,在进程间通信结束后需要及时关闭套接字,并在父进程中等待子进程结束。

6、文件映射(File Mapping):将磁盘上的文件映射到进程的地址空间中,多个进程可以同时对该文件进行读写操作,需要使用同步机制确保数据的完整性。

下面是C++使用共享内存(Shared Memory)进行进程间通信的一个例子:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>

using namespace std;

int main() {
    pid_t pid;
    int shmid;
    key_t key = 1234;
    char* message;
    const char* data = "Hello, shared memory.";

    // 创建共享内存
    if ((shmid = shmget(key, 1024, IPC_CREAT | 0666)) < 0) {
        cerr << "Failed to create shared memory." << endl;
        return 1;
    }
    
    // 将共享内存映射到当前进程的地址空间中
    if ((message = (char*)shmat(shmid, NULL, 0)) == (char*)-1) {
        cerr << "Failed to attach shared memory to process." << endl;
        return 1;
    }
    
    // 创建子进程
    if ((pid = fork()) < 0) {
        cerr << "Failed to create child process." << endl;
        return 1;
    } else if (pid > 0) { // 父进程
        cout << "Parent process is waiting for child process..." << endl;
    
        sleep(1); // 等待子进程执行
    
        cout << "Message from child process: " << message << endl;
    
        wait(NULL); // 等待子进程结束
    
        // 删除共享内存
        shmctl(shmid, IPC_RMID, NULL);
    } else { // 子进程
        strcpy(message, data); // 向共享内存中写入消息
    
        // 解除共享内存映射
        shmdt((void*)message);
    
        exit(0); // 退出子进程
    }
    
    return 0;

}

上述代码中,先调用shmget()函数创建了一个共享内存,并使用key指定其唯一标识符。然后将共享内存映射到当前进程的地址空间中。父进程在等待子进程向共享内存中写入消息之前一直处于阻塞状态,子进程向共享内存中写入消息后即可解除共享内存映射并退出子进程。需要注意的是,不同进程之间对同一块共享内存的访问需要加锁,以避免数据竞争等问题。

另外,在进程间通信结束后需要及时删除共享内存。

  • 7
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不停奔跑的码农

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

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

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

打赏作者

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

抵扣说明:

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

余额充值