目录
1、system V提供了三种IPC(interprocess communication)进程间通信方式 消息队列 共享内存 信号量集
2、该通信机制是独立于进程而存在的:当进程将数据写入该通信对象中后,即使进程已经结束,对象中保存的内容仍然存在
2、消息队列的相关API函数接口 ftok msgget msgsnd msgrcv msgctl
2、共享内存的PAI函数接口 shmget shmat shmdt
一、IPC通信机制
1、system V提供了三种IPC(interprocess communication)进程间通信方式 消息队列 共享内存 信号量集
消息队列:本质上是在内核空间维护的一个队列,发送者可以将消息先放入到该队列上,接收者根据自己的需求读取队列上的信息
共享内存:在进程外申请一个物理内存,不同进程可以通过内存映射的方式共同使用这段内存,类似于多线程中的临界资源,同样存在竟态,通过使用信号量解决
信号量集:将多个无名信号量,放入一个集合中,分别控制不同的进程,用于进程间同步问题
2、该通信机制是独立于进程而存在的:当进程将数据写入该通信对象中后,即使进程已经结束,对象中保存的内容仍然存在
3、IPC对象相关指令 ipcs ipcrm
1)ipcs 查看当前所有的ipc对象的信息
2)ipcs -q 查看消息队列的信息
3)ipcs -m 查看共享内存的信息
4)ipcs -s 查看信号量集的信息
5)ipcrm -q\-m\-s ID号 删除指定的ipc对象
二、消息队列
1、消息队列的特点
1)放入消息队列中的消息需要进行封装,包括消息类型和消息数据
2)消息队列的消息遵循先进先出原则,如果取出时不指定类型,则默认取第一个,如果指定了类型,则取该类型第一个放入队列中的消息
3)消息队列相关API函数接口
4)消息队列的大小为 16k
2、消息队列的相关API函数接口 ftok msgget msgsnd msgrcv msgctl
1)创建用于生成消息队列的钥匙
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
功能:通过给定的文件路径和一个随机ID值创建出一个用于IPC通信的key值 ftok("/", 'k');
参数1:文件路径,该文件的inode号占key值的2字节,该文件的设备号占key值的1字节
参数2:一个给定的随机值,该值占key值的1字节
返回值:成功返回创建出的key值,失败返回-1并置位错误码2)通过钥匙创建出一个消息队列对象
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflg);
功能:通过给定的key值创建一个消息队列
参数1:用于创建消息队列的key值,该值可以由ftok创建出来,也可以是 IPC_PRIVATE,表示进行亲缘进程间的通信
参数2:创建标识位
IPC_CREAT:表示本次操作要创建一个消息队列,如果该key值对应的消息队列已经存在,则直接打开该消息对象
IPC_EXCL:表示本次确保要创建一个新的消息队列,如果该消息队列已经存在,则该函数报错,错误码为EEXIST
创建文件的权限,也在该参数中,使用位或连接
返回值:成功返回消息队列的id号,失败返回-1并置位错误码3)向消息队列中存放消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列中存放消息,要求当前进程对消息队列具有可写权限
参数1:消息队列的id号
参数2:是指向封装好的消息的起始地址,通常类型如下,但是需要用户自己定义
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
参数3:表示参数2中,消息正文的大小,不包含消息类型的大小
参数4:发送标识位,表示是否阻塞
0:表示阻塞
IPC_NOWAIT:表示非阻塞
返回值:成功返回0,失败返回-1并置位错误码4)从消息队列中取消息
size_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从消息队列中取消息
参数1:消息队列的id号
参数2:存放消息的容器起始地址
参数3:消息正文的大小
参数4:取出消息的类型
>0: 表示取出该类型的消息的第一个消息
=0:不限制类型,直接取消息队列中的第一个
<0: 取出消息队列中类型小于msgtyp绝对值的第一个
例如: 50 10 2 10 6 6 8
-7: 2 6 6 ---> 2
参数5:是否阻塞
0:表示阻塞
IPC_NOWAIT:表示非阻塞
返回值:成功返回成功读取的字节个数,失败返回-1并置位错误码5)控制消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:完成对消息队列指定的cmd操作
参数1:消息队列的id号
参数2:操作指令
IPC_STAT:获取消息队列的属性,此时参数3必须要给定,表示接收消息队列的属性
struct msqid_ds {
struct ipc_perm msg_perm; /* 拥有者和权限 */
time_t msg_stime; /* 最新一次向消息队列中发送数据的时间 */
time_t msg_rtime; /* 最新一次消息队列接受数据的时间 */
time_t msg_ctime; /* 最新一次操作消息队列的时间 */
unsigned long __msg_cbytes; /* 当前队列中已用的字节数 */
msgqnum_t msg_qnum; /* 当前队列中消息的个数*/
msglen_t msg_qbytes; /* 队列的最大容量,默认是16K */
pid_t msg_lspid; /* 最后一次向消息队列中发送消息的进程id号 */
pid_t msg_lrpid; /* 最后一次从消息队列中取消息的进程id号 */
};
对第一个成员的介绍
struct ipc_perm {
key_t __key; /* 键值 */
uid_t uid; /* 当前拥有者的用户id号 */
gid_t gid; /*当前拥有者的组id号 */
uid_t cuid; /* 创建消息队列的进程的用户id */
gid_t cgid; /* 创建消息队列进程的组id号 */
unsigned short mode; /* 操作权限 */
unsigned short __seq; /* 队列号 */
};
IPC_SET:设置消息队列的属性
IPC_RMID:删除消息队列,当参数2位该值时,参数3可以忽略,直接填NULL即可
返回值:成功返回0,失败返回-1并置位错误码
3、发送端实现
#include<myhead.h>
//要发送的消息类型
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[1024]; /* message data */
};
#define SIZE sizeof(struct msgbuf)-sizeof(long)
int main(int argc, const char *argv[])
{
//1、创建key值,用于生产消息队列
key_t key = ftok("/", 'k');
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %#x\n", key);
//2、通过key值创建一个消息队列
int msqid = msgget(key, IPC_CREAT|0664);
if(msqid == -1)
{
perror("msgget error");
return -1;
}
printf("msqid = %d\n", msqid); //id号
//向消息队列中存放消息
struct msgbuf buf;
while(1)
{
printf("请输入消息类型:");
scanf("%ld", &buf.mtype);
getchar(); //吸收回车
printf("请输入消息正文:");
fgets(buf.mtext, SIZE, stdin); //从终端获取数据
buf.mtext[strlen(buf.mtext)-1] = 0; //将换行换成 '\0'
//将消息发送到消息队列中
msgsnd(msqid, &buf, SIZE, 0);
//参数1:消息队列id号
//参数2:消息的起始地址
//参数3:消息正文大小
//参数4:阻塞形式发送数据
printf("发送成功\n");
if(strcmp(buf.mtext, "quit") == 0)
{
break;
}
}
return 0;
}
4、接收端实现
#include<myhead.h>
//要发送的消息类型
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[1024]; /* message data */
};
#define SIZE sizeof(struct msgbuf)-sizeof(long)
int main(int argc, const char *argv[])
{
//1、创建key值,用于生产消息队列
key_t key = ftok("/", 'k');
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %#x\n", key);
//2、通过key值创建一个消息队列
int msqid = msgget(key, IPC_CREAT|0664);
if(msqid == -1)
{
perror("msgget error");
return -1;
}
printf("msqid = %d\n", msqid); //id号
//从消息队列中读取消息
struct msgbuf buf;
while(1)
{
msgrcv(msqid, &buf, SIZE, 0, 0);
//参数1:消息队列id号
//参数2:数据容器起始地址
//参数3:数据的正文大小
//参数4:消息类型,0表示任意类型
//参数5:表示阻塞读取消息
printf("收到消息为:%s\n", buf.mtext);
}
return 0;
}
5、 获取消息队列的属性
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建key值,用于生产消息队列
key_t key = ftok("/", 'k');
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %#x\n", key);
//2、通过key值创建一个消息队列
int msqid = msgget(key, IPC_CREAT|0664);
if(msqid == -1)
{
perror("msgget error");
return -1;
}
printf("msqid = %d\n", msqid); //id号
//获取该消息队列的属性
struct msqid_ds ds; //用于存放消息队列属性的变量
if(msgctl(msqid, IPC_STAT, &ds) == -1)
{
perror("msgctl error");
return -1;
}
//程序执行至此,该消息队列中的相关信息就被存入到ds结构体中了
printf("键:%#x, msqid:%d, 权限:%#o, 已用字节:%ld, 消息:%ld\n", \
ds.msg_perm.__key, msqid, ds.msg_perm.mode, ds.__msg_cbytes,\
ds.msg_qnum);
return 0;
}
三、共享内存
1、特点
1)共享内存是多个进程共享一个外部的物理内存,直接访问内存获取数据,避免了数据的传输,使得效率较高
2)共享内存具有时效性,存放到共享内存区域中的数据,如果不及时读取,下一次写入后,前面的数据会被覆盖
3)共享内存的操作不是一次性的,写入到共享内存中的数据,即使读取出去后,依然存在于共享内存,直到下一次写入
2、共享内存的PAI函数接口 shmget shmat shmdt
1)创建共享内存对象
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
功能:通过给定的key值创建一个共享内存的对象,并返回该对象的id
参数1:key值,可以是IPC_PRIVATE也可以是由ftok创建出来的
参数2:申请的共享内存段的大小,必须是PAGE_SIZE的整数倍,如果超过,则向上取整
参数3:创建的标识
IPC_CREAT:表示本次操作要创建一个共享内存,如果该key值对应的共享内存已经存在,则直接打开该共享内存对象
IPC_EXCL:表示本次确保要创建一个新的共享内存,如果该共享内存已经存在,则该函数报错,错误码为EEXIST
创建文件的权限,也在该参数中,使用位或连接
返回值:成功返回创建出来的共享内存的id,失败返回-1并置位错误码
2)将共享内存地址映射到用户空间
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存的地址到用户空间
参数1:共享内存ID
参数2:对齐页地址,一般填NULL,让系统选择合适的对齐页
参数3:共享内存的权限
SHM_RDONLY:只读权限
0: 读写权限
返回值: 成功返回映射的共享内存段的地址,失败返回(void *)-1并置位错误码
3)取消共享内存的映射
int shmdt(const void *shmaddr);
功能:取消共享内存的映射
参数:共享内存映射的地址
返回值:成功返回0,失败返回-1并置位错误码
4)共享内存的控制函数
#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:控制共享内存对象
参数1:共享内存的id号
参数2:操作指令
IPC_STAT:获取共享内存的属性,此时参数3必须要给定,表示接收共享内存的属性
IPC_SET:设置共享内存的属性
IPC_RMID:删除共享内存,当参数2位该值时,参数3可以忽略,直接填NULL即可
返回值: 成功返回0,失败返回-1并置位错误码
3、发送端实现
#include<myhead.h>
#include<sys/user.h>
int main(int argc, const char *argv[])
{
//1、创建key值用于创建共享内存段
key_t key = ftok("/", 't');
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %d\n", key);
//2、创建一个共享内存的对象
int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
if(shmid == -1)
{
perror("shmget error");
return -1;
}
printf("shmid = %d\n", shmid);
//3、将共享内存段映射到程序中来
char *addr = (char *)shmat(shmid, NULL, 0);
//参数1:共享内存的id号
//参数2:系统自动映射对齐页
//参数3:表示对共享内存的操作权限为读写权限
printf("addr = %p\n", addr); //输出映射的地址
//向共享内存中写入数据
strcpy(addr, "hello a hua qing yuan jian\n");
sleep(5);
//取消映射关系
if(shmdt(addr) ==-1)
{
perror("shmdt error");
return -1;
}
while(1);
return 0;
}
4、接收端实现
#include<myhead.h>
#include<sys/user.h>
int main(int argc, const char *argv[])
{
//1、创建key值用于创建共享内存段
key_t key = ftok("/", 't');
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %d\n", key);
//2、创建一个共享内存的对象
int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
if(shmid == -1)
{
perror("shmget error");
return -1;
}
printf("shmid = %d\n", shmid);
//3、将共享内存段映射到程序中来
char *addr = (char *)shmat(shmid, NULL, 0);
//参数1:共享内存的id号
//参数2:系统自动映射对齐页
//参数3:表示对共享内存的操作权限为读写权限
printf("addr = %p\n", addr); //输出映射的地址
//读出共享内存中的数据
printf("消息为:%s", addr);
sleep(5);
if(shmdt(addr) == -1)
{
perror("shmdt error");
return -1;
}
//删除共享内存
if(shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("shmctl error");
return -1;
}
while(1);
return 0;
}
使用消息队列完成两个进程之间相互通信
00snd.c
#include <myhead.h>
// 要发送的信息类型
struct msgbuf
{
long mtype;
char mtext[1024];
};
#define SIZE sizeof(struct msgbuf) - sizeof(long)
int main(int argc, char const *argv[])
{
// 1、创建key值用于生成消息队列
key_t key = ftok("/", 'k');
if (key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %#x\n", key);
// 2、通过key值创建一个消息队列
int msqid = msgget(key, IPC_CREAT | 0664);
if (msqid == -1)
{
perror("msgget error");
return -1;
}
printf("msqid = %d\n", msqid);
// 3、创建父子进程
pid_t pid;
if ((pid = fork()) == -1)
{
perror("fork error");
return -1;
}
else if (pid == 0)
{
// 子进程
// 4、从消息队列接收信息
struct msgbuf buf;
while (1)
{
msgrcv(msqid, &buf, SIZE, 2, 0);
printf("进程1子程序收到信息:%s\n", buf.mtext);
if (strcmp(buf.mtext, "quit") == 0)
{
break;
}
}
exit(EXIT_SUCCESS);
}
// 父进程
// 3、向消息队列中输入数据
struct msgbuf buf;
buf.mtype = 1;
while (1)
{
printf("进程1父程序输入信息:");
fgets(buf.mtext, SIZE, stdin);
buf.mtext[strlen(buf.mtext) - 1] = 0;
// 将信息发送到消息队列中
msgsnd(msqid, &buf, SIZE, 0);
printf("发送结束\n");
if (strcmp(buf.mtext, "quit") == 0)
{
break;
}
}
//回收子程序资源
wait(NULL);
// 删除消息队列
if (msgctl(msqid, IPC_RMID, NULL) == -1)
{
perror("msgctl error");
return -1;
}
return 0;
}
00rcv.c
#include <myhead.h>
// 要发送的信息类型
struct msgbuf
{
long mtype;
char mtext[1024];
};
#define SIZE sizeof(struct msgbuf) - sizeof(long)
int main(int argc, char const *argv[])
{
// 1、创建key值用于生成消息队列
key_t key = ftok("/", 'k');
if (key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %#x\n", key);
// 2、通过key值创建一个消息队列
int msqid = msgget(key, IPC_CREAT | 0664);
if (msqid == -1)
{
perror("msgget error");
return -1;
}
printf("msqid = %d\n", msqid);
// 3、创建父子进程
pid_t pid;
if ((pid = fork()) == -1)
{
perror("fork error");
return -1;
}
else if (pid == 0)
{
// 子进程
// 4、向消息队列中输入数据
struct msgbuf buf;
buf.mtype = 2;
while (1)
{
printf("进程2子程序输入信息:");
fgets(buf.mtext, SIZE, stdin);
buf.mtext[strlen(buf.mtext) - 1] = 0;
// 将信息发送到消息队列中
msgsnd(msqid, &buf, SIZE, 0);
printf("发送结束\n");
if (strcmp(buf.mtext, "quit") == 0)
{
break;
}
}
exit(EXIT_SUCCESS);
}
// 父进程
// 4、从消息队列读取信息
struct msgbuf buf;
while (1)
{
msgrcv(msqid, &buf, SIZE, 1, 0);
printf("进程2父程序收到信息:%s\n", buf.mtext);
if (strcmp(buf.mtext, "quit") == 0)
{
break;
}
}
// 回收子程序资源
wait(NULL);
// 删除消息队列
if (msgctl(msqid, IPC_RMID, NULL) == -1)
{
perror("msgctl error");
return -1;
}
return 0;
}