任务间通信(1)

news2024/11/25 10:54:47

任务间通信

目录

任务间通信

回顾

-- WiFi模块:1、所有和服务器相关的操作,2、可以实现局域网通信

-- 操作系统(Freertos):

FreeRTOS之任务间通信

消息队列

信号量

更改接收数据方式

互斥量


回顾

-- 我们要学的是什么?

-- 首先要了解模块的基本作用,第二知道模块更进一步的应用,或者说知道这个模块的应用场景

-- 要学会理代码思路

-- 上一周讲的是wifi和操作系统(Freertos)

-- WiFi模块:1、所有和服务器相关的操作,2、可以实现局域网通信

-- wifi是咱们的联网模块(esp8266,4G,NBIOT模块等都可以实现联网)

-- 大家要记忆的是首先要知道服务器怎么操作?

-- 怎么连接阿里云?(阿里云是支持mqtt协议的服务器)

  • 1、连接阿里云服务器
  • 2、发送mqtt连接报文,连接物联网平台
  • 3、订阅或者发布数据

-- 阿里云也可以获取实时时间,阿里云上有一个NTP的主题,订阅之后,发布请求,就可以获得实时时间

-- 其他的服务器支持的协议可能也会不同(mqtt、http、tcp/ip。。。)

-- 目前手机APP是咱们借助阿里云平台自己开发的,到公司的时候有专门人做APP,那么咱们所作的工作就是将数据上传云平台

-- 如何保证数据每次都能上传成功?

  • mqtt协议上有一个发布确认报文,阿里云平台上有一个主题叫做发布响应主题

  • 当数据上传成功之后,阿里云平台会向这个主题发布一个响应报文,咱们只要订阅这个主题,就可以知道数据是否上传成功

-- 操作系统(Freertos):

-- 主要做的是操作系统的移植和简单应用

-- 加入操作系统之后,逻辑变复杂了,但是出去工作99%都要加操作系统

-- 操作系统有很多,比如FREERTOS,UCOS,RTTHREAD、LINUX等。。。

-- 每一种操作系统,使用起来都会有所不同,但是操作系统的核心思想都是一致的

-- 那什么是操作系统的核心思想?是任务管理

FreeRTOS之任务间通信

-- 参考文档 

alt text

-- 这个文档从17章开始之后就是任务间通信

消息队列

alt text

-- 核心用途:消息传递

-- 裸机中传递消息的是数组

-- 消息队列就是来传递消息的

-- 那么数组就可以传递消息,为什么要引入消息队列呢?

-- 消息队列的特性 

alt text

-- 数组中的消息会被覆盖

alt text

alt text

alt text

alt text

-- 消息队列的三种阻塞方式

alt text

alt text

alt text

-- 消息队列的核心控制有四个函数,基本上每一个任务间通信都有这四个函数

alt text

-- 可以说任务间通信就是学这四个函数,一般情况下,创建和删除是固定的,实际上是学不同通信的读写

alt text

alt text

-- 下面依旧是找到事例复制到工程中

alt text

 

alt text

alt text

-- 发送和接收函数

alt text

alt text

alt text

-- 在工程中编写代码

-- 这里将发送写在按键任务中

#include "freertos.h"
#include "task.h"
#include "queue.h"

QueueHandle_t Test_Queue =NULL;
//例如我们创建一个按键的任务
//第一步
TaskHandle_t keyTaskCreat_Handle = NULL;//任务句柄
//2
void KETTaskCreat(void *pvParameters)//函数名称不固定,参数格式是固定的(任务一定是一个while循环,每个任务一定要有自己的任务周期)//设置任务周期的目的就是可以让任务可以主动释放cpu(指的是任务能够自动释放cpu)
{
	uint8_t keyflag = 0;
	uint8_t buff[10]={0};
	BaseType_t xReturn = pdPASS;
    while(1)
    {
				keyflag = get_key();
				switch(keyflag)
				{
					case 1:
//					vTaskDelete( NULL );//删除自身
					//vTaskDelete(Led1TaskCreat_Handle);//删除led的任务
					printf("发送消息 send_data1!\n");
					xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */
						"123456qwe",0);/* 发送的消息内容 */
					/* 等待时间 0 */
					if (pdPASS == xReturn)
					printf("消息 send_data1 发送成功!\n\n");
					break;
					case 2: 
						xReturn = xQueueReceive( Test_Queue,
				/* 消息队列的句柄 */
					buff,/* 接收的消息内容 */
					10000); //超时时间(等待10s)/* 等待时间 一直等 */
					if (pdTRUE== xReturn)
					printf("本次接收到的数据是:%s\r\n",buff);
					else
					printf("数据接收出错,错误代码: 0x%lx\r\n",xReturn);
					break;
				}  
			vTaskDelay(50);				//在任务周期时可以释放cpu
    }
}

