进程间通信--消息队列

来源:互联网 发布:知乎一小时 编辑:程序博客网 时间:2024/06/05 02:06

1.创建消息队列

 

#include <sys/msg.h>

int msgget(key_t key,int flags);

 

参数中key用来转换成一个标识符,flags表明函数的行为。

 

//creat_msg.c

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

int main()
{
int qid;
key_t key;

key=113;
qid=msgget(key,IPC_CREAT|0666);//创建一个权限为0666的消息队列

if(qid<0){
perror("msgget");
exit(1);
}

printf("created queue id : %d/n",qid);

system("ipcs -q");//查看系统IPC的状态

return 0;
}

 

结果:

 ./creat_msg
created queue id : 0

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages   
0x00000071 0          alei       666        0            0          

函数msgctl可以在队列上做多种操作。

 

#include <sys/msg.h>

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

 

返回值: 0 ,如果成功。
- 1,如果失败:errno = EACCES (没有读的权限同时cmd 是IPC_STAT )
EFAULT (buf 指向的地址无效)
EIDRM (在读取中队列被删除)
EINVAL (msgqid无效, 或者msgsz 小于0 )
EPERM (IPC_SET或者IPC_RMID 命令被使用,但调用程序没有写的权限)
下面我们看一下可以使用的几个命令(即cmd参数):
IPC_STAT
读取消息队列的数据结构msqid_ds,并将其存储在b u f指定的地址中。
IPC_SET
设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。
IPC_RMID
从系统内核中移走消息队列

 

//del_msg.c

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

int main(int argc ,char * argv[])
{
int qid;

if(argc!=2){
puts("usage:del_msg queue_id");
exit(1);
}

qid=atoi(argv[1]);
system("ipcs -q");

if((msgctl(qid,IPC_RMID,NULL))<0){//删除指定的消息队列
perror("msgctl");
exit(1);
}

system("ipcs -q");
printf("successfully removed %d queue/n",qid);

return 0;
}

 

 

结果:

alei@alei-desktop:~/linux/code/14$ ./del_msg
usage:del_msg queue_id
alei@alei-desktop:~/linux/code/14$ ./del_msg 0

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages   
0x00000071 0      alei       666        0            0          


------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages   

successfully removed 0 queue

 

 

2.读写消息队列

 

将一个消息加入队列:

 

#include <sys/msg.h>

intmsgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg);
返回值:如果成功,0。
如果失败,-1:errno=EAGAIN(队列已满,并且使用了IPC_NOWAIT)
EACCES(没有写的权限)
EFAULT(msgp地址无效)
EIDRM(消息队列已经删除)
EINTR(当等待写操作时,收到一个信号)
EINVAL(无效的消息队列标识符,非正数的消息类型,或
者无效的消息长度)
ENOMEM(没有足够的内存复制消息缓冲区)

    系统调用msgsnd()的第一个参数是消息队列标识符,它是由系统调用msgget返回的。第二个参数是msgp,是指向消息缓冲区的指针。参数msgsz中包含的是消息的字节大小,但不包括消息类型的长度(4个字节)。
    参数msgflg可以设置为0(此时为忽略此参数),或者使用IPC_NOWAIT。

    如果消息队列已满,那么此消息则不会写入到消息队列中,控制将返回到调用进程中。如果没有指明,调用进程将会挂起,直到消息可以写入到队列中。

 

ptr指向一个msgbuf的结构,定义如下:

struct msgbuf{

long mtype;

char mbuf[];

}

长度根据具体的消息来定,但是切记:消息不能以NULL结尾。

 

使用msgrcv可以从队列中读取消息:

#include <sys/msg.h>

int msgrcv(int msqid,struct msgbuf * msgp,int msgsz,long mtype,int msgflg);
返回值:如果成功,则返回复制到消息缓冲区的字节数。
如果失败,则返回-1:errno=E2BIG(消息的长度大于msgsz,没有MSG_NOERROR)
EACCES(没有读的权限)
EFAULT(msgp指向的地址是无效的)
EIDRM(队列已经被删除)
EINTR(被信号中断)
EINVAL(msgqid无效,或者msgsz小于0)
ENOMSG(使用IPC_NOWAIT,同时队列中的消息无法满足要求)    很明显,第一个参数用来指定将要读取消息的队列。第二个参数代表要存储消息的消息缓冲区的地址。第三个参数是消息缓冲区的长度,不包括mtype的长度,它可以按照如下的方法计算:
        msgsz=sizeof(structmymsgbuf)-sizeof(long);
    第四个参数是要从消息队列中读取的消息的类型。如果此参数的值为0,那么队列中最长时间的一条消息将返回,而不论其类型是什么。
如果调用中使用了IPC_NOWAIT作为标志,那么当没有数据可以使用时,调用将把ENOMSG返回到调用进程中。否则,调用进程将会挂起,直到队列中的一条消息满足msgrcv()的参数要求。如果当客户端等待一条消息的时候队列为空,将会返回EIDRM。如果进程在等待消息的过程中捕捉到一个信号,则返回EINTR。

 

 

//snd_msg.c

#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
struct msg{  
    long msg_types;  
    char msg_buf[511]; 
};
 
int main( void ) {
    int        qid;
    int         pid;
    int         len;
    struct msg pmsg;   
     
    pmsg.msg_types = getpid();   //消息类型为当前进程的id
    sprintf (pmsg.msg_buf,"hello!this is :%d/n/0", getpid() );//初始化消息  
    len = strlen ( pmsg.msg_buf ); //或者可理解为sizeof(struct msg)-sizeof(long) 
     
    if ( (qid=msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0 ) { //创建一个消息队列 
        perror ( "msgget" );
        exit (1) ;
    }
     
    if ( (msgsnd(qid, &pmsg, len, 0 )) < 0 ){ //想消息队列中发送消息 
        perror ( "msgsn" );
        exit ( 1 );
    }
 
    printf ("successfully send a message to the queue: %d /n", qid);
    exit ( 0 ) ;
}

 

 

//rcv_msg.c

#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
 
#define BUFSZ 4096
 
struct msg{   
    long msg_types;  
    char msg_buf[511]; 
};
 
int main( int argc, char * argv[] ) {
    int        qid;
    int         len;
    struct msg pmsg;
 
    if ( argc != 2 ){ 
        perror ( "USAGE: read_msg <queue ID>" );
        exit ( 1 );
    }
     
    qid = atoi ( argv[1] );   
 
    len = msgrcv ( qid, &pmsg, BUFSZ, 0, 0 );
     
    if ( len > 0 ){
        pmsg.msg_buf[len] = '/0';  //为消息添加结束符
        printf ("reading queue id :%05ld/n", qid ); //输出队列id
        printf ("message type : %05ld/n", pmsg.msg_types ); 
        printf ("message length : %d bytes/n", len ); 
        printf ("mesage text: %s/n", pmsg.msg_buf);  
    }
    else if ( len == 0 )
        printf ("have no message from queue %d/n", qid );
    else {
        perror ( "msgrcv");
        exit (1);
    }
    system("ipcs -q");     
    return 0;
}

 

结果:

alei@alei-desktop:~/linux/code/14$ ./snd_msg
successfully send a message to the queue: 98304
alei@alei-desktop:~/linux/code/14$ ./rcv_msg 98304
reading queue id :98304
message type : 05788
message length : 20 bytes
mesage text: hello!this is :5788


------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages   
0x00000000 98304      alei       666        0            0