c语言系统编程八:Linux进程间通信之消息队列

Linux进程间通信之消息队列

  • 一 消息队列概述
  • 二 消息队列的特点
  • 三 消息队列的创建和使用
    • 3.1 获取系统唯一的key值
    • 3.2 创建消息队列
    • 3.3 查看消息队列和删除消息队列的shell命令
    • 3.4 消息队列的信息格式的定义
    • 3.5 发送消息函数msgsnd
    • 3.6 接收消息函数msgrcv
    • 3.7 总结
    • 3.8 消息队列的控制
  • 四 消息队列练习题:实现多人聊天程序

一 消息队列概述

消息队列是消息的链表,存放在内存中,由内核维护;

二 消息队列的特点

1. 消息队列中的消息是有类型的;
2. 消息队列中的消息是有格式的;
3. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取;
4. 消息队列允许一个或多个进程向它写入或读取消息;
5. 与无名管道、命名管道一样,从消息队列中读取消息,消息队列中对应的消息将被删除;
6. 每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的;
7. 只有内核重启或人工删除消息队列时,该消息队列才会被删除;若不人工删除消息队列,消息队列将一直存在于系统中

c语言系统编程八:Linux进程间通信之消息队列_第1张图片

三 消息队列的创建和使用

System V提供的IPC通信机制需要一个key值,通过key值就可以在系统内获得一个唯一的消息队列标识符。key值可以人工指定,也可以通过ftok函数获得

3.1 获取系统唯一的key值

  1. 需要的头文件和函数原型
#include 
#include 
key_t ftok(const char *pathname, int proj_id);
  1. 功能
获得项目相关的唯一的IPC键值
  1. 参数
pathname:路径名
proj_id:项目ID,非0整数(只有低8位有效)
  1. 返回值
成功:返回key值;
失败:返回-1

c语言系统编程八:Linux进程间通信之消息队列_第2张图片

3.2 创建消息队列

  1. 需要的头文件和函数原型
#include 

int msgget(key_t key, int msgflg);

  1. 功能
创建一个新的或者打开一个已经存在的消息队列。不同进程调用此函数,只有用相同的key值就能得到同一个消息队列的标识符
  1. 参数
key:IPC的key值
msgflg:标识函数的行为以及消息队列的权限
			msgflg的取值:
					IPC_CREAT:创建消息队列
					IPC_EXCL:检测消息队列是否存在
					位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和open函数的mode_t一样,但可执行权限未使用
  1. 返回值
成功:返回消息队列的标识符;
失败:返回-1

c语言系统编程八:Linux进程间通信之消息队列_第3张图片
c语言系统编程八:Linux进程间通信之消息队列_第4张图片

3.3 查看消息队列和删除消息队列的shell命令

  1. 查看消息队列
[root@ansible9 ~]# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x01000080 0          root       666        0            0   
  1. 删除消息队列
    在这里插入图片描述

3.4 消息队列的信息格式的定义

消息队列的信息格式是需要人工定义的,格式放在一个结构体中;
信息结构体中的第一个long型的成员的作用是,只有对这类信息感兴趣的进程才能拿到这类信息

c语言系统编程八:Linux进程间通信之消息队列_第5张图片

3.5 发送消息函数msgsnd

  1. 需要的头文件和函数原型
#include 

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

  1. 功能
    向消息队列添加新消息
  2. 参数
msqid:消息队列标识符
msgp:要是的消息的结构体变量的地址
msgsz:消息正文的字节数(等于消息结构体的大小减去long类型的大小)
msgflg:函数的控制属性
			0:msgsnd调用阻塞直到条件满足为止
			IPC_NOWAIT:若消息没有立刻发送则调用该函数的进程会立刻返回
  1. 返回值
成功:0
失败:-1
  1. 实例
    c语言系统编程八:Linux进程间通信之消息队列_第6张图片

3.6 接收消息函数msgrcv

  1. 需要的头文件和函数原型
#include 

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
           int msgflg);
  1. 功能
    从标识符为msqid的信息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除
  2. 参数
