专题 12 IPC之消息队列

本文详细介绍了Linux消息队列的概念、结构及实现方法,并通过具体示例展示了如何使用消息队列进行进程间通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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()函数将路径名和项目的表示符转变为一个系统VIPC键值,其原型如下:

#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);

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值