Linux进程间通信方式之消息队列

简介

消息队列是systemV风格中一种进程间通信的方式,它提供了一种从一个进程向另一个进程发送一个数据块的方法。他在Linux下是以双向链表(list_head)的形式实现,可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。
可以在内核中的/include/linux/msg.h路径下找到它的定义:

struct msg_queue {
    struct kern_ipc_perm q_perm;/* systemV的IPC对象描述结构 */
    time_t q_stime;         /* 最后一次发送消息时间 */
    time_t q_rtime;         /* 最后一次接收消息时间 */
    time_t q_ctime;         /* 最后一次修改消息时间 */
    unsigned long q_cbytes;     /*当前队列上的字节数 */
    unsigned long q_qnum;       /* 队列中的消息数 */
    unsigned long q_qbytes;     /* 队列上的最大字节数 */
    pid_t q_lspid;          /* 最后一次发送消息的进程的PID */
    pid_t q_lrpid;          /* 最后一次接受消息的进程的PID */

    /*三个双向链表*/
    struct list_head q_messages;/* 存储消息的双向链表 */
    struct list_head q_receivers;/* 存储消息接收者的双向链表 */
    struct list_head q_senders;/*  存储消息发送者的双向链表*/
};

同时在用户空间中我的ubuntu也给出了用户调用的消息队列的描述信息结构体:
Linux进程间通信方式之消息队列_第1张图片

操作函数

1.创建新的消息队列或者获取已存在的消息队列

int msgget(key_t key, int msgflg);

参数:
key:由ftok()函数生成的消息队列文件的文件号。
msflg:msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。
IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。
IPC_EXCL值,若没有该队列,则返回-1;若已存在,则返回0。

2.读写操作

msgrcv从队列中取用消息:

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

msgsnd将数据放到消息队列中:

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:
msqid:消息队列的标识码;
msgp:是一个指向消息缓冲区的指针,它用来存储发送和接收的消息,以一个长整型开头的结构体,接收函数以此确定消息的类型。该结构为用户自己定义例如:

struct my_message{    
    long int message_type;    
    /* The data you wish to transfer*/    
};

msgsz:消息所占的长度,特指消息的长度,开头的长整型不计算在内;
msgtyp:从消息队列中读取消息的形态,如果值为零,则表示读取消息队列全部内容;
msgflg:⽤来指明核⼼程序在队列没有数据的情况下所应采取的⾏动。如果msgflg和常数IPC_NOWAIT合⽤,则在msgsnd()执⾏时若是消息队列已满,则msgsnd()将不会阻塞,⽽会⽴即返回-1,如果执⾏的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。

3.设置消息队列的属性

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

参数:
对消息队列msgqid进行cmd操作,cmd操作类型有以下三种,
IPC_STAT:该命令用来获取msqid_ds的数据结构,把它存放到buf指定的地址空间;
IPC_SET:设置消息队列的属性,并存放在buf内;
IPC_RMID:从内核中删除msgqid标识的消息队列。

代码示例


comm.h
------

#ifndef __COMM__ 
#define __COMM__ 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#define __MSG_SIZE__ 1024 
#define FILEPATH "/tmp/.msg" 
#define ID 0 
extern const int g_ser_send_type;//server
extern const int g_cli_send_type;//client 
typedef struct _msginfo{ 
long mtype; 
char mtext[__MSG_SIZE__]; 
}msginfo; 
void print_log(char *); 
#endif

comm.c
------

#include "comm.h" 
const int g_ser_send_type=1;//server
const int g_cli_send_type=2;//client 
void print_log(char *msg) 
{ 
printf("%s[%d] : %s\n", __FUNCTION__,__LINE__,msg); 
}

msg_server.h
------------

#ifndef _MSG_SERVER_ 
#define _MSG_SERVER_ 
#include"comm.h" 
int msg_server_start(); 
int msg_server_end(int);
#endif

msg_server.c
------------