int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	usart_init();
	key_init();
	
	/* 创建 Test_Queue */
    Test_Queue = xQueueCreate(3,/* 消息队列的长度 */
                           10);/* 消息的大小 */
	if (NULL != Test_Queue)
	printf("创建 Test_Queue 消息队列成功!\r\n");
	//Delay_nms(2000);
                        
//KEY*******************************************************************************************************

		BaseType_t xReturn1  = pdPASS;
    
    xReturn1 = xTaskCreate(KETTaskCreat,                    //任务函数接口 函数名称																//任务主体函数名称(也叫做任务函数接口)
                                                "key",                                    //任务别名不能一样			//人为起的一个任务别名(不能超过16个字节)(不能太长)(相当于给任务起绰号)(每个任务不能相同)
                                                200,                                        //任务栈空间(字节)	//单位是字节(一般在创建任务的时候空间尽量给大一些)
                                                NULL,                                        //任务传参						//任务传参,有参数的时候写,没参数写NULL
                                                1,                                            //任务优先级  那个任务重要,优先级高		//任务优先级,
                                                &keyTaskCreat_Handle); //任务句柄    													
				if(xReturn == pdPASS)
					vTaskStartScheduler();//任务调度
}

alt text

 -- 效果图

alt text

-- 注意,消息队列读完数据后,数据就没有了。

-- 执行的结果,如果先点发送数据,再点接收数据,就会比较快的接收到,如果先按按键2(也就是先接收数据的时候),消息队列里面没有数据,就会等待设置的超时时间后,再结束。

信号量

alt text

-- 信号量用于任务同步,资源保护。。。

信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。 抽象的来讲,信号量是一个非负整数,所有获取它的任务都会将该整数减一(获取它当然是为了使用资源),当该整数值为零时,所有试图获取它的任务都将处于阻塞状态。 通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其值的含义分两种情况:

  • 0:表示没有积累下来的释放信号量操作,且有可能有在此信号量上阻塞的任务。
  • 正值,表示有一个或多个释放信号量操作。

-- 信号量本质就是一个数字(非负整数)

-- 在操作系统中,信号量是有分类的,

-- 信号量中有两种信号量是比较关键的,一个叫做二值信号量,一个叫做互斥信号量

-- 二值信号量更偏向于任务同步,互斥信号量更偏向于资源保护 

alt text

-- 任务同步: 

alt text

-- 什么叫做任务同步?

  • 例如屏幕显示与数据获取,数据获取之后,立马就要在屏幕上显示
  • 任务同步用于两个有关联的任务

-- 如果一个信号量只有0和1两个值,那么这个信号量就是二值信号量

-- 为什么叫二值信号量呢?因为信号量资源被获取了,信号量值就是 0,信号量资源被释放,信号量值就是 1,把这种只有 0 和 1 两种情况的信号量称之为二值信号量。

-- 二值信号量的应用场景?

-- 二值信号量在任务与中断同步的应用场景:我们在串口接收中,我们不知道啥 时候有数据发送过来,有一个任务是做接收这些数据处理,总不能在任务中每时每刻都在任务查询有没有数据到来,那样会浪费 CPU 资源,所以在这种情况下使用二值信号量是很好的办法,当没有数据到来的时候,任务就进入阻塞态,不参与任务的调度,等到数据到来了,释放一个二值信号量,任务就立即从阻塞态中解除,进入就绪态,然后运行的时候处理数据,这样子系统的资源就会很好的被利用起来。

-- 编写代码

