System V IPC 机制:消息队列
消息队列
消息队列与 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_CREAT和IPC_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;
}