C++ 进阶 day12 多进程编程

system V提供的进程间通信

  • 对于内核提供的三种通信方式,对于管道而言,只能实现单向的数据通信,对于信号通信而言,只能完成多进程之间消息的通知,不能起到数据传输的效果。为了解决上述问题,引入的系统V进程间通信

  • system V提供的进程间通信方式分别是:消息队列、共享内存、信号量(信号灯集)

  • 有关system V进程间通信对象相关的指令

  • ipcs :可以查看所有消息队列(消息队列,内存共享,信号量)

  • ipcs -q:可以查看消息队列

  • ipcs -m:可以查看共享内存的消息

  • ipcs -s:可以查看信号量的信息

  • ipcrm -q/m/s ID:可以删除指定ID的IPC对象

  • 上述的三种通信方式,也是借助内核空间完成的相关通信,原理是在内核空间创建出相关的对象容器,在进行进程间通信时,可以将信息放入对象中,另一个进程就可以从该容器中取数据了。

  • 与内核提供的管道、信号通信不同:system V的ipc对象实现了数据传递的容器与程序相分离,也就是说,即使程序以己经结束,但是放入到容器中的数据依然存在,除非将容器手动删除

消息队列

实现原理

消息队列实现的API

创建key值

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);//ftok("/",'k');
功能:通过给定的文件以及给定的一个随机值,创建出一个4字节整数的key值,用于system V IPC对象的创建

  • 参数1:一个文件路径,要求是已经存在的文件路径,提供了key值3字节的内容,其中文件的设备号占1字节,文件的inode号占2字节
  • 参数2:一个随机整数,取后8位(1字节)跟前面的文件共同组成key值,必须是非0的数字
  • 返回值:成功返回key值,失败返回-1并置位错误码

通过key值,创建消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
好多头文件(
int msgget(key_t key, int msgflg);

  • 功能:通过给定的key值,创建出一个消息队列的对象,并返回消息队列的句柄ID,后期可以通过该ID操作整个消息队列
  • 参数1:key值,该值可以是IPC_PRIVATE,也可以是ftok创建出来的,前者只用于亲缘进程间的通信
  • 参数2:创建标识
  • IPC_CREAT:创建并打开一个消息队列,如果消息队列已经存在,则直接打开
  • IPC_EXCL:确保本次创建处理的是一个新的消息队列,如果消息队列已经存在,则报错,错误码位EEXIST
  • 0664:该消息队列的操作权限
  • 返回值:成功返回消息队列的ID号,失败返回-1并置位错误码

向消息队列中存放数据

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

  • 功能:向消息队列中存放一个指定格式的消息
  • 参数1:打开的消息队列的id号
  • 参数2:要发送的消息的起始地址,消息一般定义为一个结构体类型,由用户手动定义
struct msgbuf{
   long mtype;//消息类型
   char mtext[1];//详细内容
。。。
}
  • 参数3:消息正文的大小(宏定义mtext)
  • 参数4:是否阻塞的标识
  • 0:标识阻塞形式向消息队列中存放消息,如果消息队列满了,就在该函数处阻塞
  • IPC_NOWAIT:标识非阻塞的形式向消息队列中存放消息,如果消息队列满了,直接返回
  • 返回值:成功返回0,失败返回-1并置位错误码

从消息队列中取消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int
msgflg);

  • 功能:
  • 参数1:
  • 参数2:
struct msgbuf {
     long mtype;       /* message type, must be > 0 */   消息的类型
     char mtext[1];    /* message data */    消息正文
     。。。
};
  • 参数3:消息正文的大小
  • 参数4:要接收的消息类型
    • 0:表示每次都取消息队列中的第一个消息,无论类型
    • 大于0:读取队列中第一个类型为msgtyp的消息
    • 小于0:读取队列中的一个消息,消息为绝对值小于msgtyp的第一个消息
      eg:    10-->8-->3-->6-->5-->20-->2
      -5: 会从队列中绝对值小于5的类型的消息中选取第一个消息,就是3
  • 参数5:是否阻塞的标识
    • 0:标识阻塞形式向消息队列中读取消息,如果消息队列空了,就在该函数处阻塞
    • IPC_NOWAIT:标识非阻塞的形式向消息队列中读取消息,如果消息队列空了,直接返回
  • 返回值:成功返回实际读取的正文大小,失败返回-1并置位错误码

