进程间通信之SystemV IPC--消息队列

System V IPC : 基于内核持续性

System V 消息队列:在程序之间传递数据的一种方法

System V 共享内存:用于在程序之间高效的共享数据

System V 信号量:用于管理对资源的访问

内核会为每个IPC对象维护一个数据结构:

struct ipc_perm
{
      key_t      key;                   /*  调用shmget()时给出的关键字*/
      uid_t      uid;                   /*共享内存所有者的有效用户ID */
      gid_t      gid;                   /* 共享内存所有者所属组的有效组ID*/ 
      uid_t      cuid;                  /* 共享内存创建 者的有效用户ID*/
      gid_t      cgid;                  /* 共享内存创建者所属组的有效组ID*/
      unsigned short   mode;            /* Permissions + SHM_DEST和SHM_LOCKED标志*/
      unsignedshort    seq;             /* 序列号*/
};
系统创建和打开IPC通道

1.从IPC键生成IPC标识符:


2.创建或打开一个IPC对象的逻辑:


ipcs 查看对应的IPC结构,

ipcrm 删除对应的IPC结构。

生成键值的函数:

key_t ftok(const char *path, int id);                          //返回值:成功返回键,出错返回(key_t)-1

简单用法: key_t ftok(".", 'a');

消息队列:消息队列是消息的链接表,存放在内核中并由消息队列标识符标识。

消息队列的数据结构:

struct msqid_ds {
	struct ipc_perm msg_perm;
	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 */
};

内核中的消息队列状态:


消息队列的几个基本参数:

1>一条消息最大是多大?                                         cat /proc/sys/kernel/msgmax

2>消息队列中所有消息的总和最大是多大 ?            cat /proc/sys/kernel/msgmnb    

3>系统能够创建多少个消息队列?                            cat /proc/sys/kernel/msgmni

4>消息通道即是消息类型

消息队列的创建:

int msgget(key_t key, int msgflg);               //msgflg   当消息队列已经存在,打开时置0,不存在创建时IPC_CREAT|0644

返回值:成功返回消息队列额id,出错返回-1

消息队列操作:(这里讨论做删除用时的用法)

int msgctl(int msqid, int cmd, struct msqid_ds *buf);     //msqid msgget返回的id, cmd 一般置为IPC_RMID, 最后一个参数一般不关注,置0即可

返回值:成功返回0,出错返回-1

发送消息:

消息格式:

struct msgbuf {
          long mtype;       /* message type, must be > 0 */
         char mtext[100];    /* message data,oflength nbytes */
};

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);         //msgp--数据的起始地址    msgsz--有效数据的大小(不包括通道号)  

    //msgflg--一般为0 | IPC_NOWAIT

返回值:成功返回0, 出错返回-1

从消息队列中取出数据:

ssize_t msgrcv(int msqid,  void *msgp,  size_t msgsz,  long msgtyp,  int msgflg);

//msgp--存放收到数据的缓冲区, msgsz--缓冲区大小(不包括通道大小)  msgtyp(即是通道号)--从哪个通道接收数据

//int msgflg    //0 | IPC_NOWAIT

msgtyp == 0 :返回队列中的第一个消息

返回值:成功返回0, 出错返回-1

实例应用:发送程序往指定的消息队列通道中写,接受程序从指定的消息队列通道中读


创建消息队列:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ipc.h>            
int main()
{
    int id =msgget(123, IPC_CREAT| 0644);
    if(-1 == id) perror("msgget"),exit(1);
    printf("msg create ok\n");
}
发送消息:

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

struct msgbuf{
    long channel;   //  >0  表示通道号
    char msgtext[100];      //信息缓存
};

int main()
{
    struct msgbuf buf;
    int id =msgget(123, 0);
    if(-1 == id) perror("msgget"),exit(1);

    while(1) 
    {
        printf("please enter channel num:\n");
        scanf("%d", &buf.channel);
        printf("please enter message:\n");
        scanf("%s", buf.msgtext);
        if(-1 == msgsnd(id, &buf, strlen(buf.msgtext), 0))
            perror("msgsnd"), exit(1);

        printf("msg write ok!\n");
    }
}
接受消息:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <unistd.h>

struct msgbuf{
    long channel;   //  >0  表示通道号
    char msgtext[100];     
};

int main()
{
    struct msgbuf buf;
    int id =msgget(123, 0);
    if(-1 == id) perror("msgget"),exit(1);
    
    while(1) 
    {
        int channel;

        printf("please enter channel num you want to read:\n");
        scanf("%d", &channel);
        memset(&buf, 0x00, sizeof(buf));
        msgrcv(id, &buf, 100, channel, 0);

        printf("msgtext = %s\n", buf.msgtext);
    }
}
删除消息队列:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main()
{
    int id = msgget(123, 0);
    if(-1 == id) perror("msgget"), exit(1);

    if (-1 == msgctl(id, IPC_RMID, 0)) perror("msgctl"), exit(1);
    
}

实例2:多个客户端进程分别向服务器程序发送消息,服务器程序接收到消息后转发回该程序。


客户机程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>

struct msgbuf{
    long channel;       //通道号
    char msgtext[100];  //消息
};

int main()
{
    int id = msgget(123, 0);
    if(-1 == id) perror("msgget"), exit(1);

    struct msgbuf buf;
    while(1)
    {
        memset(&buf, 0x00, sizeof(buf));
        *(long*)(buf.msgtext) = (long)getpid();
        buf.channel = 1;
        printf("please enter msg you want send:\n ");
        scanf("%s", buf.msgtext + sizeof(long));
        
        if(strncmp(buf.msgtext + sizeof(long), "quit", 4) == 0)
        {
            printf("exit");
            break;
        }

        if( -1 == msgsnd(id, &buf , strlen(buf.msgtext + sizeof(long))+ sizeof(long), 0))
        {
            perror("msgsnd");
            exit(1);
        }
        
        //这里的缓冲区大小的计算要考虑到前面加的pid的值在存储时会存在小端存储的问题
        memset(&buf, 0x00, sizeof(buf));
        if(-1 == msgrcv(id, &buf, 100, getpid(), 0))
        {
            perror("msgrcv");
            exit(1);
        }
        printf("rcv = %s\n", buf.msgtext + sizeof (long));
    }        
}

服务器程序:

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

struct msgbuf{
    long channel;       //通道号
    char msgtext[100];  //消息
};

int main()
{
    int id = msgget(123, IPC_CREAT|0644);
    if(-1 == id) perror("msgget"), exit(1);

    struct msgbuf buf;
    while(1)
    {   
        //清空缓存区
        memset(&buf, 0x00, sizeof(buf));
        //从1号通道接收,存放在buf
        if (-1 == msgrcv(id, &buf, 100, 1, 0)) 
        {
            perror("msgrcv");
            exit(1);
        }
        buf.channel = *(long*)(buf.msgtext);
        printf("rcv %d msg %s\n", buf.channel, buf.msgtext + sizeof(long));       
        //将消息发送到返回通道,getpid得到的通号,这里存放在msgtext前sizeof(long)个字节中

        if(-1 == msgsnd(id, &buf, strlen(buf.msgtext+sizeof(long)) + sizeof(long), 0))
        {
            perror("msgsnd");
            exit(1);
        }

        printf("send ok\n");
    }
}
清除程序:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>


int main()
{
    int id = msgget(123, 0);
    if(-1 == id) perror("msgget"), exit(1);

    msgctl(id, IPC_RMID, 0);
}



猜你喜欢

转载自blog.csdn.net/bian_cheng_ru_men/article/details/80029665
今日推荐