消息队列

system v消息队列

消息队列可以认为是一个消息链表,对于来说除非有读者存在否则写着是没有意义的,但是消息队列可以往某个队列写入消息,然后终止,等待另一进成在某一时刻去读出这些消息,并且system消息队列可以返回指定的特定优先级消息

对于系统中的每个消息队列,内核维护一个定义在头文件<sys/msg.h>的信息结构

 15 struct msqid_ds {
 16     struct ipc_perm msg_perm;
 17     struct msg *msg_first;      /* first message on queue,unused  */
 18     struct msg *msg_last;       /* last message in queue,unused */
 19     __kernel_time_t msg_stime;  /* last msgsnd time */
 20     __kernel_time_t msg_rtime;  /* last msgrcv time */
 21     __kernel_time_t msg_ctime;  /* last change time */
 22     unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
 23     unsigned long  msg_lqbytes; /* ditto */
 24     unsigned short msg_cbytes;  /* current number of bytes on queue */
 25     unsigned short msg_qnum;    /* number of messages in queue */
 26     unsigned short msg_qbytes;  /* max number of bytes on queue */
 27     __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
 28     __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */

 29 };

主要的函数介绍:

#include <sys/msg.h>

int msgget(key_t key, int flag);

此函数返回key指定消息的标识符

key 一般有ftok函数产生 ,该函数为key_t ftok(const char *pathname, int proj_id);

该函数把从pathname导出的信息与id低8位组合成一个整数IPC键, 调用时pathname必须存在,若不存在ftok调用失败,返回-1,成功返回该整数IPC键值

flag 为该消息队列的读写权限组合,可以与IPC_CREAT 或IPC_EXCL相与,其中创建对列时都要使用IPC_CREAT,其中IPC_CREAT|IPC_EXCL含义是若已有该队列则返回错误

此函数成功时,返回非负队列标识符;失败时返回-1

 

int msgsnd(int msqid, const void *ptr, size_t length, int flag);

此函数发送消息到指定的消息对列


其中msgid是msgget返回的消息队列标识符

ptr是一个结构体指针,该结构体有如下模板:

strut msgbuf

{

      long msgbuf;

      char mtext[1];

}

其中msgbuf必需大于0,因为接受的函数对于负数消息类型用作特殊指示器,当然这个模板因为只能发送1个字符的数据,所以大多数应用都不使用该模板,而是自己定义根据该结构体的结构,例如我自己定义的消息结构

typedef struct my_msgbuf
{
    long mtype; //类型
    char mchar[MAXSIZE];//消息空间
}Message;

length是结构体中具体数据的长度

flga可以为0或者IPC_NOWAIT。

当指定IPC_NOWAIT时,msgsnd函数成为非阻塞,若发送消息时该队列没有存放消息的可用空间时,该函数立即放回

发送成功返回0,发送失败返回-1.


ssize_t msgrcv(int msqid, void *ptr, size_t length, long type, flag);

 该函数接受指定类型的消息


其中msgid是msgget返回的消息队列标识符

ptr为接受消息存放的位置

type指定待接收消息的类型

当flag指定为IPC_NOWAIT时,msgrcv函数成为非阻塞,若消息队列中没有消息时,该函数立即返回,否则阻塞到有消息发送给该消息队列

其中type的规则为:

当type=0时,返回消息队列中的第一个消息,消息队列是作为一个fifo链表维护的

当type>0时,返回类型值为type的第一个消息

当type<0时,返回类型值小于或者等于type绝对值的消息中类型值最小的消息。

msgrcv函数成功时返回为读入缓冲区的字节数,失败返回-1.


int msgctl(int msqid, int cmd, struct msqid_ds *buf);

消息队列控制函数

其中msqid为消息队列描述符

cmd有以下三种:

IPC_RMID:删除msgid指定的消息队列,当前在该队列上的任何消息都被丢弃,对于该命令,buf参数可忽略

IPC_SET:设置消息队列msgid_ds结构体的四个成员:msg_perm.uid,msg_perm_gid,msg_perm.mode和msg_qbytes。它们的值来自由buf指向的结构体中的相应成员。

IPC_STAT:给调用者通过buf返回指定消息队列当前对应msgid_ds结构体

函数执行成功返回0,失败返回-1

 

例如: 本程序完成了创建一个消息队列,并通过msgctl函数得到该消息队列的msqid_ds结构体信息,打印出来

#include<sys/msg.h>
#include<stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>

#define SVMSG_MODE (0400|0200|0040|0004)
#define MAXSIZE 200

typedef struct my_msgbuf
{
    long mtype;
    char mchar[MAXSIZE];
}Message;

