Linux进程间通信---使用【共享内存+信号量+消息队列】的组合来实现服务器进程与客户进程间的通信

IPC结合实现进程间通信实例

下面将使用【共享内存+信号量+消息队列】的组合来实现服务器进程与客户进程间的通信。

  • 共享内存用来传递数据;
  • 信号量用来同步;
  • 消息队列用来 在客户端修改了共享内存后通知服务器读取。

server.c:服务端接收信息

#include <sys/ipc.h>    // 包含进程间通信相关的头文件
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义消息队列中的消息结构
struct msgbuf {
    long mtype; // 消息类型
    char mtext;  // 消息文本
};

// 定义信号量联合体,用于semctl函数
union semun {
    int val;            // 用于SETVAL命令
    struct semid_ds *buf; // 用于IPC_STAT和IPC_SET命令
};

// 删除IPC资源的函数
void delet_IPC(int msgid, char *shm, int shmid, int semid) {
    shmdt(shm);// 断开共享内存 
    // 删除共享内存
    if (shmctl(shmid, IPC_RMID, NULL) == -1){
        perror("Failed to delete shared memory");
    }
        // 删除消息队列
	if (msgctl(msgid, IPC_RMID, NULL) == -1){
        perror("Failed to delete message queue");
    }
    // 删除信号量集
    if (semctl(semid, 0, IPC_RMID) == -1){
        perror("Failed to delete semaphore");
    }
}

// P操作:等待信号量
void p_handler(int semid) {
    struct sembuf set;
    set.sem_num = 0;
    set.sem_op = -1; // 执行P操作
    set.sem_flg = SEM_UNDO;
    semop(semid, &set, 1);
}

// V操作:释放信号量
void v_handler(int semid) {
    struct sembuf set;
    set.sem_num = 0;
    set.sem_op = 1; // 执行V操作
    set.sem_flg = SEM_UNDO;
    semop(semid, &set, 1);
}

int main() {
    key_t key;              // 用于ftok的键
    char *shm;               // 共享内存的指针
    int flag = 1;           // 控制循环的标记
    int msgid, shmid, semid; // 消息队列、共享内存和信号量的ID
    struct msgbuf rcvmsg;   // 定义接收消息的缓冲区
    union semun initsem;	// 定义信号量的初始化联合体

    // 获取或创建key    
    if ((key = ftok(".", 'a')) == -1){
        perror("ftok failed");
        exit(EXIT_FAILURE);
    }
    // 创建或获取消息队列
    if ((msgid = msgget(key, IPC_CREAT | 0666)) == -1) {
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }
    // 创建或获取共享内存
   
    if ((shmid = shmget(key, 1024, IPC_CREAT | 0666)) == -1) {
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }
    // 挂载共享内存 
    if ((shm = (char *)shmat(shmid, 0, 0)) == (void *)(-1)) {
        perror("shmat failed");
        exit(EXIT_FAILURE);
    }
    // 创建或获取信号量集
    if ((semid = semget(key, 1, IPC_CREAT | 0666)) == -1) {
        perror("semget failed");
        exit(EXIT_FAILURE);
    }
    // 初始化信号量的值为1
    initsem.val = 1;
    if (semctl(semid, 0, SETVAL, initsem) == -1) {
        perror("semctl SETVAL failed");
    }

    while (flag) {
        // 接收消息
        msgrcv(msgid, &rcvmsg, sizeof(struct msgbuf), 888, 0);
        // 根据消息内容执行不同的操作
        switch (rcvmsg.mtext) {
            case 'r': // 读操作
                printf("read: ");
                p_handler(semid); // 执行P操作
                printf("%s\n", shm); // 打印共享内存中的内容
                v_handler(semid); // 执行V操作
                break;

            case 'q': // 退出操作
                printf("quit\n");
                // 删除所有IPC资源
                delet_IPC(msgid, shm, shmid, semid);
                flag = 0; // 设置退出标志
                break;
        }
    }
    return 0;
}

client.c客户端发送消息

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <string.h>

struct msgbuf {
    long mtype; // 消息类型
    char mtext; // 消息文本
};

// P操作:等待信号量,直到信号量的值大于0,然后将其减1
void p_handler(int semid) {
    struct sembuf set;
    set.sem_num = 0;
    set.sem_op = -1;
    set.sem_flg = SEM_UNDO;
    semop(semid, &set, 1);
}

