目录
system-V IPC
消息队列
消息队列和信号管道的对比
消息队列和信号的对比
消息队列和管道的对比
消息队列函数API
msgget():打开或创建消息队列
msgsnd():发送消息
msgrcv():接收消息
msgctl():控制消息队列
msgsnd.c文件
msgrcv.c文件
Makefile文件
执行过程
system-V IPC:消息队列、共享内存、信号量。
这些对象的操作接口都类似,在系统中都使用key的键值来唯一标识,而且都是持续性资源(即它们被创建后不会因为进程的退出而消失,而会持续性存在,除非调用特殊的函数或命令删除它们)。
Linux的IPC对象在内核内部使用链表维护,不同的对象使用IPC标识符来标识(如消息队列标识符msqid、共享内存标识符shmid、信号量标识符semid)。
对于用户而言,内核提供了简洁的接口,不同的进程通过IPC关键字(key)即可访问具体的对象。
ipcs:查看系统当前IPC对象。
信号队列:从一个进程发送一个数据块到另一个进程。每个数据块含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。
信号承载的信息量少,而消息队列可以承载大量自定义的数据。
消息队列和有名管道都可以在不相关的进程间通信,同时都是通过发送和接收的方式来传递数据。
有名管道:发送数据使用write()函数、接收数据使用read()函数。
消息队列:发送数据使用msgsnd()函数、接收数据使用msgrcv()函数。消息队列对每个数据都有一个最大长度的限制。
消息队列也可以独立于发送和接收进程而存在,在进程终止时,消息队列及其内容并不会被删除。
管道只能承载无格式字节流,而消息队列提供有格式的字节流,可以减少开发人员的工作量。
消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级,接收程序可以通过消息类型有选择地接收数据,而不像有名管道只能默认接收。
消息队列可以实现消息的随机查询,消息不一定要以先进先出的顺序接收,也可以按消息的类型接收。
收发消息需要具体的消息队列对象,函数作用就是创建或获取一个消息队列对象,并返回消息队列标识符。创建的消息队列的数量受到系统可支持的消息队列数量的限制。
#include
#include
#include
int msgget(key_t key, int msgflg);
/*
key:消息队列的关键字值,多个进程可以通过它访问同一个消息队列。例如收发进程都使用。有个特殊值IPC_PRIVATE,用于创建当前进程的私有消息队列。
msgflg:表示创建的消息队列的模式标志参数,主要有IPC_CREAT,IPC_EXCL和权限mode。
如果IPC_CREAT为真:如果内核中不存在关键字与key相等的消息队列,则新建一个消息队列;如果存在,返回此消息队列的标识符
如果IPC_CREAT | IPC_EXCL为真:如果内核中不存在关键字与key相等的消息队列,则新建一个消息队列;如果存在,则报错
mode指IPC对象存取权限。如0666等
返回值:
执行成功:队列ID
执行失败:-1,并且错误原因存于error
*/
权限只有读写,没有执行。
当key被指定为IPC_PRIVATE,系统会自动产生一个未用的key来对应一个新的消息队列对象,这个消息队列一般用于进程内部间通信。
该函数可能返回以下错误代码error:
EACCES:指定的消息队列已存在,但调用进程没有权限访问它
EEXIST:key指定的消息队列已存在,而msgflg同时指定IPC_CREAT | IPC_EXCL标志
ENOENT:key指定的消息队列不存在,同时msgflg没有指定IPC_CREAT标志
ENOMEM:需要建立消息队列,但内存不足
ENOSPC:需要建立消息队列,但已达到系统的限制
msgsnd()函数把消息发送到已打开的消息队列的末尾。
#include
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
/*
msqid:消息队列标识符
msgp:发送给队列的消息。可以是任何类型的结构体,但第一个字段必须为long类型(即表明此发送消息的类型),如下结构体
msgsz:待发送消息的大小,不包含消息类型占用的4个字节(即下面结构体的mtext的长度)
msgflg:
0:当消息队列满时,该函数会阻塞,直到消息能写入消息队列
IPC_NOWAIT:当消息队列满时,该函数不等待立即返回
IPC_NOERROR:要发送的消息大于size字节,则把消息截断,截断部分将被丢弃,且不通知发送进程
返回值:
执行成功:0
执行失败:-1,并且错误原因存于error
*/
/* msgp定义的参数格式 */
struct s_msg{
long type; /* 消息类型,必须大于0 */
char mtext[1]; /* 消息正文,可以是其他任何类型 */
}msgp;
msgsnd()为阻塞函数,解除阻塞的条件:
消息队列有容乃该消息的空间
msqid代表的消息队列被删除
调用msgsnd函数的进程被信号中断
该函数可能返回以下错误代码error:
EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满
EIDRM:msqid标识符的消息队列已被删除
EACCESS:无权限写入消息队列
EFAULT:参数msgp指向无效的内存地址
EINTR:队列已满而处于等待情况下被信号中断
EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0
msgrcv()函数把消息从消息队列取出(可以指定取出某一条消息),即从msqid标识符的消息队列读取消息并将消息存储在msgp中,读取后把此消息从消息队列中删除。
#include
#include
#include
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
/*
msqid:消息队列标识符
msgp:发送给队列的消息。可以是任何类型的结构体,但第一个字段必须为long类型(即表明此发送消息的类型)
msgsz:待发送消息的大小,不包含消息类型占用的4个字节
msgtyp:
0:接收第一个消息
>0:接收类型等于msgtyp的第一个消息
<0:接收类型等于或小于msgtyp绝对值的第一个消息
msgflg:
0:阻塞式接收消息,没有该类型的消息时该函数一直会阻塞等待
IPC_NOWAIT:若消息队列中没有相应类型的消息可以接收,则函数error为ENOMSG
IPC_EXCEPT:与msgtype配合使用返回队列第一个类型不为msgtype的消息
IPC_NOERROR:若队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃
返回值:
执行成功:实际读取到的消息数据长度
执行失败:-1,并且错误原因存于error
*/
msgrcv()为阻塞函数,解除阻塞的条件:
消息队列中有了满足条件的消息
msqid代表的消息队列被删除
调用msgrcv()函数的进程被信号中断
该函数可能返回以下错误代码error:
E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR
EIDRM:msqid标识符的消息队列已被删除
EACCESS:无权限读取消息队列
EFAULT:参数msgp指向无效的内存地址
ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读
EINTR:等待读取队列内的消息情况下被信号中断
msgctl()可以操作消息队列,如设置或者获取消息队列的相关属性。
#include
#include
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
/*
msqid:消息队列标识符
cmd:
IPC_STAT:获取该msg的信息,保存在结构体msqid_ds类型的buf中
IPC_SET:设置消息队列的属性,要设置的属性需先存储在结构体msqid_ds类型的buf中,可设置的属性包括msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes。
IPC_RMID:立即删除该msg,并且唤醒所有阻塞在该msg上的进程,同时忽略第三个参数
IPC_INFO:获得关于当前系统中msg的限制值信息
MSG_INFO:获得关于当前系统中msg的相关资源消耗信息
MSG_STAT:同IPC_STAT,但msgid为该消息队列在内核中记录所有消息队列信息的数组的下标,因此通过迭代所有的下标可以获得系统中所有消息队列的相关消息
返回值:
执行成功:0
执行失败:-1,并且错误原因存于error
*/
该函数可能返回以下错误代码error:
EACCESS:cmd参数为IPC_STAT,却无权限读取该消息队列
EFAULT:buf参数指向无效的内存地址
EIDRM:msqid标识符的消息队列已被删除
EINVAL:无效的参数msqid、cmd
EPERM:cmd参数为IPC_SET | IPC_RMID,却无权限执行
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 512
typedef struct message{
long msg_type;
char msg_text[BUFFER_SIZE];
}message;
int main(void)
{
int qid;
message msg;
if((qid = msgget((key_t)1234, IPC_CREAT|0666)) == -1){
perror("msgget\n");
exit(1);
}
printf("open queue %d\n", qid);
while(1){
printf("Enter some message to the queue:");
if((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL){
printf("get message end\n");
exit(1);
}
msg.msg_type = getpid();
if((msgsnd(qid, &msg, strlen(msg.msg_text), 0)) < 0){
perror("msgsnd");
exit(1);
}else{
printf("send message\n");
}
if(strncmp(msg.msg_text, "quit", 4) == 0){
printf("quit get message\n");
break;
}
}
exit(0);
}
include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 512
typedef struct message{
long msg_type;
char msg_text[BUFFER_SIZE];
}message;
int main(void)
{
int qid;
message msg;
if((qid = msgget((key_t)1234, IPC_CREAT|0666)) == -1){
perror("msgget\n");
exit(1);
}
printf("open queue %d\n", qid);
do{
memset(msg.msg_text, 0, BUFFER_SIZE);
if(msgrcv(qid, (void *)&msg, BUFFER_SIZE, 0, 0) < 0){
perror("msgrcv");
exit(1);
}
printf("the message from process %ld:%s", msg.msg_type, msg.msg_text);
}while(strncmp(msg.msg_text, "quit", 4));
if((msgctl(qid, IPC_RMID, NULL)) < 0){
perror("msgctl");
exit(1);
}else{
printf("Delete msg qid:%d\n", qid);
}
exit(0);
}
为两个工程,但都类似,照旧。