进程间通信——消息队列

消息队列:

    1.消息队列提供了从一个进程向另一个进程发送一块数据的方法

    2.每个数据块都被认为是有一种类型,接收者进程接收的数据块可以有不同的类型值。

    3.消息队列也有管道一样的不足,就是每个消息的最大长度是有限的,每个消息队列的总字节长数也是有上限的,系统的消息队列总数也有一个上限。

IPC对象的数据结构:

struct ipc_perm {  
key_t __key; /* Key supplied to xxxget(2) */  
uid_t uid; /* Effective UID of owner */  
gid_t gid; /* Effective GID of owner */  
uid_t cuid; /* Effective UID of creator */  
gid_t cgid; /* Effective GID of creator */  
unsigned short mode; /* Permissions */  
unsigned short __seq; /* Sequence number */  
};  

注意 :_key 和mode

消息队列结构:

消息队列结构/usr/include/linux/msg.h  
struct msqid_ds {  
struct ipc_perm msg_perm;//即上面IPC对象的数据结构  
struct msg *msg_first; /* first message on queue,unused */  
struct msg *msg_last; /* last message in queue,unused */  
__kernel_time_t msg_stime; /* last msgsnd time */  
__kernel_time_t msg_rtime; /* last msgrcv time */  
__kernel_time_t msg_ctime; /* last change time */  
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */  
unsigned long msg_lqbytes; /* ditto */  
unsigned short msg_cbytes; /* current number of bytes on queue */  
unsigned short msg_qnum; /* number of messages in queue */  
unsigned short msg_qbytes; /* max number of bytes on queue */  
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */  
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */  
};
 
 
 
 
看看几个系统调用:  
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgget(key_t key, int msgflg);
key:某个消息队列的名字
msgflg:如果是创建消息队列,该参数为:IPC_CREAT | IPC_EXCL | 权限值(如0666)
如果是访问消息队列,该参数是:IPC_CREAT
返回值:如果是创建消息队列,若消息队列已经存在,则出错返回-1;
若不存在,则创建后成功返回该消息队列标识符(非负整数)。
如果是访问消息队列,若消息队列存在,则成功返回该消息队列的标识符(非负整数),
若不存在,则创建后返回。访问失败返回-1。
  int msgctl(int msqid, int cmd, struct msqid_ds *buf);

com:将要采取的动作

 IPC_STAT//将msqid_ds结构体中的数据设置为消息队列大当前关联值

 IPC_SET//在进程有足够的前提下,把消息队列的当前关联值设置为看看几个系统调用:
msqid_ds结构体中个给出的值
IPC_RMID//删除消息队列
 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//将一条消息添加到消息队列

参数:

        msgid:要添加消息的消息队列标识符

        msgp:指向要发送的消息,即上述的消息结构体

        msgsz:msgp指向的消息长度,不包含消息结构体中的long int长整型

        msgflg:控制着当前消息队列满或到达系统上显示要发生的事情

                      msgflg = IPC_NOWAIT表示队列满不等待,出错返回

                      msgflg = 0表示阻塞等待

返回值:成功返回0,失败返回-1

制约条件:

struct msgbuf  
{  
    long mtype;//消息的类型  
    char mtext[1];//消息的长度  
}
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);

参数:

        msgid:要接收消息来自的消息队列标识符

        msgp:指向准备接收的消息,即指向消息结构体

        size:由msgp指向的消息长度,不包含long int长整型

        msgtyp:指定要接收消息的类型

                       msgtyp=0表示返回消息队列中的第一条消息

                       msgtyp>0表示返回队列中第一条类型等于msgtyp的消息

                       msgtyp<0表示返回队列中小于等于msgtyp绝对值的消息,且是满足条件的消息类型最小的消息

        msgflg:消息队列中没有要接收的消息时要发生的事情。

                       msgflg=IPC_NOWAIT表示没有可读时不等待,出错返回

                       msgflg=MSG_NOERROR表示消息大小超过msgsz时被截断

        msgtyp>0且msgflg=MSG_EXCEPT表示接收类型不等于msgtyp的第一条消息

返回值:成功返回实际接受到的消息字符个数,失败返回-1.

下面是代码实现

comm.h

#ifndef _COMM_H
#define _COMM_H

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

#define PATHNAME "."
#define PROJ_ID 0x6666

#define SERVER_TYPE 1
#define CLIENT_TYPE 2

struct msgbuf
{
    long mtype;
    char mtext[1024];
};

int createMsgQueue();

int getMsgQueue();

int destoryMsgQueue(int msgid);

int sendMsg(int msgid,int who,char *msg);

int recvMsg(int msgid,int recvType,char out[]);


#endif

comm.c

#include<stdio.h>
#include"comm.h"

static int commMsgQueue(int flags)
{
    key_t _key = ftok(PATHNAME,PROJ_ID);
    if(_key < 0)
    {
        perror("ftok");
        return -1;
    }
    int msgid = msgget(_key,flags);
    if(msgid < 0)
    {
        perror("msgget");
    }
    return msgid;
}

int createMsgQueue()
{
    return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}

int getMsgQueue()
{
    return commMsgQueue(IPC_CREAT);
}

int destoryMsgQueue(int msgid)
{
    if(msgctl(msgid,IPC_RMID,NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    return 0;
}

int sendMsg(int msgid,int who,char* msg)
{
    struct msgbuf buf;
    buf.mtype = who;
    strcpy(buf.mtext,msg);

    if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0) < 0)
    {
        perror("msgsnd");
        return -1;
    }
    return 0;
}

int recvMsg(int msgid,int recvType,char out[])
{
   struct msgbuf buf;
    if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0) < 0)
    {
        perror("msgrcv");
        return -1;
    }
    strcpy(out,buf.mtext);
    return 0;
}

server.c

#include"comm.h"
int main()
{
    int msgid = createMsgQueue();
    char buf[1024];
    while(1)
    {
        buf[0] = 0;
        recvMsg(msgid,CLIENT_TYPE,buf);
        printf("client# %s\n",buf);
        printf("please Enter# ");
        fflush(stdout);
        ssize_t s = read(0,buf,sizeof(buf));
        if(s>0)
        {
            buf[s-1] = 0;
            sendMsg(msgid,SERVER_TYPE,buf);
            printf("send done,wait recv...\n");
        }
    }
    destoryMsgQueue(msgid);
    return 0;
}

client.c

#include<stdio.h>
#include"comm.h"

int main()
{
    int msgid = getMsgQueue();
    char buf[1024];
    while(1)
    {
        buf[0] = 0;
        printf("please Enter# ");
        fflush(stdout);
        ssize_t s = read(0,buf,sizeof(buf));
        if(s > 0)
        {
            buf[s-1] = 0;
            sendMsg(msgid,CLIENT_TYPE,buf);
            printf("send done,wait recv...\n");
        }
        recvMsg(msgid,SERVER_TYPE,buf);
        printf("server# %s\n",buf);
    }
    return 0;
}

Makefile


.PHONY:all

all:client server

client:client.c comm.c
	gcc -o $@ $^

server:server.c comm.c
	gcc -o $@ $^

.PHONY:clean

clean:
	rm -f client server

那么虽然代码有了,每次开机第一次执行也是正确的,但是之后再也执行不了了,显示文件已存在,这是为什么?

再提出一个知识,就是消息队列的生命周期随内核,

此时使用命令:

ipcs -q //显示IPC资源
ipcrm    //手动删除IPC资源
方法:ipcrm -q +msqid的编号


猜你喜欢

转载自blog.csdn.net/duckyloser/article/details/79947466