一、消息队列创建和使用流程
- 使用ftok创建一个key
- 使用msgget创建或者打开一个消息队列
- 使用msgsnd发送消息
- 使用msgrcv接收消息
- 使用msgctl获取、修改或者删除消息队列
二、消息队列函数
2.1、ftok函数
函数原型:
key_t ftok(const char *pathname, int proj_id);
函数功能:
- 使用一个已经存在的文件名和实际只有8为的proj_id创建一个key;
2.2、msgget函数
函数原型:int msgget(key_t key, int msgflg);
函数功能和注意事项:
- 如果指定的key值已经和消息队列绑定,则msgget返回对应消息队列的标识,相当于打开一个已经存在的队列;
- 如果msgflag的值设置为IPC_PRIVATE或者没有消息队列和key绑定,那么msgget会创建一个新的消息队列;
- 如果msgget函数指定了IPC_PRIVATE选项,那么这个消息队列就属于进程私有,只有和其有亲缘关系的进程如子进程可以使用这个消息队列;
函数参数:
- key表示使用ftok创建的key值;
- msgflag的最低6位表示消息队列的读写权限,格式和文件的读写权限相同,但是不使用可执行权限;其它选项和读写权限是或的关系,如msgget(key,0666|IPC_PRIVATE)、msgget(key,0666|IPC_CREAT|IPC_EXCL);
返回值:
- 成功返回消息队列的标识,msgsnd和msgrcv需要使用这个标识符进行数据的发送和接收;
- 失败返回-1,并将相应的错误信息存储在errno中;
errno值对应的错误原因
EACCES A message queue exists for key, but the calling process does not
have permission to access the queue, and does not have the
CAP_IPC_OWNER capability.
EEXIST A message queue exists for key and msgflg specified both
IPC_CREAT and IPC_EXCL.
ENOENT No message queue exists for key and msgflg did not specify
IPC_CREAT.
ENOMEM A message queue has to be created but the system does not have
enough memory for the new data structure.
ENOSPC A message queue has to be created but the system limit for the
maximum number of message queues (MSGMNI) would be exceeded.
2.3、msgctl函数
函数原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函数功能:
- 设置或者查询消息队列的属性信息,如获取消息队列的权限信息、队列中消息条数等;
函数参数:
- msqid 消息队列ID,由msgget函数返回;
- cmd 指定需要进行的操作命令,如果cmd为IPC_STAT表示获取这个消息队列的信息,结果会存放在buf中; 如果cmd为IPC_SET表示需要设置或者修改消息队列属性;如果cmd为IPC_RMID表示从系统中删除该消息队列以及仍在该队列中的所有数据,这种删除立即生效,仍在使用这一消息队列的其它进程在下一次试图对此队列进行操作时,会得到EIDRM错误。
- buf msqid_ds结构体的原型如下,通过设置不同的cmd可以设置或者得到这些信息。
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 {
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.4、消息发送msgsnd函数
函数原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数功能和注意事项
- 向指定的消息队列ID发送一条消息,系统会将最后发送的消息存放到队列末尾;
- 消息队列msqid必须具有可读的属性;
函数参数
- msqid 消息队列ID
- msgp 这个参数表示队列需要发送的消息内容,msgp通常是如下类型的结构体,mtype必须大于0:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
- msgflag 指定传递的消息类型,如果msgflag为IPC_NOWAIT则不管队列是否为满消息立即发送不等待;如果为0,当消息队列满之后,msgsnd函数会一直阻塞,直到消息队列中有空间存储新的消息;
返回值
- 成功返回实际拷贝到mtext中的字节数,失败返回-1,错误原因同msgget;
2.5、消息接收msgrcv函数
函数原型
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
函数功能和注意事项
- 接收msgsnd发送的消息,将消息从队列的首部取出存放到msgp中;
函数参数
- msqid 消息队列ID
- msgp 用于接收消息的缓冲区
- msgtyp 需要接收的消息类型,如果为0表示从队列中接收最早放入队列的消息,如果大于0,那么消息队列中第一个type等于msgtyp的消息将被取出;如果小于0,那么消息队列中第一个type值小于或等于msgtype绝对值的消息将被取出;
- msgflg 为0表示当消息队列为空时会一直阻塞等待,IPC_NOWAIT表示不阻塞接收。
2.6、示例代码
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
typedef struct{
long mtype;
char msg[1024];
}message_t;
int main()
{
int ret = -1;
char path[]=".";
key_t key;
int proj_id = 'a';
int msgid = -1;
pid_t pid = 0;
key = ftok(path,proj_id);
if(key < 0)
{
printf("key create fail\r\n");
return -1;
}
msgid = msgget(key,0666|IPC_CREAT);
if(msgid<0)
{
perror("msgget error\r\n");
return -1;
}
pid = fork();
if(0 == pid)
{
message_t message;
message.mtype = 0;
while(1)
{
message.mtype++;
strcpy(message.msg,"test message");
if(msgsnd(msgid,&message,sizeof(message),0) < 0)
{
perror("message send fail\r\n");
continue;
}
printf("send message,count=%d\r\n",message.mtype);
if(message.mtype == 5)
{
exit(0);
}
}
}
else
{
message_t message;
struct msqid_ds msg_info;
waitpid(NULL);//等待子进程退出
while(1)
{
if(0 == msgctl(msgid, IPC_STAT, &msg_info))
{
printf("before recv %d message in queue,max %d\r\n",msg_info.msg_qnum,msg_info.msg_qbytes);
}
if(msgrcv(msgid,&message,sizeof(message),0,0)<0)
{
perror("message recv error\r\n");
continue;
}
if(0 == msgctl(msgid, IPC_STAT, &msg_info))
{
printf("after recv %d message in queue,max %d\r\n",msg_info.msg_qnum,msg_info.msg_qbytes);
}
printf("recv message,type:%ld,content:%s\r\n",message.mtype,message.msg);
if(message.mtype == 5)
{
break;
}
}
}
ret = msgctl(msgid,IPC_RMID,NULL); //删除消息队列
if(ret < 0)
{
perror("delete queue fail.\n");
return -1;
}
return 0;
}
运行结果: