1. XSI(Unix System Interface and Headers):
代表了一种Unix系统的标准。
XSI IPC
,依托标识符和键来实现的,如同管道靠文件描述符来实现一样
有三种IPC
我们称作XSI IPC,即消息队列
、信号量
、共享内存
。
-
IPC标识
内核为每个进程间通信维护一个结构体形式的IPC对象。该对象可以通过一个非负整数的IPC标识来引用。 -
注意
:
与文件描述符不同,文件描述符总是找当前系统中可用的最小的数,而IPC标识是持续加一的。
1.1 IPC键值
IPC标识
是IPC对象在内核空间
中的唯一性标识ID;
IPC键值
是IPC对象在用户空间
中的唯一性标识ID。
1)无论何时,只要创建IPC对象,就必须指定一个键值;
2)键值的数据类型在sys/types.h头文件中被定义为key_t,其原始类型就是长整型long。
1.2 IPC对象的访问
IPC对象是全局对象
可用ipcs、ipcrm
等命令查看和删除
每个IPC对象都由get函数
创建
msgget、shmget、semget
调用get函数的时候必须指定关键字key
IPC对象的权限和所有者结构体
XSI IPC为每一个IPC结构设置了一个ipc_perm结构。
该结构规定了权限和所有者,至少包括下列成员:
#include <sys/ipc.h>
struct ipc_perm
{
uid_t uid; /*owner`s effective user id*/
gid_t gid; /*owner`s effective group id*/
uid_t cuid; /*creator`s effective user id*/
gid_t cgid; /*creator`s effective group id*/
mode_t mode; /*access modes*/
...
};
每种实现在其ipc_perm结构中会包括另外一些成员。
如果想要查看完整定义,参见<sys/ipc.h>
对于IPC来说,消息队列
就是内核中一个消息的链表
。
可以把消息看作是一个记录,具有特定的格式以及特定的优先级,对消息队列有写权限的进程
可以向消息队列中按照一定的规则添加消息
;对消息队列有读权限的进程
则可以从消息队列中读走消息
。
2 类型
POSIX消息队列
系统V(System V)消息队列
目前主要有两种类型
的消息队列:POSIX消息队列
以及系统V(System V)消息队列
。系统V消息队列
目前仍大量被使用
,考虑到程序的可移植性
,新开发的应用程序应尽量使用POSIX消息队列
。
3 消息
- 在进程间通信中,用户进程将数据传输到内核后,内核重新添加一些比如用户id、组id、读写进程的id和优先级等相关信息后打包成一个数据称为消息。
4 特性
- 允许
一个
或者多个进程
往消息队列中写消息和读消息
,但一个消息
只能被一个进程读取
,读取完毕后就会自动删除
;
消息队列具有一定的FIFO(First In First Out:先入先出)
的特性,消息可以按照顺序发送到队列
中,也可以按不同的方式从队列中读取
。每一个消息队列在内核中用一个唯一的IPC标识ID表示。
消息队列属性结构体
每一个消息队列都有一个msqid_ds结构体:
#include <sys/msg.h>
struct msqid_ds
{
struct ipc_perm msg_perm;
msgqnum_t msg_qnum; /* # of message on queue*/
msglen_t msg_qbytes; /* max # of bytes on queue*/
pid_t msg_lspid; /* pid of last msgsnd*/
pid_t msg_lrpid; /* pid of last msgrcv*/
time_t msg_stime; /* last-msgsnd() time*/
time_t msg_rtime; /* last-msgrcv() time*/
time_t msg_ctime; /* last-change time*/
...
};
5 接口
5.1 msgget函数原型
如果用msgget创建了一个新的消息队列对象时,则msqid_ds结构成员变量的值设置如下:
msg_qnum、msg_lspid、msg_lrpid、 msg_stime、msg_rtime设置为0。
msg_ctime
设置为当前时间。
msg_qbytes
设成系统的限制值。
msgflg
的读写权限写入msg_perm.mode中。
msg_perm
结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cuid成员被设置成当前进程的有效组ID。
5.2 msgctl函数原型
5.3 msgsnd函数原型
msgsnd()
为阻塞函数,当消息队列容量满
或消息个数满会阻塞
。消息队列已被删除,则返回EIDRM错误
;被信号中断返回E_INTR错误
。
如果设置IPC_NOWAIT
消息队列满
或个数满
时会返回-1,并且置EAGAIN
错误。
- msgsnd()解除阻塞的条件有以下三个条件:
① 不满足消息队列满或个数满两个条件,即消息队列中有容纳该消息的空间。
② msqid
代表的消息队列被删除。
③ 调用msgsnd
函数的进程被信号中断。
5.4 msgrcv函数原型
- msgrcv()解除阻塞的条件有以下三个:
① 消息队列中有了满足条件的消息。
② msqid代表的消息队列被删除。
③ 调用msgrcv()的进程被信号中断。
3.demo示例
msgctl.c源代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <error.h>
#define TEXT_SIZE 512
struct msgbuf
{
long mtype ;
char mtext[TEXT_SIZE] ;
} ;
int main(int argc, char **argv)
{
int msqid ;
struct msqid_ds info ;
struct msgbuf buf ;
struct msgbuf buf1 ;
int flag ;
int sendlength, recvlength ;
msqid = msgget( IPC_PRIVATE, 0666 ) ;
if ( msqid < 0 )
{
perror("get ipc_id error") ;
return -1 ;
}
buf.mtype = 1 ;
strcpy(buf.mtext, "happy new year!") ;
sendlength = sizeof(struct msgbuf) - sizeof(long) ;
flag = msgsnd( msqid, &buf, sendlength , 0 ) ;
if ( flag < 0 )
{
perror("send message error") ;
return -1 ;
}
buf.mtype = 3 ;
strcpy(buf.mtext, "good bye!") ;
sendlength = sizeof(struct msgbuf) - sizeof(long) ;
flag = msgsnd( msqid, &buf, sendlength , 0 ) ;
if ( flag < 0 )
{
perror("send message error") ;
return -1 ;
}
flag = msgctl( msqid, IPC_STAT, &info ) ;
if ( flag < 0 )
{
perror("get message status error") ;
return -1 ;
}
printf("uid:%d, gid = %d, cuid = %d, cgid= %d\n" ,
info.msg_perm.uid, info.msg_perm.gid, info.msg_perm.cuid, info.msg_perm.cgid ) ;
printf("read-write:%03o, cbytes = %lu, qnum = %lu, qbytes= %lu\n" ,
info.msg_perm.mode&0777, info.msg_cbytes, info.msg_qnum, info.msg_qbytes ) ;
system("ipcs -q") ;
recvlength = sizeof(struct msgbuf) - sizeof(long) ;
memset(&buf1, 0x00, sizeof(struct msgbuf)) ;
flag = msgrcv( msqid, &buf1, recvlength ,3,0 ) ;
if ( flag < 0 )
{
perror("recv message error") ;
return -1 ;
}
printf("type=%d, message=%s\n", buf1.mtype, buf1.mtext) ;
flag = msgctl( msqid, IPC_RMID,NULL) ;
if ( flag < 0 )
{
perror("rm message queue error") ;
return -1 ;
}
system("ipcs -q") ;
return 0 ;
}
编译 gcc msgctl.c –o msgctl。
执行 ./msg
,执行结果如下:
uid:1008, gid = 1003, cuid = 1008, cgid= 1003
read-write:666, cbytes = 1024, qnum = 2, qbytes= 163840
- ipc-msgque-send.cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
typedef struct
{
long msg_type; //消息类型
// 下面是自己需要传递的数据
int start; // 自定义消息
int end; // 自定义消息
}msg_t;
/*
* 往消息队列中发送消息
*/
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("Lack arguments %s\n", argv[0]);
exit(1);
}
key_t key = atoi(argv[1]);
printf("key: %d\n", key);
// 创建消息队列
int msq_id;
if ((msq_id = msgget(key, IPC_CREAT | IPC_EXCL | 0777/*读写权限*/)) < 0)
{
perror("msgget error");
}
printf("msq id: %d\n", msq_id);
// 定义要发送的消息
msg_t arr[5] = { /*{0, 0, 0},注意0号type不被插入*/ {1, 1, 100}, {2, 2, 200}, {3, 3, 300}, {4, 4, 400}, {5, 5, 500} };
// 发送消息到消息队列
for (int i = 0; i < 5; ++i)
{
if (msgsnd(msq_id, (const void *)&arr[i], sizeof(msg_t) - sizeof(long), IPC_NOWAIT) < 0)
{
printf("msgsnd error i:%d\n", i);
}
}
// 发送后去获得消息队列中消息的总数
struct msqid_ds ds;
if (msgctl(msq_id, IPC_STAT, &ds) < 0)
{
perror("msgctl error");
}
/* msg_qnum:number of messages currently on queue */
printf("total message: %ld\n", ds.msg_qnum);
exit(0);
}
- ipc-msgque-recv.cpp
#include <sys/msg.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
long msg_type; //消息类型
// 下面是自己需要传递的数据
int start; // 自定义消息
int end; // 自定义消息
}msg_t;
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("Lack arguments: %s\n", argv[0]);
exit(1);
}
key_t key = atoi(argv[1]);
long type = atoi(argv[2]);
// 获得指定的消息队列
int msq_id;
if ((msq_id = msgget(key, 0777)) < 0)
{
perror("msgget error");
exit(1);
}
printf("msg id: %d\n", msq_id);
msg_t m;
if ((msgrcv(msq_id, &m, sizeof(msg_t) - sizeof(long), type, IPC_NOWAIT)) < 0)
{
perror("msgrcv error");
exit(1);
}
else
{
printf("type: %ld start: %d end: %d\n", m.msg_type, m.start, m.end);
}
exit(0);
}