int main(int argc,char **argv)
{
      int c,flags =SVMSG_MODE , mqid, key;
    struct msqid_ds info;

    while((c=getopt(argc,argv,"e"))!=-1)  //若在命令行加上-e参数,则该flags =  flags|=O_EXCL,否则flags不变
    {                                        //即若在命令行加上-e参数,则指定已经创建过的消息队列,创建失败
        switch(c)
         {
             case 'e':
            flags|=IPC_EXCL;
            break;
        }
    }
    if(optind!=argc-1)
         perror("usage:mscreate[-e]<name>");
    key = ftok(argv[optind], 0);    //返回键值
    printf("key = %d\n", key);
    mqid = msgget(key, flags|IPC_CREAT);   //获取队列描述符
    printf("mqid = %d\n", mqid);
    if(mqid==-1)
         printf("error:create MSG fail!\n");
    msgctl(mqid, IPC_STAT, &info); //取出当前队列描述符的msqid_ds到info中
    printf("mode = %o, cbytes = %lu, qnum = %lu, qtbyes= %lu\n",info.msg_perm.mode, (unsigned long )info.msg_cbytes, (unsigned long)info.msg_qnum, (unsigned long)info.msg_qbytes ); //打印当前信息
    return 0;

}

 运行结果:


例如: 服务器 、客户端程序,本程序实现了一个服务器多个客户端,使用类型为1表示任意客户发送到服务器的消息,服务器可接收,进行应答,该程序应答为读出客户请求文件到消息队列,客户端将自己的进程id作为发送给服务器的消息一部分,可以使服务器将该进程id作为消息类型,再将作出的应答发送,客户端通过类型接受自己的消息:

其中 客户到服务器 发送格式  类型 = 1, pid 路径名

服务器到客户端  发送格式 类型 = pid 文件内容

头文件 :mymsgbuf.h内容

#include<sys/msg.h>
#include<stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>

#define SVMSG_MODE (0400|0200|0040|0004)
#define MAXSIZE 200

typedef struct my_msgbuf     / /定义自己的消息类型
{
    long mtype;   
    char mchar[MAXSIZE];
}Message;

ssize_t mesg_recv (int id, Message *mptr)   //调用msgrcv函数发送消息
{
    ssize_t n;
    n = msgrcv(id, &(mptr->mtype), MAXSIZE, mptr->mtype, 0);
    return n;
}
ssize_t mesg_send(int id, Message *mptr, int n)//调用msgsnd接收消息
{
    return (msgsnd(id, &(mptr->mtype), n, 0));
}

客户端程序:

#include "mymsgbuf.h"

void client (int , int );

int main(int argc, char ** argv)
{
    int msqid;
    msqid = msgget(ftok(".", 0), 0);   //得到消息队列描述符
    if(msqid == -1)
    {
        printf("msqid error");
        exit(1);
    }
    client(msqid, msqid); 
    exit(0);
}
void client(int readfd, int writefd)
{;
    char *ptr;
    ssize_t n;
    size_t len;
    Message mesg;

    snprintf(mesg.mchar, MAXSIZE, "%ld ", (long)getpid());
    len = strlen(mesg.mchar);
    ptr = mesg.mchar +len;
    fgets(ptr, MAXSIZE-len, stdin);//输入要请求的文件
    len = strlen(mesg.mchar);
    if(mesg.mchar[len -1] == '\n')
        len--;
    mesg.mtype = 1; //将类型赋值为1,代表发送在消息队列中服务器可以接收该消息
    mesg_send(writefd, &mesg, len); //发送到消息队列中
    mesg.mtype = getpid(); 
    while((n = mesg_recv(readfd, &mesg)) > 0)
    {
        write(1, mesg.mchar, n);    
    }
}

 

服务器端程序:

#include "mymsgbuf.h"

void server (int , int );

int main(int argc, char ** argv)
{
    int msqid;
    msqid = msgget(ftok(".", 0), 0666|IPC_CREAT);//创建消息队列
    if(msqid == -1)
    {
        printf("msqid error");
        exit(1);
    }
    server(msqid, msqid);
    exit(0);
}
void server(int readfd, int writefd)
{
    FILE *fp;
    char *ptr;
    pid_t pid;
    ssize_t n;
    Message mesg;

    
    for(;;)
    {
        mesg.mtype = 1;
          if ((n = mesg_recv(readfd, &mesg)) == 0)   //接受消息类型为1的消息,即客户发送给服务器的消息

        {
            printf("mesg_recv error\n");
            exit(1);
        }
        mesg.mchar[n] = 0;
        
        if((ptr = strchr(mesg.mchar, ' ')) == NULL)   //定位空格
        {
            printf("pathname :%s", mesg.mchar);
            continue;
        }
        *ptr++ = 0;
        printf("ptr = %s\n", ptr); //ptr为客户端请求打开的文件
        pid = atol(mesg.mchar);//得到客户端的pid
        mesg.mtype = pid;//指定消息类型为pid

        if((fp = fopen(ptr, "r")) == NULL)//打开失败
        {
            snprintf(mesg.mchar+n, sizeof(mesg.mchar) - n, ":can't open, %s\n", strerror(errno));
            n = strlen(ptr);
            memmove(mesg.mchar, ptr, n);
            mesg_send(writefd, &mesg, n );
        }
        else//打开成功,得到文件内容到mesg.mchar中,并发送到消息队列中
        {
            while(fgets(mesg.mchar, MAXSIZE, fp)!= NULL)    
                mesg_send(writefd, &mesg, strlen(mesg.mchar));
            fclose(fp);
        }
        mesg_send(writefd, &mesg, 0);    //发送一个0,表示结束
    
    }
}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值