销毁消息队列

#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_RMID:删除一个消息队列,当cmd为该值时,第三个参数可以省略填NULL即可
    • IPC_STAT:表示获取当前消息队列的属性,此时第三个参数就是存放获取的消息队列属性的容器起始地址
    • IPC_SET:设置当前消息队列的属性,此时第三个参数就是要设置消息队列的属性数据的起始地址

实践

发送端

#include <myhead.h>
//消息类型的定义
struct msgBuf{
	long mtype;
	char mtext[1024];
};
#define MSGSZ (sizeof(struct msgBuf)-sizeof(long)) //正文的大小

int main(){
	//1.创建key值,用于创建出一个消息队列
	key_t key=ftok("/",'k');
	//参数1:已经存在的路径
	//参数2:是一个随机值
	printf("key = %#x\n",key);

	//2.通过key值创建出消息队列,并返回该消息队列的id
	int msqid=-1;
	if((msqid=msgget(key,IPC_CREAT|0664))==-1){
		perror("msgget error");
		return -1;
	}
	printf("msggid = %d\n",msqid);//输出id号

	//3.像消息队列中存放消息
	//组建一个消息
	struct msgBuf buf;
	while(1){
		printf("请输入消息的类型:");
		scanf("%ld",&buf.mtype);
		getchar();//吸收回车fgets()
		printf("请输入消息正文:");
		fgets(buf.mtext,MSGSZ,stdin);//从终端中输入数据
		//1.指针地址
		//2.大小
		//3.特殊标识符标准输入0,1,2 没有引入其他文件
		buf.mtext[strlen(buf.mtext)-1]='\0';

		//4.将上述组装的消息放入消息队列中,以阻塞的方式将其放入消息队列
		msgsnd(msqid,&buf,MSGSZ,0);
		//1.id号
		//2.地址
		//3.大小
		//4.处理形式,队列为空时堵塞
		printf("消息存入成功\n");

		//判断推出条件
		if(strcmp(buf.mtext,"quit")==0){
			break;
		}
	}
	return 0;
}

接收端

#include <myhead.h>
//消息类型的定义
struct msgBuf{
	long mtype; //消息类型
	char mtext[1024]; //消息正文
};

#define MSGSZ (sizeof(struct msgBuf)-sizeof(long))

int main(){
	//1.创建key值,用于创建出一个消息队列
	key_t key = ftok("/",'k');
	//参数1:已经存在的路径
	//参数2:是个随机数
	if(key==-1){
		perror("ftok error");
		return -1;
	}
	printf("key = %#x\n",key);
	//2.通过key值创建出一个消息队列,并返回该消息队列的id
	int msqid=-1;
	if((msqid=msgget(key,IPC_CREAT|0664))==-1){
		perror("msgget error");
		return -1;
	}

	printf("msgqid = %d\n",msqid);

	//3.从消息队列中取出消息
	//组建一个消息
	struct msgBuf buf;
	while(1){
		//清空容器
		bzero(&buf,sizeof(buf));

		//读取信息
		msgrcv(msqid, &buf, MSGSZ, 1, 0);
		//参数4:表示读取的消息类型
		//参数5:是否堵塞

		printf("读取到的消息类型为:%s\n",buf.mtext);

		if(strcmp(buf.mtext,"quit")==0){
			break;
		}
	}

	//4.删除消息对列
	if(msgctl(msqid,IPC_RMID,NULL)==-1){
		perror("msgctl error");
		return -1;
	}

	return 0;
}

分屏观察:

posted @ 2025-05-15 22:24  北燃  阅读(32)  评论(0)    收藏  举报