-- 这里由于消息队列对于项目的用处没有那么大,可以尝试将消息队列裁剪掉,操作系统的空间就会更大些.将消息队列的宏定义由1变为0.这就叫做操作系统裁剪。

alt text

-- 这样操作系统就可以不需要那么多空间,所以分给操作系统的空间也可以适量降低

alt text

-- 编写二值信号量代码

-- 依旧是复制事例

-- 创建二值信号量(文档P261页)

-- 释放信号量应该放在采集DHt11的任务里,为什么呢?

-- 因为咱们希望的是采集完数据后,再在LCD中显示

-- 获取写在LCD显示任务里,因为咱们希望的是先采集数据,再显示数据

-- 如果不用二值信号量,获取一次DHT11数据的时间是1s,在LCD上刷新数据的时间是0.5s,所以至少有两次LCD上显示的数据是一样的,那是不是就浪费了cpu的资源,所以我们设置一个同步机制。采取到时间后,再去显示LCD上。

#include "freertos.h"
#include "semphr.h"

SemaphoreHandle_t xSemaphore = NULL;

TaskHandle_t DHTTaskCreat_Handle = NULL;
TaskHandle_t LCDTaskCreat_Handle = NULL;

void DHTTaskCreat(void *pvParameters)//函数名称不固定,参数格式是固定的(任务一定是一个while循环,每个任务一定要有自己的任务周期)//设置任务周期的目的就是可以让任务可以主动释放cpu(指的是任务能够自动释放cpu)
{
	while(1)
	{
			
			get_dht11_val();
			xSemaphoreGive( xSemaphore );//释放信号量
			vTaskDelay(1000);
	}
}

void LCDTaskCreat(void *pvParameters)//函数名称不固定,参数格式是固定的(任务一定是一个while循环,每个任务一定要有自己的任务周期)//设置任务周期的目的就是可以让任务可以主动释放cpu(指的是任务能够自动释放cpu)
{
	BaseType_t xReturn = pdPASS;//有没有获取成功,再显示LCD
	while(1)
	{
		xReturn = xSemaphoreTake(xSemaphore,/* 二值信号量句柄 */
					500); /* 等待时间 */
		if (pdTRUE == xReturn)//获取成功
		{
				printf("BinarySem_Handle 二值信号量获取成功!\n\n");
				LCD_ShowPhoto(0,80,98,100,(uint8_t *)gImage[i++]);
				if(i>=2)i=0;
				sprintf(D_wen, "tem: %.2f℃",dht.tem);
				sprintf(D_shi, "hum: %.2f%RH", dht.hum);
				//printf("tem:%.2f℃\r\n",dht.tem);
				LCD_ShowString(0,16,strlen(D_wen)*8,16,16,D_wen);
				LCD_ShowString(0,32,120,16,16,D_shi);
				sprintf(D_time, "%04d/%02d/%02d   %02d:%02d:%02d",a.tm_year+1900,a.tm_mon+1,a.tm_mday,a.tm_hour+8,a.tm_min,a.tm_sec);
				LCD_ShowString(0,48,180,16,16,D_time);
				//vTaskDelay(500);
		}
			
		
		
	}
			
}

int main()
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	dht11_init();
	usart_init();;
	LCD_Init();//屏幕初始化一定要写到串口初始化后面
    //创建信号量**********************************************
	/* 尝试创建一个信号量 */
	LCD_DrawPoint(10,10);
	LCD_ShowString(0,0,80,16,16,"123456qwer");						//宽度字符是汉字的一半,是8,汉字是16,8*10
	Delay_nms(2000);

	xSemaphore = xSemaphoreCreateBinary();

	if( xSemaphore == NULL ) {
	/* 内存不足,创建失败 */
	} else {
		printf("二值信号量创建成功\r\n");
    }

	BaseType_t xReturn  = pdPASS;

    //DHT11******************************************************************************************************

    
    xReturn = xTaskCreate(DHTTaskCreat,                    //任务函数接口 函数名称																//任务主体函数名称(也叫做任务函数接口)
                                                "dht",                                    //任务别名不能一样			//人为起的一个任务别名(不能超过16个字节)(不能太长)(相当于给任务起绰号)(每个任务不能相同)
                                                200,                                        //任务栈空间(字节)	//单位是字节(一般在创建任务的时候空间尽量给大一些)
                                                NULL,                                        //任务传参						//任务传参,有参数的时候写,没参数写NULL
                                                2,                                            //任务优先级  那个任务重要,优先级高		//任务优先级,
                                                &DHTTaskCreat_Handle); //任务句柄    									
