Linux IPC 消息队列 创建访问 msgget,发送信息 msgsnd,接受信息 msgrcv,删除 msgctl

消息队列

消息队列与 FIFO 很相似,都是一个队列结构,都可以有多个进程往队列里面写信息,多个进程从队列中读取信息。但 FIFO 需要读、写的两端事先都打开,才能够开始信息传递工作。而消息队列可以事先往队列中写信息,需要时再打开读取信息。但是,消息队列先打开读,仍然会阻塞,因为此时没有消息可读。
函数原型:

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/msg.h> 
int msgget(key_t key, int msgflg); 
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); 
ssize_t  msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); 
int msgctl(int msqid, int cmd, struct msqid_ds *buf); 

函数 msgget()

int msgget(key_t key, int msgflg);

创建和访问一个消息队列。该函数成功则返回一个唯一的消息队列标识符(类似于进程 ID 一样),失败则返回-1。

参数 key 是唯一标识一个消息队列的关键字,如果为IPC_PRIVATE(值为 0),用创建一个只有创建者进程才可以访问的消息队列, 可以用于父子间通信; 非0值的 key值(可以通过 ftok 函数获得)表示创建一个可以被多个进程共享的消息队列;

参数 msgflg 指明消息队列的访问权限和创建标志,创建标志的可选值为IPC_CREATIPC_EXCL 。如果单独指定 IPC_CREAT,msgget 要么返回新创建的消息队列 id,要么返回具有相同 key 值的消息队列 id。 如果 IPC_EXCL 和 IPC_CREAT 同时设定, 则要么创建新的消息队列,要么当队列存在时,调用失败并返回-1。

msgget.c

#include <func.h>

typedef struct msgbuf{
    long mtype;
    char mtext[64];
}MSG_T;

int main(int argc, char* argv[])
{
    int msgid=msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msgid,-1,"msgget");
    struct msgbuf msgInfo;
    msgInfo.mtype = 1;
    strcpy(msgInfo.mtext,"hello");
    int ret=msgsnd(msgid,&msgInfo,strlen(msgInfo.mtext),0);
    ERROR_CHECK(ret,-1,"msgsnd");
    return 0;
}

函数msgsnd()和函数msgrcv()

int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); 
ssize_t  msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); 

函数 msgsnd 和 msgrcv 用来将消息添加到消息队列中和从一个消息队列中获取信息。

参数 msgid 指明消息队列的 ID; 通常是 msgget 函数成功的返回值。

参数 msgbuf 是消息结构体,它的长度必须小于系统规定的上限,必须以一个长整型成员变量开始, 接收函数将用这个成员变量来确定消息的类型。 必须重写这个结构体,其中第一个参数不能改,其他自定义。如下:

struct msgbuf { 
	long mtype;         /* type of message */ 
	char mtext[1];       /* message text */ 
}; 

字段 mtype 是用户自己指定的消息类型(必须是正整数),该结构体第 2 个成员仅仅是一种说明性的结构,实际上用户可以使用任何类型的数据,指消息内容。

参数 msgsz 是消息体的大小,每个消息体最大不要超过 4K。

参数 msgflg 可以为 0(通常为 0)或 IPC_NOWAIT,如果设置 IPC_NOWAIT,则msgsnd 和 msgrcv 都不会阻塞,此时如果队列满并调用 msgsnd 或队列空时调用 msgrcv将返回错误。

参数 msgtyp 有 3 种选项:

msgtyp == 0      接收队列中的第 1 个消息(通常为 0) 
msgtyp > 0      接收对列中的第 1 个类型等于 msgtyp 的消息 
msgtyp < 0      接收其类型小于或等于 msgtyp 绝对值的第 1 个最低类型消息 

msgget_msgsnd.c

#include <func.h>

typedef struct msgbuf{
    long mtype;
    char mtext[64];
}MSG_T;

int main(int argc, char* argv[])
{
    ARGS_CHECK(argc,3);
    int msgid=msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msgid,-1,"msgget");
    struct msgbuf msgInfo;
    msgInfo.mtype = atoi(argv[1]);
    strcpy(msgInfo.mtext,argv[2]);
    int ret=msgsnd(msgid,&msgInfo,strlen(msgInfo.mtext),0);
    ERROR_CHECK(ret,-1,"msgsnd");
    return 0;
}

msgget_msgrcv.c

#include <func.h>

typedef struct msgbuf{
    long mtype;
    char mtext[64];
}MSG_T;

int main(int argc, char* argv[])
{
    int msgid=msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msgid,-1,"msgget");
    struct msgbuf msgInfo;
    msgInfo.mtype = 1;
    strcpy(msgInfo.mtext,"hello");
    int ret=msgsnd(msgid,&msgInfo,strlen(msgInfo.mtext),0);
    ERROR_CHECK(ret,-1,"msgsnd");
    return 0;
}

msgget_msgrcv_nowait.c

#include <func.h>

typedef struct msgbuf{
    long mtype;
    char mtext[64];
}MSG_T;

int main(int argc, char* argv[])
{
    ARGS_CHECK(argc,2);
    int msgid=msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msgid,-1,"msgget");
    struct msgbuf msgInfo;
    bzero(&msgInfo,sizeof(msgInfo));
    int ret=msgrcv(msgid,&msgInfo,sizeof(msgInfo.mtext),atoi(argv[1]),IPC_NOWAIT);
    printf("errno=%d\n",errno);
    ERROR_CHECK(ret,-1,"msgrcv");
    printf("mtype=%ld,mtext=%s\n",msgInfo.mtype,msgInfo.mtext);
    return 0;
}

函数msgctl()

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

函数 msgctl 是消息队列的控制函数,常用来删除消息队列

msgctl() performs the control operation specified by cmd on the System V message queue with identifier msqid.

参数 msqid 是由 msgget 返回的消息队列标识符。
参数 cmd 通常为 IPC_RMID 表示删除消息队列。

IPC_STAT: 读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址

IPC_STAT: Copy information from the kernel data structure associated with msqid into the msqid_ds structure pointed to by buf. The caller must have read permission on the message queue.

IPC_SET: 设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数

IPC_SET: Write the values of some members of the msqid_ds structure pointed to by buf to the kernel data structure associated with this message queue, updating also its msg_ctime member. The following members of the structure are updated: msg_qbytes, msg_perm.uid, msg_perm.gid, and msg_perm.mode.

IPC_RMID: 从系统内核中移走消息队列

IPC_RMID: Immediately remove the message queue, awakening all waiting reader and writer pro‐cesses (with an error return and errno set to EIDRM).

参数 buf 通常为 NULL 即可。

消息队列的msqid_ds结构,描述了消息队列当前的状态。对于每一个消息队列都有一个msqid_ds来描述消息队列当前的状态。

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:

消息队列中的数据结构中唯一可以改动的元素就是ipc_perm。它包括队列的存取权限和关于队列创建者和拥有者的信息。仅可以改变用户的id、用户的组id以及消息队列的存取权限

The ipc_perm structure is defined as follows , the highlighted fields are settable using IPC_SET
uid gid mode

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 */
};

msgget_msgctl.c

#include <func.h>

typedef struct msgbuf{
    long mtype;
    char mtext[64];
}MSG_T;

int main(int argc, char* argv[])
{
    int msgid=msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msgid,-1,"msgget");
    int ret=msgctl(msgid,IPC_RMID,NULL);
    ERROR_CHECK(ret,-1,"msgctl");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章