使用信号和管道传递消息存在一定的限制:信号传递的消息有限,管道虽然可以传输一定量的信息,但是只能传递无格式的字节流。3种新的进程间通信(IPC)机制---消息队列、信号量、共享内存,可以解决这些问题。这些机制最早出现在UNIX中,被编入POSIC:XSI中,Linux支持POSIX标准。
(1)关于IPC资源
消息队列、信号量、共享内存都是IPC资源,这些资源在使用之前都需要先创建。Linux内核中,IPC资源都可以使用一个非负整数——IPC标识符(消息队列标识符、信号量标识符、共享内存标识符)来进行标识。
在创建一个IPC标识符之前,需要指定一个关键字key,可以调用函数ftok()获得。
int ftok(const char *pathname, int prj_id);//失败返回-1
(2)关于消息队列
消息队列实际上就是一个链表(工作就是倒腾链表的,从来没倒腾过这种,o(∩_∩)o...),而消息就是链表中具有特定格式和优先级的记录,进程可以根据一定规则在消息链表中添加消息,需要消息的进程则可以从消息队列中获得所需的信息。
消息写入消息队列时,系统会将消息加入到消息队列维护的消息链表中。如图所示:
msg_next------|
msg_type |------>msg_next------|
msg_ts msg_type |------>msg_next
msg_spot| msg_ts
| -消息 msg_spot---消息
msg_next:下一个消息指针
msg_type:消息类型,用户自己定义
msg_ts:消息长度
msg_spot:消息内容
(3)消息队列相关函数
1、创建消息队列:int msgget(key_t key,int msgflag)
该函数用于创建或者访问消息队列,获得消息队列标识符符。key就是上面用ftok()获得的IPC关键字。
2、获得/修改消息队列属性:int msgctl(int msqid,int cmd,struct msqid_ds *buf);
msqid为要修改、读取属性的消息队列标识符,
参数cmd如果为IPC_STAT时表示读取内核中记录消息队列信息的数据结构msqid_ds,
返回的消息队列属性存放在buf指向的结构体中。
3、消息发送:int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
msqid:要将消息发送的消息队列标识符
msgp:指针指向消息结构体msgbuf
typedef struct {
long msgtype; //消息类型,任何大于0的整数,自定义
char mtext[?]; //消息文本,文本大小由msgsnd中的msgsz决定
}msgbuf;
msgsz:消息字节数
msgflg:可以为0(忽略该参数)或者IPC_NOWAIT(如果消息队列没有足够空间,如果设置了IPC_NOWAIT,立刻返回;否则函数阻塞,直到等到空间)。
4、消息接收:int msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
从msqid代表的消息队列取出消息,存放在msgp指向的缓冲区,缓冲结构定义和发送时使用的msgbuf类似;
msgsz:消息内容长度,msgbuf结构体中mtext的长度;
msgtpye:请求读取的消息类型
msgflg:参数可以取0,也可以取IPC_NOWAIT等。如果为IPC_NOWAIT时,消息队列如果为空,则函数阻塞,直到有消息可以取。
(4)实例:
【创建消息队列、发消息】
代码保存systemcall2.c
#include
#include
#include
#include
#include
#define BUF_SIZE 100
typedef struct {
long mtype;
char mtext[BUF_SIZE];
}msg_info; //用于消息发送
//创建消息队列
int creat_msg_queue()
{
int proj_id = 1;
int msg_id;
key_t key;
key = ftok("/home/gaolu",proj_id); //创建IPC标识符,需要先获得一个key
if(-1 == key)
{
perror("Can't generate the IPC key.\n");
return -1;
}
msg_id = msgget(key,IPC_CREAT|0660); //如果msg_id不存在,则创建,否则,返回已经存在的队列标识符
if(-1 == msg_id)
{
printf("Can't creat message queue resource.\n");
return -1;
}
return msg_id;
}
//向队列发消息
int send_msg(int msg_id,char* message)
{
int result;
msg_info MsgInfo;
MsgInfo.mtype = 10; //随意定义,大于0 && 整数 即可
strcpy(MsgInfo.mtext,message);
result = msgsnd(msg_id,&MsgInfo,strlen(message),0);
if(-1 == result)
{
perror("Fail to send message to message queue.\n");
}
return result;
}
//显示消息信息|验证消息是否发入队列
int show_msg_queue_stat(int msg_id)
{
struct msqid_ds MsgQueueInfo;
int result;
result = msgctl(msg_id,IPC_STAT,&MsgQueueInfo); //获得消息队列信息(所有者,权限,消息数……)
if(-1 == result)
{
perror("Can't get status of message queue.\n");
return -1;
}
printf("======================Message Queue Info=======================\n");
printf("Effective user id: %d.\n",MsgQueueInfo.msg_perm.uid);
printf("Effective user group id: %d.\n",MsgQueueInfo.msg_perm.gid);
printf("Current numbers of bytes in message queue(non-standard): %ld.\n",MsgQueueInfo.__msg_cbytes);
printf("Current numbers of message in message queue: %ld.\n",MsgQueueInfo.msg_qnum);
printf("===============================================================\n");
return 0;
}
int main(void)
{
int msg_id;
int result;
msg_id = creat_msg_queue();
send_msg(msg_id,"Test data..............");
show_msg_queue_stat(msg_id);
return 0;
}
【取消息】
代码保存msgrcv.c
#include
#include
#include
#include
#include
#define BUF_SIZE 100
typedef struct {
long mtype;
char mtext[BUF_SIZE];
}msg_info;
//读取消息队列中指定类型的消息,将消息内容保存在msg指向的地址空间
int rcv_msg(int msg_id,int msg_type,char* msg)
{
int result;
msg_info MsgInfo;
result = msgrcv(msg_id,&MsgInfo,BUF_SIZE,msg_type,0);
if(-1 == result)
{
perror("Can't receive message from message queue.\n");
return result;
}
strcpy(msg,MsgInfo.mtext);
return result;
}
int main(int argc,char* argv[])
{
int result;
char message[BUF_SIZE];
int msg_type;
int msg_id;
if(3 != argc)
{
printf("Useage:%s msgid msg_type.\n",argv[0]);
return -1;
}
msg_type = atoi(argv[2]);
msg_id = atoi(argv[1]);
//获得指定类型消息并显示消息内容
result = rcv_msg(msg_id,msg_type,message);
if(-1 == result)
{
perror("Fail to receive message.\n");
}
else
{
printf("Message: %s.\n",message);
}
return result;
}
【执行结果】
gcc -o msgq systemcall2.c
gcc -o rcv msgrcv.c
./msgq
======================Message Queue Info=======================
Effective user id: 1000.
Effective user group id: 1000.
Current numbers of bytes in message queue(non-standard): 23.
Current numbers of message in message queue: 1.
================================================================
ipcs -q //IPC命令 参数q表示消息队列(m:共享内存,s:信号量)
------ Message Queues --------
key msqid owner perms used-bytes messages
0x01018b15 0 gaolu 777 23 1
./rcv 0 10 //发消息时消息类型定义为10
Message: Test data...............