// V操作:释放信号量,将其值加1,并可能唤醒等待该信号量的其他进程
void v_handler(int semid) {
    struct sembuf set;
    set.sem_num = 0;
    set.sem_op = 1;
    set.sem_flg = SEM_UNDO;
    if (semop(semid, &set, 1) == -1){
        perror("V operation failed");
    }
}

int main() {
    key_t key;
    char *shm;
    char str[128];
    int flag = 1;
    int msgid, shmid, semid;
    struct msgbuf readmsg;

    // 创建或获取IPC的键
  	if ((key = ftok(".", 'a')) < 0){
        perror("ftok failed");
        exit(EXIT_FAILURE);
    }
    // 获取消息队列ID
    if ((msgid = msgget(key, 0666 | IPC_CREAT)) == -1){
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }
    // 获取共享内存ID
    if ((shmid = shmget(key, 1024, 0666 | IPC_CREAT)) == -1){
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }
    // 将共享内存附加到当前进程的地址空间
    shm = (char *)shmat(shmid, 0, 0);
    if (shm == (char *)(-1)){
        perror("shmat failed");
        exit(EXIT_FAILURE);
    }
    
    // 获取信号量ID
    if ((semid = semget(key, 0, 0666 | IPC_CREAT)) == -1){
        perror("semget failed");
        exit(EXIT_FAILURE);
    }

    // 打印菜单
    printf("---------------------------------------\n");
    printf("--                IPC                --\n");
    printf("--  input w :write data send client  --\n");
    printf("--  input q :quit procedure          --\n");
    printf("---------------------------------------\n");

    // 主循环
    while (flag) {
        char c;
        printf("input:");
        scanf("%c", &c); // 读取用户输入
        switch (c) {
            // 写操作
            case 'w':
                getchar(); // 消耗掉scanf后的换行符
                p_handler(semid); // 执行P操作
                memset(str, 0, sizeof(str)); // 清空字符串
                printf("write:\n");
                // 读取用户输入的字符串
                fgets(str, sizeof(str), stdin); // 使用fgets代替gets以避免缓冲区溢出
                strcpy(shm, str); // 将用户输入的字符串复制到共享内存
                v_handler(semid); // 执行V操作

                // 发送消息给读者,告知有新数据写入共享内存
                readmsg.mtype = 888;
                readmsg.mtext = 'r';
                msgsnd(msgid, &readmsg, sizeof(struct msgbuf), 0);
                break;

            // 退出操作
            case 'q':
                printf("quit\n");
                // 清除输入缓冲区直到遇到换行符或文件结束符
                while ((c = getchar()) != '\n' && c != EOF);
                // 发送退出消息
                readmsg.mtype = 888;
                readmsg.mtext = 'q';
                msgsnd(msgid, &readmsg, sizeof(struct msgbuf), 0);
                flag = 0; // 设置退出标志
                break;

            // 输入错误处理
            default:
                printf("%c input error\n", c);
                // 清除输入缓冲区
                while ((c = getchar()) != '\n' && c != EOF);
                break;
        }
    }
    return 0;
}

运行结果:左侧为服务端,接收来自客户端的消息,右侧为客户端,发送指令然后传递消息。
在这里插入图片描述

参考文章:【Linux编程】进程间通信(IPC)

最近更新

  1. TCP协议是安全的吗?

    2024-06-13 08:48:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-13 08:48:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-13 08:48:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-13 08:48:03       20 阅读

热门阅读

  1. 设计模式之观察者模式

    2024-06-13 08:48:03       6 阅读
  2. TensorFlow 1.x 版本保存模型的三种方式的优缺点

    2024-06-13 08:48:03       6 阅读
  3. 【电子信息工程专业课】学习记录

    2024-06-13 08:48:03       5 阅读
  4. Mongodb使用$<identifier>过滤更新数组元素

    2024-06-13 08:48:03       8 阅读
  5. NAT概述

    2024-06-13 08:48:03       5 阅读
  6. redis字典

    2024-06-13 08:48:03       5 阅读
  7. Solus Linux: 有自己的软件包管理器

    2024-06-13 08:48:03       6 阅读
  8. 「前端+鸿蒙」鸿蒙应用开发-TS-模块化

    2024-06-13 08:48:03       11 阅读