//LCD******************************************************************************************************
				
    
   xReturn = xTaskCreate(LCDTaskCreat,                    //任务函数接口 函数名称																//任务主体函数名称(也叫做任务函数接口)
                                                "lcd",                                    //任务别名不能一样			//人为起的一个任务别名(不能超过16个字节)(不能太长)(相当于给任务起绰号)(每个任务不能相同)
                                                512,                                        //任务栈空间(字节)	//单位是字节(一般在创建任务的时候空间尽量给大一些)
                                                NULL,                                        //任务传参						//任务传参,有参数的时候写,没参数写NULL
                                                3,                                            //任务优先级  那个任务重要,优先级高		//任务优先级,
                                                &LCDTaskCreat_Handle); //任务句柄    		
                if(xReturn == pdPASS)
					vTaskStartScheduler();//任务调度器			//当程序执行完这句话之后,正常情况下下面的代码不会再运行,只会运行任务主题函数
}
更改接收数据方式

-- 将之前裸机的写法改为操作系统

alt text

-- 这里是中断接收数据完成后,再去判断数据,所以先要判断数据是否接收完成,再判断数据是否正确。 

alt text

-- 因此将释放信号量写在接收数据完成之后。而判断接收数据完成是在中断里,而中断中的释放函数跟普通的释放函数不一样。 

alt text

alt text

-- 在判断接收的数据前获取信号量

alt text

-- 代码

