进程间通信-消息队列

    消息队列是进程间通信方式的一种,两外两种是信号量和共享存储。这三种方式都非常的相似,在内核中有一个IPC结构,只要进程获得了这个IPC结构的编号,便能向其中读写数据了。如典型的 client-server模型,他们要向同一个IPC结构读写,那么这个IPC结构的编号就应该是被他们两个所共同知道的。其实,IPC只在内核中,用ls等命令看不到他,在外面,只能是用一个KEY与之关联。取得IPC编号的方法有如下几种
   1、服务器进程可以指定键IPC_PRIVATE创建一个新的IPC结构,并将返回的标识符放在某个文件中这样名,客户端进程只需要在这个文件中来读取便可(IPC_PRIVAGE只能用来创建IPC结构)
   2、在一个公共的头文件中顶一个大家都认可的KEY,然后服务器进程用该KEY创建IPC。但是如何该KEY已经于另外一个IPC绑定,那么msgget,semget,shmget等方法会出错。
   3、找一个服务端进程和客户端进程都认同的文件及一个整数ID。由他们来确定一个key,(用fto函数来转换)。 最常用的是第三中方法:
#include<sys/ipc.h>
key_t ftok(const char *path,int id);

key的确定规则: 用平path的stat结构中的st_dev和st_ino子u字段与id组合起来。理论上,文件不同,key也应该不同。但是因为i节点和key是 在long类型的数据结构中,如果发生越界,那么就有可能出现不同的文件产生相同的key的情况。
ipcs命令的使用:

命令ipcs用于读取System V IPC目标的状态。
ipcs -q: 只显示消息队列。
ipcs -s: 只显示信号量。
ipcs -m: 只显示共享内存。
ipcs –help: 其他的参数。

下面是ipcs命令输出的例子:

[root@wanglong wanglong]# ipcs

—— Shared Memory Segments ——–

key        shmid      owner      perms      bytes      nattch     status      

0×00000000 0          root      644        40         2                       

0×00000000 32769      root      644        16384      2                       

0×00000000 65538      root      644        268        2                       

—— Semaphore Arrays ——–

key        semid      owner      perms      nsems     


0×00000000 98305      apache    600        1     

0×000000a7 0          root      600        1         

0×00000000 65538      apache    600        1         

0×00000000 131075     apache    600        1         

0×00000000 163844     apache    600        1         

0×00000000 196613     apache    600        1         

0×00000000 229382     apache    600        1         

0×00000000 262151     apache    600        1         

0×00000000 294920     apache    600        1         

—— Message Queues ——–

key        msqid      owner      perms      used-bytes   messages 



与消息队列相关的常用SYSTEM CALL。

1、消息队列的创建和打开

#include<sys/msg.h>
int msgget(key_t key, int flag);


成功返回消息队列的ID,出错返回-1

flag:

IPC_CREAT 如果当前的key没有与任何IPC结合,则创建IPC并返回该IPC 标识符。如果IPC已经存在,打开IPC并返回IPC的标识符

IPC_EXCL IPC_CREAT连用,当IPC存在时,就出错.否则就创建

 

同时我们需要指定IPC的权限.例如

msgget(key,IPC_CREAT|IPC_EXCL|0666);

 

2、发送消息

#include<sys/msg.h>
int msgsnd(int msgid, const void*ptr,size_t nbytes, int flag);


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

 

ptr是指向消息的指针,消息的结构通常为

struct mymsg{

long mtype; //指定消息的类型,因为可以按消息类型接收

char mtext[512]; //消息的内容

};

 

flag:

IPC_NOWAIT 非阻塞标志,如果消息队列已满,msgsnd立即出错返回。

如果没有指定IPC_NOWAITmsgsnd一直阻塞直到:

1、有空间可以容纳要发送的消息

2、从系统中删除了此队列(返回EIDRM)或捕捉到信号,并从信号处理程序返回 (返回EINTR...

发送的消息会一直存在消息队列中直到有进程去取走

删除消息队列的机制不是很完善,这里并没有用引用计数.不像删除文件那样要等没有 进程访问该文件时才能删除

 

 

 

3、接收消息

#include<sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type , int flag);


成功返回消息的数据部分的长度,出错返回-1

ptr是指向消息的结构体

nbytes是消息结构体中数据缓存区的长度

 

type对应消息结构体中type

type == 0 返回队列的第一个消息

type > 0 返回类型为type的第一个消息

type < 0 返回队列中消息类型小于或等于type绝对值的消息

 

flag:

IPC_NOWAIT 如果没有找到相应的消息立即返回-1errnoENOMSG

MSG_NOERROR    nbytes小于消息的长度时,消息被截短

 

 

当消息接收了后,该消息就从消息队列中被删除

 

4、msgctl函数

msgctl对队列执行多种操作

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

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

 

cmd

IPC_STAT  取消息队列的msqid_ds结构放在buf

IPC_SET buf中的值设置msqid_ds

IPC_RMID 删除消息队列及队列中的数据

//-----------server.c--------------------------
#include<sys/ipc.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<string.h>
#define PATH "/home/jyl/db1"
#define BUFSIZE 512
struct mymsg{
long mtype;
char mtext[BUFSIZE];
};
 
int main(){
 
key_t key = ftok(PATH,1024);
if(key < 0){
perror("ftok error\n");
exit(0);
}
 
int msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666);
if(msgid < 0){
//	perror("msgget error\n");
//	exit(0);
printf("ipc已经存在\n");
msgid = msgget(key,IPC_CREAT);
}
struct mymsg msg;
msg.mtype = 1;
strcpy(msg.mtext,"hellochild");
 
if(!msgsnd(msgid, &msg, BUFSIZE,IPC_NOWAIT)){
printf("msgsend success !\n");
}else{
perror("msgsend fail\n");
exit(0);
}
struct msqid_ds m_ds;
/*	if(msgctl(msgid,IPC_RMID,&m_ds) == 0){
printf("remove success\n");
}else{
perror("remove fail\n");
exit(0);
}
*/
return 0;
}
 
 
 
//--------client.c----------------------
#include<sys/ipc.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<string.h>
#define PATH "/home/jyl/db1"
#define BUFSIZE 512
struct mymsg{
long mtype;
char mtext[BUFSIZE];
};
 
int main(){
 
key_t key = ftok(PATH,1024);
if(key < 0){
perror("ftok error\n");
exit(0);
}
 
int msgid = msgget(key,0);
if(msgid < 0){
perror("msgget error\n");
exit(0);
}
struct mymsg msg;
 
if(msgrcv(msgid, &msg, BUFSIZE,0,IPC_NOWAIT) > 0){
printf("%s\n",msg.mtext);
}else{
perror("msgrcv fail\n");
exit(0);
}
struct msqid_ds buf;
if(msgctl(msgid,IPC_RMID,&buf) < 0){
perror("remove ipc fail\n");
exit(0);
}else{
printf("remove ipc success\n");
}
return 0;
}
 


你可能感兴趣的:(进程间通信-消息队列)