msqid:消息队列的标识符,代表要从哪个消息队列中获取消息
msgp:存放消息的结构体地址
msgsz:消息正文的字节数
msgtyp:感兴趣的消息类型,可以有以下几种类型
	msgtyp=0:返回队列中第一个消息
	msgtyp>0:返回队列中消息类型为msgtyp的消息
	msgtyp<0:返回队列中消息类型小于等于msgtyp的绝对值的消息,如果这种消息有若干个,则取类型值最小的消息
	msgflg:函数的控制属性
		0:msgrcv调用阻塞直到接收消息成功为止
		MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截断到nbytes字节,且不通知消息发送进程;
		IPC_NOWAIT:调用进程会立即返回,若没有收到消息则立即返回-1
  1. 注意
若消息队列中有多种类型的消息,msgrcv获取消息的时候按消息类型获取,不是先进先出的;
在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出;
  1. 返回值
成功:返回读取消息的长度;
失败:返回-1
  1. 实例
    发:
#include 
#include 
#include 
#include 
#include 
#include 

int main(int arg, char *argv[])
{
	//获取IPC唯一key值key
	key_t key = ftok("/",1);
	printf("%#x\n",key);
	//创建一个消息队列,队列标识符为msg_id
	int msg_id = msgget(key,IPC_CREAT|0666);
	printf("%d\n",msg_id);
	//定义消息队列的信息格式MSG
	typedef struct msg
	{
		long type;
		char text[100];
		int a;
		char name[32];
	}MSG;

	//新建一个信息
	MSG msg;
	memset(&msg,0,sizeof(msg));
	msg.type=10;
	msg.a = 100;
	strcpy(msg.name,"andy");
	strcpy(msg.text,"ni hao");

	//发送上面新建的信息
	msgsnd(msg_id, &msg, sizeof(MSG)-sizeof(long),0);

	return 0;
}

收:

#include 
#include 
#include 
#include 
#include 
#include 

int main(int arg, char *argv[])
{
	//获取IPC唯一key值key
	key_t key = ftok("/",1);
	printf("%#x\n",key);
	//创建一个消息队列,队列标识符为msg_id
	int msg_id = msgget(key,IPC_CREAT|0666);
	printf("%d\n",msg_id);
	//定义消息队列的信息格式MSG
	typedef struct msg
	{
		long type;
		char text[100];
		int a;
		char name[32];
	}MSG;

	//接收消息
	MSG msg;
	memset(&msg,0,sizeof(msg));
	msgrcv(msg_id, &msg, sizeof(MSG)-sizeof(long), 10,0);
	printf("发送者:%s\n", msg.name);
	printf("消息:%s\n", msg.text);
	return 0;
}

3.7 总结

不管是发送者还是接收者

1. ftok得到唯一key
2. msgget创建消息队列

发送者

3. MSG msg;
4. msg.mtype=接收感兴趣的类型值;
5. msgsnd(msg_id, &msg, sizeof(MSG)-sizeof(long), 0);//发送消息到消息队列

接收者

1. MSG msg;
2. msgrcv(msg_id, &msg, sizeof(MSG)-sizeof(long), 接收感兴趣的类型值, 0);

3.8 消息队列的控制

  1. 需要的头文件和函数原型
#include 

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

  1. 功能
    对消息队列进行各种控制,如删除消息队列,修改消息队列的属性
  2. 参数
msqid:消息队列的标识符
cmd:函数功能的控制
buf:msqid_ds数据类型的地址,用来存放或更改消息队列的属性

cmd函数功能的控制:
	IPC_RMID:删除由msqid标识的消息队列,将他从系统中删除并破坏相关的数据结构
	IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中
	IPC_SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值
  1. 返回值
成功:返回0;
失败:返回-1
  1. 实例
    a. 删除消息队列
int w = msgctl(msg_id,IPC_RMID,NULL);

b. 查看消息队列属性
c语言系统编程八:Linux进程间通信之消息队列_第7张图片

