什么是IPC对象?
在Linux下,IPC对象是指消息队列、共享内存、信号量,如果用户想要利用IPC对象进行进程间通信的话,首先需要为IPC对象申请对应的资源,即该对象的key值和id,key值可以理解为文件操作里面的文件路径名,而id号可以理解为文件操作里面的文件描述符。
Linux系统下的IPC资源
查看IPC资源: ipcs -a
删除IPC资源
删除消息队列:ipcrm -q key值或id值
删除共享内存:ipcrm -m key值或id值
删除信号量:ipcrm -s key值或id值
使用IPC对象,使用ftok申请key值
key_t ftok(const char *pathname, int proj_id);
函数功能:
为IPC对象获取一个key值
参数:
pathname----->一个合法的路径,常用"."当前路径
proj_id----->非0常数,常用10
返回值:
成功返回key值;失败返回-1
注意:
当使用ftok来申请key值时,如果pathname和proj_id值和上一次的使用ftok时的值都相同时,两次获得的key值都相同;只要有pathname或者proj_id有一个不同时,获得的key值就不同。
IPC-消息队列
- 管道通信和消息队列之间的区别?
管道通信:不能读取指定的数据,只要管道中有数据,就一定要读出来,操作函数是open/read/write;
消息队列:消息队列是带有特殊标识的特殊管道,可以读取指定的数据,不符合类型就不读取,操作函数是msgget/msgsnd/msgrcv;
- 消息队列机制?
进程1往消息队列里发送消息:“类型编号”+“数据正文”
进程2从消息队列里读取消息,只用提供相应的数据编号就可以读取到指定的数据。
作用范围:Linux下任意两个进程
- 消息格式?
strcut msgbuf
{
long type; //消息的编号
char buf[n]; //消息的正文部分,大小n自行设置
}
消息队列操作函数
int msgget(key_t key, int msgflg);
函数功能:
得到消息队列的id号
函数参数:
key----->消息队列的key值
msgflg----->IPC | 0666 (如果不存在就创建,并给读写权限,Linux下IPC资源的访问权限一般为读和写,没有执行权限)
返回值:成功返回消息队列的id号;失败返回-1。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数功能:
往消息队列里写入数据
参数:
msqid----->消息队列id号
msgp----->要写入的消息的地址
msgsz----->消息的数据正文的大小(不是整个消息结构体的大小)
msgflg----->一般属性,默认为0
返回值:成功返回0;失败返回-1。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
函数功能:
在消息队列里读取指定的消息
参数:
msqid----->消息队列的id值
msgp----->接受消息的指针
msgsz----->接受消息的字节大小
msgtyp----->消息队列里指定的消息编号
msgflg----->一般属性,默认为0
返回值:成功返回读取到的字节数;失败返回-1。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函数功能:
操作消息队列的状态
参数:
msgid----->消息队列的id值
cmd----->IPC_STAT(获取消息队列的状态,获取到第三个参数里)或 IPC_RMID(删除消息队列,第三个参数设置为NULL)
buf----->将获取到的消息队列的状态存储在buf里
返回值:成功返回0;失败返回-1。
例子:进程2可以不断地发送数据给进程1,进程1将数据打印出来。
//进程2:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
//定义一个记录消息队列里的消息的结构体
struct msg
{
long type;
char buf[1024];
};
int main(int argc, char const *argv[])
{
key_t msgq_key; //ipc资源键值
msgq_key = ftok(".", 10); //获取键值
printf("key = %x\n", msgq_key);
int msgq_id = msgget(msgq_key, IPC_CREAT|0666); //创建消息队列并获得ipc资源id
if(-1 == msgq_id) //获取id失败返回-1
{
perror("msgget");
exit(1);
}
else
printf("msdid=%d\n", msgq_id);
struct msg msg_buf; //消息的结构体
bzero(&msg_buf, sizeof(msg_buf));
msg_buf.type = 2; //消息类型
strcpy(msg_buf.buf, "Are you happy?"); //消息内容
//向消息队列一直写入信息
while(1)
{
scanf("%[^\n]", msg_buf.buf); //读取用户写入到消息队列里的数据
while(getchar() != '\n'); //清空缓冲区
//向这个消息队列里写入数据
if(msgsnd(msgq_id, &msg_buf, strlen(msg_buf.buf), 0) == -1)
{
printf("msgsnd error");
exit(2);
}
memset(msg_buf.buf, 0, sizeof(msg_buf)); //清空消息buf
}
return 0;
}
//进程1:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
//定义一个记录消息队列里的消息的结构体
struct msg
{
long type;
char buf[1024];
};
int main(int argc, char const *argv[])
{
key_t msgq_key; //ipc资源键值
msgq_key = ftok(".", 10); //获取键值
printf("key = %x\n", msgq_key);
int msgq_id = msgget(msgq_key, IPC_CREAT|0666); //创建消息队列并获得ipc资源id
if(-1 == msgq_id) //获取id失败返回-1
{
perror("msgget");
exit(1);
}
else
printf("msdid=%d\n", msgq_id);
struct msg msg_buf; //消息的结构体
bzero(&msg_buf, sizeof(msg_buf));
int ret = 0; //ret代表从消息队列里读取到的字节数
while(1)
{
//向这个消息队列里读出数据
ret = msgrcv(msgq_id, &msg_buf, sizeof(msg_buf.buf), 2, 0);
if(ret > 0)
{
printf("from msq:%s\n", msg_buf.buf);
}
memset(msg_buf.buf, 0, sizeof(msg_buf.buf));
}
return 0;
}
结果分析:进程2将消息消息的编号设置为了2,进程一通过msgsrc接收消息队列里的消息编号为2的消息。
利用ipcs -a查看ipc资源:
把进程1终止,在进程2里继续发送2条消息,查看到消息队列里的消息数量更新到了2,说明进程1终止后,进程2发送的消息还留在消息队列里: