linux -- System V 消息队列

一、相关函数

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

1.1、创建:msgget()

功能:返回与键值对应的消息队列id,若没有则创建新的消息队列

int msgget(key_t key, int msgflg);
key_t ftok(const char *pathname, int proj_id);

msgget

  • key:消息队列的键值
    可以自定义(>0),也可以由ftok生成
    key 为 IPC_PRIVATE时将创建新的消息队列,且只能创建
  • msgflg:消息队列的权限
    IPC_CREAT:创建新的消息队列
    IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回 -1
    IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。
  • 返回值
    成功:消息队列id (msqid)
    失败:-1

ftok:返回消息队列键值

  • pathname:文件名
  • projid:项目ID,必须为非0整数(0-255).
    pathname通常设置为当前目录“.” ,若projid为’a’,则为"./a"文件

创建新的消息队列会初始化msqid_ds结构体

1.2、发送:msgsend()

功能:往消息队列发送数据

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  • msqid:消息队列的id
  • msgp:要发送消息的指针
  • msgsz:消息的长度
  • msgflg:当消息队列已满时的处理方式
    IPC_NOWAIT:若消息队列已满,使用msgsend直接返回-1
    0:若消息队列已满,则调用msgsend被挂起,直到可写入
  • 返回值
    0 成功
    -1 失败

msgp指向的结构为

 struct msgbuf {
     long mtype;       /* message type, must be > 0 */
     char mtext[1];    /* message data 不是真的为1*/
 };

1.3、接收:msgrcv()

功能:从消息队列读取一个消息。如果读取成功,队列中的该消息将被删除

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
  • msqid:消息队列id
  • msgp:要存放消息的指针
  • msgsz:消息内容的长度(字节)
  • msgtyp:消息的读取类型
    0:返回队列中的第一个消息
    >0:返回队列中种类(mtype)为msgtyp的第一个消息
    <0:返回队列中小于等于负msgtyp类型值最小的一个
  • msgflg:消息不存在时的处理方式
    IPC_NOWAIT:若没有符合的消息,则返回-1
    MSG_NOERROR:若返回的消息比msgsz多,则消息就会截短,且不通知消息发送进程.
    0:挂起调用进程直到有对应的消息
  • 返回值
    -1 失败
    > -1 返回成功读出消息的字节数

1.4、操作:msgctl()

功能·:控制消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • msqid:消息队列id
  • cmd:要执行的操作
    IPC_STAT:将当前队列msqid_ds的结构存放在buf所指向的结构中
    IPC_SET:按buf所指向的结构设置当前队列的msqid_ds
    IPC_RMID:删除消息队列和其中的数据.
  • buf:存放消息队列属性
  • 返回值
    -1 失败
    0 成功
  • struct msqid_ds
 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 last change */
     unsigned long   __msg_cbytes; /* Current number of bytes in
                                      queue (nonstandard) */
     msgqnum_t       msg_qnum;     /* Current number of messages
                                      in queue */
     msglen_t        msg_qbytes;   /* Maximum number of bytes
                                      allowed in queue */
     pid_t           msg_lspid;    /* PID of last msgsnd(2) */
     pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
 };
  • struct ipc_perm
 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 */
 };

二、简单测试

2.1、用单进程来测试消息队列的所有操作

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <time.h>
#include <string.h>


typedef struct MsgBuf{
    long mtype;
    char mtext[10];
}MsgBuf;

void MsgStat(int, struct msqid_ds,char *);//打印消息队列属性

int main() {
    pid_t pid;
    key_t key;
    int ret;
    int msg_id,msg_flg;
    struct msqid_ds msginfo;
    MsgBuf msgbuf;

    /*获取消息队列键值*/
    key = ftok(".",'a');
    printf("key = %d\n",key);
    printf("pid = %d\n",getpid());

    /*获取消息队列id*/
    msg_flg = IPC_CREAT|IPC_EXCL;
    msg_id = msgget(key,msg_flg|0666);
    if(msg_id == -1){
        printf("msgget error\n");
        return 0;
    }
    printf("msgid = %d\n",msg_id);
    memset(&msginfo,0, sizeof(struct msqid_ds));
    MsgStat(msg_id,msginfo,"msgget");

    /*往消息队列发送消息*/
    memset(&msgbuf,0, sizeof(MsgBuf));
    msgbuf.mtype = 16;
    memcpy(msgbuf.mtext,"abcde",5);
    msg_flg = IPC_NOWAIT;
    ret = msgsnd(msg_id,&msgbuf, sizeof(msgbuf.mtext),msg_flg);
    if(ret == -1){
        printf("msgsnd error\n");
        msgctl(msg_id,IPC_RMID,NULL);
        return 0;
    }
    MsgStat(msg_id,msginfo,"msgsnd");

    /*从消息队列读取消息*/
    memset(&msgbuf,0, sizeof(MsgBuf));
    msg_flg = MSG_NOERROR|IPC_NOWAIT;
    ret = (int)msgrcv(msg_id,&msgbuf,4,16,msg_flg);
    if(ret == -1){
        printf("msgrcv error\n");
        msgctl(msg_id,IPC_RMID,NULL);
        return 0;
    }
    else{
        printf("从消息队列读到 %d 个字节\t",ret);
        printf("消息是:%s\n",msgbuf.mtext);
    }
    MsgStat(msg_id,msginfo,"msgrcv");

    /*修改消息队列最大容量   执行时需要加sudo提权*/
    msginfo.msg_perm.uid = 8;
    msginfo.msg_perm.gid = 8;
    msginfo.msg_qbytes = 16386;
    ret = msgctl(msg_id,IPC_SET,&msginfo);
    if(ret == -1){
        printf("msgctl error\n");
        msgctl(msg_id,IPC_RMID,NULL);
        return 0;
    }
    MsgStat(msg_id,msginfo,"msgctl");

    /*删除消息队列*/
   ret = msgctl(msg_id,IPC_RMID,NULL);

    return 0;
}

void MsgStat(int msg_id, struct msqid_ds msg_info,char *buf){
    int ret;
    sleep(2);//方便观察
    printf("\n******************************************\n");
    printf("after %s: \n\n",buf);
    ret = msgctl(msg_id,IPC_STAT,&msg_info);
    if(ret == -1){
        printf("get msg info error\n");
        printf("******************************************\n");
        return;
    }

    printf("msg uid:%d\t",msg_info.msg_perm.uid);
    printf("msg gid:%d\n",msg_info.msg_perm.gid);
    printf("上一次使用msgsnd时间:%s",ctime(&(msg_info.msg_stime)));
    printf("上一次使用msgrcv时间:%s",ctime(&(msg_info.msg_rtime)));
    printf("上一次修改时间:%s",ctime(&(msg_info.msg_ctime)));
    printf("当前队列里的字节数为:%ld\n",msg_info.msg_cbytes);
    printf("当前队列里的消息数量:%ld\n",msg_info.msg_qnum);
    printf("队列的最大容量(字节数):%ld\n",msg_info.msg_qbytes);
    printf("上次使用msgsnd的进程pid:%d\n",msg_info.msg_lspid);
    printf("上次使用msgrcv的进程pid:%d\n",msg_info.msg_lrpid);
    printf("******************************************\n");
}

在这里插入图片描述
在这里插入图片描述

参考:man手册、《高质量嵌入式Linux C编程》梁庚 陈明 马小陆 编著 第265 -273页

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值