c语言系统编程八:Linux进程间通信之消息队列_第8张图片

c语言系统编程八:Linux进程间通信之消息队列_第9张图片

四 消息队列练习题:实现多人聊天程序

c语言系统编程八:Linux进程间通信之消息队列_第10张图片

#include
#include
#include
#include
#include 
#include 
#include 


//定义人名和消息类型的对应关系
char *peple[3]  = {
	"andy:10",
	"linda:20",
	"tony:30"
};

long type = 0; //消息类型

//定义消息结构体
typedef struct _msg{
	long mtype; //接收者类型
	char content[100]; //发送的消息
	char name[20]; //发送者姓名
}MSG;

//标记我是谁
char me[20]="";

int main(int argc, char * kwargs[])
{
	//获取系统ipc key
	key_t key = ftok("/",100);
	//创建消息队列
	int msg_id = msgget(key, IPC_CREAT|0666);
	printf("消息id:%d\n",msg_id);
	//创建两个子进程一个发消息,一个收消息
	int i = 0;
	for(;i<2;i++)
	{
		pid_t pid = fork();
		if (pid == 0)
			break;
	}
	if(i==0) //第一个子进程负责发送消息
	{
		while(1)
		{
			char who[32] = "";
			printf("给谁发送消息:");
			fgets(who,sizeof(who),stdin);
			who[strlen(who)-1]='\0';
			strcat(who,":");
			for(int n=0;n<3;n++)
			{
				if(strncmp(who,peple[n],strlen(who))==0)
				{
					char *tmp1;
					char name[10]="";
					memcpy(name,peple[n],strlen(peple[n]));
					tmp1 = strtok(name,":");
					tmp1 = strtok(NULL,":");
					type = atol(tmp1);
					break;
				}
			}	
			char msg[128] = "";
			printf("请输入要发送的消息:");
			fgets(msg,sizeof(msg),stdin);
			msg[strlen(msg)-1]='\0';
			//将消息放入消息队列
			MSG new_msg;
			new_msg.mtype = type;
			memcpy(new_msg.content,msg,strlen(msg));
#ifdef ANDY
			memcpy(new_msg.name,"andy",sizeof("andy"));
#endif		
#ifdef LINDA
			memcpy(new_msg.name,"linda",sizeof("lnda"));
#endif		
#ifdef TONY
			memcpy(new_msg.name,"tony",sizeof("tony"));
#endif		
			int res = msgsnd(msg_id,&new_msg,sizeof(MSG)-sizeof(long),0);
		}
	}
	else if(i==1) //第二个子进程负责接收消息
	{
		while(1)
		{
			MSG new_msg;
			char who[20];
#ifdef ANDY
			memcpy(who,"andy:",strlen("andy:"));
#endif
#ifdef LINDA
			memcpy(who,"linda:",strlen("linda:"));
#endif
#ifdef TONY
			memcpy(who,"tony:",strlen("tony:"));
#endif
			for(int n=0;n<3;n++)
			{
				if(strncmp(who,peple[n],strlen(who)-1)==0)
				{
					char *tmp1;
					char name[10]="";
					memcpy(name,peple[n],strlen(peple[n]));
					tmp1 = strtok(name,":");
					tmp1 = strtok(NULL,":");
					type = atol(tmp1);
					break;
				}
			}	
			//接收消息
			int res = msgrcv(msg_id,&new_msg,sizeof(MSG)-sizeof(long),type,0);
			printf("\r%s说:%s\n",new_msg.name,new_msg.content);
			printf("给谁发消息: ");
			fflush(NULL);
		}	
	}
	else if(i==2) //父进程负责子进程资源回收
	{
		while(1)
		{
			pid_t pid = waitpid(-1,NULL,WNOHANG);
			if(pid>0)
			{
				printf("子进程%d退出了\n",pid);
			}
			else if(pid==0)
			{
				continue;
			}
			else if(pid == -1)
			{
				break;
			}
		}
	}
	return 0;
}

你可能感兴趣的:(c语言,C语言系统编程,linux,c语言,运维)