Linux进程间通信(IPC)-消息队列(转)
转:http://blog.sina.com.cn/s/blog_7f98bac10100s91d.html
进程间通信——消息队列
进程间通信——消息队列
System V IPC包括三种进程通信的方式:消息队列、信号量、共享内存,是一种较老的方式。
管道和FIFO是基于文件系统的,而System V IPC是基于系统内核的,用ipcs可以查看系统当前的IPC对象的状态。
消息队列是一个存放在内核中的消息链表,由消息队列标识符标识。它克服了信号传递信息量少、管道所传送的是无格式字节流以及缓冲区大小受到限制的缺点。由于消息队列存放在内核中,所以只有在内核重启(OS重启)的时候或者显式的删除一个消息队列时,它才能够真正的被删除。
消息队列与管道的区别:最主要的区别是管道通信是要求两个进程之间要有亲缘关系,
而消息队列当中,通信的两个进程之间可以是完全无关的进程。
操作消息队列时,用到的重要的数据结构:
(1)消息缓冲结构(定义在linux/msg.h中)
struct messagebuf{
long messageType; //消息类型,正整数
char messageText; //消息内容,可以为任意类型,由用户自定义
}消息的大小是受限制的,由<linux/msg.h>中的MSGMAX限制;
(2)msqid_ds 此结构体保存消息队列当前的状态信息;
(3)ipc_perm 保存消息队列的重要信息(包括键值、用户id、组id等)。
操作消息队列的一些函数:
(1)key_t ftok(const char *pathname,int proj_id); //获取键值(每个消息队列在系统范围内对应唯一的键值)proj_id 的取值范围是1-255。
(2)int msgget(key_t key,int msgflg); //创建消息队列
key 调用ftok获得,msgflg 可能的取值为IPC_CREAT,IPC_EXCL
(3)int msgsnd(int msgid,struct msgbuf *msgbuf,size_t msgsize,int msgflag); //发送
msgid 消息队列标识符 msgbuf 指向要发送的消息
msgsize 消息的大小 msgflag 操作标志位
(4)int msgrcv(int msgid,struct msgbuf *msgbuf,size_t msgsize,long int msgtype,int msgflag);//接收 接收消息时需要一个同类型的消息缓冲结构、要接收消息队列的标识符、要接收消息的类型
msgtype用来指定要接收的消息,分三种情况:
等于 0 返回消息队列中的第一个消息
大于0 返回消息队列中类型为msgtype的第一个消息
小于0 返回消息队列中类型值小于等于msgtype绝对值的消息中类型值最小的第一条消息
(5)获取和设置消息队列的属性
int msgctl(int msgid,int cmd,struct msqid_ds *buf);
cmd 有三种操作:IPC_STAT 获取消息队列当前的状态信息,保存到buf指向的空间
IPC_SET 设置消息队列的属性
IPC_RMID 从内核中删除msgid标识的消息队列
练习:
1.利用read/write 访问生成了具有读/写权限的5 个消息队列,并用ipcs 命令(通过一个管道)显示消息队列的状态,然后删除该消息队列。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define BUF_SIZE 1024
#define PATH_NAME "."
#define PROJ_ID 32
#define MAX_SIZE 5120
int main(void)
{
struct
mymsgbuf
{
long
msgtype;
char
message[BUF_SIZE];
}msgbuf;
int
queueid,i;
int
msglen;
key_t
key;
FILE
*fp;
char
string[BUF_SIZE];
//获取键值
if((key =
ftok(PATH_NAME,PROJ_ID)) == -1)
{
perror("ftok
error!\n");
exit(1);
}
//创建消息队列或者获取消息队列标识符
if((queueid
= msgget(key,IPC_CREAT|0660)) == -1)
{
perror("msgget error!\n");
exit(1);
}
//发送5条消息到消息队列
for(i = 0; i
< 5; i++)
{
msgbuf.msgtype = i+1;
strcpy(msgbuf.message,"Goodnight,see you later!");
msglen =
sizeof(struct mymsgbuf) - 4;
if(msgsnd(queueid,&msgbuf,msglen,0) == -1)
{
perror("msgsnd error!\n");
exit(1);
}
}
//调用popen函数,开启了一个进程
if((fp =
popen("ipcs","r"))== NULL)
{
perror("popen error!\n");
exit(1);
}
//调用read读出管道中的数据
if(read(fileno(fp),string,MAX_SIZE) == -1)
{
perror("read
error!\n");
exit(1);
}
else
printf("%s\n",string);
//读取消息队列
for(i = 1; i
< 6; i++ )
{
if((msgrcv(queueid,&msgbuf,BUF_SIZE,i,0)) ==
-1)
{
perror("msgrcv error!\n");
exit(1);
}else
printf("Get
message :%s,message type is
%ld\n",msgbuf.message,msgbuf.msgtype);
}
//删除消息队列
if((msgctl(queueid,IPC_RMID,NULL)) == -1)
{
perror("delete msgctl failed!\n");
exit(1);
}
//处理popen开启的进程
pclose(fp);
}
2.通过消息队列实现两个进程之间的通信。
题目描述:编写两个程序,其中,一个进程从消息队列接收消息,另外一个进程发送消息。
编写两个程序,分别对应”send.c”和”receive.c”,其中”send.c”是发送消息,而”receive.c”是从消息队列当中接收消息。每一条消息是用户输入的一个任意字符串,字符串”end”表示输入结束。消息队列的关键字可以设为1234。
sen.c
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/stat.h>
#define BUF_SIZE 256
#define PROJ_ID 32
#define PATH_NAME "/tmp"
#define SEND_MSG 1
#define RECEIVE_MSG 2
int main(void)
{
struct
mymsgbuf
{
long
msgtype;
char
message[BUF_SIZE];
}msgbuf;
int
queueid;
int
msglen;
if((queueid
= msgget(1234,IPC_CREAT|0660)) == -1)
{
perror("msgget error!\n");
exit(1);
}
while(1)
{
printf("send:");
fgets(msgbuf.message,BUF_SIZE,stdin);//从标准输入读取字符存至message
if(strncmp("end",msgbuf.message,3) == 0)
{
msgctl(queueid,IPC_RMID,NULL);
break;
}
msgbuf.message[strlen(msgbuf.message)-1] = '\0';
msgbuf.msgtype = SEND_MSG;
if(msgsnd(queueid,&msgbuf,strlen(msgbuf.message)+1,0)
== -1)
{
perror("Send
msgsnd error!\n");
exit(1);
}
if(msgrcv(queueid,&msgbuf,BUF_SIZE,RECEIVE_MSG,0)
== -1) //缓冲区的大小,请求读取消息的类型
{
perror("Send
msgrcv error\n");
exit(1);
}
printf("receive: %s\n",msgbuf.message);
}
exit(0);
}
receive.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/stat.h>
#define BUF_SIZE 256
#define PROJ_ID 32
#define PATH_NAME "/tmp"
#define SEND_MSG 1
#define RECEIVE_MSG 2
int main(void)
{
struct
mymsgbuf
{
long
msgtype;
char
message[BUF_SIZE];
}msgbuf;
int
queueid;
int
msglen;
if((queueid
= msgget(1234,IPC_CREAT|0660)) == -1)
{
perror("msgget eror!\n");
exit(1);
}
while(1)
{
if(msgrcv(queueid,&msgbuf,BUF_SIZE,SEND_MSG,0) ==
-1)
{
perror("Send
msgrcv error!\n");
exit(1);
}
printf("send:%s\n",msgbuf.message);
printf("receive:");
fgets(msgbuf.message,BUF_SIZE,stdin);
if(strncmp("end",msgbuf.message,3) == 0)
{
msgctl(queueid,IPC_RMID,NULL);
break;
}
msgbuf.message[strlen(msgbuf.message)-1]='\0';
msgbuf.msgtype = RECEIVE_MSG;
if(msgsnd(queueid,&msgbuf,strlen(msgbuf.message)+1,0)
== -1)
{
perror("reveive msgsnd error!\n");
exit(1);
}
}
exit(0);
}
这两个程序基本和linux c实战上的server.c 和client.c一样,send和receive可任意发送消息,当任意一方输入end时结束会话。至于从队列中,按照正常的顺序、阻塞方式反复接收消息不太明白这几种方式是怎么实现的。
popen
进程I/O函数,与pclose函数一起使用,头文件#include <stdio.h>
函数原型:
FILE *
popen ( const char * command , const char *
type );
int pclose ( FILE * stream );
说明:
popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell
以运行命令来开启一个进程。这个进程必须由 pclose() 函数关闭,而不是 fclose()
函数。pclose() 函数关闭标准 I/O 流,等待命令执行结束,然后返回 shell 的终止状态。如果 shell 不能被执行,则
pclose() 返回的终止状态与 shell 已执行 exit 一样。
type 参数只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type
相应的只读或只写类型。如果 type 是 "r" 则文件指针连接到 command 的标准输出;如果 type 是
"w" 则文件指针连接到 command 的标准入。
command
参数是一个指向以 NULL 结束的 shell 命令字符串的指针。 这行命令将被传到 bin/sh
并使用-c 标志,shell 将执行这个命令。
popen 的返回值是个标准 I/O 流,必须由 pclose 来终止。
前面提到这个流是单向的。所以向这个流写内容相当于写入该命令的标准输入;命令的标准输出和调用 popen
的进程相同。与之相反的,从流中读数据相当于读取命令的标准输出;命令的标准输入和调用 popen 的进程相同。
返回值: 如果调用 fork() 或 pipe() 失败,或者不能分配内存将返回NULL,否则返回标准 I/O 流。
测试执行完popen后有几个进程:
#include<stdio.h>
int main(void)
{
FILE
*fp;
fp =
popen("ipcs","r");
system("ps");
pclose(fp);
system("ps");
}
运行结果:
PID
TTY
TIME CMD
4451
pts/0
00:00:00 bash
4545
pts/0
00:00:00 a
4546
pts/0
00:00:00 sh
<defunct>
//僵尸进程
4547
pts/0
00:00:00 sh
4549
pts/0
00:00:00 ps
PID
TTY
TIME CMD
4451
pts/0
00:00:00 bash
4545
pts/0
00:00:00 a
4550
pts/0
00:00:00 sh
4551
pts/0
00:00:00 ps

浙公网安备 33010602011771号