Linux System V IPC 消息队列

本文详细介绍了SystemV IPC消息队列的使用方法,包括msgget、msgsnd、msgrcv和msgctl函数的参数及功能。通过示例代码展示了如何发送和接收消息,以及如何设置消息队列的权限和属性。

system V IPC 消息队列

reference: linux manual
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

获取 SYSTEM V 消息队列标识符.

key_t key: 有两种方式设置参数key_t
1.使用ftok 函数去获取一个key_t
key_t ftok(const char *pathname,int id);
该函数把从pathname导出的信息与id的低8位组合成一个key。
2.指定key 为IPC_PRIVATE, 这将会创建一个新的,唯一的IPC对象
int msgflg: 如果同时定义了 IPC_CREAT 和 IPC_EXCL,消息队列的key已经存在,api msgget(…)会调用出错,并且会把错误值EEXIST写进errno中。
在创建时,msgflg的低8位定义了消息队列的读写权限,这些权限位的格式跟系统调用 open(…)函数的参数mode 是一样的。

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

在打开一个消息队列后,可以用msgsnd放置一个消息

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

在打开一个消息队列后,可以用msgrcv读取一个消息。
int msgid:msgget() 返回的标识符
const void msgp:结构体指针,一般模板:
结构体中mtext 数据太小,一般用户会去自定义一个消息结构体。
struct msgbuf {
long mtype; /
message type, must be > 0 /
char mtext[1]; /
message data */
};
int msgflg:可以是0,也可以是如下参数:
IPC_NOWAIT:非阻塞模式,没有相应type的消息,立即返回。并把ENOMSG写进errno中。
MSG_COPY(since linux3.8):在msgtpy中指定的队列中的序数位置获取无损消息副本
MSG_EXCEPT:与大于0的msgtyp一起使用,以读取消息类型不同于msgtyp的队列中的第一条消息
MSG_NOERROR:如果msg长度大于msgsz,截短msg的长度.
msgrcv():
long msgtyp:
1.msgtpy==0,消息队列中第一条消息会被读取.
2.msgtpy>0, 消息队列中关于msgtpy的第一条消息会被读取,除非msgflg被指定为MSG_EXCEPT。在这种情况下消息队列中第一条消息会被读取,尽管msgtpy不相等。
3.msgtpy<0, 消息队列中msgtpy在小于等于给定的msgtpy绝对值中,最小的msgtpy的第一条消息会被读取。

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

System V消息控制操作
int msgid: msgget 返回的标识符
struct msqid_ds buf:这个结构体定义在 <sys/msg.h> 中,定义如下
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 */
};
int cmd:
如果以下命令执行成功会返回0.
1.IPC_STAT
从内核数据里复制关于msqid这个消息队列的信息到 struct msqid_ds *buf 中。调用者必须有读消息队列的权限。
2.IPC_RMID
从系统中删除msqid指定的消息队列,并且唤醒所有的读进程跟写进程(返回错误码EIDRM)。
3.IPC_SET
给指定的消息队列设置msqid_ds结构体的成员参数:msg_qbytes, msg_perm.uid, msg_perm.gid,msg_perm.mode(the least significant 9 bits)

/*************************************************************************
	> File Name: ipc_msg.c
	> Author: 
	> Mail: 
	> Created Time: Tue 31 Mar 2020 11:03:34 AM CST
 ************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

struct msgbuf {
    long mtype;
    char mtext[80];
};

static void
usage(char *prog_name, char *msg)
{
    if (msg != NULL)
        fputs(msg, stderr);
    fprintf(stderr, "Usage: %s [options]\n", prog_name);
    fprintf(stderr, "Options are:\n");
    fprintf(stderr, "-s        send message using msgsnd()\n");
    fprintf(stderr, "-r        read message using msgrcv()\n");
    fprintf(stderr, "-t        message type (default is 1)\n");
    fprintf(stderr, "-k        message queue key (default is 1234)\n");
    exit(EXIT_FAILURE);
}

static void
send_msg(int qid, int msgtype)
{
    struct msgbuf msg;
    time_t t;

    msg.mtype = msgtype;

    time(&t);
    snprintf(msg.mtext, sizeof(msg.mtext), "a message at %s",
            ctime(&t));

    if (msgsnd(qid, (void *) &msg, sizeof(msg.mtext),
                IPC_NOWAIT) == -1) {
        perror("msgsnd error");
        exit(EXIT_FAILURE);
    }
    printf("sent: %s\n", msg.mtext);
}

static void
get_msg(int qid, int msgtype)
{
    struct msgbuf msg;

    if (msgrcv(qid, (void *) &msg, sizeof(msg.mtext), msgtype,
               MSG_NOERROR | IPC_NOWAIT) == -1) {
        if (errno != ENOMSG) {
            perror("msgrcv");
            exit(EXIT_FAILURE);
        }
        printf("No message available for msgrcv()\n");
    } else
         printf("message received: %s\n", msg.mtext);
}

int
main(int argc, char *argv[])
{
    int qid, opt;
    int mode = 0;               /* 1 = send, 2 = receive */
    int msgtype = 1;
    int msgkey = 1234;

    while ((opt = getopt(argc, argv, "srt:k:")) != -1) {
        switch (opt) {
        case 's':
            mode = 1;
            break;
        case 'r':
            mode = 2;
            break;
        case 't':
            msgtype = atoi(optarg);
            if (msgtype <= 0)
                usage(argv[0], "-t option must be greater than 0\n");
            break;
        case 'k':
            msgkey = atoi(optarg);
            break;
        default:
            usage(argv[0], "Unrecognized option\n");
        }
    }

    if (mode == 0)
        usage(argv[0], "must use either -s or -r option\n");

    qid = msgget(msgkey, IPC_CREAT | 0666);

    if (qid == -1) {
        perror("msgget");
        exit(EXIT_FAILURE);
    }

    if (mode == 2)
        get_msg(qid, msgtype);
    else
        send_msg(qid, msgtype);

    exit(EXIT_SUCCESS);
}

执行结果:
放置一个消息:
在这里插入图片描述

ipcs
在这里插入图片描述
从消息队列读取消息:
在这里插入图片描述

ipcs
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值