void USART3_IRQHandler(void)//串口收到1字节数据,中断就会被触发一次
{
	uint8_t data = 0;
	//判断接收中断是否发生
	if(USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
	{
		//处理中断:保存数据
			data = USART_ReceiveData(USART3);
		USART_SendData(USART1, data);
		
		esp.rxbuff[esp.rxlen++] = data;
		esp.rxlen%=1024;				//计数值最大是1024(前面一堆乱码也会存在buff里面,要控制数据)
		
		//清理终端 
		USART_ClearITPendingBit(USART3,USART_IT_RXNE);
	}
	
	
	
	if(USART_GetITStatus(USART3, USART_IT_IDLE) == SET)
	{
		//esp.rxflag=1;
		 BaseType_t pxHigherPriorityTaskWoken = 0;
		 //释放二值信号量,发送接收到新数据标志,供前台程序查询
			xSemaphoreGiveFromISR(xSemaphore,&pxHigherPriorityTaskWoken);
			//如果需要的话进行一次任务切换,系统会判断是否需要进行切换
			portYIELD_FROM_ISR(pxHigherPriorityTaskWoken); 
		//清理空闲中断标志(比较特殊)
		data = USART_ReceiveData(USART3);
		
	}
}


//发送不定长数组
void uart3_txstr(uint8_t *buff)
{
	while( *buff )
	{
		uart3_tx(*buff);
		buff++;
	}
}


//判断指令是否发送成功
uint8_t SendCmdAndJudgeSuccess(uint8_t *cmd,uint8_t * ack,uint16_t outtime)//指令和回复的,超时时间(每条指令都有最大的超时时间)
{
	//清空缓冲区
	
	memset(esp.rxbuff,0,1024);
	esp.rxlen = 0;
	esp.rxflag = 0;
	
	
	//1、发送指令
	uart3_txstr(cmd);
	
	//2、判断接收数据中是否有对应的ack
//	while(outtime--)
//	{
//		if(esp.rxflag==1)
//		{
//			esp.rxflag=0;							//接受两次数据,一次是(前三句话)disconnect会触发,没有OK,然后退出,后面输出ok会再次触发
//			if(strstr((char *)esp.rxbuff,(char *)ack)!=NULL)//strstr找到返回地址,没找到返回空
//			return 1;
//		}
//		Delay_nms(1);
//	}
//	
//改成操作系统
	BaseType_t xReturn = pdPASS;
	AAA:
		xReturn = xSemaphoreTake(xSemaphore,outtime);
		if(pdTRUE == xReturn)
		{
			if(strstr((char *)esp.rxbuff,(char *)ack)!=NULL)//strstr找到返回地址,没找到返回空
			{
				return 1;
			}	else 
				goto AAA;
		}



	return 0;
}

-- 执行效果图,数据都在获取,不会相互影响 

alt text

-- 注意:信号量无论释放多少次,都只能获取一次,因为他只有两个值

互斥量

-- 他有一个很重要的特性:防止优先级翻转

-- 本来我们写的代码,LCD屏幕的优先级比DHT11高,但是我们写了同步之后,我们要等到DHT11任务执行完,才会执行LCD任务,这样优先级翻转就发生了,低优先级的比高优先级的先执行。这就叫做优先级翻转

alt text

-- 互斥量怎么防止优先级翻转

-- 互斥量有个特点叫做优先级继承P277

-- 

alt text

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2223553.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS设置层叠样式时报红(identifier expected css/selector expected css)

不规范语法 如上图所示,在一个 css 文件中添加层叠样式时报红:at-rule or selector expected,意思就是说我们的语句不符合 css 的语法书写规范,虽然不会导致启动报错并且还能达到预期的样式效果,但是对于有强迫症的同学…

Python爬虫进阶(实战篇一)

接,基础篇,链接:python爬虫入门(所有演示代码,均有逐行分析!)-CSDN博客 目录 1.爬取博客网站全部文章列表 ps:补充(正则表达式) 爬虫实现 爬虫代码: 2.爬…

java控制台打印乘法口诀表

目录 前言具体代码完整代码 前言 背乘法口诀表我没记错话,应该是我们在上小学二年级的时候,相信大家对乘法表相当熟悉,那你知道如何用java打印这个漂亮的表吗?下面咱们一起来学习学习。 具体代码 数字乘法表 关键代码&#xf…

shell编程实例1—猜数字游戏

脚本生成一个100以内的随机数,提示用户猜数字,根据用户的输入,提示用户猜对了, 猜小了或猜大了,直至用户才对数字结束 #!/bin/bash #脚本生成一个100以内的随机数,提示用户猜数字,根据用户的输…

大模型生图安全疫苗注入——进阶解决方案与系统优化(DataWhale组队学习)

引言 大家好,我是GISer Liu😁,上篇博客中,我们基于DataWhale 2024年10月大模型生图安全疫苗注入赛道的任务,介绍了攻击与防御的基本策略,如通过上下文稀释法、隐喻替换等绕过检测机制,并提出了多…

分布式IO模拟量模块:多领域应用的高效能解决方案

分布式IO模拟量模块是分布式IO系统中的重要组件,用于实现现场设备或过程的模拟量信号的采集、监视和控制。该模块通常与现场总线耦合器配合使用,能够接收来自现场设备的模拟量信号(如电流、电压等),并将其转换为数字信…

利用飞腾派进行OpenCV开发

实验目标: 完成飞腾平台OpenCV开发。 实验大纲: Mat数据结构加载、显示、保存图像读写像素RGB图像分离彩色图转灰度图 Mat数据结构 Mat是一个类,由两个数据部分组成:矩阵头(大小,通道,数据类型等)和数据块(像素 值)。创建示例…

Chat-macOS:HuggingChat 开源 MACOS 原生 AI 聊天神器,让你的 Mac 变成智能助手!

❤️ 如果你也关注大模型与 AI 的发展现状,且对大模型应用开发非常感兴趣,我会快速跟你分享最新的感兴趣的 AI 应用和热点信息,也会不定期分享自己的想法和开源实例,欢迎关注我哦! 🥦 微信公众号&#xff…

毕业设计—基于 Inception-ResNet模型的皮肤癌分类系统实现

1.摘要 皮肤癌是人类最常见的恶性肿瘤,主要通过视觉诊断进行初步临床筛查。但是由于皮肤病变外观的细微变化性,使用图像自动分类皮肤病变是一项具有挑战性的任务。本文为了提高深度学习算法在皮肤病检测上的准确率,本文提出了基于Inception和…

bootstrap模态框myModalLabel遇到做复制的功能失效解决方案整理

bootstrap模态框myModalLabel遇到做复制的功能失效解决方案整理 解决办法:标红色的去掉就可以 tabindex“-1”

SOLIDWORKS专业版企业购买多少钱一套?

SOLIDWORKS正版软件分为三个版本,主要以每个版本的功能不同对价格进行划分,SOLIDWWORKS代理商硕迪科技将为企业提供优惠的采购价格,欢迎通过电话或者在线咨询联系我们,洽谈价格和服务。 ▲ SOLIDWORKS Professional 是应用最为广…

网站建设中需要注意哪些安全问题?----雷池社区版

服务器与应用安全指南 1. 服务器安全 1.1 操作系统安全 及时更新补丁:确保操作系统始终安装最新补丁,以防范系统漏洞。例如,Windows Server 定期推送安全更新,修复如远程代码执行等潜在威胁。优化系统服务配置:关闭不…

什么是3D展厅?有哪些应用场景?

3D展厅是一种利用三维技术构建的虚拟展示空间。它借助虚拟现实(VR)、增强现实(AR)等现代科技手段,将真实的展示空间数字化,呈现出逼真、立体、沉浸的展示效果。视创云展通过整合虚拟展厅、数字人互动、音视…

【真题笔记】09-12年系统架构设计师要点总结

【真题笔记】09-12年系统架构设计师要点总结 41 视图DSSA(特定领域架构)集成系统数据库管理设计模式操作符运算符综合布线备份数据库集成工作流技术软件质量保证需求管理需求开发结构化方法企业战略数据模型事务数据库主题数据库系统设计原型开发静态分析…

组件通信八种方式(vue3)

一、父传子&#xff08;props&#xff09; 关于Props的相关内容可以参考&#xff1a;Props-CSDN博客 父组件通过 props 向子组件传递数据。适合简单的单向数据流。 <!-- Parent.vue --> <template><Child :message"parentMessage" /> </temp…

useEffect简单介绍

react组件生命周期 比如说&#xff0c;某些操作就只在初始渲染后执行&#xff0c;我们就可以使用useEffect。 useEffect(function () {fetch(http://www.omdbapi.com/?apikey${KEY}&sinterstellar).then((res) > res.json()).then((data) > setMovies(data.Search)…

[C#][winform]基于yolov5的驾驶员抽烟打电话安全带检测系统C#源码+onnx模型+评估指标曲线+精美GUI界面

【重要说明】 该系统以opencvsharp作图像处理,onnxruntime做推理引擎&#xff0c;使用CPU进行推理&#xff0c;适合有显卡或者没有显卡windows x64系统均可&#xff0c;不支持macOS和Linux系统&#xff0c;不支持x86的windows操作系统。由于采用CPU推理&#xff0c;要比GPU慢。…

传统数据仓库升级版:云数据仓库!

随着公司业务拓展&#xff0c;数据爆炸性增长&#xff0c;数据驱动的见解已成为决策过程中关键指标。对公司来说&#xff0c;怎么选择存储所有这些信息的简单方法并运行必要的数据分析以获得有用的见解变得更加重要。 在过去的50多年里&#xff0c;传统的本地数据仓库一直是一…

ffmpeg视频滤镜:腐蚀滤镜

滤镜简述 erosion 官网链接> FFmpeg Filters Documentation 这个滤镜会在视频上应用腐蚀操作&#xff0c;腐蚀操作是形态学中一种操作&#xff0c;接触过opencv的同学应该很熟悉。滤镜主要有如下作用&#xff1a; 去除噪声&#xff1a;腐蚀可以帮助去除图像中的小颗粒噪…

构建后端为etcd的CoreDNS的容器集群(六)、编写自动维护域名记录的代码脚本

本文为系列测试文章&#xff0c;拟基于自签名证书认证的etcd容器来构建coredns域名解析系统。 一、前置文章 构建后端为etcd的CoreDNS的容器集群&#xff08;一&#xff09;、生成自签名证书 构建后端为etcd的CoreDNS的容器集群&#xff08;二&#xff09;、下载最新的etcd容…