Linux下进程间的通信--消息队列

System V IPC的概念

System V IPC(System V Interprocess Communication)是Unix和类Unix操作系统中一套传统的进程间通信机制,它包括三种主要的通信方式:消息队列、信号量和共享内存。这些机制提供了一种在不同进程之间交换数据和同步操作的手段。

System V IPC 是由内核维护的若干个对象,在Linux系统下可以通过 ipcs 命令查询

ftok() 函数用于生成一个IPC(进程间通信)对象(如消息队列、信号量或共享内存)的唯一标识符(key)。这个key用于后续的IPC操作,如创建或访问这些IPC资源。

ftok()函数

函数描述:

函数头文件
#include <sys/types.h>
#include <sys/ipc.h>

函数原型
key_t ftok(const char *pathname, int proj_id);

函数参数
pathname:指向一个存在的文件的路径。ftok()会使用该文件的inode号来帮助生成 key。这个文件不必与IPC通信有任何直接关系,但它必须存在于文件系统中。
proj_id:是一个用户定义的值,用于进一步确保key的唯一性。这是一个非负整数,但通常是一个小于 256 的正整数。

函数返回值
成功 返回一个key_t类型的值。
错误 返回-1,并设置errno以指示错误原因。

示例代码: 

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>

int main() {
    key_t key;
    const char *path = "/etc/passwd";  // 一个存在的文件
    int proj_id = 123;  // 标识符

    key = ftok(path, proj_id);
    if (key == -1) {
        perror("ftok error");
        exit(1);
    }

    printf("The key value is %d\n", key);
    return 0;
}

消息队列

消息队列简介

在Linux系统中,消息队列是一种进程间通信(IPC)机制,允许不同进程向队列中添加消息,其他进程可以从队列中读取消息。消息队列可以跨多个进程共享,支持多种数据类型,并且可以设置消息优先级。

消息队列具有FIFO的特性,具有无名管道与有名管道各自的优势,可以支持任意两个进程的进程间通讯

消息队列的操作步骤:

创建消息队列

使用msgget()函数创建一个新的消息队列或访问一个现有的消息队列。

msgget()函数

函数描述:

函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

函数原型
int msgget(key_t key, int msgflg);

函数功能
获取System V消息队列标识符

函数参数
key:由ftok函数合成
msgflg:消息队列标志      I
       PC_CREAT 创建标志      
       IPC_EXCL  如果消息队列存在,则报错, errno设置为EEXIST      
       权限控制标志

函数返回值
成功:返回消息队列id
失败:返回-1,并设置errno

发送消息

使用msgsnd()函数将消息发送到消息队列。

函数描述:

msgsnd()函数
函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

函数原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

函数参数:    
msqid:消息队列ID    
msgp:消息结构体指针    
msgsz:消息内容的长度    
msgflg:消息队列标志,默认可以填0,IPC_NOWAIT,可以设置非阻塞

函数返回值
成功:返回0
失败:-1,并设置errno

接收消息

使用msgrcv()函数从消息队列中接收消息。

msgrcv()函数
函数描述:

函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

函数原型
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

函数参数    
msqid:消息队列id    
msgp:消息结构指针    
msgsz:最大读取的字节数    
msgtyp:消息类型    
msgflg:消息队列标志,默认可以填0,IPC_NOWAIT,可以设置非阻塞

函数返回值
成功:返回实际读取消息内容的字节数
失败:-1,并设置 errno

删除消息队列

使用msgctl()函数删除消息队列

msgctl()函数

msgctl()对标识符为msqid的System V消息队列执行cmd指定的控制操作

函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

函数原型int msgctl(int msqid, int cmd, struct msqid_ds *buf);

函数参数
msqid:消息队列id
cmd:命令字    
IPC_STAT:获取消息队列属性    
IPC_SET:设置消息队列属性    
IPC_RMID:删除消息队列,用此命名时,第三个参数为NULL
buf:消息队列属性结构体对象指针

函数返回值
成功:IPC_STAT, IPC_SET, and IPC_RMID 返回0
失败:返回-1,并设置 errno
 消息队列属性结构体定义
struct ipc_perm {
               key_t          __key;       /* Key supplied to msgget(2) */
               uid_t          uid;         /* Effective UID of owner */
               gid_t          gid;         /* Effective GID of owner */
               uid_t          cuid;        /* Effective UID of creator */
               gid_t          cgid;        /* Effective GID of creator */
               unsigned short mode;        /* Permissions */
               unsigned short __seq;       /* Sequence number */
           };
struct msqid_ds {
               struct ipc_perm msg_perm;   /* Ownership and permissions */
               time_t          msg_stime;  /* Time of last msgsnd(2) */
               time_t          msg_rtime;  /* Time of last msgrcv(2) */
               time_t          msg_ctime;  /* Time of creation or last
                                              modification by msgctl() */
               unsigned long   msg_cbytes; /* # of bytes in queue */
               msgqnum_t       msg_qnum;   /* # number of messages in queue */
               msglen_t        msg_qbytes; /* Maximum # of bytes in queue */
               pid_t           msg_lspid;  /* PID of last msgsnd(2) */
               pid_t           msg_lrpid;  /* PID of last msgrcv(2) */
           };

总结:

消息队列整体代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <unistd.h>

// 定义消息结构体
struct my_msg {
    long mtype; // 消息类型,必须大于0
    char mtext[100]; // 消息数据
};

int main() {
    int msqid;
    struct my_msg msg;

    // 创建消息队列
    msqid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
    if (msqid == -1) {
        perror("msgget");
        exit(1);
    }

    // 向消息队列发送消息
    msg.mtype = 1;
    strcpy(msg.mtext, "Hello, World!");
    if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1) {
        perror("msgsnd");
        exit(1);
    }

    // 从消息队列接收消息
    if (msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0) == -1) {
        perror("msgrcv");
        exit(1);
    }

    printf("Received: %s\n", msg.mtext);

    // 删除消息队列
    if (msgctl(msqid, IPC_RMID, NULL) == -1) {
        perror("msgctl");
        exit(1);
    }

    return 0;
}

代码解读:

  • 定义了一个结构体my_msg,用于存储消息队列中的消息。
  • mtype是消息的类型,它必须是一个大于0的长整型数,用于区分不同的消息类型。
  • mtext是消息的数据部分,这里定义为一个100字符的数组
  • 定义了一个消息队列标识符msqid和一个消息结构体变量msg
  • 使用msgget函数创建一个消息队列,IPC_PRIVATE作为键值参数表示创建一个只对当前进程可见的私有消息队列。0666 | IPC_CREAT设置了消息队列的权限,并指示如果队列不存在则创建它。
  • 如果创建失败,使用perror打印错误信息并退出
  • 设置消息的类型为1,并设置消息文本为"Hello, World!"。
  • 使用msgsnd函数向消息队列发送消息。msgsnd的第四个参数0表示消息按类型排序。
  • 如果发送失败,使用perror打印错误信息并退出。
  • 使用msgrcv函数从消息队列接收类型为1的消息。sizeof(msg.mtext)指定了消息数据的最大长度。
  • 如果接收失败,使用perror打印错误信息并退出
  • 打印接收到的消息。
  • 使用msgctl函数删除消息队列,IPC_RMID是控制命令,指示删除消息队列。
  • 如果删除失败,使用perror打印错误信息并退出。

结语:

无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值