Linux操作系统--消息队列

一、什么是消息队列
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。  每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

     1、消息队列的特点

   (1)消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.
    (2)消息队列允许一个或多个进程向它写入与读取消息.
    (3)管道和命名管道都是通信数据都是先进先出的原则。
    (4)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。

   目前主要有两种类型的消息队列:POSIX消息队列以及System V消息队列,System V消息队列目前被大量使用。System V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。 

二、消息队列的创建

1、msgget函数
该函数用来创建和访问一个消息队列。它的原型为:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t, key, int msgflg);
参数key为消息队列的键值,通常是一个长整型,可以设置为任何整数值。该参数可以用户直接指定,也可以调用ftok函数来生成,如果直接设为IPC_RPIVATE,表示总是创建新的消息队列。

参数msgflg用来建立消息队列并设定存取权限,例如IPC_CREAT|0666,它表示创建一个当前用户,用户组以及其他用户有读写权限的消息队列。如果加上IPC_EXCL,则表示只有在指定的消息队列不存在时,才会创建新的消息队列。函数执行成功后,返回消息队列的标识符,否则返回-1.

ftok函数的一般形式如下:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname,int proj_id);
参数pathname用来指定进程有存取权限的一个路径。

参数proj_id用来指定某个特定字符。函数执行成功后,返回一个消息队列的键值,否则返回-1.

//使用msgget函数来创建一个消息队列
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main()
{
    int qid;
    key_t key;
    key=ftok("/home/", 'a');      //生成消息队列的键值
    if (key<0)
    {
        perror("ftok error");
        exit(1);
    }
    qid =msgget(key,IPC_CREAT|0666); //创建一个消息队列
    if (qid<0)
    {
        perror("msgget error");
        exit(1);
        
    }
    else
    {
        printf("Done!\n");
    }
    return 0;
}


通过ipcs -q 可以看到创建的消息队列的键值为 0X61020005,标识符为65536.

三、消息队列的控制

1、msgctl函数
该函数用来对消息队列进行各种操作,例如修改消息队列的属性,清除队列中的所有消息等。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid, int command, struct msgid_ds *buf);
参数msqid为消息队列的标识符;
参数cmd为所要进行的操作,包括以下三种:
IPC_STAT :获取消息队列的状态,返回的信息将会存储在buf指向的msqid_ds结构中。
IPC_SET   :设置消息队列的属性,要设置的属性存储在buf指向的msqid_ds结构中。
IPC_RMID:  删除消息队列,同时清除队列中的所有消息。
msqid_ds结构的定义如下:
struct msqid_ds

{
    
    struct ipc_perm msg_perm;            //存取权限
    
    struct msg *msg_first;               //消息队列头指针
    
    struct msg *msg_last;                //消息队列尾指针
    
    __kernel_time_t msg_stime;           //最后一次插入消息队列消息的时间
    
    __kernel_time_t msg_rtime;           //最后一次接收消息即删除队列中一个消息的时间
    
    __kernel_time_t msg_ctime;           //最后一次修改的时间
    
    struct wait_queue *wwait;            //发送消息等待进程队列
    
    struct wait_queue *rwait;
    
    unsigned short msg_cbytes;           //当前队列的字节数
    
    unsigned short msg_qnum;             //消息队列中的消息个数
    
    unsigned short msg_qbytes;           //队列的最大字节数
    
    __kernel_ipc_pid_t msg_lspid;         //最后一次消息发送进程的pid
    
    __kernel_ipc_pid_t msg_lrpid;         //最后一次消息发送进程的pid
    
};

消息队列被创建后,即使不再使用,系统也不会自动清理。
//清除创建的消息队列
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main()
{
    int qid;
    int status;
    printf("请输入需要删除的消息队列的标识符:");
    scanf("%d",&qid);
    status=msgctl(qid, IPC_RMID, NULL);  //删除指定的消息队列
    if(status<0)
    {
        perror("msgctl error");
        exit(1);
    }
    printf("removed!\n");
    return 0;
}

四、消息队列的读写

1、msgsnd函数

该函数用来把消息添加到消息队列中。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsend(int msqid, struct msgbuf  *msgp, int msgsz, int msgflg);
参数msqid为消息队列的标识符,要写入的消息存储在参数msgp所指向的msgbuf结构中,消息的 大小由参数msgsz决定,参数msgflg用来设置消息队列没有足够空间时msgsnd函数执行的动作,例如是否等待。

msgbuf结构用来包含一个消息,其定义如下:

