1.概述
消息队列是内核地址空间中的内部链表,通过Linux内核在各个进程之间传递内容。消息顺序地发送到消息队列中,并以几种不同的方式从队列中获取,每个消息队列可以用IPC标识符唯一的进行标识。内核中的消息队列是通过IPC的标识符来区别的,不同的消息队列之间是相对独立的。每个消息队列中的消息,又构成一个独立的链表。
2.消息缓冲区结构
消息缓冲结构(模板)如下所示:
struct msgbuf
{
long mtype; //消息类型
char mtext[1]; //消息数据,这个域并不一定要类型为char或长度为1,可根据实际情况设定。
};
自定义消息缓冲区例子:
struct msgbuf
{
long mtype;
char mtext[10];
long length;
};
3.结构msgid_ds
struct msgid_ds
{
struct ipc_perm msg_perm; //存放队列的许可权限信息
time_t msg_stime; //发送队列的最后一个消息的时间戳。
time_t msg_rtime; //从消息队列中获取最后一个消息的时间戳。
time_t msg_ctime; //对队列进行最后一次变动的时间戳。
unsigned long __msg_cbytes; //在队列上所驻留的字节总数。(即所有消息大小的总和)
msgqnum_t msg_qnum; //当前处于队列中的消息数目
msglen_t msg_qbytes; //队列中消息所容纳的字节的最大数目
pid_t msg_lspid; //发送最后一个消息进程的PID
pid_t msg_lrpid; //接收最后一个消息进程的PID
};
4.结构ipc_perm
struct ipc_perm
{
key_t key; /*函数msgget()使用的键值*/
uid_t uid; /*用户的UID*/
gid_t gid; /*用户的GID*/
uid_t cuid; /*建立者的UID*/
gid_t cgid; /*建立堵的GID*/
unsigned short mode; /*权限*/
unsigned short seq; /*序列号*/
};
5.键值构建ftok()函数
ftok()函数将路径名和项目的表示符转变为一个系统V的IPC键值,其原型如下:
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
注:也可以通过IPC_PRIVATE产生一个键值:
id = msgget(IPC_PRIVATE, S_IRUSR | S_IWUSR);
6.消息队列相关的函数原型
int msgget(key_t key, int msgflg); //消息队列创建
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//发送消息
msgrcv(int msqid, void *msgq, size_t msgsz, long msgtyp, int msgflg);//接收消息
msgctl(int msgqid, int cmd, struct msqid_ds *buf);//消息队列控制
7.消息队列操作实例
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define BUFSZ 512
struct message
{
long msg_type;
char msg_text[BUFSZ];
};
int main()
{
int qid;
key_t key;
int len;
struct message msg;
//产生键值
if((key= ftok(".", 'a')) == -1)
{
perror("ftok");
exit(1);
}
if((qid= msgget(key, IPC_CREAT | 0666)) == -1)
{
perror("msgget");
exit(1);
}
printf("openedqueue %d\n", qid);
puts("Pleaseenter the message to queue:");
if((fgets(msg.msg_text,BUFSZ, stdin)) == NULL)
{
puts("nomessage");
exit(1);
}
msg.msg_type=getpid();
len= strlen(msg.msg_text);
if(msgsnd(qid,&msg,len,0)) < 0)
{
perror("messageposted");
exit(1);
}
printf("messageis :%s\n", msg.msg_text);
if(msgctl(qid,IPC_RMID,NULL)) < 0)
{
perror("msgctl");
exit(1);
}
exit(0);
}
代码参考:
client 向server请求读取某个文件的内容
#include <sys/types.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <stddef.h> /* For definition of offsetof() */
#include <limits.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#define SERVER_KEY 0x1aaaaaa1 /* Key for server's message queue */
struct requestMsg { /* Requests (client to server) */
long mtype; /* unused */
int clientId; /* ID of client's message queue */
char pathname[PATH_MAX]; /* File to be returned */
};
#define REQ_MSG_SIZE (offsetof(struct requestMsg, pathname) - \
offsetof(struct requestMsg, clientId) + PATH_MAX)
#define RESP_MSG_SIZE 8192
struct responseMsg {
long mtype;
char data[RESP_MSG_SIZE];
};
/* Types for response messages sent from server to client */
#define RESP_MT_FAILURE 1 /* File couldn't be opened */
#define RESP_MT_DATA 2 /* Message contains file data */
#define RESP_MT_END 3 /* File data complete */
server:
#include "svmsg_file.h"
static void /* SIGCHLD handler */
grimReaper(int sig)
{
int savedErrno;
savedErrno = errno; /* waitpid() might change 'errno' */
while (waitpid(-1, NULL, WNOHANG) > 0)
continue;
errno = savedErrno;
}
static void
serveRequest(const struct requestMsg *req) /* Executed in child process: serve a single client */
{
int fd;
ssize_t numRead;
struct responseMsg resp;
fd = open(req->pathname, O_RDONLY);
if (fd == -1) { /* Open failed: send error text */
resp.mtype = RESP_MT_FAILURE;
snprintf(resp.data, sizeof(resp.data), "%s", "Couldn't open");
msgsnd(req->clientId, &resp, strlen(resp.data) + 1, 0);
exit(EXIT_FAILURE);
}
resp.mtype = RESP_MT_DATA;
while ((numRead = read(fd, resp.data, RESP_MSG_SIZE)) > 0)
if (msgsnd(req->clientId, &resp, numRead, 0) == -1)
break;
/* Send a message of type RESP_MT_END to signify end-of-file */
resp.mtype = RESP_MT_END;
msgsnd(req->clientId, &resp, 0, 0); /* Zero-length mtext */
}
int
main(int argc, char *argv[])
{
struct requestMsg req;
pid_t pid;
ssize_t msgLen;
int serverId;
struct sigaction sa;
/* Create server message queue */
serverId = msgget(SERVER_KEY, IPC_CREAT | IPC_EXCL |
S_IRUSR | S_IWUSR | S_IWGRP);
if (serverId == -1)
perror("msgget");
/* Establish SIGCHLD handler to reap terminated children */
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = grimReaper;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
perror("sigaction error!");
/* Read requests, handle each in a separate child process */
for (;;) {
msgLen = msgrcv(serverId, &req, REQ_MSG_SIZE, 0, 0);
if (msgLen == -1) {
if (errno == EINTR)
continue;
perror("msgrcv error");
break;
}
pid = fork();
if (pid == -1) {
perror("fork");
break;
}
if (pid == 0) {
serveRequest(&req);
_exit(EXIT_SUCCESS);
}
}
/* If msgrcv() or fork() fails, remove server MQ and exit */
if (msgctl(serverId, IPC_RMID, NULL) == -1)
perror("msgctl");
exit(EXIT_SUCCESS);
}
client:
#include "svmsg_file.h"
static int clientId;
static void
removeQueue(void)
{
if (msgctl(clientId, IPC_RMID, NULL) == -1)
perror("msgctl");
}
int
main(int argc, char *argv[])
{
struct requestMsg req;
struct responseMsg resp;
int serverId, numMsgs;
ssize_t msgLen, totBytes;
if (argc != 2 || strcmp(argv[1], "--help") == 0)
perror("%s pathname\n", argv[0]);
if (strlen(argv[1]) > sizeof(req.pathname) - 1)
perror("pathname too long (max: %ld bytes)\n",
(long) sizeof(req.pathname) - 1);
/* Get server's queue identifier; create queue for response */
serverId = msgget(SERVER_KEY, S_IWUSR);
if (serverId == -1)
perror("msgget - server message queue");
clientId = msgget(IPC_PRIVATE, S_IRUSR | S_IWUSR | S_IWGRP);
if (clientId == -1)
perror("msgget - client message queue");
if (atexit(removeQueue) != 0)
perror("atexit");
/* Send message asking for file named in argv[1] */
req.mtype = 1; /* Any type will do */
req.clientId = clientId;
strncpy(req.pathname, argv[1], sizeof(req.pathname) - 1);
req.pathname[sizeof(req.pathname) - 1] = '\0';
if (msgsnd(serverId, &req, REQ_MSG_SIZE, 0) == -1)
perror("msgsnd");
/* Get first response, which may be failure notification */
msgLen = msgrcv(clientId, &resp, RESP_MSG_SIZE, 0, 0);
if (msgLen == -1)
perror("msgrcv");
if (resp.mtype == RESP_MT_FAILURE) {
printf("%s\n", resp.data);
if (msgctl(clientId, IPC_RMID, NULL) == -1)
perror("msgctl");
exit(EXIT_FAILURE);
}
totBytes = msgLen;
for (numMsgs = 1; resp.mtype == RESP_MT_DATA; numMsgs++) {
msgLen = msgrcv(clientId, &resp, RESP_MSG_SIZE, 0, 0);
if (msgLen == -1)
perror("msgrcv");
totBytes += msgLen;
}
printf("Received %ld bytes (%d messages)\n", (long) totBytes, numMsgs);
exit(EXIT_SUCCESS);
}