消息队列是消息的链表,存放在内核中并由消息队列标识符表示。
消息队列提供了一个从一个进程向另一个进程发送数据块的方法,每个数据块都可以被认为是有一个类型,接受者接受的数据块可以有不同的类型
1.msgget
功能:创建和访问一个消息队列
原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflag);
参数:
key:某个消息队列的名字,用ftok()产生
msgflag:有两个选项IPC_CREAT和IPC_EXCL,单独使用IPC_CREAT,如果消息队列不存在则创建之,如果存在则打开返回;单独使用IPC_EXCL是没有意义的;两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回。
返回值:成功返回一个非负整数,即消息队列的标识码,失败返回-1
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
2.msgctl
功能:消息队列的控制函数
原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:
msqid:由msgget函数返回的消息队列标识码
cmd:有三个可选的值,在此我们使用IPC_RMID
IPC_STAT 把msqid_ds结构中的数据设置为消息队列的当前关联值
IPC_SET 在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值
IPC_RMID 删除消息队列
返回值:
成功返回0,失败返回-1
3.msgsnd
功能:把一条消息添加到消息队列中
原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
msgid:由msgget函数返回的消息队列标识码
msgp:指针指向准备发送的消息的结构体
msgze:msgp指向的消息的长度(不包括消息类型的long int长整型)
msgflg:其值为0表示阻塞方式
返回值:成功返回0,失败返回-1
消息结构一方面必须小于系统规定的上限,另一方面必须以一个long int长整型开始,接受者以此来确定消息的类型
struct msgbuf
{
long mtye;
char mtext[1];
};
4.msgrcv
功能:是从一个消息队列接受消息
原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:
msgsz: 消息数据的长度
msgtyp: 决定从队列中返回哪条消息:
msgtyp=0 返回消息队列中第一条消息
msgtyp>0 返回消息队列中等于mtype 类型的第一条消息。
msgtyp<0 返回mtype<=type 绝对值最小值的第一条消息。
msgflg 为0表示阻塞方式,设置IPC_NOWAIT 表示非阻塞方式
返回值:
msgrcv 调用成功返回接收的数据长度,不成功返回-1。
注意
1:在发送函数中使用的结构,如下:
struct msgbuf
{
long mtye;
char mtext[1];
};
其中第一个长整型数据表示发送消息类型,必须大于0
2:接收函数有一个参数成员long msgtyp,可以这样理解。如果msgtyp = 0,则接收所有发送类型的的数据,如果msgtyp >= 1 ,则只接受和msgtyp类型一样的发送数据。
3:发送和接收都有一个参数是size_t msgsz,这个大小为要传输的结构体的大小减去第一个long型数据之后的大小。
线程之间使用消息队列
在主线程中创建一个子线程,主线程中不停的向消息队列中发送数据,子线程不停的从消息队列中接收数据
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<errno.h>
#include<pthread.h>
#define MAX_TEXT 16
int msgid = -1;
/*消息对列使用的固定格式的结构体,第二个成员是消息对列传递信息的数组*/
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
/*子线程函数,用于接收数据,其中msgid要与主线程获取的一致*/
void *rev_func(void *data){
struct msg_st data1;
long int msgtype = 0;
key_t key = ftok("/home/kayshi/code", 0x666);
printf("key = %d\n", key);
msgid = msgget(key, 0666|IPC_CREAT);
printf("rev : msgid = %d\n", msgid);
while(1) {
printf("wait for data:\n");
/*接收数据,会刚在data1的数组中*/
if(msgrcv(msgid, (void *)&data1, MAX_TEXT, msgtype, 0) == -1) {
fprintf(stderr, "msgget failed error: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("rec:%s\n", data1.text);
}
}
int main(int argc, char **argv)
{
struct msg_st data;
char buffer[MAX_TEXT];
int msgid = -1;
pthread_t thread_id;
char *p= "this is a test\n";
// strncpy(data.text, "hello", 5);
// printf("send data: %s\n", data.text);
data.msg_type = 1;
key_t key = ftok("/home/kayshi/code", 0x666);/*利用ftok生成key值*/
printf("key = %d\n", key);
msgid = msgget(key, 0666|IPC_CREAT);/*用key值生成消息队列标识码*/
printf("main : msgid = %d\n", msgid);
if(msgid < 0) {
fprintf(stderr, "msgget failed error: %d\n", errno);
exit(EXIT_FAILURE);
}
/*创建一个子线程用来接收进程消息*/
int ret = pthread_create(&thread_id, NULL, rev_func, (void *)p);
if(ret !=0 )
printf("thread create failed\n");
while(1) {
printf("Enter some text:");
fgets(data.text, MAX_TEXT, stdin);/*获取标准输入的字符串,并存于data的数组中*/
printf("send begin!\n");
/*向消息队列中发送数据,注意地第二个参数是指向结构体的指针*/
if(msgsnd(msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr,"msg_snd failed\n");
exit(EXIT_FAILURE);
}
printf("send sucess!\n");
if(strncmp(buffer, "end", 3) == 0)
{
break;
}
sleep(1);
}
exit(EXIT_SUCCESS);
}
由于创建了线程,编译的时候指定第三库-pthread,否则会报错
gcc -o msg msg.c -pthread
进程之间使用消息队列
与线程调用的方法一致,只不过需要两个代码,分别运行
接收端
msgreceive.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/msg.h>
#include<errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(int argc, char **argv){
int msgid = -1;
struct msg_st data;
long int msgtype = 0;
key_t key = ftok("/home/kayshi/", 0x0666);
printf("key = %d\n", key);
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1){
fprintf(stderr, "msgget failed witdth error: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("receive data:\n");
while(1) {
if (msgrcv(msgid, (void*)&data, MAX_TEXT, msgtype, 0) == -1) {
fprintf(stderr, "msgrcv failed width: %d\n", errno);
}
printf("You wrote:%s", data.text);
if(strncmp(data.text, "end", 3) == 0)
{
break;
}
}
if(msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl failed width: %d\n", errno);
}
exit(EXIT_SUCCESS);
}
发送端
msgsend.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/msg.h>
#include<errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(int argc, char **argv)
{
struct msg_st data;
char buffer[MAX_TEXT];
int msgid = -1;
key_t key = ftok("/home/kayshi/", 0x6666);
printf("key = %d\n", key);
msgid = msgget(key, 0666|IPC_CREAT);
if(msgid == -1) {
fprintf(stderr, "msgget failed error: %d", errno);
exit(EXIT_FAILURE);
}
while(1) {
printf("Enter some text: \n");
fgets(data.text, MAX_TEXT, stdin);
data.msg_type = 1;
if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1){
fprintf(stderr,"msg_snd failed\n");
exit(EXIT_FAILURE);
}
if(strncmp(buffer, "end", 3) == 0)
{
break;
}
sleep(1);
}
exit(EXIT_SUCCESS);
}
分别编译并开两个客户端执行
执行接收端
kayshi@ubuntu:~/code/message_queue$ gcc -o msgreceive msgreceive.c
kayshi@ubuntu:~/code/message_queue$ gcc -o msgsend msgsend.c
kayshi@ubuntu:~/code/message_queue$ ./msgreceive
key = 1711356889
receive data:
执行发送端,并在发送端写入数据hello
root@ubuntu:/home/kayshi/code/message_queue# ./msgsend
key = 1711356889
Enter some text:
hello
Enter some text:
查看接收端是否有数据接收
kayshi@ubuntu:~/code/message_queue$ ./msgreceive
key = 1711356889
receive data:
You wrote:hello