struct msgbuf
{
    long mtype;  //消息的类型
    char mtext[];//消息的内容
};
当进程调用msgsnd函数写入一个消息时,系统会首先检查进程对该消息队列是否有写权限,接着查看消息的长度是否超过系统允许的范围,以及消息队列的剩余空间情况等。如果条件都符合,系统会为消息分配消息头和消息数据区,将消息从用户空间复制到消息数据区后,并链入消息队列的尾部。同时,在消息头中填写消息的类型、大小以及指向消息数据区的指针等。

如果msgsnd函数被阻塞,则在下面某个条件满足时解除阻塞。

(1)消息队列中有容纳要写入消息的空间。

(2)消息队列被删除。

(3)进程被信号中断。

msgrcv函数用来从消息队列中读取(接收)一个消息。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msqid,struct msgbuf *msgp,int msgsz,long msgtyp,int msgflg);
参数msqid为 为消息队列的标识符,消息返回后将会存储在参数msgp指向的msgbuf结构中,该结构mtext成员的长度由参数msgsz决定,参数msgtyp为请求读取消息的类型,有如下3种情况:

(1)msgtyp=0:返回消息队列中的第一个消息。

(2)msgtyp>0:返回消息队列中该类型的第一个消息。

(3)msgtyp<0:在类型小于等于msgtyp绝对值的所有消息中,返回类型值最小的第一个消息。

当进程调用msgrcv函数读取一个消息时,如果返回消息小于等于用户请求,系统会将消息复制到用户空间,然后从消息队列中删除该消息,并唤醒阻塞的写入进程;如果消息大于用户请求,则返回错误信息。

如果msgrcv函数被阻塞,则在下面某个条件满足时解除阻塞:

(1)消息队列中有了满足条件的消息。

(2)消息队列被删除。

(3)进程被信号中断。


示例:

发送消息程序:

//实现两个进程间的消息传递,消息发送程序
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSG_SIZE 128
struct msgbuf           //定义消息结构
{
    long mtype;         //消息类型
    char mtext[MSG_SIZE];//消息的内容
};
int main()
{
    int qid;
    key_t key;
    int ret;
    struct msgbuf buf;    //消息缓冲区
    key=ftok("/home", 'a');  //生成消息队列的键值
    if (key<0)
    {
        perror("ftok error");
        exit(1);
    }
    qid=msgget(key, IPC_CREAT|0666);  //创建一个消息队列
    if (qid<0)
    {
        perror("msgget error");
        exit(1);
    }
    while (1)
    {
        printf("input the message:");
        fgets(buf.mtext,MSG_SIZE,stdin);  //从键盘输入消息的内容
        if (strncmp(buf.mtext, "exit",4)==0)  //如果键盘输入exit,退出循环
        {
            buf.mtype=getpid();
            ret=msgsnd(qid, &buf, MSG_SIZE, 0);
            break;
        }
        buf.mtype=getpid();                //消息的类型,这里设置为当前进程的标识符
        ret=msgsnd(qid, &buf, MSG_SIZE, 0); //向消息队列中发送一个消息
        if (ret<0)
        {
            perror("msgsnd error");
            exit(1);
        }
        else
        {
            printf("send!\n");
        }
    }
    return 0;
}


接收端:

//实现两个进程间的消息传递,编写消息接收程序
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSG_SIZE 128
struct msgbuf           //定义消息结构
{
    long mtype;         //消息类型
    char mtext[MSG_SIZE];//消息的内容
};
int main()
{
    int qid;
    key_t key;
    int ret;
    struct msgbuf buf;
    key=ftok("/home", 'a');
    if (key<0)
    {
        printf("ftok error");
        exit(1);
    }
    qid=msgget(key,IPC_EXCL|0666);  //打开消息队列
    if (qid<0)
    {
        perror("msgget error");
        exit(1);
    }
    while (1)
    {
        memset(&buf, 0, sizeof(buf));
        ret=msgrcv(qid, &buf, MSG_SIZE, 0, 0); //读取消息队列中的一个的消息
        if (ret<0)
        {
            perror("msgrcv error");
            exit(1);
        }
        else
        {
            if (strncmp(buf.mtext, "exit",4)==0)
            {
                break;
            }
            printf("received message:\n");
            printf("type=%ld,length=%ld,text:%s\n",buf.mtype,strlen(buf.mtext)-1,buf.mtext); //输入消息
        }
    }
    
    return  0;
    
}





  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值