#include "msg_server.h" 
int _msg_id = -1; 
int msg_server_start() 
{ 
key_t _key = ftok(FILEPATH,ID);//创建键值
if( _key < 0 ){ 
print_log("get key id error"); 
return 1; 
} 
msginfo _ser_info; 
_msg_id = msgget(_key, IPC_CREAT); //获取信号队列ID 
if( _msg_id < 0 ){ 
print_log("msg_server get key id failed\n"); 
return 1; 
} 
while(1){ 
// sleep(10); 
if( msgrcv(_msg_id, &_ser_info, sizeof(_ser_info), g_cli_send_type, 0) == -1 ){ 
print_log("msg rcv error"); 
return 1; 
} 
printf("client :> %s\n",_ser_info.mtext); 
printf("server :>"); 
memset(_ser_info.mtext, '\0', sizeof(_ser_info.mtext)); 
fgets(_ser_info.mtext, __MSG_SIZE__, stdin); 
if(strncasecmp(_ser_info.mtext, "quit", 4) == 0){ 
printf("server bye!\n"); 
break; 
} 
_ser_info.mtype = g_ser_send_type; 
if( msgsnd(_msg_id, &_ser_info, __MSG_SIZE__, 0) == -1 ){ 
printf("server send msg error\n"); 
exit(0); 
} } 
return 0; 
} 
int msg_server_end(int id) 
{ 
if(msgctl(id, IPC_RMID, NULL) == -1){ 
printf("delete msg kernel info error\n"); 
return 1; 
} 
return 0; 
} 
static void delete_msg(void) 
{ 
if( _msg_id != -1 ){ 
msg_server_end(_msg_id); 
} 
printf("delete msg queue end\n"); 
} 
int main(int argc, char *argv[]) 
{ 
atexit(delete_msg); 
if(msg_server_start() == 0){ 
print_log("msg_server start success\n"); 
}else{ 
print_log("msg_server start failed\n"); 
} 
return 0; 
}

msg_client.h
------------


#ifndef _MSG_CLIENT_ 
#define _MSG_CLIENT_ 
#include"comm.h" 
int msg_client_start(); 
int msg_client_end(int); 
#endif


msg_client.c
------------

#include "msg_client.h" 
int _msg_id = -1; 
int msg_client_start() 
{ 
key_t _key = ftok(FILEPATH,ID);//创建键值
if( _key < 0 ){ 
print_log("client get key id error"); 
return 1; 
} 
msginfo _cli_info; 
_msg_id = msgget(_key, 0); //获取信号队列ID 
if( _msg_id < 0 ){ 
print_log("msg_server get key id failed\n"); 
return 1; 
} 
while(1){ 
printf("client :>"); 
fgets(_cli_info.mtext, sizeof(_cli_info.mtext),stdin); 
if(strncasecmp(_cli_info.mtext, "quit", 4) == 0){ 
printf("client bye!\n"); 
break; 
} 
_cli_info.mtype = g_cli_send_type; 
if( msgsnd(_msg_id, &_cli_info,sizeof(_cli_info), 0) == -1 ){ 
printf("client send msg error\n"); 
exit(0); 
} 
memset(_cli_info.mtext, '\0', sizeof(_cli_info.mtext)); 
if( msgrcv(_msg_id, &_cli_info, __MSG_SIZE__, g_ser_send_type, 0) == -1 ){ 
print_log("client recive msg error"); 
return 1; 
} 
printf("server :>%s\n",_cli_info.mtext); } 
return 0; 
} 
int msg_client_end(int id) 
{ 
if(msgctl(id, IPC_RMID, NULL) == -1){ 
return 1; 
} 
return 0; 
} 
static void delete_msg(void) 
{ 
if( _msg_id != -1 ){ 
msg_client_end(_msg_id); 
} 
printf("delete msg queue end\n"); 
} 
int main(int argc, char *argv[]) 
{ 
atexit(delete_msg); 
if(msg_client_start() == 0){ 
print_log("msg_server start success\n"); 
}else{ 
print_log("msg_server start failed\n"); 
} 
return 0; 

你可能感兴趣的:(IPC)