一、XSL IPC
1、key值和ID值
Linux系统为每个IPC机制都分配了唯一一个ID,所有针对IPC机制的操作都使用对应的ID。为了通信的双方获得ID值,IPC在实现时约定使用key值作为参数创建,如果在创建时使用相同的key值将得到同一个IPC对象的ID,这样就保证了双方可以获取用于传递参数的IPC机制ID值。key值为一个32位的整型数据。
为了尽可能的与系统信息的载体(文件)关联,Linux提供函数ftok()来创建key值,在此次函数的参数中,需要特定文件做为参数。
#include <sys/ipc.h>
key_t ftok(const char *path, int id);
//返回值:若成功,返回值;若出错,返回(key_t)-1
此函数有两个参数,path为文件路径名,可以是特殊文件(例如目录文件),也可以是当前目录”. “,通常设置此参数为当前目录,因为当前目录一般都存在,且不会被立即删除。第二个参数为一个int型变量。当产生键时,只使用id参数的低8位。
2、拥有者及权限
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 */
g i d_t cgid ; /* creator's effective group id */
modet mode; /* access modes */
ulong seq ; /* slot usage sequence number */
key_t key; /* key */
}
二、消息队列
1、消息队列IPC原理
消息队列是消息的链式队列,整个消息队列有两种类型的数据结构:
(1)msqid_ds消息队列数据结构:标识整个消息队列的基本情况,主要包括整个消息队列的权限,包括拥有者和操作权限等信息,另外还包括两个重要的指针分别指向消息队列中的第一个消息和最后一个消息。
(2)msg消息队列数据结构:整个消息队列的主体,一个消息队列有若干个消息,每个消息数据结构的基本属性包括消息类型、消息大小、消息内容指针和下一个消息数据结构位置。
2、消息队列的管理
(1)msgget
#include <sys/msg.h>
int msgget(key_t key, int flag) ;
//返回:若成功则为消息队列ID,若出错则为- 1
第一个参数key为由ftok创建的key值。
第二个参数 flag的低位用来确定消息队列的访问权限,如0770为文件的访问权限类型。此外,还可以附加以下参数值。这些值可以与基本权限以或的方式一起使用。
(2)msgctl
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid *_dsbuf) ;
//返回:若成功则为0,出错则为- 1
第一个参数 msqid为消息队列标识符,该值为使用msgget函数创建消息队列的返回值。
第二个参数cmd为执行的控制命令,即要执行的操作。包括以下选项:
• IPC_STAT 取此队列的msqid_ds结构,并将其存放在buf指向的结构中。
• IPC_SET 按由buf指向的结构中的值,设置与此队列相关的结构中的下列四个字段:msg_permuid、msg_perm . gid、msg_perm. mode和msg_qbytes。此命令只能由下列两种进程执行:一种是其有效用户I D等于msg_perm.cuid或msg_perm.uid ;另一种是具有超级用户特权的进程。只有超级用户才能增加msgqbytes的值
• IPC_RMID 从系统中删除该消息队列以及仍在该队列上的所有数据。这种删除立即生效。仍在使用这一消息队列的其他进程在它们下一次试图对此队列进行操作时,将出错返回E I D R M。此命令只能由下列两种进程执行:一种是其有效用户ID等于msg_perm.cuid或msgperm.uid ;另一种是具有超级用户特权的进程。
这三条命令(IPCSTAT、IPCSET和IPCRMID)也可用于信号量和共享存储。
(3)msgsnd
#include <sys/msg.h>
int msgsnd(int msqid, const void *ptr, size_tn bytes, int flag) ;
//返回:若成功则为0,若出错则为- 1
msgsnd()函数用于将新的消息添加到消息队列尾端,每个消息中包含一个正长整型的字段,一个非负长度及一个实际数据字节(对应于长度)。
第一个参数msqid为指定的消息对垒标识符,将消息添加到那个消息队列中。
第二个参数ptr为指向mymesg结构的指针。
struct mymesg {
long mtype; /* positive message type */
char mtext[512]; /* message data,of length nbytes * /
} ;
mtype是一个正整数,由产生消息的进程生成,用于表示消息的类型,因此,接收进程可以用来进行消息选择(消息队列在存储信息时时按发送的先后顺序放置的)。
mtext是文本内容,即消息内容。
第三个参数为接收信息的大小,其数据类型为size_t,即unsigned int 类型。其大小为0到系统对消息队列的限制值。
第四个参数用来指定在达到系统为消息队列所定的界限(如达到字数限制)时应采取的操作。
(1)如果设置为IPC_NOWAIT,如果需要等待,则不发送消息并且调用进程立即返回错误信息EAGAIN。
(2)如果设置为0,则调用进程挂起执行,知道达到系统所规定的最大值为止,并发送消息。
成功调用后,此函数将返回0,否则返回-1,同时将对消息队列msqid数据结构的成员执行虾类操作:
(1)msg_qnum以1为增量递增。
(2)msg_lspid设置为调用进程的进程ID。
(3)msg_stime设置为当前时间
(4)msgrcv
#include <sys/msg.h>
int msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag) ;
//返回:若成功则为消息数据部分的长度,若出错则为- 1
此函数从msqid指定的消息队列标识符相关联的队列中读取信息,将其放置到由ptr指向的结构中。
第一个参数为读的对象,即从那个消息队列中获得消息。
第二个参数为一个临时消息数据结构,用来保存读取的信息。
第三个参数nbytes用于指定数据缓冲区的长度。若返回的消息成都大于nbytes,而且在flag中设置了MSG_NOERROR位,则该消息会被截断,并且不向调用进程提供截断的提示。
第四个参数type可以指定请求的消息类型:
• type == 0 返回队列中的第一个消息。
• type > 0 返回队列中消息类型为type的第一个消息。
• type < 0 返回队列中消息类型值小于或等于type绝对值,而且在这种消息中,其类型值又最小的消息。
非0 type用于以非先进先出次序读消息。例如,若应用程序对消息赋优先权,那么type就可以是优先权值。如果一个消息队列由多个客户机和一个服务器使用,那么type字段可以用来包含客户机进程ID。
第五个参数flag用于指定所需类型消息不在队列上时要采取的操作。
(1)如果设置flag值为IPC_NOWAIT,使操作不阻塞。这使得如果没有所指定类型的消息,则msgrcv出错返回ENOMSG。
(2)如果没有指定IPC_NOWAIT,则进程阻塞,挂起执行直至:
(a)有了指定类型的消息
(b)从系统中删除了此队列(出错返回EIDRM),并返回-1
(c)捕捉到一个信号并从信号处理程序返回(出错返回EINTR)。
接收消息成功完成后,该消息将自动从消息队列中删除,并返回接收到消息大小,并将对整个消息队列msqid数据结构的成员执行虾类操作:
(1)msg_qnum以1为增量递减。
(2)msg_lspid设置为调用进程的进程ID。
(3)msg_stime设置为当前时间
三、消息队列应用实例--使用消息队列实现实时通信
此实例是一个简单的使用消息队列进行实时聊天的本机通信程序,发送端每发送一个消息,会立即被接受读取,在没有消息在消息队列中时,将处于阻塞状态。
下面是发送端源代码分析:
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st //临时消息结构
{
int my_msg_type;
char msg_text[MAX_TEXT]; //自己修改了存储消息的空间大小
};
int main(int argc, char *argv[])
{
int running = 1;
struct my_msg_st some_data;
int msgid;
char buffer[MAX_TEXT];
if ((msgid = msgget((key_t)12345, 0666|IPC_CREAT)) == -1) //创建消息队列
{
perror("msgget");
exit(EXIT_FAILURE);
}
while(running)
{
printf("Enter the message to send:");
fgets(buffer, BUFSIZ, stdin); //读取信息
some_data.my_msg_type = 1; //设置类型为1
strcpy(some_data.msg_text, buffer); //复制消息
if((msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0)) == -1) //发送消息
{
perror("msgsnd");
exit(EXIT_FAILURE);
}
if(strncmp(buffer, "end", 3) == 0) //判断是否为结束消息
{
running = 0;
}
}
return 0;
}
下面是接送端源代码分析:
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
struct my_msg_st
{
int my_meg_type;
char msg_text[BUFSIZ];
};
int main(int argc, char* argv[])
{
int running = 1;
int msgid;
struct my_msg_st some_data;
int msg_to_receive = 0; //类型设置为0, 可以接收任何消息类型的消息
if((msgid = msgget((key_t)12345, 0666|IPC_CREAT)) == -1) //消息队列标识要一致
{
perror("msgget");
exit(EXIT_FAILURE);
}
while(running)
{
if(msgrcv(msgid, (void*)&some_data, BUFSIZ, msg_to_receive, 0) == -1) //接收消息
{
perror("msgrcv");
exit(EXIT_FAILURE);
}
printf("receiver mssage: %s", some_data.msg_text);
if(strncmp(some_data.msg_text, "end", 3) == 0)
running = 0;
}
if(msgctl(msgid, IPC_RMID, 0) == -1) //删除消息队列
{
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
return 0;
}